opencv学习笔记五十九:图像融合之背景替换

以证件照为例,图片中有大部分为背景,先用kmeans对图像进行分割,可以得到背景的标签,然后将图像分为前景和背景两部分,非背景的都当作前景,显示kmeans分割后的图像dst,将原图像前景赋给dst, 背景都设为0,得到kmeans分割后的图像如下,可看到边缘处有一些小蓝边,过渡比较粗超:

 所以设置遮罩层对边缘进行融合,新建掩码mask单通道图像,将前景部分置1,背景部分置0,然后对mask进行腐蚀和高斯模糊,则mask前景部分为1,背景部分为0,边缘部分非0和1。新建结果图像result,对于mask中前景部分,将原图像赋给result,对于mask中背景部分,将随机生成的颜色赋给result,对于边缘部分,对前景和背景进行融合。

可看到边缘融合后的图像看起来就比较和谐了。

#include<opencv2\opencv.hpp>
using namespace cv;

int main(int arc, char** argv) { 
	Mat src = imread("1.jpg");
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	imshow("input", src);

	//组装数据并运行KMeans
	int width = src.cols;
	int height = src.rows;
	int dims = src.channels();
	int pointsCount = width * height;
	Mat points(pointsCount, dims, CV_32F);//kmeans要求的数据为float类型的
	int index = 0;
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			index = i*width + j;
			points.at<float>(index, 0) = src.at<Vec3b>(i, j)[0];
			points.at<float>(index, 1) = src.at<Vec3b>(i, j)[1];
			points.at<float>(index, 2) = src.at<Vec3b>(i, j)[2];
		}
	}
	Mat bestLabels;
	Mat centers;
	kmeans(points, 4, bestLabels, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 10, 0.1),3,2,centers);

	//去背景+遮罩层
	Mat mask(src.size(), CV_8UC1);
	index = src.cols * 2 + 2;
	int bindex = bestLabels.at<int>(index, 0);//获得kmeans后背景的标签

	Mat dst;
	src.copyTo(dst);
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			index = i*width + j;
			int label = bestLabels.at<int>(index, 0);
			if (label == bindex) {
				dst.at<Vec3b>(i, j)[0] = 0;
				dst.at<Vec3b>(i, j)[1] = 0;
				dst.at<Vec3b>(i, j)[2] = 0;
				mask.at<uchar>(i, j) = 0;
			}
			else {
				mask.at<uchar>(i, j) = 255;
			}
		}
	}
	imshow("mask", mask);
	imshow("kmeans", dst);

    //对掩码进行腐蚀+高斯模糊
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	erode(mask, mask, kernel);
	imshow("erode mask", mask);
	GaussianBlur(mask, mask, Size(3, 3), 0, 0);
	imshow("blur mask", mask);

	//通道混合
	Vec3b color;
	color[0] = theRNG().uniform(0, 255);
	color[1] = theRNG().uniform(0, 255);
	color[2] = theRNG().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 i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			int m = mask.at<uchar>(i, j);
			if (m == 255) {
				result.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);//将原图像中前景赋给结果图像中的前景
			}
			else if (m == 0) {
				result.at<Vec3b>(i, j) = color;//将随机生成的颜色赋给结果图像中的背景
			}
			else {
				w = m / 255.0;//权重
				//边缘前景
				b1 = src.at<Vec3b>(i, j)[0];
				g1= src.at<Vec3b>(i, j)[1];
				r1 = src.at<Vec3b>(i, j)[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>(i, j)[0] = b;
				result.at<Vec3b>(i, j)[1] = g;
				result.at<Vec3b>(i, j)[2] = r;
			}
		}
	}
	imshow("result",result);
	waitKey(0);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_24946843/article/details/82827947