diff --git a/ntrfc/turbo/profile_tele_extraction.py b/ntrfc/turbo/profile_tele_extraction.py index e473ce0add98171dacaf47a5bf5c1549ac1e1055..252d0528be47948e914ce93ccd62c4d7babcd64a 100644 --- a/ntrfc/turbo/profile_tele_extraction.py +++ b/ntrfc/turbo/profile_tele_extraction.py @@ -12,6 +12,8 @@ from ntrfc.geometry.line import lines_from_points from ntrfc.turbo.pointcloud_methods import extractSidePolys, midline_from_sides from ntrfc.math.vectorcalc import findNearest, vecDir from scipy.optimize import differential_evolution +from dataclasses import dataclass + def detect_inliers_tukey(data): """ Detect inliers using Tukey's method. @@ -133,17 +135,19 @@ def extract_vk_hk(sortedPoly: pv.PolyData) -> (int, int): valid_midline = clean_sites(voronoi_sites, skeletonize_sites) sort_indices = np.argsort(valid_midline[:, 0]) valid_midline_sorted = valid_midline[sort_indices] - smoothing = 0.01 + smoothing = 0.00001 u_new = np.arange(0, 1, 1 / 1024) (tck, u), fp, ier, msg = splprep((valid_midline_sorted[::, 0], valid_midline_sorted[::, 1]), u=None, per=0, k=3, s=smoothing, full_output=True) # s = optional parameter (default used here) x_new, y_new = splev(u_new, tck, der=0) + + le_ind, te_ind, skeletonline_complete = skeletonline_completion(2, points_orig, points_2d_closed_refined_voronoi, - np.stack([x_new[1:-1], y_new[1:-1]]).T) + np.stack([x_new, y_new]).T) - rms_error = compute_midline_error(le_ind, te_ind,sortedPoly,shapelypoly) + rms_error_ref = compute_midline_error(le_ind, te_ind,sortedPoly,shapelypoly) # Define the objective function def objective_function(indices, sortedPoly, shapelypoly): @@ -151,32 +155,95 @@ def extract_vk_hk(sortedPoly: pv.PolyData) -> (int, int): rms_error_le_up = compute_midline_error(le_ind, te_ind, sortedPoly, shapelypoly) return rms_error_le_up - le_search_interval = 6 - te_search_interval = 6 - - le_min = (le_ind - le_search_interval) % npts - te_min = (te_ind - te_search_interval) % npts - -# le_max = (le_ind + 20) % npts -# te_max = (te_ind + 20) % npts - - allowed_le = [(i+le_min)% npts for i in range(2*le_search_interval)] - allowed_te = [(i+te_min)% npts for i in range(2*te_search_interval)] - - specific_combinations = np.array(np.meshgrid(allowed_le, allowed_te)).T.reshape(-1, 2) - - # Initialize the best values - best_combination = None - best_rms_error = float('inf') - - # Evaluate the objective function for each combination - for indices in specific_combinations: - rms_error = objective_function(indices, sortedPoly, shapelypoly) - if rms_error < best_rms_error: - best_rms_error = rms_error - best_combination = indices - - le_ind_best, te_ind_best = best_combination + @dataclass + class SkeletonResult: + le_ind: int + te_ind: int + rms_error: float + + + # optimize te + search_te_pos = 3 + te_pos_ind = te_ind + search_te_neg = 3 + te_neg_ind = te_ind + search_le_pos = 3 + le_pos_ind = le_ind + search_le_neg = 3 + le_neg_ind = le_ind + search_le_neg_te_pos = 3 + le_neg_te_pos_le_ind = le_ind + le_neg_te_pos_te_ind = te_ind + search_le_pos_te_neg = 3 + le_pos_te_neg_le_ind = le_ind + le_pos_te_neg_te_ind = te_ind + search_le_neg_te_neg = 3 + le_neg_te_neg_le_ind = le_ind + le_neg_te_neg_te_ind = te_ind + search_le_pos_te_pos = 3 + le_pos_te_pos_le_ind = le_ind + le_pos_te_pos_te_ind = te_ind + + skeletonresults = [] + skeletonresults.append(SkeletonResult(le_ind, te_ind, rms_error_ref)) + + + while search_te_pos > 0: + te_pos_ind = (te_pos_ind + 1) % npts + rms_error_te_pos = objective_function([le_ind, te_pos_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_ind, te_pos_ind, rms_error_te_pos)) + if rms_error_te_pos > rms_error_ref: + search_te_pos -= 1 + while search_te_neg > 0: + te_neg_ind = (te_neg_ind - 1) % npts + rms_error_te_neg = objective_function([le_ind, te_neg_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_ind, te_neg_ind, rms_error_te_neg)) + if rms_error_te_neg > rms_error_ref: + search_te_neg -= 1 + while search_le_pos > 0: + le_pos_ind = (le_pos_ind + 1) % npts + rms_error_le_pos = objective_function([le_pos_ind, te_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_pos_ind, te_ind, rms_error_le_pos)) + if rms_error_le_pos > rms_error_ref: + search_le_pos -= 1 + while search_le_neg > 0: + le_neg_ind = (le_neg_ind - 1) % npts + rms_error_le_neg = objective_function([le_neg_ind, te_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_neg_ind, te_ind, rms_error_le_neg)) + if rms_error_le_neg > rms_error_ref: + search_le_neg -= 1 + while search_le_neg_te_pos > 0: + le_neg_te_pos_le_ind = (le_neg_te_pos_le_ind - 1) % npts + le_neg_te_pos_te_ind = (le_neg_te_pos_te_ind + 1) % npts + rms_error_le_neg_te_pos = objective_function([le_neg_te_pos_le_ind, le_neg_te_pos_te_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_neg_te_pos_le_ind, le_neg_te_pos_te_ind, rms_error_le_neg_te_pos)) + if rms_error_le_neg_te_pos > rms_error_ref: + search_le_neg_te_pos -= 1 + while search_le_pos_te_neg > 0: + le_pos_te_neg_le_ind = (le_pos_te_neg_le_ind + 1) % npts + le_pos_te_neg_te_ind = (le_pos_te_neg_te_ind - 1) % npts + rms_error_le_pos_te_neg = objective_function([le_pos_te_neg_le_ind, le_pos_te_neg_te_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_pos_te_neg_le_ind, le_pos_te_neg_te_ind, rms_error_le_pos_te_neg)) + if rms_error_le_pos_te_neg > rms_error_ref: + search_le_pos_te_neg -= 1 + while search_le_neg_te_neg > 0: + le_neg_te_neg_le_ind = (le_neg_te_neg_le_ind - 1) % npts + le_neg_te_neg_te_ind = (le_neg_te_neg_te_ind - 1) % npts + rms_error_le_neg_te_neg = objective_function([le_neg_te_neg_le_ind, le_neg_te_neg_te_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_neg_te_neg_le_ind, le_neg_te_neg_te_ind, rms_error_le_neg_te_neg)) + if rms_error_le_neg_te_neg > rms_error_ref: + search_le_neg_te_neg -= 1 + while search_le_pos_te_pos > 0: + le_pos_te_pos_le_ind = (le_pos_te_pos_le_ind + 1) % npts + le_pos_te_pos_te_ind = (le_pos_te_pos_te_ind + 1) % npts + rms_error_le_pos_te_pos = objective_function([le_pos_te_pos_le_ind, le_pos_te_pos_te_ind], sortedPoly, shapelypoly) + skeletonresults.append(SkeletonResult(le_pos_te_pos_le_ind, le_pos_te_pos_te_ind, rms_error_le_pos_te_pos)) + if rms_error_le_pos_te_pos > rms_error_ref: + search_le_pos_te_pos -= 1 + + best_combination = min(skeletonresults, key=lambda x: x.rms_error) + + le_ind_best, te_ind_best = best_combination.le_ind, best_combination.te_ind return le_ind_best, te_ind_best @@ -268,6 +335,8 @@ def polygon_to_binary_image(polygon, image_size): def compute_midline(polygon, image_size): # Convert polygon to binary image binary_img = polygon_to_binary_image(polygon, image_size) + # crop all-zero rows of the binary_img + binary_img = binary_img[~np.all(binary_img == 0, axis=1)] # Apply skeletonization skeleton = skeletonize(binary_img, method='lee')