首页 > 解决方案 > OpenCV resize() 中的 INTER_LINEAR 插值如何工作?

问题描述

我正在弄清楚当我们设置fx=2和时 OpenCV resize() 函数如何计算线性插值fy=1。我写了以下最小工作示例,

import cv2
import numpy as np

pattern_img = np.zeros((6, 6), np.uint8)
pattern_img[:, 0::2] = 255
patteen_img_x2 = cv2.resize(pattern_img, None, fx=2, fy=1, interpolation=cv2.INTER_LINEAR)

如果我们查看 and 的第一行pattern_imgpattern_img_x2我们将有,

pattern_img[0, :]
> array([255,   0, 255,   0, 255,   0], dtype=uint8)
pattern_img_x2[0, :]
> array([[255, 191,  64,  64, 191, 191,  64,  64, 191, 191,  64,   0]], dtype=uint8)

我无法弄清楚数字 191 和 64 是如何计算的。我知道它实现了bilinear算法,但在这种情况下我们已经设置fy=1了,所以它应该是一个简单的沿 x 轴的线性插值。但我无法弄清楚如何resize()计算这些插值。有人可以帮我理解背后的算法吗?

标签: opencvresizeinterpolation

解决方案


这与像素“网格”有关。

0,0 是第一个像素的中心,还是它的左上角?像素的角在哪里?计算机图形学中的一个常见问题。

插值增加了另一个复杂性。一个像素是否定义了它的整个正方形区域?然后你得到最近邻插值。还是仅仅定义了中心点?然后,从技术上讲,介于两者之间的任何内容都是undefined,并且插值将决定如何填充空间。

通常在 OpenCV 中,像素中心位于整数坐标处。这意味着第一个像素的左上角位于 (-0.5, -0.5),所以这就是图片左上角的起点。

现在,如果您要使用 fx=1 进行采样,即恒等变换,您将从 -0.5 开始,这应该是像素的左边缘,并且输出像素的宽度为 1,因此第一个输出像素跨越 -0.5 到 +0.5,其中心位于 0.0

由于您想要 fx=2,因此您的输出像素为 0.5 宽。您仍然从 -0.5 开始,并且您的输出像素跨度... -0.5 到 0.0、0.0 到 +0.5、0.5 到 1.0、1.0 到 1.5...

他们的中心位于 -0.25, +0.25, +0.75, +1.25, ...

就是你获得那些 1/4 和 3/4 值的方式。64 是 255 的四分之一,191 是 255 的四分之三。这也是第一个输出像素为 255 的原因。它位于第一个输入像素的左侧,因此这是它的唯一支持,并确定其值的 100%。

你可以“索引移位”这一切,所以它更容易可视化。那么图片的左上角像素的左上角在(0,0),像素延伸到(1,1),中心在(0.5,0.5)。输出像素网格相应地位于,左上像素从 0 到 0.5,中心在 0.25,其右侧的邻居跨越 0.5 到 1.0,中心在 0.75,依此类推。

如果您想完全控制这种疯狂,请构建自己的仿射变换(我建议使用 3x3 矩阵,易于组合/矩阵乘法),然后使用warpAffine. 它将采用整数坐标作为输出,使用您的矩阵对其进行转换(它隐式反转它),并在源图像中查找结果坐标,包括在源图像空间中的插值。

像素网格:输入 像素网格:输入和输出

在这里做了一个小图形(点击查看完整尺寸)。黑色方块是输入像素,黑点是它们的中心。红色方块和圆点是输出像素及其中心。你看,如果你在红点位置采样,你会坐在输入像素中心之间的四分之三。


推荐阅读