From be6f31f29828d68d2bcdd4b0c4be28343f0982f7 Mon Sep 17 00:00:00 2001 From: Unique-Usman Date: Sun, 24 Mar 2024 14:34:54 +0530 Subject: [PATCH 1/8] A python script to wrap image read and write for Nifti Images. --- WrapImage/nifti_wrapper.py | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 WrapImage/nifti_wrapper.py diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py new file mode 100644 index 0000000..245c7cb --- /dev/null +++ b/WrapImage/nifti_wrapper.py @@ -0,0 +1,64 @@ +import argparse +import json +import os +import nibabel as nib +import numpy as np + +def read_nifti_4d(input_file): + """ + For reading the 4d nifti image + """ + nifti_img = nib.load(input_file) + return nifti_img.get_fdata() + +def read_json_file(json_file): + """ + For reading the json file + """ + + if not os.path.exists(json_file): + raise FileNotFoundError(f"File '{json_file}' not found.") + + with open(json_file, "r") as f: + try: + json_data = json.load(f) + except json.JSONDecodeError as e: + raise ValueError(f"Error decoding JSON in file '{json_file}': {e}") + + return json_data + +def save_nifti_3d(data, output_file): + """ + For saving the 3d nifti images of the output of the algorithm + """ + output_img = nib.Nifti1Image(data, np.eye(4)) + nib.save(output_img, output_file) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Read a 4D NIfTI phantom file along with BIDS JSON, b-vector, and b-value files.") + parser.add_argument("input_file", type=str, help="Path to the input 4D NIfTI file.") + parser.add_argument("bids_dir", type=str, help="Path to the BIDS directory.") + parser.add_argument("--algorithm", type=str, choices=["algorithm1", "algorithm2"], default="algorithm1", help="Select the algorithm to use.") + parser.add_argument("algorithm_args", nargs=argparse.REMAINDER, help="Additional arguments for the algorithm.") + + args = parser.parse_args() + + try: + # Read the 4D NIfTI file + data = read_nifti_4d(args.input_file) + + # Construct the full paths for the JSON, b-vector, and b-value files + json_file = os.path.join(args.bids_dir, "dataset_description.json") + bvec_file = os.path.join(args.bids_dir, "bvecs.json") + bval_file = os.path.join(args.bids_dir, "bvals.json") + + # Read the JSON, b-vector, and b-value files + json_data = read_json_file(json_file) + bvecs = read_json_file(bvec_file) + bvals = read_json_file(bval_file) + + # Pass additional arguments to the algorithm + # Passing the values to the selectect algorithm and saving it + + except Exception as e: + print(f"Error: {e}") From 6c779170ea70b9d4b98617d04d9043b97c87e995 Mon Sep 17 00:00:00 2001 From: Unique-Usman Date: Tue, 26 Mar 2024 02:08:18 +0530 Subject: [PATCH 2/8] Followed up with the required change --- WrapImage/nifti_wrapper.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index 245c7cb..4324bfc 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -2,9 +2,12 @@ import json import os import nibabel as nib +from src.wrappers.OsipiBase import OsipiBase +from utilities.data_simulation.GenerateData import GenerateData import numpy as np +import random -def read_nifti_4d(input_file): +def read_nifti_file(input_file): """ For reading the 4d nifti image """ @@ -27,11 +30,11 @@ def read_json_file(json_file): return json_data -def save_nifti_3d(data, output_file): +def save_nifti_3d(data, output_file, **kwargs): """ For saving the 3d nifti images of the output of the algorithm """ - output_img = nib.Nifti1Image(data, np.eye(4)) + output_img = nib.nifti1.Nifti1Image(data, np.eye(4), **kwargs) nib.save(output_img, output_file) if __name__ == "__main__": @@ -45,7 +48,7 @@ def save_nifti_3d(data, output_file): try: # Read the 4D NIfTI file - data = read_nifti_4d(args.input_file) + data = read_nifti_file(args.input_file) # Construct the full paths for the JSON, b-vector, and b-value files json_file = os.path.join(args.bids_dir, "dataset_description.json") @@ -58,7 +61,20 @@ def save_nifti_3d(data, output_file): bvals = read_json_file(bval_file) # Pass additional arguments to the algorithm + rng = np.random.RandomState(42) + fit = OsipiBase(algorithm=args.algorithm) + S0 = 1 + gd = GenerateData(rng=rng) + D = data["D"] + f = data["f"] + Dp = data["Dp"] + # signal = gd.ivim_signal(D, Dp, f, S0, bvals, SNR, rician_noise) + # Passing the values to the selectect algorithm and saving it + [f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals) + save_nifti_3d(f_fit, "f.nii.gz") + save_nifti_3d(Dp_fit, "dp.nii.gz") + save_nifti_3d(D_fit, "d.nii.gz") except Exception as e: print(f"Error: {e}") From 6db42c1dcc41221c293c9f7dd347c5da6fee7f06 Mon Sep 17 00:00:00 2001 From: Unique-Usman Date: Tue, 26 Mar 2024 23:23:32 +0530 Subject: [PATCH 3/8] Make the required change for follow up --- WrapImage/nifti_wrapper.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index 4324bfc..65ad3f6 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -5,14 +5,13 @@ from src.wrappers.OsipiBase import OsipiBase from utilities.data_simulation.GenerateData import GenerateData import numpy as np -import random def read_nifti_file(input_file): """ For reading the 4d nifti image """ nifti_img = nib.load(input_file) - return nifti_img.get_fdata() + return nifti_img.get_fdata(), nifti_img.header def read_json_file(json_file): """ @@ -30,17 +29,20 @@ def read_json_file(json_file): return json_data -def save_nifti_3d(data, output_file, **kwargs): +def save_nifti_file(data, output_file, affine=None, **kwargs): """ For saving the 3d nifti images of the output of the algorithm """ - output_img = nib.nifti1.Nifti1Image(data, np.eye(4), **kwargs) + if affine is None: + affine = np.eye(data.ndim + 1) + output_img = nib.nifti1.Nifti1Image(data, affine , **kwargs) nib.save(output_img, output_file) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Read a 4D NIfTI phantom file along with BIDS JSON, b-vector, and b-value files.") parser.add_argument("input_file", type=str, help="Path to the input 4D NIfTI file.") parser.add_argument("bids_dir", type=str, help="Path to the BIDS directory.") + parser.add_argument("--affine", type=float, nargs="+", help="Affine matrix for NIfTI image.") parser.add_argument("--algorithm", type=str, choices=["algorithm1", "algorithm2"], default="algorithm1", help="Select the algorithm to use.") parser.add_argument("algorithm_args", nargs=argparse.REMAINDER, help="Additional arguments for the algorithm.") @@ -48,7 +50,7 @@ def save_nifti_3d(data, output_file, **kwargs): try: # Read the 4D NIfTI file - data = read_nifti_file(args.input_file) + data, _ = read_nifti_file(args.input_file) # Construct the full paths for the JSON, b-vector, and b-value files json_file = os.path.join(args.bids_dir, "dataset_description.json") @@ -61,20 +63,14 @@ def save_nifti_3d(data, output_file, **kwargs): bvals = read_json_file(bval_file) # Pass additional arguments to the algorithm - rng = np.random.RandomState(42) fit = OsipiBase(algorithm=args.algorithm) - S0 = 1 - gd = GenerateData(rng=rng) - D = data["D"] - f = data["f"] - Dp = data["Dp"] # signal = gd.ivim_signal(D, Dp, f, S0, bvals, SNR, rician_noise) # Passing the values to the selectect algorithm and saving it [f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals) - save_nifti_3d(f_fit, "f.nii.gz") - save_nifti_3d(Dp_fit, "dp.nii.gz") - save_nifti_3d(D_fit, "d.nii.gz") + save_nifti_file(f_fit, "f.nii.gz", args.affine) + save_nifti_file(Dp_fit, "dp.nii.gz", args.affline) + save_nifti_file(D_fit, "d.nii.gz", args.affline) except Exception as e: print(f"Error: {e}") From 5ad4cbca865b26ed740d1ff5fc79b370327aa5e4 Mon Sep 17 00:00:00 2001 From: Unique-Usman Date: Wed, 27 Mar 2024 04:15:30 +0530 Subject: [PATCH 4/8] Update the pull request --- WrapImage/nifti_wrapper.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index 65ad3f6..0f2d1fe 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -38,6 +38,23 @@ def save_nifti_file(data, output_file, affine=None, **kwargs): output_img = nib.nifti1.Nifti1Image(data, affine , **kwargs) nib.save(output_img, output_file) +def loop_over_first_n_minus_1_dimensions(arr): + """ + Loops over the first n-1 dimensions of a numpy array. + + Args: + arr: A numpy array. + + Yields: + A tuple containing the indices for the current iteration and a flattened view of the remaining dimensions. + """ + n = arr.ndim + for idx in np.ndindex(*arr.shape[:n-1]): + flat_view = arr[idx].flatten() + yield idx, flat_view + + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Read a 4D NIfTI phantom file along with BIDS JSON, b-vector, and b-value files.") parser.add_argument("input_file", type=str, help="Path to the input 4D NIfTI file.") @@ -64,13 +81,17 @@ def save_nifti_file(data, output_file, affine=None, **kwargs): # Pass additional arguments to the algorithm fit = OsipiBase(algorithm=args.algorithm) - # signal = gd.ivim_signal(D, Dp, f, S0, bvals, SNR, rician_noise) - - # Passing the values to the selectect algorithm and saving it - [f_fit, Dp_fit, D_fit] = fit.osipi_fit(signal, bvals) - save_nifti_file(f_fit, "f.nii.gz", args.affine) - save_nifti_file(Dp_fit, "dp.nii.gz", args.affline) - save_nifti_file(D_fit, "d.nii.gz", args.affline) + f_image = np.zeros_like(data.shape[:data.ndim-1]) + D_image = np.zeros_like(data.shape[:data.ndim-1]) + Dp_image = np.zeros_like(data.shape[:data.ndim-1]) + for idx, view in loop_over_first_n_minus_1_dimensions(data): + [f_fit, Dp_fit, D_fit] = fit.osipi_fit(view, bvals) + f_image[idx]=f_fit + Dp_image[idx]=Dp_fit + D_image[idx]=D_fit + save_nifti_file(f_image, "f.nii.gz", args.affine) + save_nifti_file(Dp_image, "dp.nii.gz", args.affline) + save_nifti_file(D_image, "d.nii.gz", args.affline) except Exception as e: print(f"Error: {e}") From ade52fd8e76c0b0ab016062d641f5fdffb3054af Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Wed, 10 Apr 2024 15:10:09 -0400 Subject: [PATCH 5/8] Added read_bval_file and read_bvec_file to read the bvec and the bval data --- WrapImage/nifti_wrapper.py | 50 +++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index 0f2d1fe..84cbd9a 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -29,6 +29,39 @@ def read_json_file(json_file): return json_data +def read_bval_file(bval_file): + """ + For reading the bval file + """ + if not os.path.exists(bval_file): + raise FileNotFoundError(f"File '{bval_file}' not found.") + + with open(bval_file, "r") as f: + try: + bval_data = [int(val) for val in f.read().split()] + except ValueError as e: + raise ValueError(f"Error reading bval file '{bval_file}': {e}") + + return bval_data + +def read_bvec_file(bvec_file): + """ + For reading the bvec file + """ + if not os.path.exists(bvec_file): + raise FileNotFoundError(f"File '{bvec_file}' not found.") + + with open(bvec_file, "r") as f: + try: + lines = f.readlines() + bvec_data = [] + for line in lines: + bvec_data.append([float(val) for val in line.split()]) + except ValueError as e: + raise ValueError(f"Error reading bvec file '{bvec_file}': {e}") + + return bvec_data + def save_nifti_file(data, output_file, affine=None, **kwargs): """ For saving the 3d nifti images of the output of the algorithm @@ -58,9 +91,10 @@ def loop_over_first_n_minus_1_dimensions(arr): if __name__ == "__main__": parser = argparse.ArgumentParser(description="Read a 4D NIfTI phantom file along with BIDS JSON, b-vector, and b-value files.") parser.add_argument("input_file", type=str, help="Path to the input 4D NIfTI file.") - parser.add_argument("bids_dir", type=str, help="Path to the BIDS directory.") + parser.add_argument("bvec_file", type=str, help="Path to the b-vector file.") + parser.add_argument("bval_file", type=str, help="Path to the b-value file.") parser.add_argument("--affine", type=float, nargs="+", help="Affine matrix for NIfTI image.") - parser.add_argument("--algorithm", type=str, choices=["algorithm1", "algorithm2"], default="algorithm1", help="Select the algorithm to use.") + parser.add_argument("--algorithm", type=str, choices=["algorithm1", "algorithm2"], default="OJ_GU_seg", help="Select the algorithm to use.") parser.add_argument("algorithm_args", nargs=argparse.REMAINDER, help="Additional arguments for the algorithm.") args = parser.parse_args() @@ -69,15 +103,9 @@ def loop_over_first_n_minus_1_dimensions(arr): # Read the 4D NIfTI file data, _ = read_nifti_file(args.input_file) - # Construct the full paths for the JSON, b-vector, and b-value files - json_file = os.path.join(args.bids_dir, "dataset_description.json") - bvec_file = os.path.join(args.bids_dir, "bvecs.json") - bval_file = os.path.join(args.bids_dir, "bvals.json") - - # Read the JSON, b-vector, and b-value files - json_data = read_json_file(json_file) - bvecs = read_json_file(bvec_file) - bvals = read_json_file(bval_file) + # Read the b-vector, and b-value files + bvecs = read_bvec_file(args.bvec_file) + bvals = read_bval_file(args.bval_file) # Pass additional arguments to the algorithm fit = OsipiBase(algorithm=args.algorithm) From 5b6f6ae4a78c91f178e54d209c8f9768050bdd99 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Tue, 23 Apr 2024 13:41:23 -0400 Subject: [PATCH 6/8] Fixed the error in the script wrapper --- WrapImage/nifti_wrapper.py | 48 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index 84cbd9a..c4cc590 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -36,12 +36,7 @@ def read_bval_file(bval_file): if not os.path.exists(bval_file): raise FileNotFoundError(f"File '{bval_file}' not found.") - with open(bval_file, "r") as f: - try: - bval_data = [int(val) for val in f.read().split()] - except ValueError as e: - raise ValueError(f"Error reading bval file '{bval_file}': {e}") - + bval_data = np.genfromtxt(bval_file, dtype=float) return bval_data def read_bvec_file(bvec_file): @@ -51,15 +46,8 @@ def read_bvec_file(bvec_file): if not os.path.exists(bvec_file): raise FileNotFoundError(f"File '{bvec_file}' not found.") - with open(bvec_file, "r") as f: - try: - lines = f.readlines() - bvec_data = [] - for line in lines: - bvec_data.append([float(val) for val in line.split()]) - except ValueError as e: - raise ValueError(f"Error reading bvec file '{bvec_file}': {e}") - + bvec_data = np.genfromtxt(bvec_file) + bvec_data = np.transpose(bvec_data) # Transpose the array return bvec_data def save_nifti_file(data, output_file, affine=None, **kwargs): @@ -108,18 +96,32 @@ def loop_over_first_n_minus_1_dimensions(arr): bvals = read_bval_file(args.bval_file) # Pass additional arguments to the algorithm + fit = OsipiBase(algorithm=args.algorithm) - f_image = np.zeros_like(data.shape[:data.ndim-1]) - D_image = np.zeros_like(data.shape[:data.ndim-1]) - Dp_image = np.zeros_like(data.shape[:data.ndim-1]) + f_image = [] + Dp_image = [] + D_image = [] + for idx, view in loop_over_first_n_minus_1_dimensions(data): [f_fit, Dp_fit, D_fit] = fit.osipi_fit(view, bvals) - f_image[idx]=f_fit - Dp_image[idx]=Dp_fit - D_image[idx]=D_fit + f_image.append(f_fit) + Dp_image.append(Dp_fit) + D_image.append(D_fit) + + # Convert lists to NumPy arrays + f_image = np.array(f_image) + Dp_image = np.array(Dp_image) + D_image = np.array(D_image) + + # Reshape arrays if needed + f_image = f_image.reshape(data.shape[:data.ndim-1]) + Dp_image = Dp_image.reshape(data.shape[:data.ndim-1]) + D_image = D_image.reshape(data.shape[:data.ndim-1]) + save_nifti_file(f_image, "f.nii.gz", args.affine) - save_nifti_file(Dp_image, "dp.nii.gz", args.affline) - save_nifti_file(D_image, "d.nii.gz", args.affline) + save_nifti_file(Dp_image, "dp.nii.gz", args.affine) + save_nifti_file(D_image, "d.nii.gz", args.affine) except Exception as e: print(f"Error: {e}") + From f75534d867e43be292fad24cb1856d967153e47e Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Wed, 24 Apr 2024 08:05:05 -0400 Subject: [PATCH 7/8] Use tqdm python3 library to show the progress of the wrapper when running --- WrapImage/nifti_wrapper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index c4cc590..90e311b 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -3,8 +3,9 @@ import os import nibabel as nib from src.wrappers.OsipiBase import OsipiBase -from utilities.data_simulation.GenerateData import GenerateData import numpy as np +from tqdm import tqdm + def read_nifti_file(input_file): """ @@ -102,7 +103,7 @@ def loop_over_first_n_minus_1_dimensions(arr): Dp_image = [] D_image = [] - for idx, view in loop_over_first_n_minus_1_dimensions(data): + for idx, view in tqdm(loop_over_first_n_minus_1_dimensions(data), desc=f"{args.algorithm} is fitting", dynamic_ncols=True, total=702464): [f_fit, Dp_fit, D_fit] = fit.osipi_fit(view, bvals) f_image.append(f_fit) Dp_image.append(Dp_fit) From 14d1793e815dc0e336db04aeacaa0f4faca81bed Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Mon, 29 Apr 2024 16:29:16 -0400 Subject: [PATCH 8/8] Changed the total argument from hard coded value to dynamic value which is gotten from the image itself --- WrapImage/nifti_wrapper.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WrapImage/nifti_wrapper.py b/WrapImage/nifti_wrapper.py index 90e311b..a0d65af 100644 --- a/WrapImage/nifti_wrapper.py +++ b/WrapImage/nifti_wrapper.py @@ -83,7 +83,7 @@ def loop_over_first_n_minus_1_dimensions(arr): parser.add_argument("bvec_file", type=str, help="Path to the b-vector file.") parser.add_argument("bval_file", type=str, help="Path to the b-value file.") parser.add_argument("--affine", type=float, nargs="+", help="Affine matrix for NIfTI image.") - parser.add_argument("--algorithm", type=str, choices=["algorithm1", "algorithm2"], default="OJ_GU_seg", help="Select the algorithm to use.") + parser.add_argument("--algorithm", type=str, default="OJ_GU_seg", help="Select the algorithm to use.") parser.add_argument("algorithm_args", nargs=argparse.REMAINDER, help="Additional arguments for the algorithm.") args = parser.parse_args() @@ -103,7 +103,10 @@ def loop_over_first_n_minus_1_dimensions(arr): Dp_image = [] D_image = [] - for idx, view in tqdm(loop_over_first_n_minus_1_dimensions(data), desc=f"{args.algorithm} is fitting", dynamic_ncols=True, total=702464): + # This is necessary for the tqdm to display progress bar. + n = data.ndim + total_iteration = np.prod(data.shape[:n-1]) + for idx, view in tqdm(loop_over_first_n_minus_1_dimensions(data), desc=f"{args.algorithm} is fitting", dynamic_ncols=True, total=total_iteration): [f_fit, Dp_fit, D_fit] = fit.osipi_fit(view, bvals) f_image.append(f_fit) Dp_image.append(Dp_fit)