import streamlit as st from PIL import Image import onnx import os import cv2 import numpy as np import pandas as pd import tensorflow as tf import plotly.express as px import matplotlib.pyplot as plt import xml.etree.ElementTree as xet import pytesseract from transformers import TrOCRProcessor, VisionEncoderDecoderModel from PIL import Image import torch import re from glob import glob from skimage import io from shutil import copy from tensorflow.keras.models import Model from tensorflow.keras.callbacks import TensorBoard from sklearn.model_selection import train_test_split from tensorflow.keras.applications import InceptionResNetV2 from tensorflow.keras.layers import Dense, Dropout, Flatten, Input from tensorflow.keras.preprocessing.image import load_img, img_to_array #Define the Image height and width INPUT_WIDTH = 640 INPUT_HEIGHT = 640 # load YOLO model net = cv2.dnn.readNetFromONNX('./best.onnx') net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) def get_detections(img, net): # 1. CONVERT IMAGE TO YOLO FORMAT image = np.array(img) # Ensure the image has three channels (remove alpha channel if present) if image.shape[2] == 4: image = image[:, :, :3] row, col, _ = image.shape # Extract row, column, and channels of image max_rc = max(row, col) # Calculate max number of rows and cols input_image = np.zeros((max_rc, max_rc, 3), dtype=np.uint8) input_image[0:row, 0:col, :] = image # Assign values to the channels # 2. GET PREDICTION FROM YOLO MODEL blob = cv2.dnn.blobFromImage(input_image, 1/255.0, (INPUT_WIDTH, INPUT_HEIGHT), swapRB=True, crop=False) net.setInput(blob) preds = net.forward() detections = preds[0] return input_image, detections def non_maximum_supression(input_image,detections): ''' This function takes the preprocessed image (input_image) and the raw detections obtained from the YOLO model (detections) and performs non-maximum suppression (NMS) to filter out redundant detections. ''' # 3. FILTER DETECTIONS BASED ON CONFIDENCE AND PROBABILIY SCORE # center x, center y, w , h, conf, proba boxes = [] confidences = [] image_w, image_h = input_image.shape[:2] 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. y_factor = image_h/INPUT_HEIGHT #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 for i in range(len(detections)): row = detections[i] confidence = row[4] # confidence of detecting license plate if confidence > 0.4: class_score = row[5] # probability score of license plate if class_score > 0.25: cx, cy , w, h = row[0:4] left = int((cx - 0.5*w)*x_factor) top = int((cy-0.5*h)*y_factor) width = int(w*x_factor) height = int(h*y_factor) box = np.array([left,top,width,height]) confidences.append(confidence) boxes.append(box) # CLEAN boxes_np = np.array(boxes).tolist() confidences_np = np.array(confidences).tolist() # NMS index = cv2.dnn.NMSBoxes(boxes_np,confidences_np,0.25,0.45) return boxes_np, confidences_np, index def drawings(image, boxes_np, confidences_np, index): # Drawings image_draw = image.copy() image_draw = np.array(image_draw)# Create a copy to avoid modifying the original image for ind in index: x, y, w, h = boxes_np[ind] bb_conf = confidences_np[ind] conf_text = 'plate: {:.0f}%'.format(bb_conf * 100) cv2.rectangle(image_draw, (int(x), int(y)), (int(x+w), int(y+h)), (255, 0, 255), 2) cv2.rectangle(image_draw, (int(x), int(y-30)), (int(x+w), int(y)), (255, 0, 255), -1) cv2.rectangle(image_draw, (int(x), int(y+h)), (int(x+w), int(y+h+25)), (0, 0, 0), -1) cv2.putText(image_draw, conf_text, (int(x), int(y-10)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 1) return image_draw def yolo_predictions(img, net): # Step 1: Get detections input_image, detections = get_detections(img, net) # Step 2: Non-Maximum Suppression boxes_np, confidences_np, index = non_maximum_supression(input_image, detections) # Step 3: Drawings result_img = drawings(img, boxes_np, confidences_np, index) # Extract bounding box coordinates bounding_box_coords = [boxes_np[i] for i in index] return result_img, bounding_box_coords #Crop the image def crop_image(img, x, y, width, height): # Convert the image array to a Pillow Image object pil_img = Image.fromarray(img) # Crop the image using the provided coordinates cropped_img = pil_img.crop((x, y, x + width, y + height)) # Convert the cropped image back to a NumPy array cropped_img_array = np.array(cropped_img) return cropped_img_array def tr_ocr_image_from_array(image_array): processor = TrOCRProcessor.from_pretrained("microsoft/trocr-large-printed") model = VisionEncoderDecoderModel.from_pretrained("microsoft/trocr-large-printed") def tr_ocr_image(src_img): pixel_values = processor(images=src_img, return_tensors="pt").pixel_values generated_ids = model.generate(pixel_values) return processor.batch_decode(generated_ids, skip_special_tokens=True)[0] # Convert the image array to a PIL image img = Image.fromarray(image_array).convert("RGB") # Perform OCR on the image return tr_ocr_image(img) def main(): st.title("Automatic Number Plate Recognition (ANPR) App") st.write("Upload an image and let the app detect the vehicle number plate.") uploaded_image = st.file_uploader("Choose an image ", type=["jpg", "jpeg", "png"]) if uploaded_image is not None: # Display the uploaded image st.image(uploaded_image, caption="Uploaded Image", use_column_width=True) # Convert the uploaded image to a PIL Image image = Image.open(uploaded_image) # Detect the number plate number_plate_img, coords = yolo_predictions(image, net) # Display the detected number plate image # st.image(number_plate_img, caption="Detected Number Plate", use_column_width=True) for box in coords: x, y, width, height = box # Crop the image cropped_image = crop_image(np.array(number_plate_img), x, y, width, height) # Display the coordinates # st.write("Detected Number Plate Coordinates:", coords) # Display the cropped image st.image(cropped_image, caption="Detected Number Plate", use_column_width=True) #display the extracted number from number plate cropped_image = np.array(cropped_image) # Replace ... with your actual image array extracted_text = tr_ocr_image_from_array(cropped_image) #Remove speical characters from the extracted text def remove_special_characters(text): pattern = r'[^a-zA-Z0-9\s]' # This pattern matches any character that is not alphanumeric or whitespace # Use re.sub() to replace all occurrences of the pattern with an empty string cleaned_text = re.sub(pattern, '', text) return cleaned_text extracted_text = remove_special_characters(extracted_text) st.markdown("Extracted text:", unsafe_allow_html=True) st.text(extracted_text) if __name__ == "__main__": main()