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