Contagem de objetos em movimento com OpenCV e Python usando Raspberry Pi

A visão computacional é fantástica. Com este recurso, um sistema computacional "aprende a enxergar" e, com isso, fazer tarefas cada vez mais complexas e úteis ao dia a dia moderno, tais como: identificar objetos, identificar pessoas e/ou faces, reconhecer objetos e obter características deles, determinar movimento de objetos, mensurar velocidade de objetos, e por aí vai. E o sistema computacional em questão pode ser uma Single-Board Computer comum, como uma Raspberry Pi, por exemplo.

 

Neste artigo será mostrado um uso da Raspberry Pi em visão computacional: com base no OpenCV e Python, permitir contagem de objetos em movimento utilizando visão computacional.

 

 

Material necessário

 

Para reproduzir este projeto, serão necessários os seguintes materiais:

  • Uma Raspberry Pi 3B (com cartão de tamanho recomendado de 16GB);
  • Uma webcam qualquer*;
  • Fonte de alimentação para Raspberry Pi.

* Pode ser qualquer modelo, desde que seja compatível com o Linux rodando na Raspberry Pi.

 

 

Montagem

 

Assumindo que você já tem controle sobre a Raspberry Pi (seja por acesso via VNC, SSH ou por um teclado e mouse nela conectados), a montagem do hardware é muito simples: basta ligar a webcam USB a uma das portas USB da Raspberry Pi.

 

 

Preparação: Instalação do OpenCV na Raspberry Pi

 

Antes de prosseguir com o projeto, é necessário instalar o OpenCV na Raspberry Pi. No caso, isto envolverá compilar o OpenCV na Raspberry (processo um tanto quanto lento, mas que garante máximo desempenho deste na Raspberry Pi). Para isso, recomendo fortemente seguir o tutorial deste link.

 

Observação: apesar do tutorial recomendar a utilização de um virtual environment (para "isolar" o OpenCV das demais dependências e bibliotecas do Python no Linux), eu não segui este conselho. Escolhi esta opção pois, no meu "quadro geral de bibliotecas", convém ter disponível a OpenCV para todo o sistema. Ou seja, foi uma opção baseada no meu uso pessoal. Portanto, se você quiser não utilizar um virtual environment (e fazer como eu fiz), sinta-se à vontade.

 

 

Overview do projeto

 

O projeto em questão se trata de um contador de objetos em movimento, com contagens independentes para objetos saindo e entrando na zona monitorada. Veja a figura 1.

 

OpenCV e Python - Zona monitorada e suas definições
Figura 1 - Zona monitorada e suas definições

 

 

Procedimento de contabilização

 

O incremento da contagem se dá quando o centróide do objeto (será explicado a seguir a definição deste) cruza uma das linhas de referência. Portanto, não importa o tamanho do objeto e tão pouco sua forma, a contabilização será feita sem mais problemas.

 

A contabilização de objetos é feita com base em duas características:

  • Somente objetos em movimento serão detectados;
  • A contabilização ocorre somente se o objeto cruzar uma das linhas de referência;
  • A direção do movimento importa.

 

Ambas as características combinadas permitem contabilizar não só o número bruto de objetos que passaram, mas sim quantos objetos entraram e saíram da zona monitorada.

 

OpenCV e Python - Detecção de direção do movimento de um objeto com base nas linhas de referência
Figura 2 - Detecção de direção do movimento de um objeto com base nas linhas de referência

 

 A contabilização de objetos em movimento será feita da seguinte forma:

  • Contabilização de objetos que entraram na zona monitorada: tudo que cruza a linha azul, vindo a partir da linha vermelha (ou seja, está entre as linhas de referência, mas cruza a linha azul), será contabilizado como entrada da zona de monitoramento. Esta situação está evidenciada na Figura 2.a.
  • Contabilização de objetos que entraram na zona monitorada: analogamente, qualquer objeto que cruza a linha vermelha, vindo da linha azul (ou seja, está entre as linhas de referência, mas cruza a linha vermelha), será contabilizado como saída da zona de monitoramento. Esta situação está evidenciada na Figura 2.b.

 

Importante:

 

A captura de frames do stream da câmera e processamento de imagem de cada frame logo em seguida faz com que a captura de frames não seja em tempo real. Isso significa que alguns quadros podem ser ignorados / perdidos. Isso leva a uma conclusão ruim: há chances de que o exato quadro do centróide do objeto cruzando uma das linhas de referência seja perdido, afetando assim a contagem e funcionamento do projeto como um todo. Para minimizar este problema, foi adotada uma solução de tolerância de cruzamento das linhas de referência.

 

Tal tolerância consiste em uma faixa de 2 pixels, para cima e para baixo da linha de referência. Se o centróide estiver nessa zona de tolerância, é considerado que este está cruzando a linha de referência em questão. Observe a figura 3.

 

Zona de tolerância (onde é considerado que o centróide está cruzando uma linha de referência
Figura 3 - Zona de tolerância (onde é considerado que o centróide está cruzando uma linha de referência

 

 

Raciocínio do projeto para a detecção de objetos em movimento

 

Conforme explicado no tópico anterior, tem-se um raciocínio claro de como detectar que um objeto cruzou uma linha de referência e, além disso, saber sua direção. Agora, será visto como detectar um objeto em movimento. As etapas a seguir são executadas no processamento/tratamento de imagens na ordem que são apresentadas.

 

Realce do objeto em movimento

 

Na física, para classificar se algo está em movimento ou não, deve-se adotar uma referência. Aqui, o princípio é o mesmo: para saber se há movimento, compara-se um frame capturado recentemente com um frame-referência. Esta comparação consiste em um recurso de visão computacional chamado subtração de background. Este recurso consiste em eliminar o máximo possível de informações irrelevantes da imagem (o fundo dela, por exemplo, daí o "background" do nome) e realçar características desejadas. Cada aplicação tem um método adequado de subtração de background, pois cada aplicação vai querer realçar uma determinada característica da imagem.

 

No caso deste projeto, a subtração de background é feita da forma mais intuitiva possível: uma imagem em escala de cinza é, basicamente, um grande array bidimensional. Portanto, quaisquer operações matemáticas pixel-a-pixel (ou qualquer outro tipo de operação matricial) podem ser feitas. Considerando isso, dois frames (um capturado recentemente e um frame-referência) são convertidos para escala de cinza, sofrem ação do filtro Gaussian Blur (para suavizar contornos, o que permitirá deixar objetos mais "uniformes" nas etapas subsequentes de tratamento/processamento de imagem) e subtraídos (ponto-a-ponto / pixel-a-pixel), de modo que somente o que variou de um frame para outro seja realçado. Isso acontece pois, como objetos em movimento vão, obrigatoriamente, causar variações em relação ao frame-referência, e portanto estes serão realçados pela subtração de background. Sendo assim, após a subtração do background, idealmente ficará em destaque somente o objeto em movimento. É importante ressaltar aqui que, na prática, devido às características de iluminação e da captura de frames da câmera utilizada, praticamente nunca dois frames terão resultados perfeitos da subtração do background (somente com o objeto em movimento realçado). 

 

Um frame com o background subtraído e o movimento realçado em cores mais claras pode ser visto na figura 4.

 

Frame com background subtraído e objeto em movimento realçado por cores mais claras
Figura 4 - Frame com background subtraído e objeto em movimento realçado por cores mais claras

 

Binarização

 

Normalmente em visão computacional, após realçar as características desejadas, bizariza-se a imagem. Isso é feito pois, matematicamente, é muito mais simples se trabalhar com imagens binarizadas (de apenas 2 valores possíveis de cor), levando a uma redução drástica de demanda de processamento do hardware que executa o projeto/análise. Aqui, isto não é diferente.

 

O único ponto de atenção aqui é o threshold (limite do valor de cor do pixel, que vai de 0 a 255 em escala de cinza, para se atribuir valor preto ou branco). Infelizmente, este é fortemente dependente da iluminação do local. Portanto, muito provavelmente este valor deve ser ajustado, de caso para caso. Na figura 5, ve-se o frame após binarização.

 

Imagem após binarização com threshold adequado
Figura 5 - Imagem após binarização com threshold adequado

 

Dilatação

 

Os objetos em movimento até aqui detectados, suavizados e binarizados já estão quase na sua forma ideal para se trabalhar. O "quase" se caracteriza por haver possibilidade de existirem "buracos" no meio de objetos (ou seja, objetos que não são uma "massa" de imagem). Se existirem, estes buracos prejudicarão o posterior processamento/tratamento de imagem, aumentando as chances de erros na identificação de contornos (etapa posterior). Isso ocorre pois, em uma imagem com buracos, podem ser detectados contornos falsos (ou contornos dentro de contornos), afetando assim a contagem final de objetos em movimento.

 

Para eliminar esta possibilidade, é feito o processo de dilatação. No final deste processo, os objetos serão uma "massa" única de pixels de uma só cor.

 

Procura por contornos (e seu centróide)

 

Neste ponto, temos o objeto realçado / bem definido e sem "buracos" / falhas (ou seja, objetos aqui são uma "massa" de pixels de uma só cor). Essa "massa" de pixels, em visão computacional, é chamada de contorno. 

 

Agora, o que é feito é a detecção dos contornos da imagem (= objetos em movimento) considerando suas áreas (em pixels²). Desta detecção, é feita a obtenção das coordenadas e dimensões de retângulos que "cercam" os objetos. Uma vez em posse destes dados, o centro deste retângulo equivalerá ao centróide do objeto em questão. Portanto, temos aqui uma vantagem: todo o movimento do objeto pode ser analisado pelo movimento de seu centróide.

 

Outra grande vantagem é que, por considerarmos apenas o centróide do objeto na análise de seu movimento, não importa no algoritmo de contagem nem o tamanho tão pouco a forma do objeto em questão. 

 

Na figura 6, ve-se em preto o centróide do objeto em movimento e, em verde, o retângulo que envolve o contorno do objeto detectado.

 

Frame colorido, com destaque para centróide do objeto em movimento (em preto) e do retângulo que envolve o contorno do objeto detectado (em verde)
Figura 6 - Frame colorido, com destaque para centróide do objeto em movimento (em preto) e do retângulo que envolve o contorno do objeto detectado (em verde)

 

Análise da posição do centróide de cada objeto em relação às linhas e referência

 

Uma vez analisando a trajetória do objeto através de seu centróide, basta comparar a coordenada Y do mesmo com as coordenadas Y das linhas de referência e, com base nisso, aplicar o algoritmo explicado no tópico "Procedimento de contabilização". Desta forma, contabiliza-se quem entrou e saiu da zona monitorada.

 

Com isso, chega-se ao fim do processamento de imagens do projeto.

 

 

Código-fonte

 

Para visualizar o código-fonte do projeto, acesse-o no meu GitHub clicando aqui. Por favor, atente-se aos comentários antes de utilizar o projeto.

 

Para clonar o repositório e utilizar / rodar o projeto, utilize os comandos abaixo:

 

 

 

Demonstração - GIF Animado

 

Segue uma demonstração do projeto em ação, no formato de GIF animado:

 

GIF animado do projeto em ação
GIF animado do projeto em ação

 

 

Melhorias

 

Assim como todo projeto de visão computacional, este projeto pode estar sujeito a erros. Por esse motivo, é importante sempre melhorar o código (processo que chamo aqui de melhoria contínua) e deixá-lo menos sujeito a variações e características irrelevantes das imagens. Portanto, listo aqui alguns pontos de melhoria contínua deste projeto:

  1. O funcionamento do mecanismo de tolerância de cruzamento de linha de referência funciona corretamente se o fluxo de objetos for ininterrupto (ou seja, não haver chance do objeto "parar" sobre a tolerância). Portanto, melhorar este mecanismo seria muito importante, dependendo de seu uso (sugestão: marcar o objeto contado, de forma que não seja contado novamente);
  2. Melhorar o processo de dilatação e Blur, de modo que o sistema dependa menos do valor de threshold de binarização e valor mínimo da área de objeto detectado;
  3. Melhoria de tempo de processamento de imagens para minimizar perda de quadros (sugestão: reduzir o tamanho da imagem antes de trabalhar com a mesma).

 

 

Aprenda mais

 

Aplicação de visão computacional com OpenCV

Visão Computacional com OpenCV na Intel Edison

OpenCV 2.4.9 + QT5 no Ubuntu

Instalando SimpleCV no Ubuntu

 

 

Referências

Sou engenheiro eletricista formado pela Faculdade de Engenharia de Guaratinguetá (FEG – UNESP) e trabalho com desenvolvimento de sistemas embarcados em São Paulo capital.
Curioso e viciado em tecnologia, sempre busco me aprimorar na área de eletrônica e programação, em especial em desenvolvimento de firmware (área que mais gosto de trabalhar e estudar).

Para mais informações, acesse minha página no Facebook:https://www.facebook.com/pbertoleti

Deixe um comentário

21 Comentários em "Contagem de objetos em movimento com OpenCV e Python usando Raspberry Pi"

Notificar
avatar
Ordenar por:   recentes | antigos | mais votados
Guilherme Chiqueti
Visitante
Guilherme Chiqueti

Você conseguiu fazer um texto de forma que até um iniciante em visão computacional como eu conseguisse entender. Parabéns pelo nível de explicação e formatação do texto.

Julio Calvo
Visitante
Julio Calvo

Hola.
Cómo vocé hizo para no instalar el entorno virtual?
Yo he tratado de instalar OpenCV siguiendo el tutorial del link pero siempre tengo errores.
No estoy seguro si estoy ignorando los comandos correctos.
Ya he intentado 15 veces, con diferentes maneras, pero no logro instalar OpenCV en mi Raspberry Pi.
Gracias.

igorgallon76
Membro
igorgallon76

Parabéns pelo projeto! Muito bem explicado, fácil de entender! Porém quando fui testar na minha Raspberry (para testar o openCV), surgiu o seguinte erro: https://uploads.disquscdn.com/images/4a007049e133e5e2d9a1dc222939900666ed4e8df5359d5ccc9d9db188253b8d.png

phfbertoleti
Visitante
phfbertoleti

Igor, boa tarde.

Você seguiu o procedimento de instalação do OpenCV por completo, incluindo isntalação do numpy?

Atenciosamente,
Pedro Bertoleti

igorgallon76
Membro
igorgallon76

Sim, segui. Estou utilizando o módulo Python picamera e não uma câmera USB. Seria esse o problema?

phfbertoleti
Visitante
phfbertoleti

Olha, não sei dizer, pois nunca usei uma camera diferente de USB. Você teria disponibilidade de testar com uma camera USB?

igorgallon76
Membro
igorgallon76
phfbertoleti
Visitante
phfbertoleti

Isso tá com cara que, por alguma razão, a resolução da sua câmera é um número ímpar, seja na altura e/ou largura.
Acabei de atualizar o código no github forçando a resolução da câmera para 640x480, mesma resolução que utilizei no desenvolvimento. Baixe por favor o código-fonte novamente e teste.

igorgallon76
Membro
igorgallon76

Essa configuração não funcionou também. Eu andei procurando e achei que o problema estaria no "cv2.VideoCapture" que não reconhece bem o módulo pi camera. Então fiz um workaround seguindo esse tutorial http://www.pyimagesearch.com/2016/01/04/unifying-picamera-and-cv2-videocapture-into-a-single-class-with-opencv/

Agora está funcionando!

phfbertoleti
Visitante
phfbertoleti

Opa, boas noticias! Desculpa não ter conseguido responder sua pergunta, eu nunca trabalhei com este modulo pi camera.

Fico feliz que deu certo!

igorgallon76
Membro
igorgallon76

Parabéns pelo projeto, código de fácil entendimento! Tentei rodar na minha Raspberry (para testar o openCV) e apareceu o seguinte erro:

Juraci Nascimento
Visitante
Juraci Nascimento

Parabéns Pedro, muito bom o artigo. Estou tentando utilizar ele como teste no Python p/ windows e estou tendo alguns problemas para rodar. No entanto gostaria de ter o seu contato pois tenho uma aplicação que estou desenvolvendo e acredito que seria interessante contratar sua assessoria. Obrigado

phfbertoleti
Visitante
phfbertoleti

Juraci, muito obrigado pela leitura e elogios!

Vi seu e-mail agora, acabei de responder. Por favor, de uma olhada.

Diego Andrade
Visitante
Diego Andrade

Parabéns cara, muito bem explicado! Já tive a oportunidade de trabalhar em um projeto parecido e acho essa área muito legal.

phfbertoleti
Visitante
phfbertoleti

Diego, muito obrigado

Weder Iglesias
Visitante
Weder Iglesias

fera parabens pela materia.. no caso.. uso o cubianboard.. sera que consigo fazer o mesmo?
outra coisa gostaria de saber se é possivel eu conseguir contar uma quantidade de itens em duas cameras e dai o que tiver mais itens executar um comando..

phfbertoleti
Visitante
phfbertoleti

Weder, boa noite.

Primeiramente, muito obrigado! Quanto a sua placa, você quis dizer Cubieboard, certo?
Olha, imagino que não haja problemas. Eu nunca utilizei uma, mas não vejo porque não funcionar. Os únicos gargalos que imagino seriam o processamento (se a placa for single-core, vai ficar lento) e memória RAM (se a placa tiver 512MB ou mais, funciona tranquilo).

Quanto a aplicação que perguntou, eu nunca utilizei duas câmeras na mesma placa, portanto não sei responder. Mas em duas placas diferentes, isso é possível de ser feito sim.

Atenciosamente,
Pedro Bertoleti

Weder Iglesias
Visitante
Weder Iglesias

oi isso mesmo.. eu sempre confundo o nome do sistema operacional Debian para Cubieboard.. rsrs
entao ele é dual core com 2gb de memoria..
no caso preciso contar determinados objetos de uma camera e comparar na outra quantas tem .. a que tiver maior quantidade ele executa uma funcao..

phfbertoleti
Visitante
phfbertoleti

Eu faria o seguinte: tentaria rodar o projeto na placa e veria a performance. Se ficar bom, pode-se pensar em pesquisar em como utilizar duas webcams na mesma placa para sua ideia. O que acha?

Haroldo Amaral
Membro
Haroldo Amaral

Excelente artigo Pedro, parabéns!

phfbertoleti
Visitante
phfbertoleti

Haroldo, muito obrigado!

wpDiscuz