ÍNDICE DE CONTEÚDO
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.
Embarcados Experience 2024: Evento Presencial
Participe do Embarcados Experience 2024 em São Paulo. Conhecimento técnico, palestras, workshops e oportunidade de networking com profissionais experientes.
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.
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.
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.
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.
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.
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.
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:
1 2 3 4 |
cd ~ git clone https://github.com/phfbertoleti/ContadorObjetosEmMovimento cd ContadorObjetosEmMovimento python ContadorObjetosEmMovimento.py |
Demonstração – GIF Animado
Segue uma demonstração do projeto em ação, no formato de GIF animado:
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:
- 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);
- 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;
- 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
Referências
Alguém aí tem um projeto em que o python identifica quantos elementos estão se movimentando e conte o total deles?
Muito bom mesmo, saberia dizer se há alguma biblioteca, ou algo do tipo que reconheça por exemplo se o objeto é um carro? Estou iniciando na parte de visão computacional e estou com este projeto em mente. Abraços
Bom dia Fernando, também estou iniciando em visão computacional, com relação a tua pergunta primeiramente você precisa definir alguns pontos, por exemplo, os carros estarão parados ou em movimento? Você quer apenas reconhecer que existe um carro ou definir um carro específico? Pois pra realizar o reconhecimento podem ser usadas algumas técnicas como, definição por tamanho, por cor, por tipo do contorno, etc.
Amigo, perdão a demora, mas teria alguma bibliografia que sugeres?
Bom dia Pedro, quando rodo seu codigo, recebo o seguinte erro:
$ python ContadorObjetosEmMovimento.py
Traceback (most recent call last):
File “ContadorObjetosEmMovimento.py”, line 50, in
height = np.size(Frame,0)
File “/usr/lib/python2.7/dist-packages/numpy/core/fromnumeric.py”, line 2700, in size
return asarray(a).shape[axis]
IndexError: tuple index out of range
Saberia me dizer o que e? Estou usando Raspbian Strecth mais novo e OpenCv 3.3.0 .
Consegui resolver meu erro Pedro, ajustando parte do codigo para funcionar tambem com a camera do proprio raspberry ao inves de uma webcam. Se for util para voce, aqui esta o codigo alterado, tomei a liberdade tambem de adicionar algumas novas funcionalidades, como fechar o programa por uma tecla ou por tempo, escrever os resultados em um arquivo e tambem envia-lo por email. Grande abraco! https://pastebin.com/f5Z4eBC7
Olá Pedro, li todo o tutorial porem quando compilo o código fonte no meu Raspberry o seguinte erro é printado na tela: ” File “ContadorObjetosEmMovimento.py”, line 20
return 1
^
TabError: inconsistent use of tabs and spaces in indentation
“
Eu estou tentando testar no meu computador usando Jupyter Notebook e aparece o mesmo.
consegui corrigir esses erros usando a mesma quantidade de espaços na indentação.
exemplo
if (condições): #4 barras de espaços aqui ou 1 tab
instruçoes # 8 espaços aqui ou 2 tabs
o importante para o erro TabError sair é que no bloco if, na indentação seja apenas com espaços ou apenas com tabulações, daí o erro some
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.
Guilherme, fico muito feliz com seu feedback! Este é justamente um de meus objetivos (tornar as coisas simples de entender), e é gratificante saber que estou cumprindo-o.
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.
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
Igor, boa tarde.
Você seguiu o procedimento de instalação do OpenCV por completo, incluindo isntalação do numpy?
Atenciosamente,
Pedro Bertoleti
Sim, segui. Estou utilizando o módulo Python picamera e não uma câmera USB. Seria esse o problema?
Olha, não sei dizer, pois nunca usei uma camera diferente de USB. Você teria disponibilidade de testar com uma camera USB?
Eu testei com uma camera USB e surgiu o seguinte erro: https://uploads.disquscdn.com/images/ceab5d43185b0a586d936fdf50301c39cd09a1855dfcf3051296e9a29a2d269f.png
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 640×480, mesma resolução que utilizei no desenvolvimento. Baixe por favor o código-fonte novamente e teste.
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 https://www.pyimagesearch.com/2016/01/04/unifying-picamera-and-cv2-videocapture-into-a-single-class-with-opencv/
Agora está funcionando!
Opa, boas noticias! Desculpa não ter conseguido responder sua pergunta, eu nunca trabalhei com este modulo pi camera.
Fico feliz que deu certo!
Parabéns pelo projeto, código de fácil entendimento! Tentei rodar na minha Raspberry (para testar o openCV) e apareceu o seguinte erro:
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
Juraci, muito obrigado pela leitura e elogios!
Vi seu e-mail agora, acabei de responder. Por favor, de uma olhada.
Parabéns cara, muito bem explicado! Já tive a oportunidade de trabalhar em um projeto parecido e acho essa área muito legal.
Diego, muito obrigado