Hoje vamos aprender como detectar linhas e círculos em uma imagem, com a ajuda de uma técnica chamada Hough transform.
O que é o espaço Hough?
Antes de começarmos a aplicar a transformação Hough às imagens, precisamos entender o que é um espaço Hough, e aprenderemos isso no caminho de um exemplo.
Espaço parâmetro
Quando trabalhamos com imagens, podemos imaginar a imagem sendo uma matriz 2d sobre algumas coordenadas x e y, sob a qual uma linha poderia ser descrita como y = mx + b
Mas no espaço parâmetro, que vamos chamar de espaço Hough, eu posso representar essa mesma linha como m
vs b
, então a caracterização de uma linha no espaço da imagem, será um único ponto na posição m-b
no espaço Hough.
Mas temos um problema, com y = mx + b
, não podemos representar uma linha vertical, pois a inclinação é infinita. Portanto, precisamos de uma melhor parametrização, coordenadas polares (rho e theta).
>
Espaço difícil
- rho: descreve a distância da linha à origem
- theta: descreve o ângulo a partir da horizontal
Uma observação muito importante, no entanto, é o que acontece quando tomamos múltiplos pontos em torno de uma linha, e nos transformamos no nosso espaço Hough.
Um único ponto no espaço da imagem traduz-se numa curva no espaço Hough, com a particularidade de que os pontos entre uma linha no espaço da imagem serão representados por múltiplas curvas com um único ponto de toque.
E este será o nosso alvo, encontrando os pontos onde um grupo de curvas se intersecta.
O que é Hough transform?
Hough transform é um método de extração de características para detectar formas simples como círculos, linhas, etc em uma imagem.
A característica “simples” é derivada pela representação da forma em termos de parâmetros. Uma forma “simples” será representada apenas por alguns parâmetros, por exemplo uma linha pode ser representada pela sua inclinação e interceptação, ou uma circunferência que pode ser representada por x, y e raio.
No nosso exemplo de linha, uma transformada Hough será responsável por processar os pontos na imagem e calcular os valores no espaço Hough.
O algoritmo para fazer a transformação acontecer e posteriormente encontrar as curvas de interseção é um pouco complicado, e portanto fora do escopo deste post. No entanto vamos dar uma olhada em uma implementação deste algoritmo, que é parte da biblioteca do OpenCV.
Detecting lines using OpenCV
No OpenCV, a detecção de linha usando Hough Transform é implementada nas funções HoughLines
e HoughLinesP
(Probabilistic Hough Transform). Vamos focar neste último.
A função espera os seguintes parâmetros:
-
image
: imagem de fonte binária de 8 bits, monocanal. A imagem pode ser modificada pela função. -
lines
: Vector de saída de linhas. Cada linha é representada por um vector de 4 elementos (x_1, y_1, x_2, y_2) , onde (x_1,y_1) e (x_2, y_2) são os pontos finais de cada segmento de linha detectado. - >
rho
: Resolução da distância do acumulador em pixels. -
theta
: Resolução angular do acumulador em radianos. -
threshold
: Parâmetro de limiar do acumulador. Somente são retornadas aquelas linhas que obtêm votos suficientes -
minLineLength
: Comprimento mínimo da linha. Segmentos de linha mais curtos que esses são rejeitados. -
maxLineGap
: Abertura máxima permitida entre pontos na mesma linha para ligá-los.
>
Demasiado complicado? é mais fácil com um exemplo:
# Read image img = cv2.imread('lanes.jpg', cv2.IMREAD_COLOR)# Convert the image to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Find the edges in the image using canny detectoredges = cv2.Canny(gray, 50, 200)# Detect points that form a linelines = cv2.HoughLinesP(edges, 1, np.pi/180, max_slider, minLineLength=10, maxLineGap=250)# Draw lines on the imagefor line in lines: x1, y1, x2, y2 = line cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 3)# Show resultcv2.imshow("Result Image", img)
>E aqui está o resultado:
É muito importante que nós realmente utilizemos uma imagem apenas de borda como parâmetro para a Transformada Hough, caso contrário o algoritmo não funcionará como pretendido.
Detecting circles using OpenCV
O processo vai mais ou menos como para as linhas, com a excepção de que desta vez vamos usar uma função diferente da biblioteca do OpenCV. Vamos usar agora HoughCircles
, que aceita os seguintes parâmetros:
-
image
: 8-bit, single-channel, grayscale input image. -
circles
: Vetor de saída de círculos encontrados. Cada vector é codificado como um vector de 3 elementos de ponto flutuante (x, y, raio) . -
circle_storage
: Na função C este é um armazenamento de memória que conterá a sequência de saída dos círculos encontrados. -
method
: Método de detecção a usar. Atualmente, o único método implementado é CV_HOUGH_GRADIENT , que é basicamente 21HT -
dp
: Relação inversa entre a resolução do acumulador e a resolução da imagem. Por exemplo, se dp=1 , o acumulador tem a mesma resolução que a imagem de entrada. Se dp=2 , o acumulador tem a metade da largura e altura. -
minDist
: Distância mínima entre os centros dos círculos detectados. Se o parâmetro for muito pequeno, vários círculos vizinhos podem ser falsamente detectados, além de um verdadeiro. Se for muito grande, alguns círculos podem não ser detectados. -
param1
: Primeiro parâmetro específico do método. No caso do CV_HOUGH_GRADIENT , é o limite superior dos dois passados para o detector de borda Canny() (o mais baixo é duas vezes menor). -
param2
: Segundo parâmetro específico do método. No caso do CV_HOUGH_GRADIENT , é o limiar do acumulador para os centros do círculo na fase de detecção. Quanto menor for, mais falsos círculos podem ser detectados. Círculos, correspondentes aos valores maiores do acumulador, serão retornados primeiro. -
minRadius
: Raio mínimo do círculo. -
maxRadius
: Raio máximo do círculo.
Lembre que os parâmetros precisam ser diferentes, pois não podemos descrever um círculo com a mesma parametrização que usamos para as linhas, e em vez disso, precisamos usar uma equação como (x - x0)^^2 + (y - y0)^^2 = r^^2
.
E para o código:
# Read image as gray-scaleimg = cv2.imread('circles.png', cv2.IMREAD_COLOR)# Convert to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Blur the image to reduce noiseimg_blur = cv2.medianBlur(gray, 5)# Apply hough transform on the imagecircles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, img.shape/64, param1=200, param2=10, minRadius=5, maxRadius=30)# Draw detected circlesif circles is not None: circles = np.uint16(np.around(circles)) for i in circles: # Draw outer circle cv2.circle(img, (i, i), i, (0, 255, 0), 2) # Draw inner circle cv2.circle(img, (i, i), 2, (0, 0, 255), 3)
Nota que comparada com o exemplo anterior, não estamos aplicando aqui nenhuma função de detecção de bordas. Isto porque a função HoughCircles
tem embutida a detecção de canny.
E o resultado:
Conclusão
Hough Transform é uma excelente técnica para detectar formas simples em imagens e tem várias aplicações, desde aplicações médicas como raios-x, análise de tomografia computadorizada e ressonância magnética, até carros auto-conduzidos. Se você está interessado em saber mais sobre o espaço Hough, eu recomendo que você realmente execute o código, tente configurações diferentes por si mesmo, e que você verifique a documentação do OpenCV para informações adicionais.
Possibilidades de leitura!