From e1d7218815f92a12a71b7e5e4bddad6d25b8da63 Mon Sep 17 00:00:00 2001 From: Ibrahim Hadzic Date: Fri, 8 Jan 2021 15:37:41 +0100 Subject: [PATCH] removed old projects, formatting --- .gitattributes | 1 + midaGAN/configs/utils/initializers.py | 10 +- midaGAN/data/__init__.py | 4 +- midaGAN/data/image_dataset.py | 4 +- midaGAN/data/samplers.py | 2 +- midaGAN/data/utils/__init__.py | 3 +- midaGAN/data/utils/body_mask.py | 8 +- midaGAN/data/utils/image_pool.py | 18 +- midaGAN/data/utils/registration_methods.py | 21 ++- midaGAN/data/utils/slice_sampler.py | 29 ++-- .../data/utils/stochastic_focal_patching.py | 27 +-- midaGAN/data/utils/transforms.py | 3 +- midaGAN/evaluator.py | 19 +-- midaGAN/inferer.py | 23 ++- midaGAN/nn/gans/basegan.py | 2 +- midaGAN/nn/utils.py | 2 +- midaGAN/trainer.py | 7 +- midaGAN/utils/__init__.py | 11 +- midaGAN/utils/communication.py | 50 +++--- midaGAN/utils/environment.py | 22 +-- midaGAN/utils/io.py | 8 +- midaGAN/utils/sitk_utils.py | 2 +- midaGAN/utils/trackers/__init__.py | 24 ++- midaGAN/utils/trackers/base_tracker.py | 2 +- midaGAN/utils/trackers/eval_tracker.py | 25 +-- midaGAN/utils/trackers/tensorboard_tracker.py | 1 + midaGAN/utils/trackers/training_tracker.py | 10 +- midaGAN/utils/trackers/wandb_tracker.py | 12 +- notebooks/Mask_and_Bound_Proton.ipynb | 2 +- projects/brats_flair_to_t1w/brats_dataset.py | 11 +- projects/dummy/dummy.yaml | 28 --- projects/for_jonas/slice_based_dataset.py | 8 +- projects/lidc_intervendor_ct/ct_dataset.py | 70 -------- .../maastro_cbct_to_ct/cbcttoct2d_dataset.py | 159 ------------------ .../maastro_cbct_to_ct/cbcttoct_dataset.py | 153 ----------------- .../experiments/2d_ex1.yaml | 40 ----- .../experiments/2d_ex2.yaml | 39 ----- .../maastro_cbct_to_ct/experiments/ex1.yaml | 28 --- .../maastro_cbct_to_ct/experiments/ex10.yaml | 43 ----- .../maastro_cbct_to_ct/experiments/ex11.yaml | 42 ----- .../maastro_cbct_to_ct/experiments/ex12.yaml | 42 ----- .../maastro_cbct_to_ct/experiments/ex13.yaml | 42 ----- .../maastro_cbct_to_ct/experiments/ex14.yaml | 43 ----- .../maastro_cbct_to_ct/experiments/ex15.yaml | 45 ----- .../maastro_cbct_to_ct/experiments/ex16.yaml | 45 ----- .../maastro_cbct_to_ct/experiments/ex2.yaml | 32 ---- .../maastro_cbct_to_ct/experiments/ex4.yaml | 39 ----- .../maastro_cbct_to_ct/experiments/ex5.yaml | 39 ----- .../maastro_cbct_to_ct/experiments/ex6.yaml | 40 ----- .../maastro_cbct_to_ct/experiments/ex7.yaml | 43 ----- .../maastro_cbct_to_ct/experiments/ex8.yaml | 43 ----- .../maastro_cbct_to_ct/experiments/ex9.yaml | 44 ----- projects/maastro_cbct_to_ct/jobscripts/ex2.sh | 37 ---- .../cbcttoct_dataset.py | 0 .../experiments/ex1.yaml | 2 +- .../experiments/ex16.yaml | 2 +- .../experiments/ex1_continued.yaml | 2 +- .../experiments/ex1_cut.yaml | 2 +- .../experiments/ex2.yaml | 2 +- tools/infer.py | 3 +- tools/train.py | 5 +- 61 files changed, 208 insertions(+), 1317 deletions(-) create mode 100644 .gitattributes delete mode 100644 projects/dummy/dummy.yaml delete mode 100644 projects/lidc_intervendor_ct/ct_dataset.py delete mode 100755 projects/maastro_cbct_to_ct/cbcttoct2d_dataset.py delete mode 100644 projects/maastro_cbct_to_ct/cbcttoct_dataset.py delete mode 100755 projects/maastro_cbct_to_ct/experiments/2d_ex1.yaml delete mode 100755 projects/maastro_cbct_to_ct/experiments/2d_ex2.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex1.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex10.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex11.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex12.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex13.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex14.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex15.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex16.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex2.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex4.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex5.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex6.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex7.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex8.yaml delete mode 100644 projects/maastro_cbct_to_ct/experiments/ex9.yaml delete mode 100644 projects/maastro_cbct_to_ct/jobscripts/ex2.sh rename projects/{proton_cbct_to_ct => maastro_lung_proton_cbct_to_ct}/cbcttoct_dataset.py (100%) rename projects/{proton_cbct_to_ct => maastro_lung_proton_cbct_to_ct}/experiments/ex1.yaml (93%) rename projects/{proton_cbct_to_ct => maastro_lung_proton_cbct_to_ct}/experiments/ex16.yaml (93%) rename projects/{proton_cbct_to_ct => maastro_lung_proton_cbct_to_ct}/experiments/ex1_continued.yaml (94%) rename projects/{proton_cbct_to_ct => maastro_lung_proton_cbct_to_ct}/experiments/ex1_cut.yaml (94%) rename projects/{proton_cbct_to_ct => maastro_lung_proton_cbct_to_ct}/experiments/ex2.yaml (93%) diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..5be91f97 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.ipynb linguist-vendored diff --git a/midaGAN/configs/utils/initializers.py b/midaGAN/configs/utils/initializers.py index 30989cfe..b3e6e34d 100755 --- a/midaGAN/configs/utils/initializers.py +++ b/midaGAN/configs/utils/initializers.py @@ -25,9 +25,8 @@ def init_config(conf, config_class=configs.training.TrainConfig): # Allows the framework to find user-defined, project-specific, dataset classes and their configs if conf.project_dir: IMPORT_LOCATIONS["dataset"].append(conf.project_dir) - logger.info( - f"Project directory {conf.project_dir} added to path to allow imports of modules from it." - ) + logger.info(f"Project directory {conf.project_dir} added to the" + " path to allow imports of modules from it.") # Make yaml mergeable by instantiating the dataclasses conf = instantiate_dataclasses_from_yaml(conf) @@ -37,7 +36,7 @@ def init_config(conf, config_class=configs.training.TrainConfig): def instantiate_dataclasses_from_yaml(conf): - """Goes through a config and instantiates the fields that are dataclasses. + """Goes through a config and instantiates the fields that are dataclasses. A field is a dataclass if its key can be found in the keys of the IMPORT_LOCATIONS. Each such dataclass should have an entry "name" which is used to import its dataclass class using that "name" + "Config" as class name. @@ -78,4 +77,5 @@ def get_all_conf_keys(conf): """Get all keys from a conf and order from them the deepest to the shallowest.""" conf = OmegaConf.to_container(conf) keys = list(utils.iterate_nested_dict_keys(conf)) - return keys[::-1] # order by depth + # Order deeper to shallower + return keys[::-1] diff --git a/midaGAN/data/__init__.py b/midaGAN/data/__init__.py index d29eb094..c44900ee 100644 --- a/midaGAN/data/__init__.py +++ b/midaGAN/data/__init__.py @@ -24,8 +24,8 @@ def build_loader(conf): dataset, shuffle=False, num_replicas=communication.get_world_size(), - rank=communication.get_local_rank( - )) # TODO: verify that this indeed should be local rank and not rank + # TODO: should it be rank instead? + rank=communication.get_local_rank()) loader = DataLoader(dataset, batch_size=conf.batch_size, diff --git a/midaGAN/data/image_dataset.py b/midaGAN/data/image_dataset.py index ce4bc157..d703523d 100644 --- a/midaGAN/data/image_dataset.py +++ b/midaGAN/data/image_dataset.py @@ -16,7 +16,9 @@ class ImageDatasetConfig(configs.base.BaseDatasetConfig): name: str = "ImageDataset" image_channels: int = 3 - preprocess: str = "resize_and_crop" # scaling and cropping of images at load time [resize_and_crop | crop | scale_width | scale_width_and_crop | none]' + # Scaling and cropping of images at load time: + # [resize_and_crop | crop | scale_width | scale_width_and_crop | none]' + preprocess: str = "resize_and_crop" load_size: int = 286 crop_size: int = 256 flip: bool = True diff --git a/midaGAN/data/samplers.py b/midaGAN/data/samplers.py index e64a89ee..e9760fe5 100644 --- a/midaGAN/data/samplers.py +++ b/midaGAN/data/samplers.py @@ -10,8 +10,8 @@ # - Docstring to match the rest of the library # - Calls to other subroutines which do not exist in DIRECT. -import torch import itertools +import torch from torch.utils.data.sampler import Sampler from midaGAN.utils import communication diff --git a/midaGAN/data/utils/__init__.py b/midaGAN/data/utils/__init__.py index 1ad1acb4..1c9724ed 100644 --- a/midaGAN/data/utils/__init__.py +++ b/midaGAN/data/utils/__init__.py @@ -12,7 +12,8 @@ def pad(volume, target_shape): assert len(target_shape) == len(volume.shape) - pad_width = [(0, 0) for _ in range(len(target_shape))] # by default no padding + # By default no padding + pad_width = [(0, 0) for _ in range(len(target_shape))] for dim in range(len(target_shape)): if target_shape[dim] > volume.shape[dim]: diff --git a/midaGAN/data/utils/body_mask.py b/midaGAN/data/utils/body_mask.py index b9d54578..826d278d 100644 --- a/midaGAN/data/utils/body_mask.py +++ b/midaGAN/data/utils/body_mask.py @@ -134,9 +134,11 @@ def get_body_mask_and_bound(image: np.ndarray, hu_threshold: int) -> np.ndarray: return body_mask, bound - -def apply_body_mask_and_bound(array: np.ndarray, masking_value: int =-1024, \ - apply_mask: bool =False, apply_bound: bool=False, hu_threshold: int =-300) -> np.ndarray: +def apply_body_mask_and_bound(array: np.ndarray, + masking_value: int = -1024, + apply_mask: bool = False, + apply_bound: bool = False, + hu_threshold: int = -300) -> np.ndarray: """ Function to apply mask based filtering and bound the array diff --git a/midaGAN/data/utils/image_pool.py b/midaGAN/data/utils/image_pool.py index f7eae902..de2b2a46 100644 --- a/midaGAN/data/utils/image_pool.py +++ b/midaGAN/data/utils/image_pool.py @@ -16,7 +16,8 @@ def __init__(self, pool_size): pool_size (int) -- the size of image buffer, if pool_size=0, no buffer will be created """ self.pool_size = pool_size - if self.pool_size > 0: # create an empty pool + # Create an empty pool + if self.pool_size > 0: self.num_imgs = 0 self.images = [] @@ -37,18 +38,23 @@ def query(self, images): return_images = [] for image in images: image = torch.unsqueeze(image.data, 0) - if self.num_imgs < self.pool_size: # if the buffer is not full; keep inserting current images to the buffer + # If the buffer is not full, keep inserting current images to the buffer + if self.num_imgs < self.pool_size: self.num_imgs = self.num_imgs + 1 self.images.append(image) return_images.append(image) else: p = random.uniform(0, 1) - if p > 0.5: # by 50% chance, the buffer will return a previously stored image, and insert the current image into the buffer - random_id = random.randint(0, self.pool_size - 1) # randint is inclusive + # By 50% chance, the buffer will return a previously stored image, + # and insert the current image into the buffer + if p > 0.5: + random_id = random.randint(0, self.pool_size - 1) tmp = self.images[random_id].clone() self.images[random_id] = image return_images.append(tmp) - else: # by another 50% chance, the buffer will return the current image + # By another 50% chance, the buffer will return the current image + else: return_images.append(image) - return_images = torch.cat(return_images, 0) # collect all the images and return + # Collect all the images and return + return_images = torch.cat(return_images, 0) return return_images diff --git a/midaGAN/data/utils/registration_methods.py b/midaGAN/data/utils/registration_methods.py index d6d18187..eee01d58 100644 --- a/midaGAN/data/utils/registration_methods.py +++ b/midaGAN/data/utils/registration_methods.py @@ -51,9 +51,8 @@ def truncate_CT_to_scope_of_CBCT(CT, CBCT): end_slice = int(round(mean(z_corners[4:]))) # When the registration fails, just return the original CT. Happens infrequently. if start_slice < 0: - logger.info( - "Registration failed as the at least one corner is below 0 in one of the axes. Passing the whole CT volume." - ) + logger.info("Registration failed as the at least one corner is below 0 in one of the axes." + " Passing the whole CT volume.") return CT return CT[:, :, start_slice:end_slice] @@ -80,11 +79,12 @@ def register_CT_to_CBCT(CT, CBCT, registration_type="Rigid"): def get_registration_transform(fixed_image, moving_image, registration_type="Rigid"): """Performs the registration and returns a SimpleITK's `Transform` class which can be used to resample an image so that it is registered to another one. However, in our code - should be truncated so that it contains only the part of the body that is found in the `fixed_image`. - Registration parameters are hardcoded and picked for the specific task of CBCT to CT translation. + should be truncated so that it contains only the part of the body that is + found in the `fixed_image`. + Registration parameters are hardcoded and picked for the specific + task of CBCT to CT translation. TODO: consider making the adjustable in config. - Parameters: ------------------------ fixed_image: @@ -95,8 +95,10 @@ def get_registration_transform(fixed_image, moving_image, registration_type="Rig """ # Get seed from environment variable if set for registration randomness - seed = int( - os.environ.get('PYTHONHASHSEED')) if 'PYTHONHASHSEED' in os.environ else sitk.sitkWallClock + if 'PYTHONHASHSEED' in os.environ: + seed = int(os.environ.get('PYTHONHASHSEED')) + else: + seed = sitk.sitkWallClock # SimpleITK registration's supported pixel types are sitkFloat32 and sitkFloat64 fixed_image = sitk.Cast(fixed_image, sitk.sitkFloat32) @@ -130,7 +132,8 @@ def get_registration_transform(fixed_image, moving_image, registration_type="Rig logger.warning("Unsupported transform provided, falling back to Rigid transformation") registration_transform = REGISTRATION_MAP["Rigid"] - # Align the centers of the two volumes and set the center of rotation to the center of the fixed image + # Align the centers of the two volumes and set the + # center of rotation to the center of the fixed image initial_transform = sitk.CenteredTransformInitializer( fixed_image, moving_image, registration_transform, sitk.CenteredTransformInitializerFilter.GEOMETRY) diff --git a/midaGAN/data/utils/slice_sampler.py b/midaGAN/data/utils/slice_sampler.py index f0888cdf..643954c4 100755 --- a/midaGAN/data/utils/slice_sampler.py +++ b/midaGAN/data/utils/slice_sampler.py @@ -4,16 +4,17 @@ from midaGAN.data.utils import pad +# TODO: Differentiate from Stochasting Focal Patching class SliceSampler: - """ Stochasting Focal Patching technique achieves spatial correspondance of patches extracted from a pair + """ Stochasting Focal Patching technique achieves spatial correspondance of patches extracted from a pair of volumes by: (1) Randomly selecting a slice from volume_A (slice_A) - (2) Calculating the relative start position of the slice_A + (2) Calculating the relative start position of the slice_A (3) Translating the slice_A's relative position in volume_B (4) Placing a focal region (a proportion of volume shape) around the focal point (5) Randomly selecting a start point in the focal region and extracting the slice_B - The added stochasticity in steps (4) and (5) aims to account for possible differences in positioning + The added stochasticity in steps (4) and (5) aims to account for possible differences in positioning of the object between volumes. """ @@ -24,7 +25,7 @@ def __init__(self, self.patch_size = np.array(patch_size) try: assert len(self.patch_size) == 2 - except AssertionError as error: + except AssertionError: print("Patch size needs to be 2D, use StochasticFocalPatchSampler for 3D!") exit() @@ -40,9 +41,8 @@ def get_slice_pair(self, volume_A, volume_B): def slice_and_focal_point_from_A(self, volume): """Return random patch from volume A and its relative start position.""" z, x, y = self.pick_random_start(volume) - - x_end, y_end = [sum(pair) for pair in zip((x, y), self.patch_size) - ] # start + patch size for each coord + # start + patch size for each coord + x_end, y_end = [sum(pair) for pair in zip((x, y), self.patch_size)] volume = pad(volume, (0, x_end, y_end)) @@ -53,8 +53,8 @@ def slice_and_focal_point_from_A(self, volume): def slice_from_B(self, volume, relative_focal_point): """Return random patch from volume B that is in relative neighborhood of patch_A.""" z, x, y = self.pick_stochastic_focal_start(volume, relative_focal_point) - x_end, y_end = [sum(pair) for pair in zip((x, y), self.patch_size) - ] # start + patch size for each coord + # start + patch size for each coord + x_end, y_end = [sum(pair) for pair in zip((x, y), self.patch_size)] volume = pad(volume, (0, x_end, y_end)) @@ -76,7 +76,8 @@ def pick_stochastic_focal_start(self, volume, relative_focal_point): focal_region = self.focal_region_proportion * volume_size focal_region = focal_region.astype(np.int64) - focal_point = relative_focal_point * volume_size # map relative point to corresponding point in this volume + # Map relative point to corresponding point in this volume + focal_point = relative_focal_point * volume_size valid_start_region = self.calculate_valid_start_region(volume) start_coordinates = self.apply_stochastic_focal_method(focal_point, focal_region, @@ -99,10 +100,11 @@ def apply_stochastic_focal_method(self, focal_point, focal_region, valid_start_r min_position = max(0, min_position) max_position = min(max_position, valid_start_region[axis]) - if min_position > max_position: # edge cases # TODO: is it because there's no min(min_position, valid_start_region[axis]) + # Edge cases # TODO: is it because there's no min(min_position, valid_start_region[axis]) + if min_position > max_position: start_point.append(max_position) else: - start_point.append(random.randint(min_position, max_position)) # regular case + start_point.append(random.randint(min_position, max_position)) return start_point @@ -123,4 +125,5 @@ def calculate_valid_start_region(self, volume): return valid_start_region def get_size(self, volume): - return np.array(volume.shape[-3:]) # last three dimension (Z,X,Y) + # last three dimension (Z,X,Y) + return np.array(volume.shape[-3:]) diff --git a/midaGAN/data/utils/stochastic_focal_patching.py b/midaGAN/data/utils/stochastic_focal_patching.py index d452c51f..64f08c1b 100644 --- a/midaGAN/data/utils/stochastic_focal_patching.py +++ b/midaGAN/data/utils/stochastic_focal_patching.py @@ -3,15 +3,15 @@ class StochasticFocalPatchSampler: - """ Stochasting Focal Patching technique achieves spatial correspondance of patches extracted from a pair + """ Stochasting Focal Patching technique achieves spatial correspondance of patches extracted from a pair of volumes by: (1) Randomly selecting a patch from volume_A (patch_A) - (2) Calculating the relative start position of the patch_A + (2) Calculating the relative start position of the patch_A (3) Translating the patch_A's relative position in volume_B (4) Placing a focal region (a proportion of volume shape) around the focal point (5) Randomly selecting a start point in the focal region and extracting the patch_B - The added stochasticity in steps (4) and (5) aims to account for possible differences in positioning + The added stochasticity in steps (4) and (5) aims to account for possible differences in positioning of the object between volumes. """ @@ -28,8 +28,8 @@ def get_patch_pair(self, volume_A, volume_B): def patch_and_focal_point_from_A(self, volume): """Return random patch from volume A and its relative start position.""" z, x, y = self.pick_random_start(volume) - z_end, x_end, y_end = [sum(pair) for pair in zip((z, x, y), self.patch_size) - ] # start + patch size for each coord + # start + patch size for each coord + z_end, x_end, y_end = [sum(pair) for pair in zip((z, x, y), self.patch_size)] patch = volume[z:z_end, x:x_end, y:y_end] relative_focal_point = self.calculate_relative_focal_point(z, x, y, volume) @@ -38,8 +38,8 @@ def patch_and_focal_point_from_A(self, volume): def patch_from_B(self, volume, relative_focal_point): """Return random patch from volume B that is in relative neighborhood of patch_A.""" z, x, y = self.pick_stochastic_focal_start(volume, relative_focal_point) - z_end, x_end, y_end = [sum(pair) for pair in zip((z, x, y), self.patch_size) - ] # start + patch size for each coord + # start + patch size for each coord + z_end, x_end, y_end = [sum(pair) for pair in zip((z, x, y), self.patch_size)] patch = volume[z:z_end, x:x_end, y:y_end] return patch @@ -56,7 +56,8 @@ def pick_stochastic_focal_start(self, volume, relative_focal_point): focal_region = self.focal_region_proportion * volume_size focal_region = focal_region.astype(np.int64) - focal_point = relative_focal_point * volume_size # map relative point to corresponding point in this volume + # Map relative point to corresponding point in this volume + focal_point = relative_focal_point * volume_size valid_start_region = self.calculate_valid_start_region(volume) z, x, y = self.apply_stochastic_focal_method(focal_point, focal_region, valid_start_region) @@ -71,14 +72,15 @@ def apply_stochastic_focal_method(self, focal_point, focal_region, valid_start_r min_position = int(focal_point[axis] - focal_region[axis] / 2) max_position = int(focal_point[axis] + focal_region[axis] / 2) - # if one of the boundaries of the focus is outside of the possible area to sample from, cap it + # If one of the boundaries of the focus is outside of the possible area to sample from, cap it min_position = max(0, min_position) max_position = min(max_position, valid_start_region[axis]) - if min_position > max_position: # edge cases # TODO: is it because there's no min(min_position, valid_start_region[axis]) + # Edge cases # TODO: is it because there's no min(min_position, valid_start_region[axis]) + if min_position > max_position: start_point.append(max_position) else: - start_point.append(random.randint(min_position, max_position)) # regular case + start_point.append(random.randint(min_position, max_position)) return start_point @@ -97,4 +99,5 @@ def calculate_valid_start_region(self, volume): return valid_start_region def get_size(self, volume): - return np.array(volume.shape[-3:]) # last three dimension (Z,X,Y) + # last three dimension (Z,X,Y) + return np.array(volume.shape[-3:]) diff --git a/midaGAN/data/utils/transforms.py b/midaGAN/data/utils/transforms.py index 26b56e16..ac7c17e7 100644 --- a/midaGAN/data/utils/transforms.py +++ b/midaGAN/data/utils/transforms.py @@ -1,3 +1,4 @@ +import numpy as np from PIL import Image import torchvision.transforms as transforms @@ -12,7 +13,7 @@ def get_transform(conf, method=Image.BICUBIC): transform_list = [] if 'resize' in preprocess: - osize = [load_size, load_size] # TODO: make it a tuple from config + osize = [load_size, load_size] transform_list.append(transforms.Resize(osize, method)) elif 'scale_width' in preprocess: diff --git a/midaGAN/evaluator.py b/midaGAN/evaluator.py index 89c0fd3d..a9b7cd61 100644 --- a/midaGAN/evaluator.py +++ b/midaGAN/evaluator.py @@ -61,18 +61,17 @@ def infer(self, data): # Sliding window (i.e. patch-wise) inference if self.sliding_window_inferer: return self.sliding_window_inferer(data, self.model.infer) - else: - return self.model.infer(data) + return self.model.infer(data) def _init_sliding_window_inferer(self): - if self.conf.sliding_window: - return SlidingWindowInferer(roi_size=self.conf.sliding_window.window_size, - sw_batch_size=self.conf.sliding_window.batch_size, - overlap=self.conf.sliding_window.overlap, - mode=self.conf.sliding_window.mode, - cval=-1) - else: - return None + if not self.conf.sliding_window: + return + + return SlidingWindowInferer(roi_size=self.conf.sliding_window.window_size, + sw_batch_size=self.conf.sliding_window.batch_size, + overlap=self.conf.sliding_window.overlap, + mode=self.conf.sliding_window.mode, + cval=-1) def calculate_metrics(self, pred, target): # Check if dataset has scale_to_hu method defined, diff --git a/midaGAN/inferer.py b/midaGAN/inferer.py index 3adb7f23..c47a2be8 100644 --- a/midaGAN/inferer.py +++ b/midaGAN/inferer.py @@ -27,8 +27,8 @@ def run(self): for i, data in enumerate(self.data_loader, start=1): # Sometimes, metadata is necessary to be able to store the generated outputs. # E.g. origin, spacing and direction is required in order to properly save medical images. - if isinstance(data, - list): # dataloader yields a list when passing multiple values at once + # Dataloader yields a list when passing multiple values at once. + if isinstance(data, list): has_metadata = True data, metadata = data metadata = decollate(metadata) @@ -51,18 +51,17 @@ def run(self): def infer(self, data): data = data.to(self.model.device) + # Sliding window (i.e. patch-wise) inference if self.sliding_window_inferer: return self.sliding_window_inferer(data, self.model.infer) - else: - return self.model.infer(data) + return self.model.infer(data) def _init_sliding_window_inferer(self): - if self.conf.sliding_window: - return SlidingWindowInferer(roi_size=self.conf.sliding_window.window_size, - sw_batch_size=self.conf.sliding_window.batch_size, - overlap=self.conf.sliding_window.overlap, - mode=self.conf.sliding_window.mode, - cval=-1) - else: - return None + if not self.conf.sliding_window: + return + return SlidingWindowInferer(roi_size=self.conf.sliding_window.window_size, + sw_batch_size=self.conf.sliding_window.batch_size, + overlap=self.conf.sliding_window.overlap, + mode=self.conf.sliding_window.mode, + cval=-1) diff --git a/midaGAN/nn/gans/basegan.py b/midaGAN/nn/gans/basegan.py index ba6a219f..009ba195 100644 --- a/midaGAN/nn/gans/basegan.py +++ b/midaGAN/nn/gans/basegan.py @@ -5,7 +5,6 @@ import torch from torch.nn.parallel import DistributedDataParallel -from apex import amp from midaGAN.utils import communication # midaGAN.nn imports @@ -119,6 +118,7 @@ def setup(self): "When inferring there should be only one network initialized - generator.") if self.conf.mixed_precision: + from apex import amp self.convert_to_mixed_precision() if self.conf.load_checkpoint: diff --git a/midaGAN/nn/utils.py b/midaGAN/nn/utils.py index 14fd1f81..4ade9e01 100644 --- a/midaGAN/nn/utils.py +++ b/midaGAN/nn/utils.py @@ -86,7 +86,7 @@ def get_norm_layer_3d(norm_type='instance'): def is_bias_before_norm(norm_type='instance'): - """When using BatchNorm, the preceding Conv layer does not use bias, + """When using BatchNorm, the preceding Conv layer does not use bias, but it does if using InstanceNorm. """ if norm_type == 'instance': diff --git a/midaGAN/trainer.py b/midaGAN/trainer.py index aee1765c..9adfc02d 100644 --- a/midaGAN/trainer.py +++ b/midaGAN/trainer.py @@ -16,7 +16,8 @@ def __init__(self, conf): self.logger = logging.getLogger(type(self).__name__) self.conf = conf - torch.backends.cudnn.benchmark = True # https://stackoverflow.com/a/58965640 + # https://stackoverflow.com/a/58965640 + torch.backends.cudnn.benchmark = True # Set reproducibility parameters (random numbers and cudnn backend) if self.conf.seed: @@ -67,8 +68,8 @@ def _do_iteration(self, data): self.model.optimize_parameters() def _perform_scheduler_step(self): - self.model.update_learning_rate( - ) # perform a scheduler step # TODO: better to make decaying rate in checkpoints rather than per iter + # perform a scheduler step + self.model.update_learning_rate() def _save_checkpoint(self): # TODO: save on cancel diff --git a/midaGAN/utils/__init__.py b/midaGAN/utils/__init__.py index 56d92baf..9bbdfb75 100644 --- a/midaGAN/utils/__init__.py +++ b/midaGAN/utils/__init__.py @@ -35,15 +35,14 @@ def import_class_from_dirs_and_modules(class_name, dirs_modules): return attribute raise ValueError( - f"Class with name `{class_name}`` not found in any of the given directories or modules.\ - If it is located in a project folder, set `project_dir` in config as the project's path." - ) + f"Class with name `{class_name}`` not found in any of the given directories or modules. " + "If it is located in a project folder, set `project_dir` in config as the project's path.") def iterate_nested_dict_keys(dictionary): - """Returns an iterator that returns all keys of a - nested dictionary ordered from shallowest to deepest key. - The nested keys have are in dot-list format, e.g. "gan.discriminator.name". + """Returns an iterator that returns all keys of a nested dictionary ordered + from the shallowest to the deepest key. The nested keys are in the dot-list format, + e.g. "gan.discriminator.name". """ if isinstance(dictionary, dict): current_level_keys = [] diff --git a/midaGAN/utils/communication.py b/midaGAN/utils/communication.py index ce077fef..36d9569f 100644 --- a/midaGAN/utils/communication.py +++ b/midaGAN/utils/communication.py @@ -24,7 +24,7 @@ def init_distributed(): synchronize() logger.info(f'Number of GPUs available in world: {num_gpu}.') else: - raise ValueError("Distributed ON but but running single process") # TODO make nicer + raise ValueError("Distributed ON but but running single process") def synchronize(): @@ -121,20 +121,25 @@ def shared_random_seed() -> int: def reduce(input_data, average=False, all_reduce=False): """ - Interface function for performing reduce on any type of data [int, float, tensor, dict, list, tuple] - by summing or averaging the value(s) using one of the methods: - (1) rank 0 reduce (torch.distributed.reduce) - communicates the sum or average of - all processes to the process of rank 0 only - (2) all reduce (torch.distributed.all_reduce) - communicates the sum or average of - all processes to each process - + Interface function for performing reduce on any type of + data [int, float, tensor, dict, list, tuple] by summing or + averaging the value(s) using one of the methods: + + (1) rank 0 reduce (torch.distributed.reduce): + communicates the sum or average of + all processes to the process of rank 0 only + + (2) all reduce (torch.distributed.all_reduce) + communicates the sum or average of + all processes to each process + Parameters: input_dict (int, float, tensor, dict, list, tuple) -- data for reduction average (bool) -- true if the results should be averaged - all_reduce (bool) -- true if communicating the reduced value to all processes, + all_reduce (bool) -- true if communicating the reduced value to all processes, otherwise process of rank 0 only - Returns the sum or average of the input data from across processes. + Returns the sum or average of the input data from across processes. """ if get_world_size() < 2: return input_data @@ -147,20 +152,20 @@ def reduce(input_data, average=False, all_reduce=False): elif isinstance(input_data, dict): reduced_data = reduce_dict(input_data, average, all_reduce, device) - elif isinstance(input_data, list) or isinstance(input_data, tuple): + elif isinstance(input_data, (list, tuple)): reduced_data = reduce_list_tuple(input_data, average, all_reduce, device) - elif isinstance(input_data, int) or isinstance(input_data, float): + elif isinstance(input_data, (float, int)): reduced_data = reduce_int_float(input_data, average, all_reduce, device) else: - data_type = str(type(data)) + data_type = str(type(input_data)) raise NotImplementedError(f"Reduction on data type `{data_type}` is not implemented.") return reduced_data is_not_tensor = lambda x: not isinstance(x, torch.Tensor) -is_float_or_int = lambda x: isinstance(x, float) or isinstance(x, int) +is_float_or_int = lambda x: isinstance(x, (float, int)) def reduce_tensor(tensor, average, all_reduce, device): @@ -188,11 +193,9 @@ def reduce_int_float(input_value, average, all_reduce, device): def reduce_dict(input_dict, average, all_reduce, device): - """ Reduce a dict by extracting all of its values into a tensor and communicating it. . + """ Reduce a dict by extracting all of its values into a tensor and communicating it. Returns a dict with the same fields as input_dict, after reduction. If its values were int or float, they are converted to tensors. - - * if order of the keys matters, convert the dict to OrderedDict before passing it to the function. """ names = [] values = [] @@ -223,7 +226,7 @@ def reduce_dict(input_dict, average, all_reduce, device): def reduce_list_tuple(input_data, average, all_reduce, device): - """ Reduce a list or tuple whose elements are either tensors, floats or integers. + """ Reduce a list or tuple whose elements are either tensors, floats or integers. Returns reduced list/tuple with its elements as tensors. """ data_type = type(input_data) # save the original data type @@ -234,10 +237,10 @@ def reduce_list_tuple(input_data, average, all_reduce, device): if is_float_or_int(value): input_data[i] = torch.Tensor([value]) else: - raise NotImplementedError("List/tuple reduction supported only if \ - its values are tensors, floats or integers.") - values = torch.stack(input_data, - dim=0).to(device) # convert list/tuple of tensors to a single tensor + raise NotImplementedError("List/tuple reduction supported only if" + " its values are tensors, floats or integers.") + # Convert list/tuple of tensors to a single tensor + values = torch.stack(input_data, dim=0).to(device) if all_reduce: torch.distributed.all_reduce(values) @@ -247,5 +250,6 @@ def reduce_list_tuple(input_data, average, all_reduce, device): if average and (get_rank() == 0 or all_reduce): values /= get_world_size() - reduced_sequence = data_type(values) # cast it back to tuple or list + # Cast it back to tuple or list + reduced_sequence = data_type(values) return reduced_sequence diff --git a/midaGAN/utils/environment.py b/midaGAN/utils/environment.py index 702685de..39c3bed6 100644 --- a/midaGAN/utils/environment.py +++ b/midaGAN/utils/environment.py @@ -94,10 +94,10 @@ def setup_logging(use_stdout: Optional[bool] = True, root.addHandler(handler) if filename is not None: - fh = logging.FileHandler(filename) - fh.setLevel(log_level) - fh.setFormatter(formatter) - root.addHandler(fh) + handler = logging.FileHandler(filename) + handler.setLevel(log_level) + handler.setFormatter(formatter) + root.addHandler(handler) def set_seed(seed=0): @@ -113,20 +113,16 @@ def set_seed(seed=0): torch.backends.cudnn.benchmark = False -def threading_setup(): +def setup_threading(): """ Sets max threads for SimpleITK and Opencv. - For numpy etc. set OMP_NUM_THREADS=1 as an env var while - running the training script - E.g - OMP_NUM_THREADS=1 python tools/train.py ... + For numpy etc. set OMP_NUM_THREADS=1 as an env var while running the training script, + e.g., OMP_NUM_THREADS=1 python tools/train.py ... """ logger.warning(""" Max threads for SimpleITK and Opencv set to 1 - For numpy etc. set OMP_NUM_THREADS=1 as an env var while - running the training script - E.g - OMP_NUM_THREADS=1 python tools/train.py ... + For numpy etc. set OMP_NUM_THREADS=1 as an env var while running the training script, + e.g., OMP_NUM_THREADS=1 python tools/train.py ... """) MAX_THREADS = 1 sitk.ProcessObject_SetGlobalDefaultNumberOfThreads(MAX_THREADS) diff --git a/midaGAN/utils/io.py b/midaGAN/utils/io.py index 24b6cda1..4938c099 100644 --- a/midaGAN/utils/io.py +++ b/midaGAN/utils/io.py @@ -7,7 +7,7 @@ def mkdirs(*paths): Path(path).mkdir(parents=True, exist_ok=True) -def make_dataset_of_files(root, extensions=['.npy']): +def make_dataset_of_files(root, extensions): """The root of dataset contains files of the given extension.""" root = Path(root).resolve() assert root.is_dir(), f"{root} is not a valid directory" @@ -15,12 +15,12 @@ def make_dataset_of_files(root, extensions=['.npy']): return sorted(paths) -def make_recursive_dataset_of_files(root, extensions=['.npy']): +def make_recursive_dataset_of_files(root, extensions): root = Path(root).resolve() assert root.is_dir(), f"{root} is not a valid directory" paths = [] for ext in extensions: - paths.extend([file for file in root.rglob(f"*{ext}")]) + paths.extend(list(root.rglob(f"*{ext}"))) return sorted(paths) @@ -30,7 +30,7 @@ def has_extension(file, extensions): return any(ext in suffix for ext in extensions) -def make_dataset_of_directories(root, extensions=['.npy']): +def make_dataset_of_directories(root, extensions): """The root of dataset contains folders for each data point. Each data point folder has to have (at least) one file of the specified extension. The dataset has to define which file it takes from such folder. Useful when using a dataset that stores, for example, an image and a mask together diff --git a/midaGAN/utils/sitk_utils.py b/midaGAN/utils/sitk_utils.py index 84c88416..8b2adc59 100644 --- a/midaGAN/utils/sitk_utils.py +++ b/midaGAN/utils/sitk_utils.py @@ -87,7 +87,7 @@ def apply_mask(sitk_image, negated_mask=False): """ Apply SimpleITK mask on a SimpleITK image. - + Parameters ------------------ sitk_image: Input SimpleITK image diff --git a/midaGAN/utils/trackers/__init__.py b/midaGAN/utils/trackers/__init__.py index cdbe6aa5..6e5ad74f 100644 --- a/midaGAN/utils/trackers/__init__.py +++ b/midaGAN/utils/trackers/__init__.py @@ -5,19 +5,25 @@ def visuals_to_combined_2d_grid(visuals): # if images are 3D (5D tensors) if len(list(visuals.values())[0].shape) == 5: # TODO make nicer - # Concatenate slices that are at the same level from different visuals along - # width (each tensor from visuals.values() is NxCxDxHxW, hence dim=4) + # Concatenate slices that are at the same level from different visuals along width. + # Each tensor from visuals.values() is NxCxDxHxW, hence dim=4. combined_slices = torch.cat(tuple(visuals.values()), dim=4) - combined_slices = combined_slices[0] # we plot a single volume from the batch - combined_slices = combined_slices.permute(1, 0, 2, 3) # CxDxHxW -> DxCxHxW - # Concatenate all combined slices along height to form a single 2d image (tensors in tuple are CxHxW, hence dim=1) + # We plot a single volume from the batch + combined_slices = combined_slices[0] + # CxDxHxW -> DxCxHxW + combined_slices = combined_slices.permute(1, 0, 2, 3) + # Concatenate all combined slices along height to form a single 2d image. + # Tensors in the tuple are CxHxW, hence dim=1 combined_image = torch.cat(tuple(combined_slices), dim=1) else: # NxCxHxW combined_image = torch.cat(tuple(visuals.values()), dim=3) combined_image = combined_image[0] - combined_image = (combined_image + - 1) / 2 # [-1,1] -> [0,1]. Data range important when saving images. - name = "-".join(visuals.keys()) # e.g. "real_A-fake_B-rec_A-real_B-fake_A-rec_B" - return {'name': name, 'image': combined_image} # NOTE: image format is CxHxW + # Convert data range [-1,1] to [0,1]. Important when saving images. + combined_image = (combined_image + 1) / 2 + + # Name would be, e.g., "real_A-fake_B-rec_A-real_B-fake_A-rec_B" + name = "-".join(visuals.keys()) + # NOTE: image format is CxHxW + return {'name': name, 'image': combined_image} diff --git a/midaGAN/utils/trackers/base_tracker.py b/midaGAN/utils/trackers/base_tracker.py index 6b1be746..0c5d7fac 100644 --- a/midaGAN/utils/trackers/base_tracker.py +++ b/midaGAN/utils/trackers/base_tracker.py @@ -41,6 +41,6 @@ def end_computation_timer(self): self.t_comp = communication.reduce(self.t_comp, average=True, all_reduce=False) def end_dataloading_timer(self): - self.t_data = self.iter_start_time - self.iter_end_time # is it per sample or per batch? + self.t_data = self.iter_start_time - self.iter_end_time # reduce data loading per data point (avg) and send to the process of rank 0 self.t_data = communication.reduce(self.t_data, average=True, all_reduce=False) diff --git a/midaGAN/utils/trackers/eval_tracker.py b/midaGAN/utils/trackers/eval_tracker.py index cb1acfb5..01d2b3d2 100644 --- a/midaGAN/utils/trackers/eval_tracker.py +++ b/midaGAN/utils/trackers/eval_tracker.py @@ -41,10 +41,8 @@ def add_sample(self, visuals, metrics): """ visuals = {k: v for k, v in visuals.items() if v is not None} metrics = {k: v for k, v in metrics.items() if v is not None} - - metrics = communication.reduce( - metrics, average=True, - all_reduce=False) # reduce metrics (avg) and send to the process of rank 0 + # reduce metrics (avg) and send to the process of rank 0 + metrics = communication.reduce(metrics, average=True, all_reduce=False) if communication.get_local_rank() == 0: visuals = visuals_to_combined_2d_grid(visuals) @@ -61,18 +59,23 @@ def push_samples(self, iter_idx): self._save_image(visuals) # Averages list of dictionaries within self.metrics - averaged_metrics = { - key: sum(metric[key] for metric in self.metrics) / len(self.metrics) - for key in self.metrics[0] - } + averaged_metrics = {} + # Each element of self.metrics has the same metric keys so + # so fetching the key names from self.metrics[0] is fine + for key in self.metrics[0]: + metric_average = sum(metric[key] for metric in self.metrics) / len(self.metrics) + averaged_metrics[key] = metric_average self._log_message(iter_idx, averaged_metrics) if self.wandb: - self.wandb.log_iter(iter_idx, {}, {}, - self.visuals, - averaged_metrics, + self.wandb.log_iter(iter_idx=iter_idx, + learning_rates={}, + losses={}, + visuals=self.visuals, + metrics=averaged_metrics, mode='validation') + #TODO: Adapt eval tracker for tensorboard if self.tensorboard: raise NotImplementedError("Tensorboard evaluation tracking not implemented") diff --git a/midaGAN/utils/trackers/tensorboard_tracker.py b/midaGAN/utils/trackers/tensorboard_tracker.py index 41b44a6f..e542c24c 100644 --- a/midaGAN/utils/trackers/tensorboard_tracker.py +++ b/midaGAN/utils/trackers/tensorboard_tracker.py @@ -10,6 +10,7 @@ def close(self): self.writer.close() def log_iter(self, iter_idx, learning_rates, losses, visuals, metrics): + # TODO: metrics unused # Learning rates lr_G, lr_D = learning_rates["lr_G"], learning_rates["lr_D"] self.writer.add_scalar('Learning Rates/lr_G', lr_G, iter_idx) diff --git a/midaGAN/utils/trackers/training_tracker.py b/midaGAN/utils/trackers/training_tracker.py index 043d9250..5e672b28 100644 --- a/midaGAN/utils/trackers/training_tracker.py +++ b/midaGAN/utils/trackers/training_tracker.py @@ -38,12 +38,10 @@ def log_iter(self, learning_rates, losses, visuals, metrics): losses = {k: v for k, v in losses.items() if v is not None} metrics = {k: v for k, v in metrics.items() if v is not None} - metrics = communication.reduce( - metrics, average=True, - all_reduce=False) # reduce metrics (avg) and send to the process of rank 0 - losses = communication.reduce( - losses, average=True, - all_reduce=False) # reduce losses (avg) and send to the process of rank 0 + # reduce metrics (avg) and send to the process of rank 0 + metrics = communication.reduce(metrics, average=True, all_reduce=False) + # reduce losses (avg) and send to the process of rank 0 + losses = communication.reduce(losses, average=True, all_reduce=False) self._log_message(learning_rates, losses) diff --git a/midaGAN/utils/trackers/wandb_tracker.py b/midaGAN/utils/trackers/wandb_tracker.py index b11b059c..660edfca 100644 --- a/midaGAN/utils/trackers/wandb_tracker.py +++ b/midaGAN/utils/trackers/wandb_tracker.py @@ -23,7 +23,7 @@ def __init__(self, conf): ] def log_iter(self, iter_idx, learning_rates, losses, visuals, metrics, mode='train'): - """TODO""" + """""" mode = mode.capitalize() log_dict = {} @@ -52,8 +52,7 @@ def create_wandb_images(self, visuals, image_threshold=None): """ Create wandb images from visuals """ - # Check if visuals is a list of images and create a list - # of wandb.Image + # Check if visuals is a list of images and create a list of wandb.Image's if isinstance(visuals, list): wandb_images = [] for idx, visual in enumerate(visuals): @@ -62,12 +61,10 @@ def create_wandb_images(self, visuals, image_threshold=None): visual['name'] = f"Sample: {idx} {visual['name']}" wandb_images.append(self._wandb_image_from_visual(visual)) - return wandb_images # If visual is an image then a single wandb.Image is created - else: - return self._wandb_image_from_visual(visuals) + return self._wandb_image_from_visual(visuals) def _wandb_image_from_visual(self, visual, image_threshold=None): """ @@ -75,7 +72,8 @@ def _wandb_image_from_visual(self, visual, image_threshold=None): https://docs.wandb.ai/library/log#images-and-overlays """ name, image = visual['name'], visual['image'] - image = image.permute(1, 2, 0) # CxHxW -> HxWxC + # CxHxW -> HxWxC + image = image.permute(1, 2, 0) # Check if a threshold is defined while creating the wandb image. if image_threshold: diff --git a/notebooks/Mask_and_Bound_Proton.ipynb b/notebooks/Mask_and_Bound_Proton.ipynb index dea53f74..f6e45145 100644 --- a/notebooks/Mask_and_Bound_Proton.ipynb +++ b/notebooks/Mask_and_Bound_Proton.ipynb @@ -70,7 +70,7 @@ "\n", "root_path = Path('/mnt/14F117A002BC354B/maastro_proton_cbct_ct_processed/')\n", "\n", - "# take from the dataset class of the proton_cbct_to_ct project\n", + "# take from the dataset class of the maastro_lung_proton_cbct_to_ct project\n", "paths_CBCT = []\n", "paths_CT = []\n", "for patient in root_path.iterdir():\n", diff --git a/projects/brats_flair_to_t1w/brats_dataset.py b/projects/brats_flair_to_t1w/brats_dataset.py index 2562acc4..1b11eac5 100644 --- a/projects/brats_flair_to_t1w/brats_dataset.py +++ b/projects/brats_flair_to_t1w/brats_dataset.py @@ -20,7 +20,8 @@ class BratsDatasetConfig(configs.base.BaseDatasetConfig): name: str = "BratsDataset" patch_size: Tuple[int, int, int] = (32, 32, 32) - focal_region_proportion: float = 0 # Proportion of focal region size compared to original volume size + # Proportion of focal region size compared to original volume size + focal_region_proportion: float = 0 source_sequence: str = "flair" target_sequence: str = "t1w" @@ -38,10 +39,10 @@ def get_mri_sequence(sitk_image, sequence_name): size[3] = 0 index = [0, 0, 0, z_index] - Extractor = sitk.ExtractImageFilter() - Extractor.SetSize(size) - Extractor.SetIndex(index) - return Extractor.Execute(sitk_image) + extractor = sitk.ExtractImageFilter() + extractor.SetSize(size) + extractor.SetIndex(index) + return extractor.Execute(sitk_image) class BratsDataset(Dataset): diff --git a/projects/dummy/dummy.yaml b/projects/dummy/dummy.yaml deleted file mode 100644 index 783f3174..00000000 --- a/projects/dummy/dummy.yaml +++ /dev/null @@ -1,28 +0,0 @@ - -use_cuda: True -n_iters: 50000 -n_iters_decay: 0 -batch_size: 1 - -logging: - checkpoint_dir: "./checkpoints/dummy/" - wandb: null - tensorboard: False - log_freq: 1 - checkpoint_freq: 1000 - -dataset: - name: "DummyDataset" - root: '.' - -gan: - name: "PiCycleGAN" - -generator: - name: "Vnet3D" - use_memory_saving: False - in_channels: 1 - -discriminator: - name: "PatchGAN3D" - n_layers: 3 \ No newline at end of file diff --git a/projects/for_jonas/slice_based_dataset.py b/projects/for_jonas/slice_based_dataset.py index 67b9981e..6311479d 100644 --- a/projects/for_jonas/slice_based_dataset.py +++ b/projects/for_jonas/slice_based_dataset.py @@ -41,12 +41,10 @@ def __init__(self, conf): elif conf.dataset.crop: self.crop_size = conf.dataset.crop_size - dataset_summary = pd.read_csv(Path(conf.dataset.root) / 'dataset_summary.csv') + data_summary = pd.read_csv(Path(conf.dataset.root) / 'dataset_summary.csv') # Filter out rows by their domain - self.domain_A_summary = dataset_summary[dataset_summary["volume_filename"].str.startswith( - 'A')] - self.domain_B_summary = dataset_summary[dataset_summary["volume_filename"].str.startswith( - 'B')] + self.domain_A_summary = data_summary[data_summary["volume_filename"].str.startswith('A')] + self.domain_B_summary = data_summary[data_summary["volume_filename"].str.startswith('B')] self.num_datapoints_A = len(self.domain_A_summary) self.num_datapoints_B = len(self.domain_B_summary) diff --git a/projects/lidc_intervendor_ct/ct_dataset.py b/projects/lidc_intervendor_ct/ct_dataset.py deleted file mode 100644 index 4fe08f90..00000000 --- a/projects/lidc_intervendor_ct/ct_dataset.py +++ /dev/null @@ -1,70 +0,0 @@ -from pathlib import Path -import random -import numpy as np -import torch -from torch.utils.data import Dataset -from midaGAN.utils.io import make_dataset_of_files, load_json -from midaGAN.data.utils.normalization import min_max_normalize -from midaGAN.data.utils.stochastic_focal_patching import StochasticFocalPatchSampler - -# Config imports -from typing import Tuple -from dataclasses import dataclass, field -from omegaconf import MISSING -from midaGAN import configs - - -@dataclass -class CTDatasetConfig(configs.base.BaseDatasetConfig): - name: str = "CTDataset" - patch_size: Tuple[int] = field(default_factory=lambda: (32, 32, 32)) - focal_region_proportion: float = 0.2 # Proportion of focal region size compared to original volume size - - -EXTENSIONS = ['.npy'] - - -class CTDataset(Dataset): - - def __init__(self, conf): - dir_A = Path(conf.dataset.root) / 'A' - dir_B = Path(conf.dataset.root) / 'B' - self.A_paths = make_dataset_of_files(dir_A, EXTENSIONS) - self.B_paths = make_dataset_of_files(dir_B, EXTENSIONS) - self.A_size = len(self.A_paths) - self.B_size = len(self.B_paths) - - # Dataset range of values information for normalization - norm_A = Path(conf.dataset.root) / 'normalize_A.json' - norm_B = Path(conf.dataset.root) / 'normalize_B.json' - self.norm_A = load_json(norm_A) - self.norm_B = load_json(norm_B) - - patch_size = conf.dataset.patch_size - focal_region_proportion = conf.dataset.focal_region_proportion - self.patch_sampler = StochasticFocalPatchSampler(patch_size, focal_region_proportion) - - def __getitem__(self, index): - index_A = index % self.A_size - index_B = random.randint(0, self.B_size - 1) - - A_path = self.A_paths[index_A] - B_path = self.B_paths[index_B] - - A = torch.Tensor(np.load(A_path)) - B = torch.Tensor(np.load(B_path)) - - A, B = self.patch_sampler.get_patch_pair(A, B) # Extract patches - - # Normalize Hounsfield units to range [-1,1] - A = min_max_normalize(A, self.norm_A["min"], self.norm_A["max"]) - B = min_max_normalize(B, self.norm_B["min"], self.norm_B["max"]) - - # Add channel dimension (1 = grayscale) - A = A.unsqueeze(0) - B = B.unsqueeze(0) - - return {'A': A, 'B': B} - - def __len__(self): - return max(self.A_size, self.B_size) diff --git a/projects/maastro_cbct_to_ct/cbcttoct2d_dataset.py b/projects/maastro_cbct_to_ct/cbcttoct2d_dataset.py deleted file mode 100755 index 12ce627b..00000000 --- a/projects/maastro_cbct_to_ct/cbcttoct2d_dataset.py +++ /dev/null @@ -1,159 +0,0 @@ -from pathlib import Path -import random -import logging -import numpy as np -import torch -from torch.utils.data import Dataset - -import midaGAN -from midaGAN.utils.io import make_dataset_of_directories, load_json -from midaGAN.utils import sitk_utils -from midaGAN.data.utils.normalization import min_max_normalize, min_max_denormalize -from midaGAN.data.utils.slice_sampler import SliceSampler - -# Config imports -from typing import Tuple -from dataclasses import dataclass, field -from omegaconf import MISSING -from midaGAN import configs - -logger = logging.getLogger(__name__) - -EXTENSIONS = ['.nrrd'] - - -@dataclass -class CBCTtoCT2DDatasetConfig(configs.base.BaseDatasetConfig): - name: str = "CBCTtoCTDataset" - load_size: int = 256 - hounsfield_units_range: Tuple[int, int] = field( - default_factory=lambda: (-1000, 2000)) #TODO: what should be the default range - focal_region_proportion: float = 0.2 # Proportion of focal region size compared to original volume size - enable_cache: bool = False - image_channels: int = 1 - - -class CBCTtoCT2DDataset(Dataset): - - def __init__(self, conf): - dir_CBCT = Path(conf.dataset.root) / 'CBCT' - dir_CT = Path(conf.dataset.root) / 'CT' - self.paths_CBCT = make_dataset_of_directories(dir_CBCT, EXTENSIONS) - self.paths_CT = make_dataset_of_directories(dir_CT, EXTENSIONS) - self.num_datapoints_CBCT = len(self.paths_CBCT) - self.num_datapoints_CT = len(self.paths_CT) - - # Min and max HU values for clipping and normalization - self.hu_min, self.hu_max = conf.dataset.hounsfield_units_range - - focal_region_proportion = conf.dataset.focal_region_proportion - self.patch_size = np.array([conf.dataset.load_size] * 2) - self.slice_sampler = SliceSampler(self.patch_size, focal_region_proportion) - self.conf = conf - self.data_cache = {} - - def add_to_cache(self, path, data): - self.data_cache[path] = data - - def clear_cache(self): - self.data_cache = {} - - def __getitem__(self, index): - index_CBCT = index % self.num_datapoints_CBCT - index_CT = random.randint(0, self.num_datapoints_CT - 1) - - path_CBCT = Path(self.paths_CBCT[index_CBCT]) / 'CT.nrrd' - path_CT = Path(self.paths_CT[index_CT]) / 'CT.nrrd' - - # load nrrd as SimpleITK objects - - if self.conf.dataset.enable_cache: - if path_CBCT not in self.data_cache: - CBCT = sitk_utils.load(path_CBCT) - self.add_to_cache(path_CBCT, CBCT) - else: - CBCT = self.data_cache[path_CBCT] - - if path_CT not in self.data_cache: - CT = sitk_utils.load(path_CT) - self.add_to_cache(path_CT, CT) - else: - CT = self.data_cache[path_CT] - else: - CBCT = sitk_utils.load(path_CBCT) - CT = sitk_utils.load(path_CT) - - CBCT = sitk_utils.get_npy(CBCT) - CT = sitk_utils.get_npy(CT) - - # Extract patches - CBCT_slice, CT_slice = self.slice_sampler.get_slice_pair(CBCT, CT) - - CBCT_slice = torch.Tensor(CBCT_slice) - CT_slice = torch.Tensor(CT_slice) - - # Limits the lowest and highest HU unit - CBCT_slice = torch.clamp(CBCT_slice, self.hu_min, self.hu_max) - CT_slice = torch.clamp(CT_slice, self.hu_min, self.hu_max) - - # Normalize Hounsfield units to range [-1,1] - CBCT_slice = min_max_normalize(CBCT_slice, self.hu_min, self.hu_max) - CT_slice = min_max_normalize(CT_slice, self.hu_min, self.hu_max) - - # Add channel dimension (1 = grayscale) - CBCT_slice = CBCT_slice.unsqueeze(0) - CT_slice = CT_slice.unsqueeze(0) - - return {'A': CBCT_slice, 'B': CT_slice} - - def __len__(self): - return max(self.num_datapoints_CBCT, self.num_datapoints_CT) - - -# @dataclass -# class CBCTtoCT2DInferenceDatasetConfig(configs.base.BaseDatasetConfig): -# name: str = "CBCTtoCTInferenceDataset" -# hounsfield_units_range: Tuple[int, int] = field(default_factory=lambda: (-1000, 2000)) #TODO: what should be the default range - -# class CBCTtoCT2DInferenceDataset(Dataset): -# def __init__(self, conf): -# self.paths = make_dataset_of_directories(conf.dataset.root, EXTENSIONS) -# self.num_datapoints = len(self.paths) -# # Min and max HU values for clipping and normalization -# self.hu_min, self.hu_max = conf.dataset.hounsfield_units_range - -# def __getitem__(self, index): -# path = str(Path(self.paths[index]) / 'CT.nrrd') -# # load nrrd as SimpleITK objects -# volume = sitk_utils.load(path) -# metadata = (path, -# volume.GetOrigin(), -# volume.GetSpacing(), -# volume.GetDirection(), -# sitk_utils.get_npy_dtype(volume)) - -# volume = sitk_utils.get_tensor(volume) -# # Limits the lowest and highest HU unit -# volume = torch.clamp(volume, self.hu_min, self.hu_max) -# # Normalize Hounsfield units to range [-1,1] -# volume = min_max_normalize(volume, self.hu_min, self.hu_max) -# # Add channel dimension (1 = grayscale) -# volume = volume.unsqueeze(0) - -# return volume, metadata - -# def __len__(self): -# return self.num_datapoints - -# def save(self, tensor, metadata, output_dir): -# tensor = tensor.squeeze() -# tensor = min_max_denormalize(tensor, self.hu_min, self.hu_max) - -# datapoint_path, origin, spacing, direction, dtype = metadata -# sitk_image = sitk_utils.tensor_to_sitk_image(tensor, origin, spacing, direction, dtype) - -# # Dataset used has a directory per each datapoint, the name of each datapoint's dir is used to save the output -# datapoint_name = Path(str(datapoint_path)).parent.name -# save_path = Path(output_dir) / Path(datapoint_name).with_suffix('.nrrd') - -# sitk_utils.write(sitk_image, save_path) diff --git a/projects/maastro_cbct_to_ct/cbcttoct_dataset.py b/projects/maastro_cbct_to_ct/cbcttoct_dataset.py deleted file mode 100644 index 01dfd7ef..00000000 --- a/projects/maastro_cbct_to_ct/cbcttoct_dataset.py +++ /dev/null @@ -1,153 +0,0 @@ -from pathlib import Path -import random -import logging -import numpy as np -import torch -from torch.utils.data import Dataset - -import midaGAN -from midaGAN.utils.io import make_dataset_of_directories, load_json -from midaGAN.utils import sitk_utils -from midaGAN.data.utils.normalization import min_max_normalize, min_max_denormalize -from midaGAN.data.utils.registration_methods import truncate_CT_to_scope_of_CBCT -from midaGAN.data.utils.stochastic_focal_patching import StochasticFocalPatchSampler - -# Config imports -from typing import Tuple -from dataclasses import dataclass, field -from omegaconf import MISSING -from midaGAN import configs - -logger = logging.getLogger(__name__) - -EXTENSIONS = ['.nrrd'] - - -@dataclass -class CBCTtoCTDatasetConfig(configs.base.BaseDatasetConfig): - name: str = "CBCTtoCTDataset" - patch_size: Tuple[int, int, int] = field(default_factory=lambda: (32, 32, 32)) - hounsfield_units_range: Tuple[int, int] = field( - default_factory=lambda: (-1000, 2000)) #TODO: what should be the default range - focal_region_proportion: float = 0.2 # Proportion of focal region size compared to original volume size - - -class CBCTtoCTDataset(Dataset): - - def __init__(self, conf): - dir_CBCT = Path(conf.dataset.root) / 'CBCT' - dir_CT = Path(conf.dataset.root) / 'CT' - self.paths_CBCT = make_dataset_of_directories(dir_CBCT, EXTENSIONS) - self.paths_CT = make_dataset_of_directories(dir_CT, EXTENSIONS) - self.num_datapoints_CBCT = len(self.paths_CBCT) - self.num_datapoints_CT = len(self.paths_CT) - - # Min and max HU values for clipping and normalization - self.hu_min, self.hu_max = conf.dataset.hounsfield_units_range - - focal_region_proportion = conf.dataset.focal_region_proportion - self.patch_size = np.array(conf.dataset.patch_size) - self.patch_sampler = StochasticFocalPatchSampler(self.patch_size, focal_region_proportion) - - def __getitem__(self, index): - index_CBCT = index % self.num_datapoints_CBCT - index_CT = random.randint(0, self.num_datapoints_CT - 1) - - path_CBCT = Path(self.paths_CBCT[index_CBCT]) / 'CT.nrrd' - path_CT = Path(self.paths_CT[index_CT]) / 'CT.nrrd' - - # load nrrd as SimpleITK objects - CBCT = sitk_utils.load(path_CBCT) - CT = sitk_utils.load(path_CT) - - # TODO: make a function - if (sitk_utils.is_image_smaller_than(CBCT, self.patch_size) or - sitk_utils.is_image_smaller_than(CT, self.patch_size)): - raise ValueError("Volume size not smaller than the defined patch size.\ - \nCBCT: {} \nCT: {} \npatch_size: {}."\ - .format(sitk_utils.get_torch_like_size(CBCT), - sitk_utils.get_torch_like_size(CT), - self.patch_size)) - - -# limit CT so that it only contains part of the body shown in CBCT - CT_truncated = truncate_CT_to_scope_of_CBCT(CT, CBCT) - if sitk_utils.is_image_smaller_than(CT_truncated, self.patch_size): - logger.info( - "Post-registration truncated CT is smaller than the defined patch size. Passing the whole CT volume." - ) - del CT_truncated - else: - CT = CT_truncated - - CBCT = sitk_utils.get_tensor(CBCT) - CT = sitk_utils.get_tensor(CT) - - # Extract patches - CBCT, CT = self.patch_sampler.get_patch_pair(CBCT, CT) - - # Limits the lowest and highest HU unit - CBCT = torch.clamp(CBCT, self.hu_min, self.hu_max) - CT = torch.clamp(CT, self.hu_min, self.hu_max) - - # Normalize Hounsfield units to range [-1,1] - CBCT = min_max_normalize(CBCT, self.hu_min, self.hu_max) - CT = min_max_normalize(CT, self.hu_min, self.hu_max) - - # Add channel dimension (1 = grayscale) - CBCT = CBCT.unsqueeze(0) - CT = CT.unsqueeze(0) - - return {'A': CBCT, 'B': CT} - - def __len__(self): - return max(self.num_datapoints_CBCT, self.num_datapoints_CT) - - -@dataclass -class CBCTtoCTInferenceDatasetConfig(configs.base.BaseDatasetConfig): - name: str = "CBCTtoCTInferenceDataset" - hounsfield_units_range: Tuple[int, int] = field( - default_factory=lambda: (-1000, 2000)) #TODO: what should be the default range - - -class CBCTtoCTInferenceDataset(Dataset): - - def __init__(self, conf): - self.paths = make_dataset_of_directories(conf.dataset.root, EXTENSIONS) - self.num_datapoints = len(self.paths) - # Min and max HU values for clipping and normalization - self.hu_min, self.hu_max = conf.dataset.hounsfield_units_range - - def __getitem__(self, index): - path = str(Path(self.paths[index]) / 'CT.nrrd') - # load nrrd as SimpleITK objects - volume = sitk_utils.load(path) - metadata = (path, volume.GetOrigin(), volume.GetSpacing(), volume.GetDirection(), - sitk_utils.get_npy_dtype(volume)) - - volume = sitk_utils.get_tensor(volume) - # Limits the lowest and highest HU unit - volume = torch.clamp(volume, self.hu_min, self.hu_max) - # Normalize Hounsfield units to range [-1,1] - volume = min_max_normalize(volume, self.hu_min, self.hu_max) - # Add channel dimension (1 = grayscale) - volume = volume.unsqueeze(0) - - return volume, metadata - - def __len__(self): - return self.num_datapoints - - def save(self, tensor, metadata, output_dir): - tensor = tensor.squeeze() - tensor = min_max_denormalize(tensor, self.hu_min, self.hu_max) - - datapoint_path, origin, spacing, direction, dtype = metadata - sitk_image = sitk_utils.tensor_to_sitk_image(tensor, origin, spacing, direction, dtype) - - # Dataset used has a directory per each datapoint, the name of each datapoint's dir is used to save the output - datapoint_name = Path(str(datapoint_path)).parent.name - save_path = Path(output_dir) / Path(datapoint_name).with_suffix('.nrrd') - - sitk_utils.write(sitk_image, save_path) diff --git a/projects/maastro_cbct_to_ct/experiments/2d_ex1.yaml b/projects/maastro_cbct_to_ct/experiments/2d_ex1.yaml deleted file mode 100755 index 997e661e..00000000 --- a/projects/maastro_cbct_to_ct/experiments/2d_ex1.yaml +++ /dev/null @@ -1,40 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: False - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_2dex1" - wandb: - project: "CBCT-CT-2D" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCT2DDataset" - root: "/workspace/data/2D_CBCT_CT_data" - num_workers: 8 - load_size: 512 - -gan: - name: "CycleGAN" - -generator: - name: "Unet2D" - in_channels: 1 - -discriminator: - name: "PatchGAN2D" - - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0004 - ssim_type: "ThreeComponentSSIM" diff --git a/projects/maastro_cbct_to_ct/experiments/2d_ex2.yaml b/projects/maastro_cbct_to_ct/experiments/2d_ex2.yaml deleted file mode 100755 index 1ea7f6b2..00000000 --- a/projects/maastro_cbct_to_ct/experiments/2d_ex2.yaml +++ /dev/null @@ -1,39 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: False - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_2dex2" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCT2DDataset" - root: "/workspace/data/2D_CBCT_CT_data" - num_workers: 8 - load_size: 512 - -gan: - name: "CycleGAN" - -generator: - name: "Unet2D" - in_channels: 1 - -discriminator: - name: "PatchGAN2D" - - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0004 diff --git a/projects/maastro_cbct_to_ct/experiments/ex1.yaml b/projects/maastro_cbct_to_ct/experiments/ex1.yaml deleted file mode 100644 index bf6f5d68..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex1.yaml +++ /dev/null @@ -1,28 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 1 - -logging: - checkpoint_dir: ./checkpoints/cbct_ex2/ - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 10000 - -dataset: - name: "CBCTtoCTDataset" - root: "/work/ft002207/CT_CBCT_dataset_resampled" - hounsfield_units_range: [-1000, 2000] - num_workers: 8 - patch_size: [32, 288, 288] - -gan: - name: "PiCycleGAN" - -generator: - name: "Vnet3D" - -discriminator: - name: "PatchGAN3D" diff --git a/projects/maastro_cbct_to_ct/experiments/ex10.yaml b/projects/maastro_cbct_to_ct/experiments/ex10.yaml deleted file mode 100644 index 02033d21..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex10.yaml +++ /dev/null @@ -1,43 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex10" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 5000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "CycleGAN" - -generator: - name: Vnets3D - in_channels: 1 - use_memory_saving: false - use_inverse: false - -discriminator: - name: "Discrim3D" - n_layers: 3 - in_channels: 1 - input_size: [48, 224, 224] # TODO: same as dataset.patch_size - how to make it less repetive and intuitive? - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0001 diff --git a/projects/maastro_cbct_to_ct/experiments/ex11.yaml b/projects/maastro_cbct_to_ct/experiments/ex11.yaml deleted file mode 100644 index 39f46b3a..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex11.yaml +++ /dev/null @@ -1,42 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex11" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 5000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "CycleGAN" - -generator: - name: Vnets3D - in_channels: 1 - use_memory_saving: false - use_inverse: false - -discriminator: - name: "PatchGAN3D" - n_layers: 3 - in_channels: 1 - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0001 diff --git a/projects/maastro_cbct_to_ct/experiments/ex12.yaml b/projects/maastro_cbct_to_ct/experiments/ex12.yaml deleted file mode 100644 index 2e77ebfb..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex12.yaml +++ /dev/null @@ -1,42 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex12" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "CycleGAN" - -generator: - name: Vnets3D - in_channels: 1 - use_memory_saving: false - use_inverse: false - -discriminator: - name: "PatchGAN3D" - n_layers: 2 - in_channels: 1 - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0001 - lr_G: 0.0002 diff --git a/projects/maastro_cbct_to_ct/experiments/ex13.yaml b/projects/maastro_cbct_to_ct/experiments/ex13.yaml deleted file mode 100644 index 7380ce5a..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex13.yaml +++ /dev/null @@ -1,42 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex13" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "CycleGAN" - -generator: - name: Vnets3D - in_channels: 1 - use_memory_saving: false - use_inverse: false - -discriminator: - name: "PatchGAN3D" - n_layers: 2 - in_channels: 1 - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0004 diff --git a/projects/maastro_cbct_to_ct/experiments/ex14.yaml b/projects/maastro_cbct_to_ct/experiments/ex14.yaml deleted file mode 100644 index e9221076..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex14.yaml +++ /dev/null @@ -1,43 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex14" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: Vnets3D - in_channels: 1 - use_memory_saving: false - use_inverse: True - is_separable: True - -discriminator: - name: "PatchGAN3D" - n_layers: 2 - in_channels: 1 - -optimizer: - lambda_A: 10.0 - lambda_B: 10.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0004 diff --git a/projects/maastro_cbct_to_ct/experiments/ex15.yaml b/projects/maastro_cbct_to_ct/experiments/ex15.yaml deleted file mode 100644 index 8b5a18d9..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex15.yaml +++ /dev/null @@ -1,45 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex15" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: Vnet3D - in_channels: 1 - use_memory_saving: false - use_inverse: True - is_separable: False - down_blocks: [2, 2, 3, 3] - up_blocks: [3, 3, 3, 3] - -discriminator: - name: "PatchGAN3D" - n_layers: 3 - in_channels: 1 - -optimizer: - lambda_A: 25.0 - lambda_B: .0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0004 diff --git a/projects/maastro_cbct_to_ct/experiments/ex16.yaml b/projects/maastro_cbct_to_ct/experiments/ex16.yaml deleted file mode 100644 index fb1bed93..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex16.yaml +++ /dev/null @@ -1,45 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex16" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 2000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: Vnet3D - in_channels: 1 - use_memory_saving: False - use_inverse: True - is_separable: False - down_blocks: [2, 2, 3] - up_blocks: [3, 3, 3] - -discriminator: - name: "PatchGAN3D" - n_layers: 2 - in_channels: 1 - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0004 diff --git a/projects/maastro_cbct_to_ct/experiments/ex2.yaml b/projects/maastro_cbct_to_ct/experiments/ex2.yaml deleted file mode 100644 index 503c0186..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex2.yaml +++ /dev/null @@ -1,32 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: ./checkpoints/cbct_ex2/ - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 10000 - -dataset: - name: "CBCTtoCTDataset" - root: "/work/ft002207/CT_CBCT_dataset_resampled" - hounsfield_units_range: [-1000, 2000] - num_workers: 8 - patch_size: [32, 320, 320] - -gan: - name: "CycleGAN" - -generator: - name: "Vnet3D" - use_inverse: False - use_memory_saving: True - -discriminator: - name: "PatchGAN3D" - diff --git a/projects/maastro_cbct_to_ct/experiments/ex4.yaml b/projects/maastro_cbct_to_ct/experiments/ex4.yaml deleted file mode 100644 index bbe2f7d8..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex4.yaml +++ /dev/null @@ -1,39 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 1 -mixed_precision: False - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex4" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 10000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [32, 128, 128] - -gan: - name: "CycleGAN" - -generator: - name: "Unet3D" - num_downs: 5 - -discriminator: - name: "PatchGAN3D" - n_layers: 3 - -optimizer: - lambda_A: 20.0 - lambda_B: 20.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0002 diff --git a/projects/maastro_cbct_to_ct/experiments/ex5.yaml b/projects/maastro_cbct_to_ct/experiments/ex5.yaml deleted file mode 100644 index 69b2c82c..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex5.yaml +++ /dev/null @@ -1,39 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 8 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex5" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 5000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [32, 128, 128] - -gan: - name: "CycleGAN" - -generator: - name: "Unet3D" - num_downs: 5 - -discriminator: - name: "PatchGAN3D" - n_layers: 2 - -optimizer: - lambda_A: 10.0 - lambda_B: 10.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.002 - lr_G: 0.002 diff --git a/projects/maastro_cbct_to_ct/experiments/ex6.yaml b/projects/maastro_cbct_to_ct/experiments/ex6.yaml deleted file mode 100644 index 4aaa6093..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex6.yaml +++ /dev/null @@ -1,40 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 2 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex6" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 10000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: "Vnet3D" - use_inverse: True - use_memory_saving: False - -discriminator: - name: "PatchGAN3D" - n_layers: 3 - -optimizer: - lambda_A: 10.0 - lambda_B: 10.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0002 diff --git a/projects/maastro_cbct_to_ct/experiments/ex7.yaml b/projects/maastro_cbct_to_ct/experiments/ex7.yaml deleted file mode 100644 index c47b7a5c..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex7.yaml +++ /dev/null @@ -1,43 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex7" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 10000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: "Piresnet3D" - use_memory_saving: True - use_inverse: True - depth: 3 - in_channels: 1 - -discriminator: - name: "PatchGAN3D" - n_layers: 3 - in_channels: 1 - -optimizer: - lambda_A: 5.0 - lambda_B: 5.0 - lambda_identity: 0.0 - lambda_inverse: 0.05 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0002 diff --git a/projects/maastro_cbct_to_ct/experiments/ex8.yaml b/projects/maastro_cbct_to_ct/experiments/ex8.yaml deleted file mode 100644 index a18b09dc..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex8.yaml +++ /dev/null @@ -1,43 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 200000 -n_iters_decay: 0 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex8" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 5000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: "Vnet3D" - use_inverse: True - use_memory_saving: False - in_channels: 1 - -discriminator: - name: "Discrim3D" - n_layers: 3 - in_channels: 1 - input_size: [48, 224, 224] # TODO: same as dataset.patch_size - how to make it less repetive and intuitive? - -optimizer: - lambda_A: 10.0 - lambda_B: 10.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0002 diff --git a/projects/maastro_cbct_to_ct/experiments/ex9.yaml b/projects/maastro_cbct_to_ct/experiments/ex9.yaml deleted file mode 100644 index f1ccf4aa..00000000 --- a/projects/maastro_cbct_to_ct/experiments/ex9.yaml +++ /dev/null @@ -1,44 +0,0 @@ -project_dir: "./projects/maastro_cbct_to_ct" -use_cuda: True -n_iters: 20000 -n_iters_decay: 20000 -batch_size: 1 -mixed_precision: True - -logging: - checkpoint_dir: "./checkpoints/cbcttoct_ex9" - wandb: - project: "my-project" - log_freq: 50 - checkpoint_freq: 5000 - -dataset: - name: "CBCTtoCTDataset" - root: "/workspace/data/lung1_ct_cbct_nrrd_resampled/" - num_workers: 16 - patch_size: [48, 224, 224] - -gan: - name: "PiCycleGAN" - -generator: - name: Piresnet3D - in_channels: 1 - use_memory_saving: true - use_inverse: true - first_layer_channels: 32 - depth: 1 - -discriminator: - name: "PatchGAN3D" - n_layers: 3 - in_channels: 1 - -optimizer: - lambda_A: 25.0 - lambda_B: 25.0 - lambda_identity: 0.0 - lambda_inverse: 0.0 - proportion_ssim: 0.84 - lr_D: 0.0002 - lr_G: 0.0002 diff --git a/projects/maastro_cbct_to_ct/jobscripts/ex2.sh b/projects/maastro_cbct_to_ct/jobscripts/ex2.sh deleted file mode 100644 index 5dfd283c..00000000 --- a/projects/maastro_cbct_to_ct/jobscripts/ex2.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -### #SBATCH directives need to be in the first part of the jobscript - -### Job name -#SBATCH --job-name=ex2 - -### Output path for stdout and stderr -### %J is the job ID, %I is the array ID -#SBATCH --output=output_%J.txt - -### Request the time you need for execution. The full format is D-HH:MM:SS -### You must at least specify minutes OR days and hours and may add or -### leave out any other parameters -#SBATCH --time=5-00:00:00 - -### Request the amount of memory you need for your job. -### You can specify this in either MB (1024M) or GB (4G). - -#SBATCH --nodes=1 -#SBATCH --mem=40G -#SBATCH --cpus-per-task=24 -#SBATCH --ntasks-per-node=1 - -### Request a host with a Volta GPU -### If you need two GPUs, change the number accordingly -#SBATCH --gres=gpu:volta:2 - -### if needed: switch to your working directory (where you saved your program) -export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK - -source $HOME/.bashrc -cd $HOME/midaGAN/ - -### your code goes here, the second part of the jobscript -# DONT FORGET TO UPDATE THE SBATCH jobname -python -m torch.distributed.launch --use_env --nproc_per_node 2 tools/train.py config=projects/maastro_cbct_to_ct/experiments/ex2.yaml diff --git a/projects/proton_cbct_to_ct/cbcttoct_dataset.py b/projects/maastro_lung_proton_cbct_to_ct/cbcttoct_dataset.py similarity index 100% rename from projects/proton_cbct_to_ct/cbcttoct_dataset.py rename to projects/maastro_lung_proton_cbct_to_ct/cbcttoct_dataset.py diff --git a/projects/proton_cbct_to_ct/experiments/ex1.yaml b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex1.yaml similarity index 93% rename from projects/proton_cbct_to_ct/experiments/ex1.yaml rename to projects/maastro_lung_proton_cbct_to_ct/experiments/ex1.yaml index 059063d0..c0058a1c 100644 --- a/projects/proton_cbct_to_ct/experiments/ex1.yaml +++ b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex1.yaml @@ -1,4 +1,4 @@ -project_dir: "./projects/proton_cbct_to_ct" +project_dir: "./projects/maastro_lung_proton_cbct_to_ct" use_cuda: True n_iters: 20000 n_iters_decay: 20000 diff --git a/projects/proton_cbct_to_ct/experiments/ex16.yaml b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex16.yaml similarity index 93% rename from projects/proton_cbct_to_ct/experiments/ex16.yaml rename to projects/maastro_lung_proton_cbct_to_ct/experiments/ex16.yaml index 13ca1e5d..1e2c8fe5 100644 --- a/projects/proton_cbct_to_ct/experiments/ex16.yaml +++ b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex16.yaml @@ -1,4 +1,4 @@ -project_dir: "./projects/proton_cbct_to_ct" +project_dir: "./projects/maastro_lung_proton_cbct_to_ct" use_cuda: True n_iters: 20000 n_iters_decay: 20000 diff --git a/projects/proton_cbct_to_ct/experiments/ex1_continued.yaml b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex1_continued.yaml similarity index 94% rename from projects/proton_cbct_to_ct/experiments/ex1_continued.yaml rename to projects/maastro_lung_proton_cbct_to_ct/experiments/ex1_continued.yaml index 6d8dc4b8..8ffaa50f 100644 --- a/projects/proton_cbct_to_ct/experiments/ex1_continued.yaml +++ b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex1_continued.yaml @@ -1,4 +1,4 @@ -project_dir: "./projects/proton_cbct_to_ct" +project_dir: "./projects/maastro_lung_proton_cbct_to_ct" use_cuda: True n_iters: 45000 # starting from 40000 n_iters_decay: 5000 diff --git a/projects/proton_cbct_to_ct/experiments/ex1_cut.yaml b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex1_cut.yaml similarity index 94% rename from projects/proton_cbct_to_ct/experiments/ex1_cut.yaml rename to projects/maastro_lung_proton_cbct_to_ct/experiments/ex1_cut.yaml index d602e2ff..ba15e36c 100644 --- a/projects/proton_cbct_to_ct/experiments/ex1_cut.yaml +++ b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex1_cut.yaml @@ -1,4 +1,4 @@ -project_dir: "./projects/proton_cbct_to_ct" +project_dir: "./projects/maastro_lung_proton_cbct_to_ct" use_cuda: True n_iters: 20000 n_iters_decay: 20000 diff --git a/projects/proton_cbct_to_ct/experiments/ex2.yaml b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex2.yaml similarity index 93% rename from projects/proton_cbct_to_ct/experiments/ex2.yaml rename to projects/maastro_lung_proton_cbct_to_ct/experiments/ex2.yaml index 5b81d72f..34ab944d 100644 --- a/projects/proton_cbct_to_ct/experiments/ex2.yaml +++ b/projects/maastro_lung_proton_cbct_to_ct/experiments/ex2.yaml @@ -1,4 +1,4 @@ -project_dir: "./projects/proton_cbct_to_ct" +project_dir: "./projects/maastro_lung_proton_cbct_to_ct" use_cuda: True n_iters: 20000 n_iters_decay: 20000 diff --git a/tools/infer.py b/tools/infer.py index 43469986..d7bb981d 100644 --- a/tools/infer.py +++ b/tools/infer.py @@ -16,7 +16,8 @@ def main(): - communication.init_distributed() # inits distributed mode if ran with torch.distributed.launch + # inits distributed mode if ran with torch.distributed.launch + communication.init_distributed() conf = builders.build_inference_conf() environment.setup_logging_with_config(conf) diff --git a/tools/train.py b/tools/train.py index 3354e4c2..e835e607 100644 --- a/tools/train.py +++ b/tools/train.py @@ -17,8 +17,9 @@ def main(): - environment.threading_setup() - communication.init_distributed() # inits distributed mode if ran with torch.distributed.launch + environment.setup_threading() + # inits distributed mode if ran with torch.distributed.launch + communication.init_distributed() conf = configs.utils.builders.build_training_conf() environment.setup_logging_with_config(conf)