以证件照为例,图片中有大部分为背景,先用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; -
}
