如何使用Python基于手部关键点数组值绘制各关键点频谱图
实现结论
完全可以基于Python实现手部关键点数组的多频谱整合展示,用常规科学计算栈就可以完成,不需要特殊定制工具。
依赖安装
先装好需要的库,执行以下命令即可:pip install numpy matplotlib scipy
各库作用:
numpy:做快速傅里叶变换,计算频谱核心数据matplotlib:完成多子图/单图多曲线的频谱渲染、布局调整scipy:可选,用来做关键点序列的去噪、加窗预处理,解决关键点抖动带来的频谱杂波问题
分步实现指引
1. 数据格式整理
先把你从姿态模型拿到的关键点数组规整成标准结构:
- 最终数组形状建议为
(总采样帧数, 关键点总数, 坐标维度数),坐标维度一般是2(平面x/y坐标)或3(空间x/y/z坐标) - 单独提取每个关键点对应维度的一维时序序列即可用于频谱计算,比如要分析所有关键点x方向的运动频谱,就取所有帧对应关键点的x值组成长度等于总帧数的一维数组。
- 可选预处理:如果关键点序列存在检测抖动、跳变,先用滑动平均做平滑;计算频谱前给序列加汉明窗,减少傅里叶变换的频谱泄漏问题。
2. 单关键点频谱计算
对每个关键点的一维时序序列,用快速傅里叶变换得到频率和对应振幅值,可直接复用以下工具函数:
注意不要直接使用全量FFT结果绘图,负频率部分是算法产生的共轭对称冗余数据,仅保留正频率段即可
import numpy as np def get_spectrum(time_seq, sample_fps): """ time_seq: 单个关键点的单维度时序数组 sample_fps: 提取关键点时的源视频帧率,即采样频率 返回: 频率轴数组、对应振幅数组 """ seq_len = len(time_seq) # 加汉明窗抑制频谱泄漏 window = np.hamming(seq_len) fft_res = np.fft.fft(time_seq * window) # 截取正频率部分 freq_axis = np.fft.fftfreq(seq_len, d=1/sample_fps)[:seq_len//2] amplitude = 2.0 / seq_len * np.abs(fft_res[:seq_len//2]) return freq_axis, amplitude
3. 多频谱整合展示
根据需要的效果选对应布局即可:
网格分图布局(单关键点单独小图展示)
按手部关键点的排布逻辑做网格子图,每个子图画对应关键点的频谱,示例代码如下(以常规21个手部关键点为例,排7行3列网格):
import matplotlib.pyplot as plt # 配置参数 SAMPLE_FPS = 30 # 替换成你自己的视频帧率 KEYPOINT_NUM = 21 # 替换成你自己的关键点总数 # 这里替换成你自己规整好的关键点数组 # kp_seq形状: (总帧数, KEYPOINT_NUM, 坐标维度) kp_seq = np.load("your_hand_keypoints.npy") # 建7行3列的子图网格,共享xy轴刻度避免重复标注 fig, axes = plt.subplots(nrows=7, ncols=3, figsize=(12, 16), sharex=True, sharey=True) axes = axes.flatten() for kp_id in range(KEYPOINT_NUM): # 示例取x维度序列,要分析y/z维度就改最后一维的索引 single_seq = kp_seq[:, kp_id, 0] freq, amp = get_spectrum(single_seq, SAMPLE_FPS) current_ax = axes[kp_id] current_ax.plot(freq, amp, linewidth=0.8) current_ax.set_title(f"关键点 {kp_id}", fontsize=9) current_ax.grid(alpha=0.3) # 加整图公共标签 fig.supxlabel("频率 (Hz)") fig.supylabel("振幅") plt.tight_layout() plt.show()
单轴堆叠布局(所有频谱同图展示)
不需要建多子图,在同一个坐标轴上循环绘制所有关键点的频谱曲线,用不同颜色区分、加图例即可,核心逻辑:
fig, ax = plt.subplots(figsize=(12, 6)) for kp_id in range(KEYPOINT_NUM): single_seq = kp_seq[:, kp_id, 0] freq, amp = get_spectrum(single_seq, SAMPLE_FPS) ax.plot(freq, amp, label=f"关键点{kp_id}", linewidth=1) ax.set_xlabel("频率 (Hz)") ax.set_ylabel("振幅") ax.legend(ncol=3, fontsize=8) ax.grid(alpha=0.3) plt.tight_layout() plt.show()
效果优化技巧
- 按手部生理分组给曲线配色:比如拇指、食指、中指、无名指、小指、掌根组分别用不同色系,辨识度更高
- 如果不同关键点的振幅差异过大,可对每个关键点的振幅做0-1归一化,避免大振幅曲线压缩小振幅曲线的展示空间
- 如果需要动态实时展示频谱,可接入
matplotlib.animation.FuncAnimation,每次取固定长度的滑动窗口序列计算频谱更新图表即可
内容的提问来源于stack exchange,提问作者blake banker
相关产品推荐
相关产品推荐

