Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 23 additions & 10 deletions TPTBox/core/bids_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,6 @@ def Buffered_BIDS_Global_info(
additional_key: Extra BIDS entity keys beyond the official spec that
should not trigger validation warnings.
verbose: Print progress and cache-status messages.
file_name_manipulation: Optional callable applied to each filename
before BIDS parsing, e.g. to normalise non-conformant names.
sequence_splitting_keys: Keys used to group files into sequences
(families). Defaults to the library-level constant when
``None``.
Expand Down Expand Up @@ -478,6 +476,8 @@ def iter_subjects(self, sort: bool = False, shuffle: bool = False) -> list[tuple
Args:
sort: If ``True``, return subjects sorted alphabetically by
subject ID.
shuffle: If ``True``, return subjects in a random order. Ignored when
``sort`` is also ``True``.

Returns:
A list of ``(subject_id, Subject_Container)`` pairs.
Expand Down Expand Up @@ -660,10 +660,15 @@ def __init__(self, file: Path | str, dataset: Path | str, verbose=True, bids_ds:

@property
def file(self) -> dict[str, Path]:
"""Returns a dict mapping file types to paths. ["nii.gz", "json", "png"] are automatic searched for.
"""Return the dict of file-type → path for this BIDS entry.

On first access, sibling files with the same BIDS key and one of the
extensions ``nii.gz``, ``json``, ``png`` are auto-discovered next to
the primary file.

Returns:
dict[str, Path]: _description_
Dict mapping file extension (e.g. ``"nii.gz"``, ``"json"``) to its
resolved :class:`~pathlib.Path`, sorted alphabetically by key.
"""
if not self._checked:
files = {p.parent for p in self._file.values()}
Expand Down Expand Up @@ -1031,12 +1036,12 @@ def get_changed_path( # noqa: C901
"""Changes part of the path to generate new flies. The new parent will be derivatives as a default.

Examples:
subreg_path = ct_bids.get_changed_path(file_type="nii.gz",parent = "derivatives",info={"seg": "subreg"}, format="cdt")
subreg_path = ct_bids.get_changed_path(file_type="nii.gz",parent = "derivatives",info={"seg": "subreg"}, bids_format="cdt")

Args:
file_type (str | None, optional): Override the file type, like nii.gz to json Defaults to "nii.gz".

format (str | None, optional): Changes the "format key" like ct, msk, T1w. Defaults to None.
bids_format (str | None, optional): Changes the "format key" like ct, msk, T1w. Defaults to None.

parent (str, optional): derivatives or rawdata or any parent folder. Defaults to "derivatives".

Expand All @@ -1053,9 +1058,11 @@ def get_changed_path( # noqa: C901
dataset_path (str | None, optional): Override the dataset_path. Defaults to None.

no_sorting_mode (bool): If true, will keep the order of the origin nii. Defaults to False
make_parent (bool, optional): If true, create missing parent directories for the returned path. Defaults to False.
non_strict_mode (bool, optional): If true, downgrade unknown BIDS entity errors to warnings and allow non-``sub``-prefixed keys. Defaults to False.

Returns:
_type_: _description_
Path: The newly constructed BIDS-conform file path (not yet written to disk).
"""
if info is None:
info = {}
Expand Down Expand Up @@ -1951,13 +1958,16 @@ def __str__(self) -> str:
return s

def loop_list(self, sort=False) -> typing.Iterator[BIDS_FILE]:
"""Returns an iterator. Flatten must be True.
"""Iterate the flattened candidate list as individual BIDS_FILE objects.

Requires :meth:`flatten` to have been called first.

Args:
sort (bool, optional): Sort alphabetically. Defaults to False.
sort (bool, optional): If True, iterate in alphabetical order.
Defaults to False.

Returns:
typing.Iterator[BIDS_FILE]: _description_
Iterator over the matching :class:`BIDS_FILE` objects.
"""
assert isinstance(self.candidates, list), "call flatten() before looping as a list"
if sort:
Expand All @@ -1975,6 +1985,9 @@ def loop_dict(
Args:
sort (bool, optional): Sort alphabetically. Defaults to False.
key_transform (typing.Callable[[BIDS_FILE], str | None]): provide alternative dict name for certain fils, if default should be used return None
key_addendum (list[str] | None, optional): Extra info-key names appended to each family's dict keys to
disambiguate otherwise-identical entries. Defaults to None.

Returns:
typing.Iterator[typing.Dict[str, BIDS_FILE | list[BIDS_FILE]]]
"""
Expand Down
14 changes: 13 additions & 1 deletion TPTBox/core/dicom/dicom_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def _generate_bids_path(
"""Generate a BIDS-compatible file path for NIfTI outputs based on extracted keys from DICOM headers.

Args:
nifti_dir (str | Path): Directory where the NIfTI file will be stored.
dataset_nifti_dir (str | Path): Root dataset directory where the NIfTI file will be stored.
keys (dict): Dictionary containing metadata keys extracted from DICOM headers.
mri_format (str): The format or sequence type of the MRI (e.g., T1, T2).
simp_json (dict): JSON dictionary with extracted DICOM information to avoid file naming conflicts.
Expand Down Expand Up @@ -822,7 +822,19 @@ def extract_dicom_folder(
verbose (bool, optional): Whether to print detailed log information. Defaults to True.
parts_mapping (dict, optional): A dictionary mapping DICOM part identifiers to specific descriptions (e.g., "f" -> "fat").
Used for categorizing DICOM series. Defaults to a predefined mapping. The parts tag is only generated if the ImageType causes an image split.
map_series_description_to_file_format (dict | Callable | None, optional): Overrides the default mapping from
SeriesDescription to output ``mri_format``. Defaults to the built-in mapping.
validate_slicecount (bool, optional): Enable ``dicom2nifti`` slice-count validation. Defaults to True.
validate_orientation (bool, optional): Enable ``dicom2nifti`` orientation validation. Defaults to True.
validate_orthogonal (bool, optional): Enable ``dicom2nifti`` orthogonality validation. Defaults to False.
validate_slice_increment (bool, optional): Enable ``dicom2nifti`` slice-increment validation. Defaults to True.
n_cpu (int, optional): Number of CPU cores to use for parallel processing. Defaults to 1 (sequential).
override_subject_name (Callable[[dict, Path], str] | None, optional): Callable receiving the parsed DICOM
header dict and file path; returns the subject id to use in the BIDS output. Defaults to None.
skip_localizer (bool, optional): If True, skip series identified as scanner localisers. Defaults to True.
parent (str, optional): Parent folder inside ``dataset_path_out`` under which subjects are written
(typically ``"rawdata"``). Defaults to ``"rawdata"``.
censor_list (list | None, optional): List of series keys to skip entirely. Defaults to an empty list.

Returns:
dict: A dictionary with keys representing DICOM series and values as paths to the generated NIfTI files.
Expand Down
7 changes: 5 additions & 2 deletions TPTBox/core/internal/elastic_deform.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@ def deformed_nii(
the `deform_factor`. The deformed objects are returned as a dictionary.

Args:
arr_dic (dict[str, NII]): A dictionary containing NII objects to be deformed.
nii_dic (dict[str, NII]): A dictionary containing NII objects to be deformed.
sigma (float, optional): The standard deviation of the deformation field. If not provided,
it will be generated based on the `deform_factor`.
points (int, optional): The number of control points for the deformation grid. If not provided,
it will be generated based on the `deform_factor`.
deform_factor (float, optional): A factor used to determine the deformation parameters if
`sigma` and `points` are not specified. Larger values result in stronger deformations.
deform_padding (int, optional): The padding added to the deformed objects to avoid edge artifacts.
verbose (bool, optional): If True, enable verbose logging. Default is True.
normalize (bool, optional): If True, per-entry normalise non-segmentation images to [0, 1] before
deforming and re-scale them back afterwards. Ignored when ``joint_normalize`` is True. Defaults to True.
joint_normalize (bool, optional): If True, use a single shared max across all non-segmentation
images for normalisation instead of per-entry min/max. Defaults to False.

Returns:
dict[str, NII]: A dictionary where keys correspond to the input dictionary keys, and values
Expand Down
2 changes: 2 additions & 0 deletions TPTBox/core/internal/nii_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def secure_save(func, *, file_types=tuple(_supported_img_files)) -> Callable:
Args:
func (callable): The function to be wrapped. It should take a file path (`str`, `Path`, or `bids_files.BIDS_FILE`)
as one of its arguments.
file_types (tuple[str, ...], keyword-only): File-extension keys tried in order when a ``bids_files.BIDS_FILE`` is
passed in place of a path. Defaults to ``tuple(_supported_img_files)``.

Returns:
callable: The wrapped function with added safety mechanisms.
Expand Down
10 changes: 6 additions & 4 deletions TPTBox/core/internal/slicer_nrrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from TPTBox.core.nii_wrapper import NII


def _read(filename, skip_voxels=False, verbos=True):
def _read(filename, skip_voxels=False, verbose=True):
r"""Read segmentation metadata from a .seg.nrrd file or NIFTI file and store it in a dict.

Example header:
Expand Down Expand Up @@ -258,7 +258,9 @@ def _read(filename, skip_voxels=False, verbos=True):
else:
segment_id = _generate_unique_segment_id(segment_ids)
segment_ids.add(segment_id)
log.on_fail(f"Segment ID was not found for index {segment_index}, use automatically generated ID: {segment_id}", verbose=verbos)
log.on_fail(
f"Segment ID was not found for index {segment_index}, use automatically generated ID: {segment_id}", verbose=verbose
)

segment_info = {}
segment_info["id"] = segment_id
Expand Down Expand Up @@ -637,7 +639,7 @@ def remove_not_supported_values(nrrd_dict: dict) -> None:
i.pop("extent", None)


def load_slicer_nrrd(filename: str | Path, seg: bool, skip_voxels: bool = False, verbos: bool = True) -> NII:
def load_slicer_nrrd(filename: str | Path, seg: bool, skip_voxels: bool = False, verbose: bool = True) -> NII:
"""Load a 3D Slicer ``.seg.nrrd`` segmentation file and return a NII wrapper.

Reads the NRRD header and optional voxel data, converts the LPS affine to
Expand Down Expand Up @@ -666,7 +668,7 @@ def load_slicer_nrrd(filename: str | Path, seg: bool, skip_voxels: bool = False,
from TPTBox import NII

# Read segmentation
nrrd_dict = _read(filename, skip_voxels=skip_voxels, verbos=verbos)
nrrd_dict = _read(filename, skip_voxels=skip_voxels, verbose=verbose)

# Voxel array
arr = nrrd_dict.pop("voxels")
Expand Down
12 changes: 10 additions & 2 deletions TPTBox/core/internal/train_nnUnet/prepere_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,18 @@ def _build_label_mapping(


def build_dataset(cfg: DatasetConfig) -> None:
"""Build a nnUnet dataset.
"""Build a nnUNet dataset on disk from the configured file list.

Sets the ``nnUNet_raw`` / ``nnUNet_preprocessed`` / ``nnUNet_results``
environment variables from ``cfg.nnunet_base`` before importing nnUNet
helpers, builds the label mapping (including mirror pairs), and then
delegates to ``set_up_dataset`` / ``add_file`` / ``finalize_ds`` from the
``_prep_ds`` module.

Args:
cfg (DatasetConfig): _description_
cfg (DatasetConfig): Fully populated dataset configuration (ID,
trainer, spacing, augmentation counts, file pairs, output paths,
...). See :class:`DatasetConfig`.
"""
# ── nnUNet env MUST be set before any nnunet import ───────────────────────────
# These are module-level so they take effect the moment this file is imported.
Expand Down
15 changes: 11 additions & 4 deletions TPTBox/core/nii_poi_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,20 @@ def assert_affine(

Args:
other (Has_Grid | None, optional): If set, will assert each entry of that object instead. Defaults to None.
ignore_missing_values (bool, optional): If True, comparisons are skipped when the corresponding
attribute on ``self`` is None. Defaults to False.
affine (AFFINE | None, optional): Affine matrix to compare against. If none, will not assert affine. Defaults to None.
zms (Zooms | None, optional): Zoom to compare against. If none, will not assert zoom. Defaults to None.
orientation (Ax_Codes | None, optional): Orientation to compare against. If none, will not assert orientation. Defaults to None.
zoom (ZOOMS | None, optional): Zoom to compare against. If none, will not assert zoom. Defaults to None.
orientation (AX_CODES | None, optional): Orientation to compare against. If none, will not assert orientation. Defaults to None.
rotation (ROTATION | None, optional): Rotation matrix to compare against. If none, will not assert rotation. Defaults to None.
origin (ORIGIN | None, optional): Origin to compare against. If none, will not assert origin. Defaults to None.
shape (SHAPE | None, optional): Shape to compare against. If none, will not assert shape. Defaults to None.
shape_tolerance (float, optional): error tolerance in shape as float, as POIs can have float shapes. Defaults to 0.0.
error_tolerance (float, optional): Accepted error tolerance in all assertions except shape. Defaults to 1e-4.
origin_tolerance (float, optional): Accepted error tolerance for the origin comparison. Defaults to 0.01.
error_tolerance (float, optional): Accepted error tolerance in all assertions except shape and origin. Defaults to 1e-4.
raise_error (bool, optional): If true, will raise AssertionError if anything is found. Defaults to True.
verbose (logging, optional): If true, will print out each assertion mismatch. Defaults to False.
text (str, optional): Extra label prepended to any raised/printed assertion message for context. Defaults to "".

Raises:
AssertionError: If any of the assertions failed and raise_error is True
Expand Down Expand Up @@ -464,7 +469,7 @@ def make_nii(self, arr: np.ndarray | None = None, seg=False) -> NII:
"""Make a nii with the same grid as object. Shape must fit the Grid.

Args:
arr np.ndarray: array. Defaults to None.
arr (np.ndarray | None, optional): Voxel data. Must match ``self.shape_int``. Defaults to None (zeros).
seg (bool, optional): Is it a segmentation. Defaults to False.

Returns:
Expand All @@ -482,6 +487,7 @@ def global_to_local(self, x: COORDINATE, itk=False) -> tuple:

Args:
x (COORDINATE): World-space coordinate as a 3-element sequence.
itk (bool, optional): If True, treat ``x`` as an ITK/LPS coordinate (negate the first two axes) before inversion. Defaults to False.

Returns:
tuple: Voxel-space coordinate rounded to 7 decimal places.
Expand Down Expand Up @@ -513,6 +519,7 @@ def local_to_global(self, x: COORDINATE | np.ndarray, itk=False) -> tuple:

Args:
x (COORDINATE): Voxel-space coordinate as a 3-element sequence.
itk (bool, optional): If True, return the coordinate in ITK/LPS convention (negate the first two axes) after the forward transform. Defaults to False.

Returns:
tuple: World-space coordinate rounded to 7 decimal places.
Expand Down
Loading
Loading