实验性功能新增「差异放大」,并修正「构建为新信号」按钮的间距
1. 新增「实验性功能 → 差异放大」(与其它实验功能一样是带勾选的开关)
- 用途:纹波类信号某个周期(甚至半个周期)的 M 形下凹明显偏弱时,
把这段下凹按比例拉回周边振幅,恢复正常周期的对比度,便于看出波形异常。
- 算法(全 O(n),不额外占用与信号长度成正比以外的内存):
以半径约半个纹波周期的滑动平均作为纹波中心线,用邻域内「交流分量的最大值」
作为局部纹波振幅,取全信号振幅的 75 分位作为目标振幅,
逐点增益 = 目标振幅 / 局部振幅(夹在 1 到放大倍数之间),
输出 = 中心线 + 增益 × 交流分量,再夹在周边包络内。
- 关键性质(均已用合成正弦逐点验证):
* 增益下界恒为 1,因此**只有被判定为"偏弱"的采样点会改变**,
其余采样点输出与原值逐位相同 —— 实测逐周期表格中只有一个周期发生变化;
* 放大结果夹在周边包络内,因此**任何一点都不超出原有的振幅范围**,
整体值域与其它周期的峰峰值都不变;
* 端点自动收敛到不放大,避免记录首尾被整片抬升。
- 参数「窗口点数 X」(窗口长度,建议略大于纹波周期的一半)与
「放大倍数 N」(增益上限)都可在浮动面板内按信号编辑。
2. 浮动面板
- 与「均值滤波」完全同一套排版:自上而下逐信号条目,
第一排信号名与复选框、第二排两个参数输入框;取消勾选即不显示该信号的参数与预览曲线;
启用时默认只勾选网格内第一个可见信号。
- 新增「构建为新信号」按钮,按各信号自己的参数把结果插入到源信号下方,
与「均值滤波」的构建机制完全一致(统一走同一套请求与插入流程)。
3. 光标交互
- 纵向光标额外标注差异放大曲线在该时刻的取值,配色与原始信号一致,
数值尾部括号内容为「差异化」,双光标时同样带增量。
4. 修复「构建为新信号」按钮与面板下边界的间距
- 原按钮行直接贴着面板下边框,与面板左右内边距及内容间距都不协调;
现在按钮上方与下方各保留一个与左右一致的内边距(均值滤波与差异放大同时生效)。
测试:离屏验证套件新增两组检查(合成正弦上算法只改动一个周期、放大不越界、
参数上限生效、面板排版与按钮间距;再写一份合成纹波文件真正加载渲染并核对曲线数值),
合计 289 项检查全部通过;验证过程不显示窗口、不移动鼠标键盘、不抓屏。
This commit is contained in:
1 parent
8801c8f019
commit
d4206a8d67
6 files changed
+1188
-185
No files matched your search
@@ -373,6 +373,15 @@ namespace jdi
|
||||
/** 中心轴边沿计数的平滑参数:与均值滤波共用同一结构与同一套语义。 */
|
||||
using EdgeCenterItem = MeanFilterItem;
|
||||
|
||||
/**
|
||||
* @brief 「差异放大」中某个信号自己的参数。
|
||||
*
|
||||
* 字段含义与均值滤波/中心轴不同(但同样是「一个信号 + 两个整数参数」,
|
||||
* 因此复用同一结构以共享面板、缓存与引用重映射的实现):
|
||||
* `points` = 窗口点数 X(趋势窗口半径),`order` = 放大倍数 N。
|
||||
*/
|
||||
using DiffAmpItem = MeanFilterItem;
|
||||
|
||||
std::wstring name; /**< 网格名称。 */
|
||||
std::vector<SignalRef> signals; /**< 该网格包含的信号(按加入顺序)。 */
|
||||
bool autoRange = true; /**< 是否依据信号取值范围自动计算 Y 轴。 */
|
||||
@@ -490,6 +499,48 @@ namespace jdi
|
||||
|
||||
/** 均值滤波面板左上角纵坐标(<0 表示自动定位)。 */
|
||||
double meanFilterPanelY = -1.0;
|
||||
|
||||
/**
|
||||
* @brief 「实验性功能 → 差异放大」开关。
|
||||
*
|
||||
* 开启后为本网格内**被勾选**的信号额外绘制一条「差异放大」曲线:
|
||||
* 把信号与局部趋势线的差值按“局部差异强度”归一化后放大,
|
||||
* 使不明显的下凹(如纹波里的弱 M 形)变明显,而差异已经很大的区域**逐点不变**。
|
||||
* 这条曲线只是叠加显示,不会作为信号加入左侧列表。
|
||||
*/
|
||||
bool diffAmp = false;
|
||||
|
||||
/**
|
||||
* @brief 参与差异放大预览的信号及其参数(只包含被勾选的信号)。
|
||||
*
|
||||
* `points` = 窗口点数 X(局部差异强度统计半径,约取纹波周期的一半),
|
||||
* `order` = 放大倍数 N(最大放大倍数)。
|
||||
*/
|
||||
std::vector<DiffAmpItem> diffAmpItems;
|
||||
|
||||
/** 取某个信号的差异放大参数(未勾选时返回空指针)。 */
|
||||
const DiffAmpItem* FindDiffAmpItem(const SignalRef& ref) const;
|
||||
|
||||
/** 取得或创建某信号的差异放大参数(用于勾选)。 */
|
||||
DiffAmpItem& EnsureDiffAmpItem(const SignalRef& ref);
|
||||
|
||||
/** 取消某信号的差异放大勾选。 */
|
||||
void RemoveDiffAmpItem(const SignalRef& ref);
|
||||
|
||||
/** 取某信号的差异放大勾选状态。 */
|
||||
bool DiffAmpEnabled(const SignalRef& ref) const
|
||||
{
|
||||
return FindDiffAmpItem(ref) != nullptr;
|
||||
}
|
||||
|
||||
/** 差异放大浮动面板是否展开。 */
|
||||
bool diffAmpPanelOpen = true;
|
||||
|
||||
/** 差异放大面板左上角位置(客户区像素;<0 表示自动定位)。 */
|
||||
double diffAmpPanelX = -1.0;
|
||||
|
||||
/** 差异放大面板左上角纵坐标(<0 表示自动定位)。 */
|
||||
double diffAmpPanelY = -1.0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -519,6 +570,45 @@ namespace jdi
|
||||
*/
|
||||
void ComputeMovingAverage(const std::vector<double>& values, int radius, std::vector<float>& out);
|
||||
|
||||
/**
|
||||
* @brief 计算 N 点滑动平均(float 输入版本)。
|
||||
*
|
||||
* 与 `ComputeMovingAverage(const std::vector<double>&, ...)` 完全同一实现,
|
||||
* 供「差异放大」在 float 缓冲上二次滑动平均使用:亿级采样下若用 double 中转
|
||||
* 会多占一倍内存,而曲线本身只要求 float 精度。
|
||||
*
|
||||
* @param [IN] values 原始采样值。
|
||||
* @param [IN] radius 平滑半径 N(0 表示原样返回)。
|
||||
* @param [OUT] out 输出数组(长度与 values 相同)。
|
||||
*/
|
||||
void ComputeMovingAverage(const std::vector<float>& values, int radius, std::vector<float>& out);
|
||||
|
||||
/**
|
||||
* @brief 「差异放大」:把不明显的局部差异放大,而差异已经很大的区域逐点不变。
|
||||
*
|
||||
* 算法(三部分,全部 O(n)):
|
||||
* 1. **趋势线** `trend` = 以 8×window 为半径的滑动平均 —— 把“整体”(低频成分)分离出来;
|
||||
* 该半径必须远大于纹波周期,趋势线才不带着纹波。
|
||||
* 2. **局部差异强度** `strength` = |采样值 − 趋势线| 的滑动平均(半径 = window)
|
||||
* —— 衡量该处“纹波有多明显”。窗口恰好覆盖一个纹波周期时该值与相位无关
|
||||
* (正弦为 2A/π),因此 `window` 应取**纹波周期的一半左右**;
|
||||
* 3. **逐点增益** `gain = clamp(目标强度 / 该处强度, 1, 放大倍数)`,
|
||||
* 目标强度取全信号差异强度的 75 分位(用直方图统计,O(1) 额外内存;
|
||||
* 用均值会被个别尖峰抬高,用最大值则完全被最尖处主导)。
|
||||
*
|
||||
* 输出 `out = trend + gain × (采样值 − trend)`。
|
||||
* **增益下界为 1**,所以差异强度不低于目标强度的采样点(即绝大多数“正常”数据点)
|
||||
* 输出与原值逐位相同 —— 这正是“不影响其它数据点的振幅范围”的实现方式;
|
||||
* 只有强度低于目标强度的区域(如被削弱的那个周期)才被放大,最多放大到 `gain` 倍。
|
||||
*
|
||||
* @param [IN] values 原始采样值。
|
||||
* @param [IN] window 窗口点数 X(局部差异强度统计半径,应取纹波周期的一半左右)。
|
||||
* @param [IN] gain 放大倍数 N(最大增益,至少 1)。
|
||||
* @param [OUT] out 输出数组(长度与 values 相同;原值为 NaN 处保持 NaN)。
|
||||
*/
|
||||
void ComputeDiffAmplify(const std::vector<double>& values, int window, int gain,
|
||||
std::vector<float>& out);
|
||||
|
||||
/**
|
||||
* @brief 计算多阶(迭代)滑动平均:把 N 点滑动平均重复应用 `order` 次。
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user