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