在libtiff保存文件的情况下进入这个,但现在我只是困惑。谁能告诉我为什么这两个不相同?
ar1 = zeros((1000,1000),dtype=uint16)
ar1 = ar1.view(dtype=uint8) # works
ar2 = zeros((1000,2000),dtype=uint16)
ar2 = ar2[:,1000:]
ar2 = ar2.view(dtype=uint8) # ValueError: new type not compatible with array.
编辑: 所以这也有效吗?
ar2 = zeros((1000,2000),dtype=uint16)
ar2 = array(ar2[:,1000:])
ar2 = ar2.view(dtype=uint8)
答案 0 :(得分:3)
简而言之,只需在切片前移动视图。
而不是:
ar2 = zeros((1000,2000),dtype=uint16)
ar2 = ar2[:,1000:]
ar2 = ar2.view(dtype=uint8)
执行:
ar2 = zeros((1000,2000),dtype=uint16)
ar2 = ar2.view(dtype=uint8) # ar2 is now a 1000x4000 array...
ar2 = ar2[:,2000:] # Note the 2000 instead of 1000!
正在发生的事情是切片数组不是连续的(如@Craig所指出的)和保守一侧的view
错误,并且不会尝试重新解释非连续的内存缓冲区。 (在这种情况下,它恰好是可能的,但在某些情况下,它会导致非均匀跨越的数组,而numpy不允许这样做。)
如果您对[{1}}不是很熟悉,可能会误解numpy
,而您确实需要view
。
astype
做什么?首先,让我们详细了解一下view
的作用。在这种情况下,如果可能的话,它会将numpy数组的内存缓冲区重新解释为新的数据类型。这意味着当您使用视图时,数组中元素的数量通常会发生变化。 (您也可以使用它将数组视为view
的不同子类,但我们暂时跳过该部分。)
您可能已经注意到以下情况(您的问题有点微妙),但如果没有,这是一个解释。
举个例子:
ndarray
如果您想使用新数据类型复制数组,请使用In [1]: import numpy as np
In [2]: x = np.zeros(2, dtype=np.uint16)
In [3]: x
Out[3]: array([0, 0], dtype=uint16)
In [4]: x.view(np.uint8)
Out[4]: array([0, 0, 0, 0], dtype=uint8)
In [5]: x.view(np.uint32)
Out[5]: array([0], dtype=uint32)
:
astype
现在让我们来看看查看2D数组时会发生什么。
In [6]: x
Out[6]: array([0, 0], dtype=uint16)
In [7]: x.astype(np.uint8)
Out[7]: array([0, 0], dtype=uint8)
In [8]: x.astype(np.uint32)
Out[8]: array([0, 0], dtype=uint32)
请注意,数组的形状已更改,并且更改已沿最后一个轴发生(在这种情况下,已添加了额外的列)。
乍一看,似乎已经添加了额外的零。 不正在插入额外的零,即In [9]: y = np.arange(4, dtype=np.uint16).reshape(2, 2)
In [10]: y
Out[10]:
array([[0, 1],
[2, 3]], dtype=uint16)
In [11]: y.view(np.uint8)
Out[12]:
array([[0, 0, 1, 0],
[2, 0, 3, 0]], dtype=uint8)
的{{1}}表示相当于两个uint16
s,其中一个值{{1}一个值为2
的值。因此,低于255的任何uint8
将导致该值和零,而超过该值的任何值将导致两个较小的2
s。举个例子:
0
Numpy数组由一个“原始”内存缓冲区组成,它通过一个形状,一个dtype和一个strides(以及一个偏移量来解释),但现在让我们忽略它。有关更多详细信息,有几个好的概述:the official documentation,the numpy book或scipy-lectures。
这允许numpy非常节省内存,并且可以通过许多不同方式“切片和切块”底层内存缓冲区而无需复制。
Strides告诉numpy在内存缓冲区内跳转多少字节沿特定轴增加一个增量。
例如:
uint16
因此,要在数组中更深一行,numpy需要在内存缓冲区中前进4个字节,而在数组中更远一列,numpy需要步骤2个字节。转置数组只是为了反转步幅(和形状,但在这种情况下,uint8
是2x2):
In [13]: y * 100
Out[14]:
array([[ 0, 100],
[200, 300]], dtype=uint16)
In [15]: (y * 100).view(np.uint8)
Out[15]:
array([[ 0, 0, 100, 0],
[200, 0, 44, 1]], dtype=uint8)
当我们将数组视为In [17]: y
Out[17]:
array([[0, 1],
[2, 3]], dtype=uint16)
In [18]: y.strides
Out[18]: (4, 2)
时,步幅会发生变化。我们仍然每行前进4个字节,但每列只有一个字节:
y
但是,numpy数组必须具有每个维度的一个步长。这就是“均匀跨越”的意思。换句话说,向前移动一行/列/无论如何,numpy需要能够每次通过底层内存缓冲区步进相同的量。换句话说,没有办法告诉numpy为每一行/列/其他任何步骤设置不同的金额。
出于这个原因,In [19]: y.T.strides
Out[19]: (2, 4)
采取了非常保守的路线。如果数组不连续,并且视图将更改数组的形状和步幅,则不会尝试处理它。正如@Craig所指出的那样,这是因为uint8
的切片与In [20]: y.view(np.uint8).strides
Out[20]: (4, 1)
不起作用是不连续的。
有很多情况(你的是一个),结果数组是有效的,但view
方法不会试图太聪明。
要真正发挥可能性,可以使用y
或直接使用__array_interface__
。这是一个很好的学习工具来试验它,但你必须真正理解你正在做些什么才能有效地使用它。
希望无论如何都有所帮助!对不起啰嗦的答案!
答案 1 :(得分:1)
这不是一个完整的答案,但可能指出我错过的细节。制作数组切片时,您不再拥有连续数据,也不拥有数据。要看这个看数组的标志:
ar2 = zeros((1000,2000),dtype=uint16)
ar2.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
ar2 = ar2[:,1000:]
ar2.flags
C_CONTIGUOUS : False
F_CONTIGUOUS : False
OWNDATA : False
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
我不知道其中哪一个导致实际问题。正如您在编辑中所注意到的,如果您创建切片数组的新副本,那么事情就可以了。您可以在注意时使用array()
或类似ar2=ar2[:,1000:].copy()
。