109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import sys
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
from ..configs import resolve_sensor_asset_dir
|
|
from ..unroll import (
|
|
compute_uv_distortion_metrics,
|
|
load_exported_data,
|
|
recommend_edge_expand,
|
|
scaled_map_cache_path,
|
|
)
|
|
|
|
DEFAULT_FINGER_EXPORT_DIR = "sensors/finger/assets/30fps-0703"
|
|
DEFAULT_FINGER_MAP_CACHE = f"{DEFAULT_FINGER_EXPORT_DIR}/unroll_map_uv.npz"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FingerExportPaths:
|
|
"""Resolved Blender export bundle paths for finger UV pipelines."""
|
|
|
|
export_dir: Path
|
|
mesh: Path
|
|
intrinsics: Path
|
|
pose: Path
|
|
|
|
def as_tuple(self) -> tuple[str, str, str]:
|
|
return (str(self.mesh), str(self.intrinsics), str(self.pose))
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FingerExportBundle:
|
|
"""Loaded Blender export data and UV map-cache settings."""
|
|
|
|
export_paths: FingerExportPaths
|
|
data: dict
|
|
map_cache: str
|
|
edge_expand: float
|
|
|
|
|
|
def _candidate_export_dirs(export_dir) -> list[Path]:
|
|
raw = Path(export_dir).expanduser()
|
|
if raw.is_absolute():
|
|
return [raw]
|
|
|
|
here = Path(__file__).resolve()
|
|
candidates = []
|
|
bundled_assets = resolve_sensor_asset_dir("finger")
|
|
if bundled_assets is not None and raw.as_posix() == DEFAULT_FINGER_EXPORT_DIR:
|
|
candidates.append(bundled_assets)
|
|
candidates.extend([
|
|
Path.cwd() / raw,
|
|
here.parents[1] / raw, # packaged wheel filesystem layout
|
|
here.parents[3] / raw, # editable install: repo/assets/...
|
|
])
|
|
if getattr(sys, "frozen", False):
|
|
candidates.insert(0, Path(sys.executable).resolve().parent / raw)
|
|
unique: list[Path] = []
|
|
for candidate in candidates:
|
|
resolved = candidate.resolve()
|
|
if resolved not in unique:
|
|
unique.append(resolved)
|
|
return unique
|
|
|
|
|
|
def resolve_export_paths(export_dir) -> FingerExportPaths:
|
|
"""Return Blender export paths expected inside one folder."""
|
|
checked: list[str] = []
|
|
for root in _candidate_export_dirs(export_dir):
|
|
paths = FingerExportPaths(
|
|
export_dir=root,
|
|
mesh=root / "mesh.npz",
|
|
intrinsics=root / "camera_intrinsics.npz",
|
|
pose=root / "world_to_cam.npy",
|
|
)
|
|
missing = [name for name, path in vars(paths).items() if name != "export_dir" and not path.is_file()]
|
|
if not missing:
|
|
return paths
|
|
checked.append(f"{root} (missing: {', '.join(missing)})")
|
|
|
|
raise FileNotFoundError(
|
|
f"Missing export file(s) for finger bundle '{export_dir}'. "
|
|
f"Checked: {'; '.join(checked)}"
|
|
)
|
|
|
|
|
|
def load_finger_export_bundle(
|
|
export_dir,
|
|
*,
|
|
map_cache=DEFAULT_FINGER_MAP_CACHE,
|
|
source_scale=1.0,
|
|
) -> FingerExportBundle:
|
|
"""Resolve export paths, load bundle data, and pick the UV map cache path."""
|
|
export_paths = resolve_export_paths(export_dir)
|
|
if Path(map_cache) == Path(DEFAULT_FINGER_MAP_CACHE):
|
|
map_cache = str(export_paths.export_dir / "unroll_map_uv.npz")
|
|
map_cache = scaled_map_cache_path(map_cache, source_scale)
|
|
mesh_path, intrinsics_path, pose_path = export_paths.as_tuple()
|
|
data = load_exported_data(mesh_path, intrinsics_path, pose_path)
|
|
distortion = compute_uv_distortion_metrics(data)
|
|
edge_expand = recommend_edge_expand(distortion)
|
|
return FingerExportBundle(
|
|
export_paths=export_paths,
|
|
data=data,
|
|
map_cache=map_cache,
|
|
edge_expand=float(edge_expand),
|
|
)
|