필터링이란 단어 뜻 그대로 필요한 부분을 취하기 위하여 필요없는 부분을 걸러내는 작업을 의미한다. 영상 데이터에서의 필터링 방법은 블러링
, 샤프닝
, 잡음 제거
, 에지 검출
등이 이에 해당한다.
푸리에 변환(Fourier transform)을 통해 영상을 주파수 공간으로 변환한 상태에서 이 주파수 공간에서의 필터링 작업을 수행하는 방법이 주파수 공간에서의 필터링 방법이다.
마스크(Mask) 연산을 이용하여 영상의 필셀에 직접 변환를 가하는 방법이다. 여기서 마스크는 커널(Kernel)이라는 용어로 불리는 경우도 있다. 특히 공간적 필터링은 마스크 영역의 연산을 통한 변환이 수행되기 때문에 마스크의 크기가 커지는 경우 연산량의 증가로 인해 속도가 느려질 수 있다. 때문에 OpenCV에서는 마스크의 크기가 작은 경우에는 공간적 필터링을 수행하고, 마스크의 크기가 커지면 주파수 공간에서의 필터링을 수행한다.
공간적 필터링은 위 그림처럼 마스크가 영상 위를 지나가면서 마스크 영역과 영상의 영역이 겹치는 부분에 대해서 곱셉 연산을 수행하고 이 결과를 모두 더해서 결과 영상의 한 픽셀이 된다.
convolution은 필터 g의 모양이 좌우 반전된 형태로 사용되는 반면 correlation은 필터 모양 그대로 사용되는 것을 볼 수 있다. 필터의 모양이 좌우 대칭인 경우 두 함수는 동일한 역할을 수행하기 때문에 이를 혼용해서 사용된다. 특히, 공간적 필터링에서는 좌우, 상하 대칭 형태의 필터를 사용하기 때문에 convolution 이라고도 할 수 있다. (정확하게는 correlation이지만 관용적으로 convolution이라고 불린다)
이미지의 끝 부분에도 연산을 수행하기 때문에 모서리 부분에 모자라는 부분이 발생할 수 있다. 이를 처리하는 방법들에 대한 예시이다.
void filter2D( InputArray src, OutputArray dst, int ddepth,
InputArray kernel, Point anchor = Point(-1,-1),
double delta = 0, int borderType = BORDER_DEFAULT );
src: 입력 영상
dst: 출력 영상(원본 영상과 동일한 크기와 채널 수를 갖음)
ddepth: 출력 영상의 깊이를 조절 (-1: 입력 영상과 동일)
// @anchor filter_depths
// ### Depth combinations
// Input depth (src.depth()) | Output depth (ddepth)
// --------------------------|----------------------
// CV_8U | -1/CV_16S/CV_32F/CV_64F
// CV_16U/CV_16S | -1/CV_32F/CV_64F
// CV_32F | -1/CV_32F
// CV_64F | -1/CV_64F
kernel: 필터 마스크 행렬(1차원 행렬 형태)
anchor: 고정점 지정((-1, -1)의 경우 중앙을 기준으로 함)
delta: 결과 영상에 추가적으로 더해지는 값
void copyMakeBorder(InputArray src, OutputArray dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value = Scalar() );
src: 입력 영상
dst: 출력 영상 (src.cols+left+right, src.rows+top+bottom) .
top: the top pixels
bottom: the bottom pixels
left: the left pixels
right: Parameter specifying how many pixels in each direction from the source image rectangle
to extrapolate. For example, top=1, bottom=1, left=1, right=1 mean that 1 pixel-wide border needs
to be built.
borderType: Border type. See borderInterpolate for details.
//! Various border types, image boundaries are denoted with `|`
//! @see borderInterpolate, copyMakeBorder
// enum BorderTypes {
// BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
// BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
// BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb`
// BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg`
// BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
// BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`
// BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
// BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
// BORDER_ISOLATED = 16 //!< do not look outside of ROI
// };
value: Border value if borderType==BORDER_CONSTANT .
직물이나 종이, 금속판 등에 올록볼록한 형태로 만든 객체의 윤곽 또는 무늬를 의마한다.
void ex_filtering(){
Mat src = imread("../data/rose.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return ;
}
float data[] = { -1, -1, 0, -1, 0, 1, 0, 1, 1 };
Mat emboss(3, 3, CV_32FC1, data);
Mat dst;
filter2D(src, dst, -1, emboss, Point(-1, -1), 128);
imshow("src", src);
imshow("dst", dst);
waitKey();
}
위와 같은 형태를 엠보싱 필터라고 하며 결과 영상에 128을 더해서 결과를 두드러지게 할 수 있다.
주변 픽셀 값들의 산술 평균을 계산하고, 이를 출력 영상의 픽셀 값으로 설정한다. 영상의 인접한 픽셀 간의 급격한 변화가 줄어들어, 날카로운 엣지가 무뎌지고 영상에 있는 잡음이 감소하는 효과가 있다.
void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
src: 입력 영상
dst: 출력 영상
ksize: blurring kernel size.
anchor: anchor point; default value Point(-1,-1) means that the anchor is at the kernel center.
borderType: border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported.
void ex_filtering(){
Mat src = imread("../data/rose.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return ;
}
Mat kernel = Mat::ones(3,3, CV_32FC1) / 9.f;
Mat dst;
filter2D(src, dst, -1, kernel, Point(-1, -1));
imshow("src", src);
imshow("dst", dst);
waitKey();
}
원본 영상부터 필터의 크기가 3, 5인 평균값 필터를 적용시킨 결과 영상이다.
void ex_filtering(){
Mat src = imread("../data/rose.bmp", IMREAD_GRAYSCALE);
Mat dst;
if (src.empty()) {
cerr << "Image load failed!" << endl;
return ;
}
imshow("src", src);
blur(src, dst, Size(3,3));
imshow("dst3", dst);
blur(src, dst, Size(5,5));
imshow("dst5", dst);
blur(src, dst, Size(7,7));
imshow("dst7", dst);
waitKey();
}
평균값을 이용한 필터에서는 필터의 중앙 점을 기준으로 주변 영상의 가중치가 높아지기 때문에 현재 위치의 픽셀 값의 비중은 줄어들고 주변 픽셀들의 영향이 커지는 문제가 나타난다.
평균을 중심으로 좌우대칭인 종 모양을 갖는 확률 분포이다. 자연에서 발생하는 많은 사건들을 설명할 수 있어 많은 확률 모델에 사용된다(e.g. 키, 몸무게, 시험점수, 제품의 수명 등).
가우시안 함수의 경우 내에 전체의 68%, 내에 전체의 95%, 내에 전체의 99.7%를 포함한다.
가우시안 필터는 2차원 가우시안 함수의 결과값을 필터 형태로 만든 것이다. 필터의 크기는 +1 혹은 의 형태를 갖는다.
가우시한 필터를 적용하는 함수이다. ksize
의 경우 Size()
를 넣어주면 sigma
에 따라서 자동으로 필터 사이즈를 결정한다(e.g. 1 sigma = (7,7) filter)
void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
src: input image; the image can have any number of channels, which are processed independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
dst: output image of the same size and type as src.
ksize: Gaussian kernel size. ksize.width and ksize.height can differ but they both must be positive and odd. Or, they can be zero's and then they are computed from sigma.
sigmaX: Gaussian kernel standard deviation in X direction.
sigmaY: 0인경우 sigmax와 같은 값을 취함
borderType: pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported.
void ex_filtering(){
Mat src = imread("../data/rose.bmp", IMREAD_GRAYSCALE);
Mat dst;
if (src.empty()) {
cerr << "Image load failed!" << endl;
return ;
}
imshow("src", src);
GaussianBlur(src, dst, Size(), 2.0);
imshow("gaussian", dst);
waitKey();
}