用Python和NumPy动手画一画:8种离散正弦变换(DST)的‘基图像’长啥样?
当你在处理图像压缩或信号处理时,可能听说过离散余弦变换(DCT),但它的近亲——离散正弦变换(DST)同样值得关注。今天,我们就用Python和NumPy来亲手绘制DST-I到DST-VIII这8种变换的基图像,看看这些数学公式背后的视觉模式究竟长什么样。
1. 准备工作:理解DST的基本概念
离散正弦变换是信号处理中常用的一类正交变换,与离散余弦变换类似,但使用正弦函数而非余弦函数作为基函数。DST有8种主要变体(I-VIII),每种对应不同的边界条件和对称性。
为什么需要8种不同的DST?这源于信号延拓方式的不同:
- 奇对称延拓:产生正弦函数的基
- 不同的延拓点:导致不同的频率采样方式
- 归一化系数:确保变换的正交性
在开始编码前,我们需要安装必要的Python库:
pip install numpy matplotlib2. 构建DST核函数
每种DST类型都有其独特的变换核。让我们用NumPy实现所有8种类型的核函数:
import numpy as np def dst1_kern(N): """DST-I核函数""" x = np.zeros([N, N]) for k in range(N): x[k, :] = np.sin(np.pi * (k + 1) * (np.arange(N) + 1) / (N + 1)) x *= np.sqrt(2. / (N + 1)) return x def dst2_kern(N): """DST-II核函数""" x = np.zeros([N, N]) for k in range(N): x[k, :] = np.sin(np.pi * (k + 1) * (2 * np.arange(N) + 1) / (2 * N)) x[-1, :] /= np.sqrt(2) x *= np.sqrt(2. / N) return x # 类似地实现DST-III到DST-VIII的核函数 [...其余6种核函数实现...]每种核函数的实现都遵循其数学定义,主要区别在于:
- 正弦函数的参数不同
- 归一化系数不同
- 某些特定行/列需要额外处理
3. 可视化基图像
有了核函数,我们可以生成并可视化基图像。基图像展示了变换如何分解信号:
import matplotlib.pyplot as plt def plot_basis_images(kern, title): N = kern.shape[0] img = kern.T @ kern # 生成所有基图像的组合 plt.figure(figsize=(10, 10)) plt.imshow(img, cmap='gray') plt.title(f'DST-{title} Basis Images (Combined)') plt.colorbar() plt.show() # 单独显示每个基图像 plt.figure(figsize=(15, 15)) for i in range(N): for j in range(N): plt.subplot(N, N, i*N + j + 1) basis_img = kern[[i], :].T @ kern[[j], :] plt.imshow(basis_img, cmap='gray') plt.axis('off') plt.suptitle(f'DST-{title} Individual Basis Images') plt.tight_layout() plt.show()4. 8种DST基图像对比
让我们生成8×8的变换矩阵,并比较不同类型的DST基图像特征:
| DST类型 | 基图像特征 | 主要应用场景 |
|---|---|---|
| DST-I | 边缘值非零,对称性较弱 | 信号边界处理 |
| DST-II | 类似DCT但使用正弦函数 | 图像压缩 |
| DST-III | DST-II的逆变换版本 | 逆变换运算 |
| DST-IV | 双重奇对称性 | 重叠变换 |
| DST-V | 周期边界条件 | 周期性信号 |
| DST-VI | 混合边界条件 | 特殊变换需求 |
| DST-VII | H.266编解码标准采用 | 视频压缩 |
| DST-VIII | 复杂对称模式 | 高级信号处理 |
注意:DST-VII是现代视频编码标准H.266/VVC中采用的变换方式,因其在特定信号类型上的优异能量集中特性。
5. 不同尺寸下的基图像变化
变换尺寸(N)对基图像的影响很大。让我们看看N=4和N=8时的差异:
sizes = [4, 8] dst_types = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII'] for n in sizes: plt.figure(figsize=(20, 10)) for i, dst in enumerate(dst_types): kern = globals()[f'dst{dst.lower()}_kern'](n) combined_img = kern.T @ kern plt.subplot(2, 4, i+1) plt.imshow(combined_img, cmap='gray') plt.title(f'DST-{dst} (N={n})') plt.axis('off') plt.tight_layout() plt.show()随着N增大,基图像表现出:
- 更高频的波动模式
- 更精细的细节表示能力
- 更复杂的交叉模式
6. 在图像压缩中的应用原理
DST基图像代表了信号可以被分解的不同频率分量。在压缩时:
- 将图像分块(如8×8)
- 对每块应用DST
- 保留大幅值系数(通常为低频)
- 丢弃小幅值系数(通常为高频)
def dst_compress(image, dst_type='VII', block_size=8, keep_ratio=0.2): """模拟DST压缩过程""" kern = globals()[f'dst{dst_type.lower()}_kern'](block_size) # 分块处理 compressed = np.zeros_like(image, dtype=float) for i in range(0, image.shape[0], block_size): for j in range(0, image.shape[1], block_size): block = image[i:i+block_size, j:j+block_size] coeffs = kern @ block @ kern.T # 保留前keep_ratio比例的系数 threshold = np.sort(np.abs(coeffs).ravel())[-int(keep_ratio*block_size**2)] coeffs[np.abs(coeffs) < threshold] = 0 # 重构块 compressed[i:i+block_size, j:j+block_size] = kern.T @ coeffs @ kern return compressed7. 实际效果对比
让我们用不同DST类型压缩同一幅图像,比较效果:
from skimage import data image = data.camera().astype(float)/255 dst_types = ['I', 'II', 'IV', 'VII'] plt.figure(figsize=(15, 10)) for i, dst in enumerate(dst_types): compressed = dst_compress(image, dst_type=dst) plt.subplot(2, 2, i+1) plt.imshow(compressed, cmap='gray') plt.title(f'DST-{dst} Compression (PSNR: {peak_signal_noise_ratio(image, compressed):.2f}dB)') plt.axis('off') plt.tight_layout() plt.show()从实验结果可以看到:
- DST-VII通常提供最佳压缩效果
- DST-I因边界处理方式不同,可能产生更多块效应
- DST-IV在某些纹理区域表现优异
8. 进阶:自定义DST可视化工具
为了更深入理解,我们可以创建一个交互式可视化工具:
from ipywidgets import interact, Dropdown, IntSlider @interact def explore_dst( dst_type=Dropdown(options=[(f'DST-{i}', i) for i in ['I','II','III','IV','V','VI','VII','VIII']]), size=IntSlider(min=4, max=16, step=2, value=8), component_x=IntSlider(min=0, max=7, value=0), component_y=IntSlider(min=0, max=7, value=0) ): kern = globals()[f'dst{dst_type.lower()}_kern'](size) basis_img = kern[[component_x], :].T @ kern[[component_y], :] plt.figure(figsize=(8, 4)) plt.subplot(1, 2, 1) plt.imshow(kern.T @ kern, cmap='gray') plt.title(f'All {dst_type} Basis (N={size})') plt.subplot(1, 2, 2) plt.imshow(basis_img, cmap='gray') plt.title(f'Component ({component_x},{component_y})') plt.colorbar() plt.show()这个工具允许你:
- 选择DST类型
- 调整变换尺寸
- 查看特定频率分量对应的基图像
- 直观理解不同基图像对信号的影响