Python 以3x3D阵列作为索引的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] ][

我有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] ][ 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
是最后一个箱子的右边缘