본문 바로가기
OpenCV/OpenCV-Chapter

CH06 Opencv-Python Hough Transform(직선, 원)

by kiimy 2021. 12. 19.
728x90

허프 변환(Hough transform) 직선 검출이란?

• 2차원 영상 좌표에서의 직선의 방정식을 파라미터(parameter)

  공간으로 변환하여 직선을 찾는 알고리즘

그림2. 하나의 점(좌표)로 표현할 수 있다(a,b의 파라미터 공간)

결국에는 y= a'x+b'에서의 한 점은 b= -xa+y에서의 한점을 지나는데 
이 점은 y= a'x+b'에 들어가는 파라미터 값

구체적으로 보자면 

축적배열(accumulation array)

- 직선 성분과 관련된 원소 값을 1씩 증가시키는 배열

a', b' = 3을 구해서 y = a'x+b'으로 표현할 수 있다.

직선의 방정식 y = ax + b 를 사용할 때의 문제점

- y 축과 평행한 수직선을 표현하지 못함

==> 극좌표계 직선의 방정식을 사용(Hough Transform)

why?

a = 0 이면 수평선은 만들 수 있지만 수직선은 a가 무한대로 가야하기때문 표현 불가능

y= ax+b를 극좌표계로, p=rho

cv2.HoughLinesP(image, rho, theta, threshold, lines=None, 
				minLineLength=None, maxLineGap=None) -> lines
P = Probability 확률
'''
rho = 축적 배열에서 rho 값의 간격 -> 1pixel 간격 (보통 1을 준다)
theta = 축적 배열에서 theta 값의 간격 -> 1 theta 간격(보통 np.pi/180)
line= 선분의 시작과 끝 좌표(x1, y1, x2, y2) 정보를 담고 있는 numpy.ndarray.
      shape=(N, 1, 4). dtype=numpy.int32. 시작점(x1,y1) 끝 점(x2,y2)
minLineLength: 검출할 선분의 최소 길이
maxLineGap= 조명으로인해 edge가 끊어질 수 있음 해당 부분을 얼마정도의 값으로 이어줄거냐
==> 5로 설정한다면 5pixel이 떨어져 있어도 하나의 직선으로 만들어주겠다.
값이 작으면 확실한 선만 판단하게됨
'''

# Hough Transform
src = cv2.imread('ch06\\images\\building.jpg', cv2.IMREAD_GRAYSCALE)

# 노이즈 제거 후 진행하는 것이 더 선명한 line 검출
src = cv2.GaussianBlur(src, (0,0), 1)

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=10)    
# print(lines.shape) shape=(N, 1, 4)

dst = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)

if lines is not None:
    for i in range(lines.shape[0]): # 직선의 개수
        # shape=(N, 1, 4)
        pt1 = (lines[i][0][0], lines[i][0][1]) # 시작점 좌표(x,y)
        pt2 = (lines[i][0][2], lines[i][0][3]) # 끝점 좌표(x,y)

        # dst에 line 표시
        cv2.line(dst, pt1, pt2, (0,0,255), 2, cv2.LINE_AA)

축적배율이 높으면 = 시간이 오래걸리지만 정교함(rho = 0.5, theta = np.pi / 360

축적배율이 낮으면 = 빠르지만 정밀함이 떨어짐

Canny, HoughLinesP


허프 변환

- 허프 변환을 응용하여 원을 검출할 수 있음

원의 방정식: (𝑥 − 𝑎)²+(𝑦 − 𝑏)²= 𝑐² => 3차원 축적 평면?

원의 중심 : (a, b)

반지름 : c

--------------------> 시간이 오래걸림

So, 속도 향상을 위해 Hough gradient method 사용

-> line의 경우는 xy공간에서 ab공간으로 변환을 했지만 Circle은 변환이 없음

 입력 영상과 동일한 2차원 평면 공간에서

축적 영상(축적배열)을 생성

• 에지 픽셀에서 Gradient 계산

• 에지 방향에 따라 직선을 그리면서 값을 누적

• 원의 중심을 먼저 찾고, 적절한 반지름을 검출

 

 

How?
*원의 중심*
==> 검출된 하나의 점에서 노란색(Edge)방향에 수직인 Gradient 방향으로 선을 긋는다
       이 작업을 반복하다보면 가운데 점에 값들이 계속 누적이 됨

*반지름*
구해진 원의 중심에 대해서 원을 적당히 키워가면서 원주와 많이 맞나는지 판단함
if 과녁처럼 안에 원이 여러개 존재하는 동심원 경우에

 단점

* 여러 개의 동심원을 검출 못함

=> 가장 작은 원 하나만 검출됨

cv2.HoughCircles(image, method, dp, minDist, circles=None, param1=None,
				param2=None, minRadius=None, maxRadius=None) -> circles
'''
image: 입력 영상. (에지 영상이 아닌 일반 영상) = 내부에서 Canny를 진행함
method: OpenCV 4.2 이하에서는 cv2.HOUGH_GRADIENT만 지정 가능 'ALT'도 쓸 수 있음
dp = 입력영상과 축적배열의 크기 비율
minDist = 검출된 원 중심점들의 최소 거리
circles =(cx, cy, r) 정보를 담은 numpy.ndarray. shape=(1, N, 3), dtype=np.float32
             ==> 중심점 좌표 두개와 반지름 np.float32
param1 = canny edge 검출기의 높은 임계값
param2 = 축적배열에서 원 검출을 위한 임계값== 높게 주냐 안주냐에 따라 원을 검출
'''

'''
Opencv 4.3version
method = cv2.HOUGH_GRADIENT_ALT 사용하면 더 좋은 원을 검출 
but 파리마터 설정값이 다르니 docu 참고
'''

# 입력 이미지 불러오기
src = cv2.imread('ch06\\images\\dial.jpg')

if src is None:
    print('Image open failed!')
    sys.exit()

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# 가우시안 사용하면 더 원을 잘 검출
blr = cv2.GaussianBlur(gray, (0, 0), 2.0)

def onChange(pos):
    minr = cv2.getTrackbarPos('minRadius', 'img')
    maxr = cv2.getTrackbarPos('maxRadius', 'img')
    thresh = cv2.getTrackbarPos('threshold', 'img')

    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50,
                               param1=120, param2=thresh, minRadius=minr, maxRadius=maxr)
    
    # print(circles) (cx, cy, r) shape=(1, N, 3)
    dst = src.copy()
    if circles is not None:
        for i in range(circles.shape[1]):
            # numpy.float32
            cx, cy, radius = circles[0][i] # [0]: [[[]]] => [[]]각 circle 접근
            # circle ==> int(center, radius)
            cv2.circle(dst, (int(cx), int(cy)), int(radius), (0,0,255), 2, cv2.LINE_AA)

    cv2.imshow('img', dst)

# trackbar(사용할 함수 파라미터 지정)
cv2.namedWindow('img')
cv2.createTrackbar('minRadius', 'img', 0, 100, onChange)
cv2.createTrackbar('maxRadius', 'img', 0, 150, onChange)
cv2.createTrackbar('threshold', 'img', 0, 100, onChange)
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()

OpenCV: Feature Detection

cv2.HOUGH_GRADIENT_ALT 방법

 

OpenCV: Feature Detection

param2Second method-specific parameter. In case of HOUGH_GRADIENT, it is the accumulator threshold for the circle centers at the detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator va

docs.opencv.org

 

728x90

댓글