以证件照为例,图片中有大部分为背景,先用kmeans对图像进行分割,可以得到背景的标签,然后将图像分为前景和背景两部分,非背景的都当作前景,显示kmeans分割后的图像dst,将原图像前景赋给dst, 背景都设为0,得到kmeans分割后的图像如下,可看到边缘处有一些小蓝边,过渡比较粗超:
所以设置遮罩层对边缘进行融合,新建掩码mask单通道图像,将前景部分置1,背景部分置0,然后对mask进行腐蚀和高斯模糊,则mask前景部分为1,背景部分为0,边缘部分非0和1。新建结果图像result,对于mask中前景部分,将原图像赋给result,对于mask中背景部分,将随机生成的颜色赋给result,对于边缘部分,对前景和背景进行融合。
可看到边缘融合后的图像看起来就比较和谐了。
-
#include
-
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(index, 0) = src.at(i, j)[0];
-
points.at(index, 1) = src.at(i, j)[1];
-
points.at(index, 2) = src.at(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(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(index, 0);
-
if (label == bindex) {
-
dst.at(i, j)[0] = 0;
-
dst.at(i, j)[1] = 0;
-
dst.at(i, j)[2] = 0;
-
mask.at(i, j) = 0;
-
}
-
else {
-
mask.at(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(i, j);
-
if (m == 255) {
-
result.at(i, j) = src.at(i, j);//将原图像中前景赋给结果图像中的前景
-
}
-
else if (m == 0) {
-
result.at(i, j) = color;//将随机生成的颜色赋给结果图像中的背景
-
}
-
else {
-
w = m / 255.0;//权重
-
//边缘前景
-
b1 = src.at(i, j)[0];
-
g1= src.at(i, j)[1];
-
r1 = src.at(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(i, j)[0] = b;
-
result.at(i, j)[1] = g;
-
result.at(i, j)[2] = r;
-
}
-
}
-
}
-
imshow("result",result);
-
waitKey(0);
-
return 0;
-
}