본문 바로가기
코딩/OpenCV

[C++ opencv] opening, closing 기법 사용하여 경계 확실히 하기

by DIYver 2020. 7. 3.

본문 목표

저번시간에 침식연산(Erosion)팽창연산(Dilation) 에 대해서 알아보았었다.

 

https://diyver.tistory.com/61

 

[C++ opencv] erode, dilate 사용하여 물체 명확하게 하기

본문 목표 이 전까지 grayscale 과 threshold 에 대해서 배웠었다. 이제 노이즈를 제거함에 있어서 획기적인 방법 중 하나인 erode 또는 dilate 기법에 대해서 알아보도록 하자. Erode 와 Dilate 연산을 모폴��

diyver.tistory.com

이번에는 그 두가지 연산을 혼합하는 새로운 기법을 알아보도록 한다.

 

openingclosing 기법인데, 말 그대로 열어주고 닫아주는 작업이다.

 

무슨 소리인지 이해 안되는 것이 당연하다.

 

opening"닫혀 있는 것을 열어주다" 라는 것이고,

 

closing"열려 있던 것을 닫아주다" 라고 우선적으로 이해해자.

 

원리를 이해해야 기억이 오래가고, 정상적으로 사용 가능하다.

 

두 가지 기법은 물체를 인식할 때에 매우 유용하다는 것을 알아가면 된다.

 

 

 

 

 

키워드 : opening, closing

 

 

 

 

 

 

 

Opening 개념 이해

 

opening 은 "닫혀있는 것을 열어주는 것" 이라고 생각하면 된다.

 

좀 다르게 해석하면 "연결 되어 있는 것을 끊어 주는 것" 이라고 할 수 있겠다.

 

위의 이미지를 보면,

 

원본 이미지의 두 원이 서로 연결 되어 있다.

 

이 이미지에 opening 기법을 적용시키면 우측처럼 되는데,

 

연결 되어 있는 것을 끊어 준 결과가 되었다고 할 수 있다.

 

 

방법은 간단하다.

 

침식연산(Erosion) 후에 바로 팽창연산(Dilation)을 적용하면 된다.

(Erosion → Dilation)

 

침식연산을 함으로써, 일단 연결 되어있던 부위가 끊기게 된다.

 

문제는 침식연산을 하면 물체(object)의 영역이 전체적으로 줄어들게 되는데

 

이를 보정하기 위해서 다시 팽창연산을 해주는 것이다.

 

그렇게 되면 쌤쌤이 되어서 연결되어 있던 부위만 끊기게 되고

 

확인하고자 하는 물체(object)의 영역 크기는 유지하게 된다.

 

 

 

Closing 개념 이해

 

closing 은 "열려있던 것을 닫게하는 것" 이라고 생각하면 된다.

 

좀 다르게 해석하면 "끊겨 있는 것을 합치게 하는 것" 이라고 할 수 있겠다.

 

위의 이미지를 보면,

 

원본 이미지는 물체(object)가 누가봐도 하나의 볼트인 것을 사람이라면 유추해서 알 수 있다.

 

그런데 하나의 물체인데 픽셀로 보면 끊겨있는 문제점이 있다.

 

이럴 때, 끊겨 있는 것을 합치게 하는 closing 연산을 사용하는 것이다.

 

closing 연산을 사용한 결과는 우측 이미지인데 확실히 픽셀이 합쳐진 것을 확인할 수 있다.

 

 

이 역시 방법은 간단하다.

 

팽창연산(Dilation)을 한 후에 바로 침식연산(Erosion)을 사용하면 된다.

(Dilation → Erosion)

 

팽창연산을 함으로써 끊겨 있던 부위들이 결합되게 된다.

 

대신에 물체(object)의 영역이 전체적으로 커지게 되므로, 

 

이를 보정하기 위해서 침식연산을 적용하는 것이다.

 

그러면 끊겨있던 부위들은 합쳐지면서 물체의 전체 영역크기는 변함이 없게된다.

 

 

 

 

 

 

Opening 코드 테스트 결과

- CODE

#include <opencv2/opencv.hpp>


using namespace cv;
using namespace std;

int main(int ac, char** av) {
	Mat img = imread("lane.png", 0);	//이미지를 grayscale로 불러옴

	Mat img_dilate;
	Mat img_erode;

	Mat img_threshold;

	threshold(img, img_threshold, 180, 255, THRESH_BINARY);
	
	erode(img_threshold, img_erode, Mat::ones(Size(3, 3), CV_8UC1), Point(-1, -1));
	dilate(img_erode, img_dilate, Mat::ones(Size(3, 3), CV_8UC1), Point(-1, -1));

	imshow("original", img_threshold);

	imshow("img_dilate", img_dilate);
	imshow("img_erode", img_erode);
	waitKey(0);


	return 0;
}

 

- RESULT

lane 검출용 이미지 _ 원본 _ color

 

이진화 시킨 원본 이미지

원본 이미지를 이진화 한 결과이다.

 

확실히 주변에 노이즈가 많이 끼는 것을 볼 수 있다.

 

 

 

침식연산 적용한 이미지

침식 연산을 적용하게 되니 노이즈가 확 없어진 것을 볼 수 있다.

 

대신 차선(lane)의 영역 크기와 경찰복 입은 마네킹의 영역도 줄어든 것을 볼 수 있다.

 

 

팽창연산 적용한 이미지

팽창 연산을 적용하니 차선과 경찰복 입은 마네킹 영역이 원본과 같아졌음을 확인할 수 있다.

 

대신 침식 연산을 통해서 차선 중앙에 있는 점선 부분이 없어져버렸다...

 

점선 때문에 차선 검출에 문제가 생기는 경우라면 opening 기법이 도움이 될 것이다.

 

하지만, 점선을 검출해야 하는 경우에는 opening 기법이 좋지 않음을 알 수 있다.

 

절대적인 것은 없다.

 

언제나 상황에 맞게 알맞은 방법을 사용해야 하는 것이다.

 

 

 

 

 

 

Closing 코드 테스트 결과

- CODE

#include <opencv2/opencv.hpp>


using namespace cv;
using namespace std;

int main(int ac, char** av) {
	Mat img = imread("rice.tif", 0);	//이미지를 grayscale로 불러옴

	Mat img_dilate;
	Mat img_erode;

	Mat img_threshold;

	threshold(img, img_threshold, 180, 255, THRESH_BINARY);

	dilate(img_threshold, img_dilate, Mat::ones(Size(3, 3), CV_8UC1), Point(-1, -1),2);
	erode(img_dilate, img_erode, Mat::ones(Size(3, 3), CV_8UC1), Point(-1, -1),2);
	

	imshow("original", img_threshold);

	imshow("img_dilate", img_dilate);
	imshow("img_erode", img_erode);
	waitKey(0);


	return 0;
}

 

 

- RESULT

원본 이미지 _ grayscale 

 

이진화 연산 적용한 원본 이미지

원본 이미지를 이진화 시킨 결과이다.

 

연산결과를 잘 보여주기 위해서 임계값을 일부러 높게 잡았다.

 

사진을 보면 하나의 쌀알들이 선명하게 보이지 않고, 노이즈가 많이 꼈음을 알 수 있다.

 

이런 사진에서 쌀알 개수를 세라고하면 제대로 세기 어려울 것이다.

 

 

 

팽창연산 2회 적용한 이미지

팽창연산을 2번 적용하여 분리되어있던 픽셀끼리 합치게 하였다.

 

 

물체의 원래 크기로 되돌리기 위해서 침식연산을 2번 적용하였다.

 

그 결과로 확실히 원본 이미지 보다는 쌀알 하나하나가 형태가 정상적으로 잡히는 것을 확인할 수 있다.

 

closing 기법은 확실히 분리되어 있는 물체를 하나로 합치게 해주는 것임을 알 수 있다.

 

 

 

 

 

 

 

결론.

확실히 openingclosingerosiondilation을 적절히 이용한 기법임을 알 수 있었다.

 

하지만 어느 상황에 무엇을 적용할지는 사용자의 몫이다.

 

좋은 결과를 줄 수도 있는 반면에, 중요한 정보를 얻지 못하게 되는 문제가 발생할 수 있다.

 

두 기법을 가장 많이 사용하는 곳은 공장에서 물체의 개수를 셀 때 많이 사용한다.

 

하지만 꼭 물체의 개수를 셀 때에만 사용하라는 법은 없다.

 

필자는 이를 자율주행 자동차 시스템에서 차선인식에 사용한다.

 

차선을 인식하려 할 때, 정말 생각치도 못한 노이즈가 많이 발생하는데, 그런 노이즈를 잡아주는데 큰 도움을 주는 기법이었다.

 

이처럼 노이즈 제거에도 유용하게 쓰일 수 있는 방법이므로, 꼭 알아둘 필요가 있다.

 

 

 

 

 

 

 

 

도움이 되었거나, 문제가 있는 경우 댓글로 알려주세요~!

감사의 댓글은 작성자에게 큰 힘이 됩니다 ^^

댓글