feat(762-1): 지면점 다듬기(SMRF 꼴) · 고를 갈래 업체 분류 + 다듬기 · 점진 TIN + 다듬기 · 제안 흐름 스위치(기본 끔)
- 새 Engine_Filter_Refine — 1 m 칸 최저 · 낮은 잡점(뒤집어 열림) · 창 키움 열림(최대 18 m · 기울기 0.3) · 걸린 칸 빼고 임시 지면 다시 그림 · 점마다 0.5 m + 1.25 × 기울기 - 필터 이름 classification_refine · ptd_refine — Ground _FILTERS · Schema · sampler SurfaceFilter 에 더함 · 엔진은 바탕 마스크 저장본을 다듬음(점진 TIN 두 번 안 돌림) - 스위치 SURFACE_REFINE_FLOW(프로세스 환경 변수 · 기본 끔) — 켜면 자동 전처리 · 확정 기본 = 다듬기 판 · 끄면 지금 길 그대로 - 기울기 0.3 = 용화 훑기에서 고름(PDAL 0.15 는 급경사 능선을 더 깎음 · 0.45 · 0.6 은 덤불 혹 더 남김) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""B04 지면 필터 오케스트레이션.
|
||||
|
||||
구조화된 포인트클라우드(structured.npz)에 대해 grid_min_z/csf/pmf/ransac
|
||||
구조화된 포인트클라우드(structured.npz)에 대해 grid_min_z/csf/pmf/ransac/ptd/classification
|
||||
(+ `_refine` 다듬기 판)
|
||||
필터를 실행하여 지면 마스크 딕셔너리를 만든다. 필터 선택은 config의
|
||||
source_filters를 따른다.
|
||||
"""
|
||||
@@ -20,7 +21,12 @@ from B04_PreProcess.B04_PreProcess_Engine_Filter_Grid import filter_grid_min_z
|
||||
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 config.config_system import SURFACE_AUTO_FALLBACK_FILTER
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Filter_Refine import refine_ground_mask
|
||||
from config.config_system import SURFACE_AUTO_FALLBACK_FILTER, SURFACE_REFINE_FLOW
|
||||
|
||||
# 다듬기 판(762) — `{기본 필터}_refine` = 기본 필터 지면점을 SMRF 꼴로 한 번 더 거름
|
||||
REFINE_SUFFIX = "_refine"
|
||||
_REFINABLE = ("classification", "ptd")
|
||||
|
||||
# 필터 키 → 함수 매핑
|
||||
_FILTERS = {
|
||||
@@ -30,10 +36,17 @@ _FILTERS = {
|
||||
"ransac": filter_ransac,
|
||||
"ptd": filter_ptd,
|
||||
"classification": filter_classification,
|
||||
"classification_refine": lambda data: refine_ground_mask(
|
||||
data["xyz"], filter_classification(data)
|
||||
),
|
||||
"ptd_refine": lambda data: refine_ground_mask(data["xyz"], filter_ptd(data)),
|
||||
}
|
||||
|
||||
# LAS에 지면분류가 실제로 들어 있을 때만 쓰는 필터 (미분류 LAS에서는 마스크가 빈다)
|
||||
_CONDITIONAL_FILTERS = {"classification": has_classified_ground}
|
||||
_CONDITIONAL_FILTERS = {
|
||||
"classification": has_classified_ground,
|
||||
"classification_refine": has_classified_ground,
|
||||
}
|
||||
|
||||
|
||||
def available_filters() -> tuple[str, ...]:
|
||||
@@ -61,15 +74,29 @@ def resolve_auto_source_filters(
|
||||
usable = usable_conditional_filters(structured_data)
|
||||
if usable and not has_classified_coverage(structured_data, processed_dir):
|
||||
usable = []
|
||||
return [*usable[:1], SURFACE_AUTO_FALLBACK_FILTER]
|
||||
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
|
||||
|
||||
|
||||
def base_filter(filter_key: str) -> str | None:
|
||||
"""다듬기 판이면 바탕 필터 이름 — 엔진이 바탕 마스크 저장본을 다시 쓰게."""
|
||||
base = filter_key.removesuffix(REFINE_SUFFIX)
|
||||
return base if base != filter_key and base in _REFINABLE else None
|
||||
|
||||
|
||||
def run_ground_filter(
|
||||
filter_key: str, structured_data: dict[str, Any] | np.lib.npyio.NpzFile
|
||||
filter_key: str,
|
||||
structured_data: dict[str, Any] | np.lib.npyio.NpzFile,
|
||||
base_mask: np.ndarray | None = None,
|
||||
) -> np.ndarray:
|
||||
"""단일 지면 필터를 실행해 불리언 마스크를 반환한다."""
|
||||
"""단일 지면 필터를 실행해 불리언 마스크를 반환한다 — 다듬기 판은 `base_mask` 를 다듬음."""
|
||||
if filter_key not in _FILTERS:
|
||||
raise ValueError(f"알 수 없는 지면 필터입니다: {filter_key}")
|
||||
if base_mask is not None and base_filter(filter_key):
|
||||
return refine_ground_mask(structured_data["xyz"], base_mask)
|
||||
return np.asarray(_FILTERS[filter_key](structured_data), dtype=bool)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user