¿Cómo desenfoque gaussiano una imagen sin usar ninguna función gaussiana incorporada?

7 minutos de lectura

avatar de usuario
Moeb

Quiero desenfocar mi imagen usando la fórmula nativa de desenfoque gaussiano. Yo leo el artículo de wikipediapero no estoy seguro de cómo implementar esto.

¿Cómo uso la fórmula para decidir los pesos?

No quiero usar ninguna función integrada como la que tiene MATLAB

  • básicamente necesitas implementar un operador de convolución equivalente al conv2() función en MATLAB. Sin embargo, dado que 2D Gaussian se puede separar en dos 1D Gaussian, todo lo que necesita es una implementación de la función de convolución en 1D, junto con la matriz kernel correcta.

    – Amro

    8 de noviembre de 2009 a las 12:13

avatar de usuario
Goz

Escribir un desenfoque gaussiano ingenuo es bastante fácil. Se hace exactamente de la misma manera que cualquier otro filtro de convolución. La única diferencia entre una caja y un filtro gaussiano es la matriz que utiliza.

Imagina que tienes una imagen definida de la siguiente manera:

 0  1  2  3  4  5  6  7  8  9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99

Una matriz de filtro de caja de 3×3 se define de la siguiente manera:

0.111 0.111 0.111
0.111 0.111 0.111
0.111 0.111 0.111

Para aplicar el desenfoque gaussiano, haría lo siguiente:

Para el píxel 11, deberá cargar los píxeles 0, 1, 2, 10, 11, 12, 20, 21, 22.

luego multiplicaría el píxel 0 por la parte superior izquierda del filtro de desenfoque de 3×3. El píxel 1 por la parte superior central, el píxel 2, el píxel 3 por la parte superior derecha, el píxel 10 por la parte central izquierda y así sucesivamente.

Luego, agréguelos por completo y escriba el resultado en el píxel 11. Como puede ver, el píxel 11 ahora es el promedio de sí mismo y de los píxeles circundantes.

Los casos extremos se vuelven un poco más complejos. ¿Qué valores usas para los valores del borde de la textura? Una forma puede ser envolver al otro lado. Esto se ve bien para una imagen que luego se coloca en mosaico. Otra forma es empujar el píxel hacia los lugares circundantes.

Entonces, para la parte superior izquierda, puede colocar las muestras de la siguiente manera:

 0  0  1
 0  0  1
10 10 11

Espero que pueda ver cómo esto se puede extender fácilmente a núcleos de filtro grandes (es decir, 5×5 o 9×9, etc.).

La diferencia entre un filtro gaussiano y un filtro de caja son los números que van en la matriz. Un filtro gaussiano utiliza una distribución gaussiana en una fila y una columna.

por ejemplo, para un filtro definido arbitrariamente como (es decir, esto no es gaussiano, pero probablemente no esté muy lejos)

0.1 0.8 0.1

la primera columna sería la misma pero multiplicada por el primer elemento de la fila de arriba.

0.01 0.8 0.1
0.08 
0.01 

La segunda columna sería la misma pero los valores se multiplicarían por el 0,8 de la fila de arriba (y así sucesivamente).

0.01 0.08 0.01
0.08 0.64 0.08
0.01 0.08 0.01

El resultado de sumar todo lo anterior debe ser igual a 1. La diferencia entre el filtro anterior y el filtro de cuadro original sería que el píxel final escrito tendría una ponderación mucho mayor hacia el píxel central (es decir, el que está en esa posición). ya). El desenfoque ocurre porque los píxeles circundantes se desdibujan en ese píxel, aunque no tanto. Usando este tipo de filtro obtienes un desenfoque pero uno que no destruye la mayor parte de la información de alta frecuencia (es decir, el cambio rápido de color de píxel a píxel).

Este tipo de filtros pueden hacer muchas cosas interesantes. Puede hacer una detección de bordes usando este tipo de filtro restando los píxeles circundantes del píxel actual. Esto dejará atrás solo los cambios realmente grandes en el color (altas frecuencias).

Editar: un kernel de filtro 5×5 se define exactamente como se indica arriba.

por ejemplo, si su fila es 0.1 0.2 0.4 0.2 0.1 entonces si multiplica cada valor en su por el primer elemento para formar una columna y luego multiplica cada uno por el segundo elemento para formar la segunda columna y así sucesivamente, terminará con un filtro de

0.01 0.02 0.04 0.02 0.01
0.02 0.04 0.08 0.04 0.02
0.04 0.08 0.16 0.08 0.04
0.02 0.04 0.08 0.04 0.02
0.01 0.02 0.04 0.02 0.01

tomando algunas posiciones arbitrarias, puede ver que la posición 0, 0 es simple 0.1 * 0.1. La posición 0, 2 es 0,1 * 0,4, la posición 2, 2 es 0,4 * 0,4 y la posición 1, 2 es 0,2 * 0,4.

Espero que eso te dé una buena explicación.

  • @Goz Supongamos que quiero usar un kernel de filtro de 5×5, ¿cómo calculo los pesos que deben ir en el filtro?

    – Moeb

    8 de noviembre de 2009 a las 12:18

  • Podría haber mencionado la palabra clave: Redes neuronales respectivamente redes neuronales artificiales.

    – Azul amargo

    11 de marzo de 2015 a las 12:45

  • @bitterblue: ¿Puede explicar por qué las redes neuronales tienen algo que ver con el filtrado del espacio de imágenes?

    – Goz

    11 de marzo de 2015 a las 14:29

  • @Bitterblue: De verdad. ¿Cómo? Las redes neuronales son un algoritmo de aprendizaje automático. Se trata de filtrado de espacio de imagen 2D.

    – Goz

    11 de marzo de 2015 a las 16:59

  • @Bitterblue: punto justo, puede usar filtros para extraer funciones para el aprendizaje automático. Sin embargo, sigue siendo completamente irrelevante para esta pregunta …

    – Goz

    23 de marzo de 2015 a las 9:59

avatar de usuario
Cecil tiene un nombre

Aquí está el pseudocódigo del código que usé en C# para calcular el núcleo. Sin embargo, no me atrevo a decir que trato las condiciones finales correctamente:

double[] kernel = new double[radius * 2 + 1];
double twoRadiusSquaredRecip = 1.0 / (2.0 * radius * radius);
double sqrtTwoPiTimesRadiusRecip = 1.0 / (sqrt(2.0 * Math.PI) * radius);
double radiusModifier = 1.0;

int r = -radius;
for (int i = 0; i < kernel.Length; i++)
{
    double x = r * radiusModifier;
    x *= x;
    kernel[i] = sqrtTwoPiTimesRadiusRecip * Exp(-x * twoRadiusSquaredRecip);
    r++;
}

double div = Sum(kernel);
for (int i = 0; i < kernel.Length; i++)
{
    kernel[i] /= div;
}

Espero que esto ayude.

  • Creo que esta línea: sqrtTwoPiTimesRadiusRecip * Exp(-x * sqrtTwoPiTimesRadiusRecip); debe ser: sqrtTwoPiTimesRadiusRecip * Exp(-x * twoRadiusSquaredRecip);

    – ashagi

    30 de junio de 2011 a las 6:07


  • la multiplicacion por sqrtTwoPiTimesRadiusRecip no es necesario en absoluto porque normaliza el kernel de todos modos.

    – Emilio L.

    21/10/2014 a las 17:52

  • @Simson, no estoy seguro de qué hizo su edición, además de quitarme el crédito por sugerirlo formalmente después de encontrarme con el mismo problema con el código de mi compañero de trabajo

    – Charlie G.

    10 de abril de 2020 a las 1:38

  • @Charlie, mi edición le dio un +2 instantáneo en reputación, ya que cuando hice clic en “Mejorar edición”, su edición no tuvo que esperar a que la confirmara un segundo revisor. Cuando hay algo más que hacer en una publicación en la cola de revisión, esta es la forma de solucionarlo. Verifique el historial de edición, no se le robará ningún crédito.

    – Simón

    10 de abril de 2020 a las 3:17


  • @Simson Lo entiendo, pero estaba teniendo un viaje de poder sobre tener mi nombre / imagen en la página de preguntas, no solo en la historia 🙁 ja, oh, bueno, gracias por explicar

    – Charlie G.

    10 de abril de 2020 a las 3:59

Para usar el kernel de filtro discutido en el artículo de Wikipedia, debe implementar (discreto) circunvolución. La idea es que tenga una pequeña matriz de valores (el núcleo), mueva este núcleo de píxel a píxel en la imagen (es decir, de modo que el centro de la matriz esté en el píxel), multiplique los elementos de la matriz con la imagen superpuesta elementos, sume todos los valores en el resultado y reemplace el valor de píxel anterior con esta suma.

El desenfoque gaussiano se puede separar en dos circunvoluciones 1D (una vertical y otra horizontal) en lugar de una convolución 2D, lo que también acelera un poco las cosas.

No tengo claro si quieres restringir esto. para ciertas tecnologías, pero si no SVG (gráficos vectoriales escalables) tiene una implementación de Gaussian Blur. Creo que se aplica a todas las primitivas, incluidos los píxeles. SVG tiene la ventaja de ser un estándar abierto y ampliamente implementado.

avatar de usuario
Royi

Bueno, Gaussian Kernel es un kernel separable.
Por lo tanto, todo lo que necesita es una función que admita convolución 2D separable como: ImageConvolutionSeparableKernel().

Una vez que lo tenga, todo lo que necesita es un contenedor para generar el kernel gaussiano 1D y enviarlo a la función como se hizo en ImageConvolutionGaussianKernel().

El código es una implementación en C directa de 2D Image Convolution acelerada por SIMD (SSE) y Multi Threading (OpenMP).

Todo el proyecto está dado por – Convolución de imágenes – GitHub.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad