OpenCV实现图像背景虚化效果原理详解
作者:Mr.Winter` 发布时间:2021-12-06 02:25:59
0 写在前面
相信用过相机的同学都知道虚化特效,这是一种使焦点聚集在拍摄主题上,让背景变得朦胧的效果,例如本文最后实现的背景虚化效果
相机虚化特效背后的原理是什么?和计算机视觉有什么关系?本文带你研究这些问题。
1 小孔成像
小学我们就知道,没有光就不存在图像,为了产生图像,场景必须有一个或多个、直接或间接的光源。
如图所示,光照主要分为三类:
散射
直接光照
漫反射
在获得光源后,将产生从物体到检测平面的光线。
由于从物体上某点A出发存在无数条四散的光线到达检测平面,因此可以认为A的成像点A’均匀地分布在成像平面上,同理其他点亦然。所以这种情况下,检测平面上是无数张物体图像的混叠,导致成像模糊甚至无法成像。
面对一张白纸上看不到你的脸,不是因为白纸上没有来自于你的光线,而是因为来自于你不同部分的光线在白纸上产生了重叠,不信你试试?
那如何在白纸上成像?
其实非常简单,采用小学就尝试过的小孔成像
本质上小孔相当于一个滤光器,仅保留从物点发出的少数光线,此时应能获得清晰的图像。
2 光学成像
小孔成像的缺陷是成像光线较少,亮度低。为了既能获得较多光线,又不让像点四散在检测面上造成影像重叠,引入具有聚光性的透镜。透镜成像与小孔成像的本质都是避免因像点四散导致的无法成像,前者利用聚光,后者则利用滤光。
现代相机在应用上通常使用透镜成像,但不管是透镜成像还是小孔成像,都是计算机视觉研究的基本模型和假设,例如透视几何、相机内参矩阵、畸变修正等等,因此本节对于建立机器视觉的研究思维很有帮助。
3 虚化效果
介绍完前面的基础知识,终于开始图像虚化特效的原理啦!
理想透镜应保证光线聚焦于一点——焦点,这个点不会产生任何成像混叠,图像最清晰。在焦点前后光线开始四散,形成不同程度的成像重叠区域,称为弥散圆,对于人眼而言,在一定范围内影象产生的模糊是不能辨认的,不能辨认的弥散圆范围称为容许弥散圆
当对被摄主体平面调焦时,因为容许弥散圆的存在,在一定离焦范围内,成像仍然清晰,这个范围称为焦深。调整成像面和镜头距离,使成像面处于焦深内,物体可以清晰成像的过程,称为对焦。
类似地,对被摄物体而言,位于调焦物平面前后的能相对清晰成像的景物间纵深距离称为景深。图像虚化效果就和这个景深有关系!
景深越小,被摄物体前后能清晰成像的范围越小,也就相应地出现朦胧虚化的效果
景深越大,被摄物体前后能清晰成像的范围越大,也就没有虚化效果
如何调节景深?记住一句话:光圈越大景深越小,所以拿手机拍照的时候,大光圈也就代表了虚化效果!
所以下次有机会给女生拍照的话,请先确认
“请问你喜欢小景深还是大景深?”
4 代码实战
相机背景虚化特效在图像处理中可以采用引导滤波器实现,源码如下。
//引导滤波器
Mat guidedFilter(Mat& srcMat, Mat& guidedMat, int radius, double eps)
{
srcMat.convertTo(srcMat, CV_64FC1);
guidedMat.convertTo(guidedMat, CV_64FC1);
// 计算均值
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_Ip
boxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius)); // 生成引导图像自相关均值mean_II
// 计算相关系数、Ip的协方差cov和I的方差var------------------
Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);
Mat var_I = mean_II - mean_I.mul(mean_I);
// 计算参数系数a、b
Mat a = cov_Ip / (var_I + eps);
Mat b = mean_p - a.mul(mean_I);
// 计算系数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));
// 生成输出矩阵
Mat dstImage = mean_a.mul(srcMat) + mean_b;
return dstImage;
}
关于引导滤波器的相关原理我们下次再开新的章节阐述。
主函数内调用滤波器即可,效果如文首所示。
int main()
{
Mat resultMat;
Mat vSrcImage[3], vResultImage[3];
Mat vResultImage[3];
Mat srcImage = imread("1.jpg");
imshow("源图像", srcImage);
// 对源图像进行通道分离,并对每个分通道进行引导滤波
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();
Mat resultImage = guidedFilter(tempImage, cloneImage, 5, 0.3);
vResultImage[i] = resultImage;
}
// 将分通道导向滤波后结果合并
merge(vResultImage, 3, resultMat);
imshow("背景虚化特效", resultMat);
waitKey(0);
return 0;
}
一个小小的图像虚化特效,背后牵扯出光学成像的各种原理,构建了计算机视觉模型的地基。正如我们每个人一样,也许你觉得自己很渺小,说不定也是别人的中流砥柱呢!
来源:https://blog.csdn.net/FRIGIDWINTER/article/details/123431702


猜你喜欢
- 使用SwingWorker线程模式谨慎地使用并发机制对Swing开发人员来说非常重要。一个好的Swing程序使用并发机制来创建不会失去响应的
- for循环语句重复执行语句,直到条件变为 false。语法for ( init-expression ; cond-expression ;
- 前言最近搭建的springbootboot的网关,配置请求路径,竟然没有生效现象配置文件如下:启动类,控制台打印的结果如下:我随便更换端口都
- 桥接模式桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)
- 本文实例为大家分享了Unity3D实现物体旋转缩放移动的具体代码,供大家参考,具体内容如下由于项目运行在安卓上,运用到了插件,比较麻烦。你们
- public class MyGestureLintener extends SimpleOnGestureListener {privat
- 目录1、java有8种基本类型,请问byte、int、long、char、float、double、boolean各占多少个字节?2、在 A
- 使用Zenject和UniRx的入门级技术实现了伪登录注册功能。运行效果登录面板using System;using UniRx;using
- 目录1、Java Application 源程序的主类是指包含有( )方法的类。2、如果定义一种表达式结构:(+ 6 3)的值为9,(- 6
- 本文实例讲述了android动态布局之动态加入TextView和ListView的方法。分享给大家供大家参考。具体实现方法如下:packag
- 流行的应用的导航一般分为两种,一种是底部导航,一种是侧边栏。说明IDE:AS,Android studio;模拟器:genymotion;实
- 比如在窗体中显示时间:错误思路一:我在窗体结构函数中写入一个死循环,每隔一秒显示一次当前时间public Form6() &n
- 一、导入JAR包二、配置applicationContext.xml的spring核心配置三、 public static void mai
- 本文实例讲述了C#使用IComparer自定义List类实现排序的方法。分享给大家供大家参考。具体如下:List类中不带参数的Sort函数可
- 作者: juky_huang 事件的简单解释: 事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如
- 栈和队列:都是线性表,都是基于List基础上的实现线性表:数组,链表,字符串,栈,队列元素按照一条“直线&rdq
- 基于 springboot+vue的测试平台开发一、前端环境搭建在前端框架vue-element-admin这个项目中,有一个简洁轻量型的项
- springBoot yml文件的list读取问题折腾了很久,记录下。配置如下# 自定义数据上报信息xx: # 机组信息 &nb
- Java for循环打印菱形Java代码输出菱形的方法和思路有很多,在此分享一个稍带模块化拆分思想的解决方案,将需要输出的菱形拆分成8个模块
- Statement 和 PreparedStatement之间的关系和区别. 关系:Prepa