Skip to content

Commit 70fad8e

Browse files
authored
Merge pull request #101 from Hendrik-code/dev_hendrik
Dev hendrik
2 parents 7d54443 + 80cd37d commit 70fad8e

10 files changed

Lines changed: 111 additions & 25 deletions

File tree

TPTBox/core/bids_constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"MP2RAG",
3535
"MPM",
3636
"MT",
37-
"MT",
37+
"MTS",
3838
"T1map",
3939
"T2map",
4040
"T2starmap",

TPTBox/core/bids_files.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,22 @@ def get_frame_of_reference_uid(self, default=None):
11041104
base36 = chars[i] + base36
11051105
return base36[:length]
11061106

1107+
def get_identifier(self, sequence_splitting_keys: list[str]) -> str:
1108+
"""Generates an identifier for the BIDS_FILE based on subject and splitting keys
1109+
1110+
Args:
1111+
sequence_splitting_keys (list[str]): list of keys to use for splitting
1112+
"""
1113+
if "sub" not in self.info:
1114+
print(f"family_id, no sub-key, got {self.info}")
1115+
identifier = "sub-404"
1116+
else:
1117+
identifier = "sub-" + self.info["sub"]
1118+
for s in self.info.keys():
1119+
if s in sequence_splitting_keys:
1120+
identifier += "_" + s + "-" + self.info[s]
1121+
return identifier
1122+
11071123

11081124
class Searchquery:
11091125
def __init__(self, subj: Subject_Container, flatten=False) -> None:
@@ -1464,15 +1480,16 @@ def __lt__(self, other):
14641480

14651481
def get_identifier(self):
14661482
first_e = self.data_dict[next(iter(self.data_dict.keys()))][0]
1467-
if "sub" not in first_e.info:
1468-
print(f"family_id, no sub-key, got {first_e.info} and data_dict {list(self.data_dict.keys())}")
1469-
identifier = "sub-404"
1470-
else:
1471-
identifier = "sub-" + first_e.info["sub"]
1472-
for s in first_e.info.keys():
1473-
if s in self.sequence_splitting_keys:
1474-
identifier += "_" + s + "-" + first_e.info[s]
1475-
return identifier
1483+
return first_e.get_identifier(self.sequence_splitting_keys)
1484+
# if "sub" not in first_e.info:
1485+
# print(f"family_id, no sub-key, got {first_e.info} and data_dict {list(self.data_dict.keys())}")
1486+
# identifier = "sub-404"
1487+
# else:
1488+
# identifier = "sub-" + first_e.info["sub"]
1489+
# for s in first_e.info.keys():
1490+
# if s in self.sequence_splitting_keys:
1491+
# identifier += "_" + s + "-" + first_e.info[s]
1492+
# return identifier
14761493

14771494
def items(self):
14781495
return self.data_dict.items()

TPTBox/core/nii_poi_abstract.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,12 @@ def assert_affine(
305305

306306
# Print errors
307307
for err in found_errors:
308-
log.print(err, ltype=Log_Type.FAIL, verbose=verbose)
308+
text = f"{text}; {err}" if text else f"{err}"
309+
log.print(f"{text}", ltype=Log_Type.FAIL, verbose=verbose)
309310
# Final conclusion and possible raising of AssertionError
310311
has_errors = len(found_errors) > 0
311312
if raise_error and has_errors:
312-
raise AssertionError(f"{text}; assert_affine failed with {found_errors}")
313+
raise AssertionError(f"{text} assert_affine failed with {found_errors}")
313314

314315
return not has_errors
315316

TPTBox/core/nii_wrapper.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ def dtype(self)->type:
408408
if self.__unpacked:
409409
return self._arr.dtype # type: ignore
410410
return self.nii.dataobj.dtype #type: ignore
411+
@dtype.setter
412+
def dtype(self, dtype:type):
413+
self.set_dtype_(dtype)
411414
@property
412415
def header(self) -> Nifti1Header:
413416
if self.__unpacked:
@@ -428,6 +431,9 @@ def affine(self,affine:np.ndarray):
428431
def orientation(self) -> AX_CODES:
429432
ort = nio.io_orientation(self.affine)
430433
return nio.ornt2axcodes(ort) # type: ignore
434+
@orientation.setter
435+
def orientation(self, value: AX_CODES):
436+
self.reorient_(value, verbose=False)
431437
@property
432438
def dims(self)->int:
433439
self._unpack()
@@ -443,6 +449,9 @@ def zoom(self) -> ZOOMS:
443449
z = z[:n]
444450
#assert len(z) == 3,z
445451
return z # type: ignore
452+
@zoom.setter
453+
def zoom(self, value: tuple[float, float, float]):
454+
self.rescale_(value, verbose=False)
446455

447456
@property
448457
def origin(self) -> tuple[float, float, float]:
@@ -477,10 +486,6 @@ def direction_itk(self) -> list:
477486
a[:len(a)//3*2]*=-1
478487
return a.tolist()
479488

480-
@orientation.setter
481-
def orientation(self, value: AX_CODES):
482-
self.reorient_(value, verbose=False)
483-
484489

485490
def split_4D_image_to_3D(self):
486491
assert self.get_num_dims() == 4,self.get_num_dims()

TPTBox/core/np_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ def np_calc_crop_around_centerpoint(
471471
arr: np.ndarray,
472472
cutout_size: tuple[int, ...],
473473
pad_to_size: Sequence[int] | np.ndarray | int = 0,
474-
) -> tuple[np.ndarray, tuple, tuple]:
474+
) -> tuple[np.ndarray, tuple[slice, slice, slice], tuple]:
475475
"""
476476
477477
Args:

TPTBox/core/poi.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,10 +918,10 @@ def calc_poi_from_subreg_vert(
918918
save_buffer_file=False, # used by wrapper # noqa: ARG001
919919
decimals=2,
920920
subreg_id: int | Abstract_lvl | Sequence[int | Abstract_lvl] | Sequence[Abstract_lvl] | Sequence[int] = 50,
921-
verbose: logging = True,
921+
verbose: logging = False,
922922
extend_to: POI | None = None,
923923
# use_vertebra_special_action=True,
924-
_vert_ids=None,
924+
_vert_ids: list[int] | None = None,
925925
_print_phases=False,
926926
_orientation_version=0,
927927
) -> POI:
@@ -1058,6 +1058,7 @@ def calc_poi_from_subreg_vert(
10581058
subreg_msk,
10591059
_vert_ids=_vert_ids,
10601060
log=log,
1061+
verbose=verbose,
10611062
_orientation_version=_orientation_version,
10621063
)
10631064
extend_to.apply_crop_reverse(crop, org_shape, inplace=True)

TPTBox/core/poi_fun/poi_abstract.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def map_labels_(
441441

442442
def sort(self, inplace=True, order_dict: dict | None = None) -> Self:
443443
"""Sort vertebra dictionary by sorting_list"""
444-
if self.level_one_info is not None:
444+
if self.level_one_info is not None and self.level_one_info != Any:
445445
order_dict = self.level_one_info.order_dict()
446446
poi = self.centroids._sort(inplace=inplace, order_dict=order_dict)
447447
if inplace:

TPTBox/core/poi_fun/ray_casting.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def unit_vector(vector):
2323

2424
# @njit(fastmath=True)
2525
def trilinear_interpolate(volume, x, y, z):
26-
xi, yi, zi = int(x), int(y), int(z)
26+
xi, yi, zi = np.floor(x).astype(int), np.floor(y).astype(int), np.floor(z).astype(int)
2727
if xi < 0 or yi < 0 or zi < 0 or xi >= volume.shape[0] - 1 or yi >= volume.shape[1] - 1 or zi >= volume.shape[2] - 1:
2828
return 0.0
2929

@@ -51,7 +51,7 @@ def max_distance_ray_cast_convex_npfast(
5151
region_array: np.ndarray,
5252
start_coord: np.ndarray,
5353
direction_vector: np.ndarray,
54-
acc_delta=0.05,
54+
acc_delta=0.00005,
5555
):
5656
# Normalize direction
5757
norm_vec = direction_vector / np.sqrt((direction_vector**2).sum())
@@ -70,6 +70,7 @@ def max_distance_ray_cast_convex_npfast(
7070
y = start_coord[1] + norm_vec[1] * mid
7171
z = start_coord[2] + norm_vec[2] * mid
7272
val = trilinear_interpolate(region_array, x, y, z)
73+
print(f"Raycast check at distance {mid:.2f}: value={val:.4f}")
7374
if val > 0.5:
7475
min_v = mid
7576
else:
@@ -86,6 +87,64 @@ def max_distance_ray_cast_convex_npfast(
8687
)
8788

8889

90+
def max_distance_ray_cast_convex_np(
91+
region: np.ndarray,
92+
start_coord: COORDINATE | np.ndarray,
93+
direction_vector: np.ndarray,
94+
acc_delta: float = 0.00005,
95+
max_v: int | None = None,
96+
):
97+
"""
98+
Computes the maximum distance a ray can travel inside a convex region before exiting.
99+
100+
Parameters:
101+
region (NII): The region of interest as a 3D NIfTI image.
102+
start_coord (COORDINATE | np.ndarray): The starting coordinate of the ray.
103+
direction_vector (np.ndarray): The direction vector of the ray.
104+
acc_delta (float, optional): The accuracy threshold for bisection search. Default is 0.00005.
105+
106+
Returns:
107+
np.ndarray: The exit coordinate of the ray within the region.
108+
"""
109+
start_point_np = np.asarray(start_coord)
110+
if start_point_np is None:
111+
return None
112+
113+
"""Convex assumption!"""
114+
# Compute a normal vector, that defines the plane direction
115+
normal_vector = np.asarray(direction_vector)
116+
normal_vector = normal_vector / norm(normal_vector)
117+
# Create a function to interpolate within the mask array
118+
interpolator = RegularGridInterpolator([np.arange(region.shape[i]) for i in range(3)], region)
119+
120+
def is_inside(distance):
121+
coords = [start_point_np[i] + normal_vector[i] * distance for i in [0, 1, 2]]
122+
if any(i < 0 for i in coords):
123+
return 0
124+
if any(coords[i] > region.shape[i] - 1 for i in range(len(coords))):
125+
return 0
126+
# Evaluate the mask value at the interpolated coordinates
127+
mask_value = interpolator(coords)
128+
return mask_value > 0.5
129+
130+
if not is_inside(0):
131+
return start_point_np
132+
count = 0
133+
min_v = 0
134+
if max_v is None:
135+
max_v = sum(region.shape)
136+
delta = max_v * 2
137+
while acc_delta < delta:
138+
bisection = (max_v - min_v) / 2 + min_v
139+
if is_inside(bisection):
140+
min_v = bisection
141+
else:
142+
max_v = bisection
143+
delta = max_v - min_v
144+
count += 1
145+
return start_point_np + normal_vector * ((min_v + max_v) / 2)
146+
147+
89148
def max_distance_ray_cast_convex(
90149
region: NII,
91150
start_coord: COORDINATE | np.ndarray,

TPTBox/core/poi_fun/vertebra_pois_non_centroids.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ def __init__(self, target: Location, *prerequisite: Location, **args) -> None:
236236
Strategy_Computed_Before(L.Spinal_Canal,L.Vertebra_Corpus)
237237
Strategy_Computed_Before(L.Vertebra_Disc_Inferior,L.Vertebra_Disc_Inferior)
238238

239+
239240
# fmt: on
240241
def compute_non_centroid_pois( # noqa: C901
241242
poi: POI,
@@ -244,6 +245,7 @@ def compute_non_centroid_pois( # noqa: C901
244245
subreg: NII,
245246
_vert_ids: Sequence[int] | None = None,
246247
log: Logger_Interface = _log,
248+
verbose: bool | None = True,
247249
_orientation_version=0,
248250
):
249251
if _vert_ids is None:
@@ -254,7 +256,7 @@ def compute_non_centroid_pois( # noqa: C901
254256
assert 52 not in poi.keys_region()
255257

256258
if Location.Vertebra_Direction_Inferior in locations:
257-
log.on_text("Compute Vertebra DIRECTIONS")
259+
log.on_text("Compute Vertebra DIRECTIONS", verbose=verbose)
258260
### Calc vertebra direction; We always need them, so we just compute them. ###
259261
sub_regions = poi.keys_subregion()
260262
if any(a.value not in sub_regions for a in vert_directions):
@@ -268,7 +270,7 @@ def compute_non_centroid_pois( # noqa: C901
268270
set(locations),
269271
key=lambda x: all_poi_functions[x.value].prority() if x.value in all_poi_functions else x.value,
270272
) # type: ignore
271-
log.on_text("Calc pois from subregion id", {l.name for l in locations})
273+
log.on_text("Calc pois from subregion id", {l.name for l in locations}, verbose=verbose)
272274
### DENSE ###
273275
if Location.Dens_axis in locations and 2 in _vert_ids and (2, Location.Dens_axis.value) not in poi:
274276
a = subreg * vert.extract_label(2)

TPTBox/tests/speedtests/speedtest_npunique.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
np_bbox_binary,
1414
np_bounding_boxes,
1515
np_center_of_mass,
16+
np_is_empty,
1617
np_map_labels,
1718
np_unique,
1819
np_unique_withoutzero,
@@ -33,7 +34,7 @@ def get_nii_array():
3334
speed_test(
3435
repeats=50,
3536
get_input_func=get_nii_array,
36-
functions=[np_unique, np.unique],
37+
functions=[np_unique, np.unique, np_is_empty, np.max],
3738
assert_equal_function=lambda x, y: True, # np.all([x[i] == y[i] for i in range(len(x))]), # noqa: ARG005
3839
# np.all([x[i] == y[i] for i in range(len(x))])
3940
)

0 commit comments

Comments
 (0)