opencv-视频处理-实时前景检测-阈值法

阈值法:

对每一帧进行阈值处理,取较低的一个阈值进行二值化处理。假设以下为视频流中的任意一帧

opencv-视频处理-实时前景检测-阈值法_第1张图片


代表任意一点处的亮度值(灰度空间),代表一个固定的阈值,对当前帧做以下二值化处理:


该算法比较适合运动物体的亮度大于周围环境的情况,如夜晚的汽车前灯、尾灯等。

下面基于阈值法的前景检测,完成夜晚视频中车辆的检测、跟踪和计数:

【算法的步骤】

1、首先画出感兴趣区域,步骤再此博文已详细描述:视频中画出感兴趣区域

opencv-视频处理-实时前景检测-阈值法_第2张图片

2、对进入感兴趣区域的车辆进行前灯的检测,跟踪和计数

代码如下:

#include
using namespace std;
#include
#include
#include
#include
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> contours;
vector 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> contours;
	vector hierarchy;
	
	//找到边缘,注意为什么先把binaryFrame克隆一遍
	Mat tempBinaryFrame = binaryFrame.clone();
	findContours( tempBinaryFrame,contours,hierarchy, CV_RETR_TREE , CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );//注意这个findContours中binaryImage既是输入又是输出
	vector> contours_poly( contours.size() );

	//存储
	vector 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];//得到最大的矩形
	}
}




【运行结果】:

opencv-视频处理-实时前景检测-阈值法_第3张图片


opencv-视频处理-实时前景检测-阈值法_第4张图片


你可能感兴趣的:(视频处理,opencv之视频处理)