Filtro de sal y pimienta
Un filtro basado en la obtención de la media de una ventana en una imagen permite que elementos aislados con ruido sean filtrados de forma efectiva, ello ya que esta clase de filtros es capaz de ignorar por completo a todos aquellos elementos espurios que se desvíen fuertemente de la población de la ventana. Un punto a considerar en esta clase de filtros es el tamaño del mismo, ya que a mayor tamaño el filtro será mas efectivo para eliminar el ruido, sin embargo, también puede generar un efecto de homogenización que afecte a las características de la imagen.
Imagen original con ruido de sal y pimienta
Imagen filtrada
Con objeto de aplicar un filtro de media, el siguiente procedimiento debe de ser aplicado.
Por cada pixel de la imagen
Extraer la información de la vecindad del pixel
Ordenar todos los valores de la vecindad de mayor a menor
Extraer el valor que se encuentre a la mitad de la lista ordenada
Almacenar ese valor como representativo para la imagen filtrada
Como ejemplo, suponiendo se esta analizando una ventana 5x5 centrada en el pixel con el valor 104
[__0, _96, 109, 106, 255]
[255, 109, 105, __0, 103]
[089, _87, 104, 108, __0]
[131, __0, 102, 255, __0]
[102, 255, 109, 112, 255]
Considerando que la lista ordenada de valores en esta ventana es:
0, 0, 0, 0, 0, 87, 89, 96, 103, 102, 102, 104, 105, 106, 109, 109, 109, 108, 112, 131, 255, 255, 255, 255, 255
El valor representativo de la ventana es: 105, lo cual es apropiado ya que no modifica notoriamente los valores de casillas sin ruido, pero infiere valores esperables para casillas que presenten interferencias de sal o pimienta.
Script del ejemplo empleado
import cv2
import matplotlib.pyplot as plt
import numpy as np
# Se carga la imagen
original = cv2.imread('sydney con sal y pimienta.png', 0)
# Se extraen los tamaños de la imagen
y, x = original.shape
# Se define un tamaño de kernel
kernel_s = 7
# Se definen variables utiles
km = int((kernel_s-1)/2)
punto_medio = int((kernel_s**2)/2)
# Se genera una matriz donde depositar el resultado
filtrada = np.zeros((y-kernel_s+1, x-kernel_s+1))
# Por cada pixel com una ventana valida
for yi in range(km, y-km):
for xi in range(km, x-km):
# Se extrae la ventana
ventana = original[yi-km:yi+km+1, xi-km:xi+km+1]
# Se extrae el valor medio de la ventana
vectorizado = ventana.flatten()
i = np.argsort(vectorizado)[punto_medio]
# Se almacenta el valor medio en la matriz de resultados
filtrada[yi-km, xi-km] = ventana[i//kernel_s, i%kernel_s]
# Se guarda la imagen
cv2.imwrite('sydney filtrada.png', filtrada)