数字图像处理,二维图像小波阈值去噪的C++实现(matlab验证)

来源:互联网 发布:电路设计软件哪个好 编辑:程序博客网 时间:2024/04/29 12:20

本文代码的实现严重依赖前面的两篇文章:


1,一维信号的小波阈值去噪


2,小波变换一维Mallat算法的C++实现


注本文的大部分文字提取于参考论文
重要说明:本文代码是学习小波变换时写的,文中的代码有较严重的性能问题(但是运算结果是正确的),如你需要本代码,需要自行优化或者更改(一维阈值去噪那篇文章中的性能挺快的)

一,小波阈值去噪基本理论

      图像在获取或传输过程中会因各种噪声的干扰使质量下降,这将对后续图像的处理产生不利影响.所以必须对图像进行去噪处理,而去噪所要达到的目的就是在较好去除噪声的基础上,良好的保持图像的边缘等重要细节.在图像去噪领域得到广泛的应用.本博文根据小波的分解与重构原理,实现了基于硬阈值和软阈值函数的小波阈值去噪的C++版本,最终结果与matlab库函数运算结果完全一致。

1,小波阈值处理

小波阈值收缩法是Donoho和Johnstone提出的,一下便是养活不少学者的三篇基础论文:

【1】 Donoho D L. De-noising by soft-thresholding. IEEE Trans- actions on Information Theory, 1995, 41(3): 613−627
【2】 Donoho D L, Johnstone I M. Adapting to unknown smooth- ness via wavelet shrinkage. Journal of the American Statistic
Association, 1995, 90(432): 1200−1224
【3】 Donoho D L, Johnstone I M, Kerkyacharian G, Picard D. Wavelet shrinkage: asymptopia? Journal of Royal Statisti-
cal Society Series B, 1995, 57(2): 301−369

小波阈值去噪其主要理论依据是,小波变换具有很强的去数据相关性,它能够使信号的能量在小波域集中在一些大的小波系数中;而噪声的能量却分布于整个小波域内.因此,经小波分解后,信号的小波系数幅值要大于噪声的系数幅值.可以认为,幅值比较大的小波系数一般以信号为主,而幅值比较小的系数在很大程度上是噪声.于是,采用阈值的办法可以把信号系数保留,而使大部分噪声系数减小至零.小波阈值收缩法去噪的具体处理过程为:将含噪信号在各尺度上进行小波分解,设定一个阈值,幅值低于该阈值的小波系数置为0,高于该阈值的小波系数或者完全保留,或者做相应的“收缩(shrinkage)”处理.最后将处理后获得的小波系数用逆小波变换进行重构,得到去噪后的图像.


2,阈值函数的选取

  阈值去噪中,阈值函数体现了对超过和低于阈值的小波系数不同处理策略,是阈值去噪中关键的一步。设w表示小波系数,T为给定阈值,sign(*)为符号函数,常见的阈值函数有:


硬阈值函数:     (小波系数的绝对值低于阈值的置零,高于的保留不变)

     

软阈值函数:   (小波系数的绝对值低于阈值的置零,高于的系数shrinkage处理)

      


值得注意的是:

1) 硬阈值函数在阈值点是不连续的,在下图中已经用黑线标出。不连续会带来振铃,伪吉布斯效应等。

2) 软阈值函数,原系数和分解得到的小波系数总存在着恒定的偏差,这将影响重构的精度使得重构图像的边缘模糊等现象.

所以这里也添加一种简单的改进阈值函数,我们称之为半阈值

注意:图片中的公式有误,应该是sgn(w)*(|w|-P*T)(即将软函数中的阈值T缩小):

         

三种阈值处理策略见下图:



其实不少文章出现各种优秀的改进方案(于是有养活了不少学者,非本文重点):


3,阈值的确定

选取的阈值最好刚好大于噪声的最大水平,可以证明的是噪声的最大限度以非常高的概率低于,此阈值是Donoho和Johnstone提出的,其实我一直很想吐槽为什么阈值和信号的size相关呢?当然我的疑问也是大家的疑问,此问题有养活了一批学者,其中根号右边的这个参数(叫做sigma)就是估计出来的噪声标准偏差(第一级分解出的小波细节系数,即整个HH系数的绝对值的中值),本文将用此阈值去处理各尺度上的细节系数。


4,阈值策略

阈值策略有两种,一种是全局阈值策略,一种是分层阈值策略,从读论文了解到,全局阈值策略有他的缺陷:如果采用全局阈值进行处理,则会对所有尺度下的高频系数进行同一阈值处理,然而随着小波分解尺度的增加,有用信息分解后的小波系数将会越来越大,而噪声系数却会越来越小,所以为了防止高尺度下有用信息的分解系数被过度处理,分层阈值处理将会更合理。但是从我实际测试的结果来看------没什么区别!!!简便起见代码还是采用全局阈值。


为了不误导小伙伴们,请注意:这张图中阈值的估算不是来自CV1而是来自CD1,因为CD1是两次高通滤波的结果,里面含有最多的噪声,估计出来的噪声偏差也是最准确的。





5,小波阈值去噪过程


   有用信号经小波变换后, 其能量将集中在少数的小波系数上, 而噪声点的小波系数互不相关, 分布在各个尺度的所有时间轴上. 保留小波变换的各尺度下的模极大值点, 而将其他点置零或最大程度的减小, 然后将处理后的小波系数做小波逆变换, 即可达到抑制噪声的目的.阈值去噪是通过对变换域系数与阈值进行比较判断, 然后将处理后的系数进行逆变换重构去噪图像. 小波阈值去噪法的具体步骤如下:

步骤 1. 图像的小波分解: 确定小波函数和分解层次 N, 对图像进行 N 层的小波分解;
步骤 2. 阈值处理: 对分解得到的各层系数选择阈值, 并对细节系数进行阈值判断;
步骤 3. 图像重构: 对阈值处理后的系数通过小波逆变换重建图像.
   信号和噪声在小波域内具有不同的相关性. 信号在尺度间相应位置上的小波系数具有很强的相关性, 而噪声的小波系数则具有弱相关性或者不相关. 在阈值去噪中, 由于所选定的阈值通常固定, 不会随着小波系数的不同而变化, 这就不可避免地会对部分小波系数进行误判,于是又养活了一批学者。。。分析改进,分析改进,发文章!


1),小波的分解





2,小波的重构





二,Matlab库函数实现

1,核心库函数说明

1)wavedec2

图像的多级小波分解,将返回分解出来的小波系数以及小波系数的各级长度

2)waverec2

多级小波系数的重构,重构出原信号

3)wthresh函数

对系数进行指定类型(全局阈值或者分层阈值)的阈值去噪处理,软硬阈值处理函数。下图所示程序和运行结果可以比较清晰地看出该程序的执行过程。

clear;y = linspace(-1,1,100);thr = 0.4;ythard = wthresh(y,'h',thr);ytsoft = wthresh(y,'s',thr);subplot(311);plot(y);subplot(312);plot(ythard);subplot(313);plot(ytsoft);



更具体的函数说明可以在,matlab里键入“doc 函数名”将得到很详细的说明,当然也可以百度

2,软,硬阈值处理效果:



哎哟,不错哦,半阈值去噪效果在视觉上的确有明显的改进效果




局部放大图像:

四幅图象均放大两倍,便于查看区别

这幅图像是源图像放大的效果:









3,完整的代码实现

说明:代码实现对图像一层分解后的细节系数进行全局阈值处理(即LHL,LH,HH均用同一阈值处理)。

% 获取输入参数  w    = 'db3';%小波类型  n    = 3;%分解层数   sorh1 = 'hard';%硬阈值  sorh2 = 'soft';%软阈值  sorh3 = 'half';%半阈值   % 对图像进行小波分解  [c,l] = wavedec2(octimage,n,w);    %求取阈值  N = numel(octimage);  [chd1,cvd1,cdd1] = detcoef2('all',c,l,1);   cdd1=cdd1(:)';  sigma = median(abs(cdd1))/0.6745;%提取细节系数求中值并除以0.6745  thr = sigma*sqrt(2*log(N))/sqrt(1+sqrt(n)); %对阈值做了改进% 对小波系数全局阈值处理  cxchard = c;% 保留近似系数  cxcsoft = c;% 保留近似系数  cxchalf = c;% 保留近似系数  justdet = prod(l(1,:))+1:length(c);%截取细节系数(不处理近似系数)    % 阈值处理细节系数  cxchard(justdet) = myWthresh(cxchard(justdet),sorh1,thr);%硬阈值去噪  cxcsoft(justdet) = myWthresh(cxcsoft(justdet),sorh2,thr);%软阈值去噪  cxchalf(justdet) = myWthresh(cxchalf(justdet),sorh3,thr);%软阈值去噪  %小波重建  xchard = waverec2(cxchard,l,w);  xcsoft = waverec2(cxcsoft,l,w);  xchalf = waverec2(cxchalf,l,w);   figure(2);   imshow(uint8(xchard(1:iCount, :)));title('硬阈值去噪图像');        figure(3);   imshow(uint8(xcsoft(1:iCount, :)));title('软阈值去噪图像');   figure(4);   imshow(uint8(xchalf(1:iCount, :)));title('半阈值去噪图像'); 




三,C加加实现

说明:如同一维的阈值去噪一样,在执行自己编写的wavedec2函数时必须先初始化,初始化的目的是为了获取信号的长度,选择的是什么小波,以及分解的等级等信息,然后计算出未来的各种信息,比如每个等级的系数的size,其中共有变量m_msgCL2D记录了这些信息。二维小波分解的初始化函数如下:

//初始化二维图像的分解信息,保存未来需要的信息bool  CWavelet::InitDecInfo2D(const int height,//预分解的图像的高度const int width,//预分解的图像的宽度const int Scale,//分解尺度const int dbn//db滤波器编号,有默认值){if (dbn != 3)SetFilter(dbn);if (height < m_dbFilter.filterLen - 1 || width < m_dbFilter.filterLen - 1){cerr << "错误信息:滤波器长度大于信号的高度或者宽度!" << endl;return false;}int srcHeight = height;int srcWidth = width;m_msgCL2D.dbn = dbn;m_msgCL2D.Scale = Scale;m_msgCL2D.msgHeight.resize(Scale + 2);m_msgCL2D.msgWidth.resize(Scale + 2);//源图像的尺寸m_msgCL2D.msgHeight[0] = height;m_msgCL2D.msgWidth[0] = width;//每一尺度上的尺寸for (int i = 1; i <= Scale; i++)//注意:每个尺度的四个分量的长宽是一样的{int exHeight = (srcHeight + m_dbFilter.filterLen - 1) / 2;//对称拓延后系数的长度的一半srcHeight = exHeight;m_msgCL2D.msgHeight[i] = srcHeight;int exWidth = (srcWidth + m_dbFilter.filterLen - 1) / 2;//对称拓延后系数的长度一半srcWidth = exWidth;m_msgCL2D.msgWidth[i] = srcWidth;}m_msgCL2D.msgHeight[Scale + 1] = srcHeight;m_msgCL2D.msgWidth[Scale + 1] = srcWidth;//计算总的数据个数int tmpAllSize = 0;int curPartSize = 0;int prePartSize = 0;for (int i = 1; i <= Scale; i++){curPartSize = m_msgCL2D.msgHeight[i] * m_msgCL2D.msgWidth[i];tmpAllSize += curPartSize * 4 - prePartSize;prePartSize = curPartSize;}m_msgCL2D.allSize = tmpAllSize;m_bInitFlag2D = true;return true;}


1,核心函数的实现

1)二维信号的单次分解

说明:本函数建立在一维的小波分解函数基础上(DWT)

// 二维数据的小波分解void  CWavelet::DWT2(double *pSrcImage,//源图像数据(存储成一维数据,行优先存储)int height,//图像的高int width,//图像的宽double *pDstCeof//分解出来的图像系数){if (!m_bInitFlag2D){cerr << "错误信息:未初始化,无法对信号进行分解!" << endl;return;}if (pSrcImage == NULL || pDstCeof == NULL){cerr << "错误信息:dwt2数据无内存" << endl;Sleep(3000);exit(1);}int exwidth = (width + m_dbFilter.filterLen - 1) / 2 * 2;//pImagCeof的宽度int exheight = (height + m_dbFilter.filterLen - 1) / 2 * 2;//pImagCeof的高度double *tempImage = new double[exwidth*height];// 对每一行进行行变换double *tempAhang = new double[width]; double *tempExhang = new double[exwidth]; // 临时存放每一行的处理数据for (int i = 0; i < height; i++){for (int j = 0; j < width; j++)tempAhang[j] = pSrcImage[i*width + j];//提取每一行的数据DWT(tempAhang, width, tempExhang);for (int j = 0; j < exwidth; j++)tempImage[i*exwidth + j] = tempExhang[j];}// 对每一列进行列变换double *tempAlie = new double[height]; // 临时存放每一列的转置数据double *tempexlie = new double[exheight]; // 临时存放每一列的处理数据for (int i = 0; i < exwidth; i++){// 列转置for (int j = 0; j < height; j++)tempAlie[j] = tempImage[j*exwidth + i];//提取每一列数据//执行变换DWT(tempAlie, height, tempexlie);// 反转置for (int j = 0; j < exheight; j++)pDstCeof[j*exwidth + i] = tempexlie[j];}AdjustData(pDstCeof, exheight, exwidth);//调整数据delete[] tempAlie;tempAlie = NULL;delete[] tempexlie;tempexlie = NULL;delete[] tempAhang;tempAhang = NULL;delete[] tempExhang;tempExhang = NULL;delete[] tempImage;tempImage = NULL;}



2)二维信号的单次重构

说明:

//二维小波反变换void  CWavelet::IDWT2(double *pSrcCeof, //二维源图像系数数据int dstHeight,//重构出来后数据的高度int dstWidth,//重构出来后数据的宽度double *pDstImage//重构出来的图像){int srcHeight = (dstHeight + m_dbFilter.filterLen - 1) / 2 * 2;int srcWidth = (dstWidth + m_dbFilter.filterLen - 1) / 2 * 2;//pSrcCeof的高度IAdjustData(pSrcCeof, srcHeight, srcWidth);//调整成LL,HL,LH,HHdouble *tempAline = new double[srcHeight]; // 临时存放每一列的数据double *tempdstline = new double[dstHeight]; // 临时存放每一列的重构结果double *pTmpImage = new double[srcWidth*dstHeight];// 列重构for (int i = 0; i < srcWidth; i++)//每一列{// 列转置for (int j = 0; j<srcHeight; j++)tempAline[j] = pSrcCeof[j*srcWidth + i];//提取每一列IDWT(tempAline, dstHeight, tempdstline);// 反转置for (int j = 0; j < dstHeight; j++)pTmpImage[j*srcWidth + i] = tempdstline[j];}// 对每一行进行行变换double *tempAhang = new double[srcWidth];double *tempdsthang = new double[dstWidth]; // 临时存放每一行的处理数据for (int i = 0; i < dstHeight; i++){for (int j = 0; j < srcWidth; j++)tempAhang[j] = pTmpImage[i*srcWidth + j];//提取每一行的数据IDWT(tempAhang, dstWidth, tempdsthang);for (int j = 0; j < dstWidth; j++)pDstImage[i*dstWidth + j] = tempdsthang[j];}delete[] tempAline;tempAline = NULL;delete[] tempdstline;tempdstline = NULL;delete[] tempAhang;tempAhang = NULL;delete[] tempdsthang;tempdsthang = NULL;delete[] pTmpImage;pTmpImage = NULL;}


3)二维信号的多级分解

说明:对于每一级分解都将调用单次二维分解函数来实现,所以本函数是建立在函数IDW2基础上

// 二维小波多级分解,需要先初始化获取未来数据信息bool CWavelet::WaveDec2(double *pSrcData,//源图像数据,存储为一维信号double *pDstCeof//分解后的系数,它的大小必须是m_msgCL2D.allSize){if (!m_bInitFlag2D){cerr << "错误信息:未初始化,无法对图像进行分解!" << endl;return false;}if (pSrcData == NULL || pDstCeof == NULL)//错误:无内存return false;int height = m_msgCL2D.msgHeight[0];int width = m_msgCL2D.msgWidth[0];int scale = m_msgCL2D.Scale;// 临时变量,图像数据double *tempImage = new double[height*width];int maxCoefSize =4 * m_msgCL2D.msgHeight[1] * m_msgCL2D.msgWidth[1];double *tempDst = new double[maxCoefSize];for (int i = 0; i < height*width; i++)tempImage[i] = pSrcData[i];int gap = m_msgCL2D.allSize - maxCoefSize;for (int i = 1; i <= scale; i++){DWT2(tempImage, height, width, tempDst);// 低频子图像的高和宽height = m_msgCL2D.msgHeight[i];width = m_msgCL2D.msgWidth[i];for (int j = 0; j < height*width; j++)tempImage[j] = tempDst[j];//提取低频系数(近似系数)//for (int j = 0, k = gap; j < 4 * height*width; j++, k++)pDstCeof[k] = tempDst[j];//所有系数gap -= 4 * m_msgCL2D.msgWidth[i + 1] * m_msgCL2D.msgHeight[i + 1] - height*width;}delete[] tempDst;tempDst = NULL;delete[] tempImage;tempImage = NULL;return true;}


4)多级分解系数的重构

// 根据多级分解系数重构出二维信号,必须先初始化获取分解信息bool CWavelet::WaveRec2(double *pSrcCoef,//多级分解出的源系数double *pDstData//重构出来的信号){if (!m_bInitFlag2D){cerr << "错误信息:未初始化,无法对信号进行分解!" << endl;return false;}if (pSrcCoef == NULL || pDstData == NULL)//错误:无内存return false;int height = m_msgCL2D.msgHeight[0];int width = m_msgCL2D.msgWidth[0];int decLevel = m_msgCL2D.Scale;int maxCeofSize = 4 * m_msgCL2D.msgHeight[1] * m_msgCL2D.msgWidth[1];double *pTmpImage = new double[maxCeofSize];int minCeofSize = 4 * m_msgCL2D.msgHeight[decLevel] * m_msgCL2D.msgWidth[decLevel];for (int i = 0; i < minCeofSize; i++)pTmpImage[i] = pSrcCoef[i];int gap = minCeofSize;for (int i = decLevel; i >= 1; i--){int nextheight = m_msgCL2D.msgHeight[i - 1];//重构出来的高度int nextwidth = m_msgCL2D.msgWidth[i - 1];//重构出来的宽度IDWT2(pTmpImage, nextheight, nextwidth, pDstData);if (i > 1)//i==1已经重构出来了,不再需要提取系数{for (int j = 0; j < nextheight*nextwidth; j++)pTmpImage[j] = pDstData[j];for (int j = 0; j < 3 * nextheight*nextwidth; j++)pTmpImage[nextheight*nextwidth + j] = pSrcCoef[gap + j];gap += 3 * nextheight*nextwidth;}}delete[] pTmpImage;pTmpImage = NULL;return true;}


2,函数正确性验证

1),二维单次分解与重构测试



2)二维多级分解与重构测试

说明:对二维数据进行了5层分解,选取的是小波族db3



3,阈值去噪结果:

说明:本测试只是模拟测试,对图像的处理也是一样的(完全一致)

硬阈值去噪结果




软阈值去噪结果





实际的VC++图像处理结果为:

源噪声图像为:



注意以下是采用:db6,3层分解,软阈值去噪,阈值是在前文提及的阈值基础上缩小2.5倍得到的效果:



注意以下是采用:db6,3层分解,硬阈值去噪,阈值是在前文提及的阈值基础上缩小2.5倍得到的效果:




附带上述matlab验证程序

clc;  clear all;  close all;  % %%%%%%%%%%%%%%%%%%%%%%%%通过matlab的函数来实现阈值去噪%%%%%%%%%%%%%%%%%%%%%%%%%% %  X=[ 10, 12, 30, 4, 5, 61, 2, 3;    41, 5, 6, 27, 3, 4, 15, 6;    72, 8, 41, 5, 6, 7, 8, 9;    5, 64, 7, 8, 9, 14, 6, 27;    8, 9, 40, 31,10, 12, 30, 4;    50, 61, 2, 3, 41, 5, 6, 27];X=double(X);  % 获取输入参数  wname    = 'db3';%小波类型  n    = 3;%分解层数  sorh1 = 'h';%硬阈值  sorh2 = 's';%软阈值  % 对图像进行小波分解  [c,l] = wavedec2(X,n,wname);%求取阈值N = numel(X);[chd1,cvd1,cdd1] = detcoef2('all',c,l,1); cvd1=cvd1(:)';sigma = median(abs(cvd1))/0.6745;%提取细节系数求中值并除以0.6745thr = sigma*sqrt(2*log(N)); % 对小波系数全局阈值处理  cxch = c;% 保留近似系数  cxcs = c;% 保留近似系数  justdet = prod(l(1,:))+1:length(c);%截取细节系数(不处理近似系数)  % 阈值处理细节系数  cxch(justdet) = wthresh(cxch(justdet),sorh1,thr);%硬阈值去噪  cxcs(justdet) = wthresh(cxcs(justdet),sorh2,thr);%软阈值去噪  %小波重建  xch = waverec2(cxch,l,wname);  xcs = waverec2(cxcs,l,wname);  


鉴于不少人要代码,出于不误导人,给一些我自己的建议:
1,对于本文程序要有自己的思考,不要尽信!
2,二维小波变换不建议用,因为速度太慢了(自己徒手写的),一维可以用。
3,推荐你另外的小波库网站,http://wavelet2d.sourceforge.net/

注:本博文为EbowTang原创,后续可能继续更新本文。如果转载,请务必复制本条信息!

原文地址:http://blog.csdn.net/ebowtang/article/details/40481539

原作者博客:http://blog.csdn.net/ebowtang


参考资源:

【1】《数字图像处理》(冈萨雷斯matlab第二版)
【2】 Donoho D L. De-noising by soft-thresholding. IEEE Trans- actions on Information Theory, 1995, 41(3): 613−627
【3】 Donoho D L, Johnstone I M. Adapting to unknown smooth- ness via wavelet shrinkage. Journal of the American Statistic
Association, 1995, 90(432): 1200−1224
【4】 Donoho D L, Johnstone I M, Kerkyacharian G, Picard D. Wavelet shrinkage: asymptopia? Journal of Royal Statisti-
cal Society Series B, 1995, 57(2): 301−369
【5】杨恢先,王绪四,改进阈值与尺度间相关的小波红外图像去噪
【6】《小波分析及其应用》,孙延奎著
【7】杨建国.小波分析及其工程应用[M].北京:机械工业出版社.2005
【8】毛艳辉.小波去噪在语音识别预处理中的应用.上海交通大学硕士学位论文.2010
【9】matlab各种函数说明,及其内部函数实现
【10】http://www.bearcave.com/misl/misl_tech/wavelets/haar.html

4 0
原创粉丝点击