From ade18bf25b4a2800041a1c38cb5911f95373ec2a Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Thu, 28 May 2026 23:20:17 +0200 Subject: [PATCH] weight apply = 0 running --- pipeline/2_Multiview.py | 15 +- pipeline/2_Multiview_ersteVersion.py | 706 ++++++ pipeline/2_Multiview_neu.py | 1158 +++++++++ pipeline/2_Multiview_ohne_confidence.py | 971 ++++++++ .../__pycache__/2_Multiview.cpython-311.pyc | Bin 64548 -> 76709 bytes pipeline/multiview_pose.json | 1252 +++++----- pipeline/multiview_pose_Weighted.json | 966 ++++++++ pipeline/multiview_pose_neu.json | 2144 +++++++++++++++++ pipeline/multiview_pose_ohne_confidence.json | 966 ++++++++ pipeline/multiview_pose_summary.json | 72 + pipeline/render_1a_aruco_detection.json | 36 +- pipeline/render_1b_aruco_detection.json | 22 +- pipeline/render_1c_aruco_detection.json | 16 +- pipeline/run_pipeline.bat | 2 +- 14 files changed, 7656 insertions(+), 670 deletions(-) create mode 100644 pipeline/2_Multiview_ersteVersion.py create mode 100644 pipeline/2_Multiview_neu.py create mode 100644 pipeline/2_Multiview_ohne_confidence.py create mode 100644 pipeline/multiview_pose_Weighted.json create mode 100644 pipeline/multiview_pose_neu.json create mode 100644 pipeline/multiview_pose_ohne_confidence.json create mode 100644 pipeline/multiview_pose_summary.json diff --git a/pipeline/2_Multiview.py b/pipeline/2_Multiview.py index 712a981..84ea3b5 100644 --- a/pipeline/2_Multiview.py +++ b/pipeline/2_Multiview.py @@ -149,9 +149,9 @@ class ObservationQualityConfig: sharpness_ref: float = 2500.0 homography_ref: float = 0.18 - # factor f in min(1, q + f) - # f = 1 -> factor disabled / neutral - # f = 0 -> factor fully active + # factor f scales the effect of each quality indicator: + # f = 1 -> indicator is fully active + # f = 0 -> indicator is ignored (neutral weight = 1) size_factor: float = 1.0 aspect_factor: float = 1.0 border_factor: float = 1.0 @@ -341,9 +341,12 @@ def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[s # ----------------------------------------------------------------------------- def quality_factor(q: float, f: float) -> float: - # Saturating factor in [0, 1]. - # f = 1.0 -> ignore this q-indicator completely. - return clamp01(q + f) + # Interpret f in [0, 1] as the per-indicator influence. + # f = 1.0 -> indicator is fully active; q is applied. + # f = 0.0 -> indicator is ignored and contributes a neutral weight of 1.0. + q = clamp01(q) + f = clamp01(f) + return 1.0 - f + f * q def projective_homography_quality(image_points_px: np.ndarray, image_shape: Tuple[int, int], ref: float) -> float: diff --git a/pipeline/2_Multiview_ersteVersion.py b/pipeline/2_Multiview_ersteVersion.py new file mode 100644 index 0000000..59c3b1e --- /dev/null +++ b/pipeline/2_Multiview_ersteVersion.py @@ -0,0 +1,706 @@ +#!/usr/bin/env python3 + +""" +============================================================ +STEP 2b — Simultane Multiview-Optimierung für Roboterpose +============================================================ + +Ziel: + Aus mehreren ArUco-Detektionsdateien die gemeinsame + Roboterpose (x,y,z,a,b,c,e) schätzen und jede Kamera-Pose + sowie Marker-Weltpositionen ausgeben. + +Eingabe: + --robot ../robot.json + --detections render_1a_aruco_detection.json render_1b_aruco_detection.json ... + --outDir . + +Ausgabe: + multiview_pose.json + +Hinweis: + Dieses Skript verwendet die Markerpositionen aus robot.json + als kinematische Constraints und optimiert gleichzeitig: + - Roboterzustand (x,y,z,a,b,c,e) + - Kameraextrinsische Parameter pro Bild + +""" + +import argparse +import datetime +import json +import math +import os +import time +from pathlib import Path +from typing import Any, Dict, List, Tuple + +import cv2 +import numpy as np +from scipy.optimize import least_squares + +STATE_KEYS = ["x", "y", "z", "a", "b", "c", "e"] + + +# ------------------------------------------------------------------ +# JSON helpers +# ------------------------------------------------------------------ + +def load_json(path: str) -> Dict[str, Any]: + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + + +def save_json(data: Dict[str, Any], path: Path) -> None: + with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=2) + + +# ------------------------------------------------------------------ +# robot.json helpers +# ------------------------------------------------------------------ + +def resolve_scalar(value: Any, default: float = 0.0) -> float: + if value is None: + return default + if isinstance(value, (int, float)): + return float(value) + try: + return float(str(value).strip()) + except ValueError: + return default + + +def resolve_vector(value: Any, default_len: int = 3) -> Tuple[float, ...]: + if value is None: + return tuple(0.0 for _ in range(default_len)) + if isinstance(value, (int, float, str)): + return (resolve_scalar(value),) + tuple(0.0 for _ in range(default_len - 1)) + if isinstance(value, (list, tuple)): + resolved = [resolve_scalar(v) for v in value] + if len(resolved) < default_len: + resolved.extend([0.0] * (default_len - len(resolved))) + return tuple(resolved[:default_len]) + return tuple(0.0 for _ in range(default_len)) + + +def parse_metric_scale(robot: Dict[str, Any]) -> float: + rendering_info = robot.get('renderingInfo', {}) or {} + metric = rendering_info.get('metric', 'mm') + return 0.001 if str(metric).strip().lower() == 'mm' else 1.0 + + +def normalize_axis(axis: Any) -> np.ndarray: + vec = np.asarray(axis, dtype=np.float64) + if vec.shape != (3,): + vec = vec.reshape(-1)[:3] + norm = np.linalg.norm(vec) + return vec / max(norm, 1e-9) + + +def euler_deg_to_matrix(euler_deg: Any) -> np.ndarray: + x_deg, y_deg, z_deg = resolve_vector(euler_deg, 3) + x = math.radians(x_deg) + y = math.radians(y_deg) + z = math.radians(z_deg) + + cx = math.cos(x) + sx = math.sin(x) + cy = math.cos(y) + sy = math.sin(y) + cz = math.cos(z) + sz = math.sin(z) + + Rx = np.array([ + [1.0, 0.0, 0.0], + [0.0, cx, -sx], + [0.0, sx, cx] + ], dtype=np.float64) + + Ry = np.array([ + [cy, 0.0, sy], + [0.0, 1.0, 0.0], + [-sy, 0.0, cy] + ], dtype=np.float64) + + Rz = np.array([ + [cz, -sz, 0.0], + [sz, cz, 0.0], + [0.0, 0.0, 1.0] + ], dtype=np.float64) + + return Rz @ Ry @ Rx + + +def transform_from_translation_rotation(translation: Any, rotation_deg: Any) -> np.ndarray: + T = np.eye(4, dtype=np.float64) + pos = np.asarray(resolve_vector(translation, 3), dtype=np.float64) + T[:3, 3] = pos + T[:3, :3] = euler_deg_to_matrix(rotation_deg) + return T + + +def axis_angle_matrix(axis: Any, angle_deg: float) -> np.ndarray: + axis_vec = normalize_axis(axis) + theta = math.radians(angle_deg) + kx, ky, kz = axis_vec + c = math.cos(theta) + s = math.sin(theta) + v = 1.0 - c + R = np.array([ + [kx * kx * v + c, kx * ky * v - kz * s, kx * kz * v + ky * s], + [ky * kx * v + kz * s, ky * ky * v + c, ky * kz * v - kx * s], + [kz * kx * v - ky * s, kz * ky * v + kx * s, kz * kz * v + c] + ], dtype=np.float64) + T = np.eye(4, dtype=np.float64) + T[:3, :3] = R + return T + + +# ------------------------------------------------------------------ +# Kinematics and marker extraction +# ------------------------------------------------------------------ + +def extract_markers(robot: Dict[str, Any], scale: float) -> Dict[int, Dict[str, Any]]: + markers = {} + links = robot.get('links', {}) or {} + marker_defaults = (robot.get('renderingInfo', {}) or {}).get('markerDefaults', {}) or {} + default_size_mm = float(marker_defaults.get('size', 25.0)) + + for link_name, link_info in links.items(): + for marker in link_info.get('markers', []) or []: + marker_id = int(marker.get('id', -1)) + if marker_id < 0: + continue + + pos = resolve_vector(marker.get('position', [0, 0, 0]), 3) + size_mm = float(marker.get('size', default_size_mm)) + markers[marker_id] = { + 'marker_id': marker_id, + 'link_name': link_name, + 'position_m': np.asarray([pos[0] * scale, pos[1] * scale, pos[2] * scale], dtype=np.float64), + 'normal': normalize_axis(resolve_vector(marker.get('normal', [0, 0, 1]), 3)), + 'spin_deg': float(marker.get('spin', 0.0)), + 'size_m': size_mm * scale, + } + + return markers + + +def marker_plane_axes(normal: np.ndarray, spin_deg: float) -> Tuple[np.ndarray, np.ndarray]: + n = normalize_axis(normal) + candidate = np.array((0.0, 0.0, 1.0), dtype=np.float64) + if abs(np.dot(n, candidate)) > 0.99: + candidate = np.array((1.0, 0.0, 0.0), dtype=np.float64) + + x_dir = np.cross(candidate, n) + x_dir /= max(np.linalg.norm(x_dir), 1e-9) + y_dir = np.cross(n, x_dir) + + if abs(spin_deg) > 1e-6: + theta = math.radians(spin_deg) + cos_t = math.cos(theta) + sin_t = math.sin(theta) + x_rot = x_dir * cos_t + np.cross(n, x_dir) * sin_t + n * np.dot(n, x_dir) * (1.0 - cos_t) + y_rot = y_dir * cos_t + np.cross(n, y_dir) * sin_t + n * np.dot(n, y_dir) * (1.0 - cos_t) + return x_rot, y_rot + + return x_dir, y_dir + + +def marker_object_corners(marker: Dict[str, Any]) -> np.ndarray: + half = marker['size_m'] * 0.5 + x_dir, y_dir = marker_plane_axes(marker['normal'], marker['spin_deg']) + corners = np.stack([ + -x_dir * half + y_dir * half, + x_dir * half + y_dir * half, + x_dir * half - y_dir * half, + -x_dir * half - y_dir * half + ], axis=0) + return marker['position_m'].reshape(1, 3) + corners + + +def build_link_chain(robot: Dict[str, Any]) -> List[str]: + links = robot.get('links', {}) or {} + ordered: List[str] = [] + remaining = set(links.keys()) + + while remaining: + progress = False + for name in list(remaining): + parent = links[name].get('parent') + if not parent or parent in ordered: + ordered.append(name) + remaining.remove(name) + progress = True + if not progress: + raise RuntimeError('Cycle detected in robot link tree or missing parent link') + return ordered + + +def compute_link_transforms(robot: Dict[str, Any], state: Dict[str, float], scale: float) -> Dict[str, np.ndarray]: + links = robot.get('links', {}) or {} + ordered_links = build_link_chain(robot) + transforms: Dict[str, np.ndarray] = {} + + for link_name in ordered_links: + link_info = links[link_name] or {} + parent_name = link_info.get('parent') + parent_transform = transforms[parent_name] if parent_name else np.eye(4, dtype=np.float64) + + mount_translation = np.asarray(resolve_vector(link_info.get('mountPosition', [0, 0, 0]), 3), dtype=np.float64) * scale + mount = transform_from_translation_rotation( + mount_translation, + link_info.get('mountRotation', [0, 0, 0]) + ) + + joint_info = link_info.get('jointToParent', {}) or {} + joint_origin = np.asarray(resolve_vector(joint_info.get('origin', [0, 0, 0]), 3), dtype=np.float64) * scale + joint = transform_from_translation_rotation( + joint_origin, + joint_info.get('rotation', [0, 0, 0]) + ) + + motion = np.eye(4, dtype=np.float64) + joint_type = str(joint_info.get('type', 'fixed')).strip().lower() + control_var = str(joint_info.get('variable', joint_info.get('control', ''))).strip().lower() + axis = resolve_vector(joint_info.get('axis', [1, 0, 0]), 3) + + if joint_type == 'linear': + motion[:3, 3] = normalize_axis(axis) * state.get(control_var, 0.0) * scale + elif joint_type == 'revolute': + motion = axis_angle_matrix(axis, state.get(control_var, 0.0)) + + transforms[link_name] = parent_transform @ mount @ joint @ motion + + return transforms + + +def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + link_transform = link_transforms[marker['link_name']] + local = np.ones(4, dtype=np.float64) + local[:3] = marker['position_m'] + world = link_transform @ local + return world[:3] + + +# ------------------------------------------------------------------ +# Camera / observation helpers +# ------------------------------------------------------------------ + +def load_intrinsics(detection_json: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]: + cam = detection_json['camera'] + K = np.asarray(cam['camera_matrix'], dtype=np.float64) + D = np.asarray(cam.get('distortion_coefficients', [0, 0, 0, 0, 0]), dtype=np.float64).reshape(-1, 1) + return K, D + + +def collect_views_and_observations( + detection_files: List[str], + robot_markers: Dict[int, Dict[str, Any]] +) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: + views: List[Dict[str, Any]] = [] + observations: List[Dict[str, Any]] = [] + + for idx, det_path in enumerate(detection_files): + detection_json = load_json(det_path) + K, D = load_intrinsics(detection_json) + views.append({ + 'index': idx, + 'source_file': os.path.abspath(det_path), + 'camera_id': detection_json.get('camera', {}).get('camera_id', f'cam{idx+1}'), + 'image_file': detection_json.get('image', {}).get('image_file'), + 'K': K, + 'D': D + }) + + for det in detection_json.get('detections', []) or []: + marker_id = int(det.get('marker_id', -1)) + if marker_id < 0 or marker_id not in robot_markers: + continue + + image_points = det.get('image_points_px') + if isinstance(image_points, list) and len(image_points) == 4: + image_points = np.asarray(image_points, dtype=np.float64) + else: + center = resolve_vector(det.get('center_px', [0, 0]), 2) + image_points = np.asarray([center], dtype=np.float64) + + confidence = float(det.get('confidence', 1.0)) + marker = robot_markers[marker_id] + observations.append({ + 'view_index': idx, + 'marker_id': marker_id, + 'marker_link_corners': marker_object_corners(marker), + 'image_points_px': image_points, + 'confidence': max(0.01, min(1.0, confidence)) + }) + + if len(views) == 0: + raise RuntimeError('No valid detection views found') + + if len(observations) == 0: + raise RuntimeError('No marker observations matched robot.json markers') + + return views, observations + + +def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + link_transform = link_transforms[marker['link_name']] + local = marker_object_corners(marker) + homogeneous = np.concatenate([local, np.ones((local.shape[0], 1), dtype=np.float64)], axis=1) + world = (link_transform @ homogeneous.T).T + return world[:, :3] + + +def initial_camera_guess( + view: Dict[str, Any], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + default_state: Dict[str, float], + scale: float, + robot: Dict[str, Any] +) -> Tuple[np.ndarray, np.ndarray]: + object_points = [] + image_points = [] + + link_transforms = compute_link_transforms(robot, default_state, scale) + + for obs in observations: + if obs['view_index'] != view['index']: + continue + marker = robot_markers[obs['marker_id']] + object_points.append(compute_marker_world_corners(marker, link_transforms)) + image_points.append(obs['image_points_px']) + + if len(object_points) == 0: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + object_points = np.vstack(object_points) + image_points = np.vstack(image_points) + + if object_points.shape[0] < 4: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + success, rvec, tvec = cv2.solvePnP( + object_points, + image_points, + view['K'], + view['D'], + flags=cv2.SOLVEPNP_ITERATIVE + ) + + if not success: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + return rvec, tvec + + +def project_points( + points_3d: np.ndarray, + rvec: np.ndarray, + tvec: np.ndarray, + K: np.ndarray, + D: np.ndarray +) -> np.ndarray: + projected, _ = cv2.projectPoints(points_3d, rvec, tvec, K, D) + return projected.reshape(-1, 2) + + +# ------------------------------------------------------------------ +# Optimization +# ------------------------------------------------------------------ + +def pack_parameters(robot_state: Dict[str, float], camera_params: List[Tuple[np.ndarray, np.ndarray]]) -> np.ndarray: + state_vec = np.asarray([robot_state[k] for k in STATE_KEYS], dtype=np.float64) + cams = [] + for rvec, tvec in camera_params: + cams.append(rvec.reshape(3)) + cams.append(tvec.reshape(3)) + return np.concatenate([state_vec] + cams) + + +def unpack_parameters(params: np.ndarray, n_views: int) -> Tuple[Dict[str, float], List[Tuple[np.ndarray, np.ndarray]]]: + robot_state = {STATE_KEYS[i]: float(params[i]) for i in range(len(STATE_KEYS))} + camera_params = [] + offset = len(STATE_KEYS) + for _ in range(n_views): + rvec = params[offset:offset + 3].reshape(3, 1) + tvec = params[offset + 3:offset + 6].reshape(3, 1) + camera_params.append((rvec, tvec)) + offset += 6 + return robot_state, camera_params + + +def residuals_for_parameters( + params: np.ndarray, + views: List[Dict[str, Any]], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + robot: Dict[str, Any], + scale: float, + default_state: Dict[str, float] +) -> np.ndarray: + robot_state, camera_params = unpack_parameters(params, len(views)) + link_transforms = compute_link_transforms(robot, robot_state, scale) + + residuals = [] + for obs in observations: + marker = robot_markers[obs['marker_id']] + world_corners = compute_marker_world_corners(marker, link_transforms) + rvec, tvec = camera_params[obs['view_index']] + proj = project_points(world_corners, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D']) + diffs = proj - obs['image_points_px'] + weight = math.sqrt(obs['confidence']) + residuals.extend((diffs * weight).reshape(-1)) + + for key in STATE_KEYS: + diff = robot_state[key] - default_state.get(key, 0.0) + if key in ('x', 'y', 'z', 'e'): + w = 0.001 + else: + w = 0.01 + residuals.append(diff * w) + + return np.asarray(residuals, dtype=np.float64) + + +def estimate_uncertainty(result: Any, n_params: int) -> np.ndarray: + if result.jac is None: + return np.full(n_params, float('nan'), dtype=np.float64) + J = result.jac + m, n = J.shape + JTJ = J.T @ J + try: + cov = np.linalg.pinv(JTJ) + except np.linalg.LinAlgError: + cov = np.linalg.pinv(JTJ + np.eye(n) * 1e-9) + residuals = result.fun + dof = max(1, m - n) + sigma2 = float(np.sum(residuals ** 2) / dof) + cov *= sigma2 + return np.sqrt(np.diag(cov)) + + +# ------------------------------------------------------------------ +# Output assembly +# ------------------------------------------------------------------ + +def camera_position_world(rvec: np.ndarray, tvec: np.ndarray) -> np.ndarray: + R, _ = cv2.Rodrigues(rvec) + return (-R.T @ tvec).reshape(3) + + +def build_output( + robot_state: Dict[str, float], + state_uncertainty: np.ndarray, + views: List[Dict[str, Any]], + camera_params: List[Tuple[np.ndarray, np.ndarray]], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + scale: float, + robot: Dict[str, Any], + robot_json_path: str +) -> Dict[str, Any]: + link_transforms = compute_link_transforms(robot, robot_state, scale) + + marker_summary: Dict[int, Dict[str, Any]] = {} + for marker_id, marker in robot_markers.items(): + marker_summary[marker_id] = { + 'marker_id': marker_id, + 'link_name': marker['link_name'], + 'position_world_m': compute_marker_world_position(marker, link_transforms).tolist(), + 'size_m': marker['size_m'], + 'observation_count': 0, + 'mean_confidence': None, + 'mean_reprojection_error_px': None, + 'observations': [] + } + + per_marker_errors: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + per_marker_confidences: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + + link_transforms = compute_link_transforms(robot, robot_state, scale) + for obs in observations: + marker_id = obs['marker_id'] + marker = robot_markers[marker_id] + object_points_m = compute_marker_world_corners(marker, link_transforms) + rvec, tvec = camera_params[obs['view_index']] + proj = project_points(object_points_m, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D']) + diffs = proj - obs['image_points_px'] + errors = np.linalg.norm(diffs, axis=1) + repro_error = float(np.mean(errors)) + per_marker_errors[marker_id].extend(errors.tolist()) + per_marker_confidences[marker_id].append(obs['confidence']) + marker_summary[marker_id]['observation_count'] += 1 + marker_summary[marker_id]['observations'].append({ + 'view_index': obs['view_index'], + 'source_file': views[obs['view_index']]['source_file'], + 'image_file': views[obs['view_index']]['image_file'], + 'confidence': obs['confidence'], + 'mean_reprojection_error_px': repro_error, + 'corner_reprojection_errors_px': errors.tolist() + }) + + for marker_id, summary in marker_summary.items(): + if summary['observation_count'] > 0: + summary['mean_confidence'] = float(np.mean(per_marker_confidences[marker_id])) + summary['mean_reprojection_error_px'] = float(np.mean(per_marker_errors[marker_id])) + + camera_outputs = [] + for idx, view in enumerate(views): + rvec, tvec = camera_params[idx] + cam_pos = camera_position_world(rvec, tvec) + observed_count = sum(1 for obs in observations if obs['view_index'] == idx) + camera_outputs.append({ + 'view_index': idx, + 'source_file': view['source_file'], + 'camera_id': view['camera_id'], + 'camera_position_world_m': cam_pos.tolist(), + 'rvec': rvec.reshape(-1).tolist(), + 'tvec': tvec.reshape(-1).tolist(), + 'intrinsics': { + 'camera_matrix': view['K'].tolist(), + 'distortion_coefficients': view['D'].reshape(-1).tolist() + }, + 'observation_count': observed_count + }) + + robot_pose_output = { + 'state': {k: float(robot_state[k]) for k in STATE_KEYS}, + 'uncertainty': { + 'x_mm': float(state_uncertainty[0]), + 'y_mm': float(state_uncertainty[1]), + 'z_mm': float(state_uncertainty[2]), + 'a_deg': float(state_uncertainty[3]), + 'b_deg': float(state_uncertainty[4]), + 'c_deg': float(state_uncertainty[5]), + 'e_mm': float(state_uncertainty[6]) + }, + 'confidence': { + 'x': float(math.exp(-state_uncertainty[0] / 10.0)), + 'y': float(math.exp(-state_uncertainty[1] / 10.0)), + 'z': float(math.exp(-state_uncertainty[2] / 10.0)), + 'a': float(math.exp(-state_uncertainty[3] / 10.0)), + 'b': float(math.exp(-state_uncertainty[4] / 10.0)), + 'c': float(math.exp(-state_uncertainty[5] / 10.0)), + 'e': float(math.exp(-state_uncertainty[6] / max(1.0, state_uncertainty[6]))) + } + } + + return { + 'schema_version': '1.0', + 'created_utc': datetime.datetime.utcnow().isoformat() + 'Z', + 'source_robot_json': os.path.abspath(robot_json_path), + 'source_detections': [view['source_file'] for view in views], + 'robot_pose': robot_pose_output, + 'camera_poses': camera_outputs, + 'marker_positions': list(marker_summary.values()) + } + + +# ------------------------------------------------------------------ +# Main +# ------------------------------------------------------------------ + +def main() -> None: + parser = argparse.ArgumentParser(description='Multiview optimization of robot pose and camera extrinsics') + parser.add_argument('--robot', required=True, help='Path to robot.json') + parser.add_argument('--detections', required=True, nargs='+', help='List of detection JSON files') + parser.add_argument('--outDir', required=True, help='Output directory') + parser.add_argument('--write-summary', action='store_true', help='Write summary file') + parser.add_argument('--max-iter', type=int, default=500, help='Maximum optimizer iterations') + args = parser.parse_args() + + os.makedirs(args.outDir, exist_ok=True) + + robot_json_path = os.path.abspath(args.robot) + robot = load_json(robot_json_path) + scale = parse_metric_scale(robot) + + default_state = { + k: float(robot.get('defaultPosition', {}).get(k, 0.0) or 0.0) + for k in STATE_KEYS + } + + robot_markers = extract_markers(robot, scale) + views, observations = collect_views_and_observations(args.detections, robot_markers) + + camera_guesses = [] + for view in views: + rvec, tvec = initial_camera_guess(view, observations, robot_markers, default_state, scale, robot) + camera_guesses.append((rvec, tvec)) + + x0 = pack_parameters(default_state, camera_guesses) + + progress = { + 'iter': 0, + 'last_cost': None, + 'last_print': time.time(), + 'prev_x': x0.copy() + } + + def progress_callback(xk: np.ndarray) -> None: + progress['iter'] += 1 + now = time.time() + if progress['iter'] == 1 or now - progress['last_print'] >= 1.0: + res = residuals_for_parameters(xk, views, observations, robot_markers, robot, scale, default_state) + cost = 0.5 * float(np.dot(res, res)) + delta_cost = None + convergence = '' + if progress['last_cost'] is not None: + delta_cost = cost - progress['last_cost'] + if abs(delta_cost) < 1e-3: + convergence = ' stable' + elif delta_cost < 0: + convergence = ' improving' + else: + convergence = ' worsening' + step_norm = float(np.linalg.norm(xk - progress['prev_x'])) + print( + f'[Multiview] iter={progress["iter"]:4d} cost={cost:.4f}' + + (f' delta={delta_cost:.4g}' if delta_cost is not None else '') + + f' step={step_norm:.4g}' + + convergence + ) + progress['last_cost'] = cost + progress['last_print'] = now + progress['prev_x'] = xk.copy() + + result = least_squares( + residuals_for_parameters, + x0, + args=(views, observations, robot_markers, robot, scale, default_state), + jac='2-point', + method='trf', + loss='soft_l1', + f_scale=1.0, + max_nfev=args.max_iter, + callback=progress_callback + ) + + robot_state, camera_params = unpack_parameters(result.x, len(views)) + uncertainties = estimate_uncertainty(result, len(result.x)) + + output = build_output(robot_state, uncertainties[:len(STATE_KEYS)], views, camera_params, observations, robot_markers, scale, robot, robot_json_path) + + out_path = Path(args.outDir) / 'multiview_pose.json' + save_json(output, out_path) + + print(f'Saved: {out_path}') + if args.write_summary: + summary_path = Path(args.outDir) / 'multiview_pose_summary.json' + summary = { + 'final_cost': float(result.cost), + 'status': int(result.status), + 'message': result.message, + 'robot_state': output['robot_pose'], + 'camera_count': len(views), + 'marker_count': len(robot_markers) + } + save_json(summary, summary_path) + print(f'Saved: {summary_path}') + + +if __name__ == '__main__': + main() diff --git a/pipeline/2_Multiview_neu.py b/pipeline/2_Multiview_neu.py new file mode 100644 index 0000000..84ea3b5 --- /dev/null +++ b/pipeline/2_Multiview_neu.py @@ -0,0 +1,1158 @@ +#!/usr/bin/env python3 +""" +Phase 1 — robust multiview robot pose estimation from aruco_detection.json + robot.json + +This version keeps the original kinematic model and optimizer structure, but changes: +- observation weighting to a saturating factor model: min(1, q + f) +- quality indicators are normalized to 0..1 +- blur/sharpness is supported but disabled by default (f=1) +- homography skew quality is added +- summary is built from the final output, so values stay consistent +- duplicate marker ids are warned about instead of being silently overwritten + +Input: + --robot robot.json + --detections render_1a_aruco_detection.json ... + --outDir output + +Output: + multiview_pose.json + multiview_pose_summary.json (optional) +""" + +import argparse +import datetime as _dt +import json +import math +import os +import time +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, List, Tuple, Optional + +import cv2 +import numpy as np +from scipy.optimize import least_squares + +STATE_KEYS = ["x", "y", "z", "a", "b", "c", "e"] + + +# ----------------------------------------------------------------------------- +# Small helpers +# ----------------------------------------------------------------------------- + +def clamp01(x: float) -> float: + return float(max(0.0, min(1.0, x))) + + +def load_json(path: str) -> Dict[str, Any]: + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def save_json(data: Dict[str, Any], path: Path) -> None: + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + +def resolve_scalar(value: Any, default: float = 0.0) -> float: + if value is None: + return default + if isinstance(value, (int, float)): + return float(value) + try: + return float(str(value).strip()) + except Exception: + return default + + +def resolve_vector(value: Any, default_len: int = 3) -> Tuple[float, ...]: + if value is None: + return tuple(0.0 for _ in range(default_len)) + if isinstance(value, (int, float, str)): + return (resolve_scalar(value),) + tuple(0.0 for _ in range(default_len - 1)) + if isinstance(value, (list, tuple)): + resolved = [resolve_scalar(v) for v in value] + if len(resolved) < default_len: + resolved.extend([0.0] * (default_len - len(resolved))) + return tuple(resolved[:default_len]) + return tuple(0.0 for _ in range(default_len)) + + +def parse_metric_scale(robot: Dict[str, Any]) -> float: + rendering_info = robot.get("renderingInfo", {}) or {} + metric = str(rendering_info.get("metric", "mm")).strip().lower() + return 0.001 if metric == "mm" else 1.0 + + +def normalize_axis(axis: Any) -> np.ndarray: + vec = np.asarray(axis, dtype=np.float64).reshape(-1)[:3] + norm = np.linalg.norm(vec) + if norm < 1e-12: + return np.array([1.0, 0.0, 0.0], dtype=np.float64) + return vec / norm + + +def euler_deg_to_matrix(euler_deg: Any) -> np.ndarray: + x_deg, y_deg, z_deg = resolve_vector(euler_deg, 3) + x = math.radians(x_deg) + y = math.radians(y_deg) + z = math.radians(z_deg) + + cx = math.cos(x) + sx = math.sin(x) + cy = math.cos(y) + sy = math.sin(y) + cz = math.cos(z) + sz = math.sin(z) + + Rx = np.array([[1.0, 0.0, 0.0], [0.0, cx, -sx], [0.0, sx, cx]], dtype=np.float64) + Ry = np.array([[cy, 0.0, sy], [0.0, 1.0, 0.0], [-sy, 0.0, cy]], dtype=np.float64) + Rz = np.array([[cz, -sz, 0.0], [sz, cz, 0.0], [0.0, 0.0, 1.0]], dtype=np.float64) + return Rz @ Ry @ Rx + + +def transform_from_translation_rotation(translation: Any, rotation_deg: Any) -> np.ndarray: + T = np.eye(4, dtype=np.float64) + T[:3, 3] = np.asarray(resolve_vector(translation, 3), dtype=np.float64) + T[:3, :3] = euler_deg_to_matrix(rotation_deg) + return T + + +def axis_angle_matrix(axis: Any, angle_deg: float) -> np.ndarray: + axis_vec = normalize_axis(axis) + theta = math.radians(angle_deg) + kx, ky, kz = axis_vec + c = math.cos(theta) + s = math.sin(theta) + v = 1.0 - c + R = np.array([ + [kx * kx * v + c, kx * ky * v - kz * s, kx * kz * v + ky * s], + [ky * kx * v + kz * s, ky * ky * v + c, ky * kz * v - kx * s], + [kz * kx * v - ky * s, kz * ky * v + kx * s, kz * kz * v + c], + ], dtype=np.float64) + T = np.eye(4, dtype=np.float64) + T[:3, :3] = R + return T + + +# ----------------------------------------------------------------------------- +# Optional quality configuration +# ----------------------------------------------------------------------------- + +@dataclass +class ObservationQualityConfig: + # q indicators are normalized to 0..1 + size_ref_px: float = 50.0 + border_ref_px: float = 120.0 + center_ref_norm: float = 1.0 + sharpness_ref: float = 2500.0 + homography_ref: float = 0.18 + + # factor f scales the effect of each quality indicator: + # f = 1 -> indicator is fully active + # f = 0 -> indicator is ignored (neutral weight = 1) + size_factor: float = 1.0 + aspect_factor: float = 1.0 + border_factor: float = 1.0 + center_factor: float = 1.0 + sharpness_factor: float = 1.0 + homography_factor: float = 1.0 + + # Placeholders for later phases + normal_visibility_factor: float = 1.0 + spin_factor: float = 1.0 + + # Keep a tiny floor for weights so the optimizer remains numerically stable + weight_floor: float = 0.01 + + +def _load_nested_quality_config(src: Dict[str, Any]) -> Dict[str, Any]: + if not isinstance(src, dict): + return {} + for key in ("multiview_quality", "multiviewQuality", "quality_config", "multiview"): + v = src.get(key) + if isinstance(v, dict): + return v + return {} + + +def load_quality_config(robot: Dict[str, Any]) -> ObservationQualityConfig: + cfg = ObservationQualityConfig() + candidates = [] + candidates.append(_load_nested_quality_config(robot)) + candidates.append(_load_nested_quality_config(robot.get("vision_config", {}) or {})) + + for src in candidates: + if not src: + continue + for field_name in cfg.__dataclass_fields__.keys(): + if field_name in src: + setattr(cfg, field_name, resolve_scalar(src.get(field_name), getattr(cfg, field_name))) + return cfg + + +# ----------------------------------------------------------------------------- +# Marker extraction and kinematics +# ----------------------------------------------------------------------------- + +class ConstraintResult: + def __init__(self, name: str, enabled: bool, reason: str = ""): + self.name = name + self.enabled = enabled + self.reason = reason + self.residuals = [] + + def __str__(self) -> str: + status = "✓ ENABLED" if self.enabled else "✗ DISABLED" + return f"{self.name:40s} {status:12s} {self.reason}" + + +def build_link_chain(robot: Dict[str, Any]) -> List[str]: + links = robot.get("links", {}) or {} + ordered: List[str] = [] + remaining = set(links.keys()) + while remaining: + progress = False + for name in list(remaining): + parent = links[name].get("parent") + if not parent or parent in ordered: + ordered.append(name) + remaining.remove(name) + progress = True + if not progress: + raise RuntimeError("Cycle detected in robot link tree or missing parent link") + return ordered + + +def extract_markers(robot: Dict[str, Any], scale: float) -> Dict[int, Dict[str, Any]]: + markers: Dict[int, Dict[str, Any]] = {} + links = robot.get("links", {}) or {} + marker_defaults = (robot.get("renderingInfo", {}) or {}).get("markerDefaults", {}) or {} + default_size_mm = float(marker_defaults.get("size", 25.0)) + + for link_name, link_info in links.items(): + for marker in link_info.get("markers", []) or []: + marker_id = int(marker.get("id", -1)) + if marker_id < 0: + continue + if marker_id in markers: + # Duplicate ids exist in the provided robot.json. Keep the first entry and warn. + print(f"[WARN] Duplicate marker id {marker_id} on link '{link_name}'. Ignoring this duplicate entry.") + continue + + pos = resolve_vector(marker.get("position", [0, 0, 0]), 3) + size_mm = float(marker.get("size", default_size_mm)) + markers[marker_id] = { + "marker_id": marker_id, + "link_name": link_name, + "position_m": np.asarray([pos[0] * scale, pos[1] * scale, pos[2] * scale], dtype=np.float64), + "normal": normalize_axis(resolve_vector(marker.get("normal", [0, 0, 1]), 3)), + "spin_deg": float(marker.get("spin", 0.0)), + "size_m": size_mm * scale, + } + + return markers + + +def compute_link_transforms(robot: Dict[str, Any], state: Dict[str, float], scale: float) -> Dict[str, np.ndarray]: + links = robot.get("links", {}) or {} + ordered_links = build_link_chain(robot) + transforms: Dict[str, np.ndarray] = {} + + for link_name in ordered_links: + link_info = links[link_name] or {} + parent_name = link_info.get("parent") + parent_transform = transforms[parent_name] if parent_name else np.eye(4, dtype=np.float64) + + mount_translation = np.asarray(resolve_vector(link_info.get("mountPosition", [0, 0, 0]), 3), dtype=np.float64) * scale + mount = transform_from_translation_rotation(mount_translation, link_info.get("mountRotation", [0, 0, 0])) + + joint_info = link_info.get("jointToParent", {}) or {} + joint_origin = np.asarray(resolve_vector(joint_info.get("origin", [0, 0, 0]), 3), dtype=np.float64) * scale + joint = transform_from_translation_rotation(joint_origin, joint_info.get("rotation", [0, 0, 0])) + + motion = np.eye(4, dtype=np.float64) + joint_type = str(joint_info.get("type", "fixed")).strip().lower() + control_var = str(joint_info.get("variable", joint_info.get("control", ""))).strip().lower() + axis = resolve_vector(joint_info.get("axis", [1, 0, 0]), 3) + + if joint_type == "linear": + motion[:3, 3] = normalize_axis(axis) * state.get(control_var, 0.0) * scale + elif joint_type == "revolute": + motion = axis_angle_matrix(axis, state.get(control_var, 0.0)) + + transforms[link_name] = parent_transform @ mount @ joint @ motion + + return transforms + + +def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + link_transform = link_transforms[marker["link_name"]] + local = np.ones(4, dtype=np.float64) + local[:3] = marker["position_m"] + world = link_transform @ local + return world[:3] + + +def marker_plane_axes(normal: np.ndarray, spin_deg: float) -> Tuple[np.ndarray, np.ndarray]: + n = normalize_axis(normal) + candidate = np.array((0.0, 0.0, 1.0), dtype=np.float64) + if abs(np.dot(n, candidate)) > 0.99: + candidate = np.array((1.0, 0.0, 0.0), dtype=np.float64) + + x_dir = np.cross(candidate, n) + x_dir /= max(np.linalg.norm(x_dir), 1e-9) + y_dir = np.cross(n, x_dir) + + if abs(spin_deg) > 1e-9: + theta = math.radians(spin_deg) + cos_t = math.cos(theta) + sin_t = math.sin(theta) + x_rot = x_dir * cos_t + np.cross(n, x_dir) * sin_t + n * np.dot(n, x_dir) * (1.0 - cos_t) + y_rot = y_dir * cos_t + np.cross(n, y_dir) * sin_t + n * np.dot(n, y_dir) * (1.0 - cos_t) + return x_rot, y_rot + + return x_dir, y_dir + + +def marker_object_corners(marker: Dict[str, Any]) -> np.ndarray: + half = marker["size_m"] * 0.5 + x_dir, y_dir = marker_plane_axes(marker["normal"], marker["spin_deg"]) + corners = np.stack([ + -x_dir * half + y_dir * half, + x_dir * half + y_dir * half, + x_dir * half - y_dir * half, + -x_dir * half - y_dir * half, + ], axis=0) + return marker["position_m"].reshape(1, 3) + corners + + +def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + link_transform = link_transforms[marker["link_name"]] + local = marker_object_corners(marker) + homogeneous = np.concatenate([local, np.ones((local.shape[0], 1), dtype=np.float64)], axis=1) + world = (link_transform @ homogeneous.T).T + return world[:, :3] + + +# ----------------------------------------------------------------------------- +# Quality model +# ----------------------------------------------------------------------------- + +def quality_factor(q: float, f: float) -> float: + # Interpret f in [0, 1] as the per-indicator influence. + # f = 1.0 -> indicator is fully active; q is applied. + # f = 0.0 -> indicator is ignored and contributes a neutral weight of 1.0. + q = clamp01(q) + f = clamp01(f) + return 1.0 - f + f * q + + +def projective_homography_quality(image_points_px: np.ndarray, image_shape: Tuple[int, int], ref: float) -> float: + if image_points_px is None or len(image_points_px) != 4: + return 1.0 + + h, w = image_shape + if h <= 0 or w <= 0: + return 1.0 + + src = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], dtype=np.float32) + dst = np.asarray(image_points_px, dtype=np.float32).copy() + dst[:, 0] /= float(w) + dst[:, 1] /= float(h) + + try: + H = cv2.getPerspectiveTransform(src, dst).astype(np.float64) + if abs(H[2, 2]) > 1e-12: + H = H / H[2, 2] + proj_strength = float(abs(H[2, 0]) + abs(H[2, 1])) + q = 1.0 / (1.0 + proj_strength / max(ref, 1e-6)) + return clamp01(q) + except Exception: + return 1.0 + + +def compute_observation_quality( + det: Dict[str, Any], + image_shape: Tuple[int, int], + cfg: ObservationQualityConfig, +) -> Dict[str, Any]: + quality = det.get("quality", {}) or {} + geometry = quality.get("geometry", {}) or {} + sharpness = quality.get("sharpness", {}) or {} + + edge_lengths = quality.get("edge_lengths_px", []) or [] + edge_lengths = [float(x) for x in edge_lengths if x is not None] + mean_edge_px = float(np.mean(edge_lengths)) if len(edge_lengths) else math.sqrt(max(float(quality.get("area_px", 0.0)), 0.0)) + + edge_ratio = float(quality.get("edge_ratio", 1.0) or 1.0) + distance_to_border_px = float(geometry.get("distance_to_border_px", 0.0) or 0.0) + distance_to_center_norm = float(geometry.get("distance_to_center_norm", 1.0) or 1.0) + laplacian_var = float(sharpness.get("laplacian_var", 0.0) or 0.0) + + # q in 0..1 + q_size = clamp01(mean_edge_px / max(cfg.size_ref_px, 1e-6)) + q_aspect = clamp01(2.0 / (1.0 + max(edge_ratio, 1e-6))) + q_border = clamp01(distance_to_border_px / max(cfg.border_ref_px, 1e-6)) + q_center = clamp01(1.0 - (distance_to_center_norm / max(cfg.center_ref_norm, 1e-6))) + q_sharpness = clamp01(laplacian_var / max(cfg.sharpness_ref, 1e-6)) + q_homography = projective_homography_quality(np.asarray(det.get("image_points_px", []), dtype=np.float64), image_shape, cfg.homography_ref) + + factor_map = { + "size": quality_factor(q_size, cfg.size_factor), + "aspect": quality_factor(q_aspect, cfg.aspect_factor), + "border": quality_factor(q_border, cfg.border_factor), + "center": quality_factor(q_center, cfg.center_factor), + "sharpness": quality_factor(q_sharpness, cfg.sharpness_factor), + "homography": quality_factor(q_homography, cfg.homography_factor), + } + + # Currently not active in phase 1, but already supported for later phases. + # They default to f=1, which makes them neutral. + q_normal_visibility = 1.0 + q_spin = 1.0 + factor_map["normal_visibility"] = quality_factor(q_normal_visibility, cfg.normal_visibility_factor) + factor_map["spin"] = quality_factor(q_spin, cfg.spin_factor) + + weight_multiplier = 1.0 + for v in factor_map.values(): + weight_multiplier *= float(v) + + # Conservative default: if no factor is activated in robot.json, + # this remains effectively neutral. + + detector_confidence = clamp01(float(det.get("confidence", 1.0) or 1.0)) + weighted_confidence = detector_confidence * weight_multiplier + weighted_confidence = max(cfg.weight_floor, min(1.0, weighted_confidence)) + + return { + "detector_confidence": detector_confidence, + "weighted_confidence": weighted_confidence, + "q": { + "size": q_size, + "aspect": q_aspect, + "border": q_border, + "center": q_center, + "sharpness": q_sharpness, + "homography": q_homography, + "normal_visibility": q_normal_visibility, + "spin": q_spin, + }, + "factor": factor_map, + "weight_multiplier": weight_multiplier, + "raw": { + "mean_edge_px": mean_edge_px, + "edge_ratio": edge_ratio, + "distance_to_border_px": distance_to_border_px, + "distance_to_center_norm": distance_to_center_norm, + "laplacian_var": laplacian_var, + }, + } + + +# ----------------------------------------------------------------------------- +# Constraints (kept from the existing approach) +# ----------------------------------------------------------------------------- + + +def validate_constraints(robot: Dict[str, Any], robot_markers: Dict[int, Dict[str, Any]]) -> Dict[str, ConstraintResult]: + results = {} + + rigid_body_result = ConstraintResult("RigidBodyDistances", False) + try: + rigid_body_count = 0 + for link_name in ["Arm1", "Ellbow", "Arm2"]: + link_markers = [m for m in robot_markers.values() if m["link_name"] == link_name] + if len(link_markers) >= 2: + rigid_body_count += 1 + if rigid_body_count >= 2: + rigid_body_result.enabled = True + rigid_body_result.reason = f"Found {rigid_body_count} links with 2+ markers each" + else: + rigid_body_result.reason = "Not enough rigid links with multiple markers" + except Exception as e: + rigid_body_result.reason = f"Error: {str(e)}" + results["RigidBodyDistances"] = rigid_body_result + + inter_link_x_result = ConstraintResult("InterLinkXDistances", False) + try: + links_with_markers = set(m["link_name"] for m in robot_markers.values()) + x_rotated_links = [] + for link_name in ["Arm1", "Ellbow"]: + if link_name in links_with_markers: + link_markers = [m for m in robot_markers.values() if m["link_name"] == link_name] + if len(link_markers) >= 1: + x_rotated_links.append(link_name) + if len(x_rotated_links) >= 2: + inter_link_x_result.enabled = True + inter_link_x_result.reason = f"Found {len(x_rotated_links)} X-rotation links: {', '.join(x_rotated_links)}" + else: + inter_link_x_result.reason = "Not enough X-rotation links" + except Exception as e: + inter_link_x_result.reason = f"Error: {str(e)}" + results["InterLinkXDistances"] = inter_link_x_result + + arm2_sina_result = ConstraintResult("Arm2SinADependency", True, "Sanity check only (not enforced)") + try: + arm2_markers = [m for m in robot_markers.values() if m["link_name"] == "Arm2"] + if len(arm2_markers) >= 2: + z_values = set(float(m["position_m"][2]) for m in arm2_markers) + if len(z_values) > 1: + arm2_sina_result.enabled = True + arm2_sina_result.reason = "Multiple Z-values detected; sin(a) dependency confirmed" + else: + arm2_sina_result.enabled = False + arm2_sina_result.reason = "No Z-variation in Arm2 markers (cannot use sin(a) constraint)" + else: + arm2_sina_result.enabled = False + arm2_sina_result.reason = "Not enough Arm2 markers" + except Exception as e: + arm2_sina_result.reason = f"Error: {str(e)}" + results["Arm2SinADependency"] = arm2_sina_result + + return results + + +def compute_soft_constraint_residuals( + robot_state: Dict[str, float], + robot_markers: Dict[int, Dict[str, Any]], + link_transforms: Dict[str, np.ndarray], + robot: Dict[str, Any], + enabled_constraints: Dict[str, ConstraintResult], +) -> List[float]: + residuals = [] + weight_scale = 0.1 + + if enabled_constraints["RigidBodyDistances"].enabled: + for link_name in ["Arm1", "Ellbow", "Arm2"]: + link_markers = [m for m in robot_markers.values() if m["link_name"] == link_name] + if len(link_markers) < 2: + continue + for i in range(len(link_markers)): + for j in range(i + 1, len(link_markers)): + m_i = link_markers[i] + m_j = link_markers[j] + pos_i = compute_marker_world_position(m_i, link_transforms) + pos_j = compute_marker_world_position(m_j, link_transforms) + dist_world = np.linalg.norm(pos_i - pos_j) + dist_local = np.linalg.norm(m_i["position_m"] - m_j["position_m"]) + error = dist_world - dist_local + residuals.append(error * weight_scale * 0.1) + + if enabled_constraints["InterLinkXDistances"].enabled: + arm1_markers = [m for m in robot_markers.values() if m["link_name"] == "Arm1"] + ellbow_markers = [m for m in robot_markers.values() if m["link_name"] == "Ellbow"] + if len(arm1_markers) >= 1 and len(ellbow_markers) >= 1: + m_arm1 = arm1_markers[0] + m_ellbow = ellbow_markers[0] + pos_arm1 = compute_marker_world_position(m_arm1, link_transforms) + pos_ellbow = compute_marker_world_position(m_ellbow, link_transforms) + x_diff_world = pos_ellbow[0] - pos_arm1[0] + x_diff_ref = m_ellbow["position_m"][0] - m_arm1["position_m"][0] + residuals.append((x_diff_world - x_diff_ref) * weight_scale) + + return residuals + + +# ----------------------------------------------------------------------------- +# Camera / observation helpers +# ----------------------------------------------------------------------------- + + +def load_intrinsics(detection_json: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]: + cam = detection_json["camera"] + K = np.asarray(cam["camera_matrix"], dtype=np.float64) + D = np.asarray(cam.get("distortion_coefficients", [0, 0, 0, 0, 0]), dtype=np.float64).reshape(-1, 1) + return K, D + + +def detection_image_shape(detection_json: Dict[str, Any]) -> Tuple[int, int]: + image = detection_json.get("image", {}) or {} + h = int(image.get("height_px", detection_json.get("height_px", 720)) or 720) + w = int(image.get("width_px", detection_json.get("width_px", 1280)) or 1280) + return h, w + + +def collect_views_and_observations( + detection_files: List[str], + robot_markers: Dict[int, Dict[str, Any]], + quality_cfg: ObservationQualityConfig, +) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: + views: List[Dict[str, Any]] = [] + observations: List[Dict[str, Any]] = [] + + for idx, det_path in enumerate(detection_files): + detection_json = load_json(det_path) + K, D = load_intrinsics(detection_json) + image_shape = detection_image_shape(detection_json) + + views.append({ + "index": idx, + "source_file": os.path.abspath(det_path), + "camera_id": detection_json.get("camera", {}).get("camera_id", f"cam{idx+1}"), + "image_file": detection_json.get("image", {}).get("image_file"), + "image_shape": image_shape, + "K": K, + "D": D, + }) + + for det in detection_json.get("detections", []) or []: + if str(det.get("type", "aruco")).lower() != "aruco": + continue + marker_id = int(det.get("marker_id", -1)) + if marker_id < 0 or marker_id not in robot_markers: + continue + + image_points = det.get("image_points_px") + if not (isinstance(image_points, list) and len(image_points) == 4): + # Phase 1 uses full marker corners only. + continue + + image_points = np.asarray(image_points, dtype=np.float64) + marker = robot_markers[marker_id] + obs_quality = compute_observation_quality(det, image_shape, quality_cfg) + + observations.append({ + "view_index": idx, + "marker_id": marker_id, + "marker_link_corners": marker_object_corners(marker), + "image_points_px": image_points, + "confidence_base": obs_quality["detector_confidence"], + "confidence": obs_quality["weighted_confidence"], + "quality": obs_quality, + "raw_detection": det, + }) + + if len(views) == 0: + raise RuntimeError("No valid detection views found") + if len(observations) == 0: + raise RuntimeError("No marker observations matched robot.json markers") + return views, observations + + +def initial_camera_guess( + view: Dict[str, Any], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + default_state: Dict[str, float], + scale: float, + robot: Dict[str, Any], +) -> Tuple[np.ndarray, np.ndarray]: + object_points = [] + image_points = [] + link_transforms = compute_link_transforms(robot, default_state, scale) + + for obs in observations: + if obs["view_index"] != view["index"]: + continue + marker = robot_markers[obs["marker_id"]] + object_points.append(compute_marker_world_corners(marker, link_transforms)) + image_points.append(obs["image_points_px"]) + + if len(object_points) == 0: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + object_points = np.vstack(object_points) + image_points = np.vstack(image_points) + + if object_points.shape[0] < 4: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + success, rvec, tvec = cv2.solvePnP( + object_points, + image_points, + view["K"], + view["D"], + flags=cv2.SOLVEPNP_ITERATIVE, + ) + + if not success: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + return rvec, tvec + + +def project_points(points_3d: np.ndarray, rvec: np.ndarray, tvec: np.ndarray, K: np.ndarray, D: np.ndarray) -> np.ndarray: + projected, _ = cv2.projectPoints(points_3d, rvec, tvec, K, D) + return projected.reshape(-1, 2) + + +# ----------------------------------------------------------------------------- +# Optimization +# ----------------------------------------------------------------------------- + + +def pack_parameters(robot_state: Dict[str, float], camera_params: List[Tuple[np.ndarray, np.ndarray]]) -> np.ndarray: + state_vec = np.asarray([robot_state[k] for k in STATE_KEYS], dtype=np.float64) + cams = [] + for rvec, tvec in camera_params: + cams.append(rvec.reshape(3)) + cams.append(tvec.reshape(3)) + return np.concatenate([state_vec] + cams) + + +def unpack_parameters(params: np.ndarray, n_views: int) -> Tuple[Dict[str, float], List[Tuple[np.ndarray, np.ndarray]]]: + robot_state = {STATE_KEYS[i]: float(params[i]) for i in range(len(STATE_KEYS))} + camera_params = [] + offset = len(STATE_KEYS) + for _ in range(n_views): + rvec = params[offset:offset + 3].reshape(3, 1) + tvec = params[offset + 3:offset + 6].reshape(3, 1) + camera_params.append((rvec, tvec)) + offset += 6 + return robot_state, camera_params + + +def residuals_for_parameters( + params: np.ndarray, + views: List[Dict[str, Any]], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + robot: Dict[str, Any], + scale: float, + default_state: Dict[str, float], + enabled_constraints: Dict[str, ConstraintResult], +) -> np.ndarray: + robot_state, camera_params = unpack_parameters(params, len(views)) + link_transforms = compute_link_transforms(robot, robot_state, scale) + + residuals = [] + + for obs in observations: + marker = robot_markers[obs["marker_id"]] + world_corners = compute_marker_world_corners(marker, link_transforms) + rvec, tvec = camera_params[obs["view_index"]] + proj = project_points(world_corners, rvec, tvec, views[obs["view_index"]]["K"], views[obs["view_index"]]["D"]) + diffs = proj - obs["image_points_px"] + weight = math.sqrt(max(float(obs["confidence"]), 1e-9)) + residuals.extend((diffs * weight).reshape(-1)) + + for key in STATE_KEYS: + diff = robot_state[key] - default_state.get(key, 0.0) + w = 0.001 if key in ("x", "y", "z", "e") else 0.01 + residuals.append(diff * w) + + residuals.extend(compute_soft_constraint_residuals(robot_state, robot_markers, link_transforms, robot, enabled_constraints)) + + return np.asarray(residuals, dtype=np.float64) + + +def estimate_uncertainty(result: Any, n_params: int) -> np.ndarray: + if result.jac is None: + return np.full(n_params, float("nan"), dtype=np.float64) + J = result.jac + m, n = J.shape + JTJ = J.T @ J + try: + cov = np.linalg.pinv(JTJ) + except np.linalg.LinAlgError: + cov = np.linalg.pinv(JTJ + np.eye(n) * 1e-9) + residuals = result.fun + dof = max(1, m - n) + sigma2 = float(np.sum(residuals ** 2) / dof) + cov *= sigma2 + return np.sqrt(np.diag(cov)) + + +def camera_position_world(rvec: np.ndarray, tvec: np.ndarray) -> np.ndarray: + R, _ = cv2.Rodrigues(rvec) + return (-R.T @ tvec).reshape(3) + + +# ----------------------------------------------------------------------------- +# Output building +# ----------------------------------------------------------------------------- + + +def build_output( + robot_state: Dict[str, float], + state_uncertainty: np.ndarray, + views: List[Dict[str, Any]], + camera_params: List[Tuple[np.ndarray, np.ndarray]], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + scale: float, + robot: Dict[str, Any], + robot_json_path: str, + quality_cfg: ObservationQualityConfig, + final_cost: Optional[float] = None, + solver_status: Optional[int] = None, + solver_message: Optional[str] = None, +) -> Dict[str, Any]: + link_transforms = compute_link_transforms(robot, robot_state, scale) + + marker_summary: Dict[int, Dict[str, Any]] = {} + for marker_id, marker in robot_markers.items(): + marker_summary[marker_id] = { + "marker_id": marker_id, + "link_name": marker["link_name"], + "position_world_m": compute_marker_world_position(marker, link_transforms).tolist(), + "size_m": marker["size_m"], + "observation_count": 0, + "mean_confidence": None, + "mean_detector_confidence": None, + "mean_reprojection_error_px": None, + "observations": [], + } + + per_marker_errors: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + per_marker_confidences: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + per_marker_detector_conf: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + + for obs in observations: + marker_id = obs["marker_id"] + marker = robot_markers[marker_id] + object_points_m = compute_marker_world_corners(marker, link_transforms) + rvec, tvec = camera_params[obs["view_index"]] + proj = project_points(object_points_m, rvec, tvec, views[obs["view_index"]]["K"], views[obs["view_index"]]["D"]) + diffs = proj - obs["image_points_px"] + errors = np.linalg.norm(diffs, axis=1) + repro_error = float(np.mean(errors)) + per_marker_errors[marker_id].extend(errors.tolist()) + per_marker_confidences[marker_id].append(float(obs["confidence"])) + per_marker_detector_conf[marker_id].append(float(obs["confidence_base"])) + marker_summary[marker_id]["observation_count"] += 1 + marker_summary[marker_id]["observations"].append({ + "view_index": obs["view_index"], + "source_file": views[obs["view_index"]]["source_file"], + "image_file": views[obs["view_index"]]["image_file"], + "confidence_detector": float(obs["confidence_base"]), + "confidence_weighted": float(obs["confidence"]), + "quality": obs["quality"], + "mean_reprojection_error_px": repro_error, + "corner_reprojection_errors_px": errors.tolist(), + }) + + for marker_id, summary in marker_summary.items(): + if summary["observation_count"] > 0: + summary["mean_confidence"] = float(np.mean(per_marker_confidences[marker_id])) + summary["mean_detector_confidence"] = float(np.mean(per_marker_detector_conf[marker_id])) + summary["mean_reprojection_error_px"] = float(np.mean(per_marker_errors[marker_id])) + + camera_outputs = [] + for idx, view in enumerate(views): + rvec, tvec = camera_params[idx] + cam_pos = camera_position_world(rvec, tvec) + observed_count = sum(1 for obs in observations if obs["view_index"] == idx) + camera_outputs.append({ + "view_index": idx, + "source_file": view["source_file"], + "camera_id": view["camera_id"], + "camera_position_world_m": cam_pos.tolist(), + "rvec": rvec.reshape(-1).tolist(), + "tvec": tvec.reshape(-1).tolist(), + "intrinsics": { + "camera_matrix": view["K"].tolist(), + "distortion_coefficients": view["D"].reshape(-1).tolist(), + }, + "observation_count": observed_count, + }) + + robot_pose_output = { + "state": {k: float(robot_state[k]) for k in STATE_KEYS}, + "uncertainty": { + "x_mm": float(state_uncertainty[0]), + "y_mm": float(state_uncertainty[1]), + "z_mm": float(state_uncertainty[2]), + "a_deg": float(state_uncertainty[3]), + "b_deg": float(state_uncertainty[4]), + "c_deg": float(state_uncertainty[5]), + "e_mm": float(state_uncertainty[6]), + }, + "confidence": { + "x": float(math.exp(-state_uncertainty[0] / 10.0)) if np.isfinite(state_uncertainty[0]) else 0.0, + "y": float(math.exp(-state_uncertainty[1] / 10.0)) if np.isfinite(state_uncertainty[1]) else 0.0, + "z": float(math.exp(-state_uncertainty[2] / 10.0)) if np.isfinite(state_uncertainty[2]) else 0.0, + "a": float(math.exp(-state_uncertainty[3] / 10.0)) if np.isfinite(state_uncertainty[3]) else 0.0, + "b": float(math.exp(-state_uncertainty[4] / 10.0)) if np.isfinite(state_uncertainty[4]) else 0.0, + "c": float(math.exp(-state_uncertainty[5] / 10.0)) if np.isfinite(state_uncertainty[5]) else 0.0, + "e": float(math.exp(-state_uncertainty[6] / max(1.0, state_uncertainty[6]))) if np.isfinite(state_uncertainty[6]) else 0.0, + }, + } + + all_conf = np.asarray([obs["confidence"] for obs in observations], dtype=np.float64) + all_det_conf = np.asarray([obs["confidence_base"] for obs in observations], dtype=np.float64) + all_q_size = np.asarray([obs["quality"]["q"]["size"] for obs in observations], dtype=np.float64) + all_q_aspect = np.asarray([obs["quality"]["q"]["aspect"] for obs in observations], dtype=np.float64) + all_q_border = np.asarray([obs["quality"]["q"]["border"] for obs in observations], dtype=np.float64) + all_q_homography = np.asarray([obs["quality"]["q"]["homography"] for obs in observations], dtype=np.float64) + + all_errors = [] + for marker in marker_summary.values(): + if marker["mean_reprojection_error_px"] is not None: + all_errors.append(marker["mean_reprojection_error_px"]) + + statistics = { + "observation_count": len(observations), + "camera_count": len(views), + "marker_count": len(robot_markers), + "observed_marker_count": int(sum(1 for m in marker_summary.values() if m["observation_count"] > 0)), + "mean_detector_confidence": float(np.mean(all_det_conf)) if len(all_det_conf) else None, + "mean_weighted_confidence": float(np.mean(all_conf)) if len(all_conf) else None, + "mean_reprojection_error_px": float(np.mean(all_errors)) if len(all_errors) else None, + "quality_means": { + "size": float(np.mean(all_q_size)) if len(all_q_size) else None, + "aspect": float(np.mean(all_q_aspect)) if len(all_q_aspect) else None, + "border": float(np.mean(all_q_border)) if len(all_q_border) else None, + "homography": float(np.mean(all_q_homography)) if len(all_q_homography) else None, + }, + "quality_config": { + "size_ref_px": quality_cfg.size_ref_px, + "border_ref_px": quality_cfg.border_ref_px, + "center_ref_norm": quality_cfg.center_ref_norm, + "sharpness_ref": quality_cfg.sharpness_ref, + "homography_ref": quality_cfg.homography_ref, + "size_factor": quality_cfg.size_factor, + "aspect_factor": quality_cfg.aspect_factor, + "border_factor": quality_cfg.border_factor, + "center_factor": quality_cfg.center_factor, + "sharpness_factor": quality_cfg.sharpness_factor, + "homography_factor": quality_cfg.homography_factor, + }, + } + + output = { + "schema_version": "1.0", + "created_utc": _dt.datetime.utcnow().isoformat() + "Z", + "source_robot_json": os.path.abspath(robot_json_path), + "source_detections": [view["source_file"] for view in views], + "robot_pose": robot_pose_output, + "camera_poses": camera_outputs, + "marker_positions": list(marker_summary.values()), + "statistics": statistics, + "solver": { + "final_cost": final_cost, + "status": solver_status, + "message": solver_message, + }, + } + return output + + +def build_summary(output: Dict[str, Any]) -> Dict[str, Any]: + return { + "schema_version": output.get("schema_version"), + "created_utc": output.get("created_utc"), + "source_robot_json": output.get("source_robot_json"), + "source_detections": output.get("source_detections"), + "solver": output.get("solver", {}), + "robot_pose": output.get("robot_pose"), + "statistics": output.get("statistics", {}), + } + + +# ----------------------------------------------------------------------------- +# Diagnostics +# ----------------------------------------------------------------------------- + + +def print_constraint_sanity_check( + robot_state: Dict[str, float], + robot_markers: Dict[int, Dict[str, Any]], + link_transforms: Dict[str, np.ndarray], + robot: Dict[str, Any], + enabled_constraints: Dict[str, ConstraintResult], + scale: float, +) -> None: + print("\n" + "=" * 70) + print("CONSTRAINT SANITY CHECKS (after optimization)") + print("=" * 70) + + if enabled_constraints["RigidBodyDistances"].enabled: + print("\n1. RIGID BODY DISTANCES") + for link_name in ["Arm1", "Ellbow", "Arm2"]: + link_markers = [m for m in robot_markers.values() if m["link_name"] == link_name] + if len(link_markers) < 2: + continue + max_error = 0.0 + for i in range(len(link_markers)): + for j in range(i + 1, len(link_markers)): + m_i = link_markers[i] + m_j = link_markers[j] + pos_i = compute_marker_world_position(m_i, link_transforms) + pos_j = compute_marker_world_position(m_j, link_transforms) + dist_world = np.linalg.norm(pos_i - pos_j) + dist_local = np.linalg.norm(m_i["position_m"] - m_j["position_m"]) + max_error = max(max_error, abs(dist_world - dist_local)) + status = "✓" if max_error < 1.0 else "⚠" if max_error < 5.0 else "✗" + print(f" {link_name:10s}: max_error = {max_error:.3f} mm {status}") + + if enabled_constraints["InterLinkXDistances"].enabled: + print("\n2. INTER-LINK X-DISTANCES") + arm1_markers = [m for m in robot_markers.values() if m["link_name"] == "Arm1"] + ellbow_markers = [m for m in robot_markers.values() if m["link_name"] == "Ellbow"] + if len(arm1_markers) >= 1 and len(ellbow_markers) >= 1: + m_arm1 = arm1_markers[0] + m_ellbow = ellbow_markers[0] + pos_arm1 = compute_marker_world_position(m_arm1, link_transforms) + pos_ellbow = compute_marker_world_position(m_ellbow, link_transforms) + x_diff_world = pos_ellbow[0] - pos_arm1[0] + x_diff_ref = m_ellbow["position_m"][0] - m_arm1["position_m"][0] + error = abs(x_diff_world - x_diff_ref) + status = "✓" if error < 1.0 else "⚠" if error < 5.0 else "✗" + print(f" Arm1 <-> Ellbow: error = {error:.3f} mm {status}") + + if enabled_constraints["Arm2SinADependency"].enabled: + print("\n3. ARM2 sin(a) DEPENDENCY (sanity check)") + arm2_markers = [m for m in robot_markers.values() if m["link_name"] == "Arm2"] + if len(arm2_markers) >= 2: + a_rad = math.radians(robot_state["a"]) + sin_a = math.sin(a_rad) + cos_a = math.cos(a_rad) + max_error = 0.0 + # This remains only a qualitative check. + for m in arm2_markers: + pos_world = compute_marker_world_position(m, link_transforms) + x_world = pos_world[0] + x_local = m["position_m"][0] + z_local = m["position_m"][2] + x_expected = (90.0 * scale) + x_local * cos_a - z_local * sin_a + max_error = max(max_error, abs(x_world - x_expected)) + status = "✓" if max_error < 5.0 else "⚠" + print(f" X-consistency with sin(a): max_error = {max_error:.3f} mm {status}") + print(" (Note: this is a consistency check, not a hard constraint)") + + print("=" * 70) + + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- + + +def main() -> None: + parser = argparse.ArgumentParser(description="Multiview optimization of robot pose and camera extrinsics") + parser.add_argument("--robot", required=True, help="Path to robot.json") + parser.add_argument("--detections", required=True, nargs="+", help="List of detection JSON files") + parser.add_argument("--outDir", required=True, help="Output directory") + parser.add_argument("--write-summary", action="store_true", help="Write summary file") + parser.add_argument("--max-iter", type=int, default=500, help="Maximum optimizer iterations") + args = parser.parse_args() + + os.makedirs(args.outDir, exist_ok=True) + + robot_json_path = os.path.abspath(args.robot) + robot = load_json(robot_json_path) + scale = parse_metric_scale(robot) + quality_cfg = load_quality_config(robot) + + default_state = {k: float(robot.get("defaultPosition", {}).get(k, 0.0) or 0.0) for k in STATE_KEYS} + robot_markers = extract_markers(robot, scale) + + print("\n" + "=" * 70) + print("CONSTRAINT VALIDATION") + print("=" * 70) + enabled_constraints = validate_constraints(robot, robot_markers) + for _, result in enabled_constraints.items(): + print(result) + print("=" * 70) + + views, observations = collect_views_and_observations(args.detections, robot_markers, quality_cfg) + + print("\n" + "=" * 70) + print("OBSERVATION QUALITY SUMMARY") + print("=" * 70) + print(f"Total observations: {len(observations)}") + print() + + quality_by_marker: Dict[int, List[Dict[str, Any]]] = {} + for obs in observations: + quality_by_marker.setdefault(obs["marker_id"], []).append(obs["quality"]) + + print(f"{'Marker':>8} {'Link':>12} {'Count':>6} {'Avg Size':>10} {'Avg Aspec':>10} {'Avg Hmg.':>10} {'Avg Conf.':>10}") + print("-" * 74) + for marker_id in sorted(quality_by_marker.keys()): + marker = robot_markers[marker_id] + qlist = quality_by_marker[marker_id] + avg_size = float(np.mean([q["q"]["size"] for q in qlist])) + avg_aspect = float(np.mean([q["q"]["aspect"] for q in qlist])) + avg_homog = float(np.mean([q["q"]["homography"] for q in qlist])) + obs_for_marker = [o for o in observations if o["marker_id"] == marker_id] + avg_conf = float(np.mean([o["confidence"] for o in obs_for_marker])) + print(f"{marker_id:8d} {marker['link_name']:>12} {len(qlist):6d} {avg_size:10.3f} {avg_aspect:10.3f} {avg_homog:10.3f} {avg_conf:10.3f}") + print("=" * 70) + + camera_guesses = [] + for view in views: + rvec, tvec = initial_camera_guess(view, observations, robot_markers, default_state, scale, robot) + camera_guesses.append((rvec, tvec)) + + x0 = pack_parameters(default_state, camera_guesses) + + progress = { + "iter": 0, + "last_cost": None, + "last_print": time.time(), + "prev_x": x0.copy(), + } + + def progress_callback(xk: np.ndarray) -> None: + progress["iter"] += 1 + now = time.time() + if progress["iter"] == 1 or now - progress["last_print"] >= 1.0: + res = residuals_for_parameters(xk, views, observations, robot_markers, robot, scale, default_state, enabled_constraints) + cost = 0.5 * float(np.dot(res, res)) + delta_cost = None + convergence = "" + if progress["last_cost"] is not None: + delta_cost = cost - progress["last_cost"] + if abs(delta_cost) < 1e-3: + convergence = " stable" + elif delta_cost < 0: + convergence = " improving" + else: + convergence = " worsening" + step_norm = float(np.linalg.norm(xk - progress["prev_x"])) + print(f'[Multiview] iter={progress["iter"]:4d} cost={cost:.4f}' + (f' delta={delta_cost:.4g}' if delta_cost is not None else "") + f' step={step_norm:.4g}' + convergence) + progress["last_cost"] = cost + progress["last_print"] = now + progress["prev_x"] = xk.copy() + + result = least_squares( + residuals_for_parameters, + x0, + args=(views, observations, robot_markers, robot, scale, default_state, enabled_constraints), + jac="2-point", + method="trf", + loss="soft_l1", + f_scale=1.0, + max_nfev=args.max_iter, + callback=progress_callback, + ) + + robot_state, camera_params = unpack_parameters(result.x, len(views)) + uncertainties = estimate_uncertainty(result, len(result.x)) + + link_transforms = compute_link_transforms(robot, robot_state, scale) + print_constraint_sanity_check(robot_state, robot_markers, link_transforms, robot, enabled_constraints, scale) + + output = build_output( + robot_state, + uncertainties[:len(STATE_KEYS)], + views, + camera_params, + observations, + robot_markers, + scale, + robot, + robot_json_path, + quality_cfg, + final_cost=float(result.cost), + solver_status=int(result.status), + solver_message=str(result.message), + ) + + out_path = Path(args.outDir) / "multiview_pose.json" + save_json(output, out_path) + print(f"Saved: {out_path}") + + if args.write_summary: + summary_path = Path(args.outDir) / "multiview_pose_summary.json" + summary = build_summary(output) + save_json(summary, summary_path) + print(f"Saved: {summary_path}") + + +if __name__ == "__main__": + main() diff --git a/pipeline/2_Multiview_ohne_confidence.py b/pipeline/2_Multiview_ohne_confidence.py new file mode 100644 index 0000000..666bc1d --- /dev/null +++ b/pipeline/2_Multiview_ohne_confidence.py @@ -0,0 +1,971 @@ +#!/usr/bin/env python3 + +""" +============================================================ +STEP 2b — Simultane Multiview-Optimierung für Roboterpose +============================================================ + +Ziel: + Aus mehreren ArUco-Detektionsdateien die gemeinsame + Roboterpose (x,y,z,a,b,c,e) schätzen und jede Kamera-Pose + sowie Marker-Weltpositionen ausgeben. + +Eingabe: + --robot ../robot.json + --detections render_1a_aruco_detection.json render_1b_aruco_detection.json ... + --outDir . + +Ausgabe: + multiview_pose.json + +Hinweis: + Dieses Skript verwendet die Markerpositionen aus robot.json + als kinematische Constraints und optimiert gleichzeitig: + - Roboterzustand (x,y,z,a,b,c,e) + - Kameraextrinsische Parameter pro Bild + +""" + +import argparse +import datetime +import json +import math +import os +import time +from pathlib import Path +from typing import Any, Dict, List, Tuple + +import cv2 +import numpy as np +from scipy.optimize import least_squares + +STATE_KEYS = ["x", "y", "z", "a", "b", "c", "e"] + +# ------------------------------------------------------------------ +# Constraint definitions and validation +# ------------------------------------------------------------------ + +class ConstraintResult: + """Result of validating/applying a single constraint""" + def __init__(self, name: str, enabled: bool, reason: str = ""): + self.name = name + self.enabled = enabled + self.reason = reason + self.residuals = [] + + def __str__(self) -> str: + status = "✓ ENABLED" if self.enabled else "✗ DISABLED" + return f"{self.name:40s} {status:12s} {self.reason}" + + +def validate_constraints(robot: Dict[str, Any], robot_markers: Dict[int, Dict[str, Any]]) -> Dict[str, ConstraintResult]: + """ + Validate which constraints can be applied based on robot geometry. + Returns a dict of constraint_name -> ConstraintResult + """ + results = {} + + # --- Constraint 1: Rigid body distances within each link --- + rigid_body_result = ConstraintResult("RigidBodyDistances", False) + try: + rigid_body_count = 0 + for link_name in ['Arm1', 'Ellbow', 'Arm2']: + link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name] + if len(link_markers) >= 2: + rigid_body_count += 1 + if rigid_body_count >= 2: + rigid_body_result.enabled = True + rigid_body_result.reason = f"Found {rigid_body_count} links with 2+ markers each" + else: + rigid_body_result.reason = "Not enough rigid links with multiple markers" + except Exception as e: + rigid_body_result.reason = f"Error: {str(e)}" + results['RigidBodyDistances'] = rigid_body_result + + # --- Constraint 2: Fixed X-distances between links (rotation around X-axis) --- + inter_link_x_result = ConstraintResult("InterLinkXDistances", False) + try: + links_with_markers = set(m['link_name'] for m in robot_markers.values()) + x_rotated_links = [] + for link_name in ['Arm1', 'Ellbow']: + if link_name in links_with_markers: + link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name] + if len(link_markers) >= 1: + x_rotated_links.append(link_name) + if len(x_rotated_links) >= 2: + inter_link_x_result.enabled = True + inter_link_x_result.reason = f"Found {len(x_rotated_links)} X-rotation links: {', '.join(x_rotated_links)}" + else: + inter_link_x_result.reason = "Not enough X-rotation links" + except Exception as e: + inter_link_x_result.reason = f"Error: {str(e)}" + results['InterLinkXDistances'] = inter_link_x_result + + # --- Sanity check (not a hard constraint): Arm2 sin(a) dependency --- + arm2_sina_result = ConstraintResult("Arm2SinADependency", True, "Sanity check only (not enforced)") + try: + arm2_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm2'] + if len(arm2_markers) >= 2: + z_values = set(m['position_m'][2] for m in arm2_markers) + if len(z_values) > 1: + arm2_sina_result.enabled = True + arm2_sina_result.reason = "Multiple Z-values detected; sin(a) dependency confirmed" + else: + arm2_sina_result.enabled = False + arm2_sina_result.reason = "No Z-variation in Arm2 markers (cannot use sin(a) constraint)" + else: + arm2_sina_result.enabled = False + arm2_sina_result.reason = "Not enough Arm2 markers" + except Exception as e: + arm2_sina_result.reason = f"Error: {str(e)}" + results['Arm2SinADependency'] = arm2_sina_result + + return results + + +# ------------------------------------------------------------------ +# JSON helpers +# ------------------------------------------------------------------ + +def load_json(path: str) -> Dict[str, Any]: + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + + +def save_json(data: Dict[str, Any], path: Path) -> None: + with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=2) + + +# ------------------------------------------------------------------ +# robot.json helpers +# ------------------------------------------------------------------ + +def resolve_scalar(value: Any, default: float = 0.0) -> float: + if value is None: + return default + if isinstance(value, (int, float)): + return float(value) + try: + return float(str(value).strip()) + except ValueError: + return default + + +def resolve_vector(value: Any, default_len: int = 3) -> Tuple[float, ...]: + if value is None: + return tuple(0.0 for _ in range(default_len)) + if isinstance(value, (int, float, str)): + return (resolve_scalar(value),) + tuple(0.0 for _ in range(default_len - 1)) + if isinstance(value, (list, tuple)): + resolved = [resolve_scalar(v) for v in value] + if len(resolved) < default_len: + resolved.extend([0.0] * (default_len - len(resolved))) + return tuple(resolved[:default_len]) + return tuple(0.0 for _ in range(default_len)) + + +def parse_metric_scale(robot: Dict[str, Any]) -> float: + rendering_info = robot.get('renderingInfo', {}) or {} + metric = rendering_info.get('metric', 'mm') + return 0.001 if str(metric).strip().lower() == 'mm' else 1.0 + + +def normalize_axis(axis: Any) -> np.ndarray: + vec = np.asarray(axis, dtype=np.float64) + if vec.shape != (3,): + vec = vec.reshape(-1)[:3] + norm = np.linalg.norm(vec) + return vec / max(norm, 1e-9) + + +def euler_deg_to_matrix(euler_deg: Any) -> np.ndarray: + x_deg, y_deg, z_deg = resolve_vector(euler_deg, 3) + x = math.radians(x_deg) + y = math.radians(y_deg) + z = math.radians(z_deg) + + cx = math.cos(x) + sx = math.sin(x) + cy = math.cos(y) + sy = math.sin(y) + cz = math.cos(z) + sz = math.sin(z) + + Rx = np.array([ + [1.0, 0.0, 0.0], + [0.0, cx, -sx], + [0.0, sx, cx] + ], dtype=np.float64) + + Ry = np.array([ + [cy, 0.0, sy], + [0.0, 1.0, 0.0], + [-sy, 0.0, cy] + ], dtype=np.float64) + + Rz = np.array([ + [cz, -sz, 0.0], + [sz, cz, 0.0], + [0.0, 0.0, 1.0] + ], dtype=np.float64) + + return Rz @ Ry @ Rx + + +def transform_from_translation_rotation(translation: Any, rotation_deg: Any) -> np.ndarray: + T = np.eye(4, dtype=np.float64) + pos = np.asarray(resolve_vector(translation, 3), dtype=np.float64) + T[:3, 3] = pos + T[:3, :3] = euler_deg_to_matrix(rotation_deg) + return T + + +def axis_angle_matrix(axis: Any, angle_deg: float) -> np.ndarray: + axis_vec = normalize_axis(axis) + theta = math.radians(angle_deg) + kx, ky, kz = axis_vec + c = math.cos(theta) + s = math.sin(theta) + v = 1.0 - c + R = np.array([ + [kx * kx * v + c, kx * ky * v - kz * s, kx * kz * v + ky * s], + [ky * kx * v + kz * s, ky * ky * v + c, ky * kz * v - kx * s], + [kz * kx * v - ky * s, kz * ky * v + kx * s, kz * kz * v + c] + ], dtype=np.float64) + T = np.eye(4, dtype=np.float64) + T[:3, :3] = R + return T + + +# ------------------------------------------------------------------ +# Kinematics and marker extraction +# ------------------------------------------------------------------ + +def extract_markers(robot: Dict[str, Any], scale: float) -> Dict[int, Dict[str, Any]]: + markers = {} + links = robot.get('links', {}) or {} + marker_defaults = (robot.get('renderingInfo', {}) or {}).get('markerDefaults', {}) or {} + default_size_mm = float(marker_defaults.get('size', 25.0)) + + for link_name, link_info in links.items(): + for marker in link_info.get('markers', []) or []: + marker_id = int(marker.get('id', -1)) + if marker_id < 0: + continue + + pos = resolve_vector(marker.get('position', [0, 0, 0]), 3) + size_mm = float(marker.get('size', default_size_mm)) + markers[marker_id] = { + 'marker_id': marker_id, + 'link_name': link_name, + 'position_m': np.asarray([pos[0] * scale, pos[1] * scale, pos[2] * scale], dtype=np.float64), + 'normal': normalize_axis(resolve_vector(marker.get('normal', [0, 0, 1]), 3)), + 'spin_deg': float(marker.get('spin', 0.0)), + 'size_m': size_mm * scale, + } + + return markers + + +def marker_plane_axes(normal: np.ndarray, spin_deg: float) -> Tuple[np.ndarray, np.ndarray]: + n = normalize_axis(normal) + candidate = np.array((0.0, 0.0, 1.0), dtype=np.float64) + if abs(np.dot(n, candidate)) > 0.99: + candidate = np.array((1.0, 0.0, 0.0), dtype=np.float64) + + x_dir = np.cross(candidate, n) + x_dir /= max(np.linalg.norm(x_dir), 1e-9) + y_dir = np.cross(n, x_dir) + + if abs(spin_deg) > 1e-6: + theta = math.radians(spin_deg) + cos_t = math.cos(theta) + sin_t = math.sin(theta) + x_rot = x_dir * cos_t + np.cross(n, x_dir) * sin_t + n * np.dot(n, x_dir) * (1.0 - cos_t) + y_rot = y_dir * cos_t + np.cross(n, y_dir) * sin_t + n * np.dot(n, y_dir) * (1.0 - cos_t) + return x_rot, y_rot + + return x_dir, y_dir + + +def marker_object_corners(marker: Dict[str, Any]) -> np.ndarray: + half = marker['size_m'] * 0.5 + x_dir, y_dir = marker_plane_axes(marker['normal'], marker['spin_deg']) + corners = np.stack([ + -x_dir * half + y_dir * half, + x_dir * half + y_dir * half, + x_dir * half - y_dir * half, + -x_dir * half - y_dir * half + ], axis=0) + return marker['position_m'].reshape(1, 3) + corners + + +def build_link_chain(robot: Dict[str, Any]) -> List[str]: + links = robot.get('links', {}) or {} + ordered: List[str] = [] + remaining = set(links.keys()) + + while remaining: + progress = False + for name in list(remaining): + parent = links[name].get('parent') + if not parent or parent in ordered: + ordered.append(name) + remaining.remove(name) + progress = True + if not progress: + raise RuntimeError('Cycle detected in robot link tree or missing parent link') + return ordered + + +def compute_link_transforms(robot: Dict[str, Any], state: Dict[str, float], scale: float) -> Dict[str, np.ndarray]: + links = robot.get('links', {}) or {} + ordered_links = build_link_chain(robot) + transforms: Dict[str, np.ndarray] = {} + + for link_name in ordered_links: + link_info = links[link_name] or {} + parent_name = link_info.get('parent') + parent_transform = transforms[parent_name] if parent_name else np.eye(4, dtype=np.float64) + + mount_translation = np.asarray(resolve_vector(link_info.get('mountPosition', [0, 0, 0]), 3), dtype=np.float64) * scale + mount = transform_from_translation_rotation( + mount_translation, + link_info.get('mountRotation', [0, 0, 0]) + ) + + joint_info = link_info.get('jointToParent', {}) or {} + joint_origin = np.asarray(resolve_vector(joint_info.get('origin', [0, 0, 0]), 3), dtype=np.float64) * scale + joint = transform_from_translation_rotation( + joint_origin, + joint_info.get('rotation', [0, 0, 0]) + ) + + motion = np.eye(4, dtype=np.float64) + joint_type = str(joint_info.get('type', 'fixed')).strip().lower() + control_var = str(joint_info.get('variable', joint_info.get('control', ''))).strip().lower() + axis = resolve_vector(joint_info.get('axis', [1, 0, 0]), 3) + + if joint_type == 'linear': + motion[:3, 3] = normalize_axis(axis) * state.get(control_var, 0.0) * scale + elif joint_type == 'revolute': + motion = axis_angle_matrix(axis, state.get(control_var, 0.0)) + + transforms[link_name] = parent_transform @ mount @ joint @ motion + + return transforms + + +def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + link_transform = link_transforms[marker['link_name']] + local = np.ones(4, dtype=np.float64) + local[:3] = marker['position_m'] + world = link_transform @ local + return world[:3] + + +# ------------------------------------------------------------------ +# Camera / observation helpers +# ------------------------------------------------------------------ + +def load_intrinsics(detection_json: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]: + cam = detection_json['camera'] + K = np.asarray(cam['camera_matrix'], dtype=np.float64) + D = np.asarray(cam.get('distortion_coefficients', [0, 0, 0, 0, 0]), dtype=np.float64).reshape(-1, 1) + return K, D + + +def collect_views_and_observations( + detection_files: List[str], + robot_markers: Dict[int, Dict[str, Any]] +) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: + views: List[Dict[str, Any]] = [] + observations: List[Dict[str, Any]] = [] + + for idx, det_path in enumerate(detection_files): + detection_json = load_json(det_path) + K, D = load_intrinsics(detection_json) + views.append({ + 'index': idx, + 'source_file': os.path.abspath(det_path), + 'camera_id': detection_json.get('camera', {}).get('camera_id', f'cam{idx+1}'), + 'image_file': detection_json.get('image', {}).get('image_file'), + 'K': K, + 'D': D + }) + + for det in detection_json.get('detections', []) or []: + marker_id = int(det.get('marker_id', -1)) + if marker_id < 0 or marker_id not in robot_markers: + continue + + image_points = det.get('image_points_px') + if isinstance(image_points, list) and len(image_points) == 4: + image_points = np.asarray(image_points, dtype=np.float64) + else: + center = resolve_vector(det.get('center_px', [0, 0]), 2) + image_points = np.asarray([center], dtype=np.float64) + + confidence = float(det.get('confidence', 1.0)) + marker = robot_markers[marker_id] + observations.append({ + 'view_index': idx, + 'marker_id': marker_id, + 'marker_link_corners': marker_object_corners(marker), + 'image_points_px': image_points, + 'confidence': max(0.01, min(1.0, confidence)) + }) + + if len(views) == 0: + raise RuntimeError('No valid detection views found') + + if len(observations) == 0: + raise RuntimeError('No marker observations matched robot.json markers') + + return views, observations + + +def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + link_transform = link_transforms[marker['link_name']] + local = marker_object_corners(marker) + homogeneous = np.concatenate([local, np.ones((local.shape[0], 1), dtype=np.float64)], axis=1) + world = (link_transform @ homogeneous.T).T + return world[:, :3] + + +def initial_camera_guess( + view: Dict[str, Any], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + default_state: Dict[str, float], + scale: float, + robot: Dict[str, Any] +) -> Tuple[np.ndarray, np.ndarray]: + object_points = [] + image_points = [] + + link_transforms = compute_link_transforms(robot, default_state, scale) + + for obs in observations: + if obs['view_index'] != view['index']: + continue + marker = robot_markers[obs['marker_id']] + object_points.append(compute_marker_world_corners(marker, link_transforms)) + image_points.append(obs['image_points_px']) + + if len(object_points) == 0: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + object_points = np.vstack(object_points) + image_points = np.vstack(image_points) + + if object_points.shape[0] < 4: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + success, rvec, tvec = cv2.solvePnP( + object_points, + image_points, + view['K'], + view['D'], + flags=cv2.SOLVEPNP_ITERATIVE + ) + + if not success: + return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64) + + return rvec, tvec + + +def project_points( + points_3d: np.ndarray, + rvec: np.ndarray, + tvec: np.ndarray, + K: np.ndarray, + D: np.ndarray +) -> np.ndarray: + projected, _ = cv2.projectPoints(points_3d, rvec, tvec, K, D) + return projected.reshape(-1, 2) + + +def compute_soft_constraint_residuals( + robot_state: Dict[str, float], + robot_markers: Dict[int, Dict[str, Any]], + link_transforms: Dict[str, np.ndarray], + robot: Dict[str, Any], + enabled_constraints: Dict[str, ConstraintResult] +) -> List[float]: + """ + Compute residuals from soft constraints (kinematic consistency, rigid body distances). + Returns a list of constraint residuals to append to the total residual vector. + """ + residuals = [] + weight_scale = 0.1 # Weight for soft constraints relative to reprojection errors + + # Constraint 1: Rigid body distances within each link + if enabled_constraints['RigidBodyDistances'].enabled: + for link_name in ['Arm1', 'Ellbow', 'Arm2']: + link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name] + if len(link_markers) < 2: + continue + + # Compute all pairwise distances in world coords + for i in range(len(link_markers)): + for j in range(i + 1, len(link_markers)): + m_i = link_markers[i] + m_j = link_markers[j] + + pos_i = compute_marker_world_position(m_i, link_transforms) + pos_j = compute_marker_world_position(m_j, link_transforms) + + dist_world = np.linalg.norm(pos_i - pos_j) + + # Reference distance in local coords + dist_local = np.linalg.norm(m_i['position_m'] - m_j['position_m']) + + # Residual: difference should be zero (rigid body) + error = dist_world - dist_local + residuals.append(error * weight_scale * 0.1) # Very soft weight + + # Constraint 2: Fixed X-distances between links (Arm1 <-> Ellbow) + if enabled_constraints['InterLinkXDistances'].enabled: + arm1_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm1'] + ellbow_markers = [m for m in robot_markers.values() if m['link_name'] == 'Ellbow'] + + if len(arm1_markers) >= 1 and len(ellbow_markers) >= 1: + # Get first marker from each link + m_arm1 = arm1_markers[0] + m_ellbow = ellbow_markers[0] + + pos_arm1 = compute_marker_world_position(m_arm1, link_transforms) + pos_ellbow = compute_marker_world_position(m_ellbow, link_transforms) + + # X-distance in world should match reference (relative position) + # Since both rotate around X-axis at different points, we check consistency + x_diff_world = pos_ellbow[0] - pos_arm1[0] + x_diff_ref = m_ellbow['position_m'][0] - m_arm1['position_m'][0] + + error = x_diff_world - x_diff_ref + residuals.append(error * weight_scale) + + return residuals + + +def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray: + """Compute the world position of a marker given current link transforms.""" + link_transform = link_transforms[marker['link_name']] + local_pos = np.concatenate([marker['position_m'], [1.0]]) + world_pos = (link_transform @ local_pos)[:3] + return world_pos + + +# ------------------------------------------------------------------ +# Optimization +# ------------------------------------------------------------------ + +def pack_parameters(robot_state: Dict[str, float], camera_params: List[Tuple[np.ndarray, np.ndarray]]) -> np.ndarray: + state_vec = np.asarray([robot_state[k] for k in STATE_KEYS], dtype=np.float64) + cams = [] + for rvec, tvec in camera_params: + cams.append(rvec.reshape(3)) + cams.append(tvec.reshape(3)) + return np.concatenate([state_vec] + cams) + + +def unpack_parameters(params: np.ndarray, n_views: int) -> Tuple[Dict[str, float], List[Tuple[np.ndarray, np.ndarray]]]: + robot_state = {STATE_KEYS[i]: float(params[i]) for i in range(len(STATE_KEYS))} + camera_params = [] + offset = len(STATE_KEYS) + for _ in range(n_views): + rvec = params[offset:offset + 3].reshape(3, 1) + tvec = params[offset + 3:offset + 6].reshape(3, 1) + camera_params.append((rvec, tvec)) + offset += 6 + return robot_state, camera_params + + +def residuals_for_parameters( + params: np.ndarray, + views: List[Dict[str, Any]], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + robot: Dict[str, Any], + scale: float, + default_state: Dict[str, float], + enabled_constraints: Dict[str, ConstraintResult] +) -> np.ndarray: + robot_state, camera_params = unpack_parameters(params, len(views)) + link_transforms = compute_link_transforms(robot, robot_state, scale) + + residuals = [] + + # Reprojection residuals (primary observation) + for obs in observations: + marker = robot_markers[obs['marker_id']] + world_corners = compute_marker_world_corners(marker, link_transforms) + rvec, tvec = camera_params[obs['view_index']] + proj = project_points(world_corners, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D']) + diffs = proj - obs['image_points_px'] + weight = math.sqrt(obs['confidence']) + residuals.extend((diffs * weight).reshape(-1)) + + # Weak priors on robot state + for key in STATE_KEYS: + diff = robot_state[key] - default_state.get(key, 0.0) + if key in ('x', 'y', 'z', 'e'): + w = 0.001 + else: + w = 0.01 + residuals.append(diff * w) + + # Soft constraints (kinematic consistency, rigid body constraints) + soft_constraint_residuals = compute_soft_constraint_residuals( + robot_state, robot_markers, link_transforms, robot, enabled_constraints + ) + residuals.extend(soft_constraint_residuals) + + return np.asarray(residuals, dtype=np.float64) + + + +def estimate_uncertainty(result: Any, n_params: int) -> np.ndarray: + if result.jac is None: + return np.full(n_params, float('nan'), dtype=np.float64) + J = result.jac + m, n = J.shape + JTJ = J.T @ J + try: + cov = np.linalg.pinv(JTJ) + except np.linalg.LinAlgError: + cov = np.linalg.pinv(JTJ + np.eye(n) * 1e-9) + residuals = result.fun + dof = max(1, m - n) + sigma2 = float(np.sum(residuals ** 2) / dof) + cov *= sigma2 + return np.sqrt(np.diag(cov)) + + +def print_constraint_sanity_check( + robot_state: Dict[str, float], + robot_markers: Dict[int, Dict[str, Any]], + link_transforms: Dict[str, np.ndarray], + robot: Dict[str, Any], + enabled_constraints: Dict[str, ConstraintResult] +) -> None: + """ + Print sanity checks for all constraints to verify the optimization result. + """ + print("\n" + "=" * 70) + print("CONSTRAINT SANITY CHECKS (after optimization)") + print("=" * 70) + + # Check 1: Rigid body distances + if enabled_constraints['RigidBodyDistances'].enabled: + print("\n1. RIGID BODY DISTANCES") + for link_name in ['Arm1', 'Ellbow', 'Arm2']: + link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name] + if len(link_markers) < 2: + continue + + max_error = 0.0 + for i in range(len(link_markers)): + for j in range(i + 1, len(link_markers)): + m_i = link_markers[i] + m_j = link_markers[j] + + pos_i = compute_marker_world_position(m_i, link_transforms) + pos_j = compute_marker_world_position(m_j, link_transforms) + + dist_world = np.linalg.norm(pos_i - pos_j) + dist_local = np.linalg.norm(m_i['position_m'] - m_j['position_m']) + error = abs(dist_world - dist_local) + max_error = max(max_error, error) + + status = "✓" if max_error < 1.0 else "⚠" if max_error < 5.0 else "✗" + print(f" {link_name:10s}: max_error = {max_error:.3f} mm {status}") + + # Check 2: Inter-link X distances + if enabled_constraints['InterLinkXDistances'].enabled: + print("\n2. INTER-LINK X-DISTANCES") + arm1_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm1'] + ellbow_markers = [m for m in robot_markers.values() if m['link_name'] == 'Ellbow'] + + if len(arm1_markers) >= 1 and len(ellbow_markers) >= 1: + m_arm1 = arm1_markers[0] + m_ellbow = ellbow_markers[0] + + pos_arm1 = compute_marker_world_position(m_arm1, link_transforms) + pos_ellbow = compute_marker_world_position(m_ellbow, link_transforms) + + x_diff_world = pos_ellbow[0] - pos_arm1[0] + x_diff_ref = m_ellbow['position_m'][0] - m_arm1['position_m'][0] + error = abs(x_diff_world - x_diff_ref) + + status = "✓" if error < 1.0 else "⚠" if error < 5.0 else "✗" + print(f" Arm1 <-> Ellbow: error = {error:.3f} mm {status}") + + # Check 3: Arm2 sin(a) dependency + if enabled_constraints['Arm2SinADependency'].enabled: + print("\n3. ARM2 sin(a) DEPENDENCY (sanity check)") + arm2_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm2'] + if len(arm2_markers) >= 2: + # Check that markers with different Z values have different X spreads + a_rad = math.radians(robot_state['a']) + sin_a = math.sin(a_rad) + cos_a = math.cos(a_rad) + + z_variations = {} + for m in arm2_markers: + z_local = m['position_m'][2] + x_local = m['position_m'][0] + pos_world = compute_marker_world_position(m, link_transforms) + x_world = pos_world[0] + + # Expected: x_world = 90 + x_local * cos(a) - z_local * sin(a) + x_expected = 90 * (robot.get('renderingInfo', {}).get('metric', 'mm') == 'mm' and 0.09 or 0.09) + x_local * cos_a - z_local * sin_a + x_error = abs(x_world - x_expected) + + if z_local not in z_variations: + z_variations[z_local] = [] + z_variations[z_local].append(x_error) + + max_error = max(max(errors) for errors in z_variations.values()) if z_variations else 0.0 + status = "✓" if max_error < 5.0 else "⚠" if max_error < 10.0 else "⚠" + print(f" X-consistency with sin(a): max_error = {max_error:.3f} mm {status}") + print(f" (Note: this is a consistency check, not a hard constraint)") + + print("=" * 70) + + +def camera_position_world(rvec: np.ndarray, tvec: np.ndarray) -> np.ndarray: + R, _ = cv2.Rodrigues(rvec) + return (-R.T @ tvec).reshape(3) + + +def build_output( + robot_state: Dict[str, float], + state_uncertainty: np.ndarray, + views: List[Dict[str, Any]], + camera_params: List[Tuple[np.ndarray, np.ndarray]], + observations: List[Dict[str, Any]], + robot_markers: Dict[int, Dict[str, Any]], + scale: float, + robot: Dict[str, Any], + robot_json_path: str +) -> Dict[str, Any]: + link_transforms = compute_link_transforms(robot, robot_state, scale) + + marker_summary: Dict[int, Dict[str, Any]] = {} + for marker_id, marker in robot_markers.items(): + marker_summary[marker_id] = { + 'marker_id': marker_id, + 'link_name': marker['link_name'], + 'position_world_m': compute_marker_world_position(marker, link_transforms).tolist(), + 'size_m': marker['size_m'], + 'observation_count': 0, + 'mean_confidence': None, + 'mean_reprojection_error_px': None, + 'observations': [] + } + + per_marker_errors: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + per_marker_confidences: Dict[int, List[float]] = {mid: [] for mid in marker_summary} + + link_transforms = compute_link_transforms(robot, robot_state, scale) + for obs in observations: + marker_id = obs['marker_id'] + marker = robot_markers[marker_id] + object_points_m = compute_marker_world_corners(marker, link_transforms) + rvec, tvec = camera_params[obs['view_index']] + proj = project_points(object_points_m, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D']) + diffs = proj - obs['image_points_px'] + errors = np.linalg.norm(diffs, axis=1) + repro_error = float(np.mean(errors)) + per_marker_errors[marker_id].extend(errors.tolist()) + per_marker_confidences[marker_id].append(obs['confidence']) + marker_summary[marker_id]['observation_count'] += 1 + marker_summary[marker_id]['observations'].append({ + 'view_index': obs['view_index'], + 'source_file': views[obs['view_index']]['source_file'], + 'image_file': views[obs['view_index']]['image_file'], + 'confidence': obs['confidence'], + 'mean_reprojection_error_px': repro_error, + 'corner_reprojection_errors_px': errors.tolist() + }) + + for marker_id, summary in marker_summary.items(): + if summary['observation_count'] > 0: + summary['mean_confidence'] = float(np.mean(per_marker_confidences[marker_id])) + summary['mean_reprojection_error_px'] = float(np.mean(per_marker_errors[marker_id])) + + camera_outputs = [] + for idx, view in enumerate(views): + rvec, tvec = camera_params[idx] + cam_pos = camera_position_world(rvec, tvec) + observed_count = sum(1 for obs in observations if obs['view_index'] == idx) + camera_outputs.append({ + 'view_index': idx, + 'source_file': view['source_file'], + 'camera_id': view['camera_id'], + 'camera_position_world_m': cam_pos.tolist(), + 'rvec': rvec.reshape(-1).tolist(), + 'tvec': tvec.reshape(-1).tolist(), + 'intrinsics': { + 'camera_matrix': view['K'].tolist(), + 'distortion_coefficients': view['D'].reshape(-1).tolist() + }, + 'observation_count': observed_count + }) + + robot_pose_output = { + 'state': {k: float(robot_state[k]) for k in STATE_KEYS}, + 'uncertainty': { + 'x_mm': float(state_uncertainty[0]), + 'y_mm': float(state_uncertainty[1]), + 'z_mm': float(state_uncertainty[2]), + 'a_deg': float(state_uncertainty[3]), + 'b_deg': float(state_uncertainty[4]), + 'c_deg': float(state_uncertainty[5]), + 'e_mm': float(state_uncertainty[6]) + }, + 'confidence': { + 'x': float(math.exp(-state_uncertainty[0] / 10.0)), + 'y': float(math.exp(-state_uncertainty[1] / 10.0)), + 'z': float(math.exp(-state_uncertainty[2] / 10.0)), + 'a': float(math.exp(-state_uncertainty[3] / 10.0)), + 'b': float(math.exp(-state_uncertainty[4] / 10.0)), + 'c': float(math.exp(-state_uncertainty[5] / 10.0)), + 'e': float(math.exp(-state_uncertainty[6] / max(1.0, state_uncertainty[6]))) + } + } + + return { + 'schema_version': '1.0', + 'created_utc': datetime.datetime.utcnow().isoformat() + 'Z', + 'source_robot_json': os.path.abspath(robot_json_path), + 'source_detections': [view['source_file'] for view in views], + 'robot_pose': robot_pose_output, + 'camera_poses': camera_outputs, + 'marker_positions': list(marker_summary.values()) + } + + +# ------------------------------------------------------------------ +# Main +# ------------------------------------------------------------------ + +def main() -> None: + parser = argparse.ArgumentParser(description='Multiview optimization of robot pose and camera extrinsics') + parser.add_argument('--robot', required=True, help='Path to robot.json') + parser.add_argument('--detections', required=True, nargs='+', help='List of detection JSON files') + parser.add_argument('--outDir', required=True, help='Output directory') + parser.add_argument('--write-summary', action='store_true', help='Write summary file') + parser.add_argument('--max-iter', type=int, default=500, help='Maximum optimizer iterations') + args = parser.parse_args() + + os.makedirs(args.outDir, exist_ok=True) + + robot_json_path = os.path.abspath(args.robot) + robot = load_json(robot_json_path) + scale = parse_metric_scale(robot) + + default_state = { + k: float(robot.get('defaultPosition', {}).get(k, 0.0) or 0.0) + for k in STATE_KEYS + } + + robot_markers = extract_markers(robot, scale) + + # Validate constraints + print("\n" + "=" * 70) + print("CONSTRAINT VALIDATION") + print("=" * 70) + enabled_constraints = validate_constraints(robot, robot_markers) + for constraint_name, result in enabled_constraints.items(): + print(result) + print("=" * 70) + + views, observations = collect_views_and_observations(args.detections, robot_markers) + + camera_guesses = [] + for view in views: + rvec, tvec = initial_camera_guess(view, observations, robot_markers, default_state, scale, robot) + camera_guesses.append((rvec, tvec)) + + x0 = pack_parameters(default_state, camera_guesses) + + progress = { + 'iter': 0, + 'last_cost': None, + 'last_print': time.time(), + 'prev_x': x0.copy() + } + + def progress_callback(xk: np.ndarray) -> None: + progress['iter'] += 1 + now = time.time() + if progress['iter'] == 1 or now - progress['last_print'] >= 1.0: + res = residuals_for_parameters(xk, views, observations, robot_markers, robot, scale, default_state, enabled_constraints) + cost = 0.5 * float(np.dot(res, res)) + delta_cost = None + convergence = '' + if progress['last_cost'] is not None: + delta_cost = cost - progress['last_cost'] + if abs(delta_cost) < 1e-3: + convergence = ' stable' + elif delta_cost < 0: + convergence = ' improving' + else: + convergence = ' worsening' + step_norm = float(np.linalg.norm(xk - progress['prev_x'])) + print( + f'[Multiview] iter={progress["iter"]:4d} cost={cost:.4f}' + + (f' delta={delta_cost:.4g}' if delta_cost is not None else '') + + f' step={step_norm:.4g}' + + convergence + ) + progress['last_cost'] = cost + progress['last_print'] = now + progress['prev_x'] = xk.copy() + + result = least_squares( + residuals_for_parameters, + x0, + args=(views, observations, robot_markers, robot, scale, default_state, enabled_constraints), + jac='2-point', + method='trf', + loss='soft_l1', + f_scale=1.0, + max_nfev=args.max_iter, + callback=progress_callback + ) + + robot_state, camera_params = unpack_parameters(result.x, len(views)) + uncertainties = estimate_uncertainty(result, len(result.x)) + + # Print constraint sanity checks + link_transforms = compute_link_transforms(robot, robot_state, scale) + print_constraint_sanity_check(robot_state, robot_markers, link_transforms, robot, enabled_constraints) + + output = build_output(robot_state, uncertainties[:len(STATE_KEYS)], views, camera_params, observations, robot_markers, scale, robot, robot_json_path) + + out_path = Path(args.outDir) / 'multiview_pose.json' + save_json(output, out_path) + + print(f'Saved: {out_path}') + if args.write_summary: + summary_path = Path(args.outDir) / 'multiview_pose_summary.json' + summary = { + 'final_cost': float(result.cost), + 'status': int(result.status), + 'message': result.message, + 'robot_state': output['robot_pose'], + 'camera_count': len(views), + 'marker_count': len(robot_markers) + } + save_json(summary, summary_path) + print(f'Saved: {summary_path}') + + +if __name__ == '__main__': + main() diff --git a/pipeline/__pycache__/2_Multiview.cpython-311.pyc b/pipeline/__pycache__/2_Multiview.cpython-311.pyc index 141feee61fbcc3273b9b979dbf9a002f96c69779..92ecbda38895def5e5d93c0f5e5df5aa61f0504d 100644 GIT binary patch literal 76709 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 diff --git a/pipeline/multiview_pose.json b/pipeline/multiview_pose.json index 7942ef2..11a79d5 100644 --- a/pipeline/multiview_pose.json +++ b/pipeline/multiview_pose.json @@ -1,36 +1,36 @@ { "schema_version": "1.0", - "created_utc": "2026-05-28T20:33:16.813434Z", - "source_robot_json": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json", + "created_utc": "2026-05-28T21:18:54.942233Z", + "source_robot_json": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json", "source_detections": [ - "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", - "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", - "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json" + "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json" ], "robot_pose": { "state": { - "x": 216.09132484852927, - "y": 39.690843564316594, - "z": -55.07137540784025, - "a": -131.24664072646647, - "b": 22.00000000000524, - "c": 91.0000000000042, - "e": 9.999999999997005 + "x": 100.21525656040845, + "y": 29.397626553503173, + "z": -36.04825291833897, + "a": -120.21337951359496, + "b": 22.00000000000008, + "c": 90.99999999999989, + "e": 10.000000000000258 }, "uncertainty": { - "x_mm": 225.298670975141, - "y_mm": 594.8766035835473, - "z_mm": 259.0225242970393, - "a_deg": 1077.9536617200663, - "b_deg": 12207.30048251762, - "c_deg": 12207.300378181952, - "e_mm": 122073.0039182515 + "x_mm": 17194.235819424397, + "y_mm": 6014.1408132596725, + "z_mm": 3621.298501957444, + "a_deg": 8796.009134059024, + "b_deg": 12562.017200522667, + "c_deg": 12562.017199380893, + "e_mm": 125620.17196327279 }, "confidence": { - "x": 1.6421130086210916e-10, - "y": 1.4616297196124474e-26, - "z": 5.6337127678067286e-12, - "a": 1.531324732033841e-47, + "x": 0.0, + "y": 6.444409678452354e-262, + "z": 5.358019964976179e-158, + "a": 0.0, "b": 0.0, "c": 0.0, "e": 0.36787944117144233 @@ -39,22 +39,22 @@ "camera_poses": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "camera_id": "cam1", "camera_position_world_m": [ - -38.34617352837666, - -74.24635979425081, - -20.188423219708135 + -16.343352953589292, + -63.12937932142545, + -42.02161528801491 ], "rvec": [ - 111.52327474141508, - -126.11068340719217, - 44.203270370543855 + 118.44192782487391, + -119.13238841589752, + 44.062625605263676 ], "tvec": [ - 0.46738739070887664, - 3.5663317158792367, - -85.89287645773129 + 0.4631129054169859, + 3.1298682546353254, + -77.51276876640009 ], "intrinsics": { "camera_matrix": [ @@ -86,22 +86,22 @@ }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "camera_id": "cam1", "camera_position_world_m": [ - -18.749065173061467, - 50.58295606304779, - -8.605729054607524 + -57.84264421184561, + 32.9115287367793, + 4.0804577977879966 ], "rvec": [ - 0.329953779441624, - 1.990640379537416, - 1.5517306914240998 + -0.8564265551247701, + 6.744826532647081, + 4.65580193037499 ], "tvec": [ - -1.9764953616377263, - 2.0849863370638175, - -54.55243128918203 + -2.6186145648975523, + 2.5499603647238676, + -66.57500213254504 ], "intrinsics": { "camera_matrix": [ @@ -133,22 +133,22 @@ }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "camera_id": "cam1", "camera_position_world_m": [ - 0.2728534702741987, - -0.6585880592537502, - 0.4418986748635548 + 0.3512702688674852, + -1.2406150586644955, + 0.23747342372270985 ], "rvec": [ - 2.1894889099532713, - -0.20366077493465337, - -0.3361765800667276 + -4.448618579992061, + -0.5683334688730436, + 0.6217033179458588 ], "tvec": [ - -0.13776092296131418, - 0.016095933671670594, - 0.8271786602074209 + 0.020309498830570788, + 0.02559958355628428, + 1.3106648458431038 ], "intrinsics": { "camera_matrix": [ @@ -190,40 +190,40 @@ ], "size_m": 0.025, "observation_count": 3, - "mean_confidence": 0.6997700579082148, + "mean_confidence": 0.6882418402751833, "mean_detector_confidence": 0.6997700579082148, - "mean_reprojection_error_px": 348.3808535157419, + "mean_reprojection_error_px": 441.0426420373767, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.5673043275049208, - "confidence_weighted": 0.5673043275049208, + "confidence_weighted": 0.556048533067895, "quality": { "detector_confidence": 0.5673043275049208, - "weighted_confidence": 0.5673043275049208, + "weighted_confidence": 0.556048533067895, "q": { "size": 0.8108224868774414, "aspect": 0.9096486158109359, "border": 0.2833333333333333, - "center": 0.2385730743408203, + "center": 0.0, "sharpness": 1.0, "homography": 0.9999983069718208, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, - "border": 1.0, - "center": 1.0, + "size": 0.9981082248687744, + "aspect": 0.9990964861581093, + "border": 0.9928333333333333, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9999999830697182, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9801591599229813, "raw": { "mean_edge_px": 40.54112434387207, "edge_ratio": 1.1986511772098227, @@ -232,44 +232,44 @@ "laplacian_var": 3797.5312506581813 } }, - "mean_reprojection_error_px": 587.0995139995744, + "mean_reprojection_error_px": 585.2466131635871, "corner_reprojection_errors_px": [ - 612.7534949573636, - 568.3181566115126, - 561.4573585579957, - 605.8690458714257 + 610.9384647476247, + 566.4528099051602, + 559.5701708238369, + 604.0250071777266 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.7813266383036261, - "confidence_weighted": 0.7813266383036261, + "confidence_weighted": 0.7686240953581938, "quality": { "detector_confidence": 0.7813266383036261, - "weighted_confidence": 0.7813266383036261, + "weighted_confidence": 0.7686240953581938, "q": { "size": 0.7824347686767578, "aspect": 0.8792155830729744, "border": 1.0, - "center": 0.49675697088241577, + "center": 0.0, "sharpness": 0.9904164099455028, "homography": 0.7145588675251666, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9978243476867675, + "aspect": 0.9987921558307298, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.999904164099455, + "homography": 0.9971455886752516, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9837423398579991, "raw": { "mean_edge_px": 39.12173843383789, "edge_ratio": 1.274754950327127, @@ -278,44 +278,44 @@ "laplacian_var": 2476.0410248637572 } }, - "mean_reprojection_error_px": 427.8772796416253, + "mean_reprojection_error_px": 432.8261187281506, "corner_reprojection_errors_px": [ - 446.3587605969117, - 404.1867109578964, - 408.4069136534603, - 452.55673335823275 + 451.23541594327764, + 408.7849241393045, + 413.4238931097967, + 457.8602417202238 ] }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.7506792079160973, - "confidence_weighted": 0.7506792079160973, + "confidence_weighted": 0.740052892399461, "quality": { "detector_confidence": 0.7506792079160973, - "weighted_confidence": 0.7506792079160973, + "weighted_confidence": 0.740052892399461, "q": { "size": 0.8434026336669922, "aspect": 0.8575862494073492, "border": 1.0, - "center": 0.6442468464374542, + "center": 0.0, "sharpness": 1.0, "homography": 0.8786678750976363, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9984340263366699, + "aspect": 0.9985758624940735, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9987866787509764, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9858443987730322, "raw": { "mean_edge_px": 42.17013168334961, "edge_ratio": 1.3321269451115116, @@ -324,12 +324,12 @@ "laplacian_var": 2842.0926549741707 } }, - "mean_reprojection_error_px": 30.16576690602588, + "mean_reprojection_error_px": 305.0551942203927, "corner_reprojection_errors_px": [ - 36.22778096708586, - 21.548187717111936, - 42.84248647061261, - 20.044612469293106 + 336.8958377277025, + 282.2427865676158, + 272.7772202436447, + 328.3049323426078 ] } ] @@ -344,40 +344,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.6412317666518965, + "mean_confidence": 0.6305246964757232, "mean_detector_confidence": 0.6412317666518965, - "mean_reprojection_error_px": 213.51155158837116, + "mean_reprojection_error_px": 211.60483046120936, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.6412317666518965, - "confidence_weighted": 0.6412317666518965, + "confidence_weighted": 0.6305246964757232, "quality": { "detector_confidence": 0.6412317666518965, - "weighted_confidence": 0.6412317666518965, + "weighted_confidence": 0.6305246964757232, "q": { "size": 0.6744685745239258, "aspect": 0.9460729944417767, "border": 1.0, - "center": 0.7332987189292908, + "center": 0.0, "sharpness": 1.0, "homography": 0.7016198391981602, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9967446857452392, + "aspect": 0.9994607299444178, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9970161983919816, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9833023397576218, "raw": { "mean_edge_px": 33.72342872619629, "edge_ratio": 1.1140017860673477, @@ -386,12 +386,12 @@ "laplacian_var": 3165.7184033540047 } }, - "mean_reprojection_error_px": 213.51155158837116, + "mean_reprojection_error_px": 211.60483046120936, "corner_reprojection_errors_px": [ - 236.74653013262179, - 201.34252196509405, - 190.76098744554534, - 225.1961668102236 + 234.98916143435187, + 199.5386782266223, + 188.70294650504465, + 223.18853567881862 ] } ] @@ -406,40 +406,40 @@ ], "size_m": 0.025, "observation_count": 2, - "mean_confidence": 0.8108527003549935, + "mean_confidence": 0.7997306526656554, "mean_detector_confidence": 0.8108527003549935, - "mean_reprojection_error_px": 159.0807109791299, + "mean_reprojection_error_px": 157.11052614310307, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.7952557920267838, - "confidence_weighted": 0.7952557920267838, + "confidence_weighted": 0.7849458403437536, "quality": { "detector_confidence": 0.7952557920267838, - "weighted_confidence": 0.7952557920267838, + "weighted_confidence": 0.7849458403437536, "q": { "size": 0.7146316909790039, "aspect": 0.9859016728248396, "border": 1.0, - "center": 0.786280557513237, + "center": 0.0, "sharpness": 1.0, "homography": 1.0, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.99714631690979, + "aspect": 0.9998590167282484, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, "homography": 1.0, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9870356786000208, "raw": { "mean_edge_px": 35.731584548950195, "edge_ratio": 1.0285998645985972, @@ -448,44 +448,44 @@ "laplacian_var": 3704.0260869185627 } }, - "mean_reprojection_error_px": 202.87090079707735, + "mean_reprojection_error_px": 200.42541542932048, "corner_reprojection_errors_px": [ - 216.05481694083824, - 182.3251487108743, - 191.11678207445826, - 221.98685546213872 + 213.57305595230443, + 179.84901855319694, + 188.742976102597, + 219.5366111091836 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.826449608683203, - "confidence_weighted": 0.826449608683203, + "confidence_weighted": 0.8145154649875572, "quality": { "detector_confidence": 0.826449608683203, - "weighted_confidence": 0.826449608683203, + "weighted_confidence": 0.8145154649875572, "q": { "size": 0.8409605407714844, "aspect": 0.9049793706370471, "border": 1.0, - "center": 0.9083917960524559, + "center": 0.0, "sharpness": 0.8049029197222222, "homography": 1.0, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9984096054077148, + "aspect": 0.9990497937063705, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, + "center": 0.99, + "sharpness": 0.9980490291972222, "homography": 1.0, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9855597442720546, "raw": { "mean_edge_px": 42.04802703857422, "edge_ratio": 1.2099951279465397, @@ -494,12 +494,12 @@ "laplacian_var": 2012.2572993055553 } }, - "mean_reprojection_error_px": 115.2905211611824, + "mean_reprojection_error_px": 113.79563685688566, "corner_reprojection_errors_px": [ - 102.14384765398431, - 92.14591665858303, - 129.81168442827544, - 137.0606359038868 + 100.764594616081, + 90.3700880410387, + 128.18921061659032, + 135.8586541538326 ] } ] @@ -514,40 +514,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.6041252446922665, + "mean_confidence": 0.5924337979452556, "mean_detector_confidence": 0.6041252446922665, - "mean_reprojection_error_px": 338.5476435670504, + "mean_reprojection_error_px": 175.5030184777268, "observations": [ { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.6041252446922665, - "confidence_weighted": 0.6041252446922665, + "confidence_weighted": 0.5924337979452556, "quality": { "detector_confidence": 0.6041252446922665, - "weighted_confidence": 0.6041252446922665, + "weighted_confidence": 0.5924337979452556, "q": { "size": 0.9836519622802734, "aspect": 0.7532145593882991, "border": 1.0, - "center": 0.32276880741119385, + "center": 0.0, "sharpness": 0.5256355772646303, "homography": 0.7899472530560614, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9998365196228027, + "aspect": 0.997532145593883, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9952563557726463, + "homography": 0.9978994725305605, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.980647313036941, "raw": { "mean_edge_px": 49.18259811401367, "edge_ratio": 1.655285901037602, @@ -556,12 +556,12 @@ "laplacian_var": 1314.0889431615756 } }, - "mean_reprojection_error_px": 338.5476435670504, + "mean_reprojection_error_px": 175.5030184777268, "corner_reprojection_errors_px": [ - 334.40236466141283, - 319.8057940927665, - 346.37370249262483, - 353.6087130213974 + 174.74636916466218, + 153.27311517519206, + 183.05906730414804, + 190.93352226690504 ] } ] @@ -576,40 +576,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.6280424706698967, + "mean_confidence": 0.6179064007574118, "mean_detector_confidence": 0.6280424706698967, - "mean_reprojection_error_px": 100.412293994181, + "mean_reprojection_error_px": 98.18080189388743, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.6280424706698967, - "confidence_weighted": 0.6280424706698967, + "confidence_weighted": 0.6179064007574118, "quality": { "detector_confidence": 0.6280424706698967, - "weighted_confidence": 0.6280424706698967, + "weighted_confidence": 0.6179064007574118, "q": { "size": 0.6625362014770508, "aspect": 0.9537163525948422, "border": 1.0, - "center": 0.9434776194393635, + "center": 0.0, "sharpness": 1.0, "homography": 0.7625645902124035, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9966253620147705, + "aspect": 0.9995371635259485, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.997625645902124, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9838608527514494, "raw": { "mean_edge_px": 33.12681007385254, "edge_ratio": 1.0970595655180506, @@ -618,12 +618,12 @@ "laplacian_var": 2848.9886204705635 } }, - "mean_reprojection_error_px": 100.412293994181, + "mean_reprojection_error_px": 98.18080189388743, "corner_reprojection_errors_px": [ - 95.36403494262059, - 79.9557213035894, - 108.22499615920151, - 118.10442357131254 + 92.6502159223099, + 77.77959676707061, + 106.52467862737805, + 115.76871625879116 ] } ] @@ -638,40 +638,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.33820423087105295, + "mean_confidence": 0.33185586633646474, "mean_detector_confidence": 0.33820423087105295, - "mean_reprojection_error_px": 276.15167212095713, + "mean_reprojection_error_px": 278.58790262226194, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.33820423087105295, - "confidence_weighted": 0.33820423087105295, + "confidence_weighted": 0.33185586633646474, "quality": { "detector_confidence": 0.33820423087105295, - "weighted_confidence": 0.33820423087105295, + "weighted_confidence": 0.33185586633646474, "q": { "size": 0.5361141967773437, "aspect": 0.8936906729306451, "border": 0.9833333333333333, - "center": 0.5868735313415527, + "center": 0.0, "sharpness": 1.0, "homography": 0.6985669093200941, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, - "border": 1.0, - "center": 1.0, + "size": 0.9953611419677735, + "aspect": 0.9989369067293065, + "border": 0.9998333333333334, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.996985669093201, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9812291983508372, "raw": { "mean_edge_px": 26.805709838867188, "edge_ratio": 1.2379107901411548, @@ -680,12 +680,12 @@ "laplacian_var": 4112.338450277431 } }, - "mean_reprojection_error_px": 276.15167212095713, + "mean_reprojection_error_px": 278.58790262226194, "corner_reprojection_errors_px": [ - 257.9111382626554, - 282.35364104879613, - 294.3934037418535, - 269.9485054305234 + 260.5220487854409, + 284.9566681230145, + 296.68536419274807, + 272.18752938784417 ] } ] @@ -700,40 +700,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.28724459014346065, + "mean_confidence": 0.2820948444008322, "mean_detector_confidence": 0.28724459014346065, - "mean_reprojection_error_px": 426.6190397265363, + "mean_reprojection_error_px": 428.543316908284, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.28724459014346065, - "confidence_weighted": 0.28724459014346065, + "confidence_weighted": 0.2820948444008322, "quality": { "detector_confidence": 0.28724459014346065, - "weighted_confidence": 0.28724459014346065, + "weighted_confidence": 0.2820948444008322, "q": { "size": 0.5248580741882324, "aspect": 0.839187418386558, "border": 0.8333333333333334, - "center": 0.4003753066062927, + "center": 0.0, "sharpness": 1.0, "homography": 0.9999815099800249, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, - "border": 1.0, - "center": 1.0, + "size": 0.9952485807418823, + "aspect": 0.9983918741838655, + "border": 0.9983333333333333, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9999998150998003, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9820719139042569, "raw": { "mean_edge_px": 26.24290370941162, "edge_ratio": 1.383257847031654, @@ -742,12 +742,12 @@ "laplacian_var": 5067.414824476962 } }, - "mean_reprojection_error_px": 426.6190397265363, + "mean_reprojection_error_px": 428.543316908284, "corner_reprojection_errors_px": [ - 406.15079651506966, - 428.05267082059254, - 446.9579519399622, - 425.3147396305208 + 408.1250357795538, + 430.13216881859233, + 448.84097211823865, + 427.0750909167511 ] } ] @@ -762,40 +762,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.27228561618555186, + "mean_confidence": 0.2656859333164099, "mean_detector_confidence": 0.27228561618555186, - "mean_reprojection_error_px": 374.50255105417807, + "mean_reprojection_error_px": 376.9955122102373, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.27228561618555186, - "confidence_weighted": 0.27228561618555186, + "confidence_weighted": 0.2656859333164099, "quality": { "detector_confidence": 0.27228561618555186, - "weighted_confidence": 0.27228561618555186, + "weighted_confidence": 0.2656859333164099, "q": { "size": 0.5014523983001709, "aspect": 0.8554115971298921, "border": 0.5, - "center": 0.4565395712852478, + "center": 0.0, "sharpness": 1.0, "homography": 0.6975670005943979, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, - "border": 1.0, - "center": 1.0, + "size": 0.9950145239830017, + "aspect": 0.998554115971299, + "border": 0.995, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.996975670005944, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9757619114751748, "raw": { "mean_edge_px": 25.072619915008545, "edge_ratio": 1.3380557461583085, @@ -804,12 +804,12 @@ "laplacian_var": 4016.6321593416924 } }, - "mean_reprojection_error_px": 374.50255105417807, + "mean_reprojection_error_px": 376.9955122102373, "corner_reprojection_errors_px": [ - 357.4436839806056, - 379.04037742345474, - 391.88854109463733, - 369.63760171801465 + 360.06398388824374, + 381.6816681569444, + 394.27105560686095, + 371.9653411889002 ] } ] @@ -824,40 +824,40 @@ ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.35596573288593486, + "mean_confidence": 0.3494362969135403, "mean_detector_confidence": 0.35596573288593486, - "mean_reprojection_error_px": 330.11805773797414, + "mean_reprojection_error_px": 331.82951948750804, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.35596573288593486, - "confidence_weighted": 0.35596573288593486, + "confidence_weighted": 0.3494362969135403, "quality": { "detector_confidence": 0.35596573288593486, - "weighted_confidence": 0.35596573288593486, + "weighted_confidence": 0.3494362969135403, "q": { "size": 0.5561128711700439, "aspect": 0.8721453879265322, "border": 1.0, - "center": 0.5334544777870178, + "center": 0.0, "sharpness": 1.0, "homography": 0.726899391813532, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9955611287117004, + "aspect": 0.9987214538792654, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9972689939181353, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9816571221070685, "raw": { "mean_edge_px": 27.805643558502197, "edge_ratio": 1.2931956388084183, @@ -866,12 +866,12 @@ "laplacian_var": 3793.153007417641 } }, - "mean_reprojection_error_px": 330.11805773797414, + "mean_reprojection_error_px": 331.82951948750804, "corner_reprojection_errors_px": [ - 308.2113330466644, - 332.08999978676184, - 351.98742228618335, - 328.1834758322872 + 309.97533094447215, + 333.9867434221059, + 353.66344926865025, + 329.6925543148037 ] } ] @@ -880,46 +880,46 @@ "marker_id": 198, "link_name": "Arm1", "position_world_m": [ - 0.21609132484852928, - -0.10076769046005479, - 0.12911573319834646 + 0.10021525656040844, + -0.12221709509972764, + 0.10903202128450216 ], "size_m": 0.025, "observation_count": 2, - "mean_confidence": 0.16652042240877096, + "mean_confidence": 0.1628904920977715, "mean_detector_confidence": 0.16652042240877096, - "mean_reprojection_error_px": 243.66958450030666, + "mean_reprojection_error_px": 245.0843876652231, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.20150745250271476, - "confidence_weighted": 0.20150745250271476, + "confidence_weighted": 0.19749969171522036, "quality": { "detector_confidence": 0.20150745250271476, - "weighted_confidence": 0.20150745250271476, + "weighted_confidence": 0.19749969171522036, "q": { "size": 0.6339855194091797, "aspect": 0.7539930512070505, "border": 1.0, - "center": 0.6189768612384796, + "center": 0.0, "sharpness": 1.0, "homography": 0.6098583763162305, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9963398551940917, + "aspect": 0.9975399305120705, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9960985837631623, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9801111038935872, "raw": { "mean_edge_px": 31.699275970458984, "edge_ratio": 1.652544339497884, @@ -928,44 +928,44 @@ "laplacian_var": 3972.096222043203 } }, - "mean_reprojection_error_px": 248.654861221765, + "mean_reprojection_error_px": 248.07751803913456, "corner_reprojection_errors_px": [ - 274.50699319923274, - 246.03321812358865, - 223.2084648682347, - 250.8707686960039 + 273.96976726259226, + 245.4724112741704, + 222.59090992086755, + 250.2769836989081 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.13153339231482716, - "confidence_weighted": 0.13153339231482716, + "confidence_weighted": 0.12828129248032266, "quality": { "detector_confidence": 0.13153339231482716, - "weighted_confidence": 0.13153339231482716, + "weighted_confidence": 0.12828129248032266, "q": { "size": 0.6576880264282227, "aspect": 0.42738020051250675, "border": 0.5916666666666667, - "center": 0.6157204806804657, + "center": 0.0, "sharpness": 0.9362022578647182, "homography": 0.8917140926962271, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, - "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "size": 0.9965768802642823, + "aspect": 0.994273802005125, + "border": 0.9959166666666667, + "center": 0.99, + "sharpness": 0.9993620225786471, + "homography": 0.9989171409269623, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9752754811742362, "raw": { "mean_edge_px": 32.88440132141113, "edge_ratio": 3.6796739708616246, @@ -974,12 +974,12 @@ "laplacian_var": 2340.5056446617955 } }, - "mean_reprojection_error_px": 238.6843077788483, + "mean_reprojection_error_px": 242.0912572913116, "corner_reprojection_errors_px": [ - 255.20670426390527, - 233.9309789087676, - 221.28127646869942, - 244.318271474021 + 258.3749702118821, + 237.59562364753367, + 224.8980425891153, + 247.4963927167153 ] } ] @@ -988,46 +988,46 @@ "marker_id": 229, "link_name": "Arm1", "position_world_m": [ - 0.21609132484852928, - -0.1700228368607847, - 0.18659376984725667 + 0.10021525656040844, + -0.2006281682211539, + 0.15321011101172263 ], "size_m": 0.025, "observation_count": 2, - "mean_confidence": 0.2274747191640703, + "mean_confidence": 0.22245621469865084, "mean_detector_confidence": 0.2274747191640703, - "mean_reprojection_error_px": 172.23377000525875, + "mean_reprojection_error_px": 173.6079280482102, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.26810902547334975, - "confidence_weighted": 0.26810902547334975, + "confidence_weighted": 0.2630164795315655, "quality": { "detector_confidence": 0.26810902547334975, - "weighted_confidence": 0.26810902547334975, + "weighted_confidence": 0.2630164795315655, "q": { "size": 0.6824631023406983, "aspect": 0.7938768481947832, "border": 1.0, - "center": 0.733243316411972, + "center": 0.0, "sharpness": 0.9086003975826067, "homography": 0.7035912150387432, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.996824631023407, + "aspect": 0.9979387684819478, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.999086003975826, + "homography": 0.9970359121503874, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9810056900069167, "raw": { "mean_edge_px": 34.12315511703491, "edge_ratio": 1.5192824359947652, @@ -1036,44 +1036,44 @@ "laplacian_var": 2271.5009939565166 } }, - "mean_reprojection_error_px": 155.22297984485186, + "mean_reprojection_error_px": 154.45285855459778, "corner_reprojection_errors_px": [ - 179.66512440166122, - 156.3996648998681, - 130.99743974604678, - 153.82969033183136 + 178.89833200454908, + 155.66332803657198, + 130.23647076368366, + 153.01330341358639 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.18684041285479083, - "confidence_weighted": 0.18684041285479083, + "confidence_weighted": 0.18189594986573623, "quality": { "detector_confidence": 0.18684041285479083, - "weighted_confidence": 0.18684041285479083, + "weighted_confidence": 0.18189594986573623, "q": { "size": 0.7395829486846924, "aspect": 0.44796521997625727, "border": 1.0, - "center": 0.6955729424953461, + "center": 0.0, "sharpness": 0.5568649165432099, "homography": 0.5823463065693475, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9973958294868469, + "aspect": 0.9944796521997625, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9955686491654321, + "homography": 0.9958234630656935, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9735364372540894, "raw": { "mean_edge_px": 36.97914743423462, "edge_ratio": 3.4646323214690695, @@ -1082,12 +1082,12 @@ "laplacian_var": 1392.1622913580247 } }, - "mean_reprojection_error_px": 189.24456016566566, + "mean_reprojection_error_px": 192.76299754182264, "corner_reprojection_errors_px": [ - 210.91668755526047, - 180.02751146800094, - 165.080603015969, - 200.95343862343222 + 214.02378857320852, + 184.04050020559077, + 168.9825131053272, + 204.00518828316393 ] } ] @@ -1096,9 +1096,9 @@ "marker_id": 242, "link_name": "Arm1", "position_world_m": [ - 0.21609132484852928, - -0.21472797647660377, - 0.13272865598002226 + 0.10021525656040844, + -0.23498890467565872, + 0.09222372080616886 ], "size_m": 0.025, "observation_count": 0, @@ -1111,46 +1111,46 @@ "marker_id": 243, "link_name": "Arm1", "position_world_m": [ - 0.21609132484852928, - -0.21930796360231145, - 0.182013782721549 + 0.10021525656040844, + -0.2483017315511832, + 0.13989728413619817 ], "size_m": 0.025, "observation_count": 3, - "mean_confidence": 0.9174550709990235, + "mean_confidence": 0.903504284314228, "mean_detector_confidence": 0.9174550709990235, - "mean_reprojection_error_px": 76.42176737251886, + "mean_reprojection_error_px": 81.5029264885797, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.8899728634608616, - "confidence_weighted": 0.8899728634608616, + "confidence_weighted": 0.8780472636948905, "quality": { "detector_confidence": 0.8899728634608616, - "weighted_confidence": 0.8899728634608616, + "weighted_confidence": 0.8780472636948905, "q": { "size": 0.8810945701599121, "aspect": 0.9417837479752699, "border": 1.0, - "center": 0.8596725612878799, + "center": 0.0, "sharpness": 1.0, "homography": 0.8333271723300208, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9988109457015991, + "aspect": 0.9994178374797527, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9983332717233002, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9866000411297984, "raw": { "mean_edge_px": 44.054728507995605, "edge_ratio": 1.1236297656439467, @@ -1159,28 +1159,28 @@ "laplacian_var": 2678.6956247003295 } }, - "mean_reprojection_error_px": 77.81290659108863, + "mean_reprojection_error_px": 76.87929689884885, "corner_reprojection_errors_px": [ - 100.17650566852666, - 71.07480085005695, - 49.77846871424659, - 90.22185113152435 + 99.14225948732798, + 70.2117506861392, + 48.833911722956465, + 89.3292656989718 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.9132502955127223, - "confidence_weighted": 0.9132502955127223, + "confidence_weighted": 0.8990944567220429, "quality": { "detector_confidence": 0.9132502955127223, - "weighted_confidence": 0.9132502955127223, + "weighted_confidence": 0.8990944567220429, "q": { "size": 1.0, "aspect": 0.9546584653922503, "border": 1.0, - "center": 0.8345897197723389, + "center": 0.0, "sharpness": 0.5809177986450728, "homography": 0.9082012101971718, "normal_visibility": 1.0, @@ -1188,15 +1188,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9995465846539225, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9958091779864507, + "homography": 0.9990820121019717, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9844994971693583, "raw": { "mean_edge_px": 55.26974582672119, "edge_ratio": 1.0949900645130086, @@ -1205,28 +1205,28 @@ "laplacian_var": 1452.2944966126818 } }, - "mean_reprojection_error_px": 122.71205765924921, + "mean_reprojection_error_px": 123.84143010803572, "corner_reprojection_errors_px": [ - 157.5892876335061, - 109.93561027893956, - 83.96066221414912, - 139.36267051040204 + 159.2946943920584, + 112.54406878360543, + 84.16105909185964, + 139.36589816461947 ] }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.9491420540234864, - "confidence_weighted": 0.9491420540234864, + "confidence_weighted": 0.9333711325257503, "quality": { "detector_confidence": 0.9491420540234864, - "weighted_confidence": 0.9491420540234864, + "weighted_confidence": 0.9333711325257503, "q": { "size": 1.0, "aspect": 0.9739075220959238, "border": 1.0, - "center": 0.8123328536748886, + "center": 0.0, "sharpness": 0.43477113518953137, "homography": 0.9224349218355518, "normal_visibility": 1.0, @@ -1234,15 +1234,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9997390752209593, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9943477113518953, + "homography": 0.9992243492183555, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9833840240974658, "raw": { "mean_edge_px": 75.11714553833008, "edge_ratio": 1.0535830709016873, @@ -1251,12 +1251,12 @@ "laplacian_var": 1086.9278379738284 } }, - "mean_reprojection_error_px": 28.740337867218727, + "mean_reprojection_error_px": 43.788052458854565, "corner_reprojection_errors_px": [ - 13.91523395008655, - 28.107202384113876, - 48.80100478346, - 24.137910351214487 + 66.92563132518778, + 41.81087505439111, + 17.311321747071958, + 49.104381708767434 ] } ] @@ -1265,7 +1265,7 @@ "marker_id": 244, "link_name": "Ellbow", "position_world_m": [ - 0.3410913248485293, + 0.22521525656040844, 0.0, 0.0 ], @@ -1280,9 +1280,9 @@ "marker_id": 245, "link_name": "Ellbow", "position_world_m": [ - 0.3060913248485293, - 0.009282998148214212, - -0.03374649530514616 + 0.19021525656040844, + 0.004053519626182114, + -0.03476447869075785 ], "size_m": 0.025, "observation_count": 0, @@ -1295,46 +1295,46 @@ "marker_id": 246, "link_name": "Ellbow", "position_world_m": [ - 0.3060913248485293, - -0.009282998148214212, - 0.03374649530514616 + 0.19021525656040844, + -0.004053519626182114, + 0.03476447869075785 ], "size_m": 0.025, "observation_count": 3, - "mean_confidence": 0.6792447690577089, + "mean_confidence": 0.6682660968032644, "mean_detector_confidence": 0.6792447690577089, - "mean_reprojection_error_px": 112.48128522376857, + "mean_reprojection_error_px": 161.1324352664503, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.8015179238063419, - "confidence_weighted": 0.8015179238063419, + "confidence_weighted": 0.7895911927962278, "quality": { "detector_confidence": 0.8015179238063419, - "weighted_confidence": 0.8015179238063419, + "weighted_confidence": 0.7895911927962278, "q": { "size": 0.7650188636779786, "aspect": 0.9580747137765034, "border": 1.0, - "center": 0.7408380806446075, + "center": 0.0, "sharpness": 1.0, "homography": 0.7832604752816245, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9976501886367798, + "aspect": 0.999580747137765, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9978326047528162, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9851198199617608, "raw": { "mean_edge_px": 38.250943183898926, "edge_ratio": 1.0875198679615226, @@ -1343,28 +1343,28 @@ "laplacian_var": 3236.7051337367984 } }, - "mean_reprojection_error_px": 133.93671094067233, + "mean_reprojection_error_px": 134.6218442755581, "corner_reprojection_errors_px": [ - 134.92477269026298, - 152.54499570886057, - 136.3819175139715, - 111.89515784959431 + 135.7150033827045, + 153.20493069778522, + 136.97402164430275, + 112.59342137743992 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.7581700566520715, - "confidence_weighted": 0.7581700566520715, + "confidence_weighted": 0.7467269698455701, "quality": { "detector_confidence": 0.7581700566520715, - "weighted_confidence": 0.7581700566520715, + "weighted_confidence": 0.7467269698455701, "q": { "size": 1.0, "aspect": 0.8624536105407097, "border": 1.0, - "center": 0.6523004770278931, + "center": 0.0, "sharpness": 0.7134050632712029, "homography": 0.9089132203508788, "normal_visibility": 1.0, @@ -1372,15 +1372,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9986245361054071, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.997134050632712, + "homography": 0.9990891322035088, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9849069655203322, "raw": { "mean_edge_px": 50.77022838592529, "edge_ratio": 1.3189653049815784, @@ -1389,28 +1389,28 @@ "laplacian_var": 1783.5126581780073 } }, - "mean_reprojection_error_px": 163.5949522441255, + "mean_reprojection_error_px": 166.1189645701224, "corner_reprojection_errors_px": [ - 164.9348955776692, - 195.44647967290177, - 166.50970828513942, - 127.48872544079168 + 167.75756135019049, + 198.24116321049402, + 168.6490336545454, + 129.8281000652596 ] }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.4780463267147134, - "confidence_weighted": 0.4780463267147134, + "confidence_weighted": 0.46848012776799536, "quality": { "detector_confidence": 0.4780463267147134, - "weighted_confidence": 0.4780463267147134, + "weighted_confidence": 0.46848012776799536, "q": { "size": 1.0, "aspect": 0.6468624400661076, "border": 1.0, - "center": 0.468610942363739, + "center": 0.0, "sharpness": 0.6283678308439046, "homography": 0.7101453224803067, "normal_visibility": 1.0, @@ -1418,15 +1418,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9964686244006611, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.996283678308439, + "homography": 0.997101453224803, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9799889709174003, "raw": { "mean_edge_px": 59.82627487182617, "edge_ratio": 2.0918474719224776, @@ -1435,12 +1435,12 @@ "laplacian_var": 1570.9195771097613 } }, - "mean_reprojection_error_px": 39.91219248650781, + "mean_reprojection_error_px": 182.65649695367037, "corner_reprojection_errors_px": [ - 41.72658331078573, - 33.04479526975366, - 66.19011920628998, - 18.687272159201875 + 186.41931794153945, + 204.89340651658281, + 185.29874269593594, + 154.01452066062325 ] } ] @@ -1449,46 +1449,46 @@ "marker_id": 247, "link_name": "Ellbow", "position_world_m": [ - 0.26859132484852927, - -0.009282998148214212, - 0.03374649530514616 + 0.15271525656040844, + -0.004053519626182114, + 0.03476447869075785 ], "size_m": 0.025, "observation_count": 3, - "mean_confidence": 0.7117000257529918, + "mean_confidence": 0.7010103986241017, "mean_detector_confidence": 0.7117000257529918, - "mean_reprojection_error_px": 102.31904563726128, + "mean_reprojection_error_px": 146.47131004262607, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.8796777163094818, - "confidence_weighted": 0.8796777163094818, + "confidence_weighted": 0.8670496997853304, "quality": { "detector_confidence": 0.8796777163094818, - "weighted_confidence": 0.8796777163094818, + "weighted_confidence": 0.8670496997853304, "q": { "size": 0.7921941375732422, "aspect": 0.9657885238796377, "border": 1.0, - "center": 0.7714440822601318, + "center": 0.0, "sharpness": 1.0, "homography": 0.8015391946807702, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9979219413757324, + "aspect": 0.9996578852387964, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9980153919468077, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9856447238687257, "raw": { "mean_edge_px": 39.60970687866211, "edge_ratio": 1.0708467232203849, @@ -1497,28 +1497,28 @@ "laplacian_var": 4202.582433286217 } }, - "mean_reprojection_error_px": 112.21393223629931, + "mean_reprojection_error_px": 112.65188559107314, "corner_reprojection_errors_px": [ - 128.20833066079467, - 129.7787908831529, - 100.21730102185268, - 90.65130637939703 + 128.71700121736973, + 130.18085487709857, + 100.60148705412828, + 91.10819921569595 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.7448005120854795, - "confidence_weighted": 0.7448005120854795, + "confidence_weighted": 0.7340842516844052, "quality": { "detector_confidence": 0.7448005120854795, - "weighted_confidence": 0.7448005120854795, + "weighted_confidence": 0.7340842516844052, "q": { "size": 1.0, "aspect": 0.8537371543928008, "border": 1.0, - "center": 0.7152222096920013, + "center": 0.0, "sharpness": 0.8698383110697441, "homography": 0.8325296434913341, "normal_visibility": 1.0, @@ -1526,15 +1526,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.998537371543928, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9986983831106975, + "homography": 0.9983252964349133, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9856119051649572, "raw": { "mean_edge_px": 51.05013561248779, "edge_ratio": 1.3426413969560103, @@ -1543,28 +1543,28 @@ "laplacian_var": 2174.5957776743603 } }, - "mean_reprojection_error_px": 133.70775260569667, + "mean_reprojection_error_px": 136.19499595934474, "corner_reprojection_errors_px": [ - 153.58015251204978, - 156.00959760114938, - 114.82745208859265, - 110.41380822099482 + 156.16137047168513, + 158.6202327519144, + 117.10218236400607, + 112.89619824977336 ] }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.5106218488640142, - "confidence_weighted": 0.5106218488640142, + "confidence_weighted": 0.5018972444025696, "quality": { "detector_confidence": 0.5106218488640142, - "weighted_confidence": 0.5106218488640142, + "weighted_confidence": 0.5018972444025696, "q": { "size": 1.0, "aspect": 0.6760419217397143, "border": 1.0, - "center": 0.5872762501239777, + "center": 0.0, "sharpness": 0.9265392364192184, "homography": 0.6801291688207284, "normal_visibility": 1.0, @@ -1572,15 +1572,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9967604192173971, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9992653923641922, + "homography": 0.9968012916882073, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9829137658702729, "raw": { "mean_edge_px": 58.36018657684326, "edge_ratio": 1.958396418454695, @@ -1589,12 +1589,12 @@ "laplacian_var": 2316.348091048046 } }, - "mean_reprojection_error_px": 61.03545206978789, + "mean_reprojection_error_px": 190.5670485774603, "corner_reprojection_errors_px": [ - 81.11386344556932, - 67.01512262029568, - 55.69321608125353, - 40.319606132033016 + 210.72653639156906, + 204.3452514096146, + 172.84658735917168, + 174.349819149486 ] } ] @@ -1603,46 +1603,46 @@ "marker_id": 124, "link_name": "Arm2", "position_world_m": [ - 0.18116461496794556, - -0.10738107374625039, - -0.03191480750403911 + 0.06637262457056678, + -0.11021173982435686, + -0.0218443050905359 ], "size_m": 0.025, "observation_count": 2, - "mean_confidence": 0.4106676690676095, + "mean_confidence": 0.4015628677453015, "mean_detector_confidence": 0.4106676690676095, - "mean_reprojection_error_px": 260.0804359426223, + "mean_reprojection_error_px": 259.7297501072294, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.34091855704160245, - "confidence_weighted": 0.34091855704160245, + "confidence_weighted": 0.33488305339370045, "quality": { "detector_confidence": 0.34091855704160245, - "weighted_confidence": 0.34091855704160245, + "weighted_confidence": 0.33488305339370045, "q": { "size": 0.7142756080627441, "aspect": 0.7379840018220383, "border": 1.0, - "center": 0.8538652509450912, + "center": 0.0, "sharpness": 1.0, "homography": 0.7675736815140904, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9971427560806274, + "aspect": 0.9973798400182203, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9976757368151409, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9822963475491728, "raw": { "mean_edge_px": 35.71378040313721, "edge_ratio": 1.7100858488288635, @@ -1651,28 +1651,28 @@ "laplacian_var": 3569.5639450778804 } }, - "mean_reprojection_error_px": 133.7951322833782, + "mean_reprojection_error_px": 134.91037554166337, "corner_reprojection_errors_px": [ - 119.77684898897931, - 102.66323049904321, - 148.05883888894743, - 164.6816107565428 + 120.9873933400657, + 103.67198851474541, + 149.06804198829764, + 165.91407832354471 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.4804167810936165, - "confidence_weighted": 0.4804167810936165, + "confidence_weighted": 0.46824268209690245, "quality": { "detector_confidence": 0.4804167810936165, - "weighted_confidence": 0.4804167810936165, + "weighted_confidence": 0.46824268209690245, "q": { "size": 1.0, "aspect": 0.9604332717578841, "border": 0.21666666666666667, - "center": 0.49520277976989746, + "center": 0.0, "sharpness": 0.5040158791602892, "homography": 0.7617971982706998, "normal_visibility": 1.0, @@ -1680,15 +1680,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, - "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "aspect": 0.9996043327175789, + "border": 0.9921666666666666, + "center": 0.99, + "sharpness": 0.9950401587916029, + "homography": 0.997617971982707, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9746592969358792, "raw": { "mean_edge_px": 61.16414737701416, "edge_ratio": 1.0823934976132112, @@ -1697,12 +1697,12 @@ "laplacian_var": 1260.039697900723 } }, - "mean_reprojection_error_px": 386.36573960186644, + "mean_reprojection_error_px": 384.5491246727954, "corner_reprojection_errors_px": [ - 336.07046868011287, - 377.8751921124279, - 434.5865464648627, - 396.9307511500621 + 334.3673362766447, + 376.12029027919453, + 432.6602329793538, + 395.04863915598855 ] } ] @@ -1711,46 +1711,46 @@ "marker_id": 122, "link_name": "Arm2", "position_world_m": [ - 0.2391668855143965, - -0.11496847136439253, - -0.004332331022756229 + 0.11782801799927344, + -0.11474921044648306, + 0.017070715755537844 ], "size_m": 0.025, "observation_count": 3, - "mean_confidence": 0.5690911450460799, + "mean_confidence": 0.5578116149791655, "mean_detector_confidence": 0.5690911450460799, - "mean_reprojection_error_px": 156.68910851305085, + "mean_reprojection_error_px": 185.0724839723273, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.18665602922528673, - "confidence_weighted": 0.18665602922528673, + "confidence_weighted": 0.18263714689166236, "quality": { "detector_confidence": 0.18665602922528673, - "weighted_confidence": 0.18665602922528673, + "weighted_confidence": 0.18263714689166236, "q": { "size": 0.7027967643737792, "aspect": 0.654945658590568, "border": 1.0, - "center": 0.720594972372055, + "center": 0.0, "sharpness": 0.9331720780648362, "homography": 0.539630944261658, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9970279676437378, + "aspect": 0.9965494565859057, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9993317207806484, + "homography": 0.9953963094426166, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9784690462434849, "raw": { "mean_edge_px": 35.139838218688965, "edge_ratio": 2.0536884606639982, @@ -1759,28 +1759,28 @@ "laplacian_var": 2332.9301951620905 } }, - "mean_reprojection_error_px": 193.48950896029461, + "mean_reprojection_error_px": 194.5564447603344, "corner_reprojection_errors_px": [ - 163.89399315841172, - 190.3342215815896, - 221.39744874765296, - 198.33237235352422 + 164.85878003772754, + 191.48901171235315, + 222.56759495263603, + 199.31039233862094 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.9456847621099602, - "confidence_weighted": 0.9456847621099602, + "confidence_weighted": 0.9281720009504786, "quality": { "detector_confidence": 0.9456847621099602, - "weighted_confidence": 0.9456847621099602, + "weighted_confidence": 0.9281720009504786, "q": { "size": 1.0, "aspect": 0.9720842559145406, "border": 1.0, - "center": 0.6961156129837036, + "center": 0.0, "sharpness": 0.4361118058809464, "homography": 0.7295816020733066, "normal_visibility": 1.0, @@ -1788,15 +1788,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9997208425591454, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9943611180588094, + "homography": 0.997295816020733, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9814813964852218, "raw": { "mean_edge_px": 53.96408939361572, "edge_ratio": 1.0574348240198506, @@ -1805,28 +1805,28 @@ "laplacian_var": 1090.279514702366 } }, - "mean_reprojection_error_px": 181.76476175391187, + "mean_reprojection_error_px": 181.55342368543512, "corner_reprojection_errors_px": [ - 138.7329595472684, - 193.13356440282328, - 223.10719220107293, - 172.0853308644829 + 138.79861947036503, + 193.60467544781787, + 222.68010835264752, + 171.13029147090998 ] }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.5749326438029929, - "confidence_weighted": 0.5749326438029929, + "confidence_weighted": 0.5626256970953559, "quality": { "detector_confidence": 0.5749326438029929, - "weighted_confidence": 0.5749326438029929, + "weighted_confidence": 0.5626256970953559, "q": { "size": 1.0, "aspect": 0.7301044220084257, "border": 1.0, - "center": 0.5485925674438477, + "center": 0.0, "sharpness": 0.34034870646107274, "homography": 0.7735552097414035, "normal_visibility": 1.0, @@ -1834,15 +1834,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9973010442200843, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9934034870646107, + "homography": 0.997735552097414, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9785941069092363, "raw": { "mean_edge_px": 75.16977119445801, "edge_ratio": 1.7393341824971433, @@ -1851,12 +1851,12 @@ "laplacian_var": 850.8717661526819 } }, - "mean_reprojection_error_px": 94.81305482494598, + "mean_reprojection_error_px": 179.10758347121242, "corner_reprojection_errors_px": [ - 53.41451564830506, - 104.51039185970349, - 144.44374195328376, - 76.8835698384916 + 132.65923607725063, + 207.86142341101734, + 235.5981225376692, + 140.31155185891245 ] } ] @@ -1865,9 +1865,9 @@ "marker_id": 218, "link_name": "Arm2", "position_world_m": [ - 0.19301576418266206, - -0.10100909858854287, - -0.05507885712581472 + 0.08260249512154345, + -0.10774345317436712, + -0.043013241363103374 ], "size_m": 0.025, "observation_count": 0, @@ -1880,46 +1880,46 @@ "marker_id": 101, "link_name": "Arm2", "position_world_m": [ - 0.18116461496794556, - -0.1748740643565427, - -0.05048080380046753 + 0.06637262457056678, + -0.17974069720587255, + -0.029951344342900126 ], "size_m": 0.025, "observation_count": 1, - "mean_confidence": 0.3325004465415643, + "mean_confidence": 0.3264687769670023, "mean_detector_confidence": 0.3325004465415643, - "mean_reprojection_error_px": 264.8653586916646, + "mean_reprojection_error_px": 266.6725297371658, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.3325004465415643, - "confidence_weighted": 0.3325004465415643, + "confidence_weighted": 0.3264687769670023, "quality": { "detector_confidence": 0.3325004465415643, - "weighted_confidence": 0.3325004465415643, + "weighted_confidence": 0.3264687769670023, "q": { "size": 0.7345538902282714, "aspect": 0.6782259971718826, "border": 1.0, - "center": 0.6848434209823608, + "center": 0.0, "sharpness": 1.0, "homography": 0.7627185514162481, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9973455389022827, + "aspect": 0.9967822599717188, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9976271855141625, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9818596647394037, "raw": { "mean_edge_px": 36.727694511413574, "edge_ratio": 1.9488695631540953, @@ -1928,12 +1928,12 @@ "laplacian_var": 3563.870814189421 } }, - "mean_reprojection_error_px": 264.8653586916646, + "mean_reprojection_error_px": 266.6725297371658, "corner_reprojection_errors_px": [ - 248.76482349823732, - 232.76864494991185, - 282.02433999470924, - 295.9036263237998 + 250.67737404692798, + 234.4550017828906, + 283.7216101380938, + 297.83613298075073 ] } ] @@ -1942,46 +1942,46 @@ "marker_id": 102, "link_name": "Arm2", "position_world_m": [ - 0.2138000507668149, - -0.18474533510517926, - -0.014595760341876172 + 0.09128210146267585, + -0.18469476841972596, + 0.012536596762424725 ], "size_m": 0.025, "observation_count": 3, - "mean_confidence": 0.8473641170835915, + "mean_confidence": 0.8354983021138915, "mean_detector_confidence": 0.8473641170835915, - "mean_reprojection_error_px": 203.00720872867376, + "mean_reprojection_error_px": 230.53888976377948, "observations": [ { "view_index": 0, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", "confidence_detector": 0.9494437061622057, - "confidence_weighted": 0.9494437061622057, + "confidence_weighted": 0.9376120619964978, "quality": { "detector_confidence": 0.9494437061622057, - "weighted_confidence": 0.9494437061622057, + "weighted_confidence": 0.9376120619964978, "q": { "size": 0.9537287521362304, "aspect": 0.9740662971297988, "border": 1.0, - "center": 0.6450249254703522, + "center": 0.0, "sharpness": 1.0, "homography": 0.8234129943538817, "normal_visibility": 1.0, "spin": 1.0 }, "factor": { - "size": 1.0, - "aspect": 1.0, + "size": 0.9995372875213623, + "aspect": 0.9997406629712979, "border": 1.0, - "center": 1.0, + "center": 0.99, "sharpness": 1.0, - "homography": 1.0, + "homography": 0.9982341299435388, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9875383405156971, "raw": { "mean_edge_px": 47.68643760681152, "edge_ratio": 1.0532483321651058, @@ -1990,28 +1990,28 @@ "laplacian_var": 3935.778766550373 } }, - "mean_reprojection_error_px": 274.81864950073583, + "mean_reprojection_error_px": 276.6085282390233, "corner_reprojection_errors_px": [ - 238.5526540769381, - 266.0117341060898, - 309.2297757901905, - 285.48043402972485 + 240.26917031381674, + 267.8790547795138, + 311.1033048450262, + 287.1825830177366 ] }, { "view_index": 1, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", "confidence_detector": 0.8913851020933449, - "confidence_weighted": 0.8913851020933449, + "confidence_weighted": 0.880139786945703, "quality": { "detector_confidence": 0.8913851020933449, - "weighted_confidence": 0.8913851020933449, + "weighted_confidence": 0.880139786945703, "q": { "size": 1.0, "aspect": 0.9425738852513735, "border": 1.0, - "center": 0.6652068793773651, + "center": 0.0, "sharpness": 0.7931141622847128, "homography": 0.9999959590215692, "normal_visibility": 1.0, @@ -2019,15 +2019,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.9994257388525137, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9979311416228471, + "homography": 0.9999999595902157, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9873844479549488, "raw": { "mean_edge_px": 62.59456253051758, "edge_ratio": 1.1218495773056807, @@ -2036,28 +2036,28 @@ "laplacian_var": 1982.7854057117818 } }, - "mean_reprojection_error_px": 267.48197698933274, + "mean_reprojection_error_px": 265.88170983930263, "corner_reprojection_errors_px": [ - 230.94675676068127, - 248.4628484358831, - 301.9047245920965, - 288.61357816867 + 229.04927984617981, + 247.40709917850447, + 300.4580491614664, + 286.61241117106 ] }, { "view_index": 2, - "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "source_file": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", "confidence_detector": 0.7012635429952238, - "confidence_weighted": 0.7012635429952238, + "confidence_weighted": 0.6887430573994738, "quality": { "detector_confidence": 0.7012635429952238, - "weighted_confidence": 0.7012635429952238, + "weighted_confidence": 0.6887430573994738, "q": { "size": 1.0, "aspect": 0.8244031865405023, "border": 1.0, - "center": 0.6730169951915741, + "center": 0.0, "sharpness": 0.6987050522983833, "homography": 0.6814939272403986, "normal_visibility": 1.0, @@ -2065,15 +2065,15 @@ }, "factor": { "size": 1.0, - "aspect": 1.0, + "aspect": 0.998244031865405, "border": 1.0, - "center": 1.0, - "sharpness": 1.0, - "homography": 1.0, + "center": 0.99, + "sharpness": 0.9969870505229839, + "homography": 0.9968149392724039, "normal_visibility": 1.0, "spin": 1.0 }, - "weight_multiplier": 1.0, + "weight_multiplier": 0.9821458198977909, "raw": { "mean_edge_px": 84.7633228302002, "edge_ratio": 1.4259974156489281, @@ -2082,12 +2082,12 @@ "laplacian_var": 1746.7626307459584 } }, - "mean_reprojection_error_px": 66.72099969595267, + "mean_reprojection_error_px": 149.12643121301247, "corner_reprojection_errors_px": [ - 110.45363721468823, - 54.785346050607366, - 11.647409112989692, - 89.99760640552542 + 100.71621265239685, + 145.42820590819824, + 193.79388723836442, + 156.5674190530904 ] } ] @@ -2096,9 +2096,9 @@ "marker_id": 219, "link_name": "Arm2", "position_world_m": [ - 0.19301576418266206, - -0.20417695566427538, - -0.08345830860749817 + 0.08260249512154345, + -0.21402343088611253, + -0.0554054299345744 ], "size_m": 0.025, "observation_count": 0, @@ -2114,8 +2114,8 @@ "marker_count": 23, "observed_marker_count": 18, "mean_detector_confidence": 0.5871913728875101, - "mean_weighted_confidence": 0.5871913728875101, - "mean_reprojection_error_px": 231.06066327218028, + "mean_weighted_confidence": 0.5774319023193206, + "mean_reprojection_error_px": 236.06726174073253, "quality_means": { "size": 0.8143773112577551, "aspect": 0.834768084426124, @@ -2125,19 +2125,19 @@ "quality_config": { "size_ref_px": 50.0, "border_ref_px": 120.0, - "center_ref_norm": 1.0, + "center_ref_norm": 0.01, "sharpness_ref": 2500.0, "homography_ref": 0.18, - "size_factor": 1.0, - "aspect_factor": 1.0, - "border_factor": 1.0, - "center_factor": 1.0, - "sharpness_factor": 1.0, - "homography_factor": 1.0 + "size_factor": 0.01, + "aspect_factor": 0.01, + "border_factor": 0.01, + "center_factor": 0.01, + "sharpness_factor": 0.01, + "homography_factor": 0.01 } }, "solver": { - "final_cost": 24956.498214769963, + "final_cost": 26976.87543820547, "status": 0, "message": "The maximum number of function evaluations is exceeded." } diff --git a/pipeline/multiview_pose_Weighted.json b/pipeline/multiview_pose_Weighted.json new file mode 100644 index 0000000..4538c77 --- /dev/null +++ b/pipeline/multiview_pose_Weighted.json @@ -0,0 +1,966 @@ +{ + "schema_version": "1.0", + "created_utc": "2026-05-28T21:07:44.288252Z", + "source_robot_json": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json", + "source_detections": [ + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json" + ], + "robot_pose": { + "state": { + "x": 193.66787382210828, + "y": 61.23821620356439, + "z": -119.96654664833596, + "a": -107.97036991399465, + "b": 21.99999999999934, + "c": 91.00000000000014, + "e": 9.999999999999135 + }, + "uncertainty": { + "x_mm": 444.1920850838452, + "y_mm": 2085.804738658986, + "z_mm": 216.50850058940748, + "a_deg": 273.19721127112774, + "b_deg": 9704.418760205353, + "c_deg": 9704.420149983844, + "e_mm": 97044.43203671245 + }, + "confidence": { + "x": 5.116616350285866e-20, + "y": 2.598071909247533e-91, + "z": 3.9550801677927533e-10, + "a": 1.3651987041054823e-12, + "b": 0.0, + "c": 0.0, + "e": 0.36787944117144233 + } + }, + "camera_poses": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + -77.74926115576925, + -73.0492976258425, + -147.8089302398559 + ], + "rvec": [ + -4.597800910443656, + -21.216692970790984, + 5.280603517092184 + ], + "tvec": [ + 1.3186034894317018, + 7.494557764024174, + -182.12830708406332 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 17 + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + 0.0906907584418463, + 0.2776617967661424, + -0.7247751841355033 + ], + "rvec": [ + -0.5104408724728655, + -3.1865020935343082, + -0.7599897453664757 + ], + "tvec": [ + 0.18575285646205408, + 0.031786357242087525, + -0.7583570784256441 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 10 + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + 0.2258549281270687, + -0.3663871310029323, + 0.6799707410888476 + ], + "rvec": [ + -3.4730218301317435, + 0.5992584877895241, + 0.35858647149574585 + ], + "tvec": [ + -0.1367303214358514, + 0.0014859998076042164, + 0.7930402247461223 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 7 + } + ], + "marker_positions": [ + { + "marker_id": 210, + "link_name": "Board", + "position_world_m": [ + 0.02, + -0.02, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.6024643807746477, + "mean_reprojection_error_px": 219.44641687346314, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.5042074435920213, + "mean_reprojection_error_px": 584.8502187266689, + "corner_reprojection_errors_px": [ + 610.6060261840274, + 566.4030412437936, + 559.1036732421863, + 603.2881342366682 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.6639531667043603, + "mean_reprojection_error_px": 43.90181819055642, + "corner_reprojection_errors_px": [ + 30.0527741674556, + 58.738230110803315, + 62.58264430539862, + 24.23362417856814 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.6392325320275614, + "mean_reprojection_error_px": 29.587213703164036, + "corner_reprojection_errors_px": [ + 44.13807594611845, + 1.1413153391747903, + 31.635331629981838, + 41.434131897381064 + ] + } + ] + }, + { + "marker_id": 211, + "link_name": "Board", + "position_world_m": [ + 0.25, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.5607331763105995, + "mean_reprojection_error_px": 215.88919812501328, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.5607331763105995, + "mean_reprojection_error_px": 215.88919812501328, + "corner_reprojection_errors_px": [ + 239.060567969831, + 203.87810525007305, + 193.21032729986996, + 227.40779198027906 + ] + } + ] + }, + { + "marker_id": 215, + "link_name": "Board", + "position_world_m": [ + 0.25, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.6915990015172901, + "mean_reprojection_error_px": 185.82669857910088, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.6816798505186958, + "mean_reprojection_error_px": 206.76829215011884, + "corner_reprojection_errors_px": [ + 220.0957850912882, + 186.544457069329, + 194.82996305260656, + 225.60296338725155 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.7015181525158845, + "mean_reprojection_error_px": 164.88510500808295, + "corner_reprojection_errors_px": [ + 197.75737719196465, + 146.9529114839187, + 132.41058001353588, + 182.41955134291254 + ] + } + ] + }, + { + "marker_id": 214, + "link_name": "Board", + "position_world_m": [ + 0.35000000000000003, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.5223267506441452, + "mean_reprojection_error_px": 430.28505616193627, + "observations": [ + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.5223267506441452, + "mean_reprojection_error_px": 430.28505616193627, + "corner_reprojection_errors_px": [ + 435.27929209106946, + 407.3352893643994, + 427.77239968551254, + 450.7532435067638 + ] + } + ] + }, + { + "marker_id": 208, + "link_name": "Board", + "position_world_m": [ + 0.35000000000000003, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.5511135304821347, + "mean_reprojection_error_px": 104.93132940823983, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.5511135304821347, + "mean_reprojection_error_px": 104.93132940823983, + "corner_reprojection_errors_px": [ + 101.05437746855068, + 84.48184159814882, + 111.54460519378607, + 122.64449337247373 + ] + } + ] + }, + { + "marker_id": 206, + "link_name": "Board", + "position_world_m": [ + 0.65, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.3243040576749168, + "mean_reprojection_error_px": 265.1269045088845, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.3243040576749168, + "mean_reprojection_error_px": 265.1269045088845, + "corner_reprojection_errors_px": [ + 246.4689237594987, + 270.74271590412576, + 283.7439899333724, + 259.551988438541 + ] + } + ] + }, + { + "marker_id": 205, + "link_name": "Board", + "position_world_m": [ + 0.75, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.2813323639079811, + "mean_reprojection_error_px": 414.07105145074036, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.2813323639079811, + "mean_reprojection_error_px": 414.07105145074036, + "corner_reprojection_errors_px": [ + 393.31517220079184, + 414.88055122924146, + 434.6820277122509, + 413.40645466067724 + ] + } + ] + }, + { + "marker_id": 207, + "link_name": "Board", + "position_world_m": [ + 0.75, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.270961511076242, + "mean_reprojection_error_px": 361.40219383857607, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.270961511076242, + "mean_reprojection_error_px": 361.40219383857607, + "corner_reprojection_errors_px": [ + 343.99480245991305, + 365.3864615302313, + 379.1131712173157, + 357.11434014684426 + ] + } + ] + }, + { + "marker_id": 217, + "link_name": "Board", + "position_world_m": [ + 0.65, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.3364649702867649, + "mean_reprojection_error_px": 319.9894030743949, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.3364649702867649, + "mean_reprojection_error_px": 319.9894030743949, + "corner_reprojection_errors_px": [ + 297.7784176112965, + 321.2632775986662, + 342.12879416557814, + 318.78712292203863 + ] + } + ] + }, + { + "marker_id": 198, + "link_name": "Arm1", + "position_world_m": [ + 0.1936678738221083, + -0.04630507797358846, + 0.15710136776571965 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.1708737557403893, + "mean_reprojection_error_px": 173.75585674911616, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.20939472520051636, + "mean_reprojection_error_px": 250.68402341711896, + "corner_reprojection_errors_px": [ + 276.58098681044964, + 248.3015862011881, + 225.19824045129988, + 252.6552802055382 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.13235278628026223, + "mean_reprojection_error_px": 96.82769008111335, + "corner_reprojection_errors_px": [ + 86.14028865296076, + 96.54841237858139, + 117.89661118485392, + 86.72544810805726 + ] + } + ] + }, + { + "marker_id": 229, + "link_name": "Arm1", + "position_world_m": [ + 0.1936678738221083, + -0.08961029442397768, + 0.2359978710354143 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.2199830218620032, + "mean_reprojection_error_px": 111.48418089668428, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.2634483409899881, + "mean_reprojection_error_px": 158.57789833970793, + "corner_reprojection_errors_px": [ + 183.23095634369403, + 159.8980008563882, + 134.10338119920908, + 157.07925495954032 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.17651770273401826, + "mean_reprojection_error_px": 64.39046345366064, + "corner_reprojection_errors_px": [ + 53.76348865479547, + 65.98012218463036, + 89.14976602616233, + 48.66847694905442 + ] + } + ] + }, + { + "marker_id": 242, + "link_name": "Arm1", + "position_world_m": [ + 0.1936678738221083, + -0.15097424141151797, + 0.2023160360184449 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 243, + "link_name": "Arm1", + "position_world_m": [ + 0.1936678738221083, + -0.13713318542623254, + 0.24983892702069976 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.783838885722734, + "mean_reprojection_error_px": 44.94196369988338, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.753480565397225, + "mean_reprojection_error_px": 82.27135266344914, + "corner_reprojection_errors_px": [ + 104.64641625637452, + 75.05759016037157, + 54.73600619528254, + 94.64539804176789 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.7779633248368785, + "mean_reprojection_error_px": 17.133880995253847, + "corner_reprojection_errors_px": [ + 13.52613306369719, + 20.79198380803457, + 20.738458388035028, + 13.478948721248596 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.8200727669340984, + "mean_reprojection_error_px": 35.42065744094714, + "corner_reprojection_errors_px": [ + 43.2051334803787, + 2.291855750175984, + 46.29738248992781, + 49.88825804330606 + ] + } + ] + }, + { + "marker_id": 244, + "link_name": "Ellbow", + "position_world_m": [ + 0.31866787382210826, + 0.0, + 0.0 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 245, + "link_name": "Ellbow", + "position_world_m": [ + 0.2836678738221083, + 0.029915046239746786, + -0.018168379357383857 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 246, + "link_name": "Ellbow", + "position_world_m": [ + 0.2836678738221083, + -0.029915046239746786, + 0.018168379357383857 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.5848503519198133, + "mean_reprojection_error_px": 71.95677931061337, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.6846593913143055, + "mean_reprojection_error_px": 129.77061184774738, + "corner_reprojection_errors_px": [ + 131.10152630147377, + 148.01757225834027, + 132.02269451379757, + 107.940654317378 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.6496660357669276, + "mean_reprojection_error_px": 39.429662635023675, + "corner_reprojection_errors_px": [ + 54.265219903618416, + 25.613227047621212, + 36.12153940947933, + 41.71866417937572 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.4202256286782067, + "mean_reprojection_error_px": 46.67006344906903, + "corner_reprojection_errors_px": [ + 17.76180722171543, + 53.41185602621912, + 87.55552599091499, + 27.95106455742659 + ] + } + ] + }, + { + "marker_id": 247, + "link_name": "Ellbow", + "position_world_m": [ + 0.24616787382210828, + -0.029915046239746786, + 0.018168379357383857 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.6103408077771776, + "mean_reprojection_error_px": 72.45582056932597, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.7451994373791682, + "mean_reprojection_error_px": 109.52982904634278, + "corner_reprojection_errors_px": [ + 126.13937483803942, + 126.72281250876043, + 96.93499026995522, + 88.3221385686161 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.6388783942274684, + "mean_reprojection_error_px": 42.48268761553851, + "corner_reprojection_errors_px": [ + 66.85934117736593, + 52.808931850254176, + 27.34232661092379, + 22.920150823610143 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.44694459172489637, + "mean_reprojection_error_px": 65.35494504609662, + "corner_reprojection_errors_px": [ + 65.94881395992364, + 79.79307781356299, + 80.2686839721486, + 35.40920443875125 + ] + } + ] + }, + { + "marker_id": 124, + "link_name": "Arm2", + "position_world_m": [ + 0.20446625306675464, + -0.14213782157794694, + -0.16990066121932695 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.38376633052345943, + "mean_reprojection_error_px": 119.57328172201322, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.31559423864949376, + "mean_reprojection_error_px": 134.82416625853605, + "corner_reprojection_errors_px": [ + 121.33281341523052, + 103.50465151028168, + 148.6485339972427, + 165.81066611138934 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.4519384223974251, + "mean_reprojection_error_px": 104.32239718549039, + "corner_reprojection_errors_px": [ + 97.26022837754157, + 65.51154041781498, + 119.230195720015, + 135.28762422659 + ] + } + ] + }, + { + "marker_id": 122, + "link_name": "Arm2", + "position_world_m": [ + 0.22220435967071417, + -0.08656453557061031, + -0.16036287025635595 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.5007571026495876, + "mean_reprojection_error_px": 100.5976968444762, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.19097609792206996, + "mean_reprojection_error_px": 191.04201527524393, + "corner_reprojection_errors_px": [ + 161.2461896774225, + 187.67710037351839, + 219.07478119264024, + 196.16998985739463 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.8023854845720537, + "mean_reprojection_error_px": 37.97273645764808, + "corner_reprojection_errors_px": [ + 36.73735527126386, + 66.77343554076022, + 29.865847324866966, + 18.51430769370129 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.5089097254546391, + "mean_reprojection_error_px": 72.77833880053663, + "corner_reprojection_errors_px": [ + 79.87926104625087, + 114.41534929802285, + 63.25062596802093, + 33.56811888985185 + ] + } + ] + }, + { + "marker_id": 218, + "link_name": "Arm2", + "position_world_m": [ + 0.18286949457746193, + -0.029683137487597516, + -0.11301020464799262 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 101, + "link_name": "Arm2", + "position_world_m": [ + 0.1624892762454371, + -0.0808799499047689, + -0.16381530379227105 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.30496613331262606, + "mean_reprojection_error_px": 266.775950196611, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.30496613331262606, + "mean_reprojection_error_px": 266.775950196611, + "corner_reprojection_errors_px": [ + 251.08509253516107, + 234.8164115324836, + 283.5537072385155, + 297.64858948028376 + ] + } + ] + }, + { + "marker_id": 102, + "link_name": "Arm2", + "position_world_m": [ + 0.17776126974857978, + -0.12112440660686963, + -0.13937353791513551 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.7291583793913716, + "mean_reprojection_error_px": 139.1860336202907, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.8030005342619584, + "mean_reprojection_error_px": 274.10231703594735, + "corner_reprojection_errors_px": [ + 237.94975046516782, + 264.59434010512416, + 308.4630690582601, + 285.4021085152374 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.7644345761691943, + "mean_reprojection_error_px": 72.4273672978813, + "corner_reprojection_errors_px": [ + 46.62612295920637, + 34.03763203823939, + 100.37413904993618, + 108.67157514414326 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.6200400277429619, + "mean_reprojection_error_px": 71.02841652704336, + "corner_reprojection_errors_px": [ + 102.37939191515659, + 31.002903116690042, + 42.58563098754617, + 108.14574008878068 + ] + } + ] + }, + { + "marker_id": 219, + "link_name": "Arm2", + "position_world_m": [ + 0.18286949457746193, + -0.08522646866588529, + -0.20446477458093276 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + } + ] +} \ No newline at end of file diff --git a/pipeline/multiview_pose_neu.json b/pipeline/multiview_pose_neu.json new file mode 100644 index 0000000..730682d --- /dev/null +++ b/pipeline/multiview_pose_neu.json @@ -0,0 +1,2144 @@ +{ + "schema_version": "1.0", + "created_utc": "2026-05-28T20:51:28.589969Z", + "source_robot_json": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json", + "source_detections": [ + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json" + ], + "robot_pose": { + "state": { + "x": 216.2125544201488, + "y": 39.806379269892254, + "z": -54.991084397171825, + "a": -130.65882483284662, + "b": 22.000000000002032, + "c": 90.99999999999798, + "e": 10.000000000001718 + }, + "uncertainty": { + "x_mm": 224.10687807100763, + "y_mm": 605.4181830366131, + "z_mm": 248.0806970645967, + "a_deg": 997.7447286683357, + "b_deg": 12094.993764453631, + "c_deg": 12094.993807226765, + "e_mm": 120949.93791544222 + }, + "confidence": { + "x": 1.8499583454054539e-10, + "y": 5.093571259225315e-27, + "z": 1.6826439170056386e-11, + "a": 4.66119207217934e-44, + "b": 0.0, + "c": 0.0, + "e": 0.36787944117144233 + } + }, + "camera_poses": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + -38.46428656854336, + -74.09008711895505, + -21.334303864681747 + ], + "rvec": [ + 110.95428904561572, + -126.68939270587695, + 43.92636633496647 + ], + "tvec": [ + 0.4736830640184213, + 3.5722846578605267, + -86.08721901995688 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 17 + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + -17.75034590814752, + 49.992391204647504, + -9.204276670320416 + ], + "rvec": [ + 0.31469131936958156, + 2.0195329754781035, + 1.5707717909374486 + ], + "tvec": [ + -1.9399600172876972, + 2.0553500305952532, + -53.7684364532229 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 10 + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + 0.2729594318862425, + -0.6573042534860901, + 0.4430179270292649 + ], + "rvec": [ + -3.9931652753171334, + 0.3705917863703731, + 0.6116696935981153 + ], + "tvec": [ + -0.13797082899413618, + 0.016029224160589235, + 0.8267573592628941 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 7 + } + ], + "marker_positions": [ + { + "marker_id": 210, + "link_name": "Board", + "position_world_m": [ + 0.02, + -0.02, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.6882418402751833, + "mean_detector_confidence": 0.6997700579082148, + "mean_reprojection_error_px": 348.28128404408494, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.5673043275049208, + "confidence_weighted": 0.556048533067895, + "quality": { + "detector_confidence": 0.5673043275049208, + "weighted_confidence": 0.556048533067895, + "q": { + "size": 0.8108224868774414, + "aspect": 0.9096486158109359, + "border": 0.2833333333333333, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.9999983069718208, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9981082248687744, + "aspect": 0.9990964861581093, + "border": 0.9928333333333333, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9999999830697182, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9801591599229813, + "raw": { + "mean_edge_px": 40.54112434387207, + "edge_ratio": 1.1986511772098227, + "distance_to_border_px": 34.0, + "distance_to_center_norm": 0.7614269256591797, + "laplacian_var": 3797.5312506581813 + } + }, + "mean_reprojection_error_px": 586.9935302222972, + "corner_reprojection_errors_px": [ + 612.6505119930617, + 568.2120894731479, + 561.3485382966923, + 605.7629811262867 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.7813266383036261, + "confidence_weighted": 0.7686240953581938, + "quality": { + "detector_confidence": 0.7813266383036261, + "weighted_confidence": 0.7686240953581938, + "q": { + "size": 0.7824347686767578, + "aspect": 0.8792155830729744, + "border": 1.0, + "center": 0.0, + "sharpness": 0.9904164099455028, + "homography": 0.7145588675251666, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9978243476867675, + "aspect": 0.9987921558307298, + "border": 1.0, + "center": 0.99, + "sharpness": 0.999904164099455, + "homography": 0.9971455886752516, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9837423398579991, + "raw": { + "mean_edge_px": 39.12173843383789, + "edge_ratio": 1.274754950327127, + "distance_to_border_px": 252.0, + "distance_to_center_norm": 0.5032430291175842, + "laplacian_var": 2476.0410248637572 + } + }, + "mean_reprojection_error_px": 427.63428905378987, + "corner_reprojection_errors_px": [ + 446.12766553590336, + 403.96001290416933, + 408.1519561283892, + 452.29752164669765 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.7506792079160973, + "confidence_weighted": 0.740052892399461, + "quality": { + "detector_confidence": 0.7506792079160973, + "weighted_confidence": 0.740052892399461, + "q": { + "size": 0.8434026336669922, + "aspect": 0.8575862494073492, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.8786678750976363, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9984340263366699, + "aspect": 0.9985758624940735, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9987866787509764, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9858443987730322, + "raw": { + "mean_edge_px": 42.17013168334961, + "edge_ratio": 1.3321269451115116, + "distance_to_border_px": 298.0, + "distance_to_center_norm": 0.3557531535625458, + "laplacian_var": 2842.0926549741707 + } + }, + "mean_reprojection_error_px": 30.216032856167953, + "corner_reprojection_errors_px": [ + 35.67266624562453, + 21.942859474246884, + 43.42315186071883, + 19.82545384408156 + ] + } + ] + }, + { + "marker_id": 211, + "link_name": "Board", + "position_world_m": [ + 0.25, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.6305246964757232, + "mean_detector_confidence": 0.6412317666518965, + "mean_reprojection_error_px": 213.454796419243, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.6412317666518965, + "confidence_weighted": 0.6305246964757232, + "quality": { + "detector_confidence": 0.6412317666518965, + "weighted_confidence": 0.6305246964757232, + "q": { + "size": 0.6744685745239258, + "aspect": 0.9460729944417767, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.7016198391981602, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9967446857452392, + "aspect": 0.9994607299444178, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9970161983919816, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9833023397576218, + "raw": { + "mean_edge_px": 33.72342872619629, + "edge_ratio": 1.1140017860673477, + "distance_to_border_px": 276.0, + "distance_to_center_norm": 0.26670128107070923, + "laplacian_var": 3165.7184033540047 + } + }, + "mean_reprojection_error_px": 213.454796419243, + "corner_reprojection_errors_px": [ + 236.6948016318256, + 201.2877507939219, + 190.69927477677504, + 225.13735847444946 + ] + } + ] + }, + { + "marker_id": 215, + "link_name": "Board", + "position_world_m": [ + 0.25, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.7997306526656554, + "mean_detector_confidence": 0.8108527003549935, + "mean_reprojection_error_px": 158.99744861646724, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.7952557920267838, + "confidence_weighted": 0.7849458403437536, + "quality": { + "detector_confidence": 0.7952557920267838, + "weighted_confidence": 0.7849458403437536, + "q": { + "size": 0.7146316909790039, + "aspect": 0.9859016728248396, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 1.0, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.99714631690979, + "aspect": 0.9998590167282484, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 1.0, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9870356786000208, + "raw": { + "mean_edge_px": 35.731584548950195, + "edge_ratio": 1.0285998645985972, + "distance_to_border_px": 211.0, + "distance_to_center_norm": 0.213719442486763, + "laplacian_var": 3704.0260869185627 + } + }, + "mean_reprojection_error_px": 202.81242503739065, + "corner_reprojection_errors_px": [ + 215.99477550077216, + 182.26503017592103, + 191.06124849415332, + 221.92864597871608 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.826449608683203, + "confidence_weighted": 0.8145154649875572, + "quality": { + "detector_confidence": 0.826449608683203, + "weighted_confidence": 0.8145154649875572, + "q": { + "size": 0.8409605407714844, + "aspect": 0.9049793706370471, + "border": 1.0, + "center": 0.0, + "sharpness": 0.8049029197222222, + "homography": 1.0, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9984096054077148, + "aspect": 0.9990497937063705, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9980490291972222, + "homography": 1.0, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9855597442720546, + "raw": { + "mean_edge_px": 42.04802703857422, + "edge_ratio": 1.2099951279465397, + "distance_to_border_px": 290.0, + "distance_to_center_norm": 0.0916082039475441, + "laplacian_var": 2012.2572993055553 + } + }, + "mean_reprojection_error_px": 115.1824721955438, + "corner_reprojection_errors_px": [ + 102.04871121339015, + 92.02281620719849, + 129.7002932180607, + 136.95806814352588 + ] + } + ] + }, + { + "marker_id": 214, + "link_name": "Board", + "position_world_m": [ + 0.35000000000000003, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.5924337979452556, + "mean_detector_confidence": 0.6041252446922665, + "mean_reprojection_error_px": 338.292638805463, + "observations": [ + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.6041252446922665, + "confidence_weighted": 0.5924337979452556, + "quality": { + "detector_confidence": 0.6041252446922665, + "weighted_confidence": 0.5924337979452556, + "q": { + "size": 0.9836519622802734, + "aspect": 0.7532145593882991, + "border": 1.0, + "center": 0.0, + "sharpness": 0.5256355772646303, + "homography": 0.7899472530560614, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9998365196228027, + "aspect": 0.997532145593883, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9952563557726463, + "homography": 0.9978994725305605, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.980647313036941, + "raw": { + "mean_edge_px": 49.18259811401367, + "edge_ratio": 1.655285901037602, + "distance_to_border_px": 137.0, + "distance_to_center_norm": 0.6772311925888062, + "laplacian_var": 1314.0889431615756 + } + }, + "mean_reprojection_error_px": 338.292638805463, + "corner_reprojection_errors_px": [ + 334.17680905883867, + 319.4817160418119, + 346.09532378608355, + 353.4167063351179 + ] + } + ] + }, + { + "marker_id": 208, + "link_name": "Board", + "position_world_m": [ + 0.35000000000000003, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.6179064007574118, + "mean_detector_confidence": 0.6280424706698967, + "mean_reprojection_error_px": 100.37855071417272, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.6280424706698967, + "confidence_weighted": 0.6179064007574118, + "quality": { + "detector_confidence": 0.6280424706698967, + "weighted_confidence": 0.6179064007574118, + "q": { + "size": 0.6625362014770508, + "aspect": 0.9537163525948422, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.7625645902124035, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9966253620147705, + "aspect": 0.9995371635259485, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.997625645902124, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9838608527514494, + "raw": { + "mean_edge_px": 33.12681007385254, + "edge_ratio": 1.0970595655180506, + "distance_to_border_px": 303.0, + "distance_to_center_norm": 0.05652238056063652, + "laplacian_var": 2848.9886204705635 + } + }, + "mean_reprojection_error_px": 100.37855071417272, + "corner_reprojection_errors_px": [ + 95.32006064225416, + 79.9218729308236, + 108.20304850767852, + 118.06922077593458 + ] + } + ] + }, + { + "marker_id": 206, + "link_name": "Board", + "position_world_m": [ + 0.65, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.33185586633646474, + "mean_detector_confidence": 0.33820423087105295, + "mean_reprojection_error_px": 276.1297836775614, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.33820423087105295, + "confidence_weighted": 0.33185586633646474, + "quality": { + "detector_confidence": 0.33820423087105295, + "weighted_confidence": 0.33185586633646474, + "q": { + "size": 0.5361141967773437, + "aspect": 0.8936906729306451, + "border": 0.9833333333333333, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.6985669093200941, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9953611419677735, + "aspect": 0.9989369067293065, + "border": 0.9998333333333334, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.996985669093201, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9812291983508372, + "raw": { + "mean_edge_px": 26.805709838867188, + "edge_ratio": 1.2379107901411548, + "distance_to_border_px": 118.0, + "distance_to_center_norm": 0.41312646865844727, + "laplacian_var": 4112.338450277431 + } + }, + "mean_reprojection_error_px": 276.1297836775614, + "corner_reprojection_errors_px": [ + 257.8900669402429, + 282.33421351116806, + 294.3714307574213, + 269.9234235014133 + ] + } + ] + }, + { + "marker_id": 205, + "link_name": "Board", + "position_world_m": [ + 0.75, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.2820948444008322, + "mean_detector_confidence": 0.28724459014346065, + "mean_reprojection_error_px": 426.56646984308156, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.28724459014346065, + "confidence_weighted": 0.2820948444008322, + "quality": { + "detector_confidence": 0.28724459014346065, + "weighted_confidence": 0.2820948444008322, + "q": { + "size": 0.5248580741882324, + "aspect": 0.839187418386558, + "border": 0.8333333333333334, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.9999815099800249, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9952485807418823, + "aspect": 0.9983918741838655, + "border": 0.9983333333333333, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9999998150998003, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9820719139042569, + "raw": { + "mean_edge_px": 26.24290370941162, + "edge_ratio": 1.383257847031654, + "distance_to_border_px": 100.0, + "distance_to_center_norm": 0.5996246933937073, + "laplacian_var": 5067.414824476962 + } + }, + "mean_reprojection_error_px": 426.56646984308156, + "corner_reprojection_errors_px": [ + 406.09564835107483, + 428.0016350720032, + 446.9080809260819, + 425.26051502316636 + ] + } + ] + }, + { + "marker_id": 207, + "link_name": "Board", + "position_world_m": [ + 0.75, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.2656859333164099, + "mean_detector_confidence": 0.27228561618555186, + "mean_reprojection_error_px": 374.4597857850409, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.27228561618555186, + "confidence_weighted": 0.2656859333164099, + "quality": { + "detector_confidence": 0.27228561618555186, + "weighted_confidence": 0.2656859333164099, + "q": { + "size": 0.5014523983001709, + "aspect": 0.8554115971298921, + "border": 0.5, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.6975670005943979, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9950145239830017, + "aspect": 0.998554115971299, + "border": 0.995, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.996975670005944, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9757619114751748, + "raw": { + "mean_edge_px": 25.072619915008545, + "edge_ratio": 1.3380557461583085, + "distance_to_border_px": 60.0, + "distance_to_center_norm": 0.5434604287147522, + "laplacian_var": 4016.6321593416924 + } + }, + "mean_reprojection_error_px": 374.4597857850409, + "corner_reprojection_errors_px": [ + 357.40079371775073, + 378.999822513009, + 391.8463073017326, + 369.592219607671 + ] + } + ] + }, + { + "marker_id": 217, + "link_name": "Board", + "position_world_m": [ + 0.65, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.3494362969135403, + "mean_detector_confidence": 0.35596573288593486, + "mean_reprojection_error_px": 330.08452718425906, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.35596573288593486, + "confidence_weighted": 0.3494362969135403, + "quality": { + "detector_confidence": 0.35596573288593486, + "weighted_confidence": 0.3494362969135403, + "q": { + "size": 0.5561128711700439, + "aspect": 0.8721453879265322, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.726899391813532, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9955611287117004, + "aspect": 0.9987214538792654, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9972689939181353, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9816571221070685, + "raw": { + "mean_edge_px": 27.805643558502197, + "edge_ratio": 1.2931956388084183, + "distance_to_border_px": 161.0, + "distance_to_center_norm": 0.4665455222129822, + "laplacian_var": 3793.153007417641 + } + }, + "mean_reprojection_error_px": 330.08452718425906, + "corner_reprojection_errors_px": [ + 308.1750045594493, + 332.05822953172486, + 351.9569332059738, + 328.14794143988826 + ] + } + ] + }, + { + "marker_id": 198, + "link_name": "Arm1", + "position_world_m": [ + 0.2162125544201488, + -0.10050712667090819, + 0.12931866643434742 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.1628904920977715, + "mean_detector_confidence": 0.16652042240877096, + "mean_reprojection_error_px": 243.65457954180246, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.20150745250271476, + "confidence_weighted": 0.19749969171522036, + "quality": { + "detector_confidence": 0.20150745250271476, + "weighted_confidence": 0.19749969171522036, + "q": { + "size": 0.6339855194091797, + "aspect": 0.7539930512070505, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.6098583763162305, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9963398551940917, + "aspect": 0.9975399305120705, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9960985837631623, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9801111038935872, + "raw": { + "mean_edge_px": 31.699275970458984, + "edge_ratio": 1.652544339497884, + "distance_to_border_px": 237.0, + "distance_to_center_norm": 0.3810231387615204, + "laplacian_var": 3972.096222043203 + } + }, + "mean_reprojection_error_px": 248.60962775444693, + "corner_reprojection_errors_px": [ + 274.4618015839291, + 245.99327248236204, + 223.16393875898757, + 250.8194981925089 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.13153339231482716, + "confidence_weighted": 0.12828129248032266, + "quality": { + "detector_confidence": 0.13153339231482716, + "weighted_confidence": 0.12828129248032266, + "q": { + "size": 0.6576880264282227, + "aspect": 0.42738020051250675, + "border": 0.5916666666666667, + "center": 0.0, + "sharpness": 0.9362022578647182, + "homography": 0.8917140926962271, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9965768802642823, + "aspect": 0.994273802005125, + "border": 0.9959166666666667, + "center": 0.99, + "sharpness": 0.9993620225786471, + "homography": 0.9989171409269623, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9752754811742362, + "raw": { + "mean_edge_px": 32.88440132141113, + "edge_ratio": 3.6796739708616246, + "distance_to_border_px": 71.0, + "distance_to_center_norm": 0.3842795193195343, + "laplacian_var": 2340.5056446617955 + } + }, + "mean_reprojection_error_px": 238.699531329158, + "corner_reprojection_errors_px": [ + 255.24209245503738, + 233.94584808506465, + 221.27827772724922, + 244.3319070492807 + ] + } + ] + }, + { + "marker_id": 229, + "link_name": "Arm1", + "position_world_m": [ + 0.2162125544201488, + -0.16964622912493513, + 0.18693623764184944 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.22245621469865084, + "mean_detector_confidence": 0.2274747191640703, + "mean_reprojection_error_px": 172.21902964047672, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.26810902547334975, + "confidence_weighted": 0.2630164795315655, + "quality": { + "detector_confidence": 0.26810902547334975, + "weighted_confidence": 0.2630164795315655, + "q": { + "size": 0.6824631023406983, + "aspect": 0.7938768481947832, + "border": 1.0, + "center": 0.0, + "sharpness": 0.9086003975826067, + "homography": 0.7035912150387432, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.996824631023407, + "aspect": 0.9979387684819478, + "border": 1.0, + "center": 0.99, + "sharpness": 0.999086003975826, + "homography": 0.9970359121503874, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9810056900069167, + "raw": { + "mean_edge_px": 34.12315511703491, + "edge_ratio": 1.5192824359947652, + "distance_to_border_px": 235.0, + "distance_to_center_norm": 0.26675668358802795, + "laplacian_var": 2271.5009939565166 + } + }, + "mean_reprojection_error_px": 155.18292375210498, + "corner_reprojection_errors_px": [ + 179.61793897038316, + 156.36707592341475, + 130.9673904026988, + 153.77928971192316 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.18684041285479083, + "confidence_weighted": 0.18189594986573623, + "quality": { + "detector_confidence": 0.18684041285479083, + "weighted_confidence": 0.18189594986573623, + "q": { + "size": 0.7395829486846924, + "aspect": 0.44796521997625727, + "border": 1.0, + "center": 0.0, + "sharpness": 0.5568649165432099, + "homography": 0.5823463065693475, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9973958294868469, + "aspect": 0.9944796521997625, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9955686491654321, + "homography": 0.9958234630656935, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9735364372540894, + "raw": { + "mean_edge_px": 36.97914743423462, + "edge_ratio": 3.4646323214690695, + "distance_to_border_px": 130.0, + "distance_to_center_norm": 0.30442705750465393, + "laplacian_var": 1392.1622913580247 + } + }, + "mean_reprojection_error_px": 189.25513552884843, + "corner_reprojection_errors_px": [ + 210.953664691596, + 180.02926949382038, + 165.06706834326602, + 200.9705395867113 + ] + } + ] + }, + { + "marker_id": 242, + "link_name": "Arm1", + "position_world_m": [ + 0.2162125544201488, + -0.21445989561965892, + 0.1331613801776063 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_detector_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 243, + "link_name": "Arm1", + "position_world_m": [ + 0.2162125544201488, + -0.21894049110441863, + 0.1824556421570898 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.903504284314228, + "mean_detector_confidence": 0.9174550709990235, + "mean_reprojection_error_px": 76.41294345524527, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.8899728634608616, + "confidence_weighted": 0.8780472636948905, + "quality": { + "detector_confidence": 0.8899728634608616, + "weighted_confidence": 0.8780472636948905, + "q": { + "size": 0.8810945701599121, + "aspect": 0.9417837479752699, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.8333271723300208, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9988109457015991, + "aspect": 0.9994178374797527, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9983332717233002, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9866000411297984, + "raw": { + "mean_edge_px": 44.054728507995605, + "edge_ratio": 1.1236297656439467, + "distance_to_border_px": 275.0, + "distance_to_center_norm": 0.14032743871212006, + "laplacian_var": 2678.6956247003295 + } + }, + "mean_reprojection_error_px": 77.73247765881362, + "corner_reprojection_errors_px": [ + 100.09663777901342, + 71.03155845847478, + 49.68444998113758, + 90.11726441662871 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.9132502955127223, + "confidence_weighted": 0.8990944567220429, + "quality": { + "detector_confidence": 0.9132502955127223, + "weighted_confidence": 0.8990944567220429, + "q": { + "size": 1.0, + "aspect": 0.9546584653922503, + "border": 1.0, + "center": 0.0, + "sharpness": 0.5809177986450728, + "homography": 0.9082012101971718, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9995465846539225, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9958091779864507, + "homography": 0.9990820121019717, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9844994971693583, + "raw": { + "mean_edge_px": 55.26974582672119, + "edge_ratio": 1.0949900645130086, + "distance_to_border_px": 219.0, + "distance_to_center_norm": 0.16541028022766113, + "laplacian_var": 1452.2944966126818 + } + }, + "mean_reprojection_error_px": 122.78655606717538, + "corner_reprojection_errors_px": [ + 157.6458318939967, + 109.99288557766964, + 84.06400631503655, + 139.44350048199863 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.9491420540234864, + "confidence_weighted": 0.9333711325257503, + "quality": { + "detector_confidence": 0.9491420540234864, + "weighted_confidence": 0.9333711325257503, + "q": { + "size": 1.0, + "aspect": 0.9739075220959238, + "border": 1.0, + "center": 0.0, + "sharpness": 0.43477113518953137, + "homography": 0.9224349218355518, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9997390752209593, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9943477113518953, + "homography": 0.9992243492183555, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9833840240974658, + "raw": { + "mean_edge_px": 75.11714553833008, + "edge_ratio": 1.0535830709016873, + "distance_to_border_px": 185.0, + "distance_to_center_norm": 0.1876671463251114, + "laplacian_var": 1086.9278379738284 + } + }, + "mean_reprojection_error_px": 28.719796639746797, + "corner_reprojection_errors_px": [ + 13.96625838999557, + 28.16351736568848, + 48.750308006934766, + 23.999102796368366 + ] + } + ] + }, + { + "marker_id": 244, + "link_name": "Ellbow", + "position_world_m": [ + 0.3412125544201488, + 0.0, + 0.0 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_detector_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 245, + "link_name": "Ellbow", + "position_world_m": [ + 0.30621255442014883, + 0.009167604674109032, + -0.033778025764382 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_detector_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 246, + "link_name": "Ellbow", + "position_world_m": [ + 0.30621255442014883, + -0.009167604674109032, + 0.033778025764382 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.6682660968032644, + "mean_detector_confidence": 0.6792447690577089, + "mean_reprojection_error_px": 112.50880855094645, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.8015179238063419, + "confidence_weighted": 0.7895911927962278, + "quality": { + "detector_confidence": 0.8015179238063419, + "weighted_confidence": 0.7895911927962278, + "q": { + "size": 0.7650188636779786, + "aspect": 0.9580747137765034, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.7832604752816245, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9976501886367798, + "aspect": 0.999580747137765, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9978326047528162, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9851198199617608, + "raw": { + "mean_edge_px": 38.250943183898926, + "edge_ratio": 1.0875198679615226, + "distance_to_border_px": 150.0, + "distance_to_center_norm": 0.25916191935539246, + "laplacian_var": 3236.7051337367984 + } + }, + "mean_reprojection_error_px": 133.98596502059948, + "corner_reprojection_errors_px": [ + 134.9693924937541, + 152.59340276536824, + 136.43578909428862, + 111.9452757289869 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.7581700566520715, + "confidence_weighted": 0.7467269698455701, + "quality": { + "detector_confidence": 0.7581700566520715, + "weighted_confidence": 0.7467269698455701, + "q": { + "size": 1.0, + "aspect": 0.8624536105407097, + "border": 1.0, + "center": 0.0, + "sharpness": 0.7134050632712029, + "homography": 0.9089132203508788, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9986245361054071, + "border": 1.0, + "center": 0.99, + "sharpness": 0.997134050632712, + "homography": 0.9990891322035088, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9849069655203322, + "raw": { + "mean_edge_px": 50.77022838592529, + "edge_ratio": 1.3189653049815784, + "distance_to_border_px": 149.0, + "distance_to_center_norm": 0.34769952297210693, + "laplacian_var": 1783.5126581780073 + } + }, + "mean_reprojection_error_px": 163.62727079508792, + "corner_reprojection_errors_px": [ + 164.9925267514254, + 195.46921994489523, + 166.5180336585285, + 127.52930282550257 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.4780463267147134, + "confidence_weighted": 0.46848012776799536, + "quality": { + "detector_confidence": 0.4780463267147134, + "weighted_confidence": 0.46848012776799536, + "q": { + "size": 1.0, + "aspect": 0.6468624400661076, + "border": 1.0, + "center": 0.0, + "sharpness": 0.6283678308439046, + "homography": 0.7101453224803067, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9964686244006611, + "border": 1.0, + "center": 0.99, + "sharpness": 0.996283678308439, + "homography": 0.997101453224803, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9799889709174003, + "raw": { + "mean_edge_px": 59.82627487182617, + "edge_ratio": 2.0918474719224776, + "distance_to_border_px": 163.0, + "distance_to_center_norm": 0.531389057636261, + "laplacian_var": 1570.9195771097613 + } + }, + "mean_reprojection_error_px": 39.913189837151904, + "corner_reprojection_errors_px": [ + 41.87309117550474, + 32.973954442868155, + 66.11821920721233, + 18.68749452302238 + ] + } + ] + }, + { + "marker_id": 247, + "link_name": "Ellbow", + "position_world_m": [ + 0.2687125544201488, + -0.009167604674109032, + 0.033778025764382 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.7010103986241017, + "mean_detector_confidence": 0.7117000257529918, + "mean_reprojection_error_px": 102.35857446070808, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.8796777163094818, + "confidence_weighted": 0.8670496997853304, + "quality": { + "detector_confidence": 0.8796777163094818, + "weighted_confidence": 0.8670496997853304, + "q": { + "size": 0.7921941375732422, + "aspect": 0.9657885238796377, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.8015391946807702, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9979219413757324, + "aspect": 0.9996578852387964, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9980153919468077, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9856447238687257, + "raw": { + "mean_edge_px": 39.60970687866211, + "edge_ratio": 1.0708467232203849, + "distance_to_border_px": 184.0, + "distance_to_center_norm": 0.22855591773986816, + "laplacian_var": 4202.582433286217 + } + }, + "mean_reprojection_error_px": 112.24175599930025, + "corner_reprojection_errors_px": [ + 128.22755608967458, + 129.80902970204835, + 100.25704493487244, + 90.67339327060562 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.7448005120854795, + "confidence_weighted": 0.7340842516844052, + "quality": { + "detector_confidence": 0.7448005120854795, + "weighted_confidence": 0.7340842516844052, + "q": { + "size": 1.0, + "aspect": 0.8537371543928008, + "border": 1.0, + "center": 0.0, + "sharpness": 0.8698383110697441, + "homography": 0.8325296434913341, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.998537371543928, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9986983831106975, + "homography": 0.9983252964349133, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9856119051649572, + "raw": { + "mean_edge_px": 51.05013561248779, + "edge_ratio": 1.3426413969560103, + "distance_to_border_px": 145.0, + "distance_to_center_norm": 0.28477779030799866, + "laplacian_var": 2174.5957776743603 + } + }, + "mean_reprojection_error_px": 133.79252008274474, + "corner_reprojection_errors_px": [ + 153.68125849002428, + 156.09016013832314, + 114.89720630378679, + 110.50145539884473 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.5106218488640142, + "confidence_weighted": 0.5018972444025696, + "quality": { + "detector_confidence": 0.5106218488640142, + "weighted_confidence": 0.5018972444025696, + "q": { + "size": 1.0, + "aspect": 0.6760419217397143, + "border": 1.0, + "center": 0.0, + "sharpness": 0.9265392364192184, + "homography": 0.6801291688207284, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9967604192173971, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9992653923641922, + "homography": 0.9968012916882073, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9829137658702729, + "raw": { + "mean_edge_px": 58.36018657684326, + "edge_ratio": 1.958396418454695, + "distance_to_border_px": 143.0, + "distance_to_center_norm": 0.41272374987602234, + "laplacian_var": 2316.348091048046 + } + }, + "mean_reprojection_error_px": 61.0414473000793, + "corner_reprojection_errors_px": [ + 81.18204901625982, + 66.99465013231631, + 55.649947539132306, + 40.339142512608746 + ] + } + ] + }, + { + "marker_id": 124, + "link_name": "Arm2", + "position_world_m": [ + 0.18131118911414393, + -0.10739570221017908, + -0.03189330410226154 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.4015628677453015, + "mean_detector_confidence": 0.4106676690676095, + "mean_reprojection_error_px": 260.07136092003475, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.34091855704160245, + "confidence_weighted": 0.33488305339370045, + "quality": { + "detector_confidence": 0.34091855704160245, + "weighted_confidence": 0.33488305339370045, + "q": { + "size": 0.7142756080627441, + "aspect": 0.7379840018220383, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.7675736815140904, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9971427560806274, + "aspect": 0.9973798400182203, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9976757368151409, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9822963475491728, + "raw": { + "mean_edge_px": 35.71378040313721, + "edge_ratio": 1.7100858488288635, + "distance_to_border_px": 332.0, + "distance_to_center_norm": 0.14613474905490875, + "laplacian_var": 3569.5639450778804 + } + }, + "mean_reprojection_error_px": 133.84586705079283, + "corner_reprojection_errors_px": [ + 119.82530745688197, + 102.71844746528825, + 148.11005996258078, + 164.72965331842028 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.4804167810936165, + "confidence_weighted": 0.46824268209690245, + "quality": { + "detector_confidence": 0.4804167810936165, + "weighted_confidence": 0.46824268209690245, + "q": { + "size": 1.0, + "aspect": 0.9604332717578841, + "border": 0.21666666666666667, + "center": 0.0, + "sharpness": 0.5040158791602892, + "homography": 0.7617971982706998, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9996043327175789, + "border": 0.9921666666666666, + "center": 0.99, + "sharpness": 0.9950401587916029, + "homography": 0.997617971982707, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9746592969358792, + "raw": { + "mean_edge_px": 61.16414737701416, + "edge_ratio": 1.0823934976132112, + "distance_to_border_px": 26.0, + "distance_to_center_norm": 0.5047972202301025, + "laplacian_var": 1260.039697900723 + } + }, + "mean_reprojection_error_px": 386.2968547892767, + "corner_reprojection_errors_px": [ + 335.99015619978667, + 377.81233094568637, + 434.52780813886665, + 396.8571238727673 + ] + } + ] + }, + { + "marker_id": 122, + "link_name": "Arm2", + "position_world_m": [ + 0.23901692370487546, + -0.11504425270797457, + -0.003712231187506812 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.5578116149791655, + "mean_detector_confidence": 0.5690911450460799, + "mean_reprojection_error_px": 156.97704137303592, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.18665602922528673, + "confidence_weighted": 0.18263714689166236, + "quality": { + "detector_confidence": 0.18665602922528673, + "weighted_confidence": 0.18263714689166236, + "q": { + "size": 0.7027967643737792, + "aspect": 0.654945658590568, + "border": 1.0, + "center": 0.0, + "sharpness": 0.9331720780648362, + "homography": 0.539630944261658, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9970279676437378, + "aspect": 0.9965494565859057, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9993317207806484, + "homography": 0.9953963094426166, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9784690462434849, + "raw": { + "mean_edge_px": 35.139838218688965, + "edge_ratio": 2.0536884606639982, + "distance_to_border_px": 242.0, + "distance_to_center_norm": 0.27940502762794495, + "laplacian_var": 2332.9301951620905 + } + }, + "mean_reprojection_error_px": 193.56682332292803, + "corner_reprojection_errors_px": [ + 163.97016509333642, + 190.41476052018322, + 221.47627845820935, + 198.40608921998313 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.9456847621099602, + "confidence_weighted": 0.9281720009504786, + "quality": { + "detector_confidence": 0.9456847621099602, + "weighted_confidence": 0.9281720009504786, + "q": { + "size": 1.0, + "aspect": 0.9720842559145406, + "border": 1.0, + "center": 0.0, + "sharpness": 0.4361118058809464, + "homography": 0.7295816020733066, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9997208425591454, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9943611180588094, + "homography": 0.997295816020733, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9814813964852218, + "raw": { + "mean_edge_px": 53.96408939361572, + "edge_ratio": 1.0574348240198506, + "distance_to_border_px": 283.0, + "distance_to_center_norm": 0.3038843870162964, + "laplacian_var": 1090.279514702366 + } + }, + "mean_reprojection_error_px": 181.7153931265307, + "corner_reprojection_errors_px": [ + 138.684540863363, + 193.0880077987136, + 223.0588957628043, + 172.030128081242 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.5749326438029929, + "confidence_weighted": 0.5626256970953559, + "quality": { + "detector_confidence": 0.5749326438029929, + "weighted_confidence": 0.5626256970953559, + "q": { + "size": 1.0, + "aspect": 0.7301044220084257, + "border": 1.0, + "center": 0.0, + "sharpness": 0.34034870646107274, + "homography": 0.7735552097414035, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9973010442200843, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9934034870646107, + "homography": 0.997735552097414, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9785941069092363, + "raw": { + "mean_edge_px": 75.16977119445801, + "edge_ratio": 1.7393341824971433, + "distance_to_border_px": 251.0, + "distance_to_center_norm": 0.45140743255615234, + "laplacian_var": 850.8717661526819 + } + }, + "mean_reprojection_error_px": 95.64890766964893, + "corner_reprojection_errors_px": [ + 53.773295761932275, + 105.21890905303789, + 145.62101368093536, + 77.98241218269024 + ] + } + ] + }, + { + "marker_id": 218, + "link_name": "Arm2", + "position_world_m": [ + 0.19340818513542216, + -0.10113511218407022, + -0.05496043872679098 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_detector_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 101, + "link_name": "Arm2", + "position_world_m": [ + 0.18131118911414393, + -0.1749517537389431, + -0.0502285134504796 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.3264687769670023, + "mean_detector_confidence": 0.3325004465415643, + "mean_reprojection_error_px": 264.93045143660436, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.3325004465415643, + "confidence_weighted": 0.3264687769670023, + "quality": { + "detector_confidence": 0.3325004465415643, + "weighted_confidence": 0.3264687769670023, + "q": { + "size": 0.7345538902282714, + "aspect": 0.6782259971718826, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.7627185514162481, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9973455389022827, + "aspect": 0.9967822599717188, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9976271855141625, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9818596647394037, + "raw": { + "mean_edge_px": 36.727694511413574, + "edge_ratio": 1.9488695631540953, + "distance_to_border_px": 265.0, + "distance_to_center_norm": 0.31515657901763916, + "laplacian_var": 3563.870814189421 + } + }, + "mean_reprojection_error_px": 264.93045143660436, + "corner_reprojection_errors_px": [ + 248.83022765040747, + 232.83467620506548, + 282.08856122928034, + 295.96834066166423 + ] + } + ] + }, + { + "marker_id": 102, + "link_name": "Arm2", + "position_world_m": [ + 0.2135630828168288, + -0.1847875031094183, + -0.013988709547700082 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.8354983021138915, + "mean_detector_confidence": 0.8473641170835915, + "mean_reprojection_error_px": 202.84175431125945, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence_detector": 0.9494437061622057, + "confidence_weighted": 0.9376120619964978, + "quality": { + "detector_confidence": 0.9494437061622057, + "weighted_confidence": 0.9376120619964978, + "q": { + "size": 0.9537287521362304, + "aspect": 0.9740662971297988, + "border": 1.0, + "center": 0.0, + "sharpness": 1.0, + "homography": 0.8234129943538817, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 0.9995372875213623, + "aspect": 0.9997406629712979, + "border": 1.0, + "center": 0.99, + "sharpness": 1.0, + "homography": 0.9982341299435388, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9875383405156971, + "raw": { + "mean_edge_px": 47.68643760681152, + "edge_ratio": 1.0532483321651058, + "distance_to_border_px": 309.0, + "distance_to_center_norm": 0.3549750745296478, + "laplacian_var": 3935.778766550373 + } + }, + "mean_reprojection_error_px": 274.9015986721038, + "corner_reprojection_errors_px": [ + 238.63013881195602, + 266.10100794961005, + 309.3183640595005, + 285.55688386734846 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence_detector": 0.8913851020933449, + "confidence_weighted": 0.880139786945703, + "quality": { + "detector_confidence": 0.8913851020933449, + "weighted_confidence": 0.880139786945703, + "q": { + "size": 1.0, + "aspect": 0.9425738852513735, + "border": 1.0, + "center": 0.0, + "sharpness": 0.7931141622847128, + "homography": 0.9999959590215692, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.9994257388525137, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9979311416228471, + "homography": 0.9999999595902157, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9873844479549488, + "raw": { + "mean_edge_px": 62.59456253051758, + "edge_ratio": 1.1218495773056807, + "distance_to_border_px": 134.0, + "distance_to_center_norm": 0.3347931206226349, + "laplacian_var": 1982.7854057117818 + } + }, + "mean_reprojection_error_px": 267.3922477582417, + "corner_reprojection_errors_px": [ + 230.84709969997198, + 248.37489636844154, + 301.82494379742434, + 288.52205116712895 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence_detector": 0.7012635429952238, + "confidence_weighted": 0.6887430573994738, + "quality": { + "detector_confidence": 0.7012635429952238, + "weighted_confidence": 0.6887430573994738, + "q": { + "size": 1.0, + "aspect": 0.8244031865405023, + "border": 1.0, + "center": 0.0, + "sharpness": 0.6987050522983833, + "homography": 0.6814939272403986, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "factor": { + "size": 1.0, + "aspect": 0.998244031865405, + "border": 1.0, + "center": 0.99, + "sharpness": 0.9969870505229839, + "homography": 0.9968149392724039, + "normal_visibility": 1.0, + "spin": 1.0 + }, + "weight_multiplier": 0.9821458198977909, + "raw": { + "mean_edge_px": 84.7633228302002, + "edge_ratio": 1.4259974156489281, + "distance_to_border_px": 140.0, + "distance_to_center_norm": 0.3269830048084259, + "laplacian_var": 1746.7626307459584 + } + }, + "mean_reprojection_error_px": 66.23141650343285, + "corner_reprojection_errors_px": [ + 109.10953068690768, + 54.093739871757776, + 12.868357032820903, + 88.85403842224508 + ] + } + ] + }, + { + "marker_id": 219, + "link_name": "Arm2", + "position_world_m": [ + 0.19340818513542216, + -0.20439936237803805, + -0.08298711587335288 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_detector_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + } + ], + "statistics": { + "observation_count": 34, + "camera_count": 3, + "marker_count": 23, + "observed_marker_count": 18, + "mean_detector_confidence": 0.5871913728875101, + "mean_weighted_confidence": 0.5774319023193206, + "mean_reprojection_error_px": 231.03443493219376, + "quality_means": { + "size": 0.8143773112577551, + "aspect": 0.834768084426124, + "border": 0.9237745098039215, + "homography": 0.7934741744632792 + }, + "quality_config": { + "size_ref_px": 50.0, + "border_ref_px": 120.0, + "center_ref_norm": 0.01, + "sharpness_ref": 2500.0, + "homography_ref": 0.18, + "size_factor": 0.01, + "aspect_factor": 0.01, + "border_factor": 0.01, + "center_factor": 0.01, + "sharpness_factor": 0.01, + "homography_factor": 0.01 + } + }, + "solver": { + "final_cost": 24726.245833952267, + "status": 0, + "message": "The maximum number of function evaluations is exceeded." + } +} \ No newline at end of file diff --git a/pipeline/multiview_pose_ohne_confidence.json b/pipeline/multiview_pose_ohne_confidence.json new file mode 100644 index 0000000..90170d9 --- /dev/null +++ b/pipeline/multiview_pose_ohne_confidence.json @@ -0,0 +1,966 @@ +{ + "schema_version": "1.0", + "created_utc": "2026-05-28T21:06:06.508985Z", + "source_robot_json": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json", + "source_detections": [ + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json" + ], + "robot_pose": { + "state": { + "x": 178.4429017494112, + "y": 58.47337818356634, + "z": -115.44834336922801, + "a": -110.23287555471994, + "b": 21.99999999999879, + "c": 91.00000000000222, + "e": 10.000000000003752 + }, + "uncertainty": { + "x_mm": 452.93520846643963, + "y_mm": 2505.6853087014742, + "z_mm": 479.7547548672694, + "a_deg": 419.0184635434338, + "b_deg": 10357.838151169784, + "c_deg": 10357.838358456424, + "e_mm": 103578.27625405855 + }, + "confidence": { + "x": 2.1343902596830898e-20, + "y": 1.5117142415373693e-109, + "z": 1.460547647110293e-21, + "a": 6.342483509852416e-19, + "b": 0.0, + "c": 0.0, + "e": 0.36787944117144233 + } + }, + "camera_poses": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + -152.34091569714903, + -19.013446299323782, + -99.72128351445228 + ], + "rvec": [ + -5.499131960772023, + -21.91270066095422, + 3.835322497331174 + ], + "tvec": [ + 1.5121559708775556, + 7.396415796967461, + -182.91147186295072 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 17 + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + 0.08962448933807897, + 0.30899658581804224, + -0.6948843092613544 + ], + "rvec": [ + -0.523540707058038, + -3.160660993109504, + -0.8426789395152159 + ], + "tvec": [ + 0.17310377758414208, + 0.03144977103054412, + -0.7452661514399899 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 10 + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "camera_id": "cam1", + "camera_position_world_m": [ + 0.19930315742175841, + -0.4034279127468583, + 0.6690051405865579 + ], + "rvec": [ + -3.515407873484386, + 0.6592488811642211, + 0.3554510183912642 + ], + "tvec": [ + -0.12629764596438484, + 0.0026714931553666757, + 0.7962948418902289 + ], + "intrinsics": { + "camera_matrix": [ + [ + 1777.77783203125, + 0.0, + 640.0 + ], + [ + 0.0, + 1500.0, + 360.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "distortion_coefficients": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "observation_count": 7 + } + ], + "marker_positions": [ + { + "marker_id": 210, + "link_name": "Board", + "position_world_m": [ + 0.02, + -0.02, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.6997700579082148, + "mean_reprojection_error_px": 219.98520262396713, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.5673043275049208, + "mean_reprojection_error_px": 582.6928891559149, + "corner_reprojection_errors_px": [ + 608.4008310831335, + 564.1978138064919, + 556.9944685718621, + 601.1784431621722 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.7813266383036261, + "mean_reprojection_error_px": 39.955946109809304, + "corner_reprojection_errors_px": [ + 50.05440600297249, + 62.06318313444874, + 46.054117736318254, + 1.6520775654977295 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.7506792079160973, + "mean_reprojection_error_px": 37.30677260617712, + "corner_reprojection_errors_px": [ + 63.966234566185975, + 24.372791532782248, + 11.26324714586789, + 49.62481717987238 + ] + } + ] + }, + { + "marker_id": 211, + "link_name": "Board", + "position_world_m": [ + 0.25, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.6412317666518965, + "mean_reprojection_error_px": 213.01123894247837, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.6412317666518965, + "mean_reprojection_error_px": 213.01123894247837, + "corner_reprojection_errors_px": [ + 236.1031649719927, + 200.91925610460427, + 190.4144522672059, + 224.6080824261106 + ] + } + ] + }, + { + "marker_id": 215, + "link_name": "Board", + "position_world_m": [ + 0.25, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.8108527003549935, + "mean_reprojection_error_px": 198.10515794746527, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.7952557920267838, + "mean_reprojection_error_px": 204.57188538155356, + "corner_reprojection_errors_px": [ + 217.65412604361978, + 184.2181761177656, + 192.91255827135913, + 223.50268109346976 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.826449608683203, + "mean_reprojection_error_px": 191.63843051337702, + "corner_reprojection_errors_px": [ + 226.90990661141322, + 176.45008691276055, + 156.32935430702966, + 206.86437422230463 + ] + } + ] + }, + { + "marker_id": 214, + "link_name": "Board", + "position_world_m": [ + 0.35000000000000003, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.6041252446922665, + "mean_reprojection_error_px": 443.42359136317395, + "observations": [ + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.6041252446922665, + "mean_reprojection_error_px": 443.42359136317395, + "corner_reprojection_errors_px": [ + 448.5785815165172, + 422.5680699902232, + 440.44289571629525, + 462.10481822966005 + ] + } + ] + }, + { + "marker_id": 208, + "link_name": "Board", + "position_world_m": [ + 0.35000000000000003, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.6280424706698967, + "mean_reprojection_error_px": 104.44146786678962, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.6280424706698967, + "mean_reprojection_error_px": 104.44146786678962, + "corner_reprojection_errors_px": [ + 99.73569678834396, + 84.11026583409073, + 111.81629138739532, + 122.10361745732845 + ] + } + ] + }, + { + "marker_id": 206, + "link_name": "Board", + "position_world_m": [ + 0.65, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.33820423087105295, + "mean_reprojection_error_px": 269.1974700827843, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.33820423087105295, + "mean_reprojection_error_px": 269.1974700827843, + "corner_reprojection_errors_px": [ + 250.50345115135508, + 274.79359034013294, + 287.8396527117673, + 263.6531861278821 + ] + } + ] + }, + { + "marker_id": 205, + "link_name": "Board", + "position_world_m": [ + 0.75, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.28724459014346065, + "mean_reprojection_error_px": 418.6572411272094, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.28724459014346065, + "mean_reprojection_error_px": 418.6572411272094, + "corner_reprojection_errors_px": [ + 397.9342521865105, + 419.4901443591502, + 439.2360756416858, + 417.96849232149117 + ] + } + ] + }, + { + "marker_id": 207, + "link_name": "Board", + "position_world_m": [ + 0.75, + -0.01, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.27228561618555186, + "mean_reprojection_error_px": 365.7887375890425, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.27228561618555186, + "mean_reprojection_error_px": 365.7887375890425, + "corner_reprojection_errors_px": [ + 348.36626075065936, + 369.7628063312705, + 383.5097638627369, + 361.51611941150327 + ] + } + ] + }, + { + "marker_id": 217, + "link_name": "Board", + "position_world_m": [ + 0.65, + -0.09, + 0.0003 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.35596573288593486, + "mean_reprojection_error_px": 324.2590328208219, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.35596573288593486, + "mean_reprojection_error_px": 324.2590328208219, + "corner_reprojection_errors_px": [ + 302.09103568606685, + 325.56745248682427, + 346.3582043745394, + 323.01943873585714 + ] + } + ] + }, + { + "marker_id": 198, + "link_name": "Arm1", + "position_world_m": [ + 0.1784429017494112, + -0.05382924293825046, + 0.15468488162937843 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.16652042240877096, + "mean_reprojection_error_px": 173.03325228464936, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.20150745250271476, + "mean_reprojection_error_px": 247.8149724280193, + "corner_reprojection_errors_px": [ + 273.59743355359444, + 245.42488018268014, + 222.46143771784855, + 249.77613825795413 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.13153339231482716, + "mean_reprojection_error_px": 98.25153214127941, + "corner_reprojection_errors_px": [ + 86.59176111374053, + 97.5474468583387, + 120.03541185467505, + 88.83150873836338 + ] + } + ] + }, + { + "marker_id": 229, + "link_name": "Arm1", + "position_world_m": [ + 0.1784429017494112, + -0.100889763924023, + 0.23140063858026605 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.2274747191640703, + "mean_reprojection_error_px": 110.21276026061363, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.26810902547334975, + "mean_reprojection_error_px": 155.73254773328267, + "corner_reprojection_errors_px": [ + 180.08971330368811, + 157.10232370463987, + 131.63039756460225, + 154.10775636020045 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.18684041285479083, + "mean_reprojection_error_px": 64.69297278794461, + "corner_reprojection_errors_px": [ + 54.16045919019677, + 65.65659031871526, + 89.34107228321712, + 49.613769359649325 + ] + } + ] + }, + { + "marker_id": 242, + "link_name": "Arm1", + "position_world_m": [ + 0.1784429017494112, + -0.1605575748858245, + 0.19479801114688738 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 243, + "link_name": "Arm1", + "position_world_m": [ + 0.1784429017494112, + -0.14902498312161308, + 0.24293323034447747 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.9174550709990235, + "mean_reprojection_error_px": 44.17934855555228, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.8899728634608616, + "mean_reprojection_error_px": 78.81046188174007, + "corner_reprojection_errors_px": [ + 101.13197340765323, + 72.09114541671448, + 50.99718387664307, + 91.0215448259495 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.9132502955127223, + "mean_reprojection_error_px": 16.837449190455786, + "corner_reprojection_errors_px": [ + 14.377732738720587, + 19.045826627822404, + 19.362443297536196, + 14.563794097743955 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.9491420540234864, + "mean_reprojection_error_px": 36.89013459446098, + "corner_reprojection_errors_px": [ + 44.535439947724775, + 1.0820871934520055, + 48.70513064720212, + 53.23788058946502 + ] + } + ] + }, + { + "marker_id": 244, + "link_name": "Ellbow", + "position_world_m": [ + 0.3034429017494112, + 0.0, + 0.0 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 245, + "link_name": "Ellbow", + "position_world_m": [ + 0.2684429017494112, + 0.02934513796721045, + -0.019075190108761277 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 246, + "link_name": "Ellbow", + "position_world_m": [ + 0.2684429017494112, + -0.02934513796721045, + 0.019075190108761277 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.6792447690577089, + "mean_reprojection_error_px": 75.53581325353834, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.8015179238063419, + "mean_reprojection_error_px": 130.96780196095426, + "corner_reprojection_errors_px": [ + 131.52551717948674, + 149.219632082114, + 133.91939902794635, + 109.2066595542699 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.7581700566520715, + "mean_reprojection_error_px": 40.470485652838775, + "corner_reprojection_errors_px": [ + 54.08557804238714, + 23.05261314724495, + 38.690970054967124, + 46.052781366755866 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.4780463267147134, + "mean_reprojection_error_px": 55.16915214682197, + "corner_reprojection_errors_px": [ + 4.544326976345962, + 67.214794241062, + 103.3039176555024, + 45.61356971437756 + ] + } + ] + }, + { + "marker_id": 247, + "link_name": "Ellbow", + "position_world_m": [ + 0.23094290174941118, + -0.02934513796721045, + 0.019075190108761277 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.7117000257529918, + "mean_reprojection_error_px": 71.76825320561402, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.8796777163094818, + "mean_reprojection_error_px": 109.30571474994146, + "corner_reprojection_errors_px": [ + 125.10305772921419, + 126.687286672494, + 97.67977416118754, + 87.7527404368701 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.7448005120854795, + "mean_reprojection_error_px": 41.53123348024193, + "corner_reprojection_errors_px": [ + 62.91946766510497, + 50.39956997542899, + 30.245119355881712, + 22.56077692455205 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.5106218488640142, + "mean_reprojection_error_px": 64.46781138665867, + "corner_reprojection_errors_px": [ + 53.21697434340821, + 81.71744187395875, + 89.31098122106415, + 33.62584810820357 + ] + } + ] + }, + { + "marker_id": 124, + "link_name": "Arm2", + "position_world_m": [ + 0.1905471840415046, + -0.14689057815962717, + -0.16571856986506908 + ], + "size_m": 0.025, + "observation_count": 2, + "mean_confidence": 0.4106676690676095, + "mean_reprojection_error_px": 110.73873242579455, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.34091855704160245, + "mean_reprojection_error_px": 135.68416308738642, + "corner_reprojection_errors_px": [ + 122.09592215917837, + 104.54432626694273, + 149.5516106827851, + 166.54479324063954 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.4804167810936165, + "mean_reprojection_error_px": 85.79330176420268, + "corner_reprojection_errors_px": [ + 80.25443845468878, + 44.769514846082515, + 101.1257798229199, + 117.02347393311956 + ] + } + ] + }, + { + "marker_id": 122, + "link_name": "Arm2", + "position_world_m": [ + 0.2065917439726603, + -0.09049216814460652, + -0.1582492027972934 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.5690911450460799, + "mean_reprojection_error_px": 112.17658015146829, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.18665602922528673, + "mean_reprojection_error_px": 193.21424613028685, + "corner_reprojection_errors_px": [ + 163.5154782260392, + 189.8197178899714, + 221.16013279196892, + 198.36165561316793 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.9456847621099602, + "mean_reprojection_error_px": 59.619753741849934, + "corner_reprojection_errors_px": [ + 62.57446736991388, + 94.35842345190635, + 52.23720146118015, + 29.308922684399352 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.5749326438029929, + "mean_reprojection_error_px": 83.69574058226807, + "corner_reprojection_errors_px": [ + 95.82678742449872, + 125.24546180132528, + 68.46920157539058, + 45.24151152785769 + ] + } + ] + }, + { + "marker_id": 218, + "link_name": "Arm2", + "position_world_m": [ + 0.16633861945731776, + -0.03350621972608657, + -0.1118025920534069 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + }, + { + "marker_id": 101, + "link_name": "Arm2", + "position_world_m": [ + 0.146660650151536, + -0.08689676917303694, + -0.16058631632445308 + ], + "size_m": 0.025, + "observation_count": 1, + "mean_confidence": 0.3325004465415643, + "mean_reprojection_error_px": 267.5418618263792, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.3325004465415643, + "mean_reprojection_error_px": 267.5418618263792, + "corner_reprojection_errors_px": [ + 251.71309778665415, + 235.71974142034514, + 284.44360570880553, + 298.291002389712 + ] + } + ] + }, + { + "marker_id": 102, + "link_name": "Arm2", + "position_world_m": [ + 0.1637795636789253, + -0.12583826165265122, + -0.13527321767766717 + ], + "size_m": 0.025, + "observation_count": 3, + "mean_confidence": 0.8473641170835915, + "mean_reprojection_error_px": 135.5185983259116, + "observations": [ + { + "view_index": 0, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png", + "confidence": 0.9494437061622057, + "mean_reprojection_error_px": 275.69603657656097, + "corner_reprojection_errors_px": [ + 239.57198324805327, + 266.35716033620514, + 310.0207932359541, + 286.8342094860313 + ] + }, + { + "view_index": 1, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png", + "confidence": 0.8913851020933449, + "mean_reprojection_error_px": 62.952236808026484, + "corner_reprojection_errors_px": [ + 32.059770721250835, + 38.09936127795048, + 90.48302392022927, + 91.16679131267536 + ] + }, + { + "view_index": 2, + "source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json", + "image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png", + "confidence": 0.7012635429952238, + "mean_reprojection_error_px": 67.90752159314737, + "corner_reprojection_errors_px": [ + 102.72093538416912, + 41.99761561398122, + 26.866949386310086, + 100.04458598812904 + ] + } + ] + }, + { + "marker_id": 219, + "link_name": "Arm2", + "position_world_m": [ + 0.16633861945731776, + -0.09182180091572817, + -0.201514870981736 + ], + "size_m": 0.025, + "observation_count": 0, + "mean_confidence": null, + "mean_reprojection_error_px": null, + "observations": [] + } + ] +} \ No newline at end of file diff --git a/pipeline/multiview_pose_summary.json b/pipeline/multiview_pose_summary.json new file mode 100644 index 0000000..60c6c42 --- /dev/null +++ b/pipeline/multiview_pose_summary.json @@ -0,0 +1,72 @@ +{ + "schema_version": "1.0", + "created_utc": "2026-05-28T21:18:54.942233Z", + "source_robot_json": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json", + "source_detections": [ + "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json", + "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json", + "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json" + ], + "solver": { + "final_cost": 26976.87543820547, + "status": 0, + "message": "The maximum number of function evaluations is exceeded." + }, + "robot_pose": { + "state": { + "x": 100.21525656040845, + "y": 29.397626553503173, + "z": -36.04825291833897, + "a": -120.21337951359496, + "b": 22.00000000000008, + "c": 90.99999999999989, + "e": 10.000000000000258 + }, + "uncertainty": { + "x_mm": 17194.235819424397, + "y_mm": 6014.1408132596725, + "z_mm": 3621.298501957444, + "a_deg": 8796.009134059024, + "b_deg": 12562.017200522667, + "c_deg": 12562.017199380893, + "e_mm": 125620.17196327279 + }, + "confidence": { + "x": 0.0, + "y": 6.444409678452354e-262, + "z": 5.358019964976179e-158, + "a": 0.0, + "b": 0.0, + "c": 0.0, + "e": 0.36787944117144233 + } + }, + "statistics": { + "observation_count": 34, + "camera_count": 3, + "marker_count": 23, + "observed_marker_count": 18, + "mean_detector_confidence": 0.5871913728875101, + "mean_weighted_confidence": 0.5774319023193206, + "mean_reprojection_error_px": 236.06726174073253, + "quality_means": { + "size": 0.8143773112577551, + "aspect": 0.834768084426124, + "border": 0.9237745098039215, + "homography": 0.7934741744632792 + }, + "quality_config": { + "size_ref_px": 50.0, + "border_ref_px": 120.0, + "center_ref_norm": 0.01, + "sharpness_ref": 2500.0, + "homography_ref": 0.18, + "size_factor": 0.01, + "aspect_factor": 0.01, + "border_factor": 0.01, + "center_factor": 0.01, + "sharpness_factor": 0.01, + "homography_factor": 0.01 + } + } +} \ No newline at end of file diff --git a/pipeline/render_1a_aruco_detection.json b/pipeline/render_1a_aruco_detection.json index 4fc9276..be039ab 100644 --- a/pipeline/render_1a_aruco_detection.json +++ b/pipeline/render_1a_aruco_detection.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "created_utc": "2026-05-28T20:31:58Z", + "created_utc": "2026-05-28T21:11:29Z", "vision_config": { "MarkerType": "DICT_4X4_250", "MarkerSize": 0.025 @@ -46,7 +46,7 @@ }, "detections": [ { - "observation_id": "11b87d79-31e1-4293-abe2-4327e2021077", + "observation_id": "e4f9bd9f-a867-4d2d-b0d3-c438f92fa01f", "type": "aruco", "marker_id": 102, "marker_size_m": 0.025, @@ -100,7 +100,7 @@ "confidence": 0.9494437061622057 }, { - "observation_id": "3f6119be-7ed5-4ba2-b0b8-f2eb85c0f4ac", + "observation_id": "3669c72d-6c64-46a9-9041-e0f332ed19a3", "type": "aruco", "marker_id": 243, "marker_size_m": 0.025, @@ -154,7 +154,7 @@ "confidence": 0.8899728634608616 }, { - "observation_id": "a963cd6a-dea8-47d2-ba29-7410d1add84e", + "observation_id": "920dccb3-6db8-43fb-b0b3-036af4ed6acb", "type": "aruco", "marker_id": 210, "marker_size_m": 0.025, @@ -208,7 +208,7 @@ "confidence": 0.5673043275049208 }, { - "observation_id": "5da8c3b3-2b28-47b4-9132-c5225c5ba0bb", + "observation_id": "858fbb79-58d9-436a-9147-96b0a15fd61e", "type": "aruco", "marker_id": 247, "marker_size_m": 0.025, @@ -262,7 +262,7 @@ "confidence": 0.8796777163094818 }, { - "observation_id": "b765e797-5dd9-4123-ac41-85b5606132a0", + "observation_id": "cafb1327-eefa-457c-9b1e-ed2d2ad78a8c", "type": "aruco", "marker_id": 246, "marker_size_m": 0.025, @@ -316,7 +316,7 @@ "confidence": 0.8015179238063419 }, { - "observation_id": "aca8152b-525f-45e7-9849-c46d2123a21c", + "observation_id": "cb014fcc-1cbe-4e59-afaa-6ea599b7f5ac", "type": "aruco", "marker_id": 101, "marker_size_m": 0.025, @@ -370,7 +370,7 @@ "confidence": 0.3325004465415643 }, { - "observation_id": "f0a37d05-7187-4ae1-a53a-2b0260b751b4", + "observation_id": "a59f4cec-6038-48f8-b124-11a1b6e89cf3", "type": "aruco", "marker_id": 215, "marker_size_m": 0.025, @@ -424,7 +424,7 @@ "confidence": 0.7952557920267838 }, { - "observation_id": "38cfa10c-246e-439d-ba19-ab664d030ccc", + "observation_id": "0d4d4912-7df5-4189-80d0-cc1066edd2e1", "type": "aruco", "marker_id": 124, "marker_size_m": 0.025, @@ -478,7 +478,7 @@ "confidence": 0.34091855704160245 }, { - "observation_id": "95edc7e5-0883-4ab5-8b6a-1f35d392d98a", + "observation_id": "47b5571d-68d0-4738-93c5-06c45c91168f", "type": "aruco", "marker_id": 229, "marker_size_m": 0.025, @@ -532,7 +532,7 @@ "confidence": 0.26810902547334975 }, { - "observation_id": "602d2bac-2427-4211-86be-9a2b5905a4e4", + "observation_id": "3a5f11f4-1233-41b7-9ce8-db0acaa2855e", "type": "aruco", "marker_id": 122, "marker_size_m": 0.025, @@ -586,7 +586,7 @@ "confidence": 0.18665602922528673 }, { - "observation_id": "308e1529-24a2-49e0-a7ad-6ac4fdead845", + "observation_id": "76947529-1679-4274-b547-56c7d61dde94", "type": "aruco", "marker_id": 198, "marker_size_m": 0.025, @@ -640,7 +640,7 @@ "confidence": 0.20150745250271476 }, { - "observation_id": "0159ed42-fec0-4b1c-a383-9ca83f5a320f", + "observation_id": "7a157cbe-69a0-4f8d-b89d-af82d8470779", "type": "aruco", "marker_id": 211, "marker_size_m": 0.025, @@ -694,7 +694,7 @@ "confidence": 0.6412317666518965 }, { - "observation_id": "4fa2ceb4-7915-4dba-b0f8-9700378eec0b", + "observation_id": "19c6229a-77c2-4846-86c2-2fe3cabd5ace", "type": "aruco", "marker_id": 208, "marker_size_m": 0.025, @@ -748,7 +748,7 @@ "confidence": 0.6280424706698967 }, { - "observation_id": "d3bc0898-720e-4180-b70a-57a9adc4a30e", + "observation_id": "cf1fe906-7f6e-459b-96cc-a983a15d58b7", "type": "aruco", "marker_id": 217, "marker_size_m": 0.025, @@ -802,7 +802,7 @@ "confidence": 0.35596573288593486 }, { - "observation_id": "d0d5506c-abf1-495f-bce9-33d4b374e599", + "observation_id": "249e0b3c-e2b1-40e4-baf8-073d85acc08c", "type": "aruco", "marker_id": 206, "marker_size_m": 0.025, @@ -856,7 +856,7 @@ "confidence": 0.33820423087105295 }, { - "observation_id": "7b5443c9-fb76-4270-8c88-32c5a20953c1", + "observation_id": "152dc984-1ca4-4ca0-aad7-1bbbd8aba04f", "type": "aruco", "marker_id": 205, "marker_size_m": 0.025, @@ -910,7 +910,7 @@ "confidence": 0.28724459014346065 }, { - "observation_id": "ac8f8ef1-039a-4762-a4f6-8bdea9e3a4e4", + "observation_id": "66ce6541-9898-4bfc-9201-0d05fa7e6e62", "type": "aruco", "marker_id": 207, "marker_size_m": 0.025, diff --git a/pipeline/render_1b_aruco_detection.json b/pipeline/render_1b_aruco_detection.json index f2e2679..ed0a67f 100644 --- a/pipeline/render_1b_aruco_detection.json +++ b/pipeline/render_1b_aruco_detection.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "created_utc": "2026-05-28T20:31:58Z", + "created_utc": "2026-05-28T21:11:29Z", "vision_config": { "MarkerType": "DICT_4X4_250", "MarkerSize": 0.025 @@ -46,7 +46,7 @@ }, "detections": [ { - "observation_id": "e2619c8d-750d-4334-9b42-cb95426ed979", + "observation_id": "e57e88d8-4e83-459d-aacb-54dfb3d1a39f", "type": "aruco", "marker_id": 102, "marker_size_m": 0.025, @@ -100,7 +100,7 @@ "confidence": 0.8913851020933449 }, { - "observation_id": "d1d011f0-a579-448e-ace9-ea148ecbc5e7", + "observation_id": "3bb219ef-c7ff-4f0b-8cfa-284ead393c38", "type": "aruco", "marker_id": 124, "marker_size_m": 0.025, @@ -154,7 +154,7 @@ "confidence": 0.4804167810936165 }, { - "observation_id": "1b5435e6-7883-434c-a9c7-63f4b8d750d1", + "observation_id": "26604964-f6c1-492f-964c-850826bc69cf", "type": "aruco", "marker_id": 243, "marker_size_m": 0.025, @@ -208,7 +208,7 @@ "confidence": 0.9132502955127223 }, { - "observation_id": "a673d83e-73ff-4d2d-a412-470b6da12d56", + "observation_id": "5feff871-616f-43e8-b817-f5d67116b577", "type": "aruco", "marker_id": 122, "marker_size_m": 0.025, @@ -262,7 +262,7 @@ "confidence": 0.9456847621099602 }, { - "observation_id": "a755c678-9ad1-4af2-a744-a3c1a8ae0596", + "observation_id": "957db844-bb09-47b0-95a9-c2703a9f8a07", "type": "aruco", "marker_id": 247, "marker_size_m": 0.025, @@ -316,7 +316,7 @@ "confidence": 0.7448005120854795 }, { - "observation_id": "38397299-767f-490d-a47f-a3f09635e995", + "observation_id": "ce61eb47-425e-45a1-8079-a8da9414a07b", "type": "aruco", "marker_id": 246, "marker_size_m": 0.025, @@ -370,7 +370,7 @@ "confidence": 0.7581700566520715 }, { - "observation_id": "273cc5e9-e6e4-4359-8fb1-02a33a935f0d", + "observation_id": "408b46a0-047b-4718-bc75-25fe6edef9ed", "type": "aruco", "marker_id": 215, "marker_size_m": 0.025, @@ -424,7 +424,7 @@ "confidence": 0.826449608683203 }, { - "observation_id": "8da984fe-3152-43d6-8a39-b4e9266c2184", + "observation_id": "e971c366-cf09-4c98-8aba-6778415b54e2", "type": "aruco", "marker_id": 210, "marker_size_m": 0.025, @@ -478,7 +478,7 @@ "confidence": 0.7813266383036261 }, { - "observation_id": "92d1be16-65a4-4855-a63c-4d21357d0a9d", + "observation_id": "898e0c56-a4d2-4cbf-945e-1fa1cbec6ad5", "type": "aruco", "marker_id": 229, "marker_size_m": 0.025, @@ -532,7 +532,7 @@ "confidence": 0.18684041285479083 }, { - "observation_id": "5cbcbaf2-7581-4f25-81e3-85b4ce5a03b2", + "observation_id": "87adf180-14ed-459f-8d33-64e48ff2b517", "type": "aruco", "marker_id": 198, "marker_size_m": 0.025, diff --git a/pipeline/render_1c_aruco_detection.json b/pipeline/render_1c_aruco_detection.json index a5b9b3d..9aadc56 100644 --- a/pipeline/render_1c_aruco_detection.json +++ b/pipeline/render_1c_aruco_detection.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "created_utc": "2026-05-28T20:31:59Z", + "created_utc": "2026-05-28T21:11:30Z", "vision_config": { "MarkerType": "DICT_4X4_250", "MarkerSize": 0.025 @@ -46,7 +46,7 @@ }, "detections": [ { - "observation_id": "6df4525f-58c7-4757-8a5e-130486fc123b", + "observation_id": "55dad250-7787-4d70-b26b-51bf9c63187f", "type": "aruco", "marker_id": 102, "marker_size_m": 0.025, @@ -100,7 +100,7 @@ "confidence": 0.7012635429952238 }, { - "observation_id": "c4dd28db-6041-4464-867a-96b25d671918", + "observation_id": "5af894e7-68a0-4872-ada4-689be1d630b6", "type": "aruco", "marker_id": 122, "marker_size_m": 0.025, @@ -154,7 +154,7 @@ "confidence": 0.5749326438029929 }, { - "observation_id": "5bdf65ad-9a27-4a28-be1e-6e925df61c72", + "observation_id": "47253afe-906c-444d-af3f-db6e99351519", "type": "aruco", "marker_id": 243, "marker_size_m": 0.025, @@ -208,7 +208,7 @@ "confidence": 0.9491420540234864 }, { - "observation_id": "37b9dec9-2578-4765-a40a-516fbe0424f5", + "observation_id": "4b090e16-a2e9-4cb6-81e4-307a04f04820", "type": "aruco", "marker_id": 246, "marker_size_m": 0.025, @@ -262,7 +262,7 @@ "confidence": 0.4780463267147134 }, { - "observation_id": "a49c96d9-7884-4ddb-8c07-8f86130b5220", + "observation_id": "8d0cb44f-1cd1-41a3-a6bc-c39527148ec7", "type": "aruco", "marker_id": 247, "marker_size_m": 0.025, @@ -316,7 +316,7 @@ "confidence": 0.5106218488640142 }, { - "observation_id": "4da1fc9d-a7a9-4dd8-a39b-a667b8d5d165", + "observation_id": "cde0d217-048d-42a3-88bd-52c1d620984f", "type": "aruco", "marker_id": 214, "marker_size_m": 0.025, @@ -370,7 +370,7 @@ "confidence": 0.6041252446922665 }, { - "observation_id": "a88abb43-d181-4584-b59b-28c65a2fff45", + "observation_id": "1e6148fd-0380-48d2-93f9-b4a546a35a7c", "type": "aruco", "marker_id": 210, "marker_size_m": 0.025, diff --git a/pipeline/run_pipeline.bat b/pipeline/run_pipeline.bat index fb817a1..26a5ea4 100644 --- a/pipeline/run_pipeline.bat +++ b/pipeline/run_pipeline.bat @@ -3,4 +3,4 @@ python3 1_detect_aruco_observations.py --image render_1a.png -npz render.npz -ro python3 1_detect_aruco_observations.py --image render_1b.png -npz render.npz -robot ../robot.json -cameraId cam1 -outDir . python3 1_detect_aruco_observations.py --image render_1c.png -npz render.npz -robot ../robot.json -cameraId cam1 -outDir . -python3 2_Multiview.py --robot ../robot.json --detections render_1a_aruco_detection.json render_1b_aruco_detection.json render_1c_aruco_detection.json --outDir . +python3 2_Multiview_ersteVersion.py --robot ../robot.json --detections render_1a_aruco_detection.json render_1b_aruco_detection.json render_1c_aruco_detection.json --outDir .