为什么我的3D numpy数组中的值在写入文件时会发生变化?

时间:2016-05-27 13:02:19

标签: python arrays opencv numpy scipy

奇怪的问题是我有一个标签的3D数组(比如1-36),名为labelled_stack。这些只是数组中的值等于给定标签的区域。带有5个标签的快速2D示例如下:

labelled_stack = (0 0 0 0 0 0 0 0 0 0)
                 (0 1 1 0 0 0 2 2 2 0)
                 (0 1 0 0 3 0 0 2 2 0)
                 (0 0 0 3 3 0 0 0 0 0)
                 (0 4 0 0 3 0 0 0 5 0)
                 (0 4 0 0 0 0 5 5 5 0)
                 (0 0 0 0 0 0 0 0 0 0)

但想象一下它的阵容......

我尝试使用cv2.imwrite和scipy.misc.imsave来保存堆栈,但是当我这样做然后打开它们时,它们的值已经改变,因此值= 5,现在等于255,并且值相等到1,等于51等。这是不可接受的。我需要值保持原样..以1为单位增加整数值。我知道cv2和scipy.misc都写为8位图像,但这并不意味着我得到了这个错误。

我甚至已经将图像重新读回到python中,以检查这是否正在发生。它是。

我的labelled_stack类型为np.uint32

编辑以包含保存命令:
for j in np.arange(0,l,1):
     misc.imsave(save_path+Folder+'/Labelled/slice_{:04d}of{:}.tif'.format(j+1,l),labelled_stack[:,:,j])

...或

cv2.imwrite(save_path+Folder+'/Labelled/slice_{:04d}of{:}.tif'.format(j+1,l),labelled_stack[:,:,j])

2 个答案:

答案 0 :(得分:3)

您可以阅读documentation

  

imwrite函数将图像保存到指定的文件中。基于文件扩展名选择图像格式(请参阅imread()以获取扩展名列表)。 仅PNG,JPEG 2000和TIFF的8位(或16位无符号(CV_16U))单通道或3通道(带'BGR'通道顺序)图像可以使用此功能保存

你说“labelled_stack的类型为np.uint32”(32位无符号整数)。那就是问题所在。你需要自己处理类型转换。

答案 1 :(得分:3)

函数imwrite以8位格式保存数据。当函数将32位数据转换为8位时,它会将数据缩放为使用8位的完整位深度,因此例如,您的数据的最大值可扩展到255.有关其他示例,请参阅scipy imsave saves wrong values

要避免这种情况,请在保存之前将数据转换为numpy.uint8。在以下示例中,a的数据类型为numpy.uint32

In [98]: from scipy.misc import imsave, imread

In [99]: a
Out[99]: 
array([[0, 3, 2, 2, 4, 0, 3, 0],
       [2, 0, 0, 3, 3, 1, 3, 0],
       [2, 4, 4, 0, 2, 3, 1, 3],
       [0, 1, 3, 1, 0, 0, 0, 4],
       [2, 1, 1, 2, 1, 1, 3, 1],
       [0, 4, 0, 1, 0, 0, 2, 3],
       [3, 1, 3, 3, 3, 2, 3, 4],
       [0, 4, 1, 4, 2, 2, 0, 2]], dtype=uint32)

astype方法用于在将数组提供给numpy.uint8函数之前将其转换为imsave

In [100]: imsave("a.tif", a.astype(np.uint8))  # Convert to 8 bit before saving

使用scipy.misc.imread读回来:

In [101]: b = imread("a.tif")

In [102]: b
Out[102]: 
array([[0, 3, 2, 2, 4, 0, 3, 0],
       [2, 0, 0, 3, 3, 1, 3, 0],
       [2, 4, 4, 0, 2, 3, 1, 3],
       [0, 1, 3, 1, 0, 0, 0, 4],
       [2, 1, 1, 2, 1, 1, 3, 1],
       [0, 4, 0, 1, 0, 0, 2, 3],
       [3, 1, 3, 3, 3, 2, 3, 4],
       [0, 4, 1, 4, 2, 2, 0, 2]], dtype=uint8)

请注意,使用scipy.misc.imread读取数据时,结果的数据类型为numpy.uint8

另一个推荐的选项,特别是当您尝试保留需要16位或32位的数据时,使用tifffile

在此示例中,a的数据类型为numpy.uint32,其中一些值大于无符号的8位数。

In [152]: from tifffile import imsave, imread

In [153]: a
Out[153]: 
array([[  0,   5,  10,  15,  20,  25,  30,  35,  40,  45],
       [ 50,  55,  60,  65,  70,  75,  80,  85,  90,  95],
       [100, 105, 110, 115, 120, 125, 130, 135, 140, 145],
       [150, 155, 160, 165, 170, 175, 180, 185, 190, 195],
       [200, 205, 210, 215, 220, 225, 230, 235, 240, 245],
       [250, 255, 260, 265, 270, 275, 280, 285, 290, 295],
       [300, 305, 310, 315, 320, 325, 330, 335, 340, 345],
       [350, 355, 360, 365, 370, 375, 380, 385, 390, 395],
       [400, 405, 410, 415, 420, 425, 430, 435, 440, 445],
       [450, 455, 460, 465, 470, 475, 480, 485, 490, 495]], dtype=uint32)

In [154]: imsave("a.tif", a)

In [155]: b = imread("a.tif")

In [156]: b
Out[156]: 
array([[  0,   5,  10,  15,  20,  25,  30,  35,  40,  45],
       [ 50,  55,  60,  65,  70,  75,  80,  85,  90,  95],
       [100, 105, 110, 115, 120, 125, 130, 135, 140, 145],
       [150, 155, 160, 165, 170, 175, 180, 185, 190, 195],
       [200, 205, 210, 215, 220, 225, 230, 235, 240, 245],
       [250, 255, 260, 265, 270, 275, 280, 285, 290, 295],
       [300, 305, 310, 315, 320, 325, 330, 335, 340, 345],
       [350, 355, 360, 365, 370, 375, 380, 385, 390, 395],
       [400, 405, 410, 415, 420, 425, 430, 435, 440, 445],
       [450, 455, 460, 465, 470, 475, 480, 485, 490, 495]], dtype=uint32)