๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

AI/Computer Vision

[Computer Vision] ๋ ˆ์ด๋ธ”๋ง๊ณผ ์™ธ๊ฐ์„  ๊ฒ€์ถœ

728x90
๋ฐ˜์‘ํ˜•
๐Ÿ‘€ ๋ณธ ์˜ˆ์ œ๋Š” Window10์˜ VSCode, Python3.11.0๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

๋ ˆ์ด๋ธ”๋ง(Labeling)

์ด์ „ ํฌ์ŠคํŠธ์—์„œ ์˜์ƒ์˜ ์ด์ง„ํ™”๋ฅผ ํ†ตํ•ด ์ฃผ์š” ๊ฐ์ฒด์™€ ๋ฐฐ๊ฒฝ์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๊ฐ์ฒด์™€ ๋ฐฐ๊ฒฝ์„ ๊ตฌ๋ถ„ ํ›„ ๋‹ค์‹œ ๊ฐ๊ฐ์˜ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.

 

์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๋ ˆ์ด๋ธ”๋ง(Labeling)์ด๋ผ๊ณ  ํ•œ๋‹ค.

  • ์˜์ƒ ๋‚ด์— ์กด์žฌํ•˜๋Š” ๊ฐ์ฒด ํ”ฝ์…€ ์ง‘ํ•ฉ์— ๊ณ ์œ  ๋ฒˆํ˜ธ๋ฅผ ๋งค๊ธฐ๋Š” ์ž‘์—…์œผ๋กœ ์—ฐ๊ฒฐ๋œ ๊ตฌ์„ฑ ์š”์†Œ ๋ ˆ์ด๋ธ”๋ง(Connected Components Labeling)์ด๋ผ๊ณ ๋„ ํ•œ๋‹ค.

 

์ด๋ฅผ ํ†ตํ•ด  ๊ฐ ๊ฐ์ฒด์˜ ์œ„์น˜, ํฌ๊ธฐ ๋“ฑ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ์ž‘์—…์€ ๊ฐ์ฒด ์ธ์‹์„ ์œ„ํ•œ ์ „์ฒ˜๋ฆฌ ๊ณผ์ •์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

 

์˜์ƒ์˜ ๋ ˆ์ด๋ธ”๋ง์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ด์ง„ํ™”๋„๋‹ˆ ์˜์ƒ์—์„œ ์ˆ˜ํ–‰๋œ๋‹ค. ์ด๋•Œ ๊ฒ€์€์ƒ‰ ํ”ฝ์…€์€ ๋ฐฐ๊ฒฝ์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ , ํฐ์ƒ‰ ํ”ฝ์…€์€ ๊ฐ์ฒด๋กœ ๊ฐ„์ฃผํ•œ๋‹ค.

 

ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋Š” ํ•œ ๊ฐœ ์ด์ƒ์˜ ์ธ์ ‘ํ•œ ํ”ฝ์…€๋กœ ์ด๋ฃจ์–ด์ง€๋ฉฐ, ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ชจ๋“  ํ”ฝ์…€์—๋Š” ๊ฐ™์€ ๋ ˆ์ด๋ธ” ๋ฒˆํ˜ธ๊ฐ€ ์ง€์ •๋œ๋‹ค.

 

ํŠน์ • ํ”ฝ์…€๊ณผ ์ด์›ƒํ•œ ํ”ฝ์…€์˜ ์—ฐ๊ฒฐ ๊ด€๊ณ„๋Š” ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • 4-๋ฐฉํ–ฅ ์—ฐ๊ฒฐ์„ฑ(4-way connectivity) : ํŠน์ • ํ”ฝ์…€์˜ ์ƒํ•˜์ขŒ์šฐ๋กœ ๋ถ™์–ด ์žˆ๋Š” ํ”ฝ์…€๋ผ๋ฆฌ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค๊ณ  ์ •์˜
  • 8-๋ฐฉํ–ฅ ์—ฐ๊ฒฐ์„ฑ(8-way connectivity) : ์ƒํ•˜์ขŒ์šฐ๋กœ ์—ฐ๊ฒฐ๋œ ํ”ฝ์…€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋Œ€๊ฐ์„  ๋ฐฉํ–ฅ์œผ๋กœ ์ธ์ ‘ํ•œ ํ”ฝ์…€๋„ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค๊ณ  ์ •์˜.

 

4-way connectivity(์ขŒ), 8-way connectivity(์šฐ)

 

 

๋ ˆ์ด๋ธ”๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

import cv2
import random

# ์ด๋ฏธ์ง€ ๋กœ๋“œ
image = cv2.imread('test.png')
if image is None:
    print("์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    exit()
sp = image.shape
image = cv2.resize(image,(int(sp[1]*0.5),int(sp[0]*0.5)),cv2.INTER_LANCZOS4)

# ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# ์ด์ง„ํ™”
_, thresh = cv2.threshold(gray, 162, 255, cv2.THRESH_BINARY)
cv2.imshow("thres",thresh)

# ์—ฐ๊ฒฐ๋œ ์ปดํฌ๋„ŒํŠธ ์ฐพ๊ธฐ
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh, connectivity=8)

# ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค ๊ทธ๋ฆฌ๊ธฐ
for i in range(1, num_labels):  # 0์€ ๋ฐฐ๊ฒฝ์ด๋ฏ€๋กœ 1๋ถ€ํ„ฐ ์‹œ์ž‘
    # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค ์ •๋ณด
    x = stats[i, cv2.CC_STAT_LEFT]
    y = stats[i, cv2.CC_STAT_TOP]
    w = stats[i, cv2.CC_STAT_WIDTH]
    h = stats[i, cv2.CC_STAT_HEIGHT]

    # ๋žœ๋ค ์ƒ‰์ƒ ์ƒ์„ฑ
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค ๊ทธ๋ฆฌ๊ธฐ
    cv2.rectangle(image, (x, y), (x + w, y + h), color, 2)

    # ์ค‘์‹ฌ์  ๊ณ„์‚ฐ
    cX = int(centroids[i, 0])
    cY = int(centroids[i, 1])

# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ
cv2.imshow('Labeled Image with Bounding Boxes', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

 

์™ธ๊ฐ์„  ๊ฒ€์ถœ(Contour)

๊ฐ์ฒด์˜ ์™ธ๊ฐ์„ ์€ ๊ฐ์ฒด ์˜์—ญ ํ”ฝ์…€ ์ค‘์—์„œ ๋ฐฐ๊ฒฝ ์˜์—ญ๊ณผ ์ธ์ ‘ํ•œ ์ผ๋ จ์˜ ํ”ฝ์…€์„ ์˜๋ฏธํ•œ๋‹ค.

 

๋ณดํ†ต ๊ฒ€์€ ๋ฐฐ๊ฒฝ ๋‚ด ํฐ์ƒ‰ ๊ฐ์ฒด ์˜์—ญ์˜ ๊ฐ€์žฅ ์ตœ์™ธ๊ณฝ์— ์žˆ๋Š” ํ”ฝ์…€์„ ์ฐพ์•„ ์™ธ๊ฐ์„ ์œผ๋กœ ์ •์˜ํ•œ๋‹ค.

 

๋˜ ํฐ์ƒ‰ ๊ฐ์ฒด ์˜์—ญ ๋‚ด ๊ฒ€์€์ƒ‰์œผ๋กœ ํ™€(hole)์ด ์žˆ๋Š” ๊ฒฝ์šฐ ํ™€์„ ๋‘˜๋Ÿฌ์‹ผ ๊ฐ์ฒด ํ”ฝ์…€๋“ค๋„ ์™ธ๊ฐ์„ ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

import cv2
import numpy as np
import random
# ์ด๋ฏธ์ง€ ๋กœ๋“œ
image = cv2.imread('test.png')
sp = image.shape
image = cv2.resize(image,(int(sp[1]*0.5),int(sp[0]*0.5)),cv2.INTER_LANCZOS4)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# ์ด์ง„ํ™”
_, thresh = cv2.threshold(gray, 162, 255, cv2.THRESH_BINARY)
cv2.imshow("thres",thresh)
# ์œค๊ณฝ์„  ์ฐพ๊ธฐ (8-way connectivity)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# ์œค๊ณฝ์„ ์— ๋ผ๋ฒจ ๋ถ™์ด๊ธฐ
for i, contour in enumerate(contours):
    # ์œค๊ณฝ์„ ์˜ ์ค‘์‹ฌ์  ๊ณ„์‚ฐ
    M = cv2.moments(contour)
    if M['m00'] != 0:
        cX = int(M['m10'] / M['m00'])
        cY = int(M['m01'] / M['m00'])
    else:
        cX, cY = 0, 0

    # ๋žœ๋ค ์ƒ‰์ƒ ์ƒ์„ฑ
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    
    # ์œค๊ณฝ์„  ๊ทธ๋ฆฌ๊ธฐ
    cv2.drawContours(image, contours, i, color, 2)

# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ
cv2.imshow('Labeled Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

๋”๊ธ€๋ผ์Šค-ํฌ์ด์ปค(Douglas-Peucker) ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์™ธ๊ฐ์„ ์„ ๋‹จ์ˆœํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

import cv2
import random

# ์ด๋ฏธ์ง€ ๋กœ๋“œ
image = cv2.imread('test.png')
if image is None:
    print("์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    exit()
sp = image.shape
image = cv2.resize(image,(int(sp[1]*0.5),int(sp[0]*0.5)),cv2.INTER_LANCZOS4)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.Canny(gray,50,200)
cv2.imshow("thres",thresh)
# ์™ธ๊ณฝ์„  ์ฐพ๊ธฐ
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# ์™ธ๊ณฝ์„  ๊ทผ์‚ฌํ™”
for contour in contours:
    # ํ—ˆ์šฉ ์˜ค์ฐจ ์„ค์ •
    epsilon = 0.02 * cv2.arcLength(contour, True)  # ์ฃผ๋ณ€ ๊ธธ์ด์— ๋น„๋ก€
    approx = cv2.approxPolyDP(contour, epsilon, True)
     # ๋žœ๋ค ์ƒ‰์ƒ ์ƒ์„ฑ
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    # ๊ทผ์‚ฌํ™”๋œ ์™ธ๊ณฝ์„  ๊ทธ๋ฆฌ๊ธฐ
    cv2.drawContours(image, [approx], -1, color, 2)

# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ
cv2.imshow('Approximated Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

์™ธ๊ฐ์„ ์„ ์ผ์ • ๋น„์œจ๋กœ ๊ทผ์‚ฌํ™”ํ•œ๋‹ค.

 

728x90
๋ฐ˜์‘ํ˜•