본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] 모폴로지(morphology) 연산 총정리(1) - 침식(Erode), 팽창(Dilate) getStructuringElement

 

이번에는 이진 영상처리에서 은근히 많이 사용되는 모폴로지(morphology) 연산에 대해 알아보겠습니다.

노이즈 제거, hole 채우기, 끊어진 선 이어 붙이기 등에 많이 사용되고 있습니다.

모폴로지 연산 이진화 영상에서 가장 많이 사용되며, OpenCV에서는 binary 영상 뿐만 아니라 gray-scale 영상, color 영상까지 지원이 됩니다.

모폴로지를 알기 위해서는 먼저 Structuring Element를 알아야 합니다.

Structuring Element

가장 많이 사용되는 형태는 아래와 같습니다.

일단 3x3 window로 설명을 드리자면 4가지 정도의 모양을 넣을 수 있고,

가운데 노란색 부분은 anchor point 로써 주변 값을 보고 변경할 값의 위치를 나타냅니다.

함수 기본형은 아래와 같습니다.

cv::MORPH_RECT
사각형 모양
cv::MORPH_CROSS
십자가 모양
cv::MORPH_ELLIPSE
타원 모양 (ksize의 내접 타원)

 

  • ksize: kernel size로 모양의 크기(가로 x 세로, 홀수)를 나타냅니다.
  • anchor: 지정이 되지 않으면 (-1, -1)이 default로 입력되고 변경될 값의 기준위치를 나타냅니다.

 

직접 확인해보겠습니다. 다음과 같이 코드를 짰을 경우 생성된 element를 보면,

아래와 같이 생성됩니다.

각 모양에 따라 팽창(Dilation)을 시켰을 경우의 예시를 아래와 같이 그려봤습니다.

언제 저런 모양을 사용하는지 감이 잡히시나요?

침식과 팽창에 대해 설명을 할텐데,

침식 깎인다는 뜻이고, 팽창 늘어난다는 뜻인 것만 아시고,

모든 것은 흰색(foreground) 기준으로 용어를 정의했다고 보시면 헷갈리지 않습니다.

침식 (erosion)

침식은 위에서 설정한 모양을 적용해서 하나라도 0이 있으면 대상 픽셀을 0으로 바꾸는 방법입니다. 침식의 원형은 아래와 같습니다.

src
입력 영상
dst
출력 영상
kernel
Structuring Element, cv::Mat() 입력 시 3x3 Rect 구조 사용
anchor
중심 위치, default인 (-1, -1) 입력 시 중앙점
iteration
반복 횟수
borderType
가장자리 픽셀 확장 방식
borderValue
borderType이 BORDER_CONSTANT인 경우의 값

 

인자는 많지만 src, dst, kernel을 제외한 나머지는 기본값이 있으므로 생략이 가능합니다.

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
	cv::Mat src = cv::imread("./binImg.bmp", cv::IMREAD_GRAYSCALE);

	cv::Mat element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, 3));
	cv::Mat element2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(2, 2));

	cv::Mat dst1, dst2;
	cv::erode(src, dst1, element1);
	cv::erode(src, dst2, element2);

	return 0;
}
 

결과는 아래와 같습니다.

element1처럼 cv::Size(1, 3)으로 할 경우 위아래만 침식이 일어나는 것을 볼 수 있고,

사각형일 경우에는 상하좌우대각선까지 침식이 이루어지는 것을 볼 수 있습니다.

중요!! anchor point는 MORPH_CROSS일 경우에만 적용된다는 사실!!!

 

팽창(Dilation)

 

팽창은 침식과 반대로 위에서 설정한 모양 내에 하나라도 255가 있을 경우 대상 픽셀을 255로 바꾸는 연산입니다. 팽창의 원형은 아래와 같습니다.

인자는 erode와 동일합니다. 설명은 생략할께요.

위 영상 그대로 dilate를 적용해보면, 소스는 아래와 같고

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
 	cv::Mat src = cv::imread("./binImg.bmp", cv::IMREAD_GRAYSCALE);

	cv::Mat element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 1));
	cv::Mat element2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(2, 2));	

	cv::Mat dst1, dst2;
	cv::dilate(src, dst1, element1);
	cv::dilate(src, dst2, element2);

	return 0;
}
 

결과는 아래와 같습니다.

element1은 팽창을 좌우로 시켰고, element2 좌우상하대각으로 시킨 경우 입니다.

 

위의 설명을 쉽게 정리하면,

침식(Erosion): 모양 영역 내 픽셀 중 최소 픽셀 값을 현재 픽셀 값에 대입
팽창(Dilation): 모양 영역 내 픽셀 중 최대 픽셀 값을 현재 픽셀 값에 대입하는 연산

다른 모폴로지 연산에 대해서는 아래 글에서 확인하세요!~