File size: 12,121 Bytes
5b6e956
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# composition_service.py

## Purpose
Business logic for smart multi-image composition. Builds intelligent prompts based on image types, camera angles, shot types, and lighting conditions. Implements Google's best practices for Gemini 2.5 Flash Image multi-image composition.

## Responsibilities
- Build intelligent composition prompts from user selections
- Support multiple image types (Subject, Background, Style, etc.)
- Apply camera angle and lighting best practices
- Generate compositions with consistent perspective
- Suggest appropriate aspect ratios for composition types
- Validate composition inputs
- Inherit generation capabilities from GenerationService

## Dependencies
- `services.generation_service.GenerationService` - Base class (inherits generation methods)
- `models.generation_request.GenerationRequest` - Request dataclass
- `models.generation_result.GenerationResult` - Result dataclass
- `utils.logging_utils` - Logging
- `config.settings.Settings` - Configuration
- `PIL.Image` - Image handling

## Source
Extracted from `composition_assistant_addon.py` (Gradio implementation). Refactored to use new architecture and add generation capabilities.

## Public Interface

### `CompositionService(GenerationService)` class

**Inheritance:**
- Extends `GenerationService` to reuse generation methods
- Adds composition-specific prompt building

**Class Constants:**
```python
IMAGE_TYPES = [
    "Subject/Character",
    "Background/Environment",
    "Style Reference",
    "Product",
    "Texture",
    "Not Used"
]

SHOT_TYPES = [
    "close-up shot",
    "medium shot",
    "full body shot",
    "wide shot",
    "extreme close-up",
    "establishing shot"
]

CAMERA_ANGLES = [
    "eye-level perspective",
    "low-angle perspective",
    "high-angle perspective",
    "bird's-eye view",
    "Dutch angle (tilted)",
    "over-the-shoulder"
]

LIGHTING_OPTIONS = [
    "Auto (match images)",
    "natural daylight",
    "golden hour sunlight",
    "soft diffused light",
    "dramatic side lighting",
    "backlit silhouette",
    "studio lighting",
    "moody atmospheric lighting",
    "neon/artificial lighting"
]
```

**Constructor:**
```python
def __init__(self, api_key: Optional[str] = None)
```
- `api_key`: Optional Gemini API key
- Initializes parent GenerationService

### Key Methods

#### `build_composition_prompt(image1_type="Subject/Character", image2_type="Background/Environment", image3_type="Not Used", camera_angles=None, lighting="Auto (match images)", shot_type="medium shot", custom_instructions="", is_character_sheet=False) -> str`

Build intelligent composition prompt based on selections.

**Based on Google's Best Practices:**
- Narrative, descriptive language
- Camera angles, lens types, lighting
- Match perspectives and light direction
- Specific about placement

**Args:**
- `image1_type`: Type of first image (default: "Subject/Character")
- `image2_type`: Type of second image (default: "Background/Environment")
- `image3_type`: Type of third image (default: "Not Used")
- `camera_angles`: List of selected camera angles (optional)
- `lighting`: Lighting description (default: "Auto (match images)")
- `shot_type`: Type of shot (default: "medium shot")
- `custom_instructions`: Additional instructions (default: "")
- `is_character_sheet`: Character sheet mode (default: False)

**Returns:**
- Formatted prompt string

**Usage:**
```python
service = CompositionService()

# Subject into background
prompt = service.build_composition_prompt(
    image1_type="Subject/Character",
    image2_type="Background/Environment",
    camera_angles=["eye-level perspective"],
    lighting="natural daylight",
    shot_type="full body shot",
    custom_instructions="Character is walking forward"
)

# Style transfer
prompt = service.build_composition_prompt(
    image1_type="Subject/Character",
    image2_type="Style Reference",
    shot_type="medium shot",
    custom_instructions="Apply watercolor painting style"
)

# Character sheet
prompt = service.build_composition_prompt(
    image1_type="Subject/Character",
    image2_type="Style Reference",
    is_character_sheet=True
)
```

#### `compose_images(images, image_types, camera_angles=None, lighting="Auto (match images)", shot_type="medium shot", custom_instructions="", is_character_sheet=False, aspect_ratio="16:9", temperature=0.7, backend=Settings.BACKEND_GEMINI) -> GenerationResult`

Complete composition workflow: build prompt + generate.

**Args:**
- `images`: List of up to 3 images (None for unused slots)
- `image_types`: List of image types corresponding to images
- `camera_angles`: Selected camera angles (optional)
- `lighting`: Lighting option (default: "Auto (match images)")
- `shot_type`: Shot type (default: "medium shot")
- `custom_instructions`: Custom instructions (default: "")
- `is_character_sheet`: Character sheet mode (default: False)
- `aspect_ratio`: Output aspect ratio (default: "16:9")
- `temperature`: Generation temperature (default: 0.7)
- `backend`: Backend to use (default: Gemini)

**Returns:**
- `GenerationResult` object

**Usage:**
```python
service = CompositionService(api_key="your-key")

# Load images
subject = Image.open("character.png")
background = Image.open("forest.png")

# Compose
result = service.compose_images(
    images=[subject, background, None],
    image_types=["Subject/Character", "Background/Environment", "Not Used"],
    camera_angles=["eye-level perspective", "low-angle perspective"],
    lighting="soft diffused light",
    shot_type="full body shot",
    custom_instructions="Character is exploring the forest",
    aspect_ratio="16:9",
    temperature=0.7,
    backend="Gemini API (Cloud)"
)

if result.success:
    result.image.show()
    print(f"Generated in {result.generation_time:.1f}s")
else:
    print(f"Error: {result.message}")
```

#### `get_suggested_aspect_ratio(shot_type, is_character_sheet=False) -> str`

Suggest aspect ratio based on composition type.

**Logic:**
- Character sheet → "16:9" (wide for multi-view)
- Full body/wide shots → "16:9" (landscape)
- Close-ups → "3:4" (portrait)
- Balanced compositions → "1:1" (square)

**Args:**
- `shot_type`: Shot type
- `is_character_sheet`: Character sheet mode (default: False)

**Returns:**
- Suggested aspect ratio string

**Usage:**
```python
ratio = service.get_suggested_aspect_ratio(
    shot_type="full body shot",
    is_character_sheet=False
)  # Returns "16:9"

ratio = service.get_suggested_aspect_ratio(
    shot_type="close-up shot"
)  # Returns "3:4"
```

#### `validate_composition_inputs(images, image_types) -> tuple[bool, Optional[str]]`

Validate composition inputs.

**Checks:**
- At least one image provided
- Image types length matches images
- Valid image types

**Args:**
- `images`: List of images
- `image_types`: List of image types

**Returns:**
- Tuple of `(is_valid: bool, error_message: Optional[str])`

**Usage:**
```python
is_valid, error = service.validate_composition_inputs(
    images=[subject, background, None],
    image_types=["Subject/Character", "Background/Environment", "Not Used"]
)
if not is_valid:
    st.error(f"Invalid input: {error}")
```

## Prompt Building Logic

### Subject + Background
```python
image1_type="Subject/Character"
image2_type="Background/Environment"

# Generates:
"A photorealistic full body shot placing the subject from image one
into the environment from image two. Shot from a eye-level perspective.
The scene is illuminated by natural daylight, matching the lighting
direction and quality across all elements. Maintain consistent perspective,
scale, and depth. Create a natural, seamless composition with realistic
shadows and reflections. Photorealistic, high quality, professional photography."
```

### Style Transfer
```python
image1_type="Subject/Character"
image2_type="Style Reference"

# Generates:
"Transform the subject from image one into the artistic style shown
in image two. Maintain consistent perspective, scale, and depth.
Create a natural, seamless composition with realistic shadows and
reflections. Photorealistic, high quality, professional photography."
```

### Character Sheet
```python
image1_type="Subject/Character"
is_character_sheet=True

# Generates:
"Create a character sheet design with multiple views and poses of
the same character. Based on the character from image one, Include
front view, side view, back view, and detail shots. Maintain consistent
character design, colors, and proportions across all views. Create a
natural, seamless composition with realistic shadows and reflections.
Photorealistic, high quality, professional photography."
```

## Best Practices Applied

Based on Google's Gemini 2.5 Flash Image documentation:

1. **Narrative Language**: Use descriptive, story-like prompts
2. **Camera Specifics**: Include angle, perspective, lens type
3. **Lighting Details**: Specify lighting type and direction
4. **Perspective Matching**: Explicitly request consistent perspective
5. **Realism Keywords**: Include "photorealistic", "professional photography"
6. **Element Ordering**: Images before text in API calls

## Usage Examples

### Example 1: Character in Environment
```python
service = CompositionService(api_key="your-key")

character = Image.open("hero.png")
environment = Image.open("castle.png")

result = service.compose_images(
    images=[character, environment],
    image_types=["Subject/Character", "Background/Environment"],
    camera_angles=["low-angle perspective"],
    lighting="dramatic side lighting",
    shot_type="full body shot",
    custom_instructions="Hero standing heroically in front of castle",
    aspect_ratio="16:9"
)
```

### Example 2: Product with Texture
```python
product = Image.open("watch.png")
texture = Image.open("marble.png")

result = service.compose_images(
    images=[product, texture],
    image_types=["Product", "Texture"],
    camera_angles=["high-angle perspective"],
    lighting="studio lighting",
    shot_type="close-up shot",
    aspect_ratio="1:1"
)
```

### Example 3: Three-Image Composition
```python
character = Image.open("character.png")
background = Image.open("background.png")
style_ref = Image.open("style.png")

result = service.compose_images(
    images=[character, background, style_ref],
    image_types=["Subject/Character", "Background/Environment", "Style Reference"],
    camera_angles=["eye-level perspective"],
    lighting="natural daylight",
    shot_type="medium shot",
    custom_instructions="Apply style from third image to the composition",
    aspect_ratio="16:9"
)
```

### Example 4: Auto Aspect Ratio
```python
# Get suggested aspect ratio
aspect_ratio = service.get_suggested_aspect_ratio(
    shot_type="full body shot"
)

result = service.compose_images(
    images=[character, background],
    image_types=["Subject/Character", "Background/Environment"],
    shot_type="full body shot",
    aspect_ratio=aspect_ratio  # Uses "16:9"
)
```

## Error Handling

All methods return consistent error format:
```python
try:
    result = service.compose_images(...)
    if not result.success:
        print(f"Composition failed: {result.message}")
except Exception as e:
    logger.exception(f"Composition error: {e}")
    return GenerationResult.error_result(f"Composition error: {str(e)}")
```

## Inheritance from GenerationService

Reuses these methods:
- `router.generate()` - Backend generation
- `check_backend_availability()` - Backend health
- `get_all_backend_status()` - All backend status
- All BackendRouter functionality

Adds:
- `build_composition_prompt()` - Intelligent prompt building
- `compose_images()` - Complete composition workflow
- `get_suggested_aspect_ratio()` - Aspect ratio suggestions
- `validate_composition_inputs()` - Input validation

## Related Files
- `services/generation_service.py` - Parent class
- `composition_assistant_addon.py` (old) - Original Gradio implementation source
- `core/backend_router.py` - Backend routing
- `models/generation_request.py` - Request structure
- `models/generation_result.py` - Result structure
- `ui/pages/02_🎬_Composition_Assistant.py` - UI that uses this service