[英]Let's make a reference implementation of N-dimensional pixel binning/bucketing for python's numpy
我经常想要像素bin /像素桶一个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维参考实现,我会喜欢它。
优选地,函数调用将允许沿不同轴的不同的bin大小(可能类似于bucket(x, bucket_size=(2, 2, 3))
)
优选地,解决方案将是合理有效的(重塑和总和相当快速的numpy)
当数组没有很好地划分为整数个桶时,处理边缘效应的加成点。
允许用户选择初始bin边缘偏移的加分点。
正如Divakar所建议的那样,这是我在样本2-D案例中所希望的行为:
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的view_as_blocks
完成大部分繁琐的工作。 此函数使用as_strided
实现,因此它非常有效(它只是更改步幅信息以重塑数组)。 因为它是用Python / NumPy编写的,所以如果你没有安装skimage,你总是可以复制代码。
应用该函数后,您只需要对重新整形的数组的N个尾轴求和(其中N是bucket_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
案例指定沿每个轴的不同bin大小,可以沿着它的每个轴迭代地使用np.add.reduceat
,就像这样 -
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
本地来自as_strided:
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]])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.