메뉴 닫기

OpenCV 모폴로지 연산 (2) – 열기, 닫기

이전 포스팅에 이어, 모폴로지 연산의 열기(Open)와 닫기(Close)에 대해 알아보고자 합니다.

관련글

모폴로지 연산 – 열기&닫기

모폴로지 연산에서 침식은 노이즈를 제거할 수 있으나 객체의 형태도 깎이는 변형이 발생하고, 반대로 팽창은 객체 내부의 작은 구멍들을 메워주지만 객체의 형태가 팽창하며 노이즈도 진해집니다.

그러므로 노이즈가 많은 영상에서는 침식 연산 후 팽창 연산-열기; Open-을 하면 노이즈만 제거하는 효과 (객체가 깎인 형태에서 다시 팽창됨)를 얻을 수 있고, 반면 노이즈는 적지만 작은 구멍이 많은 영상에서는 팽창 연산 후 침식 연산-닫기; Close-을 하면 구멍을 메우는 효과 (객체가 팽창된 형태에서 다시 깎임)를 얻을 수 있습니다.

  • 열기: 침식 → 팽창
  • 닫기: 팽창 → 침식

범용 모폴로지 연산 함수

범용 모폴로지 연산함수 morphologyEx 는 이전 포스팅에서 소개한 erode/dilate 함수와 사용방법이 거의 유사합니다.

void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1, -1),

int iterations = 1, int borderType = BORDER_CONSTANT,

const Scalar & boderValue = morphologyDefaultBorderValue())

  • InputArray src: 입력 영상; 채널 수는 임의로 지정할 수 있으나 depth는 CV_8U, CV_16U, CV_16S, CV_32F, CV_64F 중 하나여야 함.
  • OutputArray dst: 출력영상. 입력영상과 크기 및 타입 같음.
  • int op: 모폴로지 연산 타입 (침식, 팽창, 열기, 닫기, 그래디언트, 탑햇, 블랙햇, 힛미스)
  • InputArray kernel: structuring element; Mat()을 지정하면 $3 times 3$ 사이즈의 사각 element가 사용됨. getStructuringElement 를 사용할 수도 있음.
  • Point anchor: element 내 앵커 위치로 기본값(-1, -1)은 앵커의 중심을 의미함.
  • int iteration: 연산 반복 횟수.
  • int borderType: 가장자리 픽셀의 extrapolation 메소드 타입 선택. (가장자리에 대한 연산 타입은 BORDER_CONSTANT, BORDER_REPLICATE 등이 있으며 BORDER_WRAP는 지원 안함)
  • const Scalar & borderValue: 가장자리 픽셀 처리 방식이 BORDER_CONSTANT일 경우, 가장자리를 채울 값

노란색으로 마킹한 3번째 파라미터(int op)는 모폴로지 연산 타입을 선택하는 것인데요, 다음과 같이 8개의 옵션이 있습니다.

Enumerator Description
MORPH_ERODE 침식 연산
MORPH_DILATE 팽창 연산
MORPH_OPEN 열기(Opening) 연산
$mathrm{dilate} (mathrm{erode}(texttt{src}, texttt{element}))$
MORPH_CLOSE 닫기(Closing) 연산
$mathrm{erode} (mathrm{dilate}(texttt{src}, texttt{element}))$
MORPH_GRADIENT 그래디언트 연산 – 경계값 검출 효과
$mathrm{dilate} (texttt{src}, texttt{element} )-mathrm{erode}(texttt{src}, texttt{element})$
MORPH_TOPHAT 탑햇 연산 – Open 연산을 통해 침식된 밝은 부분 검출
$texttt{src} – mathrm{open} (texttt{src}, texttt{element})$
MORPH_BLACKHAT 블랙햇 연산 – Close 연산을 통해 메워진 어두운 부분 검출
$mathrm{close}(texttt{src}, texttt{element})-texttt{src}$
MORPH_HITMISS Hit or Miss – structuring element와 비교하여 일치하는 패턴 찾기
* https://docs.opencv.org/master/db/d06/tutorial_hitOrMiss.html

모폴로지 연산 적용 예제

아래의 원본 영상에 모폴로지 연산을 적용해보도록 하겠습니다.



원본 영상

예제 코드

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

using namespace std;
using namespace cv;

int main()
{
	Mat src = imread("paint.bmp", IMREAD_GRAYSCALE);
	if(src.empty())
	{
		cerr << "file error" << endl;
		return 1;
	}
	
	Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
	
	Mat dst_erode;
	morphologyEx(src, dst_erode, MORPH_ERODE, element);

	Mat dst_dilate;
	morphologyEx(src, dst_dilate, MORPH_DILATE, element);
	
	Mat dst_open;
	morphologyEx(src, dst_open, MORPH_OPEN, element);
	
	Mat dst_close;
	morphologyEx(src, dst_close, MORPH_CLOSE, element);
	
	Mat dst_gradient;
	morphologyEx(src, dst_gradient, MORPH_GRADIENT, element);

	Mat dst_tophat;
	morphologyEx(src, dst_tophat, MORPH_TOPHAT, element);
	
	Mat dst_blackhat;
	morphologyEx(src, dst_blackhat, MORPH_BLACKHAT, element);
	
	Mat dst_hitmiss;
	morphologyEx(src, dst_hitmiss, MORPH_HITMISS, element);
	

	imshow("src", src);	
	imshow("erode", dst_erode);
	imshow("dilate", dst_dilate);
	imshow("open", dst_open);
	imshow("close", dst_close);
	imshow("gradient", dst_gradient);
	imshow("tophat", dst_tophat);
	imshow("blackhat", dst_blackhat);
	imshow("hitmiss", dst_hitmiss);
	
	waitKey();
	destroyAllWindows();
	
	return 0;
}

실행 결과



실행 결과

결과를 보면, 위에서 설명한 내용을 쉽게 이해할 수 있으리라 생각합니다. 영상의 특징 및 해결해야 할 문제가 무엇이냐에 따라, 적절한 모폴로지 연산을 선택해야 하고 structuring element의 속성도 적절히 선택해야 합니다. 그리고 비슷한 문제를 해결하기 위한 다른 방법, 더 좋은 방법들도 많답니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다