opencv学习—图像融合之背景替换

Posted 机器人与智能系统研究院

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv学习—图像融合之背景替换相关的知识,希望对你有一定的参考价值。

证件照大家都回经常用,但是在公司和学校有时需要蓝底免冠照,有的需要白底免冠照,有的还需要红底照,那我们是都准备好以备不时之需还是用的时候直接操作?急用直接换背景色无非就是PS来P图,但不是所有的人都会玩PS,而且技术不好,P出来的图也不能用,今天小编用opencv来教你怎么完美更换照片底色。


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


opencv学习(一)—图像融合之背景替换


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


opencv学习(一)—图像融合之背景替换

opencv学习(一)—图像融合之背景替换


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


源代码:


#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;

}

 

研究院交流群

机器人与智能系统研究院 微信交流学习群已经建立,有问题请@群中相关方向博士和工作大佬解答讨论,研究方向主要有计算机视觉之三维重构,图像处理,机器人视觉系统。长按二维码加管理员微信,管理员抱你入群,本群纯粹为了学习讨论,与广告宣传的朋友请绕行。

以上是关于opencv学习—图像融合之背景替换的主要内容,如果未能解决你的问题,请参考以下文章

opencv::证件照背景替换

[opencv-python]学习-图像分割

《opencv学习》 之 二值化

OpenCV竟然可以这样学!成神之路终将不远(十六)

OpenCV图像无缝融合-seamlessClone介绍与使用(附Python/C++源码)

opencv中如何让白色的区域变成透明背景,然后和别的图片融合???