본문 바로가기
OpenCV/OpenCV-Chapter

CH08 OpenCV-Python 템플릿 매칭(Template matching)

by kiimy 2021. 12. 24.
728x90

템플릿 매칭(Template matching)이란?

- Classification

• 입력 영상에서 (작은 크기의) 템플릿 영상과 일치하는 부분을 찾는 기법

• 템플릿: 찾을 대상이 되는 작은 영상. 패치(patch)

기본적으로 같은 위치의 픽셀끼리 곱하고 다 더해서 유사도를 판단
==> 회전, 크기변환이 클 때는 찾기 어려움
==> Key point활용(= Local feature matching)

찾고자하는 객체(lenna 눈) == Kernel(=filter)

유사도 => 최댓값(비슷한 부분이 밝게)
비유사도 => 최솟값(비슷한 부분이 어둡게)
==> 거리를 나타낸다

OpenCV filter VS Template filter

OpenCV filtering은 영상 이미지의 가상의 픽셀(padding)이 있다는 가정하에 filter를 돌린다
== 동일한 크기를 얻을 수 있다.
But
Template은 가상의 필터가 없음(= 영상안에서만 scanning)
== 주변 테두리가 검은색으로 나온다
# templ 위치 찾기
src = cv2.imread('ch08\\images\\circuit.bmp', cv2.IMREAD_GRAYSCALE)
templ = cv2.imread('ch08\\images\\crystal.bmp', cv2.IMREAD_GRAYSCALE)

if src is None or templ is None:
    print('Image load failed!')
    sys.exit()

# noise 추가
# 입력 영상 밝기 50증가, 가우시안 잡음(sigma=10) 추가
# np.random.normal(loc=0.0, scale=1.0, size=None)
# 평균, 표준편차
noise = np.zeros(src.shape, np.int32)
cv2.randn(noise, 50, 10)
src_noise = cv2.add(src, noise, dtype=cv2.CV_8UC3)

# 템플릿 매칭 & 결과 분석
res = cv2.matchTemplate(src_noise, templ, cv2.TM_CCOEFF_NORMED)
# 영상을 보기 위함(normalize) = 유난히 밝은 흰색 부분을 찾을 수 있음
res_norm = cv2.normalize(res, None, 0,255, cv2.NORM_MINMAX, cv2.CV_8U)
'''
cv2.matchTemplate(image, templ, method, result=None, mask=None) -> result

image와 templ는 8비트 or 32비트
result=  (W - w + 1) * (H - h +1) = 32비트의 단일 채널 이미지로 반환
W, H = 입력 영상의 이미지
w, h = template 이미지
method = SQDIFF(완전히 같으면 0, 다르면 값이 커짐)
         CCORR(같으면 큰값, 다르면 작은 값)
         CCOEFF_NORMED(완전히 일치하면 1, 역일치 -1, 연관성 없으면 0)
'''

# minv, maxv, minloc, maxloc(위치)
_, maxv, _, maxloc = cv2.minMaxLoc(res)
'''
Tip : cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED는 최소 지점(minLoc)이 검출된 위치
Tip : cv2.TM_CCORR, cv2.TM_CCORR_NORMED, cv2.TM_CCOEFF, cv2.TM_CCOEFF_NORMED는 
      최대 지점(maxLoc)이 검출된 위치
'''
print('maxv:', maxv)
print('maxloc:', maxloc) # x, y
'''
maxv: 0.9796600341796875
maxloc: (568, 320)
'''

# 매칭 결과를 빨간색 사각형으로 표시
th, tw = templ.shape[:2]
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
cv2.rectangle(dst, maxloc, (maxloc[0] + tw, maxloc[1] + th), (0,0,255), 2)

cv2.imshow('src', src)
cv2.imshow('templ', templ)
cv2.imshow('src_noise', src_noise)
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()

method

SQDIFF - Distance를 반환
* 상관계수 = [-1, 0 ,1] 사이 값을 가짐

CCOEFF - 밝기에 대한 보정을 한 후에 Correlation= 결과가 좋음
CCOEFF_NORMED - 연산량이 많다는 단점 but 성능이 제일 좋음

찾을 객체
matchTemplate시각화 / 객체 검출

* Tip

만약 내가 찾고자하는 이미지가 없는 이미지에서 실행을 한다?
어느 위치든 박스를 그려 찾아낼 것이다


즉, 찾고자하는 객체가 있으면 maxv가 큰 값을 가질테고

없다면 고만고만한것으로 return 하게 될것인데
이는 Threshold(임계값)을 부여해서 임계값보다 클 경우에만 있다고 판단하게 할 수 있다.

 

인식(Recognition)이란?

- 인식이라는 단어는 너무 거창함

- Classification

- 고양이를 보고 단순히 고양이라고 판단하는 것이 아니라 해당 고양이 사진과
  내가 가지고 있는 고양이 정보와 비교하여 비슷한지를 판단

• Classifying a detected object into different categories.

• 여러 개의 클래스 중에서 가장 유사한 클래스를 선택

 

숫자인식

src = cv2.imread('ch08\\images\\digits_print.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()

# load_images 폴더 이미지 불러오기
def load_digits():
    img_digits = []
    for i in range(10):
        filename = f'ch08\\digits\\digit{i}.bmp'
        img_digits.append(cv2.imread(filename, cv2.IMREAD_GRAYSCALE))

        if img_digits[i] is None:
            print(('Image load failed'))
            return None
    
    return img_digits

# 숫자(index)를 반환
def find_digit(img, img_digits):
    max_idx = -1
    max_ccoeff = -1
    for i in range(10):
        # img_digits.shape (150, 100)
        img = cv2.resize(img, (100, 150))
        res = cv2.matchTemplate(img, img_digits[i], cv2.TM_CCOEFF_NORMED)

        # res = img 영상과, img_digits 영상의 크기가 같으면 (1*1)행렬로 나옴
        # -1 ~ 1 의 값이 나옴
        # == 즉, 최대값을 찾겠다는 code
        if res[0,0] > max_ccoeff:
            max_idx = i
            max_ccoeff = res[0,0]

    return max_idx

# load_digits 함수, 폴더에 있는 이미지 다 불러오기
img_digits = load_digits() # list
print(img_digits[0].shape)

# 입력 영상 이진화 & 레이블링
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
cnt, _, stats, _ = cv2.connectedComponentsWithStats(src_bin)

# 숫자 인식 결과 영상 생성
dst = src.copy()
for i in range(1, cnt):
    (x, y, w, h, area) = stats[i]

    if area < 1000:
        continue

    # 가장 유사한 숫자 이미지를 선택
    # find_digit
    digit = find_digit(src_gray[y:y+h, x:x+w], img_digits)
    cv2.rectangle(dst, (x,y,w,h), (0,255,255))
    cv2.putText(dst, str(digit), (x, y - 4), cv2.FONT_HERSHEY_SIMPLEX,\
                0.7, (0,0,255), 1, cv2.LINE_AA)

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()

728x90

댓글