8位灰度图两次一维卷积高斯模糊(VC++)

来源:互联网 发布:ios windows 编辑:程序博客网 时间:2024/04/29 13:07

上篇文章中高斯模糊的处理方法是使用二维高斯卷积,但是这种方法是非常耗时间的,因此可以根据高斯函数的可分离性,对二维高斯模糊函数进行改进。

高斯函数的可分离性是指使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。从计算的角度来看,这是一项有用的特性,因为这样只需要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;    }    // 锁定内存    lpNewDIBBits = (char *)LocalLock(hNewDIBBits);    // 初始化图像为原始图像    memcpy(lpNewDIBBits, lpDIBBits, lLineBytes * lHeight);    //矩阵长度的一半    int nHalfLen;    // 高斯系数与图象数据的点乘    double  dDotMul;    // 高斯滤波系数的总和    double  dWeightSum;    // 中间变量    double *pdTmp;    // 分配内存    pdTmp = new double[lWidth*lHeight];    nHalfLen = iVectorSize / 2;    // x方向进行滤波    for (int y = 0; y < lHeight; y++)    {        for (int x = 0; x < lWidth; x++)        {            dDotMul = 0;            dWeightSum = 0;            //lpDst = (unsigned char*)lpNewDIBBits + lLineBytes*(lHeight - 1 - y) + x;            fResult = 0;            for (int i = (-nHalfLen); i <= nHalfLen; i++)            {                // 判断是否在图象内部                if ((i + x) >= 0 && (i + x) < lWidth)                {                    lpSrc = (unsigned char*)lpDIBBits + lLineBytes*(lHeight - 1 - y) + i + x;                    fResult += (*lpSrc)*fArray1D[nHalfLen + i];                }            }            // 取绝对值            fResult = (double)fabs(fResult);            // 判断是否超过255            if (fResult > 255)            {                // 直接赋值为255                pdTmp[y*lWidth + x] = 255;            }            else            {                // 赋值                pdTmp[y*lWidth + x] = fResult;            }        }    }    //y方向进行滤波    for (int x = 0; x<lWidth; x++)    {        for (int y = 0; y<lHeight; y++)        {            dDotMul = 0;            dWeightSum = 0;            lpDst = (unsigned char*)lpNewDIBBits + lLineBytes*(lHeight - 1 - y) + x;            fResult = 0;            for (int i = (-nHalfLen); i <= nHalfLen; i++)            {                // 判断是否在图象内部                if ((i + y) >= 0 && (i + y) < lHeight)                {                    //lpSrc = (unsigned char*)lpDIBBits + lLineBytes*(lHeight - 1 - i - y) + x;                    //fResult += (*lpSrc)*dpArray1D[nHalfLen + i];                    fResult += pdTmp[(y + i)*lWidth + x] * fArray1D[nHalfLen + i];                }            }            // 取绝对值            fResult = (double)fabs(fResult);            // 判断是否超过255            if (fResult > 255)            {                // 直接赋值为255                *lpDst = 255;            }            else            {                // 赋值                *lpDst = fResult;            }        }    }    // 复制变换后的图像    memcpy(lpDIBBits, lpNewDIBBits, lLineBytes * lHeight);    // 释放内存    LocalUnlock(hNewDIBBits);    LocalFree(hNewDIBBits);    // 返回    return TRUE;}

二、使用两次一维高斯卷积处理,边缘已处理,无黑边(效果在后面)

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;    }    // 指向源图像的指针    unsigned char*  lpSrc;    unsigned char*  lpDst;    // 计算结果    FLOAT   fResult;    // 图像每行的字节数    LONG lLineBytes;    // 计算图像每行的字节数    lLineBytes = WIDTHBYTES(lWidth * 8);    //定义暂时存储图像指针数组    int *buf = new int[(lHeight + iTemplateM * 2) * (lWidth + iTemplateM * 2)];    //定义扩充后的数组大小,扩充为了使边缘不出现黑边    //memset(buf, 0, (lHeight + iTempMY * 2) * (lWidth + iTempMX * 2));    //sizeof()返回内存的字节数,memset的第三个参数为字节数,所以上面的高*宽是不对的    //memset是按字节进行填充的,只能一个一个的字节进行赋值;    //而int型是占4个字节的,所以使用memset进行赋值时,会将int型的4个字节    //赋值成0x01010101,因此打印出来的结果就是一大串数字(十进制的数字)。    // 中间变量    int *Tmp;    // 分配内存    Tmp = new int[(lHeight + iTemplateM * 2) * (lWidth + iTemplateM * 2)];    //把图像复制给buf数组,并且把边缘也复制给buf数组,这样buf的大小就为(lHeight + iTempMY * 2) * (lWidth + iTempMX * 2)    for (int i = 0; i < lHeight; i++)    {        for (int j = 0; j < lWidth; j++)        {            //扫描方式为从左到右,从下到上方式            lpSrc = (unsigned char*)lpDIBBits + lLineBytes*(lHeight - 1 - i) + j;            buf[(i + iTemplateM)*(lWidth + iTemplateM * 2) + j + iTemplateM] = *lpSrc;        }    }    //复制图像左方iTempMX列(1)    for (int i = 0; i < lHeight; i++)    {        for (int j = 0; j < iTemplateM; j++)        {            //扫描方式为从左到右,从下到上方式            lpSrc = (unsigned char*)lpDIBBits + lLineBytes*(lHeight - 1 - i) + j;            buf[(i + iTemplateM)*(lWidth + iTemplateM * 2) + j] = *lpSrc;        }    }    //复制图像右方iTempMX列(2)    for (int i = 0; i < lHeight; i++)    {        for (int j = lWidth - iTemplateM; j < lWidth; j++)        {            //扫描方式为从左到右,从下到上方式            lpSrc = (unsigned char*)lpDIBBits + lLineBytes*(lHeight - 1 - i) + j;            buf[(i + iTemplateM)*(lWidth + iTemplateM * 2) + iTemplateM + j + iTemplateM] = *lpSrc;        }    }    //把buf数组复制给pfTmp数组    for (int i = 0; i < lHeight + iTemplateM * 2; i++)    {        for (int j = 0; j < lWidth + iTemplateM * 2; j++)        {            Tmp[i*(lWidth + iTemplateM * 2) + j] = buf[i*(lWidth + iTemplateM * 2) + j];        }    }    //模板运算    //先水平方向运算    for (int i = iTemplateM; i < lHeight + iTemplateM; i++)    {        for (int j = iTemplateM; j < lWidth + iTemplateM; j ++)        {            fResult = 0;            // 计算            for (int k = 0; k < iVectorSize; k++)            {                fResult += buf[i*(lWidth + iTemplateM * 2) + j - iTemplateM + k] * fArray1D[k];            }            // 判断是否超过255            if (fResult > 255)            {                // 直接赋值为255                Tmp[i*(lWidth + iTemplateM * 2) + j] = 255;            }            else if (fResult < 0)            {                // 直接赋值为0                Tmp[i*(lWidth + iTemplateM * 2) + j] = 0;            }            else            {                // 赋值                Tmp[i*(lWidth + iTemplateM * 2) + j] = (int)fResult;            }        }    }    //这里要解释一下,为什么不在前面就把图像的上方和下方像素复制给数组    //Tmp,因为程序处理的顺序是先水平方向处理,后垂直方向处理,所以如果在    //一开始就复制给Tmp,这样就会导致在水平方向处理完之后,进行垂直方向处    //理时Tmp上下方的像素数据是没有模糊过的,而Tmp的iTemplateM行以后的    //数据是模糊过的,这样它们一结合处理的图像上方就会参差不齐,具体可以试    //验一下。    //复制图像上方iTempMY行(3)    for (int i = 0; i < iTemplateM; i++)    {        for (int j = iTemplateM; j < lWidth + iTemplateM; j++)        {            Tmp[i*(lWidth + iTemplateM * 2) + j] = Tmp[(i + iTemplateM)*(lWidth + iTemplateM * 2) + j];        }    }    //复制图像下方iTempMY行(4)    for (int i = lHeight; i < lHeight + iTemplateM; i++)    {        for (int j = iTemplateM; j < lWidth + iTemplateM; j++)        {            Tmp[(i + iTemplateM)*(lWidth + iTemplateM * 2) + j] = Tmp[i*(lWidth + iTemplateM * 2) + j];        }    }    //然后垂直方向运算    for (int j = iTemplateM; j < lWidth + iTemplateM; j ++)    {        for (int i = iTemplateM; i < lHeight + iTemplateM; i++)        {            fResult = 0;            // 计算            for (int k = 0; k < iVectorSize; k++)            {                fResult += Tmp[(i - iTemplateM + k)*(lWidth + iTemplateM * 2) + j] * fArray1D[k];            }            lpDst = (unsigned char*)lpDIBBits + lLineBytes*(lHeight - 1 - i + iTemplateM) + j - iTemplateM;            // 判断是否超过255            if (fResult > 255)            {                // 直接赋值为255                *lpDst = 255;            }            else if (fResult < 0)            {                // 直接赋值为0                *lpDst = 0;            }            else            {                // 赋值                *lpDst = (int)fResult;            }        }    }    delete[] buf;    delete[] Tmp;    delete[] fArray1D;    // 返回    return TRUE;}

下面是效果图:

原图:

这里写图片描述

边缘已处理,但有黑边(fsigma=5):

这里写图片描述

边缘已处理,无黑边(fsigma=5):

这里写图片描述

1 0