๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป Programming/Computer Vision

[OpenCV] Template Matching ํ…œํ”Œ๋ฆฟ ๋งค์นญ | ์ด๋ฏธ์ง€์—์„œ ์œ ์‚ฌํ•œ ๋ถ€๋ถ„ ์ฐพ์•„๋‚ด๊ธฐ

by ๋ญ…์ฆค 2023. 3. 31.
๋ฐ˜์‘ํ˜•
Template Matching

 

์ด๋ฏธ์ง€ ํ…œํ”Œ๋ฆฟ ๋งค์นญ์€ ๋Œ€์ƒ ์ด๋ฏธ์ง€์—์„œ ์ž‘์€ ์ด๋ฏธ์ง€(ํ…œํ”Œ๋ฆฟ)๋ฅผ ์ฐพ๋Š” ๊ธฐ์ˆ ๋กœ, ์ปดํ“จํ„ฐ ๋น„์ „ ๋ถ„์•ผ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ์ˆ  ์ค‘ ํ•˜๋‚˜์ด๋‹ค. OpenCV์—์„œ๋Š” cv2.matchTemplate() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€ ํ…œํ”Œ๋ฆฟ ๋งค์นญ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด ํ•จ์ˆ˜๋Š” ์ž…๋ ฅ ์ด๋ฏธ์ง€์™€ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„์„œ, ์ž…๋ ฅ ์ด๋ฏธ์ง€์—์„œ ํ…œํ”Œ๋ฆฟ๊ณผ ๊ฐ€์žฅ ์œ ์‚ฌํ•œ ๋ถ€๋ถ„์„ ์ฐพ์•„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

๋”ฅ๋Ÿฌ๋‹์„ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ•์ฒ˜๋Ÿผ ๊ฐ์ฒด์˜ deformation์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ ์ž˜ ๋งค์นญ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์•„๋‹ˆ์ง€๋งŒ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€์™€ ์•„์ฃผ ์œ ์‚ฌํ•œ ๋ถ€๋ถ„์ด ์ž…๋ ฅ ์ด๋ฏธ์ง€์— ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.

 

๊ฐ€์žฅ ํฐ ๋‹จ์ ์€ opencv์˜ ํ…œํ”Œ๋ฆฟ ๋งค์นญ์€ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€๋กœ ๋Œ€์ƒ ์ด๋ฏธ์ง€๋ฅผ ์Šฌ๋ผ์ด๋”ฉ ํ•˜๋Š”๋ฐ, ์ด๋•Œ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ๋ฅผ ๋”ฐ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์—†๊ณ  ์ž…๋ ฅ ์ด๋ฏธ์ง€์™€ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ๊ฒฐ์ •๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋•Œ๋ฌธ์— ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€์™€ ์ž…๋ ฅ ์ด๋ฏธ์ง€์˜ ๋น„์œจ์„ ์•Œ์•„์„œ ์ž˜... ์กฐ์ ˆํ•ด์•ผ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

 

 

OpenCV ํ…œํ”Œ๋ฆฟ ๋งค์นญ
  • cv2.matchTemplate() ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๋Š”๋‹ค.
  • image: ๋Œ€์ƒ ์ด๋ฏธ์ง€
  • template: ์ฐพ๊ณ ์ž ํ•˜๋Š” ์ž‘์€ ์ด๋ฏธ์ง€(ํ…œํ”Œ๋ฆฟ)
  • method: ๋งค์นญ ๋ฐฉ๋ฒ•
    • cv2.TM_CCOEFF: ์ƒ๊ด€๊ด€๊ณ„ ๊ณ„์ˆ˜๋ฅผ ์ด์šฉํ•œ ๋งค์นญ
    • cv2.TM_CCOEFF_NORMED: ์ •๊ทœํ™”๋œ ์ƒ๊ด€๊ด€๊ณ„ ๊ณ„์ˆ˜๋ฅผ ์ด์šฉํ•œ ๋งค์นญ
    • cv2.TM_CCORR: ํ”ฝ์…€ ํ•ฉ์„ ์ด์šฉํ•œ ๋งค์นญ
    • cv2.TM_CCORR_NORMED: ์ •๊ทœํ™”๋œ ํ”ฝ์…€ ํ•ฉ์„ ์ด์šฉํ•œ ๋งค์นญ
    • cv2.TM_SQDIFF: ์ œ๊ณฑ ์ฐจ์ด ํ•ฉ์„ ์ด์šฉํ•œ ๋งค์นญ
    • cv2.TM_SQDIFF_NORMED: ์ •๊ทœํ™”๋œ ์ œ๊ณฑ ์ฐจ์ด ํ•ฉ์„ ์ด์šฉํ•œ ๋งค์นญ

cv2.matchTemplate() ํ•จ์ˆ˜๋Š” ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ํžˆํŠธ๋งต์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ํžˆํŠธ๋งต์€ ๋Œ€์ƒ ์ด๋ฏธ์ง€์˜ ๊ฐ ํ”ฝ์…€๋งˆ๋‹ค ํ…œํ”Œ๋ฆฟ๊ณผ์˜ ๋งค์นญ ์ •๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’์„ ๊ฐ€์ง€๋Š” ์ด๋ฏธ์ง€๋กœ ์ด ํžˆํŠธ๋งต์—์„œ ๊ฐ€์žฅ ๊ฐ’์ด ํฐ ์œ„์น˜๋ฅผ ์ฐพ์œผ๋ฉด ๋Œ€์ƒ ์ด๋ฏธ์ง€์—์„œ ํ…œํ”Œ๋ฆฟ๊ณผ ๊ฐ€์žฅ ์ผ์น˜ํ•˜๋Š” ์œ„์น˜๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, OpenCV์—์„œ๋Š” cv2.minMaxLoc() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํžˆํŠธ๋งต์—์„œ ์ตœ๋Œ“๊ฐ’, ์ตœ์†Ÿ๊ฐ’์˜ ์œ„์น˜๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

 

 

ํ…œํ”Œ๋ฆฟ ๋งค์นญ ์˜ˆ์‹œ
  • ์˜ค์‚ฌ์นด์— ์—ฌํ–‰ ๊ฐ€์„œ ์ดฌ์˜ํ•œ ๋„ํ†ค๋ณด๋ฆฌ ์‚ฌ์ง„์—์„œ ๊ธ€๋ฆฌ์ฝ”์ƒ์˜ ์–ผ๊ตด๋งŒ ํฌ๋กญํ•ด์„œ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๊ตฌ๊ธ€์—์„œ ์ฐพ์€ ๋„ํ†ค๋ณด๋ฆฌ ์‚ฌ์ง„์—์„œ ๊ธ€๋ฆฌ์ฝ”์ƒ ์–ผ๊ตด์„ ์ฐพ๋Š” ์˜ˆ์‹œ
  • ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž…๋ ฅ ์ด๋ฏธ์ง€์™€ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€์˜ ์‚ฌ์ด์ฆˆ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ๊ฒฐ์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ๋ฒˆ์˜ ์‹œ๋„ ๋์— ๊ฐ€์žฅ ์ ์ ˆํ•œ ๋น„์œจ์„ ์ฐพ์€ ๊ฒฐ๊ณผ์ด๋‹ค (์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ ์กฐ์ ˆ์„ ์œ„ํ•ด ์ž…๋ ฅ ์ด๋ฏธ์ง€ ๋ณ„๋กœ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€๋ฅผ ๋ฆฌ์‚ฌ์ด์ง•)

 

import cv2

image_list = [cv2.imread('๊ธ€๋ฆฌ์ฝ”์ƒ1.jpg'), cv2.imread('๊ธ€๋ฆฌ์ฝ”์ƒ2.webp'), cv2.imread('๊ธ€๋ฆฌ์ฝ”์ƒ3.jpg')]
template = cv2.imread('๊ธ€๋ฆฌ์ฝ”์ƒ๋จธ๋ฆฌ.png')
template_list = [cv2.resize(template, (40, 40)), cv2.resize(template, (20,20)), cv2.resize(template, (80,80))]

cv2.imshow('template', template)

for i in range(len(image_list)):
    img_draw = image_list[i].copy()
    template_image = template_list[i]
    th, tw = template_image.shape[:2]

    # ํ…œํ”Œ๋ฆฟ ๋งค์นญ 
    res = cv2.matchTemplate(image_list[i], template_image, cv2.TM_CCOEFF_NORMED)

    # ํžˆํŠธ๋งต์—์„œ์˜ ์ตœ์†Ÿ๊ฐ’, ์ตœ๋Œ“๊ฐ’ ๊ตฌํ•˜๊ธฐ
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    top_left = max_loc
    match_val = max_val

    # ๋งค์นญ๋œ ์˜์—ญ ํ‘œ์‹œ 
    bottom_right = (top_left[0] + tw, top_left[1] + th)
    cv2.rectangle(img_draw, top_left, bottom_right, (0,0,255),2)
    cv2.putText(img_draw, str(match_val), top_left, cv2.FONT_HERSHEY_PLAIN, 2,(0,255,0), 1, cv2.LINE_AA)
    cv2.imshow('output', img_draw)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

 

๊ฒฐ๊ณผ ์ด๋ฏธ์ง€
  • ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€ : ๊ธ€๋ฆฌ์ฝ”์ƒ ์–ผ๊ตด
  • ์ž…๋ ฅ ์ด๋ฏธ์ง€ : ๊ตฌ๊ธ€์—์„œ ๊ฒ€์ƒ‰ํ•œ ๋„ํ†ค๋ณด๋ฆฌ 

ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€

 

# ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ตœ์ ํ™” ๋œ ๊ฒฝ์šฐ (ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ง• O)

ํ…œํ”Œ๋ฆฟ ๋งค์นญ ๊ฒฐ๊ณผ

 

 

# ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ตœ์ ํ™” ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ (ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ง• X)

 

์ตœ์ ํ™”๋œ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ์—์„œ ํ…œํ”Œ๋ฆฟ ๋งค์นญ ์Šค์ฝ”์–ด ๋†’๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์–‘ํ•œ ํฌ๊ธฐ์˜ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ€์žฅ ๋†’์€ ์Šค์ฝ”์–ด๋ฅผ ์ฐพ๋Š”๋‹ค๋ฉด ์ ์ ˆํ•œ ์œˆ๋„์šฐ ์‚ฌ์ด์ฆˆ๋ฅผ ์ฐพ์•„ ์ตœ์ ์˜ ํ…œํ”Œ๋ฆฟ ๋งค์นญ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜๋„ ์žˆ์ง€ ์•Š์„๊นŒ? 

๋ฐ˜์‘ํ˜•