105 lines
2.7 KiB
Python
105 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from copy import deepcopy
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
|
|
DEFAULT_CONFIG: dict[str, Any] = {
|
|
"paths": {
|
|
"cad": "CAD/11.step",
|
|
"mesh": "Mesh/11.stl",
|
|
"pointcloud": "PointCloud/11.ply",
|
|
"noisy_pointcloud": "Noise_PointCloud/11_noisy.ply",
|
|
"output_mesh": "Mesh/11.stl",
|
|
"curvature_output": None,
|
|
},
|
|
"cad": {
|
|
"precision": 1.0,
|
|
"enabled": False,
|
|
},
|
|
"noise": {
|
|
"enabled": True,
|
|
"std": 4.0,
|
|
"seed": None,
|
|
},
|
|
"filters": {
|
|
"enabled": True,
|
|
"methods": ["SOR"],
|
|
"sor_neighbors": 20,
|
|
"sor_std_ratio": 2.0,
|
|
"ror_points": 5,
|
|
"ror_radius": 20.0,
|
|
"voxel_size": 0.05,
|
|
"pass_axis": "z",
|
|
"pass_min": None,
|
|
"pass_max": None,
|
|
"bf_sigma_s": 0.01,
|
|
"bf_sigma_r": 0.1,
|
|
},
|
|
"registration": {
|
|
"voxel_size": 10.0,
|
|
"roll": 10.0,
|
|
"pitch": 10.0,
|
|
"yaw": 10.0,
|
|
"trans_range": 0.0,
|
|
"random_seed": None,
|
|
},
|
|
"evaluation": {
|
|
"chunk_size": 2048,
|
|
"triangle_chunk_size": 4096,
|
|
"cad_mesh_deflection": 0.1,
|
|
},
|
|
"curvature": {
|
|
"radius": 2.0,
|
|
"max_nn": 30,
|
|
"color_scale": 3.0,
|
|
},
|
|
"plane": {
|
|
"size_mm": 1000.0,
|
|
"noise_std_mm": 0.5,
|
|
"num_points": 10000,
|
|
"threshold_mm": 0.5,
|
|
"max_iterations": 2000,
|
|
},
|
|
"visualization": {
|
|
"enabled": True,
|
|
},
|
|
}
|
|
|
|
|
|
def deep_update(base: dict[str, Any], updates: dict[str, Any]) -> dict[str, Any]:
|
|
result = deepcopy(base)
|
|
for key, value in updates.items():
|
|
if isinstance(value, dict) and isinstance(result.get(key), dict):
|
|
result[key] = deep_update(result[key], value)
|
|
else:
|
|
result[key] = value
|
|
return result
|
|
|
|
|
|
class ConfigLoader:
|
|
"""Load YAML config and merge it with project defaults."""
|
|
|
|
def __init__(self, default_config: dict[str, Any] | None = None) -> None:
|
|
self.default_config = default_config or DEFAULT_CONFIG
|
|
|
|
def load(self, config_path: str | None = None) -> dict[str, Any]:
|
|
config = deepcopy(self.default_config)
|
|
if not config_path:
|
|
return config
|
|
|
|
path = Path(config_path)
|
|
if not path.exists():
|
|
raise FileNotFoundError(f"Config file not found: {path}")
|
|
|
|
with path.open("r", encoding="utf-8") as f:
|
|
user_config = yaml.safe_load(f) or {}
|
|
|
|
if not isinstance(user_config, dict):
|
|
raise ValueError("Config file must contain a YAML mapping at the top level.")
|
|
|
|
return deep_update(config, user_config)
|