1.图像采样
数字图像的获取途径有很多方式,通常可利用图像采样在连续图像上进行数字化。采样点之间形成的集合关系就是栅格,栅格间无限小的采样点对应于图像中的像素点。图像变换上的采样就是将图像分辨率改变的过程,采样分为上采样和下采样。上采样是指将图像分辨率扩大,下采样是将图像分辨率缩小(其实就是缩放,不过初始图像受限制于硬件成像设备的芯片分辨率尺寸)。
2.插值
PS:这是resize函数的第六个含参量
之前提过五种插值,这里重点介绍两个时间复杂度较低且好用的最近邻插值和双线性插值。
<1>最近邻插值
最近邻插值是最简单的图像缩放处理方法,其原理是提取源数据图像中与邻域最近像素值来作为目标图像相对应点的像素值。源图像f(x,y)的分辨率为w x h,进行缩放后目标图像f(x',y')的分辨率为w' x h',最近邻插值变换如下式所示:
最近邻插值缩放最关键的步骤是找到缩放倍数,然后邻域向上取证完成坐标映射。
PS:取整函数看第十三章
<2>双线性插值
双线性插值处理是应用最广泛的图像缩放方法之一,其稳定性高且时间复杂度较优。双线性插值的原理是通过计算源数据图像的位置点邻域(2x2)像素值的加权平均,进而得到目标图像相对应的位置点。源图像f(x,y)的分辨率为w x h,进行缩放后,目标图像f(x',y')的分辨率为w' x h',由最近邻插值可知,变换后的目标图像的第(i,j)像素坐标可以通过函数映射关系对应于源图像坐标,映射关系为(i x w / w',j x h / h'),因此映射后的坐标点必须为整数,在最近邻插值中是采用cvFloor方法进行向上取整来保证的。而在双线性插值中,我们可以通过寻找距离这个对应坐标点最近的四个相邻坐标点来计算对应坐标。计算公式如下:,其中为距离最近的四个相邻点,为对应点相应的权重比例。需要注意的是,双线性插值中涉及的大部分都是浮点数数据的运算,对坐标转换应考虑优化,常见的优化是改变映射关系为((i+0.5)* w /w' -0.5,(j+0.5)* h /h' -0.5)。双线性插值利用区域邻域的特征使得变换后的图像像素值连续,一定程度上弱化了高频分量信息,图像轮廓将出现一定程度上模糊。
补充:
<1>余弦插值
余弦插值就是为了解决线性插值不平滑的问题的,余弦插值思想也非常简单,将线性插值中的 t 替换成一个余弦值。
float Cos_Interpolate(float a, float b, float t)
{
float ft = t * 3.1415927;
t = (1 - cos(ft)) * 0.5;
return a*t + (1-t)b;
}
其中参数 0<= t<=1,对上图线性插值的结果如下:
可以看到,经过余弦插值后,生成的曲线平滑多了,这也更符合自然规律了。
<2>Hermite插值
Hermite插值是我们最熟悉的陌生插值算法,熟悉是smoothstep就是采用的Hermite插值来做计算的(采用了优化版本),三次Hermite插值公式是 :
f(t)=(2∗t3−3∗t2+1)v0+(t3−2∗t2+t)m0+(t3−t2)m1+(−2∗t3+3∗t2)v1
其中v0,v1是两个端点,m0是v0点处的方向,m1是v1处的方向,或者说是插值后平滑曲线在v0,v1处的切向量,即m0=v1-v0,m1=v3-v2,参数t从0变化到1的过程中f(t)形成的轨迹构成了从v0到v1的平滑曲线,m0,m1的大小会影响到曲线的形状。
float Hermite_Interpolate(float v0, float m0, float v1, float m1, float t)
{
return v0 + t*(m0 + t*(-3v0 - 2m0 - m1 + 3v1 + t*(2v0 + m0 + m1 - 2v1)));
}
<3>Sphere 插值
Sphere插值往往跟四元数插值相关,用来在两个四元数之间进行平滑插值以达到动画平滑的目的。Sphere插值原理也很简单,但需要注意处理小角度和正反旋转的问题。
private Quternion LerpAndNormalize(Quternion p,Quternion q, float t)
{
return Normalize((1.0f-t)*p+t*q);
}
public Quternion Slerp(Quternion p,Quternion q, float t)
{
if(Length(p-q) > Length(p+q))
q=-q;
float cosphi = DotP(p,q);
if(cosphi > (1.0f - 0.001))
return LerpAndNormalize(p,q,t);
float phi = (float)Math.Acos(cosphi);
float sinphi =(float)Math.Sin(phi);
return ((float)Math.Sin(phi*(1.0f - t)/sinphi)*p + ((float)Math.Sin(phi*t)/sinphi)*q;
}