3 분 소요

SIFT 디스크립터가 뭔지 알아보자!


SIFT 디스크립터,, 너란 녀석….

현재 나는 박순용교수님의 컴퓨터비전을 듣고 있고, 저번주에 배운것이 바로 이 SIFT 기술자(Descriptors)라는 알고리즘이다. 현재까지 내가 배운 컴퓨터비전의 목적은 보다 단순하다.

  1. 이 작은 패턴이 저 이미지에 어디에 있는지 찾아보자! (어떻게? 알고리즘으로!)
  2. 만약 이미지의 크기가 변하더라도 패턴을 찾을 수 있게 하자!
  3. 만약 이미지가 회전하더라도 패턴을 찾을 수 있게 하자!
  4. 만약 이미지의 밝기가 좀 어두워져도 패턴을 찾을 수 있게 하자!

대충 이 정도로 그 목적을 정리할 수 있을 것 같다.

이 목적을 이루기 위해 여러 교수님과 연구자들이 고군분투 한 결과, 1999년에, 즉 성준이 형이 태어날때 이 알고리즘이 탄생하게 된 것이다.(감회가 새롭네~)

이 알고리즘에 대해 한줄요약소개를 하자면, 이렇다

이미지에서 특징점을 검출하고 이를 기술하는데 사용되는 알고리즘이다.

좀 더 이해하기 위해서 관련 영상을 유튜브에 찾아보았고, 이에 대한 설명정보는 많으니 나는 Q&A형식으로 자문자답을 해볼려 한다.

SIFT 알고리즘에 대한 자문자답

image-20240428050747966

1.스케일 공간 극값 검출(Scale-Space Extremum Detection):

  • 이 단계에서는 이미지의 여러 스케일에서 극값을 찾는다. 이를 위해 가우시안 블러를 다양한 스케일로 적용하고, 각 스케일에서 인접 픽셀과 비교를 거치고 난 후, 극값을 추출해낸다. 그리고 이 극값들을 키포인트로 식별해낸다.

2.키포인트 로컬라이제이션(Keypoint Localization)

  • 스케일 공간 극값 검출에서 얻은 잠재적 키포인트를 기반으로, 실제 키포인트의 위치를 더 정확하게 파악한다. 이 과정에서 강도와 대비가 낮거나 잡음에 민감한 키포인트는 제거된다. 이를 통해 키포인트의 정확한 위치, 스케일, 방향 등을 파악한다고 한다.(쉽게 말해서 우리가 극값들에 대해서 무작위로 키포인트로 식별했다고 위해서 말했는데, 사실 여기에는 여러 결함적 요인들에 의해서 허수들이 존재한다. 그래서 우리는 여기서 필터링을 거쳐서 좀더 실수에 가까운! 순도높은! 그러한 키포인트를 추출할 필요가 있다는 뜻이다.)

3.방향 할당(Orientation Assignment)

  • 이제 우리가 괜찮은 키포인트들을 정재해냈으면, 여기다가 방향을 할당해주어야지! 키포인트로 지정된 그 지점의 주변픽셀에 대해 가우시간 스무딩을 적용하여 그래디언트 값을 구해준다. 말을 괜히 어렵게 했는데, 그냥 우리가 slope를 구할때 x값의 변화량 분의 y값의 변화량을 해서 기울기를 구하는 것처럼 방향을 구해주는 것이다.(어려울 거 읎다ㅋㅋ)

4.키포인트 기술자 생성(Keypoint Descriptor Generation)

  • 각 키포인트 주위에 일정한 영역을 설정, 그리고 그 영역 내에서 방향과 크기를 기반으로 한 특징 추출을 진행하여 기술자를 생성한다. 이 디스크립터는 주로 128차원 벡터로 구성되어있는데, 이를 통해 이미지간의 특징점 비교가 가능하다.

이러한 4단계를 통해 이미지의 다양한 스케일과 회전에 대응할 수 있는 강력한 특징점을 생성할 수 있다.

(여기서 일부러 강력하다는 어휘를 사용한 이유는, 여러 악조건에서도 우리가 원하는 목적을 달성할 수 있다는 맥락에서 쓰여진 것인데, 비유하자면 이런거다, 약한 사람은 그냥 등산하는 것만 해도 힘이 든다. 하지만 강한 사람은 갑자기 폭풍우가 몰아쳐도 비가오고 우박이 떨어져도 등산을 할수가 있다. 뭔가 좀 이상하긴 한데ㅋㅋ. 암튼 이러한 악조건에서도 그 목표! 패턴을 찾아내는거! 요거를 할 수 있는 것이 바로 강력한 특징점을 생성할 수 있는 SIFT 디스크립터, 이 친구라는 것이다. 이러한 맥락에서 SIFT의 I가 Invarient(시불변)이라는 약자인 것 같다ㅋㅋ)

image-20240428052559825

이번에는 좀더 다양한 다이어그램을 통해 설명해보았다.

image-20240428052619617

SIFT 알고리즘에서는 이미지에 여러수준의 가우시안 블러링을 적용하여 스케일 공간이 생성되고, 이때 가우시안 필터간의 차이를 구하는 DoG를 생성하여 스케일 간의 변화를 강조하고 노이즈를 줄일 수 있다. 이를 통해 각 단계에서 로컬극값을 찾을 수가 있고,

image-20240428052724582

이렇게 검출된 극값은 포인트로 식별이 된다. 이러 방식으로 다양한 크기의 이미지를 분석하면서 각 스케일에서의 특징점을 검출해보았을때, 동일한 특징점을 일관되게 식별한다는 점에서 스케일 불변성을 확보했다고 볼 수 있다.(여기서는 불변성을 충족하기 위한 설명만 들어갔기에 그래디언트와 키포인트 방향할당에 대한 내용을 제외했다. 다음 문제에서 이에 대해 자세하게 다룰거다.)

image-20240428052858799

image-20240428052908862

SIFT 알고리즘은 이미지에서 검출된 키포인트의 주변정보를 분석하여 기술자를 생성하는데, 이를 위해 DoG 필터를 통해 검출된 극값을 키포인트로 식별하고 이때, 키포인트 주변의 픽셀저옵를 가지고, 16x16 픽셀창을 4x4격자로 나누고 이때 만들어진 각 셀에서 그래디언트 방향을 계산하여

image-20240428053016967

128차원 벡터로 구성된 히스토그램을 구성한다. 이런 방식으로 다양한 크기의 이미지를 분석하면서 스케일에서의 특징점을 검출해보았을때, 동일한 특징점을 일관되게 식별한다는 점에서 스케일에 대해 불변성을 갖는다고 볼 수 있다.

image-20240428053112580

위 네가지 보기 모두 SIFT의 응용분야로 적합하다. 그 중에 이미지 정합에 대해서만 설명하자면, 이미지 정합은 서로 다른 각도나 스케일로 촬영된 이미지에서 동일한 특징점을 찾아 일치시킨다. 이는 파노라마 사진을 만드는데 매우 유용한데! 다양한 각도에서 촬영된 이미지 간의 정렬을 통해 복잡한 화면도 재구성할 수 있을 것이다.

image-20240428053637823

위 사진이 내가 예전에 본가에 올라갔을때 휴대폰으로찍은 파노라마 사진이다. 카메라가 연속적으로 사진을 찍고 나서 SIFT 알고리즘등을 써서 템플릿 매칭을 시켜 하나의 사진으로 깔쌈하게? 이어붙여준 것 같다.

image-20240428054306560

해리스 코너 검출기는 빠르고 간단한 알고리즘으로, 이미지의 코너를 효과적으로 검출할 수 있지만, 스케일 변화에 민감하여, 스케일 불변성이 부족하다.

반면 SIFT알고리즘은 스케일, 회전, 조명변화에 강인한 특성을 제공하여, 기술자를 통해 이미지 정합과 물체인식에 활용할 수 있다. 하지만 알고리즘이 복잡하고 계산비용이 높다.

(그렇기 때문에 저성능 컴퓨터에서 Template Matching을 수행해야 한다면, 해리스 코너 검출기를 쓰면 될 것 같고, 고성능 컴퓨터에서 Template Matching을 수행해야 한다면, SIFT Descriptor를 쓰면 될 것 같다,,흠흠)

이제 실제로 한번 코드를 돌려보자!

코드 실습

코드>

import cv2
import matplotlib.pyplot as plt

# 이미지 읽기
image = cv2.imread('./temp_image/siba.jpg')
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #흑백 이미지로 변환

# SIFT 객체 생성
sift = cv2.SIFT_create()

# 특징점 검출 및 기술자 생성
keypoints,descriptors = sift.detectAndCompute(gray_image, None)

# 이미지에 키포인트 그리기
image_with_keypoints = cv2.drawKeypoints(
    image, keypoints, None, flags = cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)

# 키포인트가 그려진 이미지 표시
plt.figure(figsize=(15,15))
plt.imshow(cv2.cvtColor(image_with_keypoints, cv2.COLOR_BGR2RGB))
plt.title('Image with SIFT Keypoints')
plt.show()

# 기술자 출력
print("Number of keypoints detected: ", len(keypoints))
print("Descriptors shape: ", descriptors.shape)

결과>

image-20240428065913869

Number of keypoints detected:  171
Descriptors shape:  (171, 128)

댓글남기기