Python中的中位数下采样

3
我想通过中值滤波对图像进行下采样。看起来,使用skimage中的block_reduce方法和func=numpy.median似乎很合适。然而,我有一种感觉,block_reduce会一次在一个轴上应用func,而我希望中值滤波能够一次应用到整个块中——对于非平凡的输入,结果并不相同。
以下是一个最小演示示例:
from skimage.measure import block_reduce
import numpy as np

image = np.array([[4, 6, 6, 2],
 [6, 7, 9, 9],
 [3, 0, 9, 0],
 [0, 6, 6, 4]])

expected = np.array([[np.median(image[0:2,0:2]), np.median(image[0:2,2:4])],
            [np.median(image[2:4,0:2]), np.median(image[2:4,2:4])]])

actual = block_reduce(image, (2,2), func=np.median)

assert np.array_equal(expected, actual)

最后一项断言失败是因为中值滤波器的应用方式不符合我的预期。

您能否建议其他使用中值滤波器进行图像下采样的方法?

2个回答

1
更方便的解决方案是使用view_as_blocks(),该函数将图像重塑以引入新的轴线,使您可以通过前两个轴线寻址块,并通过最后两个轴线寻址每个块内的像素。
对于形状为(Y, X)的图像,如果您想将其分成2x2的块,则块视图将具有尺寸(Y/2, X/2, 2, 2)。一旦您获得了数据的这个视图,使用np.median计算每个块的中位数就很容易了。
In [15]: import numpy as np
    ...: from skimage.util import view_as_blocks
    ...:
    ...: image = np.array(
    ...:     [[4, 6, 6, 2],
    ...:      [6, 7, 9, 9],
    ...:      [3, 0, 9, 0],
    ...:      [0, 6, 6, 4]])
    ...:
    ...:
    ...: block_view = view_as_blocks(image, (2,2))
    ...: block_view
Out[15]:
array([[[[4, 6],
         [6, 7]],

        [[6, 2],
         [9, 9]]],


       [[[3, 0],
         [0, 6]],

        [[9, 0],
         [6, 4]]]])

In [16]: downsampled = np.median(block_view, axis=[2,3])
    ...: downsampled
Out[16]:
array([[6. , 7.5],
       [1.5, 5. ]])

将其概括成一个适用于ND图像和(ND块)的函数:
import numpy as np
from skimage.util import view_as_blocks

def blockwise_median(a, blockshape):
    assert a.ndim == len(blockshape), \
        "blocks must have same dimensionality as the input image"
    assert not (np.array(a.shape) % blockshape).any(), \
        "blockshape must divide cleanly into the input image shape"

    block_view = view_as_blocks(a, blockshape)
    assert block_view.shape[a.ndim:] == blockshape
    block_axes = [*range(a.ndim, 2*a.ndim)]
    return np.median(block_view, axis=block_axes)


In [18]: image = np.array(
    ...:     [[4, 6, 6, 2],
    ...:      [6, 7, 9, 9],
    ...:      [3, 0, 9, 0],
    ...:      [0, 6, 6, 4]])
    ...:
    ...: blockwise_median(image, (2,2))
Out[18]:
array([[6. , 7.5],
       [1.5, 5. ]])

1
这里有一些针对你特定问题的 技巧,可以解决它。
def clever_func(block, axis):
    # axis unused on purpose
    if len(block.shape) == 4:
        return np.median(block, axis=[2, 3])
    else:
        return block

actual = block_reduce(image, (2,2), func=clever_func)

基本上,在代码中,block_reduce 中的参数func 将被调用为 func(out, axis=-1) (source)。我已经篡改了它,以便正确地使用axis参数调用中位数,而不是调用numpy.median
在您的示例中,第一次调用时block.shape将为(2, 2, 2, 2),然后在clever_func的第二次调用中为(2, 2, 2)。我仅在第一次调用时使用np.median (if len(block.shape) == 4),第二次调用时不使用任何东西。 这不是一个很好的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接