
The latest trend in technology at the moment is the amazingly powerful ChatGPT by the OpenAI group. While it will soon be available through OpenAI’s API, I couldn’t wait to see what incredible things were possible with their existing APIs.
This is where I came across DALL·E 2, an artificial intelligence model and language and image-processing model that generates images from textual descriptions.
The name DALL-E is a combination of the artist Salvador Dali and the Pixar character Wall-E, and it is meant to represent the model’s ability to create surreal and imaginative images from text.
We will use OpenAI’s API to create an image generator using DALL·E 2. The application will be built in Blazor using C# and .NET 7. In order to use OpenAI’s API, you will first need to sign up in order to obtain an API key.
Creating an image
According to the documentation, the user provides a text prompt to the API, which will then generate an image based on the prompt and return a URL or Base64.
For reference, we will be implementing the API as documented here.
Let’s create a CreateImageRequestDTO that will capture the user’s text prompt:
public class CreateImageRequestDTO { [Required] [StringLength(1000)] public string? Prompt { get; set; } }
Referring to the documentation, we can create a class that will represent the response from the API. Create the following ImageResponseDTO:
public class ImageResponseDTO { public int Created { get; set; } public List<Image>? Data { get; set; } } public class Image { public string? Url { get; set; } }
Now we can create an ImageService that will perform the request to the API:
public interface IImageService { Task<ImageResponseDTO?> CreateImage(CreateImageRequestDTO createImageRequestDTO); Task<ImageResponseDTO?> EditImage(EditImageRequestDTO editImageRequestDTO); } public class ImageService : IImageService { private readonly string apiKey; private readonly string createImageRequestUri; private readonly string imageSize; public ImageService(IConfiguration configuration) { apiKey = configuration["OpenAIAPIKey"]!; createImageRequestUri = "https://api.openai.com/v1/images/generations"; imageSize = "512x512"; } public async Task<ImageResponseDTO?> CreateImage(CreateImageRequestDTO createImageRequestDTO) { using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); using StringContent jsonContent = new( JsonSerializer.Serialize(new { prompt = createImageRequestDTO.Prompt, size = imageSize }), Encoding.UTF8, "application/json"); using HttpResponseMessage response = await client.PostAsync( createImageRequestUri, jsonContent); return await response.Content.ReadFromJsonAsync<ImageResponseDTO>(); } }
Now we can build a CreateImage.razor page to pull it all together:
@page "/" <PageTitle>Create Image</PageTitle> <div class="row"> <div class="col-md-6"> <CreateImageForm OnValidSubmit="HandleValidSubmit" /> </div> <div class="col-md-6"> @if (loading) { <p>Loading...</p> } else { <img src="@imageUrl" class="img-fluid w-100" /> } </div> </div> @code { [Inject] IImageService ImageService { get; set; } = default!; private bool loading; private string? imageUrl; private async Task HandleValidSubmit(CreateImageRequestDTO createImageRequestDTO) { loading = true; var response = await ImageService.CreateImage(createImageRequestDTO); imageUrl = response != null ? response!.Data![0].Url : null; loading = false; } }
Finally, create the following CreateImageForm component:
<EditForm Model="@createImageRequestDTO" OnValidSubmit="@HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <InputTextArea id="Prompt" @bind-Value="createImageRequestDTO.Prompt" class="form-control mb-2" rows="4" /> <button type="submit" class="btn btn-primary">Submit</button> </EditForm> @code { [Parameter] public EventCallback<CreateImageRequestDTO> OnValidSubmit { get; set; } CreateImageRequestDTO createImageRequestDTO = new(); private async Task HandleValidSubmit() => await OnValidSubmit.InvokeAsync(createImageRequestDTO); }
And that’s it! We should be able to run the application and let our imagination go wild:

Editing an image
Editing an image can be achieved in a very similar way. An image edit requires what is called a mask. This is a transparent area of the image in which the edit will take place. For this demonstration, I’ll be using the following image in which the lenses of the glasses are transparent:

Firstly, let’s create an EditImageRequestDTO:
public class EditImageRequestDTO { public IBrowserFile? File { get; set; } [Required] [StringLength(1000)] public string? Prompt { get; set; } }
Next, let’s update our ImageService to include the following methods:
public async Task<ImageResponseDTO?> EditImage(EditImageRequestDTO editImageRequestDTO) { string imagePath = await SaveFile(editImageRequestDTO!.File!); using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); using var formData = new MultipartFormDataContent(); var imageContent = new ByteArrayContent(File.ReadAllBytes(imagePath)); imageContent.Headers.Add("Content-Type", "image/png"); formData.Add(imageContent, "image", "otter.png"); formData.Add(new StringContent(editImageRequestDTO.Prompt!), "prompt"); formData.Add(new StringContent(imageSize), "size"); var response = client.PostAsync(editImageRequestUri, formData).Result; File.Delete(imagePath); return await response.Content.ReadFromJsonAsync<ImageResponseDTO>(); } private async Task<string> SaveFile(IBrowserFile file) { string imageUploadPath = Path.Combine(webHostEnvironment.WebRootPath, "img"); Directory.CreateDirectory(imageUploadPath); string fullFilePath = Path.Combine(imageUploadPath, file.Name); await using FileStream fs = new(fullFilePath, FileMode.Create); await file.OpenReadStream().CopyToAsync(fs); fs.Close(); return fullFilePath; }
We will also need to add the request Uri and IWebHostEnvironment to our service:
private readonly string apiKey; private readonly string createImageRequestUri; private readonly string editImageRequestUri; private readonly string imageSize; private readonly IWebHostEnvironment webHostEnvironment; public ImageService(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { apiKey = configuration["OpenAIAPIKey"]!; createImageRequestUri = "https://api.openai.com/v1/images/generations"; editImageRequestUri = "https://api.openai.com/v1/images/edits"; imageSize = "512x512"; this.webHostEnvironment = webHostEnvironment; }
Now we can add our EditPage.razor page:
@page "/edit" <PageTitle>Edit Image</PageTitle> <div class="row"> <div class="col-md-6"> <EditImageForm OnValidSubmit="HandleValidSubmit" /> </div> <div class="col-md-6"> @if (loading) { <p>Loading...</p> } else { <img src="@imageUrl" class="img-fluid w-100" /> } </div> </div> @code { [Inject] IImageService ImageService { get; set; } = default!; private bool loading; private string? imageUrl; private async Task HandleValidSubmit(EditImageRequestDTO editImageRequestDTO) { loading = true; var response = await ImageService.EditImage(editImageRequestDTO); imageUrl = response != null ? response!.Data![0].Url : null; loading = false; } }
Finally, add the following EditImageForm component:
<EditForm Model="@editImageRequestDTO" OnValidSubmit="@HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <InputFile id="File" OnChange="@LoadFiles" class="form-control mb-2" /> <InputTextArea id="Prompt" @bind-Value="editImageRequestDTO.Prompt" class="form-control mb-2" rows="4" /> <button type="submit" class="btn btn-primary">Submit</button> </EditForm> @code { [Parameter] public EventCallback<EditImageRequestDTO> OnValidSubmit { get; set; } EditImageRequestDTO editImageRequestDTO = new(); private async Task HandleValidSubmit() => await OnValidSubmit.InvokeAsync(editImageRequestDTO); private void LoadFiles(InputFileChangeEventArgs e) => editImageRequestDTO.File = e.File; }
Now we can run our application, and submit our image along with a text prompt:

That concludes this tutorial! I am eagerly awaiting the release of ChatGPT to OpenAI’s API. In the meantime, I hope that this blog post will have at least demonstrated the growing capabilities of artificial intelligence and how easy it can be to incorporate them into our applications.
IImageService is not found?
I’ve updated the post to include the IImageService interface. Thanks for reading the blog!
Pingback: How to Use ChatGPT in C# - Writing a ChatGPT client