HoughCircles — Detecção de círculos em imagens com OpenCV e Python

Rodrigo Estevam
Turing Talks
Published in
6 min readMay 9, 2021

Bem vindo, caro leitor!

Este é o segundo, e ultimo, texto de uma minissérie sobre Transformada Hough aqui no Turing Talks. Se você não leu o primeiro, você deveria, já que lá explicamos toda a teoria por trás da transformada e aqui focaremos em sua aplicação. Conceitos já explicados serão retomados e usados sem serem explorados novamente, então dê uma pausa aqui e antes leia o artigo: Transformada Hough — Detectando formas geométricas em imagens.

O objetivo deste texto é explorar os usos da função cv2.HoughCircles(). Entenderemos como utilizá-la de forma eficiente em diversos tipos de imagens.

Todo os exemplos serão em Python, mas como o OpenCV também está disponível em outras linguagens, a “tradução” deve ser simples.

cv2.HoughCircles()

Antes de qualquer outra coisa é legal entender qual o funcionamento interno do detector. Inicialmente vamos listar os argumentos da função e entender o uso de cada um deles. Você sempre pode olhar na documentação oficial, mas tramemos aqui uma descrição traduzida e simplificada:

image: a imagem em grayscale na qual se deseja detectar os círculos.

method: técnica a ser aplicada para detecção. Por enquanto o OpenCV somente tem o método cv2.HOUGH_GRADIANT. Para mais informações sobre esse método e um benchmark com alguns outros baseados em Transformada Hough, leia este paper.

dp: razão inversa da resolução do acumulador em relação a imagem. Esse é o parametro mais confuso de entender, mas normalmente você deixará ele entre 1 e 2 (pelo menos foi o que experenciei pessoalmente). O que é importante você gravar é que quanto maior esse valor, mais sensivel o detector é, podendo causar falsos positivos. Já se ele for muito baixo, alguns circulos podem não ser detectados.

minDist: a distância mínima entre dois círculos. Infelizmente essa função não consegue detectar círculos concêntricos. Se este argumento não for corretamente ajustado é possível que falsos círculos sejam detectados, por isso é importante entender qual a distância ideal no caso da sua imagem. Um exemplo básico de que esse parâmetro está muito pequeno é quando uma circunferência está sendo detectada mais de uma vez.

param1: segundo parâmetro do Canny interno que a função aplica antes do método em si, o primeiro parâmetro é param1/2.

Se você não sabe o que é Canny, você deveria ler nosso excelente artigo sobre. Basicamente este é um filtro de bordas. Como vimos no texto de Transformada Hough, a técnica se baseia em percorrer a imagem em busca de pontos que possam pertencer a borda de uma circunferência, então esse filtro é indispensável para preparar a imagem para essa tarefa.

param2: limite do acumulador. Como dito no texto passado, os círculos são guardados no acumulador e aqueles com “maior confiabilidade” tem maior “score” na lista. Este argumento age como pontuação mínima que um círculo tem que ter para ser detectado. Se param2 for muito baixo podem haver falsas detecções. Já se ele for muito alto, podem ocorrer falhas em círculos “verdadeiros”.

minRadius e maxRadius: limites para o tamanho dos círculos a serem detectados. Esses parâmetros não são obrigatórios, mas se você tiver uma estimativa dos tamanhos do círculos que quer encontrar eles podem prevenir falsas detecções fora desta faixa.

Ao final, a função retorna circles: uma lista com (x,y,raio) dos círculos encontrados.

Pré processamento

Para melhor performance do nosso detector é aconselhável aplicar algum processamento na imagem para facilitar a tarefa dele.

Quanto mais as circunferências estiverem em destaque melhor, pois sua detecção será mais fácil. Além disso, quanto menos detalhes não relevantes na imagem menos processamento é necessário (o que aumenta a velocidade de execução) e maior acurácia é atingida.

Exemplo: detector de rodas

Imagine agora que você precisa contar o número de automóveis que passam por algum local, por exemplo um pedágio, como faria?

Foto de Red John no Unsplash

Uma opção é treinar um modelo de Rede Neural para detectar veículos (como uma YOLO) e contá-los com ela. Entretanto, treinar um modelo não é uma tarefa fácil e com certeza não é algo rápido. Adicionalmente, você teria que ter um computador (ou controlador) que aguentasse rodar a rede a um framerate (quadros por segundo) decente.

Outra opção é contar as rodas dos veículos, deste modo saberemos quantos veículos passaram por lá. Essa técnica não precisa de treinamento, como uma Rede Neural, e é muito mais leve, o que a torna mais viável para vídeos.

Então, será a segunda abordagem que teremos aqui!

Esse exemplo está disponível no meu Github, caso você queira ter acesso ao código (e me doar uma estrelinha). Mas faremos o passo a passo aqui também.

De inicio, precisamos saber o tipo de imagem que iremos analisar. Por isso escolhi a foto de uma moto para ajustar o nosso detector:

Como rodas normalmente são pretas, não há muito tratamento de cores a ser feito. Partiremos então direto para o greyscale:

A imagem ainda possui muitos detalhes que são irrelevantes para a nossa detecção. Por isso teremos uma etapa de pré-processamento que diminuirá a complexidade da mesma:

Aplicamos um Blur Gaussiano, que borra a imagem, para eliminar pequenos detalhes que não nos servirão. Em seguida fizemos duas Transformações Morfológicas, essas são filtros que, novamente, ajustam a forma do nosso objeto para simplificá-lo. Você pode ver mais sobre elas no tutorial do OpenCV, eu pessoalmente sempre o consulto quando preciso de uma transformação dessas.

Como última etapa do pré-processamento iremos apagar tudo que não é preto da imagem. Como as rodas geralmente são pretas não há a necessidade de analisar outras cores (ou tom de cinza)

Esta ultima imagem já está pronta para ser usada na função mas vamos fazer um passo extra: aplicar o Canny nela para entendermos como a imagem vai ser recebida pela Transformada Hough. Isso também nos ajuda a ajustar o param1, que vai no Canny interno da função.

É importante que as rodas não tenham muitos detalhes internos, visto que isso pode “confundir”o detector. Mas, como visto na imagem, as rodas estão suficientemente destacadas e há poucos elementos para gerar incerteza à função.

Hora da verdade

Já ajustamos o param1 quando experimentamos com o Canny acima e os parâmetros minDist, minRadius e maxRadius podem ser obtidos intuitivamente olhado para a imagem, já que as rodas normalmente tem tamanhos parecidos e uma distância mínima entre elas.

Falta ajustar o parâmetro dp e param2. Esses dois podem ser encontrados por tentativa e erro, já que sabemos mais ou menos o comportamento deles.

Após alguma experimentação o melhor ajuste de parâmetros foi:

que encontrou os seguintes círculos:

[[[605.55    374.55002  66.39   ]
[194.15001 374.55002 68.04 ]]]

Para saber se esses são realmente os círculos que correspondem as rodas podemos desenhá-los na imagem original:

Yeah, temos o nosso detector!

Chamando novamente a função com esses parâmetros em outras imagens parecidas também deve gerar um resultado satisfatório. Só não esqueça de submeter as novas imagens ao mesmo pré-processamento.

Espero que esse artigo e pequeno exemplo te ajude pra quando for precisar aplicar a cv.HoughCircles(). Novamente, você pode baixar um JNotebook com o código lá no meu repositório do GitHub.

Para mais textos sobre Visão Computacional e IA em geral, continue ligado no Turing Talks. E para mais informações sobre o Turing USP siga a gente nas redes sociais: Facebook, Linkedin, Instagram, e servidor no Discord!

--

--

Rodrigo Estevam
Turing Talks

Aluno de Engenharia Elétrica na Poli-USP Membro do Grupo Turing de IA da USP