numpy 两个uint8数组的快速绝对差分

kknvjkwl  于 2023-08-05  发布在  其他
关注(0)|答案(3)|浏览(113)

我有两个numpy-arrays与dtype=np.uint8-像这样:

img1=np.uint8(np.random.randint(0, 255, (480, 640)))
img2=np.uint8(np.random.randint(0, 255, (480, 640)))

字符串
我想建立这些数组的正差值。
以下是我的前两种方法(第三种方法可供参考):

def differenceImageV1(img1, img2):
  diff=np.empty_like(img1)
  h, w=img1.shape
  for y in range(h):
    for x in range(w):
      if img1[y, x]<img2[y, x]: diff[y, x]=img2[y, x]-img1[y, x]
      else:                     diff[y, x]=img1[y, x]-img2[y, x]
  return(diff)

def differenceImageV2(img1, img2):
  return(np.uint8(np.absolute(np.int16(img1)-np.int16(img2))))

def differenceImageV3(img1, img2):  # fast - but wrong result
  return(img1-img2)


我得到这些执行时间(以及要检查的总和,如果它们相等):

10x: 1893.54 ms  np.sum=26122208
1000x:  411.71 ms  np.sum=26122208
1000x:   26.60 ms  np.sum=39123624


有没有一种方法可以像V2一样更快地获得正确的结果?

j2datikz

j2datikz1#

下面是一个比V2快得多的方法:取img1-img2,然后乘以1或-1,具体取决于img1>img2。以下是它的实现方式:

def differenceImageV6(img1, img2):
  a = img1-img2
  b = np.uint8(img1<img2) * 254 + 1
  return a * b

字符串
用于测试性能的测试线束:

import numpy as np

img1=np.uint8(np.random.randint(0, 255, (480, 640)))
img2=np.uint8(np.random.randint(0, 255, (480, 640)))

def differenceImageV1(img1, img2):
  diff=np.empty_like(img1)
  h, w=img1.shape
  for y in range(h):
    for x in range(w):
      if img1[y, x]<img2[y, x]: diff[y, x]=img2[y, x]-img1[y, x]
      else:                     diff[y, x]=img1[y, x]-img2[y, x]
  return(diff)

def differenceImageV2(img1, img2):
  return(np.uint8(np.abs(np.int16(img1)-img2)))

def differenceImageV3(img1, img2):  # fast - but wrong result
  return(img1-img2)

def differenceImageV4(img1, img2):
  return np.where(img1>img2, img1-img2, img2-img1)

def differenceImageV5(img1, img2):
  a = img1-img2
  b = img2-img1
  c = img1>img2
  return a*c + b*(~c)

def differenceImageV6(img1, img2):
  a = img1-img2
  b = np.uint8(img1<img2) * 254 + 1
  return a * b

import timeit
def testit():
  for fn in [differenceImageV2, differenceImageV3, differenceImageV4, differenceImageV5, differenceImageV6]:
    print fn.__name__, np.sum(fn(img1, img2).astype('int64')),
    print timeit.timeit("%s(img1, img2)" % fn.__name__, "from test import img1, img2, %s" % fn.__name__, number=1000)

if __name__ == '__main__':
    testit()


以及由此产生的性能数字:

differenceImageV2 26071358 0.982538938522
differenceImageV3 39207702 0.0261280536652
differenceImageV4 26071358 1.36270809174
differenceImageV5 26071358 0.220561981201
differenceImageV6 26071358 0.154536962509


differenceImageV6比不正确的differenceImageV3慢6倍,但仍然比以前最好的differenceImageV2快6倍。differenceImageV1没有经过测试,因为它比其他的要慢几个数量级。
注意:我包含了一个np.where方法用于比较;我以为它可能有很好的性能,但结果是相当差。在NumPy中,通过布尔数组执行切片似乎非常慢。

ajsxfq5m

ajsxfq5m2#

如果你有opencv可用,你也可以用途:

def differenceImageV4(img1, img2):
  return cv2.absdiff(img1, img2)

字符串
其速度几乎与differenceImageV3相同。

v8wbuo2f

v8wbuo2f3#

自从接受答案以来已经有一段时间了,所以仅供参考,我在Python3.10上与OpenCV 4.6.0和Numpy 1.23.0进行了一些比较。OpenCV版本显然上级,运行速度与错误版本(* 编辑:我刚刚意识到这正是Jan Rüegg所指出的,但我误读了函数的编号 *)。而且,int16版本现在和整洁技巧一样快。

from time import perf_counter

import cv2
import numpy as np

def wrong(img1, img2):  # fast - but wrong result
    return img1 - img2

def with_int16(img1, img2):
    return np.abs((img1.astype(np.int16) - img2.astype(np.int16))).astype(np.uint8)

def neat_trick(img1, img2):
    a = img1 - img2
    b = np.uint8(img1 < img2) * 254 + 1
    return a * b

def opencv(img1, img2):
    return cv2.absdiff(img1, img2)

def testit():
    for fn in [wrong, with_int16, neat_trick, opencv]:
        times = []
        np_random = np.random.RandomState(0)
        for _ in range(10000):
            img1 = np.uint8(np_random.randint(0, 255, (480, 640)))
            img2 = np.uint8(np_random.randint(0, 255, (480, 640)))
            start = perf_counter()
            fn(img1, img2)
            times.append(perf_counter() - start)
        print(f"{fn.__name__.rjust(10)}: [mean]: {np.mean(times):.3E}, [std] {np.std(times):.3E}")

if __name__ == "__main__":
    testit()

字符串
输出量:

wrong: [mean]: 9.198E-06, [std] 1.765E-06
with_int16: [mean]: 6.072E-05, [std] 4.590E-06
neat_trick: [mean]: 5.746E-05, [std] 5.453E-06
    opencv: [mean]: 9.660E-06, [std] 1.589E-06

相关问题