Vecinos de píxeles en matriz 2d (imagen) usando Python

Tengo una matriz numpy como esta:

x = np.array([[1,2,3],[4,5,6],[7,8,9]])

Necesito crear una función, llamémosla "vecinos" con el siguiente parámetro de entrada:

  • x: una matriz numpy 2d
  • (i,j): el índice de un elemento en una matriz 2d
  • d: radio de vecindad

Como salida quiero obtener los vecinos de la celda. i,j con una distancia dada d. Así que si corro

neighbors(im, i, j, d=1) with i = 1 and j = 1 (element value = 5) 

Debo obtener los índices de los siguientes valores: [1,2,3,4,6,7,8,9]. Espero dejarlo claro. ¿Hay alguna biblioteca como scipy que se ocupe de esto?

He hecho algo que funciona, pero es una solución aproximada.

def pixel_neighbours(self, p):

    rows, cols = self.im.shape

    i, j = p[0], p[1]

    rmin = i - 1 if i - 1 >= 0 else 0
    rmax = i + 1 if i + 1 < rows else i

    cmin = j - 1 if j - 1 >= 0 else 0
    cmax = j + 1 if j + 1 < cols else j

    neighbours = []

    for x in xrange(rmin, rmax + 1):
        for y in xrange(cmin, cmax + 1):
            neighbours.append([x, y])
    neighbours.remove([p[0], p[1]])

    return neighbours

¿Cómo puedo mejorar esto?

preguntado el 12 de junio de 12 a las 13:06

7 Respuestas

Echa un vistazo a scipy.ndimage.generic_filter.

Como un ejemplo:

import numpy as np
import scipy.ndimage as ndimage

def test_func(values):
    print(values)
    return values.sum()


x = np.array([[1,2,3],[4,5,6],[7,8,9]])

footprint = np.array([[1,1,1],
                      [1,0,1],
                      [1,1,1]])

results = ndimage.generic_filter(x, test_func, footprint=footprint)

Por defecto, "reflejará" los valores en los límites. Puedes controlar esto con el mode argumento de palabra clave.

Sin embargo, si desea hacer algo como esto, es muy probable que pueda expresar su problema como una convolución de algún tipo. Si es así, será mucho más rápido dividirlo en pasos convolucionales y usar funciones más optimizadas (por ejemplo, la mayoría de scipy.ndimage).

contestado el 12 de mayo de 21 a las 06:05

Este método es más lento que mi propio método personalizado usando bucles for - Angel

EDITAR: ah mierda, mi respuesta es solo escribir im[i-d:i+d+1, j-d:j+d+1].flatten() pero escrito de una manera incomprensible :)


El viejo y buen truco de la ventana corrediza puede ayudar aquí:

import numpy as np
from numpy.lib.stride_tricks import as_strided

def sliding_window(arr, window_size):
    """ Construct a sliding window view of the array"""
    arr = np.asarray(arr)
    window_size = int(window_size)
    if arr.ndim != 2:
        raise ValueError("need 2-D input")
    if not (window_size > 0):
        raise ValueError("need a positive window size")
    shape = (arr.shape[0] - window_size + 1,
             arr.shape[1] - window_size + 1,
             window_size, window_size)
    if shape[0] <= 0:
        shape = (1, shape[1], arr.shape[0], shape[3])
    if shape[1] <= 0:
        shape = (shape[0], 1, shape[2], arr.shape[1])
    strides = (arr.shape[1]*arr.itemsize, arr.itemsize,
               arr.shape[1]*arr.itemsize, arr.itemsize)
    return as_strided(arr, shape=shape, strides=strides)

def cell_neighbors(arr, i, j, d):
    """Return d-th neighbors of cell (i, j)"""
    w = sliding_window(arr, 2*d+1)

    ix = np.clip(i - d, 0, w.shape[0]-1)
    jx = np.clip(j - d, 0, w.shape[1]-1)

    i0 = max(0, i - d - ix)
    j0 = max(0, j - d - jx)
    i1 = w.shape[2] - max(0, d - i + ix)
    j1 = w.shape[3] - max(0, d - j + jx)

    return w[ix, jx][i0:i1,j0:j1].ravel()

x = np.arange(8*8).reshape(8, 8)
print x

for d in [1, 2]:
    for p in [(0,0), (0,1), (6,6), (8,8)]:
        print "-- d=%d, %r" % (d, p)
        print cell_neighbors(x, p[0], p[1], d=d)

No hice ningún cronometraje aquí, pero es posible que esta versión tenga un rendimiento razonable.

Para obtener más información, busque en la red las frases "ventana móvil numpy" o "ventana deslizante numpy".

Respondido el 12 de junio de 12 a las 16:06

No conozco ninguna función de biblioteca para esto, pero puede escribir fácilmente algo como esto usted mismo usando la excelente funcionalidad de corte de numpy:

import numpy as np
def neighbors(im, i, j, d=1):
    n = im[i-d:i+d+1, j-d:j+d+1].flatten()
    # remove the element (i,j)
    n = np.hstack((b[:len(b)//2],b[len(b)//2+1:] ))
    return n

Por supuesto, debe realizar algunas comprobaciones de rango para evitar el acceso fuera de los límites.

Respondido el 12 de junio de 12 a las 13:06

¿Qué b referirse aquí? - James

b no está definido, como dijo James: Tommaso Guerrini

Mediante el uso max y min, maneja píxeles en los límites superior e inferior:

im[max(i-1,0):min(i+2,i_end), max(j-1,0):min(j+2,j_end)].flatten()

Respondido 10 Feb 20, 14:02

Estoy de acuerdo con la respuesta de Joe Kington, solo una adición a las huellas

import numpy as np
from scipy.ndimage import generate_binary_structure
from scipy.ndimage import iterate_structure
foot = np.array(generate_binary_structure(2, 1),dtype=int)

o para huellas más grandes/diferentes, por ej.

np.array(iterate_structure(foot , 2),dtype=int)

Respondido el 16 de enero de 17 a las 09:01

Posiblemente use un KDÁrbol in Ciencia ?

Respondido el 12 de junio de 12 a las 13:06

Solo para lo que sea que valga, para datos cuadriculados regularmente, un quadtree no es ideal. La indexación de la cuadrícula en la que se encuentran los datos le brinda una búsqueda de vecinos mucho más rápida. - jose kington

Primero iniciamos nuestra matriz de interés usando numpy

import numpy as np

x = np.array([[1,2,3],[4,5,6],[7,8,9]])

print(x)

[[1 2 3]
 [4 5 6]
 [7 8 9]]

Nuestros vecinos son una función de la distancia, por ejemplo, podríamos estar interesados ​​en los vecinos de la distancia 2, esto nos dice cómo debemos rellenar nuestra matriz x. Elegimos rellenar con ceros, pero puede rellenar con lo que quiera que sea media, moda, mediana de una fila/columna

d = 2

x_padded = np.pad(x,d,mode='constant')

print(x_padded)

[[0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]
 [0 0 1 2 3 0 0]
 [0 0 4 5 6 0 0]
 [0 0 7 8 9 0 0]
 [0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]]

Utilizamos x_padded matrix para obtener vecinos de cualquier valor en matrix x. Dejar (i,j) y (s,t) ser índices de x y x_padded respectivamente. Ahora tenemos que traducir (i,j) a (s,t) conseguir vecinos de (i,j)

i,j = 2,1
s,t = 2*d+i+1, 2*d+j+1

window = x_padded[i:s, j:t]

print(window)

[[0 1 2 3 0]
 [0 4 5 6 0]
 [0 7 8 9 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]

¡¡¡Tenga en cuenta!!! los índices (i,j) apunte a cualquier valor que desee para obtener sus vecinos en la matriz x

Uno podría desear iterar sobre cada punto en la matriz x, obtenga sus vecinos y realice cálculos utilizando los vecinos, por ejemplo, en el procesamiento de imágenes, la convolución con un kernel. Uno podría hacer lo siguiente para obtener vecinos de cada píxel en una imagen x

for i in range(x.shape[0]):
    for j in range(x.shape[1]):
        i,j = 2,1
        s,t = 2*d+i+1, 2*d+j+1
        window = x_padded[i:s, j:t]

Respondido el 16 de Septiembre de 20 a las 22:09

Haga editar su respuesta para que explique cómo responde la pregunta de OP. Los volcados de código no se consideran respuestas útiles en Stack Overflow. - crisluengo

Agregue un comentario antes de su código para aclarar cómo esto resuelve el problema, separe el código de su salida y agregue un comentario después del resultado para analizar qué es específicamente útil en el resultado. - miguel baudin

Traté de explicar mi proceso de pensamiento si hay algún problema o confusión que con gusto abordaré. - Omfemetse

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.