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.
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](https://www.embarcados.com.br/wp-content/uploads/2017/08/ZonaMonitoradaContador.jpg)
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](https://www.embarcados.com.br/wp-content/uploads/2017/08/DirecaoMovimentoObjeto.jpg)
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](https://www.embarcados.com.br/wp-content/uploads/2017/08/ZonaToleranciaProjetoContador.jpg)
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](https://www.embarcados.com.br/wp-content/uploads/2017/08/FrameSubback.png)
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](https://www.embarcados.com.br/wp-content/uploads/2017/08/FrameDelta.png)
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)](https://www.embarcados.com.br/wp-content/uploads/2017/08/FrameOriginal.png)
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:
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:
![GIF animado do projeto em ação](https://www.embarcados.com.br/wp-content/uploads/2017/08/GIFAnimadoProjetoContagem.gif)
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:
- 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
- http://www.pyimagesearch.com/2016/04/18/install-guide-raspberry-pi-3-raspbian-jessie-opencv-3/
- http://www.pyimagesearch.com/2015/05/25/basic-motion-detection-and-tracking-with-python-and-opencv/
- https://github.com/LukashenkoEvgeniy/People-Counter
Este texto foi originalmente publicado em: https://www.embarcados.com.br/objetos-opencv-e-python-raspberry-pi/