Descarga la aplicación para disfrutar aún más
Vista previa del material en texto
1 INSTITUCIÓN: TECNOLÓGICO DE LA LAGUNA CARRERA: MECATRÓNICA MATERIA: SENSORES INTELIGENTES DOCENTE: HERRERA CARRILLO NAZLE EDITH ALUMNO: RODRÍGUEZ GUERRA EDUARDO ANTONIO MATRICULA: 19131252 PRÁCTICA 7: DETECCIÓN DE COLORES EN VIDEO FECHA DE ENTREGA: 31 DE MARZO DE 2023 2 La finalidad de la presente practica es que una vez que adquirimos las competencias necesarias, para la extracción de información a imágenes y videos, por medio de diferentes elementos, trabajemos con dicha información a fin de re accionar un servomotor dependiendo de la posición de un objeto por medio de Arduino. Es importante destacar que el reconocimiento y el procesamiento del video lo realizamos por medio Python y open cv, el Arduino solo lo usamos para expresar la salida ante determinados datos arrojados por los programas antes mencionados. Básicamente abriremos el video y en tiempo real encontraremos la posición de objetos de un cierto color, colocando en la pantalla la posición del objeto detectado, aparte usaremos de una librería “serial” para comunicarnos con el Arduino y accionar diferentes ángulos de un servo, son solo recibir palabras diferentes al detectar el color. Programa en Python: import cv2 import NumPy as np import serial COM = 'COM5' BAUD = 9600 ser = serial.Serial(COM, BAUD) cap = cv2.VideoCapture(0) azulBajo = np.array([90, 100, 20], np.uint8) azulAlto = np.array([120, 255, 255], np.uint8) while True: ret, frame = cap.read() if ret: frame = cv2.flip(frame, 1) frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mascara = cv2.inRange(frameHSV, azulBajo, azulAlto) contornos, _ = cv2.findContours(mascara, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(frame, contornos, -1, (255, 0, 0), 4) for c in contornos: area = cv2.contourArea(c) if area > 6000: M = cv2.moments(c) if M["m00"] == 0: 3 M["m00"] = 1 x = int(M["m10"] / M["m00"]) y = int(M['m01'] / M['m00']) cv2.circle(frame, (x, y), 7, (0, 0, 255), -1) font = cv2.FONT_HERSHEY_SIMPLEX cv2.putText(frame, '{},{}'.format(x, y), (x + 10, y), font, 1.2, (0, 0, 255), 2, cv2.LINE_AA) nuevoContorno = cv2.convexHull(c) cv2.drawContours(frame, [nuevoContorno], 0, (255, 0, 0), 3) if x < 200: print("Mover a la izquierda 100%") ser.write(b"izq1\n") elif 420 > x >= 200: print("Mover a la izquierda 60%") ser.write(b"izq2\n") elif 520 > x >= 420: print("Mover a la izquierda 30%") ser.write(b"izq3\n") # Mover al centro elif 520 <= x < 650: print("Mover al centro") ser.write(b"ctr\n") elif 650 <= x < 860: print("moviendo a la derecha 30%") ser.write(b"der3\n") elif 860 <= x < 1080: print("moviendo a la derecha 60%") ser.write(b"der2\n") elif x >= 1080: print("Moviendo a la derecha 100%") ser.write(b"der1\n") # cv2.imshow('mascaraAzul', mascara) cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('s'): ser.close() break cap.release() cv2.destroyAllWindows() 4 Asignamos las librerías para el procesamiento de imágenes (open cv, numpy), además de una librería para escribir en el puerto serial import cv2 import numpy as np import serial Después asignamos el puerto COM al que este conectad el Arduino, seguido de la velocidad de transmisión de datos para Arduino (9600), en caso de que use otra tarjeta variar este dato en función de la datasheet, ser es una variable. COM = 'COM5' BAUD = 9600 ser = serial.Serial(COM, BAUD) Este comando mágico, nos da acceso a la cámara de la computadora para tomar prestada la imagen que esta detecte dentro del paréntesis ira un numero de acuerdo a la cantidad de cámaras que tenga el dispositivo. cap = cv2.VideoCapture(0) Ahora si viene lo emocionante, en las siguientes variables designaremos un rango de colores que detectará el programa para la extracción de datos, ósea si someto la cámara a colores dentro de otro rango simplemente no los reconocerá. Cabe destacar que el rango se selecciona de acuerdo a esta gama de colores en HSV. 5 azulBajo = np.array([90, 100, 20], np.uint8) azulAlto = np.array([120, 255, 255], np.uint8) “Dentro del ciclo while tendremos el código que se ejecutará continuamente. Aquí vamos a describir la primer línea: ret, frame = cap.read() con ella obtenemos dos variables ret y frame. En ret se almacena un true si se realizó una captura de manera correcta y frame es la imagen capturada por la webcam.” AbrahamG-2021 En caso de que tengamos un objeto frente la cámara dentro del rango de colores, ingresaremos al if . Dentro del if reflejamos la imagen para ser capaces de analizarla mejor, y sigue esta parte del código frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) Convertimos el espacio de color BGR a HSV, BGR es el formato en que se toma la imagen atreves de la cámara del equipo. Después crea una mascara con los limites superior e inferior de los colores que deseemos detectar, así le aplicamos una operación para detectar contornos, y por último dibujamos los contornos sobre la imagen original. Para dibujarla utilizamos la función cv2.drawContours además le agregamos el color que deseamos que utilice para destacar los contornos, en mi caso elegí un color azul muy marcado (255,0,0) recordando que OpenCV utiliza un formato (B,G,R). while True: ret, frame = cap.read() if ret: frame = cv2.flip(frame, 1) frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mascara = cv2.inRange(frameHSV, azulBajo, azulAlto) contornos, _ = cv2.findContours(mascara, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(frame, contornos, -1, (255, 0, 0), 4) De ahí sigue la verificación del centroide, ósea por medio del if preguntamos si el área del contorno es mayor a 600 entonces buscará centroide. for c in contornos: area = cv2.contourArea(c) if area > 6000: Ahora calculamos los momentos M = cv2.moments(c) y revisamos que si el momento «m00» es igual a cero, lo igualamos a 1, ya que realizaremos una división y dividir entre 0 es infinito y luego aparecerá un error. M = cv2.moments(c) if M["m00"] == 0: M["m00"] = 1 x = int(M["m10"] / M["m00"]) y = int(M['m01'] / M['m00']) 6 Una vez que hicimos las divisiones dibujamos un círculo, que contendrá las coordenadas del centroide en color rojo, lo que le sigue del código solo da más formato a dicho texto: cv2.circle(frame, (x, y), 7, (0, 0, 255), -1) font = cv2.FONT_HERSHEY_SIMPLEX cv2.putText(frame, '{},{}'.format(x, y), (x + 10, y), font, 1.2, (0, 0, 255), 2, cv2.LINE_AA) Crearemos un nuevo contorno en caso de que la figura sea irregular, colocando puntos y cerrando la imagen, para trabajarla como lo hicimos anteriormente. nuevoContorno = cv2.convexHull(c) cv2.drawContours(frame, [nuevoContorno], 0, (255, 0, 0), 3) Ojo con esta imagen ya que el autor original Dividirá la imagen para establecer fronteras así asignará en que momentos el servo cambiará de posición. if x < 200: print("Mover a la izquierda 100%") ser.write(b"izq1\n")elif 420 > x >= 200: print("Mover a la izquierda 60%") ser.write(b"izq2\n") elif 520 > x >= 420: print("Mover a la izquierda 30%") ser.write(b"izq3\n") # Mover al centro elif 520 <= x < 650: print("Mover al centro") ser.write(b"ctr\n") elif 650 <= x < 860: print("moviendo a la derecha 30%") ser.write(b"der3\n") elif 860 <= x < 1080: print("moviendo a la derecha 60%") ser.write(b"der2\n") elif x >= 1080: print("Moviendo a la derecha 100%") ser.write(b"der1\n") 7 Establecemos lo que hará el programa cuando las coordenadas del centroide superan algunas de las fronteras La tabla anexada nos muestra lo que realizara el Arduino ante determinados comandos que reciba en el serial port. En el ultimo fragmento del programa en Python mostramos la imagen ya con todos los contornos y centroides que tenga, además de agregar el gran detalle de que en caso de que en medio de la ejecución aprietes la tecla S el programa se detendrá por sí solo. cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('s'): ser.close() break cap.release() cv2.destroyAllWindows() 8 Codigo para arduino: #include <Servo.h> Servo servo1; String entradaSerial = ""; // String para almacenar entrada bool entradaCompleta = false; // Indicar si el String está completo int pin = 9; // pin de conexión PWM al servo int pulsoMinimo = 580; // Duración en microsegundos del pulso para girar 0º int pulsoMaximo = 2500; // Duración en microsegundos del pulso para girar 180º int angulo = 0; // Variable para guardar el angulo que deseamos de giro void setup() { servo1.attach(pin, pulsoMinimo, pulsoMaximo); Serial.begin(9600); } void loop() { if (entradaCompleta) { if (entradaSerial == "izq1\n") { Serial.print("0 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(0); } else if (entradaSerial == "izq2\n") { Serial.print("30 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(30); } else if (entradaSerial == "izq3\n") { Serial.print("60 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(60); } else if (entradaSerial == "ctr\n") { Serial.print("Centro 90 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(90); } else if (entradaSerial == "der3\n") { Serial.print("120 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(120); } else if (entradaSerial == "der2\n") { Serial.print("150 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(150); } else if (entradaSerial == "der1\n") { Serial.print("180 grados\n"); // Mandamos escribir el angulo deseado del giro. servo1.write(180); } else { // Cualquier otro dato recibido Serial.println("El dato recibido es inválido!!"); } entradaSerial = ""; entradaCompleta = false; 9 } } // Función que se activa al recibir algo por // el puerto serie, Interrupción del Puerto Serie. void serialEvent() { while (Serial.available()) { // Obtener bytes de entrada: char inChar = (char)Serial.read(); // Agregar al String de entrada: entradaSerial += inChar; // Para saber si el string está completo, se detendrá al recibir // el caracter de retorno de línea ENTER \n if (inChar == '\n') { entradaCompleta = true; } } } Empleamos de la libreria para girar el servomotor conforme reciba indicaciones del serial port. Imágenes de ejecución de práctica: 10 11 12 Conclusiones: En lo particular esta ha sdio mi práctica favorita ya que aparte de ser la más dificil fue la primera donde empleamos del analisis de imagenes para controlar un proceso, esto se está poniendo interesante, es importante destacar que tal y como indica el autor, la luz altera mucho la caidad del video, pues al correr el programa en mi casa como estoy debajo de la lampara, tengo una iluminación considerable, pues dichailuminación me provocaba detecciones de objetos que no existian, para correguir eso solo tuve que cambiar el coigo justo en el if de 6000, o una idea que se me ocurrio en un principio ir a un lugar con iluminación más variada. Y tambien esta prática me permitio comprender mejor la importancia del orden dentro de la programación en python ya que dure media hora buscando un error de sintaxis en el programa, y solo era que los ultimos comandos estaban un paso delante de su posición, me quedaré con el codigo para cerrar programas al apretar una tecla, valla que me será util, por ultimo me gustaria destacar que tengo que abrir python como administrador ya que de lo contrario no me dejaba leer el puerto donde estuviese el arduino de ahí en fuera todo chid, hasta el tiempo de respuesta, que era fluido para considerar la complejidad de lo que estaba llevando acabo.
Compartir