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 = ['') return ''.join(html_parts)