MIC-DKFZ / nnDetection

nnDetection is a self-configuring framework for 3D (volumetric) medical object detection which can be applied to new data sets without manual intervention. It includes guides for 12 data sets that were used to develop and evaluate the performance of the proposed method.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

issues related to resolution at which nnDetection training is to be carried out

DSRajesh opened this issue · comments

Hello

  1. How is the resolution at which nnDetection models that are trained obtained ? Is it the median spacing in the dataset.
  2. Will choosing the median spacing be adverse ? (We have a lot of ground truth data including data from scanners as far back as 15 years. This would make the median resolution fairly coarser than what current scanners are capable of). In other words inference on newer generation data will effectively downsample it to the median (coarser) spacing and detection is done at coarser resolution than the data that was acquired ?
  3. How can we specify a custom resolution instead of the median ?

Thanking you

Rajesh D S

Dear @DSRajesh ,

(1) yes, the spacing is the median spacing of the dataset
(2) the implicit assumption in nnDetection/nnU-Net (and general ML) is that your training data reflects your inference/testing data, otherwise you have a distribution shift / domain gap. If you assume that the model is primarily applied to new images (with smaller spacing) it would make sense to use a different rule/manually set the spacing to reduce the domain gap between training and testing. In general, one might need to check how beneficial the old data actually is for the training and if it helps with newer data as well.
(3) You can set the target spacing manually here:

def determine_target_spacing(self, mode: str) -> np.ndarray:
"""
Determine target spacing
Args:
mode: Current planning mode. Typically one of '2d' | '3d' | '3dlr1'
Raises:
RuntimeError: not supported mode (supported are 2d, 3d, 3dlrX)
Returns:
np.ndarray: target spacing
"""
base_target_spacing = self._target_spacing_base()
if mode == "3d" or mode == "2d":
target_spacing = base_target_spacing
else:
if not "lr" in mode:
raise RuntimeError(f"Mode {mode} is not supported for target spacing.")
downscale = int(mode.split('lr')[-1])
target_spacing = base_target_spacing * (2 ** downscale)
return target_spacing
def _target_spacing_base(self) -> np.ndarray:
"""
Determine target spacing.
Same as nnUNet v21
https://github.com/MIC-DKFZ/nnUNet/blob/master/nnunet/experiment_planning/experiment_planner_baseline_3DUNet_v21.py
"""
spacings = self.data_properties['all_spacings']
sizes = self.data_properties['all_sizes']
target = np.percentile(np.vstack(spacings), self.target_spacing_percentile, 0)
target_size = np.percentile(np.vstack(sizes), self.target_spacing_percentile, 0)
target_size_mm = np.array(target) * np.array(target_size)
# we need to identify datasets for which a different target spacing could be beneficial. These datasets have
# the following properties:
# - one axis which much lower resolution than the others
# - the lowres axis has much less voxels than the others
# - (the size in mm of the lowres axis is also reduced)
worst_spacing_axis = np.argmax(target)
other_axes = [i for i in range(len(target)) if i != worst_spacing_axis]
other_spacings = [target[i] for i in other_axes]
other_sizes = [target_size[i] for i in other_axes]
has_aniso_spacing = target[worst_spacing_axis] > (self.anisotropy_threshold * min(other_spacings))
has_aniso_voxels = target_size[worst_spacing_axis] * self.anisotropy_threshold < min(other_sizes)
# we don't use the last one for now
# median_size_in_mm = target[target_size_mm] * RESAMPLING_SEPARATE_Z_ANISOTROPY_THRESHOLD < max(target_size_mm)
if has_aniso_spacing and has_aniso_voxels:
spacings_of_that_axis = np.vstack(spacings)[:, worst_spacing_axis]
target_spacing_of_that_axis = np.percentile(spacings_of_that_axis, 10)
# don't let the spacing of that axis get higher than the other axes
if target_spacing_of_that_axis < min(other_spacings):
target_spacing_of_that_axis = max(min(other_spacings), target_spacing_of_that_axis) + 1e-5
target[worst_spacing_axis] = target_spacing_of_that_axis
return target

Best,
Michael

This issue is stale because it has been open for 30 days with no activity.

This issue was closed because it has been inactive for 14 days since being marked as stale.