Sarvamangalak commited on
Commit
db3baf2
Β·
verified Β·
1 Parent(s): 1ec0bfd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1939 -769
app.py CHANGED
@@ -1,12 +1,7 @@
1
  # Author: Sarvamangala Kokatanur
2
-
3
- # ============================================================
4
-
5
- # SMART TRAFFIC & EV ANALYTICS SYSTEM
6
-
7
- # WITH AGENTIC AI DYNAMIC ANALYTICS DASHBOARD
8
-
9
- # ============================================================
10
 
11
  import gradio as gr
12
  import torch
@@ -15,1166 +10,2341 @@ import cv2
15
  import sqlite3
16
  import pandas as pd
17
  import matplotlib.pyplot as plt
 
18
 
19
  from datetime import datetime, timedelta
20
  from PIL import Image, ImageDraw
21
 
22
  from transformers import (
23
- YolosImageProcessor,
24
- YolosForObjectDetection
25
  )
26
 
 
 
 
27
  # ============================================================
28
 
29
- # LOAD LICENSE PLATE DETECTION MODEL
 
 
 
 
 
30
 
 
 
 
31
  # ============================================================
32
 
 
 
33
  processor = YolosImageProcessor.from_pretrained(
34
- "nickmuchi/yolos-small-finetuned-license-plate-detection"
35
  )
36
 
37
  model = YolosForObjectDetection.from_pretrained(
38
- "nickmuchi/yolos-small-finetuned-license-plate-detection"
39
  )
40
 
41
  model.eval()
42
 
 
 
 
 
 
43
  # ============================================================
44
 
45
- # DATABASE
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
 
 
47
  # ============================================================
48
 
49
  conn = sqlite3.connect(
50
- "vehicles.db",
51
- check_same_thread=False
52
  )
53
 
54
  cursor = conn.cursor()
55
 
 
56
  cursor.execute("""
57
  CREATE TABLE IF NOT EXISTS vehicles(
58
 
59
- ```
60
- id INTEGER PRIMARY KEY AUTOINCREMENT,
61
 
62
- timestamp TEXT,
63
 
64
- license_plate TEXT,
65
 
66
- vehicle_status TEXT,
67
 
68
- discount INTEGER
69
- ```
70
 
71
  )
72
  """)
73
 
 
74
  conn.commit()
75
 
 
 
 
76
  # ============================================================
77
 
78
- # PLATE COLOR CLASSIFIER
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  # ============================================================
81
 
82
  def classify_plate_color(plate_img):
83
 
84
- ```
85
- img = np.array(plate_img)
86
 
87
- # Convert RGB to HSV
88
- hsv = cv2.cvtColor(
89
- img,
90
- cv2.COLOR_RGB2HSV
91
- )
92
 
93
- h, w = hsv.shape[:2]
 
94
 
95
- # Inspect left portion of the plate
96
- left = hsv[:, :int(w * 0.2)]
 
 
 
97
 
98
- # Detect green color
99
- green = cv2.inRange(
100
- left,
101
- (35, 40, 40),
102
- (90, 255, 255)
103
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- green_ratio = (
106
- np.count_nonzero(green)
107
- / green.size
108
- )
109
 
110
- # EV classification
111
- if green_ratio > 0.15:
112
- return "EV"
113
 
114
- return "Non-EV"
115
- ```
116
 
117
- # ============================================================
118
 
119
- # DUPLICATE VEHICLE CHECK
120
 
121
- # ============================================================
122
 
123
- def is_duplicate_vehicle(plate_number):
124
 
125
- ```
126
- cursor.execute("""
127
- SELECT timestamp
128
- FROM vehicles
129
- WHERE license_plate=?
130
- ORDER BY id DESC
131
- LIMIT 1
132
- """, (plate_number,))
133
 
134
- row = cursor.fetchone()
 
 
135
 
136
- if row is None:
137
- return False
 
 
138
 
139
- last_time = datetime.strptime(
140
- row[0],
141
- "%Y-%m-%d %H:%M:%S"
142
- )
143
 
144
- if datetime.now() - last_time < timedelta(minutes=5):
145
- return True
146
 
147
- return False
148
- ```
 
149
 
150
- # ============================================================
151
 
152
- # SAVE VEHICLE TO DATABASE
 
 
 
 
 
 
 
153
 
154
- # ============================================================
155
 
156
- def save_vehicle(plate, status):
 
 
 
 
 
 
 
157
 
158
- ```
159
- if status == "EV":
160
- discount = 50
161
- else:
162
- discount = 0
163
 
164
- if is_duplicate_vehicle(plate):
165
- return "Duplicate"
 
166
 
167
- current_time = datetime.now().strftime(
168
- "%Y-%m-%d %H:%M:%S"
169
- )
170
 
171
- cursor.execute("""
172
 
173
- INSERT INTO vehicles(
174
 
175
- timestamp,
176
- license_plate,
177
- vehicle_status,
178
- discount
179
 
180
- )
181
 
182
- VALUES (?, ?, ?, ?)
183
 
184
- """, (
185
- current_time,
186
- plate,
187
- status,
188
- discount
189
- ))
190
 
191
- conn.commit()
192
 
193
- return "Saved"
194
- ```
195
 
196
- # ============================================================
197
 
198
- # GET STATIC DASHBOARD
199
 
200
- # ============================================================
201
 
202
- def get_dashboard():
203
 
204
- ```
205
- df = pd.read_sql(
206
- "SELECT * FROM vehicles",
207
- conn
208
- )
209
 
210
- fig, axs = plt.subplots(
211
- 2,
212
- 2,
213
- figsize=(10, 7)
214
- )
215
 
216
- if df.empty:
217
 
218
- for ax in axs.flatten():
219
 
220
- ax.text(
221
- 0.5,
222
- 0.5,
223
- "No Data Available",
224
- ha="center",
225
- va="center",
226
- fontsize=12
227
- )
228
 
229
- ax.axis("off")
230
 
231
- plt.tight_layout()
232
 
233
- return fig
 
 
234
 
235
- # --------------------------------------------------------
236
- # EV VS NON-EV
237
- # --------------------------------------------------------
238
 
239
- status_counts = (
240
- df["vehicle_status"]
241
- .value_counts()
242
- )
243
 
244
- axs[0, 0].bar(
245
- status_counts.index,
246
- status_counts.values
247
- )
248
 
249
- axs[0, 0].set_title(
250
- "EV vs Non-EV Vehicles"
251
- )
252
 
253
- axs[0, 0].set_ylabel(
254
- "Number of Vehicles"
255
- )
 
256
 
 
 
 
 
257
 
258
- # --------------------------------------------------------
259
- # PIE CHART
260
- # --------------------------------------------------------
 
261
 
262
- axs[0, 1].pie(
263
- status_counts.values,
264
- labels=status_counts.index,
265
- autopct="%1.1f%%"
266
- )
267
 
268
- axs[0, 1].set_title(
269
- "Vehicle Distribution"
270
- )
271
 
 
 
272
 
273
- # --------------------------------------------------------
274
- # DISCOUNT
275
- # --------------------------------------------------------
276
 
277
- total_discount = df["discount"].sum()
 
 
278
 
279
- axs[1, 0].bar(
280
- ["Total Discount"],
281
- [total_discount]
282
- )
 
 
 
 
283
 
284
- axs[1, 0].set_title(
285
- "EV Discount Provided"
286
- )
287
 
288
- axs[1, 0].set_ylabel(
289
- "Amount (β‚Ή)"
290
- )
291
 
 
 
 
292
 
293
- # --------------------------------------------------------
294
- # REPORT
295
- # --------------------------------------------------------
296
 
297
- axs[1, 1].axis("off")
 
 
298
 
299
- ev_count = len(
300
- df[
301
- df["vehicle_status"] == "EV"
302
- ]
303
- )
304
 
305
- non_ev_count = len(
306
- df[
307
- df["vehicle_status"] == "Non-EV"
308
- ]
309
- )
310
 
311
- report = (
312
- "TRAFFIC ANALYTICS REPORT\n\n"
313
- f"Total Vehicles: {len(df)}\n\n"
314
- f"EV Vehicles: {ev_count}\n\n"
315
- f"Non-EV Vehicles: {non_ev_count}\n\n"
316
- f"Total Discount: β‚Ή{total_discount}"
317
- )
318
 
319
- axs[1, 1].text(
320
- 0.05,
321
- 0.95,
322
- report,
323
- fontsize=11,
324
- va="top"
325
- )
326
 
327
- plt.tight_layout()
328
 
329
- plt.close(fig)
330
 
331
- return fig
332
- ```
333
 
334
- # ============================================================
335
 
336
- # AGENTIC AI DATABASE TOOLS
337
 
338
- # ============================================================
339
 
340
- def get_vehicle_dataframe():
 
341
 
342
- ```
343
- df = pd.read_sql(
344
- "SELECT * FROM vehicles",
345
- conn
346
- )
347
 
348
- return df
349
- ```
350
 
351
- def get_total_vehicles():
352
 
353
- ```
354
- df = get_vehicle_dataframe()
355
 
356
- return len(df)
357
- ```
358
 
359
- def get_ev_count():
360
 
361
- ```
362
- df = get_vehicle_dataframe()
363
 
364
- return len(
365
- df[
366
- df["vehicle_status"] == "EV"
367
- ]
368
- )
369
- ```
370
 
371
- def get_non_ev_count():
372
 
373
- ```
374
- df = get_vehicle_dataframe()
375
 
376
- return len(
377
- df[
378
- df["vehicle_status"] == "Non-EV"
379
- ]
380
- )
381
- ```
382
 
383
- def get_total_discount():
 
 
384
 
385
- ```
386
- df = get_vehicle_dataframe()
387
 
388
- if df.empty:
389
- return 0
390
 
391
- return int(df["discount"].sum())
392
- ```
393
 
394
- def get_ev_percentage():
395
 
396
- ```
397
- total = get_total_vehicles()
 
 
398
 
399
- ev_count = get_ev_count()
400
 
401
- if total == 0:
402
- return 0
403
 
404
- return round(
405
- (ev_count / total) * 100,
406
- 2
407
- )
408
- ```
409
 
410
- # ============================================================
411
 
412
- # DYNAMIC GRAPH GENERATOR
413
 
414
- # ============================================================
415
 
416
- def generate_ev_graph():
 
 
417
 
418
- ```
419
- df = get_vehicle_dataframe()
420
 
421
- fig, ax = plt.subplots(
422
- figsize=(8, 5)
423
- )
424
 
425
- if df.empty:
426
 
427
- ax.text(
428
- 0.5,
429
- 0.5,
430
- "No Vehicle Data Available",
431
- ha="center",
432
- va="center",
433
- fontsize=14
434
- )
435
 
436
- ax.axis("off")
 
 
437
 
438
- return fig
 
 
 
 
 
 
 
 
 
439
 
440
- counts = (
441
- df["vehicle_status"]
442
- .value_counts()
443
- )
444
 
445
- ax.bar(
446
- counts.index,
447
- counts.values
448
- )
449
 
450
- ax.set_title(
451
- "EV vs Non-EV Vehicle Analysis"
452
- )
 
453
 
454
- ax.set_xlabel(
455
- "Vehicle Type"
456
- )
457
 
458
- ax.set_ylabel(
459
- "Number of Vehicles"
460
- )
 
 
 
 
 
 
 
 
 
461
 
462
- plt.tight_layout()
463
 
464
- return fig
465
- ```
 
 
 
 
 
 
466
 
467
- def generate_discount_graph():
468
 
469
- ```
470
- df = get_vehicle_dataframe()
 
471
 
472
- fig, ax = plt.subplots(
473
- figsize=(8, 5)
474
- )
475
 
476
- if df.empty:
477
 
478
- ax.text(
479
- 0.5,
480
- 0.5,
481
- "No Discount Data Available",
482
- ha="center",
483
- va="center"
484
- )
485
 
486
- ax.axis("off")
487
 
488
- return fig
489
 
490
- discount_by_status = (
491
- df.groupby("vehicle_status")["discount"]
492
- .sum()
493
- )
494
 
495
- ax.bar(
496
- discount_by_status.index,
497
- discount_by_status.values
498
- )
499
 
500
- ax.set_title(
501
- "Discount Analysis"
502
- )
503
 
504
- ax.set_xlabel(
505
- "Vehicle Status"
506
- )
507
 
508
- ax.set_ylabel(
509
- "Total Discount (β‚Ή)"
510
- )
511
 
512
- plt.tight_layout()
513
 
514
- return fig
515
- ```
516
 
517
- # ============================================================
518
 
519
- # AGENTIC AI QUERY ENGINE
520
 
521
- # ============================================================
522
 
523
- def traffic_ai_agent(user_query):
524
 
525
- ```
526
- if not user_query:
527
- return (
528
- "Please ask a question about the traffic data.",
529
- None
530
- )
531
 
532
- query = user_query.lower()
533
 
534
- total = get_total_vehicles()
535
- ev_count = get_ev_count()
536
- non_ev_count = get_non_ev_count()
537
- discount = get_total_discount()
538
- ev_percentage = get_ev_percentage()
539
 
 
540
 
541
- # --------------------------------------------------------
542
- # EV COUNT
543
- # --------------------------------------------------------
544
 
545
- if (
546
- "how many ev" in query
547
- or "number of ev" in query
548
- or "ev count" in query
549
- ):
550
 
551
- response = (
552
- f"AI Analysis:\n\n"
553
- f"The system has detected "
554
- f"{ev_count} EV vehicle(s).\n\n"
555
- f"Total vehicles analysed: {total}."
556
- )
557
 
558
- return response, generate_ev_graph()
559
 
 
560
 
561
- # --------------------------------------------------------
562
- # NON-EV COUNT
563
- # --------------------------------------------------------
564
 
565
- elif (
566
- "non ev" in query
567
- or "non-ev" in query
568
- ):
569
 
570
- response = (
571
- f"AI Analysis:\n\n"
572
- f"Non-EV vehicles detected: "
573
- f"{non_ev_count}.\n\n"
574
- f"EV vehicles detected: "
575
- f"{ev_count}."
576
- )
577
 
578
- return response, generate_ev_graph()
579
 
580
 
581
- # --------------------------------------------------------
582
- # TOTAL VEHICLES
583
- # --------------------------------------------------------
584
 
585
- elif (
586
- "total vehicle" in query
587
- or "how many vehicle" in query
588
- or "traffic" in query
589
- ):
590
 
591
- response = (
592
- f" AI Traffic Analysis:\n\n"
593
- f"Total vehicles detected: "
594
- f"{total}.\n\n"
595
- f"EV vehicles: {ev_count}\n"
596
- f"Non-EV vehicles: {non_ev_count}"
597
- )
598
 
599
- return response, generate_ev_graph()
600
 
 
601
 
602
- # --------------------------------------------------------
603
- # EV PERCENTAGE
604
- # --------------------------------------------------------
605
 
606
- elif (
607
- "percentage" in query
608
- or "percent" in query
609
- ):
610
 
611
- response = (
612
- f" AI Analysis:\n\n"
613
- f"EV Percentage: "
614
- f"{ev_percentage}%\n\n"
615
- f"This means {ev_count} out of "
616
- f"{total} detected vehicles are EVs."
617
- )
618
 
619
- return response, generate_ev_graph()
620
 
 
621
 
622
- # --------------------------------------------------------
623
- # DISCOUNT
624
- # --------------------------------------------------------
625
 
626
- elif (
627
- "discount" in query
628
- or "incentive" in query
629
- or "money" in query
630
- ):
631
 
632
- response = (
633
- f"AI Discount Analysis:\n\n"
634
- f"Total EV discount provided: "
635
- f"β‚Ή{discount}.\n\n"
636
- f"Number of EV vehicles: "
637
- f"{ev_count}."
638
- )
639
 
640
- return response, generate_discount_graph()
641
 
 
642
 
643
- # --------------------------------------------------------
644
- # CO2 SAVINGS
645
- # --------------------------------------------------------
646
 
647
- elif (
648
- "co2" in query
649
- or "carbon" in query
650
- or "environment" in query
651
- ):
652
 
653
- # Example estimation
654
- co2_savings = ev_count * 2.3
655
 
656
- response = (
657
- f" AI Environmental Analysis:\n\n"
658
- f"Based on the configured estimation model, "
659
- f"{ev_count} EV vehicles could contribute to "
660
- f"approximately {co2_savings:.2f} kg of "
661
- f"potential COβ‚‚ savings.\n\n"
662
- f"Note: This value is an estimated project "
663
- f"metric and can be replaced with a validated "
664
- f"emission calculation model."
665
  )
666
 
667
- return response, generate_ev_graph()
668
 
 
 
 
669
 
670
- # --------------------------------------------------------
671
- # SUMMARY
672
- # --------------------------------------------------------
673
 
674
- elif (
675
- "summary" in query
676
- or "report" in query
677
- or "overall" in query
678
- ):
679
 
680
- response = (
681
- f"SMART TRAFFIC AI SUMMARY\n\n"
682
- f"Total Vehicles: {total}\n\n"
683
- f"EV Vehicles: {ev_count}\n\n"
684
- f"Non-EV Vehicles: {non_ev_count}\n\n"
685
- f"EV Percentage: {ev_percentage}%\n\n"
686
- f"Total Discount: β‚Ή{discount}"
687
- )
688
 
689
- return response, generate_ev_graph()
690
 
 
691
 
692
- # --------------------------------------------------------
693
- # DEFAULT RESPONSE
694
- # --------------------------------------------------------
695
 
696
- else:
697
 
698
- response = (
699
- "I am your Smart Traffic AI Assistant.\n\n"
700
- "You can ask questions such as:\n\n"
701
- "β€’ How many EV vehicles were detected?\n"
702
- "β€’ How many Non-EV vehicles are there?\n"
703
- "β€’ What is the total traffic?\n"
704
- "β€’ What percentage of vehicles are EVs?\n"
705
- "β€’ What is the total EV discount?\n"
706
- "β€’ Give me a traffic summary.\n"
707
- "β€’ Show COβ‚‚ savings."
708
- )
709
 
710
- return response, None
711
- ```
712
 
713
- # ============================================================
714
 
715
- # MAIN VEHICLE PROCESSING PIPELINE
716
 
717
- # ============================================================
718
 
719
- def process_image(img):
720
 
721
- ```
722
- if img is None:
723
 
724
- return (
725
- None,
726
- "Please upload an image.",
727
- "0",
728
- "0",
729
- "β‚Ή0",
730
- get_dashboard()
731
- )
732
 
 
733
 
734
- image = Image.fromarray(img)
735
 
 
736
 
737
- # --------------------------------------------------------
738
- # MODEL INPUT
739
- # --------------------------------------------------------
740
 
741
- inputs = processor(
742
- images=image,
743
- return_tensors="pt"
744
- )
745
 
746
- with torch.no_grad():
 
 
747
 
748
- outputs = model(**inputs)
749
 
 
 
 
750
 
751
- target_sizes = torch.tensor([
752
- [
753
- image.size[1],
754
- image.size[0]
755
- ]
756
- ])
757
 
758
 
759
- results = processor.post_process_object_detection(
760
- outputs,
761
- threshold=0.3,
762
- target_sizes=target_sizes
763
- )[0]
 
764
 
765
 
766
- draw = ImageDraw.Draw(image)
 
 
767
 
 
 
 
768
 
769
- ev_count = 0
770
- non_ev_count = 0
771
- discount_total = 0
772
 
773
- output_text = ""
 
 
774
 
775
 
776
- # --------------------------------------------------------
777
- # NO DETECTION
778
- # --------------------------------------------------------
779
 
780
- if len(results["boxes"]) == 0:
 
 
781
 
782
- return (
783
- image,
784
- "No license plate detected.",
785
- "0",
786
- "0",
787
- "β‚Ή0",
788
- get_dashboard()
789
- )
790
 
 
791
 
792
- # --------------------------------------------------------
793
- # PROCESS DETECTED PLATES
794
- # --------------------------------------------------------
795
 
796
- for i, box in enumerate(results["boxes"]):
 
 
 
797
 
798
- x1, y1, x2, y2 = map(
799
- int,
800
- box.tolist()
801
- )
802
 
 
803
 
804
- # Crop plate
805
- plate = image.crop(
806
- (x1, y1, x2, y2)
807
- )
808
 
 
809
 
810
- # Classify EV / Non-EV
811
- status = classify_plate_color(
812
- plate
813
- )
814
 
 
815
 
816
- # Temporary vehicle ID
817
- plate_number = (
818
- f"Vehicle_"
819
- f"{datetime.now().strftime('%H%M%S%f')}"
820
- f"_{i}"
821
- )
822
 
 
823
 
824
- saved = save_vehicle(
825
- plate_number,
826
- status
827
- )
828
 
 
 
 
829
 
830
- if saved == "Duplicate":
831
- continue
 
832
 
 
833
 
834
- # ----------------------------------------------------
835
- # EV
836
- # ----------------------------------------------------
837
 
838
- if status == "EV":
839
 
840
- ev_count += 1
841
 
842
- discount = 50
843
 
844
- discount_total += discount
 
 
845
 
846
- color = "green"
 
 
847
 
848
- label = (
849
- f"EV | β‚Ή{discount}"
850
- )
851
 
 
852
 
853
- # ----------------------------------------------------
854
- # NON-EV
855
- # ----------------------------------------------------
856
 
857
- else:
858
 
859
- non_ev_count += 1
860
 
861
- discount = 0
 
 
862
 
863
- color = "red"
864
 
865
- label = "Non-EV"
866
 
 
 
 
867
 
868
- # Draw bounding box
869
 
870
- draw.rectangle(
871
- [x1, y1, x2, y2],
872
- outline=color,
873
- width=3
874
- )
875
 
876
 
877
- # Draw label
 
 
878
 
879
- draw.text(
880
- (
881
- x1,
882
- max(0, y1 - 25)
883
- ),
884
- label,
885
- fill=color
886
- )
887
 
 
 
 
888
 
889
- output_text += (
 
 
890
 
891
- f"Vehicle {i+1}\n"
892
- f"Plate ID: {plate_number}\n"
893
- f"Status: {status}\n"
894
- f"Discount: β‚Ή{discount}\n\n"
895
 
896
- )
 
 
897
 
898
 
899
- dashboard = get_dashboard()
900
 
 
901
 
902
- return (
903
 
904
- image,
905
 
906
- output_text,
907
 
908
- str(ev_count),
909
 
910
- str(non_ev_count),
911
 
912
- f"β‚Ή{discount_total}",
913
 
914
- dashboard
915
 
916
- )
917
- ```
918
 
919
- # ============================================================
920
 
921
- # GRADIO UI
922
 
923
- # ============================================================
924
 
925
- css = """
 
 
926
 
927
- .gradio-container {
928
- max-width: 98% !important;
929
- margin: auto;
930
- }
931
 
932
- textarea {
933
- font-size: 15px !important;
934
- }
935
 
936
- h1 {
937
- text-align: center;
938
- }
939
 
940
- .block {
941
- border-radius: 12px;
942
- }
943
 
944
- .gr-image {
945
- min-height: 350px;
946
- }
 
947
 
 
 
 
948
  """
 
949
 
950
- with gr.Blocks(css=css) as demo:
951
-
952
- ```
953
- # ========================================================
954
- # TITLE
955
- # ========================================================
956
-
957
- gr.Markdown(
958
- "# Smart Traffic & EV Analytics System"
959
- )
960
 
961
- gr.Markdown(
962
- "AI License Plate Detection β€’ EV Classification β€’ "
963
- "Discount Calculation β€’ Agentic AI Analytics"
964
- )
965
 
 
966
 
967
- # ========================================================
968
- # TAB 1 - VEHICLE DETECTION
969
- # ========================================================
970
 
971
- with gr.Tab("πŸš— Vehicle Detection"):
 
 
 
972
 
 
973
 
974
- with gr.Row(equal_height=True):
975
 
976
 
977
- # INPUT
978
 
979
- with gr.Column(scale=1):
980
 
981
- input_img = gr.Image(
982
- type="numpy",
983
- label="Input Vehicle Image",
984
- sources=[
985
- "upload",
986
- "webcam"
987
- ],
988
- height=350
989
- )
990
 
 
991
 
992
- btn = gr.Button(
993
- "Scan Vehicle",
994
  variant="primary",
 
995
  size="lg"
 
996
  )
997
 
998
 
999
- # OUTPUT IMAGE
 
 
1000
 
1001
- with gr.Column(scale=1):
1002
 
1003
- output_img = gr.Image(
1004
- label="Detection Result",
1005
- height=350
1006
  )
1007
 
1008
 
1009
- # SUMMARY
 
 
1010
 
1011
- with gr.Column(scale=1):
1012
 
1013
- summary_box = gr.Textbox(
1014
- label="Detection Summary",
1015
- lines=16
1016
- )
1017
 
 
1018
 
1019
- # ====================================================
1020
- # STATISTICS
1021
- # ====================================================
1022
 
1023
- with gr.Row():
1024
 
1025
- ev_box = gr.Textbox(
1026
- label="EV Vehicles"
1027
- )
1028
 
1029
- non_ev_box = gr.Textbox(
1030
- label="Non-EV Vehicles"
1031
- )
1032
 
1033
- discount_box = gr.Textbox(
1034
- label="Total Discount"
1035
- )
1036
 
 
1037
 
1038
- # ====================================================
1039
- # STATIC DASHBOARD
1040
- # ====================================================
1041
 
1042
- gr.Markdown(
1043
- "## Traffic Analytics Dashboard"
1044
- )
1045
 
1046
- dashboard = gr.Plot(
1047
- label="Analytics"
1048
- )
1049
 
 
1050
 
1051
- btn.click(
1052
 
1053
- fn=process_image,
 
 
 
1054
 
1055
- inputs=input_img,
1056
 
1057
- outputs=[
1058
- output_img,
1059
- summary_box,
1060
- ev_box,
1061
- non_ev_box,
1062
- discount_box,
1063
- dashboard
1064
- ]
1065
 
1066
- )
1067
 
 
1068
 
1069
- # ========================================================
1070
- # TAB 2 - AGENTIC AI DASHBOARD
1071
- # ========================================================
1072
 
1073
- with gr.Tab("AI Traffic Assistant"):
 
 
 
 
1074
 
 
1075
 
1076
- gr.Markdown(
1077
- "## Agentic AI Traffic Analytics Dashboard"
1078
- )
1079
 
 
 
 
1080
 
1081
- gr.Markdown(
1082
- "Ask questions about your traffic and EV database "
1083
- "using natural language."
1084
- )
1085
 
 
 
 
 
1086
 
1087
- user_query = gr.Textbox(
 
 
 
 
1088
 
1089
- label="Ask the AI Assistant",
 
 
1090
 
1091
- placeholder=(
1092
- "Example: How many EV vehicles were detected?"
1093
- ),
1094
 
1095
- lines=2
 
 
 
1096
 
1097
- )
1098
 
 
 
 
1099
 
1100
- ask_button = gr.Button(
 
 
1101
 
1102
- "Ask AI",
1103
 
1104
- variant="primary",
 
 
1105
 
1106
- size="lg"
1107
 
1108
- )
1109
 
 
1110
 
1111
- ai_response = gr.Textbox(
1112
 
1113
- label="AI Response",
1114
 
1115
- lines=12
1116
 
1117
- )
1118
 
1119
 
1120
- ai_graph = gr.Plot(
 
 
1121
 
1122
- label="Dynamic AI Generated Graph"
1123
 
1124
- )
 
 
1125
 
1126
 
1127
- ask_button.click(
 
 
1128
 
1129
- fn=traffic_ai_agent,
1130
 
1131
- inputs=user_query,
1132
 
1133
- outputs=[
1134
- ai_response,
1135
- ai_graph
1136
- ]
1137
 
1138
- )
1139
 
 
1140
 
1141
- # ====================================================
1142
- # SUGGESTED QUESTIONS
1143
- # ====================================================
1144
 
1145
- gr.Markdown(
1146
- """
1147
- ```
1148
 
1149
- ### πŸ’‘ Suggested Questions
1150
 
1151
- β€’ How many EV vehicles were detected?
1152
 
1153
- β€’ How many Non-EV vehicles are there?
1154
 
1155
- β€’ What is the total traffic?
1156
 
1157
- β€’ What percentage of vehicles are EVs?
1158
 
1159
- β€’ What is the total EV discount?
1160
 
1161
- β€’ Give me an overall traffic summary.
1162
 
1163
- β€’ Show the COβ‚‚ savings.
1164
- """
1165
- )
1166
- # ============================================================
1167
 
1168
- # RUN APPLICATION
1169
 
1170
  # ============================================================
 
 
 
 
1171
 
1172
- if **name** == "**main**":
 
 
1173
 
1174
- ```
1175
- demo.queue()
1176
 
1177
- demo.launch(
1178
- ssr_mode=False
1179
- )
1180
- ```
 
1
  # Author: Sarvamangala Kokatanur
2
+ # Smart Traffic & EV Analytics System
3
+ # YOLOS + OpenCV + EasyOCR + SQLite + Gradio
4
+ # Dynamic AI Traffic Analytics Dashboard
 
 
 
 
 
5
 
6
  import gradio as gr
7
  import torch
 
10
  import sqlite3
11
  import pandas as pd
12
  import matplotlib.pyplot as plt
13
+ import easyocr
14
 
15
  from datetime import datetime, timedelta
16
  from PIL import Image, ImageDraw
17
 
18
  from transformers import (
19
+ YolosImageProcessor,
20
+ YolosForObjectDetection
21
  )
22
 
23
+
24
+ # ============================================================
25
+ # CONFIGURATION
26
  # ============================================================
27
 
28
+ MODEL_NAME = (
29
+ "nickmuchi/"
30
+ "yolos-small-finetuned-license-plate-detection"
31
+ )
32
+
33
+ EV_DISCOUNT = 50
34
 
35
+
36
+ # ============================================================
37
+ # LOAD YOLOS LICENSE PLATE DETECTION MODEL
38
  # ============================================================
39
 
40
+ print("Loading YOLOS license plate detection model...")
41
+
42
  processor = YolosImageProcessor.from_pretrained(
43
+ MODEL_NAME
44
  )
45
 
46
  model = YolosForObjectDetection.from_pretrained(
47
+ MODEL_NAME
48
  )
49
 
50
  model.eval()
51
 
52
+ print("YOLOS model loaded successfully.")
53
+
54
+
55
+ # ============================================================
56
+ # LOAD EASY OCR
57
  # ============================================================
58
 
59
+ print("Loading EasyOCR...")
60
+
61
+ try:
62
+ reader = easyocr.Reader(
63
+ ["en"],
64
+ gpu=torch.cuda.is_available()
65
+ )
66
+
67
+ print("EasyOCR loaded successfully.")
68
+
69
+ except Exception as e:
70
+
71
+ print("EasyOCR loading error:", e)
72
+
73
+ reader = None
74
+
75
 
76
+ # ============================================================
77
+ # DATABASE
78
  # ============================================================
79
 
80
  conn = sqlite3.connect(
81
+ "vehicles.db",
82
+ check_same_thread=False
83
  )
84
 
85
  cursor = conn.cursor()
86
 
87
+
88
  cursor.execute("""
89
  CREATE TABLE IF NOT EXISTS vehicles(
90
 
91
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
 
92
 
93
+ timestamp TEXT,
94
 
95
+ license_plate TEXT,
96
 
97
+ vehicle_status TEXT,
98
 
99
+ discount INTEGER
 
100
 
101
  )
102
  """)
103
 
104
+
105
  conn.commit()
106
 
107
+
108
+ # ============================================================
109
+ # GET VEHICLE DATA
110
  # ============================================================
111
 
112
+ def get_vehicle_dataframe():
113
 
114
+ try:
115
+
116
+ df = pd.read_sql_query(
117
+ "SELECT * FROM vehicles ORDER BY id DESC",
118
+ conn
119
+ )
120
+
121
+ return df
122
+
123
+ except Exception as e:
124
+
125
+ print("Database error:", e)
126
+
127
+ return pd.DataFrame(
128
+ columns=[
129
+ "id",
130
+ "timestamp",
131
+ "license_plate",
132
+ "vehicle_status",
133
+ "discount"
134
+ ]
135
+ )
136
+
137
+
138
+ # ============================================================
139
+ # PLATE COLOR CLASSIFIER
140
  # ============================================================
141
 
142
  def classify_plate_color(plate_img):
143
 
144
+ try:
 
145
 
146
+ img = np.array(plate_img)
 
 
 
 
147
 
148
+ if img.size == 0:
149
+ return "Non-EV"
150
 
151
+ # PIL RGB -> OpenCV RGB
152
+ hsv = cv2.cvtColor(
153
+ img,
154
+ cv2.COLOR_RGB2HSV
155
+ )
156
 
157
+ h, w = hsv.shape[:2]
158
+
159
+ if w == 0:
160
+ return "Non-EV"
161
+
162
+ # Inspect left 20% of the plate
163
+ left_width = max(
164
+ 1,
165
+ int(w * 0.20)
166
+ )
167
+
168
+ left = hsv[:, :left_width]
169
+
170
+ # Green HSV range
171
+ green = cv2.inRange(
172
+ left,
173
+ (35, 40, 40),
174
+ (90, 255, 255)
175
+ )
176
+
177
+ green_ratio = (
178
+ np.count_nonzero(green)
179
+ / green.size
180
+ )
181
+
182
+ if green_ratio > 0.15:
183
+ return "EV"
184
+
185
+ return "Non-EV"
186
+
187
+ except Exception as e:
188
+
189
+ print(
190
+ "Plate color classification error:",
191
+ e
192
+ )
193
+
194
+ return "Non-EV"
195
+
196
+
197
+ # ============================================================
198
+ # OCR PREPROCESSING
199
+ # ============================================================
200
+
201
+ def preprocess_plate_for_ocr(plate_img):
202
+
203
+ try:
204
+
205
+ img = np.array(plate_img)
206
+
207
+ if img.size == 0:
208
+ return None
209
+
210
+ # Convert RGB -> grayscale
211
+ gray = cv2.cvtColor(
212
+ img,
213
+ cv2.COLOR_RGB2GRAY
214
+ )
215
+
216
+ # Resize for OCR
217
+ scale = 3
218
+
219
+ gray = cv2.resize(
220
+ gray,
221
+ None,
222
+ fx=scale,
223
+ fy=scale,
224
+ interpolation=cv2.INTER_CUBIC
225
+ )
226
+
227
+ # Noise reduction
228
+ gray = cv2.bilateralFilter(
229
+ gray,
230
+ 9,
231
+ 75,
232
+ 75
233
+ )
234
+
235
+ # Contrast enhancement
236
+ gray = cv2.equalizeHist(
237
+ gray
238
+ )
239
+
240
+ return gray
241
+
242
+ except Exception as e:
243
+
244
+ print(
245
+ "OCR preprocessing error:",
246
+ e
247
+ )
248
+
249
+ return None
250
+
251
+
252
+ # ============================================================
253
+ # OCR FUNCTION
254
+ # ============================================================
255
+
256
+ def read_license_plate(plate_img):
257
+
258
+ if reader is None:
259
+
260
+ return "OCR_UNAVAILABLE"
261
+
262
+ try:
263
+
264
+ processed = preprocess_plate_for_ocr(
265
+ plate_img
266
+ )
267
+
268
+ if processed is None:
269
+ return "UNKNOWN"
270
+
271
+ results = reader.readtext(
272
+ processed,
273
+ detail=1,
274
+ paragraph=False
275
+ )
276
+
277
+ if not results:
278
+ return "UNKNOWN"
279
+
280
+ candidates = []
281
+
282
+ for result in results:
283
+
284
+ if len(result) >= 3:
285
+
286
+ text = str(
287
+ result[1]
288
+ ).strip()
289
+
290
+ confidence = float(
291
+ result[2]
292
+ )
293
+
294
+ if confidence >= 0.20:
295
+
296
+ candidates.append(
297
+ (text, confidence)
298
+ )
299
+
300
+ if not candidates:
301
+ return "UNKNOWN"
302
+
303
+ # Select highest-confidence result
304
+ candidates.sort(
305
+ key=lambda x: x[1],
306
+ reverse=True
307
+ )
308
+
309
+ text = candidates[0][0]
310
+
311
+ # Remove unwanted characters
312
+ cleaned = "".join(
313
+ char
314
+ for char in text
315
+ if char.isalnum()
316
+ )
317
+
318
+ if cleaned == "":
319
+ return "UNKNOWN"
320
+
321
+ return cleaned.upper()
322
+
323
+ except Exception as e:
324
+
325
+ print(
326
+ "OCR error:",
327
+ e
328
+ )
329
+
330
+ return "UNKNOWN"
331
+
332
+
333
+ # ============================================================
334
+ # DUPLICATE VEHICLE CHECK
335
+ # ============================================================
336
+
337
+ def is_duplicate_vehicle(plate_number):
338
+
339
+ if (
340
+ plate_number is None
341
+ or plate_number == ""
342
+ or plate_number == "UNKNOWN"
343
+ ):
344
+
345
+ return False
346
+
347
+ try:
348
+
349
+ cursor.execute("""
350
+ SELECT timestamp
351
+ FROM vehicles
352
+ WHERE license_plate=?
353
+ ORDER BY id DESC
354
+ LIMIT 1
355
+ """, (plate_number,))
356
+
357
+ row = cursor.fetchone()
358
+
359
+ if row is None:
360
+ return False
361
+
362
+ last_time = datetime.strptime(
363
+ row[0],
364
+ "%Y-%m-%d %H:%M:%S"
365
+ )
366
+
367
+ elapsed = (
368
+ datetime.now()
369
+ - last_time
370
+ )
371
+
372
+ if elapsed < timedelta(
373
+ minutes=5
374
+ ):
375
+
376
+ return True
377
+
378
+ return False
379
+
380
+ except Exception as e:
381
+
382
+ print(
383
+ "Duplicate check error:",
384
+ e
385
+ )
386
+
387
+ return False
388
+
389
+
390
+ # ============================================================
391
+ # SAVE VEHICLE
392
+ # ============================================================
393
+
394
+ def save_vehicle(
395
+ plate,
396
+ status
397
+ ):
398
+
399
+ if status == "EV":
400
+
401
+ discount = EV_DISCOUNT
402
+
403
+ else:
404
+
405
+ discount = 0
406
+
407
+
408
+ # Do not use duplicate filtering for UNKNOWN OCR
409
+ if plate != "UNKNOWN":
410
+
411
+ if is_duplicate_vehicle(
412
+ plate
413
+ ):
414
+
415
+ return "Duplicate"
416
+
417
+
418
+ current_time = datetime.now().strftime(
419
+ "%Y-%m-%d %H:%M:%S"
420
+ )
421
+
422
+
423
+ try:
424
+
425
+ cursor.execute("""
426
+ INSERT INTO vehicles(
427
+
428
+ timestamp,
429
+ license_plate,
430
+ vehicle_status,
431
+ discount
432
+
433
+ )
434
+
435
+ VALUES (?, ?, ?, ?)
436
+
437
+ """, (
438
+
439
+ current_time,
440
+ plate,
441
+ status,
442
+ discount
443
+
444
+ ))
445
+
446
+
447
+ conn.commit()
448
+
449
+ return "Saved"
450
+
451
+
452
+ except Exception as e:
453
+
454
+ print(
455
+ "Database save error:",
456
+ e
457
+ )
458
+
459
+ return "Error"
460
+
461
+
462
+ # ============================================================
463
+ # STATIC DASHBOARD
464
+ # ============================================================
465
+
466
+ def get_dashboard():
467
+
468
+ df = get_vehicle_dataframe()
469
+
470
+ fig, axs = plt.subplots(
471
+ 2,
472
+ 2,
473
+ figsize=(10, 7)
474
+ )
475
+
476
+
477
+ # --------------------------------------------------------
478
+ # NO DATA
479
+ # --------------------------------------------------------
480
+
481
+ if df.empty:
482
+
483
+ for ax in axs.flatten():
484
+
485
+ ax.text(
486
+ 0.5,
487
+ 0.5,
488
+ "No Data Available",
489
+ ha="center",
490
+ va="center",
491
+ fontsize=12
492
+ )
493
+
494
+ ax.axis("off")
495
+
496
+
497
+ plt.tight_layout()
498
+
499
+ return fig
500
+
501
+
502
+ # --------------------------------------------------------
503
+ # VEHICLE STATUS
504
+ # --------------------------------------------------------
505
+
506
+ status_counts = (
507
+ df["vehicle_status"]
508
+ .value_counts()
509
+ )
510
+
511
+
512
+ axs[0, 0].bar(
513
+ status_counts.index,
514
+ status_counts.values
515
+ )
516
+
517
+
518
+ axs[0, 0].set_title(
519
+ "EV vs Non-EV Vehicles"
520
+ )
521
+
522
+
523
+ axs[0, 0].set_ylabel(
524
+ "Number of Vehicles"
525
+ )
526
+
527
+
528
+ # --------------------------------------------------------
529
+ # PIE CHART
530
+ # --------------------------------------------------------
531
+
532
+ axs[0, 1].pie(
533
+ status_counts.values,
534
+ labels=status_counts.index,
535
+ autopct="%1.1f%%"
536
+ )
537
+
538
+
539
+ axs[0, 1].set_title(
540
+ "Vehicle Distribution"
541
+ )
542
+
543
+
544
+ # --------------------------------------------------------
545
+ # DISCOUNT
546
+ # --------------------------------------------------------
547
+
548
+ total_discount = int(
549
+ df["discount"].sum()
550
+ )
551
+
552
+
553
+ axs[1, 0].bar(
554
+ ["Discount"],
555
+ [total_discount]
556
+ )
557
+
558
+
559
+ axs[1, 0].set_title(
560
+ "Total EV Discount"
561
+ )
562
+
563
+
564
+ axs[1, 0].set_ylabel(
565
+ "Amount (β‚Ή)"
566
+ )
567
+
568
+
569
+ # --------------------------------------------------------
570
+ # REPORT
571
+ # --------------------------------------------------------
572
+
573
+ axs[1, 1].axis("off")
574
+
575
+
576
+ ev_count = len(
577
+ df[
578
+ df["vehicle_status"] == "EV"
579
+ ]
580
+ )
581
+
582
+
583
+ non_ev_count = len(
584
+ df[
585
+ df["vehicle_status"] == "Non-EV"
586
+ ]
587
+ )
588
+
589
+
590
+ ev_percentage = (
591
+
592
+ ev_count / len(df) * 100
593
+
594
+ if len(df) > 0
595
+
596
+ else 0
597
+
598
+ )
599
+
600
+
601
+ report = (
602
+
603
+ "TRAFFIC ANALYTICS REPORT\n\n"
604
+
605
+ f"Total Vehicles : {len(df)}\n\n"
606
+
607
+ f"EV Vehicles : {ev_count}\n\n"
608
+
609
+ f"Non-EV Vehicles : {non_ev_count}\n\n"
610
+
611
+ f"EV Percentage : "
612
+ f"{ev_percentage:.2f}%\n\n"
613
+
614
+ f"Discount Given : "
615
+ f"β‚Ή{total_discount}"
616
+
617
+ )
618
+
619
+
620
+ axs[1, 1].text(
621
+ 0.02,
622
+ 0.98,
623
+ report,
624
+ fontsize=10,
625
+ va="top"
626
+ )
627
+
628
+
629
+ plt.tight_layout()
630
+
631
+ plt.close(fig)
632
+
633
+ return fig
634
+
635
+
636
+ # ============================================================
637
+ # AI ANALYTICS FUNCTIONS
638
+ # ============================================================
639
+
640
+ def get_total_vehicles():
641
+
642
+ df = get_vehicle_dataframe()
643
+
644
+ return len(df)
645
+
646
+
647
+ def get_ev_count():
648
+
649
+ df = get_vehicle_dataframe()
650
+
651
+ if df.empty:
652
+ return 0
653
+
654
+ return len(
655
+ df[
656
+ df["vehicle_status"] == "EV"
657
+ ]
658
+ )
659
+
660
+
661
+ def get_non_ev_count():
662
+
663
+ df = get_vehicle_dataframe()
664
+
665
+ if df.empty:
666
+ return 0
667
+
668
+ return len(
669
+ df[
670
+ df["vehicle_status"] == "Non-EV"
671
+ ]
672
+ )
673
+
674
+
675
+ def get_total_discount():
676
+
677
+ df = get_vehicle_dataframe()
678
+
679
+ if df.empty:
680
+ return 0
681
+
682
+ return int(
683
+ df["discount"].sum()
684
+ )
685
+
686
+
687
+ def get_ev_percentage():
688
+
689
+ total = get_total_vehicles()
690
+
691
+ ev = get_ev_count()
692
+
693
+ if total == 0:
694
+ return 0
695
+
696
+ return round(
697
+ ev / total * 100,
698
+ 2
699
+ )
700
+
701
+
702
+ # ============================================================
703
+ # DAILY TRAFFIC
704
+ # ============================================================
705
+
706
+ def get_daily_traffic():
707
+
708
+ df = get_vehicle_dataframe()
709
+
710
+ if df.empty:
711
+ return pd.DataFrame()
712
+
713
+ try:
714
+
715
+ df["timestamp"] = pd.to_datetime(
716
+ df["timestamp"]
717
+ )
718
+
719
+ df["date"] = (
720
+ df["timestamp"]
721
+ .dt.date
722
+ )
723
+
724
+ daily = (
725
+ df.groupby("date")
726
+ .size()
727
+ .reset_index(
728
+ name="vehicles"
729
+ )
730
+ )
731
+
732
+ return daily
733
+
734
+ except Exception as e:
735
+
736
+ print(
737
+ "Daily traffic error:",
738
+ e
739
+ )
740
+
741
+ return pd.DataFrame()
742
+
743
+
744
+ # ============================================================
745
+ # GRAPH - EV VS NON-EV
746
+ # ============================================================
747
+
748
+ def generate_ev_graph():
749
+
750
+ df = get_vehicle_dataframe()
751
+
752
+ fig, ax = plt.subplots(
753
+ figsize=(8, 5)
754
+ )
755
+
756
+
757
+ if df.empty:
758
+
759
+ ax.text(
760
+ 0.5,
761
+ 0.5,
762
+ "No Vehicle Data Available",
763
+ ha="center",
764
+ va="center"
765
+ )
766
+
767
+ ax.axis("off")
768
+
769
+ return fig
770
+
771
+
772
+ counts = (
773
+ df["vehicle_status"]
774
+ .value_counts()
775
+ )
776
+
777
+
778
+ ax.bar(
779
+ counts.index,
780
+ counts.values
781
+ )
782
+
783
+
784
+ ax.set_title(
785
+ "EV vs Non-EV Analysis"
786
+ )
787
+
788
+
789
+ ax.set_xlabel(
790
+ "Vehicle Status"
791
+ )
792
+
793
+
794
+ ax.set_ylabel(
795
+ "Number of Vehicles"
796
+ )
797
+
798
+
799
+ plt.tight_layout()
800
+
801
+ plt.close(fig)
802
+
803
+ return fig
804
+
805
+
806
+ # ============================================================
807
+ # GRAPH - DISCOUNT
808
+ # ============================================================
809
+
810
+ def generate_discount_graph():
811
+
812
+ df = get_vehicle_dataframe()
813
+
814
+ fig, ax = plt.subplots(
815
+ figsize=(8, 5)
816
+ )
817
+
818
+
819
+ if df.empty:
820
+
821
+ ax.text(
822
+ 0.5,
823
+ 0.5,
824
+ "No Discount Data Available",
825
+ ha="center",
826
+ va="center"
827
+ )
828
+
829
+ ax.axis("off")
830
+
831
+ return fig
832
+
833
+
834
+ discount_data = (
835
+ df.groupby(
836
+ "vehicle_status"
837
+ )["discount"]
838
+ .sum()
839
+ )
840
+
841
+
842
+ ax.bar(
843
+ discount_data.index,
844
+ discount_data.values
845
+ )
846
+
847
+
848
+ ax.set_title(
849
+ "Discount Analysis"
850
+ )
851
+
852
+
853
+ ax.set_xlabel(
854
+ "Vehicle Status"
855
+ )
856
+
857
+
858
+ ax.set_ylabel(
859
+ "Discount Amount (β‚Ή)"
860
+ )
861
+
862
+
863
+ plt.tight_layout()
864
+
865
+ plt.close(fig)
866
+
867
+ return fig
868
+
869
+
870
+ # ============================================================
871
+ # GRAPH - DAILY TRAFFIC
872
+ # ============================================================
873
+
874
+ def generate_daily_traffic_graph():
875
+
876
+ daily = get_daily_traffic()
877
+
878
+ fig, ax = plt.subplots(
879
+ figsize=(9, 5)
880
+ )
881
+
882
+
883
+ if daily.empty:
884
+
885
+ ax.text(
886
+ 0.5,
887
+ 0.5,
888
+ "No Traffic Data Available",
889
+ ha="center",
890
+ va="center"
891
+ )
892
+
893
+ ax.axis("off")
894
+
895
+ return fig
896
+
897
+
898
+ ax.plot(
899
+ daily["date"].astype(str),
900
+ daily["vehicles"],
901
+ marker="o"
902
+ )
903
+
904
+
905
+ ax.set_title(
906
+ "Daily Traffic Volume"
907
+ )
908
+
909
+
910
+ ax.set_xlabel(
911
+ "Date"
912
+ )
913
+
914
+
915
+ ax.set_ylabel(
916
+ "Number of Vehicles"
917
+ )
918
+
919
+
920
+ plt.xticks(
921
+ rotation=45
922
+ )
923
+
924
+
925
+ plt.tight_layout()
926
+
927
+ plt.close(fig)
928
+
929
+ return fig
930
+
931
+
932
+ # ============================================================
933
+ # GRAPH - EV DAILY TRAFFIC
934
+ # ============================================================
935
+
936
+ def generate_ev_daily_graph():
937
+
938
+ df = get_vehicle_dataframe()
939
+
940
+ fig, ax = plt.subplots(
941
+ figsize=(9, 5)
942
+ )
943
+
944
+
945
+ if df.empty:
946
+
947
+ ax.text(
948
+ 0.5,
949
+ 0.5,
950
+ "No EV Data Available",
951
+ ha="center",
952
+ va="center"
953
+ )
954
+
955
+ ax.axis("off")
956
+
957
+ return fig
958
+
959
+
960
+ try:
961
+
962
+ df["timestamp"] = pd.to_datetime(
963
+ df["timestamp"]
964
+ )
965
+
966
+ df["date"] = (
967
+ df["timestamp"]
968
+ .dt.date
969
+ )
970
+
971
+
972
+ ev_df = df[
973
+ df["vehicle_status"] == "EV"
974
+ ]
975
+
976
+
977
+ if ev_df.empty:
978
+
979
+ ax.text(
980
+ 0.5,
981
+ 0.5,
982
+ "No EV Vehicles Available",
983
+ ha="center",
984
+ va="center"
985
+ )
986
+
987
+ ax.axis("off")
988
+
989
+ return fig
990
+
991
+
992
+ daily_ev = (
993
+ ev_df.groupby("date")
994
+ .size()
995
+ .reset_index(
996
+ name="EV Vehicles"
997
+ )
998
+ )
999
+
1000
+
1001
+ ax.plot(
1002
+ daily_ev["date"].astype(str),
1003
+ daily_ev["EV Vehicles"],
1004
+ marker="o"
1005
+ )
1006
+
1007
+
1008
+ ax.set_title(
1009
+ "Daily EV Traffic"
1010
+ )
1011
+
1012
+
1013
+ ax.set_xlabel(
1014
+ "Date"
1015
+ )
1016
+
1017
+
1018
+ ax.set_ylabel(
1019
+ "EV Vehicles"
1020
+ )
1021
+
1022
+
1023
+ plt.xticks(
1024
+ rotation=45
1025
+ )
1026
+
1027
+
1028
+ plt.tight_layout()
1029
+
1030
+ plt.close(fig)
1031
+
1032
+ return fig
1033
+
1034
+
1035
+ except Exception as e:
1036
+
1037
+ print(
1038
+ "EV graph error:",
1039
+ e
1040
+ )
1041
+
1042
+ ax.text(
1043
+ 0.5,
1044
+ 0.5,
1045
+ "Unable to generate graph",
1046
+ ha="center",
1047
+ va="center"
1048
+ )
1049
+
1050
+ ax.axis("off")
1051
+
1052
+ return fig
1053
+
1054
+
1055
+ # ============================================================
1056
+ # AGENTIC AI QUERY ENGINE
1057
+ # ============================================================
1058
+
1059
+ def traffic_ai_agent(user_query):
1060
+
1061
+ if user_query is None:
1062
+
1063
+ return (
1064
+ "Please enter a question.",
1065
+ None
1066
+ )
1067
+
1068
+
1069
+ query = user_query.strip().lower()
1070
+
1071
+
1072
+ if query == "":
1073
+
1074
+ return (
1075
+ "Please enter a question.",
1076
+ None
1077
+ )
1078
+
1079
+
1080
+ # ========================================================
1081
+ # GET LIVE DATABASE VALUES
1082
+ # ========================================================
1083
+
1084
+ total = get_total_vehicles()
1085
+
1086
+ ev = get_ev_count()
1087
+
1088
+ non_ev = get_non_ev_count()
1089
+
1090
+ discount = get_total_discount()
1091
+
1092
+ ev_percentage = get_ev_percentage()
1093
+
1094
+
1095
+ # ========================================================
1096
+ # EV COUNT
1097
+ # ========================================================
1098
+
1099
+ if (
1100
+ "how many ev" in query
1101
+ or "number of ev" in query
1102
+ or "ev count" in query
1103
+ or "ev vehicles" in query
1104
+ ):
1105
+
1106
+ response = (
1107
+
1108
+ "πŸ€– AI TRAFFIC ANALYSIS\n\n"
1109
+
1110
+ f"EV vehicles detected: {ev}\n\n"
1111
+
1112
+ f"Total vehicles: {total}\n\n"
1113
+
1114
+ f"EV percentage: "
1115
+ f"{ev_percentage:.2f}%"
1116
+
1117
+ )
1118
+
1119
+
1120
+ return (
1121
+ response,
1122
+ generate_ev_graph()
1123
+ )
1124
+
1125
+
1126
+ # ========================================================
1127
+ # NON-EV
1128
+ # ========================================================
1129
+
1130
+ if (
1131
+ "non ev" in query
1132
+ or "non-ev" in query
1133
+ or "non electric" in query
1134
+ ):
1135
+
1136
+ response = (
1137
+
1138
+ "πŸ€– AI TRAFFIC ANALYSIS\n\n"
1139
+
1140
+ f"Non-EV vehicles detected: "
1141
+ f"{non_ev}\n\n"
1142
+
1143
+ f"EV vehicles detected: {ev}\n\n"
1144
+
1145
+ f"Total vehicles: {total}"
1146
+
1147
+ )
1148
+
1149
+
1150
+ return (
1151
+ response,
1152
+ generate_ev_graph()
1153
+ )
1154
+
1155
+
1156
+ # ========================================================
1157
+ # EV PERCENTAGE
1158
+ # ========================================================
1159
+
1160
+ if (
1161
+ "percentage" in query
1162
+ or "percent" in query
1163
+ or "ratio" in query
1164
+ or "proportion" in query
1165
+ ):
1166
+
1167
+ response = (
1168
+
1169
+ "πŸ€– AI EV ANALYSIS\n\n"
1170
+
1171
+ f"Total vehicles: {total}\n\n"
1172
+
1173
+ f"EV vehicles: {ev}\n\n"
1174
+
1175
+ f"Non-EV vehicles: {non_ev}\n\n"
1176
+
1177
+ f"EV percentage: "
1178
+ f"{ev_percentage:.2f}%"
1179
+
1180
+ )
1181
+
1182
+
1183
+ return (
1184
+ response,
1185
+ generate_ev_graph()
1186
+ )
1187
+
1188
+
1189
+ # ========================================================
1190
+ # DISCOUNT
1191
+ # ========================================================
1192
+
1193
+ if (
1194
+ "discount" in query
1195
+ or "incentive" in query
1196
+ or "saving" in query
1197
+ or "savings" in query
1198
+ ):
1199
+
1200
+ response = (
1201
+
1202
+ "πŸ€– AI INCENTIVE ANALYSIS\n\n"
1203
+
1204
+ f"EV vehicles: {ev}\n\n"
1205
+
1206
+ f"Discount per EV: "
1207
+ f"β‚Ή{EV_DISCOUNT}\n\n"
1208
+
1209
+ f"Total discount provided: "
1210
+ f"β‚Ή{discount}"
1211
+
1212
+ )
1213
+
1214
+
1215
+ return (
1216
+ response,
1217
+ generate_discount_graph()
1218
+ )
1219
+
1220
+
1221
+ # ========================================================
1222
+ # DAILY TRAFFIC
1223
+ # ========================================================
1224
+
1225
+ if (
1226
+ "daily traffic" in query
1227
+ or "traffic by day" in query
1228
+ or "traffic trend" in query
1229
+ or "trend" in query
1230
+ ):
1231
+
1232
+ daily = get_daily_traffic()
1233
+
1234
+
1235
+ if daily.empty:
1236
+
1237
+ response = (
1238
+ "πŸ€– No daily traffic data "
1239
+ "is currently available."
1240
+ )
1241
+
1242
+ else:
1243
+
1244
+ highest = daily.loc[
1245
+ daily["vehicles"].idxmax()
1246
+ ]
1247
+
1248
+
1249
+ response = (
1250
+
1251
+ "πŸ€– DAILY TRAFFIC ANALYSIS\n\n"
1252
+
1253
+ f"Number of recorded days: "
1254
+ f"{len(daily)}\n\n"
1255
+
1256
+ f"Highest traffic date: "
1257
+ f"{highest['date']}\n\n"
1258
+
1259
+ f"Vehicles on highest traffic date: "
1260
+ f"{highest['vehicles']}"
1261
+
1262
+ )
1263
+
1264
+
1265
+ return (
1266
+ response,
1267
+ generate_daily_traffic_graph()
1268
+ )
1269
+
1270
+
1271
+ # ========================================================
1272
+ # EV DAILY TRAFFIC
1273
+ # ========================================================
1274
+
1275
+ if (
1276
+ "ev trend" in query
1277
+ or "daily ev" in query
1278
+ or "ev traffic" in query
1279
+ ):
1280
+
1281
+ response = (
1282
+
1283
+ "πŸ€– EV TRAFFIC TREND\n\n"
1284
+
1285
+ f"Total EV vehicles: {ev}\n\n"
1286
+
1287
+ f"Overall EV percentage: "
1288
+ f"{ev_percentage:.2f}%"
1289
+
1290
+ )
1291
+
1292
+
1293
+ return (
1294
+ response,
1295
+ generate_ev_daily_graph()
1296
+ )
1297
+
1298
+
1299
+ # ========================================================
1300
+ # TOTAL VEHICLES
1301
+ # ========================================================
1302
+
1303
+ if (
1304
+ "total vehicle" in query
1305
+ or "total traffic" in query
1306
+ or "how many vehicle" in query
1307
+ or "number of vehicle" in query
1308
+ ):
1309
+
1310
+ response = (
1311
+
1312
+ "πŸ€– TRAFFIC ANALYSIS\n\n"
1313
+
1314
+ f"Total vehicles detected: {total}\n\n"
1315
+
1316
+ f"EV vehicles: {ev}\n\n"
1317
+
1318
+ f"Non-EV vehicles: {non_ev}"
1319
+
1320
+ )
1321
+
1322
+
1323
+ return (
1324
+ response,
1325
+ generate_ev_graph()
1326
+ )
1327
+
1328
+
1329
+ # ========================================================
1330
+ # CO2
1331
+ # ========================================================
1332
+
1333
+ if (
1334
+ "co2" in query
1335
+ or "carbon" in query
1336
+ or "environment" in query
1337
+ or "emission" in query
1338
+ ):
1339
+
1340
+ # Project-level illustrative estimate.
1341
+ # Replace with validated emission methodology
1342
+ # for final research evaluation.
1343
+
1344
+ estimated_co2 = ev * 2.3
1345
+
1346
+
1347
+ response = (
1348
+
1349
+ "🌱 AI ENVIRONMENTAL ANALYSIS\n\n"
1350
+
1351
+ f"EV vehicles detected: {ev}\n\n"
1352
+
1353
+ f"Illustrative estimated COβ‚‚ saving: "
1354
+ f"{estimated_co2:.2f} kg\n\n"
1355
+
1356
+ "This is a project-level estimation. "
1357
+ "For a research publication, the emission "
1358
+ "factor and travel-distance assumptions "
1359
+ "should be validated using an appropriate "
1360
+ "transportation emissions methodology."
1361
+
1362
+ )
1363
+
1364
+
1365
+ return (
1366
+ response,
1367
+ generate_ev_graph()
1368
+ )
1369
+
1370
+
1371
+ # ========================================================
1372
+ # SUMMARY / REPORT
1373
+ # ========================================================
1374
+
1375
+ if (
1376
+ "summary" in query
1377
+ or "overall" in query
1378
+ or "report" in query
1379
+ or "dashboard" in query
1380
+ ):
1381
+
1382
+ response = (
1383
+
1384
+ "πŸ€– SMART TRAFFIC AI SUMMARY\n\n"
1385
+
1386
+ f"πŸš— Total Vehicles: {total}\n\n"
1387
+
1388
+ f"⚑ EV Vehicles: {ev}\n\n"
1389
+
1390
+ f"πŸš™ Non-EV Vehicles: {non_ev}\n\n"
1391
+
1392
+ f"πŸ“Š EV Percentage: "
1393
+ f"{ev_percentage:.2f}%\n\n"
1394
+
1395
+ f"πŸ’° Total Discount: β‚Ή{discount}"
1396
+
1397
+ )
1398
+
1399
+
1400
+ return (
1401
+ response,
1402
+ generate_ev_graph()
1403
+ )
1404
+
1405
+
1406
+ # ========================================================
1407
+ # HELP
1408
+ # ========================================================
1409
+
1410
+ response = (
1411
+
1412
+ "πŸ€– Smart Traffic AI Assistant\n\n"
1413
+
1414
+ "I can analyse the vehicle database. "
1415
+ "Try asking:\n\n"
1416
+
1417
+ "β€’ How many EV vehicles were detected?\n"
1418
+
1419
+ "β€’ How many Non-EV vehicles are there?\n"
1420
+
1421
+ "β€’ What is the total traffic?\n"
1422
+
1423
+ "β€’ What percentage of vehicles are EVs?\n"
1424
+
1425
+ "β€’ What is the total discount?\n"
1426
+
1427
+ "β€’ Show daily traffic trend.\n"
1428
+
1429
+ "β€’ Show EV traffic trend.\n"
1430
+
1431
+ "β€’ Give me an overall summary.\n"
1432
+
1433
+ "β€’ What are the CO2 savings?"
1434
+
1435
+ )
1436
+
1437
+
1438
+ return (
1439
+ response,
1440
+ None
1441
+ )
1442
+
1443
+
1444
+ # ============================================================
1445
+ # VEHICLE IMAGE PROCESSING
1446
+ # ============================================================
1447
+
1448
+ def process_image(img):
1449
+
1450
+ if img is None:
1451
+
1452
+ return (
1453
+
1454
+ None,
1455
+
1456
+ "Please upload an image.",
1457
+
1458
+ "0",
1459
+
1460
+ "0",
1461
+
1462
+ "β‚Ή0",
1463
+
1464
+ get_dashboard()
1465
+
1466
+ )
1467
+
1468
+
1469
+ try:
1470
+
1471
+ # ----------------------------------------------------
1472
+ # Convert input to PIL
1473
+ # ----------------------------------------------------
1474
+
1475
+ if isinstance(img, np.ndarray):
1476
+
1477
+ image = Image.fromarray(
1478
+ img.astype(np.uint8)
1479
+ )
1480
+
1481
+ elif isinstance(img, Image.Image):
1482
+
1483
+ image = img.copy()
1484
+
1485
+ else:
1486
+
1487
+ return (
1488
 
1489
+ None,
 
 
 
1490
 
1491
+ "Unsupported image format.",
 
 
1492
 
1493
+ "0",
 
1494
 
1495
+ "0",
1496
 
1497
+ "β‚Ή0",
1498
 
1499
+ get_dashboard()
1500
 
1501
+ )
1502
 
 
 
 
 
 
 
 
 
1503
 
1504
+ # ----------------------------------------------------
1505
+ # YOLOS PROCESSING
1506
+ # ----------------------------------------------------
1507
 
1508
+ inputs = processor(
1509
+ images=image,
1510
+ return_tensors="pt"
1511
+ )
1512
 
 
 
 
 
1513
 
1514
+ with torch.no_grad():
 
1515
 
1516
+ outputs = model(
1517
+ **inputs
1518
+ )
1519
 
 
1520
 
1521
+ target_sizes = torch.tensor(
1522
+ [
1523
+ [
1524
+ image.size[1],
1525
+ image.size[0]
1526
+ ]
1527
+ ]
1528
+ )
1529
 
 
1530
 
1531
+ results = (
1532
+ processor
1533
+ .post_process_object_detection(
1534
+ outputs,
1535
+ threshold=0.30,
1536
+ target_sizes=target_sizes
1537
+ )[0]
1538
+ )
1539
 
 
 
 
 
 
1540
 
1541
+ draw = ImageDraw.Draw(
1542
+ image
1543
+ )
1544
 
 
 
 
1545
 
1546
+ ev_count = 0
1547
 
1548
+ non_ev_count = 0
1549
 
1550
+ discount_total = 0
 
 
 
1551
 
1552
+ output_text = ""
1553
 
 
1554
 
1555
+ # ----------------------------------------------------
1556
+ # NO PLATE DETECTION
1557
+ # ----------------------------------------------------
 
 
 
1558
 
1559
+ if len(results["boxes"]) == 0:
1560
 
1561
+ return (
 
1562
 
1563
+ image,
1564
 
1565
+ "No license plate detected.",
1566
 
1567
+ "0",
1568
 
1569
+ "0",
1570
 
1571
+ "β‚Ή0",
 
 
 
 
1572
 
1573
+ get_dashboard()
 
 
 
 
1574
 
1575
+ )
1576
 
 
1577
 
1578
+ # ----------------------------------------------------
1579
+ # PROCESS EACH DETECTED PLATE
1580
+ # ----------------------------------------------------
 
 
 
 
 
1581
 
1582
+ processed_count = 0
1583
 
 
1584
 
1585
+ for i, box in enumerate(
1586
+ results["boxes"]
1587
+ ):
1588
 
 
 
 
1589
 
1590
+ x1, y1, x2, y2 = map(
1591
+ int,
1592
+ box.tolist()
1593
+ )
1594
 
 
 
 
 
1595
 
1596
+ # ------------------------------------------------
1597
+ # Boundary validation
1598
+ # ------------------------------------------------
1599
 
1600
+ x1 = max(
1601
+ 0,
1602
+ min(x1, image.width - 1)
1603
+ )
1604
 
1605
+ y1 = max(
1606
+ 0,
1607
+ min(y1, image.height - 1)
1608
+ )
1609
 
1610
+ x2 = max(
1611
+ 0,
1612
+ min(x2, image.width)
1613
+ )
1614
 
1615
+ y2 = max(
1616
+ 0,
1617
+ min(y2, image.height)
1618
+ )
 
1619
 
 
 
 
1620
 
1621
+ if x2 <= x1 or y2 <= y1:
1622
+ continue
1623
 
 
 
 
1624
 
1625
+ # ------------------------------------------------
1626
+ # Crop license plate
1627
+ # ------------------------------------------------
1628
 
1629
+ plate = image.crop(
1630
+ (
1631
+ x1,
1632
+ y1,
1633
+ x2,
1634
+ y2
1635
+ )
1636
+ )
1637
 
 
 
 
1638
 
1639
+ # ------------------------------------------------
1640
+ # EV / NON-EV
1641
+ # ------------------------------------------------
1642
 
1643
+ status = classify_plate_color(
1644
+ plate
1645
+ )
1646
 
 
 
 
1647
 
1648
+ # ------------------------------------------------
1649
+ # OCR
1650
+ # ------------------------------------------------
1651
 
1652
+ plate_number = read_license_plate(
1653
+ plate
1654
+ )
 
 
1655
 
 
 
 
 
 
1656
 
1657
+ # ------------------------------------------------
1658
+ # Save database record
1659
+ # ------------------------------------------------
 
 
 
 
1660
 
1661
+ saved = save_vehicle(
1662
+ plate_number,
1663
+ status
1664
+ )
 
 
 
1665
 
 
1666
 
1667
+ if saved == "Duplicate":
1668
 
1669
+ output_text += (
 
1670
 
1671
+ f"Vehicle {i + 1}\n"
1672
 
1673
+ f"Plate: {plate_number}\n"
1674
 
1675
+ f"Status: {status}\n"
1676
 
1677
+ "Result: Duplicate - "
1678
+ "not added again\n\n"
1679
 
1680
+ )
 
 
 
 
1681
 
1682
+ continue
 
1683
 
 
1684
 
1685
+ if saved == "Error":
 
1686
 
1687
+ output_text += (
 
1688
 
1689
+ f"Vehicle {i + 1}\n"
1690
 
1691
+ f"Plate: {plate_number}\n"
 
1692
 
1693
+ f"Status: {status}\n"
 
 
 
 
 
1694
 
1695
+ "Database: Error\n\n"
1696
 
1697
+ )
 
1698
 
 
 
 
 
 
 
1699
 
1700
+ # ------------------------------------------------
1701
+ # EV
1702
+ # ------------------------------------------------
1703
 
1704
+ if status == "EV":
 
1705
 
1706
+ ev_count += 1
 
1707
 
1708
+ discount = EV_DISCOUNT
 
1709
 
1710
+ discount_total += discount
1711
 
1712
+ label = (
1713
+ f"{plate_number} | "
1714
+ f"EV | β‚Ή{discount}"
1715
+ )
1716
 
1717
+ outline_color = "green"
1718
 
 
 
1719
 
1720
+ # ------------------------------------------------
1721
+ # NON-EV
1722
+ # ------------------------------------------------
 
 
1723
 
1724
+ else:
1725
 
1726
+ non_ev_count += 1
1727
 
1728
+ discount = 0
1729
 
1730
+ label = (
1731
+ f"{plate_number} | Non-EV"
1732
+ )
1733
 
1734
+ outline_color = "red"
 
1735
 
 
 
 
1736
 
1737
+ processed_count += 1
1738
 
 
 
 
 
 
 
 
 
1739
 
1740
+ # ------------------------------------------------
1741
+ # Draw bounding box
1742
+ # ------------------------------------------------
1743
 
1744
+ draw.rectangle(
1745
+ [
1746
+ x1,
1747
+ y1,
1748
+ x2,
1749
+ y2
1750
+ ],
1751
+ outline=outline_color,
1752
+ width=4
1753
+ )
1754
 
 
 
 
 
1755
 
1756
+ # ------------------------------------------------
1757
+ # Draw label background
1758
+ # ------------------------------------------------
 
1759
 
1760
+ text_y = max(
1761
+ 0,
1762
+ y1 - 22
1763
+ )
1764
 
 
 
 
1765
 
1766
+ draw.rectangle(
1767
+ [
1768
+ x1,
1769
+ text_y,
1770
+ min(
1771
+ image.width,
1772
+ x1 + 300
1773
+ ),
1774
+ y1
1775
+ ],
1776
+ fill="black"
1777
+ )
1778
 
 
1779
 
1780
+ draw.text(
1781
+ (
1782
+ x1 + 3,
1783
+ text_y + 3
1784
+ ),
1785
+ label,
1786
+ fill=outline_color
1787
+ )
1788
 
 
1789
 
1790
+ # ------------------------------------------------
1791
+ # Output summary
1792
+ # ------------------------------------------------
1793
 
1794
+ output_text += (
 
 
1795
 
1796
+ f"Vehicle {i + 1}\n"
1797
 
1798
+ f"Plate: {plate_number}\n"
 
 
 
 
 
 
1799
 
1800
+ f"Status: {status}\n"
1801
 
1802
+ f"Discount: β‚Ή{discount}\n\n"
1803
 
1804
+ )
 
 
 
1805
 
 
 
 
 
1806
 
1807
+ # ----------------------------------------------------
1808
+ # If every detection was invalid
1809
+ # ----------------------------------------------------
1810
 
1811
+ if processed_count == 0:
 
 
1812
 
1813
+ return (
 
 
1814
 
1815
+ image,
1816
 
1817
+ "No valid license plate was processed.",
 
1818
 
1819
+ "0",
1820
 
1821
+ "0",
1822
 
1823
+ "β‚Ή0",
1824
 
1825
+ get_dashboard()
1826
 
1827
+ )
 
 
 
 
 
1828
 
 
1829
 
1830
+ # ----------------------------------------------------
1831
+ # Refresh dashboard
1832
+ # ----------------------------------------------------
 
 
1833
 
1834
+ dashboard = get_dashboard()
1835
 
 
 
 
1836
 
1837
+ return (
 
 
 
 
1838
 
1839
+ image,
 
 
 
 
 
1840
 
1841
+ output_text,
1842
 
1843
+ str(ev_count),
1844
 
1845
+ str(non_ev_count),
 
 
1846
 
1847
+ f"β‚Ή{discount_total}",
 
 
 
1848
 
1849
+ dashboard
 
 
 
 
 
 
1850
 
1851
+ )
1852
 
1853
 
1854
+ except Exception as e:
 
 
1855
 
1856
+ print(
1857
+ "Image processing error:",
1858
+ e
1859
+ )
 
1860
 
 
 
 
 
 
 
 
1861
 
1862
+ return (
1863
 
1864
+ img,
1865
 
1866
+ f"Processing error: {str(e)}",
 
 
1867
 
1868
+ "0",
 
 
 
1869
 
1870
+ "0",
 
 
 
 
 
 
1871
 
1872
+ "β‚Ή0",
1873
 
1874
+ get_dashboard()
1875
 
1876
+ )
 
 
1877
 
 
 
 
 
 
1878
 
1879
+ # ============================================================
1880
+ # REFRESH DASHBOARD
1881
+ # ============================================================
 
 
 
 
1882
 
1883
+ def refresh_dashboard():
1884
 
1885
+ return get_dashboard()
1886
 
 
 
 
1887
 
1888
+ # ============================================================
1889
+ # CLEAR AI QUERY
1890
+ # ============================================================
 
 
1891
 
1892
+ def clear_ai():
 
1893
 
1894
+ return (
1895
+ "",
1896
+ "",
1897
+ None
 
 
 
 
 
1898
  )
1899
 
 
1900
 
1901
+ # ============================================================
1902
+ # CSS
1903
+ # ============================================================
1904
 
1905
+ css = """
 
 
1906
 
1907
+ .gradio-container {
 
 
 
 
1908
 
1909
+ max-width: 98% !important;
 
 
 
 
 
 
 
1910
 
1911
+ margin: auto;
1912
 
1913
+ }
1914
 
1915
+ textarea {
 
 
1916
 
1917
+ font-size: 15px !important;
1918
 
1919
+ }
 
 
 
 
 
 
 
 
 
 
1920
 
1921
+ h1 {
 
1922
 
1923
+ text-align: center;
1924
 
1925
+ }
1926
 
1927
+ .block {
1928
 
1929
+ border-radius: 12px;
1930
 
1931
+ }
 
1932
 
1933
+ .gr-image {
 
 
 
 
 
 
 
1934
 
1935
+ min-height: 350px;
1936
 
1937
+ }
1938
 
1939
+ """
1940
 
 
 
 
1941
 
1942
+ # ============================================================
1943
+ # GRADIO APPLICATION
1944
+ # ============================================================
 
1945
 
1946
+ with gr.Blocks(
1947
+ css=css
1948
+ ) as demo:
1949
 
 
1950
 
1951
+ # ========================================================
1952
+ # TITLE
1953
+ # ========================================================
1954
 
1955
+ gr.Markdown(
1956
+ "# 🚦 Smart Traffic & EV Analytics System"
1957
+ )
 
 
 
1958
 
1959
 
1960
+ gr.Markdown(
1961
+ "YOLOS License Plate Detection β€’ "
1962
+ "OpenCV EV Classification β€’ "
1963
+ "EasyOCR β€’ EV Incentives β€’ "
1964
+ "Dynamic AI Analytics"
1965
+ )
1966
 
1967
 
1968
+ # ========================================================
1969
+ # TAB 1 - VEHICLE DETECTION
1970
+ # ========================================================
1971
 
1972
+ with gr.Tab(
1973
+ "πŸš— Vehicle Detection"
1974
+ ):
1975
 
 
 
 
1976
 
1977
+ with gr.Row(
1978
+ equal_height=True
1979
+ ):
1980
 
1981
 
1982
+ # ------------------------------------------------
1983
+ # INPUT
1984
+ # ------------------------------------------------
1985
 
1986
+ with gr.Column(
1987
+ scale=1
1988
+ ):
1989
 
1990
+ input_img = gr.Image(
 
 
 
 
 
 
 
1991
 
1992
+ type="numpy",
1993
 
1994
+ label="Input Vehicle Image",
 
 
1995
 
1996
+ sources=[
1997
+ "upload",
1998
+ "webcam"
1999
+ ],
2000
 
2001
+ height=350
 
 
 
2002
 
2003
+ )
2004
 
 
 
 
 
2005
 
2006
+ btn = gr.Button(
2007
 
2008
+ "πŸ” Scan Vehicle",
 
 
 
2009
 
2010
+ variant="primary",
2011
 
2012
+ size="lg"
 
 
 
 
 
2013
 
2014
+ )
2015
 
 
 
 
 
2016
 
2017
+ # ------------------------------------------------
2018
+ # OUTPUT
2019
+ # ------------------------------------------------
2020
 
2021
+ with gr.Column(
2022
+ scale=1
2023
+ ):
2024
 
2025
+ output_img = gr.Image(
2026
 
2027
+ label="Detection Result",
 
 
2028
 
2029
+ height=350
2030
 
2031
+ )
2032
 
 
2033
 
2034
+ # ------------------------------------------------
2035
+ # SUMMARY
2036
+ # ------------------------------------------------
2037
 
2038
+ with gr.Column(
2039
+ scale=1
2040
+ ):
2041
 
2042
+ summary_box = gr.Textbox(
 
 
2043
 
2044
+ label="Detection Summary",
2045
 
2046
+ lines=16
 
 
2047
 
2048
+ )
2049
 
 
2050
 
2051
+ # ====================================================
2052
+ # STATISTICS
2053
+ # ====================================================
2054
 
2055
+ with gr.Row():
2056
 
 
2057
 
2058
+ ev_box = gr.Textbox(
2059
+ label="⚑ EV Vehicles"
2060
+ )
2061
 
 
2062
 
2063
+ non_ev_box = gr.Textbox(
2064
+ label="πŸš— Non-EV Vehicles"
2065
+ )
 
 
2066
 
2067
 
2068
+ discount_box = gr.Textbox(
2069
+ label="πŸ’° Total Discount"
2070
+ )
2071
 
 
 
 
 
 
 
 
 
2072
 
2073
+ # ====================================================
2074
+ # STATIC DASHBOARD
2075
+ # ====================================================
2076
 
2077
+ gr.Markdown(
2078
+ "## πŸ“Š Traffic Analytics Dashboard"
2079
+ )
2080
 
 
 
 
 
2081
 
2082
+ dashboard = gr.Plot(
2083
+ label="Analytics"
2084
+ )
2085
 
2086
 
2087
+ btn.click(
2088
 
2089
+ fn=process_image,
2090
 
2091
+ inputs=input_img,
2092
 
2093
+ outputs=[
2094
 
2095
+ output_img,
2096
 
2097
+ summary_box,
2098
 
2099
+ ev_box,
2100
 
2101
+ non_ev_box,
2102
 
2103
+ discount_box,
2104
 
2105
+ dashboard
 
2106
 
2107
+ ]
2108
 
2109
+ )
2110
 
 
2111
 
2112
+ # ========================================================
2113
+ # TAB 2 - AGENTIC AI DASHBOARD
2114
+ # ========================================================
2115
 
2116
+ with gr.Tab(
2117
+ "πŸ€– Agentic AI Dashboard"
2118
+ ):
 
2119
 
 
 
 
2120
 
2121
+ gr.Markdown(
2122
+ "## πŸ€– Agentic AI Traffic Analytics Assistant"
2123
+ )
2124
 
 
 
 
2125
 
2126
+ gr.Markdown(
2127
+ """
2128
+ Ask questions about your vehicle database
2129
+ using natural language.
2130
 
2131
+ The AI analytics layer dynamically reads the
2132
+ SQLite database and selects the appropriate
2133
+ analytics operation.
2134
  """
2135
+ )
2136
 
 
 
 
 
 
 
 
 
 
 
2137
 
2138
+ # ----------------------------------------------------
2139
+ # Query input
2140
+ # ----------------------------------------------------
 
2141
 
2142
+ user_query = gr.Textbox(
2143
 
2144
+ label="Ask your traffic analytics question",
 
 
2145
 
2146
+ placeholder=(
2147
+ "Example: How many EV vehicles "
2148
+ "were detected?"
2149
+ ),
2150
 
2151
+ lines=3
2152
 
2153
+ )
2154
 
2155
 
2156
+ with gr.Row():
2157
 
 
2158
 
2159
+ ask_button = gr.Button(
 
 
 
 
 
 
 
 
2160
 
2161
+ "πŸ€– Ask AI",
2162
 
 
 
2163
  variant="primary",
2164
+
2165
  size="lg"
2166
+
2167
  )
2168
 
2169
 
2170
+ clear_button = gr.Button(
2171
+
2172
+ "Clear",
2173
 
2174
+ size="lg"
2175
 
 
 
 
2176
  )
2177
 
2178
 
2179
+ # ----------------------------------------------------
2180
+ # AI RESPONSE
2181
+ # ----------------------------------------------------
2182
 
2183
+ ai_response = gr.Textbox(
2184
 
2185
+ label="πŸ€– AI Analysis",
 
 
 
2186
 
2187
+ lines=12
2188
 
2189
+ )
 
 
2190
 
 
2191
 
2192
+ # ----------------------------------------------------
2193
+ # DYNAMIC GRAPH
2194
+ # ----------------------------------------------------
2195
 
2196
+ ai_graph = gr.Plot(
 
 
2197
 
2198
+ label="πŸ“Š Dynamic AI Visualization"
 
 
2199
 
2200
+ )
2201
 
 
 
 
2202
 
2203
+ # ----------------------------------------------------
2204
+ # BUTTON ACTION
2205
+ # ----------------------------------------------------
2206
 
2207
+ ask_button.click(
 
 
2208
 
2209
+ fn=traffic_ai_agent,
2210
 
2211
+ inputs=user_query,
2212
 
2213
+ outputs=[
2214
+ ai_response,
2215
+ ai_graph
2216
+ ]
2217
 
2218
+ )
2219
 
 
 
 
 
 
 
 
 
2220
 
2221
+ clear_button.click(
2222
 
2223
+ fn=clear_ai,
2224
 
2225
+ inputs=[],
 
 
2226
 
2227
+ outputs=[
2228
+ user_query,
2229
+ ai_response,
2230
+ ai_graph
2231
+ ]
2232
 
2233
+ )
2234
 
 
 
 
2235
 
2236
+ # ====================================================
2237
+ # SUGGESTED QUESTIONS
2238
+ # ====================================================
2239
 
2240
+ gr.Markdown(
2241
+ """
2242
+ ### πŸ’‘ Suggested Questions
 
2243
 
2244
+ **Traffic**
2245
+ - How many vehicles were detected?
2246
+ - What is the total traffic?
2247
+ - Show daily traffic trend.
2248
 
2249
+ **EV Analysis**
2250
+ - How many EV vehicles were detected?
2251
+ - How many Non-EV vehicles are there?
2252
+ - What percentage of vehicles are EVs?
2253
+ - Show EV traffic trend.
2254
 
2255
+ **Incentives**
2256
+ - What is the total discount?
2257
+ - How much incentive was provided?
2258
 
2259
+ **Environmental**
2260
+ - What are the CO2 savings?
 
2261
 
2262
+ **Overall**
2263
+ - Give me an overall traffic summary.
2264
+ """
2265
+ )
2266
 
 
2267
 
2268
+ # ========================================================
2269
+ # TAB 3 - ADMIN / DATABASE
2270
+ # ========================================================
2271
 
2272
+ with gr.Tab(
2273
+ "βš™οΈ Admin Dashboard"
2274
+ ):
2275
 
 
2276
 
2277
+ gr.Markdown(
2278
+ "## βš™οΈ Vehicle Database"
2279
+ )
2280
 
 
2281
 
2282
+ database_table = gr.Dataframe(
2283
 
2284
+ label="Stored Vehicle Records",
2285
 
2286
+ interactive=False
2287
 
2288
+ )
2289
 
 
2290
 
2291
+ with gr.Row():
2292
 
2293
 
2294
+ refresh_button = gr.Button(
2295
+ "πŸ”„ Refresh Database"
2296
+ )
2297
 
 
2298
 
2299
+ refresh_graph_button = gr.Button(
2300
+ "πŸ“Š Refresh Dashboard"
2301
+ )
2302
 
2303
 
2304
+ admin_dashboard = gr.Plot(
2305
+ label="Current Database Analytics"
2306
+ )
2307
 
 
2308
 
2309
+ def load_database():
2310
 
2311
+ return get_vehicle_dataframe()
 
 
 
2312
 
 
2313
 
2314
+ refresh_button.click(
2315
 
2316
+ fn=load_database,
 
 
2317
 
2318
+ inputs=[],
 
 
2319
 
2320
+ outputs=database_table
2321
 
2322
+ )
2323
 
 
2324
 
2325
+ refresh_graph_button.click(
2326
 
2327
+ fn=refresh_dashboard,
2328
 
2329
+ inputs=[],
2330
 
2331
+ outputs=admin_dashboard
2332
 
2333
+ )
 
 
 
2334
 
 
2335
 
2336
  # ============================================================
2337
+ # APPLICATION START
2338
+ # ============================================================
2339
+
2340
+ if __name__ == "__main__":
2341
 
2342
+ print(
2343
+ "Starting Smart Traffic & EV Analytics System..."
2344
+ )
2345
 
2346
+ demo.queue()
 
2347
 
2348
+ demo.launch(
2349
+ ssr_mode=False
2350
+ )