gengxin
This commit is contained in:
1 parent
60d973fd25
commit
33d87ac9d9
1 file changed
+42
-16
+42
-16
@@ -63,7 +63,10 @@ def camera_ptz() -> dict[str, int]:
|
|||||||
|
|
||||||
def remove_session_snapshots(paths: set[str]) -> None:
|
def remove_session_snapshots(paths: set[str]) -> None:
|
||||||
for path in sorted(paths):
|
for path in sorted(paths):
|
||||||
if not path.startswith("/smart-cam/data/scan/calibration/"):
|
if not path.startswith((
|
||||||
|
"/smart-cam/data/scan/manual/",
|
||||||
|
"/smart-cam/data/scan/calibration/",
|
||||||
|
)):
|
||||||
continue
|
continue
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
["docker", "exec", "smart-cam", "rm", "-f", "--", path],
|
["docker", "exec", "smart-cam", "rm", "-f", "--", path],
|
||||||
@@ -109,20 +112,31 @@ def choose_direction(axis: str, origin: dict[str, int], points: dict) -> int:
|
|||||||
return 1 if high - origin["tilt"] >= origin["tilt"] - low else -1
|
return 1 if high - origin["tilt"] >= origin["tilt"] - low else -1
|
||||||
|
|
||||||
|
|
||||||
def consistent_samples(reliable: list[tuple[int, float, float, dict]]) -> list:
|
def consistent_samples(reliable: list[tuple[int, float, float, dict]],
|
||||||
"""Return the largest group that estimates one mutually consistent FOV."""
|
maximum_spread: float = 0.15) -> list:
|
||||||
|
"""Return the largest group with a bounded end-to-end FOV spread.
|
||||||
|
|
||||||
|
The previous seed-radius clustering allowed one sample at -12% and
|
||||||
|
another at +12% into the same cluster. That cluster could therefore have
|
||||||
|
nearly 24% end-to-end spread and was immediately rejected by the later
|
||||||
|
15% check. Use sorted contiguous windows so membership and validation
|
||||||
|
apply the same definition of consistency.
|
||||||
|
"""
|
||||||
if not reliable:
|
if not reliable:
|
||||||
return []
|
return []
|
||||||
clusters = [
|
ordered = sorted(reliable, key=lambda sample: sample[2])
|
||||||
[
|
candidates = []
|
||||||
sample for sample in reliable
|
for begin in range(len(ordered)):
|
||||||
if abs(sample[2] - seed[2]) / max(seed[2], 1.0) <= 0.12
|
for end in range(begin, len(ordered)):
|
||||||
]
|
window = ordered[begin:end + 1]
|
||||||
for seed in reliable
|
_, spread = fov_statistics(window)
|
||||||
]
|
if spread <= maximum_spread:
|
||||||
return max(
|
candidates.append(window)
|
||||||
clusters, key=lambda cluster: (len(cluster), -max(x[2] for x in cluster))
|
return max(candidates, key=lambda group: (
|
||||||
)
|
len(group),
|
||||||
|
-fov_statistics(group)[1],
|
||||||
|
sum(int(sample[3].get("inliers") or 0) for sample in group),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
def fov_statistics(samples: list[tuple[int, float, float, dict]]) -> tuple[float, float]:
|
def fov_statistics(samples: list[tuple[int, float, float, dict]]) -> tuple[float, float]:
|
||||||
@@ -321,8 +335,17 @@ def estimate_axis(axis: str, origin: dict[str, int], points: dict,
|
|||||||
result.get("match_method") == "phase_correlation"
|
result.get("match_method") == "phase_correlation"
|
||||||
and float(result.get("phase_response") or 0) >= core.PHASE_MIN_RESPONSE
|
and float(result.get("phase_response") or 0) >= core.PHASE_MIN_RESPONSE
|
||||||
)
|
)
|
||||||
if (feature_reliable or phase_reliable) and 0.08 <= overlap <= 0.80:
|
if (feature_reliable or phase_reliable) and 0.08 <= overlap <= 0.90:
|
||||||
reliable.append((delta, overlap, delta / (1.0 - overlap), result))
|
# Hikvision absolute PTZ movement is quantized and can also have
|
||||||
|
# backlash. At high optical zoom, using the requested delta
|
||||||
|
# instead of the SDK-reported actual movement creates a large,
|
||||||
|
# systematic FOV error. Shortfall samples have already been
|
||||||
|
# rejected above, so the actual movement is authoritative here.
|
||||||
|
actual_delta = int(result.get("actual_axis_motion") or delta)
|
||||||
|
reliable.append((
|
||||||
|
actual_delta, overlap,
|
||||||
|
actual_delta / (1.0 - overlap), result,
|
||||||
|
))
|
||||||
if len(reliable) >= 4:
|
if len(reliable) >= 4:
|
||||||
provisional = consistent_samples(reliable)
|
provisional = consistent_samples(reliable)
|
||||||
if len(provisional) >= 4:
|
if len(provisional) >= 4:
|
||||||
@@ -372,7 +395,10 @@ def estimate_axis(axis: str, origin: dict[str, int], points: dict,
|
|||||||
for sample in consistent
|
for sample in consistent
|
||||||
)
|
)
|
||||||
allowed_spread = 0.08
|
allowed_spread = 0.08
|
||||||
if not strong_pair:
|
# Two independently measured FOVs that agree within 5% are already a
|
||||||
|
# strong geometric cross-check. Do not reject such a pair merely
|
||||||
|
# because a natural scene supplied 74 rather than 100 ORB inliers.
|
||||||
|
if not strong_pair and spread > 0.05:
|
||||||
core.move(origin)
|
core.move(origin)
|
||||||
core.fail(
|
core.fail(
|
||||||
f"{axis}方向目前只有2个一致样本,但特征质量不足以使用双样本规则;"
|
f"{axis}方向目前只有2个一致样本,但特征质量不足以使用双样本规则;"
|
||||||
|
|||||||
Reference in new issue
Block a user