registration_ransac_based_on_feature_matching cannot be deterministic
- Dominant language
- C++
- Stars
- 14k
- Forks
- 2.6k
- Avg merge
- 5d 18h
- Merged PRs (30d)
- 6
Description
### Checklist
- [x] I have searched for [similar issues](https://github.com/isl-org/Open3D/issues).
- [x] For Python issues, I have tested with the [latest development wheel](https://www.open3d.org/docs/latest/getting_started.html#development-version-pip).
- [x] I have checked the [release documentation](https://www.open3d.org/docs/release/) and the [latest documentation](https://www.open3d.org/docs/latest/) (for `main` branch).
### Describe the issue
The result in this Python demo for the global registration step cannot be deterministically reproduced:
https://www.open3d.org/docs/latest/tutorial/pipelines/global_registration.html
This is after setting the global random seed, as shown in PR https://github.com/isl-org/Open3D/pull/5247
```
o3d.utility.random.seed(0)
```
A user in these issues suggests that setting this global random seed works, but it does not for me.
- https://github.com/isl-org/Open3D/issues/6225
- https://github.com/isl-org/Open3D/issues/7098
The example script provided runs the global registration with RANSAC function twice, setting the global seed before each call. However, the results are different. Even when running the script twice, the first result is different each time. It doesn't seem to matter if I set the global random seed at the top of the file or right before the global registration function is called.
### Steps to reproduce the bug
```python
import numpy as np
import open3d as o3d
def preprocess_point_cloud(pcd, voxel_size):
print(":: Downsample with a voxel size %.3f." % voxel_size)
pcd_down = pcd.voxel_down_sample(voxel_size)
radius_normal = voxel_size * 2
print(":: Estimate normal with search radius %.3f." % radius_normal)
pcd_down.estimate_normals(
o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30)
)
radius_feature = voxel_size * 5
print(":: Compute FPFH feature with search radius %.3f." % radius_feature)
pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(
pcd_down,
o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature, max_nn=100),
)
return pcd_down, pcd_fpfh
def prepare_dataset(voxel_size):
print(":: Load two point clouds and disturb initial pose.")
demo_icp_pcds = o3d.data.DemoICPPointClouds()
source = o3d.io.read_point_cloud(demo_icp_pcds.paths[0])
target = o3d.io.read_point_cloud(demo_icp_pcds.paths[1])
trans_init = np.asarray(
[
[0.0, 0.0, 1.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
)
source.transform(trans_init)
source_down, source_fpfh = preprocess_point_cloud(source, voxel_size)
target_down, target_fpfh = preprocess_point_cloud(target, voxel_size)
return source, target, source_down, target_down, source_fpfh, target_fpfh
def execute_global_registration(
source_down, target_down, source_fpfh, target_fpfh, voxel_size
):
distance_threshold = voxel_size * 1.5
print(":: RANSAC registration on downsampled point clouds.")
print(" Since the downsampling voxel size is %.3f," % voxel_size)
print(" we use a liberal distance threshold %.3f." % distance_threshold)
result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(
source_down,
target_down,
source_fpfh,
target_fpfh,
True,
distance_threshold,
o3d.pipelines.registration.TransformationEstimationPointToPoint(False),
3,
[
o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),
o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(
distance_threshold
),
],
o3d.pipelines.registration.RANSACConvergenceCriteria(100000, 0.999),
)
return result
if __name__ == "__main__":
voxel_size = 0.05 # means 5cm for this dataset
source, target, source_down, target_down, source_fpfh, target_fpfh = (
prepare_dataset(voxel_size)
)
o3d.utility.random.seed(0)
result_ransac = execute_global_registration(
source_down, target_down, source_fpfh, target_fpfh, voxel_size
)
print(result_ransac)
print(result_ransac.transformation)
o3d.utility.random.seed(0)
result_ransac = execute_global_registration(
source_down, target_down, source_fpfh, target_fpfh, voxel_size
)
print(result_ransac)
print(result_ransac.transformation)
```
### Error message
```
:: Load two point clouds and disturb initial pose.
:: Downsample with a voxel size 0.050.
:: Estimate normal with search radius 0.100.
:: Compute FPFH feature with search radius 0.250.
:: Downsample with a voxel size 0.050.
:: Estimate normal with search radius 0.100.
:: Compute FPFH feature with search radius 0.250.
:: RANSAC registration on downsampled point clouds.
Since the downsampling voxel size is 0.050,
we use a liberal distance threshold 0.075.
RegistrationResult with fitness=6.771008e-01, inlier_rmse=3.694170e-02, and correspondence_set size of 3223
Access transformation to get result.
[[-0.52908008 0.84837802 0.01813838 0.60695513]
[-0.22360642 -0.16000528 0.96145644 0.86199177]
[ 0.81858075 0.50463159 0.27435838 -1.42571538]
[ 0. 0. 0. 1. ]]
:: RANSAC registration on downsampled point clouds.
Since the downsampling voxel size is 0.050,
we use a liberal distance threshold 0.075.
RegistrationResult with fitness=6.747899e-01, inlier_rmse=3.047766e-02, and correspondence_set size of 3212
Access transformation to get result.
[[-0.53022708 0.84741436 0.02735227 0.5848452 ]
[-0.19899316 -0.15573983 0.96754681 0.77459227]
[ 0.8241729 0.50757661 0.25120714 -1.44945346]
[ 0. 0. 0. 1. ]]
```
### Expected behavior
Running the global registration RANSAC code when setting the same global seed should have the same result
### Open3D, Python and System information
```markdown
- Operating system: Ubuntu 24.04
- Python version: Python 3.10.19
- Open3D version: 0.19.0
- System architecture: x86
- Is this a remote workstation?: yes
- How did you install Open3D?: pip
- Compiler version (if built from source): N/A
```
### Additional information
_No response_
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.