Voriacare · Edge Medical Imaging

Cardiac-CT Pipeline — Báo cáo trạng thái hệ thống

On-device DICOM analysis trên NVIDIA Jetson AGX Orin · Phase 0 (Ingest) + Phase 1 (EAT)
📅 2026-07-08 🖥 Jetson AGX Orin 32GB · JetPack 6.2 📦 ~/MPWorks/voriacare-edge 🔬 Dataset: ImageCAS ×200

Pipeline phân tích cardiac CT tại chỗ theo thứ tự EAT → CAC → CCTA, chạy hoàn toàn on-device. Nguyên tắc xuyên suốt: ML định vị ROI, phép tính HU deterministic ra con số (model không bao giờ regress ra số — mọi giá trị đều audit được).

Phase 0 · Ingest
Xong
HU verify 200/200
Phase 1 · EAT skeleton
Xong
end-to-end
nnU-Net + TensorRT
Xong
Dice 0.96 · FP16 verified
Low-res retrain
Đang xử lý
kẹt preprocess

1Môi trường thực thi

Thành phầnGiá trịGhi chú
BoardJetson AGX Orin Dev Kit, 32GB unifiedfull-res, không patch-based ở inference
OS / L4TUbuntu 22.04.5 · L4T R36.4.7= JetPack 6.2
CUDA12.6 (nvcc 12.6.68), driver 540.4.0
TensorRT10.3 (host) · 10.4 (container)khớp mục tiêu TRT10
cuDNN / Python9.3 · Python 3.10
Container GPUdustynv/l4t-pytorch:r36.4.0voriacare-l4t:eattorch 2.4.0, CUDA=True, device "Orin"
I/O stack (host venv)pydicom 3.0.2 · SimpleITK 2.5.5 · highdicom 0.27.0wheel aarch64 sẵn — không cần build

2Phase 0 — Ingest & HU verification Xong

Watched-folder worker: gom DICOM theo SeriesInstanceUID, debounce chờ series đủ, dựng volume 3D với HU đúng (SimpleITK/GDCM sort không gian + áp Rescale Slope/Intercept), rồi verify HU.

HU đúng là điều kiện sống còn. Cross-check độc lập: đọc 1 slice bằng pydicom, tự áp RescaleSlope/Intercept, so voxelwise với output SimpleITK → bắt đúng bug "rescale không áp" (stored-value thay vì HU).

Kiểm chứng

  • Phantom tổng hợp: HU range [−1000, 900], oracle diff = 0 HU.
  • Watcher: gom series → debounce → load → verify, chạy thật OK.
  • 200 volume CTA thật: vmin = −1024 đồng nhất, 0 bất thường.

Module

vc_ingest/dicom_loader.py vc_ingest/hu_verify.py vc_ingest/series_watcher.py scripts/verify_hu.py scripts/watch_ingest.py

Còn treo: đường DICOM loader (GDCM) mới validate trên phantom — cần 1 series DICOM thật để đóng Phase 0 đúng nghĩa (ImageCAS là NIfTI nên không chạm đường này).

3Dataset — ImageCAS ×200 (scan toàn bộ)

Volumes
200
0 flagged
HU air floor
−1024
đồng nhất cả 200
Z-spacing
0.5 mm
đồng nhất
In-plane
0.35 mm
median (0.29–0.47)

Matrix 512×512 đồng nhất; slices 206–275. HU-verify vững trên cohort thật. ImageCAS = CTA có cản quang + label lòng vành → hợp cho CCTA/coronary & anatomical prior; không dùng cho CAC (CAC cần CT non-contrast).

4Phase 1 — EAT (Epicardial Adipose Tissue) Skeleton xong

4.1 · Deterministic measure (đã chứng minh chính xác)

Trong ROI pericardium: đếm voxel HU ∈ [−190, −30] × thể tích voxel → EAT (mL) + mean HU. Không có model regress số. Test phantom: chính xác tuyệt đối (1000 voxel × 0.25 mm³ = đúng 0.25 mL; window loại đúng biên).

vc_eat/measure.pyvc_eat/harness.py — Bland-Altman + Dicetests/test_measure_eat.py ✓

4.2 · Localize (ML) — silver bootstrap

ImageCAS không có nhãn EAT/pericardium. Đường bootstrap: TotalSegmentator (whole-heart) → ROI pericardium proxy = convex-hull per-slice + dilation EDT 3mm. Proxy over-inclusive — đây là điểm cần gold label để sửa.

4.3 · Silver batch — 200 ca trên GPU

Cases
200/200
0 lỗi · 4.40h
EAT median
98.2 mL
IQR 78–121
mean HU
−81.5
−98…−72 (đúng mỡ)
ROI / heart
1.62×
hệ số phình proxy

Output: out/eat_200.csv + silver masks masks/<id>/{heart,pericardium_roi}.nii.gz. Batch resumable (ghi CSV tăng dần).

5nnU-Net pericardium — training Xong (fullres)

Train segmenter thay chuỗi hull heuristic. Cấu hình: 3d_fullres, fold 0, trainer 250 epochs, patch (96,160,160).

Held-out Dice
0.9596
40 ca · IoU 0.923
Epoch time
~338 s
~24h tổng
Worst / Best ca
0.896 / 0.978
không ca nào fail
Checkpoint
246 MB
best + final

EAT: ROI model-học vs ROI hull (40 ca held-out)

MetricGiá trịDiễn giải
ROI Dice (model vs hull)0.960model tái tạo trung thực hull
EAT MAE / MAPE6.86 mL / 6.3%sai lệch nhỏ
Bias / Pearson r+6.6 mL / 0.984hơi lớn hơn hull
Hiểu đúng: Dice 0.96 nghĩa là model học lại cái hull proxy — nó không sửa over-inclusion (sai lệch nằm trong silver label). Giá trị: thay TotalSegmentator+hull+EDT bằng 1 forward pass, exportable. EAT đúng lâm sàng vẫn cần gold pericardium label.

6TensorRT FP16 — export & verify 4/4 stage xong

StageKết quả
1 · ONNX exportpericardium.onnx (123 MB) · argmax vs torch 99.996%
2 · TRT FP16 enginepericardium_fp16.plan (63 MB) · build 516s · ~123 ms/patch
3 · Parity trên ca thậtDice(TRT,torch) 0.9997 · EAT Δ < 0.11 mL → FP16 an toàn production
4 · Wire-inrun_eat_trt.py: CT → TRT pericardium → EAT, self-contained
Finding tốc độ (trung thực): đường TRT ~64–137s/ca — KHÔNG nhanh hơn TotalSegmentator+hull (~80s). FP16 tăng tốc patch (123ms) nhưng model 3d_fullres → ~245 patch/volume + resample 512³ mới là nút cổ chai. Fix: retrain low-res (3d_lowres, volume ~8× nhỏ hơn) → inference vài giây/ca thật sự.

7Đang xử lý — Low-res retrain Kẹt preprocess

Mục tiêu: model 3d_lowres (spacing ~0.99×0.69×0.69, volume median 139×259×259) để inference nhanh vài giây/ca. Lưu ý: nnU-Net chạy 250 iter/epoch cố định + patch size như nhau → thời gian train/epoch ≈ fullres; speedup ở inference.

Blocker hiện tại: nnUNetv2_preprocess -c 3d_lowres xử lý 0 ca (folder rỗng) → training vấp infer_dataset_class assertion. Đang tách bước preprocess ra chạy riêng & xác minh ghi file trước khi train lại.

8Gotcha kỹ thuật đã xử lý (war-stories đáng giá cho Jetson)

Vấn đềTriệu chứngFix
SimpleITK dangling viewGetArrayView(ReadImage(...))segfault (view trỏ buffer đã free)giữ ref Image, hoặc GetArrayFromImage (copy)
torch PyPI trên Jetsoncu130 wheel → cuda=False "driver too old"dùng Jetson wheel trong l4t-pytorch
Docker /dev/shmmặc định 64MB → dataloader "No space left on device"--ipc=host
Triton thiếu (aarch64)torch.compile crash "Cannot find triton" sau Epoch 0nnUNet_compile=0
ROI dilation chậmstructuring-element 13×19×19 ngốn 3–4× thời gian/cadistance-transform EDT (O(n), đúng mm)

9Dòng thời gian

10Code & artifacts

vc_ingest/ · vc_eat/

dicom_loader · hu_verify · series_watcher
measure · harness · pericardium

scripts/

verify_hu · watch_ingest · scan_dataset
run_eat_batch · build_nnunet_dataset · train_pericardium.sh
export_onnx · verify_trt · predict_trt · run_eat_trt

Artifacts

nnU-Net checkpoint_best/final.pth (246MB) export/pericardium.onnx (123MB) export/pericardium_fp16.plan (63MB) out/eat_200.csv masks/<id>/*.nii.gz ×200 image: voriacare-l4t:eat

11Giới hạn & bước tiếp

Giới hạn trung thực

  • EAT hiện là proxy over-inclusive (ROI/heart 1.62×) — chưa validate lâm sàng.
  • Model chỉ tái tạo hull, không sửa sai lệch hệ thống.
  • Chưa có ground-truth EAT thật để đo độ đúng.
  • Đường DICOM (GDCM) chưa test trên series DICOM thật.

Ưu tiên tiếp

  • Đang làm: gỡ blocker preprocess → low-res model → inference vài giây/ca.
  • Chất lượng: lấy gold pericardium/EAT (VC-EAT path) để refine + validate.
  • Phase 2 (CAC): cần data CT non-contrast (ImageCAS không dùng được).