DawnC commited on
Commit
f7dcaec
·
verified ·
1 Parent(s): f66b3c8

Update scene_templates.py

Browse files
Files changed (1) hide show
  1. scene_templates.py +346 -384
scene_templates.py CHANGED
@@ -1,336 +1,297 @@
1
  import logging
2
- from dataclasses import dataclass, field
3
  from typing import Dict, List, Optional
 
4
 
5
  logger = logging.getLogger(__name__)
6
 
7
-
8
  @dataclass
9
- class InpaintingTemplate:
10
- """Data class representing an inpainting template."""
11
-
12
  key: str
13
  name: str
 
 
14
  category: str
15
  icon: str
16
- description: str
17
-
18
- # Prompt templates
19
- prompt_template: str
20
- negative_prompt: str
21
-
22
- # Recommended parameters
23
- controlnet_conditioning_scale: float = 0.7
24
- feather_radius: int = 8
25
  guidance_scale: float = 7.5
26
- num_inference_steps: int = 25
27
-
28
- # Inpainting strength (0.0-1.0)
29
- # 1.0 = fully repaint masked area, 0.0 = keep original
30
- strength: float = 1.0
31
-
32
- # Conditioning type preference
33
- preferred_conditioning: str = "canny" # "canny" or "depth"
34
-
35
- # Structure preservation in masked area
36
- # True = keep edges in mask (for color change), False = clear edges (for replacement/removal)
37
- preserve_structure_in_mask: bool = False
38
 
39
- # Prompt enhancement control
40
- enhance_prompt: bool = True # Whether to use OpenCLIP prompt enhancement
41
 
42
- # Difficulty level for UI display
43
- difficulty: str = "medium" # "easy", "medium", "advanced"
44
-
45
- # Tips for users
46
- usage_tips: List[str] = field(default_factory=list)
47
-
48
-
49
- class InpaintingTemplateManager:
50
  """
51
- Manages inpainting templates for various use cases.
52
-
53
- Provides categorized presets optimized for different inpainting scenarios
54
- including object replacement, removal, style transfer, and enhancement.
55
-
56
- Attributes:
57
- TEMPLATES: Dictionary of all available templates
58
- CATEGORIES: List of category names in display order
59
-
60
- Example:
61
- >>> manager = InpaintingTemplateManager()
62
- >>> template = manager.get_template("object_replacement")
63
- >>> print(template.prompt_template)
64
  """
65
 
66
- TEMPLATES: Dict[str, InpaintingTemplate] = {
67
- # ========================================
68
- # 4 CORE TEMPLATES - Optimized for Speed & Quality
69
- # ========================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
- # 1. CHANGE COLOR - Pure color transformation
72
- "change_color": InpaintingTemplate(
73
- key="change_color",
74
- name="Change Color",
75
- category="Color",
76
- icon="🎨",
77
- description="Change color ONLY - fills the masked area with a solid, flat color",
78
- prompt_template="{content} color, solid flat {content}, uniform color, no patterns, smooth surface",
79
- negative_prompt=(
80
- "original color, keeping same color, unchanged color, "
81
- "black, dark, keeping black, maintaining black color, "
82
- "black clothing, dark colors, dark fabric, black fabric, "
83
- "patterns, floral, stripes, plaid, checkered, decorative patterns, "
84
- "diamond pattern, grid pattern, geometric patterns, "
85
- "texture, textured, wrinkles, folds, creases, "
86
- "gradients, shading variations, color variations, "
87
- "complex patterns, printed patterns, embroidery"
88
- ),
89
- controlnet_conditioning_scale=0.3, # Low-medium: allow color freedom in masked area
90
- feather_radius=4, # Low: clean color boundaries
91
- guidance_scale=15.0, # Very high: strongly follow color prompt
92
- num_inference_steps=10, # Optimized for speed
93
- strength=1.0, # Full repaint for color change
94
- preferred_conditioning="canny", # Edge-based
95
- preserve_structure_in_mask=False, # KEY: clear edges in mask for pure color fill
96
- enhance_prompt=False, # Disabled: use color prompt directly
97
- difficulty="easy",
98
- usage_tips=[
99
- "🎯 Purpose: Fill the masked area with a solid, uniform color.",
100
- "",
101
- "📝 Example Prompts:",
102
- " 'vibrant red' - bold, saturated red",
103
- " 'soft pastel pink' - gentle, light pink",
104
- " • 'deep navy blue' - rich, dark blue",
105
- " • 'bright yellow' - eye-catching yellow",
106
- " • 'pure white' - clean, solid white",
107
- "",
108
- "💡 Tips:",
109
- " • Describe ONLY the color, not the object",
110
- " Paint the entire area you want to recolor",
111
- " Use modifiers: 'bright', 'dark', 'pastel', 'vivid'"
112
- ]
 
 
 
 
 
 
 
 
 
 
 
 
113
  ),
114
 
115
- # 2. CLOTHING CHANGE - Style and garment transformation
116
- "clothing_change": InpaintingTemplate(
117
- key="clothing_change",
118
- name="Clothing Change",
119
- category="Replacement",
120
- icon="👕",
121
- description="Change clothing style, material, or design - can include color change",
122
- prompt_template="{content}, photorealistic, realistic fabric texture, natural fit, high quality",
123
- negative_prompt=(
124
- "wrong body proportions, floating fabric, unrealistic wrinkles, "
125
- "mismatched lighting, visible edges, original clothing style, "
126
- "keeping same color, original color, faded colors, unchanged appearance, partial change, "
127
- "black clothing, dark original color, distorted body, naked, nudity, "
128
- "cartoon, anime, illustration, drawing, painted"
129
- ),
130
- controlnet_conditioning_scale=0.30, # Medium: preserves body structure, allows clothing change
131
- feather_radius=14, # Medium: natural blending with body
132
- guidance_scale=11.5, # Medium-high: accurate clothing generation
133
- num_inference_steps=10, # Optimized for speed
134
- strength=1.0, # Full repaint: completely replace clothing
135
- preferred_conditioning="depth", # Depth: preserves fabric folds and body structure
136
- enhance_prompt=True, # Enabled: enriches clothing details
137
- difficulty="easy",
138
- usage_tips=[
139
- "🎯 Purpose: Replace clothing with a different style, material, or design.",
140
- "",
141
- "📝 Example Prompts:",
142
- " • 'tailored charcoal suit with silk tie and white shirt' - formal business",
143
- " 'navy blazer with gold buttons over light blue oxford shirt' - smart casual",
144
- " • 'black tuxedo with bow tie and white dress shirt' - elegant formal",
145
- " 'white polo shirt with collar' - casual business",
146
- " 'cozy cream knit sweater' - warm casual style",
147
- " 'vintage denim jacket' - retro fashion",
148
- "",
149
- "💡 Tips:",
150
- " • Include clothing type + color + details for best results",
151
- " • For suits: mention 'tailored', 'fitted', specific fabric like 'wool' or 'silk'",
152
- " Body structure is preserved automatically"
153
- ]
 
 
 
 
 
 
154
  ),
155
 
156
- # 3. OBJECT REPLACEMENT - Replace one object with another
157
- "object_replacement": InpaintingTemplate(
158
- key="object_replacement",
159
- name="Object Replacement",
160
- category="Replacement",
161
- icon="🔄",
162
- description="Replace objects (one type at a time) - all masked areas become the SAME object",
163
- prompt_template="{content}, photorealistic, natural lighting, seamlessly integrated into scene, high quality",
164
- negative_prompt=(
165
- "inconsistent lighting, wrong perspective, mismatched colors, "
166
- "visible seams, floating objects, unrealistic placement, original object, "
167
- "poorly integrated, disconnected from scene, keeping original, remnants of original, "
168
- "multiple different objects, mixed objects, various items, "
169
- "cartoon, anime, illustration, drawing, painted"
170
- ),
171
- controlnet_conditioning_scale=0.25, # Low-medium: allows complete object replacement
172
- feather_radius=10, # Medium: natural scene integration
173
- guidance_scale=13.0, # Medium-high: accurate object generation
174
- num_inference_steps=10, # Optimized for speed
175
- strength=1.0, # Full repaint: completely replace object
176
- preferred_conditioning="canny", # Edge-based: preserves scene perspective
177
- enhance_prompt=True, # Enabled: enriches object details
178
- difficulty="medium",
179
- usage_tips=[
180
- "🎯 Purpose: Replace an object with something completely different.",
181
- "",
182
- "📝 Example Prompts:",
183
- " • 'elegant ceramic vase with fresh roses' - decorative item",
184
- " 'modern silver laptop on wooden stand' - tech gadget",
185
- " • 'stack of leather-bound vintage books' - classic decoration",
186
- " 'healthy green potted succulent' - natural element",
187
- " 'antique brass table lamp with fabric shade' - lighting",
188
- "",
189
- "💡 Tips:",
190
- " • Replace ONE object type at a time",
191
- " • Describe what you want, not what you're removing",
192
- " • Include material and style for realistic results"
193
- ]
194
  ),
195
 
196
- # 4. REMOVAL - Remove objects and fill with background
197
- "removal": InpaintingTemplate(
198
- key="removal",
199
- name="Remove Object",
200
- category="Removal",
201
- icon="🗑️",
202
- description="Remove objects and naturally fill with background - describe the background material",
203
- prompt_template="continue the background with {content}, photorealistic, seamless blending, natural texture continuation, high quality",
204
- negative_prompt=(
205
- "new object appearing, adding items, inserting objects, "
206
- "foreground elements, visible object, thing, item, "
207
- "unnatural filling, visible patches, inconsistent texture, "
208
- "mismatched pattern, color discontinuity, artificial blending, "
209
- "cartoon, anime, illustration, drawing, painted"
210
- ),
211
- controlnet_conditioning_scale=0.20, # Low: allows creative background filling
212
- feather_radius=12, # Medium: smooth background blending
213
- guidance_scale=12.0, # Medium: balanced control and naturalness
214
- num_inference_steps=10, # Optimized for speed
215
- strength=1.0, # Full repaint: completely remove and fill
216
- preferred_conditioning="depth", # Depth: preserves spatial perspective
217
- enhance_prompt=False, # Disabled: avoid generating new objects
218
- difficulty="medium",
219
- usage_tips=[
220
- "🎯 Purpose: Remove unwanted objects and fill with background.",
221
- "",
222
- "📝 Example Prompts:",
223
- " • 'polished hardwood floor with natural grain' - indoor floors",
224
- " 'smooth white painted wall' - wall backgrounds",
225
- " • 'lush green grass lawn' - outdoor areas",
226
- " 'soft beige carpet texture' - carpeted floors",
227
- " 'clear blue sky with soft clouds' - sky backgrounds",
228
- "",
229
- "💡 Tips:",
230
- " • Describe the BACKGROUND texture, not the object",
231
- " • Leave empty to auto-match surrounding area",
232
- " • Works best with uniform backgrounds"
233
- ]
234
  ),
235
  }
236
 
237
-
238
  # Category display order
239
- CATEGORIES = ["Color", "Replacement", "Removal"] # 4 core templates only
240
 
241
  def __init__(self):
242
- """Initialize the InpaintingTemplateManager."""
243
- logger.info(f"InpaintingTemplateManager initialized with {len(self.TEMPLATES)} templates")
244
-
245
- def get_all_templates(self) -> Dict[str, InpaintingTemplate]:
246
- """
247
- Get all available templates.
248
 
249
- Returns
250
- -------
251
- dict
252
- Dictionary of all templates keyed by template key
253
- """
254
  return self.TEMPLATES
255
 
256
- def get_template(self, key: str) -> Optional[InpaintingTemplate]:
257
- """
258
- Get a specific template by key.
259
-
260
- Parameters
261
- ----------
262
- key : str
263
- Template identifier
264
-
265
- Returns
266
- -------
267
- InpaintingTemplate or None
268
- Template if found, None otherwise
269
- """
270
  return self.TEMPLATES.get(key)
271
 
272
- def get_templates_by_category(self, category: str) -> List[InpaintingTemplate]:
273
- """
274
- Get all templates in a specific category.
275
-
276
- Parameters
277
- ----------
278
- category : str
279
- Category name
280
-
281
- Returns
282
- -------
283
- list
284
- List of templates in the category
285
- """
286
  return [t for t in self.TEMPLATES.values() if t.category == category]
287
 
288
  def get_categories(self) -> List[str]:
289
- """
290
- Get list of all categories in display order.
291
-
292
- Returns
293
- -------
294
- list
295
- Category names
296
- """
297
  return self.CATEGORIES
298
 
299
  def get_template_choices_sorted(self) -> List[str]:
300
  """
301
  Get template choices formatted for Gradio dropdown.
302
-
303
- Returns list of display strings sorted by category then A-Z.
304
- Format: "icon Name"
305
-
306
- Returns
307
- -------
308
- list
309
- Formatted display strings for dropdown
310
  """
311
  display_list = []
 
 
 
312
 
313
- for category in self.CATEGORIES:
314
- templates = self.get_templates_by_category(category)
315
- for template in sorted(templates, key=lambda t: t.name):
316
- display_name = f"{template.icon} {template.name}"
317
- display_list.append(display_name)
318
-
319
  return display_list
320
 
321
  def get_template_key_from_display(self, display_name: str) -> Optional[str]:
322
  """
323
  Get template key from display name.
324
-
325
- Parameters
326
- ----------
327
- display_name : str
328
- Display string like "🔄 Object Replacement"
329
-
330
- Returns
331
- -------
332
- str or None
333
- Template key if found
334
  """
335
  if not display_name:
336
  return None
@@ -340,105 +301,33 @@ class InpaintingTemplateManager:
340
  return key
341
  return None
342
 
343
- def get_parameters_for_template(self, key: str) -> Dict[str, any]:
344
- """
345
- Get recommended parameters for a template.
346
-
347
- Parameters
348
- ----------
349
- key : str
350
- Template key
351
-
352
- Returns
353
- -------
354
- dict
355
- Dictionary of parameter names and values
356
- """
357
- template = self.get_template(key)
358
- if not template:
359
- return {}
360
-
361
- return {
362
- "controlnet_conditioning_scale": template.controlnet_conditioning_scale,
363
- "feather_radius": template.feather_radius,
364
- "guidance_scale": template.guidance_scale,
365
- "num_inference_steps": template.num_inference_steps,
366
- "strength": template.strength,
367
- "preferred_conditioning": template.preferred_conditioning,
368
- "preserve_structure_in_mask": template.preserve_structure_in_mask,
369
- "enhance_prompt": template.enhance_prompt
370
- }
371
-
372
- def build_prompt(self, key: str, content: str) -> str:
373
- """
374
- Build complete prompt from template and user content.
375
-
376
- Parameters
377
- ----------
378
- key : str
379
- Template key
380
- content : str
381
- User-provided content description
382
-
383
- Returns
384
- -------
385
- str
386
- Formatted prompt with content inserted
387
- """
388
  template = self.get_template(key)
389
- if not template:
390
- return content
391
-
392
- return template.prompt_template.format(content=content)
393
-
394
- def get_negative_prompt(self, key: str) -> str:
395
- """
396
- Get negative prompt for a template.
397
-
398
- Parameters
399
- ----------
400
- key : str
401
- Template key
402
-
403
- Returns
404
- -------
405
- str
406
- Negative prompt string
407
- """
408
  template = self.get_template(key)
409
- if not template:
410
- return ""
411
- return template.negative_prompt
412
 
413
- def get_usage_tips(self, key: str) -> List[str]:
414
- """
415
- Get usage tips for a template.
416
-
417
- Parameters
418
- ----------
419
- key : str
420
- Template key
421
-
422
- Returns
423
- -------
424
- list
425
- List of tip strings
426
- """
427
  template = self.get_template(key)
428
- if not template:
429
- return []
430
- return template.usage_tips
431
 
432
  def build_gallery_html(self) -> str:
433
  """
434
- Build HTML for template gallery display.
435
-
436
- Returns
437
- -------
438
- str
439
- HTML string for Gradio display
440
  """
441
- html_parts = ['<div class="inpainting-gallery">']
442
 
443
  for category in self.CATEGORIES:
444
  templates = self.get_templates_by_category(category)
@@ -446,21 +335,94 @@ class InpaintingTemplateManager:
446
  continue
447
 
448
  html_parts.append(f'''
449
- <div class="inpainting-category">
450
- <h4 class="inpainting-category-title">{category}</h4>
451
- <div class="inpainting-grid">
452
  ''')
453
 
454
- for template in sorted(templates, key=lambda t: t.name):
455
  html_parts.append(f'''
456
- <div class="inpainting-card" data-template="{template.key}">
457
- <span class="inpainting-icon">{template.icon}</span>
458
- <span class="inpainting-name">{template.name}</span>
459
- <span class="inpainting-desc">{template.description[:50]}...</span>
460
- </div>
461
  ''')
462
 
463
  html_parts.append('</div></div>')
464
 
465
  html_parts.append('</div>')
466
- return ''.join(html_parts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import logging
 
2
  from typing import Dict, List, Optional
3
+ from dataclasses import dataclass
4
 
5
  logger = logging.getLogger(__name__)
6
 
 
7
  @dataclass
8
+ class SceneTemplate:
9
+ """Data class representing a scene template"""
 
10
  key: str
11
  name: str
12
+ prompt: str
13
+ negative_extra: str
14
  category: str
15
  icon: str
 
 
 
 
 
 
 
 
 
16
  guidance_scale: float = 7.5
 
 
 
 
 
 
 
 
 
 
 
 
17
 
 
 
18
 
19
+ class SceneTemplateManager:
 
 
 
 
 
 
 
20
  """
21
+ Manages curated scene templates for background generation.
22
+ Provides categorized presets that users can select with one click.
 
 
 
 
 
 
 
 
 
 
 
23
  """
24
 
25
+ # Scene template definitions
26
+ TEMPLATES: Dict[str, SceneTemplate] = {
27
+ # Professional Category
28
+ "office_modern": SceneTemplate(
29
+ key="office_modern",
30
+ name="Modern Office",
31
+ prompt="modern minimalist office interior, clean white desk, large floor-to-ceiling windows, natural daylight, professional corporate environment, soft shadows, contemporary furniture",
32
+ negative_extra="messy, cluttered, dark, old",
33
+ category="Professional",
34
+ icon="🏢",
35
+ guidance_scale=7.5
36
+ ),
37
+ "office_executive": SceneTemplate(
38
+ key="office_executive",
39
+ name="Executive Suite",
40
+ prompt="luxurious executive office, mahogany desk, leather chair, city skyline view through windows, warm ambient lighting, bookshelf, elegant professional setting",
41
+ negative_extra="cheap, cramped, messy",
42
+ category="Professional",
43
+ icon="👔",
44
+ guidance_scale=7.5
45
+ ),
46
+ "studio_white": SceneTemplate(
47
+ key="studio_white",
48
+ name="White Studio",
49
+ prompt="clean white photography studio background, professional lighting setup, seamless white backdrop, soft diffused light, minimal shadows",
50
+ negative_extra="colored, textured, dirty",
51
+ category="Professional",
52
+ icon="📷",
53
+ guidance_scale=8.0
54
+ ),
55
+ "coworking": SceneTemplate(
56
+ key="coworking",
57
+ name="Coworking Space",
58
+ prompt="modern coworking space, open plan office, plants, exposed brick, industrial chic design, natural light, collaborative environment",
59
+ negative_extra="empty, dark, boring",
60
+ category="Professional",
61
+ icon="💼",
62
+ guidance_scale=7.0
63
+ ),
64
+ "conference": SceneTemplate(
65
+ key="conference",
66
+ name="Conference Room",
67
+ prompt="modern conference room, large meeting table, glass walls, professional presentation screen, bright corporate lighting, clean minimal design",
68
+ negative_extra="small, cramped, outdated",
69
+ category="Professional",
70
+ icon="🤝",
71
+ guidance_scale=7.5
72
+ ),
73
 
74
+ # Nature Category
75
+ "beach_sunset": SceneTemplate(
76
+ key="beach_sunset",
77
+ name="Sunset Beach",
78
+ prompt="beautiful tropical beach at golden hour sunset, palm trees silhouette, calm turquoise ocean waves, warm orange and pink sky, soft sand, paradise vacation vibes",
79
+ negative_extra="storm, rain, crowded, trash",
80
+ category="Nature",
81
+ icon="🏖️",
82
+ guidance_scale=7.0
83
+ ),
84
+ "forest_enchanted": SceneTemplate(
85
+ key="forest_enchanted",
86
+ name="Enchanted Forest",
87
+ prompt="magical enchanted forest, sunlight streaming through tall trees, lush green foliage, mystical atmosphere, morning mist, fairy tale woodland",
88
+ negative_extra="dead trees, dark, scary, barren",
89
+ category="Nature",
90
+ icon="🌲",
91
+ guidance_scale=7.0
92
+ ),
93
+ "mountain_scenic": SceneTemplate(
94
+ key="mountain_scenic",
95
+ name="Mountain Vista",
96
+ prompt="breathtaking mountain landscape, snow-capped peaks, alpine meadow, clear blue sky, majestic scenic view, pristine nature, peaceful atmosphere",
97
+ negative_extra="industrial, polluted, crowded",
98
+ category="Nature",
99
+ icon="🏔️",
100
+ guidance_scale=7.5
101
+ ),
102
+ "garden_spring": SceneTemplate(
103
+ key="garden_spring",
104
+ name="Spring Garden",
105
+ prompt="beautiful spring flower garden, colorful blooming flowers, roses and tulips, manicured hedges, sunny day, botanical paradise, fresh and vibrant",
106
+ negative_extra="dead, winter, wilted, dry",
107
+ category="Nature",
108
+ icon="🌸",
109
+ guidance_scale=7.0
110
+ ),
111
+ "lake_serene": SceneTemplate(
112
+ key="lake_serene",
113
+ name="Serene Lake",
114
+ prompt="peaceful serene lake at dawn, mirror-like water reflection, surrounding mountains, soft morning light, tranquil atmosphere, pristine natural beauty",
115
+ negative_extra="stormy, polluted, industrial",
116
+ category="Nature",
117
+ icon="🏞️",
118
+ guidance_scale=7.0
119
+ ),
120
+ "cherry_blossom": SceneTemplate(
121
+ key="cherry_blossom",
122
+ name="Cherry Blossom",
123
+ prompt="stunning cherry blossom trees in full bloom, pink sakura petals falling gently, Japanese garden aesthetic, soft spring sunlight, romantic atmosphere",
124
+ negative_extra="winter, dead, brown, wilted",
125
+ category="Nature",
126
+ icon="🌸",
127
+ guidance_scale=7.0
128
  ),
129
 
130
+ # Urban Category
131
+ "city_skyline": SceneTemplate(
132
+ key="city_skyline",
133
+ name="City Skyline",
134
+ prompt="modern city skyline at blue hour, impressive skyscrapers, glass buildings reflecting sunset, urban metropolitan view, cinematic atmosphere",
135
+ negative_extra="slums, dirty, abandoned, ruins",
136
+ category="Urban",
137
+ icon="🌆",
138
+ guidance_scale=7.5
139
+ ),
140
+ "cafe_cozy": SceneTemplate(
141
+ key="cafe_cozy",
142
+ name="Cozy Cafe",
143
+ prompt="warm cozy coffee shop interior, wooden furniture, ambient lighting, exposed brick walls, plants, comfortable atmosphere, artisan cafe vibes",
144
+ negative_extra="fast food, plastic, harsh lighting",
145
+ category="Urban",
146
+ icon="☕",
147
+ guidance_scale=7.0
148
+ ),
149
+ "street_european": SceneTemplate(
150
+ key="street_european",
151
+ name="European Street",
152
+ prompt="charming European cobblestone street, historic buildings, outdoor cafe, flowers on balconies, warm afternoon light, romantic Paris or Rome vibes",
153
+ negative_extra="modern, industrial, ugly, dirty",
154
+ category="Urban",
155
+ icon="🏛️",
156
+ guidance_scale=7.0
157
+ ),
158
+ "night_neon": SceneTemplate(
159
+ key="night_neon",
160
+ name="Neon Nightlife",
161
+ prompt="vibrant city nightlife scene, neon lights and signs, urban night atmosphere, colorful reflections on wet street, cyberpunk aesthetic, electric energy",
162
+ negative_extra="daytime, boring, plain",
163
+ category="Urban",
164
+ icon="🌃",
165
+ guidance_scale=6.5
166
+ ),
167
+ "rooftop_view": SceneTemplate(
168
+ key="rooftop_view",
169
+ name="Rooftop Terrace",
170
+ prompt="luxury rooftop terrace, city panoramic view, modern outdoor furniture, string lights, sunset golden hour, sophisticated urban oasis",
171
+ negative_extra="cheap, dirty, crowded",
172
+ category="Urban",
173
+ icon="🏙️",
174
+ guidance_scale=7.5
175
  ),
176
 
177
+ # Artistic Category
178
+ "gradient_soft": SceneTemplate(
179
+ key="gradient_soft",
180
+ name="Soft Gradient",
181
+ prompt="smooth soft gradient background, pastel colors blending beautifully, pink to blue to purple transition, dreamy aesthetic, professional portrait backdrop",
182
+ negative_extra="harsh, noisy, textured, busy",
183
+ category="Artistic",
184
+ icon="🎨",
185
+ guidance_scale=8.0
186
+ ),
187
+ "abstract_modern": SceneTemplate(
188
+ key="abstract_modern",
189
+ name="Modern Abstract",
190
+ prompt="modern abstract art background, geometric shapes, bold colors, contemporary design, artistic composition, museum gallery aesthetic",
191
+ negative_extra="realistic, plain, boring",
192
+ category="Artistic",
193
+ icon="🖼️",
194
+ guidance_scale=6.5
195
+ ),
196
+ "vintage_retro": SceneTemplate(
197
+ key="vintage_retro",
198
+ name="Vintage Retro",
199
+ prompt="vintage retro aesthetic background, warm sepia tones, nostalgic 70s vibes, film grain texture, classic photography style, timeless elegance",
200
+ negative_extra="modern, digital, cold, harsh",
201
+ category="Artistic",
202
+ icon="📻",
203
+ guidance_scale=7.0
204
+ ),
205
+ "watercolor_dream": SceneTemplate(
206
+ key="watercolor_dream",
207
+ name="Watercolor Dream",
208
+ prompt="beautiful watercolor painting background, soft flowing colors, artistic brush strokes, dreamy ethereal atmosphere, delicate artistic aesthetic",
209
+ negative_extra="digital, sharp, photorealistic",
210
+ category="Artistic",
211
+ icon="🖌️",
212
+ guidance_scale=6.5
 
 
213
  ),
214
 
215
+ # Seasonal Category
216
+ "autumn_foliage": SceneTemplate(
217
+ key="autumn_foliage",
218
+ name="Autumn Foliage",
219
+ prompt="beautiful autumn scenery, vibrant fall foliage, orange red and golden leaves, maple trees, warm sunlight filtering through, cozy seasonal atmosphere",
220
+ negative_extra="spring, summer, green, snow",
221
+ category="Seasonal",
222
+ icon="🍂",
223
+ guidance_scale=7.0
224
+ ),
225
+ "winter_snow": SceneTemplate(
226
+ key="winter_snow",
227
+ name="Winter Wonderland",
228
+ prompt="magical winter wonderland, fresh white snow covering everything, snow-laden pine trees, soft snowfall, peaceful cold atmosphere, holiday season vibes",
229
+ negative_extra="summer, green, rain, mud",
230
+ category="Seasonal",
231
+ icon="❄️",
232
+ guidance_scale=7.0
233
+ ),
234
+ "summer_tropical": SceneTemplate(
235
+ key="summer_tropical",
236
+ name="Tropical Summer",
237
+ prompt="vibrant tropical summer scene, lush palm trees, bright sunny day, exotic flowers, paradise vacation destination, warm and inviting atmosphere",
238
+ negative_extra="winter, cold, snow, gray",
239
+ category="Seasonal",
240
+ icon="🌴",
241
+ guidance_scale=7.0
242
+ ),
243
+ "spring_meadow": SceneTemplate(
244
+ key="spring_meadow",
245
+ name="Spring Meadow",
246
+ prompt="beautiful spring meadow, wildflowers blooming, fresh green grass, butterflies, soft warm sunlight, renewal and new beginnings, pastoral beauty",
247
+ negative_extra="winter, autumn, dead, dry",
248
+ category="Seasonal",
249
+ icon="🌷",
250
+ guidance_scale=7.0
 
 
251
  ),
252
  }
253
 
 
254
  # Category display order
255
+ CATEGORIES = ["Professional", "Nature", "Urban", "Artistic", "Seasonal"]
256
 
257
  def __init__(self):
258
+ """Initialize the scene template manager"""
259
+ logger.info(f"SceneTemplateManager initialized with {len(self.TEMPLATES)} templates")
 
 
 
 
260
 
261
+ def get_all_templates(self) -> Dict[str, SceneTemplate]:
262
+ """Get all available templates"""
 
 
 
263
  return self.TEMPLATES
264
 
265
+ def get_template(self, key: str) -> Optional[SceneTemplate]:
266
+ """Get a specific template by key"""
 
 
 
 
 
 
 
 
 
 
 
 
267
  return self.TEMPLATES.get(key)
268
 
269
+ def get_templates_by_category(self, category: str) -> List[SceneTemplate]:
270
+ """Get all templates in a specific category"""
 
 
 
 
 
 
 
 
 
 
 
 
271
  return [t for t in self.TEMPLATES.values() if t.category == category]
272
 
273
  def get_categories(self) -> List[str]:
274
+ """Get list of all categories in display order"""
 
 
 
 
 
 
 
275
  return self.CATEGORIES
276
 
277
  def get_template_choices_sorted(self) -> List[str]:
278
  """
279
  Get template choices formatted for Gradio dropdown.
280
+ Returns list of display strings sorted A-Z: "🏢 Modern Office"
 
 
 
 
 
 
 
281
  """
282
  display_list = []
283
+ for key, template in self.TEMPLATES.items():
284
+ display_name = f"{template.icon} {template.name}"
285
+ display_list.append(display_name)
286
 
287
+ # Sort alphabetically by name (ignoring emoji)
288
+ display_list.sort(key=lambda x: x.split(' ', 1)[1] if ' ' in x else x)
 
 
 
 
289
  return display_list
290
 
291
  def get_template_key_from_display(self, display_name: str) -> Optional[str]:
292
  """
293
  Get template key from display name.
294
+ Example: "🏢 Modern Office" -> "office_modern"
 
 
 
 
 
 
 
 
 
295
  """
296
  if not display_name:
297
  return None
 
301
  return key
302
  return None
303
 
304
+ def get_prompt_for_template(self, key: str) -> Optional[str]:
305
+ """Get the prompt string for a template"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  template = self.get_template(key)
307
+ return template.prompt if template else None
308
+
309
+ def get_negative_prompt_for_template(
310
+ self,
311
+ key: str,
312
+ base_negative: str = "blurry, low quality, distorted, people, characters"
313
+ ) -> str:
314
+ """Get combined negative prompt for a template"""
 
 
 
 
 
 
 
 
 
 
 
315
  template = self.get_template(key)
316
+ if template and template.negative_extra:
317
+ return f"{base_negative}, {template.negative_extra}"
318
+ return base_negative
319
 
320
+ def get_guidance_scale_for_template(self, key: str) -> float:
321
+ """Get the recommended guidance scale for a template"""
 
 
 
 
 
 
 
 
 
 
 
 
322
  template = self.get_template(key)
323
+ return template.guidance_scale if template else 7.5
 
 
324
 
325
  def build_gallery_html(self) -> str:
326
  """
327
+ Build HTML for the scene template gallery.
328
+ Returns HTML string for display in Gradio.
 
 
 
 
329
  """
330
+ html_parts = ['<div class="scene-gallery">']
331
 
332
  for category in self.CATEGORIES:
333
  templates = self.get_templates_by_category(category)
 
335
  continue
336
 
337
  html_parts.append(f'''
338
+ <div class="scene-category">
339
+ <h4 class="scene-category-title">{category}</h4>
340
+ <div class="scene-grid">
341
  ''')
342
 
343
+ for template in templates:
344
  html_parts.append(f'''
345
+ <button class="scene-card" data-template="{template.key}" onclick="selectTemplate('{template.key}')">
346
+ <span class="scene-icon">{template.icon}</span>
347
+ <span class="scene-name">{template.name}</span>
348
+ </button>
 
349
  ''')
350
 
351
  html_parts.append('</div></div>')
352
 
353
  html_parts.append('</div>')
354
+ return ''.join(html_parts)
355
+
356
+ def get_gallery_css(self) -> str:
357
+ """Get CSS styles for the scene gallery"""
358
+ return """
359
+ /* Scene Gallery Styles */
360
+ .scene-gallery {
361
+ margin: 16px 0;
362
+ }
363
+
364
+ .scene-category {
365
+ margin-bottom: 20px;
366
+ }
367
+
368
+ .scene-category-title {
369
+ font-size: 0.9rem;
370
+ font-weight: 600;
371
+ color: #475569;
372
+ margin-bottom: 12px;
373
+ padding-bottom: 8px;
374
+ border-bottom: 1px solid #e2e8f0;
375
+ }
376
+
377
+ .scene-grid {
378
+ display: grid;
379
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
380
+ gap: 8px;
381
+ }
382
+
383
+ .scene-card {
384
+ display: flex;
385
+ flex-direction: column;
386
+ align-items: center;
387
+ justify-content: center;
388
+ padding: 12px 8px;
389
+ background: #f8fafc;
390
+ border: 1px solid #e2e8f0;
391
+ border-radius: 8px;
392
+ cursor: pointer;
393
+ transition: all 0.2s ease;
394
+ min-height: 70px;
395
+ }
396
+
397
+ .scene-card:hover {
398
+ background: #dbeafe;
399
+ border-color: #3b82f6;
400
+ transform: translateY(-2px);
401
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
402
+ }
403
+
404
+ .scene-card.selected {
405
+ background: #dbeafe;
406
+ border-color: #3b82f6;
407
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
408
+ }
409
+
410
+ .scene-icon {
411
+ font-size: 1.5rem;
412
+ margin-bottom: 4px;
413
+ }
414
+
415
+ .scene-name {
416
+ font-size: 0.75rem;
417
+ font-weight: 500;
418
+ color: #1e293b;
419
+ text-align: center;
420
+ line-height: 1.2;
421
+ }
422
+
423
+ @media (max-width: 768px) {
424
+ .scene-grid {
425
+ grid-template-columns: repeat(3, 1fr);
426
+ }
427
+ }
428
+ """