CBT_project/imaging/resample.py
Xiao Furen 523ec7ee16 feat(core): implement vertebral body rewards and enhanced segmentation logic
Introduces a new scoring component for vertebral body (VBODY) rewards
to improve optimization accuracy. The update includes:

- Added `vbody_tensor` to `OptimizationContext` and scoring functions
  to reward screw placement within the vertebral body.
- Enhanced `segment_spinous_process` with diagnostic capabilities to
  detect spinous process absence (e.g., post-laminectomy).
- Improved `resample_img` to prevent physical boundary clipping and
  handle interpolation more robustly for CT and label data.
- Implemented a metadata cache using TinyDB in the preprocessing
  pipeline to skip low-resolution or insufficient scans efficiently.
- Added robust error handling for NFS-based file operations and
  directory creation.
- Added new visualization tools for bone figures and level plotting.

refactor(imaging): improve segmentation and resampling precision

- Refactored `seg_bone` to support original resolution binary masks and
  Signed Maurer Distance Maps (SMD) for more accurate boundary handling.
- Updated `resample_img` to use `ceil` for output size calculation to
  ensure full physical coverage.
- Optimized `process_single_image` to utilize metadata for skipping
  processing of invalid or low-quality scans.
2026-09-05 04:30:10 +08:00

50 lines
No EOL
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import numpy as np
import SimpleITK as sitk
from config.constant import LABEL_MAP
def resample_img(sitk_image, out_spacing=[0.5, 0.5, 0.5], is_label=False,
interpolator=None, cval=None):
"""重採樣到 out_spacing完整保留物理範圍邊界 margin 不裁剪):
- CTis_label=False线性插值。本流程是上採樣~0.75-1mm -> 0.5mm
线性即無漣波的理想重建B-spline 會在身體 margin 等強邊界產生
非物理的 undershoot/overshoot量測~0.5% 體素低於 air floor
-1024 HU最低 -1274沿體輪廓形成黑暈
- 預設填充值 = 影像最小值air而非 GetPixelIDValue()
(對 int16 CT 回傳佔位值 2 ≈ 軟組織,會把 margin 填成軟組織)。
- 輸出尺寸用 ceil 覆蓋原物理尺寸round 會讓遠端端點被裁 ≤0.25mm)。
labelis_label=True最近邻、填充 0。
interpolator明確指定插值器None = 依 is_label 取 Linear / NearestNeighbor
例如 SMD 等 piecewise-linear 場用 sitk.sitkBSpline三阶 B 样条对分段
线性场为精确重建、無漣波)。
cval明確指定填充值None = 上述預設)。
"""
original_spacing = np.array(sitk_image.GetSpacing(), dtype=float)
original_size = np.array(sitk_image.GetSize())
out_spacing = np.array(out_spacing, dtype=float)
physical = original_size * original_spacing
out_size = [max(1, int(np.ceil(physical[i] / out_spacing[i] - 1e-6)))
for i in range(3)]
resample = sitk.ResampleImageFilter()
resample.SetOutputSpacing(out_spacing.tolist())
resample.SetSize(out_size)
resample.SetOutputDirection(sitk_image.GetDirection())
resample.SetOutputOrigin(sitk_image.GetOrigin())
resample.SetTransform(sitk.Transform())
if is_label:
resample.SetInterpolator(interpolator or sitk.sitkNearestNeighbor)
resample.SetDefaultPixelValue(cval if cval is not None else 0)
elif interpolator is not None:
resample.SetInterpolator(interpolator)
# SMD 等 signed 場的填充:預設 0 = 表面層值(比影像 min/max 安全,
# 不會製造假的零穿越環);可用品值可用 cval 覆蓋
resample.SetDefaultPixelValue(float(cval) if cval is not None else 0.0)
else:
resample.SetInterpolator(sitk.sitkLinear)
# air 值 = 影像最小 HUstatistics 濾波器 streaming 計算,不載入整張 array
stats = sitk.StatisticsImageFilter()
stats.Execute(sitk_image)
resample.SetDefaultPixelValue(float(stats.GetMinimum()))
return resample.Execute(sitk_image)