KI-Kunst-Kirsten-Kloeckner/KIKunstKirstenKlöckner/Pages/AiArt.razor

494 lines
19 KiB
Plaintext
Raw Normal View History

2023-08-02 21:33:09 +02:00
@page "/aiart"
2023-08-04 15:45:38 +02:00
2023-08-02 18:33:28 +02:00
@using OpenAI_API
@using OpenAI_API.Chat
@using OpenAI_API.Models
2023-08-02 21:33:09 +02:00
@using System.Diagnostics
2023-10-10 16:21:00 +02:00
@using DataAccess.Data
@using DataAccess.Models
2023-08-02 21:33:09 +02:00
2023-08-05 01:40:55 +02:00
@inject IConfiguration Config
2023-08-02 21:33:09 +02:00
@inject TooltipService TooltipService
@inject DialogService DialogService
2023-08-02 18:33:28 +02:00
2023-10-10 16:21:00 +02:00
@inject BildInfoData BildInfoData;
@inject WunschInfoData WunschInfoData;
2023-10-10 16:21:00 +02:00
2023-08-02 18:33:28 +02:00
<PageTitle>AiArt</PageTitle>
2023-10-10 17:39:37 +02:00
<section class="about_section layout_padding" style="background-image: url('images/5KeineAngstvorFehlern2014.jpeg'); background-size: cover; background-repeat: no-repeat; background-blend-mode:lighten">
<div class="container">
<RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center">
2023-08-18 14:57:12 +02:00
<h1>Wunschbilder von KI nur für dich</h1>
<RadzenText TextStyle="TextStyle.H2">Nenne uns deinen Wunsch:</RadzenText>
<RadzenTextBox @bind-Value=@request Placeholder="Dein Wunsch"/>
2023-08-03 23:24:10 +02:00
<RadzenPanel AllowCollapse="true" Style="width: 500px;" Text="Mehr Optionen">
<ChildContent>
<RadzenCard class="rz-mt-4">
<RadzenStack Orientation="Orientation.Horizontal"
MouseEnter="@(args => ShowTemperatureTooltip(args))"
MouseLeave="TooltipService.Close"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>Temperature:</RadzenText>
<RadzenSlider @bind-Value=@temperature TValue="float"
Step="0.1" Min="0.0m" Max="2.0m">
</RadzenSlider>
<RadzenText>@temperature</RadzenText>
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>Resolution:</RadzenText>
<RadzenStack Orientation="Orientation.Horizontal">
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@width />
x
<RadzenNumeric ShowUpDown = "false" TValue = "int?" @bind-Value=@height />
</RadzenStack>
</RadzenStack>
</RadzenCard>
</ChildContent>
</RadzenPanel>
2023-08-02 21:33:09 +02:00
<RadzenButton Visible=@_buttonVisible Click=@(async ()=> await DoStuff(true))>Generate</RadzenButton>
2023-08-03 23:24:10 +02:00
<RadzenPanel AllowCollapse="true" Style="width: 500px;" Text="Zeige Prompt">
<ChildContent>
<RadzenCard class="rz-mt-4">
<RadzenStack Orientation="Orientation.Horizontal"
MouseEnter="@(args => ShowTemperatureTooltip(args))"
MouseLeave="TooltipService.Close"
AlignItems="AlignItems.Center" Wrap="FlexWrap.Wrap">
<RadzenText>@_imagePromptEnglish</RadzenText>
</RadzenStack>
</RadzenCard>
</ChildContent>
</RadzenPanel>
2023-08-03 23:24:10 +02:00
<RadzenText TextStyle="TextStyle.H4">Die Idee, die gemalt wird:</RadzenText>
2023-08-04 15:54:07 +02:00
2023-08-04 15:54:37 +02:00
<RadzenCard class="rz-mt-4" Style="width: 800px;">
2023-08-04 15:54:07 +02:00
<RadzenText>@_imageDescription</RadzenText>
</RadzenCard>
2023-08-03 23:24:10 +02:00
<RadzenProgressBarCircular Visible=@_progressVisible ProgressBarStyle="ProgressBarStyle.Secondary" Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate" />
<RadzenText Visible=@_progressVisible TextStyle="TextStyle.H6" Text=@BusyMessage></RadzenText>
2023-08-03 23:24:10 +02:00
<RadzenImage Path=@_imageUrl></RadzenImage>
2023-08-02 21:33:09 +02:00
<RadzenText Visible=@_addonsVisible TextStyle="TextStyle.H2">Verändere hier dein Bild durch Worte:</RadzenText>
<RadzenTextBox Visible=@_addonsVisible @bind-Value=@addons Placeholder="z.B. Mehr Farben" />
<RadzenButton Visible=@_bothVisible Click=@(async ()=> await DoStuff(false))>Generate</RadzenButton>
<RadzenCard>
<RadzenRow Style="width:24.5em" Gap="0.5rem" RowGap="0.5rem">
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[0]" HideImage="false"
Show="@(_imageStates[0] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Up"
Click="() => ShowImageDialog(_imageUrls[0])" />
</RadzenColumn>
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[1]" HideImage="false"
Show="@(_imageStates[1] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Right" FlipDelay="200"
Click="() => ShowImageDialog(_imageUrls[1])" />
</RadzenColumn>
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[2]" HideImage="false"
Show="@(_imageStates[2] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Left" FlipDelay="600"
Click="() => ShowImageDialog(_imageUrls[2])" />
</RadzenColumn>
<RadzenColumn Size="6">
<FlippingImage ImageUrl="@_imageUrls[3]" HideImage="false"
Show="@(_imageStates[3] == ImageState.FadeIn)" FlipTo="FlippingImage.FlipDirection.Down" FlipDelay="400"
Click="() => ShowImageDialog(_imageUrls[3])" />
</RadzenColumn>
</RadzenRow>
</RadzenCard>
</RadzenStack>
2023-08-18 14:57:12 +02:00
</div>
</section>
2023-08-02 18:33:28 +02:00
@code {
private int maxAddons = 2;
private int amountOfAddons = 0; // wird später geändert
private string _imageDescriptionPrompt = "Zusätzlich zu dem Promt erkläre kurz auf deutsch, warum du dich für diese Umsetzung des Titels entschieden hast und gib zusätzlich eine Interpretation des Bildes an. Beginne diesen Teil immer mit \"Beschreibung: \". Zuletzt Beschreibe das Bild und die verbundenen Emotionen mit drei Worten und beginne den Teil mit \"Keywords: \".";
2023-08-02 21:33:09 +02:00
private bool _progressVisible = false;
2023-08-03 23:24:10 +02:00
private bool _buttonVisible = true;
private bool _addonsVisible = false;
private bool _bothVisible = false;
2023-08-02 21:33:09 +02:00
public string BusyMessage { get; set; } = "Initial Message";
private string?[] _imageUrls = new string?[4];
private ImageState[] _imageStates = new ImageState[4];
enum ImageState
{
//Hide = 0,
FadeOut,
FadeIn,
//Show,
}
2023-08-02 21:33:09 +02:00
// Busy dialog from markup
async Task ShowBusyDialog()
{
await DialogService.OpenAsync("", ds =>
@<RadzenStack AlignItems="AlignItems.Center" Gap="2rem" Class="rz-p-12">
<RadzenProgressBarCircular ProgressBarStyle="ProgressBarStyle.Secondary" Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate"/>
<RadzenText TextStyle="TextStyle.H6" Text=@BusyMessage></RadzenText>
</RadzenStack>, new DialogOptions() { ShowTitle = false, Style = "min-height:auto;min-width:auto;width:auto", CloseDialogOnEsc = false });
}
async Task ShowImageDialog(string imageUrl)
{
var result = await DialogService.OpenAsync("", ds =>
2023-09-20 15:43:10 +02:00
@<div>
<RadzenImage Style="object-fit: contain; width: 100%; height:100%;" Path="@imageUrl"/>
</div>, new DialogOptions() { CloseDialogOnOverlayClick = true });
}
2023-08-02 21:33:09 +02:00
// Busy dialog from string
async Task BusyDialog(string message)
{
await DialogService.OpenAsync("", ds =>
{
RenderFragment content = b =>
{
b.OpenElement(0, "RadzenRow");
b.OpenElement(1, "RadzenColumn");
b.AddAttribute(2, "Size", "12");
2023-08-02 18:33:28 +02:00
2023-08-02 21:33:09 +02:00
b.AddContent(3, message);
b.CloseElement();
b.CloseElement();
};
return content;
}, new DialogOptions() { ShowTitle = false, Style = "min-height:auto;min-width:auto;width:auto", CloseDialogOnEsc = false });
}
void ShowTooltip(ElementReference elementReference, string text, TooltipOptions? options = null) => TooltipService.Open(elementReference, text, options);
void ShowTemperatureTooltip(ElementReference elementReference) => TooltipService.Open(elementReference, ds =>
@<div>
Gibt an, wie <em>kreativ</em> ChatGPT sein soll.<br />
Ich glaube, eigentlich bedeutet es eher, wie <em>deterministisch</em> die Ausgabe ist.<br />
Bei 0.0 kommt immer fast die selbe Antwort. Bei zu hohen Werten kommt nur noch Schwachsinn.<br />
OpenAI empfielt einen Wert von ca. 0.9 für kreative Anwendungen.
</div>
,
2023-08-02 21:33:09 +02:00
new() { Position = TooltipPosition.Bottom, Duration = null});
2023-08-05 01:40:55 +02:00
private string _imagePromptEnglish = "";
2023-09-20 12:22:52 +02:00
private string _imageIdea = "";
2023-08-05 01:40:55 +02:00
private string _imageDescription = "";
2023-08-04 16:04:46 +02:00
private string _imagePrompt = "";
2023-08-02 21:33:09 +02:00
private float temperature = 0.9f;
private int? width = 1024;
private int? height = 1024;
2023-08-02 18:33:28 +02:00
private string request = "";
private string addons = "";
2023-08-05 01:40:55 +02:00
private OpenAIAPI _openAiApi;
private Conversation converse;
2023-08-02 18:33:28 +02:00
2023-08-02 21:33:09 +02:00
private string _basePrompt;
2023-09-20 12:22:52 +02:00
private string _ideaPrompt;
2023-08-02 21:33:09 +02:00
2023-08-03 23:24:10 +02:00
private string _imageUrl;
2023-08-02 21:33:09 +02:00
//protected override async Task OnInitializedAsync()
//{
// _basePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"\wwwroot\prompt.txt"}");
//}
private async Task FunnyMessageSwitcher_ImageGen(CancellationToken cancellationToken)
2023-08-02 18:33:28 +02:00
{
2023-08-02 21:33:09 +02:00
Stopwatch sw = Stopwatch.StartNew();
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Dauert noch eine Weile...");
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Gut Ding hat Weil...");
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Sach ma, was issn da l... achso, er ist auf Klo gegangen... Er ist bestimmt gleich wieder da...");
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage("Na, also langsam verlier ich hier die Geduld, ich dachte KI soll alles schneller machen...");
2023-08-02 21:33:09 +02:00
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(1000, cancellationToken);
await UpdateBusyMessage($"Keine Sorge, er arbeitet noch. Die Bilder werden gemalt... ({sw.Elapsed.Seconds}s)");
}
}
async Task UpdateBusyMessage(string newMessage)
{
BusyMessage = newMessage;
await InvokeAsync(StateHasChanged);
}
2023-08-04 15:37:07 +02:00
private readonly HttpClient _client = new();
2023-08-05 01:40:55 +02:00
private string _inferenceApiKey = "";
private string _openAiApiKey = "";
2023-10-10 16:21:00 +02:00
protected override async Task OnInitializedAsync()
2023-08-03 23:24:10 +02:00
{
2023-08-05 01:40:55 +02:00
_inferenceApiKey = Config.GetValue<string>("API:HF_Inference");
_openAiApiKey = Config.GetValue<string>("API:OpenAI");
2023-08-04 15:37:07 +02:00
2023-08-05 01:40:55 +02:00
_openAiApi = new OpenAIAPI(_openAiApiKey);
2023-08-04 15:37:07 +02:00
2023-09-20 15:18:21 +02:00
var inferenceModelUrl = "https://api-inference.huggingface.co/models/Nacken/kkkk-sdxl-5000";
2023-08-04 15:37:07 +02:00
//_client.BaseAddress = new Uri(inferenceModelUrl);
_client.DefaultRequestHeaders.Clear();
2023-08-05 01:40:55 +02:00
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_inferenceApiKey}");
2023-08-04 16:04:46 +02:00
2023-10-10 16:21:00 +02:00
// BildInfoModel bildInfo = new()
// {
// BildBeschreibung = "Test",
// BildPrompt = "Tost",
// Dateiname = "Task",
// Datum = DateTime.Now,
// GPTModel = "Geht dich nichts an",
// ImageModel = "Jup",
// Wunsch = request
// };
// try
// {
2023-10-10 16:21:00 +02:00
// //await BildInfoData.AddBildInfo(bildInfo);
2023-10-10 16:21:00 +02:00
// }
// catch (Exception e)
// {
// Console.WriteLine(e);
// throw;
// }
2023-10-10 16:21:00 +02:00
await base.OnInitializedAsync();
2023-08-03 23:24:10 +02:00
}
2023-08-02 21:33:09 +02:00
/// <summary>
/// Geneiert das Bild für den aktuellen <see cref="_imagePrompt"/>
/// </summary>
public async Task<string?> GenerateImageAsync(string prompt, WunschInfoModel wunschInfo, bool isRetry = false)
{
var postData = new
{
inputs = prompt,
parameters = new
{
negative_prompt = "photorealistic, highly detailed, 8K, portrait",
width = width,
height = height
},
options = new
{
// 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)
2023-08-05 01:40:55 +02:00
wait_for_model = true
}
};
JsonContent content = JsonContent.Create(postData);
async Task<string?> FailedToDrawImage()
{
bool? retry = await DialogService.Confirm("Leider konnte das Bild nicht gemalt werden. Möchtest du es noch eimal versuchen?", "Ups, ein Fehler ist aufgetreten...",
new ConfirmOptions { OkButtonText = "Ja", CancelButtonText = "Nein" });
if (retry == true)
{
return await GenerateImageAsync(prompt, wunschInfo, true);
}
return null;
}
try
{
const string modelName = "Nacken/kkkk-sdxl-5000";
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();
using Image image = await Image.LoadAsync(imageStream);
2023-10-10 16:21:00 +02:00
DateTime imageDate = DateTime.Now;
2023-10-10 16:21:00 +02:00
BildInfoModel bildInfo = new()
{
Dateiname = "PlaceHolder",
2023-10-10 16:21:00 +02:00
Datum = imageDate,
ImageModel = modelName,
WunschId = wunschInfo.Id
2023-10-10 16:21:00 +02:00
};
await BildInfoData.AddBildInfoAsync(bildInfo);
string imgUrl = $"generated_images/Image_{bildInfo.Id}.jpg";
string mapPath = $"./wwwroot/{imgUrl}";
await image.SaveAsJpegAsync(mapPath);
bildInfo.Dateiname = imgUrl;
await BildInfoData.UpdateBildInfoDateinameAsync(bildInfo);
2023-08-18 11:52:58 +02:00
return imgUrl;
}
else
{
Console.WriteLine($"Image conversion failed: {response}");
if (Debugger.IsAttached)
Debugger.Break();
return await FailedToDrawImage();
}
}
catch (Exception exception)
{
Console.WriteLine($"Image request failed: {exception}");
if (Debugger.IsAttached)
Debugger.Break();
return await FailedToDrawImage();
}
return null;
}
private async Task DoStuff(bool newPic)
2023-08-02 21:33:09 +02:00
{
for (int i = 0; i < 4; i++)
_imageStates[i] = ImageState.FadeOut;
2023-08-02 21:33:09 +02:00
// Der Dialog blokiert so lange, wie der er offen ist, deshalb dürfen wir hier nicht warten, da wir sonst nie mit der Arbeit anfangen...
//Task busyDialog = ShowBusyDialog();
_progressVisible = true;
2023-08-03 23:24:10 +02:00
_buttonVisible = false;
2023-08-02 21:33:09 +02:00
if (converse == null || newPic)
2023-08-02 21:33:09 +02:00
{
amountOfAddons = maxAddons;
_addonsVisible = false;
_bothVisible = _buttonVisible && _addonsVisible;
await UpdateBusyMessage("Kirstens Assistent zerbricht sich über deine Idee den Kopf...");
2023-09-20 12:22:52 +02:00
_ideaPrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/idea_prompt.txt"}");
ChatRequest chatRequest = new ChatRequest
{
Temperature = temperature,
Model = Model.ChatGPTTurbo,
};
2023-08-05 01:40:55 +02:00
converse = _openAiApi.Chat.CreateConversation(chatRequest);
2023-09-20 12:22:52 +02:00
converse.AppendUserInput(_ideaPrompt + " " + request);
_imageIdea = await converse.GetResponseFromChatbotAsync();
_basePrompt = await File.ReadAllTextAsync($"{Directory.GetCurrentDirectory()}{@"/wwwroot/test_prompt2.txt"}");
converse.AppendUserInput(_basePrompt);
}
else
2023-08-02 21:33:09 +02:00
{
if (amountOfAddons > 0)
{
amountOfAddons--;
_bothVisible = _buttonVisible && _addonsVisible;
await UpdateBusyMessage("Kirstens Assistent passt das Bild an deine Wünsche an...");
string addonsPrompt1 = "Erstelle einen neuen Prompt auf englisch mit den gleichen Restriktionen auf Basis des Alten mit folgender Anpassung: ";
string addonsPrompt2 = ". Denke daran nur den Prompt zu generieren und noch keine Beschreibung oder ähnliches.";
converse.AppendUserInput(addonsPrompt1 + addons + addonsPrompt2);
}
2023-08-02 21:33:09 +02:00
}
2023-08-04 16:04:46 +02:00
_imagePromptEnglish = await converse.GetResponseFromChatbotAsync();
_imagePrompt = _imagePromptEnglish;
2023-10-10 17:39:37 +02:00
_imagePrompt += "kkkk Watercolor + ink on paper, Pen drawing, wet-on-wet technique, dry-on-dry technique, dabbing technique. ";
converse.AppendUserInput(_imageDescriptionPrompt);
_imageDescription = await converse.GetResponseFromChatbotAsync();
2023-08-02 21:33:09 +02:00
await UpdateBusyMessage("Kirstens Assistent hat eine Idee! Er wird sie nun malen...");
WunschInfoModel wunschInfo = new()
{
BildBeschreibung = _imageDescription,
BildPrompt = _imagePrompt,
Datum = DateTime.Now,
GPTModel = converse.Model,
Wunsch = request
};
// TODO: Try
await WunschInfoData.AddWunschInfoAsync(wunschInfo);
2023-08-03 23:24:10 +02:00
// Vier Bilder generieren
for (int i = 0; i < 4; i++)
{
_imageUrls[i] = await GenerateImageAsync(_imagePrompt, wunschInfo);
_imageStates[i] = ImageState.FadeIn;
await InvokeAsync(StateHasChanged);
}
2023-08-02 21:33:09 +02:00
_progressVisible = false;
2023-08-03 23:24:10 +02:00
_buttonVisible = true;
if (amountOfAddons > 0)
{
_addonsVisible = true;
_bothVisible = _buttonVisible && _addonsVisible;
await InvokeAsync(StateHasChanged);
}
else
{
_addonsVisible = false;
_bothVisible = false;
}
2023-08-02 18:33:28 +02:00
}
2023-08-04 17:05:25 +02:00
private void NavigateToGallery()
{
NavigationManager.NavigateTo("/gallery");
}
[Inject]
private NavigationManager NavigationManager { get; set; }
2023-08-18 11:52:58 +02:00
[Inject]
private IWebHostEnvironment _environment { get; set; }
2023-08-02 18:33:28 +02:00
}