Fundamentos del Procesamiento de Imágenes

Domina los fundamentos del procesamiento de imágenes con nuestra guía detallada. Aprende sobre transformaciones, operaciones y segmentación. ¡Inicia tu viaje en visión artificial!

El procesamiento de imágenes es una rama fundamental de la visión artificial que permite transformar, analizar y manipular imágenes digitales para extraer información valiosa o mejorar su calidad. Comprender los fundamentos del procesamiento de imágenes es crucial para estudiantes y profesionales en campos como la inteligencia artificial, la robótica y la visión por computador.

Este artículo explora las principales transformaciones geométricas, operaciones aritméticas, análisis de histogramas, técnicas de segmentación y filtros morfológicos, todo con ejemplos prácticos utilizando la librería OpenCV en Python. Prepararemos tu base para el estudio avanzado del procesamiento de imágenes.

Fundamentos del Procesamiento de Imágenes: Transformaciones Geométricas

Las transformaciones geométricas son operaciones esenciales que cambian la posición de los píxeles en una imagen, alterando su forma, tamaño u orientación. La transformación afín es una de las más importantes, combinando traslación, rotación, escalado y cambios de perspectiva. Se representa mediante una matriz de transformación homogénea.

Traslación de una Imagen: Desplazamiento

La traslación es el desplazamiento de una imagen a lo largo de un vector dado, moviendo cada píxel una distancia finita en una dirección específica. Es como deslizar un objeto sin cambiar su orientación.

Un punto $^{A}P = [p_x, p_y, p_z]^T$ se traslada mediante un vector de traslación, resultando en un nuevo punto $[x', y', 1]^T = [x+t_x, y+t_y, 1]^T$. La matriz de traslación en coordenadas homogéneas es:

$$\left[ \begin{array}{l} x ^ {\prime} \ y ^ {\prime} \ 1 \end{array} \right] = \left[ \begin{array}{c c c} 1 & 0 & t_x \ 0 & 1 & t_y \ 0 & 0 & 1 \end{array} \right] \quad \cdot \quad \left[ \begin{array}{l} x \ y \ 1 \end{array} \right]$$

En OpenCV, la traslación manual implica recorrer cada píxel, aplicar la matriz de traslación o simplemente sumar los valores $t_x$ y $t_y$ a las coordenadas, y reasignar los píxeles a una nueva matriz de imagen.

import numpy as np
import cv2

imagen = cv2.imread("1.jpg")
imagen2 = cv2.resize(imagen, (100, 50))
alto, ancho, canales = imagen2.shape

tx = 10 # Traslación en x de 10 px
ty = 2 # Traslación en y de 2 px

# Creación de la matriz de traslación homogénea
mT = np.array([[1, 0, tx],
 [0, 1, ty],
 [0, 0, 1]], dtype=np.uint8)

imgT = np.zeros((alto + ty, ancho + tx, canales), dtype=np.uint8)

for i in range(alto):
 for j in range(ancho):
 x = j + tx
 y = i + ty
 imgT[y, x] = imagen2[i, j]

cv2.imshow("Original", imagen2)
cv2.imshow("Trasladada", imgT)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV también ofrece la función cv2.warpAffine para una traslación más eficiente, utilizando una matriz de transformación de 2x3.

import cv2
import numpy as np

imagen = cv2.imread("1.jpeg")
imagen = cv2.resize(imagen, (100, 50))
alto, ancho, canales = imagen.shape

mT = np.float32([[1, 0, 20], [0, 1, 10]]) # Matriz de 2x3: [1,0,tx], [0,1,ty]
imgT = cv2.warpAffine(imagen, mT, (ancho, alto))

cv2.imshow("Original", imagen)
cv2.imshow("Trasladada con warpAffine", imgT)
cv2.waitKey(0)
cv2.destroyAllWindows()

Rotación de Imágenes: Girando Píxeles

La rotación es una operación que gira una imagen alrededor de un punto central (generalmente el centro de la imagen) en un ángulo específico. Se puede interpretar como un operador rotacional que actúa sobre un vector de posición.

La matriz de rotación alrededor del eje Z para un ángulo $\theta$ es:

$$R_k(\theta) = \begin{bmatrix} \cos \theta & -\sin \theta & 0 \ \sin \theta & \cos \theta & 0 \ 0 & 0 & 1 \end{bmatrix}$$

OpenCV simplifica la rotación con cv2.getRotationMatrix2D para construir la matriz de rotación y cv2.warpAffine para aplicarla.

import cv2
import numpy as np

imagen = cv2.imread("2.png")
imagen2 = cv2.resize(imagen, (200, 200))
alto, ancho, canales = imagen2.shape

angle = 80
center = (ancho // 2, alto // 2)
scale = 1

M = cv2.getRotationMatrix2D(center, angle, scale) # Obtiene la matriz de rotación 2x3
imgT = cv2.warpAffine(imagen2, M, (ancho, alto)) # Aplica la transformación afín

cv2.imshow("Original", imagen2)
cv2.imshow("Rotada con warpAffine", imgT)
cv2.waitKey(0)
cv2.destroyAllWindows()

La librería imutils también ofrece una función más directa para rotar imágenes sin recortar el contenido, como imutils.rotate_bound.

Escalado Redimensionado: Cambiando el Tamaño de la Imagen

El escalado modifica el tamaño de una imagen, ya sea aumentándolo (zoom in) o disminuyéndolo (zoom out). Implica una matriz de transformación con modificaciones en la diagonal principal.

La matriz de escalado en coordenadas homogéneas es:

$$S = \begin{bmatrix} c_x & 0 & 0 \ 0 & c_y & 0 \ 0 & 0 & 1 \end{bmatrix}$$

Donde $c_x$ y $c_y$ son los factores de escalado en x e y. OpenCV ofrece la función cv2.resize para escalar imágenes de manera sencilla, permitiendo especificar el tamaño final o factores de escala, y diferentes métodos de interpolación.

import cv2

imagen = cv2.imread("1.jpeg")
escalada = cv2.resize(imagen, (1024, 720), interpolation = cv2.INTER_LINEAR) # Escalado a un tamaño fijo

cv2.imshow("Original", imagen)
cv2.imshow("Escalada", escalada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Transformaciones de Perspectiva: Sesgando la Visión

Las transformaciones de perspectiva, también conocidas como sesgado (skewing), modifican una imagen como si se viera desde un ángulo diferente. Implican modificaciones en los elementos no diagonales de la matriz de transformación afín. Se puede aplicar en el eje vertical (y) o horizontal (x).

Matriz de perspectiva vertical:

$$\left[ \begin{array}{c c c} 1 & s_v & 0 \ 0 & 1 & 0 \ 0 & 0 & 1 \end{array} \right]$$

Matriz de perspectiva horizontal:

$$\left[ \begin{array}{c c c} 1 & 0 & 0 \ s_h & 1 & 0 \ 0 & 0 & 1 \end{array} \right]$$

import cv2
import numpy as np

tMatrix = np.array([[1, 0, 0], [1, 1, 0], [0, 1, 1]], dtype=np.uint8) # Matriz para perspectiva horizontal
mResultante = np.zeros((500, 500, 3), dtype=np.uint8)

imagen = cv2.imread("1.png")
h, w, c = imagen.shape

for i in range(h):
 for j in range(w):
 pixel = imagen[i, j]
 vector = np.array([[j], [i], [1]], dtype=np.uint8) # Vector posición
 result = np.dot(tMatrix, vector)
 x = result[0][0]
 y = result[1][0]
 if 0 <= x < 500 and 0 <= y < 500: # Asegurar que las coordenadas estén dentro de los límites
 mResultante[y, x] = pixel

cv2.imshow("Original", imagen)
cv2.imshow("Perspectiva Horizontal", mResultante)
cv2.waitKey(0)
cv2.destroyAllWindows()

Operaciones Aritméticas con Imágenes en Procesamiento de Imágenes

Las operaciones aritméticas permiten combinar o manipular dos o más imágenes píxel a píxel, siempre y cuando tengan las mismas dimensiones y número de canales. Son fundamentales para tareas como la detección de movimiento o la mejora de imágenes.

Adición de Imágenes

La adición suma los valores de los píxeles correspondientes en dos imágenes. cv2.add(img1, img2) realiza esta suma. Si la suma excede el valor máximo (255 para 8-bit), se satura. cv2.addWeighted(img1, peso1, img2, peso2, valor_gama) permite controlar la contribución de cada imagen con pesos y añadir un valor gama.

Sustracción de Imágenes

La sustracción calcula la diferencia entre los valores de los píxeles. cv2.subtract(img1, img2) resta los valores. Es útil para detectar cambios entre imágenes, como en la detección de movimiento. cv2.absdiff(img1, img2) calcula la diferencia absoluta, asegurando que el resultado siempre sea positivo, lo cual es ideal para resaltar las áreas donde hubo cambios.

import cv2

img1 = cv2.imread("casaVacia.jpg")
img1 = cv2.resize(img1, (500, 500))

img2 = cv2.imread("casaDibujo.jpg") # Imagen con cambios (ej. ventanas y puertas dibujadas)
img2 = cv2.resize(img2, (500, 500))

resultadoResta = cv2.absdiff(img2, img1) # Resalta las diferencias entre las dos imágenes

cv2.imshow("Resultado Detección de Movimiento", resultadoResta)
cv2.waitKey(0)
cv2.destroyAllWindows()

Multiplicación y División de Imágenes

Similarmente, cv2.multiply(img1, img2) y cv2.divide(img1, img2) realizan la multiplicación y división de píxeles, respectivamente. Estas operaciones pueden utilizarse para ajustar el brillo, el contraste o para normalizar los valores de los píxeles.

Histograma: Análisis de Intensidad en Fundamentos del Procesamiento de Imágenes

El histograma de una imagen es una representación gráfica que muestra la distribución de la intensidad de los píxeles. El eje X representa el nivel de intensidad (0-255 para imágenes de 8 bits), y el eje Y representa el número de píxeles con ese nivel de intensidad.

Es una herramienta fundamental para entender la composición de brillo y contraste de una imagen. Una imagen con más píxeles a la derecha en el histograma es más brillante, mientras que una con más píxeles a la izquierda es más oscura.

Cálculo del Histograma con OpenCV y Matplotlib

La función cv2.calcHist(imágenes, canales, máscara, histSize, rango) permite calcular el histograma. Los parámetros clave son:

  • imágenes: Una lista de imágenes de entrada.
  • canales: El índice del canal para el que se calcula el histograma (ej., [0] para escala de grises, [0], [1], [2] para azul, verde, rojo respectivamente en color).
  • máscara: Una máscara para calcular el histograma de una región específica; None para la imagen completa.
  • histSize: Número de divisiones del histograma; [256] para cubrir todo el rango de 0 a 255.
  • rango: El rango de valores de intensidad a medir; [0, 256] para el rango completo.
import cv2
from matplotlib import pyplot as plt

img_gris = cv2.imread("Lena.png", 0) # Imagen en escala de grises
hist_gris = cv2.calcHist([img_gris], [0], None, [256], [0, 256])

plt.plot(hist_gris)
plt.title("Histograma en Escala de Grises")
plt.xlabel("Valor de Intensidad")
plt.ylabel("Frecuencia")
plt.show()

img_color = cv2.imread("Lena.png") # Imagen a color
color = ('b', 'g', 'r')
for i, col in enumerate(color):
 hist = cv2.calcHist([img_color], [i], None, [256], [0, 256])
 plt.plot(hist, color = col)
 plt.xlim([0, 256])
plt.title("Histograma de Canales de Color")
plt.xlabel("Valor de Intensidad")
plt.ylabel("Frecuencia")
plt.show()

Se puede utilizar una máscara para calcular el histograma de una región de interés específica dentro de la imagen, lo que es útil para análisis localizados.

Ecualización de Histogramas: Mejorando el Contraste

La ecualización de histogramas es una técnica de procesamiento de imágenes utilizada para mejorar el contraste global, especialmente en imágenes con bajo contraste o un rango limitado de intensidades. Redistribuye los valores de intensidad para que llenen todo el rango disponible.

cv2.equalizeHist(img) realiza esta operación. El resultado suele ser una imagen con un aspecto más equilibrado en cuanto a su distribución de brillo y oscuridad.

import cv2
import numpy as np

img = cv2.imread("a.jpg", 0) # Imagen en escala de grises
equ = cv2.equalizeHist(img)

res = np.hstack((img, equ)) # Concatena la imagen original y la ecualizada
cv2.imshow("Imagen Original vs Ecualizada", res)
cv2.waitKey(0)
cv2.destroyAllWindows()

Segmentación de Imágenes: Aislamiento de Objetos

La segmentación es el proceso de dividir una imagen digital en múltiples segmentos (conjuntos de píxeles), o

Temas relacionados