//-------------------------------------------------------------
//作者:不用先生,2018.11.26
//自实现的图像双边滤波算法
//bilateral.cpp
//-------------------------------------------------------------
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <cmath>
using namespace std;
using namespace cv;
//--------------------------------------------------------------
//函数名:my_Bilateral
//函数功能:自己编写的可用于灰度图及彩色图的双边滤波函数;
//参数:Mat &scr:输入图像,为单通道灰度图或三通道彩色图;
//参数:Mat &dst:输出图像,尺寸与通道数与输入图像吻合;
//参数:int d:滤波器大小,应该保证为奇数;
//参数:double sigmaColor:颜色域/像素值域方差,sigmaColor越大,平滑效果越好,对边缘的保护越弱;
//参数:double sigmaSpace:空间域方差,sigmaSpace越大,结果越平滑;
//--------------------------------------------------------------
bool my_Bilateral(Mat &scr, Mat &dst, int d, double sigmaColor, double sigmaSpace)
{
if (!scr.data)
{
cerr << "输入图像错误,请检查" << endl;;
return false;
}
if (d % 2 == 0)
{
cerr << "输入窗口大小应该为奇数,请修改"<<endl;
return false;
}
dst = scr.clone();
int row = dst.rows; //获取图像大小;
int col = dst.cols;
int copyBorderSize =floor(0.5+ d / 2);
Mat copyBorder_dst;
copyMakeBorder(dst, copyBorder_dst, copyBorderSize, copyBorderSize, copyBorderSize, copyBorderSize, BORDER_REFLECT);
int channels = dst.channels();
if (channels == 1) //如果是灰度图像
{
for (int i = 0; i < row; i++) //对每一个点进行处理
{
for (int j = 0; j < col; j++)
{
double weightSum = 0;
double filterValue = 0;
for (int row_d = -(d / 2); row_d <= (d / 2); row_d++) //以图像中的一点为中心,d为边长的方形区域内进行计算
{
for (int col_d = -(d / 2); col_d <= (d / 2); col_d++)
{
double distance_Square = row_d*row_d + col_d*col_d;
double value_Square = pow((scr.at<uchar>(i, j) - copyBorder_dst.at<uchar>(i + (d / 2) + row_d, j + (d / 2) + col_d)), 2);
double weight = exp(-1 * (distance_Square / (2 * sigmaSpace*sigmaSpace) + value_Square / (2 * sigmaColor*sigmaColor)));
weightSum += weight; //求滤波窗口内的权重和,用于归一化;
filterValue += (weight*copyBorder_dst.at<uchar>(i + (d / 2) + row_d, j + (d / 2) + col_d));
}
}
dst.at<uchar>(i, j) = filterValue / weightSum;
}
}
return true;
}
else if (channels == 3) //如果是RGB图像
{
for (int c = 0; c < channels; c++) //逐通道进行处理
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
double weightSum = 0;
double filterValue = 0;
for (int row_d = -(d / 2); row_d <= (d / 2); row_d++)
{
for (int col_d = -(d / 2); col_d <= (d / 2); col_d++)
{
double distance_Square = row_d*row_d + col_d*col_d;
double value_Square = pow((scr.at<Vec3b>(i, j)[c] - copyBorder_dst.at<Vec3b>(i + (d / 2) + row_d, j + (d / 2) + col_d)[c]), 2);
double weight = exp(-1 * (distance_Square / (2 * sigmaSpace*sigmaSpace) + value_Square / (2 * sigmaColor*sigmaColor)));
weightSum += weight;
filterValue += (weight*copyBorder_dst.at<Vec3b>(i + (d / 2) + row_d, j + (d / 2) + col_d)[c]);
}
}
dst.at<Vec3b>(i, j)[c] = filterValue / weightSum;
}
}
}
return true;
}
else
{
cerr << "图像通道数有误,请确定";
return false;
}
}
//----------------
//主函数
//----------------
void main()
{
Mat scr = imread("03.jpg", IMREAD_UNCHANGED); //以默认的方式读入图像
if (!scr.data) //判断图像是否被正确读取;
{
return;
}
int half_Window_Size = 10; //窗口半宽大小
int window_Size = 2 * half_Window_Size + 1; //窗口大小,应该为奇数;
double sigmaColor = 10; //像素值域的方差
double sigmaSpace = 10; //空间域的方差
//使用自实现的双边滤波对结果进行处理
Mat my_dst = scr.clone();
if (!my_Bilateral(scr, my_dst, window_Size, sigmaColor, sigmaSpace))
{
cerr << "自编写的双边滤波再计算过程中出差,请查验" << endl;
}
//用OpenCV自带的双边滤波函数进行处理
Mat auto_dst = scr.clone();
bilateralFilter(scr, auto_dst, window_Size, sigmaColor, sigmaSpace);
//用高斯函数记性处理
Mat gau_dst = scr.clone();
Size gauSize(window_Size, window_Size);
GaussianBlur(scr, gau_dst, gauSize, sigmaSpace);
imwrite("temp1.jpg", my_dst);
imwrite("temp2.jpg", auto_dst);
imwrite("temp3.jpg", gau_dst);
system("pause");
return;
}