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

最后更新于:2022-04-01 23:03:17

本文代码的实现严重依赖前面的两篇文章: [一维信号的小波阈值去噪](http://blog.csdn.net/ebowtang/article/details/40481393#t14) [小波变换一维Mallat算法的C++实现](http://blog.csdn.net/ebowtang/article/details/40433861#t0) 图像在获取或传输过程中会因各种噪声的干扰使质量下降,这将对后续图像的处理产生不利影响.所以必须对图像进行去噪处理,而去噪所要达到的目的就是在较好去除噪声的基础上,良好的保持图像的边缘等重要细节.在图像去噪领域得到广泛的应用.本博文根据小波的分解与重构原理,实现了基于硬阈值和软阈值函数的小波阈值去噪的C++版本,最终结果与matlab库函数运算结果完全一致。 注本文的大部分文字提取于参考论文 # 一,小波阈值去噪基本理论 ###小波阈值处理 小波阈值收缩法是Donoho和Johnstone提出的,其主要理论依据是,小波变换具有很强的去数据相关性,它能够使信号的能量在小波域集中在一些大的小波系数中;而噪声的能量却分布于整个小波域内.因此,经小波分解后,信号的小波系数幅值要大于噪声的系数幅值.可以认为,幅值比较大的小波系数一般以信号为主,而幅值比较小的系数在很大程度上是噪声.于是,采用阈值的办法可以把信号系数保留,而使大部分噪声系数减小至零.小波阈值收缩法去噪的具体处理过程为:将含噪信号在各尺度上进行小波分解,设定一个阈值,幅值低于该阈值的小波系数置为0,高于该阈值的小波系数或者完全保留,或者做相应的“收缩(shrinkage)”处理.最后将处理后获得的小波系数用逆小波变换进行重构,得到去噪后的图像. ###阈值函数的选取   阈值去噪中,阈值函数体现了对超过和低于阈值的小波系数不同处理策略,是阈值去噪中关键的一步。设w表示小波系数,T为给定阈值,sign(*)为符号函数,常见的阈值函数有: 硬阈值函数:     (小波系数的绝对值低于阈值的置零,高于的保留不变)      ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65265edd51.jpg) 软阈值函数:          ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65266099d9.jpg) 值得注意的是: 1) 硬阈值函数在阈值点是不连续的,在下图中已经用黑线标出。不连续会带来振铃,伪吉布斯效应等。 2) 软阈值函数,原系数和分解得到的小波系数总存在着恒定的偏差,这将影响重构的精度使得重构图像的边缘模糊等现象. 同时这两种函数不能表达出分解后系数的能量分布。见下图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652661a5a8.jpg)                                                                                图1 于是不少文章出现了折衷方案,一种新的阈值函数(非本文重点): ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652662f779.jpg) 参考代码: ~~~ %Generate signal and set threshold. y = linspace(-1,1,100); subplot(311); plot(y);title('原始线'); thr = 0.5; % Perform hard thresholding. ythard = wthresh(y,'h',thr); subplot(312); plot(ythard);title('硬阈值线'); % Perform soft thresholding. ytsoft = wthresh(y,'s',thr); subplot(313); plot(ytsoft);title('软阈值线'); ~~~ ### 阈值的确定 选取的阈值最好刚好大于噪声的最大水平,可以证明的是噪声的最大限度以非常高的概率低于![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652664b3b4.jpg) (此阈值是Donoho提出的),其中根号右边的这个参数(叫做sigma)就是估计出来的噪声标准偏差(第一级分解出的小波细节系数,即整个HH系数的绝对值的中值),本文将用此阈值去处理各尺度上的细节系数。 ### 阈值策略 曾经做的ppt挪用过来的。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652675213f.jpg) ###二维信号分解与重构 小波的分解 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526769a5d.jpg) 小波的重构 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652678016b.jpg) # 二,Matlab库函数实现 ### 1,核心库函数说明 1)wavedec2 图像的多级小波分解,将返回分解出来的小波系数以及小波系数的各级长度 2)waverec2 多级小波系数的重构,重构出原信号 3)wthresh函数 对系数进行指定类型(全局阈值或者分层阈值)的阈值去噪处理 更具体的函数说明可以在,matlab里键入“doc 函数名”将得到很详细的说明,当然也可以百度 ### 2,软,硬阈值处理效果: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526793a7a.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65267b05bf.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65267cf062.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65267ecc35.jpg) 局部放大图像: 四幅图象均放大两倍,便于查看区别 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526813430.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526830a8f.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652684fb3a.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d652687812d.jpg) ### 3,完整的代码实现 说明:代码实现对图像一层分解后的细节系数进行全局阈值处理(即LHL,LH,HH均用同一阈值处理),并且阈值是自定义的。 ~~~ clc; clear all; close all; %%%%%%%%%%%%%%%%%%%%%%%%通过matlab的函数来实现阈值去噪%%%%%%%%%%%%%%%%%%%%%%%%%% % X=imread('sample1.bmp');%有噪声的图像 X=double(X); figure(1); imshow(X,[]);title('原始图像'); %注意,原始图像带有噪声 % 获取输入参数 w = 'db3';%小波类型 n = 1;%分解层数 thr = 23.91;%自定义阈值 sorh1 = 'h';%硬阈值 sorh2 = 's';%软阈值 % 对图像进行小波分解 [c,l] = wavedec2(X,n,w); % 对小波系数全局阈值处理 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,w); xcs = waverec2(cxcs,l,w); figure(2); imshow(uint8(xch));title('硬阈值去噪图像'); figure(3); imshow(uint8(xcs));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; } ~~~ ### 2,核心函数的实现 ### 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,HH double *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= 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; } ~~~ ### 3,函数正确性验证 ### 1)二维单次分解与重构测试 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526891d2c.jpg) 附带本次测试代码: ~~~ //小波函数的二维分解与重构 int main() { system("color 0A"); CWavelet cw; double s[36] = { 1, 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 }; int height = 6; int width = 6; for (int j = 0; j < 36; j++) { cout << s[j] << " "; if ((j + 1) % 6 == 0) cout << endl; } cout << endl; cout << endl; cw.InitDecInfo2D(height, width, 1, 3); double *dst = new double[cw.m_msgCL2D.allSize]; cw.DWT2(s, height, width, dst); for (int j = 0; j < cw.m_msgCL2D.allSize; j++) { cout << dst[j] << " "; if ((j + 1) % 10 == 0) cout << endl; } cout << endl; double *dsts = new double[36]; cw.IDWT2(dst, height, width, dsts); for (int j = 0; j < 36; j++) { cout << dsts[j] << " "; if ((j + 1) % 6 == 0) cout << endl; } delete[] dst; dst = NULL; delete[] dsts; dsts = NULL; system("pasue"); return 0; } ~~~ ### 2)二维多级分解与重构测试 说明:对二维数据进行了5层分解,选取的是小波族db3 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65268adf92.jpg) 附带本次测试代码: ~~~ //小波函数二维多级分解与重构测试 int main() { system("color 0A"); CWavelet cw; double s[48] = { 1, 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 , 1, 12, 30, 4, 5, 61, 2, 3, 41, 5, 6, 27 }; int height = 6; int width = 8; for (int j = 0; j < 48; j++) { cout << s[j] << " "; if ((j + 1) % 8 == 0) cout << endl; } cout << endl; int Scale = 5; int dbn = 2; cw.InitDecInfo2D(height, width, Scale, dbn); double *dstcoef = new double[cw.m_msgCL2D.allSize]; cw.WaveDec2(s,dstcoef); for (int i = 0; i < cw.m_msgCL2D.allSize; i++) { cout << dstcoef[i] << " "; if ((i + 1) % 10 == 0) cout << endl; } double *dst = new double[48]; for (int i = 0; i < 48; i++) dst[i] = 0.0; cw.WaveRec2(dstcoef, dst); cout << endl; cout << endl; for (int i = 0; i < 48; i++) { cout << dst[i] << " "; if ((i + 1) % 8 == 0) cout << endl; } cout << endl; delete[] dst; dst = NULL; delete[] dstcoef; dstcoef = NULL; system("pause"); return 0; } ~~~ ### 3,阈值去噪结果: 说明:本测试只是模拟测试,对图像的处理也是一样的(完全一致) ### 硬阈值去噪结果 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65268d94b3.jpg) ### 软阈值去噪结果 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65269069d0.jpg) 实际的图像处理结果为: 源噪声图像为: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526922ef9.jpg) 注意以下是采用:db6,3层分解,软阈值去噪,阈值是在前文提及的阈值基础上缩小2.5倍得到的效果: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d6526965b3f.jpg) 注意以下是采用:db6,3层分解,硬阈值去噪,阈值是在前文提及的阈值基础上缩小2.5倍得到的效果: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-02_56d65269ad366.jpg) 附带上述C++测试验证主函数 ~~~ //二维阈值去噪测试 int main() { system("color 0A"); CWavelet cw; double s[48] = { 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}; int height = 6; int width = 8; for (int j = 0; j < 48; j++) { cout << s[j] << " "; if ((j + 1) % 8 == 0) cout << endl; } cout << endl; int Scale = 3; int dbn = 3; cw.InitDecInfo2D(height, width, Scale, dbn); double *dstcoef = new double[48]; if (!cw.thrDenoise2D(s, dstcoef)) cerr << "Error" << endl; for (int j = 0; j < 48; j++) { cout << dstcoef[j] << " "; if ((j + 1) % 8 == 0) cout << endl; } delete[] dstcoef; dstcoef = NULL; system("pause"); return 0; } ~~~ 附带上述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.6745 thr = 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); ~~~ 注:本博文为[EbowTang](http://my.csdn.net/EbowTang)原创,后续可能继续更新本文。如果转载,请务必复制本条信息! 原文地址:http://blog.csdn.net/ebowtang/article/details/40481539 原作者博客:http://blog.csdn.net/ebowtang # 参考资源: 【1】《数字图像处理》(冈萨雷斯matlab第二版) 【2】http://ivm.sjtu.edu.cn/files/wavelet/第3章wavelet_original.pdf 【3】http://media.cs.tsinghua.edu.cn/~ahz/digitalimageprocess/chapter12/chapt12_ahz.htm#a1 【4】http://wenku.baidu.com/link?url=OYRL2n-cYkZ2J10zaMscZQ-lhR05kysQ_CaB1YM1e_aqr3DakexZRm8rtBYOHlDmxC0cNAtiCopjyog_yOIH1zliUmyz2fKfOzFTAQ1wWj3 【5】《维基百科》 【6】来自中国知网若干论文 【7】小波分析及其应用__孙延奎 【8】杨建国.小波分析及其工程应用[M].北京:机械工业出版社.2005 【9】毛艳辉.小波去噪在语音识别预处理中的应用.上海交通大学硕士学位论文.2010 【10】matlab各种函数说明,及其内部函数实现
';