标签:
阈值法:
对每一帧进行阈值处理,取较低的一个阈值进行二值化处理。假设以下为视频流中的任意一帧
代表任意一点处的亮度值(灰度空间),代表一个固定的阈值,对当前帧做以下二值化处理:
该算法比较适合运动物体的亮度大于周围环境的情况,如夜晚的汽车前灯、尾灯等。
下面基于阈值法的前景检测,完成夜晚视频中车辆的检测、跟踪和计数:
【算法的步骤】
1、首先画出感兴趣区域,步骤再此博文已详细描述:视频中画出感兴趣区域
2、对进入感兴趣区域的车辆进行前灯的检测,跟踪和计数
代码如下:
#include<iostream> using namespace std; #include<string> #include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<opencv2\imgproc\imgproc.hpp> using namespace cv; //Trackbar控制的变量及该值的最大值 int thresh = 200; const int MAX_THRESH = 255; //Trackbar控制的函数 void thresh_callback(int,void*); int w_h = 25; const int MAX_W_H = 100; int vehicleFrequency = 2;//这个值不能过于大,否则拍到的车辆里,没有车牌 const int MAX_VEHICLEFREQUENCY = 15; //全局标量 Mat frame;//原视频流的帧 Mat grayFrame;//颜色空间转化 Mat binaryFrame;//二值化 Mat kernel;//进行形态学处理的核函数 Rect box_vehicle; bool vehicleBool;//判断是否有车辆 //存储边缘 vector<vector<Point>> contours; vector<Vec4i> hierarchy; Rect vehicleRect;//通过前灯检测,定位出来的车辆 /*----定义鼠标事件--画矩形区域:作用当两个车灯----*/ //第一步:全局变量 bool drawing_box = false; bool gotBox = false; Rect box; Point downPoint; void mouseRectHandler(int event, int x, int y, int flags, void *param) { switch (event) { case CV_EVENT_MOUSEMOVE: if (drawing_box) { //鼠标的移动到downPoint的右下角 if( x >=downPoint.x && y >= downPoint.y) { box.x = downPoint.x; box.y = downPoint.y; box.width = x - downPoint.x; box.height = y - downPoint.y; } //鼠标的移动到downPoint的右上角 if( x >= downPoint.x && y <= downPoint.y) { box.x = downPoint.x; box.y = y; box.width = x - downPoint.x; box.height = downPoint.y - y; } //鼠标的移动到downPoint的左上角 if( x <= downPoint.x && y <= downPoint.y) { box.x = x; box.y = y; box.width = downPoint.x - x; box.height = downPoint.y - y; } //鼠标的移动到downPoint的左下角 if( x <= downPoint.x && y >= downPoint.y) { box.x = x; box.y = downPoint.y; box.width = downPoint.x -x; box.height = y - downPoint.y; } } break; case CV_EVENT_LBUTTONDOWN: //按下鼠标,代表可以可以开始画矩形 drawing_box = true; //记录起点 downPoint = Point(x,y); break; case CV_EVENT_LBUTTONUP: //松开鼠标,代表结束画矩形 drawing_box = false; gotBox = true; break; default: break; } } int main(int argc,char*argv[]) { //视频流的输入 VideoCapture video(argv[1]); if( !video.isOpened()) return -1; //得到形态学处理的kernel kernel = getStructuringElement(MORPH_CROSS,Size(3,3),Point(-1,-1)); //register,注册鼠标事件 namedWindow("video",CV_WINDOW_AUTOSIZE); setMouseCallback("video",mouseRectHandler,NULL); //输出视频流,并同时画出感兴趣区域 for(;;) { video>>frame; if(!frame.data) break; //控制阈值 createTrackbar("前灯检测:","video",&thresh,MAX_THRESH,thresh_callback); thresh_callback( 0, 0 );//这一行很重要 //去除面积过小的 createTrackbar("面积:","video",&w_h,MAX_W_H,thresh_callback); thresh_callback( 0, 0 ); //代表一辆车,如果出现的频数大于 createTrackbar("频数:","video",&vehicleFrequency,MAX_VEHICLEFREQUENCY,NULL); //当得到box时,去除setMouseCallback if(gotBox) { setMouseCallback("video",NULL,NULL); break; } rectangle(frame,box,Scalar(255,0,0),2);//画出感兴趣区域 imshow("video",frame); if(waitKey(33) == ‘q‘) break; } //用于跟踪算法和计数 bool currentVehicleBool = false; bool previousVehicleBool = false; int numberVehicle = 0; //用于记录每一辆车的的帧数,避免是一闪而过的,可能不是车辆 int numberVehicleFrame = 1; int currentVehicleNumber = 0; int previousVehicleNumber = 0; //以上画出感兴趣区域,以下可以判断是否为车辆 for(;;) { vehicleRect.width = 0; vehicleRect.height = 0; video >> frame; if(!frame.data) break; //颜色空间转换 CV_Assert(frame.channels() == 3); cvtColor(frame,grayFrame,COLOR_BGR2GRAY); //控制阈值 createTrackbar("前灯检测:","video",&thresh,MAX_THRESH,thresh_callback); thresh_callback( 0, 0 );//这一行很重要 //去除面积过小的 createTrackbar("面积:","video",&w_h,MAX_W_H,thresh_callback); thresh_callback( 0, 0 ); createTrackbar("频数:","video",&vehicleFrequency,MAX_VEHICLEFREQUENCY,NULL); //画出感兴趣区域 rectangle(frame,box,Scalar(255,255,0),2); //画出检测到的前灯 rectangle(frame,vehicleRect,Scalar(0,255,0),2); //判断vehicleRect和box是否相交,来判断是否有车辆 box_vehicle = box & vehicleRect; //显示二值化 imshow("二值化",binaryFrame); if(box_vehicle.width >0 && vehicleRect.height > 0) { currentVehicleBool = true; if( previousVehicleBool ==false ) { //代表这是第几辆车 numberVehicle++; //记录当前帧的车辆的标号 currentVehicleNumber = numberVehicle; } //用于计数,该辆车出现的帧数 if( currentVehicleNumber == previousVehicleNumber ) { numberVehicleFrame ++; if( numberVehicleFrame == vehicleFrequency+1) { cout << "抓拍" << endl; //imshow("抓拍",frame);//保存图片 } } } else { currentVehicleBool = false; //将归为1,重新计数 numberVehicleFrame = 1; } //记录上一帧的是否有车辆 previousVehicleBool = currentVehicleBool; //记录上一帧,车辆的标号(number) previousVehicleNumber = currentVehicleNumber; //显示图片 imshow("video",frame); if(waitKey(33) == ‘q‘) break; } return 0; } void thresh_callback(int,void*)//得到新的vehicleRect { if(!grayFrame.data) return; //阈值处理 threshold(grayFrame,binaryFrame,double(thresh),255.0,THRESH_BINARY); //均值滤波 medianBlur(binaryFrame,binaryFrame,5); //形态学处理 morphologyEx(binaryFrame,binaryFrame,MORPH_OPEN,kernel,Point(-1,-1),7,BORDER_REPLICATE); //存储边缘 vector<vector<Point>> contours; vector<Vec4i> hierarchy; //找到边缘,注意为什么先把binaryFrame克隆一遍 Mat tempBinaryFrame = binaryFrame.clone(); findContours( tempBinaryFrame,contours,hierarchy, CV_RETR_TREE , CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );//注意这个findContours中binaryImage既是输入又是输出 vector<vector<Point>> contours_poly( contours.size() ); //存储 vector<Rect> boundRect; boundRect.clear(); for(int index = 0;index < contours.size(); index++) { approxPolyDP( Mat(contours[index]), contours_poly[index], 3, true ); Rect rect = boundingRect( Mat(contours_poly[index]) ); //做筛选(去除较小的w_h) if( rect.width < w_h || rect.height < w_h) continue; boundRect.push_back(rect); } //得到整个汽车前灯轮廓 for(int index = 0;index < boundRect.size() ;index++) { if(index ==0) vehicleRect = boundRect[0]; else vehicleRect = vehicleRect | boundRect[index];//得到最大的矩形 } }
【运行结果】:
标签:
原文地址:http://blog.csdn.net/zhangping1987/article/details/51865750