上篇文章中高斯模糊的处理方法是使用二维高斯卷积,但是这种方法是非常耗时间的,因此可以根据高斯函数的可分离性,对二维高斯模糊函数进行改进。
高斯函数的可分离性是指使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。从计算的角度来看,这是一项有用的特性,因为这样只需要O(n*M*n)+O(m*M*N)次计算,而二维不可分的矩阵则需要O(m*n*M*n)次计算,其中,m,n为高斯矩阵的维数,M,N为二维图像的维数。
下面是两次一维卷积的实现代码:
一、使用两次一维高斯卷积处理,边缘已处理,但有黑边(效果在后面)
/*************************************************************************
*
* 函数名称:
* GaussSmooth1D8()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
* float fSigma - 方差
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数对8位位图进行高斯模糊(两次1维高斯模糊)。
*
************************************************************************/
//对8位灰度图进行两次一维高斯卷积,边缘已处理,但有黑边
BOOL WINAPI GaussSmooth1D8(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, float fSigma)
{
//指向高斯数据数组的指针
float *fArray1D;
//模板的长度
int iVectorSize;
// 数组的中心点
int iTemplateM;
// 数组的某一点到中心点的距离
int iDistance;
//π值
double dPI = 3.1415926536;
// 中间变量
double dValue;
double dSum = 0;
// 数组长度,根据概率论的知识,选取[-3*sigma, 3*sigma]以内的数据。
// 这些数据会覆盖绝大部分的滤波系数
iVectorSize = (int)1 + 2 * ceil(3 * fSigma); //ceil()返回大于或等于指定表达式的最小整数
// 中心
iTemplateM = iVectorSize / 2;
// 分配内存
fArray1D = new float[iVectorSize];
for (int i = 0; i< iVectorSize; i++)
{
iDistance = abs((int)(i - iTemplateM));
double aa = -0.5 / (fSigma*fSigma);
double bb = 1 / ((sqrt(2 * dPI))*fSigma);
//dValue = exp(-(1 / 2)*iDistance*iDistance / (dSigma*dSigma)) / (sqrt(2 * dPI) * dSigma);
dValue = bb*exp(aa*(iDistance*iDistance));
(fArray1D)[i] = dValue;
dSum += dValue;
}
// 归一化
for (int i = 0; i<iVectorSize; i++)
{
(fArray1D)[i] /= dSum;
}
// 指向复制图像的指针
LPSTR lpNewDIBBits;
HLOCAL hNewDIBBits;
// 指向源图像的指针
unsigned char* lpSrc;
// 指向要复制区域的指针
unsigned char* lpDst;
// 计算结果
double fResult;
// 图像每行的字节数
LONG lLineBytes;
// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
// 暂时分配内存,以保存新图像
hNewDIBBits = LocalAlloc(LHND, lLineBytes * lHeight);
// 判断是否内存分配失败
if (hNewDIBBits == NULL)
{
// 分配内存失败
return FALSE;
}