이론
- 흑백 이미지를 토폴로지로 볼수 있는데, 고 강도 부분은 언덕, 저 강도 부분을 계곡이라 볼수 있습니다. 이제 고립된 계곡 부분(지역 최소점)에 다른색의 물(라벨)이 차있다고 해봅시다. 물이 차면서 언덕이 어떻게 되있느냐에 따라 물이 섞이게 됩니다. 이런 현상을 막으려면 물이 석기는 지점에 장벽을 만들어야 되는데요. 물이 언덕에 찰때까지 물을 더붇고 장벽을 더 지어봅시다. 그렇게 만든 장벽이 분할한 결과라 할수 있겠습니다.
=> 이것이 워터셰드 방법의 기본 개념이 되겠습니다.
- 하지만 노이즈 때문에 너무 과하게 분할될수도있어요. 그래서 OpenCV에선 마커기반 워터세드 알고리즘이 있는데, 지정한 계곡점부분은 합쳐지거나 합쳐지지 않게도 할수 있어요
일단 위 동전 이미지에서 오츠 알고리즘으로 이진화 부터 해봅시다.
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('coins.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
cv2.imshow("ret", ret)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 이제 이진화 결과중에 아직 흰색 작은 노이즈가 약간식 존재하게 되는데 이를 모폴로지 열림 연산으로 없앨수 있습니다. 작은 점들을 제거하고 나선 모폴로지 닫힘 연산을 진행하겠습니다. 그렇게 해서 물체의 중심 위치와 배경 위치를 파악할수 있게 되지만 아직은 동전들사이 경계선은 알수 없습니다. #noise removal
- 이제 확실히 동전인 영역들을 추출해내야되는데요. 여기서 침식 연산으로 경계 픽셀들을 제거하겠습니다. 그러면 동전이 서로 닫지 않는경우에 확실히 동전인걸 알수 있겠지만 여기 있는 동전들을 서로 붙어있기 때문에, 거리 변환을 수행한후 적절한 임계치로 찾는게 좋은 방법이 되겠습니다. # find sure foreground area
- 다음으로 확실히 동전이 아닌 공간을 찾아보겠습니다. 이 경우 팽창을 하여 얻을수 있는데, 팽창은 물체의 경계를 증가시켜 실제 배경 부분을 더 잘 나타낼수 있도록 도와줍니다. #sure background area
- 확실히 배경인 부분과 확실히 물체인 부분들 이외의 공간을 구하려면 sure_fg - sure_bg 로 얻을 수 있습ㄴ디ㅏ.
- 아래의 이진화 결과 이미지를 보면 붙어있지만 동전들의 위치를 알수 있게 되었습니다. 여기까지 내용이 붙어있는 물체들간의 분할이었고, 물체 분할을 하고자 한다면, 거리 분할을 할 필요없이 침식 연산이면 충분하여 앞에 존재하는 물체들끼리 나눌수 있게 됩니다.
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./res/coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
plt.subplot(131),plt.imshow(sure_bg, cmap="gray")
plt.title("sure background")
plt.subplot(132),plt.imshow(dist_transform, cmap="gray")
plt.title("distance transform")
plt.subplot(133),plt.imshow(sure_fg, cmap="gray")
plt.title("sure foreground")
plt.show()
- 이제 코인들의 위치를 알수 있게되었고 어디까지가 배경인지도 알게 되었습니다. 이제 마커를 생성하고 그 마커 공간에 라벨을 붙이겠습니다. 이 영역은 확실히 배경인지 물체인지 알수 있는 것들로 서로 다른 양의 정수값들을 주겠습니다. 배경에는 0을주고,다른 물체는 1에서 부터 라벨을 붙이기 시작하겠습니다.
- 하지만 배경이 0으로 준다면, 워터셰드는 모르는 공간으로 판단해서 동작하게 될겁니다. 그래서 배경도 마킹하려면 다른 정수를 써야됩니다.
=> 배경은 원래 0이나 1로 하고, 다른 물체들은 1 이하의 정수, 모르는 부분은 0으로 마킹
# Marker labelling
ret, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
- 마커는 준비되었으니 워터 셰드를 하겠습니다. 경계 영역은 -1로 마커되니 빨간색으로 칠하겠습니다.
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
- 정리
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./res/coins.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
# Marker labelling
ret, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
plt.subplot(121),plt.imshow(markers)
plt.title("marker image after segmentation")
plt.subplot(122),plt.imshow(img)
plt.title("result")
plt.show()
'
'로봇 > 영상' 카테고리의 다른 글
opencv-python 튜토리얼 - 20. 피처 이해하기 (0) | 2020.08.15 |
---|---|
opencv-python 튜토리얼 - 중간 정리 (0) | 2020.08.15 |
opencv-python 튜토리얼 - 18. 허프 변환 (0) | 2020.08.15 |
opencv-python 튜토리얼 - 17. 탬플릿 매칭 (0) | 2020.08.15 |
opencv-python 튜토리얼 - 16. 푸리에 변환 (2) | 2020.08.15 |