双边滤波
事先声明,这代码改编自
https://github.com/Jermmy/BilateralFilter/tree/master/bilateral_filter/bilateral_filter
不过原来的代码是用 OpenCV 写的,OpenCV 有点难配置,我把它翻译成 easyx。
简单说明
原图
这里不贴公式。
找到一个点,规定一个区域,让这个点的颜色变成这个区域的颜色的平均值,效果会是这样(区域半径为 5 像素):
平均值滤波
我把这称为平均值滤波。如果比较的区域变大了,用平均值滤波的话图像就会越模糊,就像这样(区域半径为 10 像素):
平均值滤波
为了平衡距离对结果的影响,需要让越靠近中心的点对结果影响越大,也就是权重越大,这里用正态分布的概率密度函数
y=e^(-x^2/(2*σ^2)) 来分配权重,x 为距离,y 为权重,σ 为正态分布的标准差,这里用于调整分配策略,σ 越小,越靠近中心的点分配的权重越大,越远离中心的点分配的权重越小,σ 越大就反过来,同时 σ 接近无穷大时变为平均值滤波。
分配一个 2radius * 2radius 的二维数组来存权重,因为我们比较的点只有 2radius * 2radius 这个区域内的点,所以只需要这个区域内的点的权重,每个点的值乘以每个点的权重,加起来除以总权重。这个其实跟求数学期望很像,这块区域距离中心的不同距离上出现不同的颜色,该颜色在该距离上出现的次数服从正态分布,求覆盖这块区域的颜色的数学期望(我是这么理解的)。这就是高斯滤波了。
区域半径为 5:
高斯滤波,区域半径为 5
区域半径为 10:
高斯滤波,区域半径为 10
比平均值滤波略微好了一点是吧,这还是没有调正态分布的标准差的时候,以上的图片是在标准差为 5 的时候生成的,如果我把正态分布标准差调小一点就会是
区域半径为 10,标准差为 2:
高斯滤波
反之,标准差调大一点就会是
区域半径为 10,标准差为 100:
高斯滤波
但是标准差调太小了就跟原图一样了,就无法去除噪点,同时对于色差特别大的区域,高斯滤波无法明确分辨这是个边界,于是会让这个棱角分明的像素点跟它周围的环境同化。
为了分辨边界,需要另一个权重,这个权重专门为了色差而设置,当区域内的点和中心点的色差特别大时,它分配到的权重就变小,当区域内的点和中心点的色差特别小时,它分配到的权重就大,这个权重的分配方法和高斯滤波的一样,y=e^(-x^2/(2*σ^2)),y 是权重,x 是色差,σ 是色差标准差,这里用于调整分配策略。
这是相似点滤波。
相似点滤波与距离无关,能很好的保存边界,同高斯滤波,当标准差无穷大时相似点滤波变为平均值滤波
区域半径为 10,标准差为 30:
相似点滤波
区域半径为 10,标准差为 100:
相似点滤波
由于对距离的把控不准,中心点颜色可能变为远处与它相差无几的颜色,虽然能较好地保存边界,但是不能与周围的颜色渐变,所以需要跟高斯滤波合作。
合作的具体做法就是权重直接相乘,然后权重总和是这些相乘的权重的总和,颜色乘以权重后累加起来除以权重总和。
最终的效果如下
半径为 5,高斯滤波标准差为 5,相似点滤波标准差为 30:
双边滤波
代码
#include<graphics.h>
#include<iostream>
#include<math.h>
using namespace std;
// 颜色类型,用 int 会占 4 字节,干脆自己写一个占 3 字节的颜色类型
typedef struct ColorType
{
unsigned char Red;
unsigned char Green;
unsigned char Blue;
}ColorType;
// 将 RGB 转化为 ColorType
ColorType RGBTOCOL(unsigned int rgb)
{
ColorType result;
unsigned char temp = rgb % (1 << 8);
result.Red = temp;
rgb = rgb >> 8;
temp = rgb % (1 << 8);
result.Green = temp;
rgb = rgb >> 8;
temp = rgb % (1 << 8);
result.Blue = temp;
return result;
}
// 将 ColorType 转化为 RGB
unsigned int COLTORGB(ColorType col)
{
return RGB(col.Red, col.Green, col.Blue);
}
// 创建颜色数组
ColorType** CreateColorArr(unsigned int height, unsigned int width)
{
ColorType** temp = new ColorType * [height];
for (int i = 0; i < height; i++)
temp[i] = new ColorType[width];
return temp;
}
// 清除颜色数组
void ClearColorArr(ColorType** arr, unsigned int height)
{
for (int i = 0; i < height; i++)
delete[] arr[i];
delete[] arr;
}
// 将 source 颜色数组用复制的方法复制到 dest 数组里,注意 dest 数组的尺寸比 source 数组的尺寸宽高各大 radius
void CopyImg(ColorType** Dest, ColorType** source,
const unsigned int height, const unsigned int width, const unsigned int radius)
{
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
Dest[i + radius][j + radius] = source[i][j];
for (int i = 0; i < radius; i++)
{
for (int j = 0; j < radius; j++)
{
Dest[i][j] = source[0][0];
Dest[i + radius + height][j] = source[height - 1][0];
Dest[i][j + radius + width] = source[0][width - 1];
Dest[i + radius + height][j + radius + width] = source[height - 1][width - 1];
}
}
for (int i = 0; i < radius; i++)
{
for (int j = 0; j < width; j++)
{
Dest[i][j + radius] = source[0][j];
Dest[(i + radius + height)][j + radius] = source[height - 1][j];
}
}
for (int i = 0; i < height; i++)
{
for (int j = 0; j < radius; j++)
{
Dest[i + radius][j] = source[i][0];
Dest[i + radius][j + radius + width] = source[i][width - 1];
}
}
}
// 创建距离表,weightsum 是权重总数,进行高斯模糊时可以直接用
double** BuildDistanceTable(int radius, double SigmaDis, double* weightsum)
{
*weightsum = 0;
double** result = new double*[2 * radius];
for (int i = 0; i < 2 * radius; i++)
result[i] = new double[2 * radius];
for (int i = -radius; i < radius; i++)
{
for (int j = -radius; j < radius; j++)
{
// i * i + j * j 是距离的平方,SigmaDis 是距离标准差
double value= exp(-(0.5 * (i * i + j * j) / (SigmaDis * SigmaDis)));
result[i + radius][j + radius] = value;
*weightsum += value;
}
}
return result;
}
// 清除距离表
void ClearDistanceTable(double** distable, unsigned int radius)
{
for (int i = 0; i < 2 * radius; i++)
delete[] distable[i];
delete[] distable;
}
// 创建颜色表,SigmaCol 是颜色标准差
double* BuildColorTable(double SigmaCol)
{
double* result = new double[256];
for (int i = 0; i < 256; i++)
// i * i 是色差的平方
result[i] = exp(-(0.5 * (i * i) / (SigmaCol * SigmaCol)));
return result;
}
// 清除颜色表
void ClearColorTable(double* colortable)
{
delete[]colortable;
}
// 平均值滤波
void AverageFilter(ColorType** img, const unsigned int width, const unsigned int height, const int radius)
{
ColorType** temp = CreateColorArr(height + radius * 2, width + radius * 2);
CopyImg(temp, img, height, width, radius);
double sum = 4 * radius * radius;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
double red_all = 0, green_all = 0, blue_all = 0;
for (int area_row = -radius; area_row < radius; area_row++)
{
for (int area_col = -radius; area_col < radius; area_col++)
{
red_all += temp[row + area_row + radius][col + area_col + radius].Red;
green_all += temp[row + area_row + radius][col + area_col + radius].Green;
blue_all += temp[row + area_row + radius][col + area_col + radius].Blue;
}
}
putpixel(col, row,
RGB((unsigned int)(red_all / sum),
(unsigned int)(green_all / sum),
(unsigned int)(blue_all / sum)));
}
}
ClearColorArr(temp, height + radius * 2);
}
// 高斯滤波
void GaussianFilter(ColorType** img, const unsigned int width, const unsigned int height,
const int radius, const double SigmaDis)
{
ColorType** temp = CreateColorArr(height + radius * 2, width + radius * 2);
double WeightSum;
double** distanceTable = BuildDistanceTable(radius, SigmaDis, &WeightSum);
CopyImg(temp, img, height, width, radius);
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
double red_all = 0, green_all = 0, blue_all = 0;
for (int area_row = -radius; area_row < radius; area_row++)
{
for (int area_col = -radius; area_col < radius; area_col++)
{
red_all += temp[row + area_row + radius][col + area_col + radius].Red *
distanceTable[area_row + radius][area_col + radius];;
green_all += temp[row + area_row + radius][col + area_col + radius].Green *
distanceTable[area_row + radius][area_col + radius];;
blue_all += temp[row + area_row + radius][col + area_col + radius].Blue *
distanceTable[area_row + radius][area_col + radius];;
}
}
putpixel(col, row,
RGB((unsigned int)(red_all / WeightSum),
(unsigned int)(green_all / WeightSum),
(unsigned int)(blue_all / WeightSum)));
}
}
ClearColorArr(temp, height + radius * 2);
ClearDistanceTable(distanceTable, radius);
}
// 相似点滤波
void SimilarityFilter(ColorType** img, const unsigned int width, const unsigned int height,
const int radius, const double SigmaCol)
{
ColorType** temp = CreateColorArr(height + radius * 2, width + radius * 2);
double* colorTable = BuildColorTable(SigmaCol);
CopyImg(temp, img, height, width, radius);
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
ColorType currentPoint = temp[row + radius][col + radius];
double red_all = 0, green_all = 0, blue_all = 0;
double Weight_Red = 0, Weight_Green = 0, Weight_Blue = 0;
for (int area_row = -radius; area_row < radius; area_row++)
{
for (int area_col = -radius; area_col < radius; area_col++)
{
double middle = colorTable[abs(temp[row + area_row + radius][col + area_col + radius].Red - currentPoint.Red)];
red_all += temp[row + area_row + radius][col + area_col + radius].Red * middle;;
Weight_Red += middle;
middle= colorTable[abs(temp[row + area_row + radius][col + area_col + radius].Green - currentPoint.Green)];
green_all += temp[row + area_row + radius][col + area_col + radius].Green * middle;
Weight_Green += middle;
middle= colorTable[abs(temp[row + area_row + radius][col + area_col + radius].Blue - currentPoint.Blue)];
blue_all += temp[row + area_row + radius][col + area_col + radius].Blue * middle;
Weight_Blue += middle;
}
}
putpixel(col, row,
RGB((unsigned int)(red_all / Weight_Red),
(unsigned int)(green_all / Weight_Green),
(unsigned int)(blue_all / Weight_Blue)));
}
}
ClearColorArr(temp, height + radius * 2);
ClearColorTable(colorTable);
}
// 双边滤波
void BilateralFilter(ColorType** img, const unsigned int width, const unsigned int height,
const int radius, const double SigmaDis, const double SigmaCol)
{
ColorType** temp = CreateColorArr(height + radius * 2, width + radius * 2);
double WeightSum;
double** distanceTable = BuildDistanceTable(radius, SigmaDis, &WeightSum);
double* colorTable = BuildColorTable(SigmaCol);
CopyImg(temp, img, height, width, radius);
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
ColorType currentPoint = temp[row + radius][col + radius];
double red_all = 0, green_all = 0, blue_all = 0;
double Weight_Red = 0, Weight_Green = 0, Weight_Blue = 0;
for (int area_row = -radius; area_row < radius; area_row++)
{
for (int area_col = -radius; area_col < radius; area_col++)
{
double middle = distanceTable[area_row + radius][area_col + radius] *
colorTable[abs(temp[row + area_row + radius][col + area_col + radius].Red - currentPoint.Red)];
red_all += temp[row + area_row + radius][col + area_col + radius].Red * middle;;
Weight_Red += middle;
middle = distanceTable[area_row + radius][area_col + radius] *
colorTable[abs(temp[row + area_row + radius][col + area_col + radius].Green - currentPoint.Green)];
green_all += temp[row + area_row + radius][col + area_col + radius].Green * middle;
Weight_Green += middle;
middle = distanceTable[area_row + radius][area_col + radius] *
colorTable[abs(temp[row + area_row + radius][col + area_col + radius].Blue - currentPoint.Blue)];
blue_all += temp[row + area_row + radius][col + area_col + radius].Blue * middle;
Weight_Blue += middle;
}
}
putpixel(col, row,
RGB((unsigned int)(red_all / Weight_Red),
(unsigned int)(green_all / Weight_Green),
(unsigned int)(blue_all / Weight_Blue)));
}
}
ClearColorArr(temp, height + radius * 2);
ClearColorTable(colorTable);
ClearDistanceTable(distanceTable, radius);
}
int main()
{
IMAGE img;
loadimage(&img, "project2.png"); // 在这里输入要处理的图像名字
initgraph(img.getwidth(), img.getheight());
putimage(0, 0, &img);
getchar();
ColorType **arr = CreateColorArr(img.getheight(), img.getwidth());
for (int i = 0; i < img.getheight(); i++)// 将整张图复制下来
for (int j = 0; j < img.getwidth(); j++)
arr[i][j] = RGBTOCOL(getpixel(j, i));
GaussianFilter(arr, img.getwidth(), img.getheight(), 5, 5); // 高斯滤波
getchar();
SimilarityFilter(arr, img.getwidth(), img.getheight(), 5, 100); // 相似点滤波
getchar();
BilateralFilter(arr, img.getwidth(), img.getheight(), 5, 5, 100); // 双边滤波
ClearColorArr(arr, img.getheight());
getchar();
closegraph();
return 0;
}
添加评论
取消回复