2016-06-15 12 views
5

Załóżmy, że chcę zaimplementować następujący kod w pythonieCzy można wektoryzować funkcję, która ma dostęp do różnych elementów w tablicy numpy?

Ta funkcja pobiera obraz jako tablicę jednowymiarową i iteruje po poszczególnych elementach tablicy (pikselach w obrazie wejściowym), która wpływa na tablicę wyników, która jest również obraz reprezentowany jako jeden wymiarowej macierzy

przykład: jednego piksela obrazu wejściowego, (czerwony) wpływa 8 otaczające piksele (pomarańczowy) example 1

wdrożenia podstawowego w C jest

/* C version 
* Given an input image create an 
* output image that is shaped by individual pixels 
* of the input image 
*/ 

int image[width * height]; //image retrieved elsewhere 
int output [width * height]; //output image 
int y = 0, x = 0; 
for(y = 1; y < height-1 ; ++ y) { 
    for(x = 1; x < width-1; ++ x) { 
     if (image[y * width + x] > condition) { 
      /* pixel affects the surrounding 8 pixels in the output image */ 

      output[(y-1) * width + x - 1]++; /* upper left */ 
      output[(y-1) * width + x ]++; /* above  */ 
      output[(y-1) * width + x + 1]++; /* upper right */ 
      output[y * width + x + 1 ]++; /* right  */ 
      output[y * width + x - 1 ]++; /* left  */ 
      output[(y+1) * width + x - 1]++; /* lower left */ 
      output[(y+1) * width + x ]++; /* below  */ 
      output[(y+1) * width + x + 1]++; /* lower right */ 


     } 
    } 
} 

naiwne podejście w Pythonie byłoby użyć dokładnie tego samego pierwiastka mądry dostępu, jak pokazano poniżej

#Python version 
input = blah # formed elsewhere 
output = np.zeros(width * height) 
for y in xrange(1, height-1): 
    for x in xrange(1, width-1): 
     if input[y * width + x] > condition: 
      output[(y-1) * width + x - 1]+= 1; # upper left 
      output[(y-1) * width + x ]+= 1; # above  
      output[(y-1) * width + x + 1]+= 1; # upper right 
      output[y * width + x + 1 ]+= 1; # right  
      output[y * width + x - 1 ]+= 1; # left   
      output[(y+1) * width + x - 1]+= 1; # lower left 
      output[(y+1) * width + x ]+= 1; # below  
      output[(y+1) * width + x + 1]+= 1; # lower right 

tam jest lepszym sposobem wdrożenia to? Czy można wektoryzować tę funkcję?

Odpowiedz

0

Powiedzmy, że arr reprezentuje tablicę wejściową, a thresh jest progiem porównywalnym z każdym z elementów wejściowych. Teraz możemy ustawić próg dla tablicy wejściowej na dany próg, dając nam tablicę masek/boolowską. Następnie możemy wykonać splot 2D i odjąć od niego 1s gdzie mamy wartości True dla progowanej tablicy.

Zatem realizacja będzie wyglądać mniej więcej tak -

from scipy.signal import convolve2d 

# Get thresholded mask as int array & set first, last cols and rows as 0s 
mask = (arr > thresh).astype(int) 
mask[[0,-1]] = 0 
mask[:,[0,-1]] = 0 

# Perform 2D convolution and subtract 1s corresponding to True elems in mask 
out = convolve2d(mask,np.ones((3,3),dtype=int),'same') - mask 
5

Gdybym zrozumiał pytanie poprawnie, to podejście może być odwrócony do góry nogami: jeśli piksel posiada w swojej pikseli sąsiadujących pasujących warunek, zwiększ ją o jeden na każdy mecz. Zrób to dla wszystkich pikseli. Scipy (między innymi) oferuje narzędzia do filtering images:

In [51]: import scipy.ndimage 

Utwórz obraz próbki z 1-wymiarowej tablicy. Reshape tworzy widok zamiast kopiowania:

In [62]: I1d 
Out[62]: 
array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 129, 0, 129, 129, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129]) 

In [63]: height 
Out[63]: 8 

In [64]: width 
Out[64]: 8 

In [65]: I = I1d.reshape((height, width)) 

In [66]: I 
Out[66]: 
array([[ 0, 0, 0, 0, 0, 0, 0, 0], 
     [ 0, 0, 0, 0, 0, 0, 0, 0], 
     [ 0, 0, 129, 0, 129, 129, 0, 0], 
     [ 0, 0, 0, 0, 0, 0, 0, 0], 
     [ 0, 0, 0, 0, 129, 0, 0, 0], 
     [ 0, 0, 0, 0, 0, 0, 0, 0], 
     [ 0, 0, 0, 0, 0, 0, 0, 0], 
     [ 0, 0, 0, 0, 0, 0, 0, 129]]) 

Korzystanie splot stworzyć obraz, który posiada przyrosty do każdego piksela w oryginale z binarnego maski pikseli, które przekraczają stan (128 o) :

In [67]: scipy.ndimage.filters.convolve(
    (I > 128).astype(np.int), # conditioned binary image 
    weights=np.array([[1, 1, 1], # each match weighted as 1 
         [1, 0, 1], 
         [1, 1, 1]]), 
    mode='constant', cval=0) # Use zeros as constant fill values on edges 
Out[67]: 
array([[0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 1, 1, 2, 2, 2, 1, 0], 
     [0, 1, 0, 2, 1, 1, 1, 0], 
     [0, 1, 1, 3, 3, 3, 1, 0], 
     [0, 0, 0, 1, 0, 1, 0, 0], 
     [0, 0, 0, 1, 1, 1, 0, 0], 
     [0, 0, 0, 0, 0, 0, 1, 1], 
     [0, 0, 0, 0, 0, 0, 1, 0]]) 

In [68]: conv = _ 

Jeśli ostatecznym celem jest dodanie do oryginału i przyrosty:

In [69]: I + conv 
Out[69]: 
array([[ 0, 0, 0, 0, 0, 0, 0, 0], 
     [ 0, 1, 1, 2, 2, 2, 1, 0], 
     [ 0, 1, 129, 2, 130, 130, 1, 0], 
     [ 0, 1, 1, 3, 3, 3, 1, 0], 
     [ 0, 0, 0, 1, 129, 1, 0, 0], 
     [ 0, 0, 0, 1, 1, 1, 0, 0], 
     [ 0, 0, 0, 0, 0, 0, 1, 1], 
     [ 0, 0, 0, 0, 0, 0, 1, 129]]) 

do wyprowadzenia 1-wymiarowej tablicy jak w ell, użyj albo ravel() lub flatten(). Były tworzy 1-wymiarowy widok na oryginalnej tablicy 2-wymiarowej, ta ostatnia tworzy spłaszczoną kopię:

In [70]: conv.ravel() 
Out[70]: 
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 0, 0, 1, 0, 2, 1, 1, 1, 
     0, 0, 1, 1, 3, 3, 3, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 
     0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0]) 
3

myślę, co chce zrobić, to najprostszy z 2D tablicy indeksowania. Możesz przekształcić tablice z łatwością za pomocą numpy. Żadne dane nie są kopiowane. Nowa tablica 2D zapewnia wygodny sposób indeksowania tych samych wartości, które są przechowywane w oryginalnej macierzy. Oto przykładowy kod.

#imports 
import numpy as np 


# Setup 
Nx = 5 
Ny = 7 
cutoff = 3.0 
arr_input = np.array([[0, 0, 0, 0, 0, 0, 9], 
         [0, 4, 0, 0, 0, 0, 0], 
         [0, 0, 0, 0, 0, 3, 0], 
         [0, 2, 0, 0, 0, 0, 1], 
         [0, 0, 0, 0, 5, 0, 0]]).flatten() 

# Make an array of center locations with input value bigger than the cutoff value 
centers_array_2d = np.where(arr_input>=cutoff, 1.0, 0.0) 

# Initialize the output array 
arr_output = np.zeros_like(centers_array_2d) 

# Reshape the arrays to use 2D indexing 
ai = centers_array_2d.reshape(Nx, Ny) 
ao = arr_output.reshape(Nx, Ny) 

# Do the neighbor calculation with numpy indexing rules 
ao[:-1, :-1] += ai[1:, 1:]    # lower left 
ao[:, :-1] += ai[:, 1:]     # lower center 
ao[1:, :-1] += ai[:-1, 1:]    # lower right 
ao[:-1, :] += ai[1:, :]     # middle left 
# ao[:, :] += ai[:, :]     # middle center 
ao[1:, :] += ai[:-1, :]     # middle right 
ao[:-1, 1:] += ai[1:, :-1]    # top left 
ao[:, 1:] += ai[:, :-1]     # top center 
ao[1:, 1:] += ai[:-1, :-1]    # top right 

# Reshape the output array to return a 1D array like the input 
arr_output = ao.flatten() 

# Print the results 
print('input1d: \n{}\n'.format(arr_input)) 
print("2D array 'ai':\n{}\n".format(ai)) 
print("2D array 'ao':\n{}\n".format(ao)) 
print('output1d: \n{}\n'.format(arr_output)) 

Tablice są następujące.

input1d: 
[0 0 0 0 0 0 9 0 4 0 0 0 0 0 0 0 0 0 0 3 0 0 2 0 0 0 0 1 0 0 0 0 5 0 0] 

2D array 'ai': 
[[ 0. 0. 0. 0. 0. 0. 1.] 
[ 0. 1. 0. 0. 0. 0. 0.] 
[ 0. 0. 0. 0. 0. 1. 0.] 
[ 0. 0. 0. 0. 0. 0. 0.] 
[ 0. 0. 0. 0. 1. 0. 0.]] 

2D array 'ao': 
[[ 1. 1. 1. 0. 0. 1. 0.] 
[ 1. 0. 1. 0. 1. 2. 2.] 
[ 1. 1. 1. 0. 1. 0. 1.] 
[ 0. 0. 0. 1. 2. 2. 1.] 
[ 0. 0. 0. 1. 0. 1. 0.]] 

output1d: 
[ 1. 1. 1. 0. 0. 1. 0. 1. 0. 1. 0. 1. 2. 2. 1. 1. 1. 0. 1. 0. 1. 0. 0. 0. 1. 2. 2. 1. 0. 0. 0. 1. 0. 1. 0.] 

Czy to obliczenia, których szukałeś? To właśnie powinienem zinterpretować jako wektoryzację kodu, który podałeś. Można również utworzyć listę indeksów dla tablic 1D, które odpowiadają każdemu z sąsiadów. Zasadniczo dzieje się to wewnętrznie, gdy używam indeksów 2D w celu uzyskania dostępu do elementów z tablic 2D.