이미지 Segmentation 원초적 접근

류창훈·2024년 12월 19일
1

CV (Computer Vision)

목록 보기
4/9

이번 내용은 데이터 학습으로 객체 분할 하는 것이 아닌,
이미지의 각 픽셀 단위로 접근하는 객체 분할 내용입니다.





Approach

활용한 이미지는 위와 같고, 육안으로는 눈이 내리는 상황으로 보입니다.

제가 원하는 분할 부위는 눈이 쌓인 구간이고,
내리는 눈 때문에 픽셀들이 깨진 부위가 자꾸 보이는데,

이건 현재 상황에서는 노이즈로 볼 수 있습니다.


이미지 출처는 수업 자료 만들 때 막 찾아다가 저장한건데 다시 찾으려니 못 찾겠어요...




Detectron2


객체 분할을 하려고 좀 찾아보니
대표적으로 Detectron2 라는 Meta 에서 만든 라이브러리가 존재합니다.


import cv2
import numpy as np
from detectron2 import model_zoo
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2.utils.visualizer import Visualizer
from detectron2.data import MetadataCatalog
import torch


cfg = get_cfg()
cfg.merge_from_file(model_zoo.get_config_file("COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml"))
cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml")
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5
cfg.MODEL.DEVICE = "cuda" if torch.cuda.is_available() else "cpu"


predictor = DefaultPredictor(cfg)

image = cv2.imread("snow_example.png")


outputs = predictor(image)
segmentation = outputs["sem_seg"].argmax(dim=0).to("cpu").numpy()


metadata = MetadataCatalog.get(cfg.DATASETS.TRAIN[0])

snow_class_index = metadata.stuff_classes.index("snow")

snow_mask = (segmentation == snow_class_index).astype(np.uint8)

colored_seg = Visualizer(image[:, :, ::-1], metadata, scale=1.2)
result = colored_seg.draw_binary_mask(snow_mask, color=[1.0, 1.0, 1.0], alpha=0.5)


cv2.imshow('source image', image)
cv2.imshow('semantic segmentation - just snow', result.get_image()[:, :, ::-1])
cv2.waitKey(0)
cv2.destroyAllWindows()

위의 코드는 제가 눈(Snow) 부분만 segmentation 하기 위해서
실습해본 코드이고,

coco 데이터셋으로 학습된 것을 갖다가 썼습니다.

눈이 쌓여있는 구간의 영역 분할을 쉽게 실시할 수 있었고,


위 캡쳐 본은

위의 원본 이미지 위에 Segmentation한 결과물을 불투명한 하얀색으로 나타낸건데,

약간의 잘못 추출된 것(빨간 차도, 검은 차도 부위를 인식)을 제외하면
대체적으로 잘 추출된 것을 볼 수 있습니다.


물론, 이 주제에 특화해서 커스텀을 하거나 추가학습을 한다면

더 좋은 결과물을 얻었을텐데,


제가 이 작업을 하면서 초점을 맞췄던 부분은,
데이터 학습 이 아닌,
픽셀값 뜯어보기 입니다.





Pixel-Level Segmentation: Color space


제가 구성한 코드는 다음과 같습니다.


import cv2
import numpy as np



image = cv2.imread('snow_example.png')
original = image.copy()


hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

lower_white = np.array([0, 10, 200])  
upper_white = np.array([180, 70, 255])

mask = cv2.inRange(hsv, lower_white, upper_white)


kernel = np.ones((5, 5), np.uint8)

mask_cleaned = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)  
mask_cleaned_2 = cv2.morphologyEx(mask_cleaned, cv2.MORPH_OPEN, kernel)


cv2.imshow("Original Image", original)
cv2.imshow("Mask", mask_cleaned_2)

cv2.waitKey(0)
cv2.destroyAllWindows()

먼저 BGR 형태의 이미지를 좀 더 직관적으로 보기 편하게,

또 작업하기 편하게 Independent 형태로 구성되어 있는 HSV color space 로 변형해주었습니다.


그리고 HSV color space의 값들을 간단히 봐보면,

H(색상): 0~180
S(색상 선명도): 0~255
V(밝기): 0~255

로 구성됩니다.


이제 다시 원본 이미지의 눈(Snow)을 들여다 봐보면,

노이즈 때문에 완전한 흰색이 아닌 약간의 회색 빛이 도는 것을 볼 수 있는데,

그렇기 때문에 Value값을 200 ~ 255값으로 설정했고,
Saturation값은 완전한 흰색과 약간의 탁한 흰색을 보기 위해서 10 ~ 70으로 설정,
Hue는 흰색을 보는 것이기 때문에 그냥 모든 범위로 설정해 주었습니다.





Pixel-Level Segmentation: Morphology


원본 이미지를 다시 살펴본다면,

눈이 오는 상황이어서 그런지 도로가 굉장히 흐릿해 보입니다.


사람은 이것을 어느정도 감안해서 도로 위 뭐가 있는지 알 수 있지만,

컴퓨터는 이것 하나하나를

노이즈로 판단하게 됩니다.



그래서 이것을 어떻게 처리할까 고민하다

저번학기 공부했던 '모폴로지 연산' 을 생각하게 되었습니다.


'팽창', '침식', '열림', '닫힘'

각각의 연산 과정은 참고자료에 링크 달아놓겠습니다.


이제 다시 원본 이미지를 떠올려 보면,


위와 같이 Noise가 생긴게 보입니다.


실제 눈길에도 자동차 와리가리 하다가 이런 얼룩은 충분히 생길 수 있고
다른 물체 때문에 생긴 그림자도 무시 못하기 때문에

닫힘(Closing) 연산을 먼저 거쳐주었습니다.

이 얼룩은 제 모니터 문제인건지, 아님 실제 이미지에 있는 얼룩인지, 뭐 제 눈에는 그렇게 보였습니다. ㅎㅎ


그리고, 원본 이미지를 봐보면, 현재 눈이 내리는 상황이 보입니다.

하지만 이 눈 알갱이 하나하나가 지금 당장은 쌓여 있다는 것은 아니기 때문에,

이것도 노이즈로 간주 할 수 있습니다.


그래서 위의 코드와 같이, 열림(Opening) 연산을 해서,
작은 눈 알갱이를 제거 했습니다.



그래서 최종 분할 출력물을 보면,


아까 Detectron2 썼을 때 보다는 더 많은 부분을 분할하지는 못했지만,

그래도 어느정도는 분할 할 수 있는게 보입니다.

더 세세한 파라미터값 설정으로 이 부분은 충분히 보완가능 하다고 생각합니다.





나가며


옛날에 어디선가 들었던 좋은 이야기가 생각납니다.

"케이크 먹는데 정확히 8등분 할 수 있는 빵칼이 필요할까?
지금 당장은 먹는게 중요한데,
그냥 주는 빵칼 쓰지"


여러 상황에 적용할 수 있고, 또 세세한 것도 함께 적용 가능하다면
물론 당연히 이것은 좋은 것 입니다.


다만, 이 주제는 도로에 쌓여있는 눈(Snow)을 분할하는 것 입니다.


즉, 이는 어찌보면 굉장히 축소된 범위 입니다.


Segmentation을 찾다보면, 어딘가에서 만든 모델이 나오게 되고,

모델이 나왔다는건 결국 데이터를 학습해서 만든 것 입니다.


모델 학습은 그만큼의 데이터 확보가 우선시 되어야 하고,

그 주제에 맞는 양질의 데이터 확보는...
사실 매우 힘들고 지루한 과정 입니다.

또한, 무거운 걸 돌려야 하기 때문에,
최종 출력 결과까지 확인하기까지 시간도 오래 걸린다는 문제가 존재합니다.



그래서 이렇게 원초적인 방식으로 접근하게 되었고,
실제로 빠르게 결과를 확인할 수 있었습니다.




감사합니당 ~ 🦾





참고자료
https://github.com/facebookresearch/Detectron
https://medium.datadriveninvestor.com/understanding-edge-detection-sobel-operator-2aada303b900
https://github.com/SKHU-AI-2/3rd-reference/tree/main/3%EA%B8%B0_8%EC%A3%BC%EC%B0%A8
profile
Linear AI Developer입니다.

0개의 댓글

관련 채용 정보