Florian Leininger commited on
Commit ·
c246f4d
1
Parent(s): 9eb90c4
consistency changes
Browse files- model_config.json +1 -2
- preprocessor.py +16 -3
model_config.json
CHANGED
|
@@ -31,7 +31,6 @@
|
|
| 31 |
},
|
| 32 |
"input_dimensions": [100, 100, 40],
|
| 33 |
"input_spacing": [3.0, 3.0, 2.5],
|
| 34 |
-
"input_physical_extent": [300.0, 300.0, 300.0],
|
| 35 |
"center": "target"
|
| 36 |
},
|
| 37 |
"metadata": {
|
|
@@ -43,7 +42,7 @@
|
|
| 43 |
"notes": "Dose-preserving DenseNet3D (BatchNorm, anti-aliased pooling); dose normalized by 70 Gy prescription."
|
| 44 |
},
|
| 45 |
"model_input": {
|
| 46 |
-
"type_order": ["dose", "
|
| 47 |
"tensor_shapes": {
|
| 48 |
"dose": [1, 100, 100, 40],
|
| 49 |
"ct": [1, 100, 100, 40],
|
|
|
|
| 31 |
},
|
| 32 |
"input_dimensions": [100, 100, 40],
|
| 33 |
"input_spacing": [3.0, 3.0, 2.5],
|
|
|
|
| 34 |
"center": "target"
|
| 35 |
},
|
| 36 |
"metadata": {
|
|
|
|
| 42 |
"notes": "Dose-preserving DenseNet3D (BatchNorm, anti-aliased pooling); dose normalized by 70 Gy prescription."
|
| 43 |
},
|
| 44 |
"model_input": {
|
| 45 |
+
"type_order": ["dose", "mask", "ct"],
|
| 46 |
"tensor_shapes": {
|
| 47 |
"dose": [1, 100, 100, 40],
|
| 48 |
"ct": [1, 100, 100, 40],
|
preprocessor.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"""Outcome-guided preprocessor shipped with the model repository.
|
| 2 |
|
| 3 |
Implements the outcome preprocessor contract expected by pyRadPlan's
|
| 4 |
-
``
|
| 5 |
|
| 6 |
- ``configure(ct, cst, cst_masks=None, device=None)`` resamples CT and masks
|
| 7 |
onto the fixed model input grid once.
|
|
@@ -411,15 +411,28 @@ class OutcomeCnnPreprocessor(BasePreprocessor):
|
|
| 411 |
mask = mask.amax(dim=1, keepdim=True)
|
| 412 |
self._mask_tensor = mask
|
| 413 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
def _dose_to_tensor(self, dose_values) -> "torch.Tensor":
|
| 415 |
"""Convert a dose array of any namespace to a (dZ, dY, dX) tensor on device."""
|
| 416 |
from pyRadPlan.core import xp_utils # noqa: PLC0415 - avoid import cycle at module load
|
| 417 |
|
| 418 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 419 |
if t.ndim == 1:
|
| 420 |
dx, dy, dz = self._dose_grid["size"]
|
| 421 |
# Fortran-order reshape to (X, Y, Z) == C-order reshape to (Z, Y, X)
|
| 422 |
t = t.reshape(dz, dy, dx)
|
| 423 |
else:
|
| 424 |
t = t.permute(2, 1, 0) # (X, Y, Z) -> (Z, Y, X)
|
| 425 |
-
return t.contiguous().
|
|
|
|
| 1 |
"""Outcome-guided preprocessor shipped with the model repository.
|
| 2 |
|
| 3 |
Implements the outcome preprocessor contract expected by pyRadPlan's
|
| 4 |
+
``OutcomeCNN`` objective, on top of :class:`pyRadPlan.ai_models.BasePreprocessor`:
|
| 5 |
|
| 6 |
- ``configure(ct, cst, cst_masks=None, device=None)`` resamples CT and masks
|
| 7 |
onto the fixed model input grid once.
|
|
|
|
| 411 |
mask = mask.amax(dim=1, keepdim=True)
|
| 412 |
self._mask_tensor = mask
|
| 413 |
|
| 414 |
+
# `extract` multiplies the single-channel dose by the mask; with more than
|
| 415 |
+
# one mask channel this would broadcast the dose to C channels and break the
|
| 416 |
+
# single-dose-channel model. Fail early and clearly instead.
|
| 417 |
+
if self.dose_config.get("extract", False) and mask.shape[1] > 1:
|
| 418 |
+
raise ValueError(
|
| 419 |
+
"dose 'extract' requires a single mask channel; got "
|
| 420 |
+
f"{mask.shape[1]} channels. Set mask 'collapse': true or pass a single VOI."
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
def _dose_to_tensor(self, dose_values) -> "torch.Tensor":
|
| 424 |
"""Convert a dose array of any namespace to a (dZ, dY, dX) tensor on device."""
|
| 425 |
from pyRadPlan.core import xp_utils # noqa: PLC0415 - avoid import cycle at module load
|
| 426 |
|
| 427 |
+
# Single device+dtype cast: a cupy/numpy dose is brought onto the model
|
| 428 |
+
# device and to float32 in one step (dlpack keeps it zero-copy where possible).
|
| 429 |
+
t = xp_utils.to_namespace(torch, dose_values).to(
|
| 430 |
+
device=self.device, dtype=torch.float32
|
| 431 |
+
)
|
| 432 |
if t.ndim == 1:
|
| 433 |
dx, dy, dz = self._dose_grid["size"]
|
| 434 |
# Fortran-order reshape to (X, Y, Z) == C-order reshape to (Z, Y, X)
|
| 435 |
t = t.reshape(dz, dy, dx)
|
| 436 |
else:
|
| 437 |
t = t.permute(2, 1, 0) # (X, Y, Z) -> (Z, Y, X)
|
| 438 |
+
return t.contiguous().detach()
|