当前位置:   article > 正文

OpenCV + CPP 系列(五十)背景替换与视频绿幕抠图_opencv和ncnn人像分割换背景

opencv和ncnn人像分割换背景

一、证件照背景替换

关于视频的操作: https://wangsp.blog.csdn.net/article/details/118701315

实现流程:

  1. 指定背景色
  2. 获取背景的色块形成蒙板
  3. 蒙板边缘羽化
  4. 形成新颜色背景

头文件 machine_learning_all.h

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

using namespace cv;
using namespace std;


class Machine_learning{
public:
	void replace_bg_demo(Mat& image);
	void replace_bg_video_demo(Mat& image, const char* mp4_path);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

主函数main.cpp

#include "machine_learning_all.h"


int main(int argc, char** argv) {
	const char* input_path = "D:\\Desktop\\meinv3.png";
	const char* mp4_path = "D:\\Desktop\\01.mp4";
	Mat src = imread(input_path);
	if (src.empty()) {
		cout << "Read image failed!" << endl;
		return -1;
	}

	Machine_learning ml;
	ml.replace_bg_video_demo(src, mp4_path);
	ml.replace_bg_demo(src);

	imshow("src", src);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

效果展示

void Machine_learning::replace_bg_demo(Mat& image) {
	int width  = image.cols;
	int height = image.rows;
	int samplecount = width * height;
	int dims = image.channels();
	Mat points = Mat::zeros(samplecount, dims, CV_32F);

	// image to matrix
	int index_t = 0;
	for (int row = 0; row < height; row++) {
		uchar* src_ptr = image.ptr<uchar>(row);
		for (int col = 0; col < width; col++) {
			index_t = row * width + col;
			points.at<float>(index_t, 0) = *src_ptr++;
			points.at<float>(index_t, 1) = *src_ptr++;
			points.at<float>(index_t, 2) = *src_ptr++;
		}
	}

	// 运行KMeans
	int numCluster = 4;
	Mat labels;
	Mat centers;
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
	kmeans(points, numCluster, labels, criteria, 3, KMEANS_PP_CENTERS, centers);

	// 去背景+遮罩生成
	Mat mask_mat = Mat::zeros(image.size(), CV_8UC1);
	int index = image.rows * 5 + 12;            //取此处像素为背景像素
	int cindex = labels.at<int>(index, 0);

	for (int row = 0; row < height; row++) {
		uchar* mask_ptr = mask_mat.ptr<uchar>(row);
		for (int col = 0; col < width; col++) {
			index = row * width + col;
			int label = labels.at<int>(index, 0);
			if (label == cindex) {            // 背景
				*mask_ptr++ = 0;
			}else {
				*mask_ptr++ = 255;
			}
		}
	}


	// 腐蚀 + 高斯模糊 (处理边界区域,使图像混合更自然)
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	erode(mask_mat, mask_mat, kernel);
	GaussianBlur(mask_mat, mask_mat, Size(3, 3), 0, 0);
	imshow("mask_mat", mask_mat);


	RNG rng(1232);
	Vec3b color(217, 60, 160);
	Mat result = Mat::zeros(image.size(), image.type());

	double weight = 0.0;
	for (int row = 0; row < height; row++) {
		uchar* mask_ptr = mask_mat.ptr<uchar>(row);
		uchar* src_ptr = image.ptr<uchar>(row);
		uchar* result_ptr = result.ptr<uchar>(row);
		for (int col = 0; col < width; col++) {
			if (*mask_ptr == 255) {         // 前景
				*result_ptr++ = *src_ptr++;
				*result_ptr++ = *src_ptr++;
				*result_ptr++ = *src_ptr++;
			}
			else if (*mask_ptr == 0) {      // 背景
				*result_ptr++ = color[0];
				*result_ptr++ = color[1];
				*result_ptr++ = color[2];
				src_ptr += 3;
			}
			else {                         // 加权混合
				weight = *mask_ptr / 255.0;
				*result_ptr++ = *src_ptr++ * weight + color[0] * (1.0 - weight);
				*result_ptr++ = *src_ptr++ * weight + color[1] * (1.0 - weight);
				*result_ptr++ = *src_ptr++ * weight + color[2] * (1.0 - weight);
			}
			mask_ptr++;
		}
	}
	imshow("result", result);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

二、视频绿幕背景替换

实现流程与图像相似,只是部分区域略有不同

Mat replace_and_blend(Mat& frame, Mat& mask_img, Mat& bg_img) {
	Mat result = Mat::zeros(frame.size(), frame.type());
	int h = frame.rows;
	int w = frame.cols;
	int dims = frame.channels();
	double wt = 0;

	for (int row = 0; row < h; row++) {
		uchar* current = frame.ptr<uchar>(row);
		uchar* bg_ptr = bg_img.ptr<uchar>(row);
		uchar* mask_ptr = mask_img.ptr<uchar>(row);
		uchar* result_ptr = result.ptr<uchar>(row);
		for (int col = 0; col < w; col++) {
			if (*mask_ptr == 255) {              // 背景
				*result_ptr++ = *bg_ptr++;
				*result_ptr++ = *bg_ptr++;
				*result_ptr++ = *bg_ptr++;
				current += 3;

			}
			else if (*mask_ptr == 0) {           // 前景
				*result_ptr++ = *current++;
				*result_ptr++ = *current++;
				*result_ptr++ = *current++;
				bg_ptr += 3;
			}
			else {
				wt = *mask_ptr / 255.0;           // 权重
				*result_ptr++ = *bg_ptr++ * wt + *current++ * (1.0 - wt);
				*result_ptr++ = *bg_ptr++ * wt + *current++ * (1.0 - wt);
				*result_ptr++ = *bg_ptr++ * wt + *current++ * (1.0 - wt);
			}
			mask_ptr++;
		}
	}
	return result;
}

void Machine_learning::replace_bg_video_demo(Mat& image, const char* mp4_path) {
	Mat frame, hsv_img, mask_img;
	VideoCapture capture(mp4_path);
	if (!capture.isOpened()) {
		cout << "could not find the video file..." << endl;;
		return;
	}
	int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);
	int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int height = image.rows;
	int width = image.cols;
	if (height < frame_height || width < frame_width) {
		cout << "背景图像太小了!" << endl;
		return;
	}
	Mat bg_img = image(Rect(0, 0, frame_width, frame_height));
	while (capture.read(frame)) {
		cvtColor(frame, hsv_img, COLOR_BGR2HSV);
		inRange(hsv_img, Scalar(35, 43, 46), Scalar(155, 255, 255), mask_img);

		// 形态学操作
		Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
		morphologyEx(mask_img, mask_img, MORPH_CLOSE, kernel);
		erode(mask_img, mask_img, kernel);
		GaussianBlur(mask_img, mask_img, Size(3, 3), 0, 0);

		Mat result = replace_and_blend(frame, mask_img, bg_img);
		if (waitKey(30) == 27) {
			break;
		}
		imshow("result", result);
		imshow("raw_frame", frame);
	}
	capture.release();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/285062
推荐阅读
相关标签
  

闽ICP备14008679号