由于中篇最后得到的图像还是需要手动去磨皮,边缘突出的部分还是没找好。


这里我再想办法处理一下:
现在我们已经得到了这样的一张掩模:

边缘找的不是很好
那么我们可以结合找边缘的方法对它进行处理。

第一步:
找边缘的方法常见的主要有三种:
1.1 Sobel

代码如下:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>using namespace std;
using namespace cv;  int main()
{Mat src, src_gray;Mat grad;const char* window_name = "Sobel Demo - Simple Edge Detector";int scale = 1;int delta = 0;int ddepth = CV_16S;/// Load an imagesrc = imread( "C:\\Users\\ltc\\Desktop\\data3\\mission2\\taowamirror.jpg");if( src.empty() ){ cout<<"where is your picture"<<endl;return -1; }GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );/// Convert it to graycvtColor( src, src_gray, COLOR_RGB2GRAY );/// Create windownamedWindow( window_name, WINDOW_AUTOSIZE );/// Generate grad_x and grad_yMat grad_x, grad_y;Mat abs_grad_x, abs_grad_y;/// Gradient X//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );convertScaleAbs( grad_x, abs_grad_x );/// Gradient Y//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );convertScaleAbs( grad_y, abs_grad_y );/// Total Gradient (approximate)addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );imshow( window_name, grad );imwrite( "C:\\Users\\ltc\\Desktop\\data3\\mission2\\taowasobel.jpg",grad);waitKey(0);return 0;
}

1.2 Laplacian

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>using namespace std;
using namespace cv;  int main()
{Mat src, src_gray, dst;int kernel_size = 3;int scale = 1;int delta = 0;int ddepth = CV_16S;const char* window_name = "Laplace Demo";// 读图src = imread( "C:\\Users\\ltc\\Desktop\\data3\\mission2\\taowamirror.jpg");if( src.empty() ){ cout<<"where is your picture"<<endl;return -1; }// 高斯滤波GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );// 灰度图cvtColor( src, src_gray, COLOR_RGB2GRAY );// 窗体namedWindow( window_name, WINDOW_AUTOSIZE );// Laplace 函数Mat abs_dst;Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );convertScaleAbs( dst, abs_dst );// 显示imshow( window_name, abs_dst );waitKey(0);
}

1.3Canny

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>using namespace std;
using namespace cv;  /// Global variablesMat src, src_gray;
Mat dst, detected_edges;int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
const char* window_name = "Edge Map";/*** @function CannyThreshold* @brief Trackbar callback - Canny thresholds input with a ratio 1:3*/
static void CannyThreshold(int, void*)
{/// Reduce noise with a kernel 3x3blur( src_gray, detected_edges, Size(3,3) );/// Canny detectorCanny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );/// Using Canny's output as a mask, we display our resultdst = Scalar::all(0);src.copyTo( dst, detected_edges);imshow( window_name, detected_edges );
}int main()
{/// Load an imagesrc = imread( "C:\\Users\\ltc\\Desktop\\data3\\taowamirror.jpg" );if( src.empty() ){ cout<<"where is your picture"<<endl;return -1; }/// Create a matrix of the same type and size as src (for dst)dst.create( src.size(), src.type() );/// Convert the image to grayscalecvtColor( src, src_gray, COLOR_BGR2GRAY );/// Create a windownamedWindow( window_name, WINDOW_NORMAL );/// Create a Trackbar for user to enter thresholdcreateTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );/// Show the imageCannyThreshold(0, 0);/// Wait until user exit program by pressing a keywaitKey(0);return 0;
}

这里我用的Sobel算子,得到的边缘图像如下:

然后进行二值化,二值化的滑动条程序在中篇已经给出
我们得到的图片如下:

第二步:
这里我们可以进行一些膨胀腐蚀的操作,这部分代码可以看我之前的博客:点我开始跳转

代码如下:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
//#include <Windows.h>using namespace std;
using namespace cv;Mat g_srcImage, g_dstImage;   //原始图和效果图
int g_nTrackbarNumer = 0;  //0表示腐蚀erode, 1表示膨胀dilate
int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
int iteratorNumber = 3;         //迭代次数void Process();  //膨胀和腐蚀的处理函数
void on_TrackbarNumChange( int, void *); //回调函数
void on_ElementSizeChange( int, void *); //回调函数
void on_IteratorNumChange( int, void *); //回调函数char *SourceImage = "【原始图】";
char *DstImage = "【效果图】";int main()
{system("color 5E");g_srcImage = imread("source.jpg");if(g_srcImage.empty()) { cout<< "Where is your picture"<<endl;}namedWindow(SourceImage);imshow(SourceImage,g_srcImage);Mat element = getStructuringElement(MORPH_RECT, Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1));erode( g_srcImage,g_dstImage,element);imshow(DstImage,g_dstImage);createTrackbar("腐蚀/膨胀",DstImage,&g_nTrackbarNumer,1,on_TrackbarNumChange);createTrackbar("内核尺寸",DstImage,&g_nStructElementSize, 21, on_ElementSizeChange);createTrackbar("迭代次数",DstImage,&iteratorNumber, 10, on_IteratorNumChange);cout<<endl<<"\t嗯。运行成功,请调整滚动条观察图像效果~\n\n"<<"\t按下“q”键时,程序退出~!\n"<<"\n\n\t\t\t\tby浅墨";while(char(waitKey(1))!= 'q'){}return 0;
}void Process()
{//获取自定义核Mat element = getStructuringElement(MORPH_RECT, Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1));//进行腐蚀或膨胀操作if(g_nTrackbarNumer == 0){erode( g_srcImage, g_dstImage, element,Point( g_nStructElementSize,g_nStructElementSize ),iteratorNumber);}else{dilate(g_srcImage,g_dstImage, element,Point( g_nStructElementSize,g_nStructElementSize ),iteratorNumber);}//显示效果图imshow(DstImage,g_dstImage);
}void on_TrackbarNumChange(int, void *)
{Process();
}void on_ElementSizeChange(int, void *)
{Process();
}void on_IteratorNumChange( int, void *)
{Process();
}

我腐蚀得到的结果如下:

然后我用上面的掩模减去这个腐蚀的图片得到:


这样你会发现边缘多余部分的掩模基本上都会被孤立出来,然后我们在调用之间二值图像删除多余小图形以及填充孔洞的代码,见中篇
这里就不贴了

我们可以把面积设的稍微大一些,然后得到:

再讲这张图像与上面腐蚀的图做与运算就可以把边缘补上,我忘了保存这一步图片,然后在进行一个膨胀的操作,(注意:这里膨胀的尺度要很小,我们不能把边缘整体膨胀了)只是为了把一些区域缝合起来。
得到的效果图如下:

有了这样一张图片然后又是你懂得,再次填充孔洞
就得到了下面的掩模

是不是觉得看着还不错
之前最前面小娃娃的掩模可以很好二值化前景图像得出来。然后做个减法就得到了中间三个娃娃的掩模。
分别如下:

以及

中间这个目前还没想到很好的办法将它分开,于是他们的模糊是一起加的,我也尝试着手动强行将他们分开(哈哈)

第三步
最后我们在分开进行与图像相与得到不同的图层,然后进行高斯模糊,这里不同图层高斯模糊的核半径我是根据他们深度的比例的平方关系进行模糊,这样得出的效果较为真实。

这里说一个加模糊的小技巧,我之前每次分开模糊然后相加,得到的图像是这样。

边缘有一圈黑的,很难受,最初我以为是边缘不对,对掩模膨胀了一下发现最后还是有这种现象。
最后我不加上背景,效果如下:

感觉边缘还不错,怎么最后会是那样,肯定是背景的原因。
果然,我们对背景进行高斯模糊,边上的黑色像素会进行一个串扰,导致最后会有一圈黑边。

可以很明显的看到边缘黑色像素对最后的干扰。
最初我把边缘提取出来,想对它进行一个模糊,效果不好。

因为这个边缘其实有很大的锯齿,并不平滑,导致最后合成的图像非常难看。

后面我想到了一个办法,我将得到的掩模与它进行相与,不就可以消除一些黑色像素串扰吗?

并且这里我们发现还是有一部分黑色像素的串扰,这里我们可以将后面两个图层单独的模糊调小一点,然后在整体进行一个小模糊,就会得到相对还不错的效果。

这里的边上一点点我们可以很好的用引导滤波进行一个修复。

附录:引导滤波
(关于引导滤波网上很多讲解,文章本身也不难看懂,但是却带来了惊人的效果)
何凯明博士的主页

这部分代码如下:

/*功能:图像引导滤波操作
*/
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;//-------------------【全局函数声明部分】--------------------------------------
Mat guidedFilter(Mat &srcMat, Mat &guidedMat, int radius, double eps);//引导滤波器int main()
{//------------【0】定义相关变量-------------Mat resultMat;  //最后结果图像vector<Mat> vSrcImage, vResultImage;//------------【1】读取源图像并检查图像是否读取成功------------     Mat srcImage = imread("C:\\Users\\ltc\\Desktop\\data3\\中值滤波版本\\高斯模糊深度二次方1\\中间结果以及效果\\resultmid.jpg");if (!srcImage.data){cout << "读取图片错误,请重新输入正确路径!\n";system("pause");return -1;}namedWindow("【源图像】",WINDOW_NORMAL);imshow("【源图像】", srcImage);//-------【2】对源图像进行通道分离,并对每个分通道进行导向滤波操------split(srcImage, vSrcImage);for (int i = 0; i < 3; i++){Mat tempImage;vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0);//将分通道转换成浮点型数据Mat cloneImage = tempImage.clone(); //将tempImage复制一份到cloneImageMat resultImage = guidedFilter(tempImage, cloneImage, 5, 0.01);//对分通道分别进行导向滤波,半径为1、3、5...等奇数vResultImage.push_back(resultImage);//将分通道导向滤波后的结果存放到vResultImage中}//----------【3】将分通道导向滤波后结果合并-----------------------merge(vResultImage, resultMat);namedWindow("【引导滤波/导向滤波】",WINDOW_NORMAL);imshow("【引导滤波/导向滤波】", resultMat);imwrite("C:\\Users\\ltc\\Desktop\\data3\\中值滤波版本\\高斯模糊深度二次方1\\中间结果以及效果\\resultmidgd.jpg",resultMat*255);waitKey(0);return 0;
}//导向滤波器
Mat guidedFilter(Mat &srcMat, Mat &guidedMat, int radius, double eps)
{//------------【0】转换源图像信息,将输入扩展为64位浮点型,以便以后做乘法------------srcMat.convertTo(srcMat, CV_64FC1);guidedMat.convertTo(guidedMat, CV_64FC1);//--------------【1】各种均值计算----------------------------------Mat mean_p, mean_I, mean_Ip, mean_II;boxFilter(srcMat, mean_p, CV_64FC1, Size(radius, radius));//生成待滤波图像均值mean_p boxFilter(guidedMat, mean_I, CV_64FC1, Size(radius, radius));//生成引导图像均值mean_I   boxFilter(srcMat.mul(guidedMat), mean_Ip, CV_64FC1, Size(radius, radius));//生成互相关均值mean_IpboxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius));//生成引导图像自相关均值mean_II//--------------【2】计算相关系数,计算Ip的协方差cov和I的方差var------------------Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);Mat var_I = mean_II - mean_I.mul(mean_I);//---------------【3】计算参数系数a、b-------------------Mat a = cov_Ip / (var_I + eps);Mat b = mean_p - a.mul(mean_I);//--------------【4】计算系数a、b的均值-----------------Mat mean_a, mean_b;boxFilter(a, mean_a, CV_64FC1, Size(radius, radius));boxFilter(b, mean_b, CV_64FC1, Size(radius, radius));//---------------【5】生成输出矩阵------------------Mat dstImage = mean_a.mul(srcMat) + mean_b;return dstImage;
}

其实中间也有很多步骤我也用到了引导滤波,最后得到的效果如下:

好的,到此就结束了,代码的通用性并不强,我也没有整理出一个完整的工程,就当做一个小总结了。

好了,差不多回去睡觉了。~~~

opencv背景虚化(后篇)相关推荐

  1. OpenCV背景虚化-(中篇)

    由于之前老师一直让我用我们的到的图像深度信息进行虚化,如果深度信息得到的很准确,这的确不是一件难事,只是目前我对那一套计算体系掌握的不够. 假设我们手上有一副已经获取的深度图像,现在我的手上有两份,D ...

  2. 三步集成美图软件背景虚化景深效果【iOS篇】

    ######[前文提要] 因工作一直没有接触过滤镜领域,所以在闲暇之余阅读了下官方文档,本文尝试实现通用美图软件背景虚化景深效果,纯属娱乐,大神无视勿喷. 左侧为原图,右侧为背景虚化后的效果图 大致为 ...

  3. OpenCV在Android上实现人脸背景虚化

    1. 背景 在手机拍照技术日新月异的今天,很多手机厂商都陆续上了双摄,并衍生出人像模式(背景虚化).虽然博主不是很懂双摄虚化的原理,但是看到一些样张之后,还是被深深吸引,觉得很漂亮,很有虚幻的感觉. ...

  4. opencv在手机上实现照片背景虚化

    概述 使用androidstudio开发,在手机上实现照片背景虚化 详细 代码下载:http://www.demodashi.com/demo/10599.html 一.准备工作 1.需要下载安装an ...

  5. opencv图像处理之在手机上实现背景虚化

    1.高端数码相机都具有背景虚化功能.背景虚化就是使景深变浅,使焦点聚集在主题上.一般的相机最好的虚拟方法便是用微距拍摄,如果主景与背景相距比较远,由于光学透镜对非焦点处景物的不能清晰成像的特点,可以免 ...

  6. axure 设置背景虚化_Axure如何做背景虚化?

    回答: 虚化背景的照片能够突出主体,使人的视觉重点自然地吸引到所要表现的主体上,以增强照片的艺术感染力,在拍摄时可以通过使用大光圈.长焦距.靠近被摄主体等措施来实现虚化背景的效果.不过对消费级数码相机 ...

  7. 背景虚化_太赞了!背景虚化效果原来是这样实现的啊

    "背景虚化" "背景虚化" "背景虚化" 多少人为之倾慕的"背景虚化"效果,真的很难拍出来吗? 在洪辰的文章评论区里, ...

  8. 一文掌握MobileNetV1和MobileNetV2(基于pytorch实现的人像背景虚化)

    目录 一.概述 二.MobileNetV1原理和实现 2.1 原理 2.1.1 深度卷积 2.1.2 逐像素卷积 2.2 Pytorch实现 三.MobileNetV2原理和实现 3.1 原理 3.1 ...

  9. 人工智能AI实战100讲(二十)-先拍照后对焦,基于深度估计的景深编辑与背景虚化

    1 景深与背景虚化基础 1.1 什么是景深与虚化 动植物摄影,人像摄影中常使用浅景深来虚化背景,突出目标主体,可以大幅提升作品的艺术美感. 当被摄物体位于镜头前方(焦点的前.后)一定长度的空间内时,其 ...

最新文章

  1. Qt中多线程与界面组件的通信
  2. JavaScript中获取表单信息并添加在表格中
  3. mysql 4.0.21 下载_W2K下安装 MYSQL 4.0.21 手记
  4. 不要被泛型和向后兼容性所愚弄。 使用泛型类型
  5. typec扩展坞hdmi没反应_typec扩展坞转hdmi/vga多功能网口usb转换器苹果华为电脑matebook6元优惠券券后价26.8元...
  6. 《玩转D语言系列》二、D语言现状、基本规定和相关资源介绍
  7. 初学者应该如何有效率的系统学习Python开发
  8. linux iso作为安装源,linux下使用iso文件做为yum安装源
  9. mysql开启远程可连接
  10. html5文档加载前调用函数,html调用javascript外部文件显示函数未定义
  11. Share Your Music - HTML5 Music Web App
  12. 日本经验丨被忽视的老年玩具市场需求潜力巨大,独家解析产品创新三大方向
  13. 一个C语言的基本教程—IO篇
  14. 数字电路设计之Spice仿真
  15. 解决Call to undefined function
  16. 你掌握了数控开料机维护和安全操作的重要性了吗?
  17. 阿里云CentOS7 64位下安装MySQL5.7
  18. 给nvidia显卡增加风扇速度控制的选项
  19. iOS汇编基础(二)寄存器
  20. 如何翻译Word文档?分享几种翻译方法

热门文章

  1. 从我的游戏经历中思考互联网的本质与未来
  2. 十六进制转八进制(完整代码)
  3. 学完这9个课程,你将达到高级黑客水平
  4. matlab画图:取样后零阶保持和条形图
  5. 接口测试平台167:并发报告问题解决
  6. 为信息安全操碎了心?别着急,教你一招免费解决
  7. 郭台铭操碎了心 夏普并入富士康后怎么走
  8. 如何用Camtasia给视频添加字幕?
  9. 魅族的android m l,魅族崛起:Flyme6.0+mBack+骁龙625,这次终于完美了!
  10. 三层交换与路由器通信