Este repositorio contiene la Práctica 2 sobre el uso de OpenCV y NumPy para el procesamiento de imágenes, donde se exploran tareas como la conversión a escala de grises, la detección de bordes y el análisis de píxeles en imágenes.
- Práctica 2: Funciones básicas de OpenCV
- Índice
Este proyecto fue desarrollado por:
- Canny: Se convierte la imagen a escala de grises y se aplica el detector de bordes Canny con umbrales 250 y 100.
- Análisis de Píxeles Blancos: Se cuenta el número de píxeles blancos por filas y columnas usando
cv2.reduceynp.sum. Los resultados se normalizan para obtener porcentajes.
- Cálculo de
maxfil: Se determina el número máximo de píxeles blancos por fila y columna (maxfilymaxcol). - Umbral 95%: Se identifican filas y columnas con al menos el 95% de los píxeles blancos de
maxfilymaxcol. - Resultado: Se muestran las posiciones de las filas y columnas clave y se resaltan visualmente en la imagen.
* Maximum white pixels in a row: 218
* Number of rows with white pixels >= 0.95 * maxfil: 2
* Positions of these rows: [ 12 100]
* Maximum white pixels in a column: 184
* Number of columns with white pixels >= 0.95 * maxcol: 3
* Positions of these columns: [104 115 119]
En este proyecto, aplicamos el operador Sobel a una imagen en escala de grises para detectar sus bordes y luego binarizamos el resultado mediante umbralizado. Los pasos clave fueron:
- Convertimos la imagen a escala de grises y aplicamos un filtro Gaussiano para suavizarla y reducir el ruido.
- Calculamos el gradiente Sobel en las direcciones x e y y combinamos ambos resultados.
- Convertimos el resultado a 8 bits y aplicamos un umbral para obtener una imagen binaria de los bordes.
gris = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ggris = cv2.GaussianBlur(gris, (3, 3), 0)
sobelx = cv2.Sobel(ggris, cv2.CV_64F, 1, 0)
sobely = cv2.Sobel(ggris, cv2.CV_64F, 0, 1)
sobel = cv2.add(sobelx, sobely)
sobel8 = cv2.convertScaleAbs(sobel)
_, sobel8Umbralizado = cv2.threshold(sobel8, 130, 255, cv2.THRESH_BINARY)- Contamos el número de píxeles blancos por filas y columnas utilizando
cv2.reduce. - Normalizamos estos conteos para obtener porcentajes respecto al total.
col_counts = cv2.reduce(sobel8Umbralizado, 0, cv2.REDUCE_SUM)
fil_counts = cv2.reduce(sobel8Umbralizado, 1, cv2.REDUCE_SUM).flatten()
cols = col_counts[0] / (255 * sobel8Umbralizado.shape[0])
filas = fil_counts / (255 * sobel8Umbralizado.shape[1])- Calculamos el 95% del valor máximo de los porcentajes obtenidos.
- Identificamos las filas y columnas que superan este umbral.
limiteFila = np.max(filas) * 0.95
limiteColumna = np.max(cols) * 0.95
filas_superiores = np.where(filas >= limiteFila)[0]
columnas_superiores = np.where(cols >= limiteColumna)[0]- Remarcamos las filas y columnas significativas en la imagen binarizada.
- Observamos que, en comparación con el método de Canny, Sobel es más sensible al ruido y produce bordes menos definidos, mientras que Canny ofrece una detección más precisa.
sobel8Umbralizado_bgr = cv2.cvtColor(sobel8Umbralizado, cv2.COLOR_GRAY2BGR)
for fila in filas_superiores:
cv2.line(sobel8Umbralizado_bgr, (0, fila), (sobel8Umbralizado_bgr.shape[1]-1, fila), (0, 0, 255), 1)
for columna in columnas_superiores:
cv2.line(sobel8Umbralizado_bgr, (columna, 0), (columna, sobel8Umbralizado_bgr.shape[0]-1), (255, 0, 0), 1)
plt.imshow(cv2.cvtColor(sobel8Umbralizado_bgr, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()En este proyecto, desarrollé un demostrador que captura imágenes de la cámara web y aplica diferentes funciones de OpenCV para mostrar lo aprendido en las prácticas anteriores. El programa muestra la imagen original junto con tres efectos distintos en una sola ventana.
- Iniciamos la captura de video desde la cámara web y redimensionamos el frame para facilitar el procesamiento.
- Convertimos el frame a escala de grises y aplicamos un desenfoque Gaussiano para reducir el ruido.
- Utilizamos el detector de bordes de Canny para resaltar los bordes en la imagen.
- Convertimos la imagen resultante a BGR para poder combinarla con otros efectos.
# Efecto esquina superior derecha: Canny
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blur, 50, 150)
edges_colored = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)- Encontramos los píxeles más brillantes y más oscuros en la imagen en escala de grises.
- Marcamos estos píxeles en el frame original con círculos y etiquetas.
# Efecto esquina inferior derecha: Detector de contraste
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray)
contrastDetector = frame.copy()
cv2.circle(contrastDetector, min_loc, 10, (255, 0, 0), 2) # Círculo azul
cv2.putText(contrastDetector, 'Oscuro', (min_loc[0] + 10, min_loc[1]),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
cv2.circle(contrastDetector, max_loc, 10, (0, 0, 255), 2) # Círculo rojo
cv2.putText(contrastDetector, 'Brillante', (max_loc[0] + 10, max_loc[1]),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)- Dividimos el frame en cuatro cuadrantes.
- Aplicamos diferentes mapas de color a cada cuadrante para crear un efecto de arte pop.
# Efecto Pop Art
height, width, _ = frame.shape
pop_art = np.zeros_like(frame)
pop_art[0:height//2, 0:width//2] = cv2.applyColorMap(frame[0:height//2, 0:width//2], cv2.COLORMAP_HSV)
pop_art[0:height//2, width//2:width] = cv2.applyColorMap(frame[0:height//2, width//2:width], cv2.COLORMAP_OCEAN)
pop_art[height//2:height, 0:width//2] = cv2.applyColorMap(frame[height//2:height, 0:width//2], cv2.COLORMAP_PINK)
pop_art[height//2:height, width//2:width] = cv2.applyColorMap(frame[height//2:height, width//2:width], cv2.COLORMAP_SPRING)- Combinamos la imagen original y los efectos en una sola ventana para mostrar los resultados en tiempo real.
- Organizamos las imágenes de la siguiente manera:
- Arriba: Imagen original y detección de bordes.
- Abajo: Efecto Pop Art y detección de contraste.
# Combinamos las imágenes
combined_top = np.hstack((frame, edges_colored))
combined_bottom = np.hstack((pop_art, contrastDetector))
combined = np.vstack((combined_top, combined_bottom))En esta tarea, hemos desarrollado un programa que imita la instalación artística "My Little Piece of Privacy" de Niklas Roy. Utilizamos técnicas de procesamiento de video en tiempo real con OpenCV para crear una cortina digital animada que sigue el movimiento de una persona.
El objetivo es simular una cortina que se mueve horizontalmente para cubrir la posición de una persona detectada en el video capturado por la cámara web. La cortina sigue el movimiento de la persona, ocultándola parcialmente y recreando el efecto de la instalación original.
-
Captura de Video en Tiempo Real: Utilizamos la cámara web para obtener el flujo de video.
cap = cv2.VideoCapture(0)
-
Sustracción de Fondo (Background Subtraction): Aplicamos un algoritmo de sustracción de fondo para detectar movimiento en la escena y obtener una máscara de primer plano que resalta las áreas con cambio. Ajustamos los parámetros para mejorar la detección.
background_subtractor = cv2.createBackgroundSubtractorMOG2(history=600, varThreshold=200) fgmask = background_subtractor.apply(blurred_frame)
-
Procesamiento de Imágenes: Convertimos el fotograma a escala de grises y aplicamos un desenfoque Gaussiano para reducir el ruido y mejorar la detección de movimiento.
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) blurred_frame = cv2.GaussianBlur(frame_gray, (5, 5), 0)
-
Umbralización y Operaciones Morfológicas: Realizamos una umbralización para binarizar la máscara de movimiento y aplicamos operaciones morfológicas para eliminar el ruido y pequeños objetos no deseados.
_, fgmask_thresh = cv2.threshold(fgmask, 200, 255, cv2.THRESH_BINARY) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) fgmask_clean = cv2.morphologyEx(fgmask_thresh, cv2.MORPH_OPEN, kernel)
-
Detección de Contornos: Detectamos contornos en la máscara limpia para encontrar las áreas en movimiento. Seleccionamos el contorno más grande, asumiendo que es la persona.
contours, _ = cv2.findContours(fgmask_clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) largest_contour = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(largest_contour)
-
Posicionamiento y Animación de la Cortina: Calculamos la posición objetivo de la cortina basada en la posición horizontal de la persona detectada. Utilizamos una interpolación suave para animar el movimiento de la cortina.
target_position = x curtain_position += (target_position - curtain_position) * animation_speed
-
Superposición de Imágenes con Transparencia: Sobreponemos una imagen de una cortina sobre el fotograma original, utilizando el canal alpha para manejar la transparencia y lograr una integración natural.
frame[y_start:y_end, x_start:x_end] = blend_transparent( frame[y_start:y_end, x_start:x_end], curtain_resized )
La función
blend_transparentgestiona la mezcla de la cortina con el fondo teniendo en cuenta la transparencia. -
Efecto Espejo: Reflejamos horizontalmente el fotograma para que la interacción sea más intuitiva, similar a un espejo.
mirrored_frame = cv2.flip(frame, 1)
El resultado es una ventana de video donde la cortina digital se mueve para cubrir a la persona detectada. La cortina sigue suavemente el movimiento, proporcionando una experiencia interactiva.
Este proyecto demuestra cómo combinar técnicas de visión por computadora para recrear una obra de arte interactiva. La detección de movimiento, procesamiento de imágenes y animación se integran para lograr un efecto que responde en tiempo real a la presencia del usuario.
- OpenCV Documentation: https://docs.opencv.org/
- NumPy Documentation: https://numpy.org/doc/
- Matplotlib Documentation: https://matplotlib.org/stable/contents.html
- Niklas Roy - "My Little Piece of Privacy": niklasroy.com/project/100/my-little-piece-of-privacy
- OpenCV Documentation: docs.opencv.org
- NumPy Documentation: numpy.org/doc
- Tutoriales de Python y OpenCV: pyimagesearch.com






