Opencv图像分割小案例 --- 绿幕背景视频抠图

Opencv图像分割小案例 --- 绿幕背景视频抠图_第1张图片

 Opencv图像分割小案例 --- 绿幕背景视频抠图_第2张图片

基于HSV色彩空间的实时背景替换:

Opencv图像分割小案例 --- 绿幕背景视频抠图_第3张图片

Opencv图像分割小案例 --- 绿幕背景视频抠图_第4张图片

注:新更换的背景图必须和原视频的背景图尺寸一样。 

相关API:

1. inRange()函数

opencv中的inRange()函数可实现二值化功能(这点类似threshold()函数),更关键的是可以同时

针对多通道进行操作,使用起来非常方便!主要是将在两个阈值内的像素值设置为白色(255),

而不在阈值区间内的像素值设置为黑色(0),该功能类似于之间所讲的双阈值化操作。

函数原型(C++):

    void inRange(InputArray src, InputArray lowerb,
                              InputArray upperb, OutputArray dst);

参数解释

  • 参数1:输入要处理的图像,可以为单通道或多通道。
  • 参数2:包含下边界的数组或标量。
  • 参数3:包含上边界数组或标量。
  • 参数4:输出图像,与输入图像src 尺寸相同且为CV_8U 类型。

请注意:该函数输出的dst是一幅二值化之后的图像。

代码演示:

#include
#include

using namespace std;
using namespace cv;

Mat replace_and_blend(Mat &frame, Mat &mask);
Mat background_01;
Mat background_02;

int main(int argc, char** argv)
{
	background_01 = imread("E:/技能学习/opencv图像分割/bg_01.jpg");
	background_02 = imread("E:/技能学习/opencv图像分割/bg_02.jpg");

	VideoCapture capture;
	capture.open("E:/技能学习/opencv图像分割/01.mp4");

	if (!capture.isOpened())
	{
		cout << "could not load video!" << endl;
		return -1;
	}

	char* title = "input video";
	char* resultWin = "result video";

	namedWindow(title, WINDOW_AUTOSIZE);
	namedWindow(resultWin, WINDOW_AUTOSIZE);

	Mat frame, hsv, mask;
	while (capture.read(frame))
	{
		cvtColor(frame, hsv, COLOR_BGR2HSV);
		inRange(hsv, Scalar(35, 43, 46), Scalar(155, 255, 255), mask);
		
		//对mask进行形态学操作
		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(1);
		if (c == 27)
		{
			break;
		}

		//imshow("mask", mask);
		imshow(resultWin, result);
		imshow(title, frame);
	}
	waitKey(0);
	destroyAllWindows();
	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();

	//replace and blend
	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++)
	{
		uchar* current = frame.ptr(row);
		uchar* bgrow = background_01.ptr(row);
		uchar* maskrow = mask.ptr(row);
		uchar* targetrow = result.ptr(row);
		for (int col = 0; col < w; col++)
		{
			m = *maskrow++;
			if (m == 255) //背景
			{
				*targetrow++ = *bgrow++;
				*targetrow++ = *bgrow++;
				*targetrow++ = *bgrow++;
				current += 3;

			}
			else if (m == 0) //前景
			{
				*targetrow++ = *current++;
				*targetrow++ = *current++;
				*targetrow++ = *current++;
				bgrow += 3;
			}
			else
			{
				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;
}

结果展示:

Opencv图像分割小案例 --- 绿幕背景视频抠图_第5张图片

 

 

你可能感兴趣的:(Opencv,opencv,人工智能,计算机视觉)