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

问题描述 投票:0回答:2

我正在弄清楚当我们设置

fx=2
fy=1
时,OpenCV resize() 函数如何计算线性插值。我写了以下最小工作示例,

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)

如果我们看第一行

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)

我不知道数字 191 和 64 是如何计算的。我知道它实现了

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

opencv resize interpolation
2个回答
7
投票

这与像素“网格”有关。

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

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

在 OpenCV 中,像素 centers 通常位于 integer 坐标处。这意味着第一个像素的左上角 corner 位于 (-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,...

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

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


0
投票

尽管Christoph Rackwitz的答案在精确线性插值计算方面非常详细,但它并没有帮助我理解双线性插值的思想。下面的图片对我帮助很大:

enter image description here

简单来说,我们获取原始像素值(2x2 网格),将它们放在新网格的角落(在 2x 调整大小的情况下为 4x4),然后使用系数(75% 和 25%)在它们之间插入值。对于像素 (0, 1),我们有 10 * 0.75 + 20 * 0.25 = 12。对于像素 (0, 2),我们有 10 * 0.25 + 20 * 0.75 = 17。我们对所有像素都这样做,并且就是这样!

您可以在本文中找到有关插值技术的更多详细信息

© www.soinside.com 2019 - 2024. All rights reserved.