Opencv对视频进行运动物体的提取

要求:利用帧差法或背景差分法对视频进行运动物体的提取。图像处理步骤为:读取帧、平滑、帧差或背景差、二值化、膨胀、腐蚀。每一步的处理结果都用单独窗口显示出来。

一、原理

 平滑:

       平滑也可叫滤波,或者合在一起叫平滑滤波,平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。

“平滑处理”也称“模糊处理”(blurring),是一项简单且使用频率很高的图像处理方法。平滑处理的用途很多,但最常见的是用来减少图像上的噪声或者失真。降低图像分辨率时,平滑处理是很重要的。

 二值化的基本原理

       图像的二值化处理就是讲图像上的点的灰度置为0255,也就是讲整个图像呈现出明显的黑白效果。即将256个亮度等级的灰度图像通过适当的阀值选取而获得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,二值图像占有非常重要的地位,特别是在实用的图像处理中,以二值图像处理实现而构成的系统是很多的,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像,这样子有利于再对图像做进一步处理时,图像的集合性质只与像素值为0255的点的位置有关,不再涉及像素的多级值,使处理变得简单,而且数据的处理和压缩量小。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交叠的区域。所有灰度大于或等于阀值的像素被判定为属于特定物体,其灰度值为255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。如果某特定物体在内部有均匀一致的灰度值,并且其处在一个具有其他等级灰度值的均匀背景下,使用阀值法就可以得到比较的分割效果。如果物体同背景的差别表现不在灰度值上(比如纹理不同),可以将这个差别特征转换为灰度的差别,然后利用阀值选取技术来分割该图像。动态调节阀值实现图像的二值化可动态观察其分割图像的具体结果。

膨胀

         其实,膨胀就是求局部最大值的操作。

       按数学方面来说,膨胀或者腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行卷积。

 核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点(anchorpoint)。多数情况下,核是一个小的中间带有参考点和实心正方形或者圆盘,其实,我们可以把核视为模板或者掩码。   

       而膨胀就是求局部最大值的操作,核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长。如下图所示,这就是膨胀操作的初衷

腐蚀

    再来看一下腐蚀,,大家应该知道,膨胀和腐蚀是一对好基友,是相反的一对操作,所以腐蚀就是求局部最小值的操作,我们一般都会把腐蚀和膨胀对应起来理解和学习。下文就可以看到,两者的函数原型也是基本上一样的。

   原理图:


帧差法 
       帧差法是最为常用的运动目标检测和分割方法之一,基本原理就是在图像序列相邻两帧或三帧间采用基于像素的时间差分通过闭值化来提取出图像中的运动区域。首先,将相邻帧图像对应像素值相减得到差分图像,然后对差分图像二值化,在环境亮度变化不大的情况下,如果对应像素值变化小于事先确定的阂值时,可以认为此处为背景像素:如果图像区域的像素值变化很大,可以认为这是由于图像中运动物体引起的,将这些区域标记为前景像素,利用标记的像素区域可以确定运动目标在图像中的位置。由于相邻两帧间的时间间隔非常短,用前一帧图像作为当前帧的背景模型具有较好的实时性,其背景不积累,且更新速度快、算法简单、计算量小。算法的不足在于对环境噪声较为敏感,闽值的选择相当关键,选择过低不足以抑制图像中的噪声,过高则忽略了图像中有用的变化。对于比较大的、颜色一致的运动目标,有可能在目标内部产生空洞,无法完整地提取运动目标。 

二、编码过程及实现

1、读取视频

VideoCapture video("C:\\Users\\lenovo\\Desktop\\人工智能\\多媒体\\Car.avi");

2、读取帧

video >> frame;//读帧进frame  
			imshow("frame", frame);
			if (frame.empty())//对帧进行异常检测  
			{
				cout << "frame is empty!" << endl;
				break;
			}
3、处理
if (i == 0)//如果为第一帧(temp还为空)  
			{
				result = MoveDetect(frame, frame);//调用MoveDetect()进行运动物体检测,返回值存入result  
			}
			else//若不是第一帧(temp有值了)  
			{
				result = MoveDetect(temp, frame);//调用MoveDetect()进行运动物体检测,返回值存入result  

			}

			imshow("result", result);
			if (waitKey(1000.0 / FPS) == 27)//按原FPS显示  
			{
				cout << "ESC退出!" << endl;
				break;
			}
			temp = frame.clone();

3、.平滑处理

IplImage *tempimg_src, *tempimg_dst;
	tempimg_src = &IplImage(temp);
	tempimg_dst = cvCreateImage(cvGetSize(tempimg_src), 8, 3);
	cvSmooth(tempimg_src, tempimg_dst, CV_BLUR, 3, 3, 0, 0);//平滑函数

	IplImage *frameimg_src, *frameimg_dst;
	frameimg_src = &IplImage(frame);
	frameimg_dst = cvCreateImage(cvGetSize(frameimg_src), 8, 3);
	cvSmooth(frameimg_src, frameimg_dst, CV_BLUR, 3, 3, 0, 0);//平滑函数


4、帧差

Mat temp1, frame1;
	temp1 = cvarrToMat(tempimg_dst);
	frame1 = cvarrToMat(frameimg_dst);
	//将background和frame转为灰度图  
	Mat gray1, gray2;
	cvtColor(temp1, gray1, CV_BGR2GRAY);
	cvtColor(frame1, gray2, CV_BGR2GRAY);
	//将background和frame做差  
	Mat diff;
	absdiff(gray1, gray2, diff);
	imshow("帧差图", diff);

5、二值化

Mat diff_thresh;

	Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));//函数会返回指定形状和尺寸的结构元素。
																	 //调用之后,调用膨胀与腐蚀函数的时候,第三个参数值保存了getStructuringElement返回值的Mat类型变量。也就是element变量。
	Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(18, 18));

	//进行二值化处理,选择50,255为阈值
	threshold(diff, diff_thresh, 50, 255, CV_THRESH_BINARY);
	imshow("二值化处理后", diff_thresh);


6、膨胀  

dilate(diff_thresh, diff_thresh, kernel_dilate);
imshow("膨胀处理后", diff_thresh);

7、腐蚀

erode(diff_thresh, diff_thresh, kernel_erode);
imshow("腐蚀处理后", diff_thresh);


8、查找轮廓并绘制轮廓

vector<vector<Point> > contours;
	findContours(diff_thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//找轮廓函数
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2);//在result上绘制轮廓  
															 //7.查找正外接矩形  
	vector<Rect> boundRect(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		boundRect[i] = boundingRect(contours[i]);
		rectangle(result, boundRect[i], Scalar(0, 255, 0), 2);//在result上绘制正外接矩形  
	}
	return result;//返回result  


9、完整代码

///运动物体检测——帧差法  
#include"opencv2/opencv.hpp"  
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
#include <iostream>  
using namespace std;
//运动物体检测函数声明  
Mat MoveDetect(Mat temp, Mat frame);

int main()
{
	//读取帧、平滑、帧差或背景差、二值化、膨胀、腐蚀。
	VideoCapture video("C:\\Users\\lenovo\\Desktop\\人工智能\\多媒体\\Car.avi");
	if (!video.isOpened())
		return -1;
	while (1)
	{
		int frameCount = video.get(CV_CAP_PROP_FRAME_COUNT);//获取帧数  
		double FPS = video.get(CV_CAP_PROP_FPS);//获取FPS  
		Mat frame;//存储帧  
		Mat temp;//存储前一帧图像  
		Mat result;//存储结果图像  
		for (int i = 0; i < frameCount; i++)
		{
			//读取帧
			video >> frame;//读帧进frame  
			imshow("frame", frame);
			if (frame.empty())//对帧进行异常检测  
			{
				cout << "frame is empty!" << endl;
				break;
			}
			//处理帧
			if (i == 0)//如果为第一帧(temp还为空)  
			{
				result = MoveDetect(frame, frame);//调用MoveDetect()进行运动物体检测,返回值存入result  
			}
			else//若不是第一帧(temp有值了)  
			{
				result = MoveDetect(temp, frame);//调用MoveDetect()进行运动物体检测,返回值存入result  
			}

			imshow("result", result);
			if (waitKey(1000.0 / FPS) == 27)//按原FPS显示  
			{
				cout << "ESC退出!" << endl;
				break;
			}
			temp = frame.clone();
		}
	}
	return 0;

}
Mat MoveDetect(Mat temp, Mat frame)
{

	//平滑、帧差或背景差、二值化、膨胀、腐蚀。
	Mat result = frame.clone();
	//1.平滑处理
	IplImage *tempimg_src, *tempimg_dst;
	tempimg_src = &IplImage(temp);
	tempimg_dst = cvCreateImage(cvGetSize(tempimg_src), 8, 3);
	cvSmooth(tempimg_src, tempimg_dst, CV_BLUR, 3, 3, 0, 0);//平滑函数

	IplImage *frameimg_src, *frameimg_dst;
	frameimg_src = &IplImage(frame);
	frameimg_dst = cvCreateImage(cvGetSize(frameimg_src), 8, 3);
	cvSmooth(frameimg_src, frameimg_dst, CV_BLUR, 3, 3, 0, 0);//平滑函数

															  //2.帧差
	Mat temp1, frame1;
	temp1 = cvarrToMat(tempimg_dst);
	frame1 = cvarrToMat(frameimg_dst);
	//将background和frame转为灰度图  
	Mat gray1, gray2;
	cvtColor(temp1, gray1, CV_BGR2GRAY);
	cvtColor(frame1, gray2, CV_BGR2GRAY);
	//将background和frame做差  
	Mat diff;
	absdiff(gray1, gray2, diff);
	imshow("帧差图", diff);


	//对差值图diff_thresh进行阈值化处理  二值化
	Mat diff_thresh;
	Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));//函数会返回指定形状和尺寸的结构元素。																 //调用之后,调用膨胀与腐蚀函数的时候,第三个参数值保存了getStructuringElement返回值的Mat类型变量。也就是element变量。
	Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(18, 18));

	//进行二值化处理,选择50,255为阈值
	threshold(diff, diff_thresh, 50, 255, CV_THRESH_BINARY);
	imshow("二值化处理后", diff_thresh);
	//膨胀  
	dilate(diff_thresh, diff_thresh, kernel_dilate);
	imshow("膨胀处理后", diff_thresh);
	//腐蚀  
	erode(diff_thresh, diff_thresh, kernel_erode);
	imshow("腐蚀处理后", diff_thresh);

	//查找轮廓并绘制轮廓  
	vector<vector<Point> > contours;
	findContours(diff_thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//找轮廓函数
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2);//在result上绘制轮廓  
	 //查找正外接矩形  
	vector<Rect> boundRect(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		boundRect[i] = boundingRect(contours[i]);
		rectangle(result, boundRect[i], Scalar(0, 255, 0), 2);//在result上绘制正外接矩形  
	}
	return result;//返回result  
}

猜你喜欢

转载自blog.csdn.net/myclass1312/article/details/80762855