2016-03-03 4 views
9

ho due NumPy-array con dtype=np.uint8 - come questo:veloce differenza assoluta di due array uint8

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

e voglio costruire la differenzapositiva di questi array.

Qui sono i miei primi due approches (e una terza per riferimento):

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) 

ottengo questi tempi di esecuzione (e le somme per verificare, se sono uguali):

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

C'è un modo per ottenere un risultato corretto più veloce come con la V2?

+0

Non sono sicuro che sarebbe di grande aiuto, ma probabilmente non è necessario il 'np.int16 (img2)' in 'differenceImageV2', e si può semplicemente usare' img2'. Inoltre, stai usando la libreria ['timeit'] (https://docs.python.org/2/library/timeit.html) per risultati precisi di temporizzazione? – Kupiakos

+0

Sono consapevole dell'overflow (underflow è quando la differenza tra due numeri in virgola mobile è troppo piccola per diventare distinguibile). Voglio dire che l'istruzione completa sarebbe 'return (np.uint8 (np.absolute (np.int16 (img1) -img2)))'. 'img1' è ancora castato in un' int16', quindi il risultato sarà un 'int16' e può consentire gli stessi numeri negativi che farà il cast in anticipo. 'np.sum' dà lo stesso risultato. Semplicemente non prende prima una copia dell'intero array. Mi rasero circa 70 ms per 1000 loop. – Kupiakos

+0

hai ragione. Ottengo lo stesso risultato, se faccio un 'np.int16 (img1) -img2'. Il tempo di esecuzione scende a 400,39 ms per 1000 loop. E no, io uso 'time.process_time()' qui, perché non mi interessa uno o dieci millisecondi. – dede

risposta

7

Ecco un approccio che è significativamente più veloce di V2: prendere img1-img2 e moltiplicare per 1 o -1 a seconda di img1>img2. Ecco come viene implementato:

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

Un test harness per le prestazioni di prova:

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

e prestazioni risultante numeri:

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

differenceImageV6 è circa 6 volte più lento del errato differenceImageV3, ma ancora circa 6 volte più veloce del precedente migliore differenceImageV2. differenceImageV1 non è testato perché è facilmente più lento di alcuni ordini di grandezza rispetto al resto.

Nota: ho incluso un approccio np.where per il confronto; Ho pensato che potrebbe avere buone prestazioni ma risulta essere piuttosto mediocre. Sembra che l'esecuzione dell'affinamento di un array booleano sia piuttosto lento in NumPy.

+0

Sto ottenendo circa 895 μs per 'differenceImageV2', mentre 645 μs per' differenceImageV6'. A prescindere dalla velocità effettiva, apprezzo ancora la stregoneria numpy. – Kupiakos

+1

Mi piace il tuo V6 :-) ... ma sto ancora cercando di capirlo ;-) – dede

+0

Penso che V6 sarà più veloce se lo fai come 'a = img1 - img2; un [img1 Jaime