이 글은 Google Colab 환경에서 실행 되었습니다.
!cat /etc/*release
출력:
DISTRIB_ID=Ubuntu DISTRIB_RELEASE=20.04 DISTRIB_CODENAME=focal DISTRIB_DESCRIPTION="Ubuntu 20.04.5 LTS" NAME="Ubuntu"
Google Colab의 환경은 Linux Ubuntu이니 참고한다.
OpenCV?
OpenCV는 컴퓨트 비전 라이브러리로 컴퓨터가 이미지를 인식하거나 다루는데 쓰이는 라이브러리이다. C++ 언어로 쓰였고, python wrapper인 OpenCV-python 을 이용하여 python으로도 조작이 가능하다. 이미지를 다루는 데에 array를 기본적으로 사용하여 data structure가 numpy array를 따른다. Numpy 라이브러리는 python 에서 배열 연산에 특화된 라이브러리로, 많은 머신러닝 프레임워크나 라이브러리에 쓰인다.
이미지 특징 (Image Features)
이미지를 비교하거나 어떤 이미지를 컴퓨터가 구분할 수 있으려면 이미지의 특징을 컴퓨터가 학습해야 한다. 학습을 위한 첫 단계가 이미지의 특징 추출이다. 이 글에서는 SIFT(Scale-Invariant-Feature Transform)을 이용한다. SIFT 알고리즘에 대해서 심도있게 다루기 보다는 흐름에 더 집중해서 본다.
SIFT(Scale-Invariant Feature Transform)
이미지의 특징을 추출하는 알고리즘이다. 이미지에서 특징을 추출하여 keypoint를 추출하여 각각의 keypoint에 대해서 descriptor를 만든다. 각각의 descriptor는 128 차원의 vector로 이루어져있다. (x1, x2, x3.....,x128)까지 간다.
간략히 설명하면, 이미지의 pixel에서 가장 변화가 크거나, 특징이라고 생각되는 pixel을 keypoint로 설정하고, blur를 끼워넣은 이미지와 비교 분석하여 descriptor를 설정한다. 각각의 keypoint와 descriptor를 비교하여 이미지의 유사도를 측정한다.
Setup (설정)
OpenCV는 이미지를 위한 라이브러리이니 이미지가 준비되 있어야 한다. 자신의 컴퓨터에 있는 이미지여도 되고 어떤 이미지여도 상관없다.
!pip install opencv-python==3.3.0.10
!pip install opencv-contrib-python==3.3.0.10
!wget https://raw.githubusercontent.com/opencv/opencv/master/samples/data/building.jpg
!wget https://raw.githubusercontent.com/opencv/opencv/master/samples/data/aloeR.jpg
!wget https://raw.githubusercontent.com/opencv/opencv/master/samples/data/baboon.jpg
SIFT가 opencv-python 최근 버전에서는 누락 되었다는 정보가 있어서 구버전인 3.3.0.10을 사용한다. 다운로드한 이미지들은 kaggle에서 github 이미지가 있어서 다운로드하였고, 자신이 가지고 있는 이미지를 사용해도 상관없다!
import cv2
import numpy as np
import matplotlib.pyplot as plt
import time
%matplotlib inline
pip install한 opencv, numpy, 등등 필요한 라이브러리들을 import 해준다.
# 이미지 보기
building = cv2.imread('./building.jpg')
plt.imshow(cv2.cvtColor(building, cv2.COLOR_BGR2RGB))
plt.show()
출력:
openCV에서 이미지를 읽게 되면 RGB값을 거꾸로 BGR로 읽기 때문에 우리가 보는 이미지를 얻으려면
cv2.cvtColor(image, cv2.COLOR_BGR2RGB)를 해줘야 한다.
SIFT keypoint 추출
sift = cv2.xfeatures2d.SIFT_create()
cv2.SIFT 의 인스턴스 객체를 만든다. 이 객체를 이용해서 keypoint와 descriptor를 추출한다.
# SIFT는 gray image에만 적용되니 흑백 이미지로 전환
gray = cv2.cvtColor(building, cv2.COLOR_BGR2GRAY)
# SIFT를 이용한 keypoint 추출
kp = sift.detect(gray, None)
# 추출된 keypoint 그리기
gray_sift = cv2.drawKeypoints(gray, kp, None)
plt.imshow(cv2.cvtColor(gray_sift, cv2.COLOR_BGR2RGB))
plt.show()
주석을 참고하면 이해되는 내용이니, 위의 코드는 주석을 참고하자!
출력:
# 추출된 keypoint와 친해지기
print(len(kp))
print(type(kp))
print(kp[0].angle)
print(kp[0].pt)
print(kp[0].size)
출력:
4566 <class 'tuple'> 145.64556884765625 (2.4097557067871094, 497.0863037109375) 2.0678555965423584
출력된 값을 보면 알 수 있듯이, tuple 안에 keypoint 원소가 4566개 있고, 각각의 방향, 위치, 크기를 알 수 있다.
# keypoint들의 방향과 크기까지 표시
gray_rich_sift = cv2.drawKeypoints(gray, kp, None, flags = cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
plt.imshow(cv2.cvtColor(gray_rich_sift, cv2.COLOR_BGR2RGB))
plt.show()
출력:
SIFT descriptor 추출
# SIFT descriptor 추출
# 위에서 추출한 keypoint에 대한 descriptor 추출
kp, des = sift.compute(gray, kp)
# descriptor와 친해지기
print(type(des))
print("\n")
# keypoint와 descriptor의 대략적 개요
print(kp)
print("\n")
print(des)
출력:
<class 'numpy.ndarray'>
4566
(128,)
(< cv2.KeyPoint 0x7f52a13fb900>, < cv2.KeyPoint 0x7f52a13fbd50>, ...... )
[[ 9. 6. 9. ... 0. 0. 0.]
[ 2. 1. 7. ... 1. 1. 3.]
[ 0. 0. 0. ... 8. 1. 2.]
...
[ 71. 4. 0. ... 0. 0. 20.]
[102. 2. 0. ... 0. 0. 15.]
[ 8. 2. 0. ... 0. 0. 0.]]
kp는 cv2.KeyPoint가 4566개, des는 각각의 kp에 대응하는 descriptor가 들어있으며 (4566개) 하나의 descriptor는 128 차원의 vector임을 알 수 있다. 이 vector가 kp의 특징이라고 볼 수 있다.