Malaji71 commited on
Commit
8697e16
·
verified ·
1 Parent(s): c55ae1b

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +242 -115
utils.py CHANGED
@@ -2,7 +2,7 @@
2
  Utility functions for Phramer AI
3
  By Pariente AI, for MIA TV Series
4
 
5
- Enhanced with professional cinematography knowledge and multi-engine optimization
6
  """
7
 
8
  import re
@@ -130,14 +130,14 @@ 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 rules for multi-engine compatibility with condensation
134
 
135
  Args:
136
  prompt: Raw prompt text from BAGEL analysis
137
  analysis_metadata: Enhanced metadata with cinematography suggestions
138
 
139
  Returns:
140
- Optimized and condensed prompt for Flux, Midjourney, and other generative engines
141
  """
142
  if not prompt or not isinstance(prompt, str):
143
  return ""
@@ -150,10 +150,10 @@ def apply_flux_rules(prompt: str, analysis_metadata: Optional[Dict[str, Any]] =
150
  # Extract description part only (remove CAMERA_SETUP section if present)
151
  description_part = _extract_description_only(cleaned_prompt)
152
 
153
- # NEW: Convert from descriptive to generative language
154
  if PROFESSIONAL_PHOTOGRAPHY_CONFIG.get("prompt_condensation", True):
155
- description_part = _convert_to_generative_language(description_part)
156
- logger.info("Converted to generative language")
157
 
158
  # Check if BAGEL provided intelligent camera setup with cinematography context
159
  camera_config = ""
@@ -177,15 +177,15 @@ def apply_flux_rules(prompt: str, analysis_metadata: Optional[Dict[str, Any]] =
177
  # Add style enhancement for multi-engine compatibility
178
  style_enhancement = _get_style_enhancement(scene_type, description_part.lower())
179
 
180
- # NEW: Add mandatory generative keywords
181
- mandatory_keywords = _get_mandatory_keywords(scene_type)
182
 
183
- # Build final prompt: Description + Camera + Lighting + Style + Keywords
184
- final_prompt = description_part + camera_config + lighting_enhancement + style_enhancement + mandatory_keywords
185
 
186
- # NEW: Final length optimization
187
  if PROFESSIONAL_PHOTOGRAPHY_CONFIG.get("prompt_optimization", {}).get("max_length"):
188
- final_prompt = _optimize_prompt_length(final_prompt)
189
 
190
  # Clean up formatting
191
  final_prompt = _clean_prompt_formatting(final_prompt)
@@ -224,9 +224,63 @@ def _extract_description_only(prompt: str) -> str:
224
  return description.strip()
225
 
226
 
227
- def _convert_to_generative_language(description: str) -> str:
228
- """Convert descriptive analysis language to direct generative prompt language"""
229
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  generative = description
231
 
232
  # Remove descriptive introduction phrases
@@ -238,17 +292,13 @@ def _convert_to_generative_language(description: str) -> str:
238
  r'This is (?:a|an) (?:image|photograph|picture) (?:of|showing)',
239
  r'The setting (?:appears to be|is)',
240
  r'The scene (?:appears to be|is|shows)',
241
- r'(?:In the background|In the foreground), (?:there are|there is)',
242
- r'(?:The background|The foreground) (?:features|shows|contains)',
243
- r'(?:There are|There is) [^,]+ (?:in the background|in the foreground)',
244
- r'The overall (?:setting|atmosphere|mood) (?:suggests|indicates)',
245
  ]
246
 
247
  for pattern in descriptive_intros:
248
  generative = re.sub(pattern, '', generative, flags=re.IGNORECASE)
249
 
250
- # Remove uncertainty phrases
251
- uncertainty_phrases = [
252
  r'possibly (?:a|an) ',
253
  r'appears to be (?:a|an) ',
254
  r'seems to be (?:a|an) ',
@@ -257,18 +307,26 @@ def _convert_to_generative_language(description: str) -> str:
257
  r'suggests (?:a|an) ',
258
  r'indicating (?:a|an) ',
259
  r'(?:possibly|apparently|seemingly|likely)',
 
 
 
 
 
260
  ]
261
 
262
- for pattern in uncertainty_phrases:
263
  generative = re.sub(pattern, '', generative, flags=re.IGNORECASE)
264
 
265
- # Convert descriptive structure to noun phrases
266
- structural_conversions = [
267
- # "close-up of a X" -> "close-up X"
 
 
 
 
 
 
268
  (r'(?:close-up|medium shot|wide shot) of (?:a|an|the) ', r'close-up '),
269
- # "blurred figures of people" -> "blurred people"
270
- (r'(?:blurred )?(?:figures|silhouettes) of (\w+)', r'blurred \1'),
271
- # "people walking on a sidewalk" -> "people walking on sidewalk"
272
  (r'(?:a|an|the) (\w+)', r'\1'),
273
  # Remove excessive connecting words
274
  (r'(?:, and|, with|, featuring)', ','),
@@ -277,7 +335,7 @@ def _convert_to_generative_language(description: str) -> str:
277
  (r'in (?:a|an|the) ', r'in '),
278
  ]
279
 
280
- for pattern, replacement in structural_conversions:
281
  generative = re.sub(pattern, replacement, generative, flags=re.IGNORECASE)
282
 
283
  # Convert action descriptions to present participles
@@ -291,6 +349,11 @@ def _convert_to_generative_language(description: str) -> str:
291
  for pattern, replacement in action_conversions:
292
  generative = re.sub(pattern, replacement, generative, flags=re.IGNORECASE)
293
 
 
 
 
 
 
294
  # Clean up extra spaces and punctuation
295
  generative = re.sub(r'\s+', ' ', generative)
296
  generative = re.sub(r'^\s*,\s*', '', generative) # Remove leading commas
@@ -302,47 +365,73 @@ def _convert_to_generative_language(description: str) -> str:
302
  if generative:
303
  generative = generative[0].upper() + generative[1:] if len(generative) > 1 else generative.upper()
304
 
305
- logger.info(f"Converted descriptive to generative: {len(description)} → {len(generative)} chars")
306
  return generative
307
 
308
  except Exception as e:
309
- logger.warning(f"Generative language conversion failed: {e}")
310
  return description
311
 
312
 
313
- def _get_mandatory_keywords(scene_type: str) -> str:
314
- """Get mandatory generative keywords based on scene type"""
315
  try:
316
- mandatory = FLUX_RULES.get("mandatory_keywords", {})
317
-
318
  keywords = []
319
 
320
- # Add quality keywords
321
- quality_keywords = mandatory.get("quality", [])
322
- keywords.extend(quality_keywords)
323
 
324
- # Add technical keywords
325
- technical_keywords = mandatory.get("technical", [])
326
- keywords.extend(technical_keywords)
 
 
 
 
327
 
328
- # Add scene-specific keywords
329
- style_by_scene = mandatory.get("style_by_scene", {})
330
  if scene_type in style_by_scene:
331
  scene_keywords = style_by_scene[scene_type]
332
- keywords.extend(scene_keywords)
333
-
334
- if keywords:
335
- return ", " + ", ".join(keywords)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  else:
 
337
  return ""
338
 
339
  except Exception as e:
340
- logger.warning(f"Failed to get mandatory keywords: {e}")
341
  return ""
342
 
343
 
344
- def _optimize_prompt_length(prompt: str) -> str:
345
- """Optimize prompt length while preserving essential information"""
346
  try:
347
  max_words = PROFESSIONAL_PHOTOGRAPHY_CONFIG.get("prompt_optimization", {}).get("max_length", 150)
348
 
@@ -350,34 +439,53 @@ def _optimize_prompt_length(prompt: str) -> str:
350
  if len(words) <= max_words:
351
  return prompt
352
 
353
- # Keep essential parts: subject, camera setup, technical specs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  essential_parts = []
355
- current_part = []
356
 
357
- for word in words:
358
- current_part.append(word)
359
-
360
- # Keep camera and technical information
361
- if any(tech in word.lower() for tech in ["shot", "lens", "f/", "iso", "mm"]):
362
- essential_parts.extend(current_part)
363
- current_part = []
364
- # Keep quality keywords
365
- elif any(qual in word.lower() for qual in ["photorealistic", "cinematic", "professional"]):
366
- essential_parts.extend(current_part)
367
- current_part = []
368
-
369
- # Add remaining words if under limit
370
- remaining_words = max_words - len(essential_parts)
371
- if remaining_words > 0 and current_part:
372
- essential_parts.extend(current_part[:remaining_words])
373
-
374
- optimized = " ".join(essential_parts[:max_words])
375
- logger.info(f"Optimized prompt length from {len(words)} to {len(essential_parts)} words")
 
 
 
 
376
 
377
  return optimized
378
 
379
  except Exception as e:
380
- logger.warning(f"Prompt length optimization failed: {e}")
381
  return prompt
382
 
383
 
@@ -461,15 +569,15 @@ def _format_professional_camera_suggestion(bagel_camera: str, scene_type: str) -
461
  if composition_match:
462
  camera_setup += f", {composition_match.group()}"
463
 
464
- # Add scene-specific enhancement
465
  if scene_type == "cinematic":
466
- result = f", Shot on {camera_setup}, cinematic photography"
467
  elif scene_type == "portrait":
468
- result = f", Shot on {camera_setup}, professional portrait photography"
469
  else:
470
- result = f", Shot on {camera_setup}, professional photography"
471
 
472
- logger.info(f"Formatted camera setup: {result}")
473
  return result
474
  else:
475
  # Fallback to enhanced config if parsing fails
@@ -484,12 +592,12 @@ def _get_enhanced_camera_config(scene_type: str, description_lower: str) -> str:
484
  """Get enhanced camera configuration with cinematography knowledge"""
485
  # Enhanced camera configurations with cinema equipment
486
  enhanced_configs = {
487
- "cinematic": ", Shot on ARRI Alexa LF, 35mm anamorphic lens at f/2.8, ISO 400, cinematic framing, film grain, dramatic composition",
488
- "portrait": ", Shot on Canon EOS R5, 85mm f/1.4 lens at f/2.8, ISO 200, rule of thirds composition, professional portrait photography",
489
- "landscape": ", Shot on Phase One XT, 24-70mm f/4 lens at f/8, ISO 100, hyperfocal distance, leading lines composition, epic landscape photography",
490
- "street": ", Shot on Leica M11, 35mm f/1.4 lens at f/2.8, ISO 800, decisive moment, candid composition, documentary street photography",
491
- "architectural": ", Shot on Canon EOS R5, 24-70mm f/2.8 lens at f/8, ISO 100, symmetrical composition, perspective correction, architectural photography",
492
- "commercial": ", Shot on Hasselblad X2D 100C, 90mm f/2.5 lens at f/4, ISO 100, centered composition, product photography"
493
  }
494
 
495
  # Use enhanced config if available, otherwise fall back to FLUX_RULES
@@ -510,30 +618,27 @@ def _get_cinematography_lighting_enhancement(description_lower: str, camera_conf
510
  # Enhanced lighting based on scene type and cinematography knowledge
511
  if scene_type == "cinematic":
512
  if any(term in description_lower for term in ["dramatic", "moody", "dark"]):
513
- return ", dramatic cinematic lighting with practical lights"
514
  else:
515
- return ", professional cinematic lighting"
516
  elif scene_type == "portrait":
517
- return ", professional studio lighting with subtle rim light"
518
  elif "dramatic" in description_lower or "chaos" in description_lower:
519
- return FLUX_RULES["lighting_enhancements"]["dramatic"]
520
  else:
521
- return FLUX_RULES["lighting_enhancements"]["default"]
522
 
523
 
524
  def _get_style_enhancement(scene_type: str, description_lower: str) -> str:
525
- """Get style enhancement for multi-engine compatibility"""
526
- style_enhancements = FLUX_RULES.get("style_enhancements", {})
527
-
528
  if scene_type == "cinematic":
529
  if "film grain" not in description_lower:
530
- return ", " + style_enhancements.get("cinematic", "cinematic composition, film grain")
531
- elif scene_type in ["portrait", "commercial"]:
532
- return ", " + style_enhancements.get("photorealistic", "photorealistic rendering, ultra-detailed")
533
- elif "editorial" in description_lower:
534
- return ", " + style_enhancements.get("editorial", "editorial photography style")
535
 
536
- return ""
537
 
538
 
539
  def _clean_prompt_formatting(prompt: str) -> str:
@@ -593,12 +698,16 @@ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]]
593
  if re.search(r'\d+mm.*f/[\d.]+', prompt):
594
  tech_score += 5
595
 
 
 
 
 
596
  # Anamorphic and specialized lenses
597
  if 'anamorphic' in prompt.lower():
598
  tech_score += 4
599
 
600
  # Professional terminology
601
- tech_keywords = ['shot on', 'lens', 'photography', 'lighting', 'cinematic']
602
  for keyword in tech_keywords:
603
  if keyword in prompt.lower():
604
  tech_score += 2
@@ -609,19 +718,23 @@ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]]
609
 
610
  breakdown["technical_details"] = min(25, tech_score)
611
 
612
- # Professional Cinematography (0-25 points) - NEW CATEGORY
613
  cinema_score = 0
614
 
 
 
 
 
615
  # Professional lighting techniques
616
  lighting_terms = ['cinematic lighting', 'dramatic lighting', 'studio lighting', 'rim light', 'practical lights']
617
  cinema_score += sum(3 for term in lighting_terms if term in prompt.lower())
618
 
619
  # Composition techniques
620
- composition_terms = ['composition', 'framing', 'depth of field', 'bokeh', 'rule of thirds']
621
  cinema_score += sum(2 for term in composition_terms if term in prompt.lower())
622
 
623
  # Cinematography style elements
624
- style_terms = ['film grain', 'anamorphic', 'telephoto compression', 'wide-angle']
625
  cinema_score += sum(3 for term in style_terms if term in prompt.lower())
626
 
627
  # Professional context bonus
@@ -630,24 +743,37 @@ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]]
630
 
631
  breakdown["professional_cinematography"] = min(25, cinema_score)
632
 
633
- # Multi-Engine Optimization (0-25 points)
634
  optimization_score = 0
635
 
636
- # Check for multi-engine compatible elements
637
- multi_engine_terms = ['photorealistic', 'ultra-detailed', 'professional photography', 'cinematic']
638
- optimization_score += sum(3 for term in multi_engine_terms if term in prompt.lower())
639
 
640
- # Technical specifications for generation
641
- if any(camera in prompt for camera in FLUX_RULES["camera_configs"].values()):
642
- optimization_score += 5
643
-
644
- # Lighting configuration
645
- if any(lighting in prompt for lighting in FLUX_RULES["lighting_enhancements"].values()):
646
- optimization_score += 4
647
-
648
- # Style enhancements
 
 
 
 
 
 
 
 
649
  if any(style in prompt for style in FLUX_RULES.get("style_enhancements", {}).values()):
650
  optimization_score += 3
 
 
 
 
 
651
 
652
  breakdown["multi_engine_optimization"] = min(25, optimization_score)
653
 
@@ -716,11 +842,12 @@ def format_analysis_report(analysis_data: Dict[str, Any], processing_time: float
716
  **Professional Context:** {'✅ Applied' if has_cinema_context else '❌ Not Applied'}
717
 
718
  **🎯 OPTIMIZATIONS APPLIED:**
 
719
  ✅ Professional camera configuration
720
  ✅ Cinematography lighting setup
721
- Technical specifications
722
  ✅ Multi-engine compatibility
723
- Cinema-quality enhancement
724
 
725
  **⚡ Powered by Pariente AI for MIA TV Series**"""
726
 
 
2
  Utility functions for Phramer AI
3
  By Pariente AI, for MIA TV Series
4
 
5
+ Enhanced with professional cinematography knowledge and intelligent token economy
6
  """
7
 
8
  import re
 
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 ""
 
150
  # Extract description part only (remove CAMERA_SETUP section if present)
151
  description_part = _extract_description_only(cleaned_prompt)
152
 
153
+ # NEW: Convert to generative language with cinematography angle detection
154
  if PROFESSIONAL_PHOTOGRAPHY_CONFIG.get("prompt_condensation", True):
155
+ description_part = _convert_to_cinematographic_language(description_part)
156
+ logger.info("Applied cinematographic language conversion")
157
 
158
  # Check if BAGEL provided intelligent camera setup with cinematography context
159
  camera_config = ""
 
177
  # Add style enhancement for multi-engine compatibility
178
  style_enhancement = _get_style_enhancement(scene_type, description_part.lower())
179
 
180
+ # NEW: Smart keyword insertion with token economy
181
+ smart_keywords = _apply_smart_keyword_insertion(description_part, camera_config, scene_type)
182
 
183
+ # Build final prompt: Description + Camera + Lighting + Style + Smart Keywords
184
+ final_prompt = description_part + camera_config + lighting_enhancement + style_enhancement + smart_keywords
185
 
186
+ # NEW: Final length optimization with token economy
187
  if PROFESSIONAL_PHOTOGRAPHY_CONFIG.get("prompt_optimization", {}).get("max_length"):
188
+ final_prompt = _optimize_prompt_with_token_economy(final_prompt)
189
 
190
  # Clean up formatting
191
  final_prompt = _clean_prompt_formatting(final_prompt)
 
224
  return description.strip()
225
 
226
 
227
+ def _detect_camera_angles(description: str) -> List[str]:
228
+ """Detect camera angles and perspectives using professional cinematography knowledge"""
229
  try:
230
+ angles_detected = []
231
+ description_lower = description.lower()
232
+
233
+ # Low angle (contrapicado) detection
234
+ low_angle_indicators = [
235
+ "looking up at", "from below", "upward angle", "towering", "looming",
236
+ "shot from ground level", "worm's eye", "low angle"
237
+ ]
238
+ if any(indicator in description_lower for indicator in low_angle_indicators):
239
+ angles_detected.append("low-angle shot")
240
+
241
+ # High angle (picado) detection
242
+ high_angle_indicators = [
243
+ "looking down", "from above", "overhead", "bird's eye", "aerial view",
244
+ "downward angle", "top-down", "high angle"
245
+ ]
246
+ if any(indicator in description_lower for indicator in high_angle_indicators):
247
+ angles_detected.append("high-angle shot")
248
+
249
+ # Eye level detection
250
+ eye_level_indicators = [
251
+ "eye level", "straight on", "direct view", "level with"
252
+ ]
253
+ if any(indicator in description_lower for indicator in eye_level_indicators):
254
+ angles_detected.append("eye-level shot")
255
+
256
+ # Dutch angle detection
257
+ dutch_indicators = [
258
+ "tilted", "angled", "diagonal", "off-kilter", "dutch angle"
259
+ ]
260
+ if any(indicator in description_lower for indicator in dutch_indicators):
261
+ angles_detected.append("dutch angle")
262
+
263
+ # Perspective analysis for mixed angles
264
+ if ("foreground" in description_lower and "background" in description_lower):
265
+ if ("close" in description_lower or "prominent" in description_lower) and "blurred" in description_lower:
266
+ # Suggests foreground element shot from specific angle with background perspective
267
+ if not angles_detected: # Only add if no specific angle detected
268
+ angles_detected.append("shallow depth perspective")
269
+
270
+ logger.info(f"Camera angles detected: {angles_detected}")
271
+ return angles_detected
272
+
273
+ except Exception as e:
274
+ logger.warning(f"Camera angle detection failed: {e}")
275
+ return []
276
+
277
+
278
+ def _convert_to_cinematographic_language(description: str) -> str:
279
+ """Convert descriptive analysis to cinematographic prompt language with angle detection"""
280
+ try:
281
+ # First detect camera angles
282
+ camera_angles = _detect_camera_angles(description)
283
+
284
  generative = description
285
 
286
  # Remove descriptive introduction phrases
 
292
  r'This is (?:a|an) (?:image|photograph|picture) (?:of|showing)',
293
  r'The setting (?:appears to be|is)',
294
  r'The scene (?:appears to be|is|shows)',
 
 
 
 
295
  ]
296
 
297
  for pattern in descriptive_intros:
298
  generative = re.sub(pattern, '', generative, flags=re.IGNORECASE)
299
 
300
+ # Remove uncertainty and verbose connector phrases
301
+ verbose_phrases = [
302
  r'possibly (?:a|an) ',
303
  r'appears to be (?:a|an) ',
304
  r'seems to be (?:a|an) ',
 
307
  r'suggests (?:a|an) ',
308
  r'indicating (?:a|an) ',
309
  r'(?:possibly|apparently|seemingly|likely)',
310
+ r'which (?:is|are|creates|adds)',
311
+ r'(?:In the background|In the foreground), (?:there are|there is)',
312
+ r'(?:The background|The foreground) (?:features|shows|contains)',
313
+ r'(?:There are|There is) [^,]+ (?:in the background|in the foreground)',
314
+ r'The overall (?:setting|atmosphere|mood) (?:suggests|indicates)',
315
  ]
316
 
317
+ for pattern in verbose_phrases:
318
  generative = re.sub(pattern, '', generative, flags=re.IGNORECASE)
319
 
320
+ # Convert spatial relationships to cinematographic terms
321
+ spatial_conversions = [
322
+ # Background/foreground to cinematographic terms
323
+ (r'prominently displayed in (?:the )?foreground', 'foreground focus'),
324
+ (r'in (?:the )?foreground', 'foreground'),
325
+ (r'in (?:the )?background', 'background'),
326
+ (r'blurred (?:figures|people|objects)', 'bokeh blur'),
327
+ (r'out of focus', 'soft focus'),
328
+ # Convert descriptive structure to noun phrases
329
  (r'(?:close-up|medium shot|wide shot) of (?:a|an|the) ', r'close-up '),
 
 
 
330
  (r'(?:a|an|the) (\w+)', r'\1'),
331
  # Remove excessive connecting words
332
  (r'(?:, and|, with|, featuring)', ','),
 
335
  (r'in (?:a|an|the) ', r'in '),
336
  ]
337
 
338
+ for pattern, replacement in spatial_conversions:
339
  generative = re.sub(pattern, replacement, generative, flags=re.IGNORECASE)
340
 
341
  # Convert action descriptions to present participles
 
349
  for pattern, replacement in action_conversions:
350
  generative = re.sub(pattern, replacement, generative, flags=re.IGNORECASE)
351
 
352
+ # Add detected camera angles at the beginning
353
+ if camera_angles:
354
+ angle_prefix = ", ".join(camera_angles)
355
+ generative = f"{angle_prefix}, {generative}"
356
+
357
  # Clean up extra spaces and punctuation
358
  generative = re.sub(r'\s+', ' ', generative)
359
  generative = re.sub(r'^\s*,\s*', '', generative) # Remove leading commas
 
365
  if generative:
366
  generative = generative[0].upper() + generative[1:] if len(generative) > 1 else generative.upper()
367
 
368
+ logger.info(f"Cinematographic conversion: angles={len(camera_angles)}, {len(description)} → {len(generative)} chars")
369
  return generative
370
 
371
  except Exception as e:
372
+ logger.warning(f"Cinematographic language conversion failed: {e}")
373
  return description
374
 
375
 
376
+ def _apply_smart_keyword_insertion(description: str, camera_config: str, scene_type: str) -> str:
377
+ """Smart keyword insertion with token economy - avoid redundancy"""
378
  try:
 
 
379
  keywords = []
380
 
381
+ # Token Economy Rule 1: If camera specs exist, skip "photorealistic" keywords
382
+ has_camera_specs = bool(re.search(r'(?:Canon|Sony|Leica|ARRI|RED|Hasselblad|Phase One)', camera_config))
383
+ has_lens_specs = bool(re.search(r'\d+mm.*f/[\d.]+', camera_config))
384
 
385
+ # Only add quality keywords if NO technical specs present
386
+ if not (has_camera_specs and has_lens_specs):
387
+ quality_keywords = FLUX_RULES.get("mandatory_keywords", {}).get("quality", [])
388
+ keywords.extend(quality_keywords[:2]) # Limit to 2 quality keywords max
389
+ logger.info("Added fallback quality keywords (no camera specs detected)")
390
+ else:
391
+ logger.info("Skipped redundant quality keywords (camera specs present)")
392
 
393
+ # Token Economy Rule 2: Scene-specific keywords only if they add value
394
+ style_by_scene = FLUX_RULES.get("mandatory_keywords", {}).get("style_by_scene", {})
395
  if scene_type in style_by_scene:
396
  scene_keywords = style_by_scene[scene_type]
397
+
398
+ # Check if scene keywords are already implied by camera config or description
399
+ for keyword in scene_keywords:
400
+ if keyword.lower() not in camera_config.lower() and keyword.lower() not in description.lower():
401
+ keywords.append(keyword)
402
+
403
+ # Token Economy Rule 3: Technical keywords only if not redundant
404
+ technical_keywords = FLUX_RULES.get("mandatory_keywords", {}).get("technical", [])
405
+ for tech_keyword in technical_keywords:
406
+ # Skip "professional photography" if camera specs already indicate professional level
407
+ if tech_keyword == "professional photography" and has_camera_specs:
408
+ continue
409
+ # Skip "high resolution" if camera specs include resolution indicators
410
+ if tech_keyword == "high resolution" and has_camera_specs:
411
+ continue
412
+ keywords.append(tech_keyword)
413
+
414
+ # Remove duplicates while preserving order
415
+ unique_keywords = []
416
+ for keyword in keywords:
417
+ if keyword not in unique_keywords:
418
+ unique_keywords.append(keyword)
419
+
420
+ if unique_keywords:
421
+ result = ", " + ", ".join(unique_keywords)
422
+ logger.info(f"Smart keywords applied: {unique_keywords}")
423
+ return result
424
  else:
425
+ logger.info("No additional keywords needed (all redundant)")
426
  return ""
427
 
428
  except Exception as e:
429
+ logger.warning(f"Smart keyword insertion failed: {e}")
430
  return ""
431
 
432
 
433
+ def _optimize_prompt_with_token_economy(prompt: str) -> str:
434
+ """Optimize prompt length with intelligent token economy"""
435
  try:
436
  max_words = PROFESSIONAL_PHOTOGRAPHY_CONFIG.get("prompt_optimization", {}).get("max_length", 150)
437
 
 
439
  if len(words) <= max_words:
440
  return prompt
441
 
442
+ # Priority preservation order for token economy
443
+ essential_patterns = [
444
+ # 1. Camera angles (highest priority)
445
+ r'(?:low-angle|high-angle|eye-level|dutch angle|bird\'s eye|worm\'s eye) shot',
446
+ # 2. Camera and lens specs
447
+ r'(?:Canon|Sony|Leica|ARRI|RED|Hasselblad|Phase One) [^,]+',
448
+ r'\d+mm[^,]*f/[\d.]+[^,]*',
449
+ r'ISO \d+',
450
+ # 3. Core subject and composition
451
+ r'(?:close-up|medium shot|wide shot|shallow depth)',
452
+ r'(?:foreground|background|bokeh)',
453
+ # 4. Scene-specific technical terms
454
+ r'(?:cinematic|anamorphic|telephoto|wide-angle)',
455
+ ]
456
+
457
+ # Extract essential parts first
458
  essential_parts = []
459
+ remaining_text = prompt
460
 
461
+ for pattern in essential_patterns:
462
+ matches = re.findall(pattern, remaining_text, re.IGNORECASE)
463
+ for match in matches:
464
+ if match not in essential_parts:
465
+ essential_parts.append(match)
466
+ # Remove from remaining text to avoid duplication
467
+ remaining_text = re.sub(re.escape(match), '', remaining_text, count=1, flags=re.IGNORECASE)
468
+
469
+ # Add essential parts to start
470
+ optimized_words = []
471
+ for part in essential_parts:
472
+ optimized_words.extend(part.split())
473
+
474
+ # Fill remaining space with most important remaining words
475
+ remaining_words = [w for w in remaining_text.split() if w.strip() and w not in optimized_words]
476
+ remaining_space = max_words - len(optimized_words)
477
+
478
+ if remaining_space > 0:
479
+ optimized_words.extend(remaining_words[:remaining_space])
480
+
481
+ optimized = " ".join(optimized_words[:max_words])
482
+
483
+ logger.info(f"Token economy optimization: {len(words)} → {len(optimized_words)} words, preserved {len(essential_parts)} essential elements")
484
 
485
  return optimized
486
 
487
  except Exception as e:
488
+ logger.warning(f"Token economy optimization failed: {e}")
489
  return prompt
490
 
491
 
 
569
  if composition_match:
570
  camera_setup += f", {composition_match.group()}"
571
 
572
+ # Scene-specific enhancement with token economy
573
  if scene_type == "cinematic":
574
+ result = f", Shot on {camera_setup}" # Skip redundant "cinematic photography"
575
  elif scene_type == "portrait":
576
+ result = f", Shot on {camera_setup}" # Skip redundant "professional portrait photography"
577
  else:
578
+ result = f", Shot on {camera_setup}"
579
 
580
+ logger.info(f"Formatted camera setup with token economy: {result}")
581
  return result
582
  else:
583
  # Fallback to enhanced config if parsing fails
 
592
  """Get enhanced camera configuration with cinematography knowledge"""
593
  # Enhanced camera configurations with cinema equipment
594
  enhanced_configs = {
595
+ "cinematic": ", Shot on ARRI Alexa LF, 35mm anamorphic lens at f/2.8, ISO 400",
596
+ "portrait": ", Shot on Canon EOS R5, 85mm f/1.4 lens at f/2.8, ISO 200, rule of thirds",
597
+ "landscape": ", Shot on Phase One XT, 24-70mm f/4 lens at f/8, ISO 100, hyperfocal distance",
598
+ "street": ", Shot on Leica M11, 35mm f/1.4 lens at f/2.8, ISO 800",
599
+ "architectural": ", Shot on Canon EOS R5, 24-70mm f/2.8 lens at f/8, ISO 100, symmetrical composition",
600
+ "commercial": ", Shot on Hasselblad X2D 100C, 90mm f/2.5 lens at f/4, ISO 100"
601
  }
602
 
603
  # Use enhanced config if available, otherwise fall back to FLUX_RULES
 
618
  # Enhanced lighting based on scene type and cinematography knowledge
619
  if scene_type == "cinematic":
620
  if any(term in description_lower for term in ["dramatic", "moody", "dark"]):
621
+ return ", dramatic lighting"
622
  else:
623
+ return ", cinematic lighting"
624
  elif scene_type == "portrait":
625
+ return ", studio lighting"
626
  elif "dramatic" in description_lower or "chaos" in description_lower:
627
+ return ", dramatic lighting"
628
  else:
629
+ return "" # Skip redundant lighting terms
630
 
631
 
632
  def _get_style_enhancement(scene_type: str, description_lower: str) -> str:
633
+ """Get style enhancement for multi-engine compatibility with token economy"""
634
+ # Token economy: only add style if it adds unique value
 
635
  if scene_type == "cinematic":
636
  if "film grain" not in description_lower:
637
+ return ", film grain"
638
+ elif scene_type == "architectural":
639
+ return ", clean lines"
 
 
640
 
641
+ return "" # Skip redundant style terms
642
 
643
 
644
  def _clean_prompt_formatting(prompt: str) -> str:
 
698
  if re.search(r'\d+mm.*f/[\d.]+', prompt):
699
  tech_score += 5
700
 
701
+ # Camera angles (NEW - high value)
702
+ angle_terms = ['low-angle shot', 'high-angle shot', 'eye-level shot', 'dutch angle', 'bird\'s eye', 'worm\'s eye']
703
+ tech_score += sum(4 for term in angle_terms if term in prompt.lower())
704
+
705
  # Anamorphic and specialized lenses
706
  if 'anamorphic' in prompt.lower():
707
  tech_score += 4
708
 
709
  # Professional terminology
710
+ tech_keywords = ['shot on', 'lens', 'cinematography', 'lighting']
711
  for keyword in tech_keywords:
712
  if keyword in prompt.lower():
713
  tech_score += 2
 
718
 
719
  breakdown["technical_details"] = min(25, tech_score)
720
 
721
+ # Professional Cinematography (0-25 points) - Enhanced with angle detection
722
  cinema_score = 0
723
 
724
+ # Camera angles (high value for professional cinematography)
725
+ angle_terms = ['low-angle', 'high-angle', 'eye-level', 'dutch angle', 'bird\'s eye', 'worm\'s eye']
726
+ cinema_score += sum(5 for term in angle_terms if term in prompt.lower())
727
+
728
  # Professional lighting techniques
729
  lighting_terms = ['cinematic lighting', 'dramatic lighting', 'studio lighting', 'rim light', 'practical lights']
730
  cinema_score += sum(3 for term in lighting_terms if term in prompt.lower())
731
 
732
  # Composition techniques
733
+ composition_terms = ['composition', 'framing', 'depth of field', 'bokeh', 'rule of thirds', 'foreground', 'background']
734
  cinema_score += sum(2 for term in composition_terms if term in prompt.lower())
735
 
736
  # Cinematography style elements
737
+ style_terms = ['film grain', 'anamorphic', 'telephoto compression', 'wide-angle', 'shallow depth']
738
  cinema_score += sum(3 for term in style_terms if term in prompt.lower())
739
 
740
  # Professional context bonus
 
743
 
744
  breakdown["professional_cinematography"] = min(25, cinema_score)
745
 
746
+ # Multi-Engine Optimization (0-25 points) - Token economy aware
747
  optimization_score = 0
748
 
749
+ # Check for technical specifications (more valuable than generic keywords)
750
+ if re.search(r'(?:Canon|Sony|Leica|ARRI|RED|Hasselblad|Phase One)', prompt):
751
+ optimization_score += 8 # Higher score for actual camera specs
752
 
753
+ if re.search(r'\d+mm.*f/[\d.]+.*ISO \d+', prompt):
754
+ optimization_score += 7 # Complete technical specs
755
+
756
+ # Token economy bonus: penalize redundant keywords
757
+ redundant_keywords = ['photorealistic', 'ultra-detailed', 'professional photography']
758
+ has_camera_specs = bool(re.search(r'(?:Canon|Sony|Leica|ARRI|RED)', prompt))
759
+
760
+ if has_camera_specs:
761
+ # Bonus for NOT having redundant keywords when camera specs present
762
+ redundant_count = sum(1 for keyword in redundant_keywords if keyword in prompt.lower())
763
+ optimization_score += max(0, 5 - redundant_count * 2) # Penalty for redundancy
764
+ else:
765
+ # If no camera specs, quality keywords are valuable
766
+ quality_keywords = sum(1 for keyword in redundant_keywords if keyword in prompt.lower())
767
+ optimization_score += min(5, quality_keywords * 2)
768
+
769
+ # Scene-specific optimization
770
  if any(style in prompt for style in FLUX_RULES.get("style_enhancements", {}).values()):
771
  optimization_score += 3
772
+
773
+ # Length efficiency bonus
774
+ word_count = len(prompt.split())
775
+ if word_count <= 120: # Reward conciseness
776
+ optimization_score += 2
777
 
778
  breakdown["multi_engine_optimization"] = min(25, optimization_score)
779
 
 
842
  **Professional Context:** {'✅ Applied' if has_cinema_context else '❌ Not Applied'}
843
 
844
  **🎯 OPTIMIZATIONS APPLIED:**
845
+ ✅ Camera angle detection
846
  ✅ Professional camera configuration
847
  ✅ Cinematography lighting setup
848
+ Token economy optimization
849
  ✅ Multi-engine compatibility
850
+ Redundancy elimination
851
 
852
  **⚡ Powered by Pariente AI for MIA TV Series**"""
853