wild_test

This commit is contained in:
Ankle committed 2026-08-04 17:46:08 +08:00
1 parent 136ba0ce89
commit 60d973fd25
4 files changed
+93 -27

No files matched your search

+76 -12
View File
@@ -72,9 +72,32 @@ def remove_session_snapshots(paths: set[str]) -> None:
def candidate_deltas(axis: str) -> list[int]:
"""Coarse probes from narrow to wide FOV.
Once any probe provides a usable FOV estimate, estimate_axis replaces the
wide tail with scene/camera-specific deltas. Dense small values are
important at high optical zoom, where a jump from 150 to 250 PTZ units can
go directly from useful overlap to no common image.
"""
if axis == "pan":
return [150, 250, 350, 450, 600, 800, 1100, 1500, 2100]
return [80, 120, 170, 220, 280, 350, 450, 600, 850, 1150]
return [50, 75, 110, 150, 220, 320, 470, 700, 1050, 1500, 2100]
return [25, 40, 60, 90, 130, 190, 280, 420, 620, 850, 1150]
def fov_guided_deltas(delta: int, overlap: float, axis: str) -> list[int]:
"""Choose probes that retain enough shared image for robust matching."""
shift = 1.0 - overlap
if not 0.04 <= shift <= 0.92:
return []
estimated_fov = delta / shift
minimum = 20 if axis == "pan" else 12
# Fractions are image displacement/FOV, hence target overlaps are roughly
# 82%, 72%, 60%, 48% and 35%. The middle probes provide the strongest
# cross-check, while the last one confirms the wider displacement model.
return sorted({
max(minimum, round(estimated_fov * fraction))
for fraction in (0.18, 0.28, 0.40, 0.52, 0.65)
})
def choose_direction(axis: str, origin: dict[str, int], points: dict) -> int:
@@ -239,7 +262,14 @@ def estimate_axis(axis: str, origin: dict[str, int], points: dict,
failure_reasons: dict[str, int] = {}
say(f"\n正在标定 {axis}_step,摄像头会自动移动并回到起点……")
for delta in candidate_deltas(axis):
pending = candidate_deltas(axis)
tested: set[int] = set()
adaptive_seeded = False
while pending:
delta = pending.pop(0)
if delta in tested:
continue
tested.add(delta)
result = core.measure_delta(
axis, delta, direction, 1.0, baseline=baseline, origin=origin
)
@@ -270,6 +300,22 @@ def estimate_axis(axis: str, origin: dict[str, int], points: dict,
else:
quality = f"有效特征={inliers},内点率={ratio*100:.1f}%"
say(f" step={delta}: 重叠约 {overlap*100:.1f}%,{quality}")
if not adaptive_seeded:
guided = fov_guided_deltas(delta, overlap, axis)
if guided:
# Keep only a small safety tail beyond the guided range. Very
# large fixed probes cannot match once the current optical FOV
# has been measured and only waste time.
cap = round(max(guided) * 1.15)
pending = sorted({
value for value in guided + pending
if value not in tested and value <= cap
})
adaptive_seeded = True
say(
f" 已根据首个有效样本自适应生成后续step:"
f"{', '.join(str(value) for value in pending)}"
)
feature_reliable = inliers >= 50 and ratio >= 0.45
phase_reliable = (
result.get("match_method") == "phase_correlation"
@@ -423,8 +469,19 @@ def estimate_axis(axis: str, origin: dict[str, int], points: dict,
def calculate_steps(points: dict, snapshots: set[str], mount: str) -> None:
origin = camera_ptz()
zooms = [int(points[name]["zoom"]) for name in ("top-left", "bottom-right")]
if max(zooms + [origin["zoom"]]) - min(zooms + [origin["zoom"]]) > 10:
core.fail("三个位置的zoom不一致;请固定焦段后重新开始")
target_zoom = round(sum(zooms) / len(zooms))
if origin["zoom"] != target_zoom:
say(
f"两个角点zoom的平均值为{target_zoom}({target_zoom/100:.2f}×);"
"正在自动切换到该zoom后再测量视场……"
)
origin = core.move({**origin, "zoom": target_zoom})
time.sleep(1.0)
if abs(int(origin["zoom"]) - target_zoom) > 10:
core.fail(
f"摄像头未能到达平均zoom:目标={target_zoom},实际={origin['zoom']}"
)
core.print_ptz(origin, "平均zoom到位")
top_left = points["top-left"]
bottom_right = points["bottom-right"]
# This check is only advisory and runs before signed pan direction has
@@ -624,7 +681,7 @@ def main() -> None:
mount = "hanging" if stage == "1" else "desktop"
confirm_position(
"② 请在Web界面中固定本次扫描使用的zoom,"
"② 请在Web界面中设置左上角需要的zoom,"
"再手动把摄像头移到扫描区域的【左上角】。"
)
top_left = camera_ptz()
@@ -633,11 +690,16 @@ def main() -> None:
core.save_state(state)
confirm_position(
"③ 请保持zoom不变,手动把摄像头移到扫描区域的【右下角】。"
"③ 请手动把摄像头移到扫描区域的【右下角】。\n"
"右下角可以使用不同zoom,程序会取两个角点zoom的平均值作为最终扫描zoom。"
)
bottom_right = camera_ptz()
if abs(top_left["zoom"] - bottom_right["zoom"]) > 10:
core.fail("左右角的zoom不一致,请固定焦段后重新运行")
average_zoom = round((top_left["zoom"] + bottom_right["zoom"]) / 2)
say(
f"两个角点zoom:左上={top_left['zoom']/100:.2f}×,"
f"右下={bottom_right['zoom']/100:.2f}×;"
f"最终取平均={average_zoom/100:.2f}×。"
)
state = core.load_state()
state["points"]["bottom-right"] = bottom_right
core.save_state(state)
@@ -646,10 +708,12 @@ def main() -> None:
while True:
confirm_position(
"④ 这一步用于测量当前zoom下的水平/垂直视场。\n"
"请保持zoom不变,优先把摄像头移到扫描区域内部、"
"请优先把摄像头移到扫描区域内部、"
"距离较远且纹理分布在整幅画面的位置。\n"
"请避开纯天空、纯墙面、水面、玻璃、近距离物体和重复图案;"
"回复1后不要再操作Web云台,程序会自动移动、拍照并返回起点。"
"请避开纯天空、纯墙面、纯水面、玻璃、近距离物体和重复图案;"
"水面场景需要让岸线、浮标或其他固定物体横向分布在画面中;"
"回复1后不要再操作Web云台,程序会先切换到两个角点的平均zoom,"
"再自动移动、拍照并返回起点。"
)
say("\n正在计算,请不要操作摄像头或Web控制页面……")
try: