OpenCV4学习笔记(52)——基于K-Means聚类算法的证件照背景替换

在上一篇笔记《OpenCV4学习笔记(51)》中,整理记录了基于K-Means聚类算法实现的图像分割,而本次笔记就是要将这种方法应用到实际例子中去,实现一个简单的证件照背景替换。关于K-Means聚类算法实现图像分割的内容,可以参阅我的上一篇笔记。

基于K-Means聚类算法的图像分割实现简单证件照片的背景分割提取与替换,要求照片的背景和前景的颜色差异比较大,实现的主要步骤如下:
(1) 读入证件图像,并建立样本数据集。
(2)使用K-Means算法进行图像分割,指定聚类数目,需要确保背景能够被完整分割出来。
(3)取图像中左上角像素点,认为该像素点属于证件照的背景部分,获取该像素点所属的类别标签label,从而将属于同一类别标签的像素点寻找出来。
(4)生成背景掩膜mask图像,按照mask图像进行背景替换。

下面来看代码演示:

	//读取图像,并获取该图像的宽、高、总像素数
	Mat test_image = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\toux.jpg");
	resize(test_image, test_image, Size(500, 600));
	imshow("test_image", test_image);
	int width = test_image.cols;
	int height = test_image.rows;
	int data_counts = width * height;
	//将图像转化成数据集,是一个列数为1、行数为总像素数的Mat对象,每一行的元素是一个像素点样本,具有三通道
	Mat data = test_image.reshape(3, data_counts);
	//将数据集转换为浮点型,因为K-Means聚类算法只适用于连续性数据,否则会出现中心点漂移出样本集的错误
	data.convertTo(data, CV_32F);

	Mat bestLabels, centers;
	int K = 3;			//聚类的类别数,类别数越大则结果越接近原图像
	TermCriteria criteria = TermCriteria(TermCriteria::Type::COUNT + TermCriteria::Type::EPS, 10, 0.01);
	kmeans(data, K, bestLabels, criteria, 3, KMEANS_PP_CENTERS, centers);
	
	//将输出结果图中的每一个像素点,按照其所属的类别来改变其像素值
	Mat kmeans_image = Mat::zeros(test_image.size(), test_image.type());
	Mat mask = Mat::zeros(test_image.size(), CV_8UC1);
	int flag_label = bestLabels.at<int>(0, 0);			//获取左上角像素点的标签label,作为背景像素点的标签
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			int data_index = row * width + col;		//表示该像素点在数据集中所在的行索引,也就是第几个样本像素点
			int label = bestLabels.at<int>(data_index, 0);			//获取该像素点所属的类别标签
			if (label == flag_label)			//如果某像素点的label和背景像素点的label相同,则mask中对应位置的像素点置为255
			{
				mask.at<uchar>(row, col) = 255;
			}
			if (label == 0)
			{
				Vec3b bgr = { 255,0,0 };
				kmeans_image.at<Vec3b>(row, col) = bgr;
			}
			else if (label == 1)
			{
				Vec3b bgr = { 0,255,0 };
				kmeans_image.at<Vec3b>(row, col) = bgr;
			}
			else if (label == 2)
			{
				Vec3b bgr = { 0,0,255 };
				kmeans_image.at<Vec3b>(row, col) = bgr;
			}
		}
	}
	imshow("kmeans_image", kmeans_image);

	//对mask图像进行闭操作,降低背景替换后图像的撕裂感
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(mask, mask, MORPH_CLOSE, kernel, Point(-1, -1), 1, 0);
	imshow("mask", mask);

	Mat dst;
	dst = test_image.clone();
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			//以mask中同一位置坐标像素点的值为标志flag,如果flag==255证明该位置为背景,则将该像素值设置为我们需要的颜色
			int flag = mask.at<uchar>(row, col);
			if (flag == 255)
			{
				Vec3b bgr = { 255,255,255 };
				dst.at<Vec3b>(row, col) = bgr;
			}
		}
	}
	imshow("dst", dst);

在获取图像背景的mask后,可以进行一下形态学方面的操作,用来消除一些小的干扰或者是填充一些孔洞,使得背景替换的效果更好。

下面是我们用来演示的证件照:
注意:该图像来源网络,如果涉及侵权将立马删除!!!
在这里插入图片描述
使用K-Means算法进行图像分割后的结果,这里用红、绿、蓝三种颜色来代表三种不同的类别:
在这里插入图片描述
从分割结果中提取出的背景掩膜mask图像,经过形态学闭操作处理:
在这里插入图片描述
最后将原来证件照的蓝色背景替换成白色背景:
在这里插入图片描述

这样就实现了对证件照的简单的背景提取和替换了,当然了这种方法只适用于背景单一、而且背景和前景具有较大差异的证件照,其普适性还是比较差的。但这种方法也为我们从图像中提取前景提供了一种思路,还是可以学习一下的。

好的,今天的笔记就整理到这啦,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值