Skip to content

Instantly share code, notes, and snippets.

@mattak
Last active July 22, 2025 06:18
Show Gist options
  • Save mattak/6f2d7179fb54c90805bb5769e5821406 to your computer and use it in GitHub Desktop.
Save mattak/6f2d7179fb54c90805bb5769e5821406 to your computer and use it in GitHub Desktop.
import numpy as np
import pandas as pd
import argparse
def load_points_from_tsv(path: str) -> tuple[np.ndarray, np.ndarray]:
df = pd.read_csv(path, sep='\t')
if not all(col in df.columns for col in ['ID', 'position.x', 'position.y', 'position.z']):
raise ValueError("必要な列 'ID', 'position.x', 'position.y', 'position.z' が見つかりません")
ids = df['ID'].values
points = df[['position.x', 'position.y', 'position.z']].values
return ids, points
def apply_transform(points: np.ndarray, transform: np.ndarray) -> np.ndarray:
is_single = points.ndim == 1
if is_single:
points = points[np.newaxis, :]
ones = np.ones((points.shape[0], 1))
points_h = np.hstack([points, ones])
transformed_h = (transform @ points_h.T).T
transformed = transformed_h[:, :3]
return transformed[0] if is_single else transformed
def main():
parser = argparse.ArgumentParser(description="TSVから3D座標を読み込み、変換行列を適用して出力する")
parser.add_argument("input_path", help="入力TSVファイルのパス(ID, position.x, position.y, position.z列が必要)")
args = parser.parse_args()
# 変換行列(必要に応じて変更してください)
transform = np.array([
[ 0.989162 -0.018384 0.145673 -0.038322]
[ 0.00832 0.997555 0.069393 -0.028196]
[-0.146593 -0.067429 0.986896 -0.043362]
[ 0. 0. 0. 1. ]
])
ids, points = load_points_from_tsv(args.input_path)
transformed = apply_transform(points, transform)
print("ID\tx\ty\tz")
for i, p in zip(ids, transformed):
print(f"{i}\t{p[0]:.6f}\t{p[1]:.6f}\t{p[2]:.6f}")
if __name__ == "__main__":
main()
import numpy as np
import pandas as pd
def load_points_from_tsv(path: str) -> np.ndarray:
df = pd.read_csv(path, sep='\t')
if not all(col in df.columns for col in ['position.x', 'position.y', 'position.z']):
raise ValueError("TSVファイルに 'position.x', 'position.y', 'position.z' の列が必要です")
return df[['position.x', 'position.y', 'position.z']].values
def compute_rigid_transform(src_points: np.ndarray, target_points: np.ndarray) -> np.ndarray:
assert src_points.shape == target_points.shape
centroid_src = np.mean(src_points, axis=0)
centroid_tgt = np.mean(target_points, axis=0)
src_centered = src_points - centroid_src
tgt_centered = target_points - centroid_tgt
H = src_centered.T @ tgt_centered
U, S, Vt = np.linalg.svd(H)
R = Vt.T @ U.T
if np.linalg.det(R) < 0:
Vt[2, :] *= -1
R = Vt.T @ U.T
t = centroid_tgt - R @ centroid_src
transform = np.eye(4)
transform[:3, :3] = R
transform[:3, 3] = t
return transform
def apply_transform(points: np.ndarray, transform: np.ndarray) -> np.ndarray:
ones = np.ones((points.shape[0], 1))
points_h = np.hstack([points, ones]) # (N, 4)
transformed_h = (transform @ points_h.T).T # (N, 4)
return transformed_h[:, :3] # (N, 3)
def compute_error(transformed_src: np.ndarray, target: np.ndarray) -> tuple[float, float]:
diffs = transformed_src - target
dists = np.linalg.norm(diffs, axis=1)
rms_error = np.sqrt(np.mean(dists ** 2))
max_error = np.max(dists)
return rms_error, max_error
def main():
src_path = "src_points.tsv"
tgt_path = "target_points.tsv"
src_points = load_points_from_tsv(src_path)
tgt_points = load_points_from_tsv(tgt_path)
transform = compute_rigid_transform(src_points, tgt_points)
np.set_printoptions(precision=6, suppress=True)
print("変換行列(4x4):")
print(transform)
transformed_src = apply_transform(src_points, transform)
rms, max_err = compute_error(transformed_src, tgt_points)
print(f"\n▶ RMS誤差: {rms:.6f} m")
print(f"▶ 最大誤差: {max_err:.6f} m")
if __name__ == "__main__":
main()
ID position.x position.y position.z
0 0.0304 0.1343 -0.4487
1 0.08 0.1335 -0.441
2 0.13 0.1331 -0.4346
3 0.1798 0.1322 -0.4269
4 0.0299 0.0844 -0.4525
5 0.0795 0.0836 -0.4449
6 0.1298 0.0831 -0.4391
7 0.1793 0.0822 -0.4304
8 0.0296 0.0343 -0.4562
9 0.0791 0.0335 -0.4477
10 0.1292 0.0328 -0.4416
11 0.1793 0.0321 -0.4345
12 0.0294 -0.0158 -0.4612
13 0.0788 -0.0165 -0.451
14 0.1286 -0.0172 -0.4438
15 0.1787 -0.018 -0.4372
ID position.x position.y position.z
0 -0.075 0.075 -0.5
1 -0.025 0.075 -0.5
2 0.025 0.075 -0.5
3 0.075 0.075 -0.5
4 -0.075 0.025 -0.5
5 -0.035 0.025 -0.5
6 0.025 0.025 -0.5
7 0.075 0.025 -0.5
8 -0.075 -0.025 -0.5
9 -0.025 -0.025 -0.5
10 0.025 -0.025 -0.5
11 0.075 -0.025 -0.5
12 -0.075 -0.075 -0.5
13 -0.025 -0.075 -0.5
14 0.025 -0.075 -0.5
15 0.075 -0.075 -0.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment