From f1748f268a245ccb3d088be9186f8a227635bf6d Mon Sep 17 00:00:00 2001 From: HarryHeres Date: Sun, 22 Dec 2024 13:19:23 +0100 Subject: [PATCH] Fixing typos + preprocessing params --- .../Resources/UI/SlicerBoneMorphing.ui | 28 +++++++------ SlicerBoneMorphing/src/logic/Constants.py | 12 +++--- .../src/logic/SlicerBoneMorphingLogic.py | 39 +++++++++---------- .../src/widget/SlicerBoneMorphingWidget.py | 12 +++--- 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/SlicerBoneMorphing/Resources/UI/SlicerBoneMorphing.ui b/SlicerBoneMorphing/Resources/UI/SlicerBoneMorphing.ui index dc6d815..8b4d21a 100644 --- a/SlicerBoneMorphing/Resources/UI/SlicerBoneMorphing.ui +++ b/SlicerBoneMorphing/Resources/UI/SlicerBoneMorphing.ui @@ -176,26 +176,29 @@ - Maximum radius for calculating normals. Calculated as multiplier of the object's voxel size + Maximum radius for calculating normals. Calculated as percentage of the object's size - Normals estimation radius: + Normals estimation radius [%]: - Maximum radius for calculating normals. Calculated as multiplier of the object's voxel size + Maximum radius for calculating normals. Calculated as percentage of the object's size + + + 3 - 1.000000000000000 + 0.001000000000000 100.000000000000000 - 4.000000000000000 + 8.000000000000000 @@ -205,7 +208,7 @@ Maximum amount of points considered neighbouring. Calculated as percentage of the lower count of initial points of the source or target mesh - Normals estimation max neighbours: + Normals estimation max neighbours [%]: @@ -228,26 +231,29 @@ - Maximum radius for calculating fast point feature histograms. Calculated as a multiplier of the object's voxel size + Maximum radius for calculating fast-point-feature histograms. Calculated as percentage of the object's size - FPFH search radius: + FPFH search radius [%]: - Maximum radius for calculating fast point feature histograms. Calculated as a multiplier of the object's voxel size + Maximum radius for calculating fast-point-feature histograms. Calculated as percentage of the object's size + + + 3 - 1.000000000000000 + 0.001000000000000 100.000000000000000 - 10.000000000000000 + 20.000000000000000 diff --git a/SlicerBoneMorphing/src/logic/Constants.py b/SlicerBoneMorphing/src/logic/Constants.py index d4d0d45..0ab2d4e 100644 --- a/SlicerBoneMorphing/src/logic/Constants.py +++ b/SlicerBoneMorphing/src/logic/Constants.py @@ -24,14 +24,14 @@ PREPROCESSING_KEY_DOWNSAMPLING_TARGET_TO_SOURCE = "dtts" PREPROCESSING_KEY_NORMALS_ESTIMATION_RADIUS = "ner" PREPROCESSING_KEY_FPFH_ESTIMATION_RADIUS = "fer" -PREPROCESSING_KEY_MAX_NN_NORMALS = "mnnn" -PREPROCESSING_KEY_MAX_NN_FPFH = "mnf" +PREPROCESSING_KEY_NORMALS_MAX_NN = "mnnn" +PREPROCESSING_KEY_FPFH_MAX_NN = "mnf" PREPROCESSING_DEFAULT_VALUE_DOWNSAMPLING_VOXEL_SIZE = 0.0 -PREPROCESSING_DEFAULT_VALUE_RADIUS_NORMAL_SCALE = 4 -PREPROCESSING_DEFAULT_VALUE_RADIUS_FEATURE_SCALE = 10 -PREPROCESSING_DEFAULT_VALUE_MAX_NN_NORMALS = 25 -PREPROCESSING_DEFAULT_VALUE_MAX_NN_FPFH = 50 +PREPROCESSING_DEFAULT_VALUE_NORMALS_ESTIMATION_RADIUS = 8 +PREPROCESSING_DEFAULT_VALUE_FPFPH_ESTIMATION_RADIUS = 20 +PREPROCESSING_DEFAULT_VALUE_NORMALS_MAX_NN = 25 +PREPROCESSING_DEFAULT_VALUE_FPFH_MAX_NN = 50 ### REGISTRATION PARAMETERS ### REGISTRATION_KEY_RANSAC_DISTANCE_THRESHOLD = "rrdt" diff --git a/SlicerBoneMorphing/src/logic/SlicerBoneMorphingLogic.py b/SlicerBoneMorphing/src/logic/SlicerBoneMorphingLogic.py index a52cb82..f059e5f 100644 --- a/SlicerBoneMorphing/src/logic/SlicerBoneMorphingLogic.py +++ b/SlicerBoneMorphing/src/logic/SlicerBoneMorphingLogic.py @@ -224,15 +224,15 @@ def __preprocess_model( target_pcd = self.__convert_mesh_to_point_cloud(target_mesh) points_min = np.min([len(source_pcd.points), len(target_pcd.points)]) - max_nn_normals = int(points_min * (parameters[const.PREPROCESSING_KEY_MAX_NN_NORMALS] / 100)) - max_nn_fpfh = int(points_min * (parameters[const.PREPROCESSING_KEY_MAX_NN_FPFH] / 100)) + max_nn_normals = int(points_min * (parameters[const.PREPROCESSING_KEY_NORMALS_MAX_NN] / 100)) + max_nn_fpfh = int(points_min * (parameters[const.PREPROCESSING_KEY_FPFH_MAX_NN] / 100)) - voxel_size = 0.0 + object_size = 0.0 if parameters[const.PREPROCESSING_KEY_DOWNSAMPLING] is True: if parameters[const.PREPROCESSING_KEY_DOWNSAMPLING_SOURCE_TO_TARGET] is True: - voxel_size = self.__calculate_object_size(target_pcd) + object_size = self.__calculate_object_size(target_pcd) elif parameters[const.PREPROCESSING_KEY_DOWNSAMPLING_TARGET_TO_SOURCE] is True: - voxel_size = self.__calculate_object_size(source_pcd) + object_size = self.__calculate_object_size(source_pcd) else: print("ERROR: Downsampling is enabled but neither of the downsampling options was selected") return [const.EXIT_FAILURE, None] @@ -240,7 +240,7 @@ def __preprocess_model( print("Preprocessing source mesh...") source_pcd_downsampled, source_pcd_fpfh = self.__preprocess_point_cloud( source_pcd, - voxel_size, + object_size, parameters[const.PREPROCESSING_KEY_NORMALS_ESTIMATION_RADIUS], parameters[const.PREPROCESSING_KEY_FPFH_ESTIMATION_RADIUS], max_nn_normals, @@ -250,21 +250,20 @@ def __preprocess_model( print("Preprocessing target mesh...") target_pcd_downsampled, target_pcd_fpfh = self.__preprocess_point_cloud( target_pcd, - voxel_size, + object_size, parameters[const.PREPROCESSING_KEY_NORMALS_ESTIMATION_RADIUS], parameters[const.PREPROCESSING_KEY_FPFH_ESTIMATION_RADIUS], max_nn_normals, max_nn_fpfh ) - voxel_size = np.max([self.__calculate_object_size(source_pcd), self.__calculate_object_size(target_pcd)]) - print("Calculated voxel size: " + str(voxel_size)) + object_size = np.max([self.__calculate_object_size(source_pcd), self.__calculate_object_size(target_pcd)]) try: result_ransac = self.__ransac_pcd_registration( source_pcd_downsampled, target_pcd_downsampled, source_pcd_fpfh, target_pcd_fpfh, - voxel_size * (parameters[const.REGISTRATION_KEY_RANSAC_DISTANCE_THRESHOLD] / 100), + object_size * (parameters[const.REGISTRATION_KEY_RANSAC_DISTANCE_THRESHOLD] / 100), parameters[const.REGISTRATION_KEY_FITNESS_THRESHOLD], parameters[const.REGISTRATION_KEY_MAX_ITERATIONS] ) @@ -276,7 +275,7 @@ def __preprocess_model( result_icp = o3d.pipelines.registration.registration_icp( source_pcd_downsampled, target_pcd_downsampled, - voxel_size * (parameters[const.REGISTRATION_KEY_ICP_DISTANCE_THRESHOLD] / 100), + object_size * (parameters[const.REGISTRATION_KEY_ICP_DISTANCE_THRESHOLD] / 100), result_ransac.transformation, o3d.pipelines.registration.TransformationEstimationPointToPlane() ) @@ -302,7 +301,7 @@ def __calculate_object_size(self, source: o3d.geometry.Geometry) -> float: def __preprocess_point_cloud( self, pcd: o3d.geometry.PointCloud, - downsampling_voxel_size: float, + downsampling_object_size: float, normals_estimation_radius: float, fpfh_estimation_radius: float, max_nn_normals: int, @@ -313,8 +312,8 @@ def __preprocess_point_cloud( Parameters ---------- - o3d.geometry.PointCloud pcd: Source point cloud - float downsampling_distance_threshold: Distance threshold for downsampling + o3d.geometry.PointCloud pcd: Point cloud to preprocess + float downsampling_object_size: Size of the object to downsample to float normals_estimation_radius: Radius for estimating normals float fpfh_estimation_radius: Radius for the FPFH computation int max_nn_normals: Maximum number of neighbours considered for normals estimation @@ -327,15 +326,15 @@ def __preprocess_point_cloud( - [1] = FPFH ''' - pcd_voxel_size = self.__calculate_object_size(pcd) - if downsampling_voxel_size > 0.0 and downsampling_voxel_size != pcd_voxel_size and pcd_voxel_size > downsampling_voxel_size: - print("Downsampling point cloud with voxel size: " + str(pcd_voxel_size) + " to target voxel size: " + str(downsampling_voxel_size)) - pcd = pcd.voxel_down_sample(downsampling_voxel_size) + pcd_object_size = self.__calculate_object_size(pcd) + if downsampling_object_size > 0.0 and downsampling_object_size != pcd_object_size and pcd_object_size > downsampling_object_size: + print("Downsampling point cloud with size: " + str(pcd_object_size) + " to target object size: " + str(downsampling_object_size)) + pcd = pcd.object_down_sample(downsampling_object_size) else: print("Downsampling will not be performed. The target voxel size is either less than 0, equal to the calculated voxel size and/or larger, than current voxel size") - pcd.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=(pcd_voxel_size * normals_estimation_radius), max_nn=max_nn_normals)) - pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(pcd, o3d.geometry.KDTreeSearchParamHybrid(radius=pcd_voxel_size * fpfh_estimation_radius, max_nn=max_nn_fpfh)) + pcd.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=(pcd_object_size * (normals_estimation_radius / 100)), max_nn=max_nn_normals)) + pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(pcd, o3d.geometry.KDTreeSearchParamHybrid(radius=pcd_object_size * (fpfh_estimation_radius / 100), max_nn=max_nn_fpfh)) return pcd, pcd_fpfh def __ransac_pcd_registration( diff --git a/SlicerBoneMorphing/src/widget/SlicerBoneMorphingWidget.py b/SlicerBoneMorphing/src/widget/SlicerBoneMorphingWidget.py index ebd5b3b..fa8a797 100644 --- a/SlicerBoneMorphing/src/widget/SlicerBoneMorphingWidget.py +++ b/SlicerBoneMorphing/src/widget/SlicerBoneMorphingWidget.py @@ -62,10 +62,10 @@ def __reset_parameters_to_default(self) -> None: ## Preprocessing parameters ## self.__ui.preprocessingDownsamplingCheckBox.checked = False self.__ui.preprocessingDownsamplingSourceToTargetRadioButton.checked = True - self.__ui.preprocessingNormalsEstimationRadiusDoubleSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_RADIUS_NORMAL_SCALE - self.__ui.preprocessingNormalsEstimationMaxNeighboursSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_MAX_NN_NORMALS - self.__ui.preprocessingFpfhRadiusDoubleSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_RADIUS_FEATURE_SCALE - self.__ui.preprocessingFpfhMaxNeighboursSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_MAX_NN_FPFH + self.__ui.preprocessingNormalsEstimationRadiusDoubleSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_NORMALS_ESTIMATION_RADIUS + self.__ui.preprocessingNormalsEstimationMaxNeighboursSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_NORMALS_MAX_NN + self.__ui.preprocessingFpfhRadiusDoubleSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_FPFPH_ESTIMATION_RADIUS + self.__ui.preprocessingFpfhMaxNeighboursSpinBox.value = const.PREPROCESSING_DEFAULT_VALUE_FPFH_MAX_NN ## Registration parameters ## self.__ui.registrationMaxIterationsSpinBox.value = const.REGISTRATION_DEFAULT_VALUE_MAX_ITERATIONS @@ -182,9 +182,9 @@ def __parse_parameters_preprocessing(self) -> dict: params[const.PREPROCESSING_KEY_DOWNSAMPLING_SOURCE_TO_TARGET] = self.__ui.preprocessingDownsamplingSourceToTargetRadioButton.checked params[const.PREPROCESSING_KEY_DOWNSAMPLING_TARGET_TO_SOURCE] = self.__ui.preprocessingDownsamplingTargetToSourceRadioButton.checked params[const.PREPROCESSING_KEY_NORMALS_ESTIMATION_RADIUS] = self.__ui.preprocessingNormalsEstimationRadiusDoubleSpinBox.value - params[const.PREPROCESSING_KEY_MAX_NN_NORMALS] = self.__ui.preprocessingNormalsEstimationMaxNeighboursSpinBox.value + params[const.PREPROCESSING_KEY_NORMALS_MAX_NN] = self.__ui.preprocessingNormalsEstimationMaxNeighboursSpinBox.value params[const.PREPROCESSING_KEY_FPFH_ESTIMATION_RADIUS] = self.__ui.preprocessingFpfhRadiusDoubleSpinBox.value - params[const.PREPROCESSING_KEY_MAX_NN_FPFH] = self.__ui.preprocessingFpfhMaxNeighboursSpinBox.value + params[const.PREPROCESSING_KEY_FPFH_MAX_NN] = self.__ui.preprocessingFpfhMaxNeighboursSpinBox.value # Registration params[const.REGISTRATION_KEY_MAX_ITERATIONS] = self.__ui.registrationMaxIterationsSpinBox.value