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

[OpenCV] Image Contour ์ถ”์ถœ | ์ด๋ฏธ์ง€ ์ปจํˆฌ์–ด | ๊ฐ์ฒด ์œค๊ณฝ์„  ์ถ”์ถœ | ๊ธฐ์ดˆ์ ์ธ segmentation ๋ฐฉ๋ฒ•

by ๋ญ…์ฆค 2023. 3. 30.
๋ฐ˜์‘ํ˜•
Image Contour (์ด๋ฏธ์ง€ ์œค๊ณฝ์„ )

์ด๋ฏธ์ง€ ์ปจํˆฌ์–ด ์˜ˆ์‹œ

 

์ด๋ฏธ์ง€ ์œค๊ณฝ์„ (contour)์€ ์ด๋ฏธ์ง€์—์„œ ๊ฐ์ฒด(object)์˜ ์™ธ๊ณฝ ๊ฒฝ๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ณก์„ ์ด๋‹ค. ์ด๋Ÿฌํ•œ ์œค๊ณฝ์„ ์€ ๊ฐ์ฒด์˜ ํ˜•ํƒœ, ํฌ๊ธฐ, ๋ฐฉํ–ฅ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ๋ฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋ฏธ์ง€์—์„œ ๊ฐ์ฒด์˜ ์œ„์น˜๋‚˜ ํฌ๊ธฐ๋ฅผ ๊ฒ€์ถœํ•˜๊ณ , ๊ฐœ์ฒด์˜ ์™ธ๊ณฝ์„ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, ๊ฐœ์ฒด๋ฅผ ๋ถ„ํ• (segmentation)ํ•˜๊ธฐ ์œ„ํ•ด ์œค๊ณฝ์„ ์„ ์ถ”์ถœํ•œ๋‹ค.
 
์œค๊ณฝ์„ ์„ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋Œ€๊ฐœ ์ด๋ฏธ์ง€์˜ ์ด์ง„ํ™”(binary) ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค. ์ด์ง„ํ™”๋œ ์ด๋ฏธ์ง€์—์„œ๋Š” ํฐ์ƒ‰ ํ”ฝ์…€์€ ๊ฐœ์ฒด๋ฅผ, ๊ฒ€์€์ƒ‰ ํ”ฝ์…€์€ ๋ฐฐ๊ฒฝ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
 
์ด์ง„ํ™”๋œ ์ด๋ฏธ์ง€์—์„œ ์œค๊ณฝ์„ ์„ ์ถ”์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ cv2.findContours ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ ์ด๋ฏธ์ง€์—์„œ ์œค๊ณฝ์„ ์„ ์ฐพ์•„ numpy ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋ฐ˜ํ™˜๋œ ๋ฐฐ์—ด์€ ๊ฐ์ฒด์˜ ์œค๊ณฝ์„ ์„ ๊ตฌ์„ฑํ•˜๋Š” ์ ๋“ค์˜ ์ขŒํ‘œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋“ค์„ ์—ฐ๊ฒฐํ•˜๋ฉด ๊ฐ์ฒด์˜ ์œค๊ณฝ์„ ์ด ๋œ๋‹ค.

 

 

OpenCV ์ด๋ฏธ์ง€ ์ปจํˆฌ์–ด : cv2.findContours ํ•จ์ˆ˜
contours, hierarchy = cv2.findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
  • image: 8-bit ๋‹จ์ผ ์ฑ„๋„ ์ด๋ฏธ์ง€. ์ผ๋ฐ˜์ ์œผ๋กœ ์ด์ง„ํ™”๋œ ์ด๋ฏธ์ง€๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ.
  • mode: ์™ธ๊ณฝ์„  ๊ฒ€์ถœ ๋ฐฉ๋ฒ•์„ ์ง€์ •
    • cv2.RETR_EXTERNAL: ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ์˜ ์™ธ๊ณฝ์„ ๋งŒ ๊ฒ€์ถœ.
    • cv2.RETR_LIST: ๋ชจ๋“  ์™ธ๊ณฝ์„ ์„ ๊ฒ€์ถœํ•˜๋˜, ์ƒํ•˜์œ„ ๊ด€๊ณ„๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์Œ
    • cv2.RETR_CCOMP: ์™ธ๊ณฝ์„ ์„ ๋ฐ”๊นฅ์ชฝ ์™ธ๊ณฝ์„ ๊ณผ ๋‚ด๋ถ€ ๊ตฌ๋ฉ์„ ๊ฐ€์ง„ ์™ธ๊ณฝ์„ ์œผ๋กœ ๊ตฌ๋ถ„
    • cv2.RETR_TREE: ๋ชจ๋“  ์™ธ๊ณฝ์„ ์„ ๊ฒ€์ถœํ•˜๊ณ  ์ƒํ•˜์œ„ ๊ด€๊ณ„๊นŒ์ง€ ๊ตฌํ•จ
  • method: ์™ธ๊ณฝ์„  ๊ทผ์‚ฌํ™” ๋ฐฉ๋ฒ•์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    • cv2.CHAIN_APPROX_NONE: ๋ชจ๋“  ์™ธ๊ณฝ์„  ์ขŒํ‘œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • cv2.CHAIN_APPROX_SIMPLE: ์ˆ˜ํ‰, ์ˆ˜์ง, ๋Œ€๊ฐ์„  ๋ฐฉํ–ฅ์˜ ์ ๋“ค์€ ๋ชจ๋‘ ๋ฒ„๋ฆฌ๊ณ , ๋์ ๋งŒ ๋‚จ๊ธฐ๊ณ  ๋‹ค๋ฅธ ์ ๋“ค์€ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜์—ฌ ์ขŒํ‘œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • contours: ๊ฒ€์ถœ๋œ ์™ธ๊ณฝ์„  ์ขŒํ‘œ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฆฌ์ŠคํŠธ
  • hierarchy: ๊ฒ€์ถœ๋œ ์™ธ๊ณฝ์„ ์˜ ๊ณ„์ธต ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฆฌ์ŠคํŠธ
  • offset: ์ด๋ฏธ์ง€ ์ƒ์˜ ์ขŒํ‘œ ๊ฐ’์— ๋Œ€ํ•œ ๋ณด์ •๊ฐ’์„ ์ž…๋ ฅ

 

์ด๋ฏธ์ง€ ์ปจํˆฌ์–ด ์ถ”์ถœ ์˜ˆ์‹œ

 

1. ์ด๋ฏธ์ง€ ๊ทธ๋ ˆ์ด ์Šค์ผ€์ผ ๋ณ€ํ™˜

 

2. ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ

  • OpenCV๋กœ ์ปจํˆฌ์–ด๋ฅผ ์ฐพ๋Š” ๊ฒฝ์šฐ ์–ด๋‘์šด ๋ฐฐ๊ฒฝ์—์„œ ๋ฐ์€ ๊ฐ์ฒด๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด ์œ ๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ๊ฒฝ์ด ๋” ๋ฐ์€ ๊ฒฝ์šฐ์— ์ด๋ฏธ์ง€ ๋ฐ˜์ „ ์‚ฌ์šฉ
  • ์ปจํˆฌ์–ด ์ฐพ๋Š” ์ •ํ™•๋„๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ด๋ฏธ์ง€ ๋ฐ”์ด๋„ˆ๋ฆฌํ™”
  • Canny Edge, Thresholod ๋“ฑ์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐ๊ฒฝ๊ณผ ๊ฐ์ฒด๋ฅผ ๋ถ„๋ฆฌ

 

3. ์ด๋ฏธ์ง€ ์ปจํˆฌ์–ด ์ฐพ๊ธฐ

  • cv2.findContours ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉ
  • ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ถ”์ถœ, ๊ทผ์‚ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
  • ์˜ค์ฐจ ๋ฒ”์œ„๋ฅผ ์„ค์ •ํ•ด ๊ทผ์‚ฌํ™”๋œ ์ปจํˆฌ์–ด ์ถ”์ถœ ๊ฐ€๋Šฅ
  • ํ•„์š”์— ๋”ฐ๋ผ ์ปจํˆฌ์–ด ๋ฉด์ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ฑฐ๋‚˜ ์‚ผ๊ฐํ˜•๋งŒ ์ฐพ๊ฑฐ๋‚˜ ์‚ฌ๊ฐํ˜•๋งŒ ์ฐพ๋Š” ๋“ฑ์˜ ํ•„ํ„ฐ๋ง ๊ฐ€๋Šฅ
import cv2

image = cv2.imread("contour.jpg")
image2 = image.copy()

# ์ปจํˆฌ์–ด ์ฐพ๊ธฐ ์ „ ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
gray = cv2.bitwise_not(gray) # ๊ฐ์ฒด๋ณด๋‹ค ๋ฐฐ๊ฒฝ์ด ๋ฐ์€ ๊ฒฝ์šฐ ์ด๋ฏธ์ง€ ๋ฐ˜์ „

# canny edge, threshold ๋“ฑ ๋‹ค์–‘ํ•œ ์ „์ฒ˜๋ฆฌ ์‹œ๋„ -> ๊ฐ์ฒด์™€ ๋ฐฐ๊ฒฝ์„ ๊ฐ€์žฅ ์ž˜ ๋ถ„๋ฆฌํ•˜๋Š” ์ „์ฒ˜๋ฆฌ ์‚ฌ์šฉ
edge = cv2.Canny(gray, 100, 100)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
adaptive_threshold= cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)

thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)

# ์ปจํˆฌ์–ด ์ฐพ๊ธฐ
contours, hierachy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# ์ปจํˆฌ์–ด ๋ฉด์ ์ด ํฐ ์ˆœ์œผ๋กœ ์ •๋ ฌ
sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)

for i in range(len(sorted_contours)):
    contour = sorted_contours[i]
    
    # ๊ทผ์‚ฌ ์ปจํˆฌ์–ด ๊ณ„์‚ฐ์„ ์œ„ํ•œ 0.01์˜ ์˜ค์ฐจ ๋ฒ”์œ„ ์ง€์ • 
    epsilon = 0.01 * cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, epsilon, True)

    cv2.drawContours(image, [contour], -1, (0,255,0), 3)
    cv2.drawContours(image2, [approx], -1, (0,255,0), 3)

    extLeft = tuple(contour[contour[:, :, 0].argmin()][0])
    extRight = tuple(contour[contour[:, :, 0].argmax()][0])
    extTop = tuple(contour[contour[:, :, 1].argmin()][0])
    extBot = tuple(contour[contour[:, :, 1].argmax()][0])

    cv2.circle(image, extLeft, 8, (0, 0, 255), -1)
    cv2.circle(image, extRight, 8, (0, 255, 0), -1)
    cv2.circle(image, extTop, 8, (255, 0, 0), -1)
    cv2.circle(image, extBot, 8, (255, 255, 0), -1)

# ๊ฒฐ๊ณผ ์ถœ๋ ฅ
cv2.imshow('contour', image)
cv2.imshow('approx', image2)
cv2.waitKey()
cv2.destroyAllWindows()

 

๊ฒฐ๊ณผ ์‹œ๊ฐํ™”

์ขŒ : ๋ฐ˜์ „๋œ ๊ทธ๋ ˆ์ด ์Šค์ผ€์ผ ์ด๋ฏธ์ง€, ์šฐ : canny edge

 

์ขŒ : thresholod, ์šฐ : adaptive threshold

 

์ถ”์ถœ๋œ ์ปจํˆฌ์–ด ์‹œ๊ฐํ™”. ์ขŒ : ์ปจํˆฌ์–ด, ์šฐ : ๊ทผ์‚ฌํ•˜๋œ ์ปจํˆฌ์–ด

 

 

 

๋ฐ˜์‘ํ˜•