2012-02-23 8 views
9

He tratado de replicar el cuello de botella principal en uno de mis programas.Acelerando la interpolación lineal de muchas ubicaciones de píxeles en NumPy

Quiero obtener los valores de linearly (or rather bilinearly) interpolated de varios valores de píxel no enteros simultáneamente. Es no el caso en que cada coordenada de píxel se perturba de la misma manera. A continuación se muestra un guión completo/mínimo junto con comentarios que demuestran el problema. ¿Cómo puedo acelerar el cálculo de result?

import numpy as np 
import time 

im = np.random.rand(640,480,3) # my "image" 
xx, yy = np.meshgrid(np.arange(im.shape[1]), np.arange(im.shape[0])) 
print "Check these are the right indices:",np.sum(im - im[yy,xx,:]) 

# perturb the indices slightly 
# I want to calculate the interpolated 
# values of "im" at these locations 
xx = xx + np.random.normal(size=im.shape[:2]) 
yy = yy + np.random.normal(size=im.shape[:2]) 

# integer value/pixel locations 
x_0 = np.int_(np.modf(xx)[1]) 
y_0 = np.int_(np.modf(yy)[1]) 
x_1, y_1 = x_0 + 1, y_0 + 1 

# the real-valued offsets/coefficients pixels 
a = np.modf(xx)[0][:,:,np.newaxis] 
b = np.modf(yy)[0][:,:,np.newaxis] 

# make sure we don't go out of bounds at edge pixels 
np.clip(x_0,0,im.shape[1]-1,out=x_0) 
np.clip(x_1,0,im.shape[1]-1,out=x_1) 
np.clip(y_0,0,im.shape[0]-1,out=y_0) 
np.clip(y_1,0,im.shape[0]-1,out=y_1) 

# now perform linear interpolation: THIS IS THE BOTTLENECK! 
tic = time.time() 
result = ((1-a) * (1-b) * im[y_0, x_0, :] + 
      a * (1-b) * im[y_1, x_0, :] + 
      (1-a) * b * im[y_0, x_1, :] + 
      a * b * im[y_1, x_1, :]) 
toc = time.time() 

print "interpolation time:",toc-tic 
+2

Cualquier razón por la que estás evitando 'scipy.ndimage.map_coordinates'? (Por ejemplo, ¿quieres evitar una dependencia de scipy.ndimage?) Si no, es la función que deseas. –

+0

@JoeKington No estaba al tanto de esto, déjame ver si puedo usar esta función y si es más rápida. Gracias. – YXD

Respuesta

4

Gracias a @JoeKington por la sugerencia. Esta es la mejor que se me ocurre con el uso de scipy.ndimage.map_coordinates

# rest as before 
from scipy import ndimage 
tic = time.time() 
new_result = np.zeros(im.shape) 
coords = np.array([yy,xx,np.zeros(im.shape[:2])]) 
for d in range(im.shape[2]): 
    new_result[:,:,d] = ndimage.map_coordinates(im,coords,order=1) 
    coords[2] += 1 
toc = time.time() 
print "interpolation time:",toc-tic 

Actualización: Añadido los ajustes sugeridos en los comentarios y trataron de una o dos otras cosas. Esta es la versión más rápida:

tic = time.time() 
new_result = np.zeros(im.shape) 
coords = np.array([yy,xx]) 
for d in range(im.shape[2]): 
    ndimage.map_coordinates(im[:,:,d], 
          coords,order=1, 
          prefilter=False, 
          output=new_result[:,:,d]) 
toc = time.time() 

print "interpolation time:",toc-tic 

Ejemplo tiempo de ejecución:

original version: 0.463063955307 
    better version: 0.204537153244 
    best version: 0.121845006943 
+0

Lo siento, no publiqué un ejemplo antes. Me estaba quedando corto de tiempo. ¡Me alegra que lo hayas resuelto! Puedes hacerlo con una llamada a 'map_coordinates', pero dependiendo del tamaño de tu imagen, iterar a través de cada banda es probablemente una mejor opción. El almacenamiento de una matriz de coordenadas tridimensional temporal consume mucha RAM. Puedes acelerar un poco las cosas si solo pasas en una banda a la vez a 'map_coordinates'. También le permitiría omitir la matriz de ceros en 'coords'. –

+1

Además, en el caso de la interpolación bilineal, puede guardar un poco de memoria y acelerar ligeramente si especifica 'prefilter = False'. Con una imagen pequeña, no notará la diferencia, pero con imágenes más grandes, evita hacer una copia adicional en la memoria. –

Cuestiones relacionadas