Saltar al contenido principal

Entrada y salida de archivos

info

Todo el código de ejemplo se encuentra en Google Colab para su ejecución directa. Si no puedes ejecutar OpenCV localmente, puedes usar Google Colab junto con matplotlib para visualizar imágenes.

Objetivo

  • Comprender cómo leer y mostrar imágenes desde archivos utilizando OpenCV.
  • Aprender a guardar imágenes procesadas en distintos formatos para reutilización o análisis posterior.
  • Manejar la lectura, procesamiento y visualización de videos.
  • Explorar el uso de VideoCapture para capturar datos desde archivos, cámaras locales o transmisiones en vivo.
  • Implementar la escritura de videos procesados mediante diferentes codecs y formatos.
  • Reconocer conceptos clave como resolución, FPS, profundidad de color y canales de color en imágenes y videos.
  • Identificar y gestionar problemas comunes como buffers llenos al trabajar con transmisión en tiempo real.

Conceptos clave

  • Entrada de archivos: Cómo OpenCV obtiene información desde imágenes, videos o cámaras en vivo.
  • Salida de archivos: Cómo OpenCV guarda los resultados procesados para reutilizarlos o compartirlos.
  • Frame (cuadro por video): Cada video está compuesto por una secuencia de imágenes llamadas frames.
  • Resolución: Indica el tamaño de una imagen o frame en píxeles (ancho x alto).
  • Profundidad de color (bit depth): Representa la cantidad de bits usados para cada canal de color en una imagen. Por ejemplo, imágenes RGB suelen usar 8 bits por canal, dando un total de 24 bits por píxel. Para imágenes en escala de grises, se usan 8 bits por píxel.
  • Canales de color: Una imagen puede ser escala de grises (1 canal) o color (3 canalaes: R, G, B).
  • FPS (Frames per second / cuadros por segundo): Velocidad a la que se reproducen los frames de un video.
  • Formato de archivo: El tipo de archivo influye en la compatibilidad y la compresión. Ejemplos: .jpg, .png para imágenes; .avi, .mp4 para videos.
  • Codec: Algoritmo que comprime o descomprime video. Afecta calidad, tamaño del archivo y compatibilidad. Ejemplos: XVID, MJPG, MP4V, H264, H265.
  • Captura en tiempo real: Permite obtener frames directamente de una cámara o cámara IP.
  • Buffers de video: OpenCV mantiene un buffer interno al leer frames de un video o cámara. Es importante procesar los frames a tiempo para evitar retrasos o pérdidas.

Pipeline básico

En visión por computadora, la entrada y salida de archivos es el primer paso de cualquier pipeline:

  1. Leer los datos (imágenes, videos, señal en vivo).
  2. Procesar los datos (filtros, transformaciones).
  3. Guardar o mostrar los resultados (imágenes, videos).

Funciones principales en OpenCV

  • cv2.imread(): Leer una imagen desde un archivo y cargarla en una matriz.
  • cv2.imwrite(): Guardar una matriz como imagen en un archivo.
  • cv2.VideoCapture(): Abrir un archivo de video o una cámara para capturar video.
  • cv2.VideoWriter(): Guardar una secuencia de imágenes como archivo de video.
  • cv2.imshow(): Mostrar una imagen en una ventana.
  • cv2.waitKey(): Esperar por una tecla para cerrar la ventana o avanzar en el video.
  • cv2.destroyAllWindows(): Cerrar todas las ventanas abiertas por OpenCV.

Preparación del entorno

Antes de instalar OpenCV, siempre es recomendable crear un entorno virtual para no afectar tus paquetes globales o otros proyectos.

# Crear un entorno virtual
python -m venv venv

# Activar el entorno virtual en Windows
venv\Scripts\activate
------------------------------------------
# Activar el entorno virtual en macOS / Linux
source venv/bin/activate

Ejemplos de código

info

Antes de instalar OpenCV, asegúrate de activar tu entorno virtual para que las dependencias queden aisladas. Luego, puedes instalar OpenCV con pip, usando la versión recomendada 4.11.0.86:

pip install opencv-python==4.11.0.86

Leer y mostrar una imagen

Aquí se muestra cómo abrir una imagen y visualizarla. Es el primer paso para cualquier procesamiento de imagen. El siguiente ejemplo funciona en un computador local con entorno gráfico (no en Google Colab). En caso de usar Google Colab, puedes mostrar imágenes con matplotlib.

import cv2 as cv

# Leer imagen
img = cv.imread("imagen.jpg")

# Convertir imagen a RGB (OpenCV por defecto usa BGR)
img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# Mostrar imagen en una ventana
cv.imshow("Imagen RGB", img_rgb)
cv.waitKey(0)
cv.destroyAllWindows()

Guardar una imagen procesada

En esta sección se muestra cómo guardar resultados de leer una imagen y aplicar procesamiento básico. Esto es útil para análisis posterior o para generar conjunto de datos.

import cv2 as cv

# Leer imagen
img = cv.imread("imagen.jpg")

# Convertir a gris
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# Guardar la imagen procesada
cv.imwrite("imagen_gris.jpg", img_gray)

# Mostrar la imagen procesada
cv.imshow("Imagen Gris", img_gray)
cv.waitKey(0)
cv.destroyAllWindows()

Leer y procesar un video

Se explica cómo abrir un archivo de video y procesar cada frame.

info

El código para google colab no es posible ya que no soporta ventanas gráficas.

Detalle importante:

if cv.waitKey(30) & 0xFF == ord('q'):
  1. cv.waitKey(30):
  • Espera 30 milisegundos a que el usuario presione una tecla.
  • Devuelve un valor entero que representa la tecla presionada.
  1. & 0xFF:
  • Este operador bit a bit toma los últimos 8 bits del valor devuelto por cv.waitKey().
  • La razón es que cv.waitkey() en algunos sistemas (especialmente Windows) puede devolver un entero de más de 8 bits, donde los bits más altos contiene información irrelevante para la tecla presionada.
  • Al hacer & 0xFF, nos aseguramos de obtener solo el código de la tecla, compatible con ASCII.
  1. ord('q'):
  • Devuelve el código ASCII de la tecla 'q'.

En conjunto, la condición verifica si se presionó la tecla 'q' para salir del bucle y cerrar las ventanas.

import cv2 as cv

# Abrir video
cap = cv.VideoCapture("video.mp4")

while True:
ret, frame = cap.read()
if not ret:
break

# Convertir a gris
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)

# Convertir a RGB
rgb = cv.cvtColor(frame, cv.COLOR_BGR2RGB)

# Convertir a HSV
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)

# Convertir a LAB
lab = cv.cvtColor(frame, cv.COLOR_BGR2LAB)

# Convertir a YCrCb
ycrcb = cv.cvtColor(frame, cv.COLOR_BGR2YCrCb)

# Mostrar video original
cv.imshow("Video original", frame)

# Mostrar video en escala de grises
cv.imshow("Video Gris", gray)

# Mostrar video en escala rgb
cv.imshow("Video RGB", rgb)

# Mostrar video en escala HSV
cv.imshow("Video HSV", hsv)

# Mostrar video en escala LAB
cv.imshow("Video LAB", lab)

# Mostrar video en escala YCrCb
cv.imshow("Video YCrCb", ycrcb)

if cv.waitKey(30) & 0xFF == ord('q'):
break

cap.release()
cv.destroyAllWindows()

Guardar un video procesado

Se muestra cómo usar cv.VideoWriter para guardar la secuencia de frames procesados como un nuevo video. Esto es útil para registrar resultados o compartirlos.

import cv2 as cv

cap = cv.VideoCapture("video.mp4")

# Obtener ancho, alto y FPS del video original
width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv.CAP_PROP_FPS)

# Crear VideoWriter para guardar el video procesado
out = cv.VideoWriter("video_gris.avi", cv.VideoWriter_fourcc(*'XVID'), fps, (width, height), False)

while True:
ret, frame = cap.read()
if not ret:
break

gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
out.write(gray)

cap.release()
out.release()
print("Video guardado como video_gris.avi")

Leer y procesar transmisión en vivo desde una cámara

Esta sección muestra cómo capturar video en tiempo real desde una cámara local o una cámara IP/streaming, y cómo aplicar transformaciones como escala de grises o conversión a RGB.

info

Para cámaras en red (IP), puedes usar la URL de la transmisión. Por ejemplo, en Insecam se pueden obtener URLs públicas de cámaras para pruebas.

import cv2 as cv

# Abrir video
cap = cv.VideoCapture("url_de_la_camara")

# Tamaño deseado
width, height = 920, 480

while True:
ret, frame = cap.read()
if not ret:
break

# Redimensionar frame original
frame_resized = cv.resize(frame, (width, height))

# Convertir a gris
gray = cv.cvtColor(frame_resized, cv.COLOR_BGR2GRAY)

# Convertir a RGB
rgb = cv.cvtColor(frame_resized, cv.COLOR_BGR2RGB)

# Convertir a HSV
hsv = cv.cvtColor(frame_resized, cv.COLOR_BGR2HSV)

# Mostrar video original
cv.imshow("Video original", frame_resized)

# Mostrar video en escala de grises
cv.imshow("Video Gris", gray)

# Mostrar video en escala rgb
cv.imshow("Video RGB", rgb)

# Mostrar video en escala HSV
cv.imshow("Video HSV", hsv)

if cv.waitKey(30) & 0xFF == ord('q'):
break

cap.release()
cv.destroyAllWindows()

Grabar transmisión en vivo desde una cámara

OpenCV también permite guardar la transmisión en vivo mientras la procesamos. Esto es útil para análisis posterior o generación de conjunto de datos.

import cv2 as cv

# Abrir video
cap = cv.VideoCapture("url_de_la_camara")

# Tamaño deseado
width, height = 920, 480

fourcc = cv.VideoWriter_fourcc(*'XVID')
out_original = cv.VideoWriter('video_original.avi', fourcc, 20.0, (width, height))
out_gray = cv.VideoWriter('video_gris.avi', fourcc, 20.0, (width, height), isColor=False)

while True:
ret, frame = cap.read()
if not ret:
break

# Redimensionar frame
frame_resized = cv.resize(frame, (width, height))

# Convertir a gris
gray = cv.cvtColor(frame_resized, cv.COLOR_BGR2GRAY)

# Mostrar videos
cv.imshow("Video original", frame_resized)
cv.imshow("Video Gris", gray)

# Guardar videos
out_original.write(frame_resized)
out_gray.write(gray)

if cv.waitKey(30) & 0xFF == ord('q'):
break

cap.release()
out_original.release()
out_gray.release()
cv.destroyAllWindows()

Simulación de un buffer lleno

Cuando se procesa video en tiempo real, es posible que el procesamiento no sea lo suficientemente rápido para mantener el ritmo de los frames entrantes. Esto puede causar que el buffer interno de OpenCV se llene, resultando en retrasos o pérdida de frames. El siguiente ejemplo simula un procesamiento lento para ilustrar este problema.

import cv2 as cv
import time

# Abrir la cámara
cap = cv.VideoCapture(0)

# Establecer el tamaño del buffer (número máximo de frames que puede almacenar)
buffer_size = 5
buffer = []

while True:
ret, frame = cap.read()
if not ret:
break

# Simular un procesamiento lento
time.sleep(0.1) # Ir aumentando el valor para que se note, ej: 0.5, 0.8...

# Agregar el frame al buffer
buffer.append(frame)

# Si el buffer está lleno, eliminar el frame más antiguo
if len(buffer) > buffer_size:
buffer.pop(0) # 0 -> frame más antiguo

# Mostrar el frame más reciente
cv.imshow('Frame', buffer[-1]) # -1 -> último frame agregado

# Salir si se presiona la tecla 'q'
if cv.waitKey(1) & 0xFF == ord('q'):
break

cap.release()
cv.destroyAllWindows()

Algunos Codecs de OpenCV

CodecFormato de archivo comúnDescripciónEjemplo de uso
XVID.aviCodec MPEG-4 popular, buena compatibilidad con Windowscv2.VideoWriter_fourcc(*'XVID')
MJPG.avi, .movMotion JPEG, cada frame es una imagen JPEG, buena velocidad.cv2.VideoWriter_fourcc(*'MJPG')
MP4V.mp4MPEG-4, más comprimido que XVID, soportado por muchos reproductores.cv2.VideoWriter_fourcc(*'MP4V')
H264.mp4Alta compresión, calidad alta, requiere soporte del sistema.cv2.VideoWriter_fourcc(*'H264')
H265.mp4También llamado HEVC, compresión más eficiente que H264, requiere librerías externas.cv2.VideoWriter_fourcc(*'HEVC')
DIVX.aviOtro codec MPEG-4 compatible con Windowscv2.VideoWriter_fourcc(*'DIVX')