邻近双线性插值图像缩放的Python实现
最近在查找有关图像缩放之类的算法,因工作中需要用到诸如此类的图像处理算法就在网上了解了一下相关算法,以及其原理,并用Python实现,且亲自验证过,在次与大家分享。
声明:本文代码示例针对的是planar格式的YUV数据,且只对Y分量做了缩放,因为平常工作中接触较多的是YUV格式的黑白图片,UV分量都是固定的0x80,所以针对UV分量没有做缩放操作。
先大概讲一下图像所方的原理,假设缩放之前的图像为src_img,分辨率为src_w*src_h,缩放之后的图像为dst_img,分辨率为dst_w*dst_h,那么我们可以利用缩放前后图像分辨率的比率来找两幅图像中像素点的对应关系,以及像素点值的对应关系。如src_im中的点S(x,y)经过缩放之后变成了dst_img中的点D(x',y'),那么点S到点D的映射存在这么一种关系:x / src_w = x' / dst_w; y / src_h = y' / dst_h。利用上述公式我们就可以得到,dst_img中所有点对应到src_img中的那个点,我们再去把这个点的像素值赋给dst_img中对应的点。这是邻近插值缩放算法最基本的思想,但是我们会发现有时候算出的坐标是个小数如(0.6,0.25)等等,那么这个点对应src_img中那个点呢?如果采取简单的四舍五入的话,那么这个点应该对应src_img中的点(1,0),这种四舍五入近似的插值算法,在图像放大后图像严重失真,容易形成严重的马赛克,那么虚拟点(0.6,0.25)像素值到底该怎么取值呢?最邻近双线性而插值算法认为,应该有点(0.6,0.25)周围四个点(0,0),(0,1),(1,0),(1,1)的像素值共同决定,那么究竟是怎么一种决定方式呢?很简单,按距离来分配权重,离得越近的点的像素值的权重自然要越大。公式如下:
f(i+u,j+v) = (1-u)*(1-v)*f(i,j) + (1-u)*v*f(i,j+1) + u*(1-v)*f(i+1,j) + u*v*f(i+1,j+1)
关于最邻近双线插值算法的原理,网上有很多详细的解释和推导,在此我也不再详细赘述了,直接上python的实现代码:
最近在工作中碰到了,自己的写的双线性缩放函数出来的结果和opencv resize函数出来的像素点的值不一样,在网上查了一下原因,是因为我选取的坐标顶点是图像的左上角(0,0)的位置,这样也能得到正确的缩放图像,但是质量不高,因为最右边和最下边几乎没有参与计算,所以opencv中选取了几何中心重合的方式去计算,具体可以再往上搜一下,先讲算法修 1 """ 2 @description:this module will use two classic way to scaling img.Read a img from a file and scale it to dst s 3
4 @author:GWB 5 6 @version:v0.0.0 7 """ 8 import os 9 import binascii 10 def get_min(a,b): 11 if a > b: 12 return b 13 else: 14 return a 15 16 def get_max(a,b): 17 if a > b: 18 return a 19 else: 20 return b 21 22 def read_img_from_bin_file(in_filename,list_data): 23 with open(in_filename,'rb') as fd: #open file 24 fd.seek(0,0) #relocate fd 25 while True: #while loop 26 t_byte = fd.read(1) #read one byte 27 if len(t_byte) == 0: #if reach the end of the file then break 28 break 29 else: #store the the t_byte into hex format 30 list_data.append(ord(t_byte)) 31 fd.close() 32 33 def img_scale_fuc(in_list_data,srcW,srcH,dstW,dstH,out_list_data): 34 rateW = (srcW*1.0 / dstW) 35 rateH = (srcH*1.0 / dstH) 36 data = 0 37 for i in range(0,dstH):
38 temp = rateH * (i + 0.5) -0.5
y0 = int(temp)
u = temp - y0
y0 = get_max(0,get_min(y0,srcH - 2))
39 y1 = get_min((y0 + 1),(srcH - 1)) 40 for j in range(0,dstW):
temp = rateW * (j + 0.5) - 0.5 42 x0 = int(temp)
v = temp - x0
x0 = get_max(0,get_min(x0,srcW - 2)) 43 x1 = get_min((x0 + 1),(srcW - 1)) 45 w0 = ((1 - u)*(1 - v)) 46 w1 = ((1 - u)*v) 47 w2 = (u*(1 - v)) 48 w3 = (u*v) 49 p0 = in_list_data[y0*srcW + x0] 50 p1 = in_list_data[y1*srcW + x0] 51 p2 = in_list_data[y0*srcW + x1] 52 p3 = in_list_data[y1*srcW + x1] 53 #f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1) 54 data = int(w0*p0+w1*p1+w2*p2+w3*p3) 55 out_list_data.append('%.2X' % data) 56 57 def write_data_to_text(out_filename,out_list_data): 58 with open(out_filename,'wb') as fd: 59 fd.seek(0,0) 60 for data in out_list_data: 61 fd.write(binascii.a2b_hex(data)) 62 fd.close() 63 64 list_data = [] 65 out_list_data = [] 66 67 in_filename = raw_input("Please input src file name:\n") 68 out_filename = raw_input("Please input dst file name:\n") 69 srcH = input("Please input srcH:\n") 70 srcW = input("Please input srcW:\n") 71 dstH = input("Please input dstH:\n") 72 dstW = input("Please input dstW:\n") 73 74 read_img_from_bin_file(in_filename,list_data) 75 img_scale_fuc(list_data,srcW,srcH,dstW,dstH,out_list_data) 76 write_data_to_text(out_filename,out_list_data)