关于图像模糊算法的实现,
我相信大多数学习图像算法的朋友都很熟悉。
例如常见的毛玻璃效果,高斯模糊等等。
而图像模糊最简单的实现就是 在一定区域 对像素做平均值计算。
术语描述,卷积。
1.认识卷积
而平均值计算可以,是一种常见的卷积计算,卷积核权重都为1。
OpenCV中与之对应的算法是BoxBlur。
图像方面深度学习中最重要的两个层,一个池化层,一个卷积层,
其中池化层可以认为是一种特例的卷积层,与求平均值类似。
从网上找了一张卷积操作的示例图片。
这样看,虽然知道是在做一个点面计算的操作,但是要具体描述卷积的用途或者原理,是有点困难的。
在两年前一次公司的内部技术分享会上,我是这么定义卷积的。
卷积计算从形象上来描述,在图像领域,一般是2维,
所以可以这么说 “计算两个物体在n维空间的相似度(叠加度)的操作,就称之为(n维)卷积。”
所以图像是计算两个物体在2维空间的相似度(叠加度)的操作,就称之为卷积。
如果两个物体完全一致,卷积完全重合,重合度为1,这时可以认为它就是同一个物体。
以上描述的两个物体,在算法中一般 指的是 卷积核 和被卷积图片,卷积结果就是其两者的重合度。
看下深度学习中的池化层和均值层。
根据上面的描述,重新理解一下,这两个层的作用。
这里就不展开细讲了,大概大家能理解一下卷积这个操作即可。
而一般情况下,卷积操作,是一种信息外散或内聚的计算,
当然也有卷积,反卷积,转置卷积的说法。
举个例子,例如均值池化或者说均值卷积,均值模糊。
从计算来看,是取目标像素临近区域像素的平均值,而所有临近像素的权重都为1.
最终目标像素的值为n分之1.
那么这个计算就好像是把一个物品在2维空间直接摊平,还不能理解的再看下上面对卷积的描述。
就像你有9只蚂蚁,你希望他们长得一样大,你一巴掌下去,身体全部压平。
而在图像领域,绝大多数的模糊,就是受到一个“物体”或者说“卷积核”的干扰,
致使采集到的信息丢失,或者说不准确。
例如在按下快门的一瞬间,你手抖了,或者说失去焦点,
就会很容易造成呈像模糊。
2.图像修复
假设我们能找到导致成像模糊的原因,是不是就能实现修复模糊图像呢?
答案是肯定的。
不过取决于最终采集到的信息的完整度。
如果最后成像一片漆黑,那这个时候是无能为力的。
因为已经没有足够的信息了。
而一般图像是由色光三原色组成,红绿蓝。
理论上只要其中一种颜色信息完整,就可以用于辅助修复其他色光。
只要能计算到当时的色光分布情况即可实现。
这里我们就需要稍微提及一下摄像领域3大算法,自动白平衡,自动对焦,自动曝光。
简单讲下 这三个算法的区别。
白平衡是为了解决色光分布不均,信息缺失或失真问题的。
自动对焦是为了成像清晰,人为干扰辅助最终成像。
自动曝光当然是为了解决采光问题,弥补感光元件的不足。
大概的先后顺序,理论上应该是 对焦->曝光->白平衡
一般而言,从这逻辑来看,白平衡是最难解决的,因为它受到前面两个因素的干扰。
但是实际上,白平衡和曝光目前都解决得很好。
HDR严格意义上来说,能兼顾白平衡和曝光。
但是对焦或者说失焦,抖动的问题,一直以来都是老大难。
原因也很简单,导致成像模糊的因素有很多,通过什么样的方法来有效评估修复,就显得比较困难。
假设能知道计算到当时受到什么“物体”的干扰,通过卷积的方式,移除这干扰,是不是就能修复模糊,恢复图片呈像。
假设造成模糊的因素为: 清晰的图片 + 干扰因素 = 最终成像
那么反之恢复图像即为: 最终成像 - 干扰因素 = 清晰图片
而这里把干扰因素和清晰的图片都认为是一种卷积“物体”。
3.去模糊
有了前面的基础知识,我们可以做一个假设,进行验证。
例如,我先对一张图片做卷积操作,模拟模糊成像。
这个时候: 你同时有了 清晰的图片 + 干扰因素 = 最终成像
验证一下: 最终成像 - 干扰因素 = 清晰图片
这里不展开算法实现,
但是结果我可以告诉大家,这思路没错,但是有一个干扰因素错了。
它就是卷积核的大小。
卷积核大小直接决定它的作用范围。
而我们第一步拿到的 清晰的图片 + 干扰因素 = 最终成像
这里 最终成像-干扰因素 却绝对不等于清晰的图片。
因为 这里的干扰因素和最终成像 两者之间并不是简单的线性关系,相加或者相减,
他们的关系是卷积关系,也就是说他们都受到卷积核的影响。
如果想要达到 最终成像 - 干扰因素 = 清晰图片
必须 在最终成像的时候用类似卷积的方式,消除卷积核的内容以及范围的影响。
换句话说,这个操作必须控制好变量。
这个问题说真的,我思考了很久很久。
最后终于知道问题在哪里,就是图像的明度,也就是最终成像。
也就是最终成像中是含有卷积核权重信息的,如果要消除这个信息,
必须将成像也作为权重参与计算。
也就是说,造成模糊的情况应该是:
if( sqrt(nx-x) + sqrt(ny-y) < sqrt(radius) ) { w = luminance(rgb) sum += w*s; wsum += w; } sum /= wsum;