From d5437130828136ee38984fdd78489968ff145989 Mon Sep 17 00:00:00 2001 From: Nandhini Anand Jeyahar Date: Thu, 1 May 2025 23:59:59 +0200 Subject: [PATCH] adding a new numberplate extraction stauts, and image saving for the yolo model output --- README.md | 7 ------- anpr_main.py | 18 +++++++++++++----- db/numberplates.sqlite3 | Bin 20480 -> 20480 bytes lib/anpr.py | 35 +++++++++++++++++++++++++++++++---- lib/consts.py | 1 + sql/createnumberplates.sql | 3 ++- 6 files changed, 47 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4e63e92..af39993 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,3 @@ * -- `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. -![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. - diff --git a/anpr_main.py b/anpr_main.py index f1f7e47..5ef845d 100644 --- a/anpr_main.py +++ b/anpr_main.py @@ -3,7 +3,7 @@ #* File Name : anpr_yolo_v8.py #* Purpose : #* Creation Date : 21-04-2025 -#* Last Modified : Thu 01 May 2025 08:27:26 PM CEST +#* Last Modified : Thu 01 May 2025 11:45:10 PM CEST #* Created By : Yaay Nands #_._._._._._._._._._._._._._._._._._._._._.# import glob @@ -43,10 +43,12 @@ SQL = { 'INSERT_NUMBERS': 'INSERT INTO numberplates (model, filename, ts, ) VALUES (? , ?, ?)', 'UPDATE_NUMBERS': - 'UPDATE numberplates set anal_complete=?, numberplates=? \ - WHERE model=? AND filename=? AND ts=?', + '''UPDATE numberplates set anal_complete=?, numberplates=?, np_extract_status ="success" + WHERE model=? AND filename=? AND ts=?''', '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" @@ -234,17 +236,23 @@ async def capture_number_plate(db, signal_received): cursor = await db.execute(SQL["SELECT_UNPROCESSED"]) res = await cursor.fetchall() logger.debug("found unprocessed records: %i ", len(res)) + curr_record = dict() for record in res: + curr_record = record logger.debug("processing video file %s"%record['filename']) plates = anprm.infer(record['filename']) cursor = await db.execute(SQL["UPDATE_NUMBERS"], (True, json.dumps(plates), record['model'], - record['filename'], record['ts'])) + record['filename'], record['ts']) + ) res = await cursor.fetchone() except asyncio.CancelledError: logger.debug("Closing loop capture_number_plate") #break 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"Error in capture_number_plate logic:") err_type = type(e).__name__ diff --git a/db/numberplates.sqlite3 b/db/numberplates.sqlite3 index 5fec6d00e0703c9aad4014b8a116660fc076bc3d..94fa5011632bbef5cdb4a6d47eb365f6332db8cb 100644 GIT binary patch literal 20480 zcmeI2&ubHD7{_NalixG%E4Wg+W#L6AX{2d>G+CjiP3;tfY>M_RR6)WtnQGX{Bxa_z zcynbzub#v|Kt$M!R}Z@^>}4;42qJ0@ZmW1w zkd<9Q*+qY$ZKMtx41OR01b_e#00KY&2mk>f00e-*VG;NpiNT5KY4K*s(Kk%P+Ny0B z&4#HvhTYlsojI@NsYYmiv7nL8$V`N=R;yPGlTdA)&bs%TRi>raj1HG$lO-lW&;F{B zC2cvsR-k02!XD&QYsO5>s&B_;NzAO5b(4Ey@%WyUZs}&JT(31ugDuh)S5^vIzDSBI zloZzrg?*{zd|_4F%jle*JAue7u^J^~$7$+ir(`?2vt_%1{p?u7uqsvSax8vD4o)Pe z#T((ORWWwD9$ucKhy*aGHJ3_judRsuzIZ@5xhnS)-=m9#f z?yC2dSIR9V9eo-7BRY44rVCR60zd!=00AHX1b_e#00Q5Tz#0mkuoEF+O1B!#igkxDIOQmvI2 zRrh2k$0D1`ZR%_ZTA|9ARMUyf{6a34o-WSH<9otE>t< ze6Fq{NGH;{eNk4$KMk#98XDVsbUcy;bB>XVv)wR3*=< znFCaPo>kMsRU@qCkn%~u|KU6MB0h=Up-1RCT0{}`sd`V{P*1TSG#~&3fB+Bx0zd!= z00AHX1c1Q*lE7)n7Cmg;Adh*f$2`SjUese=#A9CAV_sSJ&)qoi zkDljl4alSCxr={k^gMTY@5@P7!=u$T-xYAz|LCQF-{a@_AN(i2i*I5JU%*Q^iGRd? z^pORj0RbQY1b_e#00KY&2mk>f00e*l5IDL7evlGk?|~cC?@Z;?E@$hsi_-7RW}}0g zkzOZ1DH85+y2ma;gPg%Z&cGn2e~?oiiKmZ5;0U!VbfB+Bx0zd!=00AKIy$HxsNJQMI+$h{c bxruNS<|f2VkedKEer{xLByN0?6bgR@^E#E1 delta 179 zcmZozz}T>Wae}lU2Ll5GD-go~%S0Vxehvn`vQA#05DOnK1Ha_Pygr_d69gGI^YZa9 zay6zfvWu&#GB$HhX5+oXtqD@c$iIn!f750|hc5n!6XZC27}-P%bvYO&Pn0p5oG&Xf z$$^E9k^d9}|0$>vGZs_kLe9yKvPP2(KvHiQ_}@UKA|}7FmjDX=Vc`D*70O^?X6E1o E0JW1UEC2ui diff --git a/lib/anpr.py b/lib/anpr.py index 8c8540d..c8c8df6 100644 --- a/lib/anpr.py +++ b/lib/anpr.py @@ -6,9 +6,20 @@ import torch import ultralytics from ultralytics import YOLO from PIL import Image +import logging +import time +logger = logging.getLogger('lib') #ultralytics.checks() from xml.etree import ElementTree as ET -from .consts import BASE_PATH +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(): device = torch.device('cuda') @@ -104,18 +115,34 @@ def train_model(): def infer(filename): model2 = YOLO(f"{BASE_PATH}/models/train25/best.pt") test_result = model2.predict(source=filename) + logger.debug(len(test_result)) #onnx_model = YOLO("../models/train25/best.onnx") #model_2 = YOLO('/kaggle/input/weights/best(2).pt') #testfiles = glob.glob('../data/TEST/*') #import pdb; pdb.set_trace() #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['frames_found'] = list() for i, res in enumerate(test_result): res_img = res.plot() plate_im = Image.fromarray(res_img) - 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() + logger.debug(f"Saving result number {i} for file {vidname}") + if i % FRAME_RATE == 0: + 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 #download_kaggle_data() diff --git a/lib/consts.py b/lib/consts.py index 1cfbbf1..c9416cd 100644 --- a/lib/consts.py +++ b/lib/consts.py @@ -7,6 +7,7 @@ MODEL_NAME = "yolo_v8_custom" RTSP_URL = "rtsp://admin:C4sinfra12@192.168.1.10:554/rtsp" SCREEN_RESOLUTION = (1280, 720) # Resolution of the RTSP stream BITRATE = 1000000 # Bitrate for video recording +FRAME_RATE = 25 # Video frame rate CONTOUR_MOTION_THRESHOLD = 800 # Sensitivity for motion detection MAX_CLIP_TIME = 20 # Maximum recording time in seconds MIN_TIME = 10 # Minimum recording time in seconds diff --git a/sql/createnumberplates.sql b/sql/createnumberplates.sql index 973491b..115fb8e 100644 --- a/sql/createnumberplates.sql +++ b/sql/createnumberplates.sql @@ -4,7 +4,8 @@ CREATE TABLE numberplates( filename TEXT, ts DATETIME DEFAULT (datetime('now', 'localtime')), anal_complete BOOLEAN NOT NULL DEFAULT FALSE, - numberplates TEXT, + np_extract_status TEXT DEFAULT 'pending', + numberplates TEXT ); CREATE INDEX filename_idx ON numberplates (filename);