Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
68e3cb5
ENH: Refactor BrainAtlas class to remove hardcoded atlas data and imp…
acsenrafilho Aug 15, 2025
29aae31
ENH: Update set_image method to accept Union types and adjust test as…
acsenrafilho Aug 16, 2025
8cef1dc
ENH: Refactor isotropic Gaussian and median functions to use clone_im…
acsenrafilho Aug 16, 2025
1bb2af0
STY: Improve code readability by formatting long lines in MultiTE_ASL…
acsenrafilho Aug 16, 2025
56f055e
STY: Format long lines in CBFMapping and test cases for improved read…
acsenrafilho Aug 16, 2025
6a56818
ENH: Refactor ImageIO methods to improve image handling and add deep …
acsenrafilho Aug 16, 2025
1f3a6b6
ENH: Update calculate_snr, calculate_mean_intensity, and analyze_imag…
acsenrafilho Aug 16, 2025
90cbccd
ENH: Update image manipulation functions to use ImageIO objects; adju…
acsenrafilho Aug 16, 2025
8d1fa1f
ENH: Update test cases in test_smooth_utils to use ImageIO objects fo…
acsenrafilho Aug 16, 2025
03b0150
ENH: Refactor T2Scalar_ASLMapping to utilize ImageIO for brain mask h…
acsenrafilho Aug 16, 2025
c646f9c
ENH: Refactor MultiDW_ASLMapping methods to improve readability and m…
acsenrafilho Aug 16, 2025
87d58a4
ENH: Update functions to utilize ImageIO objects for image handling; …
acsenrafilho Aug 16, 2025
efec6c3
WIP: Refactor head_movement_correction to utilize ImageIO for referen…
acsenrafilho Aug 16, 2025
ffa39fb
Merge remote-tracking branch 'origin/main' into enh/image_io
acsenrafilho Aug 16, 2025
26ce7f1
ENH: Refactor tests to utilize ImageIO for image loading in registrat…
acsenrafilho Aug 18, 2025
6d8d6c1
WIP: Fixing registration (head_moviment)
acsenrafilho Aug 18, 2025
4d533ea
WIP: Adjusting more tests with ImageIO refactoring
acsenrafilho Aug 19, 2025
ceed790
WIP: ImageIO tests
acsenrafilho Aug 20, 2025
67df879
DOC: Enhance documentation for ImageIO class and its methods
acsenrafilho Aug 20, 2025
aac55c3
ENH: Refactor and enhance tests for ASL template registration, includ…
acsenrafilho Aug 20, 2025
f684c60
ENH: Improve test assertions for ImageIO and registration functions, …
acsenrafilho Aug 20, 2025
fecf1f5
STY: Improve code formatting and style in ImageIO class, enhancing re…
acsenrafilho Aug 20, 2025
ce3a458
ENH: Refactor and improve formatting of the collect_data_volumes and …
acsenrafilho Aug 20, 2025
c6d2d22
STY: Improve code formatting in isotropic_gaussian function for bette…
acsenrafilho Aug 20, 2025
ac4efbe
ENH: Refactor asl_template_registration function to improve atlas ref…
acsenrafilho Aug 20, 2025
ff8996f
ENH: Clean up space_normalization function by removing unused paramet…
acsenrafilho Aug 20, 2025
2f460d1
ENH: Optimize T2 map stacking and improve ImageIO object creation for…
acsenrafilho Aug 20, 2025
1ded438
ENH: Update brain mask handling in CBFMapping, MultiDW_ASLMapping, an…
acsenrafilho Aug 20, 2025
9b47011
ENH: Update ASLData methods to utilize get_as_numpy for improved data…
acsenrafilho Aug 20, 2025
d6e46df
ENH: BACKUP IF NEEDED Remove unused orientation check functions and r…
acsenrafilho Aug 20, 2025
a5dfc89
ENH: Add average_m0 option and refactor image loading to use ImageIO …
acsenrafilho Aug 20, 2025
ac85667
ENH: Add --average_m0 option and refactor image loading to use ImageI…
acsenrafilho Aug 20, 2025
8a5d11e
ENH: Add --average_m0 option and refactor image handling to improve c…
acsenrafilho Aug 20, 2025
caed32c
ENH: Add suppress_warnings option to create_map method for better con…
acsenrafilho Aug 20, 2025
5d26282
DOC: Improve commit message guidelines for clarity and organization
acsenrafilho Aug 20, 2025
4dc26c0
DOC: Update contribution guidelines links in pull request template fo…
acsenrafilho Aug 20, 2025
0c22bc9
STY: Remove logging statements from space_normalization function for …
acsenrafilho Aug 21, 2025
3b80959
ENH: Refactor ASLData initialization and improve warning suppression …
acsenrafilho Aug 21, 2025
17f01d5
ENH: Add step to move source to short path for Windows compatibility …
acsenrafilho Aug 21, 2025
9cc9cd3
ENH: Add ITK installation and configure additional dependencies for W…
acsenrafilho Aug 21, 2025
f6182f5
ENH: Upgrade Python version to 3.10 in CI workflow for all platforms
acsenrafilho Aug 21, 2025
118d5ed
ENH: Update Python version requirement to 3.10 in pyproject.toml
acsenrafilho Aug 21, 2025
df69399
ENH: Update Python version requirements and classifiers in pyproject.…
acsenrafilho Aug 21, 2025
90c3263
BUG: Update ImageIO string representation test to check for path pres…
acsenrafilho Aug 21, 2025
0ff79b9
ENH: Upgrade Python version to 3.10 in CI workflows and bumpversion p…
acsenrafilho Aug 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
- Use supported image formats: `.nii`, `.nii.gz`, `.mha`, `.nrrd`.
- Ensure that the code is syntactically correct and adheres to the project's coding standards.
- Be sure about the documentation and comments. They should be clear and concise and use the correct Python docstring format.
- Create commit messages with a detailed description of the changes made, including any bug fixes or new features.
- Be as much specific as possible in the commit messages, including the files affected and the nature of the changes.
- Create commit messages with a as much details and description as possible in order to explain all the relevant information about the changes made, including any bug fixes or new features.
- Be as much specific and complete as possible in the commit messages, including the files affected and the nature of the changes.
- Organize the commit messages in bullet points for better readability, showing all the relevant information that is needed to explain the changes made.
- Uses for commit messages prefixes the following pattern:
- `ENH:` for new features and code enhancements
- `BUG:` for bug fixes and general corrections
Expand Down
6 changes: 3 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ Please select the relevant option(s):
- [ ] Code passes linting checks (`task lint-check`)
- [ ] Code includes appropriate inline documentation

**Note:** According to our [contribution guidelines](docs/contribute.md), please target the `develop` branch for most contributions to ensure proper testing before merging to `main`.
**Note:** According to our [contribution guidelines](https://asltk.readthedocs.io/en/main/contribute/), please target the `develop` branch for most contributions to ensure proper testing before merging to `main`.

**By submitting this pull request, I confirm:**
- [ ] I have read and followed the [contribution guidelines](docs/contribute.md)
- [ ] I have read and followed the [contribution guidelines](https://asltk.readthedocs.io/en/main/contribute/)
- [ ] I have tested my changes thoroughly
- [ ] I understand this is an open source project and my contributions may be used by others
- [ ] I agree to the project's [Code of Conduct](CODE_OF_CONDUCT.md)
- [ ] I agree to the project's [Code of Conduct](https://github.com/LOAMRI/asltk/blob/main/CODE_OF_CONDUCT.md)

<!-- Thank you for contributing to asltk! πŸš€ -->
2 changes: 1 addition & 1 deletion .github/workflows/bumpversion_publish_workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.10'

- name: Install Poetry
run: pip install poetry
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci_develop.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]


steps:
Expand Down Expand Up @@ -54,7 +54,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci_main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]


steps:
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down Expand Up @@ -105,7 +105,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down
68 changes: 46 additions & 22 deletions asltk/asldata.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import copy
import os
import warnings
from typing import Union

import numpy as np

from asltk.logging_config import get_logger, log_data_info, log_function_call
from asltk.logging_config import get_logger, log_data_info
from asltk.utils.image_manipulation import collect_data_volumes
from asltk.utils.io import load_image
from asltk.utils.io import ImageIO


class ASLData:
Expand Down Expand Up @@ -69,40 +70,53 @@ def __init__(
if isinstance(kwargs.get('pcasl'), str):
pcasl_path = kwargs.get('pcasl')
logger.info(f'Loading ASL image from: {pcasl_path}')
self._asl_image = load_image(pcasl_path)
self._asl_image = ImageIO(image_path=pcasl_path)
if self._asl_image is not None:
log_data_info(
'ASL image', self._asl_image.shape, pcasl_path
'ASL image',
self._asl_image.get_as_numpy().shape,
pcasl_path,
)
elif isinstance(kwargs.get('pcasl'), np.ndarray):
self._asl_image = kwargs.get('pcasl')
logger.info('ASL image loaded as numpy array')
self._asl_image = ImageIO(image_array=kwargs.get('pcasl'))
logger.info('ASL image loaded')
log_data_info(
'ASL image', self._asl_image.shape, 'numpy array'
'ASL image', self._asl_image.get_as_numpy().shape
)

if kwargs.get('m0') is not None:
average_m0 = kwargs.get('average_m0', False)

if isinstance(kwargs.get('m0'), str):
m0_path = kwargs.get('m0')
logger.info(f'Loading M0 image from: {m0_path}')
self._m0_image = load_image(m0_path)
self._m0_image = ImageIO(
image_path=m0_path, average_m0=average_m0
)

# Check if M0 image is 4D and warn if so
if (
self._m0_image is not None
and len(self._m0_image.shape) > 3
and len(self._m0_image.get_as_numpy().shape) > 3
):
warnings.warn('M0 image has more than 3 dimensions.')

if self._m0_image is not None:
log_data_info('M0 image', self._m0_image.shape, m0_path)
log_data_info(
'M0 image',
self._m0_image.get_as_numpy().shape,
m0_path,
)
elif isinstance(kwargs.get('m0'), np.ndarray):
self._m0_image = kwargs.get('m0')
self._m0_image = ImageIO(
image_array=kwargs.get('m0'), average_m0=average_m0
)
logger.info('M0 image loaded as numpy array')
log_data_info('M0 image', self._m0_image.shape, 'numpy array')

if kwargs.get('average_m0', False):
self._m0_image = np.mean(self._m0_image, axis=0)
log_data_info(
'M0 image',
self._m0_image.get_as_numpy().shape,
'numpy array',
)

self._parameters['ld'] = (
[] if kwargs.get('ld_values') is None else kwargs.get('ld_values')
Expand Down Expand Up @@ -133,8 +147,8 @@ def __init__(

logger.debug('ASLData object created successfully')

def set_image(self, image, spec: str):
"""Insert a image necessary to define de ASL data processing.
def set_image(self, image: Union[str, np.ndarray], spec: str, **kwargs):
"""Insert an image necessary to define the ASL data processing.

The `spec` parameters specifies what is the type of image to be used in
ASL processing step. Choose one of the options: `m0` for the M0 volume,
Expand All @@ -152,7 +166,7 @@ def set_image(self, image, spec: str):
>>> data = ASLData()
>>> path_m0 = './tests/files/m0.nii.gz' # M0 file with shape (5,35,35)
>>> data.set_image(path_m0, spec='m0')
>>> data('m0').shape
>>> data('m0').get_as_numpy().shape
(5, 35, 35)

Args:
Expand All @@ -161,10 +175,18 @@ def set_image(self, image, spec: str):
"""
if isinstance(image, str) and os.path.exists(image):
if spec == 'm0':
self._m0_image = load_image(image)
self._m0_image = ImageIO(image, **kwargs)
elif spec == 'pcasl':
self._asl_image = load_image(image)
self._asl_image = ImageIO(image, **kwargs)
elif isinstance(image, np.ndarray):
warnings.warn(
'Using numpy array as image input does not preserve metadata or image properties.'
)
if spec == 'm0':
self._m0_image = ImageIO(image_array=image, **kwargs)
elif spec == 'pcasl':
self._asl_image = ImageIO(image_array=image, **kwargs)
elif isinstance(image, ImageIO):
if spec == 'm0':
self._m0_image = image
elif spec == 'pcasl':
Expand Down Expand Up @@ -277,9 +299,11 @@ def __call__(self, spec: str):
Examples:
>>> data = ASLData(pcasl='./tests/files/t1-mri.nrrd')
>>> type(data('pcasl'))
<class 'asltk.utils.io.ImageIO'>
>>> type(data('pcasl').get_as_numpy())
<class 'numpy.ndarray'>

>>> np.min(data('pcasl'))
>>> np.min(data('pcasl').get_as_numpy())
0

Returns:
Expand Down Expand Up @@ -327,7 +351,7 @@ def _check_ld_pld_sizes(self, ld, pld):
)

def _check_m0_dimension(self):
if len(self._m0_image.shape) > 3:
if len(self._m0_image.get_as_numpy().shape) > 3:
warnings.warn(
'M0 image has more than 3 dimensions. '
'This may cause issues in processing. '
Expand Down
41 changes: 32 additions & 9 deletions asltk/aux_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,38 @@
import numpy as np

from asltk.smooth import isotropic_gaussian, isotropic_median
from asltk.utils.io import ImageIO


def _check_mask_values(mask, label, ref_shape):
# Check wheter mask input is an numpy array
if not isinstance(mask, np.ndarray):
raise TypeError(f'mask is not an numpy array. Type {type(mask)}')
def _check_mask_values(mask: ImageIO, label, ref_shape):
"""Validate mask array for brain mask processing.

This function performs comprehensive validation of brain mask data to ensure
it meets the requirements for ASL processing. It checks data type, binary
format compliance, label presence, and dimensional compatibility.

Args:
mask (np.ndarray): The brain mask image to validate.
label (int or float): The label value to search for in the mask.
ref_shape (tuple): The reference shape that the mask should match.

Raises:
TypeError: If mask is not a numpy array or dimensions don't match.
ValueError: If the specified label value is not found in the mask.

Warnings:
UserWarning: If mask contains more than 2 unique values (not strictly binary).
"""
# Check wheter mask input is an ImageIO object
if not isinstance(mask, ImageIO):
raise TypeError(
f'mask is not an ImageIO object. Type {type(mask)} is not allowed.'
)

mask_array = mask.get_as_numpy()

# Check whether the mask provided is a binary image
unique_values = np.unique(mask)
unique_values = np.unique(mask_array)
if unique_values.size > 2:
warnings.warn(
'Mask image is not a binary image. Any value > 0 will be assumed as brain label.',
Expand All @@ -29,18 +52,18 @@ def _check_mask_values(mask, label, ref_shape):
raise ValueError('Label value is not found in the mask provided.')

# Check whether the dimensions between mask and input volume matches
mask_shape = mask.shape
mask_shape = mask_array.shape
if mask_shape != ref_shape:
raise TypeError(
f'Image mask dimension does not match with input 3D volume. Mask shape {mask_shape} not equal to {ref_shape}'
)


def _apply_smoothing_to_maps(
maps: Dict[str, np.ndarray],
maps: Dict[str, ImageIO],
smoothing: Optional[str] = None,
smoothing_params: Optional[Dict[str, Any]] = None,
) -> Dict[str, np.ndarray]:
) -> Dict[str, ImageIO]:
"""Apply smoothing filter to all maps in the dictionary.

This function applies the specified smoothing filter to all map arrays
Expand Down Expand Up @@ -117,7 +140,7 @@ def _apply_smoothing_to_maps(
# Apply smoothing to all maps
smoothed_maps = {}
for key, map_array in maps.items():
if isinstance(map_array, np.ndarray):
if isinstance(map_array, ImageIO):
try:
smoothed_maps[key] = smooth_func(map_array, **smoothing_params)
except Exception as e:
Expand Down
Loading
Loading