Desenvolver projetos é uma forma bem divertida de adquiri novas habilidades, então aqui vai mais um projetor para aprendermos mais sobre visão computacional.
Com certeza você conhece o jogo do dinossauro do chrome, tem alguns projeto de IA para jogar esse jogo.
Aqui vamos jogar usando gestos da mão. A pouco tempo, capturar pontos da mão era uma tarefa muito passado, restrita a GPUs. Porém, um projeto Google parece resolver esse problema, o mediapipe.
O mediapipe é um framework que usa a tecnologia xnnpack de aceleração gráfica, usando modelos de tensorflow lite é possivel obter possível obter um desempenho incrível.
Existem alguns exemplos no mediapipe, dentre eles um detector de pontos da mão.
landmarks
Landmarks (marcos) são modelos criados para detectar conjuntos de pontos relacionados.
O mediapipe combina dois modelos, um para detectar a mão e outro para detectar os conjuntos de 21 pontos.
Descrição do projeto
Utilizamos apenas os pontos 4 e 8 detectados para interagir com jogo. Calculamos a distância entre eles. Quando a distância é menor que 20 passamos o comando para o dinossauro se manter abaixado.
Quando a distância é maior que 80 geramos um evento de pressionar a tecla espaço (pular).
Para emular o teclado usamos pyautogui. OpenCv foi usado para obter fluxo de vídeo e pré-processamento.
Instalando pacotes
Todas as dependências podem ser instalada diretamente via Pip.
Com python3 e pycharm configurado. Basta abrir o projeto, criar um ambiente virtual e instalar os pacotes abaixo (Veja aqui) .
- opencv-python
- mediapipe
- pyautogui
Codando
Primeiro passo, importar os pacotes utilizados. PRESSED_SPACE e PRESSED_DOWN são flags que usamos para não executar eventos do teclado desnecessário.
import numpy as np import cv2 import pyautogui import mediapipe as mp mp_drawing = mp.solutions.drawing_utils mp_hands = mp.solutions.hands PRESSED_SPACE=False PRESSED_DOWN=False
Por algum motivo o pyautogui estava demorando um pouco para executar o primeiro evento do teclado então coloquei um evento no início na linha 9. Aqui também instanciei um objeto do mediapipe que chamei de hands.
As flags max_num_hands=1 limita o total de mão detectado para apenas uma
static_image_mode=False, usa um rastreador, assim não será executado o detector para todo frame, isso deixa os movimentos mais suaves, aumenta a desempenho. Por outro lado, é um pouco menos preciso.
pyautogui.press("space") cap = cv2.VideoCapture(0) with mp_hands.Hands( min_detection_confidence=0.5, min_tracking_confidence=0.5, max_num_hands=1, static_image_mode=False) as hands:
Na linha 16 Iniciamos o loop de processamento. A image é convertida para RGB e passada para o pipeline de processamento do mediapipe.
while cap.isOpened(): success, image = cap.read() if not success: continue image=cv2.flip(image, 1) image_RGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image.flags.writeable = False results = hands.process(image_RGB)
Na linha 27 verificamos se encontrou uma mão. Se verdade, percorremos o objeto results.multi_hand_landmarks para obter os pontos.
Nas linhas pegamos pontos que precisamos e convertamos para uma tupa de inteiros.
if results.multi_hand_landmarks: image_height, image_width, _ = image.shape annotated_image = image.copy() for hand_landmarks in results.multi_hand_landmarks: dedao_x=hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP].x * image_width dedao_y=hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP].y * image_height dedao=(int(dedao_x),int(dedao_y)) indicador_x = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].x * image_width indicador_y=hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].y * image_height indicador=(int(indicador_x),int(indicador_y))
Calculamos a distância entre os pontos. Criei também uma tupla COLOR, inicialmente como verde (0,255,0) esse é o caso em que nem um evento de teclado é disparado.
Quando a distância é maior que 80 COLOR é alterado para azul (255,0,0) e PRESSED_SPACE recebe True, isso evita que múltiplos eventos da tecla espaço seja executado.
O comando pyautogui.press invoca um evento de pyautogui.keyDown() seguido de pyautogui.keyUp. Isso corresponde ao evento de pressionar e soltar a tecla.
Para manter o dinossauro abaixado, fazemos uso dessas duas funções ao invés de pyautogui.press()
distancia=int(np.sqrt((indicador_x-dedao_x)**2+(indicador_y-dedao_y)**2)) COLOR=(0,255,0) if distancia>80: COLOR=(255,0,0) if not PRESSED_SPACE: print("tecla") pyautogui.press("space") PRESSED_SPACE=True else: PRESSED_SPACE=False if distancia<20: if not PRESSED_DOWN: print("tecla") pyautogui.keyDown('Down') PRESSED_DOWN=True COLOR=(0,0,255) else: if PRESSED_DOWN: print("tecla") pyautogui.keyUp('Down') PRESSED_DOWN=False
E por fim o resultado é desenhado na imagem
cv2.line(image, (dedao), (indicador), COLOR, 2, cv2.LINE_AA) cv2.circle(image,indicador,4,(255,0,255),2,cv2.LINE_AA) cv2.circle(image,dedao, 4, (255, 0, 0), 2, cv2.LINE_AA) mp_drawing.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS) cv2.imshow('visioncompy', image) if cv2.waitKey(30) & 0xFF == 27: break cap.release()
Para interagir com o jogo, basta abrir chrome://dino (seu chrome pode substituir chrome:// por http:// isso não funcionara).
A pagina do chrome precisa estar em primeiro plano (secionada, pois evento de teclado não é processado por programas em segundo plano).
Conclusão
Aqui está um video de exemplo do que acabamos de construir.
Você pode baixar ao código fonte completo aqui
Fique a vontade para usar os campos de perguntas abaixo. Me siga no Instagram @elton.py para mais informação sobre visão computacional.
Referência:
Mediapipe: https://github.com/google/mediapipe/blob/master/docs/solutions/hands.md
One Reply on “Controlando jogo dino com Python e OpenCV”
Bom dia Elton, to fazendo meu tcc da faculdade e vou utilizar a biblioteca do mediapipe hands para fazer reconhecimento de das mãos para fazer o abcdario de libras, e gostaria de fazer uma pergunta pra vc. Vc sabe se tem alguma forma de vez de eu abrir o video para fazer o reconhecimento da mão eu abrir uma imagem em png e trabalhar as cordenadas em cima dela e depois salvar em txt para eu jogar dentro de um treinamento de uma rede neural?