diff --git a/DataAccess/Data/BildInfoData.cs b/DataAccess/Data/BildInfoData.cs index e2def08..643e28a 100644 --- a/DataAccess/Data/BildInfoData.cs +++ b/DataAccess/Data/BildInfoData.cs @@ -19,7 +19,7 @@ public class BildInfoData /// Fügt die gegebene BildInfo zur Datenbank hinzu und aktualisiert das -Feld mit dem entsprechenden Wert. /// /// Die BildInfo, die zur Datenbank hinzugefügt werden soll. - public async Task AddBildInfoAsync(BildInfoModel bildInfo) + public async Task InsertBildInfoAsync(BildInfoModel bildInfo) { var id = await _db.LoadData("dbo.spBildInfo_Insert", bildInfo); bildInfo.Id = id.Single(); diff --git a/KIKunstKirstenKlöckner/Data/ImageGenerator.cs b/KIKunstKirstenKlöckner/Data/ImageGenerator.cs index f9e08c0..66ead66 100644 --- a/KIKunstKirstenKlöckner/Data/ImageGenerator.cs +++ b/KIKunstKirstenKlöckner/Data/ImageGenerator.cs @@ -10,8 +10,13 @@ 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"); @@ -21,13 +26,18 @@ public class ImageGenerator } /// - /// Geneiert das Bild für den aktuellen + /// Geneiert ein Bild für den gegebenen Wunsch. /// - public async Task GenerateImageAsync(string prompt, string negativePromt, int? width, int? height, WunschInfoModel wunschInfo, bool isRetry = false) + /// 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(WunschInfoModel wunschInfo, int? width = null, int? height = null, string negativePromt = "") { var postData = new { - inputs = prompt, + inputs = wunschInfo.BildPrompt, parameters = new { negative_prompt = negativePromt, //"photorealistic, highly detailed, 8K, portrait", @@ -36,9 +46,11 @@ public class ImageGenerator }, 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, - // Erst wenn wir bereits in einem retry sind warten wir implizit auf das Model. (ignoriert quasi 503-Fehler) + // 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 } }; @@ -47,10 +59,7 @@ public class ImageGenerator try { - //const string modelName = "Nacken/kkkk-sdxl-5000"; - const string modelName = "Nacken/kkk-sdxl-18000"; - - var inferenceModelUrl = $"https://api-inference.huggingface.co/models/{modelName}"; + var inferenceModelUrl = $"https://api-inference.huggingface.co/models/{ModelName}"; var response = await _client.PostAsync(inferenceModelUrl, content); @@ -58,29 +67,20 @@ public class ImageGenerator { await using Stream imageStream = await response.Content.ReadAsStreamAsync(); - using Image image = await Image.LoadAsync(imageStream); - DateTime imageDate = DateTime.Now; BildInfoModel bildInfo = new() { + // Die tatsächliche Url wird in SaveImageStreamAsync gesetzt. Dateiname = "PlaceHolder", Datum = imageDate, - ImageModel = modelName, + ImageModel = ModelName, WunschId = wunschInfo.Id }; - - await _bildInfoData.AddBildInfoAsync(bildInfo); - string imgUrl = $"generated_images/Image_{bildInfo.Id}.jpg"; + await SaveImageStreamAsync(imageStream, bildInfo); - string mapPath = $"./wwwroot/{imgUrl}"; - await image.SaveAsJpegAsync(mapPath); - - bildInfo.Dateiname = imgUrl; - await _bildInfoData.UpdateBildInfoDateinameAsync(bildInfo); - - return imgUrl; + return bildInfo; } else { @@ -102,4 +102,26 @@ public class ImageGenerator 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); + } } diff --git a/KIKunstKirstenKlöckner/Pages/AiArt.razor b/KIKunstKirstenKlöckner/Pages/AiArt.razor index f066303..a29a320 100644 --- a/KIKunstKirstenKlöckner/Pages/AiArt.razor +++ b/KIKunstKirstenKlöckner/Pages/AiArt.razor @@ -305,7 +305,7 @@ // Vier Bilder generieren for (int i = 0; i < 4; i++) { - _imageUrls[i] = await ImageGenerator.GenerateImageAsync(_imagePrompt, "", width, height, wunschInfo); + _imageUrls[i] = (await ImageGenerator.GenerateImageAsync(wunschInfo, width, height))?.Dateiname; // Kein Bild -> Fehler if (_imageUrls[i] == null) @@ -317,12 +317,14 @@ if (retry == true) { - await ImageGenerator.GenerateImageAsync(_imagePrompt, "", width, height, wunschInfo); + _imageUrls[i] = (await ImageGenerator.GenerateImageAsync(wunschInfo, width, height))?.Dateiname; } } - generatedImages++; + if (_imageUrls[i] != null) + generatedImages++; + // TODO: Fehler anzeigen _imageStates[i] = ImageState.FadeIn; await InvokeAsync(StateHasChanged); } diff --git a/KIKunstKirstenKlöckner/appsettings.json b/KIKunstKirstenKlöckner/appsettings.json index 0d8c4f9..4935601 100644 --- a/KIKunstKirstenKlöckner/appsettings.json +++ b/KIKunstKirstenKlöckner/appsettings.json @@ -1,15 +1,16 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, "AllowedHosts": "*", "API": { "OpenAI": "", "HF_Inference": "" }, + "StableDiffusionModel": "Nacken/kkk-sdxl-18000", "ConnectionStrings": { "Default": "" }