서로 다른 두 이미지나 영상을 비교해서 비슷하거나 같은 객체를 포함하는 것을 찾는 영상 매칭을 시도하고자 한다. 영상 매칭은 영상에서 유의미하다고 판단되는 특징을 숫자로 변환해서 숫자를 비교하는 방법을 사용한다. 이를 찾아내는 것이 쉽지 않으며, 영상 속의 특징을 변환한 숫자를 특징 디스크립터(feature descriptor)라고 한다.

 

1. 평균 해시 매칭

* 배틀그라운드 무기 일부를 이미지로 활용합니다.

평균 해시란 어떠한 영상이든지 동일한 숫자로 변환되며 숫자를 얻기 위해서 평균 값을 이용하는 것이다. 평균을 얻기 전에 영상을 특정 크기로 지정하고 pixel 전체의 평균을 계산한 뒤 각 pixel이 평균보다 작으면 0, 크면 1로 바뀌게 한다. 그러면 pixel 값을 한 줄로 늘어선 2진수로 볼 수 있으며, 필요에 따라서 10진수나 16진수 등으로 변환해서 확인할 수 있다. 다음은 이미지를 16x16 크기의 평균 해시로 변환한 것이다.

import cv2
img = cv2.imread('weapon/flashbang.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gray = cv2.resize(gray, (16,16))
avg = gray.mean()

hash = 1 * (gray > avg)
print(hash)

# 2진수를 16진수로 변환
'''
dhash = []
for row in hash.tolist():
    s = ''.join([str(i) for i in row])
    dhash.append('%02x'%(int(s,2)))
dhash = ''.join(dhash)

print(dhash)
'''

cv2.imshow('Flashbang', img)
cv2.waitKey()
cv2.destroyAllWindows()

hash 출력 결과(좌) / 이미지 원본(우)

코드를 실행한 결과 0과 1이 배열된 것을 보면 0으로 구성된 모양이 섬광탄 형태와 유사함을 확인할 수 있다. 이렇게 얻은 평균 해시를 다른 이미지와 비교해서 얼마나 유사한지 측정해야 하는데, 유클리드 거리와 해밍 거리를 사용해서 측정한다.

 

유클리드 거리 두 값의 차이로 거리 계산
해밍 거리 두 값의 길이가 같아야 계산 가능하며 다른 자리 수의 개수 계산

1) 유클리드 거리

10과 비교할 값으로 4와 12가 있을 때, 10과 4의 유클리드 거리는 6, 10과 12의 유클리드 거리는 2다. 이 경우, 10과 유사한 수는 12라고 판단한다.

2) 해밍 거리

13579와 비교할 값으로 13597과 23579가 있을 때, 13579와 13597은 마지막 두 자리가 다르므로 해밍거리는 2, 13579와 23579는 맨 첫 자리가 다르므로 해밍거리는 1이다. 이 경우, 13579와 유사한 수는 23579라고 판단한다.

 

평균 해시를 비교할 때는 각 자릿수의 차이 개수만 비교하는 해밍 거리로 측정하는 것이 적합하다. 위 코드에서 얻은 섬광탄의 평균 해시를 활용해서 다른 이미지의 평균 해시와의 해밍 거리를 비교해서 어떤 이미지가 유사한지 찾는데 시도한다. 비교할 사진 종류는 다음과 같다.

import cv2
import numpy as np
import glob

img = cv2.imread('weapon/flashbang.jpg')

# 파일 경로
directory = 'weapon/search'

# 평균 해시로 변환
def tohash(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.resize(gray, (16,16))
    avg = gray.mean()
    bins = 1 * (gray > avg)
    return bins

# 해밍 거리 측정
def hamming(a,b):
    a = a.reshape(1, -1)
    b = b.reshape(1, -1)
    distance = (a != b).sum()
    return distance

# 섬광탄 hash
flashbang = tohash(img)

# 비교할 이미지 경로
paths = glob.glob(directory+'/*.jpg')

for path in paths:
    img = cv2.imread(path)
    cv2.imshow('searching', img)
    cv2.waitKey(3)
    a_hash = tohash(img)
    dst = hamming(flashbang, a_hash)
    if dst/256 < 0.2:
        #print(path, dst/256) # 해밍 거리 출력
        cv2.imshow(path, img)
        
cv2.destroyWindow('searching')
cv2.waitKey()
cv2.destroyAllWindows()

섬광탄 이미지 제외한 이미지는 다른 폴더(search)에 넣는다. glob.glob을 활용하면 해당 경로의 모든 파일 리스트를 가져올 수 있다. 섬광탄과 해밍 거리가 20% 이내에 있는 이미지만 나타나도록 하면 전체 무기 목록에서 2개만 출력된 것을 확인할 수 있다. 사실, 매칭의 정확도를 높이기 위해서는 크기나 방향에 영향을 받지 않으면서 특징이 드러나는 점을 표현하는 여러 숫자로 변환되어야 한다. 이것을 keypoint라고 하는데, keypoint를 활용하는 특징 검출은 후에 다룰 예정이다.

 

2. template 매칭

템플릿 매칭은 어떤 물체가 있는 영상과 물체가 포함되었을 것이라 예상되는 입력 영상과 비교해서 매칭되는 위치를 찾는 것이며, 미리 준비한 영상을 템플릿 영상이라고 한다. 템플릿 영상을 입력 영상에서 찾는 것이기 때문에 템플릿 영상은 입력 영상보다 크기가 작아야 한다.

import cv2
import numpy as np

img = cv2.imread('image.jpg')
lion = cv2.imread('lion.jpg')
row, col = lion.shape[:2]
cv2.imshow('lion', lion)

methods = ['cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF_NORMED']

for i, name in enumerate(methods):
    draw = img.copy()
    method = eval(name)
    # template matching
    match = cv2.matchTemplate(img, lion, method)
    # 배열 전체에서의 최소, 최댓값 / 최소 값과 최댓값의 좌표
    min_value, max_value, min_loc, max_loc = cv2.minMaxLoc(match)
    
    # TM_SQDIFF는 낮은 값일수록 좋은 매칭
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_L = min_loc
        match_value = min_value
    else:
        top_L = max_loc
        match_value = max_value
        
    bottom_R = (top_L[0] + col, top_L[1] + row)
    cv2.rectangle(draw, top_L, bottom_R, (0,255,0), 3)
    
    # matching point
    text = (top_L[0], bottom_R[1])
    cv2.putText(draw, str(match_value), text, cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 2, cv2.LINE_AA)
    cv2.imshow(name, draw)
    
cv2.waitKey()
cv2.destroyAllWindows()

cv2.TM_CCOEFF_NORMED(좌) / cv2.TM_CCORR_NORMED(중) / cv2.TM_SQDIFF_NORMED(우)

template matching을 하기 전, 사자가 있는 부분만 template 이미지로 미리 캡처해둔다. template 매칭은 3가지의 방법을 적용하며, 각각의 매칭 좌표를 사각형을 표시함과 동시에 매칭 포인트가 나타나도록 한다. 매칭 종류는 다음과 같다.

cv2.TM_CCOEFF 상관계수 매칭; 완벽한 매칭 : 1, 나쁜 매칭 : -1, 상관관계 없음 : 0
cv2.TM_CCOEFF_NORMED 상관계수 매칭 정규화
cv2.TM_CCORR 상관관계 매칭; 완벽한 매칭일수록 값이 크며, 나쁜 매칭일 때 0
cv2.TM_CCORR_NORMED 상관관계 매칭 정규화
cv2.TM_SQDIFF 제곱 차이매칭; 완벽한 매칭 : 0, 나쁜 매칭일수록 값이 커짐
cv2.TM_SQDIFF_NORMED 제곱 차이 매칭 정규화

 

템플릿 매칭을 지원하는 함수는 cv2.matchTemplate()로, 입력 영상, 템플릿 영상, 매칭 방법, [매칭 결과, 적용할 마스크 종류]를 인자로 사용한다. 도표에 나열된 것 중 한 가지를 매칭 방법으로 사용하면 된다. 함수를 수행 결과로 이미지 크기에서 템플릿 크기를 뺀 것에 1만큼 큰 2차원 배열을 결과값으로 얻게 된다. 이 배열의 최대, 최소 값을 구하면 매칭 값과 매칭 좌표를 찾을 수 있으며, cv2.minMaxLoc()으로 쉽게 찾을 수 있다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기