【信号处理:小波包转换(WPT)/小波包分解(WPD) 】
- 小波包变换简介
- WPT/WPD的基础知识
- WPT/WPD的主要特点
- The Wavelet Packet Transform 小波包变换
- 前向小波数据包变换
- 最佳基础和成本函数
- 数学中波纹的最佳基础
- 其他成本函数
- 最佳基集的反变换
- 为小波分组变换选择 C++ 而不是 Java
- 天下没有免费的午餐(尤其是在使用 C++ 时)
- C++ 与 MATLAB
- Wavelet Packet Decomposition 小波数据包分解
小波包转换(Wavelet Packet Transformation WPT) 或小波包分解(Wavelet Packet Decomposition WPD) 是信号处理领域的一种高级工具,建立在小波变换的概念之上。本博客旨在介绍WPT/WPD,解释其原理,并提供示例来阐明其应用。
小波包变换简介
小波包变换是小波变换的扩展,小波变换是一种用于信号分层分解的数学技术。传统的小波变换仅分解每个水平的近似系数,而WPT/WPD则分解近似系数和细节系数。
WPT/WPD的基础知识
- 小波和小波变换:小波是一种小波,其能量集中在时间上,用于提供信号的时频表示。小波变换将信号分解为一组基函数,称为小波,由缩放和平移母小波生成。
- 分层分解:在WPT中,信号的细节和近似分量在每个级别都进一步分解,这与标准小波变换不同,标准小波变换只分裂近似分量。
WPT/WPD的主要特点
- 灵活性:与标准小波变换相比,它提供了更灵活的信号分析,因为它提供了更丰富的信号表示。
- 适应性:可以适应信号的特定特征,使其在图像压缩、降噪和特征提取等应用中特别有用。
The Wavelet Packet Transform 小波包变换
小波包变换有许多应用。 其中之一涉及“最佳基础”的计算,这是相对于特定成本函数的数据的最小表示。 “最佳基础”用于包括降噪和数据压缩在内的应用程序。
另外博客还分享了 C++ 代码,这些代码实现了小波包变换、最佳基计算和最佳基集的逆变换。
前向小波数据包变换
小波变换中的一个步骤是计算低通(缩放函数)结果和高通(小波函数)结果。 低通结果是原始信号的更平滑版本(在Haar波浪的情况下为平均值)。 低通结果递归成为下一个小波步长的输入,该小波步长计算另一个低通和高通结果,直到只计算一个低通 ( 2 0 2^0 20) 结果。
小波变换将小波变换步骤应用于低通结果。 小波数据包变换将变换步应用于低通和高通结果。
如图 1 所示,小波包变换可以看作是 atree。 树的根是原始数据集。 树的下一级是小波变换的一个步骤的结果。通过将小波变换步骤递归应用于前一个小波变换步骤的低通和高通滤波器结果来构造树中的后续级别。
图 1 中用于构建小波包树的小波函数是哈尔小波的一个版本,我称之为哈尔经典小波函数。 请注意,这不同于升序版本的哈小波。
博客中对小波包变换的描述以及相关 C++ 代码中的实现都采用树形来表示小波包数据。 在小波文献中,小波包变换的结果有时被称为小波包表。
最佳基础和成本函数
小波基是缩放小波函数不为零的向量范围。 对于 Haar 小波来说,相对于原始数据集,小波基是二的递增幂。 数据后的第一级的基数为 2(平均值和平均差值在两个元素上计算)。 下一级的基数为四,因为用于计算缩放和小波函数的两个元素是原始数据中四个元素的结果。 下一级的基数为 8,以此类推。
小波变换将输入数据集以新的形式表示出来。 但不会丢失任何信息,小波变换的结果可以完美地重建为原始数据。 假定数据不是随机的,小波变换产生的数据表示可能会比原始数据集更紧凑(由更小的值组成)(见 PredictWavelets:被视为压缩的小波)。
从压缩的角度来看,我们需要尽可能多的小数值,标准的小波变换可能不会产生最好的结果,因为它仅限于小波基(基数的复数),而小波基的每一步都是以 2 的幂次递增的。 另一种基的组合可能会产生更理想的结果。
最佳基算法可以找到一组小波基,根据特定的成本函数对数据进行最理想的表示。 成本函数可以根据特定应用来选择。 例如,在压缩算法中,代价函数可能是表示结果所需的比特数。
成本函数的值是实数。 给定两个无限长的向量 a 和 b,我们用[a b]表示它们的连接。 我们需要以下两个属性:
- 对于所有有限长度的向量 a 和 b,成本函数具有可加性,即 K([a b])=K(a)+K(b)。
- K(0) = 0,其中 0 表示零向量
数学中波纹的最佳基础
最简单的成本函数之一是阈值函数,如下所示(用 C/C++ 结合数学符号表示):
图 2 显示了图 1 中小波包树的阈值成本函数值,其中 t= 1(例如,图 2 中的每个数字表示该节点中绝对值大于 1 的值的数量)。
要计算最佳基础,需要遍历该树,并在每个节点上标注其成本值(相对于特定成本函数)。
在构建小波包树时,所有树叶上都有一个标记。 这些 "标记 "在计算最佳基点时会被修改。 最佳基础计算是自下而上进行的(即从树的叶子向树根):
- 叶子(位于树的底部,没有子节点)返回其成本值。
- 当计算沿着树根向上递归时,如果存在非叶子节点,v1 就是该节点的成本值。 值 v2 是该节点子节点的成本值总和。
- 如果(v1 <= v2),那么我们就将该节点标记为最佳基础集的一部分,并删除当前节点子树上节点的任何标记。
- 如果 (v1 > v2),则节点的成本值将被替换为 v2。
图 3 中的阴影节点表示,根据简单阈值函数计算的成本,最佳基础算法选择的节点。
图 4 中的阴影值显示了最佳基算法选择的小波包变换值。 通过自上而下、从左到右遍历小波包变换树,可以获得最佳基础集。 当遇到一个最佳基础节点时,分支上的遍历将停止,该节点将被添加到最佳基础列表中。
最佳基础算法选择的最佳基础集是相对于特定成本函数而言的。 在某些情况下,最佳基集可能与小波变换得到的基集相同(在这种情况下,我们可以使用更简单的算法)。 在其他情况下,最佳基函数可能不会产生与原始数据集不同的结果(例如,原始数据集已经是成本函数的最小表示)。
其他成本函数
文献中提出的一个成本函数是香农熵函数。 数学中的波纹》一书给出了香农熵函数的改进版本:
香农熵函数提供了一种衡量信号代表性经济性的方法。 它通常应用于固定字母表中一个字符的概率。 对于香农熵函数的标准版本如何变成修正版本,《数学涟漪》没有提供我所能理解的解释。
C++ 代码中包含了香农熵代价函数。 如果对小波变换步骤的结果进行归一化处理(在正向变换中除以 2 的平方根),似乎可以获得更好的结果。 归一化用于确保信号的能量在小波变换的每一步都保持不变。
最佳基集的反变换
在这个例子中,最佳基础数据集包含的数值较小,因此是一种更紧凑的表示。 如果将小波包变换和最佳基算法用于数据压缩,那么一定有办法从最佳基数据集恢复原始数据集。 我没有看到任何关于从最佳基集计算反变换的资料,因此这种算法完全是我自己的。
如果将最佳基集表示为一个列表,列表中的每个元素都是小波包树中的节点,我们就可以恢复原始数据。 压缩算法可以用每个节点的元素计数来表示这样一个列表,然后是数据元素。 要使这种方案切实可行,最佳基础算法必须进行足够的压缩,以弥补数据集中插入的元素计数值。
从逻辑上讲,逆计算必须从最佳基表重新生成小波包树。 如图 5 所示。 列表从起点到终点被遍历。 读取包含值{29.6, 22.2} 的第一个节点。 创建一个两倍大的父节点,{29.6, 22.2} 成为左子节点。成为左子节点。 该子树被推入堆栈。 接下来从列表中读取包含 {-4.8} 的节点。 为该节点创建的树(根部有两个元素)比堆栈顶端的节点小,因此创建了一棵新的子树并推入堆栈。 这个节点的根节点有两个元素,左侧子节点是{-4.8}。 列表中的下一个值是{0.18}。 该值成为栈顶(TOS)子树的右侧。 现在在 TOS 子树上计算小波逆变换步骤,得到一个两元素结果。 这个结果现在成为堆栈中下一个子树的右侧子树,这个子树被缩小,产生一个四元素节点。 这个四元素结果成为一棵新的子树的左侧子树,其根为八元素。 计算过程如图 5 B 和 C 所示。
图 5 中概述的算法是在相关 C++ 代码的类中实现的。
为小波分组变换选择 C++ 而不是 Java
Java 提供了与 C++ 相同的类抽象,但语言更小,更易于理解。 由于 Java 是一种较小的语言,因此发布的源代码在不同系统上编译和运行的方式相同的可能性更大。 Java 使用垃圾回收,使程序员不必担心内存恢复问题。
然而,Java 目前缺少两个强大的语言特性:运算符重载和泛型(在 C++ 中,泛型是通过模板实现的)。 运算符重载允许创建容器,从而减少计算正向和反向小波变换时的数据复制量。 模板允许以泛型形式表达小波变换算法,允许将小波变换应用于这些容器。
天下没有免费的午餐(尤其是在使用 C++ 时)
C++ 支持 Java 所缺少的强大语言功能,但这种强大功能是有代价的。 C++ 是一种复杂得多的语言,而且不支持垃圾回收(尽管 C++ 有垃圾回收包)。
在实现小波包变换和最佳基础算法时,我试图实现 "专业级 "软件。在 C++ 中,这意味着必须正确恢复内存,因为不能像在 Java 中那样依赖垃圾回收。 在 C++ 中,对象是使用 new 操作符分配的。 在 C++ 中,使用 new 操作符分配对象,使用 delete 操作符恢复对象。 使用 delete 会遇到一些问题:
- 删除操作符会产生开销(即速度较慢)
- 使用删除功能恢复内存会导致代码更加复杂
- 使用删除功能会导致错误和内存泄漏
使用删除的一种方法是使用内存池。 小波包变换代码就采用了这种方法。 对象从内存池中分配。 当不再需要这些对象时,可以收回整个内存池。 这样就可以使用新对象,而不必担心调用删除。 使用内存池需要重载 new 操作符(参见 C++ 中的重载 New)。
C++ 与 MATLAB
C++ 源代码(包括注释和空白)近 4000 行。 这比发表在《数学涟漪》(Ripples in Mathematics)一书中的小波包变换和最佳基算法的 MATLAB 代码要大得多。 不过,C++ 代码支持更多功能,包括显示小波包树和最佳基集。 核心算法的大小似乎差不多。
不过,MATLAB 和 C++ 之间的权衡至少对我来说是没有意义的。 MATLAB 看起来是个不错的产品。 它可能是最流行的计算机数学软件包。 MATLAB 有各种各样的 “工具箱”,包括统计和小波工具箱。 您甚至可以获得支持 SQL 数据库引用的软件包,并将您的 MATLAB 代码连接到路透社股市数据等数据源。 非常酷! 但是MATLAB正版需要支付比较昂贵的费用,
Wavelet Packet Decomposition 小波数据包分解
您可以使用滤波器组近似离散小波变换 (DWT)。当分解同时应用于近似系数和细节系数时,该操作称为小波包分解。
信号 X(z) 首先由由 G(z) 和
G
01
G_{01}
G01(z) 组成的滤波器组滤波。然后,
G
0
G_0
G0(z) 和
G
1
G_1
G1(z) 的输出被下采样 2 倍。经过一些处理后,修改后的信号被上采样 2 倍,并由另一个由 H(z) 和
H
01
H_{01}
H01(z) 组成的滤波器组滤波。
如果两个滤波器组之间没有进行处理,则H(z)和 H 01 H_{01} H01(z)的输出总和与原始信号X(z)相同,但时间延迟除外。该系统是一个双通道PR滤波器组,其中G(z)和 G 01 G_{01} G01(z)形成分析滤波器组,H(z)和 H 01 H_{01} H01(z)形成合成滤波器组。
传统上,G(z)和H(z)是低通滤波器, G 01 G_{01} G01(z)和 H 10 H_{10} H10(z)是高通滤波器。下标 0 和 1 分别表示低通滤波器和高通滤波器。运算 ↓2 表示信号抽取 2 倍。对信号应用抽取因子可确保两个低通滤波器的输出样本数等于原始输入样本数 X(z)。因此,在分解过程中不会添加冗余信息。有关滤波器的更多信息,请参阅LabVIEW数字滤波器设计工具包文档。
您可以使用双通道PR滤波器组系统,连续分解低通滤波器的输出,如下图所示。
低通滤波器消除信号中的高频波动并保留缓慢的趋势。低通滤波器的输出提供信号的近似值。高通滤波器消除信号中的缓慢趋势并保留高频波动。高通滤波器的输出提供有关信号的详细信息。低通滤波器和高通滤波器的输出分别定义近似系数和细节系数。上图中的符号A和D分别代表近似信息和详细信息。
您还可以将细节系数称为小波系数,因为细节系数近似于信号和小波的内积。
注意 本帮助根据上下文交替使用术语小波系数和细节系数。
LabVIEW小波分析工具使用下标0和1来描述分解路径,其中0表示低通滤波,1表示高通滤波。例如,上图中的 D 2 D_2 D2 表示两个级联滤波操作的输出 - 低通滤波后跟高通滤波。因此,可以用序列01来描述这个分解路径。同样, D L D_L DL表示滤波操作000…1的输出,其中0的总数为L–1。 000…1 的脉冲响应渐近收敛到母小波,000…0 的脉冲响应收敛到小波变换中的缩放函数。
DWT 是可逆的,这意味着您可以使用逆 DWT 从 DWT 系数重建信号。逆 DWT 还通过级联合成滤波器组来使用滤波器组来实现。下图显示了使用滤波器组的逆DWT。
使用WA离散小波变换VI计算一维和二维信号的DWT。使用WA逆离散小波变换VI计算一维和二维信号的逆DWT。使用WA获取离散小波变换系数VI可获取通过WA离散小波变换VI计算的DWT系数,并返回特定系数级别的系数类型,例如近似系数或细节系数。使用WA设置离散小波变换系数VI可设置从WA获取离散小波变换系数VI获得的系数。