绿幕背景视频抠图对实时性要求比较高,如果使用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;
- }
原始视频图:
效果视频图: