基本原理:
在数字图像处理中,一般取二维高斯函数为
由(1)可以知道,二维高斯函数,可以看成两个一维高斯函数乘积,因此先计算一维高斯模板,再计算需要的二维高斯模板。
两个归一化的一维模板相乘得到的二维高斯模板,同样为归一化结果,例:
如图1所示,
(a)为两个归一化的一维高斯模板,即,a+b+c=1,d+e+f+g+h=1;
(b)为两个一维高斯系数相乘得到的二维高斯模板,
ad + ae + af + ag + ah + bd + be + bf + bg +bh + cd + ce + cf + cg + ch
= a(d+e+f+g+h)+ b(b+e+f+g+h) + c(b+e+f+g+h)
=(a+b+c)(d+e+f+g+h) = 1 * 1 = 1。
源码分析:
在OpenCV中获取一维归一化的高斯模板的函数为
// n – 模板尺寸,sigma( – 标准差
cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
主要流程为:
源码分析如下:
【函数名称】
cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
【函数描述】用于计算一维高斯函数的模板系数
【参数描述】n — 模板尺寸,n必须为大于0的正整数,一般取n为奇数,但是通
过源代码分析可以知道,取n为偶数,同样可以计算得到对
应结果。
sigma — 高斯标准差(公式中的σ),如果sigma取小于等于0的数,则
函数根据当前n值计算对应的sigma值,
sigma = ((n-1)*0.5 – 1)*0.3 + 0.8。
ktype — 数据类型,CV_32F或者CV_64F
【返回值】 返回n行1列归一化的一维高斯系数
cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
{
// 预置的高斯系数数组,只有在
// (1)0<n<=7,n为奇数,
// (2)sigma为无效参数,即sigma<=0
// 同时满足时才会使用预置的高斯系数数组
const int SMALL_GAUSSIAN_SIZE = 7; // 预置的最大模板的尺寸
static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
{
{1.f},
{0.25f, 0.5f, 0.25f},
{0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
{0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
};
// 判断是否满足采用预置高斯系数的条件
const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
small_gaussian_tab[n>>1] : 0;
// 确定ktype参数的合法性,只能去CV_32F或者CV_64F
CV_Assert( ktype == CV_32F || ktype == CV_64F );
Mat kernel(n, 1, ktype); // 创建n行一列系数矩阵
float* cf = (float*)kernel.data; // cf赋值为数据地址
double* cd = (double*)kernel.data; // cd赋值为数据地址
// 如果sigma<0,则利用公式根据模板尺寸n计算sigma
double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;
double scale2X = -0.5/(sigmaX*sigmaX); // 计算e^(-x^2/2σ^2 ) 中的-1/2σ^2
double sum = 0; // 每一个元素之和,归一化时作为除数
int i;
for( i = 0; i < n; i++ )
{
double x = i - (n-1)*0.5; // 计算索引为0的元素的x坐标
double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x); // 取预置数组中对应元素或由高斯公式计算,1/√2π σ_x在归一化时
// 会约去,所以不用计算。
// 根据元素类型为CV_32F或CV_64F赋值,并累加和
if( ktype == CV_32F )
{
cf[i] = (float)t;
sum += cf[i];
}
else
{
cd[i] = t;
sum += cd[i];
}
}
// 做归一化计算
sum = 1./sum;
for( i = 0; i < n; i++ )
{
if( ktype == CV_32F )
cf[i] = (float)(cf[i]*sum);
else
cd[i] *= sum;
}
return kernel;
}
注:
当n = 1、3、5、7并且sigma <= 0时,不采用预置模板,采用sigma = ((n-1)*0.5 - 1)*0.3 + 0.8计算时,如下表所示:
可以看出,此时和预置模板系数并不完全一致。