双线性插值算法原理及其python实现

双线性插值算法的原理比较简单,就是这样一个需求,假如想知道一个坐标的数值,直接找到其4个周围的数值,利用这4个数值进行线性插值出目标位置的数值,比较简单的思路就是先对x方向进行插值,然后对y方向进行插值,就诞生了所谓的双线性插值,示例图如下:

 也就是使用P1/P2/P3/P4四个点插值出来P的数值

不管是先进行x坐标还是先进行y坐标都是OK的

具体来说,就是使用先计算出Px1和Px2的数值,然后再计算出来Py1和Py2,最终计算出来P的数值。具体公式化为如下:

Px1=(P4-P1)\ast ((x-x1)/(x2-x1))))+P2 \\ Px2=(P3-P2)\ast ((x-x1)/(x2-x1))))+P1 \\ P=(Px1-Px2)\ast ((y-y1)/(y2-y1))))+Px2

简单的使用python实现一下:

class Point:
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y


def bilinear_interpolation(x, y, points, values):
    px1 = (values[3]-values[0])*((x-points[1].x)/(points[2].x-points[1].x))+values[1]
    px2 = (values[2] - values[1]) * ((x - points[0].x) / (points[3].x - points[0].x)) + values[0]
    p = (px1-px2)*((y-points[0].y)/(points[1].y-points[0].y))+px2
    return p


if __name__ == '__main__':
    points = [Point(1, 2), Point(1, 4), Point(3, 4), Point(3, 2)]
    vs = [1, 2, 3, 4]
    print(bilinear_interpolation(x=2, y=3, points=points, values=vs))#2.5

经常我们都能看到使用OpenCV等工具进行resize尺寸,默认一般都是双线性插值,双线性插值是效率和精度的平衡,也就是耗时比较少效果也不错。下面我使用python简单的实现一下resize,这里所说的resize都是放大的resize,因为缩小尺寸根本用不到插值,只需要在原图进行采样即可,所以我这里说的resize是针对放大尺寸来说的。

import cv2
import numpy as np


class Point:
    def __init__(self, x=None, y=None):
        self.x = x
        self.y = y


def bilinear_interpolation(x, y, points, values):
    px1 = (values[3]-values[0])*((x-points[1].x)/(points[2].x-points[1].x))+values[1]
    px2 = (values[2] - values[1]) * ((x - points[0].x) / (points[3].x - points[0].x)) + values[0]
    p = (px1-px2)*((y-points[0].y)/(points[1].y-points[0].y))+px2
    return np.clip(p, 0, 255)


def resize(img, w_new, h_new):
    out_img = np.zeros((h_new, w_new, 3))
    h, w = img.shape[:2]
    source_w = [(i+0.5)*w/w_new-0.5 for i in range(w_new)]
    source_w_1 = [int(np.clip(np.floor(i), 0, w-2)) for i in source_w]
    source_h = [(i + 0.5) * h / h_new - 0.5 for i in range(h_new)]
    source_h_1 = [int(np.clip(np.floor(i), 0, h - 2)) for i in source_h]

    for i in range(0, h_new):
        for j in range(0, w_new):
            #寻找4个近邻点
            p1 = (source_h_1[i], source_w_1[j])
            p2 = (source_h_1[i]+1, source_w_1[j])
            p3 = (source_h_1[i]+1, source_w_1[j]+1)
            p4 = (source_h_1[i], source_w_1[j]+1)
            points = [Point(p1[1], p1[0]), Point(p2[1], p2[0]), Point(p3[1], p3[0]), Point(p4[1], p4[0])]
            vs_b = [img[p1[0], p1[1], 0], img[p2[0], p2[1], 0], img[p3[0], p3[1], 0], img[p4[0], p4[1], 0]]
            vs_g = [img[p1[0], p1[1], 1], img[p2[0], p2[1], 1], img[p3[0], p3[1], 1], img[p4[0], p4[1], 1]]
            vs_r = [img[p1[0], p1[1], 2], img[p2[0], p2[1], 2], img[p3[0], p3[1], 2], img[p4[0], p4[1], 2]]
            # print(source_w[j], source_h[i], i, j)
            out_img[i, j, 0] = bilinear_interpolation(x=source_w[j], y=source_h[i], points=points, values=vs_b)
            out_img[i, j, 1] = bilinear_interpolation(x=source_w[j], y=source_h[i], points=points, values=vs_g)
            out_img[i, j, 2] = bilinear_interpolation(x=source_w[j], y=source_h[i], points=points, values=vs_r)
    return out_img


if __name__ == '__main__':
    # points = [Point(1, 2), Point(1, 4), Point(3, 4), Point(3, 2)]
    # vs = [1, 2, 3, 4]
    # print(bilinear_interpolation(x=2, y=3, points=points, values=vs))#2.5
    img = cv2.imread('demo.jpg')
    h, w = img.shape[:2]
    img = cv2.resize(img, (w//4, h//4))
    res = resize(img.astype(np.float32), w//2, h//2).astype(np.uint8)
    cv2.imwrite('demo_resize.jpg', res)
    res_cv = cv2.resize(img, (w//2, h//2))
    cv2.imwrite('demo_resize_opencv.jpg', res_cv)

运行结果如下:

不过需要注意的是,python这样运行分辨率较大时会很慢,可以考虑使用numpy的矩阵运算的方式进行运行或者修改成一些加速库,这里只是展示一些原理学习,并不能直接使用在工程项目里面。

猜你喜欢

转载自blog.csdn.net/zhou_438/article/details/131257993