Files
CADx-4/research-wiki/scratch/2026-10-17-TIC拟合约束文献核查/D-old-sources.md
T
iomgaa 14d6f8e153 docs: 任务书 03 医生核对包;TIC 拟合约束文献核查记录
来源:负责人 2026-10-17 要求把 29 例处理结果整理成医生能看的格式并做成长期入口(表格 CSV、文字 PDF、视频不压缩、不向医生提问、问题清单只写现象);文献核查为负责人要求的质量门依据核对,结论待医生看过后再定。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 05:09:34 -04:00

15 KiB
Raw Blame History

Lognormal 拟合质量门参数:旧项目溯源

路径前缀:OLD = /home/iomgaa/Projects/CADx-4/reference/CADx-4-CAD。旧代码行号指当前 HEAD 的 OLD/app/domain/ceus_roi/tic_fitting.py。

结论

tic_analysis.fitting 里的每一个数,旧项目里都找不到论文出处,也找不到临床方出处,全部是工程侧在第一轮四段视频工程验收时定的占位值。旧 design/0043、0044 明确写了:这些数不进决策记录,要等「代表性开发集」标定后才锁定;而旧项目直到结束也没有做这次标定。两个引入提交(6630872、5d83603)的提交说明只有标题,没有正文。现在 yaml 注释写的「出自旧项目 design/0044」只说明这些数是在 0044 那次提交引入的,0044 正文里一个数都没有。

旧仓库的临床规范抄本(535c387 里的 08-16 重写稿)给多峰判据和主峰判据定了另一组数(0.80 / 3.0 秒),和拟合段的 multi_peak_*、minimum_peak_distance_s 对不上。见第 6 行和「没把握的地方」。

一、逐条溯源表

# 约束 当前值 引入提交 出处原文(文件:行号) 出处类别
1a 峰高上界 = 幅度 × 10 height_upper = max(amplitude*10.0, 1.0) 5d83603(2026-09-18,「收紧双曲线拟合与截图就绪门」,无正文)。v1 6630872 用的是面积上界 amplitude*duration*100(git show 6630872:app/domain/ceus_roi/tic_fitting.py 第 352 行) 旧代码 tic_fitting.py:457,没有注释。0043:154-155「约束至少保证 A > 0、σ > 0……边界的数值不在本文重复,最终进入工程配置并由标定数据决定」 工程侧自定(写死在代码里,没进配置)
1b 峰时刻窗:候选峰 ±candidate_long_window_s(5 s),并且离拟合区间头尾各 boundary_margin_s(0.25 s) 5.0 / 0.25 candidate_long_window_s 是 5d83603 引入的;boundary_margin_s 是 6630872 引入的,5d83603 起才用作峰窗 旧代码 tic_fitting.py:807-814。旧 yaml 注释:candidate_long_window_s: 5.0 # 宏观 bolus 候选,抑制逐帧掩膜与压缩波动(OLD/config/ceus_roi_default.yaml:117)。0044:133-134「峰值必须位于第一遍 bolus 的实际观察区间」只讲原则,没有给数 工程侧自定
1c σ 区间 [0.08, 2.5] 0.08 / 2.5 6630872 OLD/config/ceus_roi_default.yaml:127-128,没有注释。0044:141-142「否决"放宽形状参数上界直到现有样本通过"。贴边解说明参数空间或数据支持有问题」。0049:18-20 记录一次 roi_pan 拟合「sigma 到达配置上限,质量门以 parameter_on_boundary 拒绝」;0049:47「不提高 sigma_max,不为参考区另造一套未标定的拟合阈值」 工程侧自定(0049 自己写了「未标定」)
1d t0 区间:观察到基线时不早于拟合起点;估计基线时可早 estimated_baseline_lookback_s(10 s) 10.0 5d83603 旧代码 tic_fitting.py:815-819。旧 yaml :136 estimated_baseline_lookback_s: 10.0 # 允许 t0 位于视频起点前,但截图不外推。注意:旧 research_config.py:488 里 pydantic 的默认值是 5.0,和 yaml 的 10.0 不一致(yaml 覆盖默认值,所以实际跑的是 10.0)。0044:90-93 讲联合估计基线的道理,没有给数 工程侧自定
1e 基线区间:观察基线 ±max(3×噪声, 5%幅度);估计基线时下界 max(0, P1−0.25×幅度),上界 min(P45, 基线+0.35×幅度);正则尺度 max(0.2×幅度, 噪声) 3.0 / 0.05 / P1 / P45 / 0.25 / 0.35 / 0.2,全写死在代码里 5d83603(amplitude*0.05 最早出现在 6630872,当时用作残差尺度) 旧代码 tic_fitting.py:458-469,没有注释。0044:91-92「估计范围由曲线低分位、局部噪声和显示灰度范围共同约束,并用正则项阻止基线……任意漂移」只讲原则。这里的 3×噪声和临床契约的到达判据 3×σ0 是两回事(契约见 ceus.yaml 的 anchor_frames.arrival),代码里没有任何地方把两者关联起来 工程侧自定
2 parameter_boundary_fraction=0.001:参数贴边即拒绝 0.001 5d83603 旧 yaml :138 # 形状参数贴边判据占允许区间的比例。拒绝这一规则的出处是 0044:138-140「形状参数贴近边界、峰值落在观察边缘时,即使残差门通过也不能成为正式拟合」。0.001 这个数本身找不到出处 规则:工程侧设计(0044)。数值:工程侧自定
3 模型峰不得落在拟合区间头尾(model_peak_on_boundary,余量 0.25 s) 0.25 6630872(v1 已有这一拒绝;5d83603 起改用拟合区间头尾) 旧代码 tic_fitting.py:864-870。0043:206-207 失败语义「峰值落在观测边界」;0043:154「模型峰值落在观测到的有效时间范围」;0044:138-139。0.25 s 的数值找不到出处 规则:工程侧设计(0043/0044)。数值:工程侧自定
4 minimum_r2=0.35、maximum_nrmse=0.45、maximum_residual_noise_ratio=2.0;判据是 nRMSE 必须过,R² 和残差噪声比二者过一个即可 0.35 / 0.45 / 2.0 minimum_r2、maximum_nrmse 是 6630872 引入的;maximum_residual_noise_ratio 是 5d83603 引入的 旧代码 tic_fitting.py:871-876。旧 yaml :133 # 残差不高于基线噪声同量级时允许低 R²。0043:248-250「1.5 s 平滑窗、某个固定 R²、nRMSE 或信噪比门槛都可以作为实验起点,不能在只有一例真实交付时成为正式口径」。0044:168-170「具体数值进入版本化配置并由代表性开发集锁定,不在决策记录中复制」。旧 explanation/tic-quantification-encoding.md:594「Lognormal 的 fit_R2、fit_nRMSE 与残差质量门 | 工程侧占位,代表性开发集标定后固定 | 工程侧」。「二者过一个」的理由见旧 explanation/ceus-roi-pipeline.md:510-512「让低动态范围但残差已落入观测噪声的曲线不因 R² 单项被误杀」。临床规范抄本只要求「fit_R2 与 fit_nRMSE 各自过门槛时记 1」,没有给门槛值(git show 535c387:"research-wiki/scratch/2026-08-16-ceus-tic规范重写稿.md" 第 899 行) 工程侧自定(文档自己写明是占位)。临床方只定了这两个字段,没给数值
5 roi_all 和 roi_perf 两条都成功,才取 D1–D4 — 5d83603(lesion_fit_ready 由它引入) 0044:30-31「项目负责人确认了本轮交付目标:roi_all 与 roi_perf 都要得到可用于取值的 Lognormal 拟合,关键截图由 roi_perf 的连续拟合曲线定位」。0044:53-54「一次病灶截图任务只有在两条拟合都通过后才进入关键时刻提取」。旧代码 OLD/app/domain/ceus_roi/service.py:482、OLD/debugs/tic_lognormal_replay.py:514。本仓库对应位置是 src/cadx/ceus/_pipeline.py:300 和 _anchor_frames.py:234,不在 _tic.py 负责人决定(工程侧 design 记录),不是临床方,也不是论文
6a candidate_window_s=1.5、candidate_polyorder=2、max_candidate_gap_s=0.5、minimum_valid_samples=20、duplicate_mean_absdiff_threshold=0.25、max_nfev=4000、robust_loss=soft_l1 同左 全部是 6630872 旧 yaml :116-130。0043:126-128「阶数固定为 2,因为一阶导数需要表达峰附近的曲率;具体窗口时长必须通过代表性视频标定」。0043:100-101 重复帧阈值「属于待标定参数」。0043:109-110 短缺口「不能在没有批量素材时拍一个固定秒数」。0043:157-158 损失函数「Huber 或 soft_l1……实现前的开发集比较负责在两者中固定一个」,但找不到任何比较记录。旧 encoding.md:595-597 把候选窗、重复帧、短缺口列为「工程侧占位」 工程侧自定(文档自己写明待标定)
6b minimum_peak_distance_s=1.0、minimum_relative_peak_height=0.10 1.0 / 0.10 6630872 旧 yaml :122-123,没有注释。对照临床规范抄本 §7.1:「候选峰的高度要达到全局峰值的 0.80」「峰的突出度要超过"峰高的 0.10 倍"与"基线噪声 σ0 的 3 倍"中较大的那一个」「多个候选彼此要隔开多远才算两个峰,规范给的是 3.0 秒」(535c387 重写稿第 213-219 行)。拟合段的候选检测没有用这组数 工程侧自定,和临床规范 §7.1 的数不一致
6c multi_peak_minimum_separation_s=5.0、multi_peak_minimum_relative_height=0.50、multi_peak_maximum_valley_fraction=0.25 5.0 / 0.50 / 0.25 6630872 旧 yaml :124-126,没有注释。对照临床规范抄本 §15:「第二峰高度达到第一主峰的 0.80、且两峰间距达到 3.0 秒时,记下一条"这条曲线有两个峰"的提示」(535c387 重写稿第 448 行)。旧 encoding.md:578-579 把「多峰判据:第二峰占第一主峰的高度比例 / 两峰最小间距」的取值权威列为《临床口径契约》(临床方原规范 §15)。谷深比 0.25 是工程侧另加的条件,理由见 ceus-roi-pipeline.md:506-507「否则呼吸和 ROI 跟踪造成的周期振荡会被误判成再次推注」 工程侧自定,和临床规范 §15 的数不一致
6d estimated_baseline_percentile=15.0(另有写死的 min(2×15, 45) 分位低值集) 15.0 5d83603 旧 yaml :135 # 无稳定开头时的有界基线先验。旧代码 tic_fitting.py:245-248 工程侧自定
6e maximum_perf_area_step_fraction=0.15 0.15 5d83603 旧 yaml :137。0044:71-73 讲规则(相邻帧去暗区面积比例突变就排除),没有给数 规则:工程侧设计。数值:工程侧自定

旧文档里明确写出「这些数是占位」的总括引文:

  • 0043:268-269「拟合质量门槛、Savitzky-Golay 候选窗口、重复帧阈值、短缺口上限和参数边界只在开发集上确定一次,锁定后用于整个队列」
  • 0043:281-282「参数在更大开发集上锁定前不得描述成临床阈值」
  • 0044:193-194「正式门槛仍需在代表性开发集上评价……」
  • 旧 explanation/tic-quantification-encoding.md:600-603「当前值是首轮工程占位,四段本地 MP4 只验证失败可见性、帧率差异和回放链路,不能代替代表性开发集标定」
  • 旧 explanation/ceus-roi-pipeline.md:727-728「尚未在代表性开发集上锁定,不能视为临床标定」
  • 旧 research_config.py:456-459 docstring「这些值只用于第一轮工程验收,不是临床阈值」
  • 旧 guides/replaying-lognormal-tic.md:167「四例只构成工程冒烟集,不足以锁定候选窗口、重复帧、多峰和质量阈值」

临床方唯一规定过的拟合相关内容:曲线处理方法从四种里选一种(gamma-variate 拟合、lognormal 拟合、Savitzky-Golay、local regression),全队列统一、参数固定(旧 design/0030:288、encoding.md:521;原规范 §3.5);另外列了 fit_QC / fit_R2 / fit_nRMSE 三个字段(原规范 §17)。临床方没有给任何门槛值。

二、旧项目提到过的 TIC 相关文献

全部集中在旧 research-wiki/design/0043-mp4-tic-lognormal-fitting.md §13(289-310 行)。没有一篇被引来支撑具体数值,只用来支撑路线选择。下表年份是我从 URL 和期刊 ID 推的,文件里没有写年份。

作者 / 来源 年份(推断) 被引位置 引来支撑什么
Bracco,VueBox® Instructions for Use(v7.7,VBO.8.20) 2025-06-06(0042:22-23 写明) 0043:291-292;0042:22-25;原件在 OLD/debugs/VueBox说明书.pdf 删重复帧、bolus/replenishment 两种模型、保留拟合信号、输出 QOF。0043:62-63 明说说明书没有公开函数、优化器、初始化、边界和 QOF 公式
Bracco 产品页 VueBox Quantification Toolbox — 0043:293-294 bolus 与 replenishment 用的是专有拟合模型
Cooley 等,nanobubble 微流控(RSC Lab Chip,D3LC00514C) 约 2023 0043:295-297 能从 MP4 灰度矩阵生成 TIC(体外实验,0043:65-66)
Hoyt 等,pixel-by-pixel CEUS perfusion kinetics(PMC3463888) 约 2012 0043:298-300 有损压缩 CEUS 视频可以做半定量时序分析,同时说明压缩带来的局限(0043:68-70)
Gheonea 等,quantitative low-MI CE-EUS,胰腺(PMC3548695) 约 2013 0043:301-303 胰腺 CE-EUS 视频逐帧 ROI 灰度并加参照曲线,作为临床先例(0043:72-74)
Harabis 等,indicator dilution models for UCA bolus(PMID 23348425) 约 2013 0043:304-305 拟合残差最小,不代表参数最接近真值(0043:76-78)
Kuenen 等,multi-model framework(PMC6615468) 约 2019 0043:306-308 Lognormal、Gamma-variate、LDRW 等模型之间的差异,以及逐模型的拟合比较
Fujioka 等,Ceusia-Breast(PMC10466705) 约 2023 0043:309-310 被否决的 Logistic 洗入加指数洗出方案的来源(0043:240-244)

本仓库 reference-pdf/INDEX.md(50 个 PDF)里没有一篇 TIC、Lognormal 或灌注建模方法学论文。2018 年的 EFSUMB 非肝 CEUS 指南只是临床检查规范,也没有被引来支撑拟合参数。旧仓库的 research-wiki/reference/roi-tic-parameters.md 和 glossary.md 里完全没有 Lognormal 拟合的内容。

三、我没把握的地方

  1. 临床规范抄本只到 08-16 重写稿为止。 旧 yaml 里的 anchor_frames 镜像的是《临床口径契约》2026-08-25 版,而契约本身在 Linear 上,旧仓库没有它的全文。我拿来对照的 §7.1 / §15 数值(0.80、3.0 秒)出自 535c387 的 08-16 重写稿。08-25 版或之后有没有改这几个数,我没有核到;要核得去 Linear 读契约原文。拟合段的 multi_peak_* 和 minimum_peak_distance_s 是不是有意偏离临床口径(只用于找候选、不直接当临床多峰判据),旧文档没有写;我在旧文档里只看到 encoding.md:578-579 把多峰判据的权威归到契约。
  2. fit_quality ≥ 85% 的来历查不出。 旧 debugs/TIC关键帧与定量质检指标_计算方式.md:78 和 debugs/TIC指标_LLM输入清单.md:42 都写着「当前质量门不是单一的 R² ≥ 85%」,看上去有人(可能是临床方)提过 85% 这个口径,但旧仓库里找不到最早出处。最早出现在提交 2351e69,提交说明没有正文。VueBox 说明书里 QOF 的单位是 %,我在它的文本里没有找到 85% 这个门槛。
  3. 6630872 和 5d83603 两个提交的说明都只有标题,所以「为什么取 0.35 / 0.45 / 2.0 / 0.001 / ×10」只能从 design 和代码注释里推断。四段工程视频上有没有试过别的取值,旧仓库没有留下记录;我也没有翻 debugs/ 下的日志。
  4. 上表的年份是从 URL 和 ID 推断的,没有逐篇打开核对。
  5. 旧 research_config.py:488 里 estimated_baseline_lookback_s 的默认值 5.0 和 yaml 的 10.0 不一致。按 pydantic 的读法实际生效的是 yaml 的 10.0,但我没有在旧项目里跑代码确认。