1 Mallat算法
離散序列的Mallat算法分解公式如下:
其中,H(n)、G(n)分別表示所選取的小波函數對應的低通和高通濾波器的抽頭系數序列。
從Mallat算法的分解原理可知,分解后的序列就是原序列與濾波器序列的卷積再進行隔點抽取而來。
離散序列的Mallat算法重構公式如下:
其中,h(n)、g(n)分別表示所選取的小波函數對應的低通和高通濾波器的抽頭系數序列。
2 小波變換實現過程(C/C++)
2.1 小波變換結果序列長度
小波的Mallat算法分解后的序列長度由原序列長SoureLen和濾波器長FilterLen決定。從Mallat算法的分解原理可知,分解后的序列就是原序列與濾波器序列的卷積再進行隔點抽取而來。即分解抽取的結果長度為(SoureLen+FilterLen-1)/2。
2.2 獲取濾波器組
對於一些通用的小波函數,簡單起見,可以通過Matlab的wfilters(‘wavename’)獲取4個濾波器;特殊的小波函數需要自行構造獲得。
下面以db1小波函數(Haar小波)為例,其變換與重構濾波器組的結果如下:
//matlab輸入獲取命令 >> [Lo_D,Hi_D,Lo_R,Hi_R] = wfilters('db1') //獲取的結果 Lo_D = 0.7071 0.7071 Hi_D = -0.7071 0.7071 Lo_R = 0.7071 0.7071 Hi_R = 0.7071 -0.7071
2.3 信號邊界延拓
在Mallat算法中,假定輸入序列是無限長的,而實際應用中輸入的信號是有限的采樣序列,這就會出現信號邊界處理問題。對於邊界信號的延拓一般有3種方法,即零延拓、對稱延拓和周期延拓。
3種延拓方法比較情況如下:
對於正交小波變換來說,前兩種延拓方法實現起來比較簡單,但重建時會產生邊界效應,而且分解的層數越多,產生的邊界效應越顯著。零延拓方法給人一種跳躍的感覺。至於對稱性延拓,由於正交小波濾波器一般都是非對稱性的(Haar小波基雖然是正交的,但它是非連續的),重建圖象給人一種錯位的感覺。相比較而言,只有最后一種延拓方式可以得到比較精確的重建結果,它不僅能保證分解與重建正確計算,而且恢復的質量也好。不過,周期性延拓方法雖然是常用的三種方法中比較好的方法,但會導致信號邊緣的非連續性,從而會使得較高頻率(子帶)層的小波系數很大,即使信號本身相當平滑。從信號壓縮的角度看,大的系數是希望避免的。信號的對稱延拓可避免邊緣的非連續性問題。然而,對稱延拓只能和對稱的小波濾波器一起適用。如果降低正交性要求,選擇雙正交小波變換,對稱性延拓不失為一種好的方法。周期延拓可適用於任何小波變換,但可能導致輸入序列邊緣的不連續,使得高頻系數較大。而對稱延拓則避免了輸入序列邊界的不連續,是當前廣泛采用的一種延拓方法。
下式中給出了最常用的對稱延拓表達式。

當原序列長sLEN為偶數時延拓后的序列長為sLEN+2*(filterLEN),而原序列長為奇數時則需要在右端再延拓一個元素。注:在Matlab中默認使用了對稱延拓。
2.4 小波分解
在db1小波函數下,離散序列的Mallat算法分解公式展開如下:

其它的db小波,不再詳述。小波分解C++源碼如下。
/** * @brief 小波變換之分解 * @param sourceData 源數據 * @param dataLen 源數據長度 * @param db 過濾器類型 * @param cA 分解后的近似部分序列-低頻部分 * @param cD 分解后的細節部分序列-高頻部分 * @return * 正常則返回分解后序列的數據長度,錯誤則返回-1 */ int Wavelet::Dwt(double *sourceData, int dataLen, Filter db, double *cA, double *cD) { if(dataLen < 2) return -1; if((NULL == sourceData)||(NULL == cA)||(NULL == cD)) return -1; m_db = db; int filterLen = m_db.length; int n,k,p; int decLen = (dataLen+filterLen-1)/2; double tmp=0; cout<<"decLen="<<decLen<<endl; for(n=0; n<decLen; n++) { cA[n] = 0; cD[n] = 0; for(k=0; k<filterLen; k++) { p = 2*n-k; // 感謝網友onetrain指正,此處由p = 2*n-k+1改為p = 2*n-k,解決小波重構后邊界異常問題--2013.3.29 by hmm // 信號邊沿對稱延拓 if((p<0)&&(p>=-filterLen+1)) tmp = sourceData[-p-1]; else if((p>dataLen-1)&&(p<=dataLen+filterLen-2)) tmp = sourceData[2*dataLen-p-1]; else if((p>=0)&&(p<dataLen-1)) tmp = sourceData[p]; else tmp = 0; // 分解后的近似部分序列-低頻部分 cA[n] += m_db.lowFilterDec[k]*tmp; // 分解后的細節部分序列-高頻部分 cD[n] += m_db.highFilterDec[k]*tmp; } } return decLen; }
2.5 小波重構
/** * @brief 小波變換之重構 * @param cA 分解后的近似部分序列-低頻部分 * @param cD 分解后的細節部分序列-高頻部分 * @param cALength 輸入數據長度 * @param db 過濾器類型 * @param recData 重構后輸出的數據 */ void Wavelet::Idwt(double *cA, double *cD, int cALength, Filter db, double *recData) { if((NULL == cA)||(NULL == cD)||(NULL == recData)) return; m_db = db; int filterLen = m_db.length; int n,k,p; int recLen = 2*cALength-filterLen+1; cout<<"recLen="<<recLen<<endl; for(n=0; n<recLen; n++) { recData[n] = 0; for(k=0; k<cALength; k++) { p = n-2*k+filterLen-1; // 信號重構 if((p>=0)&&(p<filterLen)) { recData[n] += m_db.lowFilterRec[p]*cA[k] + m_db.highFilterRec[p]*cD[k]; //cout<<"recData["<<n<<"]="<<recData[n]<<endl; } } } }
2.6 c++實現結果

3 小波變換實現(MATLAB)
使用matlab小波工具箱實現db4的情況如下。
1、MatlabDB4.m文件內容。
%加載txt數據示例 s=importdata('data2.txt'); %load data2.txt; subplot(521);plot(s); %畫出原始信號的波形圖 title('原始信號'); [cA,cD]=dwt(s,'db4'); %采用db4小波並對信號進行一維離散小波分解。 y=idwt(cA,cD,'db4'); %一維離散小波反變換 subplot(522); plot(cA); %畫出波形圖 title('MATLAB低頻部分dwt-cA'); subplot(523); plot(cD); %畫出波形圖 title('MATLAB高頻部分dwt-cD'); subplot(524); plot(y); %畫出波形圖 title('MATLAB重構idwt');
2、波形圖如下。

4 小結
在此,采用C++實現了dbN系列小波的單層一維離散小波變換,通過對比db4小波變換發現(筆者也同樣對比驗證了db1-db3),C++實現效果和Matlab效果完全一樣。通過這一過程,可以很好地幫助理解小波變換的Mallat算法原理,並將C++代碼快速應用到工程實踐,同時對MATLAB小波工具箱的實現細節也有更深入的理解。相關文獻表明,C++代碼實現的DWT算法比Matlab小波工具箱dwt方法效率更高。
注:對小波變換見識還不深,有問題者,歡迎提出討論交流,對源碼細節感興趣的tx,可以到如下鏈接下載。
http://download.csdn.net/detail/v_hyx/5113111