Projetei este pequeno aplicativo utilizando python e opencv2 (v 3.4.5). Opencv foi eleito por compatibilidade com inúmeros dispositivos, multiplicidade de ambientes e por possuir fáceis recursos para captura da câmera, apresentação das imagens e manipulação gráfica da matriz bitmap.
Para melhor situar o caro leitor, o opencv foi desenvolvido em 2000 pela Intel, é livre regida sob a licença BSD. Nos últimos anos com atenções voltadas a área de machine learning e algorítimos de reconhecimento, recebeu importantes contribuições que enriqueceram suas bibliotecas. Originalmente projetada em C, foi migrada para C++ e possui implementação em Python via wrappers.
Instalando o OpenCv
Pré-requisitos: numpy, python / Recomendado: python 3.6.
No Ubuntu:
# Atualizamos o ambiente
sudo apt-get update
sudo apt-get install build-essential cmake unzip pkg-config
sudo apt-get install libjpeg-dev libpng-dev libtiff-dev
sudo apt-get install libxvidcore-dev libx264-dev
# Instalamos o OpenCv
sudo apt-get install git
git clone https://github.com/opencv/opencv.git
mkdir build
cd build
cmake ../
# Instalamos dependencias e bibliotecas
sudo apt-get install python-dev python-pip python3-dev python3-pip
# Instalamos o adds-on de contribuicao
git clone https://github.com/opencv/opencv_contrib.git
cd opencv_contrib
git checkout 3.4.5
cd ..
make
sudo make install
# fast-way sem garantias:
sudo apt-get install python-opencv
sudo pip install numpy
sudo pip install opencv2
# 1,2,3 .. testando
$ python
> import cv2 as cv
> print(cv.__version__)
No Windows:
.. a partir dos binários pré-compilados:
- Baixe o pacote OpenCv diretamente do sourceforge
- Copie e cole o cv2.pyd no diretório de lib\site-packages do Anaconda.
- Adicione %OPENCV_DIR%\bin ao Path, se já não estiver para que o Anaconda saiba onde encontrar o utilitário FFMPEG.
- Faça alguns testes para confirmar que o OpenCV e o FFMPEG estão funcionando:
.. compilando o source-code:
- Baixe o Visual Studio Build Tools
- Baixe e instale o CMake
- instale o numpy via pip ou conda (pip install numpy)
- Baixe o código fonte diretamente do source-forge, descompacte-o (\opencv) e crie uma nova pasta para o build (\opencv\mybuild)
- Abra o CMake-GUI e configure:Browse Source (\opencv)
- Browse Build (\opencv\mybuild)
- Clique em Configure, selecione Visual Studio 11, e Finish
- Após a análise, desmarque a opção ENABLE_SOLUTION_FOLDERS.
- Se possuir GPU e desejar utilizá-la, habilite BUILD_opencv_gpuwarping
7. No gerenciador de soluções, clique com o botão direito do mouse em Solution e execute o Build.
Pegue um café e aguarde.
Python
A aplicação funcionou bem no Python 2 e 3. Mas para evitar erros de execução inesperados, recomendo a versão 3.6, que é onde concluí o projeto.A parte mais chatinha realmente é a instalação do opencv, infelizmente. Uma vez realizada porém é possível avançar alguns passos em direção ao processamento de imagens, entendimento de algorítimos avançados que possuem aplicação variada, e tirará sua mente do limbo CRUD, webservices, databases, etc.
Projeto e Resultados
De antemão, peço desculpas pela qualidade do vídeo. Dá para notar a funcionalidade em execução que é o mais importante. Mais que isso: logo mais você mesmo estará executando-o em sua máquina, o código fonte está disponível no github.Idéia geral do processo
- Processamento quadro a quadro
- Determinar um espaço máximo para a captura da foto, para eliminar objetos diferentes da mão;
- Eliminar o máximo de ruidos da foto e limiarizá-la em preto e branco;
- Usar a função de convecidade do OpenCV, capturando o contorno da imagem.
- Usar o calculo de distância, área e ângulos para estimar qual a gesto a mão está realizando
Código - Inicialização
Inicialmente, realizamos a importação das bibliotecas e dizemos para o opencv que queremos que ele inicialize a conexão com a câmera conectada ao computador.Se houver mais de uma câmera, por óbvio que deve-se mudar o indice do equipamento. Como eu tenho apenas a câmera built-in do notebook, o indice dela é o zero. Conforme abaixo:
import cv2
import numpy as np
import math
cap = cv2.VideoCapture("0")
while(cap.isOpened()):
ret, img = cap.read()
No loop, que lê cada quadro, img é o que nos importa. É a imagem capturada pelo opencv. É com essa imagem que iremos trabalhar !
Escala de Cinzas
Antes de converter a imagem para preto e branco, temos de convertê-la para uma escala de cinzaUtilizando esta chamada ao OpenCv:
grey = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
Desfoque
A aplicação de desfoque (blur) gaussiano, é útil para eliminar ruidos, homogeneizar a foto e equilibrar melhor a distribuição dos cantos onde eles realmente existirem. Na prática um kernel gaussiano é aplicado em progressão a partir de cada ponto. Neste caso um kernel de tamanho 35, tornando-a menos sucetivel a erros na próxima etapa.
value = (35, 35)
blurred = cv2.GaussianBlur(grey, value, 0)
Binarização da imagem
A binarização da imagem ocorre mediante alguns parametros, como o range médio limiar esperado de equilibrio ao qual espera-se chegar. Variando o parâmetro, variamos o resultado, conforme podemos ver abaixo:O OpenCv utiliza o treshhold baseado na binarização de Otsu's Binarization, criado pelo matemático Nobuyuki Otsu que dá nome ao mesmo e é usado para prever o limiar de imagem baseado em agrupamento de pixels, resultando em uma imagem binária. Espero um equilibrio médio, pegando a partir de 127 até 255.
_, thresh1 = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
O Contorno
A partir daqui, peço para o opencv realizar o calculo do contorno e a superficie da área do contorno capturado:image, contours, hierarchy = cv2.findContours(thresh1.copy(), \ cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = max(contours, key = lambda x: cv2.contourArea(x)) hull = cv2.convexHull(cnt, returnPoints=False)
Na minha imagem de corte, no canto, apenas minha mão é o objeto em cena, então o maior objeto em cena será ela, o restante serão ruidos.
defects = cv2.convexityDefects(cnt, hull) count_defects = 0
Então calculo a convecidade do ângulo do contorno.
defects = cv2.convexityDefects(cnt, hull) count_defects = 0
São as linhas em vermelho que me interessam, e é a partir dela que determinarei qual gesto a mão está realizando.
Um pouco de trigonometria e aritimética nos permitem dizer o grau dos ângulos formados pelas linhas. Me permitindo distinguir entre mão fechada e aberta. E o número de quebras dos ângulos me permitirá dizer quantos dedos estão sendo mostrados (2 para tesoura, 5 para papel).
defects = cv2.convexityDefects(cnt, hull)
for i in range(defects.shape[0]):
s,e,f,d = defects[i,0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
a = math.sqrt((end[0] - start[0])**2 + (end[1] - start[1])**2)
b = math.sqrt((far[0] - start[0])**2 + (far[1] - start[1])**2)
c = math.sqrt((end[0] - far[0])**2 + (end[1] - far[1])**2)
# regra do cosceno
angle = math.acos((b**2 + c**2 - a**2)/(2*b*c)) * 57
# ignora angulos menores que 90
if angle <= 90:
count_defects += 1
cv2.circle(crop_img, far, 1, [0,0,255], -1)
count_large = 0
O artigo ficou um pouco longo, sinto se ficou cansativo. Mas para detalhar tantos pequenos passos é importante para mostraros pequenos truques utilizados na visão computacional. A beleza desta solução algorítimica é que ela é extremamente rápida.
Novamente, o código fonte está disponível no github. Mande suas sugestões e melhorias ! Será grande a satisfação em conectar conhecimentos.
Grande abraço !
Novamente, o código fonte está disponível no github. Mande suas sugestões e melhorias ! Será grande a satisfação em conectar conhecimentos.
Grande abraço !






