@@ -23,7 +23,7 @@ def unit_vector(vector):
2323
2424# @njit(fastmath=True)
2525def 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+
89148def max_distance_ray_cast_convex (
90149 region : NII ,
91150 start_coord : COORDINATE | np .ndarray ,
0 commit comments