Skip to content
Open
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
21 changes: 19 additions & 2 deletions TPTBox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,20 @@
from TPTBox.core.poi_fun.poi_global import POI_Global
from TPTBox.core.vert_constants import ZOOMS, Location, Vertebra_Instance, v_idx2name, v_idx_order, v_name2idx

# Logger
from TPTBox.logger import Log_Type, Logger, Logger_Interface, Print_Logger, String_Logger
# Logger — everything goes through `configure_logging()` and the `logger` proxy.
from TPTBox.logger import (
Log_Type,
Logger,
Logger_Interface,
Print_Logger,
Reflection_Logger,
String_Logger,
configure_logging,
get_default_logger,
log,
logger,
loguru_logger,
)
from TPTBox.logger.log_file import No_Logger

Centroids = POI
Expand All @@ -57,8 +69,13 @@
"calc_centroids",
"calc_poi_from_subreg_vert",
"calc_poi_from_two_segs",
"configure_logging",
"core",
"get_default_logger",
"load_poi",
"log",
"logger",
"loguru_logger",
"np_utils",
"to_nii",
"v_idx2name",
Expand Down
95 changes: 43 additions & 52 deletions TPTBox/core/bids_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import numpy as np

import TPTBox
from TPTBox.logger import logger

if TYPE_CHECKING:
from TPTBox.core.nii_poi_abstract import Grid
Expand Down Expand Up @@ -66,28 +67,29 @@ def validate_entities(key: str, value: str, name: str, verbose: bool) -> bool:
return True
try:
key = key.lower()
log = logger
if key not in entities_keys:
print(
f"[!] {key} is not in list of legal keys. This name '{name}' is invalid. Legal keys are: {list(entities_keys.keys())}. \nFor use see https://bids-specification.readthedocs.io/en/stable/99-appendices/09-entities.html"
log.on_fail(
f"{key} is not in list of legal keys. This name '{name}' is invalid. Legal keys are: {list(entities_keys.keys())}. \nFor use see https://bids-specification.readthedocs.io/en/stable/99-appendices/09-entities.html"
)
entities_keys[key] = key
return False
if key in entity_alphanumeric and not value.isalnum():
print(f"[!] value for {key} must be alphanumeric. This name '{name}' is invalid, with value {value}")
log.on_fail(f"value for {key} must be alphanumeric. This name '{name}' is invalid, with value {value}")
return False
if key in entity_decimal and not value.isdecimal():
print(f"[!] value for {key} must be decimal. This name '{name}' is invalid, with value {value}")
log.on_fail(f"value for {key} must be decimal. This name '{name}' is invalid, with value {value}")
return False
# if int(value) == 0:
# print(f"[!] value for {key} must be not 0. This name '{name}' is invalid, with value {value}")
# log.on_fail(f"value for {key} must be not 0. This name '{name}' is invalid, with value {value}")
if key in entity_format and value not in formats_relaxed:
print(f"[!] value for {key} must be a format. This name '{name}' is invalid, with value {value}")
log.on_fail(f"value for {key} must be a format. This name '{name}' is invalid, with value {value}")
return False
if key in entity_on_off and value not in ["on", "off"]:
print(f"[!] value for {key} must be in {['on', 'off']}. This name '{name}' is invalid, with value {value}")
log.on_fail(f"value for {key} must be in {['on', 'off']}. This name '{name}' is invalid, with value {value}")
return False
if key in entity_left_right and value not in ["L", "R"]:
print(f"[!] value for {key} must be in {['L', 'R']}. This name '{name}' is invalid, with value {value}")
log.on_fail(f"value for {key} must be in {['L', 'R']}. This name '{name}' is invalid, with value {value}")
return False
# parts = [
# "mag",
Expand Down Expand Up @@ -117,7 +119,7 @@ def validate_entities(key: str, value: str, name: str, verbose: bool) -> bool:
else:
return True
except Exception as e:
print(e)
logger.on_fail(e)
return False


Expand Down Expand Up @@ -148,29 +150,30 @@ def get_values_from_name(path: Path | str, verbose: bool) -> tuple[str, dict[str

keys = bids_key.split("_")
bids_format = keys[-1]
log = logger
if bids_format not in formats_relaxed and verbose:
print(f"[!] Unknown format {bids_format} in file {name}", formats)
log.on_fail(f"Unknown format {bids_format} in file {name}", formats)
formats_relaxed.append(bids_format)
if file_type not in file_types and verbose:
print(f"[!] Unknown file_type {file_type} in file {name}")
log.on_fail(f"Unknown file_type {file_type} in file {name}")

dic = {}
for idx, s in enumerate(keys[:-1]):
try:
key, value = s.split("-", maxsplit=1)
if idx == 0 and key != "sub" and verbose:
print(f"[!] First key must be sub not {key}. This name '{name}' is invalid")
log.on_fail(f"First key must be sub not {key}. This name '{name}' is invalid")
if idx != 1 and key == "ses" and verbose:
print(f"[!] Session must be second key. This name '{name}' is invalid")
log.on_fail(f"Session must be second key. This name '{name}' is invalid")

if key in dic and verbose:
print(f"[!] {bids_key} contains copies of the same key twice. This name '{name}' is invalid")
log.on_fail(f"{bids_key} contains copies of the same key twice. This name '{name}' is invalid")

validate_entities(key, value, name, verbose)
dic[key] = value
except Exception:
if verbose:
print(f'[!] "{s}" is not a valid key/value pair. Expected "KEY-VALUE" in {name}')
log.on_fail(f'"{s}" is not a valid key/value pair. Expected "KEY-VALUE" in {name}')
return bids_format, dic, bids_key, file_type


Expand Down Expand Up @@ -231,9 +234,10 @@ def save_buffer(f: Path, buffer_name: str) -> list[Path]:
try:
with open(str(f / buffer_name), "wb") as b:
pickle.dump(new_buffer, b)
print("\n[ ] Save Buffer:", f) if verbose else None
if verbose:
logger.on_neutral("Save Buffer:", f)
except OSError:
print("Saving not allowed")
logger.on_fail("Saving not allowed")

_cont = 0
return new_buffer
Expand All @@ -243,7 +247,8 @@ def save_buffer(f: Path, buffer_name: str) -> list[Path]:
assert "/" not in parent, "only top parent folder allowed"
folder = Path(dataset, parent)
if not folder.exists():
print("[ ] Dose not exist:", (folder), f"{' ':20}") if verbose else None
if verbose:
logger.on_neutral("Dose not exist:", (folder), f"{' ':20}")
continue
if (folder / buffer_name).exists():
import datetime
Expand All @@ -253,40 +258,31 @@ def save_buffer(f: Path, buffer_name: str) -> list[Path]:

age = today - file_mod_time
if age.days >= int(max_age_days):
(
print(
"[ ] Delete Buffer - to old:",
if verbose:
logger.on_neutral(
"Delete Buffer - to old:",
(folder / buffer_name),
f"{' ':20}",
)
if verbose
else None
)
(folder / buffer_name).unlink()
if (folder / buffer_name).exists() and parent not in recompute_parents:
with open((folder / buffer_name), "rb") as b:
l = pickle.load(b)
(
print(
f"[{len(l):8}] Read Buffer:",
if verbose:
logger.on_neutral(
f"Read Buffer [{len(l):8}]:",
(folder / buffer_name),
f"{' ':20}",
)
if verbose
else None
)
files[dataset] += l
else:
(
print(
f"[{_cont:8}] Create new Buffer:",
if verbose:
logger.on_neutral(
f"Create new Buffer [{_cont:8}]:",
(folder / buffer_name),
f"{' ':20}",
end="\r",
)
if verbose
else None
)
files[dataset] += save_buffer((folder), buffer_name)
if filter_file is not None:
files: dict[Path | str, list[Path]] = {d: [g for g in f if filter_file(g)] for d, f in files.items()}
Expand Down Expand Up @@ -315,7 +311,7 @@ def _scan_tree(path, lvl=1, filter_folder=lambda _x, _y: True, verbose=False):
yield from _scan_tree(entry.path, lvl=lvl + 1, verbose=verbose)
elif entry.name[0] != ".":
if verbose:
print(f"[{_cont:8}]", end="\r")
logger.on_neutral(f"[{_cont:8}]", end="\r", ignore_prefix=True)
_cont += 1

yield entry
Expand Down Expand Up @@ -365,10 +361,10 @@ def __init__(
for ds in datasets:
ds_path = Path(ds) if isinstance(ds, str) else ds
if not ds_path.name.startswith("dataset-"):
print(f"[!] Dataset {ds_path.name} does not start with 'dataset-'")
logger.on_fail(f"Dataset {ds_path.name} does not start with 'dataset-'")
for ps in parents:
if not any(ps.startswith(lp) for lp in parents):
print(f"[!] Parentfolder {ps} is not a legal name")
logger.on_fail(f"Parentfolder {ps} is not a legal name")

self.datasets = datasets
self.parents = parents
Expand Down Expand Up @@ -437,7 +433,7 @@ def add_file_2_subject(self, bids: BIDS_FILE | Path, ds: Path | str | None = Non
bids_key, file_type = str(bids).rsplit("/", maxsplit=1)[-1].split(".", maxsplit=1)
# print(bids_key)
except Exception:
print("[!] skip file with out a type declaration:", bids.name)
logger.on_fail("skip file with out a type declaration:", bids.name)
# raise e
return

Expand All @@ -455,14 +451,12 @@ def add_file_2_subject(self, bids: BIDS_FILE | Path, ds: Path | str | None = Non
if subject not in self.subjects:
self.subjects[subject] = Subject_Container(subject, self.sequence_splitting_keys)
self.count_file += 1
(
print(
if self.verbose:
logger.on_neutral(
f"Found: {subject}, total file keys {(self.count_file)}, total subjects = {len(self.subjects)} ",
end="\r",
ignore_prefix=True,
)
if self.verbose
else None
)
self.subjects[subject].add(bids)

def enumerate_subjects(self, sort: bool = False, shuffle: bool = False) -> list[tuple[str, Subject_Container]]:
Expand Down Expand Up @@ -1093,13 +1087,10 @@ def get_changed_path( # noqa: C901
for key, value in info.items():
# New Keys are getting checked!
if non_strict_mode:
(
print(
f"[!] {key} is not in list of legal keys. This name '{key}' is invalid. Legal keys are: {list(entities_keys.keys())}. \nFor use see https://bids-specification.readthedocs.io/en/stable/99-appendices/09-entities.html"
if key not in entities_keys:
logger.on_fail(
f"{key} is not in list of legal keys. This name '{key}' is invalid. Legal keys are: {list(entities_keys.keys())}. \nFor use see https://bids-specification.readthedocs.io/en/stable/99-appendices/09-entities.html"
)
if key not in entities_keys
else None
)
else:
assert key in entities_keys, (
f"[!] {key} is not in list of legal keys. This name '{key}' is invalid. Legal keys are: {list(entities_keys.keys())}. \nFor use see https://bids-specification.readthedocs.io/en/stable/99-appendices/09-entities.html"
Expand Down Expand Up @@ -1601,7 +1592,7 @@ def get_identifier(self, sequence_splitting_keys: list[str]) -> str:
sequence_splitting_keys (list[str]): list of keys to use for splitting
"""
if "sub" not in self.info:
print(f"family_id, no sub-key, got {self.info}")
logger.on_fail(f"family_id, no sub-key, got {self.info}")
identifier = "sub-404"
else:
identifier = "sub-" + self.info["sub"]
Expand Down
6 changes: 3 additions & 3 deletions TPTBox/core/dicom/dicom_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def dicom_to_nifti_multiframe(ds: pydicom.FileDataset, nii_path: str | Path) ->
# }
# ],
# --- No geometry info (e.g. RGB screen captures or video frames) ---
print("⚠️ No spatial metadata found — assuming pixel size = 1mm and identity orientation.")
logger.on_warning("No spatial metadata found — assuming pixel size = 1mm and identity orientation.")
affine = np.eye(4)
affine[0, 0] = 1.0
affine[1, 1] = 1.0
Expand Down Expand Up @@ -332,7 +332,7 @@ def _extract_nii_from_dicom(dicom_out_path, nii_path):

return False
except Exception:
print(nii_path)
logger.on_fail(nii_path)

return True

Expand Down Expand Up @@ -525,7 +525,7 @@ def _add_grid_info_to_json(nii_path: Path | str, simp_json: Path | str, force_up
json_dict = load_json(simp_json) if Path(simp_json).exists() else {}
if "grid" in json_dict and not force_update:
return json_dict
print("Read Grid info", Path(simp_json).exists(), "grid" in json_dict)
logger.on_neutral("Read Grid info", Path(simp_json).exists(), "grid" in json_dict)
nii = NII.load(nii_path, False)
gird = {
"shape": nii.shape,
Expand Down
5 changes: 3 additions & 2 deletions TPTBox/core/internal/elastic_deform.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from numpy.typing import NDArray

from TPTBox import NII
from TPTBox.logger import logger


def deformed_nii(
Expand Down Expand Up @@ -51,7 +52,7 @@ def deformed_nii(
if sigma is None or points is None:
sigma, points = get_random_deform_parameter(deform_factor=deform_factor)

print("deformation parameter sigma = ", round(sigma, 4), "; n_points = ", points)
logger.on_neutral("deformation parameter sigma = ", round(sigma, 4), "; n_points = ", points)
t = time.time()
values = list(nii_dic.values())
# Deform
Expand All @@ -73,7 +74,7 @@ def deformed_nii(
out2: dict[str, NII] = {}
for (k, nii), arr in zip(nii_dic.items(), out, strict=True):
out2[k] = nii.set_array(arr[p:-p, p:-p, p:-p])
print("Deformation took", round(time.time() - t, 1), "Seconds")
logger.on_neutral("Deformation took", round(time.time() - t, 1), "Seconds")
return out2


Expand Down
4 changes: 2 additions & 2 deletions TPTBox/core/nii_poi_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from TPTBox.core.np_utils import np_count_nonzero
from TPTBox.core.vert_constants import COORDINATE
from TPTBox.logger import Log_Type
from TPTBox.logger import Log_Type, logger

from .vert_constants import (
AFFINE,
Expand Down Expand Up @@ -95,7 +95,7 @@ def __str__(self) -> str:
try:
zoom = "(" + ",".join([f"{a:.2f}" for a in self.zoom]) + ")"
except Exception as e:
print(e)
logger.on_warning(e)
zoom = self.zoom

return f"shape={self.shape_int},spacing={zoom}, origin={origin}, ori={self.orientation}" # type: ignore
Expand Down
9 changes: 5 additions & 4 deletions TPTBox/core/np_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from TPTBox.core.compat import zip_strict
from TPTBox.core.vert_constants import COORDINATE, LABEL_MAP, LABEL_REFERENCE
from TPTBox.logger import logger

UINT = TypeVar("UINT", bound=np.unsignedinteger[Any])
INT = TypeVar("INT", bound=np.signedinteger[Any])
Expand Down Expand Up @@ -110,7 +111,7 @@ def cc3dstatistics(arr: UINTARRAY, use_crop: bool = True) -> dict:
arrc = arr[crop]
return _cc3dstats(arrc)
except ValueError as e:
print(e)
logger.on_warning(e)
return _cc3dstats(arr)


Expand Down Expand Up @@ -1317,7 +1318,8 @@ def _convex_hull(
"""
points = np.transpose(np.where(arr))
if len(points) <= 3:
print("To few points") if verbose else None
if verbose:
logger.on_warning("To few points")
return np.zeros_like(arr, dtype=arr.dtype)
hull = scipy.spatial.ConvexHull(points)
deln = scipy.spatial.Delaunay(points[hull.vertices])
Expand Down Expand Up @@ -1401,7 +1403,6 @@ def infect(x, y, z):
infect(*infect_list.pop())
boundary[boundary == 0] = 2
boundary -= 1
print(boundary.sum())
return boundary


Expand Down Expand Up @@ -1449,7 +1450,7 @@ def np_betti_numbers(img: np.ndarray, verbose=False) -> tuple[int, int, int]:
b2 -= 1
b1 = b0 + b2 - euler_char_num # Euler number = Betti:0 - Betti:1 + Betti:2
if verbose:
print(f"Betti number: b0 = {b0}, b1 = {b1}, b2 = {b2}")
logger.on_neutral(f"Betti number: b0 = {b0}, b1 = {b1}, b2 = {b2}")
return b0, b1, b2


Expand Down
Loading