【问题标题】:Numpy image slicing returning black patches/ wrong valuesNumpy图像切片返回黑色补丁/错误值
【发布时间】:2015-11-08 22:20:33
【问题描述】:

最终目标是拍摄一张图像并将其分割成我保存的样本。问题是我的切片随机返回黑色/不正确的补丁。 Bellow 是一个小示例程序。

import scipy.ndimage as ndimage
import scipy.misc as misc
import numpy as np

image32 = misc.imread("work0.png")
patches = np.zeros((36, 8, 8))
for i in range(4):
  for j in range(4):
    patches[i*4 + j] = image32[i:i+8,j:j+8]
    misc.imsave("{0}{1}.png".format(i,j), patches[i*4 + j])

我的图像的一个例子是:

0,0 的 8x8 补丁产生的补丁:

【问题讨论】:

    标签: python image numpy image-processing scipy


    【解决方案1】:

    两件事:

    1. 您将补丁矩阵初始化为错误的数据类型。默认情况下,numpy 将使补丁矩阵成为np.float64 类型,如果您将其与保存一起使用,您将无法获得预期的结果。具体来说,如果您咨询Mr. F's answer,实际上在浮点图像上执行了一些缩放,其中图像的最小值和最大值分别缩放为黑色和白色,因此如果您的图像在背景中完全一致,最小值和最大值都将相同,并将显示为黑色。因此,最好的办法是尊重原始图像的数据类型,即将补丁矩阵的dtype 设置为np.uint8

    2. 从您的 for 循环索引判断,您想要提取出 8 x 8 的 非重叠补丁。这意味着,如果您有一个 32 x 32 的图像和 8 x 8 块,那么您总共有 16 个块排列在 4 x 4 网格中。

    因此,您需要更改 patches 语句,使其在第一维中具有 16,而不是 36。此外,您必须调整索引到图像的方式以提取 8 x 8 个补丁,因为现在补丁是重叠的。具体来说,您想让图像补丁索引从8*i8*(i+1) 的行和8*j8*(j+1) 的列。如果您自己替换 ij 的样本值,您会看到我们为您的图像中的每个网格获得了唯一的 8 x 8 补丁。


    以上两点我都注意到了,修改后的代码应该是:

    import scipy.ndimage as ndimage
    import scipy.misc as misc
    import numpy as np
    
    image32 = misc.imread('work0.png')
    
    patches = np.zeros((16,8,8), dtype=np.uint8) # Change
    
    for i in range(4):
        for j in range(4):
            patches[i*4 + j] = image32[8*i:8*(i+1),8*j:8*(j+1)] # Change
            misc.imsave("{0}{1}.png".format(i,j), patches[i*4 + j])
    

    当我这样做并查看输出图像时,我得到了我所期望的。


    绝对确定,让我们使用matplotlib 绘制段。您已经方便地将所有补丁保存在 patches 中,因此显示我们需要的内容应该不是问题。但是,我将在 cmets 中放置一些代码,以便您可以读取使用上述代码从磁盘保存的图像,这样您就可以验证它是否仍然有效,无论查看 patches 或磁盘上的图像:

    import matplotlib.pyplot as plt
    
    plt.figure()
    for i in range(4):
        for j in range(4):
            plt.subplot(4, 4, 4*i + j + 1)
            img = patches[4*i + j]
            # or you can do this:
            # img = misc.imread('{0}{1}.png'.format(i,j))
            img = np.dstack([img, img, img])
            plt.imshow(img)
    
    plt.show()
    

    matplotlib.pyplot.imshow 的奇怪之处在于,如果您的图像是单通道图像(例如您的情况),并且周围具有相同的强度,那么无论颜色图是什么,它都会被可视化为黑色,就像我们对imsave 的体验。因此,我必须人为地将其制作为 RGB 图像,但所有通道都相同,以便在显示图像之前将其可视化为灰度。

    我们得到:

    【讨论】:

    • 谢谢,索引已被简化以演示该问题。我正在通过 theano 运行它,最终将被转换为 float32,......我认为这不会导致任何问题。无论如何,谢谢你的帮助!
    • @Dr.Knowitall - 没问题!很高兴我能帮忙:)
    • @Dr.Knowitall - 我还添加了代码来显示每个补丁,只是为了验证我们所拥有的是否可靠。
    【解决方案2】:

    根据this answer,问题在于imsave 对数据进行了规范化,以便将计算出的最小值定义为黑色(如果有明显的最大值,则定义为白色) .

    这让我开始深入研究为什么建议使用 uint8 确实可以创建所需的输出。事实证明,在源代码中有一个名为 bytescale 的函数在内部被调用。

    实际上,imsave 本身就是一个very thin wrapper around toimage,后跟save(来自图像对象)。 Inside of toimage if mode is None (which it is by default), that's when bytescale gets invoked.

    原来是that bytescale has an if statement that checks for the uint8 data type,如果数据是这种格式,它会返回未更改的数据。但如果不是,则数据将根据最大和最小转换进行缩放(其中 0 和 255 是要比较的默认低像素值和高像素值)。

    这是上面链接代码的完整sn-p:

    if data.dtype == uint8:
        return data
    
    if high < low:
        raise ValueError("`high` should be larger than `low`.")
    
    if cmin is None:
        cmin = data.min()
    if cmax is None:
        cmax = data.max()
    
    cscale = cmax - cmin
    if cscale < 0:
        raise ValueError("`cmax` should be larger than `cmin`.")
    elif cscale == 0:
        cscale = 1
    
    scale = float(high - low) / cscale
    bytedata = (data * 1.0 - cmin) * scale + 0.4999
    bytedata[bytedata > high] = high
    bytedata[bytedata < 0] = 0
    return cast[uint8](bytedata) + cast[uint8](low)
    

    对于你的数据块都是 255,cscale 将是 0,将检查并更改为 1。然后行

    bytedata = (data * 1.0 - cmin) * scale + 0.4999
    

    将导致整个图像块的浮点值为 0.4999,因此在下一个代码块中显式设置为 0(当从浮点转换为 uint8 时),例如:

    In [102]: np.cast[np.uint8](0.4999)
    Out[102]: array(0, dtype=uint8)
    

    您可以在bytescale 的正文中看到,只有两种可能的返回方式:您的数据类型为uint8 并且按原样返回,否则它会经历这种愚蠢的缩放过程。因此,最后,将uint8 用于通过这些函数专门从图像格式加载或保存为图像格式的代码片段确实是正确且良好的做法。

    所以这一系列的东西就是为什么你在输出的图像文件中得到全零以及为什么使用dtype=np.uint8 的其他建议实际上对你有帮助。这并不是因为您需要避免图像的浮点数据,只是因为imsave 有这种奇怪的惯例来检查和缩放数据。

    【讨论】:

    • 我只是想指出,我并没有说 OP 需要避免浮点数据......我只是说他/她应该尊重原始输入图像的类型。话虽如此,伟大的侦探工作。 +1。
    • 总之,我不是有意暗示。我在一秒钟前补充说,从bytescale 的正文中可以看到,确实唯一 从该函数返回而不涉及奇怪缩放的方法是使用uint8,所以它可能是各方面的最佳实践。
    • 这是您提供的一些很棒的见解。我不知道imsave 实际上对浮点数据做了一些缩放。我总是用dtype=np.uint8 保存图像以确保使用imsave 的安全...事实证明,侥幸这样做总是给我正确的结果哈哈。非常感谢。
    • 我希望我也能接受你的回答,这是惊人的信息!
    • 不用担心。我只需要知道我正在帮助保护少量图像数据的安全。
    猜你喜欢
    • 2016-07-27
    • 2015-06-19
    • 2014-04-27
    • 1970-01-01
    • 2011-05-15
    • 2018-01-03
    • 1970-01-01
    • 2011-08-08
    相关资源
    最近更新 更多