2015-03-08 12 views
8

Pracuję nad projektem, w którym trzeba uzyskać wariancję obrazu. Obecnie biorę 2 podejścia (zarówno pracę, ale są bardzo powolny):Obliczanie wariancji obrazu python sprawnie

  1. Obliczanie wariancji dla każdego piksela z osobna:

Jest to kod za pomocą numpy, varianceMatrix jest wyjście

varianceMatrix = np.zeros(im.shape,np.uint8) 
w = 1    # the radius of pixels neighbors 
ny = len(im) 
nx = len(im[0]) 


for i in range(w,nx-w): 
    for j in range(w,ny-w): 

     sampleframe = im[j-w:j+w, i-w:i+w] 
     variance = np.var(sampleframe) 
     varianceMatrix[j][i] = int(variance) 

return varianceMatrix 
  1. Stosując istniejące scipy funkcję:

To scipy funkcja:

from scipy import ndimage 

varianceMatrix = ndimage.generic_filter(im, np.var, size = 3) 

Funkcja scipy jest szybszy, ale nie tak dużo. Szukam lepszej alternatywy do obliczenia wariancji.

Jakieś pomysły ???

Odpowiedz

0

Jeśli metoda wykorzystująca ndimage.generic_filter nie jest wystarczająco szybka, można napisać własną zoptymalizowaną implementację do obliczenia odchylenia w Cython.

1

Możesz użyć znanego sliding window stride trick, aby przyspieszyć obliczenia. Dodaje dwa "wymiary wirtualne" na końcu tablicy bez kopiowania danych, a następnie oblicza wariancję nad nimi.

Zauważ, że w twoim kodzie im[j-w:j+w, ..] przechodzi przez indeksy j-w,j-w+1,...,j+w-1, ostatni jest wyłączny, co być może nie miałeś na myśli. Ponadto, wariancje są większe niż zakres uint8, więc kończysz się zawijaniem całkowitym.

import numpy as np 
import time 
np.random.seed(1234) 

img = (np.random.rand(200, 200)*256).astype(np.uint8) 

def sliding_window(a, window, axis=-1): 
    shape = list(a.shape) + [window] 
    shape[axis] -= window - 1 
    if shape[axis] < 0: 
     raise ValueError("Array too small") 
    strides = a.strides + (a.strides[axis],) 
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) 

def sliding_img_var(img, window): 
    if window <= 0: 
     raise ValueError("invalid window size") 
    buf = sliding_window(img, 2*window, 0) 
    buf = sliding_window(buf, 2*window, 1) 

    out = np.zeros(img.shape, dtype=np.float32) 
    np.var(buf[:-1,:-1], axis=(-1,-2), out=out[window:-window,window:-window]) 
    return out 

def looping_img_var(im, w): 
    nx, ny = img.shape 
    varianceMatrix = np.zeros(im.shape, np.float32) 
    for i in range(w,nx-w): 
     for j in range(w,ny-w): 
      sampleframe = im[j-w:j+w, i-w:i+w] 
      variance = np.var(sampleframe) 
      varianceMatrix[j][i] = variance 
    return varianceMatrix 

np.set_printoptions(linewidth=1000, edgeitems=5) 
start = time.time() 
print(sliding_img_var(img, 1)) 
time_sliding = time.time() - start 
start = time.time() 
print(looping_img_var(img, 1)) 
time_looping = time.time() - start 
print("duration: sliding: {0} s, looping: {1} s".format(time_sliding, time_looping)) 
+0

Oto ostatnia linia danych wyjściowych na moim komputerze, pokazująca przyspieszenie: 'duration: sliding: 0.00510311126709 s, looping: 0.955919027328 s'. –

4

Tutaj szybkie rozwiązanie za pomocą OpenCV.

import cv2 

def winVar(img, wlen): 
    wmean, wsqrmean = (cv2.boxFilter(x, -1, (wlen, wlen), 
    borderType=cv2.BORDER_REFLECT) for x in (img, img*img)) 
    return wsqrmean - wmean*wmean 

Na moim komputerze i na poniższym przykładzie winVar() jest 2915 razy szybciej niż ndimage.generic_filter() i 10,8 razy szybciej niż sliding_img_var() (patrz odpowiedź na pv):

In [66]: img = np.random.randint(0, 256, (500,500)).astype(np.float) 

In [67]: %timeit winVar(img, 3) 
100 loops, best of 3: 1.76 ms per loop 

In [68]: %timeit ndimage.generic_filter(img, np.var, size=3) 
1 loops, best of 3: 5.13 s per loop 

In [69]: %timeit sliding_img_var(img, 1) 
100 loops, best of 3: 19 ms per loop 

Wynik jest zgodny z ndimage.generic_filter():

In [70]: np.allclose(winVar(img, 3), ndimage.generic_filter(img, np.var, size=3)) 
Out[70]: True 
+1

'ndimage.uniform_filter()' może być użyte zamiast 'cv2.boxFilter()', patrz [ta odpowiedź] (http://stackoverflow.com/a/33497963/1628638) na podobne pytanie. Dla przykładu, którego tu użyłem, wersja OpenCV była 4,1 razy szybsza. –

+1

Aby zamiast tego obliczyć odchylenie standardowe (tj. Zmienić 'winVar()' na 'winStd()'), wystarczy zmienić na 'return np.sqrt (wsqrmean - wmean * wmean)'. –

Powiązane problemy