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.
50 lines
No EOL
2.7 KiB
Python
50 lines
No EOL
2.7 KiB
Python
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 不裁剪):
|
||
- CT(is_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)。
|
||
label(is_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 值 = 影像最小 HU(statistics 濾波器 streaming 計算,不載入整張 array)
|
||
stats = sitk.StatisticsImageFilter()
|
||
stats.Execute(sitk_image)
|
||
resample.SetDefaultPixelValue(float(stats.GetMinimum()))
|
||
|
||
return resample.Execute(sitk_image) |