본문 바로가기

영상처리/OpenCV 및 영상처리 이론

OpenCV 기초 - 5. 모폴로지(morphology) 연산 (1) - 침식, 팽창

 

모폴로지 연산에 대해 알아보고 이를 이용한 영상의 침식, 팽창에 대해 알아 보겠습니다.

 

모폴로지 필터링은 60년대에 영상 분석과 처리를 위해 처음 등장하였습니다. 미리 특정한 형태를 띠는 필터를 만들고 이 필터를 영상에 씌워 새로운 영상을 얻어내는 기법입니다. 이러한 모폴로지 연산은 어떻게 사용하냐에 따라서 여러 결과를 얻을 수 있습니다.

 

 

 

 

0123456789

 

<모폴로지 연산 예시 - 팽창>

 

침식과 팽창 연산은 현재 픽셀을 기준하여 필터를 적용하여 필터 영역안의 값을 확인하고 현재 픽셀의 값을 수정하게 됩니다. 

 

- 침식 : 필터 영역내 픽셀 중 최소 픽셀값을 현재 픽셀값에 대입

- 팽창 : 필터 영역내 픽셀 중 최대 픽셀값을 현재 픽셀값에 대입

 

이러한 특징을 슬라이드를 통해 확인할 수 있으며 팽창 연산을 수행하면 결과로 10.png의 파란색 박스가 오른쪽의 결과처럼 변화하게 됩니다. 흰 영역(255)가 전보다 넓어지는 형태이기 때문에 팽창이라 부르고 반대의 경우를 침식이라고 부릅니다.

 

- void erode ( InputArray src, OutputArray dst, InputArray kernel,  Point anchor = Point(-1, -1), int iteration = 1, int borderType = BORDER_CONSTANT, const Scalar & boderValue = morphologyDefaultBorderValue() )

입력 영상에 대해 입력 받은 커널을 필터로 하여 침식을 수행하는 함수입니다. 또 iteration에 값을 주어 원하는 만큼 침식을 수행할 수 있습니다. 침식 연산을 통해 이미지에서 원하지 않는 작은 노이즈들을 제거할 수 있습니다. 하지만 노이즈 성분이 제거되는 것뿐만 아니라 대상의 영역이 줄어들게 된다는 문제가 있습니다.

 

- void dilate InputArray src, OutputArray dst, InputArray kernel Point anchor = Point(-1, -1), int iteration = 1, int borderType = BORDER_CONSTANT, const Scalar & boderValue = morphologyDefaultBorderValue() )

erode()와 사용법이 동일하고 결과에만 차이가 있습니다. 팽창 연산을 통해 이미지에서 대상이 가지는 작은 구멍들을 채울 수 있습니다. 하지만 대상의 크기가 전체적으로 전보다 커진다는 문제와 노이즈 성분이 커질 가능성이 있습니다.

 

 

이러한 침식과 팽창이 가지는 문제를 해결하기 위해 침식과 팽창을 연이어 사용하는 닫힘과 열림 연산이 존재합니다.

 

 

- void morphologyEx ( InputArray src, OutputArray dst, int op, InputArray kernel Point anchor = Point(-1, -1), int iteration = 1, int borderType = BORDER_CONSTANT, const Scalar & boderValue = morphologyDefaultBorderValue() )

열림과 닫힘을 수행하는 함수로 파라미터 op의 설정에 해당하는 연산을 수행합니다. 

- 열림 연산(제거 연산)

침식 → 팽창, 2번의 모폴로지 연산을 연이어 수행

침식을 통해 잡음이 제거되고 팽창을 통해 크기가 줄어든 영역을 다시 키움

 

- 닫힘 연산(채움 연산)

팽창 → 침식, 2번의 모폴로지 연산을 연이어 수행

팽창을 통해 작은 구멍들을 채우고 침식을 통해 커진 영역을 다시 줄임

 

 

각 파라미터의 자세한 설정은

http://docs.opencv.org/3.1.0/d4/d86/group__imgproc__filter.html#gaeb1e0c1033e3f6b891a25d0511362aeb

을 참고하시길 바랍니다.

 

<테스트 이미지>

테스트 이미지로는 저자의 홈페이지에서 제공하는 위 사진을 이진화(thresholding)하여 이진 영상으로 만들어서 사용하였으며 결과 사진의 Image가 이에 해당합니다.

 

 

 

- 결과

 

 

침식 결과를 통해서 원본 이미지에 있는 잡음들이 제거된 것을 확인할 수 있고 팽창을 통해 객체의 빈 부분이 채워진 것을 볼 수 있다. 또 7x7침식과 3x3(기본)침식 3회가 같은 결과를 내는 것을 확인할 수 있다.

 

열림과 닫힘, 두 필터는 객체 감지에 주로 사용하게 된다. 열림 필터가 영상의 잡음을 제거하고 닫힘 필터가 객체의 작은 부분을 연결시키기 때문에 두 필터를 순서대로 사용하여 좋은 결과를 얻을 수 있다.

 

또 Closed and Opened Image에 해당하는 영상에 열림 혹은 닫힘을 여러번 적용해도 아무런 효과를 얻을 수 없는데, 첫 번째 열림에 의해 구멍이 채워졌다면 더 이상 동일한 필터를 적용해도 해당 영상에는 변화가 생기지 않음을 확인할 수 있다.

 

 

- 코드 첨부

더보기

/*------------------------------------------------------------------------------------------*\

   This file contains material supporting chapter 5 of the cookbook:  

   Computer Vision Programming using the OpenCV Library. 

   by Robert Laganiere, Packt Publishing, 2011.

 

   This program is free software; permission is hereby granted to use, copy, modify, 

   and distribute this source code, or portions thereof, for any purpose, without fee, 

   subject to the restriction that the copyright notice may not be removed 

   or altered from any source or altered source distribution. 

   The software is released on an as-is basis and without any warranties of any kind. 

   In particular, the software is not guaranteed to be fault-tolerant or free from failure. 

   The author disclaims all warranties with regard to this software, any use, 

   and any consequent failure, is purely the responsibility of the user.

 

   Copyright (C) 2010-2011 Robert Laganiere, www.laganiere.name

\*------------------------------------------------------------------------------------------*/

 

#include <opencv2/core/core.hpp>

#include <opencv2/imgproc/imgproc.hpp>

#include <opencv2/highgui/highgui.hpp>

 

int main()

{

// 입력 영상 읽기

cv::Mat image= cv::imread("../binary.bmp");

if (!image.data)

return 0; 

 

    // 영상 띄워 보기

cv::namedWindow("Image");

cv::imshow("Image",image);

 

// 영상 침식

cv::Mat eroded;

cv::erode(image,eroded,cv::Mat());

 

    // 침식된 영상 띄워 보기

cv::namedWindow("Eroded Image");

cv::imshow("Eroded Image",eroded);

 

// 영상 팽창

cv::Mat dilated;

cv::dilate(image,dilated,cv::Mat());

 

    // 팽창된 영상을 띄워 보기

cv::namedWindow("Dilated Image");

cv::imshow("Dilated Image",dilated);

 

// 큰 구조 요소로 영상 침식

cv::Mat element(7,7,CV_8U,cv::Scalar(1));

cv::erode(image,eroded,element);

 

    // 침식된 영상 띄워 보기

cv::namedWindow("Eroded Image (7x7)");

cv::imshow("Eroded Image (7x7)",eroded);

 

// 영상을 세 번 침식시키기.

cv::erode(image,eroded,cv::Mat(),cv::Point(-1,-1),3);

 

    // 침식된 영상 띄워 보기

cv::namedWindow("Eroded Image (3 times)");

cv::imshow("Eroded Image (3 times)",eroded);

 

// 영상 닫힘

cv::Mat element5(5,5,CV_8U,cv::Scalar(1));

cv::Mat closed;

cv::morphologyEx(image,closed,cv::MORPH_CLOSE,element5);

 

    // 영상 닫힙 띄워 보기

cv::namedWindow("Closed Image");

cv::imshow("Closed Image",closed);

 

// 영상 열림

cv::Mat opened;

cv::morphologyEx(image,opened,cv::MORPH_OPEN,element5);

 

    // 영상 열림 띄워 보기

cv::namedWindow("Opened Image");

cv::imshow("Opened Image",opened);

 

// 영상 닫힘과 영상 열림

cv::morphologyEx(image,image,cv::MORPH_CLOSE,element5);

cv::morphologyEx(image,image,cv::MORPH_OPEN,element5);

 

    // 영상 닫힘/열림 띄워 보기

cv::namedWindow("Closed and Opened Image");

cv::imshow("Closed and Opened Image",image);

cv::imwrite("binaryGroup.bmp",image);

 

// 입력 영상 읽기

image= cv::imread("../binary.bmp");

 

// 영상 닫힘과 영상 열림

cv::morphologyEx(image,image,cv::MORPH_OPEN,element5);

cv::morphologyEx(image,image,cv::MORPH_CLOSE,element5);

 

    // 영상 닫힘/열림 띄워 보기

cv::namedWindow("Opened and Closed Image");

cv::imshow("Opened and Closed Image",image);

 

cv::waitKey();

return 0;

}