2012-05-21 10 views
6

Tengo una imagen con valores que van de 0 a 1. Lo que me gusta hacer es promediar de manera simple.
Pero, más específicamente, para una celda en el borde de la imagen me gustaría calcular el promedio de los píxeles para esa parte del vecindario/kernel que se encuentra dentro de la extensión de la imagen. De hecho, esto se reduce para adaptar el denominador de la 'fórmula promedio', el número de píxeles por el que se divide la suma.Cálculo eficiente del promedio del vecindario adaptado a las fronteras

Me las arreglé para hacer esto como se muestra a continuación con scipy.ndimage.generic_filter, pero esto está lejos de ser eficiente en términos de tiempo.

def fnc(buffer, count): 
    n = float(sum(buffer < 2.0)) 
    sum = sum(buffer) - ((count - b) * 2.0) 
    return (sum/n) 

avg = scipy.ndimage.generic_filter(image, fnc, footprint = kernel, \ 
            mode = 'constant', cval = 2.0, \ 
            extra_keywords = {'count': countkernel}) 

detalles

  • kernel = array cuadrada (círculo representada por unos)
  • Relleno con 2 de y no por ceros desde entonces pude ceros no correctamente separadas de la zona acolchada y ceros del ráster actual
  • countkernel = número de unidades en el kernel
  • n = número de células que se encuentran dentro image mediante la exclusión de las células de la zona acolchada identificadas por valores de 2
  • Corregir el sum restando (número de celdas acolchadas * 2.0) a partir de la suma total barrio original de

Update (s)

1) relleno con NaNs aumenta el cálculo con alrededor del 30%:

def fnc(buffer): 
     return (numpy.nansum(buffer)/numpy.sum([~numpy.isnan(buffer)])) 

    avg = scipy.ndimage.generic_filter(image, fnc, footprint = kernel, \ 
             mode = 'constant', cval = float(numpy.nan) 

2) La aplicación de la solución propuesta por Yves Daoust (respuesta aceptada), sin duda reduce el tiempo de procesamiento al mínimo:

def fnc(buffer): 
     return numpy.sum(buffer) 

    sumbigimage = scipy.ndimage.generic_filter(image, fnc, \ 
               footprint = kernel, \ 
               mode = 'constant', \ 
               cval = 0.0) 
    summask  = scipy.ndimage.generic_filter(mask, fnc, \ 
               footprint = kernel, \ 
               mode = 'constant', \ 
               cval = 0.0) 
    avg = sumbigimage/summask 

3) Sobre la base de la puntaYves' utilizar una imagen binaria adicional, la cual de hecho está aplicando una máscara, me encontré con el principio de masked arrays. Como tal, solo se debe procesar una matriz porque una matriz enmascarada "combina" la imagen y enmascarará las matrices juntas.
Un pequeño detalle sobre el conjunto de máscaras: en lugar de llenar la parte interna (extensión de la imagen original) con 1 y rellenar la parte exterior (borde) con 0 como se hizo en la actualización anterior, debe hacerlo al revés. Un 1 en una matriz enmascarada significa 'inválido', un 0 significa 'válido'.
Este código es incluso un 50% más rápido que el código se suministra en la actualización 2):

maskedimg = numpy.ma.masked_array(imgarray, mask = maskarray) 

    def fnc(buffer): 
     return numpy.mean(buffer) 

    avg = scipy.ndimage.generic_filter(maskedimg, fnc, footprint = kernel, \ 
             mode = 'constant', cval = 0.0) 

-> debo corregirme aquí!
Debo confundirme durante la validación, ya que después de algunas ejecuciones de cálculo parecía que scipy.ndimage.<filters> no puede manejar masked_arrays en el sentido de que durante el funcionamiento del filtro la máscara no se toma en cuenta.
Algunas otras personas mencionaron esto también, como here y here.


El poder de una imagen ...

  • gris: extensión de imagen para procesar
  • blanco: acolchada área (en mi caso de llenado con 2.0)
  • roja tonos: extensión de kernel
    • rojo oscuro: efectiva neighborhoud
    • luz roja: parte del barrio para ser ignorado

enter image description here


¿Cómo puede esta pieza en lugar pragmática de código puede cambiar para mejorar el rendimiento del cálculo?

¡Muchas gracias de antemano!

Respuesta

1

No estoy seguro si esto ayudará, ya que no soy hábil en scipy: use una imagen auxiliar de 1 en el área gris y 0 en el área blanca (0 también en la imagen de origen). A continuación, aplique el filtro a ambas imágenes con una suma simple.

Hay alguna esperanza de una aceleración si scipy proporciona una versión especializada del filtro con una función incorporada para la suma.

Hecho esto, tendrá que dividir ambas imágenes píxel por píxel.

+0

Gracias Yves! Ese idd hace el truco. –

0

No estoy seguro de cuán eficiente es esto, pero estoy usando una formulación más simple con nan que maneja tanto bordes como máscaras.

Ningún caso máscara: caso

avg = scipy.ndimage.generic_filter(image, np.nanmean, mode='constant', cval=np.nan, footprint=kernel) 

Máscara:

masked_image = np.where(mask, image, np.nan) 
avg = scipy.ndimage.generic_filter(masked_image, np.nanmean, mode='constant', cval=np.nan, footprint=kernel) 

Usted puede utilizar todas las numpynan funciones.

Cuestiones relacionadas