This work is an extension of the project
https://github.com/ablanco1950/LicensePlate_Yolov8_Filters_PaddleOCR
adding the possibility to detect the speed, tracking and counting cars
Installation:
The requirements are exactly the same as those indicated in the aforementioned project:
https://github.com/ablanco1950/LicensePlate_Yolov8_Filters_PaddleOCR
A model yolov8: best.pt from that project, is used as number plate detector.
For that, an environment python and ultralytics yolov8 is required.
The following parameters are used depending on the video in question:
fps=25 #frames per second of video, see its properties
fpsReal= fps/SpeedUpFrames # To speed up the process only one of SpeedUpFrames
# is considered, SpeedUpFrames=5
lengthRegion=4.5 #the depth of the considered region corresponds
# to the length of a parking space which is usually 4.5m
From this parameters is got:
From snapshots detected in the video region
Speed (Km/hour)=lenthRegion * fpsReal * 3.6 / Snapshots
Where 3.6 = (3600 sec./ 1 hour) * (1Km/ 1000m)
This formula depends on the number of snapshots detected, which depends on the quality and speed of the plate detector, so in any case it has to be adjusted with practical tests in the field.
The tracking and counting method is asigning as identifier of the car the license plate.
To test de project execute the pythom program
VIDEODetectSpeed_and_ Counter_LicensePlate_Yolov8_Filters_PaddleOCR.py
The test program VIDEODetectSpeed_and_ Counter_LicensePlate_Yolov8_Filters_PaddleOCR.py is only prepared to work with Traffic IP Camera video.mp4 test video,
dowloaded from
https://github.com/anmspro/Traffic-Signal-Violation-Detection-System/tree/master/Resources,
since speed detection is performed over a region of the video, marked with a green rectangle, whose depth coincides with the length of a parking space that appears in the video .
The code of that program is:
"""
Created on Mon Apr 25 20:1 7:29 2022
@author: Alfonso Blanco
"""
######################################################################
#####################################################################
######################################################
dirVideo="Traffic IP Camera video.mp4"
TimeLimit=800
LimitSnapshot=1
SpeedUpFrames=5
ContFramesJumped=0
fps=25 #frames per second of video dirvideo, see its properties
fpsReal= fps/SpeedUpFrames # To speed up the process only one of SpeedUpFrames
# is considered
lenthRegion=4.5 #the depth of the considered region corresponds
# to the length of a parking space which is usually 4.5m
##############################################################
###############################################################
#dirVideo="vid.mp4"
#dirVideo="video12.mp4"
#dirVideo="C:\Car_Speed_Detection\Comma.ai.Data.and.Model\Comma.ai Model\train.mp4"
#dirVideo="Pexels Videos 1721294.mp4"
dirnameYolo="best.pt"
from ultralytics import YOLO
model = YOLO(dirnameYolo)
class_list = model.model.names
#print(class_list)
######################################################################
from paddleocr import PaddleOCR
lang
as ch
, en
, french
, german
, korean
, japan
ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log = False) # need to run only once to download and load model into memory
import numpy as np
import cv2
import time
TimeIni=time.time()
X_resize=220
Y_resize=70
import imutils
#Poligono=[[200,500],[200,700],[1250,700],[1250,500]]
Poligono=[[200,485],[200,655],[1250,655],[1250,485]]
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon
polygon1 = Polygon(Poligono)
#TabTotHitsFilter=[]
#TabTotFailuresFilter=[]
#for j in range(7):
#####################################################################
"""
Copied from https://gist.github.com/endolith/334196bac1cac45a4893#
other source:
https://stackoverflow.com/questions/46084476/radon-transformation-in-python
"""
from skimage.transform import radon
import numpy
from numpy import mean, array, blackman, sqrt, square
from numpy.fft import rfft
try:
# More accurate peak finding from
# https://gist.github.com/endolith/255291#file-parabolic-py
from parabolic import parabolic
def argmax(x):
return parabolic(x, numpy.argmax(x))[0]
except ImportError:
from numpy import argmax
def GetRotationImage(image):
I=image
I = I - mean(I) # Demean; make the brightness extend above and below zero
# Do the radon transform and display the result
sinogram = radon(I)
# Find the RMS value of each row and find "busiest" rotation,
# where the transform is lined up perfectly with the alternating dark
# text and white lines
# rms_flat does no exist in recent versions
#r = array([mlab.rms_flat(line) for line in sinogram.transpose()])
r = array([sqrt(mean(square(line))) for line in sinogram.transpose()])
rotation = argmax(r)
#print('Rotation: {:.2f} degrees'.format(90 - rotation))
#plt.axhline(rotation, color='r')
# Plot the busy row
row = sinogram[:, rotation]
N = len(row)
# Take spectrum of busy row and find line spacing
window = blackman(N)
spectrum = rfft(row * window)
frequency = argmax(abs(spectrum))
return rotation, spectrum, frequency
#####################################################################
def ThresholdStable(image):
# -- coding: utf-8 --
"""
Created on Fri Aug 12 21:04:48 2022
Author: Alfonso Blanco García
Looks for the threshold whose variations keep the image STABLE
(there are only small variations with the image of the previous
threshold).
Similar to the method followed in cv2.MSER
https://datasmarts.net/es/como-usar-el-detector-de-puntos-clave-mser-en-opencv/https://felipemeganha.medium.com/detecting-handwriting-regions-with-opencv-and-python-ff0b1050aa4e
"""
thresholds=[]
Repes=[]
Difes=[]
gray=image
grayAnt=gray
ContRepe=0
threshold=0
for i in range (255):
ret, gray1=cv2.threshold(gray,i,255, cv2.THRESH_BINARY)
Dife1 = grayAnt - gray1
Dife2=np.sum(Dife1)
if Dife2 < 0: Dife2=Dife2*-1
Difes.append(Dife2)
if Dife2<22000: # Case only image of license plate
#if Dife2<60000:
ContRepe=ContRepe+1
threshold=i
grayAnt=gray1
continue
if ContRepe > 0:
thresholds.append(threshold)
Repes.append(ContRepe)
ContRepe=0
grayAnt=gray1
thresholdMax=0
RepesMax=0
for i in range(len(thresholds)):
#print ("Threshold = " + str(thresholds[i])+ " Repeticiones = " +str(Repes[i]))
if Repes[i] > RepesMax:
RepesMax=Repes[i]
thresholdMax=thresholds[i]
#print(min(Difes))
#print ("Threshold Resultado= " + str(thresholdMax)+ " Repeticiones = " +str(RepesMax))
return thresholdMax
def OTSU_Threshold(image):
bins_num = 256
# Get the image histogram
hist, bin_edges = np.histogram(image, bins=bins_num)
# Get normalized histogram if it is required
#if is_normalized:
hist = np.divide(hist.ravel(), hist.max())
# Calculate centers of bins
bin_mids = (bin_edges[:-1] + bin_edges[1:]) / 2.
# Iterate over all thresholds (indices) and get the probabilities w1(t), w2(t)
weight1 = np.cumsum(hist)
weight2 = np.cumsum(hist[::-1])[::-1]
# Get the class means mu0(t)
mean1 = np.cumsum(hist * bin_mids) / weight1
# Get the class means mu1(t)
mean2 = (np.cumsum((hist * bin_mids)[::-1]) / weight2[::-1])[::-1]
inter_class_variance = weight1[:-1] * weight2[1:] * (mean1[:-1] - mean2[1:]) ** 2
# Maximize the inter_class_variance function val
index_of_max_val = np.argmax(inter_class_variance)
threshold = bin_mids[:-1][index_of_max_val]
#print("Otsu's algorithm implementation thresholding result: ", threshold)
return threshold
#########################################################################
def ApplyCLAHE(gray):
#https://towardsdatascience.com/image-enhancement-techniques-using-opencv-and-python-9191d5c30d45
gray_img_eqhist=cv2.equalizeHist(gray)
hist=cv2.calcHist(gray_img_eqhist,[0],None,[256],[0,256])
clahe=cv2.createCLAHE(clipLimit=200,tileGridSize=(3,3))
gray_img_clahe=clahe.apply(gray_img_eqhist)
return gray_img_clahe
def GetPaddleOcr(img):
"""
Created on Tue Mar 7 10:31:09 2023
@author: https://pypi.org/project/paddleocr/ (adapted from)
"""
cv2.imwrite("gray.jpg",img)
img_path = 'gray.jpg'
result = ocr.ocr(img_path, cls=True)
# draw result
#from PIL import Image
result = result[0]
#image = Image.open(img_path).convert('RGB')
boxes = [line[0] for line in result]
txts = [line[1][0] for line in result]
scores = [line[1][1] for line in result]
licensePlate= ""
accuracy=0.0
#print("RESULTADO "+ str(txts))
#print("confiabilidad "+ str(scores))
if len(txts) > 0:
licensePlate= txts[0]
accuracy=float(scores[0])
#print(licensePlate)
#print(accuracy)
return licensePlate, accuracy
#########################################################################
def FindLicenseNumber (gray, x_offset, y_offset, License, x_resize, y_resize,
Resize_xfactor, Resize_yfactor, BilateralOption):
#########################################################################
gray = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
TotHits=0
X_resize=x_resize
Y_resize=y_resize
gray=cv2.resize(gray,None,fx=Resize_xfactor,fy=Resize_yfactor,interpolation=cv2.INTER_CUBIC)
gray = cv2.resize(gray, (X_resize,Y_resize), interpolation = cv2.INTER_AREA)
rotation, spectrum, frquency =GetRotationImage(gray)
rotation=90 - rotation
#print("Car" + str(NumberImageOrder) + " Brillo : " +str(SumBrightnessLic) +
# " Desviacion : " + str(DesvLic))
if (rotation > 0 and rotation < 30) or (rotation < 0 and rotation > -30):
print(License + " rotate "+ str(rotation))
gray=imutils.rotate(gray,angle=rotation)
TabLicensesFounded=[]
ContLicensesFounded=[]
X_resize=x_resize
Y_resize=y_resize
print("gray.shape " + str(gray.shape))
Resize_xfactor=1.5
Resize_yfactor=1.5
TabLicensesFounded=[]
ContLicensesFounded=[]
AccuraccyMin=0.7
TotHits=0
# https://medium.com/practical-data-science-and-engineering/image-kernels-88162cb6585d
#kernel = np.array([[0, -1, 0],
# [-1, 5, -1],
# [0, -1, 0]])
kernel = np.array([[0, -1, 0],
[-1,10, -1],
[0, -1, 0]])
dst = cv2.filter2D(gray, -1, kernel)
img_concat = cv2.hconcat([gray, dst])
text, Accuraccy = GetPaddleOcr(img_concat)
if Accuraccy < AccuraccyMin:
text=""
text = ''.join(char for char in text if char.isalnum())
text=ProcessText(text)
if ProcessText(text) != "":
TabLicensesFounded, ContLicensesFounded =ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text)
if text==License:
print(text + " Hit with image concat ")
TotHits=TotHits+1
else:
print(License + " detected with Filter image concat "+ text)
"""
kernel = np.ones((3,3),np.float32)/90
gray1 = cv2.filter2D(gray,-1,kernel)
#gray_clahe = cv2.GaussianBlur(gray, (5, 5), 0)
gray_img_clahe=ApplyCLAHE(gray1)
th=OTSU_Threshold(gray_img_clahe)
max_val=255
ret, o3 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TOZERO)
text, Accuraccy = GetPaddleOcr(o3)
if Accuraccy < AccuraccyMin:
text=""
text = ''.join(char for char in text if char.isalnum())
text=ProcessText(text)
if ProcessText(text) != "":
TabLicensesFounded, ContLicensesFounded =ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text)
if text==License:
print(text + " Hit with CLAHE and THRESH_TOZERO" )
#TotHits=TotHits+1
else:
print(License + " detected with CLAHE and THRESH_TOZERO as "+ text)
"""
for z in range(5,6):
kernel = np.array([[0,-1,0], [-1,z,-1], [0,-1,0]])
gray1 = cv2.filter2D(gray, -1, kernel)
text, Accuraccy = GetPaddleOcr(gray1)
if Accuraccy < AccuraccyMin:
text=""
text = ''.join(char for char in text if char.isalnum())
text=ProcessText(text)
if ProcessText(text) != "":
ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text)
if text==License:
print(text + " Hit with Sharpen filter z= " +str(z))
TotHits=TotHits+1
else:
print(License + " detected with Sharpen filter z= " +str(z) + " as "+ text)
"""
gray_img_clahe=ApplyCLAHE(gray)
th=OTSU_Threshold(gray_img_clahe)
max_val=255
# Otsu's thresholding
ret2,gray1 = cv2.threshold(gray,0,255,cv2.THRESH_TRUNC+cv2.THRESH_OTSU)
#gray1 = cv2.GaussianBlur(gray1, (1, 1), 0)
text, Accuraccy = GetPaddleOcr(gray1)
if Accuraccy < AccuraccyMin:
text=""
text = ''.join(char for char in text if char.isalnum())
text=ProcessText(text)
if ProcessText(text) != "":
#if Detect_International_LicensePlate(text)== 1:
TabLicensesFounded, ContLicensesFounded =ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text)
if text==License:
print(text + " Hit with Otsu's thresholding of cv2 and THRESH_TRUNC" )
TotHits=TotHits+1
else:
print(License + " detected with Otsu's thresholding of cv2 and THRESH_TRUNC as "+ text)
"""
threshold=ThresholdStable(gray)
ret, gray1=cv2.threshold(gray,threshold,255, cv2.THRESH_TRUNC)
#gray1 = cv2.GaussianBlur(gray1, (1, 1), 0)
text, Accuraccy = GetPaddleOcr(gray1)
if Accuraccy < AccuraccyMin:
text=""
text = ''.join(char for char in text if char.isalnum())
text=ProcessText(text)
if ProcessText(text) != "":
#if Detect_International_LicensePlate(text)== 1:
ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text)
if text==License:
print(text + " Hit with Stable and THRESH_TRUNC" )
TotHits=TotHits+1
else:
print(License + " detected with Stable and THRESH_TRUNC as "+ text)
####################################################
# experimental formula based on the brightness
# of the whole image
####################################################
SumBrightness=np.sum(gray)
threshold=(SumBrightness/177600.00)
#####################################################
ret, gray1=cv2.threshold(gray,threshold,255, cv2.THRESH_TOZERO)
#gray1 = cv2.GaussianBlur(gray1, (1, 1), 0)
text, Accuraccy = GetPaddleOcr(gray1)
if Accuraccy < AccuraccyMin:
text=""
text = ''.join(char for char in text if char.isalnum())
text=ProcessText(text)
if ProcessText(text) != "":
#if Detect_International_LicensePlate(text)== 1:
ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text)
if text==License:
print(text + " Hit with Brightness and THRESH_TOZERO" )
TotHits=TotHits+1
else:
print(License + " detected with Brightness and THRESH_TOZERO as "+ text)
################################################################
return TabLicensesFounded, ContLicensesFounded
def Detect_International_LicensePlate(Text):
if len(Text) < 3 : return -1
for i in range(len(Text)):
if (Text[i] >= "0" and Text[i] <= "9" ) or (Text[i] >= "A" and Text[i] <= "Z" ):
continue
else:
return -1
return 1
def ProcessText(text):
if len(text) > 10:
text=text[len(text)-10]
if len(text) > 9:
text=text[len(text)-9]
else:
if len(text) > 8:
text=text[len(text)-8]
else:
if len(text) > 7:
text=text[len(text)-7:]
if Detect_International_LicensePlate(text)== -1:
return ""
else:
return text
def ApendTabLicensesFounded (TabLicensesFounded, ContLicensesFounded, text):
SwFounded=0
for i in range( len(TabLicensesFounded)):
if text==TabLicensesFounded[i]:
ContLicensesFounded[i]=ContLicensesFounded[i]+1
SwFounded=1
break
if SwFounded==0:
TabLicensesFounded.append(text)
ContLicensesFounded.append(1)
return TabLicensesFounded, ContLicensesFounded
def DetectLicenseWithYolov8 (img):
TabcropLicense=[]
y=[]
yMax=[]
x=[]
xMax=[]
results = model.predict(img)
for i in range(len(results)):
# may be several plates in a frame
result=results[i]
xyxy= result.boxes.xyxy.numpy()
confidence= result.boxes.conf.numpy()
class_id= result.boxes.cls.numpy().astype(int)
# Get Class name
class_name = [class_list[z] for z in class_id]
# Pack together for easy use
sum_output = list(zip(class_name, confidence,xyxy))
# Copy image, in case that we need original image for something
out_image = img.copy()
for run_output in sum_output :
# Unpack
#print(class_name)
label, con, box = run_output
if label == "vehicle":continue
cropLicense=out_image[int(box[1]):int(box[3]),int(box[0]):int(box[2])]
#cv2.imshow("Crop", cropLicense)
#cv2.waitKey(0)
TabcropLicense.append(cropLicense)
y.append(int(box[1]))
yMax.append(int(box[3]))
x.append(int(box[0]))
xMax.append(int(box[2]))
return TabcropLicense, y,yMax,x,xMax
###########################################################
##########################################################
TabLicensesmax=[]
ContLicensesmax=[]
TimeIniLicensesmax=[]
TimeEndLicensesmax=[]
ContDetected=0
ContNoDetected=0
TotHits=0
TotFailures=0
with open( "VIDEOLicenseResults.txt" ,"w") as w:
cap = cv2.VideoCapture(dirVideo)
# https://levelup.gitconnected.com/opencv-python-reading-and-writing-images-and-videos-ed01669c660c
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
fps=5.0
frame_width = 680
frame_height = 480
cap.set(cv2.CAP_PROP_FRAME_WIDTH, frame_width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, frame_height)
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
video_writer = cv2.VideoWriter('demonstration.mp4',fourcc,fps, size)
ContCars=0
while (cap.isOpened()):
ret, img = cap.read()
if ret != True: break
else:
#print(ret)
# cv2.imshow('original video', img)
#
# if cv2.waitKey(25) & 0xFF == ord('q'):
# break
gray=img
# speed up a little
ContFramesJumped=ContFramesJumped+1
if ContFramesJumped < SpeedUpFrames:
continue
else:
ContFramesJumped=0
pts1=np.array(Poligono,np.int32)
pts1=pts1.reshape((-1,1,2))
cv2.polylines(gray,[pts1],1,(0,255,255))
License="License"
TabImgSelect, y, yMax, x, xMax =DetectLicenseWithYolov8(gray)
if TabImgSelect==[]:
print(License + " NON DETECTED")
ContNoDetected=ContNoDetected+1
continue
else:
ContDetected=ContDetected+1
print(License + " DETECTED ")
for i in range(len(TabImgSelect)):
gray=TabImgSelect[i]
#if len(TabImgSelect) > 1:
# gray=TabImgSelect[1]
#cv2.imshow('Frame', gray)
if(polygon1.contains(Point(int((x[i]+xMax[i])/2),int((y[i]+yMax[i])/2)))):
pp=0
else:
cv2.imshow('Frame', img)
# Press Q on keyboard to exit
if cv2.waitKey(25) & 0xFF == ord('q'): break
# saving video
video_writer.write(img)
continue
#cv2.waitKey(0)
x_off=3
y_off=2
x_resize=220
y_resize=70
Resize_xfactor=1.78
Resize_yfactor=1.78
ContLoop=0
SwFounded=0
BilateralOption=0
TabLicensesFounded=[]
ContLicensesFounded=[]
TabLicensesFounded, ContLicensesFounded= FindLicenseNumber (gray, x_off, y_off, License, x_resize, y_resize, \
Resize_xfactor, Resize_yfactor, BilateralOption)
print(TabLicensesFounded)
print(ContLicensesFounded)
ymax=-1
contmax=0
licensemax=""
for z in range(len(TabLicensesFounded)):
if ContLicensesFounded[z] > contmax:
contmax=ContLicensesFounded[z]
licensemax=TabLicensesFounded[z]
if licensemax == License:
print(License + " correctly recognized")
TotHits+=1
else:
#print(License + " Detected but not correctly recognized")
TotFailures +=1
print ("")
lineaw=[]
lineaw.append(License)
lineaw.append(licensemax)
lineaWrite =','.join(lineaw)
lineaWrite=lineaWrite + "\n"
w.write(lineaWrite)
#if len(licensemax) < 2: continue
SwFounded=0
for f in range( len(TabLicensesmax)):
if licensemax==TabLicensesmax[f]:
ContLicensesmax[f]=ContLicensesmax[f]+1
TimeEndLicensesmax[f]=time.time()
SwFounded=1
break
if SwFounded ==0:
TabLicensesmax.append(licensemax)
ContLicensesmax.append(1)
ContCars+=1
TimeIniLicensesmax.append(time.time())
TimeEndLicensesmax.append(time.time())
#y-limitY:y+limitY,x-limitX:x+limitX
#print("Y="+str(y))
start_point=(x[i],y[i])
end_point=(xMax[i], yMax[i])
color=(0,0,255)
# Using cv2.rectangle() method
# Draw a rectangle with blue line borders of thickness of 2 px
img = cv2.rectangle(img, start_point, end_point,(36,255,12), 1)
# Put text
text_location = (20, 20)
textCounter=" COUNTER : " + str(ContCars)
text_color = (255,255,255)
cv2.putText(img, textCounter ,text_location
, cv2.FONT_HERSHEY_SIMPLEX , 1
, text_color, 2 ,cv2.LINE_AA)
text_location = (x[i], y[i])
text_color = (255,255,255)
cv2.putText(img, licensemax ,text_location
, cv2.FONT_HERSHEY_SIMPLEX , 1
, text_color, 2 ,cv2.LINE_AA)
cv2.imshow('Frame', img)
# Press Q on keyboard to exit
if cv2.waitKey(25) & 0xFF == ord('q'): break
# saving video
video_writer.write(img)
# a los 10 minutos = 600 segundos acaba
if time.time() - TimeIni > TimeLimit:
break
#print(TabLicensesmax)
#print(ContLicensesmax)
#break
cap.release()
video_writer.release()
cv2.destroyAllWindows()
w.close()
with open( "VIDEOLicenseSummary.txt" ,"w") as w1:
for j in range (len(TabLicensesmax)):
if ContLicensesmax[j] < LimitSnapshot:continue
if TabLicensesmax[j] == "":continue
Duration=TimeEndLicensesmax[j]-TimeIniLicensesmax[j]
#Duration=Duration/ContLicensesmax[j]
"""
SpeedUpFrames=5
# to increase speed, jump frames
ContFramesJumped=0
fps=25 #frames per second of video dirvideo, see its properties
fpsReal= fps/SpeedUpFrames # To speed up the process only one of SpeedUpFrames
# is considered
lenthRegion=4.5 #the depth of the considered region corresponds
# to the length of a parking space which is usually 4.5m
"""
Snapshots= ContLicensesmax[j]
Speed=lenthRegion * fpsReal * 3.6 / Snapshots
#print(TabLicensesmax[j] + " snapshots: "+ str(ContLicensesmax[j]) + " Duration = "+str(Duration))
print(TabLicensesmax[j] + " Speed: "+ str(Speed) + "Km/h " + " snapshots: "+ str(Snapshots) )
lineaw1=[]
lineaw1.append(TabLicensesmax[j])
lineaw1.append(str(ContLicensesmax[j]))
lineaw1.append(str(TimeIniLicensesmax[j]))
lineaw1.append(str(TimeEndLicensesmax[j]))
lineaw1.append(str(Speed))
lineaWrite =','.join(lineaw1)
lineaWrite=lineaWrite + "\n"
w1.write(lineaWrite)
w1.close()
print(" COUNTER : " + str(ContCars))
print("")
print( " Time in seconds "+ str(time.time()- TimeIni))
print("")
As a result, the console gets the following output, appart from video display with counter and licences plates:
AR606L Speed: 27.0Km/h snapshots: 3
AE670S Speed: 40.5Km/h snapshots: 2
APHI88 Speed: 81.0Km/h snapshots: 1
A3K96 Speed: 40.5Km/h snapshots: 2
A968B6 Speed: 40.5Km/h snapshots: 2
AV6190 Speed: 27.0Km/h snapshots: 3
In which it is verified that the speed is determined by the number of snapshots in the delimited region of interest. There is one error from false detections of plate A3K961 that is detected as A3k96
Adjustments would be necessary with real and verifiable cases.
You also get a logging file VIDEOLicenseResults.txt with the detected license plates
and a summary file: VIDEOLicenseSummary.txt with the following fields:
And the car`s counter
The main advantage of this method is that it seems to be insensitive to the load that the computer that executes it is supporting, which allows the observed values to be adjusted.
With respect the tracking method, depends directly on the detection of the license plate. It is more precise but produces errors if the license plate is detected incorrectly.
REFERENCES:
the program that apeears in:
https://docs.ultralytics.com/guides/speed-estimation/
in which the speeds vary at different execution times of the program.
https://www.ultralytics.com/es/blog/ultralytics-yolov8-for-speed-estimation-in-computer-vision-projects It is indicated that the results depend on the speed of the GPU
Program in recent article:
It is expected to improve the results in subsequent versions by applying the specifications that appear in https://blog.roboflow.com/estimate-speed-computer-vision/ and others
Other References:
https://github.com/amanraja/vehicle-speed-detection-
https://www.ijcseonline.org/pub_paper/124-IJCSE-06271.pdf
https://medium.com/@raja_8462/an-efficient-approach-for-vehicle-speed-detection-1fce82aacaf2
https://pypi.org/project/paddleocr/
https://learnopencv.com/ultralytics-yolov8/#How-to-Use-YOLOv8?
https://public.roboflow.com/object-detection/license-plates-us-eu/3
https://docs.ultralytics.com/python/
https://medium.com/@alimustoofaa/how-to-load-model-yolov8-onnx-cv2-dnn-3e176cde16e6
https://machinelearningprojects.net/number-plate-detection-using-yolov7/
https://github.com/ablanco1950/LicensePlate_Yolov8_MaxFilters
https://docs.ultralytics.com/guides/speed-estimation/
https://blog.roboflow.com/estimate-speed-computer-vision/
citations and thanks:
@article{akyon2022sahi,
title={Slicing Aided Hyper Inference and Fine-tuning for Small Object Detection},
author={Akyon, Fatih Cagatay and Altinuc, Sinan Onur and Temizel, Alptekin},
journal={2022 IEEE International Conference on Image Processing (ICIP)},
doi={10.1109/ICIP46576.2022.9897990},
pages={966-970},
year={2022}
}
Tracking and Counting:
https://github.com/pderrenger/pderrenger
https://github.com/pderrenger?tab=repositories
https://github.com/orgs/ultralytics/discussions/8112 (example from pderrenger)
https://towardsdatascience.com/mastering-object-counting-in-videos-3d49a9230bd2
https://docs.ultralytics.com/es/guides/sahi-tiled-inference/
Filters:
https://gist.github.com/endolith/334196bac1cac45a4893#
https://stackoverflow.com/questions/46084476/radon-transformation-in-python
https://gist.github.com/endolith/255291#file-parabolic-py
https://learnopencv.com/otsu-thresholding-with-opencv/
https://towardsdatascience.com/image-enhancement-techniques-using-opencv-and-python-9191d5c30d45
https://blog.katastros.com/a?ID=01800-4bf623a1-3917-4d54-9b6a-775331ebaf05
https://programmerclick.com/article/89421544914/
https://anishgupta1005.medium.com/building-an-optical-character-recognizer-in-python-bbd09edfe438
https://datasmarts.net/es/como-usar-el-detector-de-puntos-clave-mser-en-opencv/
https://felipemeganha.medium.com/detecting-handwriting-regions-with-opencv-and-python-ff0b1050aa4e
https://github.com/victorgzv/Lighting-correction-with-OpenCV
https://medium.com/@yyuanli19/using-mnist-to-visualize-basic-conv-filtering-95d24679643e
There are no models linked
There are no models linked