前言
当我们在相机预览时,需要对视频流buffer进行旋转及缩放时,往往是将buffer转换成image再进行旋转及缩放操作,然后转回视频流buffer,这样一次图像处理的操作消耗了太多性能,如果引入opencv,又会使得项目变大变重,这种情况下,我们可以直接操作图像元数据,这样更便捷,性能相对来说也有提升。
图像数据基本属性
像素点:组成图像的基本元素,图像包含的像素越多,图像越大
色彩空间:描述像素点颜色的色彩集,相同的RGB值对应不同的色彩空间,所展示的颜色可能不一样
通道数:像素点对应色彩通道(RGB三通道,RGBA四通道)
位图信息:像素点中通道排列顺序
位深度:一个像素所用的位数
进行图像旋转及缩放暂时只需要了解以上图像属性即可
图像旋转及缩放
图像旋转及缩放的实际上就是像素点的处理,想要得到处理后图像中每个像素点的颜色值,我们需要在原图像中找到对应的像素点,经过处理后,赋值到新图像中。所以图像旋转及缩放本质上就是一个找像素点的过程。
图像旋转
以向左旋转为例,推导图像左旋转的公式
像素点映射关系(这里只列出图像中有颜色的色块):
从像素点的映射关系可以得出目标像素点坐标与原始图像坐标之间的映射公式:
另外附上右旋转,180°旋转,镜像的公式,具体推理过程可以参考上面的方法自己进行推理
右旋转:
180°旋转:
水平镜像:
垂直镜像:
图像缩放
以图像放大为例,我们将一张2*2的图像放大到4*4,如下图所示:
先计算缩放比,目标图像的坐标除以缩放比就可以得到对应的原始图像的坐标
通过公式,得出2*2映射到4*4图像的像素点映射关系(这里只列出图像中有颜色的色块):
可以发现映射到原始图像的坐标点中包含很多小数点坐标,无法具体到某个像素点取值,所以在这里就需要进行算法运算,以求出目标像素点的最佳取值。
目前图像缩放算法已经比较成熟,较为常用的有三种缩放算法:
1.最邻近插值法:
最邻近插值法通常是直接将小数点后的数值舍去,直接取整:
使用floor函数可以对float值进行取整,取整之后的对应结果:
优缺点:最邻近插值非常简单,但得到的图像质量较差,进行缩放的比例较大时会产生较为明显的锯齿。
2.双线性插值法:
双线性插值法是根据映射像素点周围4个像素点的综合求值得出来的。
假设我们的映射的像素点坐标为P0(1.3,1.7)。
这时候四周的像素点就是P1(1,1),P2(1,2),P3(2,1),P4(2,2)。
以P0到P1,P2,P3,P4的距离为权重关系,计算4个像素点的综合像素值,得到P0的像素值。
看到这张图,有木有想起初中老师经常出的一道题,求阴影部分面积!
映射像素点坐标用表示
其中i、j代表整数部分,v、u代表小数部分
了解卷积的朋友看着这个式子是不是有点眼熟,这个式子可以用卷积公式来表达:
其中函数表达式的值为:
通过卷积公式,我们可以认为为周围4个像素的卷积和。
优缺点:双线性内插值法计算量较大,缩放后图像质量相对较高,不会出现像素值不连续的的情况。
3.三次卷积法(双立方差值算法)
三次卷积法是根据映射像素点周围16个像素点的综合求值得出来的。
映射像素点坐标用表示
其中i、j代表整数部分,v、u代表小数部分
运算所涉及到的像素点如下图:
为以上16个点卷积之和
我们已经通过双线性插值推导出图像缩放的卷积公式,只是设计的像素点数量不一样,我们将16个像素点带入到卷积公式:
在双线性差之中,决定相关像素点的权重函数在16像素点的卷积中已经不适用了,这里使用Bicubic interpolation中三次卷积的权重公式:
这个公式有3个特别重要的特点:
1)
2)
3)
这里依照Cubic Hermite spline,a取值-0.5
当或时,S(x)函数取值为正
当时,S(x)函数取值为负
我们尝试几个特殊值:
当u=0,v=0时,S(x)函数在16个像素点中的权重表现:
第一行和第一列为对应行和列的权重系数,我们可以看到,只有在时系数为1,当u,v为0时,我们映射像素点就是
当u=0,v=0.5时,S(x)函数在16个像素点中的权重表现:
当u=0时,只有col=0那一列参与了像素值计算,
当u=0.2,v=0.7时,S(x)函数在16个像素点中的权重表现:
当u,v都不为0时,16个像素点全都参与计算,权重表现如下,至于为什么中间及四个角的权重为正,侧边为负,我也还没想明白,不知道是不是因为人的视觉原因才这么去计算的,如果有知道的朋友可以告知下。
优缺点:三次卷积法能计算量比较大,缩放后的图像质量很高,是三种算法中效果最好的。
实际测试效果
基于iOS实现了下这套原理,将一张180*180的图片放大到480*480
可以明显看到使用最邻近插值法缩放的图片锯齿感明显,使用双线性插值法后,平滑感明显提升,三次卷积法缩放的图片的图片在平滑感上略有提升,图片层次更好。