插值法是一种根据原图(source)图片信息构造目标图像(destination)的方法。
我们想象一下,我们要把一个 3x3的单通道图src扩展成 4x4的图像dst:
由比例关系得:
所以有:
因此通过上式就得到 目标图dst与源图src的像素对应关系。举例,若要求dst(0,0)的值:
所以dst(0,0) 对应src(0,0),即可 dst(0,0)=src(0,0)=234
那我们接着算:dst(0,1):
dst[0][1]对应的src位置如下:
所以dst(0,1)=src(0,0.75)
这时候出现小数了,但是矩阵序号是整数啊,那怎么办呢?所以四舍五入dst[0][1] = src[0][1] = 38。根据以上公式最终结果如下:
这种插值方法叫:最近邻插值法。但是这种方法缺点很大,例如:放大后有马赛克,缩小后有严重失真。所以便有了双线性插值法。
在双线性插值法中,我们考虑到dst图像中某个像素点对应回src图像中是在4个像素点之间。例如(0,0.75)对应的四个邻近点就是:(0,0),(0,1),(1,0),(1,1),那么这四个点将会按一定规则构成(0,0.75)的值,然后放入dst(0,1)中。
在讲双线性插值之前,先说一下线性插值,因为双线性插值法的核心思想是在两个方向分别进行一次线性插值。
如图所示:
使用斜率的关系,有:(这是非常重要的,第一个式子是斜率关系,第二个式子由第一个式子推出)
其代表的意思是 f(x)的值可以由 f(x1)和f(x2)推出。这个关系放在图像中,就相当于f(x)为要求的点的像素值,f(x1)和f(x2)则是已知的像素值。
双线性插值:
那么在双线性插值中,如下图所示:
双线性插值(Bilinear Interpolation):双线性就是利用与坐标轴平行的两条直线去把小数坐标分解到相邻的四个整数坐标点。权重与距离成反比。
先对Q11、Q21用线性插值,算出R1。然后对Q12、Q22用线性插值算出R2。最后对R1、R2用线性插值算出最终的P值。
R1和R2的计算公式如下:
上述的公式中,和都是0,你们想一下是不是?若要求的点是(0,0.75),则它对应的四个临近点就是:(0,0),(0,1),(1,0),(1,1)
所以公式可以进一步化简为:
为方便理解,说明一下,f(x,y)就是(x,y)的像素值。
大致流程:
单纯按照上文实现的插值算法只能勉强完成插值的功能,速度和效果都不会理想,在具体代码实现的时候有些小技巧。
如:源图像和目标图像几何中心的对齐
方法:在计算源图像的虚拟浮点坐标的时候,一般情况:
中心对齐(OpenCV也是如此):(用下面计算的式子代替上面的)
看一个例子:假设源图像是3*3,中心点坐标(1,1)目标图像是9*9,中心点坐标(4,4),我们在进行插值映射的时候,尽可能希望均匀的用到源图像的像素信息,最直观的就是(4,4)映射到(1,1)现在直接计算srcX=4*3/9=1.3333!=1,也就是我们在插值的时候所利用的像素集中在图像的右下方,而不是均匀分布整个图像。现在考虑中心点对齐,srcX=(4+0.5)*3/9-0.5=1,刚好满足我们的要求。
代码:
import cv2
import time
from math import ceil, floor
import numpy as np
def bilinear_interpolation(img, out_dim):
src_h, src_w, channel = img.shape
dst_h, dst_w = out_dim[1], out_dim[0]
if src_h == dst_h and src_w == dst_w:
return img.copy()
dst_img = np.zeros((dst_h, dst_w, channel), dtype=np.uint8) #创建一个数组,通过往里面填值,形成新的图片
scale_x, scale_y = float(src_w) / dst_w, float(src_h) / dst_h #计算 dst与src的比例系数
for i in range(channel):
for dst_y in range(dst_h):
for dst_x in range(dst_w):
# 计算dst(x,y)对应回源图src的哪个坐标
# 使用几何中心对称的方法
# 如果不使用几何中心对成就写成: src_x = dst_x * scale_x
src_x = (dst_x + 0.5) * scale_x - 0.5
src_y = (dst_y + 0.5) * scale_y - 0.5
# 找出用于插值的四个邻近点坐标,(x0,y0),(x0,y1),(x1,y0),(x1,y1)
src_x0 = int(floor(src_x))
src_x1 = min(src_x0 + 1, src_w - 1) #与边界点比,取小的一个,减1是因为是从0开始算的
src_y0 = int(floor(src_y))
src_y1 = min(src_y0 + 1, src_h - 1) #与边界点比,取小的一个
temp0 = ((src_x1 - src_x) * img[src_y0, src_x0,i] + (src_x - src_x0) * img[src_y0, src_x1, i])
temp1 = (src_x1 - src_x) * img[src_y1, src_x0,i] + (src_x - src_x0) * img[src_y1, src_x1, i]
dst_img[dst_y, dst_x, i] = int((src_y1 - src_y) * temp0 + (src_y - src_y0) * temp1)
return dst_img
if __name__ == '__main__':
img = cv2.imread(r'C:\Users\Administrator\Desktop\pic\car.jpg')
start = time.time()
dst = bilinear_interpolation(img, (1000, 1000))
print('cost {} seconds'.format(time.time() - start))
cv2.imshow('result', dst)
cv2.waitKey()
https://blog.csdn.net/siyue0211/article/details/92660001
https://blog.csdn.net/qq_37577735/article/details/80041586