Enhance AIImages mod with settings support and improved UI for image generation. Update localized strings in English and Russian for better clarity. Refactor code for better organization and maintainability.
This commit is contained in:
353
Source/AIImages/Services/AdvancedPromptGenerator.cs
Normal file
353
Source/AIImages/Services/AdvancedPromptGenerator.cs
Normal file
@@ -0,0 +1,353 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AIImages.Models;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace AIImages.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Продвинутый генератор промптов для Stable Diffusion
|
||||
/// </summary>
|
||||
public class AdvancedPromptGenerator : IPromptGeneratorService
|
||||
{
|
||||
private static readonly Dictionary<string, string> TraitToMood = new Dictionary<
|
||||
string,
|
||||
string
|
||||
>
|
||||
{
|
||||
{ "Kind", "warm smile, gentle expression, friendly eyes" },
|
||||
{ "Bloodlust", "intense gaze, fierce expression, aggressive posture" },
|
||||
{ "Psychopath", "cold eyes, emotionless face, calculating expression" },
|
||||
{ "Pessimist", "sad eyes, worried expression, downcast gaze" },
|
||||
{ "Optimist", "bright smile, hopeful expression, cheerful demeanor" },
|
||||
{ "Nervous", "anxious expression, tense posture, worried eyes" },
|
||||
{ "Careful", "focused expression, attentive gaze, composed posture" },
|
||||
{ "Brave", "confident expression, determined gaze, strong posture" },
|
||||
{ "Wimp", "fearful eyes, nervous expression, timid posture" },
|
||||
{ "Greedy", "cunning expression, calculating eyes, sly smile" },
|
||||
{ "Jealous", "envious gaze, bitter expression, tense face" },
|
||||
{ "Ascetic", "serene expression, calm demeanor, peaceful eyes" },
|
||||
{ "Beautiful", "stunning features, attractive appearance, graceful" },
|
||||
{ "Ugly", "rough features, weathered appearance" },
|
||||
{ "Pretty", "attractive features, pleasant appearance, charming" },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<ArtStyle, string> ArtStylePrompts = new Dictionary<
|
||||
ArtStyle,
|
||||
string
|
||||
>
|
||||
{
|
||||
{ ArtStyle.Realistic, "photorealistic, hyperrealistic, realistic photo, photography" },
|
||||
{ ArtStyle.SemiRealistic, "semi-realistic, detailed illustration, realistic art" },
|
||||
{ ArtStyle.Anime, "anime style, manga style, anime character" },
|
||||
{
|
||||
ArtStyle.ConceptArt,
|
||||
"concept art, digital art, artstation, professional concept design"
|
||||
},
|
||||
{ ArtStyle.DigitalPainting, "digital painting, painterly, brush strokes, artistic" },
|
||||
{ ArtStyle.OilPainting, "oil painting, traditional painting, canvas, fine art" },
|
||||
{ ArtStyle.Sketch, "pencil sketch, hand drawn, sketch art, line art" },
|
||||
{ ArtStyle.CellShaded, "cell shaded, flat colors, toon shading, stylized" },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<ShotType, string> ShotTypePrompts = new Dictionary<
|
||||
ShotType,
|
||||
string
|
||||
>
|
||||
{
|
||||
{ ShotType.Portrait, "portrait, head and shoulders" },
|
||||
{ ShotType.HalfBody, "half body shot, waist up" },
|
||||
{ ShotType.FullBody, "full body, full length" },
|
||||
{ ShotType.CloseUp, "close up, face focus, detailed face" },
|
||||
{ ShotType.ThreeQuarter, "three-quarter view, 3/4 view" },
|
||||
};
|
||||
|
||||
public string GeneratePositivePrompt(
|
||||
PawnAppearanceData appearanceData,
|
||||
StableDiffusionSettings settings
|
||||
)
|
||||
{
|
||||
if (appearanceData == null)
|
||||
return "portrait of a person";
|
||||
|
||||
StringBuilder prompt = new StringBuilder();
|
||||
|
||||
// 1. Художественный стиль
|
||||
if (ArtStylePrompts.TryGetValue(settings.ArtStyle, out string stylePrompt))
|
||||
{
|
||||
prompt.Append(stylePrompt);
|
||||
prompt.Append(", ");
|
||||
}
|
||||
|
||||
// 2. Тип кадра
|
||||
if (ShotTypePrompts.TryGetValue(settings.ShotType, out string shotPrompt))
|
||||
{
|
||||
prompt.Append(shotPrompt);
|
||||
prompt.Append(" of ");
|
||||
}
|
||||
|
||||
// 3. Базовое описание (возраст и пол)
|
||||
prompt.Append(GetAgeAndGenderDescription(appearanceData));
|
||||
prompt.Append(", ");
|
||||
|
||||
// 4. Тип тела
|
||||
string bodyType = GetBodyTypeDescription(appearanceData.BodyType);
|
||||
if (!string.IsNullOrEmpty(bodyType))
|
||||
{
|
||||
prompt.Append(bodyType);
|
||||
prompt.Append(", ");
|
||||
}
|
||||
|
||||
// 5. Цвет кожи
|
||||
string skinTone = ColorDescriptionService.GetSkinToneDescription(
|
||||
appearanceData.SkinColor
|
||||
);
|
||||
prompt.Append(skinTone);
|
||||
prompt.Append(", ");
|
||||
|
||||
// 6. Волосы
|
||||
string hairDescription = GetHairDescription(appearanceData);
|
||||
if (!string.IsNullOrEmpty(hairDescription))
|
||||
{
|
||||
prompt.Append(hairDescription);
|
||||
prompt.Append(", ");
|
||||
}
|
||||
|
||||
// 7. Настроение и выражение на основе черт характера
|
||||
string moodDescription = GetMoodFromTraits(appearanceData.Traits);
|
||||
if (!string.IsNullOrEmpty(moodDescription))
|
||||
{
|
||||
prompt.Append(moodDescription);
|
||||
prompt.Append(", ");
|
||||
}
|
||||
|
||||
// 8. Одежда
|
||||
string apparelDescription = GetApparelDescription(appearanceData.Apparel);
|
||||
if (!string.IsNullOrEmpty(apparelDescription))
|
||||
{
|
||||
prompt.Append(apparelDescription);
|
||||
prompt.Append(", ");
|
||||
}
|
||||
|
||||
// 9. Базовый пользовательский промпт (если указан)
|
||||
if (!string.IsNullOrEmpty(settings.PositivePrompt))
|
||||
{
|
||||
prompt.Append(settings.PositivePrompt);
|
||||
prompt.Append(", ");
|
||||
}
|
||||
|
||||
// 10. Качественные теги
|
||||
prompt.Append(GetQualityTags(settings.ArtStyle));
|
||||
|
||||
return prompt.ToString().Trim().TrimEnd(',');
|
||||
}
|
||||
|
||||
public string GenerateNegativePrompt(StableDiffusionSettings settings)
|
||||
{
|
||||
StringBuilder negativePrompt = new StringBuilder();
|
||||
|
||||
// Базовые негативные промпты
|
||||
negativePrompt.Append(
|
||||
"ugly, deformed, low quality, blurry, bad anatomy, worst quality, "
|
||||
);
|
||||
negativePrompt.Append(
|
||||
"mutated, disfigured, bad proportions, extra limbs, missing limbs, "
|
||||
);
|
||||
|
||||
// Специфичные для стиля негативы
|
||||
if (
|
||||
settings.ArtStyle == ArtStyle.Realistic
|
||||
|| settings.ArtStyle == ArtStyle.SemiRealistic
|
||||
)
|
||||
{
|
||||
negativePrompt.Append("cartoon, anime, painting, drawing, illustration, ");
|
||||
}
|
||||
else if (settings.ArtStyle == ArtStyle.Anime)
|
||||
{
|
||||
negativePrompt.Append("realistic, photo, photography, 3d, ");
|
||||
}
|
||||
|
||||
// Пользовательский негативный промпт
|
||||
if (!string.IsNullOrEmpty(settings.NegativePrompt))
|
||||
{
|
||||
negativePrompt.Append(settings.NegativePrompt);
|
||||
}
|
||||
|
||||
return negativePrompt.ToString().Trim().TrimEnd(',');
|
||||
}
|
||||
|
||||
public string GetFullPromptDescription(
|
||||
PawnAppearanceData appearanceData,
|
||||
StableDiffusionSettings settings
|
||||
)
|
||||
{
|
||||
StringBuilder description = new StringBuilder();
|
||||
|
||||
description.AppendLine("=== Positive Prompt ===");
|
||||
description.AppendLine(GeneratePositivePrompt(appearanceData, settings));
|
||||
description.AppendLine();
|
||||
|
||||
description.AppendLine("=== Negative Prompt ===");
|
||||
description.AppendLine(GenerateNegativePrompt(settings));
|
||||
description.AppendLine();
|
||||
|
||||
description.AppendLine("=== Technical Parameters ===");
|
||||
description.AppendLine($"Steps: {settings.Steps}");
|
||||
description.AppendLine($"CFG Scale: {settings.CfgScale}");
|
||||
description.AppendLine($"Size: {settings.Width}x{settings.Height}");
|
||||
description.AppendLine($"Sampler: {settings.Sampler}");
|
||||
description.AppendLine(
|
||||
$"Seed: {(settings.Seed == -1 ? "Random" : settings.Seed.ToString())}"
|
||||
);
|
||||
|
||||
return description.ToString();
|
||||
}
|
||||
|
||||
private string GetAgeAndGenderDescription(PawnAppearanceData data)
|
||||
{
|
||||
string ageGroup = data.Age switch
|
||||
{
|
||||
< 18 => "young",
|
||||
< 25 => "young adult",
|
||||
< 35 => "adult",
|
||||
< 50 => "middle-aged",
|
||||
< 65 => "mature",
|
||||
_ => "elderly",
|
||||
};
|
||||
|
||||
string genderLabel = data.Gender switch
|
||||
{
|
||||
Gender.Male => "man",
|
||||
Gender.Female => "woman",
|
||||
_ => "person",
|
||||
};
|
||||
return $"{ageGroup} {genderLabel}";
|
||||
}
|
||||
|
||||
private string GetBodyTypeDescription(string bodyType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bodyType))
|
||||
return "";
|
||||
|
||||
return bodyType.ToLower() switch
|
||||
{
|
||||
"thin" => "slender build, lean physique",
|
||||
"hulk" => "muscular build, strong physique, athletic body",
|
||||
"fat" => "heavyset build, stocky physique",
|
||||
"female" => "feminine build",
|
||||
"male" => "masculine build",
|
||||
_ => "average build",
|
||||
};
|
||||
}
|
||||
|
||||
private string GetHairDescription(PawnAppearanceData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data.HairStyle))
|
||||
return "";
|
||||
|
||||
StringBuilder hair = new StringBuilder();
|
||||
|
||||
// Цвет волос
|
||||
string hairColor = ColorDescriptionService.GetHairColorDescription(data.HairColor);
|
||||
hair.Append(hairColor);
|
||||
hair.Append(" ");
|
||||
|
||||
// Стиль прически (упрощаем сложные названия)
|
||||
string style = data
|
||||
.HairStyle.ToLower()
|
||||
.Replace("_", " ")
|
||||
.Replace("shaved", "very short")
|
||||
.Replace("mohawk", "mohawk hairstyle");
|
||||
|
||||
hair.Append(style);
|
||||
hair.Append(" hair");
|
||||
|
||||
return hair.ToString();
|
||||
}
|
||||
|
||||
private string GetMoodFromTraits(List<Trait> traits)
|
||||
{
|
||||
if (traits == null || !traits.Any())
|
||||
return "neutral expression";
|
||||
|
||||
// Ищем черты, которые влияют на внешний вид
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
string traitDefName = trait.def.defName;
|
||||
if (TraitToMood.TryGetValue(traitDefName, out string mood))
|
||||
{
|
||||
return mood;
|
||||
}
|
||||
}
|
||||
|
||||
return "calm expression";
|
||||
}
|
||||
|
||||
private string GetApparelDescription(List<ApparelData> apparel)
|
||||
{
|
||||
if (apparel == null || !apparel.Any())
|
||||
return "simple clothes";
|
||||
|
||||
StringBuilder apparelDesc = new StringBuilder("wearing ");
|
||||
|
||||
// Берем топ 5 наиболее заметных предметов одежды
|
||||
var visibleApparel = apparel.Take(5).ToList();
|
||||
|
||||
List<string> items = new List<string>();
|
||||
foreach (var item in visibleApparel)
|
||||
{
|
||||
StringBuilder itemDesc = new StringBuilder();
|
||||
|
||||
// Цвет (если не белый)
|
||||
if (item.Color != UnityEngine.Color.white)
|
||||
{
|
||||
string colorDesc = ColorDescriptionService.GetApparelColorDescription(
|
||||
item.Color
|
||||
);
|
||||
itemDesc.Append(colorDesc);
|
||||
itemDesc.Append(" ");
|
||||
}
|
||||
|
||||
// Материал
|
||||
if (!string.IsNullOrEmpty(item.Material))
|
||||
{
|
||||
itemDesc.Append(item.Material.ToLower());
|
||||
itemDesc.Append(" ");
|
||||
}
|
||||
|
||||
// Название предмета
|
||||
itemDesc.Append(item.Label.ToLower());
|
||||
|
||||
items.Add(itemDesc.ToString());
|
||||
}
|
||||
|
||||
apparelDesc.Append(string.Join(", ", items));
|
||||
|
||||
return apparelDesc.ToString();
|
||||
}
|
||||
|
||||
private string GetQualityTags(ArtStyle style)
|
||||
{
|
||||
var baseTags = "highly detailed, professional, masterpiece, best quality";
|
||||
|
||||
if (style == ArtStyle.Realistic || style == ArtStyle.SemiRealistic)
|
||||
{
|
||||
return $"{baseTags}, professional photography, 8k uhd, dslr, high quality, sharp focus";
|
||||
}
|
||||
else if (style == ArtStyle.Anime)
|
||||
{
|
||||
return $"{baseTags}, anime masterpiece, high resolution, vibrant colors";
|
||||
}
|
||||
else if (style == ArtStyle.ConceptArt)
|
||||
{
|
||||
return $"{baseTags}, trending on artstation, professional digital art";
|
||||
}
|
||||
else
|
||||
{
|
||||
return baseTags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user