Malaji71 commited on
Commit
05bb27a
·
verified ·
1 Parent(s): 02c3790

Create processor.py

Browse files
Files changed (1) hide show
  1. processor.py +332 -0
processor.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main processing logic for FLUX Prompt Optimizer
3
+ Handles image analysis, prompt optimization, and scoring
4
+ """
5
+
6
+ import logging
7
+ import time
8
+ from typing import Tuple, Dict, Any, Optional
9
+ from PIL import Image
10
+ from datetime import datetime
11
+
12
+ from config import APP_CONFIG, PROCESSING_CONFIG, get_device_config
13
+ from utils import (
14
+ optimize_image, validate_image, apply_flux_rules,
15
+ calculate_prompt_score, get_score_grade, format_analysis_report,
16
+ clean_memory, safe_execute
17
+ )
18
+ from models import analyze_image
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class FluxOptimizer:
24
+ """Main optimizer class for FLUX prompt generation"""
25
+
26
+ def __init__(self, model_name: str = None):
27
+ self.model_name = model_name
28
+ self.device_config = get_device_config()
29
+ self.processing_stats = {
30
+ "total_processed": 0,
31
+ "successful_analyses": 0,
32
+ "failed_analyses": 0,
33
+ "average_processing_time": 0.0
34
+ }
35
+
36
+ logger.info(f"FluxOptimizer initialized - Device: {self.device_config['device']}")
37
+
38
+ def process_image(self, image: Any) -> Tuple[str, str, str, Dict[str, Any]]:
39
+ """
40
+ Complete image processing pipeline
41
+
42
+ Args:
43
+ image: Input image (PIL, numpy array, or file path)
44
+
45
+ Returns:
46
+ Tuple of (optimized_prompt, analysis_report, score_html, metadata)
47
+ """
48
+ start_time = time.time()
49
+ metadata = {
50
+ "processing_time": 0.0,
51
+ "success": False,
52
+ "model_used": self.model_name or "auto",
53
+ "device": self.device_config["device"],
54
+ "error": None
55
+ }
56
+
57
+ try:
58
+ # Step 1: Validate and optimize input image
59
+ logger.info("Starting image processing pipeline...")
60
+
61
+ if not validate_image(image):
62
+ error_msg = "Invalid or unsupported image format"
63
+ logger.error(error_msg)
64
+ return self._create_error_response(error_msg, metadata)
65
+
66
+ optimized_image = optimize_image(image)
67
+ if optimized_image is None:
68
+ error_msg = "Image optimization failed"
69
+ logger.error(error_msg)
70
+ return self._create_error_response(error_msg, metadata)
71
+
72
+ logger.info(f"Image optimized to size: {optimized_image.size}")
73
+
74
+ # Step 2: Analyze image with selected model
75
+ logger.info("Running image analysis...")
76
+ analysis_success, analysis_result = safe_execute(
77
+ analyze_image,
78
+ optimized_image,
79
+ self.model_name
80
+ )
81
+
82
+ if not analysis_success:
83
+ error_msg = f"Image analysis failed: {analysis_result}"
84
+ logger.error(error_msg)
85
+ return self._create_error_response(error_msg, metadata)
86
+
87
+ description, analysis_metadata = analysis_result
88
+ logger.info(f"Analysis complete: {len(description)} characters")
89
+
90
+ # Step 3: Apply FLUX optimization rules
91
+ logger.info("Applying FLUX optimization rules...")
92
+ optimized_prompt = apply_flux_rules(description)
93
+
94
+ if not optimized_prompt:
95
+ optimized_prompt = "A professional photograph"
96
+ logger.warning("Empty prompt after optimization, using fallback")
97
+
98
+ # Step 4: Calculate quality score
99
+ logger.info("Calculating quality score...")
100
+ score, score_breakdown = calculate_prompt_score(optimized_prompt, analysis_metadata)
101
+ grade_info = get_score_grade(score)
102
+
103
+ # Step 5: Generate analysis report
104
+ processing_time = time.time() - start_time
105
+ metadata.update({
106
+ "processing_time": processing_time,
107
+ "success": True,
108
+ "prompt_length": len(optimized_prompt),
109
+ "score": score,
110
+ "grade": grade_info["grade"],
111
+ "analysis_metadata": analysis_metadata
112
+ })
113
+
114
+ analysis_report = self._generate_detailed_report(
115
+ optimized_prompt, analysis_metadata, score,
116
+ score_breakdown, processing_time
117
+ )
118
+
119
+ # Step 6: Create score HTML
120
+ score_html = self._generate_score_html(score, grade_info)
121
+
122
+ # Update statistics
123
+ self._update_stats(processing_time, True)
124
+
125
+ logger.info(f"Processing complete - Score: {score}, Time: {processing_time:.1f}s")
126
+ return optimized_prompt, analysis_report, score_html, metadata
127
+
128
+ except Exception as e:
129
+ processing_time = time.time() - start_time
130
+ error_msg = f"Unexpected error in processing pipeline: {str(e)}"
131
+ logger.error(error_msg, exc_info=True)
132
+
133
+ metadata.update({
134
+ "processing_time": processing_time,
135
+ "error": error_msg
136
+ })
137
+
138
+ self._update_stats(processing_time, False)
139
+ return self._create_error_response(error_msg, metadata)
140
+
141
+ finally:
142
+ # Always clean up memory
143
+ clean_memory()
144
+
145
+ def _create_error_response(self, error_msg: str, metadata: Dict[str, Any]) -> Tuple[str, str, str, Dict[str, Any]]:
146
+ """Create standardized error response"""
147
+ error_prompt = "❌ Processing failed"
148
+ error_report = f"**Error:** {error_msg}\n\nPlease try with a different image or check the logs for more details."
149
+ error_html = self._generate_score_html(0, get_score_grade(0))
150
+
151
+ metadata["success"] = False
152
+ metadata["error"] = error_msg
153
+
154
+ return error_prompt, error_report, error_html, metadata
155
+
156
+ def _generate_detailed_report(self, prompt: str, analysis_metadata: Dict[str, Any],
157
+ score: int, breakdown: Dict[str, int],
158
+ processing_time: float) -> str:
159
+ """Generate comprehensive analysis report"""
160
+
161
+ model_used = analysis_metadata.get("model", "Unknown")
162
+ device_used = analysis_metadata.get("device", self.device_config["device"])
163
+ confidence = analysis_metadata.get("confidence", 0.0)
164
+
165
+ # Device status emoji
166
+ device_emoji = "⚡" if device_used == "cuda" else "💻"
167
+
168
+ report = f"""**Analysis Complete**
169
+ **Processing:** {device_emoji} {device_used.upper()} • {processing_time:.1f}s • Model: {model_used}
170
+ **Score:** {score}/100 • Confidence: {confidence:.0%}
171
+
172
+ **Score Breakdown:**
173
+ • **Prompt Quality:** {breakdown.get('prompt_quality', 0)}/30 - Content detail and description
174
+ • **Technical Details:** {breakdown.get('technical_details', 0)}/25 - Camera and photography settings
175
+ • **Artistic Value:** {breakdown.get('artistic_value', 0)}/25 - Creative elements
176
+ • **FLUX Optimization:** {breakdown.get('flux_optimization', 0)}/20 - Platform optimizations
177
+
178
+ **Analysis Summary:**
179
+ **Description Length:** {len(prompt)} characters
180
+ **Model Used:** {analysis_metadata.get('model', 'N/A')}
181
+
182
+ **Applied Optimizations:**
183
+ ✅ Camera settings added
184
+ ✅ Lighting configuration applied
185
+ ✅ Technical parameters optimized
186
+ ✅ FLUX rules implemented
187
+ ✅ Content cleaned and enhanced
188
+
189
+ **Performance:**
190
+ • **Processing Time:** {processing_time:.1f}s
191
+ • **Device:** {device_used.upper()}
192
+ • **Model Confidence:** {confidence:.0%}
193
+
194
+ **Frame 0 Laboratory for MIA**"""
195
+
196
+ return report
197
+
198
+ def _generate_score_html(self, score: int, grade_info: Dict[str, str]) -> str:
199
+ """Generate HTML for score display"""
200
+
201
+ html = f'''
202
+ <div style="text-align: center; padding: 2rem; background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%); border: 3px solid {grade_info["color"]}; border-radius: 16px; margin: 1rem 0; box-shadow: 0 8px 25px -5px rgba(0, 0, 0, 0.1);">
203
+ <div style="font-size: 3rem; font-weight: 800; color: {grade_info["color"]}; margin: 0; text-shadow: 0 2px 4px rgba(0,0,0,0.1);">{score}</div>
204
+ <div style="font-size: 1.25rem; color: #15803d; margin: 0.5rem 0; text-transform: uppercase; letter-spacing: 0.1em; font-weight: 700;">{grade_info["grade"]}</div>
205
+ <div style="font-size: 1rem; color: #15803d; margin: 0; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 500;">FLUX Quality Score</div>
206
+ </div>
207
+ '''
208
+
209
+ return html
210
+
211
+ def _update_stats(self, processing_time: float, success: bool) -> None:
212
+ """Update processing statistics"""
213
+ self.processing_stats["total_processed"] += 1
214
+
215
+ if success:
216
+ self.processing_stats["successful_analyses"] += 1
217
+ else:
218
+ self.processing_stats["failed_analyses"] += 1
219
+
220
+ # Update rolling average of processing time
221
+ current_avg = self.processing_stats["average_processing_time"]
222
+ total_count = self.processing_stats["total_processed"]
223
+
224
+ self.processing_stats["average_processing_time"] = (
225
+ (current_avg * (total_count - 1) + processing_time) / total_count
226
+ )
227
+
228
+ def get_stats(self) -> Dict[str, Any]:
229
+ """Get current processing statistics"""
230
+ stats = self.processing_stats.copy()
231
+
232
+ if stats["total_processed"] > 0:
233
+ stats["success_rate"] = stats["successful_analyses"] / stats["total_processed"]
234
+ else:
235
+ stats["success_rate"] = 0.0
236
+
237
+ stats["device_info"] = self.device_config
238
+
239
+ return stats
240
+
241
+ def reset_stats(self) -> None:
242
+ """Reset processing statistics"""
243
+ self.processing_stats = {
244
+ "total_processed": 0,
245
+ "successful_analyses": 0,
246
+ "failed_analyses": 0,
247
+ "average_processing_time": 0.0
248
+ }
249
+ logger.info("Processing statistics reset")
250
+
251
+
252
+ class BatchProcessor:
253
+ """Handle batch processing of multiple images"""
254
+
255
+ def __init__(self, optimizer: FluxOptimizer):
256
+ self.optimizer = optimizer
257
+ self.batch_results = []
258
+
259
+ def process_batch(self, images: list) -> list:
260
+ """Process multiple images in batch"""
261
+ results = []
262
+
263
+ for i, image in enumerate(images):
264
+ logger.info(f"Processing batch item {i+1}/{len(images)}")
265
+
266
+ try:
267
+ result = self.optimizer.process_image(image)
268
+ results.append({
269
+ "index": i,
270
+ "success": result[3]["success"],
271
+ "result": result
272
+ })
273
+
274
+ except Exception as e:
275
+ logger.error(f"Batch item {i} failed: {e}")
276
+ results.append({
277
+ "index": i,
278
+ "success": False,
279
+ "error": str(e)
280
+ })
281
+
282
+ self.batch_results = results
283
+ return results
284
+
285
+ def get_batch_summary(self) -> Dict[str, Any]:
286
+ """Get summary of batch processing results"""
287
+ if not self.batch_results:
288
+ return {"total": 0, "successful": 0, "failed": 0}
289
+
290
+ successful = sum(1 for r in self.batch_results if r["success"])
291
+ total = len(self.batch_results)
292
+
293
+ return {
294
+ "total": total,
295
+ "successful": successful,
296
+ "failed": total - successful,
297
+ "success_rate": successful / total if total > 0 else 0.0
298
+ }
299
+
300
+
301
+ # Global optimizer instance
302
+ flux_optimizer = FluxOptimizer()
303
+
304
+
305
+ def process_image_simple(image: Any, model_name: str = None) -> Tuple[str, str, str]:
306
+ """
307
+ Simple interface for image processing
308
+
309
+ Args:
310
+ image: Input image
311
+ model_name: Optional model name
312
+
313
+ Returns:
314
+ Tuple of (prompt, report, score_html)
315
+ """
316
+ if model_name and model_name != flux_optimizer.model_name:
317
+ # Create temporary optimizer with specified model
318
+ temp_optimizer = FluxOptimizer(model_name)
319
+ prompt, report, score_html, _ = temp_optimizer.process_image(image)
320
+ else:
321
+ prompt, report, score_html, _ = flux_optimizer.process_image(image)
322
+
323
+ return prompt, report, score_html
324
+
325
+
326
+ # Export main components
327
+ __all__ = [
328
+ "FluxOptimizer",
329
+ "BatchProcessor",
330
+ "flux_optimizer",
331
+ "process_image_simple"
332
+ ]