【OpenCV学习】(九)目标识别之车辆检测及计数
背景
本篇将具体介绍一个实际应用项目——车辆检测及计数,在交通安全中是很重要的一项计数;当然,本次完全采用OpenCV进行实现,和目前落地的采用深度学习的算法并不相同,但原理是一致的;本篇将从基础开始介绍,一步步完成车辆检测计数的项目;
一、图像轮廓
本质:具有 相同颜色或 强度的 连续点的曲线;
作用:
1、可用于图形分析;
2、应用于物体的识别与检测;
注意点:
1、为了检测的准确性,需要先对图像进行二值化或Canny操作;
2、画轮廓的时候回修改输入的图像,需要先深拷贝原图;
轮廓查找的函数原型:
findContours(img,mode,ApproximationMode…)
- mode RETR_EXTERNAL=0,表示只检测外轮廓; RETR_LIST=1,检测的轮廓不建立等级关系;(常用) RETR_CCOMP=2,每层最多两级; RETR_TREE=3,按树形结构存储轮廓,从右到左,从大到小;(常用)
- ApproximationMode CHAIN_APPROX_BOBE:保存轮廓上所有的点; CHAIN_APPROX_SIMPLE:只保存轮廓的角点;
代码实战:
img = cv2.imread('./contours1.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours输出结果:
(array([[[ 0, 0]],
[[ 0, 435]],
[[345, 435]],
[[345, 0]]], dtype=int32),)
可以看出,我们找最外层轮廓,找出了一个矩形轮廓的四个点;
当然,我们不需要通过绘制形状来绘制轮廓,我们可以通过内置函数来绘制轮廓。
[En]
Of course, we don’t need to draw the outline by drawing the shape, we can draw the outline through a built-in function.
绘制轮廓函数原型:
drawContours(img,contours,contoursIdx,color,thickness,…)
- contours:表示保存轮廓的数组;
- contoursIdx:表示绘制第几个轮廓,-1表示所有轮廓;
代码案例:
img = cv2.imread('./contours1.jpeg')
img2 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0, 0, 255), 1)
cv2.drawContours(img2, contours, -1, (0, 0, 255), -1)
cv2.imshow('org', img)
cv2.imshow('org2', img2)
cv2.waitKey(0)

如上图所示,左图是线宽设置为1,右图为线宽设置为-1,也就是填充的效果;
当然,OpenCV还提供了计算轮廓周长和面积的方法;
轮廓面积函数原型:
contourArea(contour)
轮廓周长函数原型:
arcLength(curve,closed)
- curve:表示轮廓;
- closed:是否是闭合的轮廓;
上述两个函数比较简单,在这就不做代码演示了;
二、多边形逼近与凸包
多边形逼近函数原型:
approxPolyDP(curve,epsilon,closed)
- epslion:精度;
凸包的函数原型:
convexHull(points,clockwise,…)
- points:轮廓;
- clockwise:绘制方向,顺时针或逆时针;(不重要)
首先我们看一下基于轮廓查找输出的轮廓形状:

您可以看到轮廓点非常密集,所以让我们来看看基于多重变形近似和凸壳的效果:
[En]
You can see that the contour points are very dense, so let’s take a look at the effect based on multi-deformation approximation and convex hull:
代码案例:
img = cv2.imread('./hand.png')
img2 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
e = 20
approx = cv2.approxPolyDP(contours[0], e, True)
approx = (approx, )
cv2.drawContours(img, approx, 0, (0, 0, 255), 3)
hull = cv2.convexHull(contours[0])
hull = (hull, )
cv2.drawContours(img2, hull, 0, (0, 0, 255), 3)
cv2.imshow('org', img)
cv2.imshow('org2', img2)
cv2.waitKey(0)

这里需要注意的是,绘制轮廓的函数需要是用于轮廓输入的元组,生成的数组需要放入元组中!
[En]
It should be noted here that the function of drawing the outline needs to be a tuple for the input of the outline, and the resulting array needs to be put into a tuple!
当然,多边形逼近这里设置的精度为20,所以比较粗糙,设置小一些可以达到更好的效果;
三、外接矩形
外接矩阵分为最大外接矩阵和最小外接矩阵,如下图所示:

最小外接矩阵的另一个功能是计算旋转角度,这应该从上图中的绿色框中清楚地看到。
[En]
Another function of the minimum circumscription matrix is to calculate the rotation angle, which should be clearly seen from the green box in the picture above.
最小外接矩阵函数原型:
minAreaRect(points)
返回值:起始点(x,y)、宽高(w,h)、角度(angle)
最大外接矩形函数原型:
boundingRect(array)
返回值:起始点(x,y)、宽高(w,h)
代码案例:
img = cv2.imread('./hello.jpeg')
img2 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
r = cv2.minAreaRect(contours[1])
box = cv2.boxPoints(r)
box = np.int0(box)
cv2.drawContours(img, (box, ), 0, (0, 0, 255), 2)
x, y, w, h = cv2.boundingRect(contours[1])
cv2.rectangle(img2, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.imshow('org', img)
cv2.imshow('org2', img2)
cv2.waitKey(0)

四、车辆统计实战
涉及的知识点:
- 窗口展示
- 图像、视频的加载
- 基本图形的绘制
- 基本图像运算与处理
- 形态学
- 轮廓查找
实现流程:
加载视频 —— 通过形态学识别车辆 —— 对车辆进行统计 —— 显示统计信息
1、加载视频
这里就是一个简单加载视频的实现:
cap = cv2.VideoCapture('video.mp4')
while True:
ret, frame = cap.read()
if(ret == True):
cv2.imshow('video', frame)
key = cv2.waitKey(1)
if(key == 27):
break
cap.release()
cv2.destroyAllWindows()
2、去除背景
函数原型:
createBackgroundSubtractorMOG()
- history:缓冲,表示多少毫秒,可不指定参数,用默认的即可;
具体的实现原理比较复杂,利用了一些视频序列的相关信息,以像素值作为背景。
[En]
The specific implementation principle is more complicated, using some video sequence correlation information, and the pixel value is regarded as the background.
注意:在opencv中已经不支持该函数,而是用createBackgroundSubtractorMOG2()替代;如果需要使用可以安装opencv_contrib模块,在其中的bgsegm中保留了该函数;
代码实现:
cap = cv2.VideoCapture('video.mp4')
bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG()
while True:
ret, frame = cap.read()
if(ret == True):
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(frame, (3, 3), 5)
mask = bgsubmog.apply(blur)
cv2.imshow('video', mask)
key = cv2.waitKey(1)
if(key == 27):
break
cap.release()
cv2.destroyAllWindows()

这里尽量采用旧版的MOG函数,新版的MOG2函数比较精细,会将树叶等信息输出,去除效果没那么好;
3、形态处理
这里主要是为了处理一些小的噪声点以及目标中的黑色块;
代码实现:
cap = cv2.VideoCapture('video.mp4')
bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG()
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
while True:
ret, frame = cap.read()
if(ret == True):
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(frame, (3, 3), 5)
mask = bgsubmog.apply(blur)
erode = cv2.erode(mask, kernel)
dilate = cv2.dilate(erode, kernel, 3)
close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel)
close = cv2.morphologyEx(close, cv2.MORPH_CLOSE, kernel)
contours, h = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE,)
for (i, c) in enumerate(contours):
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0,0,255), 2)
cv2.imshow('video', frame)
key = cv2.waitKey(1)
if(key == 27):
break
cap.release()
cv2.destroyAllWindows()

从图中效果来看,还是会有很多小的检测框,接下来就是处理重合检测框以及去掉一些多余的检测框,类似于NMS去重,当然原理还不太一样;
4、车辆统计
首先,你需要过滤一些小矩形,检查框的长度和宽度,并设置一些阈值。
[En]
First of all, you need to filter some small rectangles, check the length and width of the box, and set some thresholds.
代码实现:
cap = cv2.VideoCapture('video.mp4')
bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG()
cars = []
car_n = 0
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
while True:
ret, frame = cap.read()
if(ret == True):
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(frame, (3, 3), 5)
mask = bgsubmog.apply(blur)
erode = cv2.erode(mask, kernel)
dilate = cv2.dilate(erode, kernel, 3)
close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel)
close = cv2.morphologyEx(close, cv2.MORPH_CLOSE, kernel)
contours, h = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE,)
cv2.line(frame, (10, 550), (1200, 550), (0, 255, 255), 3)
for (i, c) in enumerate(contours):
(x, y, w, h) = cv2.boundingRect(c)
isshow = (w >= 90) and (h >= 90)
if(not isshow):
continue
cv2.rectangle(frame, (x, y), (x+w, y+h), (0,0,255), 2)
centre_p = (x + int(w/2), y + int(h/2))
cars.append(centre_p)
cv2.circle(frame, (centre_p), 5, (0,0,255), -1)
for (x, y) in cars:
if(593 < y < 607):
car_n += 1
cars.remove((x, y))
cv2.putText(frame, "Cars Count:" + str(car_n), (500, 60), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 5)
cv2.imshow('video', frame)
key = cv2.waitKey(1)
if(key == 27):
break
cap.release()
cv2.destroyAllWindows()

简单的效果已经出来了,对于大多数车辆来说都可以很好地检测和统计。
[En]
The simple effect has come out, and it can be well detected and counted for most vehicles.
存在问题:
因为是以中心点到直线的距离来判断,如果速度太慢,可能会在两帧内重复计数,如果速度太快,可能不会被统计;这是传统算法中的一个问题,基于深度学习的方法可以很好地解决这些问题。你可以关注关于目标跟踪的文章。
[En]
Because it is judged by the distance between the center point and the line, if the speed is too slow, it may be counted repeatedly in two frames, and if the speed is too fast, it may not be counted; this is a problem in the traditional algorithm, and the method based on deep learning can solve these problems very well. you can pay attention to the article on target tracking.
总结
本项目就是在这里介绍的,通过这个项目主要是把学到的知识点连接起来,重点是形态的运用!当然,这种效果可能达不到实际应用的标准,这也是传统算法的一个弊端;有能力的可以使用深度学习的方法,或者可以注意到我后续的目标跟踪是实现车辆计数。效果会比这好得多。
[En]
The project is introduced here, through this project is mainly to connect the knowledge points learned, focusing on the use of morphology! Of course, this effect may not reach the standard of practical application, which is also a drawback of traditional algorithms; those who have the ability can use the method of deep learning, or you can pay attention to the fact that my follow-up target tracking is to achieve vehicle counting. the effect will be much better than this.
Original: https://blog.csdn.net/weixin_40620310/article/details/122487454
Author: 一个热爱学习的深度渣渣
Title: 【OpenCV学习】(九)目标识别之车辆检测与计数
相关阅读
Title: 综合评价法——秩和比(RSR)
一、背景介绍
秩和比法,是我国统计学家田凤调教授于1988年提出的一种综合评价方法,是利用秩和比(RSR, Rank-sum ratio)进行统计分析的一种方法。它不仅适用于四格表资料的综合评价,也适用于n行m列资料的综合评价,同时也适用于计量资料和分类资料的综合评价。
该方法已广泛应用于医疗卫生、科技、经济等领域的多指标综合评价、统计预测、统计质量控制、判别分类等领域。
[En]
This method has been widely used in multi-index comprehensive evaluation, statistical prediction, statistical quality control, discrimination and classification in the fields of medical and health, science and technology, economy and so on.
二、原理及优缺点
原理:秩和比综合评价法基本原理是在一个n行m列,通过秩的转换,获得无量纲统计量RSR;然后运用参数统计分析的概念与方法、研究RSR的分布;以RSR值对评价对象的优劣进行分档排序,从而对评价对象做出综合评价。
优点:非参数统计分析,对指标的选取没有特殊要求,适用于各种评价对象;由于计算中使用的数值是秩次的,可以消除异常值的干扰,并且集成了参数分析的方法。结果比非参数方法更准确,可以直接排序和按等级排序,具有广泛的应用前景。
[En]
Advantages: non-parametric statistical analysis, no special requirements for the selection of indicators, suitable for all kinds of evaluation objects; because the numerical value used in the calculation is rank, it can eliminate the interference of outliers, and it integrates the method of parameter analysis. the result is more accurate than the non-parametric method, which can be sorted directly and sorted by grades, and has a wide range of applications.
缺点:是排序的主要依据是利用原始数据的秩次,最终算得的RSR值反映的是综合秩次的差距,而与原始数据的顺位间的差距程度大小无关,这样在指标转化为秩次是会失去一些原始数据的信息,如原始数据的大小差别等。
当RSR值实际说不满足正态分布时,分档归类的结果与实际情况会有偏差,且只能回答分级程度是否有差别,不能进一步回答具体的差别情况。
三、算法步骤
设

是从一元总体抽取的容量为n的样本,并按从小到大的顺序排列,设其统计量为

若

,则称k是xi在样本中的秩,记作Ri,对每一个i=1,2,…,n,称Ri是第i个秩统计量。R1,R2,…,Rn总称为秩统计量。
3.1编秩
排名编制方法包括整体秩和比法和非整数秩和比法。两者在计算排名的公式上是不同的。一般采用总和比值法。
[En]
The rank compilation methods include whole rank sum ratio method and non-integral rank sum ratio method. The two are different in the formula for calculating rank. The whole sum ratio method is generally used.
3.1.1整次秩和比法
设有n个评价对象,m个评价指标的样本数据(n行m列),分别对每个指标列的数据编秩:正向指标(值越大越好)从小到大编秩,负向指标(值越小越好)从大到小编秩,当数据的值相同时编平均秩。得到秩矩阵R=(Rij)n×m。
注:编秩即对数据排序,其顺序号作为秩。
如下表:
对X3,按从小到大排序,其中值为75的有两个指标,按算术平均进行编秩:(2+3)/2=2.5
语文(X1)R1数学(X2)R2英语(X3)R3甲661804701乙854753752.5丙792621854丁843935905戊875772752.5
3.1.2非整次秩和比法
用类似于线性插值的方式对指标值进行编秩,以改进RSR法编秩方法的不足,所以编秩次与原指标值之间存在定量的线性对应关系。
对于正向指标:

对于负向指标:

3.2计算SRS、WRSR
在一个 n 行( n 个评价对象)m 列( m个评价指标)矩阵中,RSR的计算公式为:

上式中,


当个评价指标的权重不同时,计算加权秩和比为WRSR,其计算公式为:

上式中,


3.3计算概率单位
按小到大的顺序编制RSR或者WRSR频率分布表,列出各组频数fi,计算各组累计频数Fi,计算累计频率pi=Fi/n,将pi转换为概率单位probiti。
3.4计算回归方程
以累计频率所对应的概率单位值 Probit 为自变量,以RSRi或者WRSRi值为因变量,计算回归方程:

可利用最小二乘法求出相当应参数。
3.5分档排序
按回归方程计算的RSR/WRSR估计值,对评价对象进行分档排序。分档数由研究者根据实际情况决定。一般档次数量为 3档 ,也可以是 4挡、5挡。
四、案例
以基金投资案例说明,选取5个指标对基金投资进行评价。
其中X1,X2,X3为正向指标(效益型指标),值越大越好,排序时从小到大排。
X4,X5为负向指标(成本型指标),值越低越好,排序时从大到小排。
x1x2x3x4x51341000.9990910.980.9717930.0027272226300.6739730.67397310357660.7741940.7741940.99305604345650.9103140.8968610.7960590.0062785132060.6807510.6643190.7725860.009396341000.8315670.8044760.8059490.0141347452000.8368520.8076540.8996540.00091287956010.93650.993520.2187
程序如下所示:
clc,clear
a=load('shuju.txt');%该数据横向是指标,纵向是指标每年的数据
w=[0.2 0.3 0.2 0.2 0.1];%假设求得的权重
a(:,[3,5])=-a(:,[3,5]);
%这里的3,4,5指标为负向指标,也就是指标值越小越好,为与正向指标同步,因此这里乘以个负号
ra=tiedrank(a);
%[R, tie] = tie(X)计算向量X中值的秩,如果有任何X值被束缚,tie计算它们的平均秩。
%返回值是对非参数测试信号秩和ranksum所要求的关系的调整,以及对Spearman秩相关的计算。
%例子:从最小到最大,两个20的值是2和3,所以它们都是2。5(平均2和3):
%tiedrank([10 20 30 40 20])
%ans =1.0000 2.5000 4.0000 5.0000 2.5000
[n,m]=size(ra);%求矩阵维度
RSR=mean(ra,2)/n;
%mean求数组的平均数或者均值
%mean(A,2)返回值为该矩阵的各行向量的均值(每行的平均值)
W=repmat(w,[n,1]);%用于复制平铺矩阵,相当于讲w矩阵看成一个元素,形成n×1维的矩阵并用W矩阵记录
WRSR=sum(ra.*W,2)/n;
[sWRSR,ind]=sort(WRSR);%sort为排序函数
p=[1:n]/n;
p(end)=0.99999;
%p(end)=1-1/(4*n);
Probit=norminv(p,0,1);
%norminv函数,正态分布概率值, X = NORMINV(P,A,S) ,其中,P取概率。A取均值。S取方差。
%然后返回值X就是指满足均值为A,方差为S的高斯分布的累计概率密度值。即F(a)=P,返回值就是a。
%至于F的含义:对连续函数,所有小于等于a的值,其出现概率的和为F(a)=P(x<a) x="[ones(n,1),Probit'];" [ab,abint,r,rint,stats]="regress(sWRSR,X);" %[b,bint,r,rint,statsl="regess(y,x,alpha)其中因变量数据向量y和自变量数据矩阵x按以下排列方式输入" %对一元线性回归,取k="1即可。alpha为显著性水平(缺省时设定为0.05)," %输出向量b,bint为回归系数估计值和它们的置信区间,r,rint为残差及其置信区间, %stats是用于检验回归模型的统计量,有三个数值%第一个是r2,其中r是相关系数,第二个是f统计量值,第三个是与统计量f对应的概率p,当p<α wrsrfit="ab(1)+ab(2)*Probit;" y="[1:8]';" xlswrite('jieguo.xlsx',[y(ind),wrsrfit',[n:-1:1]'],1);< code></a)>
结果:
50.4669860.4939730.5141640.5322520.5503410.5705370.5976280.77441
Original: https://blog.csdn.net/qq_58577568/article/details/120688241
Author: 加碗米
Title: 综合评价法——秩和比(RSR)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/222167/
转载文章受原作者版权保护。转载请注明原作者出处!