OpenCV

Tutorial OpenCV: Operaciones básicas en imágenes

OpenCV: Aprende a recortar y redimensionar imágenes. Domina indexación con NumPy y técnicas de interpolación para conservación de calidad en Python.

Aprende OpenCV GRATIS y certifícate

Recorte y redimensionamiento

El recorte y el redimensionamiento son operaciones fundamentales en el procesamiento de imágenes con OpenCV. Estas técnicas permiten manipular imágenes para enfocarse en regiones específicas o ajustar su tamaño para diferentes aplicaciones.

Para recortar una imagen, se utilizan las capacidades de indexación de arrays de NumPy. Dado que una imagen en OpenCV se representa como un array multidimensional, es posible seleccionar una región de interés (ROI) mediante el slicing de matrices:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Definir las coordenadas del recorte: [alto_inicio:alto_fin, ancho_inicio:ancho_fin]
roi = imagen[50:200, 100:300]

# Mostrar la región recortada
cv2.imshow('Recorte', roi)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este ejemplo, se extrae una porción de la imagen comprendida entre las filas 50 y 200 y las columnas 100 y 300. Es importante tener en cuenta que la indexación en arrays de imágenes sigue el formato [alto, ancho].

El redimensionamiento permite cambiar el tamaño de una imagen, ya sea para reducirla o ampliarla. La función cv2.resize() facilita este proceso:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Definir el nuevo tamaño (ancho, alto)
nuevo_tamano = (800, 600)

# Redimensionar la imagen
imagen_redimensionada = cv2.resize(imagen, nuevo_tamano)

# Mostrar la imagen redimensionada
cv2.imshow('Redimensionada', imagen_redimensionada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Al redimensionar, es esencial considerar la relación de aspecto para evitar distorsiones. Para mantener la proporción original de la imagen, se puede calcular el factor de escala:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Obtener dimensiones originales
alto_original, ancho_original = imagen.shape[:2]

# Definir factor de escala
factor_escala = 0.5

# Calcular nuevas dimensiones
ancho_nuevo = int(ancho_original * factor_escala)
alto_nuevo = int(alto_original * factor_escala)
dimensiones = (ancho_nuevo, alto_nuevo)

# Redimensionar la imagen con el factor de escala
imagen_redimensionada = cv2.resize(imagen, dimensiones)

# Mostrar la imagen resultante
cv2.imshow('Redimensionada con Escala', imagen_redimensionada)
cv2.waitKey(0)
cv2.destroyAllWindows()

La función cv2.resize() también permite especificar el método de interpolación, que afecta la calidad de la imagen resultante. Los métodos más comunes son:

  • cv2.INTER_LINEAR: Interpolación bilineal, adecuada para ampliar imágenes.
  • cv2.INTER_AREA: Recomendada para reducir imágenes, ya que preserva detalles.
  • cv2.INTER_CUBIC: Usa resampling bicúbico para mayor suavidad al ampliar.

Por ejemplo, para reducir una imagen con una interpolación específica:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Definir nuevo tamaño reduciendo al 60%
ancho_nuevo = int(imagen.shape[1] * 0.6)
alto_nuevo = int(imagen.shape[0] * 0.6)
dimensiones = (ancho_nuevo, alto_nuevo)

# Redimensionar con interpolación INTER_AREA
imagen_reducida = cv2.resize(imagen, dimensiones, interpolation=cv2.INTER_AREA)

# Mostrar la imagen reducida
cv2.imshow('Reducida', imagen_reducida)
cv2.waitKey(0)
cv2.destroyAllWindows()

Al combinar recorte y redimensionamiento, es posible preparar imágenes para análisis más precisos o para cumplir con requisitos específicos de tamaño.

Estas operaciones son esenciales en preprocesamiento y pueden afectar significativamente el rendimiento de los algoritmos de visión por computador. Por ello, es fundamental entender cómo aplicar el recorte y el redimensionamiento de manera correcta.

Rotación y traslación

Las operaciones de rotación y traslación son fundamentales en el procesamiento de imágenes con OpenCV, ya que permiten modificar la posición y orientación de una imagen en el espacio bidimensional. Estas transformaciones geométricas se realizan utilizando matrices de transformación y la función cv2.warpAffine.

Para trasladar una imagen, se desplaza en las direcciones x e y sin alterar su forma o tamaño. Esto se logra mediante una matriz de traslación y la aplicación de una transformación afín:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Definir la traslación en x e y
desplazamiento_x = 50  # Desplazamiento hacia la derecha
desplazamiento_y = 30  # Desplazamiento hacia abajo

# Crear la matriz de traslación
matriz_traslacion = np.float32([[1, 0, desplazamiento_x],
                                [0, 1, desplazamiento_y]])

# Obtener las dimensiones de la imagen
alto, ancho = imagen.shape[:2]

# Aplicar la traslación
imagen_trasladada = cv2.warpAffine(imagen, matriz_traslacion, (ancho, alto))

# Mostrar la imagen trasladada
cv2.imshow('Imagen Trasladada', imagen_trasladada)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este ejemplo, se crea una matriz de traslación que especifica el desplazamiento en los ejes x e y. La imagen resultante se desplaza 50 píxeles a la derecha y 30 píxeles hacia abajo.

La rotación implica girar la imagen alrededor de un punto específico, generalmente el centro de la imagen, por un ángulo determinado. OpenCV facilita esta operación mediante la función cv2.getRotationMatrix2D, que genera la matriz de rotación:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Obtener las dimensiones de la imagen
alto, ancho = imagen.shape[:2]

# Definir el centro de rotación
centro = (ancho // 2, alto // 2)

# Definir el ángulo de rotación en grados
angulo = 45

# Definir el factor de escala
escala = 1.0

# Obtener la matriz de rotación
matriz_rotacion = cv2.getRotationMatrix2D(centro, angulo, escala)

# Aplicar la rotación
imagen_rotada = cv2.warpAffine(imagen, matriz_rotacion, (ancho, alto))

# Mostrar la imagen rotada
cv2.imshow('Imagen Rotada', imagen_rotada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Aquí, la imagen se rota 45 grados alrededor de su centro sin cambiar su escala. La función cv2.warpAffine utiliza la matriz de rotación para realizar la transformación.

Es posible combinar la rotación y la traslación modificando la matriz de transformación afín. Por ejemplo, para rotar una imagen y luego trasladarla:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Dimensiones de la imagen
alto, ancho = imagen.shape[:2]

# Centro de rotación
centro = (ancho // 2, alto // 2)

# Parámetros de rotación
angulo = 30
escala = 0.8

# Obtener la matriz de rotación
matriz_rotacion = cv2.getRotationMatrix2D(centro, angulo, escala)

# Añadir traslación a la matriz (desplazamiento en x e y)
matriz_rotacion[0, 2] += 100  # Trasladar 100 píxeles en x
matriz_rotacion[1, 2] += 50   # Trasladar 50 píxeles en y

# Aplicar la transformación
imagen_transformada = cv2.warpAffine(imagen, matriz_rotacion, (ancho, alto))

# Mostrar la imagen resultante
cv2.imshow('Imagen Transformada', imagen_transformada)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este caso, se modifica directamente la matriz de transformación para incluir tanto la rotación como la traslación. Se puede observar cómo se ajustan los elementos de la matriz para desplazar la imagen después de rotarla.

Es importante considerar que, al realizar rotaciones, partes de la imagen pueden quedar fuera del marco original. Para evitar perder información, es posible recalcular el tamaño de la imagen resultante. A continuación, se muestra cómo rotar una imagen sin recortar utilizando funciones de cálculo de dimensiones:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Dimensiones originales
alto_original, ancho_original = imagen.shape[:2]

# Definir el ángulo de rotación
angulo = 45

# Calcular las dimensiones de la nueva imagen
angulo_rad = np.deg2rad(angulo)
ancho_nuevo = int(abs(np.sin(angulo_rad)*alto_original) + abs(np.cos(angulo_rad)*ancho_original))
alto_nuevo = int(abs(np.sin(angulo_rad)*ancho_original) + abs(np.cos(angulo_rad)*alto_original))

# Crear la matriz de rotación con el nuevo centro
centro = (ancho_nuevo // 2, alto_nuevo // 2)
matriz_rotacion = cv2.getRotationMatrix2D(centro, angulo, 1.0)

# Ajustar la matriz de rotación para tener en cuenta el cambio de tamaño
matriz_rotacion[0, 2] += (ancho_nuevo - ancho_original) / 2
matriz_rotacion[1, 2] += (alto_nuevo - alto_original) / 2

# Aplicar la rotación con el nuevo tamaño
imagen_rotada = cv2.warpAffine(imagen, matriz_rotacion, (ancho_nuevo, alto_nuevo))

# Mostrar la imagen rotada completa
cv2.imshow('Imagen Rotada Completa', imagen_rotada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Este método permite conservar toda la imagen tras la rotación, evitando que se recorten los bordes. Se recalculan las dimensiones necesarias y se ajusta la matriz de rotación en consecuencia.

La función cv2.warpAffine utiliza una interpolación bilineal por defecto, pero es posible especificar otros métodos de interpolación para mejorar la calidad o la velocidad del procesamiento. Por ejemplo, para utilizar interpolación bicúbica:

# Aplicar la rotación con interpolación bicúbica
imagen_rotada = cv2.warpAffine(imagen, matriz_rotacion, (ancho_nuevo, alto_nuevo), flags=cv2.INTER_CUBIC)

La comprensión de las transformaciones de rotación y traslación es esencial para tareas como la alineación de imágenes, corrección de perspectiva y aumento de datos en aprendizaje automático. Estas operaciones permiten manipular la orientación y posición de las imágenes de manera efectiva y controlada.

Suavizado y filtrado

El suavizado y el filtrado son técnicas esenciales en el procesamiento de imágenes con OpenCV, utilizadas para reducir el ruido y mejorar la calidad visual. Estas operaciones permiten preparar las imágenes para análisis más precisos y destacan características específicas al atenuar detalles menores.

Una de las formas más básicas de suavizado es mediante el filtro de caja o blur promedio, que calcula la media de los píxeles en una ventana definida y asigna ese valor al píxel central:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Aplicar el filtro de blur promedio
imagen_suavizada = cv2.blur(imagen, (5, 5))

# Mostrar la imagen suavizada
cv2.imshow('Blur Promedio', imagen_suavizada)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este ejemplo, se aplica un filtro de ventana de 5x5 píxeles. Este método es simple pero puede desenfocar los bordes importantes de la imagen.

El suavizado Gaussiano utiliza una función de distribución normal para calcular los pesos de cada píxel en la ventana, lo que da como resultado una transición más suave y preserva mejor los bordes:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Aplicar el filtro Gaussiano
imagen_gaussiana = cv2.GaussianBlur(imagen, (5, 5), sigmaX=0)

# Mostrar la imagen suavizada
cv2.imshow('Suavizado Gaussiano', imagen_gaussiana)
cv2.waitKey(0)
cv2.destroyAllWindows()

El parámetro sigmaX=0 hace que OpenCV calcule el valor adecuado en función del tamaño del kernel. El filtro Gaussiano es especialmente útil para reducir el ruido aleatorio sin sacrificar demasiada nitidez.

El filtro mediano es eficaz para eliminar el ruido sal y pimienta, ya que reemplaza cada píxel por la mediana de los píxeles en la ventana:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Aplicar el filtro mediano
imagen_mediana = cv2.medianBlur(imagen, 5)

# Mostrar la imagen suavizada
cv2.imshow('Filtro Mediano', imagen_mediana)
cv2.waitKey(0)
cv2.destroyAllWindows()

El tamaño del kernel debe ser un número impar y mayor que 1. El filtro mediano preserva mejor los bordes en comparación con el blur promedio.

Para conservar los bordes mientras se reduce el ruido, se puede utilizar el filtro bilateral, que considera tanto la proximidad espacial como la similitud de intensidad:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Aplicar el filtro bilateral
imagen_bilateral = cv2.bilateralFilter(imagen, d=9, sigmaColor=75, sigmaSpace=75)

# Mostrar la imagen filtrada
cv2.imshow('Filtro Bilateral', imagen_bilateral)
cv2.waitKey(0)
cv2.destroyAllWindows()

El filtro bilateral es más computacionalmente intenso pero es excelente para aplicaciones donde es crucial mantener los bordes nítidos.

Además de estos filtros, OpenCV permite crear filtros personalizados utilizando el concepto de convolución. Por ejemplo, para aplicar un filtro de desenfoque personalizado:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Crear un kernel personalizado
kernel = np.ones((5, 5), np.float32) / 25

# Aplicar el filtro personalizado
imagen_filtrada = cv2.filter2D(imagen, ddepth=-1, kernel=kernel)

# Mostrar la imagen filtrada
cv2.imshow('Filtro Personalizado', imagen_filtrada)
cv2.waitKey(0)
cv2.destroyAllWindows()

La convolución permite aplicar operaciones más complejas y diseñar kernels para tareas específicas, como detección de bordes o realce de detalles.

Para imágenes en entornos de poca luz, el suavizado no local (Non-local Means Denoising) puede ser útil:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Aplicar el suavizado no local
imagen_denoised = cv2.fastNlMeansDenoisingColored(imagen, h=10, hColor=10, templateWindowSize=7, searchWindowSize=21)

# Mostrar la imagen suavizada
cv2.imshow('Suavizado No Local', imagen_denoised)
cv2.waitKey(0)
cv2.destroyAllWindows()

Este método analiza conjuntos de píxeles similares en toda la imagen, ofreciendo una reducción de ruido más efectiva sin perder textura.

Es importante ajustar los parámetros de cada filtro según las características de la imagen y el efecto deseado. Por ejemplo, aumentar el tamaño del kernel en un filtro Gaussiano aumentará el nivel de suavizado pero puede difuminar detalles importantes.

Al aplicar suavizado y filtrado, es esencial considerar el balance entre la reducción de ruido y la preservación de detalles. Estas técnicas son fundamentales en preprocesamiento para segmentación, detección de bordes y mejora de imágenes, y contribuyen significativamente al rendimiento de algoritmos de visión por computador.

Ajuste de brillo y contraste

El ajuste de brillo y contraste es una operación fundamental en el procesamiento de imágenes, que permite mejorar la visualización y resaltar detalles importantes. En OpenCV, existen diversas técnicas para modificar el brillo y el contraste de una imagen de manera eficiente y controlada.

Una forma sencilla de ajustar el brillo es sumando o restando un valor constante a todos los píxeles de la imagen. Para ajustar el contraste, se puede multiplicar los píxeles por un factor determinado. Sin embargo, es recomendable utilizar la función cv2.convertScaleAbs() para manejar correctamente los límites de intensidad y evitar desbordamientos:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Factores de ajuste
alpha = 1.5  # Factor de contraste [1.0-3.0]
beta = 50    # Valor de brillo [0-100]

# Aplicar el ajuste de brillo y contraste
imagen_ajustada = cv2.convertScaleAbs(imagen, alpha=alpha, beta=beta)

# Mostrar la imagen ajustada
cv2.imshow('Imagen Ajustada', imagen_ajustada)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este ejemplo, alpha representa el factor de contraste, mientras que beta corresponde al ajuste de brillo. La función cv2.convertScaleAbs() aplica la transformación:

$$ \text{imagen\_ajustada}(x, y) = \alpha \cdot \text{imagen}(x, y) + \beta $$

Esta operación se realiza de forma eficiente y asegura que los valores resultantes estén dentro del rango permitido [0, 255].

Para un control más preciso, es posible ajustar el brillo y el contraste utilizando la función cv2.addWeighted(), que permite combinar la imagen con sí misma aplicando diferentes pesos:

import cv2

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Ajustar los factores
alpha = 1.2  # Contraste
beta = 35    # Brillo

# Aplicar el ajuste utilizando addWeighted
imagen_ajustada = cv2.addWeighted(imagen, alpha, imagen, 0, beta)

# Mostrar la imagen resultante
cv2.imshow('Ajuste con addWeighted', imagen_ajustada)
cv2.waitKey(0)
cv2.destroyAllWindows()

La función cv2.addWeighted() realiza la operación:

$$ \text{imagen\_ajustada} = \alpha \cdot \text{imagen} + \beta $$

Donde se pueden ajustar los pesos de las imágenes sumadas y el valor de brillo.

Otra técnica útil es la ecualización de histograma, que mejora el contraste distribuyendo de manera uniforme los niveles de intensidad. Aunque se profundiza en ecualización en lecciones posteriores, se puede aplicar la función cv2.equalizeHist() para imágenes en escala de grises:

import cv2

# Cargar la imagen en escala de grises
imagen = cv2.imread('imagen.jpg', cv2.IMREAD_GRAYSCALE)

# Aplicar la ecualización de histograma
imagen_ecualizada = cv2.equalizeHist(imagen)

# Mostrar la imagen ecualizada
cv2.imshow('Ecualización de Histograma', imagen_ecualizada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Para imágenes en color, se puede convertir la imagen al espacio de color YCrCb y aplicar la ecualización solo al canal de luminancia:

import cv2

# Cargar la imagen en color
imagen = cv2.imread('imagen.jpg')

# Convertir al espacio YCrCb
imagen_ycrcb = cv2.cvtColor(imagen, cv2.COLOR_BGR2YCrCb)

# Dividir los canales
canales = cv2.split(imagen_ycrcb)

# Aplicar ecualización al canal Y (luminancia)
cv2.equalizeHist(canales[0], canales[0])

# Combinar los canales nuevamente
imagen_ycrcb = cv2.merge(canales)

# Convertir de regreso al espacio BGR
imagen_ecualizada = cv2.cvtColor(imagen_ycrcb, cv2.COLOR_YCrCb2BGR)

# Mostrar la imagen resultante
cv2.imshow('Ecualización en Color', imagen_ecualizada)
cv2.waitKey(0)
cv2.destroyAllWindows()

El ajuste de brillo y contraste también puede realizarse mediante operaciones aritméticas directas utilizando arrays de NumPy. Sin embargo, es crucial manejar correctamente los tipos de datos para evitar resultados indeseados:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Convertir a tipo float32 para evitar saturación
imagen_float = imagen.astype(np.float32)

# Ajustar contraste y brillo
alpha = 1.3  # Contraste
beta = 40    # Brillo

imagen_ajustada = alpha * imagen_float + beta

# Recortar los valores para que estén en el rango [0-255]
imagen_ajustada = np.clip(imagen_ajustada, 0, 255)

# Convertir de regreso a tipo uint8
imagen_ajustada = imagen_ajustada.astype(np.uint8)

# Mostrar la imagen ajustada
cv2.imshow('Ajuste con NumPy', imagen_ajustada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Utilizando NumPy, se tiene flexibilidad para aplicar transformaciones más complejas y personalizadas.

Otro método avanzado es utilizar la curva de gamma para ajustar el brillo de manera no lineal. La corrección gamma puede aclarar o oscurecer una imagen según el valor del exponente:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Función para aplicar corrección gamma
def ajustar_gamma(imagen, gamma=1.0):
    # Construir la tabla de corrección
    inv_gamma = 1.0 / gamma
    tabla = np.array([((i / 255.0) ** inv_gamma) * 255
                      for i in range(256)]).astype("uint8")
    # Aplicar la corrección gamma
    return cv2.LUT(imagen, tabla)

# Aplicar la corrección gamma
gamma = 2.0  # Valores >1.0 oscurecen, <1.0 aclaran
imagen_gamma = ajustar_gamma(imagen, gamma=gamma)

# Mostrar la imagen resultante
cv2.imshow('Corrección Gamma', imagen_gamma)
cv2.waitKey(0)
cv2.destroyAllWindows()

La función cv2.LUT() aplica una tabla de búsqueda, lo que hace el proceso eficiente.

Es importante recordar que al modificar el brillo y el contraste, se debe tener cuidado para no perder información en las áreas oscuras o claras de la imagen. Un ajuste excesivo puede llevar a la saturación, donde los detalles se pierden al alcanzar los valores máximos o mínimos de intensidad.

Para visualizar el efecto de los ajustes, es útil comparar la imagen original con la ajustada. OpenCV permite mostrar múltiples imágenes en ventanas separadas o combinarlas en una sola:

import cv2
import numpy as np

# Crear una función para mostrar lado a lado
def mostrar_comparacion(imagen1, imagen2, titulo1='Original', titulo2='Ajustada'):
    combinada = np.hstack((imagen1, imagen2))
    cv2.imshow(f'{titulo1} vs {titulo2}', combinada)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# Llamar a la función con las imágenes
mostrar_comparacion(imagen, imagen_ajustada)

Esta comparación ayuda a entender visualmente cómo el ajuste de brillo y contraste afecta a la imagen.

En resumen, el ajuste de brillo y contraste es esencial para mejorar la calidad de las imágenes y prepararlas para análisis posteriores. OpenCV ofrece métodos eficientes y flexibles para realizar estos ajustes, permitiendo un control preciso y adaptado a las necesidades específicas del procesamiento de imágenes.

Aplicación de máscaras

La aplicación de máscaras es una técnica esencial en el procesamiento de imágenes con OpenCV, que permite manipular y analizar regiones específicas de una imagen. Una máscara es una imagen binaria que indica qué píxeles deben ser procesados y cuáles deben ser ignorados, facilitando operaciones focalizadas en áreas de interés.

Para crear una máscara, se puede utilizar una imagen del mismo tamaño que la original, donde los píxeles que se desean procesar se establecen en blanco (valor 255) y los que se desean excluir en negro (valor 0). Por ejemplo, para aislar una región específica:

import cv2
import numpy as np

# Cargar la imagen original
imagen = cv2.imread('imagen.jpg')

# Crear una máscara negra del mismo tamaño
mascara = np.zeros(imagen.shape[:2], dtype='uint8')

# Definir una región de interés (ROI)
centro = (imagen.shape[1] // 2, imagen.shape[0] // 2)
radio = 100

# Dibujar un círculo blanco en la máscara
cv2.circle(mascara, centro, radio, 255, -1)

# Aplicar la máscara a la imagen original
imagen_mascara = cv2.bitwise_and(imagen, imagen, mask=mascara)

# Mostrar la imagen resultante
cv2.imshow('Imagen enmascarada', imagen_mascara)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este ejemplo, se crea una máscara circular para extraer una región circular de la imagen. La función cv2.bitwise_and() aplica una operación lógica AND entre la imagen y la máscara, manteniendo solo los píxeles donde la máscara es blanca.

Las máscaras también pueden generarse a partir de condiciones específicas utilizando operaciones de umbrales. Por ejemplo, para crear una máscara que seleccione todos los píxeles con una intensidad de azul superior a un cierto valor:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('imagen.jpg')

# Convertir la imagen al espacio de color HSV
imagen_hsv = cv2.cvtColor(imagen, cv2.COLOR_BGR2HSV)

# Definir el rango de color para la máscara
bajo_azul = np.array([100, 150, 0])
alto_azul = np.array([140, 255, 255])

# Crear la máscara basada en el rango de color
mascara = cv2.inRange(imagen_hsv, bajo_azul, alto_azul)

# Aplicar la máscara a la imagen original
resultado = cv2.bitwise_and(imagen, imagen, mask=mascara)

# Mostrar la imagen resultante
cv2.imshow('Detección de Color Azul', resultado)
cv2.waitKey(0)
cv2.destroyAllWindows()

Aquí, se utiliza el espacio de color HSV para crear una máscara que detecta los tonos azules en la imagen. La función cv2.inRange() genera la máscara binaria según los límites de color especificados.

La aplicación de máscaras es fundamental en tareas como la segmentación de imágenes, donde se necesita separar objetos del fondo. Por ejemplo, para extraer el primer plano de una imagen utilizando la técnica de umbralización:

import cv2
import numpy as np

# Cargar la imagen en escala de grises
imagen = cv2.imread('imagen.jpg', cv2.IMREAD_GRAYSCALE)

# Aplicar umbralización binaria
ret, mascara = cv2.threshold(imagen, 127, 255, cv2.THRESH_BINARY)

# Aplicar la máscara a la imagen original
imagen_original = cv2.imread('imagen.jpg')
resultado = cv2.bitwise_and(imagen_original, imagen_original, mask=mascara)

# Mostrar la imagen enmascarada
cv2.imshow('Segmentación por Umbralización', resultado)
cv2.waitKey(0)
cv2.destroyAllWindows()

La función cv2.threshold() genera una máscara binaria aplicando un umbral a la imagen. Los píxeles con valores superiores al umbral se establecen en blanco, y el resto en negro.

Para operaciones más avanzadas, como eliminar el fondo de una imagen, se puede utilizar la función cv2.grabCut(), que requiere una máscara inicial y realiza una segmentación basada en gráficos:

import cv2
import numpy as np

# Cargar la imagen
imagen = cv2.imread('persona.jpg')

# Crear una máscara inicial
mascara = np.zeros(imagen.shape[:2], np.uint8)

# Crear modelos temporales necesarios para grabCut
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)

# Definir el rectángulo que contiene al objeto
rectangulo = (50, 50, imagen.shape[1]-100, imagen.shape[0]-100)

# Aplicar grabCut
cv2.grabCut(imagen, mascara, rectangulo, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)

# Modificar la máscara
mascara_final = np.where((mascara == 2) | (mascara == 0), 0, 1).astype('uint8')

# Aplicar la máscara a la imagen original
imagen_sin_fondo = imagen * mascara_final[:, :, np.newaxis]

# Mostrar la imagen resultante
cv2.imshow('Imagen sin Fondo', imagen_sin_fondo)
cv2.waitKey(0)
cv2.destroyAllWindows()

La función cv2.grabCut() utiliza un algoritmo iterativo para separar el primer plano del fondo, proporcionando una máscara más precisa para aplicaciones como la extracción de objetos.

Además, las máscaras pueden combinarse con operaciones de morfología matemática para refinar los resultados. Por ejemplo, para eliminar pequeñas imperfecciones en una máscara:

import cv2
import numpy as np

# Suponiendo que ya tenemos una máscara binaria 'mascara'

# Definir un elemento estructurante
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))

# Aplicar operaciones morfológicas
mascara_refinada = cv2.morphologyEx(mascara, cv2.MORPH_OPEN, kernel)
mascara_refinada = cv2.morphologyEx(mascara_refinada, cv2.MORPH_CLOSE, kernel)

# Mostrar la máscara refinada
cv2.imshow('Máscara Refinada', mascara_refinada)
cv2.waitKey(0)
cv2.destroyAllWindows()

Las operaciones MORPH_OPEN y MORPH_CLOSE ayudan a eliminar ruido y cerrar agujeros en la máscara, mejorando la precisión al aplicar la máscara a la imagen original.

En aplicaciones donde se requiere destacar ciertos detalles, como fusionar dos imágenes, las máscaras son indispensables. Por ejemplo, para crear un efecto de superposición:

import cv2
import numpy as np

# Cargar las dos imágenes
fondo = cv2.imread('fondo.jpg')
superior = cv2.imread('logo.png', cv2.IMREAD_UNCHANGED)

# Crear una máscara a partir de la imagen superior (asumiendo que tiene transparencia)
b, g, r, a = cv2.split(superior)
mascara = cv2.merge((a, a, a))

# Redimensionar si es necesario
superior = cv2.merge((b, g, r))
superior = cv2.resize(superior, (200, 200))
mascara = cv2.resize(mascara, (200, 200))

# Definir la región donde se colocará la imagen superior
x, y = 50, 50
roi = fondo[y:y+200, x:x+200]

# Aplicar la máscara a la región de interés
fondo_roi = cv2.bitwise_and(roi, cv2.bitwise_not(mascara))
superior_roi = cv2.bitwise_and(superior, mascara)
resultado = cv2.add(fondo_roi, superior_roi)

# Colocar el resultado en la imagen de fondo
fondo[y:y+200, x:x+200] = resultado

# Mostrar la imagen final
cv2.imshow('Superposición con Máscara', fondo)
cv2.waitKey(0)
cv2.destroyAllWindows()

En este caso, se utiliza la máscara para combinar la imagen del logo con el fondo, permitiendo una integración suave y precisa.

La aplicación de máscaras en OpenCV es una herramienta poderosa que permite realizar operaciones complejas de procesamiento de imágenes de manera eficiente. Al dominar el uso de máscaras, es posible abordar tareas avanzadas como la detección de objetos, análisis de regiones específicas y edición creativa de imágenes.

Aprende OpenCV GRATIS online

Todas las lecciones de OpenCV

Accede a todas las lecciones de OpenCV y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a OpenCV y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender los fundamentos del recorte y redimensionamiento de imágenes.
  • Utilizar la librería OpenCV para manipular imágenes de manera efectiva.
  • Aplicar técnicas de indexación y slicing de NumPy para seleccionar regiones de interés.
  • Redimensionar imágenes manteniendo la proporción adecuada para evitar distorsiones.
  • Experimentar con diferentes métodos de interpolación para ajustar la calidad de imagen.