필터링 개요
필터는 사전적 의미로 불순물을 걸러내는 기구, 또는 어떤 것을 선택적으로 투과시키는 장치를 의미합니다. 영상 처리에서도 필터링은 비슷한 의미로 사용됩니다. 필터링은 영상 데이터에서 원하는 정보만 선택적으로 획득하는 작업을 의미합니다.
액체 불순물을 필터링할 때 종이필터를 사용하는 것처럼, 영상처리에서도 행렬을 마치 종이필터처럼 이용합니다. 이 필터역할을 하는 행렬을 마스크=커널=윈도우=필터 라고 부릅니다. 마스크 행렬은 3 x 3 행렬이 가장 흔하게 사용됩니다. 이 3 x 3 행렬이 각 픽셀에 적용되어 픽셀마다 Output값이 존재하게 되는데, 연산 방법은 아래와 같습니다.
※ 일반적으로 마스크 행렬은 정방(N x N)행렬을 사용합니다.
그런데, 픽셀의 위치가 가장자리일 경우, 필터링 영역이 영상의 범위를 넘어가게 됩니다. 그래서 영상 바깥쪽에 가상의 픽셀을 만들어 줍니다. 만약 마스크가 3 x 3 라면 영상 바깥쪽 마진이 1px 늘어나고, 5 x 5라면 2px 늘어나게 되겠지요. 그럼 이제 가상픽셀도 어떤 값으로 채워야 할텐데요, 단순히 0으로 채우는 방법도 있고, 가장자리 값을 복사해오는 방법 등이 있습니다.
필터링 함수 사용하기
지금까지 마스크행렬을 영산에 적용하는 과정에 대해 간단히 설명을 하였으니, OpenCV에서 필터링을 하는 함수를 소개해 드리도록 하겠습니다.
void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1, -1), double delta=0, int borderType=BORDER_DEFAULT)
첫번째 인자는 입력 영상, 두번째 인자는 출력 영상입니다. 세번째 인자인 ddepth은 결과 영상의 depth를 나타내며, -1로 설정하면 입력 영상과 같은 depth를 씁니다. 네번째 인자 kernel은 바로 마스크 행렬(1채널)입니다.
나머지 인자는 기본값이 세팅되어 있으므로 필요에 따라 사용하면 됩니다. anchor은 마스크 행렬에서 고점점으로 사용할 좌표를 의미합니다. 보통 행렬의 정가운데를 중심으로 많이 사용하므로 고정점의 기본값은 행렬의 중심으로 셋팅되어 있습니다. delta는 필터링 연산 후 추가로 더할 값, borderType은 가장자리에서 확장될 픽셀값을 채우는 방법을 지정합니다.
borderType은 아래와 같습니다. (일부만 소개)
BORDER_CONSTANT : 0으로 채움 iiiiii|abcdefgh|iiiiiii |
BORDER_REPLICATE : 가장자리 픽셀로 채움 aaaaaa|abcdefgh|hhhhhhh |
BORDER_REFLECT : 가장자리 픽셀로부터 반사 fedcba|abcdefgh|hgfedcb |
BORDER_REFLECT_101 (=BORDER_DEFAULT) : 가장자리 픽셀로부터 반사 gfedcb|abcdefgh|gfedcba |
필터링 적용 예제
7 x 7 커널행렬을 사용하여 영상을 블러링해보도록 하겠습니다.
사실 blur 함수가 있지만, 아래 예제에서는 filter2D 함수를 사용해서 평균값 필터를 적용하여 영상을 블러링하였습니다.
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("lena512color.tiff", IMREAD_COLOR);
if (src.empty())
{
cerr << "Failed to read image" << endl;
return 1;
}
float data[49];
for (int i = 0; i < 49; i++)
data[i] = 1/49.0;
Mat kernel(7, 7, CV_32FC1, data);
Mat dst;
filter2D(src, dst, -1, kernel, Point(-1, -1), 0, BORDER_CONSTANT);
imshow("dst", dst);
waitKey();
destroyAllWindows();
return 0;
}