绿幕背景视频抠图替换

首先说明,参考的博客

https://blog.csdn.net/linqianbi/article/details/78975998
https://blog.csdn.net/linqianbi/article/details/79151960

一.算法的选择
绿幕背景视频抠图对实时性要求比较高,如果使用kmeans或者GMM的话那么就太耗时了,达不到要求,因此将RGB空间转换到HSV色彩空间进行处理。
关于HSV参考上面的博客
在这里插入图片描述
二.本实例的程序设计思路
在这里插入图片描述
三.源程序
(和网上的一样,我自己加了些注释,方便自己理解)

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

#define WINDOW_NAME1 "title"
#define WINDOW_NAME2 "resultWin"

Mat replace_and_blend(Mat &frame, Mat &mask);//绿幕抠图的实现函数

Mat background_01;
Mat background_02;

int main(int argc, char** argv) 
{
	//定义你想替换的背景,背景1和背景2都可选择
 	background_01 = imread("E:\\图像处理图片\\34.jpg");
 	background_02 = imread("E:\\图像处理图片\\35.jpg");

	VideoCapture capture; //读取视频
 	capture.open("E:\\图像处理图片\\01.mp4");
 	if (!capture.isOpened()) //检验是否读取到视频
 	{
 		printf("could not find the video file...\n");
  		return -1;
 	}
 	namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
 	namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);

	Mat frame, hsv, mask;  //frame表示每一帧的图像

	while (capture.read(frame)) //检测读取到的每一帧图像
	{
		cvtColor(frame, hsv, COLOR_BGR2HSV); //将帧图像转为HSV
  		inRange(hsv, Scalar(35, 43, 46), Scalar(155, 255, 255), mask);
  		//inRange()函数可实现二值化功能(这点类似threshold()函数),更关键的是可以同时针对多通道进行操作。
  		//主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0),该功能类似于之间所讲的双阈值化操作。
  		//函数原型:void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);
  		//   参数1:输入要处理的图像,可以为单通道或多通道。
  		//   参数2:包含下边界的数组或标量。
  		//   参数3:包含上边界数组或标量。
  		//   参数4:输出图像,与输入图像src 尺寸相同且为CV_8U 类型。
  		//   请注意:该函数输出的dst是一幅二值化之后的图像。

		// 形态学操作
  		Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); 
  		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); //停顿30毫米给计算机做处理
  		if (c == 27)  //按下“ESC”键即退出程序
  		{
  			break;
  		}
  		imshow(WINDOW_NAME2 result);
  		imshow(WINDOW_NAME1, frame);
	}
	waitKey(0);
 	return 0;
}

//对视频的每一帧的图像进行处理
Mat replace_and_blend(Mat &frame, Mat &mask) 
{
	Mat result = Mat::zeros(frame.size(), frame.type());
 	int h = frame.rows;
 	int w = frame.cols;
 	int dims = frame.channels();

	// 背景的融合替换
 	int m = 0;  //图像的像素值
 	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 row = 0; row < h; row++) 
 	{
 		//访问图像像素有三种可行的方法:
  		//指针访问访问的速度最快,Mat类可以通过ptr函数得到图像任意一行的首地址,
  		//同时,Mat类的一些属性也可以用到公有属性 rows和cols 表示行和列
  		//通道数可以通过channels()函数获得;
  		uchar* current = frame.ptr<uchar>(row);   //current为帧图像的行数,即高度
  		uchar* bgrow = background_02.ptr<uchar>(row);  //bgrow为要替换的背景图像的行数,即高度
  		uchar* maskrow = mask.ptr<uchar>(row);   //maskrow为二值化后的图像mask的行数,即高度
  		uchar* targetrow = result.ptr<uchar>(row);  //targetrow为输出结果图像的行数,即高度
  		for (int col = 0; col < w; col++) 
  		{
  			m = *maskrow++;  //读取mask的像素值,每运行一次行数加一
   			if (m == 255)  
   			{// 背景
   				*targetrow++ = *bgrow++;
    				*targetrow++ = *bgrow++;
    				*targetrow++ = *bgrow++;
    				current += 3;  //将frame的图像的像素的通道也移动单个保持一致
   			}
   			else if (m == 0) 
   			{// 前景
   				*targetrow++ = *current++;
    				*targetrow++ = *current++;
    				*targetrow++ = *current++;
    				bgrow += 3;   //将background_02的图像的像素的通道也移动单个保持一致
   			}
   			else
   			{
   				//要替换的背景图background_02每个像素的三个通道
    				b1 = *bgrow++;
    				g1 = *bgrow++;
    				r1 = *bgrow++;

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

				// 权重
    				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);

				*targetrow++ = b;
    				*targetrow++ = g;
    				*targetrow++ = r;
   			}
  		}
 	}
 	return result;
}

程序运行后的结果却不尽人意,原视频和背景换掉的视频都播放的很慢,我调了等待的时间也没用,不知道是电脑的问题,还是程序的问题,总之没解决,希望看到的大佬能给我指点下。。。

发布了25 篇原创文章 · 获赞 0 · 访问量 463

猜你喜欢

转载自blog.csdn.net/qq_45445740/article/details/103301883