Bilateral Filter는 영상을 평활화(smoothing)하면서도 에지를 보존할 수 있도록 설계된 대표적인 비선형 필터이다. 일반적인 Gaussian filter는 주변 픽셀과의 거리만을 고려하여 평균을 계산하므로 노이즈와 함께 에지까지 흐려지는 단점이 있다.
Bilateral Filter는 공간적인 거리(spatial distance)뿐만 아니라 픽셀 밝기값의 차이(intensity difference)도 함께 고려하여 가중치를 결정한다. 즉, 가까운 픽셀일수록 큰 가중치를 가지며, 밝기값이 비슷한 픽셀만 평균 계산에 크게 기여한다. 따라서 에지를 사이에 둔 픽셀들은 밝기 차이가 크므로 평균 과정에 거의 참여하지 않아 에지가 잘 보존된다.
결과적으로 Bilateral Filter는 균일한 영역에서는 Gaussian filter와 비슷하게 노이즈를 제거하면서도, 에지 부근에서는 서로 다른 영역이 섞이지 않아 경계를 선명하게 유지할 수 있다. 이러한 특성 때문에 영상의 잡음 제거, 사진의 contrast 향상, HDR 영상 처리, 그리고 비사실적 렌더링(non-photorealistic rendering) 등의 다양한 분야에서 널리 사용된다.
Bilateral Filter는 각 픽셀마다 주변 픽셀과의 거리와 밝기 차이를 모두 계산해야 하므로 Gaussian filter보다 계산량이 많다. 따라서 실제 응용에서는 근사 알고리즘이나 GPU를 이용하여 계산 속도를 높이는 방법이 많이 사용된다.
\begin{gather}BF[I]_{\bf p} = \frac{1}{W_\bf{p}} \sum_{ {\bf q} \in S} G_{\sigma_s} (||{\bf p} - {\bf q}||) G_{\sigma_r}(|I_{\bf p} - I_{\bf q} |) I_{\bf q} \\ W_{\bf p} = \sum_{{\bf q}\in S} G_{\sigma_s} (||{\bf p}-{\bf q}||) G_{\sigma_r}(|I_{\bf p} - I_{\bf q} |) \\ G_\sigma ({\bf r}) = e^{ - ||\bf{r}||^2/2\sigma^2 }\end{gather}




// sigmar controls the intensity range that is smoothed out.
// Higher values will lead to larger regions being smoothed out.
// The sigmar value should be selected with the dynamic range of the image pixel values in mind.
// sigmas controls smoothing factor. Higher values will lead to more smoothing.
// convolution through using lookup tables.
int BilateralFilter(BYTE *image, int width, int height,
double sigmas, double sigmar, int ksize, BYTE* out) {
//const double sigmas = 1.7;
//const double sigmar = 50.;
double sigmas_sq = sigmas * sigmas;
double sigmar_sq = sigmar * sigmar;
//const int ksize = 7;
const int hksz = ksize / 2;
ksize = hksz * 2 + 1;
std::vector<double> smooth(width * height, 0);
// LUT for spatial gaussian;
std::vector<double> spaceKer(ksize * ksize, 0);
for (int j = -hksz, pos = 0; j <= hksz; j++)
for (int i = -hksz; i <= hksz; i++)
spaceKer[pos++] = exp(- 0.5 * double(i * i + j * j)/ sigmas_sq);
// LUT for image similarity gaussian;
double pixelKer[256];
for (int i = 0; i < 256; i++)
pixelKer[i] = exp(- 0.5 * double(i * i) / sigmar_sq);
for (int y = 0, imgpos = 0; y < height; y++) {
int top = y - hksz;
int bot = y + hksz;
for (int x = 0; x < width; x++) {
int left = x - hksz;
int right = x + hksz;
// convolution;
double wsum = 0;
double fsum = 0;
int refVal = image[imgpos];
for (int yy = top, kpos = 0; yy <= bot; yy++) {
for (int xx = left; xx <= right; xx++) {
// check whether the kernel rect is inside the image;
if ((yy >= 0) && (yy < height) && (xx >= 0) && (xx < width)) {
int curVal = image[yy * width + xx];
int idiff = curVal - refVal;
double weight = spaceKer[kpos] * pixelKer[abs(idiff)];
wsum += weight;
fsum += weight * curVal;
}
kpos++;
}
}
smooth[imgpos++] = fsum / wsum;
}
}
for (int k = smooth.size(); k-- > 0;) {
int a = int(smooth[k]);
out[k] = a < 0 ? 0: a > 255 ? 255: a;
}
return 1;
}'Image Recognition > Fundamental' 카테고리의 다른 글
| Cubic Spline Kernel (2) | 2024.03.12 |
|---|---|
| Ellipse Fitting (0) | 2024.03.02 |
| 파라미터 공간에서 본 최소자승 Fitting (0) | 2023.05.21 |
| 영상에 Impulse Noise 넣기 (2) | 2023.02.09 |
| Canny Edge: Non-maximal suppression (0) | 2023.01.11 |

