SceneWeaver / inpainting_templates.py
DawnC's picture
Update inpainting_templates.py
9b38240 verified
import logging
from dataclasses import dataclass, field
from typing import Dict, List, Optional
logger = logging.getLogger(__name__)
@dataclass
class InpaintingTemplate:
"""Data class representing an inpainting template."""
key: str
name: str
category: str
icon: str
description: str
# Prompt templates
prompt_template: str
negative_prompt: str
# Recommended parameters
controlnet_conditioning_scale: float = 0.7
feather_radius: int = 8
guidance_scale: float = 7.5
num_inference_steps: int = 25
# Inpainting strength (0.0-1.0)
# 1.0 = fully repaint masked area, 0.0 = keep original
strength: float = 1.0
# Conditioning type preference
preferred_conditioning: str = "canny" # "canny" or "depth"
# Structure preservation in masked area
# True = keep edges in mask (for color change), False = clear edges (for replacement/removal)
preserve_structure_in_mask: bool = False
# Prompt enhancement control
enhance_prompt: bool = True # Whether to use OpenCLIP prompt enhancement
# Difficulty level for UI display
difficulty: str = "medium" # "easy", "medium", "advanced"
# Tips for users
usage_tips: List[str] = field(default_factory=list)
class InpaintingTemplateManager:
"""
Manages inpainting templates for various use cases.
Provides categorized presets optimized for different inpainting scenarios
including object replacement, removal, style transfer, and enhancement.
Attributes:
TEMPLATES: Dictionary of all available templates
CATEGORIES: List of category names in display order
Example:
>>> manager = InpaintingTemplateManager()
>>> template = manager.get_template("object_replacement")
>>> print(template.prompt_template)
"""
TEMPLATES: Dict[str, InpaintingTemplate] = {
# ========================================
# 4 CORE TEMPLATES - Optimized for Speed & Quality
# ========================================
# 1. CHANGE COLOR - Pure color transformation
"change_color": InpaintingTemplate(
key="change_color",
name="Change Color",
category="Color",
icon="🎨",
description="Change color ONLY - fills the masked area with a solid, flat color",
prompt_template="{content} color, solid flat {content}, uniform color, no patterns, smooth surface",
negative_prompt=(
"original color, keeping same color, unchanged color, "
"black, dark, keeping black, maintaining black color, "
"black clothing, dark colors, dark fabric, black fabric, "
"patterns, floral, stripes, plaid, checkered, decorative patterns, "
"diamond pattern, grid pattern, geometric patterns, "
"texture, textured, wrinkles, folds, creases, "
"gradients, shading variations, color variations, "
"complex patterns, printed patterns, embroidery"
),
controlnet_conditioning_scale=0.3, # Low-medium: allow color freedom in masked area
feather_radius=4, # Low: clean color boundaries
guidance_scale=15.0, # Very high: strongly follow color prompt
num_inference_steps=10, # Optimized for speed
strength=1.0, # Full repaint for color change
preferred_conditioning="canny", # Edge-based
preserve_structure_in_mask=False, # KEY: clear edges in mask for pure color fill
enhance_prompt=False, # Disabled: use color prompt directly
difficulty="easy",
usage_tips=[
"🎯 Purpose: Fill the masked area with a solid, uniform color.",
"",
"📝 Example Prompts:",
" • 'vibrant red' - bold, saturated red",
" • 'soft pastel pink' - gentle, light pink",
" • 'deep navy blue' - rich, dark blue",
" • 'bright yellow' - eye-catching yellow",
" • 'pure white' - clean, solid white",
"",
"💡 Tips:",
" • Describe ONLY the color, not the object",
" • Paint the entire area you want to recolor",
" • Use modifiers: 'bright', 'dark', 'pastel', 'vivid'"
]
),
# 2. CLOTHING CHANGE - Style and garment transformation
"clothing_change": InpaintingTemplate(
key="clothing_change",
name="Clothing Change",
category="Replacement",
icon="👕",
description="Change clothing style, material, or design - can include color change",
prompt_template="{content}, photorealistic, realistic fabric texture, natural fit, high quality",
negative_prompt=(
"wrong body proportions, floating fabric, unrealistic wrinkles, "
"mismatched lighting, visible edges, original clothing style, "
"keeping same color, original color, faded colors, unchanged appearance, partial change, "
"black clothing, dark original color, distorted body, naked, nudity, "
"cartoon, anime, illustration, drawing, painted"
),
controlnet_conditioning_scale=0.30, # Medium: preserves body structure, allows clothing change
feather_radius=14, # Medium: natural blending with body
guidance_scale=11.5, # Medium-high: accurate clothing generation
num_inference_steps=10, # Optimized for speed
strength=1.0, # Full repaint: completely replace clothing
preferred_conditioning="depth", # Depth: preserves fabric folds and body structure
enhance_prompt=True, # Enabled: enriches clothing details
difficulty="easy",
usage_tips=[
"🎯 Purpose: Replace clothing with a different style, material, or design.",
"",
"📝 Example Prompts:",
" • 'tailored charcoal suit with silk tie and white shirt' - formal business",
" • 'navy blazer with gold buttons over light blue oxford shirt' - smart casual",
" • 'black tuxedo with bow tie and white dress shirt' - elegant formal",
" • 'white polo shirt with collar' - casual business",
" • 'cozy cream knit sweater' - warm casual style",
" • 'vintage denim jacket' - retro fashion",
"",
"💡 Tips:",
" • Include clothing type + color + details for best results",
" • For suits: mention 'tailored', 'fitted', specific fabric like 'wool' or 'silk'",
" • Body structure is preserved automatically"
]
),
# 3. OBJECT REPLACEMENT - Replace one object with another
"object_replacement": InpaintingTemplate(
key="object_replacement",
name="Object Replacement",
category="Replacement",
icon="🔄",
description="Replace objects (one type at a time) - all masked areas become the SAME object",
prompt_template="{content}, photorealistic, natural lighting, seamlessly integrated into scene, high quality",
negative_prompt=(
"inconsistent lighting, wrong perspective, mismatched colors, "
"visible seams, floating objects, unrealistic placement, original object, "
"poorly integrated, disconnected from scene, keeping original, remnants of original, "
"multiple different objects, mixed objects, various items, "
"cartoon, anime, illustration, drawing, painted"
),
controlnet_conditioning_scale=0.25, # Low-medium: allows complete object replacement
feather_radius=10, # Medium: natural scene integration
guidance_scale=13.0, # Medium-high: accurate object generation
num_inference_steps=10, # Optimized for speed
strength=1.0, # Full repaint: completely replace object
preferred_conditioning="canny", # Edge-based: preserves scene perspective
enhance_prompt=True, # Enabled: enriches object details
difficulty="medium",
usage_tips=[
"🎯 Purpose: Replace an object with something completely different.",
"",
"📝 Example Prompts:",
" • 'elegant ceramic vase with fresh roses' - decorative item",
" • 'modern silver laptop on wooden stand' - tech gadget",
" • 'stack of leather-bound vintage books' - classic decoration",
" • 'healthy green potted succulent' - natural element",
" • 'antique brass table lamp with fabric shade' - lighting",
"",
"💡 Tips:",
" • Replace ONE object type at a time",
" • Describe what you want, not what you're removing",
" • Include material and style for realistic results"
]
),
# 4. REMOVAL - Remove objects and fill with background
"removal": InpaintingTemplate(
key="removal",
name="Remove Object",
category="Removal",
icon="🗑️",
description="Remove objects and naturally fill with background - describe the background material",
prompt_template="continue the background with {content}, photorealistic, seamless blending, natural texture continuation, high quality",
negative_prompt=(
"new object appearing, adding items, inserting objects, "
"foreground elements, visible object, thing, item, "
"unnatural filling, visible patches, inconsistent texture, "
"mismatched pattern, color discontinuity, artificial blending, "
"cartoon, anime, illustration, drawing, painted"
),
controlnet_conditioning_scale=0.20, # Low: allows creative background filling
feather_radius=12, # Medium: smooth background blending
guidance_scale=12.0, # Medium: balanced control and naturalness
num_inference_steps=10, # Optimized for speed
strength=1.0, # Full repaint: completely remove and fill
preferred_conditioning="depth", # Depth: preserves spatial perspective
enhance_prompt=False, # Disabled: avoid generating new objects
difficulty="medium",
usage_tips=[
"🎯 Purpose: Remove unwanted objects and fill with background.",
"",
"📝 Example Prompts:",
" • 'polished hardwood floor with natural grain' - indoor floors",
" • 'smooth white painted wall' - wall backgrounds",
" • 'lush green grass lawn' - outdoor areas",
" • 'soft beige carpet texture' - carpeted floors",
" • 'clear blue sky with soft clouds' - sky backgrounds",
"",
"💡 Tips:",
" • Describe the BACKGROUND texture, not the object",
" • Leave empty to auto-match surrounding area",
" • Works best with uniform backgrounds"
]
),
}
# Category display order
CATEGORIES = ["Color", "Replacement", "Removal"] # 4 core templates only
def __init__(self):
"""Initialize the InpaintingTemplateManager."""
logger.info(f"InpaintingTemplateManager initialized with {len(self.TEMPLATES)} templates")
def get_all_templates(self) -> Dict[str, InpaintingTemplate]:
"""
Get all available templates.
Returns
-------
dict
Dictionary of all templates keyed by template key
"""
return self.TEMPLATES
def get_template(self, key: str) -> Optional[InpaintingTemplate]:
"""
Get a specific template by key.
Parameters
----------
key : str
Template identifier
Returns
-------
InpaintingTemplate or None
Template if found, None otherwise
"""
return self.TEMPLATES.get(key)
def get_templates_by_category(self, category: str) -> List[InpaintingTemplate]:
"""
Get all templates in a specific category.
Parameters
----------
category : str
Category name
Returns
-------
list
List of templates in the category
"""
return [t for t in self.TEMPLATES.values() if t.category == category]
def get_categories(self) -> List[str]:
"""
Get list of all categories in display order.
Returns
-------
list
Category names
"""
return self.CATEGORIES
def get_template_choices_sorted(self) -> List[str]:
"""
Get template choices formatted for Gradio dropdown.
Returns list of display strings sorted by category then A-Z.
Format: "icon Name"
Returns
-------
list
Formatted display strings for dropdown
"""
display_list = []
for category in self.CATEGORIES:
templates = self.get_templates_by_category(category)
for template in sorted(templates, key=lambda t: t.name):
display_name = f"{template.icon} {template.name}"
display_list.append(display_name)
return display_list
def get_template_key_from_display(self, display_name: str) -> Optional[str]:
"""
Get template key from display name.
Parameters
----------
display_name : str
Display string like "🔄 Object Replacement"
Returns
-------
str or None
Template key if found
"""
if not display_name:
return None
for key, template in self.TEMPLATES.items():
if f"{template.icon} {template.name}" == display_name:
return key
return None
def get_parameters_for_template(self, key: str) -> Dict[str, any]:
"""
Get recommended parameters for a template.
Parameters
----------
key : str
Template key
Returns
-------
dict
Dictionary of parameter names and values
"""
template = self.get_template(key)
if not template:
return {}
return {
"controlnet_conditioning_scale": template.controlnet_conditioning_scale,
"feather_radius": template.feather_radius,
"guidance_scale": template.guidance_scale,
"num_inference_steps": template.num_inference_steps,
"strength": template.strength,
"preferred_conditioning": template.preferred_conditioning,
"preserve_structure_in_mask": template.preserve_structure_in_mask,
"enhance_prompt": template.enhance_prompt
}
def build_prompt(self, key: str, content: str) -> str:
"""
Build complete prompt from template and user content.
Parameters
----------
key : str
Template key
content : str
User-provided content description
Returns
-------
str
Formatted prompt with content inserted
"""
template = self.get_template(key)
if not template:
return content
return template.prompt_template.format(content=content)
def get_negative_prompt(self, key: str) -> str:
"""
Get negative prompt for a template.
Parameters
----------
key : str
Template key
Returns
-------
str
Negative prompt string
"""
template = self.get_template(key)
if not template:
return ""
return template.negative_prompt
def get_usage_tips(self, key: str) -> List[str]:
"""
Get usage tips for a template.
Parameters
----------
key : str
Template key
Returns
-------
list
List of tip strings
"""
template = self.get_template(key)
if not template:
return []
return template.usage_tips
def build_gallery_html(self) -> str:
"""
Build HTML for template gallery display.
Returns
-------
str
HTML string for Gradio display
"""
html_parts = ['<div class="inpainting-gallery">']
for category in self.CATEGORIES:
templates = self.get_templates_by_category(category)
if not templates:
continue
html_parts.append(f'''
<div class="inpainting-category">
<h4 class="inpainting-category-title">{category}</h4>
<div class="inpainting-grid">
''')
for template in sorted(templates, key=lambda t: t.name):
html_parts.append(f'''
<div class="inpainting-card" data-template="{template.key}">
<span class="inpainting-icon">{template.icon}</span>
<span class="inpainting-name">{template.name}</span>
<span class="inpainting-desc">{template.description[:50]}...</span>
</div>
''')
html_parts.append('</div></div>')
html_parts.append('</div>')
return ''.join(html_parts)