Python 以3x3D阵列作为索引的numpy直方图
我有3x3D阵列,它们是3D rgb图像的红色、绿色和蓝色通道。在numpy中,创建输入通道的柱状图体积的优雅方法是什么 该操作相当于Python 以3x3D阵列作为索引的numpy直方图,python,numpy,Python,Numpy,我有3x3D阵列,它们是3D rgb图像的红色、绿色和蓝色通道。在numpy中,创建输入通道的柱状图体积的优雅方法是什么 该操作相当于 """ assume R, G and B are 3D arrays and output is a 3D array filled with zeros """ for x in x_dim: for y in y_dim: for z in z_dim: output[ R[x][y][z] ][
""" assume R, G and B are 3D arrays and output is a 3D array filled with zeros """
for x in x_dim:
for y in y_dim:
for z in z_dim:
output[ R[x][y][z] ][ G[x][y][z] ][ B[x][y][z] ] += 1
此代码对于大型图像来说太慢。numpy能否提高上述算法的效率?您可以使用它来计算n维数组的直方图。如果您不希望每个2d切片都有直方图,请确保将该维度的bin
设置为1
要获得整体直方图,您可以分别计算R、G和B通道的直方图,然后为每个位置取三个直方图的最大值。您可以使用
numpy.historogramdd
进行计算,但正如您所说,@jozzas提出的方法不起作用。您需要做的是将三个三维阵列中的每一个展平,然后将它们组合成二维阵列(x_dim*y_dim*z_dim,3)
,然后将其传递到Historogramdd
。由于空间信息与计算直方图无关,因此,原始数据是三维的这一事实是一种误导
以下是在通道多维数据集中使用随机数据的示例:
import numpy
n = 400 # approximate largest cube size that works on my laptop
# Fill channel cubes with random 8-bit integers
r = numpy.random.randint(256, size=(n,n,n)).astype(numpy.uint8)
g = numpy.random.randint(256, size=(n,n,n)).astype(numpy.uint8)
b = numpy.random.randint(256, size=(n,n,n)).astype(numpy.uint8)
# reorder data into for suitable for histogramming
data = numpy.vstack((r.flat, g.flat, b.flat)).astype(numpy.uint8).T
# Destroy originals to save space
del(r); del(g); del(b)
m = 256 # size of 3d histogram cube
hist, edges = numpy.histogramdd(
data, bins=m, range=((-0.5,255.5),(-0.5,255.5),(-0.5,255.5))
)
# Check that it worked
assert hist.sum() == n**3, 'Failed to conserve pixels'
这确实比您预期的要占用更多的内存,因为
historogramdd
似乎使用64位浮点来完成其工作,即使我们向它发送8位整数 假设8位通道,3元组整数(R,G,B)可以看作是以256为基数的单个数字:R*256**2+G*256+B
。因此,我们可以将3个数组R、G、B转换为单个“颜色值”数组,并使用np.bincount
生成所需的直方图
import numpy as np
def using_bincount(r,g,b):
r=r.ravel().astype('int32')
g=g.ravel().astype('int32')
b=b.ravel().astype('int32')
output=np.zeros((base*base*base),dtype='int32')
result=np.bincount(r*base**2+g*base+b)
output[:len(result)]+=result
output=output.reshape((base,base,base))
return output
def using_histogramdd(r,g,b):
data = np.vstack((r.flat, g.flat, b.flat)).astype(np.uint8).T
del(r); del(g); del(b)
hist, edges = np.histogramdd(
data, bins=base, range=([0,base],[0,base],[0,base])
)
return hist
np.random.seed(0)
n = 200
base = 256
r = np.random.randint(base, size=(n,n,n)).astype(np.uint8)
g = np.random.randint(base, size=(n,n,n)).astype(np.uint8)
b = np.random.randint(base, size=(n,n,n)).astype(np.uint8)
if __name__=='__main__':
bhist=using_bincount(r,g,b)
hhist=using_histogramdd(r,g,b)
assert np.allclose(bhist,hhist)
这些timeit结果表明,使用_bincount比使用_histogramdd更快,这可能是因为histogramdd是为处理浮动和范围内的容器而构建的,而bincount仅用于计算整数
% python -mtimeit -s'import test' 'test.using_bincount(test.r,test.g,test.b)'
10 loops, best of 3: 1.07 sec per loop
% python -mtimeit -s'import test' 'test.using_histogramdd(test.r,test.g,test.b)'
10 loops, best of 3: 8.42 sec per loop
我喜欢使用histogramdd的想法,只需将不需要使用的维度的容器设置为1。我不知道在哪里,但我要用它+1需要输出的信息是对应索引处每个输入的值。我不明白你如何分别计算每个通道的直方图,并在每个输入上保持(1,1,1)对齐。这样一个过程的输出会产生什么结果?我在想象输出,其中
output[x,y,z]
是R[:]==x和G[:]==y和B[:]==z
的计数。是这样的吗?是的,完全正确。我得到的印象是,您的输入通道数组是整数,在这种情况下,您可以指定bins=N
和range=((0,N-1)、(0,N-1)、(0,N-1))
,其中N
是离散像素值的数量(例如,每个通道8位的N=256
)。但是,除非x_dim
等比N
大得多,否则我想这将为您提供一个相当稀疏和嘈杂的直方图。因此,您可能需要将bins
参数设置为较小的值。有关更多详细信息,请参阅文档中的historogramdd
此操作有效,但仅适用于小于40x40x40的输入阵列。。。知道为什么吗?我已经编辑了我的答案,加入了示例代码。我可以让它在一台只有很少空闲内存的机器上,在高达400x400x400的阵列上工作。这花费了30秒的CPU时间,python进程的最大内存使用量似乎为1.5GB左右。你的阵列有多大?@AlvaroJoao换句话说,-0.5
是第一个箱子的左边缘,255.5
是最后一个箱子的右边缘