Python 添加numpy数组时避免溢出

Python 添加numpy数组时避免溢出,python,numpy,image-processing,integer-overflow,numpy-ndarray,Python,Numpy,Image Processing,Integer Overflow,Numpy Ndarray,我想添加带有datatyp uint8的numpy数组。我知道这些数组中的值可能大到足以发生溢出。因此,我得到了如下结果: a = np.array([100, 200, 250], dtype=np.uint8) b = np.array([50, 50, 50], dtype=np.uint8) a += b 现在,a是[150 250 44]。但是,我希望uint8允许的最大值太大,而不是溢出。所以我想要的结果是[150250255] 我可以通过以下代码获得此结果: a = np.arr

我想添加带有datatyp uint8的numpy数组。我知道这些数组中的值可能大到足以发生溢出。因此,我得到了如下结果:

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
a += b
现在,a是
[150 250 44]
。但是,我希望uint8允许的最大值太大,而不是溢出。所以我想要的结果是
[150250255]

我可以通过以下代码获得此结果:

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
c = np.zeros((1,3), dtype=np.uint16)
c += a
c += b
c[c>255] = 255
a = np.array(c, dtype=np.uint8)
问题是,我的数组非常大,因此创建第三个具有更大数据类型的数组可能会造成内存问题。是否有一种快速且更高效的方法来实现所描述的结果?

这样做如何

>>> a + np.minimum(255 - a, b)
array([150, 250, 255], dtype=uint8)
通常,使用获取数据类型的最大值

np.iinfo(np.uint8).max

您可以通过创建第三个数据类型为uint8的数组,再加上一个bool数组(它们加在一起比一个uint16数组更节省内存)来实现这一点

用于避免临时数组

a = np.array([100, 200, 250], dtype=np.uint8)
b = np.array([50, 50, 50], dtype=np.uint8)
c = 255 - b  # a temp uint8 array here
np.putmask(a, c < a, c)  # a temp bool array here
a += b
如果您的阵列非常大,那么这种方法的内存效率最高。同样,它在处理时间上是昂贵的,因为它用较慢的2dim数组索引替换了超快速的整数加法

解释它是如何工作的

上面的
c
数组的构造利用了numpy广播技巧。将shape
(N,)
数组和shape
(1,N)
数组添加为
(N,N)
-类似,因此结果是所有可能和的NxN数组。然后,我们剪辑它。对于每个i,j,我们得到一个满足以下条件的2dim数组:
c[i,j]=min(i+j,255)

然后,剩下的就是使用奇妙的索引来获取正确的值。使用您提供的输入,我们可以访问:

c[( [100, 200, 250] , [50, 50, 50] )]
第一个索引数组表示第一个dim,第二个索引数组表示第二个dim。因此,结果是一个与索引数组形状相同的数组(
(N,)
),由值
[c[100,50]、c[200,50]、c[250,50]]
组成

>>> a = np.array([100, 200, 250], dtype=np.uint8)
>>> b = np.array([50, 50, 50], dtype=np.uint8)
>>> a+=b; a[a<b]=255
>>> a
array([150, 250, 255], dtype=uint8)
>a=np.array([100200250],dtype=np.uint8)
>>>b=np.array([50,50,50],dtype=np.uint8)
>>>a+=b;a[a>>a
数组([150250255],dtype=uint8)

您可以使用Numba真正做到这一点,例如:

import numba

@numba.jit('void(u1[:],u1[:])', locals={'temp': numba.uint16})
def add_uint8_inplace_clip(a, b):
    for i in range(a.shape[0]):
        temp = a[i] + b[i]
        a[i] = temp if temp<256 else 255

add_uint8_inplace_clip(a, b)
import numexpr

numexpr.evaluate('where((a+b)>255, 255, a+b)', out=a, casting='unsafe')
Numexpr
uint8
int32
内部,然后再将其放回
uint8
数组中。

对此有以下几点:

numpy.nan_to_num(x)[来源]

将nan替换为零,将inf替换为有限数

返回一个数组或标量,该数组或标量将非数字(NaN)替换为零,(正)无穷大替换为非常大的数字,将负无穷大替换为非常小(或负)的数字

新数组的形状与x相同,x中元素的数据类型精度最高

如果x不精确,则NaN替换为零,无穷大(-infinity)替换为适合输出数据类型的最大(最小或最负)浮点值。如果x不精确,则返回x的副本

我不确定它是否能与uint8一起工作,因为在输出中提到了浮点,但对于其他读者来说,它可能会有用

def non_overflowing_sum(a, b)
    c = np.uint16(a)+b
    c[np.where(c>255)] = 255
    return np.uint8( c )

它也交换内存,但我发现更优雅,在返回时转换后,临时uint16被释放。

OpenCV有这样一个功能:cv2.addWeighted

@PadraicCunningham,它有,但数据类型为uint8,而不是uint16。然而,它创建了三个临时uint8数组。@shx2:我只计算了两个。
255-a
,和
np.minimum(255-a,b)
。第三个是什么?@user2357112,
a+…
。如果OP希望结果代替
a
数组,可以避免这种情况。我发现这个答案对我的图像来说是最快的(
uint8
)附加内容我不知道
putmask
,谢谢!使用该函数,我认为
a+=b
后跟
np.putmask(a),a@moarningsun我认为你是对的。但是它依赖于溢出,我个人对此感到不太舒服…@moarningsun你为什么要删除你的答案?我认为这是一个不错的答案,而且它是有效的。这种方法需要
putmask
来提高效率,这是你答案中的一个要点。所以我想我是对的好吧,我也把它作为一个注释放在这里。我尝试了你的第三种方法,效果非常好。但是,你能再解释一点吗,它的作用是什么?我比较了第三种方法(“预计算”)的执行时间和我只对a使用uint16数组的情况,a+=b和a[a>255]=255.“Precalculate”大约需要两倍的时间。但是如果内存效率更高,我将能够使用uint16方法对内存不足的大型数组进行计算。我看不出这对这个问题有什么帮助。任何数组中都没有需要添加的NaN或无限值。因此,我可能是mi你回答的要点是什么?@Thomas-hmm,整数类型可能不同,但当我遇到浮点问题时,溢出显示为+/-infinities@ToshinouKyouko是的,整数的情况确实不同,它们只是像OP的例子中那样溢出。
import numexpr

numexpr.evaluate('where((a+b)>255, 255, a+b)', out=a, casting='unsafe')
def non_overflowing_sum(a, b)
    c = np.uint16(a)+b
    c[np.where(c>255)] = 255
    return np.uint8( c )