Bilinear Interpolation

转自:https://blog.youkuaiyun.com/xbinworld/article/details/65660665




线性插值

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

yy0xx0=y1y0x1x0y−y0x−x0=y1−y0x1−x0

y=x1xx1x0y0+xx0x1x0y1y=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函数五种插值算法的实现过程

转载于:https://www.cnblogs.com/leebxo/p/10549264.html

### 双线性插值概念 双线性插值是一种用于图像处理和计算机图形学中的基本技术,主要用于估计二维离散数据集之间的值。该方法通过四个最近邻像素的位置及其灰度值来计算目标位置的灰度值[^1]。 具体来说,在给定矩形网格上的已知函数值的情况下,可以利用这些点构建一个近似平面,从而得到任意位置处更精确的结果。这种方法不仅适用于单通道灰度图,也广泛应用于多通道彩色图片中。 ### 实现方法 以下是Python语言实现的一个简单版本: ```python import numpy as np def bilinear_interpolation(image, point): """ 对输入图像执行双线性插值 参数: image (numpy.ndarray): 输入图像矩阵. point (tuple of float): 需要插值得到的新坐标(x,y). 返回: interpolated_value (float or tuple): 插值得到的颜色/亮度值. """ x_floor = int(np.floor(point[0])) y_floor = int(np.floor(point[1])) dx = point[0] - x_floor dy = point[1] - y_floor Q11 = image[y_floor][x_floor] Q21 = image[y_floor][min(len(image[0])-1,x_floor+1)] Q12 = image[min(len(image)-1,y_floor+1)][x_floor] Q22 = image[min(len(image)-1,y_floor+1)][min(len(image[0])-1,x_floor+1)] if isinstance(Q11,int) or isinstance(Q11,float): R1 = (1-dx)*Q11 + dx*Q21 R2 = (1-dx)*Q12 + dx*Q22 P = (1-dy)*R1 + dy*R2 else: channels = len(Q11) P = [] for c in range(channels): R1_c = (1-dx)*Q11[c] + dx*Q21[c] R2_c = (1-dx)*Q12[c] + dx*Q22[c] Pc = (1-dy)*R1_c + dy*R2_c P.append(Pc) return P ``` 这段代码实现了基于四角权重加权平均的方法来进行双线性插值运算。对于RGB三通道颜色模型,则分别针对每个分量独立完成上述过程并最终组合成完整的色彩表示形式。 ### 应用场景 - **图像缩放**:当放大或缩小位图时,为了保持视觉质量而采用的技术之一就是双线性插值。 - **纹理映射**:在三维渲染过程中,物体表面贴附材质图案时常会遇到分辨率不匹配的问题;此时借助此算法可有效改善显示效果。 - **视频编码与解码**:某些压缩标准里也会涉及到帧间预测环节里的运动补偿操作,其中就可能运用到了类似的重采样手段以提高效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值