Skip to content

Commit

Permalink
Merge branch 'dev' (Adding support for CCFv3 2020)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-rijsketic committed Jul 11, 2024
2 parents 4836efe + b73cc9f commit c996579
Show file tree
Hide file tree
Showing 10 changed files with 723 additions and 43 deletions.
4 changes: 3 additions & 1 deletion unravel/cluster_stats/brain_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from rich import print
from rich.traceback import install

from unravel.core.argparse_utils import SuppressMetavar, SM
from unravel.core.argparse_utils import SuppressMetavar, SM
from unravel.core.config import Configuration
from unravel.core.utils import log_command, verbose_start_msg, verbose_end_msg
from unravel.voxel_stats.mirror import mirror
Expand All @@ -38,6 +38,8 @@ def parse_args():
parser.epilog = __doc__
return parser.parse_args()

# TODO: Consider consolidating regional_summary.csv (regional_summary_CCFv3-2017.csv) and regional_summary_CCFv3-2020.csv and ideally add logic to match usage automatic (i.e., no extra arg needed)


@log_command
def main():
Expand Down
4 changes: 2 additions & 2 deletions unravel/cluster_stats/org_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ def copy_stats_files(validation_dir, dest_path, vstats_path, p_val_txt):
cluster_correction_dir = validation_dir_name

# Regular expression to match the part before and after 'q*' to remove any suffix added to the rev_cluster_index<suffix>.nii.gz
pattern = r'(.*q\d+\.\d+)(_.*)'
pattern = r'(.*q\d+\.\d+)(_.+)?' # This also works when there is no "suffix"
match = re.match(pattern, cluster_correction_dir)
if match:
cluster_correction_dir = match.group(1)
suffix = match.group(2)[1:] # Remove the leading underscore
suffix = match.group(2)[1:] if match.group(2) else '' # This gets the string after the q value if there is one
else:
print("\n [red1]No match found in cluster_org_data\n")

Expand Down
10 changes: 6 additions & 4 deletions unravel/cluster_stats/recursively_mirror_rev_cluster_indices.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
cluster_mirror_indices -m RH -v
Use this command after ``cluster_fdr`` to mirror the cluster indices for the other side of the brain before running ``cluster_validation``.
Note:
- Use -ax 2 and -s 0 for the CCFv3 2020 atlas.
- Use -ax 0 and -s 2 for the 25 um Gubra atlas
"""

import argparse
Expand All @@ -27,14 +31,12 @@ def parse_args():
parser = argparse.ArgumentParser(formatter_class=SuppressMetavar)
parser.add_argument('-m', '--mas_side', help='Side of the brain corresponding to the mask used for ``vstats`` and ``cluster_fdr`` (RH or LH)', choices=['RH', 'LH'], required=True, action=SM)
parser.add_argument('-p', '--pattern', help='Glob pattern to match files. Default: **/*rev_cluster_index.nii.gz', default='**/*rev_cluster_index.nii.gz', action=SM)
parser.add_argument('-ax', '--axis', help='Axis to flip the image along. Default: 0', default=0, type=int, action=SM)
parser.add_argument('-s', '--shift', help='Number of voxels to shift content after flipping. Default: 2', default=2, type=int, action=SM)
parser.add_argument('-ax', '--axis', help='Axis to flip the image along. Default: 2', default=2, type=int, action=SM)
parser.add_argument('-s', '--shift', help='Number of voxels to shift content after flipping. Default: 0', default=0, type=int, action=SM)
parser.add_argument('-v', '--verbose', help='Increase verbosity. Default: False', action='store_true', default=False)
parser.epilog = __doc__
return parser.parse_args()

# TODO: adapt to work with CCFv3 images if needed


def process_file(file_path, args):
if not file_path.is_file():
Expand Down
2 changes: 1 addition & 1 deletion unravel/core/csvs/regional_summary.csv
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@
1089,/997/8/567/688/695/1089/,Hippocampal formation,HPF,Hippocampal,125,205,75
1090,/997/8/567/688/695/315/453/378/1090/,"Supplemental somatosensory area, layer 5",SSs5,Isocortex__Somatosensory__Supplemental,28,128,99
1091,/997/8/512/528/645/928/1091/,Lobules IV-V,"CUL4, 5",Cerebellar_cortex__Vermal_regions,252,251,144
1092,/997/1009/983/896/1092/,external medullary lamina of the thalamus,em,Fiber_tracts__medial_forebrain_bundle_system,199,202,202
1092,/997/1009/983/896/1092/,external medullary lamina of the thalamus,em,Fiber_tracts__lateral_forebrain_bundle_system,199,202,202
1093,/997/8/343/1065/771/987/1093/,"Pontine reticular nucleus, caudal part",PRNc,Pons__Motor_related,253,186,134
1094,/997/8/567/688/695/315/453/322/337/1094/,"Primary somatosensory area, lower limb, layer 4",SSp-ll4,Isocortex__Somatosensory__Lower_limb,28,127,100
1096,/997/8/343/1129/549/856/239/127/1096/,"Anteromedial nucleus, dorsal part",AMd,Thalamus__Polymodal_association_cortex_related,253,141,159
Expand Down
684 changes: 684 additions & 0 deletions unravel/core/csvs/regional_summary_CCFv3-2020.csv

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions unravel/core/img_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ def load_tifs(tif_path, desired_axis_order="xyz", return_res=False, return_metad
def load_single_tif(tif_file):
"""Load a single .tif file using OpenCV and return the ndarray."""
img = cv2.imread(str(tif_file), cv2.IMREAD_UNCHANGED)
if img is None:
raise ValueError(f"Failed to load image: {tif_file}")
return img
tif_files = sorted(Path(tif_path).parent.glob("*.tif"))
if parallel_loading:
Expand Down
21 changes: 4 additions & 17 deletions unravel/register/reg_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Usage:
------
reg_prep -i <asterisk>.czi [-e <list of paths to exp dirs>] [-v]
reg_prep -i <asterisk>.czi -x <x/y voxel size in microns> -z <z voxel size> [-e <list of paths to exp dirs>] [-v]
Run command from the experiment directory w/ sample?? folder(s), a sample?? folder, or provide -e or -d arguments.
Expand All @@ -32,7 +32,6 @@
from unravel.core.img_io import load_3D_img, resolve_path, save_as_tifs, save_as_nii
from unravel.core.img_tools import resample, reorient_for_raw_to_nii_conv
from unravel.core.utils import log_command, verbose_start_msg, verbose_end_msg, initialize_progress_bar, get_samples, print_func_name_args_times
from unravel.segment.copy_tifs import copy_specific_slices


def parse_args():
Expand All @@ -41,20 +40,18 @@ def parse_args():
parser.add_argument('-p', '--pattern', help='Pattern for sample?? dirs. Use cwd if no matches.', default='sample??', action=SM)
parser.add_argument('-d', '--dirs', help='List of sample?? dir names or paths to dirs to process', nargs='*', default=None, action=SM)
parser.add_argument('-i', '--input', help='Full res image input path relative (rel_path) to ./sample??', required=True, action=SM)
parser.add_argument('-x', '--xy_res', help='x/y voxel size in microns of the input image.', required=True, type=float, action=SM) # Set up Default: get via ``io_metadata``
parser.add_argument('-z', '--z_res', help='z voxel size in microns of the input image.', required=True, type=float, action=SM) # Set up Default: get via ``io_metadata``
parser.add_argument('-c', '--channel', help='.czi channel number. Default: 0 for autofluo', default=0, type=int, action=SM)
parser.add_argument('-o', '--output', help='Output path. Default: reg_inputs/autofl_50um.nii.gz', default="reg_inputs/autofl_50um.nii.gz", action=SM)
parser.add_argument('-x', '--xy_res', help='x/y voxel size in microns of the input image. Default: get via metadata', default=None, type=float, action=SM)
parser.add_argument('-z', '--z_res', help='z voxel size in microns of the input image. Default: get via metadata', default=None, type=float, action=SM)
parser.add_argument('-r', '--reg_res', help='Resample input to this res in um for reg. Default: 50', default=50, type=int, action=SM)
parser.add_argument('-zo', '--zoom_order', help='Order for resampling (scipy.ndimage.zoom). Default: 1', default=1, type=int, action=SM)
parser.add_argument('-td', '--target_dir', help='path/target_dir name to copy specific slices for seg_brain_mask (see usage)', default=None, action=SM)
parser.add_argument('-s', '--slices', help='List of slice numbers to copy, e.g., 0000 0400 0800', nargs='*', type=str, default=[])
parser.add_argument('-mi', '--miracl', help="Include reorientation step to mimic MIRACL's tif to .nii.gz conversion", action='store_true', default=False)
parser.add_argument('-v', '--verbose', help='Increase verbosity. Default: False', action='store_true', default=False)
parser.epilog = __doc__
return parser.parse_args()

# TODO: Remove args.target_dir since this can be done with ``seg_copy_tifs``
# TODO: The script works if -x and -z are provided. Fix it so that it works if they are not provided (i.e., get res via ``io_metadata`` output: sample??/parameters/metadata.txt)


@print_func_name_args_times()
Expand Down Expand Up @@ -89,11 +86,6 @@ def main():
Configuration.verbose = args.verbose
verbose_start_msg()

if args.target_dir is not None:
# Create the target directory for copying the selected slices for ``seg_brain_mask``
target_dir = Path(args.target_dir)
target_dir.mkdir(exist_ok=True, parents=True)

samples = get_samples(args.dirs, args.pattern, args.exp_paths)

progress, task_id = initialize_progress_bar(len(samples), "[red]Processing samples...")
Expand Down Expand Up @@ -126,11 +118,6 @@ def main():
# Save the prepped autofl image (for ``reg`` if skipping ``seg_brain_mask`` and for applying the brain mask)
save_as_nii(img_resampled, output, args.reg_res, args.reg_res, np.uint16)

if args.target_dir is not None:
# Copy specific slices to the target directory
tif_dir = str(output).replace('.nii.gz', '_tifs')
copy_specific_slices(sample_path, tif_dir, target_dir, args.slices)

progress.update(task_id, advance=1)

verbose_end_msg()
Expand Down
3 changes: 0 additions & 3 deletions unravel/segment/copy_tifs.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ def copy_specific_slices(sample_path, source_dir, target_dir, slice_numbers, ver
shutil.copy(file_path, dest_file)
if verbose:
print(f"Copied {file_path} to {dest_file}")
else:
if verbose:
print(f"File {file_path.name} does not match specified slices and was not copied.")


@log_command
Expand Down
30 changes: 18 additions & 12 deletions unravel/voxel_stats/mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
------
vstats_mirror -v
Note:
The current defaults are specific to our version of the Gubra atlas and may need to be adjusted for other atlases.
Note:
- Use -ax 2 and -s 0 for the CCFv3 2020 atlas.
- Use -ax 0 and -s 2 for the 25 um Gubra atlas
"""

import argparse
Expand All @@ -26,17 +27,15 @@
def parse_args():
parser = argparse.ArgumentParser(formatter_class=SuppressMetavar)
parser.add_argument('-p', '--pattern', help='Pattern to match files. Default: *.nii.gz', default='*.nii.gz', action=SM)
parser.add_argument('-ax', '--axis', help='Axis to flip the image along. Default: 0', default=0, type=int, action=SM)
parser.add_argument('-s', '--shift', help='Number of voxels to shift content after flipping. Default: 2', default=2, type=int, action=SM)
parser.add_argument('-ax', '--axis', help='Axis to flip the image along. Default: 2', default=2, type=int, action=SM)
parser.add_argument('-s', '--shift', help='Number of voxels to shift content after flipping. Default: 0', default=0, type=int, action=SM)
parser.add_argument('-v', '--verbose', help='Increase verbosity', default=False, action='store_true')
parser.epilog = __doc__
return parser.parse_args()

# TODO: adapt to work with CCFv3 images if needed


@print_func_name_args_times()
def mirror(img, axis=0, shift=2):
def mirror(img, axis=2, shift=0):
"""Mirror an image along the specified axis and shift the content by the specified number of voxels.
Args:
Expand All @@ -46,12 +45,19 @@ def mirror(img, axis=0, shift=2):
# Flip the image data along the specified axis
flipped_img = np.flip(img, axis=axis)

# Shift the image data by padding with zeros on the left and cropping on the right
# This adds 2 voxels of zeros on the left side (beginning) and removes 2 voxels from the right side (end)
mirrored_img = np.pad(flipped_img, ((shift, 0), (0, 0), (0, 0)), mode='constant', constant_values=0)[:-shift, :, :]

return mirrored_img
if shift == 0:
return flipped_img
else:
# Shift the image data by padding with zeros on the left and cropping on the right
# This adds 2 voxels of zeros on the left side (beginning) and removes 2 voxels from the right side (end)
if axis == 0:
mirrored_img = np.pad(flipped_img, ((shift, 0), (0, 0), (0, 0)), mode='constant', constant_values=0)[:-shift, :, :]
return mirrored_img
else:
print('[red1]Logic for shifting content in axeses other than 0 has not been added. Please request this if needed.')
return


@log_command
def main():
install()
Expand Down
6 changes: 3 additions & 3 deletions unravel/voxel_stats/whole_to_LR_avg.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
def parse_args():
parser = argparse.ArgumentParser(formatter_class=SuppressMetavar)
parser.add_argument('-p', '--pattern', help='Pattern to match atlas space input images in the working dir. Default: *.nii.gz', default='*.nii.gz', action=SM)
parser.add_argument('-k', '--kernel', help='Smoothing kernel radius in mm if > 0. Default: 0 ', default=0, type=float, action=SM)
parser.add_argument('-ax', '--axis', help='Axis to flip the image along. Default: 0', default=0, type=int, action=SM)
parser.add_argument('-s', '--shift', help='Number of voxels to shift content after flipping. Default: 2', default=2, type=int, action=SM)
parser.add_argument('-k', '--kernel', help='Smoothing kernel radius in mm if > 0. Default: 0', default=0, type=float, action=SM)
parser.add_argument('-ax', '--axis', help='Axis to flip the image along. Default: 2', default=2, type=int, action=SM)
parser.add_argument('-s', '--shift', help='Number of voxels to shift content after flipping (if atlas is asym.). Default: 0', default=0, type=int, action=SM)
parser.add_argument('-tp', '--parallel', help='Enable parallel processing with thread pools', default=False, action='store_true')
parser.add_argument('-amas', '--atlas_mask', help='path/atlas_mask.nii.gz', default=None, action=SM)
parser.add_argument('-v', '--verbose', help='Increase verbosity', default=False, action='store_true')
Expand Down

0 comments on commit c996579

Please sign in to comment.