Add style detection and model matching for auto mode

Style Detection (detect_generation_type):
- Detects 9 artistic styles: anime, photorealistic, digital_art, cgi, cartoon, fantasy, traditional, scifi, horror
- Extracts style keywords from prompts for matching
- Returns style info in generation type dict

Style Matching (select_best_model):
- Matches LoRA adapters to requested style (+60 bonus for style match)
- Matches base models to requested style (+50 bonus for style match)
- Checks model name, ID, and tags for style indicators
- Examples:
  - 'anime girl' → selects anime-optimized models/LoRAs
  - 'photorealistic portrait' → selects realism models
  - 'cyberpunk city' → selects sci-fi models/LoRAs

This allows --auto mode to intelligently select models based on
the artistic style requested in the prompt.
parent 2bdf2e8a
......@@ -2767,6 +2767,8 @@ def detect_generation_type(prompt, prompt_image=None, prompt_animation=None, arg
"nsfw_reason": "",
"motion_type": "standard",
"subject_type": "general",
"style": "general", # New: style detection
"style_keywords": [], # New: detected style keywords
}
# Detect NSFW
......@@ -2846,6 +2848,115 @@ def detect_generation_type(prompt, prompt_image=None, prompt_animation=None, arg
elif any(kw in all_text for kw in ["animal", "cat", "dog", "bird", "wildlife"]):
result["subject_type"] = "animal"
# ═══════════════════════════════════════════════════════════════
# Style Detection - Match models to artistic style
# ═══════════════════════════════════════════════════════════════
# Anime/Manga style
anime_keywords = ["anime", "manga", "anime style", "anime girl", "anime boy",
"anime character", "anime face", "anime art", "anime aesthetic",
"cel shaded", "cel shading", "anime eyes", "chibi", "kawaii",
"otaku", "waifu", "husbando", "neko", "anime portrait",
"japanese animation", "anime scene", "anime background"]
# Photorealistic style
photorealistic_keywords = ["photorealistic", "photo realistic", "realistic photo",
"realistic image", "photography", "photo", "photograph",
"real photo", "lifelike", "ultra realistic", "hyperrealistic",
"hyper realistic", "realistic portrait", "realistic face",
"dslr", "raw photo", "cinematic photo", "film photo",
"professional photo", "studio photo", "portrait photo"]
# Digital Art / Illustration
digital_art_keywords = ["digital art", "digital painting", "digital illustration",
"concept art", "artstation", "digital drawing", "digital render",
"digital artwork", "painting", "illustration", "artwork",
"digital creation", "cg art", "computer art"]
# 3D / CGI
cgi_keywords = ["3d render", "3d model", "3d art", "cgi", "3d render",
"blender", "maya", "cinema 4d", "unreal engine", "unity",
"3d character", "3d scene", "3d environment", "octane render",
"vray", "3d animation", "3d style"]
# Cartoon / Stylized
cartoon_keywords = ["cartoon", "cartoon style", "cartoonish", "toon",
"stylized", "stylized art", "cartoon character",
"animated style", "disney style", "pixar style",
"cartoon art", "flat style", "vector art"]
# Fantasy / Artistic
fantasy_keywords = ["fantasy", "fantasy art", "fantasy style", "magical",
"mystical", "ethereal", "dreamlike", "surreal",
"surrealism", "fantasy world", "fantasy character",
"dark fantasy", "high fantasy", "epic fantasy"]
# Oil Painting / Traditional Art
traditional_keywords = ["oil painting", "watercolor", "acrylic", "pencil drawing",
"sketch", "charcoal", "pastel", "traditional art",
"hand drawn", "canvas", "brush strokes", "painterly"]
# Sci-Fi / Cyberpunk
scifi_keywords = ["sci-fi", "scifi", "science fiction", "cyberpunk", "futuristic",
"cyber", "neon", "dystopian", "space", "robot", "mech",
"mechanical", "tech", "technological", "holographic"]
# Horror / Dark
horror_keywords = ["horror", "dark", "gothic", "creepy", "scary", "dark art",
"macabre", "dark aesthetic", "horror style", "spooky",
"nightmarish", "eerie", "haunting", "dark fantasy"]
# Check for style matches and collect all matching styles
detected_styles = []
if any(kw in all_text for kw in anime_keywords):
detected_styles.append("anime")
result["style_keywords"].extend([kw for kw in anime_keywords if kw in all_text])
if any(kw in all_text for kw in photorealistic_keywords):
detected_styles.append("photorealistic")
result["style_keywords"].extend([kw for kw in photorealistic_keywords if kw in all_text])
if any(kw in all_text for kw in digital_art_keywords):
detected_styles.append("digital_art")
result["style_keywords"].extend([kw for kw in digital_art_keywords if kw in all_text])
if any(kw in all_text for kw in cgi_keywords):
detected_styles.append("cgi")
result["style_keywords"].extend([kw for kw in cgi_keywords if kw in all_text])
if any(kw in all_text for kw in cartoon_keywords):
detected_styles.append("cartoon")
result["style_keywords"].extend([kw for kw in cartoon_keywords if kw in all_text])
if any(kw in all_text for kw in fantasy_keywords):
detected_styles.append("fantasy")
result["style_keywords"].extend([kw for kw in fantasy_keywords if kw in all_text])
if any(kw in all_text for kw in traditional_keywords):
detected_styles.append("traditional")
result["style_keywords"].extend([kw for kw in traditional_keywords if kw in all_text])
if any(kw in all_text for kw in scifi_keywords):
detected_styles.append("scifi")
result["style_keywords"].extend([kw for kw in scifi_keywords if kw in all_text])
if any(kw in all_text for kw in horror_keywords):
detected_styles.append("horror")
result["style_keywords"].extend([kw for kw in horror_keywords if kw in all_text])
# Set primary style (first detected, with priority for more specific styles)
style_priority = ["anime", "photorealistic", "cgi", "cartoon", "scifi", "horror",
"fantasy", "traditional", "digital_art"]
for style in style_priority:
if style in detected_styles:
result["style"] = style
break
# Remove duplicates from style_keywords
result["style_keywords"] = list(set(result["style_keywords"]))
return result
......@@ -2985,11 +3096,74 @@ def select_best_model(gen_type, models, vram_gb=24, prefer_quality=True, return_
score -= 10
reasons.append("NSFW LoRA (may affect non-NSFW output)")
# LoRA quality bonus (LoRAs are often fine-tuned for specific styles)
if "realism" in name.lower() or "realistic" in name.lower():
# ═══════════════════════════════════════════════════════════════
# Style Matching - Match LoRA to requested artistic style
# ═══════════════════════════════════════════════════════════════
requested_style = gen_type.get("style", "general")
model_name_lower = name.lower()
model_id_lower = info.get("id", "").lower()
model_tags = [t.lower() for t in info.get("tags", [])]
# Style matching bonuses for LoRAs
if requested_style == "anime":
anime_indicators = ["anime", "manga", "anime style", "cel shaded", "chibi", "kawaii", "waifu"]
if any(ind in model_name_lower or ind in model_id_lower for ind in anime_indicators):
score += 60
reasons.append("Anime-style LoRA")
elif any(ind in model_tags for ind in anime_indicators):
score += 50
reasons.append("Anime-tagged LoRA")
elif requested_style == "photorealistic":
realistic_indicators = ["realistic", "realism", "photo", "photorealistic", "lifelike", "ultra realistic"]
if any(ind in model_name_lower or ind in model_id_lower for ind in realistic_indicators):
score += 60
reasons.append("Photorealistic LoRA")
elif any(ind in model_tags for ind in realistic_indicators):
score += 50
reasons.append("Realism-tagged LoRA")
elif requested_style == "cartoon":
cartoon_indicators = ["cartoon", "toon", "stylized", "disney", "pixar", "flat"]
if any(ind in model_name_lower or ind in model_id_lower for ind in cartoon_indicators):
score += 60
reasons.append("Cartoon-style LoRA")
elif requested_style == "cgi":
cgi_indicators = ["3d", "cgi", "blender", "unreal", "octane", "vray"]
if any(ind in model_name_lower or ind in model_id_lower for ind in cgi_indicators):
score += 60
reasons.append("3D/CGI LoRA")
elif requested_style == "scifi":
scifi_indicators = ["sci-fi", "scifi", "cyberpunk", "futuristic", "cyber", "neon"]
if any(ind in model_name_lower or ind in model_id_lower for ind in scifi_indicators):
score += 60
reasons.append("Sci-Fi LoRA")
elif requested_style == "fantasy":
fantasy_indicators = ["fantasy", "magical", "mystical", "ethereal", "surreal"]
if any(ind in model_name_lower or ind in model_id_lower for ind in fantasy_indicators):
score += 60
reasons.append("Fantasy LoRA")
elif requested_style == "horror":
horror_indicators = ["horror", "dark", "gothic", "creepy", "macabre"]
if any(ind in model_name_lower or ind in model_id_lower for ind in horror_indicators):
score += 60
reasons.append("Horror LoRA")
elif requested_style == "traditional":
traditional_indicators = ["oil painting", "watercolor", "pencil", "sketch", "charcoal", "painterly"]
if any(ind in model_name_lower or ind in model_id_lower for ind in traditional_indicators):
score += 60
reasons.append("Traditional art LoRA")
# General style bonus for any style-matching LoRA
elif "realism" in model_name_lower or "realistic" in model_name_lower:
score += 15
reasons.append("Realism-focused LoRA")
elif "style" in name.lower():
elif "style" in model_name_lower:
score += 10
reasons.append("Style LoRA")
......@@ -3060,6 +3234,69 @@ def select_best_model(gen_type, models, vram_gb=24, prefer_quality=True, return_
# Prefer smaller models for speed
score += max(0, 20 - vram_est)
# ═══════════════════════════════════════════════════════════════
# Style Matching - Match model to requested artistic style
# ═══════════════════════════════════════════════════════════════
requested_style = gen_type.get("style", "general")
model_name_lower = name.lower()
model_id_lower = info.get("id", "").lower()
model_tags = [t.lower() for t in info.get("tags", [])]
# Style matching bonuses for base models
if requested_style == "anime":
anime_indicators = ["anime", "manga", "anime style", "cel shaded", "chibi", "kawaii", "waifu", "animagine", "anything", "counterfeit"]
if any(ind in model_name_lower or ind in model_id_lower for ind in anime_indicators):
score += 50
reasons.append("Anime-optimized model")
elif any(ind in model_tags for ind in anime_indicators):
score += 40
reasons.append("Anime-tagged model")
elif requested_style == "photorealistic":
realistic_indicators = ["realistic", "realism", "photo", "photorealistic", "lifelike", "ultra realistic", "real vision", "deliberate", "juggernaut", "cyberrealistic"]
if any(ind in model_name_lower or ind in model_id_lower for ind in realistic_indicators):
score += 50
reasons.append("Photorealistic model")
elif any(ind in model_tags for ind in realistic_indicators):
score += 40
reasons.append("Realism-tagged model")
elif requested_style == "cartoon":
cartoon_indicators = ["cartoon", "toon", "stylized", "disney", "pixar", "flat", "realcartoon"]
if any(ind in model_name_lower or ind in model_id_lower for ind in cartoon_indicators):
score += 50
reasons.append("Cartoon-style model")
elif requested_style == "cgi":
cgi_indicators = ["3d", "cgi", "blender", "unreal", "octane", "vray"]
if any(ind in model_name_lower or ind in model_id_lower for ind in cgi_indicators):
score += 50
reasons.append("3D/CGI model")
elif requested_style == "scifi":
scifi_indicators = ["sci-fi", "scifi", "cyberpunk", "futuristic", "cyber", "neon"]
if any(ind in model_name_lower or ind in model_id_lower for ind in scifi_indicators):
score += 50
reasons.append("Sci-Fi model")
elif requested_style == "fantasy":
fantasy_indicators = ["fantasy", "magical", "mystical", "ethereal", "surreal", "majicmix", "dreamshaper"]
if any(ind in model_name_lower or ind in model_id_lower for ind in fantasy_indicators):
score += 50
reasons.append("Fantasy model")
elif requested_style == "horror":
horror_indicators = ["horror", "dark", "gothic", "creepy", "macabre"]
if any(ind in model_name_lower or ind in model_id_lower for ind in horror_indicators):
score += 50
reasons.append("Horror model")
elif requested_style == "traditional":
traditional_indicators = ["oil painting", "watercolor", "pencil", "sketch", "charcoal", "painterly"]
if any(ind in model_name_lower or ind in model_id_lower for ind in traditional_indicators):
score += 50
reasons.append("Traditional art model")
# Popular/reliable models get bonus
downloads = info.get("downloads", 0)
if downloads > 10000:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment