eho69 commited on
Commit
f02c006
·
verified ·
1 Parent(s): 3355994

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +217 -0
app.py CHANGED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import cv2
4
+ import numpy as np
5
+ from PIL import Image
6
+ from ultralytics import YOLO
7
+ import os
8
+
9
+ # ═════════════════════════════════════════════════════════════════════════════
10
+ # Load YOLO Model
11
+ # ═════════════════════════════════════════════════════════════════════════════
12
+
13
+ MODEL_PATH = "best.pt"
14
+
15
+ try:
16
+ model = YOLO(MODEL_PATH)
17
+ print(f"✅ Model loaded: {MODEL_PATH}")
18
+ print(f"📊 Classes: {model.names}")
19
+ except Exception as e:
20
+ print(f"❌ Error loading model: {e}")
21
+ model = None
22
+
23
+
24
+ # ═════════════════════════════════════════════════════════════════════════════
25
+ # CLAHE Preprocessing (Shadow Recovery)
26
+ # ═════════════════════════════════════════════════════════════════════════════
27
+
28
+ def apply_clahe(image: np.ndarray) -> np.ndarray:
29
+ """Apply CLAHE preprocessing for shadow recovery."""
30
+ if image.dtype != np.uint8:
31
+ image = np.clip(image, 0, 255).astype(np.uint8)
32
+
33
+ # LAB color space - only enhance L channel
34
+ bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
35
+ lab = cv2.cvtColor(bgr, cv2.COLOR_BGR2LAB)
36
+ l, a, b = cv2.split(lab)
37
+
38
+ # CLAHE on L channel
39
+ clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
40
+ l_enhanced = clahe.apply(l)
41
+
42
+ # Merge back
43
+ lab_enhanced = cv2.merge([l_enhanced, a, b])
44
+ bgr_enhanced = cv2.cvtColor(lab_enhanced, cv2.COLOR_LAB2BGR)
45
+ rgb_enhanced = cv2.cvtColor(bgr_enhanced, cv2.COLOR_BGR2RGB)
46
+
47
+ return rgb_enhanced
48
+
49
+
50
+ # ═════════════════════════════════════════════════════════════════════════════
51
+ # Detection Functions
52
+ # ═════════════════════════════════════════════════════════════════════════════
53
+
54
+ def detect_engine_parts(image, conf_threshold=0.25, apply_clahe_preprocessing=True):
55
+ """
56
+ Detect engine parts with YOLO.
57
+
58
+ Args:
59
+ image: Input image (PIL or numpy)
60
+ conf_threshold: Confidence threshold (0-1)
61
+ apply_clahe_preprocessing: Whether to apply CLAHE before detection
62
+
63
+ Returns:
64
+ annotated_image: Image with bounding boxes
65
+ results_text: Detection summary
66
+ """
67
+ if model is None:
68
+ return image, "❌ Model not loaded. Please check the model file."
69
+
70
+ # Convert to numpy array
71
+ if isinstance(image, Image.Image):
72
+ image = np.array(image)
73
+
74
+ # Apply CLAHE preprocessing
75
+ if apply_clahe_preprocessing:
76
+ image = apply_clahe(image)
77
+
78
+ # YOLO inference
79
+ results = model.predict(
80
+ source=image,
81
+ conf=conf_threshold,
82
+ iou=0.45,
83
+ imgsz=640,
84
+ verbose=False,
85
+ )
86
+
87
+ # Get annotated image
88
+ annotated = results[0].plot() # BGR format
89
+ annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
90
+
91
+ # Extract detection info
92
+ boxes = results[0].boxes
93
+
94
+ if len(boxes) == 0:
95
+ summary = f"**No detections** (threshold: {conf_threshold:.0%})"
96
+ else:
97
+ summary = f"**Detected {len(boxes)} object(s):**\\n\\n"
98
+
99
+ # Group by class
100
+ class_counts = {}
101
+ for box in boxes:
102
+ class_id = int(box.cls[0])
103
+ class_name = model.names[class_id]
104
+ class_counts[class_name] = class_counts.get(class_name, 0) + 1
105
+
106
+ # Summary by class
107
+ for class_name, count in sorted(class_counts.items()):
108
+ summary += f"• **{class_name}**: {count}\\n"
109
+
110
+ summary += f"\\n**Details:**\\n"
111
+
112
+ # Individual detections
113
+ for i, box in enumerate(boxes, 1):
114
+ x1, y1, x2, y2 = box.xyxy[0].tolist()
115
+ conf = float(box.conf[0])
116
+ class_id = int(box.cls[0])
117
+ class_name = model.names[class_id]
118
+
119
+ summary += f"{i}. **{class_name}** — {conf:.2%} confidence\\n"
120
+
121
+ return annotated_rgb, summary
122
+
123
+
124
+ # ═════════════════════════════════════════════════════════════════════════════
125
+ # Gradio Interface
126
+ # ════════��════════════════════════════════════════════════════════════════════
127
+
128
+ CSS = """
129
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
130
+ body { font-family: 'Inter', sans-serif; }
131
+ .gr-button-primary { background: linear-gradient(90deg, #4CAF50 0%, #45a049 100%) !important; }
132
+ """
133
+
134
+ with gr.Blocks(css=CSS, title="Engine Part Detector") as demo:
135
+
136
+ gr.HTML("""
137
+ <div style='text-align: center; padding: 20px;'>
138
+ <h1 style='color: #2196F3; margin-bottom: 10px;'>⚙️ Engine Part Detection System</h1>
139
+ <p style='color: #666; font-size: 1.1em;'>
140
+ YOLOv8 object detection with CLAHE shadow recovery preprocessing
141
+ </p>
142
+ </div>
143
+ """)
144
+
145
+ with gr.Row():
146
+ with gr.Column(scale=1):
147
+ input_image = gr.Image(
148
+ sources=["upload", "webcam"],
149
+ type="numpy",
150
+ label="Upload Engine Part Image"
151
+ )
152
+
153
+ with gr.Row():
154
+ conf_slider = gr.Slider(
155
+ minimum=0.1,
156
+ maximum=0.9,
157
+ value=0.25,
158
+ step=0.05,
159
+ label="Confidence Threshold",
160
+ info="Lower = more detections (may include false positives)"
161
+ )
162
+
163
+ clahe_checkbox = gr.Checkbox(
164
+ value=True,
165
+ label="Apply CLAHE Preprocessing",
166
+ info="Recovers details in shadowed areas (recommended)"
167
+ )
168
+
169
+ detect_btn = gr.Button("🔍 Detect Parts", variant="primary", size="lg")
170
+
171
+ with gr.Column(scale=1):
172
+ output_image = gr.Image(label="Detection Results")
173
+ output_text = gr.Markdown(label="Summary")
174
+
175
+ # Examples
176
+ gr.Markdown("### 📸 Try these examples:")
177
+ gr.Examples(
178
+ examples=[
179
+ # Add example image paths if you have them
180
+ # ["example1.jpg", 0.25, True],
181
+ # ["example2.jpg", 0.3, True],
182
+ ],
183
+ inputs=[input_image, conf_slider, clahe_checkbox],
184
+ outputs=[output_image, output_text],
185
+ fn=detect_engine_parts,
186
+ cache_examples=False,
187
+ )
188
+
189
+ # Info section
190
+ gr.HTML("""
191
+ <div style='margin-top: 20px; padding: 15px; background: #f5f5f5; border-radius: 8px;'>
192
+ <h3 style='margin-top: 0;'>ℹ️ How to Use</h3>
193
+ <ol>
194
+ <li>Upload an image of an engine part or use your webcam</li>
195
+ <li>Adjust confidence threshold (default 0.25 works well)</li>
196
+ <li>Enable CLAHE preprocessing for better results on shadowed images</li>
197
+ <li>Click "Detect Parts" to run detection</li>
198
+ </ol>
199
+ <p><strong>Supported Classes:</strong> bearing_saddle, piston, defect, crack, corrosion (depends on your training)</p>
200
+ </div>
201
+ """)
202
+
203
+ # Wire up the button
204
+ detect_btn.click(
205
+ fn=detect_engine_parts,
206
+ inputs=[input_image, conf_slider, clahe_checkbox],
207
+ outputs=[output_image, output_text],
208
+ )
209
+
210
+
211
+ # Launch
212
+ if __name__ == "__main__":
213
+ demo.launch(
214
+ server_name="0.0.0.0",
215
+ server_port=7860,
216
+ share=False,
217
+ )