From f882beee542f730fcaf3a801ac8c53a3d5543a09 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Sat, 25 Oct 2025 21:01:38 +1000 Subject: [PATCH 01/71] Add 1_UNet_48339261 project folder --- recognition/1_UNet_48339261/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 recognition/1_UNet_48339261/README.md diff --git a/recognition/1_UNet_48339261/README.md b/recognition/1_UNet_48339261/README.md new file mode 100644 index 000000000..1ea23bd78 --- /dev/null +++ b/recognition/1_UNet_48339261/README.md @@ -0,0 +1 @@ +# 1_UNet_48339261 From 91c81b730222f8bf27628c5cb1fc634c3bc9f76e Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Sun, 26 Oct 2025 22:57:07 +1100 Subject: [PATCH 02/71] Add empty Python files for 1_UNet_48339261 --- recognition/1_UNet_48339261/dataset.py | 0 recognition/1_UNet_48339261/modules.py | 0 recognition/1_UNet_48339261/predict.py | 0 recognition/1_UNet_48339261/train.py | 0 recognition/1_UNet_48339261/utils.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 recognition/1_UNet_48339261/dataset.py create mode 100644 recognition/1_UNet_48339261/modules.py create mode 100644 recognition/1_UNet_48339261/predict.py create mode 100644 recognition/1_UNet_48339261/train.py create mode 100644 recognition/1_UNet_48339261/utils.py diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/1_UNet_48339261/predict.py new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py new file mode 100644 index 000000000..e69de29bb diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py new file mode 100644 index 000000000..e69de29bb From 1b05a9a6961cf121b9a7efa42fef2047a7e4b657 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 14:22:31 +1100 Subject: [PATCH 03/71] feat: Initialise HipMRIDataset class structure --- recognition/1_UNet_48339261/dataset.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index e69de29bb..b87079b33 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -0,0 +1,17 @@ +import torch +from torch.utils.data import Dataset + +class HipMRIDataset(Dataset): + def __init__(self, data_dir): + self.data_dir = data_dir + self.image_files = [] + self.mask_files = [] + print("Dataset initialized (basic structure).") + + def __len__(self): + + return 0 + + def __getitem__(self, idx): + + return None \ No newline at end of file From b1234385a0bd3b4f7326fc1fb186f66cb951555d Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 15:00:50 +1100 Subject: [PATCH 04/71] feat: Implement file searching logic in __init__ --- recognition/1_UNet_48339261/dataset.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index b87079b33..479a91ddd 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -1,17 +1,33 @@ import torch from torch.utils.data import Dataset +import os +import glob class HipMRIDataset(Dataset): def __init__(self, data_dir): self.data_dir = data_dir + self.image_files = [] self.mask_files = [] - print("Dataset initialized (basic structure).") + + image_paths = sorted(glob.glob(os.path.join(data_dir, "*_image.nii.gz"))) + + for img_path in image_paths: + mask_path = img_path.replace("_image.nii.gz", "_mask.nii.gz") + if os.path.exists(mask_path): + self.image_files.append(img_path) + self.mask_files.append(mask_path) + + if len(self.image_files) == 0: + print(f"Warning: No matching files found in {data_dir}") + else: + print(f"Found {len(self.image_files)} image/mask pairs.") def __len__(self): - return 0 + return len(self.image_files) + def __getitem__(self, idx): - - return None \ No newline at end of file + + return None From 87535c274a389cf5e37f57122c114254276a6520 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 15:50:11 +1100 Subject: [PATCH 05/71] feat: Convert loaded numpy arrays to PyTorch tensors --- recognition/1_UNet_48339261/dataset.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index 479a91ddd..a3a5dcfc0 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -1,5 +1,7 @@ import torch from torch.utils.data import Dataset +import nibabel as nib +import numpy as np import os import glob @@ -12,6 +14,7 @@ def __init__(self, data_dir): image_paths = sorted(glob.glob(os.path.join(data_dir, "*_image.nii.gz"))) + # match the Mask file according to the image name for img_path in image_paths: mask_path = img_path.replace("_image.nii.gz", "_mask.nii.gz") if os.path.exists(mask_path): @@ -19,7 +22,7 @@ def __init__(self, data_dir): self.mask_files.append(mask_path) if len(self.image_files) == 0: - print(f"Warning: No matching files found in {data_dir}") + print(f"No matching files found in {data_dir}") else: print(f"Found {len(self.image_files)} image/mask pairs.") @@ -29,5 +32,13 @@ def __len__(self): def __getitem__(self, idx): + image_path = self.image_files[idx] + mask_path = self.mask_files[idx] - return None + image_nii = nib.load(image_path) + mask_nii = nib.load(mask_path) + + image = image_nii.get_fdata().astype(np.float32) + mask = mask_nii.get_fdata().astype(np.uint8) + + return image, mask From 833d9ec970c3a8ffa4e1af68a2fc8b7343b9d3ee Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 15:56:55 +1100 Subject: [PATCH 06/71] feat: Add Z-score normalization for image tensors --- recognition/1_UNet_48339261/dataset.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index a3a5dcfc0..5900c8b7b 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -41,4 +41,15 @@ def __getitem__(self, idx): image = image_nii.get_fdata().astype(np.float32) mask = mask_nii.get_fdata().astype(np.uint8) - return image, mask + image_tensor = torch.from_numpy(image) + mask_tensor = torch.from_numpy(mask) + + # Z-score normalization + mean = image_tensor.mean() + std = image_tensor.std() + if std > 1e-6: + image_tensor = (image_tensor - mean) / std + else: + image_tensor = image_tensor - mean + + return image_tensor, mask_tensor From b67c55b2f59e5d64c5bd1228e60f4754442b44b1 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 15:59:02 +1100 Subject: [PATCH 07/71] feat: Implement binary mask for prostate label --- recognition/1_UNet_48339261/dataset.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index 5900c8b7b..438802309 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -8,6 +8,7 @@ class HipMRIDataset(Dataset): def __init__(self, data_dir): self.data_dir = data_dir + self.prostate_label_value = prostate_label_value self.image_files = [] self.mask_files = [] @@ -52,4 +53,6 @@ def __getitem__(self, idx): else: image_tensor = image_tensor - mean - return image_tensor, mask_tensor + binary_mask = (mask_tensor == self.prostate_label_value).long() + + return image_tensor, binary_mask From bc5069fa270af8267affe712223ea587daf30f57 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:04:56 +1100 Subject: [PATCH 08/71] fix: Add channel dimension to tensors --- recognition/1_UNet_48339261/dataset.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index 438802309..8cd907e44 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -45,6 +45,12 @@ def __getitem__(self, idx): image_tensor = torch.from_numpy(image) mask_tensor = torch.from_numpy(mask) + # add chanel dimention + if image_tensor.ndim == 2: + image_tensor = image_tensor.unsqueeze(0) # 变为 [1, H, W] + if mask_tensor.ndim == 2: + mask_tensor = mask_tensor.unsqueeze(0) # 变为 [1, H, W] + # Z-score normalization mean = image_tensor.mean() std = image_tensor.std() @@ -54,5 +60,5 @@ def __getitem__(self, idx): image_tensor = image_tensor - mean binary_mask = (mask_tensor == self.prostate_label_value).long() - + return image_tensor, binary_mask From 1bc6b56bd96e66bf72d7aafaac4c9de0a0ceb7dc Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:08:19 +1100 Subject: [PATCH 09/71] fix: Squeeze mask shape to [H, W] for loss function --- recognition/1_UNet_48339261/dataset.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index 8cd907e44..b1f347c92 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -58,7 +58,11 @@ def __getitem__(self, idx): image_tensor = (image_tensor - mean) / std else: image_tensor = image_tensor - mean - - binary_mask = (mask_tensor == self.prostate_label_value).long() + # (C, H, W) + binary_mask = (mask_tensor == self.prostate_label_value).long() + + # mask shape to [H, W] + binary_mask = binary_mask.squeeze(0) + return image_tensor, binary_mask From 15aa8562303874400820ed2bedc570f8480b1f7e Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:15:51 +1100 Subject: [PATCH 10/71] feat: Add resize for various image sizes --- recognition/1_UNet_48339261/dataset.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index b1f347c92..149d602e5 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -1,14 +1,15 @@ -import torch from torch.utils.data import Dataset +from torchvision import transforms import nibabel as nib import numpy as np import os import glob class HipMRIDataset(Dataset): - def __init__(self, data_dir): + def __init__(self, data_dir, prostate_label_value=5, resize_to=None): self.data_dir = data_dir self.prostate_label_value = prostate_label_value + self.resize_to = resize_to self.image_files = [] self.mask_files = [] @@ -45,11 +46,24 @@ def __getitem__(self, idx): image_tensor = torch.from_numpy(image) mask_tensor = torch.from_numpy(mask) - # add chanel dimention + # add chanel dimention :[1, H, W] if image_tensor.ndim == 2: - image_tensor = image_tensor.unsqueeze(0) # 变为 [1, H, W] + image_tensor = image_tensor.unsqueeze(0) if mask_tensor.ndim == 2: - mask_tensor = mask_tensor.unsqueeze(0) # 变为 [1, H, W] + mask_tensor = mask_tensor.unsqueeze(0) + + # resize + if self.resize_to: + image_tensor = transforms.functional.resize( + image_tensor, + self.resize_to, + interpolation=transforms.InterpolationMode.BILINEAR + ) + mask_tensor = transforms.functional.resize( + mask_tensor, + self.resize_to, + interpolation=transforms.InterpolationMode.NEAREST + ) # Z-score normalization mean = image_tensor.mean() @@ -59,7 +73,6 @@ def __getitem__(self, idx): else: image_tensor = image_tensor - mean - # (C, H, W) binary_mask = (mask_tensor == self.prostate_label_value).long() # mask shape to [H, W] From 56e56fbd0e44435139f3da1d84eae99ad6927f82 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:29:54 +1100 Subject: [PATCH 11/71] fix: import tourch library --- recognition/1_UNet_48339261/dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index 149d602e5..283670f5e 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -4,6 +4,7 @@ import numpy as np import os import glob +import torch class HipMRIDataset(Dataset): def __init__(self, data_dir, prostate_label_value=5, resize_to=None): From e678f3136440e58144f08a4daa36d627ceaff88d Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:47:51 +1100 Subject: [PATCH 12/71] feat: Add initial training function --- recognition/1_UNet_48339261/train.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index e69de29bb..3e6becbee 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -0,0 +1,14 @@ +def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): + + print(f"Train Starting for {epochs} epochs") + + for epoch in range(epochs): + model.train() + + # TODO: batch loop + + # 打印一下这轮的训练成果 + print(f"Epoch {epoch+1}/{epochs} Complete") + + print("Train") + return [] # 暂时返回一个空列表 \ No newline at end of file From eaff1ce8c023f4228c062bec00233f8bfb858c70 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:50:49 +1100 Subject: [PATCH 13/71] feat: Add batch loop --- recognition/1_UNet_48339261/train.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 3e6becbee..03969b88d 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -5,10 +5,15 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every for epoch in range(epochs): model.train() - # TODO: batch loop + for batch_idx, (images, masks) in enumerate(train_loader): + + images = images.to(device) + masks = masks.to(device) + + # TODO: forward and loss function + pass - # 打印一下这轮的训练成果 print(f"Epoch {epoch+1}/{epochs} Complete") - print("Train") - return [] # 暂时返回一个空列表 \ No newline at end of file + print("Training complete with enhanced U-Net") + return [] \ No newline at end of file From 7b249c9f065ff8a4c456bd27f5bedb6826e65ece Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 16:55:33 +1100 Subject: [PATCH 14/71] feat: Implement forward pass and loss calculation --- recognition/1_UNet_48339261/train.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 03969b88d..6c14dcc09 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -5,12 +5,19 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every for epoch in range(epochs): model.train() + criterion = DiceLoss() + for batch_idx, (images, masks) in enumerate(train_loader): images = images.to(device) masks = masks.to(device) + + # Forward + outputs = model(images) + pred_pet = outputs[:, 0] # (C, H, W) + loss = criterion(pred_pet, masks) - # TODO: forward and loss function + # Backward pass print(f"Epoch {epoch+1}/{epochs} Complete") From 3a501d391630db278dd2cc4db33cdb20374170d6 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 17:00:07 +1100 Subject: [PATCH 15/71] feat: Add optimizer and backpropagation --- recognition/1_UNet_48339261/train.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 6c14dcc09..54292c67b 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -1,12 +1,15 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): + + criterion = DiceLoss() + + optimizer = optim.Adam(model.parameters(), lr=lr) + print(f"Train Starting for {epochs} epochs") for epoch in range(epochs): model.train() - criterion = DiceLoss() - for batch_idx, (images, masks) in enumerate(train_loader): images = images.to(device) @@ -16,8 +19,13 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every outputs = model(images) pred_pet = outputs[:, 0] # (C, H, W) loss = criterion(pred_pet, masks) + + # Backpropagation + optimizer.zero_grad() + loss.backward() + optimizer.step() + - # Backward pass print(f"Epoch {epoch+1}/{epochs} Complete") From b6bf5b0e1438649590f671999602190aaf19085d Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 17:06:28 +1100 Subject: [PATCH 16/71] feat: Add loss tracking and epoch logging --- recognition/1_UNet_48339261/train.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 54292c67b..84be99d84 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -1,5 +1,10 @@ +import torch +import torch.optim as optim +from torch.utils.data import DataLoader, Dataset + def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): + losses = [] criterion = DiceLoss() @@ -9,26 +14,28 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every for epoch in range(epochs): model.train() + epoch_loss = 0 for batch_idx, (images, masks) in enumerate(train_loader): images = images.to(device) masks = masks.to(device) - # Forward + # Forward pass outputs = model(images) pred_pet = outputs[:, 0] # (C, H, W) loss = criterion(pred_pet, masks) - # Backpropagation + # Backward pass optimizer.zero_grad() loss.backward() optimizer.step() + epoch_loss += loss.item() - pass - - print(f"Epoch {epoch+1}/{epochs} Complete") + avg_loss = epoch_loss / len(train_loader) + losses.append(avg_loss) + print(f"Epoch {epoch+1}/{epochs} Complete: Avg Loss = {avg_loss:.4f}") print("Training complete with enhanced U-Net") - return [] \ No newline at end of file + return losses \ No newline at end of file From a30c9ef29285cc0a7f216b73716d4a2f7acfe05f Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 17:12:11 +1100 Subject: [PATCH 17/71] feat: Add a Visualize predictions after each epoch --- recognition/1_UNet_48339261/train.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 84be99d84..9257d40bf 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -3,13 +3,11 @@ from torch.utils.data import DataLoader, Dataset def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): - - losses = [] - + model.to(device) criterion = DiceLoss() - optimizer = optim.Adam(model.parameters(), lr=lr) - + + losses = [] print(f"Train Starting for {epochs} epochs") for epoch in range(epochs): @@ -37,5 +35,9 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every losses.append(avg_loss) print(f"Epoch {epoch+1}/{epochs} Complete: Avg Loss = {avg_loss:.4f}") + # Visualize predictions after each epoch (or every few epochs) + if (epoch) % visualize_every == 0: + show_epoch_predictions(model, test_dataset, epoch + 1, n=3) + print("Training complete with enhanced U-Net") return losses \ No newline at end of file From 8db9c17b968b5623d283bc70244d820bbaed9710 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 17:26:14 +1100 Subject: [PATCH 18/71] fix: change a variable name - prediction_squeezed --- recognition/1_UNet_48339261/train.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 9257d40bf..1d49b1aa4 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -21,8 +21,8 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every # Forward pass outputs = model(images) - pred_pet = outputs[:, 0] # (C, H, W) - loss = criterion(pred_pet, masks) + predictions_squeezed = outputs[:, 0] # (C, H, W) + loss = criterion(predictions_squeezed, masks) # Backward pass optimizer.zero_grad() From 971b9868e53a3ddf30a49d34bbee128f15aa0be4 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 18:08:57 +1100 Subject: [PATCH 19/71] feat: Add device to use gpu or cpu --- recognition/1_UNet_48339261/train.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 1d49b1aa4..31d25ea4a 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -2,6 +2,13 @@ import torch.optim as optim from torch.utils.data import DataLoader, Dataset +from modules import DiceLoss, SimpleUNet +import torch +import numpy as np +import matplotlib.pyplot as plt + +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): model.to(device) criterion = DiceLoss() From d9d785aed8d5304745e6d57ff98279fa2140219a Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 18:15:48 +1100 Subject: [PATCH 20/71] feat: Add dice_score metric for segmentation evaluation --- recognition/1_UNet_48339261/utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py index e69de29bb..2e7e3efc5 100644 --- a/recognition/1_UNet_48339261/utils.py +++ b/recognition/1_UNet_48339261/utils.py @@ -0,0 +1,19 @@ +import torch +import numpy as np + +def calculate_dice_score(pred_binary, true_mask): + """calculate Dice similarity coefficient""" + + if isinstance(pred_binary, torch.Tensor): + pred_binary = pred_binary.cpu().numpy() + if isinstance(true_mask, torch.Tensor): + true_mask = true_mask.cpu().numpy() + + pred_binary = pred_binary.flatten() + true_mask = true_mask.flatten() + + # + 1e-6 prevent the denominator is 0 + intersection = (pred_binary * true_mask).sum() + dice_score = (2. * intersection + 1e-6) / (pred_binary.sum() + true_mask.sum() + 1e-6) + + return dice_score \ No newline at end of file From 3cf235b62ce0988e4468216954da94952e231941 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 18:17:42 +1100 Subject: [PATCH 21/71] feat: Add plot train loss curve function --- recognition/1_UNet_48339261/utils.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py index 2e7e3efc5..f6bcf2234 100644 --- a/recognition/1_UNet_48339261/utils.py +++ b/recognition/1_UNet_48339261/utils.py @@ -1,5 +1,6 @@ import torch import numpy as np +import matplotlib.pyplot as plt def calculate_dice_score(pred_binary, true_mask): """calculate Dice similarity coefficient""" @@ -16,4 +17,16 @@ def calculate_dice_score(pred_binary, true_mask): intersection = (pred_binary * true_mask).sum() dice_score = (2. * intersection + 1e-6) / (pred_binary.sum() + true_mask.sum() + 1e-6) - return dice_score \ No newline at end of file + return dice_score + + +def plot_loss(losses): + """Plot train loss curve""" + plt.figure(figsize=(10, 5)) + plt.plot(losses, label='Training Loss') + plt.title('Training Loss vs. Epochs') + plt.xlabel('Epoch') + plt.ylabel('Loss (Dice Loss)') + plt.legend() + plt.grid(True) + plt.show() \ No newline at end of file From cdb955521b88a8958891dc168942a60f657e50b6 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 18:24:17 +1100 Subject: [PATCH 22/71] feat: Add prediction visualization function --- recognition/1_UNet_48339261/utils.py | 38 +++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py index f6bcf2234..bcdb78ccb 100644 --- a/recognition/1_UNet_48339261/utils.py +++ b/recognition/1_UNet_48339261/utils.py @@ -29,4 +29,40 @@ def plot_loss(losses): plt.ylabel('Loss (Dice Loss)') plt.legend() plt.grid(True) - plt.show() \ No newline at end of file + plt.show() + + +def show_epoch_predictions(model, dataset, epoch, n=3): + """MRI""" + + fig, axes = plt.subplots(3, n, figsize=(12, 9)) + fig.suptitle(f'Train the prediction results after the {epoch} round', fontsize=16) + + indices = np.random.choice(len(dataset), n, replace=False) + + for i, idx in enumerate(indices): + image, true_mask = dataset[idx] + + pred = model(image.unsqueeze(0).to(device)) + + pred_prob = pred[0, 0].cpu().numpy() + pred_binary = (pred_prob > 0.5).astype(int) + + # Show original image + img_display = image.squeeze().cpu().numpy() + axes[0, i].imshow(img_display, cmap='gray') + axes[0, i].set_title(f'Original {idx})') + axes[0, i].axis('off') + + # Show ground truth binary mask + axes[1, i].imshow(true_mask.cpu().numpy(), cmap='gray') + axes[1, i].set_title(f'Ground Truth (前列腺)') + axes[1, i].axis('off') + + # Show prediction + axes[2, i].imshow(pred_binary, cmap='gray') + axes[2, i].set_title(f'Prediction') + axes[2, i].axis('off') + + plt.tight_layout() + plt.show() \ No newline at end of file From 282566f75356ea0730fffd68492998f842c30f1a Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 20:58:46 +1100 Subject: [PATCH 23/71] fix: add model.eval() and no_grad() --- recognition/1_UNet_48339261/utils.py | 45 +++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py index bcdb78ccb..b8d8c3381 100644 --- a/recognition/1_UNet_48339261/utils.py +++ b/recognition/1_UNet_48339261/utils.py @@ -34,35 +34,38 @@ def plot_loss(losses): def show_epoch_predictions(model, dataset, epoch, n=3): """MRI""" + model.eval() fig, axes = plt.subplots(3, n, figsize=(12, 9)) fig.suptitle(f'Train the prediction results after the {epoch} round', fontsize=16) - indices = np.random.choice(len(dataset), n, replace=False) + with torch.no_grad(): + indices = np.random.choice(len(dataset), n, replace=False) - for i, idx in enumerate(indices): - image, true_mask = dataset[idx] + for i, idx in enumerate(indices): + image, true_mask = dataset[idx] + pred = model(image.unsqueeze(0).to(device)) - pred = model(image.unsqueeze(0).to(device)) + pred_prob = pred[0, 0].cpu().numpy() + pred_binary = (pred_prob > 0.5).astype(int) - pred_prob = pred[0, 0].cpu().numpy() - pred_binary = (pred_prob > 0.5).astype(int) + # Show original image + img_display = image.squeeze().cpu().numpy() + axes[0, i].imshow(img_display, cmap='gray') + axes[0, i].set_title(f'Original {idx})') + axes[0, i].axis('off') - # Show original image - img_display = image.squeeze().cpu().numpy() - axes[0, i].imshow(img_display, cmap='gray') - axes[0, i].set_title(f'Original {idx})') - axes[0, i].axis('off') + # Show ground truth binary mask + axes[1, i].imshow(true_mask.cpu().numpy(), cmap='gray') + axes[1, i].set_title(f'Ground Truth (前列腺)') + axes[1, i].axis('off') - # Show ground truth binary mask - axes[1, i].imshow(true_mask.cpu().numpy(), cmap='gray') - axes[1, i].set_title(f'Ground Truth (前列腺)') - axes[1, i].axis('off') - - # Show prediction - axes[2, i].imshow(pred_binary, cmap='gray') - axes[2, i].set_title(f'Prediction') - axes[2, i].axis('off') + # Show prediction + axes[2, i].imshow(pred_binary, cmap='gray') + axes[2, i].set_title(f'Prediction') + axes[2, i].axis('off') plt.tight_layout() - plt.show() \ No newline at end of file + plt.show() + + model.train() \ No newline at end of file From c41428df4412e1420f09de80b1c218e631ba60b9 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Mon, 27 Oct 2025 21:13:43 +1100 Subject: [PATCH 24/71] fix: use calculate_dice_score into prediction plot --- recognition/1_UNet_48339261/utils.py | 29 +++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py index b8d8c3381..d86fca92c 100644 --- a/recognition/1_UNet_48339261/utils.py +++ b/recognition/1_UNet_48339261/utils.py @@ -20,18 +20,6 @@ def calculate_dice_score(pred_binary, true_mask): return dice_score -def plot_loss(losses): - """Plot train loss curve""" - plt.figure(figsize=(10, 5)) - plt.plot(losses, label='Training Loss') - plt.title('Training Loss vs. Epochs') - plt.xlabel('Epoch') - plt.ylabel('Loss (Dice Loss)') - plt.legend() - plt.grid(True) - plt.show() - - def show_epoch_predictions(model, dataset, epoch, n=3): """MRI""" model.eval() @@ -61,6 +49,7 @@ def show_epoch_predictions(model, dataset, epoch, n=3): axes[1, i].axis('off') # Show prediction + dice = calculate_dice_score(pred_binary, true_mask) axes[2, i].imshow(pred_binary, cmap='gray') axes[2, i].set_title(f'Prediction') axes[2, i].axis('off') @@ -68,4 +57,18 @@ def show_epoch_predictions(model, dataset, epoch, n=3): plt.tight_layout() plt.show() - model.train() \ No newline at end of file + model.train() + + +def plot_loss(losses): + """Plot train loss curve""" + plt.figure(figsize=(10, 5)) + plt.plot(losses, label='Training Loss') + plt.title('Training Loss vs. Epochs') + plt.xlabel('Epoch') + plt.ylabel('Loss (Dice Loss)') + plt.legend() + plt.grid(True) + plt.show() + + \ No newline at end of file From 31af8d1e8bb35da188131df921da7cfc3fe9f17e Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 12:05:48 +1100 Subject: [PATCH 25/71] feat: using .copy() to prevent the negative stride --- recognition/1_UNet_48339261/dataset.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/1_UNet_48339261/dataset.py index 283670f5e..a1273c434 100644 --- a/recognition/1_UNet_48339261/dataset.py +++ b/recognition/1_UNet_48339261/dataset.py @@ -1,3 +1,6 @@ +""" +dataset.py +""" from torch.utils.data import Dataset from torchvision import transforms import nibabel as nib @@ -44,8 +47,8 @@ def __getitem__(self, idx): image = image_nii.get_fdata().astype(np.float32) mask = mask_nii.get_fdata().astype(np.uint8) - image_tensor = torch.from_numpy(image) - mask_tensor = torch.from_numpy(mask) + image_tensor = torch.from_numpy(image.copy()) + mask_tensor = torch.from_numpy(mask.copy()) # add chanel dimention :[1, H, W] if image_tensor.ndim == 2: From e5f0d2b72fb4e45b93bcdd3f9e785ae8224a8680 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 12:06:59 +1100 Subject: [PATCH 26/71] feat: import library --- recognition/1_UNet_48339261/train.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 31d25ea4a..81fd903ac 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -1,12 +1,16 @@ +""" +train.py +""" import torch import torch.optim as optim from torch.utils.data import DataLoader, Dataset -from modules import DiceLoss, SimpleUNet -import torch +from modules import DiceLoss, SimpleUNet +from dataset import HipMRIDataset +from utils import show_epoch_predictions, plot_loss, calculate_dice_score + import numpy as np import matplotlib.pyplot as plt - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): @@ -47,4 +51,5 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every show_epoch_predictions(model, test_dataset, epoch + 1, n=3) print("Training complete with enhanced U-Net") + plot_loss(losses) return losses \ No newline at end of file From 300b9d898a9c067c93fba3578b727e6160a91b76 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 12:08:20 +1100 Subject: [PATCH 27/71] fix: using plt.savefig inseat of plt.show --- recognition/1_UNet_48339261/utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/1_UNet_48339261/utils.py index d86fca92c..ae6327a94 100644 --- a/recognition/1_UNet_48339261/utils.py +++ b/recognition/1_UNet_48339261/utils.py @@ -1,3 +1,6 @@ +""" +utils.py +""" import torch import numpy as np import matplotlib.pyplot as plt @@ -55,8 +58,8 @@ def show_epoch_predictions(model, dataset, epoch, n=3): axes[2, i].axis('off') plt.tight_layout() - plt.show() - + plt.savefig(f"epoch_{epoch}_predictions.png") + plt.close(fig) model.train() @@ -69,6 +72,7 @@ def plot_loss(losses): plt.ylabel('Loss (Dice Loss)') plt.legend() plt.grid(True) - plt.show() + plt.savefig("training_loss_curve.png") + plt.close() \ No newline at end of file From bcd9a09b0f9e1985e9e9b6289020c133eddf62d7 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:05:15 +1100 Subject: [PATCH 28/71] feat: import library and implement if_main function --- recognition/1_UNet_48339261/predict.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/1_UNet_48339261/predict.py index e69de29bb..8b5bb32e9 100644 --- a/recognition/1_UNet_48339261/predict.py +++ b/recognition/1_UNet_48339261/predict.py @@ -0,0 +1,22 @@ +import torch +import os + +from modules import SimpleUNet +from dataset import HipMRIDataset + +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +print(f"Predict.py: Using device: {device}") + + + +if __name__ == '__main__': + print("Runing predict.py...") + + DATA_DIR = "/home/groups/comp3710/HipMRI_Study_open/keras_slices_data" + MODEL_SAVE_PATH = "hipmri_unet_model.pth" + RESIZE_TO = (128, 128) + PROSTATE_LABEL = 5 + NUM_EXAMPLES_TO_SHOW = 3 + + # visuliasation + pass \ No newline at end of file From d5390f49fb9db08c8c8cd27b40083ccdc923baa7 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:14:15 +1100 Subject: [PATCH 29/71] feat: implement show_prediction function --- recognition/1_UNet_48339261/predict.py | 46 +++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/1_UNet_48339261/predict.py index 8b5bb32e9..9b134438c 100644 --- a/recognition/1_UNet_48339261/predict.py +++ b/recognition/1_UNet_48339261/predict.py @@ -1,13 +1,57 @@ import torch import os - +import matplotlib.pyplot as plt +import numpy as np +from utils import calculate_dice_score from modules import SimpleUNet from dataset import HipMRIDataset device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Predict.py: Using device: {device}") +def show_predictions(model, dataset, title="Final segmentation results (HipMRI)", n=3): + model.eval() + + fig, axes = plt.subplots(3, n, figsize=(12, 9)) + fig.suptitle(title, fontsize=16, fontweight='bold') + + with torch.no_grad(): + if len(dataset) < n: + n = len(dataset) + print(f"The siza of Dataset ({len(dataset)}) smaller than n ({n}), show {n} samples.") + + indices = np.random.choice(len(dataset), n, replace=False) + + for i, idx in enumerate(indices): + image, true_mask = dataset[idx] + + pred = model(image.unsqueeze(0).to(device)) + + pred_prob = pred[0, 0].cpu().numpy() + pred_binary = (pred_prob > 0.5).astype(int) + + # Orifinal (gray) + img_display = image.squeeze().cpu().numpy() + axes[0, i].imshow(img_display, cmap='gray') + axes[0, i].set_title(f'Original {idx})', fontweight='bold') + axes[0, i].axis('off') + + # Ground Truth + axes[1, i].imshow(true_mask.cpu().numpy(), cmap='gray') + axes[1, i].set_title(f'Ground Truth (Prostate)', fontweight='bold') + axes[1, i].axis('off') + + # Prediction + dice = calculate_dice_score(pred_binary, true_mask) + axes[2, i].imshow(pred_binary, cmap='gray') + axes[2, i].set_title(f'Pridiction {dice:.3f})', fontweight='bold') + axes[2, i].axis('off') + plt.tight_layout() + save_path = "final_predictions.png" + plt.savefig(save_path) + print(f"Prediction Result saved in: {save_path}") + plt.close(fig) if __name__ == '__main__': print("Runing predict.py...") From f08abfc174775d5ef21258369b68508e33fabcb6 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:38:49 +1100 Subject: [PATCH 30/71] chore: mark restore of predict.py (no content change) From d7b40f7dd9f337a1684984ba3b2b5cde09f7d566 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:40:28 +1100 Subject: [PATCH 31/71] feat: Model loading and Dataset loading --- recognition/1_UNet_48339261/predict.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/1_UNet_48339261/predict.py index 9b134438c..b2c2c5fa7 100644 --- a/recognition/1_UNet_48339261/predict.py +++ b/recognition/1_UNet_48339261/predict.py @@ -2,6 +2,8 @@ import os import matplotlib.pyplot as plt import numpy as np +from tqdm import tqdm + from utils import calculate_dice_score from modules import SimpleUNet from dataset import HipMRIDataset @@ -23,10 +25,11 @@ def show_predictions(model, dataset, title="Final segmentation results (HipMRI)" indices = np.random.choice(len(dataset), n, replace=False) for i, idx in enumerate(indices): + # (1,H,W) & (H,W) image, true_mask = dataset[idx] - + # (1,H,W) -> (1,1,H,W) pred = model(image.unsqueeze(0).to(device)) - + # (1,1,H,W) -> (H,W) pred_prob = pred[0, 0].cpu().numpy() pred_binary = (pred_prob > 0.5).astype(int) @@ -62,5 +65,18 @@ def show_predictions(model, dataset, title="Final segmentation results (HipMRI)" PROSTATE_LABEL = 5 NUM_EXAMPLES_TO_SHOW = 3 - # visuliasation + print(f"Loading model: {MODEL_SAVE_PATH}") + model = SimpleUNet(in_channels=1, out_channels=1).to(device) + + model.load_state_dict(torch.load(MODEL_SAVE_PATH, map_location=device)) + + print(f"Loading dataset: {DATA_DIR}") + test_dataset = HipMRIDataset( + data_dir=DATA_DIR, + resize_to=RESIZE_TO, + prostate_label_value=PROSTATE_LABEL + ) + + + pass \ No newline at end of file From 7ec7c577561154c3f42fe52252a15867469c7ed3 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:44:47 +1100 Subject: [PATCH 32/71] feat: add visualization --- recognition/1_UNet_48339261/predict.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/1_UNet_48339261/predict.py index b2c2c5fa7..1639f6d28 100644 --- a/recognition/1_UNet_48339261/predict.py +++ b/recognition/1_UNet_48339261/predict.py @@ -76,7 +76,12 @@ def show_predictions(model, dataset, title="Final segmentation results (HipMRI)" resize_to=RESIZE_TO, prostate_label_value=PROSTATE_LABEL ) - + + if len(test_dataset) > 0: + print(f"Generating {NUM_EXAMPLES_TO_SHOW} prediction examples...") + show_predictions(model, test_dataset, n=NUM_EXAMPLES_TO_SHOW) + else: + print("Error: Dataset is empty.") pass \ No newline at end of file From 5d209af1825224aab6e9ccce22d5203986b26fde Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:47:22 +1100 Subject: [PATCH 33/71] feat: implement if_main with parameters and dataset loading --- recognition/1_UNet_48339261/train.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 81fd903ac..4fe84cdcf 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -52,4 +52,26 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every print("Training complete with enhanced U-Net") plot_loss(losses) - return losses \ No newline at end of file + return losses + + +if __name__ == "__main__": + + print(f"Starting training (train.py) ---") + + DATA_DIR = "/home/groups/comp3710/HipMRI_Study_open/keras_slices_data" + MODEL_SAVE_PATH = "hipmri_unet_model.pth" + EPOCHS = 20 + LEARNING_RATE = 0.001 + BATCH_SIZE = 16 + RESIZE_TO = (128, 128) + PROSTATE_LABEL = 5 + VALIDATION_SPLIT = 0.2 + + dataset = HipMRIDataset( + data_dir=DATA_DIR, + resize_to=RESIZE_TO, + prostate_label_value=PROSTATE_LABEL + ) + +pass \ No newline at end of file From 87ced9f1df253c09602a7f9ae265b4d813dbea78 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:50:57 +1100 Subject: [PATCH 34/71] feat: add training process in if_main --- recognition/1_UNet_48339261/train.py | 32 +++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 4fe84cdcf..9f0615820 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -73,5 +73,35 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every resize_to=RESIZE_TO, prostate_label_value=PROSTATE_LABEL ) + + val_size = int(len(dataset) * VALIDATION_SPLIT) + train_size = len(dataset) - val_size + + train_dataset, val_dataset = random_split(dataset, [train_size, val_size], + generator=torch.Generator().manual_seed(42)) + + print(f"Dataset split: {len(train_dataset)} training samples, {len(val_dataset)} validation samples") + + train_loader = DataLoader( + train_dataset, + batch_size=BATCH_SIZE, + shuffle=True, + num_workers=4 + ) + + val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False) + + model = SimpleUNet(in_channels=1, out_channels=1).to(device) + + training_losses = train( + model=model, + train_loader=train_loader, + test_dataset=val_dataset, + epochs=EPOCHS, + lr=LEARNING_RATE, + visualize_every=5 + ) -pass \ No newline at end of file + print("Traning Complete") + torch.save(model.state_dict(), MODEL_SAVE_PATH) + print(f"Model saved in: {MODEL_SAVE_PATH}") From 72bba85528eb4bc46b16e8abf44982567af4875f Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 13:51:38 +1100 Subject: [PATCH 35/71] fix: delete a 'pass' --- recognition/1_UNet_48339261/predict.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/1_UNet_48339261/predict.py index 1639f6d28..c68d3ae19 100644 --- a/recognition/1_UNet_48339261/predict.py +++ b/recognition/1_UNet_48339261/predict.py @@ -81,7 +81,4 @@ def show_predictions(model, dataset, title="Final segmentation results (HipMRI)" print(f"Generating {NUM_EXAMPLES_TO_SHOW} prediction examples...") show_predictions(model, test_dataset, n=NUM_EXAMPLES_TO_SHOW) else: - print("Error: Dataset is empty.") - - - pass \ No newline at end of file + print("Error: Dataset is empty.") \ No newline at end of file From 05f2c8a0978a1e9c5361025ecd0c645e55d23d46 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:01:24 +1100 Subject: [PATCH 36/71] feat: Add SimpleUNet with Conv-ReLU blocks --- recognition/1_UNet_48339261/modules.py | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index e69de29bb..bc7fdf7fe 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -0,0 +1,43 @@ +""" +modules.py +""" +import torch +import torch.nn as nn + +class SimpleUNet(nn.Module): + def __init__(self, in_channels=1, out_channels=1): + super().__init__() + + # Encoder (downsampling) + self.enc1 = self._conv_block(in_channels, 32) + self.enc2 = self._conv_block(32, 64) + self.enc3 = self._conv_block(64, 128) + + # Decoder (upsampling) + self.dec3 = self._conv_block(128 + 64, 64) + self.dec2 = self._conv_block(64 + 32, 32) + self.dec1 = nn.Conv2d(32, out_channels, 1) + + self.pool = nn.MaxPool2d(2) + self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) + + def _conv_block(self, in_ch, out_ch): + return nn.Sequential( + nn.Conv2d(in_ch, out_ch, 3, padding=1), + nn.ReLU(inplace=True), + nn.Conv2d(out_ch, out_ch, 3, padding=1), + nn.ReLU(inplace=True) + ) + + def forward(self, x): + # Encoder + e1 = self.enc1(x) + e2 = self.enc2(self.pool(e1)) + e3 = self.enc3(self.pool(e2)) + + # Decoder with skip connections + d3 = self.dec3(torch.cat([self.upsample(e3), e2], 1)) + d2 = self.dec2(torch.cat([self.upsample(d3), e1], 1)) + out = self.dec1(d2) + + return out \ No newline at end of file From d9221be8461fa99d1fa00ecec5a4cc4ffb567ed7 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:04:48 +1100 Subject: [PATCH 37/71] feat: Implement DiceLoss function --- recognition/1_UNet_48339261/modules.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index bc7fdf7fe..d23e29edb 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -40,4 +40,21 @@ def forward(self, x): d2 = self.dec2(torch.cat([self.upsample(d3), e1], 1)) out = self.dec1(d2) - return out \ No newline at end of file + return out + + +class DiceLoss(nn.Module): + def __init__(self, smooth=1e-6): + super(DiceLoss, self).__init__() + self.smooth = smooth + + def forward(self, logits, targets): + predictions = torch.sigmoid(logits) + + predictions = predictions.view(-1) + targets = targets.view(-1).float() + + intersection = (predictions * targets).sum() + dice_coeff = (2.0 * intersection + self.smooth) / (predictions.sum() + targets.sum() + self.smooth) + + return 1 - dice_coeff \ No newline at end of file From 4060a2b96033a2fd931c929ec5b28213dcb23bf5 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:06:13 +1100 Subject: [PATCH 38/71] feat: Add BatchNorm2d to conv blocks --- recognition/1_UNet_48339261/modules.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index d23e29edb..1c4c65ec9 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -24,8 +24,10 @@ def __init__(self, in_channels=1, out_channels=1): def _conv_block(self, in_ch, out_ch): return nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1), + nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), nn.Conv2d(out_ch, out_ch, 3, padding=1), + nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True) ) From e1038ddf0a481418d16213283c8f130e36bb7253 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:07:30 +1100 Subject: [PATCH 39/71] feat: Replace ReLU with LeakyReLU --- recognition/1_UNet_48339261/modules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index 1c4c65ec9..ac606c07f 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -25,10 +25,10 @@ def _conv_block(self, in_ch, out_ch): return nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), - nn.ReLU(inplace=True), + nn.LeakyReLU(negative_slope=0.2, inplace=True), nn.Conv2d(out_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), - nn.ReLU(inplace=True) + nn.LeakyReLU(negative_slope=0.2, inplace=True), ) def forward(self, x): From 86aad067fae44c5a1b76698cf02f1a482bdd233e Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:10:44 +1100 Subject: [PATCH 40/71] feat: Add Dropout2d for regularization --- recognition/1_UNet_48339261/modules.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index ac606c07f..c6d27df56 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -9,14 +9,14 @@ def __init__(self, in_channels=1, out_channels=1): super().__init__() # Encoder (downsampling) - self.enc1 = self._conv_block(in_channels, 32) - self.enc2 = self._conv_block(32, 64) - self.enc3 = self._conv_block(64, 128) + self.enc1 = self._conv_block(in_channels, 32, dropout_p) + self.enc2 = self._conv_block(32, 64, dropout_p) + self.enc3 = self._conv_block(64, 128, dropout_p) # Decoder (upsampling) - self.dec3 = self._conv_block(128 + 64, 64) - self.dec2 = self._conv_block(64 + 32, 32) - self.dec1 = nn.Conv2d(32, out_channels, 1) + self.dec3 = self._conv_block(128 + 64, 64, dropout_p) + self.dec2 = self._conv_block(64 + 32, 32, dropout_p) + self.dec1 = nn.Conv2d(32, out_channels, 1) self.pool = nn.MaxPool2d(2) self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) @@ -26,9 +26,11 @@ def _conv_block(self, in_ch, out_ch): nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Dropout2d(dropout_p), nn.Conv2d(out_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.LeakyReLU(negative_slope=0.2, inplace=True), + nn.Dropout2d(dropout_p), ) def forward(self, x): From a228390f199d9204e4430dbe07db0291a1d080de Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:28:06 +1100 Subject: [PATCH 41/71] feat: Move Sigmoid to Model output --- recognition/1_UNet_48339261/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index c6d27df56..cfdf7341d 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -5,7 +5,7 @@ import torch.nn as nn class SimpleUNet(nn.Module): - def __init__(self, in_channels=1, out_channels=1): + def __init__(self, in_channels=1, out_channels=1, dropout_p=0.2): super().__init__() # Encoder (downsampling) From 41fd381181069fe09a591febea0ed7d202fafaa3 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:30:56 +1100 Subject: [PATCH 42/71] fix: use reshape instead of view in DiceLoss --- recognition/1_UNet_48339261/modules.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index cfdf7341d..bb456eb17 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -20,6 +20,7 @@ def __init__(self, in_channels=1, out_channels=1, dropout_p=0.2): self.pool = nn.MaxPool2d(2) self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) + self.sigmoid = nn.Sigmoid() def _conv_block(self, in_ch, out_ch): return nn.Sequential( @@ -44,6 +45,8 @@ def forward(self, x): d2 = self.dec2(torch.cat([self.upsample(d3), e1], 1)) out = self.dec1(d2) + out = self.sigmoid(out) + return out @@ -52,11 +55,10 @@ def __init__(self, smooth=1e-6): super(DiceLoss, self).__init__() self.smooth = smooth - def forward(self, logits, targets): - predictions = torch.sigmoid(logits) + def forward(self, predictions, targets): - predictions = predictions.view(-1) - targets = targets.view(-1).float() + predictions = predictions.reshape(-1) + targets = targets.reshape(-1).float() intersection = (predictions * targets).sum() dice_coeff = (2.0 * intersection + self.smooth) / (predictions.sum() + targets.sum() + self.smooth) From c2657fe7ea008714f79937a30db2b35df44b436a Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:39:55 +1100 Subject: [PATCH 43/71] fix: add dropout_p parameter in function definition --- recognition/1_UNet_48339261/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/1_UNet_48339261/modules.py index bb456eb17..2ebb99565 100644 --- a/recognition/1_UNet_48339261/modules.py +++ b/recognition/1_UNet_48339261/modules.py @@ -22,7 +22,7 @@ def __init__(self, in_channels=1, out_channels=1, dropout_p=0.2): self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) self.sigmoid = nn.Sigmoid() - def _conv_block(self, in_ch, out_ch): + def _conv_block(self, in_ch, out_ch, dropout_p=0.2): return nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), From 3a0e18b8be56822862e08a19ff9091655ca32e0f Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 14:40:22 +1100 Subject: [PATCH 44/71] fix: add random_split library --- recognition/1_UNet_48339261/train.py | 1 + 1 file changed, 1 insertion(+) diff --git a/recognition/1_UNet_48339261/train.py b/recognition/1_UNet_48339261/train.py index 9f0615820..6fdaf2f3a 100644 --- a/recognition/1_UNet_48339261/train.py +++ b/recognition/1_UNet_48339261/train.py @@ -4,6 +4,7 @@ import torch import torch.optim as optim from torch.utils.data import DataLoader, Dataset +from torch.utils.data import random_split from modules import DiceLoss, SimpleUNet from dataset import HipMRIDataset From 080347ceb3951df6686de72c3dec7d3e00e9a36c Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Tue, 28 Oct 2025 15:47:30 +1100 Subject: [PATCH 45/71] Rename project folder to UNet_task3_48339261 --- .../README.md | 0 .../dataset.py | 0 .../modules.py | 0 .../predict.py | 0 .../UNet_task3_48339261/run_task3.slurm | 28 +++++++++++++++++++ .../train.py | 0 .../utils.py | 0 7 files changed, 28 insertions(+) rename recognition/{1_UNet_48339261 => UNet_task3_48339261}/README.md (100%) rename recognition/{1_UNet_48339261 => UNet_task3_48339261}/dataset.py (100%) rename recognition/{1_UNet_48339261 => UNet_task3_48339261}/modules.py (100%) rename recognition/{1_UNet_48339261 => UNet_task3_48339261}/predict.py (100%) create mode 100644 recognition/UNet_task3_48339261/run_task3.slurm rename recognition/{1_UNet_48339261 => UNet_task3_48339261}/train.py (100%) rename recognition/{1_UNet_48339261 => UNet_task3_48339261}/utils.py (100%) diff --git a/recognition/1_UNet_48339261/README.md b/recognition/UNet_task3_48339261/README.md similarity index 100% rename from recognition/1_UNet_48339261/README.md rename to recognition/UNet_task3_48339261/README.md diff --git a/recognition/1_UNet_48339261/dataset.py b/recognition/UNet_task3_48339261/dataset.py similarity index 100% rename from recognition/1_UNet_48339261/dataset.py rename to recognition/UNet_task3_48339261/dataset.py diff --git a/recognition/1_UNet_48339261/modules.py b/recognition/UNet_task3_48339261/modules.py similarity index 100% rename from recognition/1_UNet_48339261/modules.py rename to recognition/UNet_task3_48339261/modules.py diff --git a/recognition/1_UNet_48339261/predict.py b/recognition/UNet_task3_48339261/predict.py similarity index 100% rename from recognition/1_UNet_48339261/predict.py rename to recognition/UNet_task3_48339261/predict.py diff --git a/recognition/UNet_task3_48339261/run_task3.slurm b/recognition/UNet_task3_48339261/run_task3.slurm new file mode 100644 index 000000000..7fb1de057 --- /dev/null +++ b/recognition/UNet_task3_48339261/run_task3.slurm @@ -0,0 +1,28 @@ +#!/bin/bash + +#SBATCH --job-name=comp3710_task3 +#SBATCH --output=task3_job_%j.out +#SBATCH --error=task3_job_%j.err + +#SBATCH --partition=gpu +#SBATCH --gres=gpu:1 +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=1 +#SBATCH --cpus-per-task=4 +#SBATCH --mem=16G +#SBATCH --time=01:00:00 + + +module purge + +module load cuda/11.1 +module load anaconda3/2022.05 + +source ~/miniconda3/etc/profile.d/conda.sh +conda activate comp3710 + +python train.py +echo "Traning Complete, running predict.py ---" + +python predict.py +echo "Task Finish" \ No newline at end of file diff --git a/recognition/1_UNet_48339261/train.py b/recognition/UNet_task3_48339261/train.py similarity index 100% rename from recognition/1_UNet_48339261/train.py rename to recognition/UNet_task3_48339261/train.py diff --git a/recognition/1_UNet_48339261/utils.py b/recognition/UNet_task3_48339261/utils.py similarity index 100% rename from recognition/1_UNet_48339261/utils.py rename to recognition/UNet_task3_48339261/utils.py From 0272f7c3443fae0377411f75c85eb1952a83b6a3 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:18:02 +1100 Subject: [PATCH 46/71] fix: update data loading to match real dataset folder structure --- recognition/UNet_task3_48339261/dataset.py | 104 ++++++++++++--------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/recognition/UNet_task3_48339261/dataset.py b/recognition/UNet_task3_48339261/dataset.py index a1273c434..db8e779ab 100644 --- a/recognition/UNet_task3_48339261/dataset.py +++ b/recognition/UNet_task3_48339261/dataset.py @@ -2,23 +2,46 @@ dataset.py """ from torch.utils.data import Dataset -from torchvision import transforms import nibabel as nib import numpy as np +import torch import os import glob -import torch +import torchvision.transforms.functional as TF class HipMRIDataset(Dataset): - def __init__(self, data_dir, prostate_label_value=5, resize_to=None): - self.data_dir = data_dir - self.prostate_label_value = prostate_label_value - self.resize_to = resize_to + """ + keras_slices_train/ : traning figure + keras_slices_seg_train/ : traning mask + keras_slices_validate/ : validate figure + keras_slices_seg_validate/ : validate mask + keras_slices_test/ : test figure + keras_slices_seg_test/ : test mask + """ + def __init__(self, data_dir, subset="train", prostate_label_value=5, resize_to=None): + self.subset = subset # 'train', 'validate' or 'test' + self.data_dir = data_dir # the root path of keras_slices_data + self.prostate_label_value = prostate_label_value # the integer value of prostate + self.resize_to = resize_to # tuple of (H, W) + + self.img_dir = os.path.join(data_dir, f"keras_slices_{subset}") + self.seg_dir = os.path.join(data_dir, f"keras_slices_seg_{subset}") + + self.image_files = sorted(glob.glob(os.path.join(self.img_dir, "*.nii.gz"))) + self.mask_files = sorted(glob.glob(os.path.join(self.seg_dir, "*.nii.gz"))) + + # validate the files exist + if len(self.image_files) == 0: + raise FileNotFoundError(f"no file in {self.img_dir}") + if len(self.mask_files) == 0: + raise FileNotFoundError(f"no mask file in {self.seg_dir}") + + # check the number of figure and mask are mactch + if subset != "validate" and len(self.image_files) != len(self.mask_files): + print(f"The number of Figure ({len(self.image_files)}) and Mask ({len(self.mask_files)}) are not match") - self.image_files = [] - self.mask_files = [] + print(f"Success load {subset} set: find {len(self.image_files)} figure file。") - image_paths = sorted(glob.glob(os.path.join(data_dir, "*_image.nii.gz"))) # match the Mask file according to the image name for img_path in image_paths: @@ -38,48 +61,41 @@ def __len__(self): def __getitem__(self, idx): - image_path = self.image_files[idx] - mask_path = self.mask_files[idx] + img_path = self.image_files[idx] + + # path of mask + img_filename = os.path.basename(img_path) + mask_filename = img_filename.replace("case_", "seg_") + mask_path = os.path.join(self.seg_dir, mask_filename) - image_nii = nib.load(image_path) - mask_nii = nib.load(mask_path) + # back to index mathch + if not os.path.exists(mask_path): + if idx < len(self.mask_files): + mask_path = self.mask_files[idx] + else: + raise FileNotFoundError(f"can't find mask for {img_path} figure") - image = image_nii.get_fdata().astype(np.float32) - mask = mask_nii.get_fdata().astype(np.uint8) + # load Nifti file + image = nib.load(img_path).get_fdata().astype(np.float32) + mask = nib.load(mask_path).get_fdata().astype(np.uint8) - image_tensor = torch.from_numpy(image.copy()) - mask_tensor = torch.from_numpy(mask.copy()) + # change to Tensors + image_tensor = torch.from_numpy(image.copy()).unsqueeze(0) # (1,H,W) + mask_tensor = torch.from_numpy(mask.copy()).long() # (H,W) - # add chanel dimention :[1, H, W] - if image_tensor.ndim == 2: - image_tensor = image_tensor.unsqueeze(0) - if mask_tensor.ndim == 2: - mask_tensor = mask_tensor.unsqueeze(0) - - # resize + # Resize if self.resize_to: - image_tensor = transforms.functional.resize( - image_tensor, - self.resize_to, - interpolation=transforms.InterpolationMode.BILINEAR - ) - mask_tensor = transforms.functional.resize( - mask_tensor, - self.resize_to, - interpolation=transforms.InterpolationMode.NEAREST - ) + image_tensor = TF.resize(image_tensor, self.resize_to, interpolation=TF.InterpolationMode.BILINEAR) + # unsqueeze, then resize, then squeeze + mask_tensor = TF.resize(mask_tensor.unsqueeze(0), self.resize_to, interpolation=TF.InterpolationMode.NEAREST).squeeze(0) # Z-score normalization - mean = image_tensor.mean() - std = image_tensor.std() - if std > 1e-6: - image_tensor = (image_tensor - mean) / std - else: - image_tensor = image_tensor - mean - - binary_mask = (mask_tensor == self.prostate_label_value).long() + mean, std = image_tensor.mean(), image_tensor.std() + # 1e-6 to prevent dividing by 0 + image_tensor = (image_tensor - mean) / (std + 1e-6) - # mask shape to [H, W] - binary_mask = binary_mask.squeeze(0) + # binaray mask + binary_mask = (mask_tensor == self.prostate_label_value).long() return image_tensor, binary_mask + From f233458794bcbab7ac5d59a5e57175eea423e9b5 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:21:42 +1100 Subject: [PATCH 47/71] Add .gitignore to exclude .DS_Store --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..bd6cb6a05 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store + +__pycache__/ +*.pyc + +*.ipynb_checkpoints/ + +*.log From a6e9eaf8697a156b3e2d2a30e9dde89a34269927 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:24:35 +1100 Subject: [PATCH 48/71] fix: delete repetitive code --- recognition/UNet_task3_48339261/dataset.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/recognition/UNet_task3_48339261/dataset.py b/recognition/UNet_task3_48339261/dataset.py index db8e779ab..11d13d222 100644 --- a/recognition/UNet_task3_48339261/dataset.py +++ b/recognition/UNet_task3_48339261/dataset.py @@ -42,21 +42,7 @@ def __init__(self, data_dir, subset="train", prostate_label_value=5, resize_to=N print(f"Success load {subset} set: find {len(self.image_files)} figure file。") - - # match the Mask file according to the image name - for img_path in image_paths: - mask_path = img_path.replace("_image.nii.gz", "_mask.nii.gz") - if os.path.exists(mask_path): - self.image_files.append(img_path) - self.mask_files.append(mask_path) - - if len(self.image_files) == 0: - print(f"No matching files found in {data_dir}") - else: - print(f"Found {len(self.image_files)} image/mask pairs.") - def __len__(self): - return len(self.image_files) From 4e7678458f64049a14d96f4792862b0d563ae31a Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:34:29 +1100 Subject: [PATCH 49/71] fix: update the dataset path and add a checking for file exist --- recognition/UNet_task3_48339261/predict.py | 37 ++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/recognition/UNet_task3_48339261/predict.py b/recognition/UNet_task3_48339261/predict.py index c68d3ae19..5f8f4e80a 100644 --- a/recognition/UNet_task3_48339261/predict.py +++ b/recognition/UNet_task3_48339261/predict.py @@ -65,20 +65,25 @@ def show_predictions(model, dataset, title="Final segmentation results (HipMRI)" PROSTATE_LABEL = 5 NUM_EXAMPLES_TO_SHOW = 3 - print(f"Loading model: {MODEL_SAVE_PATH}") - model = SimpleUNet(in_channels=1, out_channels=1).to(device) - - model.load_state_dict(torch.load(MODEL_SAVE_PATH, map_location=device)) - - print(f"Loading dataset: {DATA_DIR}") - test_dataset = HipMRIDataset( - data_dir=DATA_DIR, - resize_to=RESIZE_TO, - prostate_label_value=PROSTATE_LABEL - ) - - if len(test_dataset) > 0: - print(f"Generating {NUM_EXAMPLES_TO_SHOW} prediction examples...") - show_predictions(model, test_dataset, n=NUM_EXAMPLES_TO_SHOW) + # Chenk the file exists + if not os.path.exists(MODEL_SAVE_PATH): + print(f"Error: can't find file '{MODEL_SAVE_PATH}'。") else: - print("Error: Dataset is empty.") \ No newline at end of file + print(f"Loading the model: {MODEL_SAVE_PATH}") + model = SimpleUNet(in_channels=1, out_channels=1).to(device) + + model.load_state_dict(torch.load(MODEL_SAVE_PATH, map_location=device)) + + print(f"Loading the test set: {DATA_DIR} (subset=test)") + test_dataset = HipMRIDataset( + data_dir=DATA_DIR, + subset="test", + resize_to=RESIZE_TO, + prostate_label_value=PROSTATE_LABEL + ) + + if len(test_dataset) > 0: + print(f"Generating {NUM_EXAMPLES_TO_SHOW} prediction examples...") + show_predictions(model, test_dataset, n=NUM_EXAMPLES_TO_SHOW) + else: + print("Error: Dataset is empty.") \ No newline at end of file From f88daf74164ea2606eef507174cab40d64984044 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:45:48 +1100 Subject: [PATCH 50/71] fix: delete the random split and use the new split method --- recognition/UNet_task3_48339261/train.py | 66 ++++++++++++++---------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/recognition/UNet_task3_48339261/train.py b/recognition/UNet_task3_48339261/train.py index 6fdaf2f3a..a3b9728cf 100644 --- a/recognition/UNet_task3_48339261/train.py +++ b/recognition/UNet_task3_48339261/train.py @@ -4,17 +4,19 @@ import torch import torch.optim as optim from torch.utils.data import DataLoader, Dataset -from torch.utils.data import random_split - from modules import DiceLoss, SimpleUNet from dataset import HipMRIDataset from utils import show_epoch_predictions, plot_loss, calculate_dice_score - +from tqdm import tqdm import numpy as np import matplotlib.pyplot as plt + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every=1): +def train(model, train_loader, val_dataset, epochs=3, lr=0.001, visualize_every=1): + """ + param val_dataset: validation set + """ model.to(device) criterion = DiceLoss() optimizer = optim.Adam(model.parameters(), lr=lr) @@ -25,15 +27,18 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every for epoch in range(epochs): model.train() epoch_loss = 0 + + # show process bar + progress_bar = tqdm(train_loader, desc=f"Epoch {epoch+1}/{epochs}", leave=True) - for batch_idx, (images, masks) in enumerate(train_loader): + for batch_idx, (images, masks) in enumerate(progress_bar): images = images.to(device) masks = masks.to(device) # Forward pass outputs = model(images) - predictions_squeezed = outputs[:, 0] # (C, H, W) + predictions_squeezed = outputs[:, 0] loss = criterion(predictions_squeezed, masks) # Backward pass @@ -42,67 +47,76 @@ def train(model, train_loader, test_dataset, epochs=3, lr=0.001, visualize_every optimizer.step() epoch_loss += loss.item() + + # show batch loss in prcocess bar + progress_bar.set_postfix(batch_loss=f"{loss.item():.4f}") avg_loss = epoch_loss / len(train_loader) losses.append(avg_loss) print(f"Epoch {epoch+1}/{epochs} Complete: Avg Loss = {avg_loss:.4f}") - # Visualize predictions after each epoch (or every few epochs) - if (epoch) % visualize_every == 0: - show_epoch_predictions(model, test_dataset, epoch + 1, n=3) + # visualization + if (epoch + 1) % visualize_every == 0 or (epoch + 1) == epochs: + show_epoch_predictions(model, val_dataset, epoch + 1, n=3) print("Training complete with enhanced U-Net") plot_loss(losses) return losses - if __name__ == "__main__": print(f"Starting training (train.py) ---") DATA_DIR = "/home/groups/comp3710/HipMRI_Study_open/keras_slices_data" MODEL_SAVE_PATH = "hipmri_unet_model.pth" + EPOCHS = 20 LEARNING_RATE = 0.001 BATCH_SIZE = 16 RESIZE_TO = (128, 128) - PROSTATE_LABEL = 5 - VALIDATION_SPLIT = 0.2 + PROSTATE_LABEL = 5 - dataset = HipMRIDataset( + # load and split dataset + print(f"Loading dataset from {DATA_DIR} ...") + + # load the split train dataset + train_dataset = HipMRIDataset( data_dir=DATA_DIR, + subset="train", resize_to=RESIZE_TO, prostate_label_value=PROSTATE_LABEL ) - val_size = int(len(dataset) * VALIDATION_SPLIT) - train_size = len(dataset) - val_size - - train_dataset, val_dataset = random_split(dataset, [train_size, val_size], - generator=torch.Generator().manual_seed(42)) + # load the split validate dataset + val_dataset = HipMRIDataset( + data_dir=DATA_DIR, + subset="validate", + resize_to=RESIZE_TO, + prostate_label_value=PROSTATE_LABEL + ) - print(f"Dataset split: {len(train_dataset)} training samples, {len(val_dataset)} validation samples") + print(f"Dataset loaded {len(train_dataset)} train dataset, {len(val_dataset)} validate sataset") + # DataLoader train_loader = DataLoader( train_dataset, batch_size=BATCH_SIZE, shuffle=True, - num_workers=4 + num_workers=2 ) - val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False) - model = SimpleUNet(in_channels=1, out_channels=1).to(device) + # start training training_losses = train( model=model, train_loader=train_loader, - test_dataset=val_dataset, + val_dataset=val_dataset, epochs=EPOCHS, lr=LEARNING_RATE, - visualize_every=5 + visualize_every=5 ) - print("Traning Complete") + print("Training Complete") torch.save(model.state_dict(), MODEL_SAVE_PATH) - print(f"Model saved in: {MODEL_SAVE_PATH}") + print(f"Model saved to: {MODEL_SAVE_PATH}") \ No newline at end of file From 7eb4608bd211620f433f69311628b561e99bc4dc Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:48:24 +1100 Subject: [PATCH 51/71] fix: change the Inappropriate word --- recognition/UNet_task3_48339261/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/UNet_task3_48339261/utils.py b/recognition/UNet_task3_48339261/utils.py index ae6327a94..7d9c7a9a8 100644 --- a/recognition/UNet_task3_48339261/utils.py +++ b/recognition/UNet_task3_48339261/utils.py @@ -48,7 +48,7 @@ def show_epoch_predictions(model, dataset, epoch, n=3): # Show ground truth binary mask axes[1, i].imshow(true_mask.cpu().numpy(), cmap='gray') - axes[1, i].set_title(f'Ground Truth (前列腺)') + axes[1, i].set_title(f'Ground Truth (Prostate)') axes[1, i].axis('off') # Show prediction From 1b4a1d6047e86c415f1ae18db2ba27c7a3cda950 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:50:19 +1100 Subject: [PATCH 52/71] change the partition zone --- recognition/UNet_task3_48339261/run_task3.slurm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/UNet_task3_48339261/run_task3.slurm b/recognition/UNet_task3_48339261/run_task3.slurm index 7fb1de057..ee9cf1ce1 100644 --- a/recognition/UNet_task3_48339261/run_task3.slurm +++ b/recognition/UNet_task3_48339261/run_task3.slurm @@ -4,7 +4,7 @@ #SBATCH --output=task3_job_%j.out #SBATCH --error=task3_job_%j.err -#SBATCH --partition=gpu +#SBATCH --partition=comp3710 #SBATCH --gres=gpu:1 #SBATCH --nodes=1 #SBATCH --ntasks-per-node=1 From 8ce825d3d105d5da4a352ae60c5650b1094164df Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 14:51:47 +1100 Subject: [PATCH 53/71] Rename run_task3.slurm to job.slurm --- recognition/UNet_task3_48339261/{run_task3.slurm => job.slurm} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename recognition/UNet_task3_48339261/{run_task3.slurm => job.slurm} (100%) diff --git a/recognition/UNet_task3_48339261/run_task3.slurm b/recognition/UNet_task3_48339261/job.slurm similarity index 100% rename from recognition/UNet_task3_48339261/run_task3.slurm rename to recognition/UNet_task3_48339261/job.slurm From 4510c156fb5eed033a017921f39223b710d3d2ce Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 15:36:12 +1100 Subject: [PATCH 54/71] fix: change the data path for process in Colab --- recognition/UNet_task3_48339261/train.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recognition/UNet_task3_48339261/train.py b/recognition/UNet_task3_48339261/train.py index a3b9728cf..da5acd6a9 100644 --- a/recognition/UNet_task3_48339261/train.py +++ b/recognition/UNet_task3_48339261/train.py @@ -67,8 +67,8 @@ def train(model, train_loader, val_dataset, epochs=3, lr=0.001, visualize_every= print(f"Starting training (train.py) ---") - DATA_DIR = "/home/groups/comp3710/HipMRI_Study_open/keras_slices_data" - MODEL_SAVE_PATH = "hipmri_unet_model.pth" + DATA_DIR = "/content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/keras_slices_data" + MODEL_SAVE_PATH = "/content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/hipmri_unet_model.pth" EPOCHS = 20 LEARNING_RATE = 0.001 From a903ede469e4145e7006067e63325a8b973ca589 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 15:36:50 +1100 Subject: [PATCH 55/71] fix: change the data path for process in Colab --- recognition/UNet_task3_48339261/predict.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recognition/UNet_task3_48339261/predict.py b/recognition/UNet_task3_48339261/predict.py index 5f8f4e80a..99c105512 100644 --- a/recognition/UNet_task3_48339261/predict.py +++ b/recognition/UNet_task3_48339261/predict.py @@ -59,8 +59,8 @@ def show_predictions(model, dataset, title="Final segmentation results (HipMRI)" if __name__ == '__main__': print("Runing predict.py...") - DATA_DIR = "/home/groups/comp3710/HipMRI_Study_open/keras_slices_data" - MODEL_SAVE_PATH = "hipmri_unet_model.pth" + DATA_DIR = "/content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/keras_slices_data" + MODEL_SAVE_PATH = "/content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/hipmri_unet_model.pth" RESIZE_TO = (128, 128) PROSTATE_LABEL = 5 NUM_EXAMPLES_TO_SHOW = 3 From 607cc993dcca2c8ece94a4ed8b4af45c3239fe42 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 23:02:03 +1100 Subject: [PATCH 56/71] write report in it --- recognition/UNet_task3_48339261/README.md | 135 +++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index 1ea23bd78..d4e349e61 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -1 +1,134 @@ -# 1_UNet_48339261 +# COMP3710 Report + +# Task 3 - 2D Prostate Segmentation with U-Net + +## Guanhua Ma 48339261 + + + +## 1. Project Description & Problem + +This project aims to solve the Task 3 (Normal Difficulty) pattern recognition problem. + +This project uses the processed 2D slices from the HipMRI Study on Prostate Cancer dataset to perform automatic semantic segmentation of the prostate region. The goal is to train a 2D Improved UNet to achieve a minimum Dice Similarity Coefficient (DSC) of 0.75 on the prostate label in the test set. + +## 2. Project File + +These are Project Files: + +- `modules.py`: Contains the definitions for the `SimpleUNet` model architecture and the `DiceLoss` function. +- `dataset.py`: Contains the `HipMRIDataset` class, responsible for loading and preprocessing the Nifti data. +- `train.py`: Contains the main training loop `train()`, the train/validation split logic, and the `if __name__ == "__main__":` entry point. +- `predict.py`: Contains the `show_predictions()` function to load the trained model and visualize its performance on test samples. +- `utils.py`: Contains helper functions such as `calculate_dice_score()`, `show_epoch_predictions()`, and `plot_loss()`. +- `README.md`: The report document for this project. + +## 3. How it Works & Algorithm + +#### Algorithm Model + +This section is based on the SimpleUNet model defined in modules.py. This is a classic 2D U-Net architecture , which is an encoder-decoder network. Its key feature is the use of skip connections. This concatenates feature maps from the encoder (down-sampling path) with the decoder (up-sampling path). UNet allows the model to use both semantic features and spatial features, making it ideal for medical image segmentation. + +The model's main components include: + +- Encoder: A series of conv_block (Conv -> BatchNorm -> LeakyReLU -> Dropout) and MaxPool2d layers to extract features and reduce spatial dimensions. +- Decoder: Uses Upsample (Bilinear Interpolation) and _conv_block layers to reconstruct the segmentation mask. +- Output Layer: A final Conv2d layer followed by a Sigmoid activation function to output a probability map in the range [0, 1]. + +The Loss Function is Dice Loss (1 - Dice Coefficient), which directly optimizes DSC. It is well-suited for imbalanced classes. Therefore, it is very suitable for this task since the prostate region is much smaller than the background. + +The optimizer is Adam. Adam is an efficient and commonly used gradient descent optimizer. + +#### Data Preprocessing + +1. Data Loading: Uses the nibabel library to load .nii.gz Nifti format images and masks. +2. Label Binarization: The HipMRI masks are multi-class. To solve Task 3, pixels with the `prostate_label_value` (set to 5 in the code) are mapped to 1, and all other pixels are mapped to 0. This creates a binary prostate vs. non-prostate mask. +3. Resizing: All images and masks are resized to a fixed (128, 128) size. + - Images are resized using Bilinear interpolation. + - Masks are resized using Nearest Neighbor interpolation to ensure the label values (0 and 1) are not corrupted. +4. Normalization: Z-score normalization ((image - mean) / std) is applied to the images to set their mean to 0 and standard deviation to 1. + +This dataset keras_slices_data folder has already split in three subset: keras_slices_train, keras_slices_validate and keras_slices_test. +For this structure, the parameter `subset` is used to construct training set and validation set. + +`subset="train"`: loading the keras_slices_train folder. + +`subset="validate"`: loading the keras_slices_validate folder + +## 4. Reproducibility + +This project was run on the Google Colab with T4 GPU orA100 GPU + +The main dependencies are: + +``` +torch (PyTorch) +numpy +matplotlib +nibabel (!pip install nibabel) +tqdm +``` + +Due to the time queue in Rangpur is too long , so this project choose to run in the Goole Colab + +The files 'modules.py', 'dataset.py', 'train.py', 'predict.py', 'utils.py' and 'keras_slices_data' need to be in the same folder in Google Drive. + +Also, create another colab.ipynb file to run this code in the same folder. + +Make sure that the path is /content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/modules.py, for example for modules.py. + +Run this code can process the whole task. + +``` +# Run Task 3 in Google Colab + +from google.colab import drive +drive.mount('/content/drive', force_remount=True) + +!pip install nibabel -q + +import os +base_dir = "/content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261" +os.chdir(base_dir) + +# load dataset and train model. save hipmri_unet_model.pth and .png +print("Starting process train.py") +!python train.py + +# save final_predictions.png +print("Starting process predict.py") +!python predict.py + +print("Successful!") +print("Check the documents:") +print("hipmri_unet_model.pth") +print("training_loss_curve.png") +print("epoch_X_predictions.png") +print("final_predictions.png") +``` + +### 5. Results & Analysis + +#### Training Loss Curve + +The training loss (Dice Loss) over time is shown below in Figure 1. The loss steadily decreases from an initial average of 0.6089 and successfully converges to a final average loss of 0.1496. This indicates that the model learned effectively from the training data. + +![training_loss_curve (1)](/Users/maguanhua/Downloads/training_loss_curve (1).png) + +[Figure 1: Training loss curve] + +#### Prediction Visualization + +The figure below shows the final segmentation performance of the model on 3 random samples from the test set after training for 20 epochs. + +![final_predictions](/Users/maguanhua/Downloads/final_predictions (1).png) + +[Figure 2: Final predictions] + +After training for 20 epochs, the model achieved a final average Dice Loss of 0.1496 on the training set, which corresponds to an average DSC of **0.8504**. + +This result exceeds the target of 0.75. + +The model's performance on the random test samples in Figure 2: Final predictions is excellent。It correctly identified two **True Negatives** (Original 110 and 39). These two samples with no prostate was present in the ground truth, and predicted an empty mask. Sample 110 and 39 result in perfect Dice scores of 1.000. The model only failed on Sample 315, which was a very small and challenging target, resulting in a Dice score of 0.000. + +Overall, the high average DSC (0.8504) and the strong performance on True Negatives confirm that the model successfully learned to segment the prostate gland. \ No newline at end of file From bdb608ffd9ce24f90eeff54f0f4a4c4ae797fa4c Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 23:14:24 +1100 Subject: [PATCH 57/71] write report in it --- recognition/UNet_task3_48339261/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index d4e349e61..0cc87510c 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -18,7 +18,7 @@ These are Project Files: - `modules.py`: Contains the definitions for the `SimpleUNet` model architecture and the `DiceLoss` function. - `dataset.py`: Contains the `HipMRIDataset` class, responsible for loading and preprocessing the Nifti data. -- `train.py`: Contains the main training loop `train()`, the train/validation split logic, and the `if __name__ == "__main__":` entry point. +- `train.py`: Contains the main training loop `train()`, the train/validation split logic. - `predict.py`: Contains the `show_predictions()` function to load the trained model and visualize its performance on test samples. - `utils.py`: Contains helper functions such as `calculate_dice_score()`, `show_epoch_predictions()`, and `plot_loss()`. - `README.md`: The report document for this project. From 973ed2095956f81fa7a36d53d7d2a9ce459bcbc6 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 23:25:25 +1100 Subject: [PATCH 58/71] docs: Remove Rangpur .slurm file, useless file --- recognition/UNet_task3_48339261/job.slurm | 28 ----------------------- 1 file changed, 28 deletions(-) delete mode 100644 recognition/UNet_task3_48339261/job.slurm diff --git a/recognition/UNet_task3_48339261/job.slurm b/recognition/UNet_task3_48339261/job.slurm deleted file mode 100644 index ee9cf1ce1..000000000 --- a/recognition/UNet_task3_48339261/job.slurm +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -#SBATCH --job-name=comp3710_task3 -#SBATCH --output=task3_job_%j.out -#SBATCH --error=task3_job_%j.err - -#SBATCH --partition=comp3710 -#SBATCH --gres=gpu:1 -#SBATCH --nodes=1 -#SBATCH --ntasks-per-node=1 -#SBATCH --cpus-per-task=4 -#SBATCH --mem=16G -#SBATCH --time=01:00:00 - - -module purge - -module load cuda/11.1 -module load anaconda3/2022.05 - -source ~/miniconda3/etc/profile.d/conda.sh -conda activate comp3710 - -python train.py -echo "Traning Complete, running predict.py ---" - -python predict.py -echo "Task Finish" \ No newline at end of file From 2687d68464b0c8b50b1af00e09bd4f52c84b9949 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 23:28:41 +1100 Subject: [PATCH 59/71] docs: Add Run_on_Colab.ipynb for reproducibility --- .../UNet_task3_48339261/Run_on_Colab.ipynb | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 recognition/UNet_task3_48339261/Run_on_Colab.ipynb diff --git a/recognition/UNet_task3_48339261/Run_on_Colab.ipynb b/recognition/UNet_task3_48339261/Run_on_Colab.ipynb new file mode 100644 index 000000000..b0d157f5c --- /dev/null +++ b/recognition/UNet_task3_48339261/Run_on_Colab.ipynb @@ -0,0 +1,49 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "86c0c3bc", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "outputs": [], + "source": [ + "# Run Task 3 in Google Colab\n", + "\n", + "from google.colab import drive\n", + "drive.mount('/content/drive', force_remount=True) \n", + "\n", + "!pip install nibabel -q \n", + "\n", + "import os\n", + "base_dir = \"/content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261\"\n", + "os.chdir(base_dir)\n", + "\n", + "# load dataset and train model. save hipmri_unet_model.pth and .png \n", + "print(\"Starting process train.py\")\n", + "!python train.py\n", + "\n", + "# save final_predictions.png\n", + "print(\"Starting process predict.py\")\n", + "!python predict.py\n", + "\n", + "print(\"Successful!\")\n", + "print(\"Check the documents:\")\n", + "print(\"hipmri_unet_model.pth\")\n", + "print(\"training_loss_curve.png\")\n", + "print(\"epoch_X_predictions.png\")\n", + "print(\"final_predictions.png\")" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 2a128a2e2e95b2368c6b4e577e11b6e9197ba9da Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 23:37:09 +1100 Subject: [PATCH 60/71] fix: specify the process for reproducibility on Colab --- recognition/UNet_task3_48339261/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index 0cc87510c..53cd5fc44 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -71,13 +71,13 @@ tqdm Due to the time queue in Rangpur is too long , so this project choose to run in the Goole Colab -The files 'modules.py', 'dataset.py', 'train.py', 'predict.py', 'utils.py' and 'keras_slices_data' need to be in the same folder in Google Drive. +The files `modules.py`, `dataset.py`, `train.py`, `predict.py`, `utils.py` and `keras_slices_data` and `Run_on_Colab.ipynb` need to be in the same folder in Google Drive. -Also, create another colab.ipynb file to run this code in the same folder. +Open the `Run_on_Colab.ipynb` and run for the whole task. -Make sure that the path is /content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/modules.py, for example for modules.py. +Make sure that the path is /content/drive/MyDrive/Colab-Notebooks/UNet_task3_48339261/modules.py, for example for `modules.py`. -Run this code can process the whole task. +This is the processing code in `Run_on_Colab.ipynb`. ``` # Run Task 3 in Google Colab From b2f36d8850e9613f0b4721b9e4c82042616d1aff Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Thu, 30 Oct 2025 23:41:18 +1100 Subject: [PATCH 61/71] cite: the code SimpleUNet in UNet_segmentation_code_demo.ipynb --- recognition/UNet_task3_48339261/modules.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recognition/UNet_task3_48339261/modules.py b/recognition/UNet_task3_48339261/modules.py index 2ebb99565..7723f52bf 100644 --- a/recognition/UNet_task3_48339261/modules.py +++ b/recognition/UNet_task3_48339261/modules.py @@ -1,5 +1,7 @@ """ modules.py + +cite: UNet_segmentation_code_demo.ipynb """ import torch import torch.nn as nn From 8923fedefc845fd2034a735dc37f7f319e47ca69 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Fri, 31 Oct 2025 00:03:11 +1100 Subject: [PATCH 62/71] cite: Ai assistant for stride error and mask resize problem --- recognition/UNet_task3_48339261/dataset.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/recognition/UNet_task3_48339261/dataset.py b/recognition/UNet_task3_48339261/dataset.py index 11d13d222..d7ccfeeb0 100644 --- a/recognition/UNet_task3_48339261/dataset.py +++ b/recognition/UNet_task3_48339261/dataset.py @@ -1,5 +1,10 @@ """ dataset.py +Gemini-Assist: +1. Suggested using .copy() to resolve negative stride error. This ensures +the numpy array is contiguous before tensor conversion. +2. Mask resize use NEAREST interpolation to prevent corrupting the (0, 1) label values. +Using BILINEAR would create invalid fractional values. """ from torch.utils.data import Dataset import nibabel as nib From e44658fc46b89b0eb3bae02126d9ecbb3d4f96e9 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Fri, 31 Oct 2025 00:05:45 +1100 Subject: [PATCH 63/71] cite: Ai assistant --- recognition/UNet_task3_48339261/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/recognition/UNet_task3_48339261/utils.py b/recognition/UNet_task3_48339261/utils.py index 7d9c7a9a8..a9f14c34e 100644 --- a/recognition/UNet_task3_48339261/utils.py +++ b/recognition/UNet_task3_48339261/utils.py @@ -1,5 +1,8 @@ """ utils.py + +Gemini assist: +plt.show() hangs in Colab. Need to use plt.savefig() and plt.close() """ import torch import numpy as np From 7d0bcb1805d4f785d2aeac70b884e4cf781e2258 Mon Sep 17 00:00:00 2001 From: GuanhuaMa Date: Fri, 31 Oct 2025 00:11:48 +1100 Subject: [PATCH 64/71] fix: specify the process for reproducibility on Colab --- recognition/UNet_task3_48339261/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index 53cd5fc44..6cff3a31a 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -69,7 +69,7 @@ nibabel (!pip install nibabel) tqdm ``` -Due to the time queue in Rangpur is too long , so this project choose to run in the Goole Colab +Due to the time queue in Rangpur is too long, so this project choose to run in the Goole Colab The files `modules.py`, `dataset.py`, `train.py`, `predict.py`, `utils.py` and `keras_slices_data` and `Run_on_Colab.ipynb` need to be in the same folder in Google Drive. From 99dd77a9597fb09cd34831b8d8e043c2dd83b2b6 Mon Sep 17 00:00:00 2001 From: GuanhuaMa <150640477+GuanhuaMa@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:49:43 +1000 Subject: [PATCH 65/71] Add Figure 1 & 2 to show in README.md --- .../final_predictions (1).png | Bin 0 -> 143247 bytes .../training_loss_curve (1).png | Bin 0 -> 28152 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 recognition/UNet_task3_48339261/final_predictions (1).png create mode 100644 recognition/UNet_task3_48339261/training_loss_curve (1).png diff --git a/recognition/UNet_task3_48339261/final_predictions (1).png b/recognition/UNet_task3_48339261/final_predictions (1).png new file mode 100644 index 0000000000000000000000000000000000000000..3d9ec17c2f268ada57c7c7ff206cbc598060f9d0 GIT binary patch literal 143247 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�TfIdip-b3gpAn@l*?aqZaj?f+ous^gk!-#*gP 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
WmZ3y{9km>=o9W#GB+W~xaQ2k zyHAn<3k2_f!HPM75T; zIHv&S-2_RU7Nj-z`7r?Xp=LY^#^`@zdPZx`+1gs;${Hg+K{}sW2W&S1;8R_c0eC~V zojKM-edd{ZBf=NR$5IJxTm0w8zrLvDWuBOjfx}JT$JLBTK!6t%F6nf0MxbYlz(!WR zop zue~&MxA4(itjK>+WZby{O=jxGF&%UF#)-8;GwOs{u0sAb9v3&B5>w|rzQ18|e|z&N zRi(ffoj1&mC|o5o%lfHk`PGW+(B&n&Wu;%%7V>BXK;vGm4gZ>XK^fzBMk0NH2MtnV z|MWIM1#k!+OXE%#aaV2p8L@Oje3oZS-#tkkB>i`+2R1lopyujxjK^q9gTiqRIR;D- zv+)>S?6)=J&)#O#Q!NeXq@3GxUk2S*oB;2$w2<{i7>1B~dX{fOX{sYDY_wY7q@1R! zh~b=Afg=>!Znl7KyM`F5ywwn4UpD0BA%vIO(t_ttpoO}Ha*A)Lu?5yZW*iz>2LoB$ zC15cxN(qpZ}4HPKa7A2mI-P|KqPFLVN;1&4B8$np)LYETiW zb8dERHilydxo`ZonIjA8)-PA11Dm^kY66hOyYBXoHd&08Z*gnfJq zuxk3IDf3UAbo z5p{IND+ln9wT)ewk^`4ADui9bd5ROlZ%M8Ffc#OKm~D!!rky>$)sO;&QxMf z0?O$Dvi5ZSCl!RM`2gKV>jjY~adZ|LekyT}E=R}`LdtxmrvN(^8!H&P2}G@1sDIP@ z2q5dgV~9QtgQL*Jk|~7WOd&R8cPbee z3wB`jKZ%H6*Q`O^wEs03;VZ)T0sbT@oMHftHKbz3ec6^v%>h6R0U1LoL`2X$WT-aD z%`0{wQXd0xwfWG4v`6A0gw_HIP!vKAw3e2pdQQR$XEj;DqMYl2{f{Na35Xg?0*|*1 zA*dK3S@$twZ`ibGE_>iodI-K28hMeuVl3etlSfo8XngiJEdKPzG60C989a+ZgkU5K z6xv4WkyxIEo#xsS^EIfl5-k87H;=Ga_c-y9K(1^I#3B%Qat?EKvL0(3QgO#Oamz+# z+>90q0deF{La(?}KD%BJ zz}~9;g&-|Hsz3h6BJ7!I*#Hf16VWY~lNwC-JDyk>#x(gXrxXKZiCT8nDBNWyH|ixKbkH10nLzqmF=C3sh91(IlDsDGO(A+H;i+Ht z?;+EVLM|sH=*zF^XQ*wU@U^)skfrA#O-{~icg~$)0qA+&Q;MtxNOMi0Qw*QIF+`G@ z^EY={>!*ipTf=Lnj5PD^h~xZbK44}K)q%n!DIo#Dk4eU~ADMTzoVV>qhHqCR=wq00 zl7`G*ZpIjLBw?wYE^bjQ*+1dsp$K^d;LFacdZesr%|WzT^PJ*_-!RkKP{*pBeAG=#eg*7oEwuuE;0RxRKvD582_5EAssi}?4 zaL7Lb%%Ffw#yNZPfnih?u%tCD=#F={CJ9PzX}A{>(9+{!#j(!T$O|D` zgji5|?At(|7Ui>+)E`SHyfWe;4}#2eN}7}91_bCrul`vcOziNzyYIBT2q$NzQdI=R zN*tFS7~aOT-ev=laZ?hA1puDjb_z(^Qs@~^fEaECGO9B`_72;KgrCdd)u+?L1$+Tz zRKUBktt|mv13D@LRpI^!ON;u16_gH4Idyw+-S`{AhqUY;p@AGS5RDdsEKL-<@Kj2! zinK#m53$yxm+x|bJJtOlTdG3=rf7Zkg7_o?rwWv+bJ(#9o#=wJR4ItDhR$6p=4cB< z67ubxi66?kz%CU6g)wNx{VQPhKo0z*o_owe8iQaK7<^l zw5l0R*;>Z_c_WL)bUqFKoE`D>pW3=6*?-S{{v|F_kUFVWQVz+`TE>!RJHLEa@=x%# z)ZRM&ek#F+@*mF7{^eEvn}mY@S0nV-Z~rwV|9;KtUrX`djXV8?6#hrV*MBYK|M!I) q0mxDKgBYZ$pLc1+f08^^>AU@$n0CmqG!Ol@{cfjS#lM~S$NvKHBkTqM literal 0 HcmV?d00001 diff --git a/recognition/UNet_task3_48339261/training_loss_curve (1).png b/recognition/UNet_task3_48339261/training_loss_curve (1).png new file mode 100644 index 0000000000000000000000000000000000000000..3142238ab5b9334fc39a7b4887bdba0ebaaae8f4 GIT binary patch literal 28152 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 Date: Fri, 31 Oct 2025 08:53:43 +1000 Subject: [PATCH 66/71] Fix image links in README.md Updated image links in README to point to GitHub raw URLs. --- recognition/UNet_task3_48339261/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index 6cff3a31a..84a454e40 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -113,7 +113,7 @@ print("final_predictions.png") The training loss (Dice Loss) over time is shown below in Figure 1. The loss steadily decreases from an initial average of 0.6089 and successfully converges to a final average loss of 0.1496. This indicates that the model learned effectively from the training data. -![training_loss_curve (1)](/Users/maguanhua/Downloads/training_loss_curve (1).png) +![training_loss_curve (1)]((https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/training_loss_curve (1).png) [Figure 1: Training loss curve] @@ -121,7 +121,7 @@ The training loss (Dice Loss) over time is shown below in Figure 1. The loss ste The figure below shows the final segmentation performance of the model on 3 random samples from the test set after training for 20 epochs. -![final_predictions](/Users/maguanhua/Downloads/final_predictions (1).png) +![final_predictions](https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/final_predictions (1).png) [Figure 2: Final predictions] @@ -131,4 +131,4 @@ This result exceeds the target of 0.75. The model's performance on the random test samples in Figure 2: Final predictions is excellent。It correctly identified two **True Negatives** (Original 110 and 39). These two samples with no prostate was present in the ground truth, and predicted an empty mask. Sample 110 and 39 result in perfect Dice scores of 1.000. The model only failed on Sample 315, which was a very small and challenging target, resulting in a Dice score of 0.000. -Overall, the high average DSC (0.8504) and the strong performance on True Negatives confirm that the model successfully learned to segment the prostate gland. \ No newline at end of file +Overall, the high average DSC (0.8504) and the strong performance on True Negatives confirm that the model successfully learned to segment the prostate gland. From a450fb80834d53b688653255f6d5e6a724add05a Mon Sep 17 00:00:00 2001 From: GuanhuaMa <150640477+GuanhuaMa@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:54:44 +1000 Subject: [PATCH 67/71] Rename final_predictions (1).png to final_predictions_(1).png --- ...redictions (1).png => final_predictions_(1).png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename recognition/UNet_task3_48339261/{final_predictions (1).png => final_predictions_(1).png} (100%) diff --git a/recognition/UNet_task3_48339261/final_predictions (1).png b/recognition/UNet_task3_48339261/final_predictions_(1).png similarity index 100% rename from recognition/UNet_task3_48339261/final_predictions (1).png rename to recognition/UNet_task3_48339261/final_predictions_(1).png From 132bc0b8820fc68cc94e3a4b428baf1c735dfa54 Mon Sep 17 00:00:00 2001 From: GuanhuaMa <150640477+GuanhuaMa@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:54:56 +1000 Subject: [PATCH 68/71] Rename training_loss_curve (1).png to training_loss_curve.png --- ...g_loss_curve (1).png => training_loss_curve.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename recognition/UNet_task3_48339261/{training_loss_curve (1).png => training_loss_curve.png} (100%) diff --git a/recognition/UNet_task3_48339261/training_loss_curve (1).png b/recognition/UNet_task3_48339261/training_loss_curve.png similarity index 100% rename from recognition/UNet_task3_48339261/training_loss_curve (1).png rename to recognition/UNet_task3_48339261/training_loss_curve.png From 522e9086ea8aa0c630e6af4e6ef0b5ee09d25c66 Mon Sep 17 00:00:00 2001 From: GuanhuaMa <150640477+GuanhuaMa@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:55:07 +1000 Subject: [PATCH 69/71] Rename final_predictions_(1).png to final_predictions.png --- ...al_predictions_(1).png => final_predictions.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename recognition/UNet_task3_48339261/{final_predictions_(1).png => final_predictions.png} (100%) diff --git a/recognition/UNet_task3_48339261/final_predictions_(1).png b/recognition/UNet_task3_48339261/final_predictions.png similarity index 100% rename from recognition/UNet_task3_48339261/final_predictions_(1).png rename to recognition/UNet_task3_48339261/final_predictions.png From 56e11a815556491bf5a2eaa05385e9c05c0fd01a Mon Sep 17 00:00:00 2001 From: GuanhuaMa <150640477+GuanhuaMa@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:56:03 +1000 Subject: [PATCH 70/71] Fix image links in README.md Updated image links in README for training loss curve and final predictions. --- recognition/UNet_task3_48339261/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index 84a454e40..aa4da465d 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -113,7 +113,7 @@ print("final_predictions.png") The training loss (Dice Loss) over time is shown below in Figure 1. The loss steadily decreases from an initial average of 0.6089 and successfully converges to a final average loss of 0.1496. This indicates that the model learned effectively from the training data. -![training_loss_curve (1)]((https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/training_loss_curve (1).png) +![training_loss_curve]((https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/training_loss_curve.png) [Figure 1: Training loss curve] @@ -121,7 +121,7 @@ The training loss (Dice Loss) over time is shown below in Figure 1. The loss ste The figure below shows the final segmentation performance of the model on 3 random samples from the test set after training for 20 epochs. -![final_predictions](https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/final_predictions (1).png) +![final_predictions](https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/final_predictions.png) [Figure 2: Final predictions] From b92ec24766a4abaeb9d6b0d00303ca7105975fe4 Mon Sep 17 00:00:00 2001 From: GuanhuaMa <150640477+GuanhuaMa@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:56:35 +1000 Subject: [PATCH 71/71] Fix image markdown for training loss curve Corrected the image markdown syntax for the training loss curve. --- recognition/UNet_task3_48339261/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recognition/UNet_task3_48339261/README.md b/recognition/UNet_task3_48339261/README.md index aa4da465d..fdbcc776a 100644 --- a/recognition/UNet_task3_48339261/README.md +++ b/recognition/UNet_task3_48339261/README.md @@ -113,7 +113,7 @@ print("final_predictions.png") The training loss (Dice Loss) over time is shown below in Figure 1. The loss steadily decreases from an initial average of 0.6089 and successfully converges to a final average loss of 0.1496. This indicates that the model learned effectively from the training data. -![training_loss_curve]((https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/training_loss_curve.png) +![training_loss_curve](https://raw.githubusercontent.com/GuanhuaMa/PatternAnalysis-2025/topic-recognition/recognition/UNet_task3_48339261/training_loss_curve.png) [Figure 1: Training loss curve]