Abs6187 commited on
Commit
a1ab077
·
verified ·
1 Parent(s): 0161578

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -580
app.py CHANGED
@@ -8,652 +8,236 @@ from detector import (
8
  OCR_AVAILABLE
9
  )
10
 
11
- # Import advanced OCR functions if available
12
  try:
13
  from advanced_ocr import get_available_models
14
  except ImportError:
15
  def get_available_models():
16
  return {}
17
 
18
- # Download sample images
19
  download_sample_images()
20
 
21
- # Get OCR status
22
  ocr_status = get_ocr_status()
23
-
24
- # Enhanced Modern CSS
25
  custom_css = """
26
- /* Global Styles */
27
- * {
28
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
29
- }
30
-
31
- /* Main container styling */
32
  .gradio-container {
33
- max-width: 1400px !important;
34
  margin: 0 auto;
35
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
36
- min-height: 100vh;
37
  }
38
 
39
- /* Header styles */
40
- .header-container {
41
- background: rgba(255, 255, 255, 0.1);
42
- backdrop-filter: blur(20px);
43
- border-radius: 20px;
44
- padding: 2rem;
45
  margin-bottom: 2rem;
46
- border: 1px solid rgba(255, 255, 255, 0.2);
47
- box-shadow: 0 25px 50px rgba(0, 0, 0, 0.1);
48
  }
49
 
50
  .main-title {
51
- background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1);
52
- -webkit-background-clip: text;
53
- -webkit-text-fill-color: transparent;
54
- background-clip: text;
55
- font-size: 3rem;
56
- font-weight: 800;
57
- text-align: center;
58
  margin: 0;
59
- text-shadow: 0 2px 4px rgba(0,0,0,0.1);
60
- animation: gradient-shift 3s ease-in-out infinite alternate;
61
- }
62
-
63
- @keyframes gradient-shift {
64
- 0% { filter: hue-rotate(0deg); }
65
- 100% { filter: hue-rotate(20deg); }
66
  }
67
 
68
  .subtitle {
69
- text-align: center;
70
- color: rgba(255, 255, 255, 0.9);
71
- font-size: 1.2rem;
72
- margin-top: 1rem;
73
- font-weight: 300;
74
  }
75
 
76
- .status-badge {
77
- display: inline-block;
78
- padding: 0.5rem 1rem;
79
- border-radius: 25px;
80
- font-weight: 600;
81
  font-size: 0.9rem;
82
- margin: 0.5rem;
83
- backdrop-filter: blur(10px);
84
- border: 1px solid rgba(255, 255, 255, 0.2);
85
- }
86
-
87
- .status-success {
88
- background: linear-gradient(45deg, #56ab2f, #a8e6cf);
89
- color: white;
90
- box-shadow: 0 4px 15px rgba(86, 171, 47, 0.3);
91
- }
92
-
93
- .status-warning {
94
- background: linear-gradient(45deg, #f7971e, #ffd200);
95
- color: #333;
96
- box-shadow: 0 4px 15px rgba(247, 151, 30, 0.3);
97
- }
98
-
99
- .status-error {
100
- background: linear-gradient(45deg, #ff416c, #ff4b2b);
101
- color: white;
102
- box-shadow: 0 4px 15px rgba(255, 65, 108, 0.3);
103
- }
104
-
105
- /* Card styles with glassmorphism */
106
- .glass-card {
107
- background: rgba(255, 255, 255, 0.1) !important;
108
- backdrop-filter: blur(20px) !important;
109
- border-radius: 20px !important;
110
- border: 1px solid rgba(255, 255, 255, 0.2) !important;
111
- box-shadow: 0 25px 50px rgba(0, 0, 0, 0.1) !important;
112
- padding: 1.5rem !important;
113
- margin: 1rem 0 !important;
114
- transition: all 0.3s ease !important;
115
- }
116
-
117
- .glass-card:hover {
118
- transform: translateY(-5px) !important;
119
- box-shadow: 0 35px 60px rgba(0, 0, 0, 0.15) !important;
120
- }
121
-
122
- /* Input controls styling */
123
- .input-group {
124
- background: rgba(255, 255, 255, 0.05);
125
- border-radius: 15px;
126
- padding: 1.5rem;
127
- margin: 1rem 0;
128
- border: 1px solid rgba(255, 255, 255, 0.1);
129
- }
130
-
131
- /* Button styling */
132
- .btn-primary {
133
- background: linear-gradient(45deg, #667eea, #764ba2) !important;
134
- border: none !important;
135
- border-radius: 15px !important;
136
- padding: 12px 30px !important;
137
- font-weight: 600 !important;
138
- font-size: 1rem !important;
139
- color: white !important;
140
- box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3) !important;
141
- transition: all 0.3s ease !important;
142
- text-transform: uppercase !important;
143
- letter-spacing: 0.5px !important;
144
- }
145
-
146
- .btn-primary:hover {
147
- transform: translateY(-2px) !important;
148
- box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4) !important;
149
- }
150
-
151
- .btn-secondary {
152
- background: linear-gradient(45deg, #ff6b6b, #ee5a52) !important;
153
- border: none !important;
154
- border-radius: 15px !important;
155
- padding: 12px 30px !important;
156
- font-weight: 600 !important;
157
- color: white !important;
158
- box-shadow: 0 8px 25px rgba(255, 107, 107, 0.3) !important;
159
- transition: all 0.3s ease !important;
160
- }
161
-
162
- .btn-secondary:hover {
163
- transform: translateY(-2px) !important;
164
- box-shadow: 0 12px 35px rgba(255, 107, 107, 0.4) !important;
165
- }
166
-
167
- /* Output area styling */
168
- .output-container {
169
- background: rgba(255, 255, 255, 0.05);
170
- border-radius: 20px;
171
- padding: 1.5rem;
172
- border: 1px solid rgba(255, 255, 255, 0.1);
173
- }
174
-
175
- .results-grid {
176
- display: grid;
177
- gap: 1.5rem;
178
- }
179
-
180
- /* Gallery styling */
181
- .license-gallery {
182
- background: rgba(255, 255, 255, 0.05) !important;
183
- border-radius: 15px !important;
184
- padding: 1rem !important;
185
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
186
- }
187
-
188
- /* OCR section styling */
189
- .ocr-section {
190
- background: linear-gradient(135deg, rgba(78, 205, 196, 0.1), rgba(69, 183, 209, 0.1)) !important;
191
- border-radius: 20px !important;
192
- padding: 2rem !important;
193
- border: 1px solid rgba(78, 205, 196, 0.2) !important;
194
- backdrop-filter: blur(10px) !important;
195
- margin: 1.5rem 0 !important;
196
- }
197
-
198
- .ocr-title {
199
- color: #4ecdc4;
200
- font-weight: 700;
201
- font-size: 1.3rem;
202
- margin-bottom: 1rem;
203
- text-align: center;
204
- }
205
-
206
- /* Download section styling */
207
- .download-section {
208
- background: linear-gradient(135deg, rgba(255, 107, 107, 0.1), rgba(255, 75, 43, 0.1)) !important;
209
- border-radius: 20px !important;
210
- padding: 2rem !important;
211
- border: 1px solid rgba(255, 107, 107, 0.2) !important;
212
- backdrop-filter: blur(10px) !important;
213
- margin: 1.5rem 0 !important;
214
- }
215
-
216
- /* Tab styling */
217
- .tab-nav button {
218
- background: rgba(255, 255, 255, 0.1) !important;
219
- border: 1px solid rgba(255, 255, 255, 0.2) !important;
220
- border-radius: 10px 10px 0 0 !important;
221
- color: rgba(255, 255, 255, 0.8) !important;
222
- font-weight: 600 !important;
223
- padding: 12px 24px !important;
224
- margin-right: 5px !important;
225
- transition: all 0.3s ease !important;
226
- }
227
-
228
- .tab-nav button.selected {
229
- background: rgba(255, 255, 255, 0.2) !important;
230
- color: white !important;
231
- border-bottom: 1px solid transparent !important;
232
- }
233
-
234
- /* Footer styling */
235
- .footer {
236
- background: rgba(255, 255, 255, 0.05);
237
- backdrop-filter: blur(10px);
238
- border-radius: 20px;
239
- padding: 2rem;
240
- margin-top: 2rem;
241
- border: 1px solid rgba(255, 255, 255, 0.1);
242
- text-align: center;
243
- color: rgba(255, 255, 255, 0.8);
244
- }
245
-
246
- /* Animations */
247
- @keyframes fadeInUp {
248
- from {
249
- opacity: 0;
250
- transform: translateY(30px);
251
- }
252
- to {
253
- opacity: 1;
254
- transform: translateY(0);
255
- }
256
- }
257
-
258
- .animate-fade-in {
259
- animation: fadeInUp 0.6s ease-out;
260
- }
261
-
262
- /* Custom scrollbar */
263
- ::-webkit-scrollbar {
264
- width: 8px;
265
- }
266
-
267
- ::-webkit-scrollbar-track {
268
- background: rgba(255, 255, 255, 0.1);
269
- border-radius: 10px;
270
  }
271
 
272
- ::-webkit-scrollbar-thumb {
273
- background: linear-gradient(45deg, #667eea, #764ba2);
274
- border-radius: 10px;
275
- }
276
-
277
- /* Responsive design */
278
- @media (max-width: 768px) {
279
- .main-title {
280
- font-size: 2rem;
281
- }
282
-
283
- .glass-card {
284
- margin: 0.5rem 0 !important;
285
- padding: 1rem !important;
286
- }
287
- }
288
-
289
- /* Enhanced table styling */
290
- .dataframe {
291
- background: rgba(255, 255, 255, 0.05) !important;
292
- border-radius: 15px !important;
293
- overflow: hidden !important;
294
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
295
- }
296
-
297
- /* Form elements */
298
- input[type="range"] {
299
- background: transparent !important;
300
- border-radius: 10px !important;
301
- }
302
-
303
- input[type="range"]::-webkit-slider-track {
304
- background: rgba(255, 255, 255, 0.2) !important;
305
- border-radius: 10px !important;
306
- height: 6px !important;
307
- }
308
-
309
- input[type="range"]::-webkit-slider-thumb {
310
- background: linear-gradient(45deg, #667eea, #764ba2) !important;
311
- border-radius: 50% !important;
312
- width: 20px !important;
313
- height: 20px !important;
314
- border: none !important;
315
- box-shadow: 0 4px 10px rgba(102, 126, 234, 0.3) !important;
316
- }
317
-
318
- /* Checkbox styling */
319
- input[type="checkbox"] {
320
- accent-color: #667eea;
321
- transform: scale(1.2);
322
- margin-right: 8px;
323
- }
324
-
325
- /* Text styling */
326
- label, p, span {
327
- color: rgba(255, 255, 255, 0.9) !important;
328
- }
329
-
330
- .section-title {
331
- color: #4ecdc4 !important;
332
- font-weight: 700 !important;
333
- font-size: 1.2rem !important;
334
- margin-bottom: 1rem !important;
335
- text-transform: uppercase !important;
336
- letter-spacing: 1px !important;
337
  }
338
  """
339
 
340
  def toggle_sections(extract_text_checked, crop_checked):
341
- """Control visibility of Cropped Plates & OCR sections."""
342
  show_gallery = bool(extract_text_checked and crop_checked)
343
  show_ocr = bool(extract_text_checked)
344
  return (
345
- gr.update(visible=show_gallery), # license_gallery
346
- gr.update(visible=show_ocr), # ocr group container (textbox)
347
  )
348
 
349
- def get_ocr_status_badge():
350
- """Generate OCR status badge HTML."""
351
  if ADVANCED_OCR_AVAILABLE:
352
- return '<span class="status-badge status-success">✅ Advanced OCR Available</span>'
353
  elif OCR_AVAILABLE:
354
- return '<span class="status-badge status-warning">🟡 Basic OCR Available</span>'
355
  else:
356
- return '<span class="status-badge status-error">❌ OCR Not Available</span>'
357
 
358
- with gr.Blocks(
359
- css=custom_css,
360
- title="🏍️ AI Helmet Detection System",
361
- theme=gr.themes.Soft(
362
- primary_hue="blue",
363
- secondary_hue="purple",
364
- neutral_hue="slate",
365
- font=gr.themes.GoogleFont("Inter")
366
- ),
367
- ) as demo:
368
 
369
- # Enhanced Header Section
370
- with gr.Row():
371
- with gr.Column():
372
- gr.HTML("""
373
- <div class="header-container animate-fade-in">
374
- <h1 class="main-title">🏍️ AI Helmet Detection System</h1>
375
- <p class="subtitle">Advanced motorcyclist safety monitoring with intelligent license plate recognition</p>
376
- <div style="text-align: center; margin-top: 1.5rem;">
377
- <span class="status-badge status-success">🤖 YOLOv11 Powered</span>
378
- """ + get_ocr_status_badge() + """
379
- <span class="status-badge status-success">🚀 Real-time Processing</span>
380
- </div>
381
- </div>
382
- """)
383
 
384
- with gr.Tabs() as main_tabs:
385
- with gr.TabItem("🔍 Detection Studio", elem_classes=["animate-fade-in"]):
386
- with gr.Row(equal_height=True):
387
- # Left Panel - Controls
388
- with gr.Column(scale=1, elem_classes=["glass-card"]):
389
- gr.HTML('<h3 class="section-title">🎯 Detection Settings</h3>')
 
 
 
 
 
390
 
391
- with gr.Group(elem_classes=["input-group"]):
392
- gr.Markdown("**📸 Image Input**")
393
- input_image = gr.Image(
394
- type="filepath",
395
- label="Upload Image or Use Camera",
396
- sources=["upload", "webcam"],
397
- height=250
398
  )
399
 
400
- with gr.Group(elem_classes=["input-group"]):
401
- gr.Markdown("**⚙️ Model Parameters**")
402
- with gr.Row():
403
- image_size = gr.Slider(
404
- minimum=320, maximum=1280, value=640, step=32,
405
- label="🖼️ Image Resolution",
406
- info="Higher = Better accuracy, slower processing"
407
- )
408
- with gr.Row():
409
- conf_threshold = gr.Slider(
410
- minimum=0.0, maximum=1.0, value=0.4, step=0.05,
411
- label="🎯 Confidence Threshold",
412
- info="Minimum confidence for detections"
413
- )
414
- iou_threshold = gr.Slider(
415
- minimum=0.0, maximum=1.0, value=0.5, step=0.05,
416
- label="🔄 IoU Threshold",
417
- info="Overlap threshold for duplicate removal"
418
- )
419
 
420
- with gr.Group(elem_classes=["input-group"]):
421
- gr.Markdown("**🔧 Processing Options**")
422
- show_stats = gr.Checkbox(
423
- value=True,
424
- label="📊 Show Detection Statistics",
425
- info="Display summary stats on results"
 
 
 
 
 
 
 
426
  )
427
- crop_plates = gr.Checkbox(
428
- value=True,
429
- label="✂️ Extract License Plates",
430
- info="Crop and save detected license plates"
431
  )
432
-
433
- if ocr_status["any_available"]:
434
- extract_text = gr.Checkbox(
435
- value=False,
436
- label="🔤 Enable OCR Text Recognition",
437
- info="Extract text from license plates (slower processing)"
438
- )
439
- ocr_on_no_helmet = gr.Checkbox(
440
- value=True,
441
- label="🚨 Auto-OCR for Helmet Violations",
442
- info="Automatically read plates when no helmet detected"
443
- )
444
 
445
- if ADVANCED_OCR_AVAILABLE:
446
- models = get_available_models()
447
- model_choices = [("🤖 Auto (Recommended)", "auto"), ("🔤 Basic EasyOCR", "basic")]
448
- for key, info in models.items():
449
- model_choices.append((f"⚡ {info['name']}", key))
450
- selected_ocr_model = gr.Dropdown(
451
- choices=model_choices,
452
- value="auto",
453
- label="🧠 OCR Model Selection",
454
- info="Choose the best model for your needs"
455
- )
456
- else:
457
- selected_ocr_model = gr.State("basic")
458
-
459
- gr.HTML("""
460
- <div style="background: rgba(255,193,7,0.1); padding: 1rem; border-radius: 10px; border-left: 4px solid #ffc107; margin-top: 1rem;">
461
- <strong>⚠️ Performance Note:</strong> OCR processing may increase detection time by 2-5 seconds per license plate.
462
- </div>
463
- """)
464
- else:
465
- extract_text = gr.Checkbox(
466
- value=False,
467
- label="🔤 OCR Not Available",
468
- interactive=False,
469
- info="Install OCR requirements to enable text recognition"
470
- )
471
- ocr_on_no_helmet = gr.Checkbox(
472
- value=False,
473
- label="🚨 Auto-OCR (Not Available)",
474
- interactive=False
475
  )
 
476
  selected_ocr_model = gr.State("basic")
477
-
478
- # Action Buttons
479
- with gr.Row():
480
- submit_btn = gr.Button(
481
- "🚀 Start Detection",
482
- variant="primary",
483
- elem_classes=["btn-primary"],
484
- size="lg"
485
  )
486
- clear_btn = gr.Button(
487
- "🧹 Clear All",
488
- variant="secondary",
489
- elem_classes=["btn-secondary"],
490
- size="lg"
491
  )
 
 
 
 
 
492
 
493
- # Right Panel - Results
494
- with gr.Column(scale=2, elem_classes=["glass-card"]):
495
- gr.HTML('<h3 class="section-title">📋 Detection Results</h3>')
 
 
 
 
496
 
497
- with gr.Group(elem_classes=["output-container"]):
498
- output_image = gr.Image(
499
- type="pil",
500
- label="🎯 Annotated Detection Results",
501
- height=400,
502
- show_download_button=True
 
 
 
 
503
  )
504
-
505
- with gr.Row():
506
- with gr.Column(scale=2):
507
- output_table = gr.Dataframe(
508
- headers=["Object", "Confidence", "Position", "Dimensions"],
509
- label="📊 Detection Details",
510
- interactive=False,
511
- max_height=200
512
- )
513
- with gr.Column(scale=1):
514
- output_stats = gr.Textbox(
515
- label="📈 Summary Statistics",
516
- interactive=False,
517
- lines=8,
518
- placeholder="Detection statistics will appear here..."
519
- )
520
 
521
- # License Plate Gallery (conditionally visible)
522
  license_gallery = gr.Gallery(
523
- label="🚗 Extracted License Plates",
524
- show_label=True,
525
- elem_id="license_gallery",
526
- elem_classes=["license-gallery"],
527
- columns=4,
528
- rows=2,
529
- object_fit="contain",
530
  visible=False
531
  )
532
 
533
- # OCR Results Section (conditionally visible)
534
- ocr_group = gr.Group(elem_classes=["ocr-section"], visible=False)
535
  with ocr_group:
536
- gr.HTML('<div class="ocr-title">🔤 License Plate Text Recognition</div>')
537
  plate_text_output = gr.Textbox(
538
- label="📝 Extracted Text Results",
539
- placeholder="License plate text will appear here when OCR is enabled...",
540
- lines=5,
541
- interactive=False,
542
- show_copy_button=True
543
- )
544
-
545
- # Download Section
546
- with gr.Group(elem_classes=["download-section"]):
547
- gr.HTML('<div class="ocr-title">📥 Download Complete Results</div>')
548
- download_file = gr.File(
549
- label="📦 Results Package (ZIP)",
550
- interactive=False,
551
- visible=True
552
  )
553
- gr.HTML("""
554
- <div style="text-align: center; color: rgba(255,255,255,0.7); font-size: 0.9rem; margin-top: 0.5rem;">
555
- 📁 <strong>Package includes:</strong> Annotated image • Cropped license plates • Detection report (CSV) • OCR results
556
- </div>
557
- """)
558
 
559
- with gr.TabItem("🖼️ Example Gallery", elem_classes=["animate-fade-in"]):
560
- with gr.Column(elem_classes=["glass-card"]):
561
- gr.HTML('<h3 class="section-title">🎨 Try These Sample Images</h3>')
562
- gr.HTML("""
563
- <div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: rgba(255,255,255,0.05); border-radius: 15px; border: 1px solid rgba(255,255,255,0.1);">
564
- <p style="font-size: 1.1rem; margin: 0;">Click on any example below to test the detection system with pre-loaded images</p>
565
- </div>
566
- """)
567
-
568
- gr.Examples(
569
- examples=[
570
- ["sample_1.jpg"],
571
- ["sample_2.jpg"],
572
- ["sample_3.jpg"],
573
- ["sample_4.jpg"],
574
- ["sample_6.jpg"],
575
- ["sample_7.jpg"],
576
- ["sample_8.jpg"],
577
-
578
- ],
579
- inputs=input_image,
580
- outputs=[
581
- output_image,
582
- output_table,
583
- output_stats,
584
- license_gallery,
585
- download_file,
586
- plate_text_output,
587
- ],
588
- fn=lambda img: yolov8_detect(
589
- img, 640, 0.4, 0.5, True, True, True, False
590
- ),
591
- cache_examples=True,
592
- examples_per_page=5
593
- )
594
-
595
- with gr.TabItem("ℹ️ System Info", elem_classes=["animate-fade-in"]):
596
- with gr.Column(elem_classes=["glass-card"]):
597
- gr.HTML('<h3 class="section-title">🔧 System Information</h3>')
598
-
599
- gr.HTML(f"""
600
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; margin: 2rem 0;">
601
- <div style="background: rgba(255,255,255,0.05); padding: 1.5rem; border-radius: 15px; border: 1px solid rgba(255,255,255,0.1);">
602
- <h4 style="color: #4ecdc4; margin-bottom: 1rem;">🤖 AI Model</h4>
603
- <p><strong>Architecture:</strong> YOLOv11</p>
604
- <p><strong>Classes:</strong> Helmet, No Helmet, License Plate</p>
605
- <p><strong>Input Size:</strong> 320-1280px (configurable)</p>
606
- </div>
607
-
608
- <div style="background: rgba(255,255,255,0.05); padding: 1.5rem; border-radius: 15px; border: 1px solid rgba(255,255,255,0.1);">
609
- <h4 style="color: #4ecdc4; margin-bottom: 1rem;">🔤 OCR Capabilities</h4>
610
- <p><strong>Status:</strong> {'✅ Advanced Available' if ADVANCED_OCR_AVAILABLE else '🟡 Basic Available' if OCR_AVAILABLE else '❌ Not Available'}</p>
611
- <p><strong>Features:</strong> Multi-language support</p>
612
- <p><strong>Accuracy:</strong> Optimized for license plates</p>
613
- </div>
614
-
615
- <div style="background: rgba(255,255,255,0.05); padding: 1.5rem; border-radius: 15px; border: 1px solid rgba(255,255,255,0.1);">
616
- <h4 style="color: #4ecdc4; margin-bottom: 1rem;">📊 Output Features</h4>
617
- <p><strong>Detection:</strong> Bounding boxes + confidence scores</p>
618
- <p><strong>Extraction:</strong> Cropped license plate images</p>
619
- <p><strong>Export:</strong> ZIP package with all results</p>
620
- </div>
621
- </div>
622
-
623
- <div style="background: rgba(255,107,107,0.1); padding: 2rem; border-radius: 20px; border: 1px solid rgba(255,107,107,0.2); margin-top: 2rem;">
624
- <h4 style="color: #ff6b6b; margin-bottom: 1rem;">🛡️ Privacy & Ethics</h4>
625
- <p><strong>Data Privacy:</strong> All processing is done locally. No data is stored or transmitted.</p>
626
- <p><strong>Usage:</strong> This tool is for demonstration and research purposes only.</p>
627
- <p><strong>Responsibility:</strong> Users are responsible for compliance with local privacy laws.</p>
628
- </div>
629
- """)
630
 
631
- # Enhanced Footer
632
- gr.HTML("""
633
- <div class="footer">
634
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 2rem; margin-bottom: 2rem;">
635
- <div>
636
- <h4 style="color: #4ecdc4; margin-bottom: 0.5rem;">🚀 Technology Stack</h4>
637
- <p style="margin: 0;">Gradio • Ultralytics YOLO • PyTorch</p>
638
- </div>
639
- <div>
640
- <h4 style="color: #4ecdc4; margin-bottom: 0.5rem;">📋 Requirements</h4>
641
- <p style="margin: 0;">Python 3.8+ • torch • transformers • easyocr</p>
642
- </div>
643
- <div>
644
- <h4 style="color: #4ecdc4; margin-bottom: 0.5rem;">⚖️ License</h4>
645
- <p style="margin: 0;">Educational use only • Respect privacy laws</p>
646
- </div>
647
- </div>
648
- <div style="border-top: 1px solid rgba(255,255,255,0.1); padding-top: 1rem;">
649
- <p style="margin: 0; font-size: 0.9rem; opacity: 0.8;">
650
- Built with ❤️ using modern AI • Version 2.0 • Enhanced UI Experience
651
- </p>
652
- </div>
653
- </div>
654
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
655
 
656
- # Event Handlers
657
  submit_btn.click(
658
  fn=yolov8_detect,
659
  inputs=[
@@ -692,7 +276,6 @@ with gr.Blocks(
692
  ],
693
  )
694
 
695
- # Dynamic visibility controls
696
  extract_text.change(
697
  fn=toggle_sections,
698
  inputs=[extract_text, crop_plates],
 
8
  OCR_AVAILABLE
9
  )
10
 
 
11
  try:
12
  from advanced_ocr import get_available_models
13
  except ImportError:
14
  def get_available_models():
15
  return {}
16
 
 
17
  download_sample_images()
18
 
 
19
  ocr_status = get_ocr_status()
 
 
20
  custom_css = """
 
 
 
 
 
 
21
  .gradio-container {
22
+ max-width: 1200px !important;
23
  margin: 0 auto;
 
 
24
  }
25
 
26
+ .main-header {
27
+ text-align: center;
 
 
 
 
28
  margin-bottom: 2rem;
29
+ padding: 1rem;
 
30
  }
31
 
32
  .main-title {
33
+ font-size: 2rem;
34
+ font-weight: 600;
35
+ color: #333;
 
 
 
 
36
  margin: 0;
 
 
 
 
 
 
 
37
  }
38
 
39
  .subtitle {
40
+ color: #666;
41
+ font-size: 1rem;
42
+ margin: 0.5rem 0;
 
 
43
  }
44
 
45
+ .status-info {
 
 
 
 
46
  font-size: 0.9rem;
47
+ color: #888;
48
+ margin: 0.5rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
+ .section-gap {
52
+ margin: 1.5rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  }
54
  """
55
 
56
  def toggle_sections(extract_text_checked, crop_checked):
 
57
  show_gallery = bool(extract_text_checked and crop_checked)
58
  show_ocr = bool(extract_text_checked)
59
  return (
60
+ gr.update(visible=show_gallery),
61
+ gr.update(visible=show_ocr),
62
  )
63
 
64
+ def get_ocr_status_text():
 
65
  if ADVANCED_OCR_AVAILABLE:
66
+ return "Advanced OCR Available"
67
  elif OCR_AVAILABLE:
68
+ return "Basic OCR Available"
69
  else:
70
+ return "OCR Not Available"
71
 
72
+ with gr.Blocks(css=custom_css, title="AI Helmet Detection System") as demo:
 
 
 
 
 
 
 
 
 
73
 
74
+ gr.HTML(f"""
75
+ <div class="main-header">
76
+ <h1 class="main-title">AI Helmet Detection System</h1>
77
+ <p class="subtitle">Motorcyclist safety monitoring with license plate recognition</p>
78
+ <p class="status-info">YOLOv11 • {get_ocr_status_text()} • Real-time Processing</p>
79
+ </div>
80
+ """)
 
 
 
 
 
 
 
81
 
82
+ with gr.Tabs():
83
+ with gr.TabItem("Detection"):
84
+ with gr.Row():
85
+ with gr.Column(scale=1):
86
+ gr.Markdown("### Settings")
87
+
88
+ input_image = gr.Image(
89
+ type="filepath",
90
+ label="Upload Image",
91
+ sources=["upload", "webcam"]
92
+ )
93
 
94
+ with gr.Row():
95
+ image_size = gr.Slider(
96
+ minimum=320, maximum=1280, value=640, step=32,
97
+ label="Image Size"
 
 
 
98
  )
99
 
100
+ with gr.Row():
101
+ conf_threshold = gr.Slider(
102
+ minimum=0.0, maximum=1.0, value=0.4, step=0.05,
103
+ label="Confidence"
104
+ )
105
+ iou_threshold = gr.Slider(
106
+ minimum=0.0, maximum=1.0, value=0.5, step=0.05,
107
+ label="IoU Threshold"
108
+ )
 
 
 
 
 
 
 
 
 
 
109
 
110
+ show_stats = gr.Checkbox(
111
+ value=True,
112
+ label="Show Statistics"
113
+ )
114
+ crop_plates = gr.Checkbox(
115
+ value=True,
116
+ label="Extract License Plates"
117
+ )
118
+
119
+ if ocr_status["any_available"]:
120
+ extract_text = gr.Checkbox(
121
+ value=False,
122
+ label="Enable OCR"
123
  )
124
+ ocr_on_no_helmet = gr.Checkbox(
125
+ value=True,
126
+ label="Auto-OCR for No Helmet"
 
127
  )
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
+ if ADVANCED_OCR_AVAILABLE:
130
+ models = get_available_models()
131
+ model_choices = [("Auto (Recommended)", "auto"), ("Basic EasyOCR", "basic")]
132
+ for key, info in models.items():
133
+ model_choices.append((info['name'], key))
134
+ selected_ocr_model = gr.Dropdown(
135
+ choices=model_choices,
136
+ value="auto",
137
+ label="OCR Model"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  )
139
+ else:
140
  selected_ocr_model = gr.State("basic")
141
+
142
+ gr.Markdown("*Note: OCR processing may increase detection time.*")
143
+ else:
144
+ extract_text = gr.Checkbox(
145
+ value=False,
146
+ label="OCR Not Available",
147
+ interactive=False
 
148
  )
149
+ ocr_on_no_helmet = gr.Checkbox(
150
+ value=False,
151
+ label="Auto-OCR (Not Available)",
152
+ interactive=False
 
153
  )
154
+ selected_ocr_model = gr.State("basic")
155
+
156
+ with gr.Row():
157
+ submit_btn = gr.Button("Start Detection", variant="primary")
158
+ clear_btn = gr.Button("Clear")
159
 
160
+ with gr.Column(scale=2):
161
+ gr.Markdown("### Results")
162
+
163
+ output_image = gr.Image(
164
+ type="pil",
165
+ label="Detection Results"
166
+ )
167
 
168
+ with gr.Row():
169
+ output_table = gr.Dataframe(
170
+ headers=["Object", "Confidence", "Position", "Dimensions"],
171
+ label="Detection Details",
172
+ interactive=False
173
+ )
174
+ output_stats = gr.Textbox(
175
+ label="Statistics",
176
+ interactive=False,
177
+ lines=6
178
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
 
180
  license_gallery = gr.Gallery(
181
+ label="License Plates",
182
+ columns=3,
 
 
 
 
 
183
  visible=False
184
  )
185
 
186
+ ocr_group = gr.Group(visible=False)
 
187
  with ocr_group:
 
188
  plate_text_output = gr.Textbox(
189
+ label="OCR Results",
190
+ lines=4,
191
+ interactive=False
 
 
 
 
 
 
 
 
 
 
 
192
  )
 
 
 
 
 
193
 
194
+ download_file = gr.File(
195
+ label="Download Results (ZIP)",
196
+ interactive=False
197
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
+ with gr.TabItem("Examples"):
200
+ gr.Markdown("### Sample Images")
201
+ gr.Markdown("Click any example to test the detection system:")
202
+
203
+ gr.Examples(
204
+ examples=[
205
+ ["sample_1.jpg"],
206
+ ["sample_2.jpg"],
207
+ ["sample_3.jpg"],
208
+ ["sample_4.jpg"],
209
+ ["sample_6.jpg"],
210
+ ["sample_7.jpg"],
211
+ ["sample_8.jpg"],
212
+ ],
213
+ inputs=input_image,
214
+ outputs=[
215
+ output_image,
216
+ output_table,
217
+ output_stats,
218
+ license_gallery,
219
+ download_file,
220
+ plate_text_output,
221
+ ],
222
+ fn=lambda img: yolov8_detect(
223
+ img, 640, 0.4, 0.5, True, True, True, False
224
+ ),
225
+ cache_examples=True
226
+ )
227
+
228
+ with gr.TabItem("Info"):
229
+ gr.Markdown("### System Information")
230
+
231
+ gr.Markdown(f"""
232
+ **AI Model:** YOLOv11
233
+ **Classes:** Helmet, No Helmet, License Plate
234
+ **OCR Status:** {get_ocr_status_text()}
235
+ **Features:** Detection, extraction, text recognition
236
+
237
+ **Privacy:** All processing is local. No data stored.
238
+ **Usage:** For demonstration and research purposes only.
239
+ """)
240
 
 
241
  submit_btn.click(
242
  fn=yolov8_detect,
243
  inputs=[
 
276
  ],
277
  )
278
 
 
279
  extract_text.change(
280
  fn=toggle_sections,
281
  inputs=[extract_text, crop_plates],