2009-11-08 19 views
50

Quiero difuminar mi imagen con la fórmula de desenfoque Gaussiana nativa. Leí this, pero no estoy seguro de cómo implementar esto.¿Cómo difumino una imagen en gaussian sin usar ninguna función gaussiana incorporada?

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

que no quieren utilizar cualquier construido en funciones de MATLAB como lo que tiene

+0

básicamente necesita implementar un operador de convolución equivalente a la función ** conv2() ** 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 correcta del kernel. – Amro

Respuesta

125

Escribir una falta de definición gaussiana ingenua es realmente bastante fácil. Se hace exactamente de la misma manera que cualquier otro filtro de convolución. La única diferencia entre un cuadro y un filtro gaussiano es la matriz que utiliza.

Imagine que tiene una imagen definida de la siguiente manera: matriz de filtro

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 

un cuadro de 3x3 se define como sigue:

0.111 0.111 0.111 
0.111 0.111 0.111 
0.111 0.111 0.111 

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

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

, entonces multiplicar el píxel 0 por la parte superior izquierda del filtro de desenfoque 3x3. Pixel 1 por la mitad superior, pixel 2, pixel 3 por la parte superior derecha, pixel 10 por la mitad izquierda y así sucesivamente.

A continuación, agréguelas por completo y escriba el resultado en el píxel 11. Como puede ver, Pixel 11 es ahora el promedio de sí mismo y los píxeles circundantes.

Las cajas Edge se vuelven un poco más complejas. ¿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 se muestra más tarde. Otra forma es empujar el píxel hacia los lugares circundantes.

Así que para la parte superior izquierda es posible colocar las muestras de la siguiente manera:

0 0 1 
0 0 1 
10 10 11 

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

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

por ejemplo, para un filtro definido arbitrariamente como (es decir, esto no es una gausiana, pero probablemente no muy lejos de)

0.1 0.8 0.1 

la primera columna sería el mismo pero multiplicada en el primer elemento de la fila por encima .

0.01 0.8 0.1 
0.08 
0.01 

La segunda columna sería el mismo pero los valores sería multiplicado por el 0,8 en 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 la adición de todos los anteriores juntos debe ser igual a 1. La diferencia entre el filtro anterior y el filtro de la caja original sería que el píxel final escrito tendría una ponderación mucho más pesado hacia el píxel central (es decir, el que ya está en esa posición). El desenfoque se produce porque los píxeles circundantes se difuminan en ese píxel, aunque no tanto. Usando este tipo de filtro se obtiene una imagen borrosa pero 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 puede hacer muchas cosas interesantes. Puede hacer una detección de bordes utilizando este tipo de filtro al restar los píxeles circundantes del píxel actual. Esto solo dejará atrás los grandes cambios en el color (altas frecuencias).

Editar: Un núcleo de filtro de 5x5 se define exactamente como arriba.

por ejemplo si su fila es 0.1 0.2 0.4 0.2 0.1 entonces si multiplicas cada valor en su por el primer elemento para formar una columna y luego multiplicas por el segundo elemento para formar la segunda columna y así terminarás 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 te dé una explicación lo suficientemente buena.

+0

@Goz Supongamos que quiero usar un núcleo de filtro de 5x5, ¿cómo puedo calcular los pesos que deberían ir en el filtro? – Moeb

+12

http://en.wikipedia.org/wiki/Gaussian_function Espero que ayude, -Tom – tsalter

+0

@Goz gracias .... – Moeb

3

no me queda claro si desea limitar este a ciertas tecnologías, pero si no SVG (ScalableVectorGraphics) tiene una implementación de desenfoque gaussiano. 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.

+0

Quiero usar la definición básica de un filtro gaussiano, y no hay implementaciones integradas. Quiero implementarlo solo. – Moeb

9

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

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

12

Aquí está el pseudo-código para el código que usé en C# para calcular el kernel. No me atrevo a decir que el trato a las condiciones finales correctamente, sin embargo:

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 * sqrtTwoPiTimesRadiusRecip); 
    r++; 
} 

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

Esperamos que esto pueda ayudar.

+2

creo, que esta línea: 'sqrtTwoPiTimesRadiusRecip * Exp (-x * sqrtTwoPiTimesRadiusRecip);' debe ser: ' sqrtTwoPiTimesRadiusRecip * Exp (-x * twoRadiusSquaredRecip);' – ashagi

+2

La multiplicación por 'sqrtTwoPiTimesRadiusRecip' no es necesaria en absoluto porque normaliza el kernel de todos modos. –

0

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

Una vez que lo tiene, todo lo que necesita es un contenedor para generar 1D Gaussian Kernel y enviarlo a la función como se hace en ImageConvolutionGaussianKernel().

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

Todo el proyecto está dado por - Image Convolution - GitHub.

Cuestiones relacionadas