¿Cómo se implementa Gaussian Blur?


42

He leído que el desenfoque se realiza en gráficos en tiempo real al hacerlo en un eje y luego en el otro.

He hecho un poco de convolución en 1D en el pasado, pero no estoy muy cómodo con eso, ni sé exactamente qué convolucionar en este caso.

¿Alguien puede explicar en términos simples cómo se realiza un Desenfoque Gaussiano 2D de una imagen?

También escuché que el radio de Blur puede afectar el rendimiento. ¿Se debe a tener que hacer una convolución mayor?

Respuestas:


48

En convolución, dos funciones matemáticas se combinan para producir una tercera función. En las funciones de procesamiento de imágenes se suelen llamar núcleos. Un núcleo no es más que una matriz (cuadrada) de píxeles (una imagen pequeña, por así decirlo). Por lo general, los valores en el núcleo suman uno. Esto es para asegurarse de que no se agregue o elimine energía de la imagen después de la operación.

Específicamente, un núcleo gaussiano (utilizado para desenfoque gaussiano) es una matriz cuadrada de píxeles donde los valores de los píxeles corresponden a los valores de una curva gaussiana (en 2D).

Imagen vinculada desde http://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm

Cada píxel de la imagen se multiplica por el núcleo gaussiano. Esto se hace colocando el píxel central del núcleo en el píxel de la imagen y multiplicando los valores en la imagen original con los píxeles en el núcleo que se superponen. Los valores resultantes de estas multiplicaciones se suman y ese resultado se usa para el valor en el píxel de destino. Mirando la imagen, multiplicaría el valor en (0,0) en la matriz de entrada por el valor en (i) en la matriz del núcleo, el valor en (1,0) en la matriz de entrada por el valor en (h ) en la matriz del núcleo, y así sucesivamente. y luego agregue todos estos valores para obtener el valor de (1,1) en la imagen de salida.

Imagen vinculada desde http://www.songho.ca/dsp/convolution/convolution.html

Para responder a su segunda pregunta primero, cuanto más grande sea el núcleo, más costosa será la operación. Por lo tanto, cuanto mayor sea el radio del desenfoque, más durará la operación.

Para responder a su primera pregunta, como se explicó anteriormente, la convolución se puede hacer multiplicando cada píxel de entrada con todo el núcleo. Sin embargo, si el núcleo es simétrico (que es un núcleo gaussiano), también puede multiplicar cada eje (x e y) de forma independiente, lo que disminuirá el número total de multiplicaciones. En términos matemáticos adecuados, si una matriz es separable, puede descomponerse en matrices (M × 1) y (1 × N). Para el núcleo gaussiano anterior, esto significa que también puede usar los siguientes núcleos:

1256[1464141624164624362464162416414641]=1256[14641][14641]

Ahora multiplicaría cada píxel en la imagen de entrada con ambos núcleos y agregaría los valores resultantes para obtener el valor para el píxel de salida.

Para obtener más información sobre cómo ver si un núcleo es separable, siga este enlace .

Editar: los dos núcleos que se muestran arriba usan valores ligeramente diferentes. Esto se debe a que el parámetro (sigma) utilizado para la curva gaussiana para crear estos núcleos fue ligeramente diferente en ambos casos. Para obtener una explicación sobre qué parámetros influyen en la forma de la curva gaussiana y, por lo tanto, los valores en el núcleo siguen este enlace

Editar: en la segunda imagen de arriba dice que el núcleo que se usa está volteado. Por supuesto, esto solo hace alguna diferencia si el núcleo que usa no es simétrico. La razón por la que necesita voltear el núcleo tiene que ver con las propiedades matemáticas de la operación de convolución (consulte el enlace para obtener una explicación más detallada sobre la convolución). En pocas palabras: si no voltea el núcleo, el resultado de la operación de convolución será volteado. Al voltear el kernel, obtienes el resultado correcto.


1
¿Podría agregar una breve nota para explicar por qué los dos núcleos diferentes de 5 por 5 tienen números ligeramente diferentes (uno sumando a 273, el otro sumando a 256)? Parece una posible confusión para alguien nuevo en esto.
trichoplax

Del mismo modo, ¿podría explicar por qué el núcleo se voltea en su segundo diagrama? No creo que sea relevante para la explicación, pero el hecho de que sea un paso extra aparente puede dificultar la comprensión a alguien que no sabe que no es necesario.
trichoplax

No olvides trabajar en un espacio de color lineal para obtener resultados correctos.
v.oddou

16

Aquí está el mejor artículo que he leído sobre el tema: Desenfoque gaussiano eficiente con muestreo lineal . Responde a todas sus preguntas y es realmente accesible.

Para el lego, una explicación muy breve: Gaussian es una función con la agradable propiedad de ser separable, lo que significa que una función Gaussian 2D se puede calcular combinando dos funciones Gaussianas 1D.

Entonces, para un tamaño ( ), solo necesita evaluar valores ( ), que es significativamente menor. Si su operación consiste en leer un elemento de textura (comúnmente llamado "tap" ), es una buena noticia: menos toques son más baratos porque una recuperación de textura tiene un costo.n×nO(n2)2×nO(n)

Es por eso que los algoritmos de desenfoque usan esa propiedad haciendo dos pases, uno para desenfocar horizontalmente reuniendo los píxeles horizontales y otro para desenfocar verticalmente al reunir los píxeles verticales. El resultado es el color final del píxel borroso.nn


13

En general, una convolución se realiza tomando la integral del producto de dos funciones en una ventana deslizante, pero si no eres de un fondo matemático, esa no es una explicación muy útil, y ciertamente no te dará una intuición útil. para ello. Más intuitivamente, una convolución permite que múltiples puntos en una señal de entrada afecten a un solo punto en una señal de salida.

Como no te sientes muy cómodo con las convoluciones, primero revisemos lo que significa una convolución en un contexto discreto como este, y luego veamos un desenfoque más simple.

En nuestro contexto discreto, podemos multiplicar nuestras dos señales simplemente multiplicando cada muestra correspondiente. La integral también es simple de hacer discretamente, solo sumamos cada muestra en el intervalo que estamos integrando. Una convolución discreta simple es calcular un promedio móvil. Si desea tomar el promedio móvil de 10 muestras, esto puede considerarse como una convolución de su señal por una distribución de 10 muestras de largo y 0.1 de alto, cada muestra en la ventana primero se multiplica por 0.1, luego las 10 se suman para producir la media. Esto también revela una distinción interesante e importante, cuando se difumina con una convolución, la distribución que use debe sumar 1.0 sobre todas sus muestras, de lo contrario aumentará o disminuirá el brillo general de la imagen cuando la aplique.

Ahora que hemos analizado las convoluciones, podemos pasar a los desenfoques. Un desenfoque gaussiano se implementa convolucionando una imagen mediante una distribución gaussiana. Otros desenfoques se implementan generalmente al enredar la imagen mediante otras distribuciones. El desenfoque más simple es el desenfoque de cuadro, y utiliza la misma distribución que describimos anteriormente, un cuadro con área unitaria. Si deseamos desenfocar un área de 10x10, multiplicamos cada muestra en el cuadro por 0.01, y luego las sumamos todas para producir el píxel central. Todavía tenemos que asegurarnos de que la suma total de todas las muestras en nuestra distribución de desenfoque sea 1.0 para asegurarnos de que la imagen no se vuelva más brillante o más oscura.

Un desenfoque gaussiano sigue el mismo procedimiento amplio que un desenfoque de caja, pero utiliza una fórmula más compleja para determinar los pesos. La distribución se puede calcular en función de la distancia desde el centro r, evaluando La suma de todas las muestras en un gaussiano será eventualmente aproximadamente 1.0 si muestra cada píxel, pero el hecho de que un gaussiano tenga soporte infinito (tiene valores en todas partes) significa que necesita usar una versión ligeramente modificada que sume 1.0 usando solo unos pocos valores.

ex2/22π

Por supuesto, ambos procesos pueden ser muy costosos si los realiza en un radio muy grande, ya que necesita muestrear muchos píxeles para calcular el desenfoque. Aquí es donde entra el truco final: tanto un desenfoque gaussiano como un desenfoque de caja son lo que se denomina desenfoque "separable". Esto significa que si realiza el desenfoque a lo largo de un eje y luego lo realiza a lo largo del otro eje, produce exactamente el mismo resultado que si lo hubiera realizado a lo largo de ambos ejes al mismo tiempo. Esto puede ser tremendamente importante. Si su desenfoque es de 10 píxeles de ancho, se requieren 100 muestras en forma ingenua, pero solo 20 cuando se separan. La diferencia solo aumenta, ya que el desenfoque combinado es , mientras que la forma separada es .O ( n )O(n2)O(n)


1
Mirando su otra respuesta, parece que su experiencia matemática es mejor de lo que estaba trabajando, pero espero que aún entre en detalles suficientes para ser útil. Quería que fuera útil para las personas de cualquier origen que entren en esto.
porglezomp

1
Si me estás hablando, en absoluto. Tu respuesta y la de Bert son asombrosamente esclarecedoras. Muchas gracias! Tengo que digerir un poco la información ahora (:
Alan Wolfe

11

Lo más importante a tener en cuenta al implementar el desenfoque gaussiano es, como han señalado otros, separar el filtro de convolución 2D en dos convoluciones 1D porque reduce la complejidad de a .O(n2)O(n)

Pero hay dos trucos más que quizás desee considerar en una implementación real:

El filtro tiene un radio determinado y por eso, en los bordes, deberá calcular con píxeles que quedan fuera de la imagen. En tal caso, podría intentar uno de los siguientes: para los píxeles externos, simplemente tome el último valor posible (es decir, el píxel en el borde mismo, como en max(x, 0). O puede "reflejar" la imagen hacia afuera (como en x < 0 ? -x : x). O simplemente podría detenerse en el borde, pero luego necesitaría ajustar el denominador en el filtro de convolución para que sume hasta 1. Por ejemplo:

sum1256[1464141624164624362464162416414641]=sum1225[0000001624160024361600162416000000]=1.
Otro truco se refiere a cómo calcular los coeficientes reales del núcleo. Obviamente, podría intentar implementar la función gaussiana, pero una forma mucho más rápida es observar que el núcleo 1D vuelve a ensamblar el triángulo de Pascal . Por ejemplo:
     1
    1 1
   1 2 1
  1 3 3 1
[1 4 6 4 1]