Abs6187 commited on
Commit
73a7a8c
·
verified ·
1 Parent(s): 0ceebcd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +237 -103
app.py CHANGED
@@ -11,88 +11,189 @@ import zipfile
11
  import io
12
  from datetime import datetime
13
 
14
- # Download sample images (optional)
 
 
 
 
 
 
 
15
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-1.jpg?raw=true', 'sample_1.jpg')
16
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-2.jpg?raw=true', 'sample_2.jpg')
17
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-3.jpg?raw=true', 'sample_3.jpg')
18
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-4.jpg?raw=true', 'sample_4.jpg')
19
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-5.jpg?raw=true', 'sample_5.jpg')
20
 
21
- # Load model (cached for performance)
22
  model = YOLO("best.pt")
23
  class_names = {0: 'With Helmet', 1: 'Without Helmet', 2: 'License Plate'}
24
 
25
- def crop_license_plates(image, detections):
26
- """Crop license plates from the image based on detections"""
27
  cropped_plates = []
28
 
29
- if isinstance(image, str): # If image is a file path
30
- image = Image.open(image)
31
- elif isinstance(image, np.ndarray): # If image is numpy array
32
- image = Image.fromarray(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- for detection in detections:
35
- if detection['Object'] == 'License Plate':
36
- # Parse coordinates from position string
37
- pos = detection['Position'].strip('()')
38
- x1, y1 = map(int, pos.split(', '))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- # Parse dimensions
41
- dims = detection['Dimensions']
42
- width, height = map(int, dims.split('x'))
43
  x2, y2 = x1 + width, y1 + height
44
 
45
- # Add some padding around the license plate
46
- padding = 10
47
- x1 = max(0, x1 - padding)
48
- y1 = max(0, y1 - padding)
49
- x2 = min(image.width, x2 + padding)
50
- y2 = min(image.height, y2 + padding)
 
 
 
 
51
 
52
- # Crop the license plate
53
  cropped_plate = image.crop((x1, y1, x2, y2))
54
- cropped_plates.append({
 
 
 
 
 
55
  'image': cropped_plate,
56
  'confidence': detection['Confidence'],
57
- 'position': detection['Position']
58
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  return cropped_plates
61
 
62
  def create_download_files(annotated_image, cropped_plates, detections):
63
- """Create downloadable files including annotated image and cropped plates"""
64
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
65
-
66
- # Create a temporary directory for files
67
- os.makedirs("temp_downloads", exist_ok=True)
68
-
69
- # Save annotated image
70
- annotated_path = f"temp_downloads/annotated_image_{timestamp}.jpg"
71
- annotated_image.save(annotated_path)
72
-
73
- # Save cropped license plates
74
- plate_paths = []
75
- for i, plate_data in enumerate(cropped_plates):
76
- plate_path = f"temp_downloads/license_plate_{i+1}_{timestamp}.jpg"
77
- plate_data['image'].save(plate_path)
78
- plate_paths.append(plate_path)
79
-
80
- # Create detection report
81
- report_path = f"temp_downloads/detection_report_{timestamp}.csv"
82
- if detections:
83
- df = pd.DataFrame(detections)
84
- df.to_csv(report_path, index=False)
85
-
86
- # Create zip file with all results
87
- zip_path = f"temp_downloads/detection_results_{timestamp}.zip"
88
- with zipfile.ZipFile(zip_path, 'w') as zipf:
89
- zipf.write(annotated_path, f"annotated_image_{timestamp}.jpg")
90
- for plate_path in plate_paths:
91
- zipf.write(plate_path, os.path.basename(plate_path))
92
- if os.path.exists(report_path):
93
- zipf.write(report_path, f"detection_report_{timestamp}.csv")
94
-
95
- return zip_path, annotated_path, plate_paths
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  def yoloV8_func(
98
  image=None,
@@ -101,30 +202,24 @@ def yoloV8_func(
101
  iou_threshold=0.5,
102
  show_stats=True,
103
  show_confidence=True,
104
- crop_plates=True
 
105
  ):
106
- # Handle NoneType for image_size
107
  if image_size is None:
108
  image_size = 640
109
 
110
- # Ensure image_size is an integer
111
  if not isinstance(image_size, int):
112
  image_size = int(image_size)
113
 
114
- # Construct imgsz as a list of two integers [width, height]
115
  imgsz = [image_size, image_size]
116
 
117
- # Make predictions
118
  results = model.predict(image, conf=conf_threshold, iou=iou_threshold, imgsz=imgsz)
119
 
120
- # Get the output image with bounding boxes
121
- annotated_image = results[0].plot() # This returns a PIL image
122
 
123
- # Convert to PIL if it's a numpy array
124
  if isinstance(annotated_image, np.ndarray):
125
  annotated_image = Image.fromarray(cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB))
126
 
127
- # Extract detection information
128
  boxes = results[0].boxes
129
  detections = []
130
 
@@ -142,24 +237,42 @@ def yoloV8_func(
142
  "Dimensions": f"{int(x2-x1)}x{int(y2-y1)}"
143
  })
144
 
145
- # Crop license plates if requested
146
  cropped_plates = []
147
  license_plate_gallery = []
 
148
  download_files = None
149
 
150
  if crop_plates and detections:
151
- cropped_plates = crop_license_plates(image, detections)
152
- license_plate_gallery = [plate_data['image'] for plate_data in cropped_plates]
153
-
154
- # Create download files
155
- if cropped_plates or detections:
156
- try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  download_files, _, _ = create_download_files(annotated_image, cropped_plates, detections)
158
- except Exception as e:
159
- print(f"Error creating download files: {e}")
160
- download_files = None
 
 
 
 
 
161
 
162
- # Create stats text
163
  stats_text = ""
164
  if show_stats and detections:
165
  df = pd.DataFrame(detections)
@@ -168,11 +281,15 @@ def yoloV8_func(
168
  for obj, count in counts.items():
169
  stats_text += f"- {obj}: {count}\n"
170
 
171
- # Add license plate info
172
  if cropped_plates:
173
  stats_text += f"\nLicense Plates Cropped: {len(cropped_plates)}\n"
 
 
 
 
 
 
174
 
175
- # Add stats to image if requested
176
  if show_stats and stats_text:
177
  draw = ImageDraw.Draw(annotated_image)
178
  try:
@@ -180,21 +297,19 @@ def yoloV8_func(
180
  except:
181
  font = ImageFont.load_default()
182
 
183
- # Add semi-transparent background for text
184
  text_bbox = draw.textbbox((0, 0), stats_text, font=font)
185
  text_width = text_bbox[2] - text_bbox[0]
186
  text_height = text_bbox[3] - text_bbox[1]
187
  draw.rectangle([10, 10, 20 + text_width, 20 + text_height], fill=(0, 0, 0, 128))
188
 
189
- # Add text
190
  draw.text((15, 15), stats_text, font=font, fill=(255, 255, 255))
191
 
192
- # Create a detection table for display
193
  detection_table = pd.DataFrame(detections) if detections else pd.DataFrame(columns=["Object", "Confidence", "Position", "Dimensions"])
194
 
195
- return annotated_image, detection_table, stats_text, license_plate_gallery, download_files
 
 
196
 
197
- # Define custom CSS for styling
198
  custom_css = """
199
  #title { text-align: center; }
200
  #description { text-align: center; }
@@ -210,16 +325,22 @@ custom_css = """
210
  border-radius: 8px;
211
  margin-top: 10px;
212
  }
 
 
 
 
 
 
213
  """
214
 
215
- # Set up Gradio interface with Blocks for more control
216
  with gr.Blocks(css=custom_css, title="YOLOv11 Motorcyclist Helmet Detection") as demo:
217
- gr.HTML("<h1 id='title'>YOLOv11 Motorcyclist Helmet Detection with License Plate Extraction</h1>")
218
- gr.HTML("""
219
  <div id='description'>
220
  <p>This application uses YOLOv11 to detect Motorcyclists with and without Helmets in images.</p>
221
  <p>Upload an image, adjust the parameters, and view the detection results with detailed statistics.</p>
222
- <p><strong>New Features:</strong> License plate cropping and downloadable results!</p>
 
223
  </div>
224
  """)
225
 
@@ -234,8 +355,16 @@ with gr.Blocks(css=custom_css, title="YOLOv11 Motorcyclist Helmet Detection") as
234
  iou_threshold = gr.Slider(minimum=0.0, maximum=1.0, value=0.5, step=0.05, label="IOU Threshold")
235
  show_stats = gr.Checkbox(value=True, label="Show Statistics on Image")
236
 
 
237
  crop_plates = gr.Checkbox(value=True, label="Crop License Plates")
238
 
 
 
 
 
 
 
 
239
  submit_btn = gr.Button("Detect Objects", variant="primary")
240
  clear_btn = gr.Button("Clear")
241
 
@@ -249,7 +378,6 @@ with gr.Blocks(css=custom_css, title="YOLOv11 Motorcyclist Helmet Detection") as
249
  )
250
  output_stats = gr.Textbox(label="Detection Summary", interactive=False)
251
 
252
- # License plate gallery
253
  gr.Markdown("### Cropped License Plates")
254
  license_gallery = gr.Gallery(
255
  label="Extracted License Plates",
@@ -261,7 +389,15 @@ with gr.Blocks(css=custom_css, title="YOLOv11 Motorcyclist Helmet Detection") as
261
  height="auto"
262
  )
263
 
264
- # Download section
 
 
 
 
 
 
 
 
265
  gr.Markdown("### Download Results")
266
  with gr.Group(elem_classes="download-section"):
267
  download_file = gr.File(
@@ -269,38 +405,36 @@ with gr.Blocks(css=custom_css, title="YOLOv11 Motorcyclist Helmet Detection") as
269
  interactive=False,
270
  visible=True
271
  )
272
- gr.Markdown("*The ZIP file contains: annotated image, cropped license plates, and detection report (CSV)*")
273
 
274
- # Examples
275
  gr.Markdown("### Example Images")
276
  gr.Examples(
277
  examples=[["sample_1.jpg"], ["sample_2.jpg"], ["sample_3.jpg"], ["sample_4.jpg"], ["sample_5.jpg"]],
278
  inputs=input_image,
279
- outputs=[output_image, output_table, output_stats, license_gallery, download_file],
280
- fn=yoloV8_func,
281
  cache_examples=True,
282
  )
283
 
284
- # Footer
285
  gr.HTML("""
286
  <div class='footer'>
287
  <p>Built with Gradio and Ultralytics YOLO</p>
288
  <p>Note: This is a demonstration application. Detection accuracy may vary based on image quality and conditions.</p>
289
- <p><strong>License Plate Privacy:</strong> Extracted license plates are for demonstration purposes only.</p>
 
290
  </div>
291
  """)
292
 
293
- # Button actions
294
  submit_btn.click(
295
  fn=yoloV8_func,
296
- inputs=[input_image, image_size, conf_threshold, iou_threshold, show_stats, gr.State(True), crop_plates],
297
- outputs=[output_image, output_table, output_stats, license_gallery, download_file]
298
  )
299
 
300
  clear_btn.click(
301
- fn=lambda: [None, None, None, None, None],
302
  inputs=[],
303
- outputs=[input_image, output_image, output_table, output_stats, license_gallery, download_file]
304
  )
305
 
306
  if __name__ == "__main__":
 
11
  import io
12
  from datetime import datetime
13
 
14
+ try:
15
+ from license_plate_ocr import extract_license_plate_text
16
+ OCR_AVAILABLE = True
17
+ print("OCR module loaded successfully")
18
+ except ImportError as e:
19
+ print(f"OCR module not available: {e}")
20
+ OCR_AVAILABLE = False
21
+
22
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-1.jpg?raw=true', 'sample_1.jpg')
23
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-2.jpg?raw=true', 'sample_2.jpg')
24
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-3.jpg?raw=true', 'sample_3.jpg')
25
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-4.jpg?raw=true', 'sample_4.jpg')
26
  torch.hub.download_url_to_file('https://github.com/Janno1402/Helmet-License-Plate-Detection/blob/main/Sample-Image-5.jpg?raw=true', 'sample_5.jpg')
27
 
 
28
  model = YOLO("best.pt")
29
  class_names = {0: 'With Helmet', 1: 'Without Helmet', 2: 'License Plate'}
30
 
31
+ def crop_license_plates(image, detections, extract_text=False):
 
32
  cropped_plates = []
33
 
34
+ try:
35
+ if isinstance(image, str):
36
+ if not os.path.exists(image):
37
+ print(f"Error: Image file not found: {image}")
38
+ return cropped_plates
39
+ image = Image.open(image)
40
+ elif isinstance(image, np.ndarray):
41
+ image = Image.fromarray(image)
42
+ elif not isinstance(image, Image.Image):
43
+ print(f"Error: Unsupported image type: {type(image)}")
44
+ return cropped_plates
45
+
46
+ if image.size[0] == 0 or image.size[1] == 0:
47
+ print("Error: Image has zero dimensions")
48
+ return cropped_plates
49
+
50
+ except Exception as e:
51
+ print(f"Error loading image: {e}")
52
+ return cropped_plates
53
 
54
+ for i, detection in enumerate(detections):
55
+ try:
56
+ if detection['Object'] != 'License Plate':
57
+ continue
58
+
59
+ pos_str = detection['Position'].strip('()')
60
+ if ',' not in pos_str:
61
+ print(f"Error: Invalid position format for detection {i}: {detection['Position']}")
62
+ continue
63
+
64
+ x1, y1 = map(int, pos_str.split(', '))
65
+
66
+ dims_str = detection['Dimensions']
67
+ if 'x' not in dims_str:
68
+ print(f"Error: Invalid dimensions format for detection {i}: {detection['Dimensions']}")
69
+ continue
70
+
71
+ width, height = map(int, dims_str.split('x'))
72
+
73
+ if width <= 0 or height <= 0:
74
+ print(f"Error: Invalid dimensions for detection {i}: {width}x{height}")
75
+ continue
76
 
 
 
 
77
  x2, y2 = x1 + width, y1 + height
78
 
79
+ if x1 < 0 or y1 < 0 or x2 > image.width or y2 > image.height:
80
+ print(f"Warning: Bounding box extends beyond image boundaries for detection {i}")
81
+ x1 = max(0, x1)
82
+ y1 = max(0, y1)
83
+ x2 = min(image.width, x2)
84
+ y2 = min(image.height, y2)
85
+
86
+ if x2 <= x1 or y2 <= y1:
87
+ print(f"Error: Invalid crop coordinates for detection {i}: ({x1},{y1}) to ({x2},{y2})")
88
+ continue
89
 
 
90
  cropped_plate = image.crop((x1, y1, x2, y2))
91
+
92
+ if cropped_plate.size[0] == 0 or cropped_plate.size[1] == 0:
93
+ print(f"Error: Cropped image has zero dimensions for detection {i}")
94
+ continue
95
+
96
+ plate_data = {
97
  'image': cropped_plate,
98
  'confidence': detection['Confidence'],
99
+ 'position': detection['Position'],
100
+ 'crop_coords': f"({x1},{y1}) to ({x2},{y2})",
101
+ 'text': 'Processing...'
102
+ }
103
+
104
+ if extract_text and OCR_AVAILABLE:
105
+ try:
106
+ print(f"Extracting text from license plate {i+1}...")
107
+ plate_text = extract_license_plate_text(cropped_plate)
108
+ if plate_text and plate_text.strip() and not plate_text.startswith('Error'):
109
+ plate_data['text'] = plate_text.strip()
110
+ print(f"Extracted text: {plate_text.strip()}")
111
+ else:
112
+ plate_data['text'] = 'No text detected'
113
+ print(f"No text found in plate {i+1}")
114
+ except Exception as e:
115
+ print(f"OCR extraction failed for plate {i+1}: {e}")
116
+ plate_data['text'] = f'OCR Failed: {str(e)}'
117
+ elif extract_text and not OCR_AVAILABLE:
118
+ plate_data['text'] = 'OCR not available'
119
+ else:
120
+ plate_data['text'] = 'OCR disabled'
121
+
122
+ cropped_plates.append(plate_data)
123
+
124
+ except ValueError as e:
125
+ print(f"Error parsing coordinates for detection {i}: {e}")
126
+ continue
127
+ except Exception as e:
128
+ print(f"Error cropping license plate {i}: {e}")
129
+ continue
130
 
131
  return cropped_plates
132
 
133
  def create_download_files(annotated_image, cropped_plates, detections):
134
+ try:
135
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
136
+
137
+ os.makedirs("temp", exist_ok=True)
138
+
139
+ annotated_path = f"temp/annotated_image_{timestamp}.jpg"
140
+ try:
141
+ annotated_image.save(annotated_path, quality=95)
142
+ except Exception as e:
143
+ print(f"Error saving annotated image: {e}")
144
+ return None, None, []
145
+
146
+ plate_paths = []
147
+ for i, plate_data in enumerate(cropped_plates):
148
+ try:
149
+ plate_path = f"temp/license_plate_{i+1}_{timestamp}.jpg"
150
+ plate_data['image'].save(plate_path, quality=95)
151
+ plate_paths.append(plate_path)
152
+ except Exception as e:
153
+ print(f"Error saving license plate {i+1}: {e}")
154
+ continue
155
+
156
+ report_data = []
157
+ for detection in detections:
158
+ report_data.append(detection)
159
+
160
+ for i, plate_data in enumerate(cropped_plates):
161
+ report_data.append({
162
+ 'Object': f'License Plate {i+1} - Text',
163
+ 'Confidence': plate_data['confidence'],
164
+ 'Position': plate_data['position'],
165
+ 'Dimensions': 'Extracted Text',
166
+ 'Text': plate_data.get('text', 'N/A')
167
+ })
168
+
169
+ report_path = f"temp/detection_report_{timestamp}.csv"
170
+ if report_data:
171
+ try:
172
+ df = pd.DataFrame(report_data)
173
+ df.to_csv(report_path, index=False)
174
+ except Exception as e:
175
+ print(f"Error creating detection report: {e}")
176
+ report_path = None
177
+
178
+ zip_path = f"temp/detection_results_{timestamp}.zip"
179
+ try:
180
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
181
+ if os.path.exists(annotated_path):
182
+ zipf.write(annotated_path, f"annotated_image_{timestamp}.jpg")
183
+ for plate_path in plate_paths:
184
+ if os.path.exists(plate_path):
185
+ zipf.write(plate_path, os.path.basename(plate_path))
186
+ if report_path and os.path.exists(report_path):
187
+ zipf.write(report_path, f"detection_report_{timestamp}.csv")
188
+ except Exception as e:
189
+ print(f"Error creating ZIP file: {e}")
190
+ return None, annotated_path, plate_paths
191
+
192
+ return zip_path, annotated_path, plate_paths
193
+
194
+ except Exception as e:
195
+ print(f"Error in create_download_files: {e}")
196
+ return None, None, []
197
 
198
  def yoloV8_func(
199
  image=None,
 
202
  iou_threshold=0.5,
203
  show_stats=True,
204
  show_confidence=True,
205
+ crop_plates=True,
206
+ extract_text=False
207
  ):
 
208
  if image_size is None:
209
  image_size = 640
210
 
 
211
  if not isinstance(image_size, int):
212
  image_size = int(image_size)
213
 
 
214
  imgsz = [image_size, image_size]
215
 
 
216
  results = model.predict(image, conf=conf_threshold, iou=iou_threshold, imgsz=imgsz)
217
 
218
+ annotated_image = results[0].plot()
 
219
 
 
220
  if isinstance(annotated_image, np.ndarray):
221
  annotated_image = Image.fromarray(cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB))
222
 
 
223
  boxes = results[0].boxes
224
  detections = []
225
 
 
237
  "Dimensions": f"{int(x2-x1)}x{int(y2-y1)}"
238
  })
239
 
 
240
  cropped_plates = []
241
  license_plate_gallery = []
242
+ plate_texts = []
243
  download_files = None
244
 
245
  if crop_plates and detections:
246
+ try:
247
+ print(f"Processing {len([d for d in detections if d['Object'] == 'License Plate'])} license plates...")
248
+ cropped_plates = crop_license_plates(image, detections, extract_text)
249
+ print(f"Successfully cropped {len(cropped_plates)} license plates")
250
+
251
+ license_plate_gallery = [plate_data['image'] for plate_data in cropped_plates]
252
+
253
+ if extract_text and OCR_AVAILABLE:
254
+ print("Extracting text from license plates...")
255
+ plate_texts = []
256
+ for i, plate_data in enumerate(cropped_plates):
257
+ text = plate_data.get('text', 'No text detected')
258
+ print(f"Plate {i+1} text: {text}")
259
+ plate_texts.append(f"Plate {i+1}: {text}")
260
+ elif extract_text and not OCR_AVAILABLE:
261
+ plate_texts = ["OCR not available - install requirements: pip install transformers easyocr"]
262
+ elif not extract_text:
263
+ plate_texts = [f"Plate {i+1}: Text extraction disabled" for i in range(len(cropped_plates))]
264
+
265
+ if cropped_plates or detections:
266
  download_files, _, _ = create_download_files(annotated_image, cropped_plates, detections)
267
+ if download_files is None:
268
+ print("Warning: Could not create download files")
269
+ except Exception as e:
270
+ print(f"Error in license plate processing: {e}")
271
+ cropped_plates = []
272
+ license_plate_gallery = []
273
+ plate_texts = ["Error processing license plates"]
274
+ download_files = None
275
 
 
276
  stats_text = ""
277
  if show_stats and detections:
278
  df = pd.DataFrame(detections)
 
281
  for obj, count in counts.items():
282
  stats_text += f"- {obj}: {count}\n"
283
 
 
284
  if cropped_plates:
285
  stats_text += f"\nLicense Plates Cropped: {len(cropped_plates)}\n"
286
+
287
+ if extract_text and OCR_AVAILABLE:
288
+ stats_text += "Extracted Text:\n"
289
+ for i, plate_data in enumerate(cropped_plates):
290
+ text = plate_data.get('text', 'No text')
291
+ stats_text += f"- Plate {i+1}: {text}\n"
292
 
 
293
  if show_stats and stats_text:
294
  draw = ImageDraw.Draw(annotated_image)
295
  try:
 
297
  except:
298
  font = ImageFont.load_default()
299
 
 
300
  text_bbox = draw.textbbox((0, 0), stats_text, font=font)
301
  text_width = text_bbox[2] - text_bbox[0]
302
  text_height = text_bbox[3] - text_bbox[1]
303
  draw.rectangle([10, 10, 20 + text_width, 20 + text_height], fill=(0, 0, 0, 128))
304
 
 
305
  draw.text((15, 15), stats_text, font=font, fill=(255, 255, 255))
306
 
 
307
  detection_table = pd.DataFrame(detections) if detections else pd.DataFrame(columns=["Object", "Confidence", "Position", "Dimensions"])
308
 
309
+ plate_text_output = "\n".join(plate_texts) if plate_texts else "No license plates detected or OCR disabled"
310
+
311
+ return annotated_image, detection_table, stats_text, license_plate_gallery, download_files, plate_text_output
312
 
 
313
  custom_css = """
314
  #title { text-align: center; }
315
  #description { text-align: center; }
 
325
  border-radius: 8px;
326
  margin-top: 10px;
327
  }
328
+ .ocr-section {
329
+ background-color: #e8f4fd;
330
+ padding: 15px;
331
+ border-radius: 8px;
332
+ margin-top: 10px;
333
+ }
334
  """
335
 
 
336
  with gr.Blocks(css=custom_css, title="YOLOv11 Motorcyclist Helmet Detection") as demo:
337
+ gr.HTML("<h1 id='title'>YOLOv11 Motorcyclist Helmet Detection with Optional OCR</h1>")
338
+ gr.HTML(f"""
339
  <div id='description'>
340
  <p>This application uses YOLOv11 to detect Motorcyclists with and without Helmets in images.</p>
341
  <p>Upload an image, adjust the parameters, and view the detection results with detailed statistics.</p>
342
+ <p><strong>Features:</strong> License plate cropping and optional text recognition!</p>
343
+ <p><strong>OCR Status:</strong> {'✅ Available' if OCR_AVAILABLE else '❌ Not Available (install requirements)'}</p>
344
  </div>
345
  """)
346
 
 
355
  iou_threshold = gr.Slider(minimum=0.0, maximum=1.0, value=0.5, step=0.05, label="IOU Threshold")
356
  show_stats = gr.Checkbox(value=True, label="Show Statistics on Image")
357
 
358
+ gr.Markdown("### License Plate Options")
359
  crop_plates = gr.Checkbox(value=True, label="Crop License Plates")
360
 
361
+ if OCR_AVAILABLE:
362
+ extract_text = gr.Checkbox(value=False, label="Extract Text from License Plates (OCR)")
363
+ gr.Markdown("*Note: OCR processing may take additional time*")
364
+ else:
365
+ extract_text = gr.Checkbox(value=False, label="Extract Text (OCR Not Available)", interactive=False)
366
+ gr.Markdown("*Install requirements: `pip install torch transformers easyocr opencv-python`*")
367
+
368
  submit_btn = gr.Button("Detect Objects", variant="primary")
369
  clear_btn = gr.Button("Clear")
370
 
 
378
  )
379
  output_stats = gr.Textbox(label="Detection Summary", interactive=False)
380
 
 
381
  gr.Markdown("### Cropped License Plates")
382
  license_gallery = gr.Gallery(
383
  label="Extracted License Plates",
 
389
  height="auto"
390
  )
391
 
392
+ with gr.Group(elem_classes="ocr-section"):
393
+ gr.Markdown("### License Plate Text Recognition")
394
+ plate_text_output = gr.Textbox(
395
+ label="Extracted Text",
396
+ placeholder="License plate text will appear here when OCR is enabled",
397
+ lines=3,
398
+ interactive=False
399
+ )
400
+
401
  gr.Markdown("### Download Results")
402
  with gr.Group(elem_classes="download-section"):
403
  download_file = gr.File(
 
405
  interactive=False,
406
  visible=True
407
  )
408
+ gr.Markdown("*The ZIP file contains: annotated image, cropped license plates, and detection report with OCR results*")
409
 
 
410
  gr.Markdown("### Example Images")
411
  gr.Examples(
412
  examples=[["sample_1.jpg"], ["sample_2.jpg"], ["sample_3.jpg"], ["sample_4.jpg"], ["sample_5.jpg"]],
413
  inputs=input_image,
414
+ outputs=[output_image, output_table, output_stats, license_gallery, download_file, plate_text_output],
415
+ fn=lambda img: yoloV8_func(img, 640, 0.4, 0.5, True, True, True, False),
416
  cache_examples=True,
417
  )
418
 
 
419
  gr.HTML("""
420
  <div class='footer'>
421
  <p>Built with Gradio and Ultralytics YOLO</p>
422
  <p>Note: This is a demonstration application. Detection accuracy may vary based on image quality and conditions.</p>
423
+ <p><strong>License Plate Privacy:</strong> Extracted license plates and text are for demonstration purposes only.</p>
424
+ <p><strong>Requirements for OCR:</strong> torch, transformers, easyocr, opencv-python</p>
425
  </div>
426
  """)
427
 
 
428
  submit_btn.click(
429
  fn=yoloV8_func,
430
+ inputs=[input_image, image_size, conf_threshold, iou_threshold, show_stats, gr.State(True), crop_plates, extract_text],
431
+ outputs=[output_image, output_table, output_stats, license_gallery, download_file, plate_text_output]
432
  )
433
 
434
  clear_btn.click(
435
+ fn=lambda: [None, None, None, None, None, None],
436
  inputs=[],
437
+ outputs=[input_image, output_image, output_table, output_stats, license_gallery, download_file, plate_text_output]
438
  )
439
 
440
  if __name__ == "__main__":