0% found this document useful (0 votes)
6 views7 pages

License Plate Recognition System

Uploaded by

red069117
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views7 pages

License Plate Recognition System

Uploaded by

red069117
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

import cv2

import pandas as pd
import easyocr
from ultralytics import YOLO
from datetime import datetime
import os
import re
import numpy as np

# Use the 'alpr' library


try:
from openalpr import Alpr
print("[INFO] OpenALPR library loaded successfully.")
except ImportError:
Alpr = None
print("[WARN] OpenALPR library not found. Falling back to EasyOCR only. Install
OpenALPR for better accuracy.")

# ================== CONFIG ==================


MODEL_PATH = "yolo_model/[Link]" # your YOLO path (if trained for plates,
great)
REGISTERED_FILE = "[Link]"
CONF_THRESHOLD = 0.35
GUIDE_COLOR = (0, 255, 255) # Yellow
MIN_PLATE_AREA = 2000 # contour fallback minimum area
# OpenALPR specific config
ALPR_COUNTRY = "eu" # Use 'us' for North America, 'eu' for Europe, etc.
ALPR_TOP_CANDIDATES = 3
# =============================================

# load model if exists


model = None
try:
if [Link](MODEL_PATH):
model = YOLO(MODEL_PATH)
print("[INFO] YOLO model loaded:", MODEL_PATH)
else:
print("[WARN] YOLO model not found at", MODEL_PATH, "– using contour
fallback.")
model = None
except Exception as e:
print("[WARN] Failed loading YOLO model:", e)
model = None

# Load registered details


if [Link](REGISTERED_FILE):
registered_df = pd.read_csv(REGISTERED_FILE)
else:
registered_df = [Link](columns=["number_plate", "name", "department"])
registered_df.to_csv(REGISTERED_FILE, index=False)

# Create attendance file for today


date_str = [Link]().strftime("%Y-%m-%d")
attendance_file = f"attendance_{date_str}.csv"
if not [Link](attendance_file):
[Link](columns=["number_plate", "name", "department",
"time"]).to_csv(attendance_file, index=False)
attendance_df = pd.read_csv(attendance_file)
# OCR reader (easyocr)
try:
reader = [Link](['en'], gpu=False)
except Exception as e:
raise RuntimeError("EasyOCR init failed. Install easyocr and dependencies.")
from e

# OpenALPR reader
alpr_instance = None
if Alpr:
try:
alpr_instance = Alpr(ALPR_COUNTRY, "/etc/openalpr/[Link]",
"/usr/share/openalpr/runtime_data")
if not alpr_instance.is_loaded():
raise RuntimeError("Error loading OpenALPR.")
except Exception as e:
print(f"[WARN] OpenALPR init failed: {e}. Falling back to EasyOCR.")
alpr_instance = None

# Track today's attendance in-memory


today_attendance = set(attendance_df['number_plate'].astype(str).tolist())

# ROI selection vars


roi_coords = None
drawing = False
ix, iy = -1, -1

def draw_rectangle(event, x, y, flags, param):


"""Mouse callback for drawing ROI on the preview frame."""
global ix, iy, drawing, roi_coords
frame_copy = [Link]()
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
elif event == cv2.EVENT_MOUSEMOVE and drawing:
[Link](frame_copy, (ix, iy), (x, y), GUIDE_COLOR, 2)
[Link]("Select ROI", frame_copy)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
roi_coords = (min(ix, x), min(iy, y), max(ix, x), max(iy, y))
print(f"[INFO] ROI Selected: {roi_coords}")
[Link]("Select ROI")

# ---------------- image preprocessing helpers ----------------


def enhance_variants(crop):
"""Return list of RGB images (uint8) with several preprocessing variants for
OCR."""
variants = []
# Original (RGB)
[Link]([Link](crop, cv2.COLOR_BGR2RGB))

# CLAHE on grayscale
gray = [Link](crop, cv2.COLOR_BGR2GRAY)
clahe = [Link](clipLimit=3.0, tileGridSize=(8, 8))
cl = [Link](gray)
[Link]([Link](cl, cv2.COLOR_GRAY2RGB))

# Adaptive Threshold
try:
at = [Link](gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
[Link]([Link](at, cv2.COLOR_GRAY2RGB))
except Exception:
pass

# Morphological close of adaptive


try:
kernel = [Link](cv2.MORPH_RECT, (3, 3))
morph = [Link](at, cv2.MORPH_CLOSE, kernel)
[Link]([Link](morph, cv2.COLOR_GRAY2RGB))
except Exception:
pass

# Unsharp mask (sharpen)


try:
blur = [Link](crop, (0, 0), 3)
unsharp = [Link](crop, 1.5, blur, -0.5, 0)
[Link]([Link](unsharp, cv2.COLOR_BGR2RGB))
except Exception:
pass

# resized large (improve OCR for small plates)


h, w = [Link][:2]
scale = 2 if max(h, w) < 200 else 1
if scale > 1:
big = [Link](crop, (w * scale, h * scale),
interpolation=cv2.INTER_CUBIC)
[Link]([Link](big, cv2.COLOR_BGR2RGB))

# ensure unique by size to avoid duplicates


uniq = []
seen_shapes = set()
for v in variants:
if [Link] not in seen_shapes:
seen_shapes.add([Link])
[Link](v)
return uniq

alnum_re = [Link](r'[^A-Z0-9]')

def clean_plate_text(s):
if not s: return ""
s = [Link]()
s = alnum_re.sub('', s)
# small heuristics
s = [Link]('O', '0').replace('I', '1').replace('L', '1')
return [Link]()

def ocr_best_easyocr(crop):
"""Try multiple enhancements and return best (text, conf) using EasyOCR."""
variants = enhance_variants(crop)
best_text = ""
best_conf = 0.0
for v in variants:
try:
res = [Link](v, detail=1, paragraph=False)
except Exception:
res = []
for (bbox, text, conf) in res:
if not text:
continue
cleaned = clean_plate_text(text)
if not cleaned:
continue
# prefer longer plausible plate (min length 4)
score = conf * (len(cleaned) / 10.0)
if conf > best_conf or (conf == best_conf and len(cleaned) >
len(best_text)):
best_conf = conf
best_text = cleaned
return best_text, best_conf

def ocr_best_alpr(crop):
"""Use OpenALPR to get the best result."""
if not alpr_instance:
return "", 0.0

best_text = ""
best_conf = 0.0
try:
# Convert BGR image to byte string for alpr
img_bytes = [Link]('.jpg', crop)[1].tobytes()
results = alpr_instance.recognize(img_bytes)

if not results['results']:
return "", 0.0

for plate_result in results['results']:


for candidate in plate_result['candidates']:
text = clean_plate_text(candidate['plate'])
conf = candidate['confidence'] / 100.0 # ALPR confidence is 0-100
if text and conf > best_conf:
best_conf = conf
best_text = text
except Exception as e:
print(f"[WARN] OpenALPR recognition failed: {e}")
return "", 0.0
return best_text, best_conf

# --------------- contour fallback plate detection ---------------


def plate_candidates_by_contours(roi):
"""Return list of bboxes (x1,y1,x2,y2) in ROI that look like plates."""
gray = [Link](roi, cv2.COLOR_BGR2GRAY)
gray = [Link](gray, 9, 75, 75)
edged = [Link](gray, 50, 200)
cnts, _ = [Link]([Link](), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
candidates = []
cnts = sorted(cnts, key=[Link], reverse=True)[:20]
H, W = [Link][:2]
for c in cnts:
peri = [Link](c, True)
approx = [Link](c, 0.03 * peri, True)
x, y, w, h = [Link](approx)
area = w * h
if area < MIN_PLATE_AREA:
continue
aspect = w / float(h) if h > 0 else 0
if 2.0 < aspect < 7.5 and area > MIN_PLATE_AREA:
# clamp inside ROI
x1, y1, x2, y2 = max(0, x), max(0, y), min(W - 1, x + w), min(H - 1, y
+ h)
[Link]((x1, y1, x2, y2))
return candidates

# ----------------------- process once -------------------------


def process_once(frame):
global registered_df, attendance_df, today_attendance, roi_coords

if not roi_coords:
print("Error: No ROI selected.")
return

x1, y1, x2, y2 = roi_coords


roi = frame[y1:y2, x1:x2]
if [Link] == 0:
print("ROI is empty.")
return

candidates = []
# First try YOLO if available AND model likely plate-aware
used_yolo = False
if model is not None:
try:
# get detections on ROI
results = [Link](roi, conf=CONF_THRESHOLD, imgsz=640,
verbose=False)
res = results[0]
if hasattr(res, "boxes") and len([Link]) > 0:
xy = [Link]().numpy()
for (bx1, by1, bx2, by2) in xy:
[Link]((int(bx1), int(by1), int(bx2), int(by2)))
used_yolo = True
except Exception as e:
print("[WARN] YOLO detect failed:", e)
used_yolo = False

# If YOLO produced none, use contour fallback


if not candidates:
candidates = plate_candidates_by_contours(roi)
print(f"[INFO] Contour fallback produced {len(candidates)} candidates.")

detected_any = False
for (cx1, cy1, cx2, cy2) in candidates:
plate_crop = roi[cy1:cy2, cx1:cx2]
if plate_crop.size == 0:
continue

# Use OpenALPR first if available


plate_text_alpr, conf_alpr = "", 0.0
if alpr_instance:
plate_text_alpr, conf_alpr = ocr_best_alpr(plate_crop)

# Then use EasyOCR as a fallback or for comparison


plate_text_easyocr, conf_easyocr = ocr_best_easyocr(plate_crop)

# Decide which result is better


final_plate = ""
final_conf = 0.0
if conf_alpr > conf_easyocr and plate_text_alpr:
final_plate = plate_text_alpr
final_conf = conf_alpr
print(f"[DEBUG] ALPR candidate: '{final_plate}' (conf
{final_conf:.2f})")
elif plate_text_easyocr:
final_plate = plate_text_easyocr
final_conf = conf_easyocr
print(f"[DEBUG] EasyOCR candidate: '{final_plate}' (conf
{final_conf:.2f})")

if not final_plate:
continue

detected_any = True
# check registry
match = registered_df[registered_df['number_plate'] == final_plate]
time_now = [Link]().strftime("%H:%M:%S")

if not [Link]:
if final_plate not in today_attendance:
today_attendance.add(final_plate)
new_entry = {
"number_plate": final_plate,
"name": [Link][0]['name'],
"department": [Link][0].get('department', ''),
"time": time_now
}
attendance_df = [Link]([attendance_df,
[Link]([new_entry])], ignore_index=True)
attendance_df.to_csv(attendance_file, index=False)
print(f"[ATTENDANCE] Recorded {final_plate} at {time_now}")
else:
print(f"[INFO] {final_plate} already marked today.")
else:
# new plate -> prompt register
print(f"[NEW] Plate '{final_plate}' not registered.")
name = input("Enter Name (blank to skip): ").strip()
if name:
dept = input("Department (optional): ").strip()
new_reg = {"number_plate": final_plate, "name": name, "department":
dept}
registered_df = [Link]([registered_df, [Link]([new_reg])],
ignore_index=True)
registered_df.to_csv(REGISTERED_FILE, index=False)
today_attendance.add(final_plate)
new_entry = {"number_plate": final_plate, "name": name,
"department": dept, "time": time_now}
attendance_df = [Link]([attendance_df,
[Link]([new_entry])], ignore_index=True)
attendance_df.to_csv(attendance_file, index=False)
print(f"[REGISTERED & ATTENDANCE] {final_plate} -> {name} at
{time_now}")
else:
print("[INFO] Registration skipped by user.")

# draw detection on original frame (map ROI coords -> frame coords)
fx1, fy1 = x1 + cx1, y1 + cy1
fx2, fy2 = x1 + cx2, y1 + cy2
[Link](frame, (fx1, fy1), (fx2, fy2), (0, 255, 0), 2)
[Link](frame, final_plate, (fx1, max(12, fy1 - 6)),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)

if not detected_any:
print("[RESULT] No readable plate detected. Try reselecting ROI or
improving lighting/angle.")
else:
# show final frame with overlay for 3 seconds so user can see result
[Link]("Result", frame)
[Link](3000)
[Link]("Result")

# ================== RUN ==================


cap = [Link](0)
ret, first_frame = [Link]()
if not ret:
print("Error: Cannot access webcam")
[Link]()
exit()

# Show first frame and let user draw ROI


[Link]("Select ROI", first_frame)
[Link]("Select ROI", draw_rectangle, first_frame)
print("[INSTR] Draw a rectangle with mouse to select ROI, then press any key in
that window.")
[Link](0) # wait until ROI selected

# Capture a fresh frame, then process once


ret, final_frame = [Link]()
if not ret:
print("Error: failed to capture final frame")
else:
process_once(final_frame)

[Link]()
[Link]()

if alpr_instance:
alpr_instance.unload()

You might also like