OPENCV C++(十一)

鼠标响应函数


//鼠标响应函数
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));

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

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

//绘制直方图
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;
}

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

int index = grayMat.at(vP.y, vP.x);

 选取刚才选中的点

histgram[index]++;

在对应的直方图加1

		drawHist(histMat, histgram, bin_width, bin_heght);
		drawMarker(frame, vP, Scalar(255, 255, 255));

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

vp要是全局变量

完整代码:
 

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(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;
}

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


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

	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++;

	}

 其中

absdiff(frame, bgMat, subMat);

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

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

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

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


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

    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张帧是为了获取高斯分布

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

int calcGaussianBackground(std::vector 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(h, w);
			}
			meanMat.at(h, w) = (uchar)(sum / srcMats.size());
			//求方差
			for (int i = 0; i < srcMats.size(); i++) {
				var += (float)pow((srcMats[i].at(h, w) - meanMat.at(h, w)), 2);
			}
			varMat.at(h, w) = var / srcMats.size();
		}
	}

	return 0;
}

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


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(h, w) - meanMat.at(h, w));
			int th = (int)(weight * varMat.at(h, w));

			if (dif > th) {

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

	return 0;
}

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

完整代码:

VideoCapture cap(0);
	std::vector 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自带的背景差分方式

	// OPENCV的自带背景差分方式
	VideoCapture cap(0);	
	Mat inputFrame, frame, foregroundMask, foreground, background;
	int method = 0;
	Ptr 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是是否平滑 会用高斯滤波来平滑图像

空格是是否更新背景

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

你可能感兴趣的:(opencv,c++,人工智能)