Spaces:
Running
Running
Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from PIL import Image
|
3 |
+
import onnx
|
4 |
+
import easyocr
|
5 |
+
import os
|
6 |
+
import cv2
|
7 |
+
import numpy as np
|
8 |
+
import pandas as pd
|
9 |
+
import tensorflow as tf
|
10 |
+
import pytesseract as pt
|
11 |
+
import plotly.express as px
|
12 |
+
import matplotlib.pyplot as plt
|
13 |
+
import xml.etree.ElementTree as xet
|
14 |
+
import pytesseract
|
15 |
+
from transformers import TrOCRProcessor, VisionEncoderDecoderModel
|
16 |
+
from PIL import Image
|
17 |
+
import torch
|
18 |
+
|
19 |
+
from glob import glob
|
20 |
+
from skimage import io
|
21 |
+
from shutil import copy
|
22 |
+
from tensorflow.keras.models import Model
|
23 |
+
from tensorflow.keras.callbacks import TensorBoard
|
24 |
+
from sklearn.model_selection import train_test_split
|
25 |
+
from tensorflow.keras.applications import InceptionResNetV2
|
26 |
+
from tensorflow.keras.layers import Dense, Dropout, Flatten, Input
|
27 |
+
from tensorflow.keras.preprocessing.image import load_img, img_to_array
|
28 |
+
|
29 |
+
#Define the Image height and width
|
30 |
+
INPUT_WIDTH = 640
|
31 |
+
INPUT_HEIGHT = 640
|
32 |
+
|
33 |
+
# load YOLO model
|
34 |
+
net = cv2.dnn.readNetFromONNX('/Users/kartikrathi/Documents/ANPR_Project/yolov5/runs/train/Model/weights/best.onnx')
|
35 |
+
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
|
36 |
+
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
|
37 |
+
|
38 |
+
|
39 |
+
def get_detections(img, net):
|
40 |
+
# 1. CONVERT IMAGE TO YOLO FORMAT
|
41 |
+
image = np.array(img)
|
42 |
+
|
43 |
+
# Ensure the image has three channels (remove alpha channel if present)
|
44 |
+
if image.shape[2] == 4:
|
45 |
+
image = image[:, :, :3]
|
46 |
+
|
47 |
+
row, col, _ = image.shape # Extract row, column, and channels of image
|
48 |
+
|
49 |
+
max_rc = max(row, col) # Calculate max number of rows and cols
|
50 |
+
input_image = np.zeros((max_rc, max_rc, 3), dtype=np.uint8)
|
51 |
+
input_image[0:row, 0:col, :] = image # Assign values to the channels
|
52 |
+
|
53 |
+
# 2. GET PREDICTION FROM YOLO MODEL
|
54 |
+
blob = cv2.dnn.blobFromImage(input_image, 1/255.0, (INPUT_WIDTH, INPUT_HEIGHT), swapRB=True, crop=False)
|
55 |
+
net.setInput(blob)
|
56 |
+
preds = net.forward()
|
57 |
+
detections = preds[0]
|
58 |
+
|
59 |
+
return input_image, detections
|
60 |
+
|
61 |
+
|
62 |
+
def non_maximum_supression(input_image,detections):
|
63 |
+
|
64 |
+
'''
|
65 |
+
This function takes the preprocessed image (input_image) and the raw detections obtained from the YOLO model
|
66 |
+
(detections) and performs non-maximum suppression (NMS) to filter out redundant detections.
|
67 |
+
'''
|
68 |
+
|
69 |
+
# 3. FILTER DETECTIONS BASED ON CONFIDENCE AND PROBABILIY SCORE
|
70 |
+
|
71 |
+
# center x, center y, w , h, conf, proba
|
72 |
+
boxes = []
|
73 |
+
confidences = []
|
74 |
+
|
75 |
+
image_w, image_h = input_image.shape[:2]
|
76 |
+
x_factor = image_w/INPUT_WIDTH #x_factor and y_factor are scaling factors to adjust bounding box coordinates based on the original image dimensions.
|
77 |
+
y_factor = image_h/INPUT_HEIGHT
|
78 |
+
|
79 |
+
|
80 |
+
#The confidence score indicates how sure the model is that the box contains an object and also how accurate it thinks the box is that predicts
|
81 |
+
for i in range(len(detections)):
|
82 |
+
row = detections[i]
|
83 |
+
confidence = row[4] # confidence of detecting license plate
|
84 |
+
if confidence > 0.4:
|
85 |
+
class_score = row[5] # probability score of license plate
|
86 |
+
if class_score > 0.25:
|
87 |
+
cx, cy , w, h = row[0:4]
|
88 |
+
|
89 |
+
left = int((cx - 0.5*w)*x_factor)
|
90 |
+
top = int((cy-0.5*h)*y_factor)
|
91 |
+
width = int(w*x_factor)
|
92 |
+
height = int(h*y_factor)
|
93 |
+
box = np.array([left,top,width,height])
|
94 |
+
|
95 |
+
confidences.append(confidence)
|
96 |
+
boxes.append(box)
|
97 |
+
|
98 |
+
# CLEAN
|
99 |
+
boxes_np = np.array(boxes).tolist()
|
100 |
+
confidences_np = np.array(confidences).tolist()
|
101 |
+
|
102 |
+
# NMS
|
103 |
+
index = cv2.dnn.NMSBoxes(boxes_np,confidences_np,0.25,0.45)
|
104 |
+
|
105 |
+
return boxes_np, confidences_np, index
|
106 |
+
|
107 |
+
def drawings(image, boxes_np, confidences_np, index):
|
108 |
+
# Drawings
|
109 |
+
image_draw = image.copy()
|
110 |
+
image_draw = np.array(image_draw)# Create a copy to avoid modifying the original image
|
111 |
+
for ind in index:
|
112 |
+
x, y, w, h = boxes_np[ind]
|
113 |
+
bb_conf = confidences_np[ind]
|
114 |
+
conf_text = 'plate: {:.0f}%'.format(bb_conf * 100)
|
115 |
+
|
116 |
+
cv2.rectangle(image_draw, (int(x), int(y)), (int(x+w), int(y+h)), (255, 0, 255), 2)
|
117 |
+
cv2.rectangle(image_draw, (int(x), int(y-30)), (int(x+w), int(y)), (255, 0, 255), -1)
|
118 |
+
cv2.rectangle(image_draw, (int(x), int(y+h)), (int(x+w), int(y+h+25)), (0, 0, 0), -1)
|
119 |
+
|
120 |
+
cv2.putText(image_draw, conf_text, (int(x), int(y-10)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 1)
|
121 |
+
|
122 |
+
return image_draw
|
123 |
+
|
124 |
+
def yolo_predictions(img, net):
|
125 |
+
# Step 1: Get detections
|
126 |
+
input_image, detections = get_detections(img, net)
|
127 |
+
|
128 |
+
# Step 2: Non-Maximum Suppression
|
129 |
+
boxes_np, confidences_np, index = non_maximum_supression(input_image, detections)
|
130 |
+
|
131 |
+
# Step 3: Drawings
|
132 |
+
result_img = drawings(img, boxes_np, confidences_np, index)
|
133 |
+
|
134 |
+
# Extract bounding box coordinates
|
135 |
+
bounding_box_coords = [boxes_np[i] for i in index]
|
136 |
+
|
137 |
+
return result_img, bounding_box_coords
|
138 |
+
|
139 |
+
#Crop the image
|
140 |
+
def crop_image(img, x, y, width, height):
|
141 |
+
|
142 |
+
# Convert the image array to a Pillow Image object
|
143 |
+
pil_img = Image.fromarray(img)
|
144 |
+
|
145 |
+
# Crop the image using the provided coordinates
|
146 |
+
cropped_img = pil_img.crop((x, y, x + width, y + height))
|
147 |
+
|
148 |
+
# Convert the cropped image back to a NumPy array
|
149 |
+
cropped_img_array = np.array(cropped_img)
|
150 |
+
|
151 |
+
return cropped_img_array
|
152 |
+
|
153 |
+
def tr_ocr_image_from_array(image_array):
|
154 |
+
processor = TrOCRProcessor.from_pretrained("microsoft/trocr-large-printed")
|
155 |
+
model = VisionEncoderDecoderModel.from_pretrained("microsoft/trocr-large-printed")
|
156 |
+
|
157 |
+
def tr_ocr_image(src_img):
|
158 |
+
pixel_values = processor(images=src_img, return_tensors="pt").pixel_values
|
159 |
+
generated_ids = model.generate(pixel_values)
|
160 |
+
return processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
|
161 |
+
|
162 |
+
# Convert the image array to a PIL image
|
163 |
+
img = Image.fromarray(image_array).convert("RGB")
|
164 |
+
|
165 |
+
# Perform OCR on the image
|
166 |
+
return tr_ocr_image(img)
|
167 |
+
|
168 |
+
def main():
|
169 |
+
st.title("Automatic Number Plate Recognition (ANPR) App")
|
170 |
+
st.write("Upload an image and let the app detect the vehicle number plate.")
|
171 |
+
|
172 |
+
uploaded_image = st.file_uploader("Choose an image ", type=["jpg", "jpeg", "png"])
|
173 |
+
|
174 |
+
if uploaded_image is not None:
|
175 |
+
# Display the uploaded image
|
176 |
+
st.image(uploaded_image, caption="Uploaded Image", use_column_width=True)
|
177 |
+
|
178 |
+
# Convert the uploaded image to a PIL Image
|
179 |
+
image = Image.open(uploaded_image)
|
180 |
+
|
181 |
+
# Detect the number plate
|
182 |
+
number_plate_img, coords = yolo_predictions(image, net)
|
183 |
+
|
184 |
+
# Display the detected number plate image
|
185 |
+
st.image(number_plate_img, caption="Detected Number Plate", use_column_width=True)
|
186 |
+
|
187 |
+
for box in coords:
|
188 |
+
x, y, width, height = box
|
189 |
+
|
190 |
+
# Crop the image
|
191 |
+
cropped_image = crop_image(np.array(number_plate_img), x, y, width, height)
|
192 |
+
|
193 |
+
# Display the coordinates
|
194 |
+
st.write("Detected Number Plate Coordinates:", coords)
|
195 |
+
|
196 |
+
# Display the cropped image
|
197 |
+
st.image(cropped_image, caption="Detected Number Plate", use_column_width=True)
|
198 |
+
|
199 |
+
#display the extracted number from number plate
|
200 |
+
cropped_image = np.array(cropped_image) # Replace ... with your actual image array
|
201 |
+
extracted_text = tr_ocr_image_from_array(cropped_image)
|
202 |
+
|
203 |
+
st.write("Extracted text: ")
|
204 |
+
st.text(extracted_text)
|
205 |
+
|
206 |
+
|
207 |
+
if __name__ == "__main__":
|
208 |
+
main()
|