def get_properties_for_stage(self, current_spacing, original_spacing,
                                 original_shape, num_cases, num_modalities,
                                 num_classes):
        """
        Computation of input patch size starts out with the new median shape (in voxels) of a dataset. This is
        opposed to prior experiments where I based it on the median size in mm. The rationale behind this is that
        for some organ of interest the acquisition method will most likely be chosen such that the field of view and
        voxel resolution go hand in hand to show the doctor what they need to see. This assumption may be violated
        for some modalities with anisotropy (cine MRI) but we will have t live with that. In future experiments I
        will try to 1) base input patch size match aspect ratio of input size in mm (instead of voxels) and 2) to
        try to enforce that we see the same 'distance' in all directions (try to maintain equal size in mm of patch)

        The patches created here attempt keep the aspect ratio of the new_median_shape

        :param current_spacing:
        :param original_spacing:
        :param original_shape:
        :param num_cases:
        :return:
        """
        new_median_shape = np.round(original_spacing / current_spacing *
                                    original_shape).astype(int)
        dataset_num_voxels = np.prod(new_median_shape) * num_cases

        # the next line is what we had before as a default. The patch size had the same aspect ratio as the median shape of a patient. We swapped t
        # input_patch_size = new_median_shape

        # compute how many voxels are one mm
        input_patch_size = 1 / np.array(current_spacing)

        # normalize voxels per mm
        input_patch_size /= input_patch_size.mean()

        # create an isotropic patch of size 512x512x512mm
        input_patch_size *= 1 / min(
            input_patch_size) * 512  # to get a starting value
        input_patch_size = np.round(input_patch_size).astype(int)

        # clip it to the median shape of the dataset because patches larger then that make not much sense
        input_patch_size = [
            min(i, j) for i, j in zip(input_patch_size, new_median_shape)
        ]

        network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
        shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(input_patch_size,
                                                                        self.unet_featuremap_min_edge_length,
                                                                        self.unet_max_numpool,
                                                                        current_spacing)

        ref = Generic_UNet.use_this_for_batch_size_computation_3D
        here = Generic_UNet.compute_approx_vram_consumption(
            new_shp,
            network_num_pool_per_axis,
            self.unet_base_num_features,
            self.unet_max_num_filters,
            num_modalities,
            num_classes,
            pool_op_kernel_sizes,
            conv_per_stage=self.conv_per_stage)
        while here > ref:
            axis_to_be_reduced = np.argsort(new_shp / new_median_shape)[-1]

            tmp = deepcopy(new_shp)
            tmp[axis_to_be_reduced] -= shape_must_be_divisible_by[
                axis_to_be_reduced]
            _, _, _, _, shape_must_be_divisible_by_new = \
                get_pool_and_conv_props_poolLateV2(tmp,
                                                   self.unet_featuremap_min_edge_length,
                                                   self.unet_max_numpool,
                                                   current_spacing)
            new_shp[axis_to_be_reduced] -= shape_must_be_divisible_by_new[
                axis_to_be_reduced]

            # we have to recompute numpool now:
            network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
            shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(new_shp,
                                                                            self.unet_featuremap_min_edge_length,
                                                                            self.unet_max_numpool,
                                                                            current_spacing)

            here = Generic_UNet.compute_approx_vram_consumption(
                new_shp,
                network_num_pool_per_axis,
                self.unet_base_num_features,
                self.unet_max_num_filters,
                num_modalities,
                num_classes,
                pool_op_kernel_sizes,
                conv_per_stage=self.conv_per_stage)
            # print(new_shp)

        input_patch_size = new_shp

        batch_size = Generic_UNet.DEFAULT_BATCH_SIZE_3D  # This is what works with 128**3
        batch_size = int(np.floor(max(ref / here, 1) * batch_size))

        # check if batch size is too large
        max_batch_size = np.round(
            self.batch_size_covers_max_percent_of_dataset *
            dataset_num_voxels /
            np.prod(input_patch_size, dtype=np.int64)).astype(int)
        max_batch_size = max(max_batch_size, self.unet_min_batch_size)
        batch_size = max(1, min(batch_size, max_batch_size))

        do_dummy_2D_data_aug = (max(input_patch_size) / input_patch_size[0]
                                ) > self.anisotropy_threshold

        plan = {
            'batch_size': batch_size,
            'num_pool_per_axis': network_num_pool_per_axis,
            'patch_size': input_patch_size,
            'median_patient_size_in_voxels': new_median_shape,
            'current_spacing': current_spacing,
            'original_spacing': original_spacing,
            'do_dummy_2D_data_aug': do_dummy_2D_data_aug,
            'pool_op_kernel_sizes': pool_op_kernel_sizes,
            'conv_kernel_sizes': conv_kernel_sizes,
        }
        return plan
        def get_properties_for_stage(current_spacing, original_spacing,
                                     original_shape, num_cases, num_modalities,
                                     num_classes):
            """
            Computation of input patch size starts out with the new median shape (in voxels) of a dataset. This is
            opposed to prior experiments where I based it on the median size in mm. The rationale behind this is that
            for some organ of interest the acquisition method will most likely be chosen such that the field of view and
            voxel resolution go hand in hand to show the doctor what they need to see. This assumption may be violated
            for some modalities with anisotropy (cine MRI) but we will have t live with that. In future experiments I
            will try to 1) base input patch size match aspect ratio of input size in mm (instead of voxels) and 2) to
            try to enforce that we see the same 'distance' in all directions (try to maintain equal size in mm of patch)
            :param current_spacing:
            :param original_spacing:
            :param original_shape:
            :param num_cases:
            :return:
            """
            new_median_shape = np.round(original_spacing / current_spacing *
                                        original_shape).astype(int)
            dataset_num_voxels = np.prod(new_median_shape) * num_cases

            # the next line is what we had before as a default. The patch size had the same aspect ratio as the median shape of a patient. We swapped t
            # input_patch_size = new_median_shape

            # compute how many voxels are one mm
            input_patch_size = 1 / np.array(current_spacing)

            # normalize voxels per mm
            input_patch_size /= input_patch_size.mean()

            # create an isotropic patch of size 512x512x512mm
            input_patch_size *= 1 / min(
                input_patch_size) * 512  # to get a starting value
            input_patch_size = np.round(input_patch_size).astype(int)

            # clip it to the median shape of the dataset because patches larger then that make not much sense
            input_patch_size = [
                min(i, j) for i, j in zip(input_patch_size, new_median_shape)
            ]

            network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
            shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(input_patch_size,
                                                                            FEATUREMAP_MIN_EDGE_LENGTH_BOTTLENECK,
                                                                            Generic_UNet.MAX_NUMPOOL_3D,
                                                                            current_spacing)

            ref = Generic_UNet.use_this_for_batch_size_computation_3D
            here = Generic_UNet.compute_approx_vram_consumption(
                new_shp, network_num_pool_per_axis,
                Generic_UNet.BASE_NUM_FEATURES_3D,
                Generic_UNet.MAX_NUM_FILTERS_3D, num_modalities, num_classes,
                pool_op_kernel_sizes)
            while here > ref:
                argsrt = np.argsort(new_shp / new_median_shape)[::-1]
                pool_fct_per_axis = np.prod(pool_op_kernel_sizes, 0)
                bottleneck_size_per_axis = new_shp / pool_fct_per_axis
                shape_must_be_divisible_by = [
                    shape_must_be_divisible_by[i]
                    if bottleneck_size_per_axis[i] > 4 else
                    shape_must_be_divisible_by[i] / 2
                    for i in range(len(bottleneck_size_per_axis))
                ]
                new_shp[argsrt[0]] -= shape_must_be_divisible_by[argsrt[0]]

                # we have to recompute numpool now:
                network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
                shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(new_shp,
                                                                                FEATUREMAP_MIN_EDGE_LENGTH_BOTTLENECK,
                                                                                Generic_UNet.MAX_NUMPOOL_3D,
                                                                                current_spacing)

                here = Generic_UNet.compute_approx_vram_consumption(
                    new_shp, network_num_pool_per_axis,
                    Generic_UNet.BASE_NUM_FEATURES_3D,
                    Generic_UNet.MAX_NUM_FILTERS_3D, num_modalities,
                    num_classes, pool_op_kernel_sizes)
                print(new_shp)

            input_patch_size = new_shp

            batch_size = Generic_UNet.DEFAULT_BATCH_SIZE_3D  # This is what wirks with 128**3
            batch_size = int(np.floor(max(ref / here, 1) * batch_size))

            # check if batch size is too large
            max_batch_size = np.round(
                batch_size_covers_max_percent_of_dataset * dataset_num_voxels /
                np.prod(input_patch_size, dtype=np.int64)).astype(int)
            max_batch_size = max(max_batch_size, dataset_min_batch_size_cap)
            batch_size = min(batch_size, max_batch_size)

            do_dummy_2D_data_aug = (
                max(input_patch_size) / input_patch_size[0]
            ) > RESAMPLING_SEPARATE_Z_ANISOTROPY_THRESHOLD

            plan = {
                'batch_size': batch_size,
                'num_pool_per_axis': network_num_pool_per_axis,
                'patch_size': input_patch_size,
                'median_patient_size_in_voxels': new_median_shape,
                'current_spacing': current_spacing,
                'original_spacing': original_spacing,
                'do_dummy_2D_data_aug': do_dummy_2D_data_aug,
                'pool_op_kernel_sizes': pool_op_kernel_sizes,
                'conv_kernel_sizes': conv_kernel_sizes,
            }
            return plan
Exemple #3
0
    def get_properties_for_stage(self, current_spacing, original_spacing,
                                 original_shape, num_cases, num_modalities,
                                 num_classes):
        """
		"""
        new_median_shape = np.round(original_spacing / current_spacing *
                                    original_shape).astype(int)
        dataset_num_voxels = np.prod(new_median_shape) * num_cases

        input_patch_size = new_median_shape

        network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
        shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(input_patch_size,
                        self.unet_featuremap_min_edge_length,
                        self.unet_max_numpool,
                        current_spacing)

        ref = Generic_UNet.use_this_for_batch_size_computation_3D
        here = Generic_UNet.compute_approx_vram_consumption(
            new_shp,
            network_num_pool_per_axis,
            self.unet_base_num_features,
            self.unet_max_num_filters,
            num_modalities,
            num_classes,
            pool_op_kernel_sizes,
            conv_per_stage=self.conv_per_stage)
        while here > ref:
            # find the largest axis. If patch is isotropic, pick the axis with the largest spacing
            if len(np.unique(new_shp)) == 1:
                axis_to_be_reduced = np.argsort(current_spacing)[-1]
            else:
                axis_to_be_reduced = np.argsort(new_shp)[-1]

            tmp = deepcopy(new_shp)
            tmp[axis_to_be_reduced] -= shape_must_be_divisible_by[
                axis_to_be_reduced]
            _, _, _, _, shape_must_be_divisible_by_new = \
             get_pool_and_conv_props_poolLateV2(tmp,
                        self.unet_featuremap_min_edge_length,
                        self.unet_max_numpool,
                        current_spacing)
            new_shp[axis_to_be_reduced] -= shape_must_be_divisible_by_new[
                axis_to_be_reduced]

            # we have to recompute numpool now:
            network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
            shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(new_shp,
                            self.unet_featuremap_min_edge_length,
                            self.unet_max_numpool,
                            current_spacing)

            here = Generic_UNet.compute_approx_vram_consumption(
                new_shp,
                network_num_pool_per_axis,
                self.unet_base_num_features,
                self.unet_max_num_filters,
                num_modalities,
                num_classes,
                pool_op_kernel_sizes,
                conv_per_stage=self.conv_per_stage)
            print(new_shp)

        input_patch_size = new_shp

        batch_size = Generic_UNet.DEFAULT_BATCH_SIZE_3D  # This is what works with 128**3
        batch_size = int(np.floor(max(ref / here, 1) * batch_size))

        # check if batch size is too large
        max_batch_size = np.round(
            self.batch_size_covers_max_percent_of_dataset *
            dataset_num_voxels /
            np.prod(input_patch_size, dtype=np.int64)).astype(int)
        max_batch_size = max(max_batch_size, self.unet_min_batch_size)
        batch_size = min(batch_size, max_batch_size)

        do_dummy_2D_data_aug = (max(input_patch_size) / input_patch_size[0]
                                ) > self.anisotropy_threshold

        plan = {
            'batch_size': batch_size,
            'num_pool_per_axis': network_num_pool_per_axis,
            'patch_size': input_patch_size,
            'median_patient_size_in_voxels': new_median_shape,
            'current_spacing': current_spacing,
            'original_spacing': original_spacing,
            'do_dummy_2D_data_aug': do_dummy_2D_data_aug,
            'pool_op_kernel_sizes': pool_op_kernel_sizes,
            'conv_kernel_sizes': conv_kernel_sizes,
        }
        return plan
Exemple #4
0
    def get_properties_for_stage(self, current_spacing, original_spacing,
                                 original_shape, num_cases, num_modalities,
                                 num_classes):
        """
		"""
        new_median_shape = np.round(original_spacing / current_spacing *
                                    original_shape).astype(int)
        dataset_num_voxels = np.prod(new_median_shape) * num_cases

        # the next line is what we had before as a default. The patch size had the same aspect ratio as the median shape of a patient. We swapped t
        # input_patch_size = new_median_shape

        # compute how many voxels are one mm
        input_patch_size = 1 / np.array(current_spacing)

        # normalize voxels per mm
        input_patch_size /= input_patch_size.mean()

        # create an isotropic patch of size 512x512x512mm
        input_patch_size *= 1 / min(
            input_patch_size) * 512  # to get a starting value
        input_patch_size = np.round(input_patch_size).astype(int)

        # clip it to the median shape of the dataset because patches larger then that make not much sense
        input_patch_size = [
            min(i, j) for i, j in zip(input_patch_size, new_median_shape)
        ]

        network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
        shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(input_patch_size,
                        self.unet_featuremap_min_edge_length,
                        self.unet_max_numpool,
                        current_spacing)

        ref = Generic_UNet.use_this_for_batch_size_computation_3D
        here = Generic_UNet.compute_approx_vram_consumption(
            new_shp,
            network_num_pool_per_axis,
            self.unet_base_num_features,
            self.unet_max_num_filters,
            num_modalities,
            num_classes,
            pool_op_kernel_sizes,
            conv_per_stage=self.conv_per_stage)
        while here > ref:
            # here is the difference to ExperimentPlanner. In the old version we made the aspect ratio match
            # between patch and new_median_shape, regardless of spacing. It could be better to enforce isotropy
            # (in mm) instead
            current_patch_in_mm = new_shp * current_spacing
            axis_to_be_reduced = np.argsort(current_patch_in_mm)[-1]

            # from here on it's the same as before
            tmp = deepcopy(new_shp)
            tmp[axis_to_be_reduced] -= shape_must_be_divisible_by[
                axis_to_be_reduced]
            _, _, _, _, shape_must_be_divisible_by_new = \
             get_pool_and_conv_props_poolLateV2(tmp,
                        self.unet_featuremap_min_edge_length,
                        self.unet_max_numpool,
                        current_spacing)
            new_shp[axis_to_be_reduced] -= shape_must_be_divisible_by_new[
                axis_to_be_reduced]

            # we have to recompute numpool now:
            network_num_pool_per_axis, pool_op_kernel_sizes, conv_kernel_sizes, new_shp, \
            shape_must_be_divisible_by = get_pool_and_conv_props_poolLateV2(new_shp,
                            self.unet_featuremap_min_edge_length,
                            self.unet_max_numpool,
                            current_spacing)

            here = Generic_UNet.compute_approx_vram_consumption(
                new_shp,
                network_num_pool_per_axis,
                self.unet_base_num_features,
                self.unet_max_num_filters,
                num_modalities,
                num_classes,
                pool_op_kernel_sizes,
                conv_per_stage=self.conv_per_stage)
            print(new_shp)

        input_patch_size = new_shp

        batch_size = Generic_UNet.DEFAULT_BATCH_SIZE_3D  # This is what works with 128**3
        batch_size = int(np.floor(max(ref / here, 1) * batch_size))

        # check if batch size is too large
        max_batch_size = np.round(
            self.batch_size_covers_max_percent_of_dataset *
            dataset_num_voxels /
            np.prod(input_patch_size, dtype=np.int64)).astype(int)
        max_batch_size = max(max_batch_size, self.unet_min_batch_size)
        batch_size = min(batch_size, max_batch_size)

        do_dummy_2D_data_aug = (max(input_patch_size) / input_patch_size[0]
                                ) > self.anisotropy_threshold

        plan = {
            'batch_size': batch_size,
            'num_pool_per_axis': network_num_pool_per_axis,
            'patch_size': input_patch_size,
            'median_patient_size_in_voxels': new_median_shape,
            'current_spacing': current_spacing,
            'original_spacing': original_spacing,
            'do_dummy_2D_data_aug': do_dummy_2D_data_aug,
            'pool_op_kernel_sizes': pool_op_kernel_sizes,
            'conv_kernel_sizes': conv_kernel_sizes,
        }
        return plan