2020년 3월 8일 일요일

OpenCV 기반 형상 유사도 비교 방법

OpenCV는 컴퓨터 비전에 필요한 알고리즘 대부분이 잘 구현되어 있는 오픈소스 라이브러리이다. 이 글은 OpenCV를 이용해 Hu-Moment 기반 형상(모양) 유사도를 비교하는 방법을 간단히 설명한다.

형상 유사도와 모멘트 정의
예를 들어, 서로 다른 모양의 두개의 형상이 있다고 가정해 보자. 이 두개 형상의 유사한 정도는 어떻게 비교할 수 있을까? 이 글은 모양의 모멘트(moment)를 이용해 회전, 이동, 스케일에 독립적인 특성을 계산해 형상을 비교하는 방법을 소개한다.

모양의 모멘트는 모양을 이루는 각 픽셀의 강도에 대한 가중 평균이다. 예를 들어, 흑백 컬러를 가진 모양 I를 가정한다. x, y지점의 픽셀의 강도는 I(x, y)로 표현한다. I(x, y)는 0 혹은 1을 가질것이다. 이때 모엔트는 다음과 같다. 
M은 모든 픽셀 강도의 합이다. 다른말로, M은 강도에만 기반한 값이다. M은 모양의 회전에 불변인 값이 된다(RI. rotation invariant). 다음 그림을 보면, 두개의 A에 대한 M은 서로 같음을 알 수 있다. 하지만, M(C)는 다를것이다. M은 픽셀 강도의 합이므로 형상의 질량이라 볼 수 있다.
다음은 raw moment를 정의한다. 여기서, i, j는 0, 1, 2와 같은 정수값이된다. 이 정수값은 모양 질량의 중심을 계산할 때 사용한다.


다음 x̄, ȳ 는 모양 질량의 중심이라 정의한다.

이제 중심 모멘트(central moment)를 정의해보자. 이는 raw moment와 매우 유사하다. 이는 x, y 위치에서 질량 중심 x̄, ȳ 를 뺀 수치를 사용한다. 이렇게하면 이동에 불편이 된다(TI. translation invariant). 


여기에 다음 수식을 적용해 스케일에 불변인 정규화된 중심 모멘트(normalized central moments)를 얻는다(SI. scale invariant).
이제 유사도를 비교할 Hu Moment를 정의한다. Hu Moment는 7개 식으로 구성된다. 첫 6개 모멘트는 이동, 스케일, 회전 및 반사에 불편인 값을 제공한다. 마지막 7번째 모멘트는 반사에 따라 변경되는 부호이다. 

OpenCV 이용한 Hu Moments 및 유사도 계산
OpenCV는 Hu moment 계산함수를 다음과 같이 제공한다. 
이 함수는 이미지의 Hu Moment를 계산한 후, 유사도값을 리턴하는 matchShapes 함수에 사용된다. Hu moments는 log 변환을 통해 좀 더 계산하기 좋은 값으로 변환된다.

이 H값들을 이용해 유사도 비교를 위한 거리 함수 D(A, B)를 정의한다. A, B는 형상이다. OpenCV는 아래와 같이 3개의 거리 함수를 제공한다.
  • CONTOURS_MATCH_I1 
  • CONTOURS_MATCH_I2
  • CONTOURS_MATCH_I3
다음은 이를 이용한 이미지 형상 유사도 비교 파이썬 코드이다. fileList 배열에 가지고 있는 이미지 파일명으로 수정하면, 각 이미지 파일의 유사도 비교 결과를 확인할 수 있다.
import cv2
import numpy
print('OpenCV = ', cv2.__version__)

fileList = ["image1.jpg", "image2.jpg", "image1.jpg"]

imgList = []
for file in fileList:
img = cv2.imread(file, cv2.IMREAD_GRAYSCALE) 
imgList.append(img)

contours = []
for im in imgList:
ret, imgBin = cv2.threshold(im, 127, 255,0)
conts, hierarchy = cv2.findContours(imgBin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = conts[0]
contours.append(contour)

print("Shape Distances Between \n-------------------------")
index = 0
for contour in contours:
m = cv2.matchShapes(contours[0], contour, 1, 0)
print("{0} and {1} : {2}".format(fileList[0], fileList[index], m))
index = index + 1
cv2.waitKey()

입력 이미지는 다음과 같다. 

계산 결과는 다음과 같다. 십자모양과 같은 이미지는 유사도가 0이고, 기울어진 십자모양도 0.01로 거의 유사하다. 나머지는 차이가 있는 것을 알 수 있다.

댓글 없음:

댓글 쓰기