Opencv之证件照背景替换的两种方法的比较

本文对比了使用GMM和K-means两种方法进行证件照背景替换的效果。尽管GMM在理论上可能更复杂,但实际应用中,K-means在效果和速度上都优于GMM。对于多数情况,K-means能提供更好的背景替换mask,且运算速度更快。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在贾志刚老师的带领下去做了用GMM的方法做证件照背景替换。

void IDPHOTO()
{
	string image_path = "tx.png";
	Mat src_image = imread(image_path);
	if (!src_image.data)
	{
		cout << "could not load image.." << endl;
		return ;
	}
	imshow("src_image", src_image);
	int width = src_image.cols;
	int height = src_image.rows;
	int dims = src_image.channels();
	//选择浮点数,因为要用到浮点运算
	Mat samples(width * height, dims, CV_32FC1);
	for (int row = 0; row < height; row++)
	{
		uchar* ptr_src = src_image.ptr<uchar>(row);
		for (int col = 0; col < width * dims; col += dims)
		{
			int index = row * width + col / dims;
			samples.at<float>(index, 0) = ptr_src[col + 0];
			samples.at<float>(index, 1) = ptr_src[col + 1];
			samples.at<float>(index, 2) = ptr_src[col + 2];
		}
	}
	int cluster = 4;
	int covmattype = EM::Types::COV_MAT_SPHERICAL;
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
	Mat label;
        //其实Kmeans和GMM-EM就这点代码差距
	//kmeans(samples, cluster, label, criteria, 3, cv::KmeansFlags::KMEANS_PP_CENTERS);
	Ptr<EM> em_model = EM::create();
	em_model->setClustersNumber(cluster);
	em_model->setCovarianceMatrixType(covmattype);
	em_model->setTermCriteria(criteria);
	em_model->trainEM(samples, noArray(), label, noArray());

	Mat result(src_image.size(), src_image.type());
	Mat mask(src_image.size(), CV_8UC1);
	//获得坐标(2,2)像素的颜色分类
        //其实这里可以多获取几个点,然后在判断背景颜色,以防取到的是噪声点(虽然概率及其低)
	int colorid = label.at<int>(2 * width + 2, 0);

	for (int row = 0; row < height; row++)
	{
		uchar* ptr_mask = mask.ptr<uchar>(row);
		for (int col = 0; col < width; col++)
		{
			if (label.at<int>(row * width + col) == colorid)
				ptr_mask[col] = 0;
			else
				ptr_mask[col] = 255;
		}
	}
	imshow("mask", mask);
	Mat kernel = getStructuringElement(cv::MorphShapes::MORPH_RECT, Size(5, 5), Point(-1, -1));
	morphologyEx(mask, mask, cv::MorphTypes::MORPH_CLOSE, kernel, Point(-1, -1));
	
	//erode(mask, mask, kernel);
	imshow("erode-mask", mask);
	GaussianBlur(mask, mask, Size(5, 5), 0, 0);
	imshow("gau-mask", mask);
	double w = 0.0;
	Vec3b bgdcolor(255, 0, 0);
	for (int row = 0; row < height; row++)
	{
		uchar* ptr_result = result.ptr<uchar>(row);
		uchar* ptr_srcimage = src_image.ptr<uchar>(row);
		uchar* ptr_mask = mask.ptr<uchar>(row);
		for (int col = 0; col < width*3; col+=3)
		{
			if (ptr_mask[col / 3] ==0)
			{
				ptr_result[col + 0] = bgdcolor[0];
				ptr_result[col + 1] = bgdcolor[1];
				ptr_result[col + 2] = bgdcolor[2];
			}
			else if(ptr_mask[col / 3]==255)
			{
				ptr_result[col + 0] = ptr_srcimage[col + 0];
				ptr_result[col + 1] = ptr_srcimage[col + 1];
				ptr_result[col + 2] = ptr_srcimage[col + 2];
			}
			else
			{
				w = ptr_mask[col / 3] / 255.0;
				ptr_result[col + 0] = ptr_srcimage[col + 0] * w + bgdcolor[0] * (1 - w);
				ptr_result[col + 1] = ptr_srcimage[col + 1] * w + bgdcolor[1] * (1 - w);
				ptr_result[col + 2] = ptr_srcimage[col + 2] * w + bgdcolor[2] * (1 - w);
			}
		}
	}
	imwrite("tx_photo.png", result);
	imshow("result", result);
}

  从得到效果来看,GMM的方法要比K-means的方法要差一些,而且我也努力的调整了GMM的参数以及换了不同的照片,最终效果都不怎么理想,生成的mask简直没法看,可能是要给一张颜色效果非常好的原图,GMM方法才能执行出好的效果,而且从速度方面来看,K-means的方法也要比GMM快很多。所以综合来看,K-means要比GMM好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值