KI-Kunst-Kirsten-Kloeckner/KIKunstKirstenKlöckner/Data/ImageGenerator.cs

130 lines
4.9 KiB
C#

using DataAccess.Data;
using DataAccess.Models;
using System.Diagnostics;
using SixLabors.ImageSharp;
namespace KIKunstKirstenKlöckner.Data;
public class ImageGenerator
{
private readonly HttpClient _client = new();
private readonly BildInfoData _bildInfoData;
private readonly IConfiguration _config;
//private string ModelName => _config.GetValue<string>("StableDiffusionModel") ?? "No Model Defined";
public ImageGenerator(IConfiguration config, BildInfoData bildInfoData)
{
_config = config;
_bildInfoData = bildInfoData;
string? inferenceApiKey = config.GetValue<string>("API:HF_Inference");
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {inferenceApiKey}");
}
/// <summary>
/// Geneiert ein Bild für den gegebenen Wunsch.
/// </summary>
/// <param name="imagePrompt">Der Prompt für den das Bild erzeugt wird.</param>
/// <param name="wunschInfo">Der Wunsch zu dem ein Bild generiert werden soll.</param>
/// <param name="width">Die breite des zu generierenden Bildes. <see langword="null"/> für Standardbreite des Modells</param>
/// <param name="height">Die höhe des zu generierenden Bildes. <see langword="null"/> für Standardhöhe des Modells</param>
/// <param name="negativePromt">Begriffe, die explizit nicht generiert werden sollen.</param>
/// <returns>Die BildInfo des generierten Bildes; oder null, wenn ein Fehler auftrat.</returns>
public async Task<BildInfoModel?> GenerateImageAsync(string imagePrompt, string modelName, WunschInfoModel wunschInfo, int? width = null, int? height = null, string negativePromt = "")
{
var postData = new
{
inputs = imagePrompt,
parameters = new
{
negative_prompt = negativePromt, //"photorealistic, highly detailed, 8K, portrait",
width = width,
height = height
},
options = new
{
// https://huggingface.co/docs/api-inference/detailed_parameters
// Cache deaktivieren, damit Huggingface für den selben Prompt unterschiedliche Ergebnisse liefert
use_cache = false,
// Ignoriert 503-Fehler. Diese treten auf wenn Hugging Face das Model noch nicht geladen hat.
// Mit true ignorieren wir den Fehler und schlagen erst fehl, wenn wir einen timeout haben.
wait_for_model = true
}
};
JsonContent content = JsonContent.Create(postData);
try
{
var inferenceModelUrl = $"https://api-inference.huggingface.co/models/{modelName}";
var response = await _client.PostAsync(inferenceModelUrl, content);
if (response?.IsSuccessStatusCode == true)
{
await using Stream imageStream = await response.Content.ReadAsStreamAsync();
DateTime imageDate = DateTime.Now;
BildInfoModel bildInfo = new()
{
// Die tatsächliche Url wird in SaveImageStreamAsync gesetzt.
Dateiname = "PlaceHolder",
Datum = imageDate,
ImageModel = modelName,
WunschId = wunschInfo.Id,
Prompt = imagePrompt
};
await SaveImageStreamAsync(imageStream, bildInfo);
return bildInfo;
}
else
{
Console.WriteLine($"Image conversion failed: {response}");
if (Debugger.IsAttached)
Debugger.Break();
return null;
}
}
catch (Exception exception)
{
Console.WriteLine($"Image request failed: {exception}");
if (Debugger.IsAttached)
Debugger.Break();
return null;
}
}
/// <summary>
/// Speichert den Inhalt des gegebenen Datenstroms als Bild ab.
/// </summary>
/// <param name="dataStream">Der Datenstrom, der das Bild enthält.</param>
/// <param name="bildInfo">Die BildInfo des Bildes. Der Dateiname wird nach dem Speichern aktualisiert.</param>
private async Task SaveImageStreamAsync(Stream dataStream, BildInfoModel bildInfo)
{
using Image image = await Image.LoadAsync(dataStream);
// Speichert BildInfo in der Datenbank, aktualisiert Id der bildInfo
await _bildInfoData.InsertBildInfoAsync(bildInfo);
string imageUrl = $"generated_images/Image_{bildInfo.Id}.jpg";
string imageFilePath = $"./wwwroot/{imageUrl}";
await image.SaveAsJpegAsync(imageFilePath);
// Speichert den geänderten Dateinamen in der Datenbank
bildInfo.Dateiname = imageUrl;
await _bildInfoData.UpdateBildInfoDateinameAsync(bildInfo);
}
}