[OpenCV] 05-4. Introduction to SIFT (Scale-Invariant Feature Transform)
🐍Python/OpenCV

[OpenCV] 05-4. Introduction to SIFT (Scale-Invariant Feature Transform)

728x90
반응형

< 5-4. Introduction to SIFT (Scale-Invariant Feature Transform) >

이번 장에서는

  • SIFT 알고리즘의 개념에 대해서 알아보고
  • SIFT의 요점과 기술자(descriptor)를 찾는 것

에 대해서 알아볼 것이다.

Theory

이전 두 개의 챕터에서, Harris등과 같은 모서리 검출기를 봤다. 그들은 회전에 불변하다, 이는 이미지가 회적하더라고 같은 모서리를 찾을 수 있다는 것을 말한다. 이미지가 회전되도 모서리는 모서리라는 것이 명백하기 때문이다. 하지만 스케일링의 경우는 어떨까? 이미지가 스케일링된 경우 모서리는 모서리가 아닐 수도 있다. 예를 들어서, 아래의 이미지를 보자. 작은 창 안에 있는 이미지의 모서리는 같은 창으로 줌인해서 보면 평평해 보인다. 그래서 Harris 모서리는 스케일에 불변하지 않는다.


그래서 2004년에 새로운 알고리즘인 스케일에 불변한 포인트들에서 차별적인 이미지 특성을 추출하는 SIFT(Scale Invariant Feature Transform)을 발표했다.

SIFT 알고리즘은 4개의 단계로 이뤄진다. 하나하나 살펴보자!

1. Scale-space Extrema Detection

위의 이미지를 보면, 다른 스케일일 경우 같은 창으로 모서리를 탐지하지 못한다는 것을 알 수 있다. 작은 모서리에 대해서는 괜찮다. 하지만 커다란 모서리를 탐지하기 위해서는 더 큰 창이 필요하다. 이를 위해, 스케일-공간 필터링이 사용된다. 다양한 시그마 값을 가진 이미지에 대해 Laplacian of Gaussian(LoG)이 발견된다. LoG는 시그마의 변화로 인해 다양한 크기의 blob을 감지하는 blob(윤곽이 뚜렷하지 않은 것) 검출기 역할을 한다. 요컨대, 시그마는 스케일링 파라미터로 작용한다. 예를 들어 위의 그림에서 시그마가 낮은 가우스 커널은 작은 코너에 높은 값을 주고 시그마가 높은 가우스 커널은 큰 코너에 잘 맞는다. 따라서 (x,y,σ) 값의 목록을 제공하는 스케일과 공간에서 로컬 최대값을 찾을 수 있으며, 이는 σ 스케일에서 (x,y)에 잠재적인 키포인트가 있음을 의미한다.

하지만 이 LoG는 약간 비용이 많이 들기 때문에, SIFT 알고리즘은 LoG의 근사치인 가우시안의 차이를 사용한다. 가우시안의 차이는 σ kσ로 두 개의 다른 σ로 된 이미지를 흐리게 하는 차이로 얻어진다. 이 과정은 가우시안 피라미드의 여러 옥타브 이미지에 대해 수행된다. 아래 이미지를 보면서 이해하는게 빠를 것이다. (DoG를 하나의 이미지에 서로 다른 필터를 적용한 가우시안 피라미드 이미지의 차로 생각해도 된다.)


일단 이 DoG가 발견되면, 이미지들은 스케일과 공간을 넘어 local extrema에 대해 검색된다. 예를 들어, 이미지의 한 픽셀은 다음 스케일의 9개 픽셀과 이전 스케일의 9개 픽셀 뿐만 아니라 8개의 인접 픽셀들과도 비교된다. 만약 local extrema라면, 이는 잠재적인 키포인트이다. 그것은 기본적으로 키포인트가 그 스케일로 잘 표현된다는 것을 의미한다. 아래 이미지를 보자


다른 파라미터에 대해, 이 논문은 최적값으로 옥타브=4, 스케일수준=5,초기 σ=1.6,k=2등으로 요약할 수 있는 일부 경험적 데이터를 제공한다.

2. Keypoint Localization

일단 잠재적인 키포인트의 위치가 찾아지면, 더 정확한 결과를 얻기 위해 정제(refined)되어야 한다. 그들은 더 정확한 extrema의 위치를 얻기 위해 테일러 시리즈 스케일 공간을 사용했으며, 이 extrema의 강도가 임계값보다 작으면 버려진다. 이 임계값은 OpenCV에서 contrastThreshold라고 불린다.

DoG는 가장자리에 대해 더 반응이 높으므로, 가장자리를 지워야 한다. 이를 위해, Harris corner detector와 비슷한 개념이 사용된다. 2x2 헤시안 행렬을 사용하여 주요 곡률을 계산한다. Harris corner detector를 통해 가장자리는의 경우 하나의 고유값이 다른 것 보다 크다는 것을 안다. 그래서 여기서는 간단한 기능을 사용했다.

만약 이 비율이 임계값보다 클 경우, OpenCV의 edgeThreshold라고 불리고, 키포인트는 삭제될 것이다. 논문에서는 10의 값을 주었다.

그래서 대조가 적은 키포인트나 가장자리 키포인트를 제거하고 남은 것은 흥미가 강한 점들이다.

3. Orientation Assignment

이제 각 키포인트에 방향이 지정되어 이미지 회전에 대한 불변성을 얻는다. 스케일에 따라 키포인트 위치를 중심으로 인접하는 것들을 찾고, 해당 영역에서 gradient 크기와 방향이 계산된다. 36개의 bins와 360도를 포함하는 방향 히스토그램이 생긴다. 즉 이미지가 회전되거나 확대되더라도 추출된 키포인트들은 그 이미지의 특징을 보존하게 된다.

4. Keypoint Descriptor

이제 키포인트 디스크립터가 생겼다. 키포인트 주변읭 16x16 이웃점으로 취해진다. 4x4 크기의 16개 하위 블록으로 구분된다. 각 하위 블록에 대해, 8개의 bin 방향 히스토그램이 생성된다. 그래서 총 128개의 bin 값을 이용할 수 있는 것이다. 이는 키포인트 디스크립터를 구성하는 벡터로 표현된다. 추가적으로, 조명 변화, 회전 등에 대한 굳건함을 얻기위해 몇 가지 조치를 취한다.

5. Keypoint Matching

두 이미지 사이의 키포인트는 가장 가까운 이웃을 식별함으로써 일치된다. 그러나 어떤 경우에는, 두 번째로 가장 가까운 매칭이 첫 번째와 가까울 수도 있다. 이는 노이즈나 다른 이유들 때문에 발생한다. 이 경우, 가장 가까운 거리에 대한 두 번째로 가까운 거리 비율을 구한다. (가장 가까운거리 / 두 번째로 가까운 거리) 만약 0.8보다 크다면 거부된다. 정확한 매칭을 5%만을 제거하면서, 잘못된 매칭은 90%정도 제거한다.

그래서 이것이 SIFT 알고리즘의 요약이다. 이 알고리즘은 OpenCV의 Non-free 모듈에 속해있다!

SIFT in OpenCV

이제 OpenCV에서 사용할 수 있는 SIFT의 기능을 보자. 먼저 키포인트를 찾고 이를 그리자. 첫 번째로 SIFT 객체를 만들어야한다. 우리는 선택적이고 문서에 설명되어있는 것에 다른 파라미터를 전달할 수 있다.

가장 최신 버전 OpenCV에서는 SIFT함수를 이용할 수 없다. 사용하려면 버전을 낮추거나 새로 설치해야한다..! 그래서 코드와 결과만 보도록 하겠다!

import cv2
import numpy as np

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

sift = cv2.xfeatures.SIFT_create()
kp = sift.detect(gray,None)

img=cv2.drawKeypoints(gray,kp)

cv2.imwrite('sift_keypoints.jpg',img)

sift.detect() 함수는 이미지내의 키포인트를 찾아준다. 이미지의 부분만 찾고싶다면 마스크를 이용할 수 있다. 각 키포인트는 (x,y) 좌표나, 의미있는 이웃의 크기, 방향을 지정하는 각도,키포인트의 강도를 지정하는 반응과 같은 많은 특성을 가진 특수한 구조이다.

OpenCV는 또한 키포인트의 지역에 작은 원을 그리는 cv2.drawKeyPoint() 함수를 제공한다.cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS flag를 보내면 키포인트의 크기에 따른 원이 크려지고 방향도 보여줄 것이다. 아래 예를 보자!

img=cv2.drawKeypoints(gray,kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('sift_keypoints.jpg',img)

아래 두 가지 결과를 보자.



descriptor를 계산하기 위해, OpenCV는 두 가지 방법을 제공한다.

  1. 키포인트를 찾았다면, sift.compute()를 불러서 우리가 찾은 키포인트들로부터 descriptor를 계산한다.
  2. 만약 키포인트를 찾지 못했다면, sift.detectAndCompute() 함수로 한 번에 직접 키포인트와 descriptor를 찾을 수 있다.

두 번째 방법을 보자.

sift = cv2.xfeatures.SIFT_create()
kp, des = sift.detectAndCompute(gray,None)

kp는 키포인트들의 리스트이고 des는 shape의 넘파이 어레이이다.
NumberofKeypoints x 128

키포인트와 descriptor를 얻을 수 있다. 이제 다른 이미지에서 어떻게 키포인트들을 매칭하는지 볼 것이다. 다음 챕터에서 봐보자!

728x90
반응형