관리 메뉴

클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지

에지와 영역 4 - 영역 분할 1 : 배경이 단순한 영역의 영역 분할과 슈퍼 화소 분할 본문

인공지능/컴퓨터 비전

에지와 영역 4 - 영역 분할 1 : 배경이 단순한 영역의 영역 분할과 슈퍼 화소 분할

huenuri 2024. 11. 10. 06:09

영역 분할은 물체가 점유한 영역을 구분하는 작업이다. 에지는 물체의 경계를 지정하기 때문에 에지가 완벽하다면 영역 분할이 따로 필요하지 않다. 하지만 프로그램 4-3의 실행 결과에서 볼 수 있듯이 명암 변화가 낮은 곳에서 뚫려 폐곡선을 형성하지 못하는 경우가 허다하다.

뚫린 곳을 메우는 작업을 시도하면 메우지 말아야 할 곳을 메워 또 다른 부작용이 발생한다.

 

사람은 영역 분할을 할 때 뇌에 저장되어 있는 물체의 3차원 모델을 꺼내서 사용한다. 관심이 있는 물체에 집중하는 선택적 주의집중 작용을 통해 때로는 사람 영역을 분할하고 때로는 더 세밀하게 눈과 입을 분할하여 표정까지 인식한다. 이처럼 의미 있는 단위로 분할하는 방식을 의미 분할이라 한다. 


 

 

 

1. 배경이 단순한 영상의 영역 분할

책을 스캔하거나 컨베이어 벨트 위를 흐르는 물건을 카메라로 찍은 영상의 경우는 단순한 알고리즘을 사용해도 좋은 분할 성능을 얻을 수 있다. 이진화 알고리즘을 확장해 영역 분할에 쓸 수 있다. 예를 들어 오츄 이진화를 여러 임계값을 사용하도록 확장한 알고리즘으로 여상을 분할할 수 있다. 또한 군집화 알고리즘을 영역 분할에 적용할 수 있는데 (R, B, B) 3개 값으로 표현된 화소를 샘플로 보고 3차원 공간에서 클러스터링을 수행한 다음 화소 각각에 클러스터 번호를 부여한다.

 

워터 셰드는 비가 오면 오목한 곳에 웅덩이가 생기는 현상을 모방하는 연산이다. 워터셰드를 확장해 영역 분할에 활용할 수 있다. 그림 4-14 a는 영상에서 추출한 에지 강도 맵이고 그림 b는 맵을 지형으로 간주한다. 물을 채우는 연산을 반복하면 그림 c와 같이 서로 다른 웅덩이를 찾을 수 있다. 워터셰드 알고리즘은 이렇게 찾은 웅덩이를 영역으로 간주한다.

 

 


 

 

 

2. 슈퍼 화소 분할

때로 영상을 아주 작은 영역으로 분할해 다른 알고리즘의 입력으로 사용하는 경우가 있다. 작은 영역은 화소보다 크지만 물체가 작기 때문에 슈퍼 화소라 한다. 슈퍼 화소 알고리즘이 여럿 있는데 여기서는 SLIC 알고리즘을 소개한다.

 

 


 

 

SLIC 알고리즘으로 슈퍼 화소 분할

다음 프로그램은 SLIC 알고리즘으로 입력 영상을 슈퍼 화소로 분할한다. SLIC에 관한한 OpenCV보다 skimage 라이브러리가 제공하는 slic 함수가 사용하기 편하다. skimage라이브러리를 쓰려면 아래 명령어로 설치해야 한다.

 

 

이렇게 아나콘다 프롬프트에서 설치해주었다.

 

이제 코드를 해석해 보겠다. skimage도 영상을 표시하는 함수가 있는데 익숙한 OpenCV 함수를 사용한다. 두 라이브러리 모두 numpy 배열로 영상을 표현하기 때문에 호환된다. skimage는 RGB 순서로 저장하고 OpenCV는 BGR 순서로 저장하기 때문에 cvtColor 함수로 BGR로 변환하여 출력한다.

14행은 skimage의 slic 함 수로 슈퍼 화소 분할을 수행하고 결과를 slic1 객체에 저장한다. 값이 클수록 네모에 가까운 모양이 만들어지는 대신 슈퍼 화소의 색상 균일성은 희생된다.

 

SLIC 알고리즘으로 입력 영상을 슈퍼 화소 분할하기

 

 

 

 

 

 

 

 

 

 

실행 결과를 살펴보면, 책상의 경계와 커피 잔의 둥그런 경계가 잘 분할된 것을 확인할 수 있다. compactness 인수를 크게 하면 네모 모양은 잘 유지되는 반면 슈퍼 화소의 색상 균일성이 낮아지는 현상을 확인할 수 있다.

 

이 코드는 SLIC (Simple Linear Iterative Clustering) 알고리즘을 사용하여 이미지에서 슈퍼픽셀을 생성하고, 생성된 슈퍼픽셀을 이미지에 표시하여 시각화하는 작업을 수행합니다. 슈퍼픽셀은 이미지의 작은 영역들을 그룹화하여 더 높은 수준의 정보로 처리할 수 있도록 한다.

  1. 이미지 읽기와 표시
    • skimage.data.coffee()로 커피 이미지를 불러와 img에 저장한다.
    • OpenCV에서는 RGB 형식을 BGR 형식으로 변환해야 올바르게 표시되므로, cv.cvtColor를 사용하여 변환 후 cv.imshow로 이미지 창에 표시한다.
  2. SLIC 슈퍼픽셀 분할
    • skimage.segmentation.slic() 함수로 첫 번째 슈퍼픽셀 분할을 수행한다.
    • compactness=20은 색상과 거리의 가중치를 설정하여 슈퍼픽셀의 모양을 제어하는 값이다. 값이 작을수록 슈퍼픽셀이 더 불규칙한 모양을 가질 수 있다.
    • n_segments=600은 생성할 슈퍼픽셀의 개수를 의미하며, 여기서는 600개로 설정했다.
    • skimage.segmentation.mark_boundaries() 함수를 사용하여 이미지에 슈퍼픽셀의 경계를 표시하고, 경계가 표시된 이미지를 sp_img1에 저장한다. 결과는 uint8 형식으로 변환합니다.
  3. 두 번째 SLIC 슈퍼픽셀 분할
    • 두 번째 슈퍼픽셀 분할에서는 compactness=40으로 설정하여 슈퍼픽셀이 더 둥근 형태가 되도록 한다.
    • 이 값이 높을수록 각 슈퍼픽셀이 더 평평한 형태가 되어, 지역적으로 더 규칙적인 형태로 그룹화된다.
    • 이 역시 mark_boundaries 함수를 통해 경계를 표시하고 sp_img2에 저장한 뒤 uint8 형식으로 변환한다.
  4. 슈퍼픽셀 이미지 출력
    • cv.imshow를 사용해 각각의 슈퍼픽셀 경계가 그려진 이미지를 OpenCV 창에 표시한다.
    • compactness=20과 compactness=40의 차이를 시각적으로 비교할 수 있다.
  5. 결과 창 유지 및 닫기
    • cv.waitKey()는 키 입력을 대기하여 창을 유지하고, cv.destroyAllWindows()는 모든 창을 닫는다.

이 코드는 SLIC의 compactness 값을 조정하여 슈퍼픽셀의 형태가 어떻게 달라지는지 확인하는 예제이다.

 


 

 

 

학습을 마치고

뒷부분에 최적화 분할 단원이 있으나 잘 안 되는 부분이 많아서 코드가 무척 길어졌다. 다음 포스트에 이 내용을 이어가 보려고 한다. 

영역 분할로 이렇게 커피의 색깔 영역의 화소가 분할되는 것을 보는 게 참 신기했다.