[db:标题]
摘要:GIMP的小波分解只是偏重于实现,但是其效率还是很慢,本文简单的提出了其加速算法。同时对于如何使用小波分解后的数据,通过小波去噪和小波锐化两个过程予以了说明,另外,基于小波去噪的这些过程也可以使用拉普拉斯金字塔来实现。
上一篇文章谈及了GIMP里实现的小波分解,但是这仅仅是把图像分解为多层的数据,如果快速的获取分解数据以及后续怎么利用这些数据,则是本文的重点。
一、我们先来看看算法速度的优化问题。
原始的GIMP实现需要将图像数据转换为浮点数后,然后进行各级的模糊和图层混合,这样得到的结果是比较精确的,但是存在两个方面的问题,一个是占用了较多的内存,因为GIMP这个版本的小波分解各层是没有改变数据的尺寸的,因此,如果使用浮点,占用的内存要比字节版本的大四倍,而且和层数有着密切的关系。第二个是浮点的处理还是稍微慢了点,虽然对现在的CPU来说,浮点数更易用SIMD指令集优化。但是如果有更好的数据类型的话,使用SIMD可以获得更高的计算速度。
我们知道,字节版本的模糊可以获得很高的计算效率,这个场景的模糊是否可以使用呢,我个人分析认为,这个版本的算法已经不适合用字节版本的模糊了,原因主要是精度太低了,因为他每次的结果都和上一次的模糊相关,而我们通过GIMP的可是化界面可以看到,中间的每一个层很多像素都是靠近127的,这就说明细节方面的信息都是很小的数值。
个人觉得,对于这个算法,我们可以把数据放大到unsigned short范围,比如把原始的像素值都扩大256倍,然后进行模糊,这样模糊的精度就会大幅的提高,比如原始的9的像素的加权累加值(归一化后的权重)如果是100.3,那么由于字节版本取整的原因,最后的返回值为100,放大256倍后,则累加值变为25676.8,四舍五入取整后则为25677,而非100放大256倍的25600了,这样多次模糊的精度就可以加以充分的保证(和浮点数比较还是有差异,但不影响结果)。
我们在稍微观察下上一篇文章我们所提到的3*3的权重:
如果我们每个系数都放大16倍,我们会得到非常优化的一组权重系数:
可以看到,权重系数里全是1、2、4,这种系数如果被乘数是整形的话,都可以直接用移位实现,而放大16倍最后的除法,也可以直接由右移实现,那这种计算过程就完全避免了乘法,效率可想而知可以得到极大的提高。
我们在考虑一种特别的优化,因为权重整形化后的累加值是16,那么如果每个元素的最大值不超过4096,则累计值也就不会超过65536,这个时候如果是用普通C语言实现,其实没有啥区别,但是我们知道SIMD指令确有所不同,他针对不同的数据类型有不同的乘法和加法指令,其所能计算的覆盖范围也不同,如果能用16位表达,则尽量用16位表达。因此,考虑图像数据的特殊性,如果我们只把他们的数据范围扩大16倍,则不会超出4096的范围,就可以满足前面的这个假设。
放大16倍是否能满足精度的需求呢,没有具体理论的分析啊,个人觉得啊,至少在层数不大于4层时,差异不会很大,大于4层,也应该可以接受,留待实践检测吧。
对于上一篇文章所说到的取样点位置的问题,我们必须考虑边缘位置处的信息,因为随着取样范围的扩大,会有更多的取样点超出图像的有效范围,这个时候一个简单的办法就是实现准备好一副扩展过边界的图像,这里的扩展方法通道都选择边缘镜像,而非重复边缘。考虑到层数不会太多,扩展部分的边缘计算量和占用内存也不会太夸张。
同时,我们在考虑到算法的特殊性,虽然取样范围很广,但是真正用到的取样点也只有9个,所以我们也可以使用类似于我在SSE图像算法优化系列九:灵活运用SIMD指令16倍提升Sobel边缘检测的速度(4000*3000的24位图像时间由480ms降低到30ms)一文中使用到的技术,分配三行临时的内存,每次计算前填充好三行的数据,不过注意一点,由于取样的三行的内存并不是在行方向上连续的,因此,也不能像那个文章那样,可以重复利用两行的内存了,而必须每次都填充。
理论上说,这种填充技术要比前面的分配一整片的内存的内存填充工作量大3倍左右,速度应该要慢一些,优点是占用的中间内存少一些,但是实测,还是这种的速度快一些,或许是因为三行内存的大小有效,访问时的cache miss要好很多吧。
