绿幕背景视频抠图

       绿幕背景视频抠图对实时性要求比较高,如果使用kmeans或者GMM的话那么就太耗时了,达不到要求,因此将RGB空间转换到HSV色彩空间进行处理

       关于HSV 中颜色分量范围请看这篇博客:http://blog.csdn.net/linqianbi/article/details/78975998

 绿幕背景视频抠图的流程图:



下面看代码:

#include<opencv2\opencv.hpp>
using namespace cv;
//定义两张背景图
Mat background_01;
Mat background_02;
Mat background_03;
//绿幕抠图的实现函数
Mat replace_and_blend(Mat &frame, Mat &mask);
int main()
{
	background_01 = imread("1.jpg");
	background_02 = imread("2.jpg");
	background_03 = imread("3.jpg");
	//读入视频
	VideoCapture capture;
	capture.open("01.mp4");
	if (!capture.isOpened())
	{
		printf("could not open capture...\n");
		return -1;
	}
	char * title = "读取视频";
	char * resultWin = "效果图";
	namedWindow(title, CV_WINDOW_AUTOSIZE);
	namedWindow(resultWin, CV_WINDOW_AUTOSIZE);
	Mat frame;//定义一个Mat变量,用来存储每一帧的图像
	Mat hsv, mask;
	//如果读到了每一帧的图像
	while (capture.read(frame))
	{
		//将每一帧的图像转换到hsv空间
		cvtColor(frame, hsv, COLOR_BGR2HSV);
		//绿幕的颜色范围,将结果存在mask中
		inRange(hsv, Scalar(35, 43, 46), Scalar(155, 255, 255), mask);
		//对mask进行形态学操作
		//定义一个结构
		Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
		//对mask进行形态学闭操作
		morphologyEx(mask, mask, MORPH_CLOSE, k);
		erode(mask, mask, k);
		//高斯模糊
		GaussianBlur(mask, mask, Size(3, 3), 0, 0);
		Mat result = replace_and_blend(frame, mask);
		char c = waitKey(30);//延时30ms
		if (c == 27)//如果按下ESC那么就退出
			break;
		imshow(title, frame);
		imshow(resultWin, result);
		
	}
	waitKey(0);
	return 0;
}

//对视频的每一帧的图像进行处理
Mat replace_and_blend(Mat &frame, Mat &mask)
{
	//创建一张结果图
	Mat result = Mat(frame.size(), frame.type());
	//图像的高 宽 与通道数
	int height = result.rows;
	int width = result.cols;
	int channels = result.channels();
	//int nStep = width*channels;

	// replace and blend
	int m = 0;//mask的像素值
	double wt = 0;//融合的比例

	int r = 0, g = 0, b = 0;//输出的像素
	int r1 = 0, g1 = 0, b1 = 0;
	int r2 = 0, g2 = 0, b2 = 0;
	for (int i = 0; i < height; i++)
	{
		//定义每一行 每一帧图像的指针,mask图像的指针,两张背景图的指针,结果图的指针
		uchar *pbg = background_03.ptr<uchar>(i);
		uchar *pframe = frame.ptr<uchar>(i);
		uchar *pmask = mask.ptr<uchar>(i);
		uchar *presult = result.ptr<uchar>(i);
		for (int j = 0; j < width; j++)
		{
			m = *pmask++;//读取mask的像素值
			if (m == 255)//如果是背景的话
			{
				//进行三个通道的赋值
				*presult++ = *pbg++;
				*presult++ = *pbg++;
				*presult++ = *pbg++;
				pframe += 3;//将frame的图像的像素的通道也移动单个保持一致
			}
			else if (m == 0)//如果是前景的话
			{
				//进行三个通道的赋值
				*presult++ = *pframe++;
				*presult++ = *pframe++;
				*presult++ = *pframe++;
				pbg += 3;//将frame的图像的像素的通道也移动单个保持一致
			}
			else
			{
				//背景图每个像素的三个通道
				b1 = *pbg++;
				g1 = *pbg++;
				r1 = *pbg++;

				//每一帧每一个像素的三个通道
				b2 = *pframe++;
				g2 = *pframe++;
				r2 = *pframe++;

				// 权重
				wt = m / 255.0;

				// 混合
				b = b1*wt + b2*(1.0 - wt);
				g = g1*wt + g2*(1.0 - wt);
				r = r1*wt + r2*(1.0 - wt);

				*presult++ = b;
				*presult++ = g;
				*presult++ = r;
			}
		}
	}
	

	return result;
}

原始视频图:



效果视频图:





猜你喜欢

转载自blog.csdn.net/linqianbi/article/details/79151960