Files
yolov26_3d/tests/test_two_roi_inference.py
2026-06-24 09:35:46 +08:00

1529 lines
60 KiB
Python
Executable File

import argparse
from collections import defaultdict
from pathlib import Path
import random
import numpy as np
from tools.pdcl_inference.analyze_val_two_roi_badcases import (
FACE_VISIBILITY_BUCKET_ORDER,
add_interval_visual_record,
build_grouped_metric_bucket_interval_rows,
build_badcase_draw_kwargs,
flatten_grouped_metric_bucket_interval_rows,
is_signed_lateral_yaw_compare_longitudinal_eligible,
interval_label,
make_metric_bucket,
make_interval_visual_reservoirs,
make_record,
select_interval_visual_records,
)
from tools.pdcl_inference.two_roi_inference import (
DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
_project_selected_face_edges_from_box,
_selected_edge_residual_stats,
_build_edge_heading_decoded,
_restore_depth_scale,
add_two_roi_inference_args,
)
from ultralytics.utils.plotting_3d import (
compute_3d_box_corners,
decode_edge_yaw_selection_from_prediction,
decode_3d_prediction,
extract_3d_attrs_from_prediction,
face_center_from_corners,
project_3d_to_2d,
project_face_bottom_edge,
project_partial_face_bottom_edge,
reconstruct_edge_based_box_from_selection,
reconstruct_two_face_box_from_edge_selection,
)
def _encode_face_branch(pred, corners, face_type, score, anchor, stride, calib):
face_center = face_center_from_corners(corners, face_type)
face_uv = project_3d_to_2d(face_center[None, :], calib)[0]
off = face_type * 6
pred[off] = face_center[2]
pred[off + 1 : off + 3] = [face_uv[0] / stride - anchor[0], face_uv[1] / stride - anchor[1]]
pred[off + 5] = score
def _encode_edge_branch(pred_edge, face_type, points_3d, points_2d, anchor, stride):
face_block = pred_edge[face_type * 15 : (face_type + 1) * 15].reshape(5, 3)
face_block[:, 0] = points_2d[:, 0] / stride - anchor[0]
face_block[:, 1] = points_2d[:, 1] / stride - anchor[1]
face_block[:, 2] = points_3d[:, 2]
def test_signed_lateral_yaw_compare_longitudinal_eligibility_uses_lt_50m():
assert is_signed_lateral_yaw_compare_longitudinal_eligible({"gt_z_m": 49.99}) is True
assert is_signed_lateral_yaw_compare_longitudinal_eligible({"gt_z_m": 50.0}) is False
assert is_signed_lateral_yaw_compare_longitudinal_eligible({"gt_z_m": 65.0}) is False
assert is_signed_lateral_yaw_compare_longitudinal_eligible({"gt_z_m": -1.0}) is False
assert is_signed_lateral_yaw_compare_longitudinal_eligible({"gt_z_m": None}) is False
def test_build_grouped_metric_bucket_interval_rows_splits_face_visibility_buckets():
stores = {bucket_name: defaultdict(make_metric_bucket) for bucket_name in FACE_VISIBILITY_BUCKET_ORDER}
front_bucket = stores["front_rear_only"][-5.0]
front_bucket["matched_3d"] = 4
front_bucket["yaw_compare_eligible_count"] = 4
front_bucket["yaw_compare_pair_count"] = 2
front_bucket["direct_visible_yaw_abs_deg"] = [4.0, 6.0]
front_bucket["edge_visible_yaw_abs_deg"] = [5.0, 7.0]
front_bucket["direct_minus_edge_visible_yaw_abs_deg"] = [-1.0, -1.0]
two_face_bucket = stores["two-face"][0.0]
two_face_bucket["matched_3d"] = 5
two_face_bucket["yaw_compare_eligible_count"] = 5
two_face_bucket["yaw_compare_pair_count"] = 3
two_face_bucket["direct_visible_yaw_abs_deg"] = [2.0, 3.0, 4.0]
two_face_bucket["edge_visible_yaw_abs_deg"] = [1.0, 2.0, 3.0]
two_face_bucket["direct_minus_edge_visible_yaw_abs_deg"] = [1.0, 1.0, 1.0]
rows_by_group = build_grouped_metric_bucket_interval_rows(
stores,
bin_width_m=5.0,
prefix="lateral_bin",
group_order=FACE_VISIBILITY_BUCKET_ORDER,
)
assert list(rows_by_group) == list(FACE_VISIBILITY_BUCKET_ORDER)
assert [row["lateral_bin_label"] for row in rows_by_group["front_rear_only"]] == ["[-5,0)m"]
assert rows_by_group["side only"] == []
assert [row["lateral_bin_label"] for row in rows_by_group["two-face"]] == ["[0,5)m"]
assert np.isclose(rows_by_group["two-face"][0]["direct_regression_yaw_mae_deg"], 3.0)
assert np.isclose(rows_by_group["two-face"][0]["edge_based_yaw_mae_deg"], 2.0)
flat_rows = flatten_grouped_metric_bucket_interval_rows(rows_by_group, group_key="face_visibility_bucket")
assert [row["face_visibility_bucket"] for row in flat_rows] == ["front_rear_only", "two-face"]
assert [row["lateral_bin_label"] for row in flat_rows] == ["[-5,0)m", "[0,5)m"]
def test_restore_depth_scale_scales_edge_depth_channels_too():
preds_3d = np.ones((2, 41), dtype=np.float32)
preds_edge = np.ones((2, 60), dtype=np.float32)
restored_3d, restored_edge = _restore_depth_scale(preds_3d, preds_edge, 2.5)
for channel in (0, 6, 12, 18, 24):
assert np.allclose(restored_3d[:, channel], 2.5)
untouched_3d = np.setdiff1d(np.arange(preds_3d.shape[1]), np.array([0, 6, 12, 18, 24]))
assert np.allclose(restored_3d[:, untouched_3d], 1.0)
assert restored_edge is not None
assert np.allclose(restored_edge[:, 2::3], 2.5)
assert np.allclose(restored_edge[:, 0::3], 1.0)
assert np.allclose(restored_edge[:, 1::3], 1.0)
assert np.allclose(preds_3d, 1.0)
assert np.allclose(preds_edge, 1.0)
def test_build_edge_heading_decoded_anchors_cut_objects_on_primary_face():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-4.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([0.0, 120.0, 80.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0 # cut_in
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_partial_face_bottom_edge(corners, 3, calib, 640, 480, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
_encode_edge_branch(pred_edge, 0, front_points_3d, front_points_2d, anchor, stride)
_encode_edge_branch(pred_edge, 3, side_points_3d, side_points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
assert edge_confident
assert np.isfinite(edge_yaw)
assert heading_decoded is not None
assert heading_decoded["visible_face_type"] == 0
assert heading_decoded["visible_face_types"] == (0, 3)
diff = (edge_yaw - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-3)
assert np.allclose(
face_center_from_corners(heading_decoded["corners_3d"], 0),
face_center_from_corners(decoded["corners_3d"], 0),
atol=1e-4,
)
def test_build_edge_heading_decoded_falls_back_to_single_front_face_when_cut_side_edge_is_short():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-4.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([0.0, 120.0, 80.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0 # cut_in
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, _ = project_partial_face_bottom_edge(corners, 3, calib, 640, 480, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None
short_len = float(dims[0] * 0.4)
side_dir = side_points_3d[-1] - side_points_3d[0]
side_dir = side_dir / np.linalg.norm(side_dir)
short_side_points_3d = side_points_3d[0] + np.linspace(0.0, short_len, 5, dtype=np.float32)[:, None] * side_dir[None, :]
short_side_points_2d = project_3d_to_2d(short_side_points_3d, calib)
assert np.all(np.isfinite(short_side_points_2d))
_encode_edge_branch(pred_edge, 0, front_points_3d, front_points_2d, anchor, stride)
_encode_edge_branch(pred_edge, 3, short_side_points_3d, short_side_points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
img_h=480,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
edge_selection=edge_selection,
)
assert edge_selection["cut_side_visible_length_ratio"] is not None
assert edge_selection["cut_side_visible_length_ratio"] < 0.5
assert edge_selection["cut_side_visible_ratio_ok"] is False
assert edge_selection["is_valid"] is False
assert edge_selection["face_types"] == (0,)
assert heading_decoded is not None
assert edge_confident
assert np.isfinite(edge_yaw)
def test_reconstruct_two_face_box_from_edge_selection_uses_selected_edge_lengths():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
edge_box = reconstruct_two_face_box_from_edge_selection(
{
"yaw": float(rot_y),
"face_types": (0, 3),
"edge_points_3d": np.stack([front_points_3d, side_points_3d], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_height_m=float(dims[1]),
)
assert edge_box is not None
assert np.allclose(edge_box["center"], center, atol=1e-4)
assert np.allclose(edge_box["dims"], dims, atol=1e-4)
diff = (edge_box["yaw"] - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-5)
def test_reconstruct_edge_based_box_from_selection_trusts_longitudinal_label_and_ignores_side_label():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0, 2),
"face_is_partial": (False, False),
"edge_points_3d": np.stack([front_points_3d, side_points_3d], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_center_y_m=float(center[1]),
regressed_dims=dims,
)
assert edge_box is not None
assert edge_box["mode"] == "two-face"
assert np.allclose(edge_box["center"], center, atol=1e-4)
assert np.allclose(edge_box["dims"], dims, atol=1e-4)
assert edge_box["face_types"] == (0, 3)
def test_reconstruct_edge_based_box_from_selection_uses_edge_y_as_vertical_anchor():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.5, 0.3, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
noisy_front = front_points_3d.copy()
noisy_side = side_points_3d.copy()
noisy_front[:, 1] -= 0.9
noisy_side[:, 1] -= 0.7
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0, 3),
"edge_points_3d": np.stack([noisy_front, noisy_side], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_center_y_m=float(center[1]),
regressed_dims=dims,
)
assert edge_box is not None
expected_center_y = float(np.mean(np.concatenate([noisy_front[:, 1], noisy_side[:, 1]], axis=0)) - float(dims[1]) * 0.5)
assert np.isclose(float(edge_box["center"][1]), expected_center_y, atol=1e-5)
assert np.allclose(edge_box["dims"], dims, atol=1e-4)
def test_reconstruct_edge_based_box_from_selection_ignores_vertical_noise_in_edge_length():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
noisy_side = side_points_3d.copy()
noisy_side[:, 1] += np.linspace(0.0, 8.0, noisy_side.shape[0], dtype=np.float32)
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0, 3),
"edge_points_3d": np.stack([front_points_3d, noisy_side], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_center_y_m=float(center[1]),
regressed_dims=dims,
)
assert edge_box is not None
assert np.isclose(float(edge_box["dims"][0]), float(dims[0]), atol=1e-4)
assert np.isclose(float(edge_box["dims"][2]), float(dims[2]), atol=1e-4)
def test_reconstruct_edge_based_box_from_front_face_uses_regressed_length():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.0, 0.1, 20.0], dtype=np.float32)
true_dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
regressed_dims = np.array([5.2, 1.7, 1.8], dtype=np.float32)
rot_y = 0.45
corners = compute_3d_box_corners(center, true_dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0,),
"edge_points_3d": front_points_3d,
"edge_points_2d": front_points_2d,
},
box_center_y_m=float(center[1]),
regressed_dims=regressed_dims,
)
assert edge_box is not None
assert edge_box["mode"] == "front-rear"
assert np.isclose(float(edge_box["dims"][0]), float(regressed_dims[0]), atol=1e-5)
assert np.isclose(float(edge_box["dims"][1]), float(regressed_dims[1]), atol=1e-5)
assert np.isclose(float(edge_box["dims"][2]), float(true_dims[2]), atol=1e-4)
expected_face_center = face_center_from_corners(corners, 0)
assert expected_face_center is not None
assert np.isclose(float(edge_box["center"][0]), float(expected_face_center[0]), atol=1e-4)
assert np.isclose(float(edge_box["center"][2]), float(expected_face_center[2]), atol=1e-4)
rebuilt_face_center = face_center_from_corners(edge_box["corners_3d"], 0)
assert rebuilt_face_center is not None and expected_face_center is not None
assert np.allclose(rebuilt_face_center, expected_face_center, atol=1e-4)
def test_reconstruct_edge_based_box_from_side_face_uses_regressed_width():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-1.2, -0.2, 18.0], dtype=np.float32)
true_dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
regressed_dims = np.array([4.0, 1.6, 2.4], dtype=np.float32)
rot_y = -0.55
corners = compute_3d_box_corners(center, true_dims, rot_y, face_type=-1)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert side_points_3d is not None and side_points_2d is not None
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (3,),
"edge_points_3d": side_points_3d,
"edge_points_2d": side_points_2d,
},
box_center_y_m=float(center[1]),
regressed_dims=regressed_dims,
)
assert edge_box is not None
assert edge_box["mode"] == "side"
assert np.isclose(float(edge_box["dims"][0]), float(true_dims[0]), atol=1e-4)
assert np.isclose(float(edge_box["dims"][1]), float(regressed_dims[1]), atol=1e-5)
assert np.isclose(float(edge_box["dims"][2]), float(regressed_dims[2]), atol=1e-5)
expected_face_center = face_center_from_corners(corners, 3)
assert expected_face_center is not None
assert np.isclose(float(edge_box["center"][0]), float(expected_face_center[0]), atol=1e-4)
assert np.isclose(float(edge_box["center"][2]), float(expected_face_center[2]), atol=1e-4)
rebuilt_face_center = face_center_from_corners(edge_box["corners_3d"], 3)
assert rebuilt_face_center is not None and expected_face_center is not None
assert np.allclose(rebuilt_face_center, expected_face_center, atol=1e-4)
def test_decode_edge_yaw_selection_single_side_face_uses_cut_cls_for_yaw_disambiguation():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-0.5, 0.0, 18.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.55
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([80.0, 120.0, 220.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0 # cut_in
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=3, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert side_points_3d is not None and side_points_2d is not None
_encode_edge_branch(pred_edge, 3, side_points_3d, side_points_2d, anchor, stride)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
max_faces=1,
)
assert edge_selection["face_types"] == (3,)
diff = (float(edge_selection["yaw"]) - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-4)
def test_decode_edge_yaw_selection_single_side_face_left_cut_in_keeps_negative_yaw():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-4.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([0.0, 120.0, 80.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0 # cut_in
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=3, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
side_points_3d, side_points_2d = project_partial_face_bottom_edge(corners, 3, calib, 640, 480, num_samples=5)
assert side_points_3d is not None and side_points_2d is not None
_encode_edge_branch(pred_edge, 3, side_points_3d, side_points_2d, anchor, stride)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
max_faces=1,
)
assert edge_selection["face_types"] == (3,)
diff = (float(edge_selection["yaw"]) - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-4)
def test_decode_edge_yaw_selection_single_rear_face_cut_in_uses_reference_yaw_to_avoid_pi_flip():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-0.5, 0.0, 18.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([0.0, 120.0, 80.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0 # cut_in
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=1, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
rear_points_3d, rear_points_2d = project_face_bottom_edge(corners, 1, calib, num_samples=5)
assert rear_points_3d is not None and rear_points_2d is not None
_encode_edge_branch(pred_edge, 1, rear_points_3d, rear_points_2d, anchor, stride)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
max_faces=1,
)
assert edge_selection["face_types"] == (1,)
diff = (float(edge_selection["yaw"]) - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-4)
def test_reconstruct_edge_based_box_from_selection_falls_back_when_edge_metric_size_is_unstable():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([0.5, 0.0, 30.0], dtype=np.float32)
dims = np.array([4.5, 1.5, 2.0], dtype=np.float32)
rot_y = 0.6
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
inflated_front = front_points_3d.copy()
inflated_side = side_points_3d.copy()
front_mid = np.mean(inflated_front[:, [0, 2]], axis=0, keepdims=True)
side_mid = np.mean(inflated_side[:, [0, 2]], axis=0, keepdims=True)
inflated_front[:, [0, 2]] = front_mid + (inflated_front[:, [0, 2]] - front_mid) * 2.8
inflated_side[:, [0, 2]] = side_mid + (inflated_side[:, [0, 2]] - side_mid) * 2.8
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0, 3),
"face_is_partial": (False, False),
"edge_points_3d": np.stack([inflated_front, inflated_side], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_center_y_m=float(center[1]),
regressed_dims=dims,
face_regressed_dims_by_type={
0: {"height": float(dims[1]), "width": float(dims[2])},
3: {"length": float(dims[0]), "height": float(dims[1])},
},
)
assert edge_box is not None
assert np.allclose(edge_box["dims"], dims, atol=1e-4)
assert edge_box["length_source"] == "regressed"
assert edge_box["width_source"] == "regressed"
def test_reconstruct_edge_based_box_from_selection_does_not_use_partial_edge_length_as_full_box_size():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-4.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
partial_side = side_points_3d.copy()
full_len = np.linalg.norm(partial_side[-1, [0, 2]] - partial_side[0, [0, 2]])
partial_dir = partial_side[-1] - partial_side[0]
partial_dir = partial_dir / np.linalg.norm(partial_dir)
partial_side = partial_side[0] + np.linspace(0.0, float(full_len) * 0.875, 5, dtype=np.float32)[:, None] * partial_dir[None, :]
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0, 3),
"face_is_partial": (False, True),
"edge_points_3d": np.stack([front_points_3d, partial_side], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_center_y_m=float(center[1]),
regressed_dims=dims,
)
assert edge_box is not None
assert np.isclose(float(edge_box["dims"][0]), float(dims[0]), atol=1e-4)
assert np.isclose(float(edge_box["dims"][2]), float(dims[2]), atol=1e-4)
assert edge_box["length_source"] == "regressed"
assert edge_box["width_source"] == "edge"
def test_reconstruct_edge_based_box_from_selection_ignores_face_size_priors_that_disagree_with_whole_box():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([0.5, 0.0, 30.0], dtype=np.float32)
dims = np.array([4.5, 1.5, 2.0], dtype=np.float32)
rot_y = 0.6
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
shrunk_front = front_points_3d.copy()
shrunk_side = side_points_3d.copy()
front_mid = np.mean(shrunk_front[:, [0, 2]], axis=0, keepdims=True)
side_mid = np.mean(shrunk_side[:, [0, 2]], axis=0, keepdims=True)
shrunk_front[:, [0, 2]] = front_mid + (shrunk_front[:, [0, 2]] - front_mid) * 0.7
shrunk_side[:, [0, 2]] = side_mid + (shrunk_side[:, [0, 2]] - side_mid) * 0.7
edge_box = reconstruct_edge_based_box_from_selection(
{
"yaw": float(rot_y),
"face_types": (0, 3),
"face_is_partial": (False, False),
"edge_points_3d": np.stack([shrunk_front, shrunk_side], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
},
box_center_y_m=float(center[1]),
regressed_dims=dims,
face_regressed_dims_by_type={
0: {"height": float(dims[1]), "width": float(dims[2] * 0.7)},
3: {"length": float(dims[0] * 0.7), "height": float(dims[1])},
},
)
assert edge_box is not None
assert np.allclose(edge_box["dims"], dims, atol=1e-4)
assert edge_box["length_source"] == "regressed"
assert edge_box["width_source"] == "regressed"
def test_build_edge_heading_decoded_uses_edge_geometry_for_box_size():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.5, 0.0, 20.0], dtype=np.float32)
true_dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
regressed_dims = np.array([6.5, 1.5, 2.6], dtype=np.float32)
rot_y = 0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([120.0, 120.0, 240.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = regressed_dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
corners = compute_3d_box_corners(center, true_dims, rot_y, face_type=-1)
for face_type, score in ((0, 0.95), (3, 0.9)):
_encode_face_branch(pred, corners, face_type=face_type, score=score, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
for face_type in (0, 3):
points_3d, points_2d = project_face_bottom_edge(corners, face_type, calib, num_samples=5)
assert points_3d is not None and points_2d is not None
_encode_edge_branch(pred_edge, face_type, points_3d, points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
img_h=480,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
edge_selection=edge_selection,
)
assert edge_confident
assert heading_decoded is not None
diff = (edge_yaw - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-3)
front_edge_3d, _ = project_face_bottom_edge(heading_decoded["corners_3d"], 0, calib, num_samples=5)
side_edge_3d, _ = project_face_bottom_edge(heading_decoded["corners_3d"], 3, calib, num_samples=5)
assert front_edge_3d is not None and side_edge_3d is not None
rebuilt_width = float(np.linalg.norm(front_edge_3d[-1] - front_edge_3d[0]))
rebuilt_length = float(np.linalg.norm(side_edge_3d[-1] - side_edge_3d[0]))
assert np.isclose(rebuilt_length, float(true_dims[0]), atol=1e-4)
assert np.isclose(rebuilt_width, float(true_dims[2]), atol=1e-4)
assert not np.isclose(rebuilt_length, float(regressed_dims[0]), atol=1e-3)
assert not np.isclose(rebuilt_width, float(regressed_dims[2]), atol=1e-3)
def test_build_edge_heading_decoded_keeps_same_best_score_face_anchor_as_direct_box():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([0.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([120.0, 120.0, 240.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
for face_type, score in ((0, 0.95), (1, 0.99), (3, 0.80)):
_encode_face_branch(pred, corners, face_type=face_type, score=score, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
for face_type in (0, 1, 3):
points_3d, points_2d = project_face_bottom_edge(corners, face_type, calib, num_samples=5)
assert points_3d is not None and points_2d is not None
_encode_edge_branch(pred_edge, face_type, points_3d, points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
img_h=480,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
edge_selection=edge_selection,
)
assert decoded is not None
assert heading_decoded is not None
assert edge_confident
assert edge_selection["face_types"] == (1, 3)
# Direct-yaw and edge-yaw reconstruction should share the same score-chosen face anchor; only yaw changes.
assert heading_decoded["visible_face_type"] == decoded["visible_face_type"] == 1
diff = (edge_yaw - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-5), edge_yaw
def test_build_edge_heading_decoded_keeps_primary_face_when_edge_points_leak_outside_image():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-12.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.9
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert float(np.min(front_points_2d[:, 0])) < 0.0
_encode_edge_branch(pred_edge, 0, front_points_3d, front_points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=None,
calib=calib,
img_w=640,
img_h=480,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
edge_selection=edge_selection,
)
assert decoded is not None
assert edge_selection["face_types"] == (0,)
assert heading_decoded is not None
assert edge_confident
diff = (edge_yaw - rot_y + np.pi) % (2 * np.pi) - np.pi
assert np.isclose(diff, 0.0, atol=1e-4)
def test_selected_edge_residual_stats_are_zero_for_matching_reprojected_edges():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([0.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
yaw = 0.75
corners = compute_3d_box_corners(center, dims, yaw, face_type=-1)
edge_points_3d, edge_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
assert edge_points_3d is not None and edge_points_2d is not None
projected = _project_selected_face_edges_from_box(
corners,
face_types=(0,),
face_is_partial=(False,),
calib=calib,
img_w=640,
img_h=480,
)
stats = _selected_edge_residual_stats(edge_points_2d, projected)
assert stats["available"] is True
assert np.isclose(stats["mean_px"], 0.0, atol=1e-6)
assert np.isclose(stats["max_px"], 0.0, atol=1e-6)
def test_build_edge_heading_decoded_rejects_far_lateral_edge_yaw():
threshold_m = 5.0
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([6.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([200.0, 120.0, 320.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.9, anchor=anchor, stride=stride, calib=calib)
_encode_face_branch(pred, corners, face_type=3, score=0.85, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
for face_type in (0, 3):
points_3d, points_2d = project_face_bottom_edge(corners, face_type, calib, num_samples=5)
assert points_3d is not None and points_2d is not None
_encode_edge_branch(pred_edge, face_type, points_3d, points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=threshold_m,
)
assert whole_attrs is not None
assert float(abs(whole_attrs["center"][0])) > threshold_m
assert np.isfinite(edge_yaw)
assert heading_decoded is None
assert not edge_confident
def test_build_edge_heading_decoded_reconstructs_single_front_face_from_edge_and_regressed_length():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-2.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.35
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([160.0, 120.0, 260.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.9, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
points_3d, points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
assert points_3d is not None and points_2d is not None
_encode_edge_branch(pred_edge, 0, points_3d, points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
assert decoded is not None
assert decoded["visible_face_types"] == (0,)
assert heading_decoded is not None
assert np.isfinite(edge_yaw)
assert edge_confident
rebuilt_front_edge_3d, _ = project_face_bottom_edge(heading_decoded["corners_3d"], 0, calib, num_samples=5)
assert rebuilt_front_edge_3d is not None
rebuilt_width = float(np.linalg.norm(rebuilt_front_edge_3d[-1] - rebuilt_front_edge_3d[0]))
assert np.isclose(rebuilt_width, float(dims[2]), atol=1e-4)
def test_build_edge_heading_decoded_reconstructs_cut_object_from_single_front_face_when_side_edge_invalid():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-4.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([0.0, 120.0, 80.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.95, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, _ = project_partial_face_bottom_edge(corners, 3, calib, 640, 480, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None
short_len = float(dims[0] * 0.4)
side_dir = side_points_3d[-1] - side_points_3d[0]
side_dir = side_dir / np.linalg.norm(side_dir)
short_side_points_3d = side_points_3d[0] + np.linspace(0.0, short_len, 5, dtype=np.float32)[:, None] * side_dir[None, :]
short_side_points_2d = project_3d_to_2d(short_side_points_3d, calib)
_encode_edge_branch(pred_edge, 0, front_points_3d, front_points_2d, anchor, stride)
_encode_edge_branch(pred_edge, 3, short_side_points_3d, short_side_points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
edge_selection = decode_edge_yaw_selection_from_prediction(
pred,
pred_edge,
anchor,
stride,
calib,
bbox_xyxy=bbox_xyxy,
img_w=640,
img_h=480,
max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
img_h=480,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
edge_selection=edge_selection,
)
assert edge_selection["is_valid"] is False
assert edge_selection["face_types"] == (0,)
assert heading_decoded is not None
assert np.isfinite(edge_yaw)
assert edge_confident
def test_build_edge_heading_decoded_limits_near_cut_boxes_to_two_edge_yaw_faces():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([-4.0, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = -0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([140.0, 120.0, 260.0, 240.0], dtype=np.float32)
pred = np.zeros(41, dtype=np.float32)
pred[24] = center[2]
pred[25:27] = [center[0] / center[2] * calib["fx"] / stride + (calib["cx"] / stride - anchor[0]), 0.0]
pred[27:30] = dims
pred[30] = 10.0
pred[34] = np.sin(rot_y)
pred[39] = 10.0 # cut_in-like, but not actually clipped at the border
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
_encode_face_branch(pred, corners, face_type=0, score=0.82, anchor=anchor, stride=stride, calib=calib)
_encode_face_branch(pred, corners, face_type=1, score=0.95, anchor=anchor, stride=stride, calib=calib)
_encode_face_branch(pred, corners, face_type=3, score=0.9, anchor=anchor, stride=stride, calib=calib)
pred_edge = np.zeros(60, dtype=np.float32)
for face_type in (0, 1, 3):
points_3d, points_2d = project_face_bottom_edge(corners, face_type, calib, num_samples=5)
assert points_3d is not None and points_2d is not None
_encode_edge_branch(pred_edge, face_type, points_3d, points_2d, anchor, stride)
decoded = decode_3d_prediction(
pred,
anchor,
stride,
calib,
640,
480,
{0},
set(),
0,
pred_edge_60=pred_edge,
bbox_xyxy=bbox_xyxy,
)
whole_attrs = extract_3d_attrs_from_prediction(pred, anchor, stride, calib, pred_edge_60=pred_edge)
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded=decoded,
pred_41=pred,
pred_edge_60=pred_edge,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
whole_attrs=whole_attrs,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
)
assert decoded is not None
assert decoded["visible_face_types"] == (0, 1, 3)
assert edge_confident
assert np.isfinite(edge_yaw)
assert heading_decoded is not None
assert heading_decoded["visible_face_types"] == (0, 3)
assert np.asarray(heading_decoded["edge_points_2d"], dtype=np.float32).shape == (2, 5, 2)
def test_build_edge_heading_decoded_uses_resolved_side_face_type_for_edge_fit():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([1.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
rot_y = 0.75
anchor = np.array([10.0, 20.0], dtype=np.float32)
stride = 8.0
bbox_xyxy = np.array([120.0, 120.0, 240.0, 240.0], dtype=np.float32)
corners = compute_3d_box_corners(center, dims, rot_y, face_type=-1)
front_points_3d, front_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
side_points_3d, side_points_2d = project_face_bottom_edge(corners, 3, calib, num_samples=5)
assert front_points_3d is not None and front_points_2d is not None
assert side_points_3d is not None and side_points_2d is not None
pred = np.zeros(41, dtype=np.float32)
pred[27:30] = dims
edge_selection = {
"yaw": float(rot_y),
"face_types": (0, 2),
"face_is_partial": (False, False),
"edge_points_3d": np.stack([front_points_3d, side_points_3d], axis=0),
"edge_points_2d": np.stack([front_points_2d, side_points_2d], axis=0),
"lateral_ok": True,
}
heading_decoded, edge_yaw, edge_confident = _build_edge_heading_decoded(
base_decoded={},
pred_41=pred,
pred_edge_60=None,
anchor_xy=anchor,
stride=stride,
bbox_xyxy=bbox_xyxy,
calib=calib,
img_w=640,
img_h=480,
whole_attrs=None,
edge_yaw_max_lateral_dist_m=DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M,
edge_selection=edge_selection,
)
assert edge_confident
assert np.isclose(edge_yaw, rot_y, atol=1e-5)
assert heading_decoded is not None
assert heading_decoded["visible_face_types"] == (0, 3)
edge_box_selected_edges_2d = _project_selected_face_edges_from_box(
heading_decoded["corners_3d"],
heading_decoded["visible_face_types"],
edge_selection["face_is_partial"],
calib,
640,
480,
)
edge_fit = _selected_edge_residual_stats(edge_selection["edge_points_2d"], edge_box_selected_edges_2d)
assert edge_fit["available"] is True
assert edge_fit["max_px"] is not None and edge_fit["max_px"] < 1e-3
def test_add_two_roi_inference_args_includes_edge_yaw_lateral_threshold():
parser = argparse.ArgumentParser()
add_two_roi_inference_args(parser, include_output_dir=False)
args = parser.parse_args([])
assert args.edge_yaw_max_lateral_dist == DEFAULT_EDGE_YAW_MAX_LATERAL_DIST_M
def test_make_record_preserves_badcase_geometry_shapes_for_visualization():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([0.5, 0.0, 20.0], dtype=np.float32)
pred_center = center + np.array([0.3, 0.1, 0.2], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
yaw = 0.2
pred_yaw = 0.35
corners = compute_3d_box_corners(center, dims, yaw, face_type=-1).astype(np.float32)
pred_corners = compute_3d_box_corners(pred_center, dims, pred_yaw, face_type=-1).astype(np.float32)
face_center = face_center_from_corners(corners, 0).astype(np.float32)
pred_face_center = face_center_from_corners(pred_corners, 0).astype(np.float32)
edge_points_3d, edge_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
assert edge_points_3d is not None
assert edge_points_2d is not None
gt_attrs = {
"center": center,
"depth": float(center[2]),
"dims": dims,
"yaw": yaw,
"visible_face_type": 0,
"face_center": face_center,
"corners_3d": corners,
}
pred_attrs = {
"center": pred_center,
"depth": float(pred_center[2]),
"dims": dims,
"yaw": pred_yaw,
"visible_face_type": 0,
"face_center": pred_face_center,
"corners_3d": pred_corners,
}
gt_decoded = {
"face_center_2d": project_3d_to_2d(face_center[None, :], calib)[0],
"face_color": (0, 0, 255),
"visible_face_types": (0,),
"edge_points_2d": edge_points_2d,
}
pred_decoded = {
"face_center_2d": project_3d_to_2d(pred_face_center[None, :], calib)[0],
"face_color": (0, 255, 0),
"visible_face_types": (0,),
"edge_points_2d": edge_points_2d,
}
record = make_record(
sample_index=0,
roi_name="roi0",
image_path=Path("frame.png"),
label_path=Path("frame.txt"),
cls_name="car",
gt_index=0,
pred_index=0,
gt_box=np.array([100.0, 120.0, 220.0, 260.0], dtype=np.float32),
pred_box=np.array([102.0, 121.0, 222.0, 261.0], dtype=np.float32),
match_iou=0.9,
prediction={"cls_id": 0, "confidence": 0.95},
gt_attrs=gt_attrs,
pred_attrs=pred_attrs,
gt_decoded=gt_decoded,
pred_decoded=pred_decoded,
gt_visible_faces=[(0, np.zeros(8, dtype=np.float32))],
gt_visible_yaw=yaw,
pred_edge_visible_yaw=pred_yaw,
is_cut_object_flag=False,
position_eligible=True,
yaw_compare_max_lateral_dist_m=5.0,
)
assert np.asarray(record["gt_corners"], dtype=np.float32).shape == (8, 3)
assert np.asarray(record["pred_corners"], dtype=np.float32).shape == (8, 3)
assert np.asarray(record["gt_edge_points_2d"], dtype=np.float32).shape == (5, 2)
assert np.asarray(record["pred_edge_points_2d"], dtype=np.float32).shape == (5, 2)
draw_kwargs = build_badcase_draw_kwargs(record["gt_corners"], calib, visible_face_type=0, visible_face_types=[0])
assert "edge_points_2d" in draw_kwargs
def test_make_record_invalidates_side_edge_length_for_front_rear_only_edge_box():
center = np.array([0.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
attrs = {
"center": center,
"depth": float(center[2]),
"dims": dims,
"yaw": 0.0,
"visible_face_type": 0,
"face_center": center,
"corners_3d": np.zeros((8, 3), dtype=np.float32),
}
decoded = {"visible_face_types": (0,), "face_center_2d": None, "face_color": None, "edge_points_2d": None}
record = make_record(
sample_index=0,
roi_name="roi1",
image_path=Path("frame.png"),
label_path=Path("frame.txt"),
cls_name="car",
gt_index=0,
pred_index=0,
gt_box=np.array([0.0, 0.0, 10.0, 10.0], dtype=np.float32),
pred_box=np.array([0.0, 0.0, 10.0, 10.0], dtype=np.float32),
match_iou=1.0,
prediction={"cls_id": 0, "confidence": 0.95},
gt_attrs=attrs,
pred_attrs=attrs,
gt_decoded=decoded,
pred_decoded=decoded,
gt_visible_faces=[(0, np.zeros(8, dtype=np.float32))],
gt_visible_yaw=0.0,
pred_edge_visible_yaw=0.0,
pred_edge_bucket_visible_yaw=0.0,
pred_edge_box={"mode": "front-rear", "dims": np.array([9.9, 1.5, 1.8], dtype=np.float32)},
is_cut_object_flag=False,
position_eligible=True,
yaw_compare_max_lateral_dist_m=5.0,
pred_yaw_compare_face_types=(0,),
pred_yaw_compare_valid=False,
)
assert record["pred_edge_box_mode"] == "front-rear"
assert record["pred_edge_length_m"] is None
assert record["edge_length_abs_err_m"] is None
def test_make_record_tracks_strict_yaw_compare_face_bucket_metadata_separately():
calib = {"fx": 500.0, "fy": 500.0, "cx": 320.0, "cy": 240.0}
center = np.array([0.5, 0.0, 20.0], dtype=np.float32)
dims = np.array([4.0, 1.5, 1.8], dtype=np.float32)
yaw = 0.2
corners = compute_3d_box_corners(center, dims, yaw, face_type=-1).astype(np.float32)
face_center = face_center_from_corners(corners, 0).astype(np.float32)
side_center = face_center_from_corners(corners, 3).astype(np.float32)
edge_points_3d, edge_points_2d = project_face_bottom_edge(corners, 0, calib, num_samples=5)
assert edge_points_3d is not None and edge_points_2d is not None
attrs = {
"center": center,
"depth": float(center[2]),
"dims": dims,
"yaw": yaw,
"visible_face_type": 0,
"face_center": face_center,
"corners_3d": corners,
}
decoded = {
"face_center_2d": project_3d_to_2d(face_center[None, :], calib)[0],
"face_color": (0, 0, 255),
"visible_face_types": (0, 3),
"edge_points_2d": edge_points_2d,
}
record = make_record(
sample_index=0,
roi_name="roi0",
image_path=Path("frame.png"),
label_path=Path("frame.txt"),
cls_name="car",
gt_index=0,
pred_index=0,
gt_box=np.array([100.0, 120.0, 220.0, 260.0], dtype=np.float32),
pred_box=np.array([100.0, 120.0, 220.0, 260.0], dtype=np.float32),
match_iou=1.0,
prediction={"cls_id": 0, "confidence": 0.95},
gt_attrs=attrs,
pred_attrs=attrs,
gt_decoded=decoded,
pred_decoded=decoded,
gt_visible_faces=[(0, np.zeros(8, dtype=np.float32)), (3, np.zeros(8, dtype=np.float32))],
gt_visible_yaw=yaw,
pred_edge_visible_yaw=yaw,
is_cut_object_flag=False,
position_eligible=True,
yaw_compare_max_lateral_dist_m=5.0,
pred_yaw_compare_face_types=(0,),
pred_yaw_compare_valid=False,
)
assert record["visible_face_count"] == 2
assert record["has_side_face_visible"] is True
assert record["yaw_compare_visible_face_count"] == 1
assert record["yaw_compare_has_side_face_visible"] is False
assert record["yaw_compare_visible_faces"] == "front"
assert record["yaw_compare_face_bucket"] == "front_rear_only"
assert record["yaw_compare_pred_valid"] is False
def test_interval_label_supports_metric_units():
assert interval_label(5.0, 1.0, unit="deg") == "[5,6)deg"
assert interval_label(0.5, 0.5, unit="m") == "[0.5,1.0)m"
def test_select_interval_visual_records_round_robins_high_error_bins_first():
stores = make_interval_visual_reservoirs()
add_rng = random.Random(0)
for value in (5.2, 5.4, 6.2, 6.4, 7.2, 7.4):
add_interval_visual_record(
stores,
value=value,
record={"value": value},
bin_width=1.0,
per_bin_capacity=10,
rng=add_rng,
)
selected = select_interval_visual_records(stores, max_total=5, rng=random.Random(1), reverse=True)
assert list(sorted(selected.keys(), reverse=True)) == [7.0, 6.0, 5.0]
assert sum(len(records) for records in selected.values()) == 5
assert len(selected[7.0]) == 2
assert len(selected[6.0]) == 2
assert len(selected[5.0]) == 1