Florian Leininger commited on
Commit
c246f4d
·
1 Parent(s): 9eb90c4

consistency changes

Browse files
Files changed (2) hide show
  1. model_config.json +1 -2
  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", "ct", "mask"],
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
- ``OutcomeModel`` objective, on top of :class:`pyRadPlan.ml.BasePreprocessor`:
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
- t = xp_utils.to_namespace(torch, dose_values).to(dtype=torch.float32)
 
 
 
 
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().to(self.device).detach()
 
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()