繁体   English   中英

线性插值矢量化以加快矩阵计算

[英]Linear interpolation vectorized for faster calculation matrixwise

要校准 Python 中的大量图片数据,我必须对每个像素应用元素校正。 因此,我得到了一个包含 200x200x2x9 值的矩阵。 200x200 代表像素位置,2x9 是 9 对值,代表一个基数和一个相应的校正值对于这个需要线性插值的特定像素,多项式插值效果不是很好。 例如,这看起来像这样:

插值示例图

现在我有大量图片,为了节省时间,我想尽快应用每个像素的校正。 我在想一个矢量化的function。图片的输入值应该是在插值的多个点内基于position的校正。

for 循环花费了我大量的时间。

有人可以帮我吗?

***编辑

即,对于单个像素,您希望如何组合标量像素值和 2x9 插值矩阵。 结果的形状是什么,插值是如何工作的?

图片中的每个像素都有一个介于 0 和 16383 之间的值。我想找出这个值在一系列线性插值中的位置。 在上面的例子中显示了 9 对灰度值和相应的校正。 因此,如果像素的值约为 6000,则计算出的校正值为 -15。 计算之后,我想要一个 200x200 的矩阵,其值(在本例中)介于 ±15 之间。

对于单个图像,您可以以矢量化方式应用校正。 首先,这里是一些随机示例数据:

import numpy as np

rng = np.random.default_rng(42)

sample_image = rng.integers(0, 2**14, size=(200,200), dtype=np.uint16)

per_px_correction = np.empty((200,200, 9, 2), dtype=np.int32)
# anchors of known corrections
per_px_correction[..., 0] = rng.integers(0, 2**14, size=(200,200, 9), dtype=np.int32)
# correction values at anchors
per_px_correction[..., 1] = rng.integers(-15, 15, size=(200,200, 9), dtype=np.int32)

per_px_correction = np.sort(per_px_correction, axis=-2)
per_px_correction = np.swapaxes(per_px_correction, -2, -1)

顺便说一句,您可以使用以下步骤将其应用于图像:

# introduce some useful names and update dtypes
img_normal = sample_image.astype(np.float64) / 2**14
anchors = per_px_correction[..., 0, :].astype(np.float64) / 2**14
values = per_px_correction[..., 1, :]

# work out the anchor points of the interpolation
anchor_is_greater = anchors > img_normal[..., None]
upper_anchor = np.argmax(anchor_is_greater, axis=-1)
upper_anchor = np.where(
    (upper_anchor == 0) & (anchor_is_greater[..., 0] == False),
    anchors.shape[-1] - 1,
    upper_anchor,
)
lower_anchor = np.maximum(upper_anchor - 1, 0)

# work out the corresponding values
X, Y = np.meshgrid(np.arange(200), np.arange(200))
upper_value = values[Y, X, upper_anchor]
lower_value = values[Y, X, lower_anchor]

# work out correction at pixel position
alpha = (img_normal - lower_anchor) / (upper_anchor - lower_anchor)
correction = (upper_value - lower_value) * alpha + lower_value
correction = np.round(correction).astype(np.int32)  # back to integer types

# apply correction
img_corrected = np.clip(sample_image + correction, 0, 2**14).astype(np.uint16)

一位朋友评论说这个“看起来很奇怪 numpy magix”,所以这里简单解释一下发生了什么:

  • 在第一个块中,我们拆分校正矩阵并将图像和锚点值归一化为[0, 1] 这使得工作更容易,因为我们得到了描述性的名称,并且更容易计算出像素值位于两个锚点之间的位置。
  • 在第二个块中,我们计算出每个像素的相邻锚点,即大于像素值的最近锚点和小于像素值的最近锚点。 为此,我假设校正数组是按锚点排序的(我们在构建过程中这样做了)。 这样,我们只需要计算出最低的上界; 最大的下界就是前一个锚点。 在计算upper anchor的同时,我们还需要处理不存在upper anchor的特殊情况(像素值大于最大的anchor)。 在这种情况下,numpy 将返回 np.argmax(...)=0。 因此,0 要么意味着每个锚点都大于像素值,要么没有锚点大于像素值。 为了消除歧义,我们使用np.where
  • 然后我们使用花式索引(高级索引)来获取与每个锚点对应的校正值。
  • 然后我们应用插值来计算给定值的校正。 为此,我们首先计算出像素位于两个锚点之间的位置 ( alpha ),其中 0 表示低锚点,1 表示高锚点,然后我们执行插值。
  • 最后,我们采用计算出的校正系数并将其应用于图像。

要将其应用于多张图像,您可以一次或分批将其应用于一张图像,即,沿着第 0 轴堆叠多批图像和多批校正矩阵,然后批量应用上述代码片段。 不过要小心,不要炸毁你的 memory,因为堆叠大量图像会很快耗尽可用 RAM。

我们首先定义一些测试代码,以便我们有一个最小的可重现示例。 为简单起见,我对每个像素的插值使用相同的基点。 但是,为了测试稳健性,我定义了范围,以便像素低于最低基点,像素高于最高基点。

image = np.random.randint(0, 16383, (200, 200), dtype='i2')

correction = np.empty((200, 200, 2, 9), dtype='i2')
correction[..., 0, :] = np.linspace(
    0, 16383, num=11, endpoint=False, dtype='i2')[1:-1]
correction[..., 1, :] = np.random.randint(-16, 17, (200, 200, 9), dtype='i2')

为了使索引更简单,我们将图像展平为一维并将组件与校正矩阵分开。

origshape = image.shape
image = image.ravel()
correction = correction.reshape(image.shape + correction.shape[2:])
bases = correction[:, 0].T
basevalues = correction[:, 1].T

现在我们要找到每个像素的取值范围。

inrange = (image >= bases[:-1]) & (image <= bases[1:])
belowrange = image < bases[0]
aboverange = image > bases[-1]

然后我们从校正中选择相应的值。 我们可以为此使用np.choosenp.select 此步骤可以使用一些调整,以便该方法适用于花式索引。 对于高于或低于基点的值,结果将无效。 我们将在稍后的步骤中更正此问题。 请注意,对于这些情况,下限和上限默认为 0 和 1。 这避免了这些情况下的插值问题。

lowerbases = np.select(inrange, bases[:-1])
upperbases = np.select(inrange, bases[1:], default=1)
lowervalues = np.select(inrange, basevalues[:-1])
uppervalues = np.select(inrange, basevalues[1:])

现在我们应用一个普通的旧线性插值。

upperpercent = (image - lowerbases).astype('f4') / (upperbases - lowerbases)
lowerpercent = 1. - upperpercent
correctionvalues = (lowerpercent * lowervalues
                    + upperpercent * uppervalues).astype('i2')

对于超出范围的值,我们可以应用任何我们想要的条件。 我选择用最低值或最高值进行校正。 您可以选择外推。

correctionvalues[belowrange] = basevalues[0, belowrange]
correctionvalues[aboverange] = basevalues[-1, aboverange]

现在剩下的就是应用这些值并将图像转回 2D

image += correctionvalues
image = image.reshape(origshape)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM