双边滤波
实验流程
高斯滤波:计算权重时只考虑空间位置之差
双边滤波:计算权重时同时考虑空间位置和像素颜色之差
流程介绍
-
为图像加 padding。这里需要注意的是,滤波核的尺寸需要是奇数,因为一般将当前点作为滤波核的中心,如果是偶数的话就没有中心
int padding = size / 2; cv::copyMakeBorder(source_image , padding_image , padding , padding , padding , padding , cv::BORDER_DEFAULT);
-
将当前位置像素作为滤波核中心点,计算每个通道对应的滤波核
-
计算滤波核一个位置的值
db get_filterValue(int x , int y, int midRow, int midCol, int c){ // 计算当前滤波核某个位置的值 db G_value = exp(-((x * x * 1.0) + (y * y * 1.0)) / (2.0 * sigmaD * sigmaD)) * exp(-pow(source_image.at<Vec3b>(midRow + x, midCol + y)[c] - source_image.at<Vec3b>(midRow,midCol)[c],2) / (2.0 * sigmaR * sigmaR)); return G_value; }
-
计算整个滤波核的值
void getFilter(int midRow, int midCol, int c){ // 得到整个滤波核,(midRow,midCol)滤波核中心像素的真实行列 db sum = 0; size = 6 * sigmaD - 1; int center = size / 2; for(int i = 0 ; i < size; i++) for(int j = 0; j < size; j++){ BilateralFilter[i][j] = get_filterValue(i - center, j - center, midRow, midCol, c); sum += BilateralFilter[i][j]; } for(int i = 0 ; i < size ; i++) for(int j = 0; j < size; j++) BilateralFilter[i][j] /= sum; // 归一化 }
-
-
通过已经得到的滤波核,计算当前位置像素滤波后的像素值
db get_transformedValue(int row, int col, int c){ db sum = 0.0; for(int i = 0; i < size; i++) for(int j = 0; j < size; j++){ sum += padding_image.at<Vec3b>(row - size / 2 + i, col - size / 2 + j)[c] * BilateralFilter[i][j]; } return sum; }
-
整个滤波流程,遍历所有像素进行上面的操作
void transformProcess(){ if(sigmaD == 0) sigmaD = 1; int padding = size / 2; cv::copyMakeBorder(source_image , padding_image , padding , padding , padding , padding , cv::BORDER_DEFAULT); transformed_image = Mat::zeros(source_image.size(), source_image.type()); for(int i = 0 ; i < source_image.rows ; i++)//遍历所有行 for(int j = 0 ; j < source_image.cols ; j++)//填充每一行的所有值 for(int c = 0 ; c < 3 ; c++){ getFilter(i, j, c); // (i,j) 是原图像真实行列 transformed_image.at<Vec3b>(i,j)[c] = int(get_transformedValue(i, j, c)); } }
结果展示
sigmaD=4,sigmaR=5
sigmaD=5,sigmaR=5
sigmaD=4,sigmaR=10
sigmaD=5,sigmaR=100
完整代码
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/opencv.hpp"
#include <iostream>
#define db double
using namespace std;
using namespace cv;
int size; // 滤波核大小
db BilateralFilter[100][100];
int sigmaD, sigmaR; // 位置sigma和像素sigma
Mat source_image;
Mat padding_image;
Mat transformed_image;
Mat Gaussian_image;
Mat transformed_image3;
db get_filterValue(int x , int y, int midRow, int midCol, int c){ // 计算当前滤波核某个位置的值
db G_value = exp(-((x * x * 1.0) + (y * y * 1.0)) / (2.0 * sigmaD * sigmaD))
* exp(-pow(source_image.at<Vec3b>(midRow + x, midCol + y)[c] - source_image.at<Vec3b>(midRow,midCol)[c],2) / (2.0 * sigmaR * sigmaR));
return G_value;
}
void getFilter(int midRow, int midCol, int c){ // 得到整个滤波核,(midRow,midCol)滤波核中心像素的真实行列
db sum = 0;
size = 6 * sigmaD - 1;
int center = size / 2;
for(int i = 0 ; i < size; i++)
for(int j = 0; j < size; j++){
BilateralFilter[i][j] = get_filterValue(i - center, j - center, midRow, midCol, c);
sum += BilateralFilter[i][j];
}
for(int i = 0 ; i < size ; i++)
for(int j = 0; j < size; j++)
BilateralFilter[i][j] /= sum; // 归一化
}
db get_transformedValue(int row, int col, int c){
db sum = 0.0;
for(int i = 0; i < size; i++)
for(int j = 0; j < size; j++){
sum += padding_image.at<Vec3b>(row - size / 2 + i, col - size / 2 + j)[c] * BilateralFilter[i][j];
}
return sum;
}
void transformProcess(){
if(sigmaD == 0)
sigmaD = 1;
int padding = size / 2;
cv::copyMakeBorder(source_image , padding_image , padding , padding , padding , padding , cv::BORDER_DEFAULT);
transformed_image = Mat::zeros(source_image.size(), source_image.type());
for(int i = 0 ; i < source_image.rows ; i++)//遍历所有行
for(int j = 0 ; j < source_image.cols ; j++)//填充每一行的所有值
for(int c = 0 ; c < 3 ; c++){
getFilter(i, j, c); // (i,j) 是原图像真实行列
transformed_image.at<Vec3b>(i,j)[c] = int(get_transformedValue(i, j, c));
}
}
int main()
{
source_image = imread("D:\\cLion\\project\\queban.png");
sigmaD = 5;
sigmaR = 5;
cv::GaussianBlur(source_image, Gaussian_image, Size(size,size) ,sigmaD , sigmaD , cv::BORDER_DEFAULT);
int t1_s = getTickCount(); // 开始时间1
cv::bilateralFilter(source_image , transformed_image3 , size ,sigmaR , sigmaD , cv::BORDER_DEFAULT);
int t1_e = getTickCount(); // 开始时间1
cout << "bilateralFilter time:" << (t1_e - t1_s) / getTickFrequency() << endl;
int t2_s = getTickCount(); // 开始时间2
transformProcess();
int t2_e = getTickCount(); // 开始时间2
cout << "selfBilateralFilter time:" << (t2_e - t2_s) / getTickFrequency() << endl;
imshow("SelfBilateral Image", transformed_image);
imshow("Gaussian Image", Gaussian_image);
imshow("Bilateral Window", transformed_image3);
imshow("Original Image" , source_image);
waitKey(0);
}