Malaji71 commited on
Commit
0d84ddc
·
verified ·
1 Parent(s): 9abf097

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +256 -0
utils.py CHANGED
@@ -166,6 +166,262 @@ def _extract_description_only(prompt: str) -> str:
166
  description = re.sub(r'For this type of scene.*?shooting style would be.*?\.', '', description, flags=re.DOTALL)
167
  description = re.sub(r'I would recommend.*?aperture.*?\.', '', description, flags=re.DOTALL)
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  return description.strip()
170
 
171
 
 
166
  description = re.sub(r'For this type of scene.*?shooting style would be.*?\.', '', description, flags=re.DOTALL)
167
  description = re.sub(r'I would recommend.*?aperture.*?\.', '', description, flags=re.DOTALL)
168
 
169
+ # Remove numbered section residues (like "2.," at the end)
170
+ description = re.sub(r'\s*\d+\.\s*,?\s*
171
+
172
+
173
+ def _format_bagel_camera_suggestion(bagel_camera: str) -> str:
174
+ """Format BAGEL's camera suggestion into clean FLUX format"""
175
+ try:
176
+ # Clean up the BAGEL suggestion
177
+ camera_text = bagel_camera.strip()
178
+
179
+ # Remove "CAMERA_SETUP:" if it's still there
180
+ camera_text = re.sub(r'^CAMERA_SETUP:\s*', '', camera_text)
181
+
182
+ # Extract key camera info using regex patterns
183
+ camera_patterns = {
184
+ 'camera': r'(Canon EOS [^,]+|Sony A[^,]+|Leica [^,]+|Hasselblad [^,]+|Phase One [^,]+|Fujifilm [^,]+)',
185
+ 'lens': r'(\d+mm[^,]*|[^,]*lens[^,]*)',
186
+ 'aperture': r'(f/[\d.]+[^,]*)'
187
+ }
188
+
189
+ extracted_parts = []
190
+
191
+ for key, pattern in camera_patterns.items():
192
+ match = re.search(pattern, camera_text, re.IGNORECASE)
193
+ if match:
194
+ extracted_parts.append(match.group(1).strip())
195
+
196
+ if extracted_parts:
197
+ # Build clean camera config
198
+ camera_info = ', '.join(extracted_parts)
199
+ return f", Shot on {camera_info}, professional photography"
200
+ else:
201
+ # Fallback: use the first part of BAGEL's suggestion
202
+ first_sentence = camera_text.split('.')[0].strip()
203
+ if len(first_sentence) > 10:
204
+ return f", {first_sentence}, professional photography"
205
+ else:
206
+ return ", professional camera setup"
207
+
208
+ except Exception as e:
209
+ logger.warning(f"Failed to format BAGEL camera suggestion: {e}")
210
+ return ", professional camera setup"
211
+
212
+
213
+ def _get_fallback_camera_config(prompt_lower: str) -> str:
214
+ """Get fallback camera configuration when BAGEL doesn't suggest one"""
215
+ # Improved detection logic
216
+ if any(word in prompt_lower for word in ['street', 'urban', 'city', 'documentary', 'crowd', 'protest']):
217
+ return FLUX_RULES["camera_configs"]["street"]
218
+ elif any(word in prompt_lower for word in ['portrait', 'person', 'man', 'woman', 'face']) and not any(word in prompt_lower for word in ['street', 'crowd', 'scene']):
219
+ return FLUX_RULES["camera_configs"]["portrait"]
220
+ elif any(word in prompt_lower for word in ['landscape', 'mountain', 'nature', 'outdoor']):
221
+ return FLUX_RULES["camera_configs"]["landscape"]
222
+ else:
223
+ return FLUX_RULES["camera_configs"]["default"]
224
+
225
+
226
+ def _get_lighting_enhancement(prompt_lower: str, camera_config: str) -> str:
227
+ """Determine appropriate lighting enhancement"""
228
+ # Don't add lighting if already mentioned in prompt or camera config
229
+ if 'lighting' in prompt_lower or 'lighting' in camera_config.lower():
230
+ return ""
231
+
232
+ if 'dramatic' in prompt_lower or 'chaos' in prompt_lower or 'fire' in prompt_lower:
233
+ return FLUX_RULES["lighting_enhancements"]["dramatic"]
234
+ elif 'portrait' in camera_config.lower():
235
+ return FLUX_RULES["lighting_enhancements"]["portrait"]
236
+ else:
237
+ return FLUX_RULES["lighting_enhancements"]["default"]
238
+
239
+
240
+ def _clean_prompt_formatting(prompt: str) -> str:
241
+ """Clean up prompt formatting"""
242
+ if not prompt:
243
+ return ""
244
+
245
+ # Ensure it starts with capital letter
246
+ prompt = prompt.strip()
247
+ if prompt:
248
+ prompt = prompt[0].upper() + prompt[1:] if len(prompt) > 1 else prompt.upper()
249
+
250
+ # Clean up spaces and commas
251
+ prompt = re.sub(r'\s+', ' ', prompt)
252
+ prompt = re.sub(r',\s*,+', ',', prompt)
253
+ prompt = re.sub(r'^\s*,\s*', '', prompt) # Remove leading commas
254
+ prompt = re.sub(r'\s*,\s*$', '', prompt) # Remove trailing commas
255
+
256
+ # Remove redundant periods
257
+ prompt = re.sub(r'\.+', '.', prompt)
258
+
259
+ return prompt.strip()
260
+
261
+
262
+ def calculate_prompt_score(prompt: str, analysis_data: Optional[Dict[str, Any]] = None) -> Tuple[int, Dict[str, int]]:
263
+ """
264
+ Calculate quality score for a prompt
265
+
266
+ Args:
267
+ prompt: The prompt to score
268
+ analysis_data: Optional analysis data to enhance scoring
269
+
270
+ Returns:
271
+ Tuple of (total_score, breakdown_dict)
272
+ """
273
+ if not prompt:
274
+ return 0, {"prompt_quality": 0, "technical_details": 0, "artistic_value": 0, "flux_optimization": 0}
275
+
276
+ breakdown = {}
277
+
278
+ # Prompt quality score (0-30 points)
279
+ length_score = min(20, len(prompt) // 8) # Reward decent length
280
+ detail_score = min(10, len(prompt.split(',')) * 2) # Reward detail
281
+ breakdown["prompt_quality"] = length_score + detail_score
282
+
283
+ # Technical details score (0-25 points) - Enhanced for BAGEL camera suggestions
284
+ tech_score = 0
285
+ tech_keywords = ['shot on', 'lens', 'photography', 'lighting', 'camera']
286
+ for keyword in tech_keywords:
287
+ if keyword in prompt.lower():
288
+ tech_score += 5
289
+
290
+ # Bonus points for BAGEL camera suggestions
291
+ if analysis_data and analysis_data.get("has_camera_suggestion"):
292
+ tech_score += 10 # Higher bonus for intelligent camera selection
293
+
294
+ breakdown["technical_details"] = min(25, tech_score)
295
+
296
+ # Artistic value score (0-25 points)
297
+ art_keywords = ['masterful', 'professional', 'cinematic', 'dramatic', 'beautiful']
298
+ art_score = sum(5 for keyword in art_keywords if keyword in prompt.lower())
299
+ breakdown["artistic_value"] = min(25, art_score)
300
+
301
+ # Flux optimization score (0-20 points)
302
+ flux_score = 0
303
+
304
+ # Check for camera configuration (prefer BAGEL over fallback)
305
+ if analysis_data and analysis_data.get("has_camera_suggestion"):
306
+ flux_score += 15 # Higher score for BAGEL suggestions
307
+ elif any(camera in prompt for camera in FLUX_RULES["camera_configs"].values()):
308
+ flux_score += 10 # Lower score for fallback
309
+
310
+ # Check for lighting configuration
311
+ if any(lighting in prompt for lighting in FLUX_RULES["lighting_enhancements"].values()):
312
+ flux_score += 5
313
+
314
+ breakdown["flux_optimization"] = flux_score
315
+
316
+ # Calculate total
317
+ total_score = sum(breakdown.values())
318
+
319
+ return total_score, breakdown
320
+
321
+
322
+ def get_score_grade(score: int) -> Dict[str, str]:
323
+ """
324
+ Get grade information for a score
325
+
326
+ Args:
327
+ score: Numeric score
328
+
329
+ Returns:
330
+ Dictionary with grade and color information
331
+ """
332
+ from config import SCORING_CONFIG
333
+
334
+ for threshold, grade_info in sorted(SCORING_CONFIG["grade_thresholds"].items(), reverse=True):
335
+ if score >= threshold:
336
+ return grade_info
337
+
338
+ # Default to lowest grade
339
+ return SCORING_CONFIG["grade_thresholds"][0]
340
+
341
+
342
+ def format_analysis_report(analysis_data: Dict[str, Any], processing_time: float) -> str:
343
+ """
344
+ Format analysis data into a readable report
345
+
346
+ Args:
347
+ analysis_data: Analysis results
348
+ processing_time: Time taken for processing
349
+
350
+ Returns:
351
+ Formatted markdown report
352
+ """
353
+ model_used = analysis_data.get("model_used", "Unknown")
354
+ prompt_length = len(analysis_data.get("prompt", ""))
355
+
356
+ report = f"""**🚀 FLUX OPTIMIZATION COMPLETE**
357
+ **Model:** {model_used} • **Time:** {processing_time:.1f}s • **Length:** {prompt_length} chars
358
+
359
+ **📊 ANALYSIS SUMMARY:**
360
+ {analysis_data.get("summary", "Analysis completed successfully")}
361
+
362
+ **🎯 OPTIMIZATIONS APPLIED:**
363
+ ✅ Flux camera configuration
364
+ ✅ Professional lighting setup
365
+ ✅ Technical photography details
366
+ ✅ Artistic enhancement keywords
367
+
368
+ **⚡ Powered by Frame 0 Laboratory for MIA**"""
369
+
370
+ return report
371
+
372
+
373
+ def safe_execute(func, *args, **kwargs) -> Tuple[bool, Any]:
374
+ """
375
+ Safely execute a function with error handling
376
+
377
+ Args:
378
+ func: Function to execute
379
+ *args: Function arguments
380
+ **kwargs: Function keyword arguments
381
+
382
+ Returns:
383
+ Tuple of (success: bool, result: Any)
384
+ """
385
+ try:
386
+ result = func(*args, **kwargs)
387
+ return True, result
388
+ except Exception as e:
389
+ logger.error(f"Safe execution failed for {func.__name__}: {e}")
390
+ return False, str(e)
391
+
392
+
393
+ def truncate_text(text: str, max_length: int = 100) -> str:
394
+ """
395
+ Truncate text to specified length with ellipsis
396
+
397
+ Args:
398
+ text: Text to truncate
399
+ max_length: Maximum length
400
+
401
+ Returns:
402
+ Truncated text
403
+ """
404
+ if not text or len(text) <= max_length:
405
+ return text
406
+
407
+ return text[:max_length-3] + "..."
408
+
409
+
410
+ # Export main functions
411
+ __all__ = [
412
+ "setup_logging",
413
+ "optimize_image",
414
+ "validate_image",
415
+ "clean_memory",
416
+ "apply_flux_rules",
417
+ "calculate_prompt_score",
418
+ "get_score_grade",
419
+ "format_analysis_report",
420
+ "safe_execute",
421
+ "truncate_text"
422
+ ], '', description)
423
+ description = re.sub(r'\s*\d+\.\s*,?\s*', ' ', description)
424
+
425
  return description.strip()
426
 
427