?️ CONTANDO DEDOS ✌️ (Usando defectos de convexidad) | Python – OpenCV
En el post de hoy veremos como identificar cuantos dedos están siendo levantados dentro de cierta área de la imagen. Para ello estaremos usando defectos convexos, quiero aclarar que para esta aplicación no se ha realizado ningún tipo de entrenamiento, mas bien se han aplicado operaciones sobre contornos. ¿Quieres conocer el proceso?, te invito a que te quedes hasta el final.
IMPORTANTE: El código y video usados en este tutorial lo tendrás en mi respositorio en github.
CONTENIDO:
- Convex Hull y defectos de convexidad
- cv2.convexHull
- cv2.convexityDefects
- ¿Cuántos dedos he levantado?
- Leer / Capturar un video y obtener el fondo de las escena
- Sustracción de imágenes y obtención de la imagen binaria
- Encontrar el contorno de la mano
- Determinar el centro del contorno y el punto más alto
- Mediante cv2.convexHull encontramos el casco convexo
- Defectos de convexidad
- Ángulo que forma el punto inicial al punto más alejado, y del punto más alejado al punto final.
- Distancia mínima entre el punto inicial – final y distancia aproximada al punto más alejado.
- Visualizando el conteo de dedos
- Programa completo
- Resultado final
Convex Hull y defectos de convexidad
Para realizar este tutorial necesitaremos prestarle atención a convex hull o casco convexo y a convexity defects o defectos de convexidad. Para ello vamos a analizar la siguiente imagen:
Debo decir que he encontrado un post buenísimo de pyimagesearch donde Adrian Rosebrock explica estos temas, por lo que te recomiendo que le des vistazo. Volviendo a este post, en la figura 1 tenemos el dibujo de una mano, aquel contorno rojo que rodea la mano es llamado casco convexo o convex hull, el cual da un efecto de banda elástica que está rodeando el objeto, como nos lo cuenta Adrian en su explicación.
Si prestamos atención a este contorno de color rojo dibujado, vamos a darnos cuenta existen áreas en donde no está topando a la mano (en la figura 1 están representadas con flechas bidireccionales negras), todas estas cavidades son consideradas defectos de convexidad que son desviaciones máximas locales, y en ellas nos apoyaremos para construir la aplicación de hoy, el conteo de dedos levantados, debido a que existirán defectos de convexidad.
cv2.convexHull
Esta es la función que emplea OpenCV para construir un casco convexo/convex hull que rodee un objeto, por lo tanto nos permitirá conseguir la línea roja que podemos ver en la figura 1, además nos ayudará a determinar los defectos de convexidad de un objeto.
Parámetros que usaremos:
- Points, que es el contorno encontrado.
- returnPoints, por defecto es True que nos devolverá las coordenadas del casco convexo. Mientas que si es False devolverá los índices de los puntos del casco convexo, lo cual permitirá encontrar los defectos de convexidad.
Para más información sobre otros parámetros que requiere esta función por favor dirigirse a la documentanción de OpenCV.
Pero, ¿qué te parece si usamos mi mano como ejemplo? a continuación podremos ver la imagen de entrada, y luego aplicando cv2.convexHull.
cv2.convexityDefects
Una vez que se ha calculado el casco convexo y tenemos False en returnPoints, vamos a aplicar la función cv2.convexityDefects para obtener los defectos de convexidad.
Parámetros que usaremos:
- Contorno de entrada.
- Casco convexo obtenido através de cv2.convexHull y cuyo parámetro returnPoints debió estar en False. Este debe contener los índices de los puntos que forman el casco convexo.
De esta función obtendremos un array en donde cada columna tendrá estos valores: [ punto inicial, punto final, punto más alejado, distancia aproximada al punto más alejado ].
Y de igual manera, vamos aplicarlo a la imagen de la figura 2.
Como podemos ver en la figura 4, aquí he etiquetado el punto inicial, final y más alejado del defecto de convexidad de entre el pulgar y el índice, mientras que en la figura 5 está la totalidad de puntos que corresponden a los defectos de convexidad encontrados (como podemos ver son muchísimos defectos encontrados y algunos parecen no aportar mucha información).
Cabe destacar que también necesitaremos de otras funciones, aquellas que ya hemos visto en otros posts, por lo tanto cuando las usemos te dejaré el link a cada una de ellas para que aclares algún dato en caso de ser necesario.
¿Cuántos dedos estoy levantando?
Para realizar esta aplicación tendremos que seguir algunos pasos tales como, obtención del fondo de la imagen, sustracción de imágenes (primer plano y fondo), encontrar el contorno de la mano en la imagen, extraer el casco convexo y los defectos de convexidad, diferenciar los casos en que estén levantados: 0, 1, 2, 3, 4 y 5 dedos.
A continuación podrás ver algunas imágenes del video de entrada (además de distintos dedos levantados) sobre el cual se va a aplicar todos estos procedimientos:
El que usemos un video de entrada para poder trabajar no quiere decir, que no puedas realizar un video streaming y probar el programa, claro que puedes hacerlo. En este caso uso este video para poder mostrarte la entrada y al final el resultado obtenido.
NOTA: En esta aplicación estaré usando la sustracción de dos imágenes para poder realizar la detección de la mano. Tú podrías tomar además de este otros criterios para conseguir diferenciar la mano del fondo tales como: discriminar la mano dependiendo del color de la piel, sustracción de fondo, o simplemente umbralización simple, etc.
¡Empecemos!
Leer/Capturar un video y obtener el fondo de la escena
import cv2 import numpy as np import imutils #cap = cv2.VideoCapture(0,cv2.CAP_DSHOW) cap = cv2.VideoCapture('videoEntrada.mp4') bg = None # COLORES PARA VISUALIZACIÓN color_start = (204,204,0) color_end = (204,0,204) color_far = (255,0,0) color_start_far = (204,204,0) color_far_end = (204,0,204) color_start_end = (0,255,255) color_contorno = (0,255,0) color_ymin = (0,130,255) # Punto más alto del contorno #color_angulo = (0,255,255) #color_d = (0,255,255) color_fingers = (0,255,255) while True: ret, frame = cap.read() if ret == False: break # Redimensionar la imagen para que tenga un ancho de 640 frame = imutils.resize(frame,width=640) frame = cv2.flip(frame,1) frameAux = frame.copy()
Línea 1 a 3: Importamos OpenCV, numpy e imutils, este último lo emplearemos para redimensionar cada fotograma, ya lo veremos.
Línea 5: (En caso de realizar un video streaming o video en vivo). Iniciamos con cv2.VideoCapture
para realizar un video streaming. He usado cv2.CAP_DSHOW
en Windows para que la visualización del video aparezca completamente en la ventana de visualización. Podrías omitir esto en caso de no ser necesario.
Línea 6: (En caso de leer algún video, como el que vamos a trabar en este post). Para ello debemos especificar el nombre del video en caso de que este se encuentre en la misma carperta que la del script.
Línea 7: Establecemos bg
(backgroun o fondo) como None
, ya que luego con ayuda de una tecla (i) podremos capturar el fondo de la escena (esto lo veremos en la línea 138 más tarde).
if k == ord('i'): bg = cv2.cvtColor(frameAux,cv2.COLOR_BGR2GRAY)
Línea 10 a 22: Aquí únicamente se está asignando ciertos colores a variables. Esto nos servirá para poder cambiar los colores de la visualización para los defectos convexos, contornos, dedos levantados, entre otros.
Línea 25 y 26: Vamos a leer cada fotograma, entonces cada imagen se almacenará en frame
, en la línea 26 en caso de que ret
sea falso, es decir que no se haya leído una imagen, el ciclo se romperá.
Línea 29: Redimensionamos la imagen almacenada en frame
para que tenga un ancho de 640, esto nos servirá en caso de que el video sea muy grande en su alto y ancho. El uso de imutils para el redimensionamiento ya lo hemos visto en el post anterior.
Línea 30 y 31: Vamos a usar la funcióncv2.flip
para voltear frame
para que nos permita visualizar el video streaming/video como si fuera un espejo. Podrías omitir esta línea en caso de ser necesario. En la línea 31 vamos a obtener una copia de frame
que nos servirá para capturar el fondo de la imagen sin que esta sea alterada por las funciones de dibujo que usemos luego.
Hasta este punto tenemos los fotogramas y una vez que se digite «i» se almacenará una imagen (fondo) en bg
.
Sustracción de imágenes y obtención de la imagen binaria
Ahora será necesario restar los fotogramas con la imagen de fondo almacenada:
if bg is not None: # Determinar la región de interés ROI = frame[50:300,380:600] cv2.rectangle(frame,(380-2,50-2),(600+2,300+2),color_fingers,1) grayROI = cv2.cvtColor(ROI,cv2.COLOR_BGR2GRAY) # Región de interés del fondo de la imagen bgROI = bg[50:300,380:600] # Determinar la imagen binaria (background vs foreground) dif = cv2.absdiff(grayROI, bgROI) _, th = cv2.threshold(dif, 30, 255, cv2.THRESH_BINARY) th = cv2.medianBlur(th, 7)
Línea 33: Una vez que se haya almacenado una imagen en bg
entramos en esta condicional.
Línea 36: Determinamos el área de interés donde deseemos que la mano se pueda ubicar y pueda contar los dedos.
Línea 37: Dibujamos un rectángulo en dicha área de interés, puedes notar que he restado y sumado 2, esto es para dar un margen al rectángulo dibujado.
Línea 38: Transformamos de BGR a GRAY a la variable grayROI
.
Línea 41: Ahora tomamos la misma área de interés pero ahora en bg
que es el fondo de la imagen.
Línea 44 a 46: Vamos a restar la imagen de primer plano y el fondo que se almacenará en dif
, y esta a su vez aplicamos umbralización simple, finalmente para mejorar la imagen binaria he usado cv2.medianBlur
.
NOTA: Tanto la sustracción de imágenes como umbralización simple lo hemos visto en videos anteriores. Es más habíamos usado estas técnicas para realizar la detección simple de movimiento.
Si visualizamos obtendremos las siguientes imágenes:
Como te pudiste haber dado cuenta, el objetivo del proceso que hemos realizado es obtener la imagen binaria, en donde en blanco aparecerá la mano, mientras que el fondo debe mantenerse en negro. Es importante que elijas un fondo que contraste con el color de la mano para que se pueda obtener una imagen binaria óptima, de otro modo puede presentar problemas al contar los dedos en procesos posteriores.
Encontrar el contorno de la mano
Una vez que hemos obtenido la imagen binaria en donde se muestra la mano en blanco necesitamos encontrar su contorno.
# Encontrando los contornos de la imagen binaria cnts, _ = cv2.findContours(th,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = sorted(cnts,key=cv2.contourArea,reverse=True)[:1]
Línea 49: Encontramos los contornos externos de la imagen binaria (si obtienes algún problema prueba con _, cnts, _
).
Línea 50: Escogemos el contorno más grande que se muestre en la imagen. Podrías cambiar este criterio y por ejemplo determinar el contorno de la mano mediante su área.
Determinar el centro del contorno y el punto más alto del mismo
Estos pasos nos servirán para los casos en que no se haya levantado ningún dedo o se haya levantado uno, esto lo veremos más adelante:
for cnt in cnts: # Encontrar el centro del contorno M = cv2.moments(cnt) if M["m00"] == 0: M["m00"]=1 x = int(M["m10"]/M["m00"]) y = int(M["m01"]/M["m00"]) cv2.circle(ROI,tuple([x,y]),5,(0,255,0),-1) # Punto más alto del contorno ymin = cnt.min(axis=1) cv2.circle(ROI,tuple(ymin[0]),5,color_ymin,-1)
Línea 55 a 59: A través de estas líneas encontramos los puntos x
e y
que corresponden al centro del contorno. En la línea 59 estaremos dibujando el punto obtenido.
Línea 62 y 63: Encontramos el punto más alto del contorno. Esto nos servirá en caso de que no se haya levantado ningún dedo o se haya levantado un dedo.
Mediante cv2.convexHull encontramos el casco convexo
Este paso es netamente para visualización.
# Contorno encontrado a través de cv2.convexHull hull1 = cv2.convexHull(cnt) cv2.drawContours(ROI,[hull1],0,color_contorno,2)
Línea 66 y 67: Vamos con el casco convexo del contorno más grande encontrado, luego lo visualizaremos en la línea 67. Tendríamos lo siguiente:
Defectos de convexidad
# Defectos convexos hull2 = cv2.convexHull(cnt,returnPoints=False) defects = cv2.convexityDefects(cnt,hull2) # Seguimos con la condición si es que existen defectos convexos if defects is not None: inicio = [] # Contenedor en donde se almacenarán los puntos iniciales de los defectos convexos fin = [] # Contenedor en donde se almacenarán los puntos finales de los defectos convexos fingers = 0 # Contador para el número de dedos levantados for i in range(defects.shape[0]): s,e,f,d = defects[i,0] start = cnt[s][0] end = cnt[e][0] far = cnt[f][0]
Líneas 70 y 71: Como vimos en un principio, con estas líneas vamos a encontrar los defectos de convexidad. Los datos que nos devolverá defects
serán escenciales ya que con ellos vamos a calcular el número de dedos levantados.
Línea 76 a 78: Estas variables las emplearemos más tarde para visualizar información en el resultado final.
Línea 80 a 85: Procedemos a desempaquetar la información dada por defects
es decir el punto inicial, punto final, punto más alejado, distancia aproximada al punto más alejado. Si visualizamos estos puntos, obtendremos algunas imágenes como estas:
En la figura 11 podemos ver los puntos iniciales en color celeste, puntos finales en violeta mientras que los puntos más alejados en azul. Son muchos puntos ¿verdad?, pues necesitamos deshacernos de algunos de ellos que no nos aportan mucha ayuda.
Para poder tomar puntos que nos ayuden a solucionar esta aplicación he tomado 3 criteriorios:
- Ángulo que forma el punto inicial al punto más alejado, y del punto más alejado al punto final.
- Distancia mínima entre el punto inicial y final.
- Distancia aproximada al punto más alejado.
Ángulo que forma el punto inicial al punto más alejado, y del punto más alejado al punto final.
Voy a empezar explicando el procedimiento que necesita un poco más de líneas de código. Si tomamos en cuenta los ángulos que pueden formar los dedos consecutivamente, tenemos que estos podrán formar hasta unos 90 grados aproximadamente (Por el ángulo que forma el pulgar y el índice).
Para poder calcular los ángulos que forme cada par de dedos, podremos aplicar trigonometría, de tal modo que dados los lados de un triángulo formados por los segmentos del punto inicial al punto más alejado, del punto más alejado al punto final y del punto final al punto inicial, tendríamos lo siguiente:
Para obtener el ángulo entonces tendríamos lo siguiente:
Esto para en el pograma sería:
# Encontrar el triángulo asociado a cada defecto convexo para determinar ángulo a = np.linalg.norm(far-end) b = np.linalg.norm(far-start) c = np.linalg.norm(start-end) angulo = np.arccos((np.power(a,2)+np.power(b,2)-np.power(c,2))/(2*a*b)) angulo = np.degrees(angulo) angulo = int(angulo)
Línea 88 a 90: Aplicamos la distancia entre dos puntos con la ayuda de numpy, con ello encontraremos los lados a
, b
y c
.
Línea 92 a 94: Encontramos el ángulo C de la figura 12 con ayuda de la fórmula que habíamos visto recientemente. Como el resultado está en radianes lo convertiremos en grados y finalmente a enteros.
Según varias pruebas realizadas por mi parte, encontré que se puede obtener hasta 90 grados en los ángulos entre los dedos (para esta aplicación), por lo tanto este es el valor que me estaré basando para filtrar de otros ángulos obtenidos.
Distancia mínima entre el punto inicial – final y distancia aproximada al punto más alejado.
Otros aspectos que probé es la distancia entre dos puntos, el punto inicial y final ya que si están demasido cerca puede que no nos den buenos resultados al identificar los defectos convexos en esta aplciación, por ello deben cumplir con ser mayores a una distancia de 20.
Otra condición sería la distancia al punto más alejado, puesto que pueden aparecer identificaciones en los dedos que doblamos cuando extendemos otros, con esto nos aseguramos que haya una distancia mínima a cumplir.
Veamos estos 3 aspectos en el código:
# Se descartarán los defectos convexos encontrados de acuerdo a la distnacia # entre los puntos inicial, final y más alelago, por el ángulo y d if np.linalg.norm(start-end) > 20 and angulo < 90 and d > 12000: # Almacenamos todos los puntos iniciales y finales que han sido # obtenidos inicio.append(start) fin.append(end) # Visualización de distintos datos obtenidos #cv2.putText(ROI,'{}'.format(angulo),tuple(far), 1, 1.5,color_angulo,2,cv2.LINE_AA) #cv2.putText(ROI,'{}'.format(d),tuple(far), 1, 1.1,color_d,1,cv2.LINE_AA) cv2.circle(ROI,tuple(start),5,color_start,2) cv2.circle(ROI,tuple(end),5,color_end,2) cv2.circle(ROI,tuple(far),7,color_far,-1) #cv2.line(ROI,tuple(start),tuple(far),color_start_far,2) #cv2.line(ROI,tuple(far),tuple(end),color_far_end,2) #cv2.line(ROI,tuple(start),tuple(end),color_start_end,2)
Línea 98: Vamos con la condición que seleccionará a los puntos que representen a los dedos levantados.
Línea 102 y 103: Almacenamos los puntos iniciales y finales, lo que nos va a servir para visualizar los números sobre cada dedo levantado.
Línea 106 a 113: Estas líneas nos van a servir para visualizar la información de los puntos de los defectos convexos, además si descomentamos podemos visualizar las líneas que los unen o la información del ángulo o de la distancia en d
por ejemplo.
Si visualizamos tendríamos lo siguiente:
Visualizando el conteo de dedos
Ya casi terminamos, ya prácticamente tenemos como contar 2, 3, 4 y 5 dedos. Pero aún no hemos realizado el proceso para contar 1 dedo levantado, veamos el procedimiento que se ha realizado para conseguirlo.
# Si no se han almacenado puntos de inicio (o fin), puede tratarse de # 0 dedos levantados o 1 dedo levantado if len(inicio)==0: minY = np.linalg.norm(ymin[0]-[x,y]) if minY >= 110: fingers = fingers +1 cv2.putText(ROI,'{}'.format(fingers),tuple(ymin[0]), 1, 1.7,(color_fingers),1,cv2.LINE_AA)
Línea 117 y 118: Si no se ha almacenado ningún punto inicial (esto sucederá cuando se hayan levantado 0 y 1 dedo), entonces procedemos a calcular la distancia entre el centro del contorno y el punto más alto del contorno que se almacenará en minY
.
Línea 119 a 121: Si la distancia minY
es mayor o igual a 110 (valor que he determinado después de pruebas, tu podrías modificarlo), quiere decir que un dedo ha sido levantado, por lo tanto en la línea 121 se visualizará 1 en el la coordenada del punto más alto del contorno.
# Si se han almacenado puntos de inicio, se contará el número de dedos levantados for i in range(len(inicio)): fingers = fingers + 1 cv2.putText(ROI,'{}'.format(fingers),tuple(inicio[i]), 1, 1.7,(color_fingers),1,cv2.LINE_AA) if i == len(inicio)-1: fingers = fingers + 1 cv2.putText(ROI,'{}'.format(fingers),tuple(fin[i]), 1, 1.7,(color_fingers),1,cv2.LINE_AA) # Se visualiza el número de dedos levantados en el rectángulo izquierdo cv2.putText(frame,'{}'.format(fingers),(390,45), 1, 4,(color_fingers),2,cv2.LINE_AA) cv2.imshow('th',th) cv2.imshow('Frame',frame) k = cv2.waitKey(20) if k == ord('i'): bg = cv2.cvtColor(frameAux,cv2.COLOR_BGR2GRAY) if k == 27: break cap.release() cv2.destroyAllWindows()
Línea 124 a 129: Vamos a usar los puntos alamacenados en inicio
y fin
, para poder visualizar los números sobre cada dedo, para ello emplearemos un for que recorrerá cada coordenada almacenada. Usamos fingers
como contador de los dedos de la mano.
Línea 132: Visualizamos el número total de dedos encontrados.
Línea 134 y 135: Visualizamos tanto la imagen binaria th
para asegurarnos que se esté identificando de forma correcta la mano y frame
.
Línea 137 a 141: Cuando ‘i’ es presionada se guarda la imagen bg
que corresponderá al fondo de la imagen, mientras que si presiona ‘Esc’ rompe el proceso.
Línea 143 y 144: Finalizamos la captura o video y cerramos todas las ventanas que se han abierto.
Programa completo
import cv2 import numpy as np import imutils #cap = cv2.VideoCapture(0,cv2.CAP_DSHOW) cap = cv2.VideoCapture('videoEntrada.mp4') bg = None # COLORES PARA VISUALIZACIÓN color_start = (204,204,0) color_end = (204,0,204) color_far = (255,0,0) color_start_far = (204,204,0) color_far_end = (204,0,204) color_start_end = (0,255,255) color_contorno = (0,255,0) color_ymin = (0,130,255) # Punto más alto del contorno #color_angulo = (0,255,255) #color_d = (0,255,255) color_fingers = (0,255,255) while True: ret, frame = cap.read() if ret == False: break # Redimensionar la imagen para que tenga un ancho de 640 frame = imutils.resize(frame,width=640) frame = cv2.flip(frame,1) frameAux = frame.copy() if bg is not None: # Determinar la región de interés ROI = frame[50:300,380:600] cv2.rectangle(frame,(380-2,50-2),(600+2,300+2),color_fingers,1) grayROI = cv2.cvtColor(ROI,cv2.COLOR_BGR2GRAY) # Región de interés del fondo de la imagen bgROI = bg[50:300,380:600] # Determinar la imagen binaria (background vs foreground) dif = cv2.absdiff(grayROI, bgROI) _, th = cv2.threshold(dif, 30, 255, cv2.THRESH_BINARY) th = cv2.medianBlur(th, 7) # Encontrando los contornos de la imagen binaria cnts, _ = cv2.findContours(th,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = sorted(cnts,key=cv2.contourArea,reverse=True)[:1] for cnt in cnts: # Encontrar el centro del contorno M = cv2.moments(cnt) if M["m00"] == 0: M["m00"]=1 x = int(M["m10"]/M["m00"]) y = int(M["m01"]/M["m00"]) cv2.circle(ROI,tuple([x,y]),5,(0,255,0),-1) # Punto más alto del contorno ymin = cnt.min(axis=1) cv2.circle(ROI,tuple(ymin[0]),5,color_ymin,-1) # Contorno encontrado a través de cv2.convexHull hull1 = cv2.convexHull(cnt) cv2.drawContours(ROI,[hull1],0,color_contorno,2) # Defectos convexos hull2 = cv2.convexHull(cnt,returnPoints=False) defects = cv2.convexityDefects(cnt,hull2) # Seguimos con la condición si es que existen defectos convexos if defects is not None: inicio = [] # Contenedor en donde se almacenarán los puntos iniciales de los defectos convexos fin = [] # Contenedor en donde se almacenarán los puntos finales de los defectos convexos fingers = 0 # Contador para el número de dedos levantados for i in range(defects.shape[0]): s,e,f,d = defects[i,0] start = cnt[s][0] end = cnt[e][0] far = cnt[f][0] # Encontrar el triángulo asociado a cada defecto convexo para determinar ángulo a = np.linalg.norm(far-end) b = np.linalg.norm(far-start) c = np.linalg.norm(start-end) angulo = np.arccos((np.power(a,2)+np.power(b,2)-np.power(c,2))/(2*a*b)) angulo = np.degrees(angulo) angulo = int(angulo) # Se descartarán los defectos convexos encontrados de acuerdo a la distnacia # entre los puntos inicial, final y más alelago, por el ángulo y d if np.linalg.norm(start-end) > 20 and angulo < 90 and d > 12000: # Almacenamos todos los puntos iniciales y finales que han sido # obtenidos inicio.append(start) fin.append(end) # Visualización de distintos datos obtenidos #cv2.putText(ROI,'{}'.format(angulo),tuple(far), 1, 1.5,color_angulo,2,cv2.LINE_AA) #cv2.putText(ROI,'{}'.format(d),tuple(far), 1, 1.1,color_d,1,cv2.LINE_AA) cv2.circle(ROI,tuple(start),5,color_start,2) cv2.circle(ROI,tuple(end),5,color_end,2) cv2.circle(ROI,tuple(far),7,color_far,-1) #cv2.line(ROI,tuple(start),tuple(far),color_start_far,2) #cv2.line(ROI,tuple(far),tuple(end),color_far_end,2) #cv2.line(ROI,tuple(start),tuple(end),color_start_end,2) # Si no se han almacenado puntos de inicio (o fin), puede tratarse de # 0 dedos levantados o 1 dedo levantado if len(inicio)==0: minY = np.linalg.norm(ymin[0]-[x,y]) if minY >= 110: fingers = fingers +1 cv2.putText(ROI,'{}'.format(fingers),tuple(ymin[0]), 1, 1.7,(color_fingers),1,cv2.LINE_AA) # Si se han almacenado puntos de inicio, se contará el número de dedos levantados for i in range(len(inicio)): fingers = fingers + 1 cv2.putText(ROI,'{}'.format(fingers),tuple(inicio[i]), 1, 1.7,(color_fingers),1,cv2.LINE_AA) if i == len(inicio)-1: fingers = fingers + 1 cv2.putText(ROI,'{}'.format(fingers),tuple(fin[i]), 1, 1.7,(color_fingers),1,cv2.LINE_AA) # Se visualiza el número de dedos levantados en el rectángulo izquierdo cv2.putText(frame,'{}'.format(fingers),(390,45), 1, 4,(color_fingers),2,cv2.LINE_AA) cv2.imshow('th',th) cv2.imshow('Frame',frame) k = cv2.waitKey(20) if k == ord('i'): bg = cv2.cvtColor(frameAux,cv2.COLOR_BGR2GRAY) if k == 27: break cap.release() cv2.destroyAllWindows()
Resultado final
A continuación podemos apreciar como se desempeñando el programa. Recuerda presionar ‘i’ para almacenar el fondo de la imagen, para que se puedan detectar los dedos. (Puedes visualizar el video del resultado final aquí)
Hemos llegado al final de este post, espero que te haya gustado, nos vemos en el siguiente video o post. ¡Cuídate mucho!.
REFERENCIAS:
- https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html
- https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contours_more_functions/py_contours_more_functions.html#convexity-defects
- https://gurus.pyimagesearch.com/lesson-sample-advanced-contour-properties/
- https://dsp.stackexchange.com/questions/26996/what-is-the-deffinition-of-convexity-defect-in-image-processing
- https://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#convexitydefects
- https://www.youtube.com/watch?v=v-XcmsYlzjA
- https://medium.com/@soffritti.pierfrancesco/handy-hands-detection-with-opencv-ac6e9fb3cec1
- https://www.superprof.es/apuntes/escolar/matematicas/trigonometria/4-resolver-un-triangulo-conociendo-los-tres-lados.html
Podríamos hacer uno de conteo de personas, que por ejemplo, ingresan y salen de un centro comercial, por favor? (Pdta: Eres muy buena explicando)
Me pareceria interesante un conteo de personas 😀 Saludos desde Lima . Perú
me parecio un trabajo muy exelente, me gustaria saber si en vez de numers podrias conertirlos en letras estaria dispuesto a trabajar en ello muchas gracias
Hola Nicolas, muchas gracias. En vez de que aparezca 1, 2, 3 por ejemplo aparezca uno, dos, tres o cuatro en la visualización?
¿Pudiera de alguna manera combinarse este proyecto con el de reconocimiento de objetos?
Quisiera poder hacer figuras con la mano, o señales y que en pantalla me pudieran salir que significa cada señal.
Hola Jesus, si vas a usar este método para tu proyecto toma en cuenta que se distingue cada forma basándose en su forma externa, por lo que tiene sus limitantes.
Hola Gabriela ,me gusta mucho tu trabajo y la forma de exponer los temas. Quería saber si tu conoces un modulo de reconocimiento de la palma de la mano tal como existe un reconocimiento facial .
O se podrá adaptar ese reconocimiento Facial para hacer reconocimiento a la palma de la mano.
Hola Pablo muchas gracias! 😀 En realidad no me he topado con ningún módulo así. Creo que podrías experimentar con los métodos para el reconocimiento facial, pero no estoy segura de los resultados que obtendrías. Lo siento por no ser de mucha ayuda en esta ocasión.
Hola, quise utilizarlo pero con camara en vivo pero no corre, porfa puedes ayudar gaby
Hola Patricia, obtienes algún error?