edge 탐지 글에서 언급한 것처럼, 객체 인식을 위해서는 배경과 전경을 분할하는 것이 필요하다. 영상 분할은 경계선이나 영역으로 분할하는데, 임계값(threshold)는 cv2.threshold()를 활용하면 된다. Hough 변환은 Computer Vision에서 사용하는 용어로, 선분 등의 추출을 위해 평면 상에 투영하는 것을 의미한다.

 

필터 처리 2 - 에지 탐지

에지 탐지란 배경과 전경을 분리하는 작업이며, 객체 인식을 위해서는 에지 탐지가 필수적이다. 이전 글에서 살펴본 블러링이 영상을 흐릿하게 했다면, 에지 탐지를 통해 경계에 있는 pixel만 골�

dsbook.tistory.com

 

1. Canny Edge 검출

필터 처리 2 - 에지 탐지 글에서는 Sobel, 라플라시안 필터 등을 통해 검출했다. Canny Edge는 임계값을 어떻게 지정하느냐에 따라 경계를 검출할 대상을 조정할 수 있기 때문에 사용되는 빈도가 높다.

import cv2
import numpy as np

img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

edge1 = cv2.Canny(img, 50, 100)
edge2 = cv2.Canny(img, 150, 200)

cv2.imshow('edge1', edge1)
cv2.imshow('edge2', edge2)
cv2.waitKey()
cv2.destroyAllWindows()

좌측은 50~100 사이, 우측은 150~200 사이의 RGB pixel에 대해 각각 Canny edge를 적용한 것이다. Canny Edge를 얻는 cv2.Canny()는 입력 영상, 최소 threshold, 최대 threshold, [edge 결과 배열, 커널 크기, 그래디언트 크기 계산 방법]을 인자로 사용한다. 커널은 Sobel 필터로 적용되며, 그래디언트 크기 계산 방법은 True(경사각), False(맨해튼 거리)에 따라 달라진다.

 

2. Hough 변환에 의한 검출

edge 탐지 글에서 살펴본 Sobel 필터 등이나 cv2.Canny()로 검출한 edge는 단순한 pixel의 집합이기 때문에 원이나 다각형의 구조를 가지지는 않는다. 하지만 Hough 변환을 이용하면 직선이나 원과 같은 형태의 edge를 검출할 수 있다.

Hough 변환의 종류는 다음과 같다.

cv2.HoughCircles() 모든 원 검출
cv2.HoughLines() 모든 선 검출
cv2.HoughLinesP() 확률적 선 검출

 

1) cv2.HoughLines()

import cv2
import numpy as np

img = cv2.imread('chess.jpg')
draw = img.copy()
gray = cv2.cvtColor(draw, cv2.COLOR_BGR2GRAY)

row, col = draw.shape[:2]
edge = cv2.Canny(gray, 50, 100)

# Hough line 검출
line = cv2.HoughLines(edge, 1, np.pi/180, 130)
for l in line:
    r, theta = l[0] # 거리, 각도
    cos = np.cos(theta)
    sin = np.sin(theta)
    x = cos * r
    y = sin * r
    
    # 시작점, 끝점 계산
    x1= int(x - col * sin)
    x2= int(x + col * sin)
    y1= int(y + row * cos)
    y2= int(y - row * cos)
    
    cv2.line(draw, (x1, y1), (x2, y2), (0,255,0), 3)
cv2.imshow('Original', img)
cv2.imshow('image', draw)
cv2.imshow('edge', edge)
cv2.waitKey()
cv2.destroyAllWindows()

기존 이미지(좌) / Canny Edge(중) / Hough 검출(우)

cv2.HoughLines()는 입력 영상, 거리 해상도, 각도 해상도, 임계값, [검출 결과, 거리 약수, 각도 약수, 최소각, 최대각]이 인자로 사용된다. 거리 해상도는 0~1 사이의 값, 각도 해상도에는 0~180 사이의 값만 적용된다. 거리 및 각도 약수는 default로 0이 지정되며, 선 검출에서는 사용되지 않고 multi scale에서 사용된다.

 

2) cv2.HoughLinesP()

cv2.HoughLines()를 사용하면 모든 직선을 찾아내기 때문에 많은 연산이 필요하며, 이를 개선한 것이 확률적인 Hough 변환이다.

# 확률적 선 검출
import cv2
import numpy as np


img = cv2.imread('chess.jpg')
draw = img.copy()
gray = cv2.cvtColor(draw, cv2.COLOR_BGR2GRAY)

edge = cv2.Canny(gray, 50, 100)
line = cv2.HoughLinesP(edge, 1, np.pi/180, 20, None, 50, 2)

for l in line:
    x1, y1, x2, y2 = l[0]    
    cv2.line(draw, (x1, y1), (x2, y2), (0,255,0), 3)

cv2.imshow('image', draw)
cv2.waitKey()
cv2.destroyAllWindows()

cv2.HoughLinesP()는 입력 영상, 거리 해상도, 각도 해상도, 임계값, [검출된 선 좌표, 선으로 인식할 최소 길이, 선으로 인식할 최대 간격]이 인자로 사용된다. 확률적으로 선을 검출하기 때문에 cv2.HoughLines()를 사용했을 때보다 선이 적게 검출되지만, 사용하는 이미지에 따라 원하는 결과를 그대로 얻지 못할 수도 있다.

 

3) cv2.HoughCircles()

cv2.HoughCircles()는 직교좌표를 극좌표로 바꿔서 원을 검출해낸다.

import cv2
import numpy as np

img = cv2.imread('coin.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 블러 처리를 통한 noise 제거
blur = cv2.GaussianBlur(gray, (3,3), 0)

# Hough Circle 검출
circle = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1.2, 30, None, 200)
if circle is not None:
    circle = np.uint16(np.around(circle))
    for i in circle[0,:]:
        cv2.circle(img, (i[0], i[1]), i[2], (0,255,0), 2)
        cv2.circle(img, (i[0], i[1]), 2, (0,0,255),5)

cv2.imshow('image', img)
cv2.waitKey()
cv2.destroyAllWindows()

cv2.HoughCircles()로 원을 검출하기 위해서 입력 영상, 검출 방식, 입력 영상과 경사 누적 해상도의 반비례율, 원 중심 간의 최소 거리, [검출된 원 결과, threshold 최대 값, 경사도 누적 경계 값, 원의 최소 반지름, 원의 최대 반지름]이 인자로 필요하다. 입력 영상과 경사 누적 해상도의 반비례율이 1일 때 입력과 동일하며, 값이 커질수록 부정확해진다. 원 중심 간의 최소 거리로 0을 입력하면 error가 발생한다. 경사도 누적 경계 값은 작을수록 잘못된 원을 검출하게 된다.

 


*Image Reference

https://commons.wikimedia.org/wiki/File:Chaturanga_board_and_pins.png?uselang=ko

https://ko.wikipedia.org/wiki/%EC%A3%BC%ED%99%94

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기