三十分钟理解:线性插值,双线性插值Bilinear Interpolation算法

线性插值

先讲一下线性插值:已知数据 (x0, y0) 与 (x1, y1),要计算 [x0, x1] 区间内某一位置 x 在直线上的y值(反过来也是一样,略):

y?y0x?x0=y1?y0x1?x0

y=x1?xx1?x0y0+x?x0x1?x0y1

上面比较好理解吧,仔细看就是用x和x0,x1的距离作为一个权重,用于y0和y1的加权。双线性插值本质上就是在两个方向上做线性插值。

双线性插值

在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值[1]。见下图:

假如我们想得到未知函数 f 在点 P = (x, y) 的值,假设我们已知函数 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四个点的值。最常见的情况,f就是一个像素点的像素值。首先在 x 方向进行线性插值,得到

然后在 y 方向进行线性插值,得到

综合起来就是双线性插值最后的结果:

由于图像双线性插值只会用相邻的4个点,因此上述公式的分母都是1。opencv中的源码如下,用了一些优化手段,比如用整数计算代替float(下面代码中的*2048就是变11位小数为整数,最后有两个连乘,因此>>22位),以及源图像和目标图像几何中心的对齐

SrcX=(dstX+0.5)* (srcWidth/dstWidth) -0.5

SrcY=(dstY+0.5) * (srcHeight/dstHeight)-0.5

这个要重点说一下,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下。如果没有这个中心对齐,根据基本公式去算,就会得到左边这样的结果;而用了对齐,就会得到右边的结果:

cv::Mat matSrc, matDst1, matDst2;  

matSrc = cv::imread("lena.jpg", 2 | 4);
matDst1 = cv::Mat(cv::Size(800, 1000), matSrc.type(), cv::Scalar::all(0));
matDst2 = cv::Mat(matDst1.size(), matSrc.type(), cv::Scalar::all(0));  

double scale_x = (double)matSrc.cols / matDst1.cols;
double scale_y = (double)matSrc.rows / matDst1.rows;  

uchar* dataDst = matDst1.data;
int stepDst = matDst1.step;
uchar* dataSrc = matSrc.data;
int stepSrc = matSrc.step;
int iWidthSrc = matSrc.cols;
int iHiehgtSrc = matSrc.rows;  

for (int j = 0; j < matDst1.rows; ++j)
{
    float fy = (float)((j + 0.5) * scale_y - 0.5);
    int sy = cvFloor(fy);
    fy -= sy;
    sy = std::min(sy, iHiehgtSrc - 2);
    sy = std::max(0, sy);  

    short cbufy[2];
    cbufy[0] = cv::saturate_cast<short>((1.f - fy) * 2048);
    cbufy[1] = 2048 - cbufy[0];  

    for (int i = 0; i < matDst1.cols; ++i)
    {
        float fx = (float)((i + 0.5) * scale_x - 0.5);
        int sx = cvFloor(fx);
        fx -= sx;  

        if (sx < 0) {
            fx = 0, sx = 0;
        }
        if (sx >= iWidthSrc - 1) {
            fx = 0, sx = iWidthSrc - 2;
        }  

        short cbufx[2];
        cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
        cbufx[1] = 2048 - cbufx[0];  

        for (int k = 0; k < matSrc.channels(); ++k)
        {
            *(dataDst+ j*stepDst + 3*i + k) = (*(dataSrc + sy*stepSrc + 3*sx + k) * cbufx[0] * cbufy[0] +
                *(dataSrc + (sy+1)*stepSrc + 3*sx + k) * cbufx[0] * cbufy[1] +
                *(dataSrc + sy*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[0] +
                *(dataSrc + (sy+1)*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[1]) >> 22;
        }
    }
}
cv::imwrite("linear_1.jpg", matDst1);  

cv::resize(matSrc, matDst2, matDst1.size(), 0, 0, 1);
cv::imwrite("linear_2.jpg", matDst2);  

好了,本篇到这里,欢迎大家分享转载,注明出处即可。

参考资料

[1] 双线性插值(Bilinear Interpolation)

[2] OpenCV ——双线性插值(Bilinear interpolation)

[3] 双线性插值算法及需要注意事项

[4] OpenCV中resize函数五种插值算法的实现过程

时间: 2024-10-12 20:09:31

三十分钟理解:线性插值,双线性插值Bilinear Interpolation算法的相关文章

三十分钟理解计算图上的微积分:Backpropagation,反向微分

神经网络的训练算法,目前基本上是以Backpropagation (BP) 反向传播为主(加上一些变化),NN的训练是在1986年被提出,但实际上,BP 已经在不同领域中被重复发明了数十次了(参见 Griewank (2010)[1]).更加一般性且与应用场景独立的名称叫做:反向微分 (reverse-mode differentiation).本文是看了资料[2]中的介绍,写的蛮好,自己记录一下,方便理解. 从本质上看,BP 是一种快速求导的技术,可以作为一种不单单用在深度学习中并且可以胜任大

线性插值&amp;双线性插值&amp;三线性插值

http://www.cnblogs.com/yingying0907/archive/2012/11/21/2780092.html 內插是数学领域数值分析中的通过已知的离散数据求未知数据的过程或方法. 根据若干离散的数据,得到一个连续的函数(也就是曲线)或者更加密集的离散方程与已知数据相吻合.这个过程叫做拟合.內插是曲线必须通过已知点的拟合. 1.线性插值 已知坐标 (x0, y0) 与 (x1, y1),要得到 [x0, x1] 区间内某一位置 x 在直线上的值. 由于 x 值已知,所以可

三十分钟掌握STL

三十分钟掌握STL 这是本小人书.原名是<using stl>,不知道是谁写的.不过我倒觉得很有趣,所以化了两个晚上把它翻译出来.我没有对翻译出来的内容校验过.如果你没法在三十分钟内觉得有所收获,那么赶紧扔了它.文中我省略了很多东西.心疼那,浪费我两个晚上. 译者:kary contact:[email protected] STL概述 STL的一个重要特点是数据结构和算法的分离.尽管这是个简单的概念,但这种分离确实使得STL变得非常通用.例如,由于STL的sort()函数是完全通用的,你可以

[转]线性插值&amp;双线性插值&amp;三线性插值

转自:http://www.cnblogs.com/yingying0907/archive/2012/11/21/2780092.html 內插是数学领域数值分析中的通过已知的离散数据求未知数据的过程或方法. 根据若干离散的数据数据,得到一个连续的函数(也就是曲线)或者更加密集的离散方程与已知数据相吻合.这个过程叫做拟合.內插是曲线必须通过已知点的拟合. 1.线性插值 已知坐标 (x0, y0) 与 (x1, y1),要得到 [x0, x1] 区间内某一位置 x 在直线上的值. 由于 x 值已

三十分钟掌握STL(转)

这是本小人书.原名是<using stl>,不知道是谁写的.不过我倒觉得很有趣,所以化了两个晚上把它翻译出来.我没有对翻译出来的内容校验过.如果你没法在三十分钟内觉得有所收获,那么赶紧扔了它.文中我省略了很多东西.心疼那,浪费我两个晚上. 译者:kary contact:[email protected] STL概述 STL的一个重要特点是数据结构和算法的分离.尽管这是个简单的概念,但这种分离确实使得STL变得非常通用.例如,由于STL的sort()函数是完全通用的,你可以用它来操作几乎任何数

理解RMQ问题和ST算法的原理

下图为TI C6xx DSP Nyquist总线拓扑图,总线连接了master与slave,提供了高速的数据传输.有很多种速率不同的总线,如图中的红色方框,最高速总线为CPU/2 TeraNet SCR(即VBUSM SCR),带宽为256bit,其他低速总线为CPU/3,CPU/6,带宽参考图中所示.总线之间用Bridge(桥)连接,作用包括转换总线的速率,使之与所流向总线的速率相同等. 在具体应用中,各种速率的总线完全可以满足复杂的数据传输,而数据传输的瓶颈往往在于连接总线之间的Bridge

理解分布式一致性与Raft算法

理解分布式一致性与Raft算法 永远绕不开的CAP定理 出于可用性及负载方面考虑,一个分布式系统中数据必然不会只存在于一台机器,一致性简单地说就是分布式系统中的各个部分保持数据一致 但让数据保持一致往往并不像看上去那么简单,假设我们有两台机器A与B,这时A更新了数据,A需要将更新的指令同步到B,如果A到B网络传输到B数据落地的总时间为500ms,那么这个500ms就是可能造成数据不一致的时间窗口,假如两台机器分属不同机房,甚至分属不同国家的机房,其时间窗口会更大,具体会造成什么影响呢? 举个栗子

OpenCV ——双线性插值(Bilinear interpolation)

1,原理 在图像的仿射变换中,很多地方需要用到插值运算,常见的插值运算包括最邻近插值,双线性插值,双三次插值,兰索思插值等方法,OpenCV提供了很多方法,其中,双线性插值由于折中的插值效果和运算速度,运用比较广泛. 越是简单的模型越适合用来举例子,我们就举个简单的图像:3*3 的256级灰度图.假如图像的象素矩阵如下图所示(这个原始图把它叫做源图,Source):       234 38 22       67 44 12       89 65 63 这 个矩阵中,元素坐标(x,y)是这样

【深入理解JVM】:垃圾收集算法

垃圾收集算法主要有以下几种:标记-清除算法(mark-sweep).复制算法(copying)和标记-整理算法(mark-compact). 标记-清除算法 算法的执行过程与名字一样,先标记所有需要回收的对象,在标记完成后统一回收所有被标记的对象.该算法有两个问题: 标记和清除过程效率不高.主要由于垃圾收集器需要从GC Roots根对象中遍历所有可达的对象,并给这些对象加上一个标记,表明此对象在清除的时候被跳过,然后在清除阶段,垃圾收集器会从Java堆中从头到尾进行遍历,如果有对象没有被打上标记