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()
๊ฒฐ๊ณผ ์๊ฐํ