OPENCV C++(十一)

鼠标响应函数

cpp 复制代码
//鼠标响应函数
void on_mouse(int EVENT, int x, int y, int flags, void* userdata)
{

	Mat hh;
	hh = *(Mat*)userdata;
	switch (EVENT)
	{
	case EVENT_LBUTTONDOWN:
	{
		vP.x = x;
		vP.y = y;
		drawMarker(hh, vP, Scalar(255, 255, 255));
		//circle(hh, vP, 4, cvScalar(255, 255, 255), -1);
		imshow(wName, hh);
		return;
	}
	break;
	}

}

drawMarker(hh, vP, Scalar(255, 255, 255));

这个是画一个十字符号 标记一个点

绘制直方图和以前篇幅一样

cpp 复制代码
//绘制直方图
int drawHist(cv::Mat& histMat, float* srcHist, int bin_width, int bin_heght)
{
	histMat.create(bin_heght, 256 * bin_width, CV_8UC3);

	histMat = Scalar(255, 255, 255);

	float maxVal = *std::max_element(srcHist, srcHist + 256);

	for (int i = 0; i < 256; i++) {
		Rect binRect;
		binRect.x = i * bin_width;
		float height_i = (float)bin_heght * srcHist[i] / maxVal;
		binRect.height = (int)height_i;
		binRect.y = bin_heght - binRect.height;
		binRect.width = bin_width;
		rectangle(histMat, binRect, CV_RGB(255, 0, 0), -1);
	}

	return 0;
}

统计视频一个点不受大影响的时候直方图是高斯分布的(灰度)

cpp 复制代码
int index = grayMat.at<uchar>(vP.y, vP.x);

选取刚才选中的点

cpp 复制代码
histgram[index]++;

在对应的直方图加1

cpp 复制代码
		drawHist(histMat, histgram, bin_width, bin_heght);
		drawMarker(frame, vP, Scalar(255, 255, 255));

这里还要画一个drawmaker因为第二遍就不会调用了

vp要是全局变量

完整代码:

cpp 复制代码
int main() {

	// 验证某一背景像素值呈高斯分布
	
	
	VideoCapture cap(0);
	int cnt = 0;
	float histgram[256] = { 0 };
	Mat histMat;
	int bin_width = 3;
	int bin_heght = 100;
	while (1)
	{
		Mat frame;
		Mat grayMat;
		cap >> frame;
		if (cnt == 0)
		{
			Mat selectMat;
			frame.copyTo(selectMat);
			
			imshow(wName, selectMat);
			setMouseCallback(wName, on_mouse, &selectMat);
			waitKey(0);
			destroyAllWindows();
		}
		cvtColor(frame, grayMat, COLOR_BGR2GRAY);
		int index = grayMat.at<uchar>(vP.y, vP.x);
		histgram[index]++;
		drawHist(histMat, histgram, bin_width, bin_heght);
		drawMarker(frame, vP, Scalar(255, 255, 255));
		imshow("frame", frame);
		imshow("histMat", histMat);
		if (waitKey(30) == 27) {
			destroyAllWindows();
			break;
		}
		cnt++;
	}
return 0;
}

当然还有一些变量需要自己设置全局变量


直接拿原图和新图直接做差分

cpp 复制代码
	VideoCapture cap(0);
	int cnt = 0;
	Mat frame;
	while (1) {
		cap>> frame;
		cvtColor(frame, frame, COLOR_BGR2GRAY);
		if (cnt == 0) {
			//第一帧,获得背景图像
			frame.copyTo(bgMat);
		}
		else {
			//第二帧开始背景差分
			//背景图像和当前图像相减
			absdiff(frame, bgMat, subMat);
			//差分结果二值化
			namedWindow("Result", WINDOW_AUTOSIZE);
			//滑动条创建
			cv::createTrackbar("threshold", "Result", &sub_threshold, 255, threshold_track);
			threshold_track(0, 0);
			imshow("frame", frame);
		}

		if (waitKey(30) == 27) {
			destroyAllWindows();
			break;
		}

		cnt++;

	}

其中

cpp 复制代码
absdiff(frame, bgMat, subMat);

如果摄像机是固定的,那么我们可以认为场景(背景)大多数情况下是不变的,而只有前景(被跟踪的目标)会运动,这样就可以建立背景模型。通过比较当前帧和背景模型,就能轻松地跟踪目标运动情况了。这里,最容易想到的比较方式就是当前帧减去背景模型了

将差分的图像二值化 这里创建了滑动条 bar

cpp 复制代码
void threshold_track(int, void*)//这里就是定义的一个回调函数,里面是canny相关的操作
{

	threshold(subMat, bny_subMat, sub_threshold, 255, CV_THRESH_BINARY);
	imshow("Result", bny_subMat);
}


运用了高斯差分 因为本身图像的点都符合高斯分布,收光照等等影响,而这些都不能被考虑进移动物

cpp 复制代码
    int nBg = 200;	
	cap >> frame;
		cvtColor(frame, frame, COLOR_BGR2GRAY);
		if (cnt <= nBg) {

			srcMats.push_back(frame);

			if (cnt == 0) {
				std::cout << "--- reading frame --- " << std::endl;
			}
			else {
				std::cout << "-";
				if (cnt % 50 == 0)std::cout << std::endl;
			}
		}

这里是前200张帧是为了获取高斯分布

计算图像的平均值和方差(灰度)

cpp 复制代码
int calcGaussianBackground(std::vector<cv::Mat> srcMats, cv::Mat& meanMat, cv::Mat& varMat)
{

	int rows = srcMats[0].rows;
	int cols = srcMats[0].cols;


	for (int h = 0; h < rows; h++)
	{
		for (int w = 0; w < cols; w++)
		{

			int sum = 0;
			float var = 0;
			//求均值
			for (int i = 0; i < srcMats.size(); i++) {
				sum += srcMats[i].at<uchar>(h, w);
			}
			meanMat.at<uchar>(h, w) = (uchar)(sum / srcMats.size());
			//求方差
			for (int i = 0; i < srcMats.size(); i++) {
				var += (float)pow((srcMats[i].at<uchar>(h, w) - meanMat.at<uchar>(h, w)), 2);
			}
			varMat.at<float>(h, w) = var / srcMats.size();
		}
	}

	return 0;
}

利用平均值和方差来判断是否是入侵背景的前景

cpp 复制代码
int gaussianThreshold(cv::Mat srcMat, cv::Mat meanMat, cv::Mat varMat, float weight, cv::Mat& dstMat)
{
	int rows = srcMat.rows;
	int cols = srcMat.cols;

	for (int h = 0; h < rows; h++)
	{
		for (int w = 0; w < cols; w++)
		{
			int dif = abs(srcMat.at<uchar>(h, w) - meanMat.at<uchar>(h, w));
			int th = (int)(weight * varMat.at<float>(h, w));

			if (dif > th) {

				dstMat.at<uchar>(h, w) = 255;
			}
			else {
				dstMat.at<uchar>(h, w) = 0;
			}
		}
	}

	return 0;
}

这里的weight是权重,可以代表差异到什么程度就是前景

完整代码:

cpp 复制代码
VideoCapture cap(0);
	std::vector<cv::Mat> srcMats;
	int nBg = 200;		
	float wVar = 3;

	int cnt = 0;
	bool calcModel = true;
	cv::Mat frame;
	cv::Mat meanMat;
	cv::Mat varMat;
	cv::Mat dstMat;
	while (1)
	{
		cap >> frame;
		cvtColor(frame, frame, COLOR_BGR2GRAY);
		if (cnt <= nBg) {

			srcMats.push_back(frame);

			if (cnt == 0) {
				std::cout << "--- reading frame --- " << std::endl;
			}
			else {
				std::cout << "-";
				if (cnt % 50 == 0)std::cout << std::endl;
			}
		}

		else {
			if (calcModel) {
				std::cout << std::endl << "calculating background models" << std::endl;
				//计算模型
				meanMat.create(frame.size(), CV_8UC1);
				varMat.create(frame.size(), CV_32FC1);
				//调用计算模型函数
				calcGaussianBackground(srcMats, meanMat, varMat);
			}
			calcModel = false;

			//背景差分
			dstMat.create(frame.size(), CV_8UC1);
			//利用均值mat和方差mat,计算差分
			gaussianThreshold(frame, meanMat, varMat, wVar, dstMat);
			imshow("result", dstMat);
			imshow("frame", frame);

		}

		if (waitKey(30) == 27) {
			destroyAllWindows();
			break;
		}
		cnt++;


	}


opencv自带的背景差分方式

cpp 复制代码
	// OPENCV的自带背景差分方式
	VideoCapture cap(0);	
	Mat inputFrame, frame, foregroundMask, foreground, background;
	int method = 0;
	Ptr<BackgroundSubtractor> model;
	if (method == 0) {
		model = createBackgroundSubtractorKNN();
	}
	else if (method == 1) {
		model = createBackgroundSubtractorMOG2();
	}
	else {
		cout << "Can not create background model using provided method: '" << method << "'" << endl;
	}
	bool doUpdateModel = true;
	bool doSmoothMask = false;
	while (1) {
	
		cap >> frame;
		model->apply(frame, foregroundMask, doUpdateModel ? -1 : 0);
		imshow("image", frame);
		if (doSmoothMask)
		{
			GaussianBlur(foregroundMask, foregroundMask, Size(11, 11), 3.5, 3.5);
			threshold(foregroundMask, foregroundMask, 10, 255, THRESH_BINARY);
		}
		if (foreground.empty())
			foreground.create(frame.size(), frame.type());
		foreground = Scalar::all(0);
		frame.copyTo(foreground, foregroundMask);
		imshow("foreground mask", foregroundMask);
		imshow("foreground image", foreground);
		model->getBackgroundImage(background);
		if (!background.empty())
			imshow("mean background image", background);
		const char key = (char)waitKey(30);
		if (key == 27 || key == 'q') // ESC
		{
			cout << "Exit requested" << endl;
			break;
		}
		else if (key == ' ')
		{
			doUpdateModel = !doUpdateModel;
			cout << "Toggle background update: " << (doUpdateModel ? "ON" : "OFF") << endl;
		}
		else if (key == 's')
		{
			doSmoothMask = !doSmoothMask;
			cout << "Toggle foreground mask smoothing: " << (doSmoothMask ? "ON" : "OFF") << endl;
		}
	

	}
	

	return 0;
}

S是是否平滑 会用高斯滤波来平滑图像

空格是是否更新背景

目前不是太懂这里的代码 希望后续学到这里后会明白

相关推荐
蜀黍@猿2 分钟前
C/C++基础错题归纳
c++
雨中rain16 分钟前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
B站计算机毕业设计超人19 分钟前
计算机毕业设计PySpark+Hadoop中国城市交通分析与预测 Python交通预测 Python交通可视化 客流量预测 交通大数据 机器学习 深度学习
大数据·人工智能·爬虫·python·机器学习·课程设计·数据可视化
学术头条23 分钟前
清华、智谱团队:探索 RLHF 的 scaling laws
人工智能·深度学习·算法·机器学习·语言模型·计算语言学
18号房客27 分钟前
一个简单的机器学习实战例程,使用Scikit-Learn库来完成一个常见的分类任务——**鸢尾花数据集(Iris Dataset)**的分类
人工智能·深度学习·神经网络·机器学习·语言模型·自然语言处理·sklearn
feifeikon30 分钟前
机器学习DAY3 : 线性回归与最小二乘法与sklearn实现 (线性回归完)
人工智能·机器学习·线性回归
游客52033 分钟前
opencv中的常用的100个API
图像处理·人工智能·python·opencv·计算机视觉
古希腊掌管学习的神34 分钟前
[机器学习]sklearn入门指南(2)
人工智能·机器学习·sklearn
凡人的AI工具箱1 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战项目二)
数据结构·c++·算法