diff --git a/README.md b/README.md index e69de29..15e9858 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,347 @@ +--- +layout: default +title: 10. Image Segmentation +subtitle: 산업비전의실제 과목 +--- +----- + +[PINBlog Gitea Repository](https://gitea.pinblog.codes/CBNU/10_Image_Segmentation) + +----- + +# Image Segmentation +- 산업인공지능학과 대학원 + 2022254026 + 김홍열 + + +--- + + +# **Image Segmentation* +### **K-Means, Watershed, GrabCut을 통한 이미지 분할** + +오늘은 이미지 분할(Image Segmentation)의 세 가지 대표적인 방법 +즉, K-Means, Watershed, 그리고 GrabCut에 대해 알아보려고 합니다. +이미지 분할은 이미지를 의미 있는 영역으로 나누는 과정입니다. + + +# **1. 이미지 분할이란?** + +이미지 분할은 이미지를 여러 개의 세그먼트(segment)나 영역으로 나누는 과정입니다. +이를 통해 우리는 이미지 내의 특정 객체나 구조를 식별하고 분석할 수 있습니다. + + +# **2. K-Means를 이용한 분할** + +- **원리**: K-Means는 클러스터링 알고리즘으로, 이미지의 픽셀 값을 기반으로 유사한 픽셀을 그룹화합니다. +- **작동 방식**: 이미지의 각 픽셀에 대한 색상 값 (RGB 또는 HSV)을 사용하여 K 개의 클러스터를 형성합니다. +- **사용 사례**: 이미지의 색상 기반 분할, 배경과 전경의 분리 등. + +--- + +### 예제 코드[¶]() + +
+K-Means +
+ +```python + +import cv2 +import numpy as np +import matplotlib.pyplot as plt + +image = cv2.imread('./Lenna.png').astype(np.float32) / 255. +image_lab = cv2.cvtColor(image, cv2.COLOR_BGR2Lab) + +data = image_lab.reshape((-1, 3)) + +num_classes = 8 +criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 50, 0.1) +_, labels, centers = cv2.kmeans(data, num_classes, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) + +segmented_lab = centers[labels.flatten()].reshape(image.shape) +segmented = cv2.cvtColor(segmented_lab, cv2.COLOR_Lab2RGB) + +plt.subplot(121) +plt.axis('off') +plt.title('original') +plt.imshow(image[:,:,[2,1,0]]) +plt.subplot(122) +plt.axis('off') +plt.title('segmented') +plt.imshow(segmented) +plt.show() + +``` + +
+
+ +
+Result + + +![Result](./seg1.png) + + +
+
+
+ +# **3. Watershed 알고리즘** + +- **원리**: Watershed는 이미지의 그레이디언트를 '지형'으로 간주하고, '물'을 주입하여 분리된 영역을 생성하는 방식으로 작동합니다. +- **작동 방식**: 이미지의 그레이디언트를 계산한 후, 지역 최소값에서 시작하여 '물'을 주입하면서 영역을 확장합니다. +- **사용 사례**: 객체의 경계 탐지, 미세한 구조물 분리 등. + +--- + +### 예제 코드[¶]() + +
+Watershed +
+ +```python + +import cv2 +import numpy as np +from random import randint + +img = cv2.imread('./lenna.png') +show_img = np.copy(img) + +seeds = np.full(img.shape[0:2], 0, np.int32) +segmentation = np.full(img.shape, 0, np.uint8) + +n_seeds = 9 + +colors = [] +for m in range(n_seeds): + colors.append((255 * m / n_seeds, randint(0, 255), randint(0, 255))) + + +mouse_pressed = False +current_seed = 1 +seeds_updated = False + + +def mouse_callback(event, x, y, flags, param): + global mouse_pressed, seeds_updated + + if event == cv2.EVENT_LBUTTONDOWN: + mouse_pressed = True + cv2.circle(seeds, (x, y), 5, (current_seed), cv2.FILLED) + cv2.circle(show_img, (x, y), 5, colors[current_seed - 1], cv2.FILLED) + seeds_updated = True + + elif event == cv2.EVENT_MOUSEMOVE: + if mouse_pressed: + cv2.circle(seeds, (x, y), 5, (current_seed), cv2.FILLED) + cv2.circle(show_img, (x, y), 5, colors[current_seed - 1], cv2.FILLED) + seeds_updated = True + + elif event == cv2.EVENT_LBUTTONUP: + mouse_pressed = False + + +cv2.namedWindow('image') +cv2.setMouseCallback('image', mouse_callback) + +while True: + cv2.imshow('segmentation', segmentation) + cv2.imshow('image', show_img) + + k = cv2.waitKey(1) + + if k == 27: + break; + elif k == ord('c'): + show_img = np.copy(img) + seeds = np.full(img.shape[0:2], 0, np.int32) + segmentation = np.full(img.shape, 0, np.uint8) + elif k > 0 and chr(k).isdigit(): + n = int(chr(k)) + if 1 <= n <= n_seeds and not mouse_pressed: + current_seed = n + + if seeds_updated and not mouse_pressed: + seeds_copy = np.copy(seeds) + cv2.watershed(img, seeds_copy) + segmentation = np.full(img.shape, 0, np.uint8) + for m in range(n_seeds): + segmentation[seeds_copy == (m + 1)] = colors[m] + + seeds_updated = False + +cv2.destroyAllWindows() + +``` + +
+
+ +
+Result + + +![Result](./seg2.png) +![Result](./seg3.png) + + +
+
+
+ + +# **4. GrabCut 알고리즘** + +- **원리**: GrabCut은 사용자의 간단한 주석(예: 전경과 배경의 대략적인 경계)을 기반으로 이미지를 분할합니다. +- **작동 방식**: 초기 주석을 기반으로 그래프 컷(Graph Cut) 알고리즘을 사용하여 최적의 분할을 찾습니다. +- **사용 사례**: 객체 추출, 이미지 편집, 배경 제거 등. + +--- + +### 예제 코드[¶]() + +
+Grabcut +
+ +```python + +import cv2 +import numpy as np + +img = cv2.imread('./lenna.png', cv2.IMREAD_COLOR) +show_img = np.copy(img) + +mouse_pressed = False +y = x = w = h = 0 + + +def mouse_callback(event, _x, _y, flags, param): + global show_img, x, y, w, h, mouse_pressed + + if event == cv2.EVENT_LBUTTONDOWN: + mouse_pressed = True + x, y, = _x, _y + show_img = np.copy(img) + + elif event == cv2.EVENT_MOUSEMOVE: + if mouse_pressed: + show_img = np.copy(img) + cv2.rectangle(show_img, (x, y), (_x, _y), (0, 255, 0), 3) + + elif event == cv2.EVENT_LBUTTONUP: + mouse_pressed = False + w, h = _x - x, _y - y + + +cv2.namedWindow('image') +cv2.setMouseCallback('image', mouse_callback) + +while True: + cv2.imshow('image', show_img) + k = cv2.waitKey(1) + + if k == ord('a') and not mouse_pressed: + if w * h > 0: + break + +cv2.destroyAllWindows() + +labels = np.zeros(img.shape[:2], np.uint8) +labels, bgdModel, fgdModel = cv2.grabCut(img, labels, (x, y, w, h), None, None, 5, cv2.GC_INIT_WITH_RECT) + +show_img = np.copy(img) +show_img[(labels == cv2.GC_PR_BGD) | (labels == cv2.GC_BGD)] //= 3 + +cv2.imshow('image', show_img) +cv2.waitKey() +cv2.destroyAllWindows() + + +label = cv2.GC_BGD +lbl_clrs = {cv2.GC_BGD: (0, 0, 0), cv2.GC_FGD: (255, 255, 255)} + + +def mouse_callback(event, x, y, flags, param): + global mouse_pressed + + if event == cv2.EVENT_LBUTTONDOWN: + mouse_pressed = True + cv2.circle(labels, (x, y), 5, label, cv2.FILLED) + cv2.circle(show_img, (x, y), 5, lbl_clrs[label], cv2.FILLED) + + elif event == cv2.EVENT_MOUSEMOVE: + if mouse_pressed: + cv2.circle(labels, (x, y), 5, label, cv2.FILLED) + cv2.circle(show_img, (x, y), 5, lbl_clrs[label], cv2.FILLED) + + elif event == cv2.EVENT_LBUTTONUP: + mouse_pressed = False + + +cv2.namedWindow('image') +cv2.setMouseCallback('image', mouse_callback) + +while True: + cv2.imshow('image', show_img) + k = cv2.waitKey(1) + + if k == ord('a') and not mouse_pressed: + break + + elif k == ord('1'): + label = cv2.GC_FGD - label + +cv2.destroyAllWindows() + + +labels, bgdModel, fgdModel = cv2.grabCut(img, labels, None, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK) +show_img = np.copy(img) +show_img[(labels == cv2.GC_PR_BGD) | (labels == cv2.GC_BGD)] //= 3 + +cv2.imshow('image', show_img) +cv2.waitKey() +cv2.destroyAllWindows() + +``` + +
+
+ +
+Result + + +![Result](./seg4.png) +![Result](./seg5.png) +![Result](./seg6.png) +![Result](./seg7.png) + + +
+
+
+ + +# **5. 결론** + +이미지 분할은 컴퓨터 비전의 핵심 작업 중 하나로, 다양한 알고리즘과 기법이 개발되어 왔습니다. +K-Means, Watershed, GrabCut은 그 중에서도 널리 사용되는 방법들입니다. +각 방법은 특정 상황과 요구 사항에 따라 장점과 단점을 가지므로, 적절한 알고리즘을 선택하는 것이 중요합니다. + +이렇게 이미지 분할의 세 가지 대표적인 방법을 간략하게 소개하는 블로그 포스트를 작성해 보았습니다. +다음 포스트에서는 실제 코드 예제와 함께 각 방법의 실제 응용 사례를 살펴보는 것도 좋을 것 같습니다! + + +--- + +### 참고[¶]() + +- 산업비전의실제 과목, 박태형 교수 +- ChatGPT diff --git a/grabcut.py b/grabcut.py index 15036ab..2130804 100644 --- a/grabcut.py +++ b/grabcut.py @@ -31,6 +31,7 @@ cv2.setMouseCallback('image', mouse_callback) while True: cv2.imshow('image', show_img) + cv2.imwrite('seg4.png', show_img) k = cv2.waitKey(1) if k == ord('a') and not mouse_pressed: @@ -46,6 +47,7 @@ show_img = np.copy(img) show_img[(labels == cv2.GC_PR_BGD) | (labels == cv2.GC_BGD)] //= 3 cv2.imshow('image', show_img) +cv2.imwrite('seg5.png', show_img) cv2.waitKey() cv2.destroyAllWindows() @@ -76,6 +78,7 @@ cv2.setMouseCallback('image', mouse_callback) while True: cv2.imshow('image', show_img) + cv2.imwrite('seg6.png', show_img) k = cv2.waitKey(1) if k == ord('a') and not mouse_pressed: @@ -92,5 +95,6 @@ show_img = np.copy(img) show_img[(labels == cv2.GC_PR_BGD) | (labels == cv2.GC_BGD)] //= 3 cv2.imshow('image', show_img) +cv2.imwrite('seg7.png', show_img) cv2.waitKey() cv2.destroyAllWindows() \ No newline at end of file diff --git a/seg1.png b/seg1.png new file mode 100644 index 0000000..afb3c29 Binary files /dev/null and b/seg1.png differ diff --git a/seg2.png b/seg2.png new file mode 100644 index 0000000..8e6fee1 Binary files /dev/null and b/seg2.png differ diff --git a/seg3.png b/seg3.png new file mode 100644 index 0000000..83ea5a3 Binary files /dev/null and b/seg3.png differ diff --git a/seg4.png b/seg4.png new file mode 100644 index 0000000..ebb734a Binary files /dev/null and b/seg4.png differ diff --git a/seg5.png b/seg5.png new file mode 100644 index 0000000..2fea688 Binary files /dev/null and b/seg5.png differ diff --git a/seg6.png b/seg6.png new file mode 100644 index 0000000..72154a7 Binary files /dev/null and b/seg6.png differ diff --git a/seg7.png b/seg7.png new file mode 100644 index 0000000..aaf8b52 Binary files /dev/null and b/seg7.png differ diff --git a/watershed.py b/watershed.py index eeb2616..743786d 100644 --- a/watershed.py +++ b/watershed.py @@ -45,6 +45,8 @@ cv2.setMouseCallback('image', mouse_callback) while True: cv2.imshow('segmentation', segmentation) cv2.imshow('image', show_img) + cv2.imwrite('seg2.png', show_img) + cv2.imwrite('seg3.png', segmentation) k = cv2.waitKey(1)