๐ ๊ณต๋ถํ๋ ์ง์ง์ํ์นด๋ ์ฒ์์ด์ง?
[v0.26]์์์ฒ๋ฆฌ_์ฐ์ ์์ญ ๋ถํ ๋ณธ๋ฌธ
[v0.26]์์์ฒ๋ฆฌ_์ฐ์ ์์ญ ๋ถํ
์ง์ง์ํ์นด 2022. 1. 12. 23:34220112 ์์ฑ
<๋ณธ ๋ธ๋ก๊ทธ๋ ๊ทํ์ด ์์ฌ๋์ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํด์ ๊ณต๋ถํ๋ฉฐ ์์ฑํ์์ต๋๋ค>
OpenCV - 24. ์ฐ์ ์์ญ ๋ถํ (๊ฑฐ๋ฆฌ ๋ณํ, ๋ ์ด๋ธ๋ง, ์ ์ฑ์ฐ๊ธฐ, ์ํฐ์ ฐ๋, ๊ทธ๋ฉ์ปท, ํ๊ท ์ด๋ ํํฐ)
์ด์ ํฌ์คํ ์์๋ ์ธ๊ณฝ ๊ฒฝ๊ณ๋ฅผ ์ด์ฉํด์ ๊ฐ์ฒด ์์ญ์ ๋ถํ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ดค์ต๋๋ค. ํ์ง๋ง ์ค์ ์ด๋ฏธ์ง์๋ ๋ ธ์ด์ฆ๋ ๋ง๊ณ , ๊ฒฝ๊ณ์ ์ด ๋ช ํํ์ง ์์ ๊ฐ์ฒด ์์ญ์ ์ ํํ ๋ถํ ํ๋ ๊ฒ์ด
bkshin.tistory.com
1. ๊ฑฐ๋ฆฌ ๋ณํ (Distatnce Transformation)
: ์ฐ์๋ ์์ญ์ ์ฐพ์ ๋ถํ
: ์ด๋ฏธ์ง์์ ๋ฌผ์ฒด ์์ญ์ ์ ํํ ํ์ ํ๊ธฐ ์ํด์๋ ๋ฌผ์ฒด ์์ญ์ ๋ผ๋๋ฅผ ์ฐพ์์ผ ํจ
: ๋ผ๋๋ฅผ ๊ฒ์ถํ๋ ๋ฐฉ๋ฒ ์ค ํ๋๊ฐ ์ธ๊ณฝ ๊ฒฝ๊ณ๋ก๋ถํฐ ๊ฐ์ฅ ๋ฉ๋ฆฌ ๋จ์ด์ง ๊ณณ์ ์ฐพ๋ ๋ฐฉ๋ฒ
cv2.distanceTransform(src, distanceType, maskSize)
: ๊ฑฐ๋ฆฌ ๋ณํ
- src : ์
๋ ฅ ์์, ๋ฐ์ด๋๋ฆฌ ์ค์ผ์ผ
- distanceType : ๊ฑฐ๋ฆฌ ๊ณ์ฐ ๋ฐฉ์
- cv2.DIST_L2
- cv2.DIST_L1
- cv2.DIST_L12
- cv2.DIST_FAIR
- cv2.DIST_WELSCH
- cv2.DIST_HUBER
- maskSize : ๊ฑฐ๋ฆฌ ๋ณํ ์ปค๋ ํฌ๊ธฐ
# ๊ฑฐ๋ฆฌ ๋ณํ์ผ๋ก ์ ์ ์ค์ผ๋ ํค ์ฐพ๊ธฐ
import cv2
import numpy as np
# ์ด๋ฏธ์ง๋ฅผ ์ฝ์ด์ ๋ฐ์ด๋๋ฆฌ ์ค์ผ์ผ๋ก ๋ณํ
img = cv2.imread('img/full_body.jpg', cv2.IMREAD_GRAYSCALE)
_, biimg = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
# ๊ฑฐ๋ฆฌ ๋ณํ ---โ
dst = cv2.distanceTransform(biimg, cv2.DIST_L2, 3)
# ๊ฑฐ๋ฆฌ ๊ฐ์ 0 ~ 255 ๋ฒ์๋ก ์ ๊ทํ ---โก
dst = (dst/(dst.max()-dst.min()) * 255).astype(np.uint8)
# ๊ฑฐ๋ฆฌ ๊ฐ์ ์ฐ๋ ์ํ๋๋ก ์์ ํ ๋ผ๋ ์ฐพ๊ธฐ ---โข
skeleton = cv2.adaptiveThreshold(dst, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
cv2.THRESH_BINARY, 7, -3)
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('origin', img)
cv2.imshow('dist', dst)
cv2.imshow('skel', skeleton)
cv2.waitKey(0)
cv2.destroyAllWindows()
- ๊ฐ์ด๋ฐ ์ด๋ฏธ์ง : ์ ๊ทํ ํ๋๋ฐ ์ธ๊ณฝ ๊ฒฝ๊ณ๋ก๋ถํฐ ๋ฉ์ด์ง์๋ก ํฐ์์ด ์ ๋ณด์ธ๋น
- ์ค๋ฅธ์ชฝ ์ด๋ฏธ์ง : ํฝ์ ๊ฐ==255๋ง ์ถ์ถ ํ์. ์ด๋ฏธ์ง์ ๋ผ๋, ๊ฒฝ๊ณ์์ ๊ฐ์ฅ ๋ฉ๋ฆฌ ๋จ์ด์ ธ ์๋ ๋ถ๋ถ ์ถ์ถ
=> ๊ฑฐ๋ฆฌ๋ณํ์ ํตํ ๋ผ๋ ์ถ์ถ
2. ๋ ์ด๋ธ๋ง (Labeling)
: ์ฐ๊ฒฐ๋ ์์๋ผ๋ฆฌ ๋ถ๋ฆฌ
: ์ด๋ฏธ์ง์์ ํฝ์ ๊ฐ์ด 0์ผ๋ก ๋์ด์ง์ง ์๋ ๋ถ๋ถ๋ผ๋ฆฌ ๊ฐ์ ๊ฐ์ ๋ถ์ฌํด์ ๋ถ๋ฆฌ
retval, labels = cv2.connectedComponents(src, labels, connectivity=8, ltype)
: ์ด๋ฏธ์ง ์ ์ฒด์์ 0์ผ๋ก ๋์ด์ง์ง ์๋ ๋ถ๋ถ๋ผ๋ฆฌ ๊ฐ์ ๊ฐ์ ๋ถ์ฌ
: ์ฐ๊ฒฐ ์์ ๋ ์ด๋ธ๋ง๊ณผ ๊ฐ์ ๋ฐํ
- src : ์
๋ ฅ ์ด๋ฏธ์ง, ๋ฐ์ด๋๋ฆฌ ์ค์ผ์ผ
- labels(optional) : ๋ ์ด๋ธ๋ง๋ ์
๋ ฅ ์ด๋ฏธ์ง์ ๊ฐ์ ํฌ๊ธฐ์ ๋ฐฐ์ด
- connectivity(optional) : ์ฐ๊ฒฐ์ฑ์ ๊ฒ์ฌํ ๋ฐฉํฅ ๊ฐ์(4, 8 ์ค ์ ํ)
- ltype(optional) : ๊ฒฐ๊ณผ ๋ ์ด๋ธ ๋ฐฐ์ด dtype
- retval(optional) : ๋ ์ด๋ธ ๊ฐ์
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(src, labels, stats, centroids, connectivity, ltype)
: ๋ ์ด๋ธ๋ง๋ ๊ฐ์ข ์ํ ์ ๋ณด ๋ฐํ
- stats : N x 5 ํ๋ ฌ (N: ๋ ์ด๋ธ ๊ฐ์) [x์ขํ, y์ขํ, ํญ, ๋์ด, ๋๋น]
- centroids : ๊ฐ ๋ ์ด๋ธ์ ์ค์ฌ์ ์ขํ, N x 2 ํ๋ ฌ (N: ๋ ์ด๋ธ ๊ฐ์)
# ์ฐ๊ฒฐ๋ ์์ญ ๋ ์ด๋ธ๋ง
import cv2
import numpy as np
# ์ด๋ฏธ์ง ์ฝ๊ธฐ
img = cv2.imread('img/shape_donut.JPG')
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์์ฑ
img2 = np.zeros_like(img)
# ๊ทธ๋ ์ด ์ค์ผ์ผ๊ณผ ๋ฐ์ด๋๋ฆฌ ์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, th = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# ์ฐ๊ฒฐ๋ ์์ ๋ ์ด๋ธ๋ง ์ ์ฉ ---โ
cnt, labels = cv2.connectedComponents(th)
#retval, labels, stats, cent = cv2.connectedComponentsWithStats(th)
# ๋ ์ด๋ธ ๊ฐฏ์ ๋งํผ ์ํ
for i in range(cnt):
# ๋ ์ด๋ธ์ด ๊ฐ์ ์์ญ์ ๋๋คํ ์์ ์ ์ฉ ---โก
img2[labels==i] = [int(j) for j in np.random.randint(0,255, 3)]
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
cv2.imshow('origin', img)
cv2.imshow('labeled', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
- ์ฐ๊ฒฐ๋ ๋ถ๋ถ๋ผ๋ฆฌ ๊ฐ์ ์์์ด ์น ํด์ง
- cv2.connectedComponents() ํจ์๋ ์ด๋ฏธ์ง ์ ์ฒด์์ 0์ผ๋ก ๋์ด์ง์ง ์๋ ๋ถ๋ถ๋ผ๋ฆฌ ๊ฐ์ ๊ฐ์ ๋ถ์ฌ
- ๋ฐํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ํํ๋ฉด์ ๊ฐ์ ๊ฐ๋ผ๋ฆฌ๋ ๊ฐ์ ์์
3. ์ ์ฑ์ฐ๊ธฐ
retval, img, mask, rect = cv2.floodFill(img, mask, seed, newVal, loDiff, upDiff, flags)
: ์ฐ์๋๋ ์์ญ์ ๊ฐ์ ์์์ ์ฑ์ ๋ฃ๋ ๊ธฐ๋ฅ
- img : ์
๋ ฅ ์ด๋ฏธ์ง, 1 ๋๋ 3์ฑ๋
- mask : ์
๋ ฅ ์ด๋ฏธ์ง๋ณด๋ค 2 x 2 ํฝ์
์ด ๋ ํฐ ๋ฐฐ์ด, 0์ด ์๋ ์์ญ์ ๋ง๋๋ฉด ์ฑ์ฐ๊ธฐ ์ค์ง
- seed : ์ฑ์ฐ๊ธฐ ์์ํ ์ขํ
- newVal : ์ฑ์ฐ๊ธฐ์ ์ฌ์ฉํ ์์ ๊ฐ
- loDiff, upDiff(optional) : ์ฑ์ฐ๊ธฐ ์งํ์ ๊ฒฐ์ ํ ์ต์/์ต๋ ์ฐจ์ด ๊ฐ
- flags(optional) : ์ฑ์ฐ๊ธฐ ๋ฐฉ์ ์ ํ
- cv2.FLOODFILL_MASK_ONLY : img๊ฐ ์๋ mask์๋ง ์ฑ์ฐ๊ธฐ ์ ์ฉ
- cv2.FLOODFILL_FIXED_RANGE : ์ด์ ํฝ์ ์ด ์๋ seed ํฝ์ ๊ณผ ๋น๊ต
- retval : ์ฑ์ฐ๊ธฐ ํ ํฝ์
์ ๊ฐ์
- rect : ์ฑ์ฐ๊ธฐ๊ฐ ์ด๋ฃจ์ด์ง ์์ญ์ ๊ฐ์ธ๋ ์ฌ๊ฐํ
: img ์ด๋ฏธ์ง์ seed ์ขํ์์๋ถํฐ ์์ํด์ newVal์ ๊ฐ์ผ๋ก ์ฑ์ฐ๊ธฐ๋ฅผ ์์
: ์ด์ํ๋ ํฝ์ ์ ์ฑ์ฐ๊ธฐ๋ฅผ ๊ณ์ํ๋ ค๋ฉด ํ์ฌ ํฝ์ ์ด ์ด์ ํฝ์ ์ loDiff๋ฅผ ๋บ ๊ฐ๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ๊ณ
upDiff๋ฅผ ๋ํ ๊ฐ๋ณด๋ค ์๊ฑฐ๋ ๊ฐ๊ธฐ
=> ์ด์ ํฝ์ - loDiff <= ํ์ฌ ํฝ์ <= ์ด์ ํฝ์ + upDiff
: (๋ง์ฝ loDiff์ upDiff๋ฅผ ์๋ตํ๋ฉด seed์ ํฝ์ ๊ฐ๊ณผ ๊ฐ์ ๊ฐ์ ๊ฐ๋ ์ด์ ํฝ์ ๋ง ์ฑ์ฐ๊ธฐ๋ฅผ ์งํ)
: flags์ cv2.FLOODFILL_FIXED_RANGE๊ฐ ์ ๋ฌ๋๋ฉด ์ด์ ํฝ์ ์ด ์๋ seed ํฝ์ ๊ณผ ๋น๊ตํ๋ฉฐ ์์ ์ฑ์ฐ๊ธฐ
: flags์ cv2.FLOODFILL_MASK_ONLY๊ฐ ์ ๋ฌ๋๋ฉด img์ ์ฑ์ฐ๊ธฐ๋ฅผ ํ์ง ์๊ณ mask์๋ง ์ฑ์ฐ๊ธฐ
# ๋ง์ฐ์ค๋ก ์ ์ฑ์ฐ๊ธฐ
import cv2
import numpy as np
img = cv2.imread('img/taekwonv1.jpg')
rows, cols = img.shape[:2]
# ๋ง์คํฌ ์์ฑ, ์๋ ์ด๋ฏธ์ง ๋ณด๋ค 2ํฝ์
ํฌ๊ฒ ---โ
mask = np.zeros((rows+2, cols+2), np.uint8)
# ์ฑ์ฐ๊ธฐ์ ์ฌ์ฉํ ์ ---โก
newVal = (255,255,255)
# ์ต์ ์ต๋ ์ฐจ์ด ๊ฐ ---โข
loDiff, upDiff = (10,10,10), (10,10,10)
# ๋ง์ฐ์ค ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์
def onMouse(event, x, y, flags, param):
global mask, img
if event == cv2.EVENT_LBUTTONDOWN:
seed = (x,y)
# ์ ์ฑ์ฐ๊ธฐ ์ ์ฉ ---โฃ
retval = cv2.floodFill(img, mask, seed, newVal, loDiff, upDiff)
# ์ฑ์ฐ๊ธฐ ๋ณ๊ฒฝ ๊ฒฐ๊ณผ ํ์ ---โค
cv2.imshow('img', img)
# ํ๋ฉด ์ถ๋ ฅ
cv2.imshow('img', img)
cv2.setMouseCallback('img', onMouse)
cv2.waitKey(0)
cv2.destroyAllWindows()
- mask ๋ ์๋ณธ ์ด๋ฏธ์ง์ ํฌ๊ธฐ๋ณด๋ค ๋์ด, ๋๋น๋ฅผ 2๋ฐฐ ํฌ๊ฒ ๋ง๋ค๊ธฐ
- ์ฑ์ฐ๊ธฐ ์์ (255, 255, 255) ํฐ์ ์ฌ์ฉ
- loDiff์ upDiff, ์ด์ํ ํฝ์ ๊ณผ์ ์ต์/์ต๋ ์ฐจ์ด ๊ฐ์ ๊ฐ 10
4. ์ํฐ์ ฐ๋ (Watershed)
: ๊ฐ๋ฌผ์ด ํ ์ค๊ธฐ๋ก ํ๋ฅด๋ค๊ฐ ๊ฐ๋ผ์ง๋ ๊ฒฝ๊ณ์ธ ๋ถ์๋ น
: ์ ์ฑ์ฐ๊ธฐ(flood fill)๊ณผ ๋น์ทํ ๋ฐฉ์์ผ๋ก ์ฐ์๋ ์์ญ์ ์ฐพ๋ ๊ฒ
: seed๋ฅผ ํ๋๊ฐ ์๋ ์ฌ๋ฌ ๊ฐ๋ฅผ ์ง์ ํ ์ ์๊ณ ์ด๋ฅผ ๋ง์ปค
markers = cv2.watershed(img, markers)
- img : ์
๋ ฅ ์ด๋ฏธ์ง
- markers : ๋ง์ปค, ์
๋ ฅ ์ด๋ฏธ์ง์ ํฌ๊ธฐ๊ฐ ๊ฐ์ 1์ฐจ์ ๋ฐฐ์ด(int32)
: markers๋ ์ ๋ ฅ ์ด๋ฏธ์ง์ ํ๊ณผ ์ด ํฌ๊ธฐ๊ฐ ๊ฐ์ 1์ฐจ์ ๋ฐฐ์ด๋ก ์ ๋ฌ
: markers์ ๊ฐ์ ๊ฒฝ๊ณ๋ฅผ ์ฐพ๊ณ ์ ํ๋ ํฝ์ ์์ญ์ -1์ ๊ฐ๊ฒ ํ๊ณ
๋๋จธ์ง ์ฐ๊ฒฐ๋ ์์ญ์ ๋ํด์๋ ๋์ผํ ์ ์ ๊ฐ์ ๊ฐ๊ฒ ํจ
# ๋ง์ฐ์ค์ ์ํฐ์
ฐ๋๋ก ๋ฐฐ๊ฒฝ ๋ถ๋ฆฌ
import cv2
import numpy as np
img = cv2.imread('img/jadu.jpg')
rows, cols = img.shape[:2]
img_draw = img.copy()
# ๋ง์ปค ์์ฑ, ๋ชจ๋ ์์๋ 0์ผ๋ก ์ด๊ธฐํ ---โ
marker = np.zeros((rows, cols), np.int32)
markerId = 1 # ๋ง์ปค ์์ด๋๋ 1์์ ์์
colors = [] # ๋ง์ปค ์ ํํ ์์ญ ์์ ์ ์ฅํ ๊ณต๊ฐ
isDragging = False # ๋๋๊ทธ ์ฌ๋ถ ํ์ธ ๋ณ์
# ๋ง์ฐ์ค ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์
def onMouse(event, x, y, flags, param):
global img_draw, marker, markerId, isDragging
if event == cv2.EVENT_LBUTTONDOWN: # ์ผ์ชฝ ๋ง์ฐ์ค ๋ฒํผ ๋ค์ด, ๋๋๊ทธ ์์
isDragging = True
# ๊ฐ ๋ง์ปค์ ์์ด๋์ ํ ์์น์ ์์ ๊ฐ์ ์์ผ๋ก ๋งคํํด์ ์ ์ฅ
colors.append((markerId, img[y,x]))
elif event == cv2.EVENT_MOUSEMOVE: # ๋ง์ฐ์ค ์์ง์
if isDragging: # ๋๋๊ทธ ์งํ ์ค
# ๋ง์ฐ์ค ์ขํ์ ํด๋นํ๋ ๋ง์ปค์ ์ขํ์ ๋์ผํ ๋ง์ปค ์์ด๋๋ก ์ฑ์ ๋ฃ๊ธฐ ---โก
marker[y,x] = markerId
# ๋ง์ปค ํ์ํ ๊ณณ์ ๋นจ๊ฐ์์ ์ผ๋ก ํ์ํด์ ์ถ๋ ฅ
cv2.circle(img_draw, (x,y), 3, (0,0,255), -1)
cv2.imshow('watershed', img_draw)
elif event == cv2.EVENT_LBUTTONUP: # ์ผ์ชฝ ๋ง์ฐ์ค ๋ฒํผ ์
if isDragging:
isDragging = False # ๋๋๊ทธ ์ค์ง
# ๋ค์ ๋ง์ปค ์ ํ์ ์ํด ๋ง์ปค ์์ด๋ ์ฆ๊ฐ ---โข
markerId +=1
elif event == cv2.EVENT_RBUTTONDOWN: # ์ค๋ฅธ์ชฝ ๋ง์ฐ์ค ๋ฒํผ ๋๋ฆ
# ๋ชจ์ ๋์ ๋ง์ปค๋ฅผ ์ด์ฉํด์ ์ํฐ ์๋ ์ ์ฉ ---โฃ
cv2.watershed(img, marker)
# ๋ง์ปค์ -1๋ก ํ์๋ ๊ฒฝ๊ณ๋ฅผ ์ด๋ก์์ผ๋ก ํ์ ---โค
img_draw[marker == -1] = (0,255,0)
for mid, color in colors: # ์ ํํ ๋ง์ปค ์์ด๋ ๊ฐฏ์ ๋งํผ ๋ฐ๋ณต
# ๊ฐ์ ๋ง์ปค ์์ด๋ ๊ฐ์ ๊ฐ๋ ์์ญ์ ๋ง์ปค ์ ํํ ์์์ผ๋ก ์ฑ์ฐ๊ธฐ ---โฅ
img_draw[marker==mid] = color
cv2.imshow('watershed', img_draw) # ํ์ํ ๊ฒฐ๊ณผ ์ถ๋ ฅ
# ํ๋ฉด ์ถ๋ ฅ
cv2.imshow('watershed', img)
cv2.setMouseCallback('watershed', onMouse)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 1์ ๋ฐฐ๊ฒฝ, 2๋ ์ ๊ฒฝ
- ์ด๋ฏธ์ง์ ์ ๊ฒฝ ๋ฐฐ๊ฒฝ ๋ถ๋ฆฌ !
- cv2.watershed(img, marker)
=> ์ํฐ์ ฐ๋๋ฅผ ์คํํ๋ฉด ๊ฒฝ๊ณ์ ํด๋นํ๋ ์์ญ์ -1๋ก ์ฑ์์ง๊ณ ์ ๊ฒฝ์ 1, ๋ฐฐ๊ฒฝ์ 2๋ก ์ฑ์์ง๋ค
5. ๊ทธ๋ฉ์ปท (Graph Cut)
: ์ฌ์ฉ์๊ฐ ์ ๊ฒฝ(๋ฐฐ๊ฒฝ์ด ์๋ ๋ถ๋ถ)์ผ๋ก ๋ถ๋ฆฌํ ๋ถ๋ถ์ ์ฌ๊ฐํ ํ์๋ฅผ ํด์ฃผ๋ฉด
์ ๊ฒฝ๊ณผ ๋ฐฐ๊ฒฝ์ ์์ ๋ถํฌ๋ฅผ ์ถ์ ํด์ ๋์ผํ ๋ ์ด๋ธ์ ๊ฐ์ง ์ฐ๊ฒฐ๋ ์์ญ์์ ์ ๊ฒฝ๊ณผ ๋ฐฐ๊ฒฝ์ ๋ถ๋ฆฌ
mask, bgdModel, fgdModel = cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode)
- img : ์
๋ ฅ ์ด๋ฏธ์ง
- mask : ์
๋ ฅ ์ด๋ฏธ์ง์ ํฌ๊ธฐ๊ฐ ๊ฐ์ 1 ์ฑ๋ ๋ฐฐ์ด, ๋ฐฐ๊ฒฝ๊ณผ ์ ๊ฒฝ์ ๊ตฌ๋ถํ๋ ๊ฐ์ ์ ์ฅ
- cv2.GC_BGD : ํ์คํ ๋ฐฐ๊ฒฝ(0)
- cv2.GC_FGD : ํ์คํ ์ ๊ฒฝ(1)
- cv2.GC_PR_BGD : ์๋ง๋ ๋ฐฐ๊ฒฝ(2)
- cv2.GC_PR_FGD : ์๋ง๋ ์ ๊ฒฝ(3)
- rect : ์ ๊ฒฝ์ด ์์ ๊ฒ์ผ๋ก ์ถ์ธก๋๋ ์์ญ์ ์ฌ๊ฐํ ์ขํ, ํํ (x1, y1, x2, y2)
- bgdModel, fgdModel : ํจ์ ๋ด์์ ์ฌ์ฉํ ์์ ๋ฐฐ์ด ๋ฒํผ (์ฌ์ฌ์ฉํ ๊ฒฝ์ฐ ์์ ํ์ง ๋ง ๊ฒ)
- iterCount : ๋ฐ๋ณต ํ์
- mode(optional) : ๋์ ๋ฐฉ๋ฒ
- cv2.GC_INIT_WITH_RECT : rect์ ์ง์ ํ ์ขํ๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฉ์ปท ์ํ
- cv2.GC_INIT_WITH_MASK : mask์ ์ง์ ํ ๊ฐ์ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฉ์ปท ์ํ
- cv2.GC_EVAL : ์ฌ์๋
1) mode์ cv2.GC_INIT_WITH_RECT๋ฅผ ์ ๋ฌํ๋ฉด ์ธ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ธ rect์ ์ ๋ฌํ ์ฌ๊ฐํ ์ขํ๋ฅผ ๊ฐ์ง๊ณ ์ ๊ฒฝ๊ณผ ๋ฐฐ๊ฒฝ์ ๋ถ๋ฆฌ
2) ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ธ mask์ ํ ๋นํด ๋ฐํ
3) mask์ ํ ๋น๋ฐ์ ๊ฐ์ด 0๊ณผ 1์ด๋ฉด ํ์คํ ๋ฐฐ๊ฒฝ, ์ ๊ฒฝ์ ์๋ฏธํ๊ณ ,
2์ 3์ด๋ฉด ์๋ง๋ ๋ฐฐ๊ฒฝ, ์ ๊ฒฝ์ผ ๊ฐ๋ฅ์ฑ์ด ์๋ค
4) 1์ฐจ์ ์ผ๋ก ๋ฐฐ๊ฒฝ๊ณผ ์ ๊ฒฝ์ ๊ตฌ๋ถํ ๋ค mode์ cv2.GC_INIT_WITH_MASK๋ฅผ ์ง์ ํด์ ๋ค์ ํธ์ถํ๋ฉด ์ข ๋ ์ ํํ mas
5) bgdModel๊ณผ fgdModel์ ํจ์๊ฐ ๋ด๋ถ์ ์ผ๋ก ์ฐ์ฐ์ ์ฌ์ฉํ๋ ์์ ๋ฐฐ์ด๋ก ๋ค์ ํธ์ถ ์
์ด์ ์ฐ์ฐ์ ๋ฐ์ํ๊ธฐ ์ํด ์ฌ์ฌ์ฉํ๋ฏ๋ก ๊ทธ ๋ด์ฉ์ ์์ ํ๋ฉด ์ ๋๋ค
# ๋ง์ฐ์ค์ ๊ทธ๋ฉ์ปท์ผ๋ก ๋ฐฐ๊ฒฝ๊ณผ ์ ๊ฒฝ ๋ถ๋ฆฌ
import cv2
import numpy as np
img = cv2.imread('img/taekwonv1.jpg')
img_draw = img.copy()
mask = np.zeros(img.shape[:2], dtype=np.uint8) # ๋ง์คํฌ ์์ฑ
rect = [0,0,0,0] # ์ฌ๊ฐํ ์์ญ ์ขํ ์ด๊ธฐํ
mode = cv2.GC_EVAL # ๊ทธ๋ฉ์ปท ์ด๊ธฐ ๋ชจ๋
# ๋ฐฐ๊ฒฝ ๋ฐ ์ ๊ฒฝ ๋ชจ๋ธ ๋ฒํผ
bgdmodel = np.zeros((1,65),np.float64)
fgdmodel = np.zeros((1,65),np.float64)
# ๋ง์ฐ์ค ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์
def onMouse(event, x, y, flags, param):
global mouse_mode, rect, mask, mode
if event == cv2.EVENT_LBUTTONDOWN : # ์ผ์ชฝ ๋ง์ฐ์ค ๋๋ฆ
if flags <= 1: # ์๋ฌด ํค๋ ์ ๋๋ ์ผ๋ฉด
mode = cv2.GC_INIT_WITH_RECT # ๋๋๊ทธ ์์, ์ฌ๊ฐํ ๋ชจ๋ ---โ
rect[:2] = x, y # ์์ ์ขํ ์ ์ฅ
# ๋ง์ฐ์ค๊ฐ ์์ง์ด๊ณ ์ผ์ชฝ ๋ฒํผ์ด ๋๋ฌ์ง ์ํ
elif event == cv2.EVENT_MOUSEMOVE and flags & cv2.EVENT_FLAG_LBUTTON :
if mode == cv2.GC_INIT_WITH_RECT: # ๋๋๊ทธ ์งํ ์ค ---โก
img_temp = img.copy()
# ๋๋๊ทธ ์ฌ๊ฐํ ํ๋ฉด์ ํ์
cv2.rectangle(img_temp, (rect[0], rect[1]), (x, y), (0,255,0), 2)
cv2.imshow('img', img_temp)
elif flags > 1: # ํค๊ฐ ๋๋ฌ์ง ์ํ
mode = cv2.GC_INIT_WITH_MASK # ๋ง์คํฌ ๋ชจ๋ ---โข
if flags & cv2.EVENT_FLAG_CTRLKEY :# ์ปจํธ๋กค ํค, ๋ถ๋ช
ํ ์ ๊ฒฝ
# ํฐ์ ์ ํ๋ฉด์ ํ์
cv2.circle(img_draw,(x,y),3, (255,255,255),-1)
# ๋ง์คํฌ์ GC_FGD๋ก ์ฑ์ฐ๊ธฐ ---โฃ
cv2.circle(mask,(x,y),3, cv2.GC_FGD,-1)
if flags & cv2.EVENT_FLAG_SHIFTKEY : # ์ฌํํธํค, ๋ถ๋ช
ํ ๋ฐฐ๊ฒฝ
# ๊ฒ์ ์ ์ ํ๋ฉด์ ํ์
cv2.circle(img_draw,(x,y),3, (0,0,0),-1)
# ๋ง์คํฌ์ GC_BGD๋ก ์ฑ์ฐ๊ธฐ ---โค
cv2.circle(mask,(x,y),3, cv2.GC_BGD,-1)
cv2.imshow('img', img_draw) # ๊ทธ๋ ค์ง ๋ชจ์ต ํ๋ฉด์ ์ถ๋ ฅ
elif event == cv2.EVENT_LBUTTONUP: # ๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ ๋ ์ํ ---โฅ
if mode == cv2.GC_INIT_WITH_RECT : # ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ ์ข
๋ฃ
rect[2:] =x, y # ์ฌ๊ฐํ ๋ง์ง๋ง ์ขํ ์์ง
# ์ฌ๊ฐํ ๊ทธ๋ ค์ ํ๋ฉด์ ์ถ๋ ฅ ---โฆ
cv2.rectangle(img_draw, (rect[0], rect[1]), (x, y), (255,0,0), 2)
cv2.imshow('img', img_draw)
# ๊ทธ๋ฉ์ปท ์ ์ฉ ---โง
cv2.grabCut(img, mask, tuple(rect), bgdmodel, fgdmodel, 1, mode)
img2 = img.copy()
# ๋ง์คํฌ์ ํ์คํ ๋ฐฐ๊ฒฝ, ์๋ง๋ ๋ฐฐ๊ฒฝ์ผ๋ก ํ์๋ ์์ญ์ 0์ผ๋ก ์ฑ์ฐ๊ธฐ
img2[(mask==cv2.GC_BGD) | (mask==cv2.GC_PR_BGD)] = 0
cv2.imshow('grabcut', img2) # ์ต์ข
๊ฒฐ๊ณผ ์ถ๋ ฅ
mode = cv2.GC_EVAL # ๊ทธ๋ฉ์ปท ๋ชจ๋ ๋ฆฌ์
# ์ด๊ธฐ ํ๋ฉด ์ถ๋ ฅ ๋ฐ ๋ง์ฐ์ค ์ด๋ฒคํธ ๋ฑ๋ก
cv2.imshow('img', img)
cv2.setMouseCallback('img', onMouse)
while True:
if cv2.waitKey(0) & 0xFF == 27 : # esc
break
cv2.destroyAllWindows()
- ๊ทธ๋ฉ์ปท์ ํ์ฉํ์ฌ ๋ฐฐ๊ฒฝ์ ๋ถ๋ฆฌ
- ๋ง์ฐ์ค๋ก ๋๋๊ทธ(์ฌ๊ฐํ)ํ์ฌ ์ ๊ฒฝ ์ธ๊ณฝ ์์ญ์ ํ์
=> 1์ฐจ์ ์ผ๋ก ๋ฐฐ๊ฒฝ๊ณผ ์ ๊ฒฝ์ด ๋ถ๋ฆฌ
- ๋ฐฐ๊ฒฝ์ ์ถ๊ฐ๋ก ์ ๊ฑฐํ๊ณ ์ถ์ผ๋ฉด ์๋ณธ ์ด๋ฏธ์ง์ ์ฌํํธ ํค๋ฅผ ๋๋ฅธ ์ํ๋ก ๋ง์ฐ์ค๋ก ๊ฒ์์ ์ ์ ๊ทธ์ด์ฃผ๊ธฐ
- ์๋ชป ์ ๊ฑฐ๋ ์ ๊ฒฝ์ ์ถ๊ฐํ๊ณ ์ถ์ผ๋ฉด ์๋ณธ ์ด๋ฏธ์ง์ ์ปจํธ๋กคํค๋ฅผ ๋๋ฅธ ์ํ๋ก ๋ง์ฐ์ค๋ก ํฐ์ ์ ์ ๊ทธ์ด์ฃผ๊ธฐ
- cv2.GC_FGD๋ ํ์คํ ์ ๊ฒฝ, cv2.GC_BGD๋ ํ์คํ ๋ฐฐ๊ฒฝ
# ๋ง์คํฌ์ ํ์คํ ๋ฐฐ๊ฒฝ, ์๋ง๋ ๋ฐฐ๊ฒฝ์ผ๋ก ํ์๋ ์์ญ์ 0์ผ๋ก ์ฑ์ฐ๊ธฐ (๋ฐฐ๊ฒฝ์ ๊ฑฐ)
img2[(mask==cv2.GC_BGD) | (mask==cv2.GC_PR_BGD)] = 0
6. ํ๊ท ์ด๋ ํํฐ
: ๋ฌผ๊ฐ์ผ๋ก ๊ทธ๋ฆผ์ ๊ทธ๋ฆฐ ๊ฒ๊ณผ ๊ฐ์ด ์ด๋ฏธ์ง๋ฅผ ๋ฐ๊พธ๊ธฐ
dst = cv2.pyrMeanShiftFiltering(src, sp, sr, dst, maxLevel, termcrit)
- src : ์
๋ ฅ ์ด๋ฏธ์ง
- sp : ๊ณต๊ฐ ์๋ ๋ฐ์ง๋ฆ ํฌ๊ธฐ
- sr : ์์ ์๋ ๋ฐ์ง๋ฆ ํฌ๊ธฐ
- maxLevel(optional) : ์ด๋ฏธ์ง ํผ๋ผ๋ฏธ๋ ์ต๋ ๋ ๋ฒจ
- termcrit(optional) : ๋ฐ๋ณต ์ค์ง ์๊ฑด
- cv2.TERM_CRITERIA_EPS : ์ ํ๋๊ฐ ์ต์ ์ ํ๋(epsilon) ๋ณด๋ค ์์์ง๋ฉด ์ค์ง
- cv2.TERM_CRITERIA_MAX_ITER : ์ต๋ ๋ฐ๋ณต ํ์(max_iter)์ ๋๋ฌํ๋ฉด ์ค์ง
- -> default epsilon=1, max_iter=5
: ๋ด๋ถ์ ์ผ๋ก ์ด๋ฏธ์ง ํผ๋ผ๋ฏธ๋๋ฅผ ๋ง๋ค์ด ์์ ์ด๋ฏธ์ง์ ํ๊ท ์ด๋ ๊ฒฐ๊ณผ๋ฅผ ํฐ ์ด๋ฏธ์ง์ ์ ์ฉ
: sp ํ๋ผ๋ฏธํฐ๋ ํ๊ท ์ด๋(MeanShift)์ ์ฌ์ฉํ ์๋ ํฌ๊ธฐ,
๋ช ํฝ์ ์ฉ ๋ฌถ์ด์ ํ๊ท ์ ๋ด์ด ์ด๋ํ ์ง๋ฅผ ๊ฒฐ์
sr ํ๋ผ๋ฏธํฐ๋ ์์ ์๋ ํฌ๊ธฐ๋ก ์์ ๊ฐ์ ์ฐจ์ด ๋ฒ์๋ฅผ ์ง์
ํ๊ท ์ ๊ณ์ฐํ ๋ ๊ฐ์ ์ฐจ์ด๊ฐ sr ๊ฐ์ ๋ฒ์ ์์ ์๋ ํฝ์ ๋ง์ ๋์
sr์ด ๋๋ฌด ์์ผ๋ฉด ์๋ณธ๊ณผ ๋ณ ์ฐจ์ด๊ฐ ์๊ณ , ๋๋ฌด ํฌ๋ฉด ์๋ณธ๊ณผ ๋ง์ด ๋ฌ๋ผ์ง
: maxLevel์ ์ด๋ฏธ์ง ํผ๋ผ๋ฏธ๋ ์ต๋ ๋ ๋ฒจ
0๋ณด๋ค ํฌ๋ฉด ๊ทธ ๊ฐ๋งํผ ์์ ์ด๋ฏธ์ง ํผ๋ผ๋ฏธ๋๋ก ํ๊ท ์ด๋ํด์ ์ป์ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฉ
๊ฐ์ด ํด์๋ก ์๋๊ฐ ๋นจ๋ผ์ง์ง๋ง ์์ญ๊ณผ ์์์ด ๊ฑฐ์น ์ด์ง
: termcrit์ ๋ฐ๋ณต์ ์ค์งํ ๊ธฐ์ค์ ์ง์
# ํ๊ท ์ด๋ ์ธ๊ทธ๋ฉํ
์ด์
ํํฐ
import cv2
import numpy as np
img = cv2.imread('img/taekwonv1.jpg')
# ํธ๋๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์
def onChange(x):
#sp, sr, level ์ ํ ๊ฐ ์์ง
sp = cv2.getTrackbarPos('sp', 'img')
sr = cv2.getTrackbarPos('sr', 'img')
lv = cv2.getTrackbarPos('lv', 'img')
# ํ๊ท ์ด๋ ํํฐ ์ ์ฉ ---โ
mean = cv2.pyrMeanShiftFiltering(img, sp, sr, None, lv)
# ๋ณํ ์ด๋ฏธ์ง ์ถ๋ ฅ
cv2.imshow('img', np.hstack((img, mean)))
# ์ด๊ธฐ ํ๋ฉด ์ถ๋ ฅ
cv2.imshow('img', np.hstack((img, img)))
# ํธ๋๋ฐ ์ด๋ฒคํธ ํจ์ ์ฐ๊ฒฐ
cv2.createTrackbar('sp', 'img', 0,100, onChange)
cv2.createTrackbar('sr', 'img', 0,100, onChange)
cv2.createTrackbar('lv', 'img', 0,5, onChange)
cv2.waitKey(0)
cv2.destroyAllWindows()
- sp ๋ฅผ ๋์ => ํ๊ท ์ด๋์ ์ฌ์ฉํ ์๋ ํฌ๊ธฐ
- sr ์ ๋์ => ๋๋ฌด ํฌ๋ฉด ์๋ณธ๊ณผ ๋ฌ๋ผ์ง
- lr (maxLavel) => ํด์๋ก ์์ญ, ์์ ๊ฑฐ์น ์ด์ง
'๐ฉโ๐ป IoT (Embedded) > Image Processing' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[v0.28]์์์ฒ๋ฆฌ_์ด๋ฏธ์ง์ ํน์ง์ , ํน์ง์ ๊ฒ์ถ๊ธฐ (0) | 2022.01.15 |
---|---|
[v0.27]์์์ฒ๋ฆฌ_์ด๋ฏธ์ง ๋งค์นญ (Image Matching) (0) | 2022.01.13 |
[v0.25]์์์ฒ๋ฆฌ_ํํ ๋ณํ (0) | 2022.01.12 |
[v0.24]์์์ฒ๋ฆฌ_์ปจํฌ์ด (์์๋ถํ ๋ฐฉ๋ฒ) (0) | 2022.01.12 |
[v0.23]์์์ฒ๋ฆฌ_ ๋ธ๋ฌ๋งํ์ฉ & ๋ชจ์์ดํฌ ์ฒ๋ฆฌ, ์ด๋ฏธ์ง ์ค์ผ์น ํจ๊ณผ (0) | 2022.01.12 |