| | |
| | | return dict(value) if value else None |
| | | |
| | | |
| | | def validate_pointcloud_model_input(path: Path) -> None: |
| | | """Reject obviously incomplete uploads before consuming a CPU inference job.""" |
| | | size = path.stat().st_size |
| | | if size < 1: |
| | | raise ApiError("点云文件为空。请选择包含 RGB 点位的完整点云导出文件。") |
| | | if path.suffix.lower() not in {".las", ".laz"}: |
| | | return |
| | | if size < 227: |
| | | raise ApiError("LAS/LAZ 文件不完整(小于有效 LAS 文件头)。请选择完整点云文件,不要上传空白或未完成下载的分块。") |
| | | with path.open("rb") as stream: |
| | | header = stream.read(375) |
| | | if len(header) < 227 or header[:4] != b"LASF": |
| | | raise ApiError("LAS/LAZ 文件头无效。请选择完整的 LAS/LAZ 点云导出文件。") |
| | | header_size = int.from_bytes(header[94:96], "little") |
| | | if header_size < 227 or header_size > size: |
| | | raise ApiError("LAS/LAZ 文件头不完整。请选择完整的 LAS/LAZ 点云导出文件。") |
| | | version_minor = header[25] |
| | | point_count_offset, point_count_size = (247, 8) if version_minor >= 4 else (107, 4) |
| | | if len(header) < point_count_offset + point_count_size: |
| | | raise ApiError("LAS/LAZ 文件头不完整。请选择完整的 LAS/LAZ 点云导出文件。") |
| | | point_count = int.from_bytes(header[point_count_offset:point_count_offset + point_count_size], "little") |
| | | if point_count < 1: |
| | | raise ApiError("LAS/LAZ 文件没有点记录。请选择包含实际 RGB 点位的完整点云文件,而不是空分块。") |
| | | |
| | | |
| | | def execute_pointcloud_inference_job(root: Path, job_id: str, model: Path, source: Path, output: Path) -> None: |
| | | with POINTCLOUD_INFERENCE_JOBS_LOCK: |
| | | POINTCLOUD_INFERENCE_JOBS[job_id].update({"status": "running", "stage": "inference", "startedAt": datetime.now(UTC).isoformat()}) |
| | |
| | | suffixes = {".ply", ".pcd", ".xyz", ".xyzn", ".xyzrgb", ".las", ".laz"} |
| | | if Path(name).suffix.lower() not in suffixes: |
| | | raise ApiError("Model inference requires a PLY, PCD, XYZ, LAS, or LAZ point cloud.") |
| | | validate_pointcloud_model_input(staged_path) |
| | | python = self.root / ".venvs" / "05-3d-pointcloud" / "Scripts" / "python.exe" |
| | | if not python.is_file(): |
| | | raise ApiError("3D point-cloud virtual environment is unavailable. Run the capability setup first.") |