Compare commits
2 commits
fe515d61ba
...
d543713082
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d543713082 | ||
|
|
d7474a11ab |
7 changed files with 53 additions and 23 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
videos/*
|
videos/*
|
||||||
data/*
|
data/*
|
||||||
|
logs/*
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
|
|
|
||||||
25
anpr_main.py
25
anpr_main.py
|
|
@ -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.
36
lib/anpr.py
36
lib/anpr.py
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue