2023-10-11 14:41:43 +02:00
|
|
|
|
using DataAccess.Data;
|
|
|
|
|
using DataAccess.Models;
|
|
|
|
|
using Radzen;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
|
|
|
namespace KIKunstKirstenKlöckner.Data;
|
|
|
|
|
|
|
|
|
|
public class ImageGenerator
|
|
|
|
|
{
|
|
|
|
|
private readonly HttpClient _client = new();
|
|
|
|
|
private readonly BildInfoData _bildInfoData;
|
|
|
|
|
|
2023-10-13 12:20:24 +02:00
|
|
|
|
private readonly IConfiguration _config;
|
|
|
|
|
|
|
|
|
|
private string ModelName => _config.GetValue<string>("StableDiffusionModel") ?? "No Model Defined";
|
|
|
|
|
|
2023-10-11 14:41:43 +02:00
|
|
|
|
public ImageGenerator(IConfiguration config, BildInfoData bildInfoData)
|
|
|
|
|
{
|
2023-10-13 12:20:24 +02:00
|
|
|
|
_config = config;
|
2023-10-11 14:41:43 +02:00
|
|
|
|
_bildInfoData = bildInfoData;
|
|
|
|
|
|
|
|
|
|
string? inferenceApiKey = config.GetValue<string>("API:HF_Inference");
|
|
|
|
|
|
|
|
|
|
_client.DefaultRequestHeaders.Clear();
|
|
|
|
|
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {inferenceApiKey}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2023-10-13 12:20:24 +02:00
|
|
|
|
/// Geneiert ein Bild für den gegebenen Wunsch.
|
2023-10-11 14:41:43 +02:00
|
|
|
|
/// </summary>
|
2023-10-14 14:33:35 +02:00
|
|
|
|
/// <param name="imagePrompt">Der Prompt für den das Bild erzeugt wird.</param>
|
2023-10-13 12:20:24 +02:00
|
|
|
|
/// <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>
|
2023-10-14 14:33:35 +02:00
|
|
|
|
public async Task<BildInfoModel?> GenerateImageAsync(string imagePrompt, WunschInfoModel wunschInfo, int? width = null, int? height = null, string negativePromt = "")
|
2023-10-11 14:41:43 +02:00
|
|
|
|
{
|
|
|
|
|
var postData = new
|
|
|
|
|
{
|
2023-10-14 14:33:35 +02:00
|
|
|
|
inputs = imagePrompt,
|
2023-10-11 14:41:43 +02:00
|
|
|
|
parameters = new
|
|
|
|
|
{
|
|
|
|
|
negative_prompt = negativePromt, //"photorealistic, highly detailed, 8K, portrait",
|
|
|
|
|
width = width,
|
|
|
|
|
height = height
|
|
|
|
|
},
|
|
|
|
|
options = new
|
|
|
|
|
{
|
2023-10-13 12:20:24 +02:00
|
|
|
|
// https://huggingface.co/docs/api-inference/detailed_parameters
|
2023-10-11 14:41:43 +02:00
|
|
|
|
// Cache deaktivieren, damit Huggingface für den selben Prompt unterschiedliche Ergebnisse liefert
|
|
|
|
|
use_cache = false,
|
2023-10-13 12:20:24 +02:00
|
|
|
|
// 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.
|
2023-10-11 14:41:43 +02:00
|
|
|
|
wait_for_model = true
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
JsonContent content = JsonContent.Create(postData);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2023-10-13 12:20:24 +02:00
|
|
|
|
var inferenceModelUrl = $"https://api-inference.huggingface.co/models/{ModelName}";
|
2023-10-11 14:41:43 +02:00
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
{
|
2023-10-13 12:20:24 +02:00
|
|
|
|
// Die tatsächliche Url wird in SaveImageStreamAsync gesetzt.
|
2023-10-11 14:41:43 +02:00
|
|
|
|
Dateiname = "PlaceHolder",
|
|
|
|
|
Datum = imageDate,
|
2023-10-13 12:20:24 +02:00
|
|
|
|
ImageModel = ModelName,
|
2023-10-14 14:33:35 +02:00
|
|
|
|
WunschId = wunschInfo.Id,
|
|
|
|
|
Prompt = imagePrompt
|
2023-10-11 14:41:43 +02:00
|
|
|
|
};
|
|
|
|
|
|
2023-10-13 12:20:24 +02:00
|
|
|
|
await SaveImageStreamAsync(imageStream, bildInfo);
|
2023-10-11 14:41:43 +02:00
|
|
|
|
|
2023-10-13 12:20:24 +02:00
|
|
|
|
return bildInfo;
|
2023-10-11 14:41:43 +02:00
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-13 12:20:24 +02:00
|
|
|
|
|
|
|
|
|
/// <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);
|
|
|
|
|
}
|
2023-10-11 14:41:43 +02:00
|
|
|
|
}
|