feat(review): 核对包改成医生读得懂;资料库超声一路只留给 agent 的参数
- ceus:灌注指标改为对病灶去暗区(roi_perf)和紫圈(roi_pan)两组算,roi_all 指标删除; D1–D4 仍要 roi_all、roi_perf 两条都过才定位(负责人 2026-10-17:医生要正常区与病灶对比看) - build/library:tic.json 只出两组 11 个指标与 7 个槽位的视频时间;医生截图挪进 ultrasound/keyframes/key.png,UltrasoundRoute 去掉 key_image、keyframes 多一个 key - review:TIC参数.csv 取代 TIC结果.csv;问题清单按原因对照表写成大白话,对照表外的原因报错; 程序原文进 snapshot/问题清单_原文.csv;说明.pdf 整份重写为合作者口吻 - 文档:结构.md、医生核对包、装配两个库、CEUS定量 任务书 04。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -160,15 +160,28 @@ library/<构建号>/
|
||||
manifest.json 构建清单:每个文件的 SHA-256、构建时的 git 提交、配置哈希、所用 CEUS 运行号(没装为 null)
|
||||
cases/<化名>/
|
||||
clinical/clinical.json 临床那一路
|
||||
ultrasound/ 超声那一路:report.txt、key.png;装了 CEUS 结果时另有
|
||||
keyframes/<槽位>.png 和 tic.json
|
||||
ultrasound/ 超声那一路:report.txt、keyframes/key.png;装了 CEUS 结果时
|
||||
keyframes/ 里另有定位到的 <槽位>.png,另有 tic.json
|
||||
mr/report.txt MR 那一路
|
||||
```
|
||||
|
||||
`build.yaml` 的 `ceus_run` 指向某次 CEUS 定量的运行目录时,build 才往超声一路装 CEUS 结果,为 null 时不装。装的时候:
|
||||
超声一路的图都放在 `keyframes/` 一个文件夹里。医生截的关键图 `key.png` 来自交付包、不来自 CEUS,所以每例都有,没装 CEUS 时 `keyframes/` 里只有它一张;去掉文本块、EXIF 等附加数据,只按像素重新编码,不能逐像素保真重编码的整例跳过(C14)。
|
||||
|
||||
- `keyframes/<槽位>.png`:下文关键帧表里定位到的那几张(槽位即 B0、D1 等),和 `key.png` 一样去掉附加数据重新编码;每例最多 8 张图(7 张关键帧加 `key.png`)。
|
||||
- `tic.json`:从这一例的 `summary.json` 里逐字段挑出,此外什么都不写:四条曲线(`roi_all` 病灶整体、`roi_perf` 病灶去暗区、`roi_pan` 紫圈即医生圈的一块正常胰腺、`background_circle` 背景圆)各自的拟合是否成功(`success`)和拟合优度 R²(`r2`);病灶两条曲线的灌注指标(`perfusion_metrics`,拟合没过质量门为 null);紫圈曲线是否可用(`pan_curve_usable`);7 个槽位各自是否定位到(`located`)、定位到的视频时间(`video_time_s`,秒)、没定位的原因(`reason`)。视频号、文件路径、配置、帧号、拟合参数都不写。挑哪些字段只在 `src/cadx/build/_ceus.py` 里列。
|
||||
`build.yaml` 的 `ceus_run` 指向某次 CEUS 定量的运行目录时,build 才往超声一路装 CEUS 结果,为 null 时不装(没有 `tic.json`)。装的时候:
|
||||
|
||||
- `keyframes/<槽位>.png`:下文关键帧表里定位到的那几张(槽位即 B0、D1 等),和 `key.png` 一样去掉附加数据重新编码、过 C14;每例最多 8 张图(7 张关键帧加 `key.png`)。
|
||||
- `tic.json`:只放「给专家的参数」,此外什么都不写:
|
||||
|
||||
```
|
||||
{
|
||||
"perfusion_metrics": {"roi_pan": {11 个指标} 或 null, "roi_perf": {11 个指标} 或 null},
|
||||
"keyframe_times_s": {"B0": 秒或 null, …, "F2": 秒或 null}
|
||||
}
|
||||
```
|
||||
|
||||
`perfusion_metrics` 是两组灌注指标:`roi_pan` 是紫圈(医生在正常胰腺实质上圈的一块),`roi_perf` 是病灶去掉暗区(不进造影剂的囊液部分)后的区域,两组放在一起,是为了让专家把病灶和正常胰腺对着看(负责人 2026-10-17)。每组是下文第 9 步的 11 个指标,键名沿用 `summary.json` 的 `values`(`PE_au`、`TTP_s` …);这一组的拟合没过质量门整组为 null,观测不够算不出的单项为 null,不外推。`keyframe_times_s` 是 7 个槽位定位到的视频时间(秒),没定位到为 null。
|
||||
|
||||
不放:拟合成败和 R²、病灶整体(`roi_all`)和背景圆的曲线、紫圈是否可用、关键帧没定位的原因、指标的信号域等口径标签和辅助时刻(TI、TO、mTT,只是算其他指标的中间量),以及视频号、文件路径、配置、帧号、拟合参数。资料库的定义是「专家能看到什么」,可见范围只在 build 一处决定:多放进去、等会诊那边再挑,就成了两处决定,改了一处忘了另一处就会悄悄漏给专家(任务书 04,负责人 2026-10-17 认可)。挑哪些字段只在 `src/cadx/build/_ceus.py` 里列。
|
||||
- 核对这次 CEUS 运行是在这份交付包上跑的:运行快照里 `ceus.yaml` 的 `paths.delivery_dir` 与 `build.yaml` 的 `delivery_dir` 规范化后不是同一路径;或逐例比,交付包里这段视频文件的 SHA-256 与 `summary.json` 的 `video_sha256` 不同,或交付包的推注时间与 `summary.json` 的 `injection_offset_s` 不同,整次停下报错。这两个字段不进 `tic.json`。
|
||||
- CEUS 在这一例上失败(在 `failed.csv` 里)或运行目录里没有这一例的结果,这一例整例跳过,记 C15。失败的病例要不要改成「超声不可评价、仍参与会诊」等 `../待定问题/03-病例排除标准.md` 定。
|
||||
|
||||
@@ -179,7 +192,7 @@ library/<构建号>/
|
||||
| 性别、年龄 | 看得见 | 看不见 | 看不见 |
|
||||
| 5 个症状、5 项既往史、5 项化验(原值与是否异常) | 看得见 | 看不见 | 看不见 |
|
||||
| 超声报告「所见」段、医生截的关键图 | 看不见 | 看得见 | 看不见 |
|
||||
| CEUS 关键帧、TIC 灌注指标 | 看不见 | 看得见(给哪些指标、几张图由会诊任务书定) | 看不见 |
|
||||
| CEUS 关键帧图与视频时间、紫圈和病灶去暗区两组灌注指标(上文 `tic.json`) | 看不见 | 看得见 | 看不见 |
|
||||
| MR 报告「所见」段 | 看不见 | 看不见 | 看得见 |
|
||||
| 病理诊断、报告「印象 / 结论」段、医生标注工作簿、真实病例号、Excel 行序、姓名 | 看不见 | 看不见 | 看不见 |
|
||||
|
||||
@@ -198,7 +211,23 @@ library/<构建号>/
|
||||
|
||||
### 医生核对包(`runs/review_pack/<运行号>/`)
|
||||
|
||||
把一次构建的两个库和它装入的那次 CEUS 运行,整理成医生不用写代码就能看的一个文件夹(表格 CSV、文字 PDF、图和视频逐字节拷贝),由负责人打包交给临床方核对我们的处理结果。病例目录用医生的病例号,问题清单只写程序观察到的现象;包里有真实病例号、报告原文和诊断,只在临床方与我们之间传递。它不是流水线的一段:不产出任何后续段要读的东西,医生每交一版数据、跑完 CEUS 定量和装配之后出一次。包内布局和每个文件的格式见 `../任务书/03-医生核对包.md` 和包里的 `说明.pdf`,怎么跑见 `../guides/医生核对包.md`。
|
||||
把一次构建的两个库和它装入的那次 CEUS 运行,整理成医生不用写代码就能看的一个文件夹(表格 CSV、文字 PDF、图和视频逐字节拷贝),由负责人打包交给临床方核对我们的处理结果。包里有真实病例号、报告原文和诊断,只在临床方与我们之间传递。它不是流水线的一段:不产出任何后续段要读的东西,医生每交一版数据、跑完 CEUS 定量和装配之后出一次。
|
||||
|
||||
```
|
||||
runs/review_pack/<运行号>/
|
||||
snapshot/ 快照,含程序原文的 问题清单_原文.csv(不给医生)
|
||||
医生核对包_<构建号>/
|
||||
说明.pdf 问题清单.csv 病例总表.csv
|
||||
标准答案/ 答案库 4 张表原样拷贝
|
||||
病例/<病例号>/ 临床信息.csv 超声报告.pdf MR报告.pdf
|
||||
关键帧/医生截图.png、<槽位>_第<秒>秒.png
|
||||
TIC曲线.png TIC参数.csv 回放视频.mp4
|
||||
```
|
||||
|
||||
- 医生看到的超声参数就是 AI 专家看到的那份:`关键帧/医生截图.png` 拷自资料库 `ultrasound/keyframes/key.png`,`TIC参数.csv` 的数值取自资料库 `tic.json`(紫圈、病灶去暗区两组 × 11 个指标)。只有某组整组为空时的「没算出的原因」取自 CEUS 运行的 `summary.json`,这一行只给医生看。
|
||||
- 问题清单是给医生的大白话:程序记录的原因经 review 里的一张对照表(检查编号、CEUS 各步的失败原因、拟合原因码、关键帧未取到的原因、紫圈不可用)换成一句医生能懂的话,同一个原因引起的几条合成一行;对照表里没有的原因直接报错停下。程序原文留在快照里给我们自己追溯。来源:任务书 04,负责人 2026-10-17「合作方是医生,感觉他们也不一定看得懂」。
|
||||
|
||||
每个文件的格式见 `../任务书/03-医生核对包.md`(04 改过的以 `../任务书/04-核对包改成医生读得懂.md` 为准)和包里的 `说明.pdf`,怎么跑见 `../guides/医生核对包.md`。
|
||||
|
||||
### CEUS 定量的产物(`runs/ceus/` 与 `work/`)
|
||||
|
||||
@@ -216,7 +245,7 @@ CEUS 定量按下面的阶段进行(旧项目的阶段划分,迁移时逐模
|
||||
| 6 TIC 采样 | 逐帧取病灶(去不去暗区两种)平均亮度,得到 TIC(时间-强度曲线) | 否 |
|
||||
| 7 拟合 | 用 Lognormal(对数正态)曲线拟合 TIC,过质量门 | 否 |
|
||||
| 8 关键帧 | 按下面的定义挑 7 张 | 否 |
|
||||
| 9 灌注指标 | 从拟合曲线算 11 个指标(峰值强度、达峰时间、洗入速率等,口径仿照商用造影定量软件 VueBOX) | 否 |
|
||||
| 9 灌注指标 | 对病灶去暗区(`roi_perf`)和紫圈(`roi_pan`)两条曲线各算一组 11 个指标(峰值强度、达峰时间、洗入速率等,口径仿照商用造影定量软件 VueBOX),只从过了质量门的拟合曲线算,没过的那组为空。两组是为了让病灶和正常胰腺对着看;病灶整体(`roi_all`)含暗区,不算(负责人 2026-10-17,医生要正常区与病灶对比看) | 否 |
|
||||
|
||||
只有阶段 3 跨运行复用,其余每次重算:阶段 3 要显卡,最贵;其余阶段在 CPU 上算。改了后段参数不必重跑显卡。
|
||||
|
||||
@@ -309,19 +338,19 @@ Python 环境用 uv;安装与检查命令见 `../guides/开发环境.md`。
|
||||
|
||||
| 模块 | 预期行数 | 实际行数 | 旧项目对应部分的行数 |
|
||||
| --- | --- | --- | --- |
|
||||
| ceus | 6000–7500(基本照搬旧算法,去掉只为产品层服务的部分) | 7103 | `ceus_roi/` 除 `service.py` 7330,`video_decoder.py` 135 |
|
||||
| ceus | 6000–7500(基本照搬旧算法,去掉只为产品层服务的部分) | 7106 | `ceus_roi/` 除 `service.py` 7330,`video_decoder.py` 135 |
|
||||
| segment | 150–300 | 300 | `medsam2_local_track.py` 240 |
|
||||
| delivery | 400–700 | 739 | `clinical.py` 266、`modalities.py` 522、`workbook.py` 99、`delivery.py` 134,共 1021,其中入库与数据库部分不迁 |
|
||||
| library | 150–300 | 377 | `input_packages.py` 104 |
|
||||
| answers | 100–200 | 227 | — |
|
||||
| verdicts | 100–200 | — | — |
|
||||
| build | 300–500 | 537(含装入 CEUS 结果的 `_ceus.py`;读 CEUS 运行目录已挪进 runs) | `input_readers.py` 329 的装配部分 |
|
||||
| build | 300–500 | 520(含装入 CEUS 结果的 `_ceus.py`;读 CEUS 运行目录已挪进 runs) | `input_readers.py` 329 的装配部分 |
|
||||
| llm | 200–450 | — | ClinDB `llm/` 415 |
|
||||
| ledger | 200–300 | — | ClinDB `ledger/` 274 |
|
||||
| runs | 100–250 | 201(快照 `_snapshot.py` 98、读 CEUS 运行的 `_ceus_run.py` 82、`__init__.py` 21) | — |
|
||||
| consult | 500–1000(会诊口径定后重估) | — | — |
|
||||
| evaluate | 300–600(评价口径定后重估) | — | 已删的 `core/eval` 622 |
|
||||
| review | 300–500 | 610(`_pack.py` 407、`_pdf.py` 129、`_config.py` 64、`__init__.py` 10;不含说明模板 `readme_template.md` 202 行) | — |
|
||||
| review | 300–500 | 1152(`_pack.py` 447、`_problems.py` 502、`_pdf.py` 129、`_config.py` 64、`__init__.py` 10;不含说明模板 `readme_template.md` 91 行)。超出一倍:任务书 04 要求的原因对照表(`_problems.py`)大半是逐条的原因与医生写法,待负责人确认是否接受 | — |
|
||||
| scripts/ 每个 | ≤ 300 | `build.py` 37、`scan_library.py` 88、`ceus_quant.py` 403(超出,含目视核对表的读取与核对、开跑即写快照)、`ceus_align_review.py` 243、`review_pack.py` 37 | `t1_local_debug.py` 762、`tic_lognormal_replay.py` 627 |
|
||||
|
||||
---
|
||||
@@ -365,7 +394,7 @@ Python 环境用 uv;安装与检查命令见 `../guides/开发环境.md`。
|
||||
- `read_build_info(build_dir)`:读构建清单的概况,交出 `BuildInfo`:构建号、编码列是否解码、装入的 CEUS 运行号(没装为 None)。
|
||||
- `read_clinical(build_dir, alias)`、`read_ultrasound(build_dir, alias)`、`read_mr(build_dir, alias)`:按路读出一例,读时核对 SHA-256,拒绝 `.partial` 目录。
|
||||
- `ClinicalRoute`:临床一路:是否已解码、22 列字段。
|
||||
- `UltrasoundRoute`:超声一路:报告「所见」原文、关键图路径、tic.json 的内容(没装 CEUS 时为 None)、槽位 → 关键帧路径(没装时为空)。
|
||||
- `UltrasoundRoute`:超声一路:报告「所见」原文、tic.json 的内容(没装 CEUS 时为 None)、`keyframes`(`keyframes/` 下的图,文件名主干 → 路径:总有医生截的关键图 `key`,即 `KEY_SLOT`;装了 CEUS 时另有定位到的槽位)。
|
||||
- `MrRoute`:MR 一路:报告「所见」原文。
|
||||
- `scan_for_leaks(build_dir, forbidden_strings, forbidden_keys)`:在构建目录的全部 `.txt`、`.json` 里找禁止的字符串(NFKC 归一、不分大小写、按子串)和禁止的 JSON 键名,返回命中列表。
|
||||
- `LeakHit`:一处命中:文件(相对构建目录)、类别(「字符串」或「键名」)、命中的词。
|
||||
@@ -399,7 +428,7 @@ Python 环境用 uv;安装与检查命令见 `../guides/开发环境.md`。
|
||||
- `register_halves(frames, left_view, right_view, cfg.registration, cfg.prompt_source, cfg.pan_source)`:第 1 步的两半配准,交出 `HalfRegistration`:左半局部坐标 → 右半局部坐标的平移,`correlation`(两半扇形区边缘图的归一化互相关,采用它)、`template`(左半扇形内多块模板逐帧匹配后取中位数,只用来核对)、各次匹配明细、两者各分量之差的最大值;`contrast_to_target(side)` 换成造影那一半 → 另一半的平移。
|
||||
- `SegmentationInput`:视频的 fps、帧数、宽高、逐帧时间戳,分屏线,`registration`(`HalfRegistration`),`offset`(造影那一半局部坐标 + `offset` = `target_view` 局部坐标),`contrast`(`ContrastSide`:哪一半是造影画面、造影视图和分割那一半视图 `target_view`、提示帧),`target_frames()`(重新解码,逐帧交出分割那一半的 BGR 图像;不存整段),`prompt_masks`(提示帧帧号 → 绿框按 `offset` 挪到 `target_view` 局部坐标的掩膜),造影那一半的第一帧。
|
||||
- `quantify_case(prepared, target_masks, injection_offset_s, cfg)`:第 4–9 步。`target_masks` 是分割在 `target_view` 局部坐标上的 `(帧数, 高, 宽)` 布尔数组,`injection_offset_s` 是推注时间 X;分割一帧有效轮廓都没有抛 `NoValidContourError`(第 4 步)。拟合没过质量门不抛错。
|
||||
- `QuantResult`:`trajectory`(`Trajectory`)、`tic`(`TicResult`)、`curves`(`TicCurves`)、推注时间、病灶两条曲线拟合是否都过质量门、`anchors`(7 个槽位的 `AnchorFrameLocation`)、`perfusion`(`roi_all`、`roi_perf` 各自的 `LognormalPerfusionMetrics`,拟合没过为 None)、紫圈检出率。
|
||||
- `QuantResult`:`trajectory`(`Trajectory`)、`tic`(`TicResult`)、`curves`(`TicCurves`)、推注时间、病灶两条曲线拟合是否都过质量门、`anchors`(7 个槽位的 `AnchorFrameLocation`)、`perfusion`(`METRIC_CURVES` 即 `roi_perf`、`roi_pan` 各自的 `LognormalPerfusionMetrics`,拟合没过为 None)、紫圈检出率。
|
||||
- `AnchorFrameLocation`:一个关键帧槽位的帧号、视频时间、推注后时刻、状态(`ok` 或未定位的类别)、未定位的原因、锚点时刻。
|
||||
- 写产物用:`pack_trajectory(trajectory)`(trajectory.npz 字节)、`pack_tic_curves(curves)`(tic_curves.npz 字节)、`render_tic_png(tic, markers=tic_plot_markers(anchors))`(tic.png,标出 D1–D4)、`render_case_overlay(prepared, result)`(overlay.mp4 字节)、`keyframe_images(prepared, anchors)`(定位到的关键帧整幅原图,槽位 → 图像)、`quant_summary(result)`(拟合、灌注指标、紫圈检出率、7 个关键帧定位,可直接写成 JSON);读回用 `unpack_trajectory`、`unpack_tic_curves`。
|
||||
- `fan_mask(std, cfg.registration)`:由一半画面的逐像素时间标准差求扇形区掩膜;`color_marks(frames, prompt_cfg, pan_cfg, dilate)`:任一帧里落在绿框或紫圈颜色范围内的像素。两者配准用,目视核对出图也用。
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
- **配准**:估出左半画面局部坐标到右半画面局部坐标的平移 (dx, dy)(方向固定是左到右,与哪一半是造影画面无关),让两半里同一个解剖位置对上。程序在造影画面上找绿框,挪到灰阶那一半交给分割模型,分割完再把轮廓挪回造影那一半采亮度,挪的就是这个平移。在灰阶那一半分割,是因为造影画面里病灶边界随造影剂进出时明时暗,灰阶画面上边界稳定(沿用旧代码,任务书 02「先认识几个词」)。配准错了不会报错,只会让亮度采在病灶旁边,所以全量运行之前要做一轮**目视核对**(第二节)。
|
||||
- **提示帧**:绿框位置或形状明显变化的那几帧,第 2 步挑出,交给分割模型告诉它「病灶在这里」。
|
||||
- **MedSAM2**:医学图像分割模型,给它几帧提示帧上的病灶框,它把病灶轮廓传播到整段视频的每一帧。要用显卡,是最慢的一步(第 3 步)。怎么装见 `开发环境.md`。
|
||||
- **TIC**(时间-强度曲线):ROI 平均亮度随时间的变化,造影剂进来变亮、洗出变暗。程序用 Lognormal(对数正态)形状的光滑曲线去拟合它,拟合要过一道**质量门**(拟合优度等门槛,见 `ceus.yaml` 的 `tic_analysis.fitting`)才算成功。病灶有两条曲线(整体、去掉暗区),两条都过了质量门才算**病灶拟合成功**,D1–D4 和灌注指标都要它。
|
||||
- **灌注指标**:从拟合曲线算出的 11 个数(峰值强度、达峰时间、洗入速率等),口径仿照商用造影定量软件 VueBOX(`结构.md` 第四部分)。
|
||||
- **TIC**(时间-强度曲线):ROI 平均亮度随时间的变化,造影剂进来变亮、洗出变暗。程序用 Lognormal(对数正态)形状的光滑曲线去拟合它,拟合要过一道**质量门**(拟合优度等门槛,见 `ceus.yaml` 的 `tic_analysis.fitting`)才算成功。病灶有两条曲线(整体、去掉暗区),两条都过了质量门才算**病灶拟合成功**,B0、D1–D4 要它。
|
||||
- **灌注指标**:从拟合曲线算出的 11 个数(峰值强度、达峰时间、洗入速率等),口径仿照商用造影定量软件 VueBOX(`结构.md` 第四部分)。对病灶去暗区(`roi_perf`)和紫圈(`roi_pan`)两条曲线各算一组,哪条的拟合过了质量门就算哪条,没过的那组为 null。
|
||||
- **关键帧与槽位**:按 TIC 挑出的 7 个时刻的画面,每个时刻叫一个槽位:B0(刚开始变亮之前)、D1(开始变亮)、D2(变亮最快)、D3(最亮)、D4(变暗最快)从拟合曲线上取;F1、F2 是推注后第 20、60 秒。详细定义见 `../explanation/结构.md` 第四部分的关键帧表。
|
||||
- **推注时间 X**:超声表的「推注开始时间s」,含义是视频第 0 秒时已经推注了多少秒(不是「第几秒开始推注」),交付时已检查是非负整数。F1 取视频第 20 − X 秒,F2 取第 60 − X 秒。
|
||||
- **第 N 步**:一段视频依次经过的 9 步:1 解码、分屏与两半配准,2 找绿框、选提示帧,3 MedSAM2 分割,4 整理轮廓,5 找暗区,6 TIC 采样,7 拟合,8 关键帧,9 灌注指标(`结构.md` 第四部分的表)。
|
||||
@@ -162,7 +162,7 @@ snapshot/
|
||||
|
||||
几点说明:
|
||||
|
||||
- 病灶拟合没过质量门**不算失败**:这一例照常有结果目录,D1–D4 记为未定位,灌注指标写 null,原因写在 `summary.json`;F1、F2 照常按推注时间取。个别槽位未定位也不算失败。
|
||||
- 病灶拟合没过质量门**不算失败**:这一例照常有结果目录,B0、D1–D4 记为未定位,拟合没过的那条曲线(`roi_perf`、`roi_pan`)的灌注指标写 null,原因写在 `summary.json`;F1、F2 照常按推注时间取。个别槽位未定位也不算失败。
|
||||
- 不进 `failed.csv`、而是整次停下的:配准核对表对不上(第二节末)、MedSAM2 报错(如显存不够、权重找不到)、重新解码时读不到关键帧、配置写错。报错信息在终端和日志里,留下 `.partial`。
|
||||
- 失败的病例构建时按 C15 整例跳过(`装配两个库.md` 第四节);要不要改成「超声不可评价、仍参与会诊」等 `../待定问题/03-病例排除标准.md` 定。
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 医生核对包
|
||||
|
||||
医生核对包是一个文件夹,交给临床方的医生,请他们核对我们对交付资料的处理结果。医生不用写代码就能打开:表格是 CSV,文字是 PDF,图和视频原样拷贝。本文写给出包的人:什么时候跑、怎么跑、产物在哪、怎么交出去、报错了怎么办。为什么做、包里每个文件的格式,见 `../任务书/03-医生核对包.md`;它在代码里的位置见 `../explanation/结构.md` 第三、四部分。
|
||||
医生核对包是一个文件夹,交给临床方的医生,请他们核对我们对交付资料的处理结果。医生不用写代码就能打开:表格是 CSV,文字是 PDF,图和视频原样拷贝。本文写给出包的人:什么时候跑、怎么跑、产物在哪、怎么交出去、报错了怎么办。为什么做、包里每个文件的格式,见 `../任务书/03-医生核对包.md` 和改成医生读得懂的 `../任务书/04-核对包改成医生读得懂.md`(两者不一致处以 04 为准);它在代码里的位置见 `../explanation/结构.md` 第三、四部分。
|
||||
|
||||
## 先认识几个词
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
- **CEUS 定量运行**:跑一次 `scripts/ceus_quant.py`(`CEUS定量.md`),产物在 `runs/ceus/<CEUS运行号>/`。
|
||||
- **`failed.csv`**:这次运行里记各病例在第几步失败、失败原因的表。
|
||||
- **视频号**:每例 CEUS 视频的编号,`U` 加三位病例号(如 `U001`),CEUS 运行的结果目录按它命名。
|
||||
- **TIC、拟合**:TIC(时间-强度曲线)是病灶区域平均亮度随时间的变化。拟合是用一条光滑的模型曲线(Lognormal,对数正态形状)去贴 TIC。拟合要过一道质量门才算成功;没成功时,关键帧 D1–D4 和灌注指标都取不到。
|
||||
- **TIC、拟合**:TIC(时间-强度曲线)是一块区域平均亮度随时间的变化。拟合是用一条光滑的模型曲线(Lognormal,对数正态形状)去贴 TIC。拟合要过一道质量门才算成功;病灶曲线没成功时关键帧 B0、D1–D4 取不到,某条曲线没成功时它那一组灌注指标为空。
|
||||
- **TIC 参数**:从过了质量门的拟合曲线算出的 11 个灌注指标(PE、TTP 等),紫圈(胰腺实质)和病灶去暗区各一组,就是资料库 `tic.json` 里给 AI 专家的那份。
|
||||
- **紫圈**:临床方在视频上画的紫色圈,圈一块正常胰腺作对照,程序也为它画一条 TIC。
|
||||
- **关键帧、槽位**:从视频里挑出的 7 个时刻的画面,每个时刻叫一个槽位。B0 是刚开始变亮之前,D1–D4 是从拟合曲线上取的开始变亮、变亮最快、最亮、变暗最快,F1、F2 是推注造影剂后第 20、60 秒。细节见包里的 `说明.pdf`。
|
||||
- **回放视频**:原视频逐帧叠加程序画的病灶轮廓等线条,医生用它看程序圈得对不对。
|
||||
- **问题清单**:包里的 `问题清单.csv`,列出程序在每个病例上观察到的现象:某例没进资料库、CEUS 在第几步失败、拟合没完成、关键帧没取到、紫圈曲线不可用。每条都抄自某个已有文件里的值,只写现象,不写原因推测和建议。这是负责人 2026-10-17 定的(任务书 03「不做什么」):包只请医生核对事实,推测会把医生的注意力引向我们的猜测。
|
||||
- **问题清单**:包里的 `问题清单.csv`,列出程序在每个病例上观察到的现象:某例没进资料库、造影视频处理失败、曲线没能拟合、关键帧没取到、紫圈不可用。每条都抄自某个已有文件里的值,只写现象,不写原因推测和建议(负责人 2026-10-17,任务书 03「不做什么」):包只请医生核对事实,推测会把医生的注意力引向我们的猜测。
|
||||
- **写成医生能懂的话**:程序记录的原因(检查编号、原因码、英文字段名)经 `src/cadx/review/_problems.py` 里的对照表换成一句大白话,同一个原因引起的几条合成一行(任务书 04)。对照表里没有的原因直接报错停下,不把程序原文给医生;程序原文另存在快照的 `问题清单_原文.csv` 里。
|
||||
- **三种编号**:格式都是 `YYYYMMDD-HHMMSS-<git 短哈希>`,工作区有未提交改动时末尾加 `-dirty`(`装配两个库.md` 第三节「构建号」)。三者各指一样东西,本文用不同的名字区分:
|
||||
- **构建号**:一次构建,即 `library/<构建号>/`、`answers/<构建号>/`;
|
||||
- **CEUS 运行号**:一次 CEUS 定量运行,即 `runs/ceus/<CEUS运行号>/`;
|
||||
@@ -27,15 +29,16 @@
|
||||
|
||||
## 一、什么时候跑
|
||||
|
||||
医生每交一版新数据,按顺序做完:
|
||||
医生每交一版新数据,或者处理规则改了,按顺序做完:
|
||||
|
||||
1. CEUS 定量 `scripts/ceus_quant.py`,得到新的 `runs/ceus/<CEUS运行号>/`;
|
||||
2. 把 `config/build.yaml` 的 `ceus_run` 指向这个目录,跑 `scripts/build.py` 构建两个库,得到新的构建号;
|
||||
3. 再跑本入口出核对包。
|
||||
1. **干净工作区**:先确认 `git status` 没有任何改动(包括未跟踪文件),有就先提交或请改动的人处理。工作区不干净时三个运行号末尾都会带 `-dirty`,记下的提交号复现不出这次结果;
|
||||
2. **ceus_quant**:CEUS 定量 `scripts/ceus_quant.py`,得到新的 `runs/ceus/<CEUS运行号>/`;
|
||||
3. **build**:把 `config/build.yaml` 的 `ceus_run` 指向这个目录,跑 `scripts/build.py` 构建两个库,得到新的构建号;
|
||||
4. **review_pack**:再跑本入口出核对包。
|
||||
|
||||
核对包只读已经构建好的两个库和那次 CEUS 运行,不碰交付包,也不改两个库。所以交付数据或处理规则变了,要先重新构建,再出包。一次构建可以出多次包,每次一个新的出包号。
|
||||
|
||||
没装 CEUS 结果的构建(`build.yaml` 的 `ceus_run` 为 null)也能出包。包里没有关键帧、TIC 曲线、TIC 结果和回放视频,病例总表的 CEUS 各列留空,`说明.pdf` 里写明这次没有 CEUS 结果。
|
||||
没装 CEUS 结果的构建(`build.yaml` 的 `ceus_run` 为 null)也能出包。病例的 `关键帧/` 里只有医生截图,没有程序取的关键帧、TIC 曲线图、TIC 参数和回放视频,病例总表的 CEUS 各列留空,`说明.pdf` 里写明这一版没有造影视频的处理结果。
|
||||
|
||||
## 二、怎么跑
|
||||
|
||||
@@ -87,8 +90,27 @@ runs/review_pack/<出包号>/ (output_root 下)
|
||||
- `snapshot.json`:git 状态、Python 版本、`uv.lock` 哈希、开始时间、配置哈希;
|
||||
- `review_pack.yaml`:配置原文副本;
|
||||
- `review.json`:构建号、CEUS 运行号、pandoc 与 soffice 的版本行;
|
||||
- `问题清单_原文.csv`:问题清单的程序原文(检查编号、原因码、程序记的原因),列同包里的问题清单,给我们自己追溯;
|
||||
- `finished.json`:结束时间、病例数、问题清单行数。
|
||||
- `医生核对包_<构建号>/`:目录布局、每张 CSV 的列和每个文件的来源,见任务书 03「核对包的内容」。也可以直接打开包里的 `说明.pdf`:它是写给医生的完整说明,包括目录结构、每一列的意思、处理流程、关键帧和 TIC 曲线怎么看、编码含义。这里不重抄,免得两处说法不一致。`说明.pdf` 的源文件是 `src/cadx/review/readme_template.md`。
|
||||
- `医生核对包_<构建号>/`:
|
||||
|
||||
```
|
||||
说明.pdf 给医生的说明,源文件是 src/cadx/review/readme_template.md
|
||||
问题清单.csv 病例号、视频号、环节、问题(一句话)
|
||||
病例总表.csv 全部病例一例一行
|
||||
标准答案/ 化名对照.csv、诊断.csv、跳过病例.csv、报告复核句.csv(答案库原样拷贝)
|
||||
病例/<病例号>/ 只有进了资料库的病例才有
|
||||
临床信息.csv 临床一路的字段
|
||||
超声报告.pdf、MR报告.pdf 两路报告的描述段原文
|
||||
关键帧/
|
||||
医生截图.png 资料库 ultrasound/keyframes/key.png
|
||||
<槽位>_第<秒>秒.png 程序定位到的关键帧,秒数取 tic.json 的 keyframe_times_s
|
||||
TIC曲线.png CEUS 运行的 tic.png
|
||||
TIC参数.csv 11 个灌注指标 × 紫圈、病灶去暗区两组,数值取自资料库 tic.json
|
||||
回放视频.mp4 CEUS 运行的 overlay.mp4
|
||||
```
|
||||
|
||||
CSV 都是带 BOM 的 UTF-8、中文列名、缺失为空。`TIC参数.csv` 的时间类指标保留 1 位小数、其余 2 位;某一组整组为空时最后加一行「没算出的原因」,原因从 CEUS 运行的 `summary.json` 读,经对照表写成医生的话(资料库里已经没有原因,AI 专家拿到的只是 null)。图片和视频逐字节拷贝。每列的意思和怎么看写在 `说明.pdf` 里,这里不重抄,免得两处说法不一致。
|
||||
|
||||
运行目录先写成 `<出包号>.partial/`,全部写完再改名成 `<出包号>/`。看到 `.partial`,说明那次出包中途出错。它不会被自动删除,留着查原因,查完由人决定删不删。已有的运行目录不删、不覆盖(`AGENTS.md`「两档标准」)。
|
||||
|
||||
@@ -120,4 +142,7 @@ runs/review_pack/<出包号>/ (output_root 下)
|
||||
| `… 的 ceus_run 是 …,与资料库记的 CEUS 运行号 … 不符` | 答案库快照里 `build.yaml` 的 `ceus_run`(路径)与资料库 `manifest.json` 的 `ceus_source`(CEUS 运行号)对不上 | 两个库不是同一次构建写出的,检查配置是否指错 |
|
||||
| `CEUS 运行目录不存在` / `是没跑完的 .partial` / `缺快照` | 构建所用的 CEUS 运行目录被移走或删了 | CEUS 运行目录只追加不删;找回原目录 |
|
||||
| `CEUS 运行 failed.csv 里的视频号 … 对不上这次构建的任何病例` | 那次 CEUS 运行里失败的病例不在这次构建里,既没进库,也不在跳过清单上 | 检查 `build.yaml` 的 `ceus_run` 与 `delivery_dir`(交付包目录)是否配套,即那次 CEUS 运行是否在这份交付包上跑的 |
|
||||
| `tic.json 里有 TIC结果.csv 没有中文名的字段` | build 往 `tic.json`(资料库里每例的 CEUS 结果摘要)里加了新字段,核对包还没给它起中文名 | 在 `src/cadx/review/_pack.py` 的 `TIC_ITEMS` 里补上中文名,同时补说明模板 |
|
||||
| `tic.json 的字段是 …,核对包只认得 …` / `灌注指标分组是 …` / `灌注指标是 …` | build 改了 `tic.json`(资料库里每例给 AI 专家的 CEUS 参数)的字段,核对包还没跟上 | 改 `src/cadx/review/_pack.py` 的 `TIC_GROUPS`、`TIC_METRICS`,同时补说明模板 |
|
||||
| `… 不在对照表里:…` | 程序记录了一种新的原因(新的检查编号、新的失败原因、新的原因码、新的列),对照表里还没有给医生的写法 | 在 `src/cadx/review/_problems.py` 的对照表里补一句医生能懂的话,并在 `tests/test_review_reasons.py` 补一条手写预期 |
|
||||
| `给医生的话里出现了程序用语「…」` | 某句给医生的话里带出了检查编号、字段名、下划线、括号或运行号(多半是交付资料里的取值本身带下划线等字符) | 看是哪一句:对照表写法的问题就改写法;是交付取值带出来的,问负责人怎么写 |
|
||||
| `病灶曲线拟合失败 … 与因此未取到的关键帧 … 对不上` | CEUS 运行的 `summary.json` 里拟合结果和关键帧状态互相矛盾 | CEUS 运行本身有问题,查那次运行,不要改核对包绕过 |
|
||||
|
||||
@@ -94,7 +94,7 @@ manifest.json
|
||||
cases/<化名>/
|
||||
clinical/clinical.json
|
||||
ultrasound/report.txt
|
||||
ultrasound/key.png
|
||||
ultrasound/keyframes/key.png
|
||||
ultrasound/keyframes/<槽位>.png (ceus_run 不为 null 时;只有定位到的槽位)
|
||||
ultrasound/tic.json (ceus_run 不为 null 时)
|
||||
mr/report.txt
|
||||
@@ -105,9 +105,9 @@ cases/<化名>/
|
||||
| `manifest.json` | 构建号、git 提交号、工作区是否干净、配置文件(解码时含映射表)的 SHA-256、`codes_decoded`、`uncompared`(没比对的项)、`ceus_source`(所用 CEUS 运行号,`ceus_run` 为 null 时是 null)、化名列表(按化名排序)、每个文件的相对路径和 SHA-256。不含真实病例号、交付文件名和诊断 |
|
||||
| `clinical.json` | `{"codes_decoded": ..., "fields": {列名: 值}}`。字段是临床表去掉编号和诊断后的 22 列:性别、年龄、5 个症状、5 项既往史、5 项化验的原值和档位。没测的化验写 null |
|
||||
| `ultrasound/report.txt` | 超声报告的「所见」原文,UTF-8,不加标题 |
|
||||
| `ultrasound/key.png` | 医生截的超声关键图,只保留像素和颜色模式重新编码,不带文本块、EXIF、ICC 等附加数据(附加数据里可能有文件名、时间等线索)。超声帧上烧录的信息栏不遮蔽(`AGENTS.md` 硬性约束 3) |
|
||||
| `ultrasound/keyframes/key.png` | 医生截的超声关键图(来自交付包,每例都有,和关键帧放在同一个文件夹),只保留像素和颜色模式重新编码,不带文本块、EXIF、ICC 等附加数据(附加数据里可能有文件名、时间等线索)。超声帧上烧录的信息栏不遮蔽(`AGENTS.md` 硬性约束 3) |
|
||||
| `ultrasound/keyframes/<槽位>.png` | CEUS 运行里定位到的关键帧整幅画面,和 `key.png` 一样去掉附加数据重新编码 |
|
||||
| `ultrasound/tic.json` | 四条 TIC 曲线的拟合是否成功和 R²、病灶两条曲线的灌注指标、紫圈曲线是否可用、7 个槽位是否定位到及视频时间或未定位原因。字段逐个从 CEUS 的 `summary.json` 挑出,不含视频号、路径、配置、帧号(`结构.md` 第四部分) |
|
||||
| `ultrasound/tic.json` | 只有两项:紫圈(`roi_pan`)和病灶去暗区(`roi_perf`)两组各 11 个灌注指标(拟合没过整组为 null,单项算不出为 null),7 个槽位定位到的视频时间(秒,没定位到为 null)。字段逐个从 CEUS 的 `summary.json` 挑出,拟合成败、R²、没定位的原因、视频号、路径、配置、帧号都不进(`结构.md` 第四部分) |
|
||||
| `mr/report.txt` | MR 报告的「所见」原文 |
|
||||
|
||||
性别、年龄只在临床一路:两份报告表里医院也复制了这两列,但影像两路先看到会被人口学先验带偏(`结构.md` 第四部分)。
|
||||
|
||||
Reference in New Issue
Block a user