영상 내 객체를 보다 선명하게 표현하기 위해, 영상의 노이즈는 줄이면서 경계는 뚜렷하게 보이도록 처리할 수 있을까요? 양방향 필터(Bilateral Filtering)를 사용하면 영상의 에지 성분을 그대로 유지하면서 가우시안 잡음을 줄일 수 있습니다.
양방향 필터 간단 설명
양방향 필터의 수식은 위키피디아에서 가져왔습니다.
양방향 필터 수식
이전 포스팅에서 가우시안 필터를 사용해서 영상을 블러링하는 방법을 소개했습니다. 양방향 필터도 가우시안 필터를 활용하지만 픽셀값의 차이가 큰 부분은 블러링을 지양하도록 하는 방법입니다. 현재 픽셀값과 인접 픽셀값 간의 차이가 클수록 가우시안 분포 함수에서 0에 가까운 값으로 수렴하게 됩니다. 그러면 블러링 값이 작아지므로 에지가 보존됩니다.
예를 들어 흰색(255)과 검정(0)픽셀이 나란히 있다고 할 때, 차이가 255나 되므로 해당 부분은 에지일 가능성이 높습니다. 여기에는 블러링을 위한 weight가 0에 가깝게 되므로 블러링이 거의 적용되지 않게 됩니다.
이 방법은 주변 픽셀과의 픽셀값 차이에 따라 가우시안 분포함수 값이 달라지기 때문에, 마스크 행렬도 픽셀마다 달라집니다. 결국 연산량이 매우 증가합니다. 앞에서 샤프닝을 한다고 {-1, -1, -1, -1, 9, -1, -1, -1, -1}이라는 고정 마스크 행렬을 썼을 때와는 비교도 안되게 늘어나겠죠. 사용하기 전에 이 점을 참고하면 좋을 것 같습니다.
OpenCV에 이미 구현되어 있는 함수가 있으므로 굳이 수식을 외울 필요는 없습니다.
예제 코드
OpenCV에서 bliateralFilter() 함수 예제는 다음과 같습니다.
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("lena512.bmp", IMREAD_GRAYSCALE);
if (src.empty())
{
cerr << "failed to read image" << endl;
return 1;
}
Mat dst;
bilateralFilter(src, dst, -1, 10, 5);
imshow("src", src);
imshow("dst", dst);
waitKey();
destroyAllWindows();
return 0;
}
실행 결과
양방향 필터 적용 전/후
한 눈에 봐도 오른쪽의 레나 영상이 더 깔끔한 것을 알 수 있습니다. 모자 위 깃털부분이 살짝 뭉개진 느낌은 있네요. (너무 매끈해져서 약간 그림 같은 느낌도 듭니다…^^;)