OpenCV resize() 中的 INTER_LINEAR 插值如何工作?

Ras*_*oul 1 interpolation opencv resize

我正在弄清楚当我们设置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)
Run Code Online (Sandbox Code Playgroud)

如果我们看pattern_img和的第一行pattern_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)
Run Code Online (Sandbox Code Playgroud)

我不知道数字 191 和 64 是如何计算的。我知道它实现了bilinear算法,但在本例中我们设置了fy=1,所以它应该是沿 x 轴的简单线性插值。但我不知道如何resize()计算这些插值数字。有人可以帮助我理解背后的算法吗?

Chr*_*itz 6

这与像素“网格”有关。

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

插值又增加了另一个复杂性。一个像素是否定义了它的整个正方形区域?然后你得到最近邻插值。或者它仅仅定义了中心点?那么,介于两者之间的任何内容都是未定义的,并且插值可以决定如何填充空间。

在 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. 它将获取输出的整数坐标,使用矩阵对其进行转换(它隐式反转它),并在源图像中查找结果坐标,包括源图像空间中的插值。

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

在这里做了一个小图形(点击查看大图)。黑色方块是输入像素,黑点是其中心。红色方块和点是输出像素及其中心。您会看到,如果您在红点位置采样,您将位于输入像素中心之间的四分之三处。