1. 이미지 매칭
1.1 평균 해시 매칭(Average Hash Matching)
■ 영상/이미지 매칭은 서로 다른 두 영상/이미지를 비교해서 같은 형태의 객체가 있는지 찾는 것을 말한다.
■ 매칭 방법은 먼저, 이미지에서 객체를 찾기 위해 이미지의 의미 있는 특징들을 숫자로 변환한다. 이 숫자를 특징 벡터 혹은 특징 디스크립터라고 한다.
■ 그리고 이 특징 벡터를 이용해 두 영상/이미지의 유사도를 측정해서 얼마나 비슷한지 판단하다.
■ 평균 해시 매칭은 가장 원시적인 매칭 방법으로, 여기서 해시는 이미지의 특징을 모은 데이터(특징 벡터)를 의미한다.
■ 이 방법은 객체의 윤곽을 해시 테이블로 나타낸 다음, 이 해시 테이블을 이용해서 다른 이미지와 매칭을 하는 것이다.
■ 그러므로 평균 해시 매칭을 수행하기 위해서는 먼저 이미지에서 특징 벡터를 구해야 한다. 평균 해시 매칭은 특징 벡터를 구하기 위해 평균값을 사용하며, 특징 벡터를 구하는 방법은 다음과 같다,
- (1) 이미지의 가로&세로 비율 상관없이 특정 크기로 축소
- 해시 테이블 생성을 빠르게 하기 위해 특정 크기로 이미지를 축소한다.
- (2) 픽셀 전체의 평균값을 계산해서 각 픽셀 값이 평균값보다 작으면 0, 크면 1로 변환 (0 또는 1로 변환하기 때문에 2진수 숫자로 볼 수 있다.)
- 변환된 0과 1로 객체의 윤곽을 서로 비교하는 것이기 때문에, 비교하고자 하는 두 이미지를 같은 크기로 축소해야 한다.
img = cv2.imread('555.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 그레이스케일
## 16x16 크기로 축소
gray = cv2.resize(gray, (16,16))
## 픽셀 전체의 평균값을 계산 - 이 평균값을 0, 1 분류를 위한 기준점으로 사용
avg = gray.mean()
## 평균값을 기준으로 0과 1로 변환
bin = 1 * (gray > avg)
print(avg)
print(f'bin.shape : {bin.shape}, dtype : {bin.dtype}')
print(bin)
```#결과#```
89.9765625
bin.shape : (16, 16), dtype : int32
[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0]
[0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0]
[0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1]
[0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0]
[0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1]
[0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1]
[0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0]
[0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0]
[0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0]
[0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1]
[0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0]
[0 0 0 0 0 1 0 1 1 1 1 1 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]]
````````````
- 예시로 사용한 555.jpg는 이미지 가운데 부분의 픽셀 값은 255에 가깝고, 가운데 부분을 제외한 가장자리 부분은 0(검은색)에 가깝다. 그렇기 때문에 해시 테이블에서 밝은 부분은 1, 어두운 부분은 0으로 나타난다.
- 해시 테이블의 원소 1의 외곽을 연결하면 555.jpg의 객체 윤곽이 그려진다.
- 해시 테이블의 dtype은 32(4 bit)이고 한 행에는 원소가 16개이므로, 한 행은 4 x 16만큼의 메모리를 차지하게 되어 비효율적이다.
- 그러므로 다음과 같이 2진수 문자열을 16진수 문자열로 변환해서 사용하는 것이 좋다.
## 2진수 문자열을 16진수 문자열로 변환
dhash = []
for row in bin.tolist(): # bin이 numpy 배열일 때, bin.tolist()는 해당 배열을 동일한 데이터를 가진 Python 리스트 변환
s = ''.join([str(i) for i in row])
print(s)
dhash.append('%02x'%(int(s,2)))
dhash = ''.join(dhash)
print(f'dhash : {dhash}')
```#결과#```
0000000000000000
0000000011100000
0000000011111000
0000000111111000
0000000111111111
0000011111111110
0001111111111111
0001111111111101
0011111111111100
0011111111111110
0011111111111100
0000111111111111
0000111111111110
0000010111110000
0000000000000000
0000001000000000
dhash : 00e0f81f81ff7fe1fff1ffd3ffc3ffe3ffcfffffe5f000200
````````````
- 최종적으로 생성된 해시 값은 16진수 문자열로 나타난 것을 확인할 수 있다. 이 dhash라는 해시 값이 이미지의 대표적인 특징을 요약한 값으로 이미지 매칭 및 검색에 활용된다.
- 16진수 문자열로 변환하지 않는다면, 16 x 16 크기의 모든 픽셀을 비교해야 하므로 효율성이 떨어진다.
■ 16진수로 변환한 평균 해시를 다른 이미지와 비교하기 위해서는 해밍 거리를 사용해야 한다.
■ 해밍 거리는 두 개의 길이가 같은 문자열 사이의 거리로, 같은 길이의 두 문자열이 주어졌을 때, 같은 위치에 있는 서로 다른 문자들의 개수를 구하여 거리로 표시하는 방법이다.
- 즉, 해밍 거리를 사용하면 서로 다른 두 개의 평균 해시가 얼마나 유사한 자릿수가 많은지에 따라 매칭 결과(유사도)가 결정된다.
- 예를 들어 s1과 s2가 길이가 8인 문자열일 때, 같은 위치에 있지만 서로 다른 문자들의 개수가 3개라면 s1과 s2의 해밍 거리는 3이다.
- 그러므로 해밍 거리를 이용해서 유사도를 측정했을 때, 결과가 0에 가까울수록 두 이미지는 서로 비슷하다고 볼 수 있다.
import glob
img = cv2.imread('555.jpg')
search_dir = '../opencv/' # 비교할 이미지들이 있는 경로
## 이미지를 16x16 크기의 평균 해쉬로 변환하는 함수
def img2hash(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (16, 16))
avg = gray.mean()
bi = 1 * (gray > avg)
return bi
## 해밍거리 측정 함수
def hamming_distance(a, b):
a = a.reshape(1,-1)
b = b.reshape(1,-1)
distance = (a !=b).sum() # 같은 위치에 있지만 서로 다른 문자들의 총 개수 == 해밍 거리
return distance
query_hash = img2hash(img) # 555.jpg의 해쉬
img_path = glob.glob(search_dir+'/*.jpg') # opencv 파일에 있는 모든 jpg 이미지
- 이 예시는 opencv 파일에 있는 jpg 중에 555.jpg와 유사한 이미지를 찾기 위한 코드이다.
for path in img_path:
img = cv2.imread(path) # 555.jpg와 비교할 jpg
a_hash = img2hash(img)
dst = hamming_distance(query_hash, a_hash) # 해밍 거리 산출
if dst/256 < 0.10: # 해밍 거리 10% 이내만
print(path, dst/256)
cv2.imshow(path, img)
```#결과#```
['../opencv\\000.jpg', '../opencv\\004846465.jpg', ...,
...,
...,
'../opencv\\winter2.jpg', '../opencv\\yate.jpg']
../opencv\555.jpg 0.0
../opencv\777.jpg 0.09765625
````````````
- 해밍 거리를 이용해 유사도를 측정한 결과, 해밍 거리 10% 내에서 555.jpg와 비슷한 jpg로 777.jpg를 찾은 것을 볼 수 있다.
img_555 = cv2.imread('555.jpg')
img_777 = cv2.imread('777.jpg')
plt.subplot(121),plt.imshow(img_555[:,:,::-1]),plt.title('555.jpg'),plt.axis('off')
plt.subplot(122),plt.imshow(img_777[:,:,::-1]),plt.title('777.jpg'),plt.axis('off')
plt.show()
1.2 템플릿 매칭(Template Matching)
■ 템플릿 매칭은 찾고자 하는 객체가 담긴 작은 영상/이미지(이를 템플릿이라 함)를 이용해서, 템플릿과 매칭되는 위치를 찾는 방법이다.
■ 더 정확히는 그 객체가 포함되어 있을 것이라고 예상되는 입력 영상/이미지 전 영역에 걸쳐 템플릿을 이동시키며 서로 얼마나 유사한지를 계산함으로써 템플릿과 가장 비슷한 위치를 수치적으로 찾아낸다.
- 이때 템플릿 이미지는 비교 대상이 되는 이미지보다 크기가 항상 작아야 한다.
■ 템플릿 매칭의 동작 방식은 다음 그림과 같다.
- 위의 그림은 얼굴 영역을 템플릿으로 사용하여 템플릿 매칭을 수행하는 과정이다.
- 왼쪽 그림과 같이 템플릿을 입력 이미지 전체 영역에 대해 이동하면서 템플릿 이미지와 입력 이미지의 유사도 또는 비유사도를 계산한다.
- 유사도를 계산할 경우 템플릿 영상과 비슷한 부분에서 값이 작게 나타나며, 비유사도를 계산할 경우 템플릿 영상과 비슷한 부분에서 값이 작게 나타난다.
- 가운데 그림에서 가장 밝은 픽셀의 위치가 템플릿 이미지와 가장 유사한 부분이며, 이 위치를 빨간색 사각형으로 표시한 결과가 오른쪽 그림이다.
■ OpenCV에서 cv2.matchTemplate(img, templ, method, result, mask) 함수를 사용해서 템플릿 매칭을 수행할 수 있다.
- img는 입력 이미지(템플릿 이미지와 비교할 이미지)
- templ은 템플릿 이미지
- method는 템플릿 매칭 비교 방법으로 다음과 같은 방법들이 있다.
method | 의미 |
cv2.TM_SQDIFF | 제곱 차 매칭 방법 |
cv2.TM_SQDIFF_NORMED | 정규화된 제곱 차 매칭 방법 |
cv2.TM_CCORR | 상관관계 매칭 방법 |
cv2.TM_CCORR_NORMED | 정규화된 상관관계 매칭 방법 |
cv2.TM_CCOEFF | 상관계수 매칭 방법 |
cv2.TM_CCOEFF_NORMED | 정규화된 상관계수 매칭 방법 |
참고) https://docs.opencv.org/4.x/d4/dc6/tutorial_py_template_matching.html
- cv2.TM_SQDIFF는 제곱 차 매칭 방법으로, 두 영상/이미지가 완벽히 일치하면 0, 일치하지 않으면 0보다 큰 양수를 갖는다. 따라서 불일치 정도가 커질수록 큰 값을 갖게 된다.
- cv2.TM_CCORR은 상관관계 매칭 방법으로, 두 영상/이미지가 유사하면 큰 값, 유사하지 않으면 작은 값이 나오게 된다.
- cv2.TM_CCOEFF는 상관계수 매칭 방법으로, 비교할 두 영상/이미지를 미리 평균 밝기로 보정한 후 상관관계 매칭을 수행하는 방식이다. 두 영상/이미지가 유사하면 큰 양수가 나아고, 유사하지 않으면 0에 가까운 양수 또는 음수가 나오게 된다.
- 정규화 수식이 추가된 cv2.TM_CCORR_NORMED는 매칭 결과를 0에서 1 사이의 실수, cv2.TM_CCOEFF_NORMED는 매칭 결과를 -1에서 1 사이의 실수로 나타내며, 두 방법 모두 결과가 1에 가까울수록 매칭이 잘 되었음을 의미한다.
- 매칭 방법 중 정규화된 상관계수 매칭 방법이 좋은 결과를 제공하는 것으로 알려져 있다.
- cv2.matchTemplate( )의 결과로 비교 결과를 저장한 (W-w+1) x (H-h+1) 크기의 result 행렬(2차원 배열)이 반환된다.
- 여기서 W, H는 입력 이미지의 너비와 높이, w와 h는 템플릿 이미지의 높이와 너비이다.
■ cv2.matchTemplate( ) 함수의 매칭 방법으로 제곱 차 매칭 방법을 사용하면, result 행렬에서 최솟값 위치가 가장 매칭이 잘 된 위치이고, 상관관계 또는 상관계수 매칭 방법을 사용한 경우 result 행렬에서 최댓값 위치가 가장 매칭이 잘 된 위치이다.
■ 최솟값 또는 최댓값 위치는 OpenCV의 cv2.minMaxLoc(src, mask) 함수를 이용하여 손쉽게 구할 수 있다.
- src는 1 채널 이미지
- cv2.minMaxLoc( ) 함수는 최솟값, 최댓값뿐만 아니라 최솟값과 최댓값의 좌표도 반환한다.
img = cv2.imread('wolf.jpg')
template = cv2.imread('wolf_template.jpg')
methods = ['cv2.TM_CCOEFF' , 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR_NORMED',
'cv2.TM_CCORR','cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED'] # 6가지 방법 모두 사용
for m in methods:
img_copy = img.copy()
method = eval(m)
res = cv2.matchTemplate(img_copy,template,method) # 템플릿 매칭
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 최솟값, 최댓값, 최솟값 좌표, 최댓값 좌표
print(m, min_val, max_val, min_loc, max_loc)
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]: # SQDIFF는 최솟값이 좋은 매칭
top_left = min_loc
match_val = min_val
else: # 나머지는 최댓값이 좋은 매칭
top_left = max_loc
match_val = max_val
## 매칭 좌표 구해서 초록색 사각형 표시
height, width, channels = template.shape
bottom_right = (top_left[0]+width, top_left[1]+height)
cv2.rectangle(img_copy, top_left, bottom_right, (0,255,0),6)
fig, ax = plt.subplots(1, 3, figsize=(12, 4))
ax[0].imshow(img[:, :, ::-1])
ax[0].set_title('original')
ax[0].axis('off')
ax[1].imshow(res, cmap='gray')
ax[1].set_title('{}\ntemplate matching map'.format(m))
ax[1].axis('off')
ax[2].imshow(img_copy[:, :, ::-1])
ax[2].set_title('template detection')
ax[2].axis('off')
plt.tight_layout()
plt.show()
print(match_val);print()
- 결과를 보면 SQDIFF 방법은 최솟값의 위치가 템플릿 이미지와 가장 유사한 부분이기 때문에 템플릿 매칭 결과인 중간 그림을 보면 늑대의 머리 부분이 가장 진한 검은색으로 표시되는 것을 볼 수 있다.
- 나머지 방법들은 가장 밝게 나타나는 위치가 템플릿 이미지와 가장 유사한 부분이다.
- 매칭에 실패한 CCORR 방법 외의 나머지 방법들의 경우 늑대의 머리 부분이 가장 밝게 나타나는 것을 볼 수 있다.
2. 특징과 키 포인트(Keypoints)
■ 템플릿 매칭의 단점은 이미지 전체를 대상으로 템플릿을 비교하는 방법이기 때문에, 비교 이미지와 템플릿 이미지는 거의 비슷한 모양을 가지고 있어야 한다. 즉, 이미지의 크기가 다르거나 회전된 이미지, 방향이 다른 이미지에는 효과가 없다.
■ 이런 경우에는 이미지의 특징점(keypoints)을 찾아야 한다. 특징점은 코너처럼 한 점의 형태로 표현할 수 있는 특징을 말하며, 특징점은 키 포인트 또는 관심점이라 부르기도 한다.
■ 코너를 사용하는 이유는 다음 그림처럼 가로 또는 세로 엣지나 평탄한 영역은 고유한 특징이 없고 모호한 부분이 많다. 그러나 코너에는 고유한 특징(예를 들어 뾰족하게 튀어나와 있는 건물)이 있기 때문에 코너를 이용하면 기하학적 변환이 있어도 물체를 인식할 수 있다.
2.1 해리스 코너 검출(Harris Corner Detection)
■ 위의 오른쪽 그림처럼 영상/이미지의 특정 픽셀 위치에 코너가 존재하면, 코너 주변 픽셀과 코너의 픽셀 값에는 차이(더 정확히는 \( x, y \)축 방향으로 픽셀 값 차이(밝기 차이))가 존재할 것이다.
■ 해리스 코너 검출은 이 원리를 이용한 것으로 특정 위치 \( (x, y) \)에서 \( \Delta x, \Delta y \)만큼 떨어진 픽셀과의 밝기 차이를 계산한다. \( E(\Delta x, \Delta y) = \sum_{x, y} w(x, y) \left[ I(x + \Delta x, y + \Delta y) - I(x, y) \right]^2 \)
- \( w(x, y) \)는 가우시안 형태의 가중치
■ \( E(\Delta x, \Delta y) \) 값이 크다면 \( x, y \)축 모든 방향으로 밝기 차이가 큰 것이기 때문에 \( (x, y) \) 위치를 코너로 볼 수 있다. 하지만, 코너가 아님에도 밝기 차이가 크면 \( E(\Delta x, \Delta y) \) 값이 크게 나타날 수도 있다.
■ 해리스 코너 검출은 \( E(\Delta x, \Delta y) \)가 모든 방향으로 값이 크게 나타나는지 확인하기 위해 \( E(\Delta x, \Delta y) \) 수식에서 해리스 코너 응답 함수 \( R = \mathrm{Det}(\mathbf{M}) - k \cdot \mathrm{Tr}(\mathbf{M})^2 \)를 유도하였다.
■ 여기서 k는 해리스 코너 검출 상수, 행렬 M은 \( \mathbf{M} = \sum_{x, y} w(x, y)
\begin{bmatrix}
I_x I_x & I_x I_y \\
I_y I_x & I_y I_y
\end{bmatrix} \)으로 정의되며, \( I_x, I_y \)는 입력 영상 \( I \)를 \( x \)축, \( y \)축 방향으로 편미분한 결과로 소벨 필터를 사용한다.
■ 행렬 M의 고윳값 \( \lambda_1, \lambda_2 \)는 코너인지 엣지인지 판단하고자 하는 픽셀 위치 주변의 변화량이다. 해리스 코너 응답 함수를 \( R = \lambda_1 \lambda_2 - k (\lambda_1 + \lambda_2)^2 \)로 표현한다면,
- \( R \)이 0에 가까운 실수라면, 두 고윳값이 모두 0에 가까운 실수. 즉, 변화량이 거의 없는 것이므로 평평한(flat) 영역으로 볼 수 있다.
- 소벨 필터를 이용한 엣지 검출은 가로 엣지면 가로 방향으로 큰 변화량을 가지고 세로 엣지면 세로 방향으로 큰 방향을 가진다는 점을 이용한 것이다.
- 그러므로 \( R \)이 음수인 경우, 한 쪽 고윳값만 큰 값을 가지는 것이므로, 이는 어떤 한 방향으로만 큰 변화가 있고 다른 방향은 거의 변화가 없는 것이기 때문에 엣지로 볼 수 있다.
- 반대로 \( R \)이 양수인 경우는 두 고윳값이 모두 양수이므로, 모든 방향에서 변화가 발생한 것이다. 즉, 코너로 볼 수 있다.
■ OpenCV에서는 cv2.cornerHarris(src, blockSize, ksize, k, dst, borderType) 함수를 이용해 해리스 코너 검출을 수행할 수 있다.
- src는 그레이스케일 이미지
- blockSize는 행렬 M 연산에 사용할 이웃 픽셀 범위이다.
- ksize는 소벨 미분 필터 크기
- k는 해리스 코너 검출 상수로 보통 0.04~0.06 사이의 값을 사용한다.
- dst는 해리스 코너 검출 결과로 해리스 코너 응답 함수 값을 저장한 행렬이다. 즉, 변화량의 값이 저장되어 있는 행렬로 최댓값이 코너 점이다.
- borderType은 가장자리 픽셀 보정 방식이다.
img = cv2.imread('road.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## 해리스 코너 검출
corner = cv2.cornerHarris(gray, 2, 3, 0.04)
## 변화량 결과의 최댓값 20%를 임곗값으로 설정
th = 0.2*corner.max()
## 임곗값보다 큰 좌표를 코너 점으로 판단
coord = np.where(corner > th)
coord = np.stack((coord[1], coord[0]), axis=-1) # (y, x) -> (x, y)
print(coord.shape)
```#결과#```
(44, 2)
````````````
## 코너 좌표 표시
for x, y in coord:
cv2.circle(img, (x,y), 3, (0,255,0), 1, cv2.LINE_AA) # 초록색 동그라미
corner_norm = cv2.normalize(corner, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
corner_norm = cv2.cvtColor(corner_norm, cv2.COLOR_GRAY2BGR)
merged = np.hstack((corner_norm, img))
cv2.imshow('merged', merged)
cv2.waitKey()
cv2.destroyAllWindows()
- 왼쪽 그림은 해리스 코너 응답 함수 값을 0~255 사이로 정규화하여 나타낸 그레이스케일 이미지이다. 이 이미지에서 밝은색 점이 코너의 위치이다.
- 이 예에서는 도로에 있는 좌회전, 직진, 우회전의 코너를 검출하기 위해 해리스 코너 응답 함수 값의 최댓값 20%를 임곗값으로 설정하였다.
- 임곗값을 낮추면 더 많은 코너가 검출되지만, 불필요한 코너도 검출되므로 임곗값 설정이 중요하다.
2.2 시-토마시 검출(Shi-Tomasi Detection)
■ 시-토마시 검출은 해리스 코너 검출을 개선하여 Good Features to Track 방법을 제안했다. 여기서 해리스 코너 응답 함수 \( R = \lambda_1 \lambda_2 - k (\lambda_1 + \lambda_2)^2 \) 대신 \( \lambda_1, \lambda_2 \) 중 최솟값을 사용하는 \( R = \min(\lambda_1, \lambda_2) \) 방식으로 변경되었다.
■ \( \min(\lambda_1, \lambda_2) \)값이 어떤 임곗값 이상이면 그 점을 코너로 판단한다. 즉, 최솟값이라도 임곗값을 넘어야 하기 때문에 두 방향의 변화량 \( \lambda_1, \lambda_2 \)이 모두 충분히 커야 코너라고 판단되는 것이다.
■ 이 원리를 그림으로 나타내면 다음 그림과 같이 \( \lambda_1 \)축(\( x \) 축)과 \( \lambda_2 \)축(\( y \) 축)에서 \( \min(\lambda_1, \lambda_2) \) > 임곗값 \( \lambda_{\min} \)인 초록색 영역이 코너 후보가 된다.
한쪽 축만 크고 다른 쪽이 작으면 엣지(주황색 영역), 임곗값을 넘지 못하면 플랫(회색 영역)이 된다.
■ 시-토마시 검출 방식은 이렇게 단순히 두 방향에서의 최소 고윳값이 충분히 크면 코너로 보는 관점을 택하였다.
■ OpenCV에서는 cv2.goodFeaturesToTrack(img, maxCorners, qualityLevel, minDistance, corners, mask, blockSize, useHarrisDetector, k) 함수를 이용해 시-토마시 검출을 수행할 수 있다.
- img는 그레이스케일 이미지
- maxCorners는 얻고 싶은 최대 코너 개수로 값이 <=0 이면 무제한.
- qualityLevel은 코너 점으로 판단할 스레시홀드 값으로 0.01~0.1 사이의 값을 사용한다.
- minDistance는 코너 점 사이의 최소 거리
- mask는 검출에 제외할 마스크
- blockSize는 코너 주변 영역의 크기
- useHarrisDetector는 코너 검출 방법 선택으로 True를 지정하면 해리스 코너 검출, False를 지정하면 시-토마시 검출 방법을 사용한다. 디폴트는 False이다.
- k는 해리스 코너 검출 방법에 사용할 해리스 코너 검출 상수 k
img = cv2.imread('road.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray, 20, 0.1, 10)
print(corners.dtype) # 좌표가 실수로 반환된다.
## 실수 좌표를 정수 좌표로 변환
corners = np.int32(corners)
## 코너 좌표 표시
for corner in corners:
x, y = corner[0]
cv2.circle(img, (x,y), 3, (0,255,0), 1, cv2.LINE_AA) # 초록색 동그라미
cv2.imshow('corner', img)
cv2.waitKey()
cv2.destroyAllWindows()
- 시-토마시 검출을 사용한 경우 해리스 검출을 사용했을 때보다 적은 수의 코너를 검출하여 필요한 코너만 검출했을 뿐만 아니라 직진 신호의 삼각형에 있는 코너도 검출한 것을 확인할 수 있다.
- 시-토마시 검출은 추적(tracking)에 있어 해리스 검출보다 더 적합한 것으로 알려져 있다.
'OpenCV' 카테고리의 다른 글
객체 추적(Object Tracking) - 광학 흐름(Optical Flow) (0) | 2024.12.25 |
---|---|
매칭(Matching) (2) (2) | 2024.12.24 |
분할(segmentation) (2) (0) | 2024.12.22 |
분할(segmentation) (1) (0) | 2024.12.22 |
모폴로지(Morphology) 연산, 이미지 피라미드(Image Pyramid) (0) | 2024.12.21 |