iOS中的vImage


最近大家都在探讨iOS7的blur effect,用的最多的基本上是apple在WWDC2013中的demo。其实现思路是:高斯模糊+一层白色的mask,再调整一下饱和度。出于性能考虑,demo采用了Accelerate framework中的vImage,vImage提供了模版运算的api,可以直接通过openGL做向量运算,因此demo采用了三次均值滤波去逼近高斯滤波的效果,误差有3%。但却能大大提高计算效率。

那为什么处理图片要用要用卷积运算,其实也不是所有的图像处理都需要卷积运算,只有需要做邻域运算才会用到模版,比如图像平滑,图像锐化等。那卷积是什么概念呢?

从时域的角度讲(wiki百科上有很详细的解释),卷积运算是一种“加权平均”的方法。从频域的角度讲,卷积可以说是一种滤波。假设我们有一幅图像,高频信息很多(噪声,突变,边缘等),我们想对它进行去噪,就可以对这幅图片进行低通滤波,让只有低频信息的像素得以保留。进行低通滤波,就是在空域上将图像和某个系统做卷积运算,所谓的某个系统也叫做卷积核(kernel)。由于平面图像是二维的,像素点是离散的,因此,卷积运算是一种离散二维卷积运算:

将这个式子展开就是所谓的模版运算。

Apple的vImage,最大的优势就是提供卷积运算和向量运算的api,对于滤波这种图像处理算法在计算性能上能得到大大的提升,但是它也有局限性,只能做一些简单的线性滤波,复杂一点的的非线性滤波(如中值滤波),vImage就不支持了(或者是我没找对用法),因此,效果好一些的滤镜vImage也做不了,下面我们用vImage的api做两个简单的模版运算:

(1)均值滤波

(2)拉普拉斯锐化

均值滤波

均值滤波,也叫邻域平均是图像平滑去噪算法中最简单的一种,它将原图中的一个像素值和它周围临近的N个像素值相加,然后求得平均值,最为新图中该点的像素值,其数学公式为:

N是kernel中包含的像素个数。

例如3x3的kernel为:

vImage对于这种权重相同的kernel提供了一个专门的api叫做vImageBoxConvolve_ARGB8888:

err = vImageBoxConvolve_ARGB8888(   effectInBuffer, 
                                    effectOutBuffer, 
                                    NULL, 
                                    0, 0, 9, 9, 
                                    bgColor, 
                                    kvImageEdgeExtend);

原图为256x256的ARGB,结果为:

第一幅图为原图,第二幅为使用5x5模版的平滑结果,第三幅为使用9x9模版的平滑结果。显然这种方式在去掉了噪声的同时,也模糊了边缘。

拉普拉斯锐化

和上面的平滑不同,锐化是消除图片中的低频分量,保留高频分量,是一种高通滤波器。从数学上讲,平滑是一种平均运算或积分运算,那么和它相反,锐化就是一种微分运算,而微分运算又可以说是梯度运算,表征了新号的变化快慢,因此可以用来检测图像的边缘信息。一般简单的锐化用梯度锐化就可以了,拉普拉斯锐化用的是拉普拉斯算子:

拉普拉斯算子是一种二阶微分算子,我们通常用它过零点来确定像素的图片点,其离散形式为:

由于拉普拉斯算子的模版权重不同,因此需要使用vImage的api:vImageConvolve_ARGB8888,需要显示提供模版:

int16_t kernel[9] = {1,1,1,1,-8,1,1,1,1};
err = vImageConvolveWithBias_ARGB8888(  effectInBuffer,
                                        effectOutBuffer,
                                        NULL, 
                                        0, 0, kernel, 
                                        3, 3, 1, 128, 
                                        bgColor, 
                                        kvImageEdgeExtend);

Bias的意思是如果计算过程中如果有溢出(比如出现负数或者大于255),那么会自动纠正。结果如下:

可以看到通过使用高通滤波器,我们成功的检测除了图像的边缘,但是大部分低频信号也被滤除了,因此图像只保留了边缘信息