让';为python';提供一个N维像素装箱/装箱的参考实现;努比

让';为python';提供一个N维像素装箱/装箱的参考实现;努比,python,arrays,image,numpy,image-processing,Python,Arrays,Image,Numpy,Image Processing,我经常想要一个numpy数组,这意味着用单个像素替换N连续像素组,该像素是N替换像素的总和。例如,从以下值开始: x = np.array([1, 3, 7, 3, 2, 9]) 如果桶大小为2,则转换为: bucket(x, bucket_size=2) = [1+3, 7+3, 2+9] = [4, 10, 11] 据我所知,没有专门做这件事的numpy函数(如果我错了,请纠正我!),所以我经常使用自己的函数。对于1d numpy阵列,这还不错: import numpy as np

我经常想要一个numpy数组,这意味着用单个像素替换
N
连续像素组,该像素是
N
替换像素的总和。例如,从以下值开始:

x = np.array([1, 3, 7, 3, 2, 9])
如果桶大小为2,则转换为:

bucket(x, bucket_size=2) 
= [1+3, 7+3, 2+9]
= [4, 10, 11]
据我所知,没有专门做这件事的numpy函数(如果我错了,请纠正我!),所以我经常使用自己的函数。对于1d numpy阵列,这还不错:

import numpy as np

def bucket(x, bucket_size):
    return x.reshape(x.size // bucket_size, bucket_size).sum(axis=1)

bucket_me = np.array([3, 4, 5, 5, 1, 3, 2, 3])
print(bucket(bucket_me, bucket_size=2)) #[ 7 10  4  5]
…然而,我很容易对多维情况感到困惑,最终我一次又一次地推出了我自己的错误、半途而废的解决方案来解决这个“简单”的问题。如果我们能建立一个好的N维参考实现,我会很高兴的

  • 函数调用最好允许沿不同轴的不同箱子大小(可能类似于
    桶(x,桶大小=(2,2,3))

  • 最好该解决方案具有合理的效率(在numpy中重塑和求和相当快)

  • 当数组不能很好地划分为整数个存储桶时,处理边缘效果的额外点数

  • 允许用户选择初始箱子边缘偏移的额外点数

正如Divakar所建议的,以下是我在一个二维示例中想要的行为:

x = np.array([[1, 2, 3, 4],
              [2, 3, 7, 9],
              [8, 9, 1, 0],
              [0, 0, 3, 4]])

bucket(x, bucket_size=(2, 2))
= [[1 + 2 + 2 + 3, 3 + 4 + 7 + 9],
   [8 + 9 + 0 + 0, 1 + 0 + 3 + 4]]
= [[8, 23],
   [17, 8]]

…希望我的算术正确;)

我想你可以用skimage's做大部分精细的工作。此函数非常有效(它只是更改步幅信息以重塑阵列)。因为它是用Python/NumPy编写的,所以如果没有安装skimage,您可以随时复制代码

应用该函数后,只需对重塑数组的N个尾随轴求和(其中N是
bucket\u size
元组的长度)。这里有一个新的
bucket()
函数:

from skimage.util import view_as_blocks

def bucket(x, bucket_size):
    blocks = view_as_blocks(x, bucket_size)
    tup = tuple(range(-len(bucket_size), 0))
    return blocks.sum(axis=tup)
例如:

>>> x = np.array([1, 3, 7, 3, 2, 9])
>>> bucket(x, bucket_size=(2,))
array([ 4, 10, 11])

>>> x = np.array([[1, 2, 3, 4],
                  [2, 3, 7, 9],
                  [8, 9, 1, 0],
                  [0, 0, 3, 4]])

>>> bucket(x, bucket_size=(2, 2))
array([[ 8, 23],
       [17,  8]])

>>> y = np.arange(6*6*6).reshape(6,6,6)
>>> bucket(y, bucket_size=(2, 2, 3))
array([[[ 264,  300],
        [ 408,  444],
        [ 552,  588]],

       [[1128, 1164],
        [1272, 1308],
        [1416, 1452]],

       [[1992, 2028],
        [2136, 2172],
        [2280, 2316]]])

要为
ndarray
案例沿每个轴指定不同的仓位大小,可以沿每个轴迭代使用,如下所示-

def bucket(x, bin_size):
    ndims = x.ndim
    out = x.copy()
    for i in range(ndims):
        idx = np.append(0,np.cumsum(bin_size[i][:-1]))
        out = np.add.reduceat(out,idx,axis=i)
    return out
样本运行-

In [126]: x
Out[126]: 
array([[165, 107, 133,  82, 199],
       [ 35, 138,  91, 100, 207],
       [ 75,  99,  40, 240, 208],
       [166, 171,  78,   7, 141]])

In [127]: bucket(x, bin_size = [[2, 2],[3, 2]])
Out[127]: 
array([[669, 588],
       [629, 596]])

#  [2, 2] are the bin sizes along axis=0
#  [3, 2] are the bin sizes along axis=1

# array([[165, 107, 133, | 82, 199],
#        [ 35, 138,  91, | 100, 207],
# -------------------------------------
#        [ 75,  99, 40,  | 240, 208],
#        [166, 171, 78,  | 7, 141]])

In [128]: x[:2,:3].sum()
Out[128]: 669

In [129]: x[:2,3:].sum()
Out[129]: 588

In [130]: x[2:,:3].sum()
Out[130]: 629

In [131]: x[2:,3:].sum()
Out[131]: 596

从土生土长的地方开始:

x = array([[1, 2, 3, 4],
           [2, 3, 7, 9],
           [8, 9, 1, 0],
           [0, 0, 3, 4]])

from numpy.lib.stride_tricks import as_strided     
def bucket(x,bucket_size):
      x=np.ascontiguousarray(x)
      oldshape=array(x.shape)
      newshape=concatenate((oldshape//bucket_size,bucket_size))
      oldstrides=array(x.strides)
      newstrides=concatenate((oldstrides*bucket_size,oldstrides))
      axis=tuple(range(x.ndim,2*x.ndim))
      return as_strided (x,newshape,newstrides).sum(axis)
如果某个尺寸标注未均匀划分为相应的x尺寸标注,则会丢失其余的图元

核查:

In [9]: bucket(x,(2,2))
Out[9]: 
array([[ 8, 23],
       [17,  8]])

如果bucket的一个维度没有划分为数组的相应维度,则会引发一个错误,并且该函数根本不会执行——剩余的元素不会丢失。(同样,这个答案与我35分钟前建议的方法相同:-)我的测试没有出现错误。我写它是作为视图块的补充,它强制进行精确的分割。抱歉耽搁了,我不得不想;)