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; private readonly IConfiguration _config; private string ModelName => _config.GetValue("StableDiffusionModel") ?? "No Model Defined"; public ImageGenerator(IConfiguration config, BildInfoData bildInfoData) { _config = config; _bildInfoData = bildInfoData; string? inferenceApiKey = config.GetValue("API:HF_Inference"); _client.DefaultRequestHeaders.Clear(); _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {inferenceApiKey}"); } /// /// Geneiert ein Bild für den gegebenen Wunsch. /// /// Der Prompt für den das Bild erzeugt wird. /// Der Wunsch zu dem ein Bild generiert werden soll. /// Die breite des zu generierenden Bildes. für Standardbreite des Modells /// Die höhe des zu generierenden Bildes. für Standardhöhe des Modells /// Begriffe, die explizit nicht generiert werden sollen. /// Die BildInfo des generierten Bildes; oder null, wenn ein Fehler auftrat. public async Task GenerateImageAsync(string imagePrompt, 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; } } /// /// Speichert den Inhalt des gegebenen Datenstroms als Bild ab. /// /// Der Datenstrom, der das Bild enthält. /// Die BildInfo des Bildes. Der Dateiname wird nach dem Speichern aktualisiert. 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); } }