[2023-04-27] 2. 모폴로지
모폴로지
- 모폴리지 연산
- 영상을 형태학적인 측면에서 다루는 기법
- 이진 영상에서 흰색으로 되어있는 객체의 모양 및 각각의 객체가 이어져 있는지 안 이어져 있는지 등의 관점에서 다룸
- 다양한 영상 처리 시스템에서 전처리(pre-processing) 또는 후처리(post-processing) 형태로 널리 사용
- 수학적 모폴로지(mathematical morphology)
- 영상을 형태학적인 측면에서 다루는 기법
- 구조 요소(structureing element)
- 모폴로지 연산의 결과를 결정하는 커널, 마스크
- 3 x 3 크기의 정방행렬을 가장 많이 사용함
침식과 팽창
침식
- 이진 영상의 침식(erosion) 연산
- 구조 요소가 객체 영역 내부에 완전히 포함될 경우 고정점 픽셀을 255로 설정
- 침식 연산은 객체 외곽을 깎아 내는 연산으로 객체의 크기는 줄어들고 배경은 확대됨
- 실제 영상의 침식 연산 결과
- 차례대로 원본 영상, 침식 1번, 침식 2번, 침식 3번 수행
- 객체 영역(흰색)이 점점 줄어들면서 깎여 나가고, 객체 내부의 검정색 부분인 홀(hole)이 점점 커짐
- 객체 밖의 작은 크기들의 잡음이 점점 제거됨
모폴로지 침식 연산 함수
void erod(InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());
- src : 입력 영상
- dst : 출력 영상(src와 동일한 크기와 타입)
- kernel : 구조 요소
- Mat()을 지정하면 3 x 3 사각형 구성 요소를 사용
- getStructuringElement() 함수에 의해 생성 가능
- anchor : 고정점 위치. Point(-1, -1)이면 중앙점을 사용
- iterations : 반복 횟수
구조 요소(커널) 생성
Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
- shape : 구조 요소 모양 지정 상수
- MORPH_RECT : 사각형 모양
- MORPH_CROSS : 십자가 모양
- MORPH_ELLIPSE : 사각형에 내접하는 타원
- ksize : 구조 요소 크기
- anchor : MORPH_CROSS 모양의 구조 요소에서 중심 좌표로 Point(-1, -1)을 지정하면 구조 요소의 중앙점을 사용
- 반환값 : 0과 1로 구성된 CV_8UC1 타입의 행렬
#####
팽창
- 이진 영상의 팽창(dilation) 연산
- 구조 요소와 객체 영역이 한 픽셀이라도 만날 경우 고정점 픽셀을 255로 설정
- 팽창 연산은 객체 외곽을 확대시키는 연산
- 실제 영상의 팽창 연산 결과
- 차례대로 원본 영상, 팽창 1번, 팽창 2번, 팽창 3번 수행
- 객체 영역(흰색)이 점점 불어나고, 객체 내부의 홀이 채워짐
- 객체 밖의 작은 크기들의 잡음도 점점 커짐
모폴로지 팽창 연산 함수
void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());
- src : 입력 영상
- dst : 출력 영상(src와 동일한 크기와 타입)
- kernel : 구조 요소
- Mat()을 지정하면 3 x 3 사각형 구성 요소를 사용
- getStructuringElement() 함수에 의해 생성 가능
- anchor : 고정점 위치. Point(-1, -1)이면 중앙점을 사용
- iterations : 반복 횟수
열기와 닫기
열기(opening) 연산
- 열기 : 침식 -> 팽창
닫기(closing) 연산
-
닫기 : 팽창 -> 침식
-
열기와 닫기 연산 효과
- 열기 : 각각의 객체가 미세하게 연결되어있는 것을 끊어주고 작은 픽셀들의 잡음을 제거
- 닫기 : 객체 안의 홀들이 사라지고, 객체 바깥의 작은 크기들은 유지
범용 모폴로지 연산 함수
void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue());
- src : 입력 영상
- dst : 출력 영상
- op : 모폴로지 연산 상수
- MORPH_ERODE : 침식
- MORPH_DILATE : 팽창
- MORPH_OPEN : 열기
- MORPH_CLOSE : 닫기
- MORPH_GRADIENT : 모폴로지 그래디언트 = 팽창 - 침식
- kernel : 구조 요소
- anchor : 고정점
- iterations : 반복 횟수
열기 연산을 이용한 잡음 제거 예제
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(void)
{
Mat src = imread("rice.png", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
// 지역 이진화
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
int bw = src.cols / 4;
int bh = src.rows / 4;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
Mat src_ = src(Rect(x*bw, y*bh, bw, bh));
Mat dst_ = dst(Rect(x*bw, y*bh, bw, bh));
threshold(src_, dst_, 0, 255, THRESH_BINARY | THRESH_OTSU);
}
}
// 레이블링 함수를 이용한 흰색 객체 갯수 구하기
Mat labels;
int cnt1 = connectedComponents(dst, labels); // dst의 흰색 객체의 개수에 1을 더한 값을 반환함
cout << "# of objects in dst: " << cnt1 - 1 << endl;
Mat dst2;
morphologyEx(dst, dst2, MORPH_OPEN, Mat());
// 지역 이진화 결과인 dst에 대해서 모폴리지 연산의 열기 연산을 수행하고 그 결과를 dst2에 저장
// erode(dst, dst2, Mat());
// dilate(dst2, dst2, Mat()); // 다음 2줄을 morphologyEx 로 대체해도 같은 결과가 나옴
int cnt2 = connectedComponents(dst2, labels); // dst2의 흰색 객체의 개수에 1을 더한 값을 반환함
cout << "# of objects in dst2: " << cnt2 - 1 << endl;
imshow("src", src);
imshow("dst", dst);
imshow("dst2", dst2);
waitKey();
}
- dst2 결과는 dst 결과의 작은 픽셀들의 잡음을 제거
- dst의 객체 개수는 112개, dst2의 객체 개수는 98개로 검출됨