Malaji71 commited on
Commit
61f8038
·
verified ·
1 Parent(s): 6803d3e

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +137 -343
utils.py CHANGED
@@ -130,45 +130,44 @@ def detect_scene_type_from_analysis(analysis_metadata: Dict[str, Any]) -> str:
130
 
131
  def apply_flux_rules(prompt: str, analysis_metadata: Optional[Dict[str, Any]] = None) -> str:
132
  """
133
- Apply enhanced prompt optimization with cinematography knowledge and intelligent token economy
 
134
 
135
  Args:
136
- prompt: Raw prompt text from BAGEL analysis
137
  analysis_metadata: Enhanced metadata with cinematography suggestions
138
 
139
  Returns:
140
- Optimized prompt with professional cinematography terms and efficient token usage
141
  """
142
  if not prompt or not isinstance(prompt, str):
143
  return ""
144
 
145
  try:
146
- # Step 1: Extract and clean the core description
147
- core_description = _extract_clean_description(prompt)
148
- if not core_description:
149
- return "Professional photograph with technical excellence"
150
 
151
- # Step 2: Get camera configuration
152
- camera_setup = _get_camera_setup(analysis_metadata, core_description)
153
 
154
- # Step 3: Get essential style keywords
155
- style_keywords = _get_essential_keywords(core_description, camera_setup, analysis_metadata)
156
 
157
- # Step 4: Build final optimized prompt
158
- final_prompt = _build_optimized_prompt(core_description, camera_setup, style_keywords)
159
-
160
- logger.info(f"Prompt optimized: {len(prompt)} → {len(final_prompt)} chars")
161
- return final_prompt
162
 
163
  except Exception as e:
164
- logger.error(f"Prompt optimization failed: {e}")
165
- return _create_fallback_prompt(prompt)
166
 
167
 
168
- def _extract_clean_description(prompt: str) -> str:
169
- """Extract and clean the core description from BAGEL output"""
 
 
 
170
  try:
171
- # Remove CAMERA_SETUP section
172
  if "CAMERA_SETUP:" in prompt:
173
  description = prompt.split("CAMERA_SETUP:")[0].strip()
174
  elif "2. CAMERA_SETUP" in prompt:
@@ -176,342 +175,129 @@ def _extract_clean_description(prompt: str) -> str:
176
  else:
177
  description = prompt
178
 
179
- # Remove section headers
180
  description = re.sub(r'^(DESCRIPTION:|1\.\s*DESCRIPTION:)\s*', '', description, flags=re.IGNORECASE)
181
 
182
- # Remove verbose introduction phrases
183
- remove_patterns = [
184
- r'^This image (?:features|shows|depicts|presents|captures)',
185
- r'^The image (?:features|shows|depicts|presents|captures)',
186
- r'^This (?:photograph|picture|scene) (?:features|shows|depicts)',
187
- r'^(?:In this image,?|Looking at this image,?)',
188
- r'(?:possibly|apparently|seemingly|appears to be|seems to be)',
189
- ]
190
-
191
- for pattern in remove_patterns:
192
- description = re.sub(pattern, '', description, flags=re.IGNORECASE)
193
-
194
- # Convert to concise, direct language
195
- description = _convert_to_direct_language(description)
196
-
197
- # Clean up formatting
198
- description = re.sub(r'\s+', ' ', description).strip()
199
-
200
- # Limit length for efficiency
201
- if len(description) > 200:
202
- sentences = re.split(r'[.!?]', description)
203
- description = sentences[0] if sentences else description[:200]
204
-
205
- return description.strip()
206
 
207
  except Exception as e:
208
- logger.warning(f"Description extraction failed: {e}")
209
- return prompt[:100] if prompt else ""
210
 
211
 
212
- def _convert_to_direct_language(text: str) -> str:
213
- """Convert verbose descriptive text to direct, concise language"""
 
 
214
  try:
215
- # Direct conversions for common verbose phrases
216
- conversions = [
217
- # Subject identification
218
- (r'a (?:person|individual|figure|man|woman) (?:who is|that is)', r'person'),
219
- (r' (?:who is|that is) (?:wearing|dressed in)', r' wearing'),
220
- (r' (?:who appears to be|that appears to be)', r''),
221
-
222
- # Location simplification
223
- (r'(?:what appears to be|what seems to be) (?:a|an)', r''),
224
- (r'in (?:what looks like|what appears to be) (?:a|an)', r'in'),
225
- (r'(?:standing|sitting|positioned) in (?:the middle of|the center of)', r'in'),
226
-
227
- # Action simplification
228
- (r'(?:is|are) (?:currently|presently) (?:engaged in|performing)', r''),
229
- (r'(?:can be seen|is visible|are visible)', r''),
230
-
231
- # Background simplification
232
- (r'(?:In the background|Behind (?:him|her|them)),? (?:there (?:is|are)|we can see)', r'Background:'),
233
- (r'The background (?:features|shows|contains)', r'Background:'),
234
-
235
- # Remove filler words
236
- (r'\b(?:quite|rather|somewhat|fairly|very|extremely)\b', r''),
237
- (r'\b(?:overall|generally|typically|usually)\b', r''),
238
- ]
239
-
240
- result = text
241
- for pattern, replacement in conversions:
242
- result = re.sub(pattern, replacement, result, flags=re.IGNORECASE)
243
-
244
- # Clean up extra spaces and punctuation
245
- result = re.sub(r'\s+', ' ', result)
246
- result = re.sub(r'\s*,\s*,+', ',', result)
247
- result = re.sub(r'^\s*,\s*', '', result)
248
-
249
- return result.strip()
250
 
251
- except Exception as e:
252
- logger.warning(f"Language conversion failed: {e}")
253
- return text
254
-
255
-
256
- def _get_camera_setup(analysis_metadata: Optional[Dict[str, Any]], description: str) -> str:
257
- """Get camera setup configuration"""
258
- try:
259
- # Check if BAGEL provided camera setup
260
- if analysis_metadata and analysis_metadata.get("has_camera_suggestion"):
 
 
 
 
 
 
 
 
 
261
  camera_setup = analysis_metadata.get("camera_setup", "")
262
- if camera_setup and len(camera_setup) > 10:
263
- return _format_camera_setup(camera_setup)
264
 
265
- # Detect scene type and provide appropriate camera setup
266
- scene_type = _detect_scene_from_content(description)
267
- return _get_scene_camera_setup(scene_type)
 
 
 
268
 
269
  except Exception as e:
270
- logger.warning(f"Camera setup detection failed: {e}")
271
- return "shot on professional camera"
272
 
273
 
274
  def _format_camera_setup(raw_setup: str) -> str:
275
- """Format camera setup into clean, concise format"""
276
- try:
277
- # Extract camera model
278
- camera_patterns = [
279
- r'(Canon EOS R\d+)',
280
- r'(Sony A\d+[^\s,]*)',
281
- r'(Leica [^\s,]+)',
282
- r'(Phase One [^\s,]+)',
283
- r'(Hasselblad [^\s,]+)',
284
- r'(ARRI [^\s,]+)',
285
- r'(RED [^\s,]+)'
286
- ]
287
-
288
- camera = None
289
- for pattern in camera_patterns:
290
- match = re.search(pattern, raw_setup, re.IGNORECASE)
291
- if match:
292
- camera = match.group(1)
293
- break
294
-
295
- # Extract lens info
296
- lens_pattern = r'(\d+mm[^,]*f/[\d.]+[^,]*)'
297
- lens_match = re.search(lens_pattern, raw_setup, re.IGNORECASE)
298
- lens = lens_match.group(1) if lens_match else None
299
-
300
- # Extract ISO
301
- iso_pattern = r'(ISO \d+)'
302
- iso_match = re.search(iso_pattern, raw_setup, re.IGNORECASE)
303
- iso = iso_match.group(1) if iso_match else None
304
-
305
- # Build clean setup
306
- parts = []
307
- if camera:
308
- parts.append(camera)
309
- if lens:
310
- parts.append(lens)
311
- if iso:
312
- parts.append(iso)
313
-
314
- if parts:
315
- return f"shot on {', '.join(parts)}"
316
- else:
317
- return "professional photography"
318
-
319
- except Exception as e:
320
- logger.warning(f"Camera setup formatting failed: {e}")
321
- return "professional photography"
322
-
323
-
324
- def _detect_scene_from_content(description: str) -> str:
325
- """Detect scene type from description content"""
326
- description_lower = description.lower()
327
-
328
- # Scene detection patterns
329
- if any(term in description_lower for term in ["portrait", "person", "man", "woman", "face"]):
330
- return "portrait"
331
- elif any(term in description_lower for term in ["landscape", "mountain", "horizon", "nature", "outdoor"]):
332
- return "landscape"
333
- elif any(term in description_lower for term in ["street", "urban", "city", "building", "crowd"]):
334
- return "street"
335
- elif any(term in description_lower for term in ["architecture", "building", "structure", "interior"]):
336
- return "architecture"
337
- else:
338
- return "general"
339
-
340
-
341
- def _get_scene_camera_setup(scene_type: str) -> str:
342
- """Get camera setup based on scene type"""
343
- setups = {
344
- "portrait": "shot on Canon EOS R5, 85mm f/1.4 lens, ISO 200",
345
- "landscape": "shot on Phase One XT, 24-70mm f/4 lens, ISO 100",
346
- "street": "shot on Leica M11, 35mm f/1.4 lens, ISO 800",
347
- "architecture": "shot on Canon EOS R5, 24-70mm f/2.8 lens, ISO 100",
348
- "general": "shot on Canon EOS R6, 50mm f/1.8 lens, ISO 400"
349
- }
350
-
351
- return setups.get(scene_type, setups["general"])
352
-
353
-
354
- def _get_essential_keywords(description: str, camera_setup: str, analysis_metadata: Optional[Dict[str, Any]]) -> List[str]:
355
- """Get essential lighting and composition keywords without redundancy"""
356
  try:
357
- keywords = []
358
- description_lower = description.lower()
359
-
360
- # Detect and add lighting information
361
- lighting = _detect_lighting_from_description(description_lower)
362
- if lighting:
363
- keywords.append(lighting)
364
-
365
- # Detect and add composition technique
366
- composition = _detect_composition_from_description(description_lower, camera_setup)
367
- if composition:
368
- keywords.append(composition)
369
 
370
- # Only add depth of field if not already mentioned and relevant
371
- if "depth" not in description_lower and "bokeh" not in description_lower:
372
- if any(term in camera_setup for term in ["f/1.4", "f/2.8", "85mm"]):
373
- keywords.append("shallow depth of field")
374
 
375
- # Add professional photography only if no specific camera mentioned (fallback)
376
- if "shot on" not in camera_setup and len(keywords) == 0:
377
- keywords.append("professional photography")
378
 
379
- return keywords[:3] # Limit to 3 essential keywords
380
-
381
- except Exception as e:
382
- logger.warning(f"Keyword extraction failed: {e}")
383
- return ["natural lighting"]
384
-
385
-
386
- def _detect_lighting_from_description(description_lower: str) -> Optional[str]:
387
- """Detect lighting type from description"""
388
- try:
389
- # Check for existing lighting mentions
390
- if any(term in description_lower for term in ["lighting", "light", "lit", "illuminated"]):
391
- return None # Already has lighting info
392
-
393
- # Detect lighting conditions from scene context
394
- if any(term in description_lower for term in ["sunset", "sunrise", "golden", "warm"]):
395
- return "golden hour lighting"
396
- elif any(term in description_lower for term in ["twilight", "dusk", "evening", "blue hour"]):
397
- return "blue hour lighting"
398
- elif any(term in description_lower for term in ["overcast", "cloudy", "soft", "diffused"]):
399
- return "soft natural lighting"
400
- elif any(term in description_lower for term in ["bright", "sunny", "daylight", "outdoor"]):
401
- return "natural daylight"
402
- elif any(term in description_lower for term in ["indoor", "interior", "inside"]):
403
- return "ambient lighting"
404
- elif any(term in description_lower for term in ["studio", "controlled", "professional"]):
405
- return "studio lighting"
406
- elif any(term in description_lower for term in ["dramatic", "moody", "shadow"]):
407
- return "dramatic lighting"
408
- else:
409
- # Default based on scene type
410
- if any(term in description_lower for term in ["portrait", "person", "face"]):
411
- return "natural lighting"
412
- elif any(term in description_lower for term in ["landscape", "outdoor", "nature"]):
413
- return "natural daylight"
414
- else:
415
- return "natural lighting"
416
-
417
- except Exception as e:
418
- logger.warning(f"Lighting detection failed: {e}")
419
- return "natural lighting"
420
-
421
-
422
- def _detect_composition_from_description(description_lower: str, camera_setup: str) -> Optional[str]:
423
- """Detect composition technique from description and camera setup"""
424
- try:
425
- # Check for existing composition mentions
426
- if any(term in description_lower for term in ["composition", "framing", "rule of thirds"]):
427
- return None # Already has composition info
428
-
429
- # Detect composition from perspective/angle
430
- if any(term in description_lower for term in ["elevated", "above", "overhead", "aerial"]):
431
- return "elevated perspective"
432
- elif any(term in description_lower for term in ["low angle", "looking up", "from below"]):
433
- return "low angle composition"
434
- elif any(term in description_lower for term in ["close-up", "tight", "detailed"]):
435
- return "close-up framing"
436
- elif any(term in description_lower for term in ["wide", "expansive", "panoramic"]):
437
- return "wide composition"
438
-
439
- # Detect composition from subject arrangement
440
- elif any(term in description_lower for term in ["centered", "center", "middle"]):
441
- return "centered composition"
442
- elif any(term in description_lower for term in ["symmetr", "balanced", "mirror"]):
443
- return "symmetrical composition"
444
- elif any(term in description_lower for term in ["leading", "lines", "path", "diagonal"]):
445
- return "leading lines"
446
-
447
- # Default composition based on camera setup
448
- elif any(term in camera_setup for term in ["85mm", "135mm", "f/1.4", "f/2.8"]):
449
- return "rule of thirds" # Portrait/shallow DOF typically uses rule of thirds
450
- elif any(term in camera_setup for term in ["24mm", "35mm", "wide"]):
451
- return "dynamic composition" # Wide angle allows for dynamic compositions
452
- else:
453
- return "rule of thirds" # Universal fallback
454
-
455
  except Exception as e:
456
- logger.warning(f"Composition detection failed: {e}")
457
- return "rule of thirds"
458
 
459
 
460
- def _build_optimized_prompt(description: str, camera_setup: str, keywords: List[str]) -> str:
461
- """Build final optimized prompt with proper structure"""
 
 
 
462
  try:
463
- # Structure: Description + Technical + Style
464
  parts = []
465
 
466
- # Core description (clean and concise)
467
  if description:
468
  parts.append(description)
469
 
470
- # Technical setup
471
  if camera_setup:
472
  parts.append(camera_setup)
473
 
474
- # Essential keywords
475
- if keywords:
476
- parts.extend(keywords)
477
-
478
- # Join with consistent separator
479
  result = ", ".join(parts)
480
 
481
- # Final cleanup
482
  result = re.sub(r'\s*,\s*,+', ',', result) # Remove double commas
483
- result = re.sub(r'\s+', ' ', result) # Clean spaces
484
- result = result.strip().rstrip(',') # Remove trailing comma
485
 
486
- # Ensure it starts with capital letter
487
  if result:
488
  result = result[0].upper() + result[1:] if len(result) > 1 else result.upper()
489
 
490
  return result
491
 
492
  except Exception as e:
493
- logger.error(f"Prompt building failed: {e}")
494
- return "Professional photograph"
495
-
496
-
497
- def _create_fallback_prompt(original_prompt: str) -> str:
498
- """Create fallback prompt when optimization fails"""
499
- try:
500
- # Extract first meaningful sentence
501
- sentences = re.split(r'[.!?]', original_prompt)
502
- if sentences:
503
- clean_sentence = sentences[0].strip()
504
- # Remove verbose starters
505
- clean_sentence = re.sub(r'^(This image shows|The image depicts|This photograph)', '', clean_sentence, flags=re.IGNORECASE)
506
- clean_sentence = clean_sentence.strip()
507
-
508
- if len(clean_sentence) > 20:
509
- return f"{clean_sentence}, professional photography"
510
-
511
- return "Professional photograph with technical excellence"
512
-
513
- except Exception:
514
- return "Professional photograph"
515
 
516
 
517
  def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]] = None) -> Tuple[int, Dict[str, int]]:
@@ -531,8 +317,8 @@ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]]
531
  breakdown = {}
532
 
533
  # Enhanced Prompt Quality (0-25 points)
534
- length_score = min(15, len(prompt) // 10) # Reward appropriate length
535
- detail_score = min(10, len(prompt.split(',')) * 2) # Reward structured detail
536
  breakdown["prompt_quality"] = int(length_score + detail_score)
537
 
538
  # Technical Details with Cinematography Focus (0-25 points)
@@ -553,22 +339,30 @@ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]]
553
  if re.search(r'ISO \d+', prompt):
554
  tech_score += 4
555
 
556
- # Professional terminology
557
- tech_keywords = ['shot on', 'lens', 'depth of field', 'bokeh']
558
- tech_score += sum(3 for keyword in tech_keywords if keyword in prompt.lower())
559
 
560
  breakdown["technical_details"] = min(25, tech_score)
561
 
562
- # Professional Cinematography (0-25 points)
563
  cinema_score = 0
564
 
565
- # Professional lighting techniques
566
- lighting_terms = ['professional lighting', 'studio lighting', 'natural lighting']
567
- cinema_score += sum(4 for term in lighting_terms if term in prompt.lower())
 
 
 
 
 
 
 
 
568
 
569
- # Composition techniques
570
- composition_terms = ['composition', 'depth of field', 'bokeh', 'shallow depth']
571
- cinema_score += sum(3 for term in composition_terms if term in prompt.lower())
572
 
573
  # Professional context bonus
574
  if analysis_data and analysis_data.get("has_camera_suggestion"):
@@ -579,23 +373,23 @@ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]]
579
  # Multi-Engine Optimization (0-25 points)
580
  optimization_score = 0
581
 
582
- # Check for technical specifications
583
- if re.search(r'(?:Canon|Sony|Leica|Phase One)', prompt):
584
  optimization_score += 10
585
 
586
  # Complete technical specs
587
- if re.search(r'\d+mm.*f/[\d.]+.*ISO \d+', prompt):
588
  optimization_score += 8
589
 
590
- # Professional terminology
591
- pro_terms = ['professional', 'shot on', 'high quality']
592
- optimization_score += sum(2 for term in pro_terms if term in prompt.lower())
593
 
594
- # Length efficiency bonus (reward conciseness)
595
  word_count = len(prompt.split())
596
- if 30 <= word_count <= 60: # Optimal range
597
  optimization_score += 5
598
- elif word_count <= 30:
599
  optimization_score += 3
600
 
601
  breakdown["multi_engine_optimization"] = min(25, optimization_score)
@@ -664,12 +458,12 @@ def format_analysis_report(analysis_data: Dict[str, Any], processing_time: float
664
  **Professional Context:** {'✅ Applied' if has_cinema_context else '❌ Not Applied'}
665
 
666
  **🎯 OPTIMIZATIONS APPLIED:**
667
- Clean description extraction
668
- Professional camera configuration
669
- Essential keyword optimization
670
- Token economy optimization
671
- Multi-engine compatibility
672
- Redundancy elimination
673
 
674
  **⚡ Powered by Pariente AI for MIA TV Series**"""
675
 
 
130
 
131
  def apply_flux_rules(prompt: str, analysis_metadata: Optional[Dict[str, Any]] = None) -> str:
132
  """
133
+ Apply enhanced prompt optimization - FORMAT ONLY, do not filter content
134
+ Let professional_photography.py do ALL the cinematographic work
135
 
136
  Args:
137
+ prompt: Raw prompt text from BAGEL analysis (already enriched by professional_photography.py)
138
  analysis_metadata: Enhanced metadata with cinematography suggestions
139
 
140
  Returns:
141
+ Clean formatted prompt preserving ALL professional cinematography content
142
  """
143
  if not prompt or not isinstance(prompt, str):
144
  return ""
145
 
146
  try:
147
+ # Step 1: Extract the rich professional description (preserve ALL content)
148
+ description = _extract_professional_description(prompt)
 
 
149
 
150
+ # Step 2: Extract camera setup if provided by BAGEL
151
+ camera_setup = _extract_camera_setup(prompt, analysis_metadata)
152
 
153
+ # Step 3: Format into clean structure (NO filtering)
154
+ formatted_prompt = _format_professional_prompt(description, camera_setup)
155
 
156
+ logger.info(f"Professional prompt formatted: {len(prompt)} {len(formatted_prompt)} chars")
157
+ return formatted_prompt
 
 
 
158
 
159
  except Exception as e:
160
+ logger.error(f"Professional prompt formatting failed: {e}")
161
+ return prompt # Return original if formatting fails
162
 
163
 
164
+ def _extract_professional_description(prompt: str) -> str:
165
+ """
166
+ Extract the professional description - preserve ALL cinematographic content
167
+ Only clean formatting, DO NOT filter content
168
+ """
169
  try:
170
+ # Split sections if present
171
  if "CAMERA_SETUP:" in prompt:
172
  description = prompt.split("CAMERA_SETUP:")[0].strip()
173
  elif "2. CAMERA_SETUP" in prompt:
 
175
  else:
176
  description = prompt
177
 
178
+ # Remove only section headers, preserve ALL content
179
  description = re.sub(r'^(DESCRIPTION:|1\.\s*DESCRIPTION:)\s*', '', description, flags=re.IGNORECASE)
180
 
181
+ # Clean up only formatting issues, preserve ALL professional terminology
182
+ # Remove only redundant whitespace
183
+ description = re.sub(r'\s+', ' ', description)
184
+ description = description.strip()
185
+
186
+ # If description is too long, preserve the most important parts
187
+ # But DO NOT remove cinematographic terms or professional language
188
+ if len(description) > 300:
189
+ # Only truncate at sentence boundaries to preserve meaning
190
+ sentences = re.split(r'[.!?]+', description)
191
+ truncated = ""
192
+ for sentence in sentences:
193
+ if len(truncated + sentence) < 280:
194
+ truncated += sentence + ". "
195
+ else:
196
+ break
197
+ if truncated:
198
+ description = truncated.strip()
199
+
200
+ return description
 
 
 
 
201
 
202
  except Exception as e:
203
+ logger.warning(f"Professional description extraction failed: {e}")
204
+ return prompt
205
 
206
 
207
+ def _extract_camera_setup(prompt: str, analysis_metadata: Optional[Dict[str, Any]]) -> str:
208
+ """
209
+ Extract camera setup from BAGEL output or metadata
210
+ """
211
  try:
212
+ # First check if BAGEL provided camera setup in the prompt
213
+ camera_setup = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ if "CAMERA_SETUP:" in prompt:
216
+ camera_section = prompt.split("CAMERA_SETUP:")[1].strip()
217
+ # Take first substantial line
218
+ lines = camera_section.split('\n')
219
+ for line in lines:
220
+ if len(line.strip()) > 20:
221
+ camera_setup = line.strip()
222
+ break
223
+
224
+ elif "2. CAMERA_SETUP" in prompt:
225
+ camera_section = prompt.split("2. CAMERA_SETUP")[1].strip()
226
+ lines = camera_section.split('\n')
227
+ for line in lines:
228
+ if len(line.strip()) > 20:
229
+ camera_setup = line.strip()
230
+ break
231
+
232
+ # If no setup in prompt, check metadata
233
+ if not camera_setup and analysis_metadata:
234
  camera_setup = analysis_metadata.get("camera_setup", "")
 
 
235
 
236
+ # Format camera setup if found
237
+ if camera_setup:
238
+ return _format_camera_setup(camera_setup)
239
+
240
+ # Return empty if no camera setup (let the description speak for itself)
241
+ return ""
242
 
243
  except Exception as e:
244
+ logger.warning(f"Camera setup extraction failed: {e}")
245
+ return ""
246
 
247
 
248
  def _format_camera_setup(raw_setup: str) -> str:
249
+ """
250
+ Format camera setup preserving ALL technical information
251
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  try:
253
+ # Clean up common prefixes but preserve all technical specs
254
+ setup = re.sub(r'^(Based on.*?recommend|I would recommend|For this.*?setup)\s*', '', raw_setup, flags=re.IGNORECASE)
255
+ setup = re.sub(r'^(CAMERA_SETUP:|2\.\s*CAMERA_SETUP:?)\s*', '', setup, flags=re.IGNORECASE)
 
 
 
 
 
 
 
 
 
256
 
257
+ # Ensure proper formatting
258
+ if setup and not setup.lower().startswith('shot on'):
259
+ setup = f"shot on {setup}"
 
260
 
261
+ return setup.strip()
 
 
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  except Exception as e:
264
+ logger.warning(f"Camera setup formatting failed: {e}")
265
+ return raw_setup
266
 
267
 
268
+ def _format_professional_prompt(description: str, camera_setup: str) -> str:
269
+ """
270
+ Format the final prompt preserving ALL professional cinematography content
271
+ Structure: [Professional Description] + [Camera Setup]
272
+ """
273
  try:
 
274
  parts = []
275
 
276
+ # Add the rich professional description (preserve ALL content)
277
  if description:
278
  parts.append(description)
279
 
280
+ # Add camera setup if available
281
  if camera_setup:
282
  parts.append(camera_setup)
283
 
284
+ # Join with clean formatting
 
 
 
 
285
  result = ", ".join(parts)
286
 
287
+ # Clean up only formatting issues
288
  result = re.sub(r'\s*,\s*,+', ',', result) # Remove double commas
289
+ result = re.sub(r'\s+', ' ', result) # Clean multiple spaces
290
+ result = result.strip().rstrip(',') # Clean edges
291
 
292
+ # Ensure proper capitalization
293
  if result:
294
  result = result[0].upper() + result[1:] if len(result) > 1 else result.upper()
295
 
296
  return result
297
 
298
  except Exception as e:
299
+ logger.error(f"Professional prompt formatting failed: {e}")
300
+ return description if description else "Professional cinematographic photograph"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
 
303
  def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]] = None) -> Tuple[int, Dict[str, int]]:
 
317
  breakdown = {}
318
 
319
  # Enhanced Prompt Quality (0-25 points)
320
+ length_score = min(15, len(prompt) // 15) # Reward appropriate length
321
+ detail_score = min(10, len(prompt.split(',')) * 1.5) # Reward structured detail
322
  breakdown["prompt_quality"] = int(length_score + detail_score)
323
 
324
  # Technical Details with Cinematography Focus (0-25 points)
 
339
  if re.search(r'ISO \d+', prompt):
340
  tech_score += 4
341
 
342
+ # Professional terminology from professional_photography.py
343
+ tech_keywords = ['shot on', 'lens', 'depth of field', 'bokeh', 'composition', 'lighting']
344
+ tech_score += sum(2 for keyword in tech_keywords if keyword in prompt.lower())
345
 
346
  breakdown["technical_details"] = min(25, tech_score)
347
 
348
+ # Professional Cinematography (0-25 points) - Check for professional_photography.py terms
349
  cinema_score = 0
350
 
351
+ # Photographic planes
352
+ planes = ['wide shot', 'close-up', 'medium shot', 'extreme wide', 'extreme close-up', 'detail shot']
353
+ cinema_score += sum(4 for plane in planes if plane in prompt.lower())
354
+
355
+ # Camera angles
356
+ angles = ['low angle', 'high angle', 'eye level', 'dutch angle', 'elevated perspective']
357
+ cinema_score += sum(4 for angle in angles if angle in prompt.lower())
358
+
359
+ # Lighting principles
360
+ lighting = ['golden hour', 'blue hour', 'natural lighting', 'studio lighting', 'dramatic lighting', 'soft lighting']
361
+ cinema_score += sum(3 for light in lighting if light in prompt.lower())
362
 
363
+ # Composition rules
364
+ composition = ['rule of thirds', 'leading lines', 'symmetrical', 'centered', 'dynamic composition']
365
+ cinema_score += sum(3 for comp in composition if comp in prompt.lower())
366
 
367
  # Professional context bonus
368
  if analysis_data and analysis_data.get("has_camera_suggestion"):
 
373
  # Multi-Engine Optimization (0-25 points)
374
  optimization_score = 0
375
 
376
+ # Check for complete technical specifications
377
+ if re.search(r'(?:Canon|Sony|Leica|Phase One|ARRI|RED)', prompt):
378
  optimization_score += 10
379
 
380
  # Complete technical specs
381
+ if re.search(r'shot on.*\d+mm.*f/[\d.]+', prompt):
382
  optimization_score += 8
383
 
384
+ # Professional terminology density
385
+ pro_terms = ['professional', 'cinematographic', 'shot on', 'composition', 'lighting']
386
+ optimization_score += sum(1 for term in pro_terms if term in prompt.lower())
387
 
388
+ # Length efficiency (reward comprehensive but concise)
389
  word_count = len(prompt.split())
390
+ if 40 <= word_count <= 80: # Optimal range for rich but efficient prompts
391
  optimization_score += 5
392
+ elif 20 <= word_count <= 40:
393
  optimization_score += 3
394
 
395
  breakdown["multi_engine_optimization"] = min(25, optimization_score)
 
458
  **Professional Context:** {'✅ Applied' if has_cinema_context else '❌ Not Applied'}
459
 
460
  **🎯 OPTIMIZATIONS APPLIED:**
461
+ Complete professional cinematography analysis
462
+ Preserved all technical and artistic content
463
+ Structured professional prompt format
464
+ Multi-engine compatibility maintained
465
+ Professional photography knowledge integrated
466
+ Cinematographic terminology preserved
467
 
468
  **⚡ Powered by Pariente AI for MIA TV Series**"""
469