通过手动实现最邻近插值算法和双线性插值。
最邻近插值:
优点: 计算量很小,算法简单,速度快。
缺点: 仅使用离待测采样点最近的像素的灰度值作为该采样点的灰度值,而没有考虑其他相邻像素点的影响,因此采样后的灰度值有明显的不连续性,图像质量损失较大,会产生明显的马赛克和锯齿现象。
import cv2
import numpy as np
def nearest_interpolation(src_img, out_dim):
dst_width, dst_height = out_dim[0], out_dim[1] # 目标图像的宽与高
if len(src_img.shape) > 2: # 说明不是灰度图
src_width, src_height, channels = src_img.shape
# 创建新图,是一个全0矩阵
dst_img = np.zeros((dst_height, dst_width, channels), dtype=np.uint8)
scale_x, scale_y = float(src_width) / dst_width, float(src_height) / dst_height
for dsth in range(dst_height):
for dstw in range(dst_width):
# 几何中心对称
src_x = (dstw + 0.5) * scale_x - 0.5
src_y = (dsth + 0.5) * scale_y - 0.5
# 防止放大后在原图后越界,做越界处理,比如原图为512*512,取到520时不存在,就取512
src_x0 = int(np.floor(src_y))
src_x1 = min(src_x0 + 1, src_height - 1)
src_y0 = int(np.floor(src_x))
src_y1 = min(src_y0 + 1, src_width - 1)
dst_img[dsth, dstw] = img[src_x1, src_y1]
return dst_img # 返回目标图像
else: # 是灰度图
src_width, src_height = src_img.shape # 原图大小
# 创建新图的0矩阵
dst_img = np.zeros((dst_height, dst_width), dtype=np.uint8)
scale_x, scale_y = float(src_width) / dst_width, float(src_height) / dst_height
for dsth in range(dst_height):
for dstw in range(dst_width):
src_x = (dstw + 0.5) * scale_x - 0.5
src_y = (dsth + 0.5) * scale_y - 0.5
# 防止放大后在原图后越界,做越界处理,比如原图为512*512,取到520时不存在,就取512
src_x0 = int(np.floor(src_y))
src_x1 = min(src_x0 + 1, src_height - 1)
src_y0 = int(np.floor(src_x))
src_y1 = min(src_y0 + 1, src_width - 1)
dst_img[dsth, dstw] = img[src_x1, src_y1]
return dst_img
if __name__ == '__main__':
img = cv2.imread("../data/lenna.png")
print("origina_image size:", img.shape)
dst_img = nearest_interpolation(img, (1000, 1000))
print("new_image size:", dst_img.shape)
cv2.imshow("dst_img", dst_img)
cv2.imshow("original_img", img)
cv2.waitKey(0)
双线性插值算法:
优点:双线性插值效果比最近邻插值效果好,缩放后的图像质量高。 因为考虑了待采样点周围四个直接邻点对该采样点的相关性影响,基本克服了最近邻插值不连续的缺点。
缺点:计算量稍大,算法更复杂一些,运算时间长。 仅考虑待测样点周围四个点灰度值的影响,而为考虑各邻点间灰度值变化率的影响,因此具有低通滤波器的性质,导致缩放后图像的高频分量受到损失,图像边缘产生一定程度的模糊。 用此方法缩放后的图像与输入图像相比,仍然存在由于插值函数设计考虑不周而产生的图像质量受损的问题。
import numpy as np
import cv2
def bilinear_interpolation(img, out_dim):
"""双线性插值"""
# 获取原图像的高、宽和通道数
src_h, src_w, channel = img.shape
# 获取目标图像的高和宽
dst_h, dst_w = out_dim[1], out_dim[0]
print("src_h, src_w = ", src_h, src_w)
print("dst_h, dst_w = ", dst_h, dst_w)
if src_h == dst_h and src_w == dst_w: # 若目标图和原图一样大小,直接复制即可
return img.copy()
# 创建目标图像,三通道
dst_img = np.zeros((dst_h, dst_w, 3), dtype=np.uint8)
# 获取水平、垂直方向的缩放比例
scale_x, scale_y = float(src_w) / dst_w, float(src_h) / dst_h
for i in range(3): #3个通道
for dst_y in range(dst_h):
for dst_x in range(dst_w):
# 计算源图像坐标
src_x = (dst_x + 0.5) * scale_x - 0.5
src_y = (dst_y + 0.5) * scale_y - 0.5
src_x0 = int(np.floor(src_x))
src_x1 = min(src_x0 + 1, src_w - 1) # 防止坐标越界,比如原图为512*512,取到520时不存在,就取512
src_y0 = int(np.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('../data/lenna.png')
dst = bilinear_interpolation(img, (1000, 1000))
cv2.imshow("original img", img)
cv2.imshow('bilinear interp', dst)
cv2.waitKey()