小案例:使用OpenCV实现证件照背景替换
一、概述
案例:使用kmeans算法实现证件照背景替换
算法实现步骤:
1.加载原图像
2.制作kmeans输入参数所需要的数据(kmeans的输入数据类型是CV_32F,所以不能直接使用原始图像的数据,因为原始图像的数据类型为CV_8UC1)
3.使用kmeans算法实现图像分类,并得到分类标签
4.创建遮罩:通过分类标签,将背景部分的颜色标记位0,将前景(人物)像素值标记位255
5.先对mask执行形态学操作去除干扰的白点,在使用高斯模糊平滑前景和背景之前的过度
6.创建一个3通道的目标输出结果Mat,然后将目标背景填充到背景区域,将前景部分填充到前景区域。
7.输出图像
ps:算法的核心步骤其实就是找到mask,当mask找到之后就可以使用分类标签将背景和前景替换成为自己想要的像素。
二、代码示例
Id_Photo_Background_Replacement::Id_Photo_Background_Replacement(QWidget *parent) : MyGraphicsView{parent} { this->setWindowTitle("证件照背景替换"); } void Id_Photo_Background_Replacement::dropEvent(QDropEvent *event){ QString filePath = event->mimeData()->urls().at(0).toLocalFile(); showIdPhotoBackgroundReplacement(filePath.toStdString().c_str()); } void Id_Photo_Background_Replacement::showIdPhotoBackgroundReplacement(const char* filePath){ Mat src = imread(filePath); if(src.empty()){ qDebug()<<"图像为空"; return; } imshow("src",src); //制作kmeans需要的数据 int width = src.cols; int height = src.rows; int dims = src.channels(); int sampleCount = width*height;//总共的像素点 Mat points(sampleCount,dims,CV_32F,Scalar(10)); int index = 0; for(int row = 0;row<height;row++){ for(int col = 0;col<width;col++){ index = row*width +col; Vec3b bgr = src.at<Vec3b>(row,col); points.at<float>(index,0) = static_cast<int>(bgr[0]); points.at<float>(index,1) = static_cast<int>(bgr[1]); points.at<float>(index,2) = static_cast<int>(bgr[2]); } } int numCluster = 4;//多少个分类 Mat labels;//分类标签 Mat centers;//中心点 TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1); kmeans(points,numCluster,labels,criteria,3,KMEANS_PP_CENTERS,centers); //创建遮罩 Mat mask = Mat::zeros(src.size(),CV_8UC1); index = src.rows*2+2;//找到背景像素的像素点位置 int cIndex = labels.at<int>(index,0);//找到像素点位置在labels中所对应的标签,找到这个标签以后就可以根据这个标签来判断前景和背景 for(int row=0;row<height;row++){ for(int col=0;col<width;col++){ index = row*width+col; int label = labels.at<int>(index,0); if(label==cIndex){//背景 mask.at<uchar>(row,col) = 0; }else{//前景 mask.at<uchar>(row,col) = 255; } } } imshow("mask",mask); //使用形态学腐蚀操作取出遮罩中的可能干扰正常结果的白点 Mat kernel = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1)); erode(mask,mask,kernel); //使用高斯模糊平滑边缘像素 GaussianBlur(mask,mask,Size(3,3),0,0); //执行图像像素融合,执行最终的背景替换 //定义背景颜色 Vec3b bgColor; bgColor[0] = 217;//rng.uniform(0, 255); bgColor[1] = 60;// rng.uniform(0, 255); bgColor[2] = 160; Mat result = Mat::zeros(src.size(),CV_8UC3);//定义一个空的彩色图片 //下面是背景融合的代码 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 row = 0;row<height;row++){ for(int col=0;col<width;col++){ int pix = mask.at<uchar>(row,col);//获取像素值 if(pix==255){//前景 result.at<Vec3b>(row,col) = src.at<Vec3b>(row,col); }else if(pix==0){//背景 result.at<Vec3b>(row,col) = bgColor; }else{//需要像素融合的部分 w = pix/255.0;//权重 b1 = src.at<Vec3b>(row,col)[0]; g1 = src.at<Vec3b>(row,col)[1]; r1 = src.at<Vec3b>(row,col)[2]; b2 = bgColor[0]; g2 = bgColor[1]; r2 = bgColor[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>(row,col)[0] = b; result.at<Vec3b>(row,col)[1] = g; result.at<Vec3b>(row,col)[2] = r; } } } imshow("result",result); }
三、图片示例演示
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2014-04-16 Android 其他特效展示
2014-04-16 特效展示网站
2014-04-16 Android 新加几个开源项目