使用 NumPy interp() 函数进行插值

646 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

前言

当图像按缩放因子调整大小时,需要执行一些像素插值,以便在现有像素之间填充新像素值,我们可以使用 NumPy interp() 函数执行插值操作。从 NumPy 文档中可以看到,interp() 函数的一维线性插值基本用法如下:

numpy.interp(x, left=None, right=None, period=None)

NumPy 插值函数 interp()

interp() 会返回具有给定离散数据点的函数的一维分段线性插值。

x_p = np.linspace(0, 2*np.pi, 10) # 在间隔[0,2π]中生成10个均匀间隔的数字序列
y_p = np.cos(x_p)
x = np.linspace(0, 2*np.pi, 50) # 在间隔[0,2π]中生成50个均匀间隔的数字序列
y = np.cos(x)
y_interp = np.interp(x, x_p, y_p)

plt.figure(figsize=(20,10))
plt.plot(x_p, y_p, 'o', label='reference points')
plt.plot(x, y_interp, '-x', label='interpolated')
plt.plot(x, y, '--', label='true')
plt.legend(prop={'size': 16})
plt.show()

假设我们希望(线性)插值区间 [0,2π][0, 2π] 中余弦函数的值,最初区间中仅包含十个参考点。我们可以使用 interp() 函数计算剩余点处函数的值,从给定点处函数值开始,然后应用线性插值。橙色曲线表示由 interp() 函数估计的曲线,绿色曲线显示真实的余弦曲线。:

Figure_6.png

图像插值

可以以类似的方式扩展以上代码,使用 interp() 函数计算 R (红色)通道的通道内插值结果,由于图像的红色通道值本质上是一个 2D 阵列(矩阵),因此在将该函数应用于通道之前,需要执行以下操作:

  • 2D 阵列展平为 1D 阵列(使用 NumPyravel() 函数)
  • 使用 interp() 函数应用通道插值
  • 1D 阵列重新整形为图像矩阵(使用 NumPy 的整形函数)

使用 np.interp() 函数,用 11 个参考点拉伸红色通道直方图:

r, g, b = im.split() # 分割图像通道为R、G和B
r_old = np.linspace(0,255,11)   # 参考点
r_new = [0., 12.75, 25.5, 51., 76.5, 127.5, 178.5, 204., 229.5, 242.25, 255.] # 参考点的新值

r1 = Image.fromarray((np.reshape(np.interp(np.array(r).ravel(), r_old, r_new),
                                 (im.height, im.width))).astype(np.uint8), mode='L')

然后,绘制图像和红色通道直方图,如下所示:

plt.figure(figsize=(20,15))
plt.subplot(221)
plt.imshow(im)
plt.title('original', size=20)
plt.axis('off')
plt.subplot(222)
im1 = Image.merge('RGB', (r1, g, b))
plt.imshow(im1)
plt.axis('off')
plt.title('with red channel interpolation', size=20)
plt.subplot(223)
plt.hist(np.array(r).ravel())
plt.subplot(224)
plt.hist(np.array(r1).ravel())
plt.show()

下图显示了插值变换前后的图像:

Figure_7.png

通过使用以下代码,令黑色更接近蓝色值,我们将蓝色值增加了 7.65,并且使用函数 np.clip() 来确保新值保持在 0255 区间内:

plt.figure(figsize=(20,10))
plt.subplot(121)
plt.imshow(im1)
plt.title('last image', size=20)
plt.axis('off')
b1 = Image.fromarray(np.clip(np.array(b) + 7.65, 0, 255).astype(np.uint8))
im1 = Image.merge('RGB', (r1, g, b1))
plt.subplot(122)
plt.imshow(im1)
plt.axis('off')
plt.title('with transformation', size=20)
plt.tight_layout()
plt.show()

Figure_8.png

通过使用 PIL 库的 ImageEnhance 类中的 Enhance() 方法对图像执行较小的锐化:

class PIL.ImageEnhance.Sharpness(image)

ImageEnhance 类可用于调整图像的清晰度,matplotlib 库的 subplot 模块用于显示子图,向当前图像中添加子图:

subplot(nrows, ncols, index, **kwargs)

例如,使用 plt.subplot(121) 创建具有一行和两列子图的图像,并使用索引参数 index 指定绘图位置:

from PIL.ImageEnhance import Sharpness

plt.figure(figsize=(20,10))
plt.subplot(121)
plt.imshow(im1)
plt.title('last image', size=20)
plt.axis('off')
im2 = Sharpness(im1).enhance(3.0)
plt.subplot(122)
plt.imshow(im2)
plt.axis('off')
plt.title('with transformation', size=20)
plt.tight_layout()
plt.show()

Figure_9.png

减少蓝色通道的色调值,我们同样使用通道插值完成,但这次需要在 RGB 图像的蓝色通道上进行:

blue_old = np.linspace(0,255,17) # 参考点的像素值
blue_new = [0., 11.985, 30.09, 64.005, 81.09, 99.96, 107.1, 111.945, 121.125, 143.055, 147.9, 159.885, 171.105,
               186.915, 215.985, 235.875, 255.] # 参考点的新像素值

b2 = Image.fromarray((np.reshape(np.interp(np.array(b1).ravel(), blue_old, blue_new),
                                 (im.height, im.width))).astype(np.uint8), mode='L')

绘制图像以及蓝色通道直方图如下:

plt.figure(figsize=(20,15))
plt.subplot(221)
plt.imshow(im2)
plt.title('last image', size=20)
plt.axis('off')
plt.subplot(222)
im3 = Image.merge('RGB', (r1, g, b2))
plt.imshow(im3)
plt.axis('off')
plt.title('with blue channel interpolation', size=20)
plt.subplot(223)
plt.hist(np.array(b1).ravel(), normed=True)
plt.subplot(224)
plt.hist(np.array(b2).ravel(), normed=True)
plt.show()

Figure_10.png