2016-03-03 10 views
9

Mam dwie tablice z NumPy-dtype=np.uint8 - tak:Szybkie bezwzględna różnica dwóch tablic Uint8

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

I chcę zbudować różnicę pozytywny tych tablic.

Oto moje pierwsze dwa approches (i trzecia dla porównania):

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) 

uzyskać te czasy wykonania (oraz kwoty, aby sprawdzić, czy są one równe):

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

Czy jest jakiś sposób, aby uzyskać poprawny wynik szybciej, jak przy V2?

+0

Nie jestem pewien, czy naprawdę by to pomogło, ale prawdopodobnie nie potrzebujesz 'np.int16 (img2)' w 'differenceImageV2', i możesz po prostu użyć' img2'. Czy używasz biblioteki ['timeit'] (https://docs.python.org/2/library/timeit.html) do dokładnego pomiaru czasu? – Kupiakos

+0

Jestem świadomy przepełnienia (niedopełnienie jest wtedy, gdy różnica między dwoma liczbami zmiennoprzecinkowymi jest zbyt mała, aby można było je odróżnić). Mam na myśli pełne wyrażenie byłoby 'return (np.uint8 (np.absolute (np.int16 (img1) -img2)))). 'img1' jest nadal rzutowany na' int16', więc wynik będzie "int16" i może pozwolić na te same liczby ujemne, które wykona wcześniej rzutowanie. 'np.sum' daje taki sam wynik. Po prostu nie bierze najpierw kopii całej tablicy. Zgolę około 70 ms na 1000 pętli. – Kupiakos

+0

masz rację. Otrzymuję taki sam wynik, jeśli wykonuję 'np.int16 (img1) -img2'. Czas wykonania spada do 400.39 ms dla 1000 pętli. I nie, używam 'time.process_time()' tutaj, ponieważ nie dbam o jeden lub dziesięć milisekund. – dede

Odpowiedz

7

Oto jedno podejście, które jest znacznie szybsze niż V2: weź img1-img2 i pomnóż przez 1 lub -1 w zależności od img1>img2. Oto jak to jest realizowane:

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

Uprząż testy do wykonania badania:

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() 

i wynikające skuteczności liczbach:

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

differenceImageV6 wynosi około 6x wolniej niż nieprawidłowej differenceImageV3, ale wciąż około 6 razy szybszy niż poprzedni najlepszy differenceImageV2. differenceImageV1 nie jest testowany, ponieważ łatwo jest go o kilka rzędów wielkości wolniej niż pozostałe.

Uwaga: Do porównania użyto metody np.where; Pomyślałem, że może to być dobre wykonanie, ale okazuje się dość słabe. Wygląda na to, że wykonywanie skrawania przez tablicę boolowską jest dość powolne w NumPy.

+0

Otrzymuję około 895 μs dla 'differeImageV2', a 645 μs dla' differeImageV6'. Niezależnie od faktycznego przyspieszenia, wciąż oklaskiwam tę odrętną magię. – Kupiakos

+1

Podoba mi się Twój V6 :-) ... ale wciąż próbuję to zrozumieć ;-) – dede

+0

Myślę, że V6 będzie szybszy, jeśli zrobisz to jako 'a = img1 - img2; a [img1 Jaime