From 579e337892421dfa24e5c052ee97e4ff4530bb88 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 3 Nov 2026 15:20:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(766-1):=20=EB=8B=A4=EB=93=AC=EA=B8=B0=20?= =?UTF-8?q?=ED=9D=90=EB=A6=84=EC=9D=84=20=EA=B8=B0=EB=B3=B8=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=E2=80=94=20=EC=8A=A4=EC=9C=84=EC=B9=98=20SURFACE?= =?UTF-8?q?=5FREFINE=5FFLOW=20=EA=B1=B7=EC=9D=8C=20=C2=B7=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=A0=84=EC=B2=98=EB=A6=AC=20=3D=20=EC=97=85?= =?UTF-8?q?=EC=B2=B4=20=EB=B6=84=EB=A5=98=20+=20=EB=8B=A4=EB=93=AC?= =?UTF-8?q?=EA=B8=B0(=EB=B6=84=EB=A5=98=20=EC=9E=88=EC=9D=8C)=20=C2=B7=20?= =?UTF-8?q?=EC=A0=90=EC=A7=84=20TIN=20+=20=EB=8B=A4=EB=93=AC=EA=B8=B0(?= =?UTF-8?q?=EB=8A=98)=20=C2=B7=20=ED=99=95=EC=A0=95=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20ptd=5Frefine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- B04_PreProcess/B04_PreProcess_Engine_Ground.py | 9 ++++----- config/config_system_terrain.py | 7 ++----- resources/tester/test_640_lidar_default.py | 7 +++++-- resources/tester/test_762_1_refine.py | 8 ++------ resources/tester/test_b04_ground_filters.py | 10 +++++----- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/B04_PreProcess/B04_PreProcess_Engine_Ground.py b/B04_PreProcess/B04_PreProcess_Engine_Ground.py index 76393f353..fe966069a 100644 --- a/B04_PreProcess/B04_PreProcess_Engine_Ground.py +++ b/B04_PreProcess/B04_PreProcess_Engine_Ground.py @@ -22,7 +22,7 @@ from B04_PreProcess.B04_PreProcess_Engine_Filter_PMF import filter_pmf from B04_PreProcess.B04_PreProcess_Engine_Filter_PTD import filter_ptd from B04_PreProcess.B04_PreProcess_Engine_Filter_RANSAC import filter_ransac from B04_PreProcess.B04_PreProcess_Engine_Filter_Refine import refine_ground_mask -from config.config_system import SURFACE_AUTO_FALLBACK_FILTER, SURFACE_REFINE_FLOW +from config.config_system import SURFACE_AUTO_FALLBACK_FILTER # 다듬기 판(762) — `{기본 필터}_refine` = 기본 필터 지면점을 SMRF 꼴로 한 번 더 거름 REFINE_SUFFIX = "_refine" @@ -69,16 +69,15 @@ def resolve_auto_source_filters( 업체가 이미 지면을 분류해 놨으면 그것을 기본으로 쓴다 — 계산이 없고 품질도 가장 안정적이다. **ptd(점진 TIN)는 분류가 있어도 함께 만든다** — 내장 필터 결과를 눈으로 검증할 수 있어야 하기 때문이다 (2026-09-01 사용자 확정). 분류가 없으면 ptd 하나만 남는다. + 둘 다 다듬기 판(`_refine`)으로 지음 — 업체 분류 + 다듬기 · 점진 TIN + 다듬기(요구 607). 「분류 있음」 = 비율 문턱 + 덮임(일부만 분류된 LAS 거름) — 자동 고름에만(요구 558). """ usable = usable_conditional_filters(structured_data) if usable and not has_classified_coverage(structured_data, processed_dir): usable = [] filters = [*usable[:1], SURFACE_AUTO_FALLBACK_FILTER] - # 762 제안 흐름(스위치 켬) — 같은 기본 필터에 다듬기를 붙인 판을 지음 - if SURFACE_REFINE_FLOW: - filters = [f"{key}{REFINE_SUFFIX}" if key in _REFINABLE else key for key in filters] - return filters + # 다듬기 기본(요구 607) — 같은 기본 필터에 다듬기를 붙인 판을 지음(바탕 마스크도 저장) + return [f"{key}{REFINE_SUFFIX}" if key in _REFINABLE else key for key in filters] def base_filter(filter_key: str) -> str | None: diff --git a/config/config_system_terrain.py b/config/config_system_terrain.py index 8e0b260ee..2d91375ec 100644 --- a/config/config_system_terrain.py +++ b/config/config_system_terrain.py @@ -94,8 +94,7 @@ SURFACE_MODEL_SOURCE_FILTERS = tuple( SURFACE_AUTO_METHODS = tuple(os.getenv("SURFACE_AUTO_METHODS", "dtm").split(",")) # 기본 필터는 입력 LAS를 보고 정한다 — 지면분류(class 2)가 있으면 그것을, 없으면 아래 값. SURFACE_AUTO_FALLBACK_FILTER = os.getenv("SURFACE_AUTO_FALLBACK_FILTER", "ptd") -# 762 제안 흐름 스위치 — 켜면 자동 전처리 · 확정 기본 = 기본 필터 + 다듬기(`_refine`) · 기본 끔. -SURFACE_REFINE_FLOW = os.getenv("SURFACE_REFINE_FLOW", "False").lower() == "true" +# 자동 전처리 · 확정 기본은 이 필터의 다듬기 판(`_refine`) — 요구 607. # 다듬기(SMRF 꼴) — 칸 · 물체 기울기(rise/run · PDAL 0.15 는 급경사 능선을 깎아 0.3) · 최대 창 · # 판정 문턱 · 기울기 배수 · 낮은 잡점 기울기 — 나머지는 PDAL 기본. SURFACE_REFINE_CELL_M = float(os.getenv("SURFACE_REFINE_CELL_M", "1.0")) @@ -187,9 +186,7 @@ SHEET_SURFACE_AUTO_METHODS = [ ] # 일반 사용자 WF1 자동 확정 기본값 -SURFACE_CONFIRM_DEFAULT_FILTER = os.getenv( - "SURFACE_CONFIRM_DEFAULT_FILTER", "ptd_refine" if SURFACE_REFINE_FLOW else "ptd" -) +SURFACE_CONFIRM_DEFAULT_FILTER = os.getenv("SURFACE_CONFIRM_DEFAULT_FILTER", "ptd_refine") # 확정 표현 = implicit · 평활 끔 — WF1 자동 짓기도 이 표현을 기본 필터에 지음(요구 558). SURFACE_CONFIRM_DEFAULT_METHOD = os.getenv("SURFACE_CONFIRM_DEFAULT_METHOD", "implicit") SURFACE_CONFIRM_DEFAULT_SMOOTH = ( diff --git a/resources/tester/test_640_lidar_default.py b/resources/tester/test_640_lidar_default.py index fce1413c6..276ca90fb 100644 --- a/resources/tester/test_640_lidar_default.py +++ b/resources/tester/test_640_lidar_default.py @@ -43,12 +43,15 @@ def test_일부만_분류된_LAS_는_자동_기본에서_ptd() -> None: partial = np.ones(len(points), dtype=np.uint8) partial[(points[:, 0] < 30) & (np.arange(len(points)) % 4 == 0)] = 2 # 비율 7 % · 덮임 30 % assert classified_coverage(_structured(points, partial)) < 0.5 - assert resolve_auto_source_filters(_structured(points, partial)) == ["ptd"] + assert resolve_auto_source_filters(_structured(points, partial)) == ["ptd_refine"] spread = np.ones(len(points), dtype=np.uint8) spread[::37] = 2 # 비율 2.7 % · 덮임 100 % assert classified_coverage(_structured(points, spread)) == 1.0 - assert resolve_auto_source_filters(_structured(points, spread)) == ["classification", "ptd"] + assert resolve_auto_source_filters(_structured(points, spread)) == [ + "classification_refine", + "ptd_refine", + ] def test_덮임은_저장본을_다시_씀(tmp_path: Path) -> None: diff --git a/resources/tester/test_762_1_refine.py b/resources/tester/test_762_1_refine.py index 1ddbf5d95..c9a952642 100644 --- a/resources/tester/test_762_1_refine.py +++ b/resources/tester/test_762_1_refine.py @@ -1,11 +1,10 @@ -"""762-1 지면점 다듬기(SMRF 꼴) · 제안 흐름 스위치 · 762-2 품질 지표 지킴 시험. +"""762-1 지면점 다듬기(SMRF 꼴) · 766-1 다듬기 기본 흐름 · 762-2 품질 지표 지킴 시험. 합성 비탈(17°) + 능선(높이 6 m · 폭 약 16 m) + 덤불 혹(3 × 3 m · 1.5 m 뜸) — 필터가 덤불까지 지면으로 고른 판 → 다듬기가 혹은 지우고 능선 · 비탈은 남김. """ import numpy as np -import pytest import B04_PreProcess.B04_PreProcess_Engine_Ground as ground from B04_PreProcess.B04_PreProcess_Engine_Filter_Refine import refine_ground_mask @@ -51,13 +50,10 @@ def test_refine_never_adds_points() -> None: assert refine_ground_mask(xyz, np.zeros(len(xyz), dtype=bool)).sum() == 0 -def test_refine_flow_switch(monkeypatch: pytest.MonkeyPatch) -> None: +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", "ptd"] - monkeypatch.setattr(ground, "SURFACE_REFINE_FLOW", True) assert ground.resolve_auto_source_filters(data) == ["classification_refine", "ptd_refine"] data["classification"][:] = 0 assert ground.resolve_auto_source_filters(data) == ["ptd_refine"] diff --git a/resources/tester/test_b04_ground_filters.py b/resources/tester/test_b04_ground_filters.py index 9de36928d..041f9ddb7 100644 --- a/resources/tester/test_b04_ground_filters.py +++ b/resources/tester/test_b04_ground_filters.py @@ -109,13 +109,13 @@ def test_auto_filters_prefer_classification_but_always_build_ptd() -> None: classified = np.ones(len(points), dtype=np.uint8) classified[::4] = 2 - # 분류가 있으면 classification 이 기본이고, ptd 는 검증용으로 같이 만든다. + # 분류가 있으면 classification 이 기본이고, ptd 는 검증용으로 같이 만든다(둘 다 다듬기 판). assert resolve_auto_source_filters(_structured(points, classified)) == [ - "classification", - "ptd", + "classification_refine", + "ptd_refine", ] # 분류가 없으면 ptd 하나뿐 — 빈 마스크로 모델을 만들지 않는다. - assert resolve_auto_source_filters(_structured(points, unclassified)) == ["ptd"] + assert resolve_auto_source_filters(_structured(points, unclassified)) == ["ptd_refine"] assert not has_classified_ground(_structured(points, unclassified)) # classification 이 없는 구조화 데이터(옛 캐시)에서도 죽지 않는다. - assert resolve_auto_source_filters(_structured(points)) == ["ptd"] + assert resolve_auto_source_filters(_structured(points)) == ["ptd_refine"]