Webcam Detectando Movimento

Meu chefe me disse para postar meus projetos pessoais no meu super famoso e mega visitado blog, já que segundo ele “vai que você arruma um emprego melhor?!” #tumdumtss

Mas na verdade, acho que a melhor coisa desses posts é ajudar os brasileiros que querem se aventurar fazendo a mesma coisa, pois realmente existe praticamente nada de informação sobre essas coisas em português.

O primeiro projeto que irei postar aqui é o da webcam que detecta movimento e se move para acompanhá-lo.

Aqui segue um vídeo da solução “funcionando” (a solução final é cheia de problemas, mas digamos que ela “funfa!”.. lol)

A idéia por trás dessa aplicação é fazer um processamento da imagem provinda da webcam e mover a câmera para acompanhar o movimento. Logo, dois principais itens teriam que ser desenvolvidos:

  • Detector de movimento baseado na imagem da webcam
  • Mover a câmera através do computador

Vamos começar por mover a câmera pelo computador.

Movendo a câmera pelo computador


Essa parte foi realmente difícil pra mim (mas na verdade é bem fácil, eu que era um tapado em eletrônica)

Primeiro eu precisava grudar algum motor na webcam e esse motor deveria proporcionar um controle fino de quanto ele se movia. Queria dizer pro motor virar 10º graus pra esquerda e ele fazer isso corretamente. Bom, existem 2 tipos de motores que fazem isso:

  • Motores Servos: ótimos motores com torque considerável que geralmente oferecem 180º de movimento. Através do tamanho do pulso elétrico que você envia para o motor, ele posiciona seu eixo no angulo desejável. Na época do projeto, esse motor era caro e difícil de encontrar no Brasil (é um motor muito usado em aeromodelismo), mas hoje você pode comprar ele baratinho na china ( dealextremehttp://www.dealextreme.com/p/mg995-tower-pro-servo-12832 )

Motor Servo

  • Motores de Passo: esse motor não tem controle fino da rotação atual do seu eixo, porem ele gira em passos controláveis. Como assim?!  O motor de passo mais comum é formado por mais ou menos 4 mini-motores, um em cada ponta. Se você por enviando pulsos em ordem em cada ponta, ele vai girando nesse sentido em pulinhos (ou passos). Você vê quantos passos ele tem que dar para dar uma volta completa e você descobre quantos graus ele gira por passo. Diferentemente do motor servo, o motor de passo gira 360º porem não vem com caixa de torque, ou seja, se você quiser que ele tenha torque, você tem que usar um jogo de engrenagens junto ao eixo do motor. É difícil ver motores de passo para vender, mas impressoras antigas e leitor de disquetes costumam ter os seus.

Animação do funcionamento do motor de passo chupinhada do Wikipedia

Nem preciso falar que só arrumei um motor de passo véio de uma impressora véia de alguém da faculdade.  Arrumei um jeito de prender o motor na webcam e fiz um microcontrolador para dar os pulsos no motor na ordem certa e na velocidade que eu queria.

É, microcontrolador. Eu tive que programar um chip para fazer o que eu queria! auhauha O programa para controlar um motor de passo é bem simples, alias existem chips especializados em fazer isso, porem só de saber que eu tinha programado alguma coisa, metido num chip, e isso estar fazendo alguma coisa de útil pra mim, lol, foi um sonho de infância realizado!

Escolhi o microcontrolador PIC para fazer o driver do motor de passo. Queria agradecer meu amigo minero Epitácio por me apresentar o PIC junto com seu maravilho L2Pi (o fabuloso artefato que escreve no ar – veja abaixo). Ele me indicou um compilador em C para PIC e foi fácil através dos exemplos criar um programa que recebia comandos via porta serial e dava o pulso nas perninhas certas do motor de passo, movendo-o para esquerda ou para direita (programa esse gravado no pic no meu próprio gravador de PIC, que montei a partir de um site da web).

L2PI - escrevendo no ar - sucesso nas baladas - construído pelo grande Epitácio!

O difícil mesmo foi fazer o computador conversar com o PIC pela porta serial. Isso porque o PIC “conversava” na serial com pulsos variando de 0 a 5v e o computador “conversa” com pulsos de -11 a 11v. Se ligasse direto o computador no PIC era babau PIC, ele queimava, e um PIC custava uns 20 mangos e eu queimei alguns (e 20 mangos para um liso estudante de faculdade era uma fortuna). Foi ai que o pai dos burros (www.google.com) me ajudou a descobrir que para os dois se conversarem era necessário usar um tradutor, ou seja, um circuito baseado no componente MAX232.

Antes que alguém me pergunte sobre PIC, gravador de PIC, MAX232 (alias, existe alguém que lê isso? lol), hoje eu não usaria nada disso. Eu precisei fazer meu gravador de pic, fazer uma base de execução para o PIC (tinha cristal de clock, uns capacitores locos, um regulador de tensão LM7805), um circuito para o MAX232, usar o compilador de C para PIC que era uma grande bosta, enfim, mó trabalheira. Hoje existe o ARDUINO, uma placa para desenvolvimento de eletrônica que não precisa de gravador, você grava direto na placa usando o cabo USB (meu, que milagre!!), alias, a comunicação pode ser feita via serial por essa mesma porta USB (sim! simula a porta serial extinta dos computadores atuais pela USB!!!!), sem MAX232 crap!, já vem com tudo necessário para rodar, como regulador de tensão, etc, tem uma IDE para desenvolvimento em C cheia de bibliotecas (inclusive uma para motor de passo e servo), ou seja, já tá pronta pra uso, o uso é facilitado e custa barato ( procurem no ebay, cerca de 17 obamis ). Logo, usem ARDUINO!!!

Circuito Controlador da Camera + Gravador de PIC + MAX232 = muito trabalho usar PIC. USEM ARDUINO!!!

Detectando o Movimento


Existem varias maneiras de se detectar movimentos. Dessas, algumas são baseadas em imagens. Dessas, algumas são estupidamente simples (e limitadas). Adivinhem qual eu escolhi?

Antes de tudo, deixa eu fazer umas considerações. Vou considerar (acho que não usei “considerações do jeito certo.. lol) uma imagem uma matriz 2D de pixels e que cada pixel tem seus componentes R,G,B (red, green, blue – vermelho, verde, azul) variando de 0 a 255 (1 byte). Uma imagem em escala de cinza, ao invés de 3 componentes RGB variando de 0 a 255, tem somente um componente variando de 0 (preto) a 255 (branco).

Bom, o argoritmo que eu escolhi é bem simples.  Ele pega duas imagens  em seqüência (I1 e I2) trazidas pela webcam, as transforma em escala de cinza (I1C e I2C) e a subtrai uma das outras (I1C-I2C). O modulo dessa operação ( |I1C-I2C|, isso é, a imagem com os valores sempre positivos. Se o primeiro pixel da imagem I1C tiver o valor 3 e o primeiro pixel da imagem I2C tiver o valor 5, o resultado da subtração vai ser -2. O modulo desse resultado é 2.) gerara uma nova imagem.

Nessa nova imagem ( |I1C-I2C| ), 2 coisas podem acontecer.

  • Se não houve mudança nenhuma entre a imagem I1C e I2C, as duas imagens são iguais. Logo, subtraindo uma da outra gerara somente valores zero (porque os valores dos pixels são iguais em ambas as imagens). Uma imagem formada somente por zeros é uma imagem preta. Essa imagem representa que não houve nenhuma mudança/movimento naquele momento.
  • Caso tenha tido algum movimento entre a imagem I1C e I2C, parte da imagem resultante da subtração vai ser diferente de zero, pois os valores dos pixels na região que hoje mudança/movimento mudaram. Logo, descobrindo onde na imagem resultante da subtração o valor é diferente de zero você descobrirá onde houve movimento.

Algoritmo que move a webcam baseado na detecção do movimento

Uma vez que eu já tinha descoberto como mover a câmera através do computador e como detectar movimento a partir de imagens, faltava somente o código para orquestrar as duas coisas e fazer com que a câmera seguisse o movimento da pessoa.

Fiz o código todo em C++. Para conseguir a imagem da câmera, usei a biblioteca escapi (extreme simple capture API,http://sol.gfxile.net/escapi/index.html tudo muito simple… pq se não tá simples, tá errado!). Enfim, o mesmo pode ser feito em quase qualquer linguagem de programação.

O que fiz foi o seguinte:

  • Comparei o quadro atual com o quadro anterior, pegando o modulo da subtração delas duas (como explicado ai em cima)
  • O certo seria considerar valores maiores que zero como “movimento”. Porem, a webcam comprime as imagens usando JPEG e elas chegam com muito ruído, por isso considerei “movimento” pixels com valores maiores que 30.
  • Peguei a posição máxima e mínima dos pixels com movimento da imagem. Sendo uma imagem uma matriz 2D, cada pixel fica em uma coordenada X,Y. Então achei o pixel com movimento que tinha o maior valor da posição X, depois o maior valor da posição Y, depois o menor valor de X e de Y.
  • Com esses valores ( xMin, xMax, yMin,yMax) eu criei uma caixa, que abrangia todo o movimento da imagem (veja no vídeo da parte de detecção de movimento)
  • Calculei então o centro dessa caixa, que nada mais é que ( xMin + (xMax-xMin)/2) , ( yMin + (yMax-yMin)/2).
  • Se o centro estivesse pro lado direito da imagem, eu mandava a câmera um pouco para a direita. Se o centro estivesse mais pro lado esquerdo da imagem, eu mandava a câmera se mover um pouco esquerda.
  • Obviamente , eu tinha que desligar o algoritmo enquanto a câmera estava se movimentando, senão o programa ia sempre achar que havia movimento. (obviamente também que demorei para perceber isso e só cheguei a essa conclusão depois de muita tentativa e erro. Alias, a não ser que seu erro cause uma catastrofe nuclear, acho que um dos melhores jeitos de se aprender é na tentativa e erro)

Pontos fracos e melhorias

Existem INUMEROS pontos fracos nessa solução. Vamos aos principais:

  • O algoritmo de detecção de movimento é muito rudimentar. Olhem como ele falhava fácil. Eu acendia a luz do quarto e o algoritmo ficava selvagem, pq ele detectava movimento em todo o lugar (já que os valores de todos os pixels mudaram).
  • Tinha que desligar o algoritmo enquanto a câmera estava se mexendo. Se enquanto a câmera estivesse se movendo a pessoas pulasse rapidamente para fora do campo de visão da câmera, ela o perdia. Ou se a pessoa se movimentasse muito rápido, ela se perdia também.
  • O motor de passo era uma merda. Ele era lento e se eu o configurassem para rodar muito rápido, ele não tinha o torque suficiente para mover a câmera. Logo, tinha que desligar o algoritmo por um tempo longo demais, porque além de lerdo, demorava para  a câmera se estabilizar (parar de oscilar, depois de um movimento).

Porem existe algumas melhorias que poderiam deixar todo o esquema BEM melhor, mas que nunca tive tempo de implementar.

  • Usar o motor servo ao invés do motor de passo. Eles são mais rápidos, mais controláveis (você configura o angulo certinho que você quer que seu eixo se mova) e tem torque. Flawless victory!
  • Trocar essa merda de algoritmo de detecção por um mais robusto, como o Optical Flow. O Optical Flow (mais detalhes no pai dos burros – gurgol) calcula a derivada entre a imagem atual e a anterior e calcula para cada pixel um vetor de pra onde a imagem esta se movendo.  O principio continua o mesmo, onde o vetor é maior que 0,0 (porque o vetor é 2D), tem movimento. Porem, se você acende a luz, por exemplo, todos os pixel mudaram da mesma forma e a derivada vai dar o mesmo valor para todos os pixel, ou seja, é fácil detectar que não houve movimento na verdade. Além disso, também é possível detectar movimento mesmo com a câmera em movimento, pq todos os pixels estarão com vetores com a mesma magnitude e onde houver movimento os vetores estarão diferentes. Ou seja, esse tal de optical flow deve resolver vários “pobremas” do projetinho.

Espero com esse relato ajudar vocês a querem brincar com os conceitos desse projetinho multidisciplinar, desde eletrônica para comunicação PC -> microcontrolador até processamento de imagens para detecção de movimento. No que eu puder ajudar, estou disponível.

E por favor, alguém com tempo, tente fazer um algoritmo usando optical flow e motor servo. Gostaria muito mesmo de saber como ia ser!

[ ]s

Odelot

PS:  a pedidos, coloquei a ultima versão do código fonte (ultima que eu achei, hehe) no google code. Não tem como compilar o código, porque ele era parte do meu visualizador de propósito geral (um nó da arvore de visualização). Tem um código do optical flow que eu estava começando a desenvolver mas desisti. Acho que dá para ter uma ideia vendo o código (mal feito, por sinal… na faculdade, a programação sempre é orientada a resultados! lol), o arquivo principal é o motionSeekerNode.cpp . Link pro google code http://code.google.com/p/motionseeker/

Anúncios

3 Comentários

Arquivado em Arduino, Desenvolvimento, Detecção de Movimento, Eletrônica, Geek, PIC, Processamento de Imagens

3 Respostas para “Webcam Detectando Movimento

  1. Bruno

    Olá, gostei muito da explicação. Estou tendo muitas dificuldades em desenvolver meu projeto e gostaria de poder ver o código fonte desse seu projeto. Agradeceria muito.

    • Coloquei o código no googlecode. Você provavelmente vai ter que ligar o modo “leitura de código não documentado”, também conhecido como engenharia reversa. Posso tentar tirar algumas duvidas, se elas não forem muito complicadas, hehe. Mas agora sério, espero que o código te ajude! Abraços, Fábio!

  2. rodrigo born

    Oi, eu gostaria de entrar em contato com você para conversar sobre alguns projetos. vc consegue ler o meu e-mail que é enviado aqui sem ter que publicar? Se conseguir porfavor entre em contato

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s