基于opencv3实现运动物体识别

一:背景减法

         对于一个稳定的监控场景而言,在没有运动目标,光照没有变化的情况下,视频图像中各个像素点的灰度值是符合随机概率分布的。由于摄像机在采集图像的过程中,会不可避免地引入噪声,这些灰度值以某一个均值为基准线,在附近做一定范围内的随机振荡,这种场景就是所谓的“背景”。

        背景减法(Background subtraction)是当前运动目标检测技术中应用较为广泛的一类方法,它的基本思想和帧间差分法相类似,都是利用不同图像的差分运算提取目标区域。不过与帧间差分法不同的是,背景减法不是将当前帧图像与相邻帧图像相减,而是将当前帧图像与一个不断更新的背景模型相减,在差分图像中提取运动目标。

                                

   背景减法的运算过程如图2-6 所示。首先利用数学建模的方法建立一幅背景图像帧,记当前图像帧为fn,背景帧和当前帧对应像素点的灰度值分别记为B(x)fn(x , ,按照式2.17 将两帧图像对应像素点的灰度值进行相减,并取其绝对值,得到差分图像D n

                                      

   设定阈值 ,按照式2.18 逐个对像素点进行二值化处理,得到二值化图像 Rn' 。其中,灰度值为255 的点即为前景(运动目标)点,灰度值为的点即为背景点;对图像 Rn'进行连通性分析,最终可得到含有完整运动目标的图像Rn 

                                     

   背景减法计算较为简单,由于背景图像中没有运动目标,当前图像中有运动目标,将两幅图像相减,显然可以提取出完整的运动目标,解决了帧间差分法提取的目标内部含有“空洞”的问题。

   利用背景减法实现目标检测主要包括四个环节:背景建模,背景更新,目标检测,后期处理。其中,背景建模和背景更新是背景减法中的核心问题。背景模型建立的好坏直接影响到目标检测的效果。所谓背景建模,就是通过数学方法,构建出一种可以表征“背景”的模型。获取背景的最理想方法是在没有运动目标的情况下获取一帧“纯净”的图像作为背景,但是,在实际情况中,由于光照变化、雨雪天气、目标运动等诸多因素的影响,这种情况是很难实现。

代码实现:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——背景减法
#include "opencv2/opencv.hpp"  
using namespace cv;
#include <iostream>  
using namespace std;
// 运动物体检测函数声明  
Mat MoveDetect(Mat background, Mat frame);

int main()
{

	VideoCapture video("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");//定义VideoCapture类video  
	if (!video.isOpened())  //对video进行异常检测  
	{
		cout << "video open error!" << endl;
		return 0;
	}
	// 获取帧数
	int frameCount = video.get(CV_CAP_PROP_FRAME_COUNT);
	// 获取FPS 
	double FPS = video.get(CV_CAP_PROP_FPS);
	// 存储帧
	Mat frame;
	// 存储背景图像
	Mat background;
	// 存储结果图像
	Mat result;
	for (int i = 0; i < frameCount; i++)
	{
		// 读帧进frame
		video >> frame;
		imshow("frame", frame);
		// 对帧进行异常检测
		if (frame.empty())
		{
			cout << "frame is empty!" << endl;
			break;
		}
		// 获取帧位置(第几帧)
		int framePosition = video.get(CV_CAP_PROP_POS_FRAMES); 
		cout << "framePosition: " << framePosition << endl;
		// 将第一帧作为背景图像
		if (framePosition == 1)
			background = frame.clone();
		// 调用MoveDetect()进行运动物体检测,返回值存入result
		result = MoveDetect(background, frame);
		imshow("result", result);
		// 按原FPS显示
		if (waitKey(1000.0 / FPS) == 27)
		{
			cout << "ESC退出!" << endl;
			break;
		}
	}
	return 0;
}
Mat MoveDetect(Mat background, Mat frame)
{
	Mat result = frame.clone();
	// 1.将background和frame转为灰度图  
	Mat gray1, gray2;
	cvtColor(background, gray1, CV_BGR2GRAY);
	cvtColor(frame, gray2, CV_BGR2GRAY);
	// 2.将background和frame做差  
	Mat diff;
	absdiff(gray1, gray2, diff);
	imshow("diff", diff);
	// 3.对差值图diff_thresh进行阈值化处理  
	Mat diff_thresh;
	threshold(diff, diff_thresh, 50, 255, CV_THRESH_BINARY);
	imshow("diff_thresh", diff_thresh);
	// 4.腐蚀  
	Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
	Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(15, 15));
	erode(diff_thresh, diff_thresh, kernel_erode);
	imshow("erode", diff_thresh);
	// 5.膨胀  
	dilate(diff_thresh, diff_thresh, kernel_dilate);
	imshow("dilate", diff_thresh);
	// 6.查找轮廓并绘制轮廓  
	vector<vector<Point>> contours;
	findContours(diff_thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	// 在result上绘制轮廓
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2); 
	// 7.查找正外接矩形  
	vector<Rect> boundRect(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		boundRect[i] = boundingRect(contours[i]);
		// 在result上绘制正外接矩形
		rectangle(result, boundRect[i], Scalar(0, 255, 0), 2); 
	}
	// 返回result
	return result;
}





二:帧差法

      帧间差分方法利用图像序列中相邻两帧或者三帧图像对应像素值相减,然后取差值图像进行阈值化处理提取出图像中的运动区域:


代码:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

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

int main()
{
	// 定义VideoCapture类video
	VideoCapture video("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");
	if (!video.isOpened())  //对video进行异常检测  
	{
		cout << "video open error!" << endl;
		return 0;
	}
	// 获取帧数
	int frameCount = video.get(CV_CAP_PROP_FRAME_COUNT);
	// 获取FPS
	double FPS = video.get(CV_CAP_PROP_FPS);
	// 存储帧
	Mat frame;
	// 存储前一帧图像
	Mat temp;
	// 存储结果图像
	Mat result;
	for (int i = 0; i < frameCount; i++)
	{
		// 读帧进frame
		video >> frame;
		imshow("frame", frame);
		// 对帧进行异常检测
		if (frame.empty())
		{
			cout << "frame is empty!" << endl;
			break;
		}
		// 获取帧位置(第几帧)
		int framePosition = video.get(CV_CAP_PROP_POS_FRAMES); 
		cout << "framePosition: " << framePosition << endl;
		// 如果为第一帧(temp还为空)
		if (i == 0)
		{
			// 调用MoveDetect()进行运动物体检测,返回值存入result
			result = MoveDetect(frame, frame);
		}
		//若不是第一帧(temp有值了)
		else
		{
			// 调用MoveDetect()进行运动物体检测,返回值存入result
			result = MoveDetect(temp, frame);
		}
		imshow("result", result);
		// 按原FPS显示
		if (waitKey(1000.0 / FPS) == 27)
		{
			cout << "ESC退出!" << endl;
			break;
		}
		temp = frame.clone();
	}
	return 0;


}
Mat MoveDetect(Mat temp, Mat frame)
{
	Mat result = frame.clone();
	// 1.将background和frame转为灰度图  
	Mat gray1, gray2;
	cvtColor(temp, gray1, CV_BGR2GRAY);
	cvtColor(frame, gray2, CV_BGR2GRAY);
	// 2.将background和frame做差  
	Mat diff;
	absdiff(gray1, gray2, diff);
	imshow("diff", diff);
	// 3.对差值图diff_thresh进行阈值化处理  
	Mat diff_thresh;
	threshold(diff, diff_thresh, 50, 255, CV_THRESH_BINARY);
	imshow("diff_thresh", diff_thresh);
	// 4.腐蚀  
	Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
	Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(18, 18));
	erode(diff_thresh, diff_thresh, kernel_erode);
	imshow("erode", diff_thresh);
	// 5.膨胀  
	dilate(diff_thresh, diff_thresh, kernel_dilate);
	imshow("dilate", diff_thresh);
	// 6.查找轮廓并绘制轮廓  
	vector<vector<Point>> contours;
	findContours(diff_thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	// 在result上绘制轮廓
	drawContours(result, contours, -1, Scalar(0, 0, 255), 2);
	// 7.查找正外接矩形  
	vector<Rect> boundRect(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		boundRect[i] = boundingRect(contours[i]);
		// 在result上绘制正外接矩形
		rectangle(result, boundRect[i], Scalar(0, 255, 0), 2);
	}
	// 返回result
	return result;
}

优点:

  • 帧间差分方法简单、运算量小且易于实现。
  • 帧间差分方法进行运动目标检测可以较强地适应动态环境的变化,有效地去除系统误差和噪声的影响,对场景中光照的变化不敏感而且不易受阴影的影响。

缺点:

  • 不能完全提取所有相关的特征像素点,也不能得到运动目标的完整轮廓,只能得到运动区域的大致轮廓;
  • 检测到的区域大小受物体的运动速度制约:对快速运动的物体,需要选择较小的时间间隔,如果选择不合适,当物体在前后两帧中没有重叠时,会被检测为两个分开的物体;对于慢速运动的物体,应该选择较大的时间差,如果时间选择不适当,当物体在前后两帧中几乎完全重叠时,则检测不到物体。
  • 容易在运动实体内部差生空洞现象。

三:光流法

简介:在计算机视觉中,Lucas–Kanade光流算法是一种两帧差分的光流估计算法。它由Bruce D. Lucas  Takeo

Kanade提出。
光流的概念:(Optical flow or optic flow)
它是一种运动模式,这种运动模式指的是一个物体、表面、边缘在一个视角下由一个观察者(比如眼睛、摄像头等)

和背景之间形成的明显移动。光流技术,如运动检测和图像分割,时间碰撞,运动补偿编码,三维立体视差,都是

利用了这种边缘或表面运动的技术。
二维图像的移动相对于观察者而言是三维物体移动的在图像平面的投影。
有序的图像可以估计出二维图像的瞬时图像速率或离散图像转移。

光流算法:
它评估了两幅图像的之间的变形,它的基本假设是体素和图像像素守恒。它假设一个物体的颜色在前后两帧没有巨大

而明显的变化。基于这个思路,我们可以得到图像约束方程。不同的光流算法解决了假定了不同附加条件的光流问题

Lucas–Kanade算法:
这个算法是最常见,最流行的。它计算两帧在时间t + δt之间每个每个像素点位置的移动。 由于它是基于图像信号

泰勒级数,这种方法称为差分,这就是对于空间和时间坐标使用偏导数。

图像约束方程可以写为I (x ,y ,z ,t ) = I (x + δx ,y + δy ,z + δz ,t + δt )
I(x, y,z, t) 
为在(x,y,z)位置的体素。
我们假设移动足够的小,那么对图像约束方程使用泰勒公式,我们可以得到:

H.O.T. 指更高阶,在移动足够小的情况下可以忽略。从这个方程中我们可以得到:

或者

我们得到:


V x ,V y ,V z 
分别是I(x,y,z,t)的光流向量中xyz的组成。  则是图像在(x ,y ,z ,t )这一点向相应方向的差分 
所以

I x V x I y V y I z V z = − t

写做:

这个方程有三个未知量,尚不能被解决,这也就是所谓光流算法的光圈问题。那么要找到光流向量则需要另一套解决的方案。而Lucas-Kanade算法

是一个非迭代的算法:

假设流(Vx,Vy,Vz)在一个大小为m*m*m(m>1)的小窗中是一个常数,那么从像素1...n , n = m 3 中可以得到下列一组方程:

三个未知数但是有多于三个的方程,这个方程组自然是个超定方程,也就是说方程组内有冗余,方程组可以表示为:

记作:

为了解决这个超定问题,我们采用最小二乘法:

or

得到:

其中的求和是从1n

这也就是说寻找光流可以通过在四维上图像导数的分别累加得出。我们还需要一个权重函数W(i, j,k)  来突出窗口中心点的

标。高斯函数做这项工作是非常合适的,

这个算法的不足在于它不能产生一个密度很高的流向量,例如在运动的边缘和黑大的同质区域中的微小移动方面流信息会很快的褪去。它的优点在于

有噪声存在的鲁棒性还是可以的。

简单来说,上图表现的就是光流,光流描述的是图像上每个像素点的灰度的位置(速度)变化情况,光流的研究是利用图像序列中的像素强度数据的时域变化和相关性来确定各自像素位置的“运动”。研究光流场的目的就是为了从图片序列中近似得到不能直接得到的运动场。
光流法的前提假设:
(1)相邻帧之间的亮度恒定;
(2)相邻视频帧的取帧时间连续,或者,相邻帧之间物体的运动比较“微小”;
(3)保持空间一致性;即,同一子图像的像素点具有相同的运动;

代码1:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——光流法--LK金字塔
#include<iostream>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\nonfree\nonfree.hpp>
#include<opencv2\video\tracking.hpp>

using namespace std;
using namespace cv;

Mat image1, image2;
vector<Point2f> point1, point2, pointCopy;
vector<uchar> status;
vector<float> err;

int main()
{
	VideoCapture video("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");
	// 获取视频帧率
	double fps = video.get(CV_CAP_PROP_FPS);
	// 两幅画面中间间隔
	double pauseTime = 1000 / fps; 
	video >> image1;
	Mat image1Gray, image2Gray;
	cvtColor(image1, image1Gray, CV_RGB2GRAY);
	goodFeaturesToTrack(image1Gray, point1, 100, 0.01, 10, Mat());
	pointCopy = point1;
	// 绘制特征点位
	for (int i = 0; i<point1.size(); i++)
	{
		circle(image1, point1[i], 1, Scalar(0, 0, 255), 2);
	}
	namedWindow("LK--角点特征光流", 0);
	imshow("LK--角点特征光流", image1);
	while (true)
	{
		video >> image2;
		// 图像为空或Esc键按下退出播放
		if (!image2.data || waitKey(pauseTime) == 27)
		{
			break;
		}
		cvtColor(image2, image2Gray, CV_RGB2GRAY);
		// LK金字塔实现
		calcOpticalFlowPyrLK(image1Gray, image2Gray, point1, point2, status, err, Size(20, 20), 3);
		for (int i = 0; i<point2.size(); i++)
		{
			circle(image2, point2[i], 1, Scalar(0, 0, 255), 2);
			line(image2, pointCopy[i], point2[i], Scalar(255, 0, 0), 2);
		}
		imshow("LK金字塔实现--角点特征光流", image2);
		swap(point1, point2);
		image1Gray = image2Gray.clone();
	}
	return 0;
}



代码2:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——光流法--LK金字塔
#include <iostream>    
#include <opencv2/opencv.hpp>  
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>   
#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur  
#include <opencv2/ml/ml.hpp>  
#include <opencv2/contrib/contrib.hpp>  

using namespace cv;
using namespace std;

void duan_OpticalFlow(Mat &frame, Mat & result);
bool addNewPoints();
bool acceptTrackedPoint(int i);

Mat image;
vector<Point2f> point1, point2, pointCopy;
Mat curgray;    // 当前图片  
Mat pregray;    // 预测图片  
vector<Point2f> point[2]; // point0为特征点的原来位置,point1为特征点的新位置  
vector<Point2f> initPoint;    // 初始化跟踪点的位置  
vector<Point2f> features; // 检测的特征  
int maxCount = 1000;         // 检测的最大特征数  
double qLevel = 0.01;   // 特征检测的等级  
double minDist = 10.0;  // 两特征点之间的最小距离  
vector<uchar> status; // 跟踪特征的状态,特征的流发现为1,否则为0  
vector<float> err;

int main()
{
	Mat matSrc;
	Mat matRst;

	VideoCapture cap("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");
	int totalFrameNumber = cap.get(CV_CAP_PROP_FRAME_COUNT);

	cap >> image;
	Mat imageGray, image2Gray;
	cvtColor(image, imageGray, CV_RGB2GRAY);
	goodFeaturesToTrack(imageGray, point1, 100, 0.01, 10, Mat());
	pointCopy = point1;
	// 绘制特征点位
	for (int i = 0; i<point1.size(); i++)
	{
		circle(image, point1[i], 1, Scalar(0, 0, 255), 2);
	}
	namedWindow("LK--角点特征光流", 0);
	imshow("LK--角点特征光流", image);

	// perform the tracking process  
	cout << "开始检测视频,按下ESC键推出。" << endl;
	for (int nFrmNum = 0; nFrmNum < totalFrameNumber; nFrmNum++) {
		// 读取视频
		cap >> matSrc;
		if (!matSrc.empty())
		{
			duan_OpticalFlow(matSrc, matRst);
			cout << "该图片帧是 " << nFrmNum << endl;
		}
		else
		{
			cout << "获取视频帧数错误!" << endl;
		}
		if (waitKey(1) == 27) break;
	}

	waitKey(0);

	return 0;
}

void duan_OpticalFlow(Mat &frame, Mat & result)
{
	cvtColor(frame, curgray, CV_BGR2GRAY);
	frame.copyTo(result);
	// 添加特征点
	if (addNewPoints())
	{
		goodFeaturesToTrack(curgray, features, maxCount, qLevel, minDist);
		point[0].insert(point[0].end(), features.begin(), features.end());
		initPoint.insert(initPoint.end(), features.begin(), features.end());
	}

	if (pregray.empty())
	{
		curgray.copyTo(pregray);
	}

	calcOpticalFlowPyrLK(pregray, curgray, point[0], point[1], status, err);
	// 去除部分不好的光电
	int k = 0;
	for (size_t i = 0; i<point[1].size(); i++)
	{
		if (acceptTrackedPoint(i))
		{
			initPoint[k] = initPoint[i];
			point[1][k++] = point[1][i];
		}
	}

	point[1].resize(k);
	initPoint.resize(k);
	// 现实特征点和运动轨迹
	for (size_t i = 0; i<point[1].size(); i++)
	{
		line(result, initPoint[i], point[1][i], Scalar(0, 0, 255));
		circle(result, point[1][i], 3, Scalar(0, 255, 0), -1);
	}
	// 更新该次结果作为下一次的参考
	swap(point[1], point[0]);
	swap(pregray, curgray);

	imshow("LK--Demo", result);
	// waitKey(0);  
}

bool addNewPoints()
{
	return point[0].size() <= 10;
}

bool acceptTrackedPoint(int i)
{
	return status[i] && ((abs(point[0][i].x - point[1][i].x) + 
		abs(point[0][i].y - point[1][i].y)) > 2);
}


opencv源码给出的demo:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——光流法--LK金字塔
#include <stdio.h>  
#include <iostream>    
#include <opencv2/opencv.hpp>  
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>   
#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur  
#include <opencv2/ml/ml.hpp>  
#include <opencv2/contrib/contrib.hpp>  

#include <opencv2/video/tracking.hpp>  


using namespace cv;

static void convertFlowToImage(const Mat &flow_x, const Mat &flow_y, Mat &img_x, Mat &img_y, double lowerBound, double higherBound) {
#define CAST(v, L, H) ((v) > (H) ? 255 : (v) < (L) ? 0 : cvRound(255*((v) - (L))/((H)-(L))))  
	for (int i = 0; i < flow_x.rows; ++i) {
		for (int j = 0; j < flow_y.cols; ++j) {
			float x = flow_x.at<float>(i, j);
			float y = flow_y.at<float>(i, j);
			img_x.at<uchar>(i, j) = CAST(x, lowerBound, higherBound);
			img_y.at<uchar>(i, j) = CAST(y, lowerBound, higherBound);
		}
	}
#undef CAST  
}

static void drawOptFlowMap(const Mat& flow, Mat& cflowmap, int step, double, const Scalar& color)
{
	for (int y = 0; y < cflowmap.rows; y += step)
		for (int x = 0; x < cflowmap.cols; x += step)
		{
			const Point2f& fxy = flow.at<Point2f>(y, x);
			line(cflowmap, Point(x, y), Point(cvRound(x + fxy.x), cvRound(y + fxy.y)),
				color);
			circle(cflowmap, Point(x, y), 2, color, -1);
		}
}

int main(int argc, char** argv)
{
	// IO operation  

	const char* keys =
	{
		"{ f  | vidFile      | ex2.avi | filename of video }"
		"{ x  | xFlowFile    | flow_x | filename of flow x component }"
		"{ y  | yFlowFile    | flow_y | filename of flow x component }"
		"{ i  | imgFile      | flow_i | filename of flow image}"
		"{ b  | bound | 15 | specify the maximum of optical flow}"
	};

	//CommandLineParser cmd(argc, argv, keys);  
	//string vidFile = cmd.get<string>("vidFile");  
	//string xFlowFile = cmd.get<string>("xFlowFile");  
	//string yFlowFile = cmd.get<string>("yFlowFile");  
	//string imgFile = cmd.get<string>("imgFile");  
	//int bound = cmd.get<int>("bound");  
	string vidFile = "vidFile";
	string xFlowFile = "xFlowFile";
	string yFlowFile = "yFlowFile";
	string imgFile = "imgFile";
	int bound = 80;


	namedWindow("video", 1);
	namedWindow("imgX", 1);
	namedWindow("imgY", 1);
	namedWindow("Demo", 1);
	//VideoCapture capture(vidFile);  
	VideoCapture capture("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");

	if (!capture.isOpened()) {
		printf("Could not initialize capturing..\n");
		return -1;
	}

	int frame_num = 0;
	Mat image, prev_image, prev_grey, grey, frame, flow, cflow;

	while (true) {
		capture >> frame;
		if (frame.empty())
			break;
		imshow("video", frame);

		if (frame_num == 0) {
			image.create(frame.size(), CV_8UC3);
			grey.create(frame.size(), CV_8UC1);
			prev_image.create(frame.size(), CV_8UC3);
			prev_grey.create(frame.size(), CV_8UC1);

			frame.copyTo(prev_image);
			cvtColor(prev_image, prev_grey, CV_BGR2GRAY);

			frame_num++;
			continue;
		}

		frame.copyTo(image);
		cvtColor(image, grey, CV_BGR2GRAY);

		// calcOpticalFlowFarneback(prev_grey,grey,flow,0.5, 3, 15, 3, 5, 1.2, 0 );  
		calcOpticalFlowFarneback(prev_grey, grey, flow, 0.702, 5, 10, 2, 7, 1.5, cv::OPTFLOW_FARNEBACK_GAUSSIAN);

		prev_image.copyTo(cflow);
		drawOptFlowMap(flow, cflow, 12, 1.5, Scalar(0, 255, 0));
		imshow("cflow", cflow);

		Mat flows[2];
		split(flow, flows);
		Mat imgX(flows[0].size(), CV_8UC1);
		Mat imgY(flows[0].size(), CV_8UC1);
		convertFlowToImage(flows[0], flows[1], imgX, imgY, -bound, bound);
		//char tmp[20];  
		//sprintf(tmp, "_%04d.jpg", int(frame_num));  
		//imwrite(xFlowFile + tmp, imgX);  
		//imwrite(yFlowFile + tmp, imgY);  
		//imwrite(imgFile + tmp, image);  

		std::swap(prev_grey, grey);
		std::swap(prev_image, image);
		frame_num = frame_num + 1;

		imshow("imgX", imgX);
		imshow("imgY", imgY);
		imshow("Demo", image);
		if (waitKey(1) == 27) break;
	}
	waitKey(0);
	return 0;
}


  • 19
    点赞
  • 176
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
### 回答1: OpenCvSharp是一个基于开源计算机视觉库OpenCV的C#封装。在OpenCvSharp中,我们可以使用各种算法和功能来实现运动物体检测运动物体检测是一种在连续图像中检测并跟踪运动物体的技术。在OpenCvSharp中,我们可以使用差法或光流法等方法来实现运动物体检测差法是一种基于像素级别的比较之间差异的方法。通过计算当前和前一之间的差异,我们可以检测到图像中的运动物体。在OpenCvSharp中,我们可以使用absdiff函数来计算差异图像,并使用阈值化来提取感兴趣的运动物体。 光流法是一种基于像素的运动估计方法。它通过计算每个像素在连续之间的位移量来检测运动物体。在OpenCvSharp中,我们可以使用calcOpticalFlowPyrLK函数来计算光流,并通过阈值化来提取运动物体。 除了差法和光流法,OpenCvSharp还提供了其他一些功能和算法来实现运动物体检测。例如,我们可以使用背景减除方法来提取前景运动物体,或者使用卡尔曼滤波器来预测运动物体的位置。 总的来说,OpenCvSharp是一个强大的工具,可以帮助我们实现运动物体检测。通过使用OpenCvSharp中的各种功能和算法,我们可以有效地检测和跟踪运动物体,在许多实际应用中具有广泛的用途。 ### 回答2: 运动物体检测是指利用计算机视觉技术来识别和跟踪视频中的运动物体OpenCVSharp是一个基于OpenCV的跨平台计算机视觉库,通过使用OpenCVSharp库,我们可以很方便地实现运动物体检测。 通过OpenCVSharp库,我们可以先对输入的视频进行前景和背景的分割,将运动物体和静止背景进行区分。这样做的目的是为了减少噪音和其他不相关的运动。 接下来,我们可以使用运动物体检测算法,例如光流算法或差算法,来检测视频中的运动物体。光流算法可以估计图像中每个像素的运动方向和速度,从而确定运动物体的位置和形状。差算法则是通过比较相邻之间的像素差异,来确定哪些像素发生了运动。 在检测运动物体后,我们可以用矩形框或其他形状将其标记出来,并可以进行进一步的处理,例如追踪运动物体的轨迹或计算其运动速度等。 值得注意的是,运动物体检测是一个复杂的问题,受到许多因素的影响,例如背景干扰、光照条件和摄像机的运动等。因此,为了获得更好的检测结果,我们可能需要进行参数调优和算法组合。 总结而言,OpenCVSharp可以帮助我们实现运动物体检测,为我们提供了许多工具和算法来处理视频中的运动物体。通过合理地选择算法和优化参数,我们可以在视频中准确地检测和跟踪运动物体,从而满足各种应用场景的需求。 ### 回答3: OpenCvSharp 是一个基于C#的开源计算机视觉库,其中包括了丰富的图像处理和计算机视觉算法。运动物体检测是其中的一个重要应用之一。 在 OpenCvSharp 中,可以利用背景差分法来实现运动物体检测。背景差分法是一种简单且常用的方法,它通过对当前与背景图像进行像素级别的比较,来寻找出像素值变动较大的区域,从而检测运动物体。 首先,需要获取一个背景图像作为比较基准。可以通过采集一段静止的背景或者使用平均值法得到。然后,对于每一图像,可以使用差分运算求得每个像素点与背景图像之间的差值。通过设定一个阈值,将差值大于阈值的像素点标记为前景,即表示对应像素位置存在运动物体。 为了进一步优化运动物体检测的结果,可以对前景区域进行形态学操作,如膨胀和腐蚀等,以去除噪声或者连接分散的前景区域。此外还可以利用背景建模和光流等方法进行改进。 在OpenCvSharp中,提供了各种相关的函数和方法,如BackgroundSubtractor类和相应的算法实现,包括KNN、MOG和MOG2等。通过调用这些函数和方法,结合合适的参数设置,即可实现准确和可靠的运动物体检测。 总之,OpenCvSharp提供了丰富的工具和算法来实现运动物体检测。我们可以利用背景差分法,结合形态学操作和其他优化方法,将其应用于视频监控、智能交通系统等领域,实现运动物体的实时检测和跟踪。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值