基于OpenCV的简单⼈流量统计
学习OpenCV快⼀年了,最近做了⼀个简单的⼈流量统计的项⽬,分享给⼤家。
本次⼈流量统计⽤的是纯OpenCV的技术,没有涉及深度学习的知识,如果⼤家深度学习做得好的话,效果会更好。
⾸先介绍我的环境Windows10+OpenCV3.4.3+VS2017
OpenCV代码实现
⾸先是头⽂件,相关函数以及全局变量的设定。
//头⽂件
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//⿏标操作,画⼀条红线,撞线计数
void on_MouseHandle(int event, int x, int y, int flags, void* param);
void DrawLine(Mat &img, Rect box);
Rect g_box;
bool g_DrawingBox = false;
Point p1, p2;
int Thecount = 0;
int precolor = 0;
int all = 0;
然后我们将进⼊主程序部分:
int main(int argc, char**)
{
//全局变量的初始化
g_box = Rect(-1, -1, 0, 0);
p1 = Point(0, 0);
p2 = Point(0, 0);
//打开视频⽂件
VideoCapture capture;
capture.open("E:/The Program/OpenCV3.3.4/Video/manflow.mp4");
if (!capture.isOpened()) {
printf("could not find the \n");
return -1;
}
// create windows
Mat frame;
Mat bsmaskMOG2;
Mat threshimg;
//读取⼈形掩码
Mat man1, man2, man3;
man1 = imread("C:/Users/dlgker/Desktop/man1.png");
man2 = imread("C:/Users/dlgker/Desktop/man2.png");
man3 = imread("C:/Users/dlgker/Desktop/man3.png");
cvtColor(man1, man1, COLOR_BGR2GRAY);//转化为灰度图像,便于后⾯的图形矩匹配
cvtColor(man2, man2, COLOR_BGR2GRAY);
cvtColor(man3, man3, COLOR_BGR2GRAY);
vector<vector<Point>> contours;//存取轮廓的点
vector<Vec4i> hierarchy;//存取轮廓的层级
//Mat bsmaskKNN;
namedWindow("input video", CV_WINDOW_AUTOSIZE);
//namedWindow("MOG2", CV_WINDOW_AUTOSIZE);
//namedWindow("KNN Model", CV_WINDOW_AUTOSIZE);
//因⼿机像素太⾼,未来便于观察,把图像缩⼩为以前的0.6倍
resize(frame, frame, Size(ls*0.6), ws*0.6)));
//⿏标回调函数
setMouseCallback("input video", on_MouseHandle, (void*)&frame);
//3×3的矩形核
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
// intialization BS
//基于MIG2的背景去除法
Ptr<BackgroundSubtractor> pMOG2 = createBackgroundSubtractorMOG2();
//基于KNN的背景去除法
//Ptr<BackgroundSubtractor> pKNN = createBackgroundSubtractorKNN();
bool drawline = false;
进过⼀段时间的操作,我们的代码初始化完成了。代码中man1,man2和man3读取的是相关彩⾊的⼈形掩码。关于掩码的提取相信⼤家不陌⽣,利⽤⼀些简单的OpenCV处理技术就可以实现,⽐如说⼈站在⽩底的墙前,拍⼀张照⽚,然后使⽤⼀次OpenCV⾥的阈值函数就可以提取出⼈形掩码,当然,也可以使⽤背景去除法,直接⽤QQ截图去截取掩码图像就可以,我当时也是图⽅便,也是采⽤的这种⽅法,总之,⽅法有很多种,⼤家去尝试。例如这是其中⼀张⼈形掩码,做这个⼈形掩码的⽬的是为了我们后⾯的轮廓Hu据匹配,以识别出⼈。
此处,我们可以⽤两种背景剔除的⽅法,⼀种是基于BackgroundSubtractorMIG2的背景去除
法,BackgroundSubtractorMOG2⽤于动态⽬标检测,⽤到的是基于⾃适应混合⾼斯背景建模的背景减除法,相对于BackgroundSubtractorMOG,其具有更好的抗⼲扰能⼒,特别是光照变化。还有⼀种是基于KNN的背景去除法,这是⼀种基于深度学习的⽅法。
while (ad(frame)) {
//因⼿机像素太⾼,未来便于观察,把图像缩⼩为以前的0.6倍
resize(frame, frame, Size(ls*0.6), ws*0.6)));
// MOG BS
//⽤MOG⽅法实现背景去除
pMOG2->apply(frame, bsmaskMOG2);
//判断⽤户是否划线
if (g_DrawingBox)
{
drawline = true;
}
if (drawline)
{
DrawLine(frame, g_box);
}
//开运算,除去相关噪点
morphologyEx(bsmaskMOG2, bsmaskMOG2, MORPH_OPEN, kernel, Point(-1, -1));
//设置阈值,把图像变为2值图像0->0,1->255
threshold(bsmaskMOG2, threshimg, 200, 255, THRESH_BINARY);
//提取轮廓
findContours(threshimg, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (size_t i = 0; i < contours.size(); i++)
{
double length,alllength;
//获取轮廓的长度
length = arcLength(contours[i],1);
alllength = ls + ws;
//把轮廓长度⼩于阈值的轮廓去除掉,相当于再⼀次去除噪点
if (length > alllength) {
double match1, match2, match3;
//进⾏Hu矩匹配,识别出视频中的⼈
match1 = matchShapes(threshimg, man1, CONTOURS_MATCH_I1, 0);
match2 = matchShapes(threshimg, man2, CONTOURS_MATCH_I1, 0);
match3 = matchShapes(threshimg, man3, CONTOURS_MATCH_I1, 0);
/
/当匹配系数⾜够⾼的时候,开始画出⼈的识别框,矩形框的中⼼点,便于撞线计数
if (match1 < 0.1 || match2 < 0.1 || match3 < 0.1)
{
RotatedRect box;
//出最⼩的外接矩形
box = minAreaRect(contours[i]);
Point2f vertex[4];
Point center;
box.points(vertex);
center.x = round((vertex[0].x + vertex[2].x + vertex[3].x + vertex[1].x) / 4);
center.y = round((vertex[1].y + vertex[2].y + vertex[3].y + vertex[0].y) / 4);
/
/通过画四条线,画出矩形框
for (int p = 0; p < 4; p++)
{
line(frame, vertex[p], vertex[(p + 1) % 4], Scalar(0, 0, 255), 2, 8, 0);
}
//画出矩形的中⼼点
circle(frame, center, 5, Scalar(0, 255, 0), 5, 8, 0);
}
}
}
//imshow("MOG2", threshimg);
/
/创建⼀个直线迭代器对象,
//⽤于检测⽤户所画的红线上绿⾊通道上是否有上跳沿的信号
//⽤于检测⽤户所画的红线上绿⾊通道上是否有上跳沿的信号
//如果有上跳沿信号,全局变量Thecount开始计数
LineIterator it(frame, p1, p2, 8);
all = 0;
for (int j = 0; j < it.count; ++j, ++it)
{
if (precolor == 0 && (int)(*it)[1] == 255)
{
Thecount++;
/
/precolor = 0;
}
all = all + (int)(*it)[1];
if ((int)(*it)[1] == 255)
{
precolor = 255;
break;
}
}
if (all == 0)
{
precolor = 0;
}
stringstream ss;
string str;
string str1="The Count: ";
ss << Thecount;
ss >> str;
str = str1 + str;
//把记好的数打印到图像的(20,20)坐标点上
putText(frame, str, Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));
imshow("input video", frame);
/
/cout << "Count:" << Thecount << endl;
// KNN BS mask
//pKNN->apply(frame, bsmaskKNN);
//imshow("KNN Model", bsmaskKNN);
char c = waitKey(3);
if (c == 27) {
break;
}
}
return 0;
}
然后我们进⼊读取视频的环节,⾸先我们需要利⽤背景去除法来做背景去除。完成后的图像如下所⽰:
可见,仅仅通过背景去除的图形还是不能识别的,当⼈经过时,⼈的周围会产⽣很⼤的噪声造成算法的误判。但是相对⼈来说,周围像素值明显偏低。因为背景去除后的图像噪声有很多,我们需要进⾏⼀次开运算才能去除部分细⼩的噪声。开运算后的结果:
图⽚中的细⼩噪点得到充分的减少,经过⼀次阈值后,结果可见如下:
rectangle函数opencv
可见,经过⼀番的操作,⼈形提取已基本完成。接下来就是我们的识别环节了,不慌,这个时候,我
们还要最后对我们处理的图像做除去噪点的操作。这个时候,我们需要⽤arcLength()函数获取每个轮廓的长度,对于长度⽐较⼩的轮廓,我们将其剔除,不让其参与识别。把符合要求的轮廓与预先读取的掩码进⾏轮廓Hu矩匹配,因为图像的hu矩是⼀种具有平移、旋转和尺度不变性的图像特征。所以在图形识别⽅⾯具有⾮常⼴泛的应⽤。当然,现在也有⾮常多的识别⽅法,最多的就是基于深度学习的SSD和Yolov2和Yolov3模型。这些深度学习最⼤的优点就是识别稳定,识别效果好,准确性⾼。但是这些深度学习⽅法也有⼀些缺点,对硬件的要求⾼,特别是显卡,不然视频处理速度就很慢,对于我这种电脑没有N卡的⼈,也算是⼀种伤害。⼆是深度学习需要⼤量的训练集,⽽对于⼀些特殊的应⽤,劣势就明显了。好了,我们再往下说,识别出来后,我们可以把⼈给框起来,然后⽤绿⾊的点把⼈中⼼画出来。⽽对于⼈流量的计数,就是要利⽤这个绿⾊的点。当⽤户划⼀条线后,我们把线上的值设置为BGR为(0,0,255),我们再在这条直线上设置⼀个直线的迭代器,就可以把直线上的点的信息统计下来,当⼈穿过直线时,绿⾊的点会穿过直线,这时,直线上部分点的绿⾊通道将由0跳变到255,通过收集这种上跳沿的信号。我们就可以实现⼈流量的计数了。
最后,还有两个函数的代码,也分享给⼤家。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论