Chismes del Lince 56: filtros de imagen con Python
Cuando no hay ninguna misión, el Lince de la Noche se sube a lo alto de Sierra Bermeja con su cámara y un trípode, y apunta al cielo. Es su afición secreta: la astrofotografía. Esta noche ha hecho una foto larga de la Vía Láctea sobre Estepona y, al verla en el portátil, resopla: el cielo está borroso por la luz de la costa, hay puntitos de ruido por todas partes y las estrellas apenas se distinguen.
—Todo esto lo arreglaría con un programa de edición —refunfuña el Lince—. Pero no tengo ni idea de qué hace por dentro.
—Pues lo programamos nosotros —dice la Gata Montesa, sin levantar la vista—. Una foto es una tabla de números. Si sabes recorrer la tabla, sabes filtrarla.
—¿Solo números? —el Lince enarca una ceja—. Entonces esto va a ser más fácil de lo que pensaba.
—No tan fácil —sonríe ella—. Va a haber matemáticas.

Introducción
En este chisme construimos un laboratorio de imágenes que funciona en el navegador: cargas una foto, eliges un filtro y ves a la vez el resultado, el programa en Python que lo ha calculado y la ecuación que hay detrás. La foto no se sube a ningún servidor: se procesa en tu propio ordenador.
La idea de partida es sencilla. Una imagen digital (de mapa de bits) es una cuadrícula de puntos, los píxeles, y cada píxel guarda tres números de 0 a 255: cuánta luz roja, verde y azul tiene (el modelo RGB). Un filtro es un programa que recorre esa cuadrícula y calcula píxeles nuevos con una fórmula. Sin matemáticas no hay filtros: lo vamos a ver con tres de ellos, el gris, el pixelado y el desenfoque. El laboratorio trae otros 19 para que juegues.
Filtro 1: pasar a gris
El Lince quiere ver solo el brillo de su foto, sin colores que distraigan. Un gris es un color con los tres números iguales (R = G = B), así que hay que decidir qué valor ponerles. La Gata propone la media de siempre, (R + G + B) / 3, y luego la desmonta: el ojo humano es más sensible al verde que al azul, así que cada canal debe pesar distinto. La fórmula habitual (la del estándar de vídeo BT.601) es:
gris = 0{,}299\,R + 0{,}587\,G + 0{,}114\,B
Los tres pesos suman 1, así que un blanco (255, 255, 255) sigue siendo 255. En Python, el núcleo del filtro es una sola línea dentro de dos bucles que recorren filas y columnas:
resultado = []
for y in range(alto):
fila = []
for x in range(ancho):
r, g, b = orig[y][x]
gris = 0.299*r + 0.587*g + 0.114*b
fila.append((gris, gris, gris))
resultado.append(fila)
Prueba después el filtro «Gris por promedio» y compara: con el promedio, un azul puro sale mucho más claro que con los pesos. Medir el brillo no es lo mismo que percibirlo.
Filtro 2: pixelar
Los puntitos de ruido de la foto del Lince son píxeles sueltos que se salen de lo normal. Una forma de apagarlos es promediar: dividimos la imagen en bloques de b × b píxeles y pintamos cada bloque con la media de los colores que contiene.
color\ del\ bloque = \frac{1}{n}\sum_{i=1}^{n} color_i \qquad (n = b \times b)
Promediar n valores con ruido independiente reduce el ruido (en torno a 1/√n), y esa es la razón por la que los astrofótografos suman muchas fotos del mismo cielo. El precio es que se pierde detalle: con bloques grandes, la imagen queda hecha de cuadrados. En el programa, tres bucles anidados recorren cada bloque, suman sus colores y los dividen entre n:
for by in range(0, alto, bloque):
for bx in range(0, ancho, bloque):
sr = sg = sb = n = 0
for y in range(by, min(by + bloque, alto)):
for x in range(bx, min(bx + bloque, ancho)):
r, g, b = orig[y][x]
sr += r; sg += g; sb += b; n += 1
media = (sr / n, sg / n, sb / n)
# ...y después se pinta todo el bloque con "media"
En el laboratorio, el deslizador bloque te deja pasar de 2 a 30 píxeles y ver cómo la imagen deja de ser reconocible.
Filtro 3: desenfoque
Pixelar promedia por bloques separados; el desenfoque promedia alrededor de cada píxel. Cada píxel se sustituye por la media de sí mismo y sus 8 vecinos. Esa operación se llama convolución, y se describe con una pequeña matriz de pesos, el kernel, que dice cuánto cuenta cada vecino:
kernel = \frac{1}{9}\begin{pmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{pmatrix}
En el código, el kernel es una lista de listas y el bucle recorre los 9 píxeles del entorno. Con max y min evitamos salirnos de la imagen en los bordes:
kernel = [[1, 1, 1],
[1, 1, 1],
[1, 1, 1]]
divisor = 9
sr = sg = sb = 0
for dy in range(-1, 2):
for dx in range(-1, 2):
xv = max(0, min(ancho - 1, x + dx))
yv = max(0, min(alto - 1, y + dy))
r, g, b = orig[yv][xv]
peso = kernel[dy + 1][dx + 1]
sr += r * peso; sg += g * peso; sb += b * peso
nuevo = (sr / divisor, sg / divisor, sb / divisor)
La Gata señala el truco: si cambias los números del kernel, cambias de filtro. Con [[0, -1, 0], [-1, 5, -1], [0, -1, 0]] el desenfoque se convierte en enfoque, y con otros pesos sale el relieve o la detección de bordes. Para el Lince hay un compromiso claro: el desenfoque suaviza el ruido, pero también emborrona las estrellas.


Prueba el laboratorio
Aquí tienes el laboratorio completo. Carga una foto tuya (o usa el paisaje y la carta de colores de ejemplo), elige un filtro, mueve los deslizadores y pasa el ratón por las imágenes para ver los números (R, G, B) de cada píxel antes y después. Con el tamaño en 32 píxeles ves los píxeles uno a uno. Puedes editar el programa de Python y volver a ejecutarlo; si te equivocas, los avisos salen en español.
Código para insertarlo en WordPress (bloque «HTML personalizado»):
Abrir el laboratorio en pantalla completa
<iframe src="https://montesteam.org/app/chisme56-imagenes.html" title="Laboratorio de imágenes del Lince: filtros en Python" width="100%" height="1900" style="border:0;max-width:1280px;display:block;margin:0 auto" loading="lazy"></iframe>
<p><a href="https://montesteam.org/app/chisme56-imagenes.html" target="_blank" rel="noopener">Abrir el laboratorio en pantalla completa</a></p>
Si el hueco te queda corto o largo, cambia height. En el móvil conviene usar el enlace de pantalla completa. Para la imagen de cabecera usa el estilo de la serie: el Lince ante el cielo de Sierra Bermeja con el trípode y la cámara, y la Gata señalando una pantalla con la foto filtrada.
El mismo principio de «una imagen son números» está detrás de El ojo del Lince y del detector de obstáculos, donde una IA recibe los píxeles de la cámara y los convierte en una decisión.
Para el aula y retos
El chisme encaja en Creación Digital y Pensamiento Computacional (procesamiento de imágenes, diseño de patrones, funciones gráficas) y en TIC de 1.º de Bachillerato (estructuras de control y funciones). Es una buena ocasión para repasar qué es una imagen de mapa de bits y el modelo de color RGB. Preguntas para la clase: ¿cuántos números hacen falta para guardar una foto de 128 × 96?, ¿por qué el gris por promedio y el ponderado no coinciden?, ¿qué pasa con el ruido y con los detalles al aumentar el bloque o el kernel?
- Reto 1. Haz que funcione: carga una foto y pásala a gris.
- Reto 2. Modifica: cambia los pesos del gris (por ejemplo 0,33 cada uno) y compara los resultados.
- Reto 3. Cambia el kernel de la matriz libre para lograr un desenfoque más fuerte con 5 × 5 píxeles.
- Reto 4. Encadena filtros: gris, desenfoque y bordes de Sobel. ¿Se ven mejor los bordes con o sin desenfoque previo?
- Reto 5. Escribe tu propio filtro (por ejemplo, un umbral que deje solo las estrellas más brillantes) y explica su ecuación.
Etiqueta:astrofotografía, convolución, filtros de imagen, píxeles, procesamiento de imágenes, python, rgb



