본문 바로가기
코딩/OpenCV

[C++ opencv] High pass filter (HPF)로 윤곽선 검출, edge detection

by DIYver 2020. 7. 15.

본문 목표

저번 포스터에서 Low pass filter를 다뤘고, 영상에서 노이즈 제거를 함을 알아봤다.

 

이번에는 비슷한 개념이면서 다른 High pass filter를 이해해보고, 어느 상황에서 사용하는지 알아보자.

 

 

 

 

키워드 : high pass filter, HPF, 고주파 통과 필터, 라플라시안, 라플라시안-가우시안, sobel

 

 

 

 

 

 

 

 

 

 

알아볼 함수 원형

- 고역주파수 통과 필터 ( High pass filter )

	GaussianBlur(img, img_gaussian, Size(3, 3), 0, 0, 4);

	Sobel(img_gaussian, img_sobel_x, CV_8U, 1, 0, 3, (1.0), (0, 0), 4);
	Sobel(img_gaussian, img_sobel_y, CV_8U, 0, 1, 3, (1.0), (0, 0), 4);
	addWeighted(img_sobel_x, 1, img_sobel_y, 1 , 0, img_sobel);
	
	Laplacian(img_gaussian, img_lap, CV_8U, 3, (1.0), (0, 0), 4);
	
	Canny(img, img_canny, 80, 120);

 

Sobel( src, dst, ddepth, dx, dykernel_size, scale, delta, border_Type

 

  ○ src : 입력할 이미지 변수

 

  ○ dst : 필터가 적용되어 저장될 이미지 변수

 

  ○ ddepth : 이미지의 자료형 입력, CV_8U 또는 CV_16S 등...

 

  ○ dx : x 방향으로 미분을 명령, 0(false) 또는 1(true)을 입력

 

  ○ dy : y 방향으로 미분을 명령, 0(false) 또는 1(true)을 입력

 

  ○ kernel_Size : 적용시킬 평균 필터의 사이즈, Size( x, y) 로 입력해야 함

 

  ○ scale : 미분 값에 대한 배율 조절, 보통 (1,0) 사용

 

  ○ delta : 결과 이미지에 더해줄 값, 보통 (0, 0) 사용

 

  ○ border_Type : 이미지의 테두리에 대한 처리방법, 

    - zero padding 또는 reflect(mirror) padding 을 적용할 수 있음

      ■ BORDER_CONSTANT  또는  0

      ■ BORDER_REPLICATE  또는 1

      ■ BORDER_REFLECT  또는  2 
      ■ BORDER_DEFAULT  또는  4 

      ■ BORDER_REFLECT101  또는  4 (기본 값) 

      ■ BORDER_TRANSPARENT  또는  5 
      ■ BORDER_ISOLATED  또는  16

 

 

 

 

Laplacian( src, dst, ddepth, kernel_size, scale, delta, border_Type

 

  ○ src : 입력할 이미지 변수

 

  ○ dst : 필터가 적용되어 저장될 이미지 변수

 

  ○ ddepth : 이미지의 자료형 입력, CV_8U 또는 CV_16S 등...

 

  ○ kernel_Size : 적용시킬 평균 필터의 사이즈, Size( x, y) 로 입력해야 함

 

  ○ scale : 미분 값에 대한 배율 조절, 보통 (1,0) 사용

 

  ○ delta : 결과 이미지에 더해줄 값, 보통 (0, 0) 사용

 

  ○ border_Type : 이미지의 테두리에 대한 처리방법, 

    - zero padding 또는 reflect(mirror) padding 을 적용할 수 있음

      ■ BORDER_CONSTANT  또는  0

      ■ BORDER_REPLICATE  또는 1

      ■ BORDER_REFLECT  또는  2 
      ■ BORDER_DEFAULT  또는  4 

      ■ BORDER_REFLECT101  또는  4 (기본 값) 

      ■ BORDER_TRANSPARENT  또는  5 
      ■ BORDER_ISOLATED  또는  16

 

 

 

 

Canny( srcdstthreshold1threshold2, aperture_size)

 

  ○ src : 입력할 이미지 변수 (grayscale 이미지를 입력해야함! )

 

  ○ dst : 필터가 적용되어 저장될 이미지 변수

 

  ○ threshold1 : 낮은 경계 값 (0~255)

 

  ○ threshold2 : 높은 경계 값 (0~255)

 

  ○ aperture_size : sobel 연산 적용할 구멍 크기, 기본값 3

 

 

 

 

 

 

여태 정리한 것들 중에서 제일 복잡한 것 같다.

 

셋다 High Pass Filter 에 속하는 연산들이다.

 

영상처리에 있어서 High Pass Filter는 가장자리(edge)를 두드러지게 하는 것이다.

 

그러면 sobel 은 뭐고, laplacian은 뭐고, canny는 뭔지 알아보자.

 

전문적으로 알 필요는 없으니깐, 쉽게 설명해보겠다.

 

sobel 1차 미분이다.

 

laplacian 2차 미분이다.

 

canny 는 sobel 연산을 이용한 결과에 방향성을 추가로 고려한 연산이다.

 

 

High pass filter 와 Low pass filter 가 커널로 비교했을 때의 차이점이다.

커널을 알아야 이해가 될 것이다.

 

Low pass filter 는 누가봐도 평균 필터의 일종이다.

 

High pass filter 는 필터 영역 중앙에 가중치를 더 많이 주는 것을 볼 수 있다.

 

미분 연산을 해보면 high pass filter 처럼 행렬을 쓸 수 있는 것이고, 결과는 경계를 선명하게 해준다.

 

 

 

 

그러면 High pass filter 가 어떤 역할을 하는지 알아봤으니,

1차 미분은 뭐고, 2차 미분은 뭔지 알아보자

 

일단 미분이 왜 적용되는지 간단하게 짚고가자.

 

1차 미분을 하면 위와 같이 변한다.

밝기 정보가 갑작스럽게 변하는 구간이 경계일 확률이 높으므로,

1차 미분한 그래프에서 값이 높은 부분을 경계라고 보는 것이다.

 

이처럼 경계를 찾고싶으면 밝기의 변화율을 볼 수 있는 미분을 사용하는 것이다.

 

 

 

1. sobel : x와 y 방향으로 각각 1차 편미분한 결과를 합친 것

x방향으로 편미분하여 밝기 증가율이 높은 부분이 표시 되었다.

 

 

y 방향으로 편미분하여 밝기 증가율이 높은 부분이 표시 되었다.

 

위의 두 이미지를 합치면

위와 같은 이미지의 경계를 표시해주는 이미지가 나타나게 된다.

 

 

여기서 더 선명한 경계를 얻으려면 zero_crossing 을 해주어야 하는데

자세히 다루지는 않겠다.

 

 

 

 

 

2. Laplacian : 2차 미분을 하여 선명한 경계를 얻음

 

- 1차 미분만 한다면 약간 애매한 결과를 얻게 되는데, 2차 미분을 하면 확실한 밝기 변화를 볼 수 있다.

 

2차 미분을 하게되면, 밝기 변화율에 대한 변화율을 얻을 수 있게 되는데,

양에서 음으로 가는 순간 또는, 음에서 양으로 가는 순간을 경계로 인식하는 것이다.

 

결과는 위와 같이 나오게 된다.

 

opencv 에서 라플라시안을 사용하면 기본적으로 sobel 연산이 적용된다고 한다.

라플라시안도 x y 방향으로부터 미분값을 받아와 합쳐지게 된다.

 

 

 

 

 

3. Canny : sobel 연산을 적용하고 방향성을 고려한 경계 추출 방법

sobel 연산 결과에서 경계선이 연속적일 수 있게 방향성을 고려해 불필요 없는 부분을 지워준다.

 

 

 

 

 

 

 

Canny 를 제외하고

sobel 과 laplacian 을 비교해보면

 

sobel의 문제점은 검은색에서 흰색으로 진행되면 경계를 잘 찾아낸다.

하지만 밝은 부분에서 어두운 부분으로 바뀌는 부분은 경계로 인식하지 못한다.

 

laplacian은 어느 방향에서든 확실하게 경계를 잡아낸다.

 

 

 

그리고 canny 에는 자동으로 포함된 기능이 하나 있다.

바로 Gaussian filter 이다.

 

경계를 찾음에 있어서 선행되어야 하는 작업은 당연 노이즈 제거이다.

 

노이즈 제거 없이 경계를 찾으려 한다면 많은 문제가 생기게 된다.

 

미분을 하는데 방해가 되기 때문이다.

 

그래서 항상 노이즈를 제거한 이미지를 입력해 주어야 한다.

 

 

 

 

 

 

 

 

 

코드 테스트 결과

- CODE

#include <opencv2/opencv.hpp>


using namespace cv;
using namespace std;

int main(int ac, char** av)
{
	Mat img = imread("Lenna.png",0);
	Mat img_gaussian, img_canny;
	Mat img_sobel_x, img_sobel_y, img_sobel;
	Mat img_lap;

	GaussianBlur(img, img_gaussian, Size(3, 3), 0, 0, 4);

	Sobel(img_gaussian, img_sobel_x, CV_8U, 1, 0, 3, (1.0), (0, 0), 4);
	Sobel(img_gaussian, img_sobel_y, CV_8U, 0, 1, 3, (1.0), (0, 0), 4);
	addWeighted(img_sobel_x, 1, img_sobel_y, 1 , 0, img_sobel);
	
	Laplacian(img_gaussian, img_lap, CV_8U, 3, (1.0), (0, 0), 4);
	
	Canny(img, img_canny, 80, 120);

	imshow("original", img);
	imshow("img_sobel_x", img_sobel_x);
	imshow("img_sobel_y", img_sobel_y);
	imshow("img_sobel", img_sobel);
	imshow("img_lap", img_lap);
	imshow("img_canny", img_canny);
	waitKey(0);

	return 0;
}

 

- RESULT

 

너무 많이 봐온 Lenna 이미지, 흑백으로 불러오도록 했다.

 

 

x와 y 방향에 대한 1차미분 결과들이다.

sobel 연산을 적용했고, 결과가 흐릿해서 커널 사이즈를 3으로 하였다.

 

sobel 연산의 최종 이미지;

경계가 검출되었지만 밝은곳에서 어두운곳으로 이어지는 부분은 경계가 없거나 흐릿하다.

 

 

 

 

 

라플라시안 필터를 적용한 이미지이다.

 

sobel 과 다르게 섬세하게 경계가 검출된 것을 볼 수 있다.

 

 

 

 

 

위의 sobel 연산과 가우시안 블러가 포함된 canny 방식의 결과이다.

 

사용자가 쉽게 이용할 수 있게 가공된 것이 보기 좋다.

 

 

 

 

 

 

결론.

 

High Pass Filter (HPF) 는 이미지의 경계를 뚜렷하게 해주는 역할을 한다.

 

HPF 를 적용하기 전에는 무조건 LPF 를 적용하여 노이즈를 제거하는 것이 필수과정이다.

 

sobel 과 laplacian 의 차이는 1차미분이냐, 2차미분이냐는 차이이고, 각각의 사용방법이 있다.

 

sobel 연산을 적용하면서 쉽게 사용할 수 있는 Canny 함수를 opencv에서 기본적으로 지원한다.

 

 

 

 

 

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

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

댓글