Update app.py
Browse files
app.py
CHANGED
|
@@ -8,6 +8,9 @@ import torch
|
|
| 8 |
import numpy as np
|
| 9 |
import cv2
|
| 10 |
import sqlite3
|
|
|
|
|
|
|
|
|
|
| 11 |
import pandas as pd
|
| 12 |
import matplotlib.pyplot as plt
|
| 13 |
import easyocr
|
|
@@ -1879,6 +1882,253 @@ def process_image(img):
|
|
| 1879 |
)
|
| 1880 |
|
| 1881 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1882 |
# ============================================================
|
| 1883 |
# REFRESH DASHBOARD
|
| 1884 |
# ============================================================
|
|
@@ -2112,8 +2362,96 @@ with gr.Blocks(
|
|
| 2112 |
)
|
| 2113 |
|
| 2114 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2115 |
# ========================================================
|
| 2116 |
-
# TAB
|
| 2117 |
# ========================================================
|
| 2118 |
|
| 2119 |
with gr.Tab(
|
|
@@ -2267,7 +2605,7 @@ analytics operation.
|
|
| 2267 |
)
|
| 2268 |
|
| 2269 |
# ========================================================
|
| 2270 |
-
# TAB
|
| 2271 |
# ========================================================
|
| 2272 |
|
| 2273 |
with gr.Tab(
|
|
|
|
| 8 |
import numpy as np
|
| 9 |
import cv2
|
| 10 |
import sqlite3
|
| 11 |
+
import os
|
| 12 |
+
import tempfile
|
| 13 |
+
import uuid
|
| 14 |
import pandas as pd
|
| 15 |
import matplotlib.pyplot as plt
|
| 16 |
import easyocr
|
|
|
|
| 1882 |
)
|
| 1883 |
|
| 1884 |
|
| 1885 |
+
|
| 1886 |
+
# ============================================================
|
| 1887 |
+
# VIDEO PROCESSING
|
| 1888 |
+
# ============================================================
|
| 1889 |
+
|
| 1890 |
+
def process_video(video_path, frame_skip=5, max_frames=0):
|
| 1891 |
+
"""Process an uploaded traffic video and create an annotated output video."""
|
| 1892 |
+
|
| 1893 |
+
if video_path is None:
|
| 1894 |
+
return None, "Please upload a video.", "0", "0", "₹0", get_dashboard()
|
| 1895 |
+
|
| 1896 |
+
cap = None
|
| 1897 |
+
writer = None
|
| 1898 |
+
|
| 1899 |
+
try:
|
| 1900 |
+
cap = cv2.VideoCapture(video_path)
|
| 1901 |
+
|
| 1902 |
+
if not cap.isOpened():
|
| 1903 |
+
return None, "Unable to open the uploaded video.", "0", "0", "₹0", get_dashboard()
|
| 1904 |
+
|
| 1905 |
+
fps = cap.get(cv2.CAP_PROP_FPS)
|
| 1906 |
+
if not fps or fps <= 0:
|
| 1907 |
+
fps = 25.0
|
| 1908 |
+
|
| 1909 |
+
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 1910 |
+
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 1911 |
+
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 1912 |
+
|
| 1913 |
+
if width <= 0 or height <= 0:
|
| 1914 |
+
return None, "Invalid video dimensions.", "0", "0", "₹0", get_dashboard()
|
| 1915 |
+
|
| 1916 |
+
try:
|
| 1917 |
+
frame_skip = max(1, int(frame_skip))
|
| 1918 |
+
except Exception:
|
| 1919 |
+
frame_skip = 5
|
| 1920 |
+
|
| 1921 |
+
try:
|
| 1922 |
+
max_frames = max(0, int(max_frames))
|
| 1923 |
+
except Exception:
|
| 1924 |
+
max_frames = 0
|
| 1925 |
+
|
| 1926 |
+
output_path = os.path.join(
|
| 1927 |
+
tempfile.gettempdir(),
|
| 1928 |
+
f"traffic_result_{uuid.uuid4().hex}.mp4"
|
| 1929 |
+
)
|
| 1930 |
+
|
| 1931 |
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
| 1932 |
+
writer = cv2.VideoWriter(
|
| 1933 |
+
output_path,
|
| 1934 |
+
fourcc,
|
| 1935 |
+
fps,
|
| 1936 |
+
(width, height)
|
| 1937 |
+
)
|
| 1938 |
+
|
| 1939 |
+
if not writer.isOpened():
|
| 1940 |
+
return None, "Unable to create the processed output video.", "0", "0", "₹0", get_dashboard()
|
| 1941 |
+
|
| 1942 |
+
ev_count = 0
|
| 1943 |
+
non_ev_count = 0
|
| 1944 |
+
discount_total = 0
|
| 1945 |
+
detected_plates = 0
|
| 1946 |
+
processed_frames = 0
|
| 1947 |
+
frame_number = 0
|
| 1948 |
+
|
| 1949 |
+
# Prevent repeated database records for the same known plate
|
| 1950 |
+
# across multiple frames of this uploaded video.
|
| 1951 |
+
video_seen_plates = set()
|
| 1952 |
+
|
| 1953 |
+
output_lines = [
|
| 1954 |
+
"VIDEO TRAFFIC ANALYSIS",
|
| 1955 |
+
"",
|
| 1956 |
+
f"Input frames: {total_frames if total_frames > 0 else 'Unknown'}",
|
| 1957 |
+
f"Processing every {frame_skip} frame(s)",
|
| 1958 |
+
""
|
| 1959 |
+
]
|
| 1960 |
+
|
| 1961 |
+
while True:
|
| 1962 |
+
ret, frame = cap.read()
|
| 1963 |
+
|
| 1964 |
+
if not ret:
|
| 1965 |
+
break
|
| 1966 |
+
|
| 1967 |
+
if max_frames > 0 and frame_number >= max_frames:
|
| 1968 |
+
break
|
| 1969 |
+
|
| 1970 |
+
# Write unprocessed frames unchanged.
|
| 1971 |
+
if frame_number % frame_skip != 0:
|
| 1972 |
+
writer.write(frame)
|
| 1973 |
+
frame_number += 1
|
| 1974 |
+
continue
|
| 1975 |
+
|
| 1976 |
+
processed_frames += 1
|
| 1977 |
+
|
| 1978 |
+
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 1979 |
+
image = Image.fromarray(rgb_frame)
|
| 1980 |
+
|
| 1981 |
+
inputs = processor(
|
| 1982 |
+
images=image,
|
| 1983 |
+
return_tensors="pt"
|
| 1984 |
+
)
|
| 1985 |
+
|
| 1986 |
+
with torch.no_grad():
|
| 1987 |
+
outputs = model(**inputs)
|
| 1988 |
+
|
| 1989 |
+
target_sizes = torch.tensor(
|
| 1990 |
+
[[image.size[1], image.size[0]]]
|
| 1991 |
+
)
|
| 1992 |
+
|
| 1993 |
+
results = processor.post_process_object_detection(
|
| 1994 |
+
outputs,
|
| 1995 |
+
threshold=0.30,
|
| 1996 |
+
target_sizes=target_sizes
|
| 1997 |
+
)[0]
|
| 1998 |
+
|
| 1999 |
+
draw = ImageDraw.Draw(image)
|
| 2000 |
+
|
| 2001 |
+
for i, box in enumerate(results["boxes"]):
|
| 2002 |
+
x1, y1, x2, y2 = map(int, box.tolist())
|
| 2003 |
+
|
| 2004 |
+
x1 = max(0, min(x1, image.width - 1))
|
| 2005 |
+
y1 = max(0, min(y1, image.height - 1))
|
| 2006 |
+
x2 = max(0, min(x2, image.width))
|
| 2007 |
+
y2 = max(0, min(y2, image.height))
|
| 2008 |
+
|
| 2009 |
+
if x2 <= x1 or y2 <= y1:
|
| 2010 |
+
continue
|
| 2011 |
+
|
| 2012 |
+
plate = image.crop((x1, y1, x2, y2))
|
| 2013 |
+
|
| 2014 |
+
status = classify_plate_color(plate)
|
| 2015 |
+
plate_number = read_license_plate(plate)
|
| 2016 |
+
detected_plates += 1
|
| 2017 |
+
|
| 2018 |
+
normalized_plate = str(plate_number).strip().upper()
|
| 2019 |
+
should_count = True
|
| 2020 |
+
|
| 2021 |
+
if normalized_plate not in ("", "UNKNOWN", "OCR_UNAVAILABLE"):
|
| 2022 |
+
if normalized_plate in video_seen_plates:
|
| 2023 |
+
should_count = False
|
| 2024 |
+
else:
|
| 2025 |
+
video_seen_plates.add(normalized_plate)
|
| 2026 |
+
|
| 2027 |
+
if should_count:
|
| 2028 |
+
saved = save_vehicle(plate_number, status)
|
| 2029 |
+
|
| 2030 |
+
if saved not in ("Duplicate", "Error"):
|
| 2031 |
+
if status == "EV":
|
| 2032 |
+
ev_count += 1
|
| 2033 |
+
discount_total += EV_DISCOUNT
|
| 2034 |
+
else:
|
| 2035 |
+
non_ev_count += 1
|
| 2036 |
+
|
| 2037 |
+
output_lines.extend([
|
| 2038 |
+
f"Vehicle {ev_count + non_ev_count}",
|
| 2039 |
+
f"Plate: {plate_number}",
|
| 2040 |
+
f"Status: {status}",
|
| 2041 |
+
f"Discount: ₹{EV_DISCOUNT if status == 'EV' else 0}",
|
| 2042 |
+
""
|
| 2043 |
+
])
|
| 2044 |
+
|
| 2045 |
+
if status == "EV":
|
| 2046 |
+
outline_color = "green"
|
| 2047 |
+
label = f"{plate_number} | EV | ₹{EV_DISCOUNT}"
|
| 2048 |
+
else:
|
| 2049 |
+
outline_color = "red"
|
| 2050 |
+
label = f"{plate_number} | Non-EV"
|
| 2051 |
+
|
| 2052 |
+
draw.rectangle(
|
| 2053 |
+
[x1, y1, x2, y2],
|
| 2054 |
+
outline=outline_color,
|
| 2055 |
+
width=4
|
| 2056 |
+
)
|
| 2057 |
+
|
| 2058 |
+
text_y = max(0, y1 - 22)
|
| 2059 |
+
|
| 2060 |
+
draw.rectangle(
|
| 2061 |
+
[
|
| 2062 |
+
x1,
|
| 2063 |
+
text_y,
|
| 2064 |
+
min(image.width, x1 + 330),
|
| 2065 |
+
y1
|
| 2066 |
+
],
|
| 2067 |
+
fill="black"
|
| 2068 |
+
)
|
| 2069 |
+
|
| 2070 |
+
draw.text(
|
| 2071 |
+
(x1 + 3, text_y + 3),
|
| 2072 |
+
label,
|
| 2073 |
+
fill=outline_color
|
| 2074 |
+
)
|
| 2075 |
+
|
| 2076 |
+
output_frame = cv2.cvtColor(
|
| 2077 |
+
np.array(image),
|
| 2078 |
+
cv2.COLOR_RGB2BGR
|
| 2079 |
+
)
|
| 2080 |
+
|
| 2081 |
+
writer.write(output_frame)
|
| 2082 |
+
frame_number += 1
|
| 2083 |
+
|
| 2084 |
+
cap.release()
|
| 2085 |
+
cap = None
|
| 2086 |
+
|
| 2087 |
+
writer.release()
|
| 2088 |
+
writer = None
|
| 2089 |
+
|
| 2090 |
+
if processed_frames == 0:
|
| 2091 |
+
return None, "No frames were processed.", "0", "0", "₹0", get_dashboard()
|
| 2092 |
+
|
| 2093 |
+
total_unique = ev_count + non_ev_count
|
| 2094 |
+
ev_percentage = (
|
| 2095 |
+
ev_count / total_unique * 100
|
| 2096 |
+
if total_unique > 0 else 0
|
| 2097 |
+
)
|
| 2098 |
+
|
| 2099 |
+
output_lines.extend([
|
| 2100 |
+
"VIDEO SUMMARY",
|
| 2101 |
+
f"Processed frames: {processed_frames}",
|
| 2102 |
+
f"Detected plate instances: {detected_plates}",
|
| 2103 |
+
f"Unique vehicles recorded: {total_unique}",
|
| 2104 |
+
f"EV vehicles: {ev_count}",
|
| 2105 |
+
f"Non-EV vehicles: {non_ev_count}",
|
| 2106 |
+
f"EV percentage: {ev_percentage:.2f}%",
|
| 2107 |
+
f"Total EV discount: ₹{discount_total}"
|
| 2108 |
+
])
|
| 2109 |
+
|
| 2110 |
+
return (
|
| 2111 |
+
output_path,
|
| 2112 |
+
"\n".join(output_lines),
|
| 2113 |
+
str(ev_count),
|
| 2114 |
+
str(non_ev_count),
|
| 2115 |
+
f"₹{discount_total}",
|
| 2116 |
+
get_dashboard()
|
| 2117 |
+
)
|
| 2118 |
+
|
| 2119 |
+
except Exception as e:
|
| 2120 |
+
print("Video processing error:", e)
|
| 2121 |
+
|
| 2122 |
+
if cap is not None:
|
| 2123 |
+
cap.release()
|
| 2124 |
+
|
| 2125 |
+
if writer is not None:
|
| 2126 |
+
writer.release()
|
| 2127 |
+
|
| 2128 |
+
return None, f"Video processing error: {str(e)}", "0", "0", "₹0", get_dashboard()
|
| 2129 |
+
|
| 2130 |
+
|
| 2131 |
+
|
| 2132 |
# ============================================================
|
| 2133 |
# REFRESH DASHBOARD
|
| 2134 |
# ============================================================
|
|
|
|
| 2362 |
)
|
| 2363 |
|
| 2364 |
|
| 2365 |
+
|
| 2366 |
+
# ========================================================
|
| 2367 |
+
# TAB 2 - VIDEO PROCESSING
|
| 2368 |
+
# ========================================================
|
| 2369 |
+
|
| 2370 |
+
with gr.Tab("Video Processing"):
|
| 2371 |
+
|
| 2372 |
+
gr.Markdown("## Video Traffic & EV Analysis")
|
| 2373 |
+
|
| 2374 |
+
gr.Markdown(
|
| 2375 |
+
"Upload a traffic video to detect license plates, "
|
| 2376 |
+
"classify EV/Non-EV vehicles, read plate numbers using "
|
| 2377 |
+
"EasyOCR, calculate EV incentives, and generate an "
|
| 2378 |
+
"annotated output video."
|
| 2379 |
+
)
|
| 2380 |
+
|
| 2381 |
+
with gr.Row(equal_height=True):
|
| 2382 |
+
|
| 2383 |
+
with gr.Column(scale=1):
|
| 2384 |
+
|
| 2385 |
+
input_video = gr.Video(
|
| 2386 |
+
label="Upload Traffic Video",
|
| 2387 |
+
sources=["upload"],
|
| 2388 |
+
format="mp4"
|
| 2389 |
+
)
|
| 2390 |
+
|
| 2391 |
+
frame_skip = gr.Slider(
|
| 2392 |
+
minimum=1,
|
| 2393 |
+
maximum=15,
|
| 2394 |
+
value=5,
|
| 2395 |
+
step=1,
|
| 2396 |
+
label="Process Every Nth Frame",
|
| 2397 |
+
info="1 = every frame; higher values reduce processing time."
|
| 2398 |
+
)
|
| 2399 |
+
|
| 2400 |
+
max_frames = gr.Number(
|
| 2401 |
+
value=0,
|
| 2402 |
+
precision=0,
|
| 2403 |
+
label="Maximum Frames",
|
| 2404 |
+
info="0 = process the complete video."
|
| 2405 |
+
)
|
| 2406 |
+
|
| 2407 |
+
video_btn = gr.Button(
|
| 2408 |
+
"Process Video",
|
| 2409 |
+
variant="primary",
|
| 2410 |
+
size="lg"
|
| 2411 |
+
)
|
| 2412 |
+
|
| 2413 |
+
with gr.Column(scale=1):
|
| 2414 |
+
|
| 2415 |
+
output_video = gr.Video(
|
| 2416 |
+
label="Processed Video",
|
| 2417 |
+
autoplay=False
|
| 2418 |
+
)
|
| 2419 |
+
|
| 2420 |
+
video_summary = gr.Textbox(
|
| 2421 |
+
label="Video Analysis Summary",
|
| 2422 |
+
lines=18
|
| 2423 |
+
)
|
| 2424 |
+
|
| 2425 |
+
with gr.Row():
|
| 2426 |
+
|
| 2427 |
+
video_ev_box = gr.Textbox(label="EV Vehicles")
|
| 2428 |
+
video_non_ev_box = gr.Textbox(label="Non-EV Vehicles")
|
| 2429 |
+
video_discount_box = gr.Textbox(label="Total Discount")
|
| 2430 |
+
|
| 2431 |
+
gr.Markdown("## Video Traffic Analytics Dashboard")
|
| 2432 |
+
|
| 2433 |
+
video_dashboard = gr.Plot(label="Video Analytics")
|
| 2434 |
+
|
| 2435 |
+
video_btn.click(
|
| 2436 |
+
fn=process_video,
|
| 2437 |
+
inputs=[
|
| 2438 |
+
input_video,
|
| 2439 |
+
frame_skip,
|
| 2440 |
+
max_frames
|
| 2441 |
+
],
|
| 2442 |
+
outputs=[
|
| 2443 |
+
output_video,
|
| 2444 |
+
video_summary,
|
| 2445 |
+
video_ev_box,
|
| 2446 |
+
video_non_ev_box,
|
| 2447 |
+
video_discount_box,
|
| 2448 |
+
video_dashboard
|
| 2449 |
+
]
|
| 2450 |
+
)
|
| 2451 |
+
|
| 2452 |
+
|
| 2453 |
# ========================================================
|
| 2454 |
+
# TAB 3 - AGENTIC AI DASHBOARD
|
| 2455 |
# ========================================================
|
| 2456 |
|
| 2457 |
with gr.Tab(
|
|
|
|
| 2605 |
)
|
| 2606 |
|
| 2607 |
# ========================================================
|
| 2608 |
+
# TAB 4 - ADMIN / DATABASE
|
| 2609 |
# ========================================================
|
| 2610 |
|
| 2611 |
with gr.Tab(
|