算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3 适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新 后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动 前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复 文档: 新增5份算法性能分析报告
70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
import open3d as o3d
|
|
import trimesh
|
|
|
|
|
|
class IOHandler:
|
|
"""Centralized input validation and file loading."""
|
|
|
|
def resolve_path(self, path: str | Path) -> Path:
|
|
return Path(path).expanduser()
|
|
|
|
def require_file(self, path: str | Path, label: str = "file") -> Path:
|
|
resolved = self.resolve_path(path)
|
|
if not resolved.exists():
|
|
raise FileNotFoundError(f"{label} not found: {resolved}")
|
|
if not resolved.is_file():
|
|
raise ValueError(f"{label} is not a file: {resolved}")
|
|
return resolved
|
|
|
|
def ensure_parent_dir(self, path: str | Path) -> Path:
|
|
resolved = self.resolve_path(path)
|
|
resolved.parent.mkdir(parents=True, exist_ok=True)
|
|
return resolved
|
|
|
|
def load_point_cloud(self, pointcloud_path: str | Path) -> o3d.geometry.PointCloud:
|
|
path = self.require_file(pointcloud_path, "Point cloud")
|
|
pcd = o3d.io.read_point_cloud(str(path))
|
|
if len(pcd.points) == 0:
|
|
raise ValueError(f"Point cloud is empty or unreadable: {path}")
|
|
return pcd
|
|
|
|
def load_mesh(self, mesh_path: str | Path) -> trimesh.Trimesh:
|
|
path = self.require_file(mesh_path, "Mesh")
|
|
mesh = trimesh.load(str(path), force="mesh")
|
|
if mesh.is_empty:
|
|
raise ValueError(f"Mesh is empty or unreadable: {path}")
|
|
return mesh
|
|
|
|
def require_cad(self, cad_path: str | Path) -> Path:
|
|
return self.require_file(cad_path, "CAD")
|
|
|
|
def save_point_cloud(self, pointcloud: o3d.geometry.PointCloud, output_path: str | Path) -> Path:
|
|
path = self.ensure_parent_dir(output_path)
|
|
ok = o3d.io.write_point_cloud(str(path), pointcloud)
|
|
if not ok:
|
|
raise IOError(f"Failed to save point cloud: {path}")
|
|
return path
|
|
|
|
def point_cloud_to_numpy(self, pointcloud: o3d.geometry.PointCloud) -> np.ndarray:
|
|
points = np.asarray(pointcloud.points, dtype=np.float64)
|
|
if points.size == 0:
|
|
raise ValueError("Point cloud has no points.")
|
|
return points
|
|
|
|
def mesh_triangles_to_numpy(self, mesh: trimesh.Trimesh) -> tuple[np.ndarray, np.ndarray]:
|
|
vertices = np.asarray(mesh.vertices, dtype=np.float64)
|
|
faces = np.asarray(mesh.faces, dtype=np.int64)
|
|
if len(vertices) == 0 or len(faces) == 0:
|
|
raise ValueError("Mesh must contain vertices and faces.")
|
|
return vertices, faces
|
|
|
|
def print_mapping(self, data: dict[str, Any]) -> None:
|
|
for key, value in data.items():
|
|
print(f"{key}: {value}")
|