哪种插值方法最适合图像缩放?

63 投票
4 回答
134040 浏览
提问于 2025-04-18 07:31

我有一个numpy数组,想用opencv来调整它的大小。这个数组的值范围是0到255。如果我选择使用cv2.INTER_CUBIC这个方法,可能会得到超出这个范围的值。这是不可取的,因为调整大小后的数组应该仍然能表示一幅图像。

一个解决办法是把结果限制在[0, 255]这个范围内。另一个办法是使用不同的插值方法。我了解到,使用INTER_AREA适合缩小图像,但在放大时效果类似于最近邻插值,这样就不太适合我的需求了。

我应该使用INTER_CUBIC(并限制值),INTER_AREA,还是INTER_LINEAR呢?

使用INTER_CUBIC时,可能会出现超出范围的值的例子:

a = np.array( [ 0, 10, 20, 0, 5, 2, 255, 0, 255 ] ).reshape( ( 3, 3 ) )
[[  0  10  20]
 [  0   5   2]
 [255   0 255]]

b = cv2.resize( a.astype('float'), ( 4, 4 ), interpolation = cv2.INTER_CUBIC )
[[   0.            5.42489886   15.43670964   21.29199219]
 [ -28.01513672   -2.46422291    1.62949324  -19.30908203]
 [  91.88964844   25.07939219   24.75106835   91.19140625]
 [ 273.30322266   68.20603609   68.13853455  273.15966797]]

补充:正如berak指出的,把数据类型从int64转换为float后,可以得到超出原始范围的值。cv2.resize()这个函数默认不支持'int64'类型。不过,转换为'uint8'类型会自动把值限制在[0..255]的范围内。

另外,正如SaulloCastro提到的,另一个相关的回答展示了scipy的插值方法,其中默认使用的是立方插值(并且会限制值)。

4 个回答

5

我觉得你可以先用INTER_LINEAR,这个是resize()函数的默认选项。它在视觉效果和处理速度上都表现得不错(虽然没有INTER_NEAREST快)。而且它不会产生那些超出范围的值。

6

我的这个回答是基于测试的。最后的结果支持了@shivam的观点。我测试了几种插值方法,分别用于缩小和放大图像。放大后,我计算了与原始图像的峰值信噪比(psnr)。

[cv2.INTER_AREA,
cv2.INTER_BITS,
cv2.INTER_BITS2,
cv2.INTER_CUBIC,
cv2.INTER_LANCZOS4,
cv2.INTER_LINEAR,
cv2.INTER_LINEAR_EXACT,
cv2.INTER_NEAREST]

shirking=0.25
enlarge=4

我在165张不同形状的图像上进行了测试。结果中我选择了最高和第二高的psnr,并计算了它们的数量。最高的插值方法的数量在下面的图中展示。

maximum psnr

通过这次测试,最高的psnr是由AREALANCZOS4的组合提供的,这种组合在141/204张图像中得到了最高的psnr。

我还想包括第二高的结果。以下是仅第二高的结果。

2nd max psnr

在这里,AREA和CUBIC组合得到了第二好的结果。19/204张图像的psnr最高,158/347张图像使用AREA + CUBIC得到了第二高的psnr。

这些结果有些模糊,所以我查看了CUBIC给出最高psnr的图像。结果发现,很多纹理或抽象的图像在使用CUBIC时得到了最高的psnr。

因此,我进一步测试了AREA+CUBIC和AREA+LANCZOS4的组合。我得出的结论是,如果你要缩小图像小于10倍,建议使用LANCZOS4。这种方法在小于10倍的缩放下效果更好,而如果图像较大,LANCZOS4的效果会比CUBIC更好。

至于我的程序,我是将图像缩小4倍,所以对我来说,AREA+LANCZOS4的效果更好。

脚本和图像可以在这里找到:https://github.com/crackaf/triple-recovery/tree/main/tests

21

要解决这个问题,你需要找出给定图像的新大小,这样才能进行插值处理。然后把处理后的图像复制到目标图像上,像这样:

# create target image and copy sample image into it
(wt, ht) = imgSize # target image size
(h, w) = img.shape # given image size
fx = w / wt
fy = h / ht
f = max(fx, fy)
newSize = (max(min(wt, int(w / f)), 1),
           max(min(ht, int(h / f)), 1))  # scale according to f (result at least 1 and at most wt or ht)
img = cv2.resize(img, newSize, interpolation=cv2.INTER_CUBIC) #INTER_CUBIC interpolation
target = np.ones([ht, wt]) * 255  # shape=(64,800)
target[0:newSize[1], 0:newSize[0]] = img

在OpenCV中,有一些常见的插值方法:

  • INTER_NEAREST – 最近邻插值,这种方法简单直接。
  • INTER_LINEAR – 双线性插值,这是默认使用的方法。
  • INTER_AREA – 基于像素面积关系的重采样。这种方法在缩小图像时效果很好,可以避免出现摩尔纹。但当图像放大时,它的效果和最近邻插值差不多。
  • INTER_CUBIC – 三次插值,考虑周围4×4个像素。
  • INTER_LANCZOS4 – Lanczos插值,考虑周围8×8个像素。

想了解每种插值方法的效果,可以查看这里

80

如果你要放大图片,建议使用INTER_LINEAR或者INTER_CUBIC这种方法来处理。
如果你要缩小图片,建议使用INTER_AREA这种方法。

立方插值(Cubic interpolation)计算起来比线性插值(Linear interpolation)要复杂,所以速度会慢一些。不过,使用立方插值处理后,得到的图片质量会更好。

撰写回答