Compare commits

..

2 commits

Author SHA1 Message Date
Nandhini Anand Jeyahar
d543713082 adding a new numberplate extraction stauts, and image saving for the yolo model output 2025-05-01 23:59:59 +02:00
Nandhini Anand Jeyahar
d7474a11ab better logging and moved BASE_PATH to consts.py 2025-05-01 20:42:34 +02:00
7 changed files with 53 additions and 23 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
videos/* videos/*
data/* data/*
logs/*

View file

@ -4,10 +4,3 @@
* -- `mkdir db` * -- `mkdir db`
Number Plate recognition, also called License Plate realization or recognition using image processing methods is a potential research area in smart cities and the Internet of Things. An exponential increase in the number of vehicles necessitates the use of automated systems to maintain vehicle information for various purposes. Number Plate recognition, also called License Plate realization or recognition using image processing methods is a potential research area in smart cities and the Internet of Things. An exponential increase in the number of vehicles necessitates the use of automated systems to maintain vehicle information for various purposes.
![ANPR](ANPR.jpg)
## Implementation:
In the proposed algorithm an efficient method for recognition of Indian vehicle number plates has been devised. We are able to deal with noisy, low illuminated, cross angled, non-standard font number plates. This work employs several image processing techniques such as, morphological transformation, Gaussian smoothing, Gaussian thresholding and Sobel edge detection method in the pre-processing stage, afterwhich number plate segmentation, contours are applied by border following and contours are filtered based on character dimensions and spatial localization. Finally we apply Optical Character Recognition (OCR) to recognize the extracted characters. The detected texts are stored in the database, further which they are sorted and made available for searching.
This project will work efficiently in recognizing owner's vehicle in small Institutions/Housing societies/Apartments. We can further modify the code to use it in other areas where ANPR is necessary.

View file

@ -3,7 +3,7 @@
#* File Name : anpr_yolo_v8.py #* File Name : anpr_yolo_v8.py
#* Purpose : #* Purpose :
#* Creation Date : 21-04-2025 #* Creation Date : 21-04-2025
#* Last Modified : Thu 01 May 2025 05:48:26 PM UTC #* Last Modified : Thu 01 May 2025 11:45:10 PM CEST
#* Created By : Yaay Nands #* Created By : Yaay Nands
#_._._._._._._._._._._._._._._._._._._._._.# #_._._._._._._._._._._._._._._._._._._._._.#
import glob import glob
@ -43,10 +43,12 @@ SQL = {
'INSERT_NUMBERS': 'INSERT_NUMBERS':
'INSERT INTO numberplates (model, filename, ts, ) VALUES (? , ?, ?)', 'INSERT INTO numberplates (model, filename, ts, ) VALUES (? , ?, ?)',
'UPDATE_NUMBERS': 'UPDATE_NUMBERS':
'UPDATE numberplates set anal_complete=?, numberplates=? \ '''UPDATE numberplates set anal_complete=?, numberplates=?, np_extract_status ="success"
WHERE model=? AND filename=? AND ts=?', WHERE model=? AND filename=? AND ts=?''',
'SELECT_UNPROCESSED': 'SELECT_UNPROCESSED':
'SELECT model, filename, ts FROM numberplates WHERE anal_complete=0;', 'SELECT model, filename, ts FROM numberplates WHERE anal_complete=0 and np_extract_status="pending";',
'UPDATE_FAILS':
'UPDATE numberplates set np_extract_status = ? WHERE model=? AND filename=? AND ts=?'
} }
MODEL_NAME="YOLOv8" MODEL_NAME="YOLOv8"
@ -234,18 +236,23 @@ async def capture_number_plate(db, signal_received):
cursor = await db.execute(SQL["SELECT_UNPROCESSED"]) cursor = await db.execute(SQL["SELECT_UNPROCESSED"])
res = await cursor.fetchall() res = await cursor.fetchall()
logger.debug("found unprocessed records: %i ", len(res)) logger.debug("found unprocessed records: %i ", len(res))
curr_record = dict()
for record in res: for record in res:
#logger.debug("processing record: %s"%json.dumps(record)) curr_record = record
plates = anprm.infer(filename) logger.debug("processing video file %s"%record['filename'])
plates = anprm.infer(record['filename'])
cursor = await db.execute(SQL["UPDATE_NUMBERS"], cursor = await db.execute(SQL["UPDATE_NUMBERS"],
(True, 'NOMARS', record['model'], (True, json.dumps(plates), record['model'],
record['filename'], record['filename'], record['ts'])
record['ts'])) )
res = await cursor.fetchone() res = await cursor.fetchone()
except asyncio.CancelledError: except asyncio.CancelledError:
logger.debug("Closing loop capture_number_plate") logger.debug("Closing loop capture_number_plate")
#break #break
except Exception as e: except Exception as e:
cursor = await db.execute(SQL["UPDATE_FAILS"], ('failed', curr_record['model'],
curr_record['filename'],
curr_record['ts']))
logger.critical(f"traceback:{traceback.format_tb(e.__traceback__)} ") logger.critical(f"traceback:{traceback.format_tb(e.__traceback__)} ")
logger.critical(f"Error in capture_number_plate logic:") logger.critical(f"Error in capture_number_plate logic:")
err_type = type(e).__name__ err_type = type(e).__name__

Binary file not shown.

View file

@ -6,16 +6,26 @@ import torch
import ultralytics import ultralytics
from ultralytics import YOLO from ultralytics import YOLO
from PIL import Image from PIL import Image
ultralytics.checks() import logging
import time
logger = logging.getLogger('lib')
#ultralytics.checks()
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
from .consts import BASE_PATH, FRAME_RATE
from pytesseract import TesseractError
daily_log_ts = time.strftime("%Y-%m-%d")
#logging.basicConfig(
# filename= f"{BASE_PATH}/logs/main-yolo-tesseract-{daily_log_ts}.log",
# level=logging.DEBUG,
# format='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
# )
if torch.cuda.is_available(): if torch.cuda.is_available():
device = torch.device('cuda') device = torch.device('cuda')
else: else:
device = torch.device('cpu') device = torch.device('cpu')
BASE_PATH = '..'
def convert_bbox_to_yolo( def convert_bbox_to_yolo(
size: tuple[float, float], box: tuple[float, float, float, float] size: tuple[float, float], box: tuple[float, float, float, float]
) -> tuple[float, float, float, float]: ) -> tuple[float, float, float, float]:
@ -105,18 +115,34 @@ def train_model():
def infer(filename): def infer(filename):
model2 = YOLO(f"{BASE_PATH}/models/train25/best.pt") model2 = YOLO(f"{BASE_PATH}/models/train25/best.pt")
test_result = model2.predict(source=filename) test_result = model2.predict(source=filename)
logger.debug(len(test_result))
#onnx_model = YOLO("../models/train25/best.onnx") #onnx_model = YOLO("../models/train25/best.onnx")
#model_2 = YOLO('/kaggle/input/weights/best(2).pt') #model_2 = YOLO('/kaggle/input/weights/best(2).pt')
#testfiles = glob.glob('../data/TEST/*') #testfiles = glob.glob('../data/TEST/*')
#import pdb; pdb.set_trace() #import pdb; pdb.set_trace()
#test_result = model2.predict(source=testfiles[4]) #test_result = model2.predict(source=testfiles[4])
vidname = os.path.basename(filename).split('.')[0]
if not os.path.exists(f"{BASE_PATH}/images/{vidname}"):
os.mkdir(f"{BASE_PATH}/images/{vidname}")
number_plates = dict() number_plates = dict()
number_plates['frames_found'] = list()
for i, res in enumerate(test_result): for i, res in enumerate(test_result):
res_img = res.plot() res_img = res.plot()
plate_im = Image.fromarray(res_img) plate_im = Image.fromarray(res_img)
np_text = pytesseract.image_to_string(plate_im, lang='eng') logger.debug(f"Saving result number {i} for file {vidname}")
plate = str("".join(re.split("[^a-zA-Z0-9]*", np_text))) if i % FRAME_RATE == 0:
number_plates[i] = plate.upper() number_plates['frames_found'].append(i)
plate_im.save(f"{BASE_PATH}/images/{vidname}/frame-{i}.jpg")
#try:
# np_text = pytesseract.image_to_string(plate_im, lang='eng')
# plate = str("".join(re.split("[^a-zA-Z0-9]*", np_text)))
# number_plates[i] = plate.upper()
#except TesseractError as TE:
# logger.error("Failed to extract numberplate from the detected image")
# continue
#except Exception as e:
# logger.error("Failed to extract numberplate from the detected image")
# continue
return number_plates return number_plates
#download_kaggle_data() #download_kaggle_data()

View file

@ -2,10 +2,12 @@ DB_RECORDINGS = "db/numberplates.sqlite3"
VIDEO_OUT_FULL = "videos/full/" VIDEO_OUT_FULL = "videos/full/"
VIDEO_OUT_ANAL = "videos/analysis" VIDEO_OUT_ANAL = "videos/analysis"
MODEL_NAME = "models/train25/best.pt" BASE_PATH = '/home/nands/workspace/c4s/anpr'
MODEL_NAME = "yolo_v8_custom"
RTSP_URL = "rtsp://admin:C4sinfra12@192.168.1.10:554/rtsp" RTSP_URL = "rtsp://admin:C4sinfra12@192.168.1.10:554/rtsp"
SCREEN_RESOLUTION = (1280, 720) # Resolution of the RTSP stream SCREEN_RESOLUTION = (1280, 720) # Resolution of the RTSP stream
BITRATE = 1000000 # Bitrate for video recording BITRATE = 1000000 # Bitrate for video recording
FRAME_RATE = 25 # Video frame rate
CONTOUR_MOTION_THRESHOLD = 800 # Sensitivity for motion detection CONTOUR_MOTION_THRESHOLD = 800 # Sensitivity for motion detection
MAX_CLIP_TIME = 20 # Maximum recording time in seconds MAX_CLIP_TIME = 20 # Maximum recording time in seconds
MIN_TIME = 10 # Minimum recording time in seconds MIN_TIME = 10 # Minimum recording time in seconds

View file

@ -4,7 +4,8 @@ CREATE TABLE numberplates(
filename TEXT, filename TEXT,
ts DATETIME DEFAULT (datetime('now', 'localtime')), ts DATETIME DEFAULT (datetime('now', 'localtime')),
anal_complete BOOLEAN NOT NULL DEFAULT FALSE, anal_complete BOOLEAN NOT NULL DEFAULT FALSE,
numberplates TEXT, np_extract_status TEXT DEFAULT 'pending',
numberplates TEXT
); );
CREATE INDEX filename_idx ON numberplates (filename); CREATE INDEX filename_idx ON numberplates (filename);