solve-rigid-point-set-rt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solve_rigid_point_set_rt-0.1.0/LICENSE +21 -0
- solve_rigid_point_set_rt-0.1.0/PKG-INFO +69 -0
- solve_rigid_point_set_rt-0.1.0/README.md +49 -0
- solve_rigid_point_set_rt-0.1.0/pyproject.toml +18 -0
- solve_rigid_point_set_rt-0.1.0/solve_rigid_point_set_rt/__init__.py +9 -0
- solve_rigid_point_set_rt-0.1.0/solve_rigid_point_set_rt/main.py +55 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GGN_2015
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: solve-rigid-point-set-rt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Solve the transform vector and rotation matrix of a rigid point set.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: GGN_2015
|
|
8
|
+
Author-email: neko@jlulug.org
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Dist: numpy
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# solve_rigid_point_set_rt
|
|
21
|
+
Solve the transform vector and rotation matrix of a rigid point set.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install solve_rigid_point_set_rt
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from solve_rigid_point_set_rt import compute_best_rigid_transform
|
|
33
|
+
from solve_rigid_point_set_rt import apply_transform
|
|
34
|
+
from solve_rigid_point_set_rt import calculate_error
|
|
35
|
+
import numpy as np
|
|
36
|
+
|
|
37
|
+
P = np.array([
|
|
38
|
+
[ 20.606943, -0.005458, 0.040331],
|
|
39
|
+
[ -4.885081, -26.211561, -0.023027],
|
|
40
|
+
[ 12.012958, 20.049110, -0.035819],
|
|
41
|
+
[-27.734814, 6.167912, 0.018507]
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
Q = np.array([
|
|
45
|
+
[-132.94031926182967, 137.4289669332182, 782.6069181107938],
|
|
46
|
+
[-164.12739196778332, 120.93405919807722, 791.4971644346351],
|
|
47
|
+
[-115.37230320753432, 126.89401508498568, 790.1637162115628],
|
|
48
|
+
[-137.8636773243661, 96.44871350623225, 808.1944560570211]
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
# 2. Compute optimal rotation and translation
|
|
52
|
+
R_est, t_est = compute_best_rigid_transform(P, Q)
|
|
53
|
+
|
|
54
|
+
# 3. Apply transformation and calculate error
|
|
55
|
+
P_transformed = apply_transform(P, R_est, t_est)
|
|
56
|
+
rmse, mean_dist, _ = calculate_error(P_transformed, Q)
|
|
57
|
+
|
|
58
|
+
# 4. Print results
|
|
59
|
+
print("="*50)
|
|
60
|
+
print("Optimal Rotation Matrix R:")
|
|
61
|
+
print(R_est)
|
|
62
|
+
print("\nOptimal Translation Vector t:")
|
|
63
|
+
print(t_est)
|
|
64
|
+
print("\nRegistration Accuracy:")
|
|
65
|
+
print(f"Root Mean Square Error (RMSE): {rmse:.6f}")
|
|
66
|
+
print(f"Mean Euclidean Distance: {mean_dist:.6f}")
|
|
67
|
+
print("="*50)
|
|
68
|
+
```
|
|
69
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# solve_rigid_point_set_rt
|
|
2
|
+
Solve the transform vector and rotation matrix of a rigid point set.
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install solve_rigid_point_set_rt
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from solve_rigid_point_set_rt import compute_best_rigid_transform
|
|
14
|
+
from solve_rigid_point_set_rt import apply_transform
|
|
15
|
+
from solve_rigid_point_set_rt import calculate_error
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
P = np.array([
|
|
19
|
+
[ 20.606943, -0.005458, 0.040331],
|
|
20
|
+
[ -4.885081, -26.211561, -0.023027],
|
|
21
|
+
[ 12.012958, 20.049110, -0.035819],
|
|
22
|
+
[-27.734814, 6.167912, 0.018507]
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
Q = np.array([
|
|
26
|
+
[-132.94031926182967, 137.4289669332182, 782.6069181107938],
|
|
27
|
+
[-164.12739196778332, 120.93405919807722, 791.4971644346351],
|
|
28
|
+
[-115.37230320753432, 126.89401508498568, 790.1637162115628],
|
|
29
|
+
[-137.8636773243661, 96.44871350623225, 808.1944560570211]
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
# 2. Compute optimal rotation and translation
|
|
33
|
+
R_est, t_est = compute_best_rigid_transform(P, Q)
|
|
34
|
+
|
|
35
|
+
# 3. Apply transformation and calculate error
|
|
36
|
+
P_transformed = apply_transform(P, R_est, t_est)
|
|
37
|
+
rmse, mean_dist, _ = calculate_error(P_transformed, Q)
|
|
38
|
+
|
|
39
|
+
# 4. Print results
|
|
40
|
+
print("="*50)
|
|
41
|
+
print("Optimal Rotation Matrix R:")
|
|
42
|
+
print(R_est)
|
|
43
|
+
print("\nOptimal Translation Vector t:")
|
|
44
|
+
print(t_est)
|
|
45
|
+
print("\nRegistration Accuracy:")
|
|
46
|
+
print(f"Root Mean Square Error (RMSE): {rmse:.6f}")
|
|
47
|
+
print(f"Mean Euclidean Distance: {mean_dist:.6f}")
|
|
48
|
+
print("="*50)
|
|
49
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "solve-rigid-point-set-rt"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Solve the transform vector and rotation matrix of a rigid point set."
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "GGN_2015",email = "neko@jlulug.org"}
|
|
7
|
+
]
|
|
8
|
+
license = {text = "MIT"}
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"numpy"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
18
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def compute_best_rigid_transform(P: np.ndarray, Q: np.ndarray, assume_p_center_zero:bool=False):
|
|
4
|
+
"""
|
|
5
|
+
Compute the optimal 3D rotation matrix R and translation vector t
|
|
6
|
+
that satisfies: Q = R @ P + t (minimum mean square error)
|
|
7
|
+
Parameters:
|
|
8
|
+
P: Source point set, shape (N, 3), N is the number of points
|
|
9
|
+
Q: Target point set, shape (N, 3), in one-to-one correspondence with P
|
|
10
|
+
Returns:
|
|
11
|
+
R: 3x3 optimal rotation matrix
|
|
12
|
+
t: 3x1 optimal translation vector
|
|
13
|
+
"""
|
|
14
|
+
# 1. Validate input
|
|
15
|
+
assert P.shape == Q.shape, "The number and dimensions of the two point sets must be identical!"
|
|
16
|
+
assert P.shape[1] == 3, "Must be 3D point coordinates (x,y,z)"
|
|
17
|
+
|
|
18
|
+
# 2. Compute centroids of the two point sets
|
|
19
|
+
if assume_p_center_zero:
|
|
20
|
+
centroid_P = np.array([0, 0, 0])
|
|
21
|
+
else:
|
|
22
|
+
centroid_P = np.mean(P, axis=0)
|
|
23
|
+
centroid_Q = np.mean(Q, axis=0)
|
|
24
|
+
|
|
25
|
+
# 3. Center the points (subtract centroids to eliminate translation effect)
|
|
26
|
+
P_centered = P - centroid_P
|
|
27
|
+
Q_centered = Q - centroid_Q
|
|
28
|
+
|
|
29
|
+
# 4. Compute covariance matrix H (3x3)
|
|
30
|
+
H = P_centered.T @ Q_centered
|
|
31
|
+
|
|
32
|
+
# 5. Perform SVD to solve for optimal rotation
|
|
33
|
+
U, S, Vt = np.linalg.svd(H)
|
|
34
|
+
R = Vt.T @ U.T
|
|
35
|
+
|
|
36
|
+
# Correction: Ensure rotation matrix is right-handed (avoid reflection transformation)
|
|
37
|
+
if np.linalg.det(R) < 0:
|
|
38
|
+
Vt[-1, :] *= -1
|
|
39
|
+
R = Vt.T @ U.T
|
|
40
|
+
|
|
41
|
+
# 6. Compute optimal translation vector
|
|
42
|
+
t = centroid_Q - R @ centroid_P
|
|
43
|
+
|
|
44
|
+
return R, t
|
|
45
|
+
|
|
46
|
+
def apply_transform(P: np.ndarray, R: np.ndarray, t: np.ndarray):
|
|
47
|
+
"""Apply rotation and translation to the source point set to get transformed points"""
|
|
48
|
+
return (R @ P.T).T + t
|
|
49
|
+
|
|
50
|
+
def calculate_error(transformed_P: np.ndarray, Q: np.ndarray):
|
|
51
|
+
"""Calculate Root Mean Square Error (RMSE) and mean distance to evaluate registration accuracy"""
|
|
52
|
+
errors = np.linalg.norm(transformed_P - Q, axis=1) # Euclidean distance for each point
|
|
53
|
+
rmse = np.sqrt(np.mean(errors ** 2))
|
|
54
|
+
mean_dist = np.mean(errors)
|
|
55
|
+
return rmse, mean_dist, errors
|