Muhammed Sezer commited on
Commit
40c6797
Β·
1 Parent(s): 34d2ee5

parameters added

Browse files
Files changed (1) hide show
  1. app.py +121 -30
app.py CHANGED
@@ -7,7 +7,10 @@ visual annotations and structured data output.
7
  """
8
 
9
  import logging
 
10
  import sys
 
 
11
  from typing import Tuple, Optional
12
 
13
  import cv2
@@ -70,50 +73,55 @@ def preprocess_image(image: Image.Image) -> np.ndarray:
70
  raise
71
 
72
 
73
- def detect_objects(image: Image.Image) -> Tuple[Image.Image, pd.DataFrame]:
74
  """
75
  Main object detection function that processes images through YOLOv8.
76
 
77
  Args:
78
  image (Image.Image): PIL Image object to process
 
79
 
80
  Returns:
81
- Tuple[Image.Image, pd.DataFrame]: Annotated image and detection results table
82
  """
83
  if model is None:
84
  logger.error("YOLOv8 model not loaded")
85
  # Return original image and empty dataframe
86
  empty_df = pd.DataFrame(columns=['class', 'confidence', 'x1', 'y1', 'x2', 'y2'])
87
- return image, empty_df
88
 
89
  try:
90
  # Preprocess the image
91
- logger.info("Starting object detection...")
92
  processed_image = preprocess_image(image)
93
 
94
- # Run YOLOv8 inference
95
- results = model(processed_image)
96
 
97
  # Parse detection results
98
- annotated_image, detection_data = parse_detection_results(results[0], image)
99
 
100
- logger.info("Object detection completed successfully")
101
- return annotated_image, detection_data
 
 
 
102
 
103
  except Exception as e:
104
  logger.error("Error during object detection: %s", str(e))
105
  # Return original image and empty dataframe on error
106
  empty_df = pd.DataFrame(columns=['class', 'confidence', 'x1', 'y1', 'x2', 'y2'])
107
- return image, empty_df
108
 
109
 
110
- def parse_detection_results(results, original_image: Image.Image) -> Tuple[Image.Image, pd.DataFrame]:
111
  """
112
  Parse YOLOv8 detection results to extract classes, confidence scores, and bounding boxes.
113
 
114
  Args:
115
  results: YOLOv8 detection results object
116
  original_image (Image.Image): Original PIL image for annotation
 
117
 
118
  Returns:
119
  Tuple[Image.Image, pd.DataFrame]: Annotated image and structured detection data
@@ -132,7 +140,7 @@ def parse_detection_results(results, original_image: Image.Image) -> Tuple[Image
132
  classes = results.boxes.cls.cpu().numpy()
133
  confidences = results.boxes.conf.cpu().numpy()
134
 
135
- # Process each detection
136
  for i in range(len(boxes)):
137
  class_id = int(classes[i])
138
  class_name = class_names[class_id]
@@ -151,9 +159,9 @@ def parse_detection_results(results, original_image: Image.Image) -> Tuple[Image
151
 
152
  detection_list.append(detection_record)
153
 
154
- logger.info("Parsed %d detections", len(detection_list))
155
  else:
156
- logger.info("No objects detected in the image")
157
 
158
  # Create DataFrame from detection list with proper columns
159
  if detection_list:
@@ -183,7 +191,7 @@ def generate_annotated_image(results, original_image: Image.Image) -> Image.Imag
183
  original_image (Image.Image): Original PIL image
184
 
185
  Returns:
186
- Image.Image: Annotated PIL image with bounding boxes and labels
187
  """
188
  try:
189
  # Use YOLOv8's built-in plot method to generate annotations
@@ -196,11 +204,16 @@ def generate_annotated_image(results, original_image: Image.Image) -> Image.Imag
196
  font_size=12 # Font size for labels
197
  )
198
 
199
- # Convert BGR to RGB (YOLOv8 plot returns BGR format)
200
- annotated_rgb = cv2.cvtColor(annotated_array, cv2.COLOR_BGR2RGB)
 
 
 
 
 
201
 
202
  # Convert NumPy array back to PIL Image
203
- annotated_image = Image.fromarray(annotated_rgb)
204
 
205
  # Ensure the annotated image maintains aspect ratio
206
  # The YOLOv8 plot method should preserve the original dimensions
@@ -214,9 +227,55 @@ def generate_annotated_image(results, original_image: Image.Image) -> Image.Imag
214
  return original_image
215
 
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  def create_gradio_interface():
218
  """
219
- Create Gradio Interface with image upload input and dual output.
220
 
221
  Returns:
222
  gr.Interface: Configured Gradio interface
@@ -224,16 +283,26 @@ def create_gradio_interface():
224
  # Create the Gradio interface
225
  demo = gr.Interface(
226
  fn=detect_objects,
227
- inputs=gr.Image(
228
- type="pil",
229
- label="Upload Image for Object Detection",
230
- sources=["upload", "clipboard"],
231
- height=400
232
- ),
 
 
 
 
 
 
 
 
 
 
233
  outputs=[
234
  gr.Image(
235
  type="pil",
236
- label="Annotated Image with Detections",
237
  height=400,
238
  show_label=True
239
  ),
@@ -244,9 +313,13 @@ def create_gradio_interface():
244
  col_count=(6, "fixed"),
245
  row_count=(10, "dynamic"),
246
  interactive=False
 
 
 
 
247
  )
248
  ],
249
- title="🎯 YOLOv8 Object Detection",
250
  description="""
251
  **Upload an image to detect objects using YOLOv8!**
252
 
@@ -254,14 +327,29 @@ def create_gradio_interface():
254
  You'll get both a visual representation with bounding boxes and a detailed data table.
255
 
256
  **Supported formats:** JPG, PNG, JPEG
 
 
 
 
 
 
257
  """,
258
  article="""
259
  ### How it works:
260
  1. **Upload** an image using the interface above
261
- 2. **View** the annotated image with bounding boxes and labels
262
- 3. **Analyze** the detection data in the table with confidence scores and coordinates
 
 
 
 
 
 
 
 
263
 
264
  The confidence scores are rounded to 3 decimal places, and coordinates to 1 decimal place for clarity.
 
265
  """,
266
  theme=gr.themes.Soft(),
267
  css="""
@@ -271,12 +359,15 @@ def create_gradio_interface():
271
  .output-image {
272
  height: 400px !important;
273
  }
 
 
 
274
  """,
275
  examples=[
276
  # Add example images if available
277
  ],
278
  cache_examples=False,
279
- allow_flagging="never"
280
  )
281
 
282
  return demo
 
7
  """
8
 
9
  import logging
10
+ import os
11
  import sys
12
+ import tempfile
13
+ from datetime import datetime
14
  from typing import Tuple, Optional
15
 
16
  import cv2
 
73
  raise
74
 
75
 
76
+ def detect_objects(image: Image.Image, conf_threshold: float = 0.25) -> Tuple[Image.Image, pd.DataFrame, str]:
77
  """
78
  Main object detection function that processes images through YOLOv8.
79
 
80
  Args:
81
  image (Image.Image): PIL Image object to process
82
+ conf_threshold (float): Confidence threshold for filtering detections (0.0 to 1.0)
83
 
84
  Returns:
85
+ Tuple[Image.Image, pd.DataFrame, str]: Annotated image, detection results table, and CSV file path
86
  """
87
  if model is None:
88
  logger.error("YOLOv8 model not loaded")
89
  # Return original image and empty dataframe
90
  empty_df = pd.DataFrame(columns=['class', 'confidence', 'x1', 'y1', 'x2', 'y2'])
91
+ return image, empty_df, None
92
 
93
  try:
94
  # Preprocess the image
95
+ logger.info("Starting object detection with confidence threshold: %.2f", conf_threshold)
96
  processed_image = preprocess_image(image)
97
 
98
+ # Run YOLOv8 inference with confidence threshold
99
+ results = model(processed_image, conf=conf_threshold)
100
 
101
  # Parse detection results
102
+ annotated_image, detection_data = parse_detection_results(results[0], image, conf_threshold)
103
 
104
+ # Create CSV file for download
105
+ csv_file_path = create_csv_download(detection_data, conf_threshold)
106
+
107
+ logger.info("Object detection completed successfully with %d detections", len(detection_data))
108
+ return annotated_image, detection_data, csv_file_path
109
 
110
  except Exception as e:
111
  logger.error("Error during object detection: %s", str(e))
112
  # Return original image and empty dataframe on error
113
  empty_df = pd.DataFrame(columns=['class', 'confidence', 'x1', 'y1', 'x2', 'y2'])
114
+ return image, empty_df, None
115
 
116
 
117
+ def parse_detection_results(results, original_image: Image.Image, conf_threshold: float = 0.25) -> Tuple[Image.Image, pd.DataFrame]:
118
  """
119
  Parse YOLOv8 detection results to extract classes, confidence scores, and bounding boxes.
120
 
121
  Args:
122
  results: YOLOv8 detection results object
123
  original_image (Image.Image): Original PIL image for annotation
124
+ conf_threshold (float): Confidence threshold used for filtering
125
 
126
  Returns:
127
  Tuple[Image.Image, pd.DataFrame]: Annotated image and structured detection data
 
140
  classes = results.boxes.cls.cpu().numpy()
141
  confidences = results.boxes.conf.cpu().numpy()
142
 
143
+ # Process each detection (already filtered by YOLOv8 based on conf_threshold)
144
  for i in range(len(boxes)):
145
  class_id = int(classes[i])
146
  class_name = class_names[class_id]
 
159
 
160
  detection_list.append(detection_record)
161
 
162
+ logger.info("Parsed %d detections above confidence threshold %.2f", len(detection_list), conf_threshold)
163
  else:
164
+ logger.info("No objects detected above confidence threshold %.2f", conf_threshold)
165
 
166
  # Create DataFrame from detection list with proper columns
167
  if detection_list:
 
191
  original_image (Image.Image): Original PIL image
192
 
193
  Returns:
194
+ Image.Image: Annotated PIL image with bounding boxes and labels in RGB format
195
  """
196
  try:
197
  # Use YOLOv8's built-in plot method to generate annotations
 
204
  font_size=12 # Font size for labels
205
  )
206
 
207
+ # IMPORTANT: YOLOv8's plot() method returns BGR format, but Gradio expects RGB
208
+ # Convert BGR to RGB to fix color display issue
209
+ if len(annotated_array.shape) == 3 and annotated_array.shape[2] == 3:
210
+ annotated_rgb = cv2.cvtColor(annotated_array, cv2.COLOR_BGR2RGB)
211
+ else:
212
+ # Fallback if image format is unexpected
213
+ annotated_rgb = annotated_array
214
 
215
  # Convert NumPy array back to PIL Image
216
+ annotated_image = Image.fromarray(annotated_rgb.astype(np.uint8))
217
 
218
  # Ensure the annotated image maintains aspect ratio
219
  # The YOLOv8 plot method should preserve the original dimensions
 
227
  return original_image
228
 
229
 
230
+ def create_csv_download(detection_data: pd.DataFrame, conf_threshold: float = 0.25) -> str:
231
+ """
232
+ Create a CSV file from detection results for download.
233
+
234
+ Args:
235
+ detection_data (pd.DataFrame): Detection results dataframe
236
+ conf_threshold (float): Confidence threshold used for filtering
237
+
238
+ Returns:
239
+ str: Path to the created CSV file
240
+ """
241
+ try:
242
+ # Create a temporary CSV file
243
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
244
+ csv_filename = f"detection_results_{timestamp}.csv"
245
+
246
+ # Use a temporary directory
247
+ temp_dir = tempfile.gettempdir()
248
+ csv_path = os.path.join(temp_dir, csv_filename)
249
+
250
+ # Save DataFrame to CSV with additional metadata
251
+ if not detection_data.empty:
252
+ # Add metadata to the CSV
253
+ with open(csv_path, 'w', newline='', encoding='utf-8') as f:
254
+ f.write(f"# YOLOv8 Object Detection Results\n")
255
+ f.write(f"# Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
256
+ f.write(f"# Confidence threshold: {conf_threshold:.2f} ({conf_threshold*100:.0f}%)\n")
257
+ f.write(f"# Total detections: {len(detection_data)}\n")
258
+ f.write("# Columns: class, confidence, x1, y1, x2, y2\n")
259
+ f.write("# Coordinates are in pixels (x1,y1 = top-left, x2,y2 = bottom-right)\n")
260
+ f.write("#\n")
261
+
262
+ # Append the actual data
263
+ detection_data.to_csv(csv_path, mode='a', index=False)
264
+ else:
265
+ # Create empty CSV with headers
266
+ detection_data.to_csv(csv_path, index=False)
267
+
268
+ logger.info("CSV file created successfully: %s", csv_path)
269
+ return csv_path
270
+
271
+ except Exception as e:
272
+ logger.error("Error creating CSV file: %s", str(e))
273
+ return None
274
+
275
+
276
  def create_gradio_interface():
277
  """
278
+ Create Gradio Interface with image upload, confidence slider, and triple output.
279
 
280
  Returns:
281
  gr.Interface: Configured Gradio interface
 
283
  # Create the Gradio interface
284
  demo = gr.Interface(
285
  fn=detect_objects,
286
+ inputs=[
287
+ gr.Image(
288
+ type="pil",
289
+ label="Upload Image for Object Detection",
290
+ sources=["upload", "clipboard"],
291
+ height=400
292
+ ),
293
+ gr.Slider(
294
+ minimum=0.01,
295
+ maximum=1.0,
296
+ value=0.25,
297
+ step=0.01,
298
+ label="Confidence Threshold",
299
+ info="Only show detections above this confidence level (0.01 = 1%, 1.0 = 100%)"
300
+ )
301
+ ],
302
  outputs=[
303
  gr.Image(
304
  type="pil",
305
+ label="Annotated Image with Detections (RGB Fixed)",
306
  height=400,
307
  show_label=True
308
  ),
 
313
  col_count=(6, "fixed"),
314
  row_count=(10, "dynamic"),
315
  interactive=False
316
+ ),
317
+ gr.File(
318
+ label="Download Results as CSV",
319
+ visible=True
320
  )
321
  ],
322
+ title="🎯 YOLOv8 Object Detection with Confidence Control",
323
  description="""
324
  **Upload an image to detect objects using YOLOv8!**
325
 
 
327
  You'll get both a visual representation with bounding boxes and a detailed data table.
328
 
329
  **Supported formats:** JPG, PNG, JPEG
330
+
331
+ **Features:**
332
+ - βœ… Fixed RGB color display issue
333
+ - 🎚️ Adjustable confidence threshold slider
334
+ - πŸ“₯ Download detection results as CSV
335
+ - πŸ“Š Real-time filtering based on confidence scores
336
  """,
337
  article="""
338
  ### How it works:
339
  1. **Upload** an image using the interface above
340
+ 2. **Adjust** the confidence threshold slider to filter detections (default: 0.25 = 25%)
341
+ 3. **View** the annotated image with bounding boxes and labels (RGB colors fixed)
342
+ 4. **Analyze** the detection data in the table with confidence scores and coordinates
343
+ 5. **Download** the results as a CSV file for further analysis
344
+
345
+ **Confidence Threshold Guide:**
346
+ - **0.01-0.20**: Very permissive - shows many detections, including uncertain ones
347
+ - **0.25-0.50**: Balanced - good mix of accuracy and detection count (recommended)
348
+ - **0.50-0.80**: Conservative - only high-confidence detections
349
+ - **0.80-1.00**: Very strict - only extremely confident detections
350
 
351
  The confidence scores are rounded to 3 decimal places, and coordinates to 1 decimal place for clarity.
352
+ The CSV file includes all detection data with timestamps for easy tracking.
353
  """,
354
  theme=gr.themes.Soft(),
355
  css="""
 
359
  .output-image {
360
  height: 400px !important;
361
  }
362
+ .slider-container {
363
+ margin: 10px 0;
364
+ }
365
  """,
366
  examples=[
367
  # Add example images if available
368
  ],
369
  cache_examples=False,
370
+ flagging_mode="never"
371
  )
372
 
373
  return demo