高斯滤波是图像四大滤波之一,四大滤波包括均值滤波,中值滤波,高斯滤波,双边滤波。均值滤波的原理简单直接,就是遍历图像像素点,以当前像素点为中心,将卷积模板内的所有像素点取平均值并设置到当前像素点,虽然也起到了平滑作用,但由于引入了噪声成分,去噪效果不理想,但速度快。高斯滤波的原理是假设像素灰度值成正态分布,以当前像素为中心,离当前像素越远,则占的权重越低。高斯滤波其实也引入了噪声成分。均值滤波和高斯滤波时间复杂度相同。中值滤波则是取卷积模板范围内像素点的中间值,由于每次都需要对卷积模板内的像素值进行排序,所以时间复杂度高,由于中间值基本能排除了噪点,因为噪点的灰度值一般比较突兀,中值滤波去燥效果好,但出来的图像平滑度不高,锯齿现象严重。
二维高斯函数公式:
用代码实现该公式:
PI 的计算方式:const double PI = 4.0*std::atan(1.0);
e的x次方我们用std::exp函数进行计算。
算法中我们需要设置sigma的值,即上述公式中的参数,根据sigma值的不同,每个卷积模板的权重也会不同,权重可以用下图形象表示:
我们先贴一下权重的计算函数:
/**
* @brief 简单概述
* @brief 计算高斯核卷积
* @brief gaus:高斯核卷积(出参)
* @brief k:卷积模板尺寸,3*3,5*5,7*7等
* @brief sigma:权重控制参数
*/
void CaclGaussianKernel(double *gaus, Kernel k, const double sigma)
{
//圆周率π赋值
const double PI = 4.0*std::atan(1.0);
int center = k /2;
double param = 1/(2*sigma*sigma);
double sum = 0;
int count = 0;
for (int i = 0; i<k; i++)
{
for (int j = 0; j<k; j++)
{
gaus[count] = (1/PI*param)*std::exp(-((i-center)*(i-center)+
(j-center)*(j-center))*param);
sum += gaus[count];
count++;
}
}
count = 0;
for (int i = 0; i<k; i++)
{
for (int j = 0; j<k; j++)
{
gaus[count] /= sum;
count++;
}
}
}
sigma取值对高斯卷积模板的影响:
对于3*3的卷积矩阵,当sigma=0.1时,卷积模板为:
0.000000,0.000000,0.000000,
0.000000,1.000000,0.000000,
0.000000,0.000000,0.000000,
对于3*3的卷积矩阵,当sigma=0.5时,卷积模板为:
0.011344,0.083820,0.011344,
0.083820,0.619347,0.083820,
0.011344,0.083820,0.011344,
对于3*3的卷积矩阵,当sigma=1.0时,卷积模板为:
0.075114,0.123841,0.075114,
0.123841,0.204180,0.123841,
0.075114,0.123841,0.075114,
对于3*3的卷积矩阵,当sigma=5.0时,卷积模板为:
0.109630,0.111844,0.109630,
0.111844,0.114104,0.111844,
0.109630,0.111844,0.109630,
对于3*3的卷积矩阵,当sigma=10.0时,卷积模板为:
0.110741,0.111296,0.110741,
0.111296,0.111854,0.111296,
0.110741,0.111296,0.110741,
对于9*9的卷积矩阵,当sigma=5.0时,卷积模板为:
0.008386,0.009646,0.010661,0.011320,0.011549,0.011320,0.010661,0.009646,0.008386,
0.009646,0.011096,0.012263,0.013021,0.013284,0.013021,0.012263,0.011096,0.009646,
0.010661,0.012263,0.013553,0.014391,0.014681,0.014391,0.013553,0.012263,0.010661,
0.011320,0.013021,0.014391,0.015281,0.015589,0.015281,0.014391,0.013021,0.011320,
0.011549,0.013284,0.014681,0.015589,0.015904,0.015589,0.014681,0.013284,0.011549,
0.011320,0.013021,0.014391,0.015281,0.015589,0.015281,0.014391,0.013021,0.011320,
0.010661,0.012263,0.013553,0.014391,0.014681,0.014391,0.013553,0.012263,0.010661,
0.009646,0.011096,0.012263,0.013021,0.013284,0.013021,0.012263,0.011096,0.009646,
0.008386,0.009646,0.010661,0.011320,0.011549,0.011320,0.010661,0.009646,0.008386,
由此可见,模板较小时当sigma值大于1时,各个像素的权重值近似,所以对于小卷积核sigma值取值范围
应该设置在0-1之间,对于较大的模板,sigma取值可以大于1。
数据结构定义:
/**
* @brief 简单概述
* @brief 图像格式
*/
enum ImageFormat
{
IMAGE_BINARY = 0x0001, // 二值图像
IMAGE_GREY = 0x0008, // 灰度图像
IMAGE_RGB = 0x0018, // 彩色RGB图像
IMAGE_BGR = 0x0019, // 彩色BGR图像
IMAGE_RGBA = 0x0020, // 彩色RGBA图像
IMAGE_BGRA = 0x0021 // 彩色BGRA图像
};
/**
* @brief 简单概述
* @brief 图像数据结构
*/
typedef struct
{
ImageFormat format; // 图像格式
int width; // 图像宽度
int height; // 图像高度
unsigned long iBufSize; // 像素数据缓冲区大小
unsigned char* pBuffer = NULL; // 像素数据
}Image;
算法声明:
/*****************************************************************************
* C++ 图像算法
* Copyright (C) 2020 1158292387@qq.com
*
* @file
* @brief 对文件的简单概述
* Details.
*
* @author Leeme
* @email 1158292387@qq.com
* @version 1.0.0.1(版本)
* @date 2020.11.21
* @license GNU General Public License (GPL)
*
*----------------------------------------------------------------------------
* Remark : Description
*----------------------------------------------------------------------------
* Change History :
* <Date> | <Version> | <Author> | <Description>
*----------------------------------------------------------------------------
* 2020/10/30 | 1.0.0.1 | Leeme | Create file
*----------------------------------------------------------------------------
*
*****************************************************************************/
#ifndef ALGORITHM_H
#define ALGORITHM_H
#include "algdef.h"
#include <vector>
/**
* @brief 简单概述
* @brief 单幅图像作为输入的算法基类
*/
class algorithm
{
public:
algorithm(){}
~algorithm()
{
delete m_Image.pBuffer;
m_Image.pBuffer = NULL;
}
/**
* @brief 简单概述
* @brief 设置图像
*/
bool SetInputData(const Image& src)
{
if (src.pBuffer == NULL || src.width == 0 || src.height == 0)
return false;
m_Image.width = src.width;
m_Image.height = src.height;
m_Image.format = src.format;
m_Image.iBufSize = src.iBufSize;
if (m_Image.pBuffer != NULL)
{
delete[]m_Image.pBuffer;
m_Image.pBuffer = NULL;
}
m_Image.pBuffer = new unsigned char[m_Image.iBufSize];
memcpy(m_Image.pBuffer, src.pBuffer, m_Image.iBufSize);
return true;
}
/**
* @brief 简单概述
* @brief 获取图像计算结果
*/
bool GetOutputData(Image* src)
{
if (m_Image.pBuffer == NULL)
return false;
src->width = m_Image.width;
src->height = m_Image.height;
src->format = m_Image.format;
src->iBufSize = m_Image.iBufSize;
if (src->pBuffer != NULL)
{
delete[] src->pBuffer;
src->pBuffer = NULL;
}
src->pBuffer = new unsigned char[m_Image.iBufSize];
memcpy(src->pBuffer, m_Image.pBuffer, m_Image.iBufSize);
return true;
}
/**
* @brief 简单概述
* @brief 设置算法类型
*/
virtual bool SetAlgType(int) = 0;
/**
* @brief 简单概述
* @brief 图像算法实现接口
*/
virtual bool UpdateData() = 0;
protected:
Image m_Image;
};
class IMAGECORE_EXPORT GussianFilter : public algorithm
{
public:
GussianFilter()
{
m_Sigma = 0.0;
m_Kernel = K_3X3;
}
~GussianFilter(){}
enum Kernel
{
K_3X3 = 0x0003,
K_5X5 = 0x0005,
K_7X7 = 0x0007,
K_9X9 = 0x0009
};
/**
* @brief 简单概述
* @brief 设置算法类型
*/
bool SetAlgType(int type)
{
if (type < K_3X3 || type > K_9X9)
return false;
m_Kernel = (Kernel)type;
return true;
}
void SetSigma(double s) { m_Sigma = s; }
/**
* @brief 简单概述
* @brief 图像算法实现接口
*/
bool UpdateData();
private:
/**
* @brief 简单概述
* @brief 计算高斯卷积核模板
*/
void CaclGaussianKernel(double *gaus, Kernel k, const double sigma);
Kernel m_Kernel;
double m_Sigma;
};
#endif
算法实现:
/**
* @brief 简单概述
* @brief 图像算法实现接口
*/
bool GussianFilter::UpdateData()
{
if (m_Image.pBuffer == NULL || m_Image.width == 0 || m_Image.height == 0 ||
(m_Sigma-0.0)<0.0001)
return false;
short channel = 0;
if (m_Image.format == IMAGE_RGB || m_Image.format == IMAGE_BGR)
channel = 3;
else if (m_Image.format == IMAGE_RGBA || m_Image.format == IMAGE_BGRA)
channel = 4;
else
return false;
// 计算高斯卷积模板
double* pK = new double[m_Kernel*m_Kernel];
this->CaclGaussianKernel(pK, m_Kernel, m_Sigma);
unsigned char* pData = m_Image.pBuffer;
unsigned char* pTemp = m_Image.pBuffer;
short k = m_Kernel / 2;
for (unsigned long i = 0; i < m_Image.iBufSize; i += channel)
{
int x = (i + 1) % (m_Image.width*channel) / channel;
int y = i / (m_Image.width*channel);
int total_r = 0;
int total_g = 0;
int total_b = 0;
short count = 0;
short weight_index = 0;
for (short m = 0; m < m_Kernel; m++)
{
int temp_y = y - k + m;
if (temp_y >= 0 && temp_y < m_Image.height)
{
for (short n = 0; n < m_Kernel; n++)
{
int temp_x = x - k + n;
if (temp_x >= 0 && temp_x < m_Image.width)
{
unsigned long fix = temp_y*m_Image.width*
channel + temp_x*channel;
pTemp += fix;
double weight = pK[weight_index];
short r = ((short)pTemp[0])*weight;
short g = ((short)pTemp[1])*weight;
short b = ((short)pTemp[2])*weight;
total_r += r;
total_g += g;
total_b += b;
count += 1;
pTemp -= fix;
weight_index++;
}
else
{
weight_index++;
}
}
}
else
{
weight_index += m_Kernel;
}
}
pData[0] = (unsigned short)total_r;
pData[1] = (unsigned short)total_g;
pData[2] = (unsigned short)total_b;
pData += channel;
}
delete[] pK;
return true;
}
/**
* @brief 简单概述
* @brief 计算高斯核卷积
* @brief gaus:高斯核卷积(出参)
* @brief k:卷积模板尺寸,3*3,5*5,7*7等
* @brief sigma:权重控制参数
*/
void GussianFilter::CaclGaussianKernel(double *gaus, Kernel k, const double sigma)
{
//圆周率π赋值
const double PI = 4.0*std::atan(1.0);
int center = k /2;
double param = 1/(2*sigma*sigma);
double sum = 0;
int count = 0;
for (int i = 0; i<k; i++)
{
for (int j = 0; j<k; j++)
{
gaus[count] = (1/PI*param)*std::exp(-((i-center)*(i-center)+
(j-center)*(j-center))*param);
sum += gaus[count];
count++;
}
}
count = 0;
for (int i = 0; i<k; i++)
{
for (int j = 0; j<k; j++)
{
gaus[count] /= sum;
count++;
}
}
}
测试代码:
GussianFilter* mFilter = new GussianFilter();
mFilter->SetInputData(m_Image);
mFilter->SetAlgType(GussianFilter::K_3X3);
mFilter->SetSigma(0.5);
mFilter->UpdateData();
mFilter->GetOutputData(&m_Image);
测试代码:
GussianFilter* mFilter = new GussianFilter();
mFilter->SetInputData(m_Image);
mFilter->SetAlgType(GussianFilter::K_5X5);
mFilter->SetSigma(0.5);
mFilter->UpdateData();
mFilter->GetOutputData(&m_Image);
测试代码:
GussianFilter* mFilter = new GussianFilter();
mFilter->SetInputData(m_Image);
mFilter->SetAlgType(GussianFilter::K_7X7);
mFilter->SetSigma(0.5);
mFilter->UpdateData();
mFilter->GetOutputData(&m_Image);
测试代码:
GussianFilter* mFilter = new GussianFilter();
mFilter->SetInputData(m_Image);
mFilter->SetAlgType(GussianFilter::K_9X9);
mFilter->SetSigma(0.5);
mFilter->UpdateData();
mFilter->GetOutputData(&m_Image);
测试代码:
GussianFilter* mFilter = new GussianFilter();
mFilter->SetInputData(m_Image);
mFilter->SetAlgType(GussianFilter::K_9X9);
mFilter->SetSigma(0.8);
mFilter->UpdateData();
mFilter->GetOutputData(&m_Image);
测试代码:
GussianFilter* mFilter = new GussianFilter();
mFilter->SetInputData(m_Image);
mFilter->SetAlgType(GussianFilter::K_9X9);
mFilter->SetSigma(5.0);
mFilter->UpdateData();
mFilter->GetOutputData(&m_Image);
图像大小:1000*667 卷积模板大小9*9 耗时:0.551秒。