【C++】雙邊濾波器(bilateral filter)


Bilateral Filtering for Gray and Color Images

 

雙邊濾波器:保留邊界的平滑濾波器。 在局部上,就是在灰度值差異不大的區域平滑,在灰度值差異比較大的邊界地區保留邊界。所以雙邊濾波器作用於每個像素的同時,必然會受到領域像素點的距離、灰度值差的權重影響。

 

已知低通濾波可以表示為:

 

range filter可以表示為:(range filter 試選定一個數值范圍,再做濾波的一個操作)

 

所以,雙邊濾波器的定義是:

其中,k(x)是歸一化(normalize)函數,

( f 表示原圖像,h 表示處理后的圖像 x 表示 h 中某個像素點位置,ξ 表示 f 中x位置像素點的鄰域像素,f(ξ)表示該像素點的灰度值,c表示低通濾波, s表示range filter)

其中,

 

 

 

//Filters.h

#ifndef FILTERS_H
#define FILTERS_H

#include
"opencv2/imgproc.hpp"
#include
"opencv2/highgui.hpp"
#include
"opencv2/core.hpp"

#include
<iostream>
#include
<cmath>

//Bilateral Filtering
//sigmaD == sigmaSpace, sigmaR == sigmaColor
cv::Mat BilateralFilter(cv::Mat inputImg, int filterSize, double sigmaD, double sigmaR);

cv::Mat fastBilateralFilter(cv::Mat inputImg,
int filterSize, double sigmaD, double sigmaR);

#endif // ! FILTERS_H

 

//Filters.cpp

#include
"Filters.h"

double SpaceFactor(int x1, int y1, int x2, int y2, double sigmaD) {
double absX = pow(abs(x1 - x2), 2);
double absY = pow(abs(y1 - y2), 2);

return exp(-(absX + absY) / (2 * pow(sigmaD, 2)));
}

double ColorFactor(int x, int y, double sigmaR) {
double distance = abs(x - y) / sigmaR;
return exp(-0.5 * pow(distance, 2));
}

cv::Mat BilateralFilter(cv::Mat inputImg,
int filterSize, double sigmaD, double sigmaR) {
int len; //must be odd number
cv::Mat gray; // must be 1-channel image
cv::Mat LabImage; // if channels == 3

if (filterSize % 2 != 1 || filterSize <= 0) {
std::cerr
<< "Filter Size must be a positive odd number!" << std::endl;
return inputImg;
}
len
= filterSize / 2;

if (inputImg.channels() >= 3) {
cv::cvtColor(inputImg, LabImage, cv::COLOR_BGR2Lab);
gray
= cv::Mat::zeros(LabImage.size(), CV_8UC1);
for (int i = 0; i < LabImage.rows; i++) {
for (int j = 0; j < LabImage.cols; j++) {
gray.ptr
<uchar>(i)[j] = LabImage.ptr<uchar>(i, j)[0];
}
}
}
else if(inputImg.channels() == 1){
inputImg.copyTo(gray);
}
else {
std::cerr
<< "the count of input image's channel can not be 2!" << std::endl;
return inputImg;
}

cv::Mat resultGrayImg
= cv::Mat::zeros(gray.size(), CV_8UC1);
for (int i = 0; i < gray.rows; i++) {
for (int j = 0; j < gray.cols; j++) {
double k = 0;
double f = 0;
for (int r = i - len; r <= i + len; r++) {
for (int c = j - len; c <= j + len; c++) {
if (r < 0 || c < 0 || r >= gray.rows || c >= gray.cols)
continue;
f
= f + gray.ptr<uchar>(r)[c] * SpaceFactor(i, j, r, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[c], sigmaD);
k
+= SpaceFactor(i, j, r, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[c], sigmaD);
}
}
int value = f / k;
if (value < 0) value = 0;
else if (value > 255) value = 255;

resultGrayImg.ptr
<uchar>(i)[j] = (uchar)value;
}
}

cv::Mat resultImg;
if (inputImg.channels() >= 3) {
for (int i = 0; i < LabImage.rows; i++) {
for (int j = 0; j < LabImage.cols; j++) {
LabImage.ptr
<uchar>(i, j)[0] = resultGrayImg.ptr<uchar>(i)[j];
}
}
cv::cvtColor(LabImage, resultImg, cv::COLOR_Lab2BGR);
}
else {
resultGrayImg.copyTo(resultImg);
}

return resultImg;
}

cv::Mat fastBilateralFilter(cv::Mat inputImg,
int filterSize, double sigmaD, double sigmaR) {
int len; //must be odd number
cv::Mat gray; // must be 1-channel image
cv::Mat LabImage; // if channels == 3

if (filterSize % 2 != 1 || filterSize <= 0) {
std::cerr
<< "Filter Size must be a positive odd number!" << std::endl;
return inputImg;
}
len
= filterSize / 2;

if (inputImg.channels() >= 3) {
cv::cvtColor(inputImg, LabImage, cv::COLOR_BGR2Lab);
gray
= cv::Mat::zeros(LabImage.size(), CV_8UC1);
for (int i = 0; i < LabImage.rows; i++) {
for (int j = 0; j < LabImage.cols; j++) {
gray.ptr
<uchar>(i)[j] = LabImage.ptr<uchar>(i, j)[0];
}
}
}
else if (inputImg.channels() == 1) {
inputImg.copyTo(gray);
}
else {
std::cerr
<< "the count of input image's channel can not be 2!" << std::endl;
return inputImg;
}

cv::Mat resultGrayImg
= cv::Mat::zeros(gray.size(), CV_8UC1);
for (int i = 0; i < gray.rows; i++) {
for (int j = 0; j < gray.cols; j++) {
double k = 0;
double f = 0;
double sum = 0;
for (int r = i - len; r <= i + len; r++) {
if (r < 0 || r >= gray.rows)
continue;
f
= f + gray.ptr<uchar>(r)[j] * SpaceFactor(i, j, r, j, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[j], sigmaD);
k
+= SpaceFactor(i, j, r, j, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[j], sigmaD);
}
sum
= f / k;
f
= k = 0.0;
for (int c = j - len; c <= j + len; c++) {
if (c < 0 || c >= gray.cols)
continue;
f
= f + gray.ptr<uchar>(i)[c] * SpaceFactor(i, j, i, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(i)[c], sigmaD);
k
+= SpaceFactor(i, j, i, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(i)[c], sigmaD);
}
int value = (sum + f / k) / 2;
if (value < 0) value = 0;
else if (value > 255) value = 255;

resultGrayImg.ptr
<uchar>(i)[j] = (uchar)value;
}
}

cv::Mat resultImg;
if (inputImg.channels() >= 3) {
for (int i = 0; i < LabImage.rows; i++) {
for (int j = 0; j < LabImage.cols; j++) {
LabImage.ptr
<uchar>(i, j)[0] = resultGrayImg.ptr<uchar>(i)[j];
}
}
cv::cvtColor(LabImage, resultImg, cv::COLOR_Lab2BGR);
}
else {
resultGrayImg.copyTo(resultImg);
}

return resultImg;
}
//main.cpp

#include
<iostream>
#include
<time.h>

#include
"Filters.h"

using namespace std;

int main() {
cv::Mat img
= cv::imread("Capture.jpg", cv::IMREAD_UNCHANGED);
clock_t begin_time
= clock();
cv::Mat result
= BilateralFilter(img, 15, 12.5, 50);
std::cout
<< float(clock() - begin_time) / CLOCKS_PER_SEC << std:: endl;
cv::imshow(
"original", result);
cv::waitKey(
0);
cv::imwrite(
"original.jpg", result);

begin_time
= clock();
result
= fastBilateralFilter(img, 15, 12.5, 50);
std::cout
<< float(clock() - begin_time) / CLOCKS_PER_SEC << std::endl;
cv::imshow(
"fast", result);
cv::waitKey(
0);
cv::imwrite(
"fast.jpg", result);

begin_time
= clock();
cv::bilateralFilter(img, result,
15, 50, 12.5);
std::cout
<< float(clock() - begin_time) / CLOCKS_PER_SEC << std::endl;
cv::imshow(
"opencv", result);
cv::waitKey(
0);
cv::imwrite(
"opencv.jpg", result);

system(
"pause");
return 0;
}

 

運行結果:

46.889s  5.694s  0.202s

 

二維算子降成兩個一維算子之后,速度加快了一些,但是還是不如opencv的快,效果也比它差一些(No more reinventing the wheel...)

從左至右:“小雀斑”帥氣原圖、BilateralFilter處理結果、fastBilateralFilter處理結果、opencv接口處理結果

   

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com