OpenCV实现背景分离(证件照背景替换)_第1页
OpenCV实现背景分离(证件照背景替换)_第2页
OpenCV实现背景分离(证件照背景替换)_第3页
OpenCV实现背景分离(证件照背景替换)_第4页
OpenCV实现背景分离(证件照背景替换)_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第OpenCV实现背景分离(证件照背景替换)我对比了百度搜索证件照一键改色网站的效果,基本一致,它们处理一次4块钱,我们这是免费的,授人以鱼不如授人以渔对吧,学到就是赚到。当然人家的功能肯定更强大,估计集成了深度学习一类的框架,我们还需要调参。美中不足的地方就由兄弟们一起改进了。

细心的biliy发现了我贴图的问题,如图1图2图3所示,领口处被当做背景色了,这样当然不行,接下来开始改进功能。

1)首先分析原因,之所以领口被当做背景色,是因为领口为白色,同背景色一致,且连接图像边缘处,进行轮廓分析时,错将这个领口识别为轮廓外,如图4所示。

图4识别失败

2)正如图4所示,仅仅用闭运算是无法有效补偿的,如果将窗口尺寸加大还可能使其他位置过度填充,接下来考虑如何只填充这类大洞。先将处理图像的宽高各扩展50个pixel,这样做的好处是令轮廓的识别更精准和清晰,并且避免了头顶处因贴近图像边缘,而导致的过度膨胀现象。

cv::Mattmask=cv::Mat::zeros(row+50,col+50,CV_8UC1);

mask.copyTo(tmask(cv::Range(25,25+mask.rows),cv::Range(25,25+mask.cols)));

3)之后进行黑帽运算,即闭运算减原图,得到图5。

图5黑帽运算

4)用Clear_MicroConnected_Area函数清除小面积连通区,得到图6。

(该函数介绍见:/article/221904.htm)

图6清除小面积连通区

5)黑帽运算结果加至原轮廓图,并截取实际图像尺寸。

//黑帽运算获取同背景色类似的区域,识别后填充

cv::Mathat;

cv::Matelement=getStructuringElement(MORPH_ELLIPSE,Size(31,31));

cv::morphologyEx(tmask,hat,MORPH_BLACKHAT,element);

hat.setTo(255,hat

cv::Mathatd;

//清除小面积区域

Clear_MicroConnected_Areas(hat,hatd,450);

tmask=tmask+hatd;

//截取实际尺寸

mask=tmask(cv::Range(25,25+mask.rows),cv::Range(25,25+mask.cols)).clone();

6)至此,就得到完整的轮廓了,如图7所示,完整代码见后方。

图7完整轮廓图

完整改进代码

#includeopencv2/opencv.hpp

#includeiostream

#includealgorithm

#includetime.h

usingnamespacecv;

usingnamespacestd;

//输入参数

structInputparama{

intthresh=30;//背景识别阈值,该值越小,则识别非背景区面积越大,需有合适范围,目前为5-60

inttransparency=255;//背景替换色透明度,255为实,0为透明

intsize=7;//非背景区边缘虚化参数,该值越大,则边缘虚化程度越明显

cv::Pointp=cv::Point(0,0);//背景色采样点,可通过人机交互获取,也可用默认(0,0)点颜色作为背景色

cv::Scalarcolor=cv::Scalar(255,255,255);//背景色

cv::MatBackgroundSeparation(cv::Matsrc,Inputparamainput);

voidClear_MicroConnected_Areas(cv::Matsrc,cv::Matdst,doublemin_area);

//计算差值均方根

intgeiDiff(ucharb,ucharg,ucharr,uchartb,uchartg,uchartr)

returnint(sqrt(((b-tb)*(b-tb)+(g-tg)*(g-tg)+(r-tr)*(r-tr))/3));

intmain()

cv::Matsrc=imread("111.jpg");

Inputparamainput;

input.thresh=100;

input.transparency=255;

input.size=6;

input.color=cv::Scalar(0,0,255);

clock_ts,e;

s=clock();

cv::Matresult=BackgroundSeparation(src,input);

e=clock();

doubledif=e-s;

cout"time:"difendl;

imshow("original",src);

imshow("result",result);

imwrite("result1.png",result);

waitKey(0);

return0;

//背景分离

cv::MatBackgroundSeparation(cv::Matsrc,Inputparamainput)

cv::Matbgra,mask;

//转化为BGRA格式,带透明度,4通道

cvtColor(src,bgra,COLOR_BGR2BGRA);

mask=cv::Mat::zeros(bgra.size(),CV_8UC1);

introw=src.rows;

intcol=src.cols;

//异常数值修正

input.p.x=max(0,min(col,input.p.x));

input.p.y=max(0,min(row,input.p.y));

input.thresh=max(5,min(200,input.thresh));

input.transparency=max(0,min(255,input.transparency));

input.size=max(0,min(30,input.size));

//确定背景色

ucharref_b=src.atVec3b(input.p.y,input.p.x)[0];

ucharref_g=src.atVec3b(input.p.y,input.p.x)[1];

ucharref_r=src.atVec3b(input.p.y,input.p.x)[2];

//计算蒙版区域(掩膜)

for(inti=0;irow;++i)

uchar*m=mask.ptruchar

uchar*b=src.ptruchar

for(intj=0;jcol;++j)

if((geiDiff(b[3*j],b[3*j+1],b[3*j+2],ref_b,ref_g,ref_r))input.thresh)

m[j]=255;

cv::Mattmask=cv::Mat::zeros(row+50,col+50,CV_8UC1);

mask.copyTo(tmask(cv::Range(25,25+mask.rows),cv::Range(25,25+mask.cols)));

//寻找轮廓,作用是填充轮廓内黑洞

vectorvectorPointcontour;

vectorVec4ihierarchy;

//RETR_TREE以网状结构提取所有轮廓,CHAIN_APPROX_NONE获取轮廓的每个像素

findContours(tmask,contour,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE);

drawContours(tmask,contour,-1,Scalar(255),FILLED,16);

//黑帽运算获取同背景色类似的区域,识别后填充

cv::Mathat;

cv::Matelement=getStructuringElement(MORPH_ELLIPSE,Size(31,31));

cv::morphologyEx(tmask,hat,MORPH_BLACKHAT,element);

hat.setTo(255,hat

cv::Mathatd;

Clear_MicroConnected_Areas(hat,hatd,450);

tmask=tmask+hatd;

mask=tmask(cv::Range(25,25+mask.rows),cv::Range(25,25+mask.cols)).clone();

//掩膜滤波,是为了边缘虚化

cv::blur(mask,mask,Size(2*input.size+1,2*input.size+1));

//改色

for(inti=0;irow;++i)

uchar*r=bgra.ptruchar

uchar*m=mask.ptruchar

for(intj=0;jcol;++j)

//蒙版为0的区域就是标准背景区

if(m[j]==0)

r[4*j]=uchar(input.color[0]);

r[4*j+1]=uchar(input.color[1]);

r[4*j+2]=uchar(input.color[2]);

r[4*j+3]=uchar(input.transparency);

//不为0且不为255的区域是轮廓区域(边缘区),需要虚化处理

elseif(m[j]!=255)

//边缘处按比例上色

intnewb=(r[4*j]*m[j]*0.3+input.color[0]*(255-m[j])*0.7)/((255-m[j])*0.7+m[j]*0.3);

intnewg=(r[4*j+1]*m[j]*0.3+input.color[1]*(255-m[j])*0.7)/((255-m[j])*0.7+m[j]*0.3);

intnewr=(r[4*j+2]*m[j]*0.3+input.color[2]*(255-m[j])*0.7)/((255-m[j])*0.7+m[j]*0.3);

intnewt=(r[4*j+3]*m[j]*0.3+input.transparency*(255-m[j])*0.7)/((255-m[j])*0.7+m[j]*0.3);

newb=max(0,min(255,newb));

newg=max(0,min(255,newg));

newr=max(0,min(255,newr));

newt=max(0,min(255,newt));

r[4*j]=newb;

r[4*j+1]=newg;

r[4*j+2]=newr;

r[4*j+3]=newt;

returnbgra;

voidClear_MicroConnected_Areas(cv::Matsrc,cv::Matdst,doublemin_area)

//备份复制

dst=src.clone();

std::vectorstd::vectorcv::Pointcontours;//创建轮廓容器

std::vectorcv::Vec4ihierarchy;

//寻找轮廓的函数

//第四个参数CV_RETR_EXTERNAL,表示寻找最外围轮廓

//第五个参数CV_CHAIN_APPROX_NONE,表示保存物体边界上所有连续的轮廓点到contours向量内

cv::findContours(src,contours,hierarchy,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_NONE,cv::Point());

if(!contours.empty()

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论