最近邻插值法(nearest_neighbor)
最近在学习tensorflow时对于图像的缩放有疑惑,决定重新抽丝剥茧搞懂。在tf.image.resize(image,(256,256),method=0)中,mehod是默认为0,也就是双线性插值法,本篇先讲解method=1的情况,也就是最近邻插值法。
1.原理与应用
最近邻插值法nearest_neighbor是最简单的灰度值插值。也称作零阶插值,就是令变换后像素的灰度值等于距它最近的输入像素的灰度值。最近邻插值法可应用于图像的缩放,因为简单的变换与计算,效果一般不好。举例说明其变换过程:
先假设一个2X2像素的图片采用最近邻插值法需要放大到4X4像素的图片,右边?该为多少。
2. 公式及计算
最近邻插值法坐标变换计算公式:
srcX=dstX*(srcWidth/dstWidth)
srcY=dstY*(srcHeight/dstHeight)
上式中,dstX与dstY为目标图像的某个像素的横纵坐标,dstWidth与dstHeight为目标图像的长与宽;srcWidth与srcHeight为原图像的宽度与高度。srcX,srcY为目标图像在该点(dstX,dstY)对应的原图像的坐标。至于公式为啥是这个样,和容易理解,如果是放大图像,(srcWidth/dstWidth)值小于1,同样将dstX同比例映射到原图像的srcX中,如果srcWidth/dstWidth=1,就相当于复制了图片。
与opencv一样,以左上角为(0,0)坐标。
右图为经过放大后的目标图像,?处的坐标为(3,2),根据公式计算得到
srcX=3*(2/4)=1.5,srcY=2*(2/4)=1;故?处的像素应该为原图像中的(1.5,1)像素的值,但是像素坐标没有小数,一般采用四舍五入取最邻,所以最终的结果为(2,1),对应原图像的橙色。其他类比得到放大后的图像:
3.代码及实现
Python实现:
from matplotlib import pyplot as plt import numpy as np from PIL import Image #1.首先得到原图像的宽度、高度 def get_image_information(image_path): vector=plt.imread(image_path) # print(vector)#得到图像的三维矩阵 # print(type(vector)) # print(vector.shape)#得到(高,宽,通道数) height=vector.shape[0] width=vector.shape[1] return height,width,vector def getdst_image_vector(srcHeight,srcWidth,srcVector,dstHeight,dstWidth): #定义一个三维矩阵存储目标图像,每个像素由RGB三种颜色组成,shape接受一个元组,可以创建多维矩阵 dstVector=np.zeros(shape=(dstHeight,dstWidth,3),dtype=int)#默认是float64 # print(dstVector) #遍历目标图像的每一个像素 for dstX in range(1,dstWidth+1):#[0,dstWid-1],#矩阵从0开始,不是从1开始 for dstY in range(1,dstHeight+1):#[0,dstHeight-1] #坐标换算 dstX_srcX=dstX*(srcWidth/dstWidth)#python中/表示除法,//表示整除 dstY_srcY=dstY*(srcHeight/dstHeight) # print(dstX_srcX,dstY_srcY) #最近邻四舍五入,采用round函数实现 dstX_srcX_round=int(round(dstX_srcX)) dstY_srcY_round=int(round(dstY_srcY)) # print(srcVector[dstX_srcX_round][dstY_srcY_round]) # print(dstX_srcX_round,dstY_srcY_round) dstVector[dstY-1][dstX-1]=srcVector[dstY_srcY_round-1][dstX_srcX_round-1]#这个地方要认真琢磨,x,y也不能搞反了 # dstVector[dstX][dstY]=srcVector[dstX_srcX_round][dstY_srcY_round] print(dstVector) # pic=Image.fromarray(dstVector, "RGB") # pic.show() plt.imshow(dstVector) # 显示数组 plt.show() plt.imsave('邻近法得到的图像.jpg',dstVector.astype(np.uint8))#matplotlib保存图像的时候,只接受浮点数或者unit8的整数类型 # plt.imsave('邻近法得到的图像.jpg',dstVector/255) return dstVector if __name__=='__main__': imagePath=r'songshu.jpg' srcHeight,srcWidth,srcVector=get_image_information(imagePath) dstHeight=int(0.5*srcHeight) dstWidth =int(0.5*srcWidth) # print(dstHeight,dstWidth) dstVector=getdst_image_vector(srcHeight,srcWidth,srcVector,dstHeight,dstWidth) # print("srcVector",srcVector) # print("dstVector",dstVector)
C++实现:后面再补充。
4.效果展示
对自己用画图面板制作的奥运五环图及网上的图对图像采用最近邻插值法进行扩大与缩放,效果如下:
上图左边为自己画的100X80P的奥运图,右图为长宽不等比例扩大后的图像
左图为松鼠的原图,中间图为像素等比例扩大1.5倍后的图,最右边为0.2倍原图像。可以看到图像缩放后,效果并不理想,很多锯齿马赛克。
小结:最近邻插值法计算量小,但是精确度不高,并且可能破坏图像中的线性关系。另外,最近零插值法,不会产生新的像素,也就是用该算法得到的图的任意一个像素值都能在原图中找到。对于图像分割种标签掩码图的缩放可以使用最邻近,因为不会产生新的像素值,也就不会产生新的标签。