普通相机也能实现超级夜景?一种普适的图像防抖算法

手机超长曝光夜景是如何实现的?通过加速计,手机可以获得抖动的信息进行补偿;光学防抖则能够进一步消除微小抖动的影响。而超长曝光(多帧合成)还需要选取特征点进行画面对齐和矫正。
普通相机也能实现超级夜景?一种普适的图像防抖算法_第1张图片

手机特征点

运动相机搭载上述部分技术,而单反却并不在意。相比于手持的手机和剧烈抖动的运动相机,单反通常定点使用;且由于单反更大的传感器面积,更短的时间它就可以超过手机传感器的进光量,缩短曝光时间,减小抖动程度。
普通相机也能实现超级夜景?一种普适的图像防抖算法_第2张图片

CC9Pro(S5KHMX)对比中画幅传感器大小

本文尝试介绍一种能够在极端光线下用普通的成像设备进行高质量的手持拍摄的算法。
没有了三脚架的辅助,我们无法进行实际的长曝光,但是我们可以通过在电脑上用软件进行多帧合成,把图像恢复出来。没有加速计,没有专用于图像处理的ISP,一样可以实现“超级夜景”。
假设我们通过连拍,获取了多张短曝光的照片用于多帧合成,首先需要对齐这些照片。如何对齐?我们首先想到的,是直接比较每张照片。假设我们有照片A1-A5,分辨率是4000x3000,在A1上选取中间一部分(例如3000x2000)记为W1窗口,然后对A2从左上角开始,一个一个像素平移扫描,比较W2窗口和W1窗口。如果两者每个像素RGB值都一致,自然就对齐了。
普通相机也能实现超级夜景?一种普适的图像防抖算法_第3张图片
显然,这种算法需要巨大的计算量。更何况由于噪点等等原因,不存在每一个像素点都一致的情况,排除了“遇到两个像素不同就跳过这一循环”的优化,只能对W1W2的差值进行累计,然后找出最小的一个情况。这样一来,计算的次数大约在10^12这一级别,即使是2020年的家用计算机也需要几分钟的时间。
那我们如何优化呢?沿用原来的思路,我们需要一个更加合理的方式计算两个窗口的“重合度”。柯西不等式让我们知道,在正数范围内,当a+b为一个常数,a=b时,a×b能得到最大值。同样的原理,我们可以得出计算“重合度”的公式:
∑ m = 1 M ∑ n = 1 N S i j ( m , n ) ⋅ T ( m , n ) ∑ m = 1 M ∑ n = 1 N S i j ( m , n ) 2 ⋅ ∑ m = 1 M ∑ n = 1 N T ( m , n ) 2 \begin{aligned} \frac{\sum\limits_{m=1}^{M}\sum\limits_{n=1}^{N}S_{ij}(m,n)\cdot T(m,n)}{\sqrt{\sum\limits_{m=1}^{M}\sum\limits_{n=1}^{N}S_{ij}(m,n)^2}\cdot \sqrt{\sum\limits_{m=1}^{M}\sum\limits_{n=1}^{N}T(m,n)^2}} \end{aligned} m=1Mn=1NSij(m,n)2 m=1Mn=1NT(m,n)2 m=1Mn=1NSij(m,n)T(m,n)
当算出的重合度值最大时,这个点就可以用来使两张图片对齐。这就是归一化互相关算法。这个算法使用了大量的乘法和开根运算,我们可以很轻松地简化它。

为了方便说明,简化到一维。此时,x轴就是像素的坐标,y轴就是像素的RGB值。我们把由一个个像素组成的数据用多项式拟合成一个曲线。
普通相机也能实现超级夜景?一种普适的图像防抖算法_第4张图片

离散数据

普通相机也能实现超级夜景?一种普适的图像防抖算法_第5张图片

连续数据

而之前求重合度的过程,也可以简化成下面的公式:
∫ f ( x ) g ( x + Δ x ) d x \begin{aligned} \int f(x)g(x+\Delta x)\text{d}x \end{aligned} f(x)g(x+Δx)dx
其中f, g分别是两张图片拟合的多项式函数,Δx是两张图片的偏移量。
也就是说,它需要快速地求多项式的乘法。快速傅里叶变换(FFT)就是这样一种算法。
我们知道,y=x2这个式子,可以是y=x2+0x+0,也可以是“一个过点(-1,1),(0,0),(1,1)的多项式”。我们还知道复数是来自负数的平方根运算,所以显然多项式对于复数域都是成立的。一个典型的复数w=a+bi,我们令w的n次方值为1,则w会n等分复数单位圆。
普通相机也能实现超级夜景?一种普适的图像防抖算法_第6张图片

单位圆

而通过这张图,可以得到折半引理:
ω n k + n 2 = − ω n k ⇒ ( ω n k + n 2 ) 2 = ( ω n k ) 2 ⇒ ( ω n k + n 2 ) 2 = ω n 2 k = ω n 2 k \begin{aligned} \omega_n^{k+\frac{n}{2}}&=-\omega_n^k\\ \Rightarrow\left(\omega_n^{k+\frac{n}{2}}\right)^2&=\left(\omega_n^k\right)^2\\ \Rightarrow\left(\omega_n^{k+\frac{n}{2}}\right)^2&=\omega_n^{2k}=\omega_{\frac{n}{2}}^k \end{aligned} ωnk+2n(ωnk+2n)2(ωnk+2n)2=ωnk=(ωnk)2=ωn2k=ω2nk
也就是说,每代换一次,计算量就减半。
A ( x ) = a 1 x 4 + a 2 x 3 + a 3 x 2 + a 4 x + a 5 = A 1 ( x ) + A 2 ( x ) = ( a 1 x 4 + a 3 x 2 + a 5 ) + ( a 2 x 3 + a 4 x ) \begin{aligned} A(x)&=a_1x^4+a_2x^3+a_3x^2+a_4x+a_5\\ &=A_1(x)+A_2(x)\\ &=(a_1x^4+a_3x^2+a_5)+(a_2x^3+a_4x) \end{aligned} A(x)=a1x4+a2x3+a3x2+a4x+a5=A1(x)+A2(x)=(a1x4+a3x2+a5)+(a2x3+a4x)
由上式的例子可以看到,A化为了A1A2。这样我们就可以用更短的时间计算的两个多项式的乘积,再把点表示法的多项式恢复成系数表示法进行计算。定义在平面上的函数也是如此。如此一来,原本时间复杂度在n3甚至n4级别的计算量,被优化到了n·log2(n),具体到计算次数,就是把长宽像素的数值代入n,比如宽度2000,原本的计算量是10亿次数量级以上,现在则仅需要不超过百万次,对于普通智能手机来说也是一秒内的计算量。

最后多说两句。基于FFT的图像匹配算法不仅仅可以用于“超长曝光”的防抖。实际上,双目视觉、激光粒子流场测速等等很多需要图像匹配的场景都会用到这种算法。有些场景连肉眼都难以分辨。(下图是激光反射的斑纹的放大图,请调低亮度)
普通相机也能实现超级夜景?一种普适的图像防抖算法_第7张图片

觉得有用的话,不要吝惜评论点赞分享哦,希望大家多多包涵,有任何问题欢迎指正、讨论。
本文基于CC-BY-SA 4.0协议,欢迎转载
(博客看累了?去我的B站瞧一瞧?)

你可能感兴趣的:(技术杂谈,算法,计算机视觉)