En esta práctica (https://github.com/JdeRobot/Academy/tree/master/src/color_filter), el objetivo es segmentar algún objeto en los vídeos proporcionados, los cuales se pueden descargar en http://jderobot.org/JdeRobot-Academy#Color_filter. En la práctica, los objetos a segmentar son o una pelota o varias pelotas. Para realizar esta práctica emplearemos la plataforma JdeRobot, el lenguaje Python y la librería OpenCV, y haremos un filtro de color.
Para poder realizar la práctica emplearemos el componente cameraserver de JdeRobot, que nos proporciona las imágenes de un vídeo o las obtenidas por una cámara. Para ejecutar el componente cameraserver ponemos en un terminal lo siguiente:
Para ejecutar nuestro código tenemos que poner en el terminal:
Para poder realizar la práctica emplearemos el componente cameraserver de JdeRobot, que nos proporciona las imágenes de un vídeo o las obtenidas por una cámara. Para ejecutar el componente cameraserver ponemos en un terminal lo siguiente:
cameraserver cameraserver_conf.cfg
python2 ./color_filter.py color_filter_conf.yml
1. El primer paso es obtener la imagen proporcionada por cameraserver (en este caso será la del vídeo) mediante input_image = self.camera.getImage(). Este paso ya nos lo proporcionan en la plantilla de la práctica. La imagen de entrada la podemos mostrar en la GUI que se nos proporciona en la práctica. Para ello empleamos la siguiente sentencia:
self.camera.setColorImage(input_image)
2. Las imágenes proporcionadas por el cameraserver tienen mucho ruido. Por este motivo es recomendable emplear un suavizado para eliminar este ruido. En la solución se ha empleado un filtro Gaussiano.
gaussian_image = cv2.GaussianBlur(input_image, (5, 5), 0.2)
3. La imagen está en RGB, por lo que hacemos una conversión de espacio de color a HSV para poder reconocer las pelotas en un ambiente de luminosidad variado.
4. El siguiente paso es aplicar un filtro de color. Para saber qué valores aplicar para filtrar las pelotas se puede emplear la herramienta color_tuner proporcionada en la práctica. Aplicamos el filtro de color con estos valores mínimo y máximo para el tono, la saturación y la intensidad. Con dicha función obtendremos una imagen umbralizada, donde los objetos deseados aparecerá en blanco y el resto en negro.
hsv_image = cv2.cvtColor(gaussian_image, cv2.COLOR_RGB2HSV)
4. El siguiente paso es aplicar un filtro de color. Para saber qué valores aplicar para filtrar las pelotas se puede emplear la herramienta color_tuner proporcionada en la práctica. Aplicamos el filtro de color con estos valores mínimo y máximo para el tono, la saturación y la intensidad. Con dicha función obtendremos una imagen umbralizada, donde los objetos deseados aparecerá en blanco y el resto en negro.
image_HSV_filtered = cv2.inRange(hsv_image, value_min_HSV, value_max_HSV)
5. Se emplea la operación morfológica cierre para que se rellenen los huecos que quedan sin filtrar en las pelotas tratando de mejorar el umbralizado obtenido.
kernel = np.ones((19, 19), np.uint8)
image_HSV_filt_close=cv2.morphologyEx(image_HSV_filtered, cv2.MORPH_CLOSE, kernel)
self.camera.setThresoldImage(image_HSV_filt_close)
6. Copiamos la imagen original y la filtrada para no modificarlas:
image_HSV_filtered_Copy = np.copy(image_HSV_filt_close)
input_image_Copy = np.copy(input_image)
7. Se aplica findcontour de OpenCV para detectar el contorno de la zona filtrada.
_, contours, hierarchy = cv2.findContours(image_HSV_filtered_Copy, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
8. Se emplea approxPolyDP de OpenCV para aproximar los contornos.
cnt = cv2.approxPolyDP(contours[0], 3, True)
9. Se emplea boundingRect para detectar el rectángulo que se ajusta al contorno detectado.
x, y, w, h= cv2.boundingRect(cnt)
10. Por último, se pinta un rectángulo alrededor de las zonas detectadas como pelota, así como una circunferencia.
Al tener dos pelotas de diferentes colores, se ha realizado un filtrado para el rojo y otro para el azul. El resultado se puede ver en el siguiente vídeo.
drone1.mp4
Para este vídeo se han empleado los siguientes valores en el filtrado:
cv2.rectangle(input_image_Copy, (x, y), (x+w, y+h), (0, 255, 0) ,2)
center = [x + w/2, y + h/2]
cv2.circle(input_image_Copy, (center[0], center[1], h/2, (255, 0, 0), 2)
pelota_roja.avi
Para realizar la detección se han usado los siguientes valores en el filtrado:
value_min_HSV = np.array([110, 195, 144])
value_max_HSV = np.array([180, 255, 255])
Debido a que solamente hay una pelota, me he quedado con el contorno de mayor área. Se puede ver el resultado en el siguiente vídeo.
pelotas_roja_azul.avi
En el caso de este vídeo, se han empleado los siguientes valores para el filtrado:
value_min_HSV = np.array([110, 195, 144])
value_max_HSV = np.array([180, 255, 255])
value_min_HSV_blue = np.array([72, 58, 34])
value_max_HSV_blue = np.array([150, 255, 255])
value_min_HSV_blue = np.array([72, 58, 34])
value_max_HSV_blue = np.array([150, 255, 255])
Al tener dos pelotas de diferentes colores, se ha realizado un filtrado para el rojo y otro para el azul. El resultado se puede ver en el siguiente vídeo.
drone1.mp4
Para este vídeo se han empleado los siguientes valores en el filtrado:
value_min_HSV = np.array([144.55, 135.15, 76.53])
value_max_HSV = np.array([187.5, 245, 255])
value_min_HSV_blue = np.array([110.2, 145.86, 110.515])
value_max_HSV_blue = np.array([150.54, 225, 150.96])
El resultado es el siguiente:
drone2.mp4
En este vídeo se han empleado los siguientes valores en el filtrado:
value_min_HSV_blue = np.array([8, 110, 110])
value_max_HSV_blue = np.array([127, 246, 255])
El resultado es el siguiente:
value_min_HSV_blue = np.array([110.2, 145.86, 110.515])
value_max_HSV_blue = np.array([150.54, 225, 150.96])
El resultado es el siguiente:
drone2.mp4
En este vídeo se han empleado los siguientes valores en el filtrado:
value_min_HSV = np.array([120, 110, 99])
value_max_HSV = np.array([180, 255, 255]) value_min_HSV_blue = np.array([8, 110, 110])
value_max_HSV_blue = np.array([127, 246, 255])
El resultado es el siguiente: