Files
Aislo/resources/tester/test_762_1_refine.py
T
eomsangdonandClaude Opus 5.5 579e337892 feat(766-1): 다듬기 흐름을 기본으로 — 스위치 SURFACE_REFINE_FLOW 걷음 · 자동 전처리 = 업체 분류 + 다듬기(분류 있음) · 점진 TIN + 다듬기(늘) · 확정 기본 ptd_refine
- resolve_auto_source_filters 가 늘 `_refine` 판을 돌려줌 — 바탕 마스크(업체 분류 · 점진 TIN)는 다듬기 입력으로 그대로 저장(엔진 길 그대로)
- SURFACE_CONFIRM_DEFAULT_FILTER 기본 = ptd_refine · WF1 자동 확정은 엔진이 고른 첫 필터 그대로
- B04 드롭다운 갈래 넷 그대로(관리자 비교용)
- 지킴 시험 고침: test_640_lidar_default · test_b04_ground_filters 의 자동 목록 기대값을 다듬기 판으로 · test_762_1_refine 의 스위치 시험은 스위치가 없어져 「기본 흐름」 시험으로 바꿈

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:20:19 +09:00

77 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""762-1 지면점 다듬기(SMRF 꼴) · 766-1 다듬기 기본 흐름 · 762-2 품질 지표 지킴 시험.
합성 비탈(17°) + 능선(높이 6 m · 폭 약 16 m) + 덤불 혹(3 × 3 m · 1.5 m 뜸) — 필터가
덤불까지 지면으로 고른 판 → 다듬기가 혹은 지우고 능선 · 비탈은 남김.
"""
import numpy as np
import B04_PreProcess.B04_PreProcess_Engine_Ground as ground
from B04_PreProcess.B04_PreProcess_Engine_Filter_Refine import refine_ground_mask
from B04_PreProcess.B04_PreProcess_Engine_Quality import spike_stats
RNG = np.random.default_rng(7621)
def _terrain(x: np.ndarray, y: np.ndarray) -> np.ndarray:
return 0.3 * x + 6.0 * np.exp(-(((x - 60.0) / 8.0) ** 2)) + 0.02 * y
def _scene() -> tuple[np.ndarray, np.ndarray, np.ndarray]:
count = 120 * 120 * 4
x = RNG.uniform(0, 120, count)
y = RNG.uniform(0, 120, count)
z = _terrain(x, y) + RNG.normal(0, 0.03, count)
bush = np.zeros(count, dtype=bool)
for cx, cy in RNG.uniform(10, 110, (15, 2)):
if abs(cx - 60) < 12: # 능선 위 덤불은 따로 가름 — 여기선 비탈 · 골 덤불
cx += 25
inside = (np.abs(x - cx) < 1.5) & (np.abs(y - cy) < 1.5)
z[inside] += 1.5
bush |= inside
ridge = (np.abs(x - 60) < 4) & ~bush
return np.column_stack((x, y, z)), bush, ridge
def test_refine_drops_bushes_keeps_ridge() -> None:
xyz, bush, ridge = _scene()
kept = refine_ground_mask(xyz, np.ones(len(xyz), dtype=bool))
print(kept[bush].mean(), kept[ridge].mean(), kept[~bush].mean())
assert kept[bush].mean() < 0.1 # 덤불 혹 90 % 넘게 지움
assert kept[ridge].mean() > 0.95 # 능선 안 깎음
assert kept[~bush].mean() > 0.97 # 땅 점 거의 그대로
def test_refine_never_adds_points() -> None:
xyz, _, _ = _scene()
mask = RNG.random(len(xyz)) < 0.5
kept = refine_ground_mask(xyz, mask)
assert not (kept & ~mask).any()
assert refine_ground_mask(xyz, np.zeros(len(xyz), dtype=bool)).sum() == 0
def test_refine_flow_is_default() -> None:
xyz, _, _ = _scene()
bounds = np.array([[0, 120], [0, 120], [0, 50]], dtype=np.float64)
data = {"xyz": xyz, "bounds": bounds, "classification": np.full(len(xyz), 2, np.uint8)}
assert ground.resolve_auto_source_filters(data) == ["classification_refine", "ptd_refine"]
data["classification"][:] = 0
assert ground.resolve_auto_source_filters(data) == ["ptd_refine"]
assert ground.base_filter("ptd_refine") == "ptd"
assert ground.base_filter("ptd") is None
assert {"classification_refine", "ptd_refine"} <= set(ground.available_filters())
# 바탕 마스크를 넘기면 바탕 필터를 다시 돌리지 않고 다듬기만
base = np.ones(len(xyz), dtype=bool)
refined = ground.run_ground_filter("ptd_refine", data, base)
assert refined.sum() < base.sum()
def test_spike_stat_sees_bumps() -> None:
yy, xx = np.mgrid[0:100, 0:100].astype(float)
z = 0.3 * xx
valid = np.ones(z.shape, dtype=bool)
assert spike_stats(z, valid)["spike_pct"] == 0.0
z[40:42, 40:42] += 1.5
stats = spike_stats(z, valid)
assert stats["spike_count"] == 1 and stats["spike_pct"] > 0