证件照背景替换

一.设计步骤
在这里插入图片描述
二.程序

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

using namespace cv;
using namespace std;

Mat mat_to_samples(Mat &image);

int main(int argc, char** argv) 
{
	Mat src = imread("E:\\图像处理图片\\tx.png");
 	if (src.empty()) 
 	{
 		printf("could not load image...\n");
  		return -1;
 	}
 	namedWindow("原图", WINDOW_AUTOSIZE);
 	imshow("原图", src);

	// 组装数据
 	Mat points = mat_to_samples(src);

	// 运行KMeans
 	int numCluster = 4;  //定义分类的数目为 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::zeros(src.size(), CV_8UC1); //初始化遮罩,都设为0黑色
 	int index = src.rows * 2 + 2;  //这里不懂是为什么???
 	int cindex = labels.at<int>(index, 0);  //将背景设置为0
 	int height = src.rows;
 	int width = src.cols;

	//Mat dst;
 	//src.copyTo(dst);
 	//循环每个像素
 	for (int row = 0; row < height; row++) 
 	{
 		for (int col = 0; col < width; col++) 
 		{
 			index = row * width + col;
   			int label = labels.at<int>(index, 0);
   			if (label == cindex)   //如果是背景,置为0黑色,否则为前景,置为255白色
   			{// 背景
   				//dst.at<Vec3b>(row, col)[0] = 0;
    				//dst.at<Vec3b>(row, col)[1] = 0;
    				//dst.at<Vec3b>(row, col)[2] = 0;
    				mask.at<uchar>(row, col) = 0;
   			}
   			else
   			{
   				mask.at<uchar>(row, col) = 255;
   			}
 		}
 	}
 	//imshow("mask", mask);

	// 腐蚀 + 高斯模糊
 	Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
 	//getStructuringElement函数会返回指定形状和尺寸的结构元素。
 	//Mat getStructuringElement(int shape, Size esize, Point anchor = Point(-1, -1));
 	//第一个参数表示内核的形状,有三种形状可以选择。
 	//  		矩形:MORPH_RECT;
 	//  		交叉形:MORPH_CROSS;
 	//  		椭圆形:MORPH_ELLIPSE;
 	//第二和第三个参数分别是内核的尺寸以及锚点的位置。一般在调用erode以及dilate函数之前,
 	//先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心点。
 	//element形状唯一依赖锚点位置,其他情况下,锚点只是影响了形态学运算结果的偏移。
 	erode(mask, mask, k); //腐蚀操作
 	//imshow("erode-mask", mask);
 	GaussianBlur(mask, mask, Size(3, 3), 0, 0); //高斯模糊
 	//PS:腐蚀和模糊的结构元素的大小要一致!(即Size要一致)
 	//imshow("Blur Mask", mask);

	// 通道混合
 	RNG rng(12345);  //随机数产生器
 	Vec3b color;
 	//下面三个设置的是要换证件照的背景颜色,自己随意定义
 	color[0] = 100;//rng.uniform(0, 255);
 	color[1] = 100;// rng.uniform(0, 255);
 	color[2] = 100;// rng.uniform(0, 255);
 	Mat result(src.size(), src.type());

	double w = 0.0;
 	int b = 0, g = 0, r = 0;
 	int b1 = 0, g1 = 0, r1 = 0;
 	int b2 = 0, g2 = 0, r2 = 0;

	for (int row = 0; row < height; row++) 
	{
		for (int col = 0; col < width; col++) 
		{
			int m = mask.at<uchar>(row, col); //首先获取mask的值
   			if (m == 255) 
   			{
   				result.at<Vec3b>(row, col) = src.at<Vec3b>(row, col); // 前景
   			}
   			else if (m == 0) 
   			{
   				result.at<Vec3b>(row, col) = color; // 背景
   			}
   			else
   			{
   				w = m / 255.0;  //计算权重
    				b1 = src.at<Vec3b>(row, col)[0];
    				g1 = src.at<Vec3b>(row, col)[1];
    				r1 = src.at<Vec3b>(row, col)[2];

				b2 = color[0];
    				g2 = color[1];
    				r2 = color[2];

				b = b1 * w + b2 * (1.0 - w);
    				g = g1 * w + g2 * (1.0 - w);
   				r = r1 * w + r2 * (1.0 - w);

				result.at<Vec3b>(row, col)[0] = b;  //结果赋给result
    				result.at<Vec3b>(row, col)[1] = g;
    				result.at<Vec3b>(row, col)[2] = r;
   			}
		}
	}
	imshow("背景替换", result);

	waitKey(0);
 	return 0;
}

//组装数据:将图像转换为样本数据
Mat mat_to_samples(Mat &image) 
{
	//如同Kmeans中的第一步,将图像转换为样本的数据
 	int w = image.cols;
 	int h = image.rows;
 	int samplecount = w * h;  //总像素个数
 	int dims = image.channels();
 	Mat points(samplecount, dims, CV_32F, Scalar(10));//points在我的博客:Kmeans算法中有介绍

	int index = 0;
 	//遍历每个像素,将每个像素点变成对象
 	for (int row = 0; row < h; row++) 
 	{
 		for (int col = 0; col < w; col++) 
 		{
 			index = row * w + col;
   			Vec3b bgr = image.at<Vec3b>(row, col);
   			points.at<float>(index, 0) = static_cast<int>(bgr[0]);
   			points.at<float>(index, 1) = static_cast<int>(bgr[1]);
   			points.at<float>(index, 2) = static_cast<int>(bgr[2]);
 		}
 	}
 	return points;
}

输出结果:
在这里插入图片描述
在这里插入图片描述

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

猜你喜欢

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