허프 변환(Hough transform) 직선 검출이란?
에지(윤곽선) 영상은 검정색 배경에 에지부분만 흰색이다. 이 입력 영상에서 에지가 있는 좌표를 추출하는 것이다.
에지 영상에서 좀 더 고차원적인 정보를 얻기 위해 에지 영상의 분포를 보고 직선, 사각형 등 고차원적인 형태 정보를 추출한다.
직선을 추출해내는 가장 유명한 알고리즘이 허프 변환이다. 축적 알고리즘을 이용하여 내가 원하는 파라미터를 찾아낸다.
허프 변환 직선 검출은 2차원 영상 좌표에서의 직선의 방정식을 파라미터(parameter) 공간으로 변환하여 직선을 찾는 알고리즘이다.
축적 배열 accumulation array
축적 배열은 직선 성분과 관련된 원소 값을 1씩 증가시키는 배열이다.
x, y 좌표평면에서의 직선의 한 점을 a, b 좌표평면으로 옮기면 직선으로 표현된다. 이 직선이 지나가는 행렬의 값을 1씩 증가시키는 것이다.
이 작업을 반복하면 최종적으로 한 점에서 값이 높아지게 된다.
직선의 방정식 y = ax + b 를 사용할 때의 문제점
직선의 방정식 y = ax + b를 이용하면 y축과 평행한 수직선을 표현하지 못한다. 따라서 극좌표계 직선의 방정식을 이용한다.
$$xcos\theta + ysin\theta = \rho$$
극좌표계 직선의 방정식을 파라미터 공간으로 변환하면 로우와 세타 좌표평면으로 표현할 수 있다.
이 경우에 직선이 아닌 곡선으로 표현되는데, 곡선이 한 점에서 뭉치게 되며이 뭉치는 곳이 로우와 세타의 값이 된다.
✔︎ 허프 변환에 의한 선분 검출 함수 - cv2.HoughLines
캐니 함수로 윤곽선을 검출한 값을 cv2.HoughLines에 입력 값으로 설정하면 직선 파라미터 정보를 담고 있는 결과값을 반환한다.
lines = cv2.HoughLines(image, rho, theta, threshold, lines=None,
srn=None, stn=None, min_theta=None, max_theta=None)
- image : 입력 에지 영상
- rho : 축적 배열에서 rho 값의 간격.
(e.g.) 1.0 → 1픽셀 간격 - theta : 축적 배열에서 theta 값의 간격.
(e.g.) np.pi / 180 → $1^{\circ}$ 간격 - threshold : 축적 배열에서 직선으로 판단할 임계값
- lines : 직선 파라미터(rho, theta) 정보를 담고 있는 numpy.ndarray.
shape=(N, 1, 2). dtype=numpy.float32. - srn, stn : 멀티 스케일 허프 변환에서 rho 해상도, theta 해상도를 나누는 값. 기본값은 0이고, 이 경우 일반 허프 변환 수행.
- min_theta, max_theta : 검출할 선분의 최소, 최대 theta 값
로우값과 세타값의 간격은 축적 배열을 만들 때 로우 값 해당 축과 세타 해당 축의 크기를 몇으로 설정할 것인지를 의미한다.
이 값을 작게 하면 축적 배열이 커진다. (연산이 오래 걸리지만 정교) 간격을 크게 하면 축적배열은 작아진다. (연산은 빠르지만 정밀도가 떨어진다.)
✔︎ 확률적 허프 변환에 의한 선분 검출 - cv2.HoughLinesP
허프 변환과의 차이점은 결과값 lines의 파라미터가 다르다.
확률적 허프 변환 함수는 직선의 시작과 끝 정보를 제공한다.
lines = cv2.HoughLinesP(image, rho, theta, threshold,
lines=None, minLineLength=None, maxLineGap=None)
- image: 입력 에지 영상
- rho: 축적 배열에서 rho 값의 간격.
(e.g.) 1.0 → 1픽셀 간격 - theta: 축적 배열에서 theta 값의 간격.
(e.g.) np.pi / 180 → $1^{\circ}$ 간격 - threshold: 축적 배열에서 직선으로 판단할 임계값
- lines: 선분의 시작과 끝 좌표(x1, y1, x2, y2) 정보를 담고 있는 numpy.ndarray.
shape=(N, 1, 4). dtype=numpy.int32. - minLineLength: 검출할 선분의 최소 길이
- maxLineGap: 직선으로 간주할 최대 에지 점 간격
주의할 점은 maxLineGap 인자이다.
디폴트 값은 0이지만 0보다 큰 값을 주는 것이 좋다. 조명이나 외부 변수에 의해 픽셀값이 변화되서 직선이 끊길 수 있다.
0은 한 픽셀 떨어져도 끊는데 5는 5픽셀 정도 떨어져도 직선을 붙여준다. 따라서 5이상 줘야 한다.
💬 확률적 허프 변환 직선 검출 예제
import sys
import numpy as np
import cv2
src = cv2.imread('building.jpg', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
edges = cv2.Canny(src, 50, 150)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180., 160,
minLineLength=50, maxLineGap=5)
dst = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
if lines is not None:
for i in range(lines.shape[0]):
pt1 = (lines[i][0][0], lines[i][0][1]) # 시작점 좌표
pt2 = (lines[i][0][2], lines[i][0][3]) # 끝점 좌표
cv2.line(dst, pt1, pt2, (0, 0, 255), 2, cv2.LINE_AA)
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
⬇︎
'OpenCV' 카테고리의 다른 글
[OpenCV with Python] 이진 영상 처리 : 영상의 이진화 - cv2.threshold (0) | 2022.02.04 |
---|---|
[OpenCV with Python] 영상의 특징 추출 : 영상에서 원 검출하기 - 허프 변환 (0) | 2022.02.04 |
[OpenCV with Python] 영상의 특징 추출 : 캐니 에지 검출 cv2.Canny (1) | 2022.02.04 |
[OpenCV with Python] 영상의 특징 추출 : 그래디언트와 에지 검출 (0) | 2022.02.04 |
[OpenCV with Python] 영상의 특징 추출 : 영상의 미분과 소벨 필터 (0) | 2022.02.04 |