Download targets/QCS8550/FP16/code/python/utils.py from APLUX-Agora/BEVFormer-tiny-R50: direct link, hf CLI and curl.
- Browser
- Download file 11.1 kB
-
https://huggingface.co/APLUX-Agora/BEVFormer-tiny-R50/resolve/main/targets/QCS8550/FP16/code/python/utils.py
- Command line
-
hf download hf://APLUX-Agora/BEVFormer-tiny-R50/targets/QCS8550/FP16/code/python/utils.py
-
curl -L -o utils.py https://huggingface.co/APLUX-Agora/BEVFormer-tiny-R50/resolve/main/targets/QCS8550/FP16/code/python/utils.py
11.1 kB
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| from concurrent.futures import ThreadPoolExecutor | |
| import math | |
| import time | |
| from pathlib import Path | |
| from typing import Any, Iterable | |
| import numpy as np | |
| CAMERA_NAMES = ( | |
| "CAM_FRONT", | |
| "CAM_FRONT_RIGHT", | |
| "CAM_FRONT_LEFT", | |
| "CAM_BACK", | |
| "CAM_BACK_LEFT", | |
| "CAM_BACK_RIGHT", | |
| ) | |
| IMAGE_MEAN = np.asarray([123.675, 116.28, 103.53], dtype=np.float32) | |
| IMAGE_STD = np.asarray([58.395, 57.12, 57.375], dtype=np.float32) | |
| SHAPES = { | |
| "images": (6, 3, 480, 800), | |
| "images_unpadded": (6, 3, 450, 800), | |
| "img_feat": (6, 256, 15, 25), | |
| "encoder_img_feat": (1, 6, 256, 15, 25), | |
| "can_bus": (1, 18), | |
| "shift": (1, 2), | |
| "lidar2img": (1, 6, 4, 4), | |
| "bev": (1, 2500, 256), | |
| "decoder": (1, 900, 10), | |
| } | |
| EXPECTED_TENSORS = { | |
| "backbone": { | |
| "inputs": {"images": 6912000}, | |
| "outputs": {"img_feat": 576000}, | |
| }, | |
| "encoder_temporal": { | |
| "inputs": { | |
| "can_bus": 18, | |
| "img_feat": 576000, | |
| "lidar2img": 96, | |
| "shift": 2, | |
| "prev_bev": 640000, | |
| }, | |
| "outputs": {"bev_embed": 640000}, | |
| }, | |
| "encoder_scene_start": { | |
| "inputs": { | |
| "can_bus": 18, | |
| "img_feat": 576000, | |
| "lidar2img": 96, | |
| }, | |
| "outputs": {"bev_embed": 640000}, | |
| }, | |
| "decoder": { | |
| "inputs": {"bev_embed": 640000}, | |
| "outputs": {"cls_scores": 9000, "bbox_preds": 9000}, | |
| }, | |
| } | |
| ROTATE_CENTER = (100.0, 100.0) | |
| def sha256_file(path: str | Path) -> str: | |
| digest = hashlib.sha256() | |
| with open(path, "rb") as handle: | |
| for block in iter(lambda: handle.read(1024 * 1024), b""): | |
| digest.update(block) | |
| return digest.hexdigest() | |
| def load_json(path: str | Path) -> dict[str, Any]: | |
| return json.loads(Path(path).read_text(encoding="utf-8")) | |
| def normalize_rc(value: Any) -> int: | |
| return 0 if value is None else int(value) | |
| def resolve_path(path: str | Path, repo_root: str | Path) -> Path: | |
| value = Path(path) | |
| if value.is_absolute(): | |
| return value | |
| return Path(repo_root) / value | |
| def load_record(record: dict[str, Any], repo_root: str | Path) -> np.ndarray: | |
| path = resolve_path(record["path"], repo_root) | |
| if not path.is_file(): | |
| raise FileNotFoundError(path) | |
| actual_sha = sha256_file(path) | |
| expected_sha = record.get("sha256") | |
| if expected_sha and actual_sha != expected_sha: | |
| raise RuntimeError(f"SHA mismatch for {path}: expected={expected_sha} actual={actual_sha}") | |
| dtype = "<f2" if "float16" in str(record["dtype"]).lower() else "<f4" | |
| shape = tuple(int(value) for value in record["shape"]) | |
| array = np.fromfile(path, dtype=dtype) | |
| if array.size != math.prod(shape): | |
| raise RuntimeError(f"Element mismatch for {path}: expected={math.prod(shape)} actual={array.size}") | |
| return np.ascontiguousarray(array.reshape(shape)) | |
| def _load_camera_image(path: Path) -> np.ndarray: | |
| try: | |
| import cv2 | |
| except ModuleNotFoundError as exc: | |
| raise RuntimeError("OpenCV is required for board-side camera image preprocessing.") from exc | |
| image = cv2.imread(str(path), cv2.IMREAD_UNCHANGED) | |
| if image is None: | |
| raise FileNotFoundError(path) | |
| if image.ndim != 3 or image.shape[2] != 3: | |
| raise ValueError(f"Expected 3-channel camera image: {path} shape={image.shape}") | |
| image = image.astype(np.float32) | |
| image = image[..., ::-1] | |
| image = (image - IMAGE_MEAN) / IMAGE_STD | |
| image = cv2.resize(image, (800, 450), interpolation=cv2.INTER_LINEAR) | |
| return image.transpose(2, 0, 1) | |
| def _preprocess_one_camera( | |
| index: int, | |
| camera_name: str, | |
| item: dict[str, Any], | |
| repo_root: str | Path, | |
| check_sha: bool, | |
| ) -> tuple[int, np.ndarray, dict[str, Any]]: | |
| path = resolve_path(item["path"], repo_root) | |
| if not path.is_file(): | |
| raise FileNotFoundError(path) | |
| actual_sha = None | |
| expected_sha = item.get("sha256") | |
| if check_sha: | |
| actual_sha = sha256_file(path) | |
| if expected_sha and actual_sha != expected_sha: | |
| raise RuntimeError(f"SHA mismatch for {path}: expected={expected_sha} actual={actual_sha}") | |
| tensor = _load_camera_image(path) | |
| if tensor.shape != SHAPES["images_unpadded"][1:]: | |
| raise ValueError(f"Unexpected preprocessed camera tensor shape: {tensor.shape}") | |
| return index, tensor, { | |
| "name": camera_name, | |
| "path": str(path), | |
| "sha_checked": bool(check_sha), | |
| "sha256": actual_sha or expected_sha, | |
| } | |
| def preprocess_camera_images( | |
| record: dict[str, Any], | |
| repo_root: str | Path, | |
| check_sha: bool = False, | |
| num_workers: int = 6, | |
| ) -> tuple[np.ndarray, dict[str, Any]]: | |
| images_by_name = {item["name"]: item for item in record["images"]} | |
| camera_order = tuple(record.get("order", CAMERA_NAMES)) | |
| if camera_order != CAMERA_NAMES: | |
| raise RuntimeError(f"Unexpected camera order: {camera_order}") | |
| tasks = [(index, name, images_by_name[name], repo_root, check_sha) for index, name in enumerate(CAMERA_NAMES)] | |
| if int(num_workers) > 1: | |
| with ThreadPoolExecutor(max_workers=int(num_workers)) as executor: | |
| results = list(executor.map(lambda args: _preprocess_one_camera(*args), tasks)) | |
| else: | |
| results = [_preprocess_one_camera(*args) for args in tasks] | |
| padded = np.zeros(SHAPES["images"], dtype=np.float32) | |
| checked_images = [None] * len(CAMERA_NAMES) | |
| for index, tensor, image_record in results: | |
| padded[index, :, :450, :] = tensor | |
| checked_images[index] = image_record | |
| return np.ascontiguousarray(padded, dtype=np.float32), { | |
| "source": "camera_images", | |
| "pipeline": record.get( | |
| "pipeline", | |
| "BGR -> RGB -> normalize(mean/std) -> resize(800,450) -> CHW -> pad to 480 rows", | |
| ), | |
| "camera_order": list(CAMERA_NAMES), | |
| "sha_checked": bool(check_sha), | |
| "num_workers": int(num_workers), | |
| "unpadded_shape": list(SHAPES["images_unpadded"]), | |
| "padded_shape": list(padded.shape), | |
| "reference_preprocessed_sha256": record.get("reference_preprocessed_sha256"), | |
| "images": checked_images, | |
| } | |
| def load_backbone_images( | |
| assets: dict[str, Any], | |
| repo_root: str | Path, | |
| check_image_sha: bool = False, | |
| preprocess_workers: int = 6, | |
| ) -> tuple[np.ndarray, dict[str, Any]]: | |
| if "camera_images" not in assets: | |
| raise KeyError("camera_images is required; this demo starts from raw camera JPG inputs.") | |
| return preprocess_camera_images( | |
| assets["camera_images"], | |
| repo_root, | |
| check_sha=check_image_sha, | |
| num_workers=preprocess_workers, | |
| ) | |
| def as_encoder_img_feat(img_feat: np.ndarray) -> np.ndarray: | |
| native = np.ascontiguousarray(img_feat, dtype="<f2") | |
| return np.ascontiguousarray(native.astype(np.float32).reshape(SHAPES["encoder_img_feat"])) | |
| def elapsed_ms(start_ns: int) -> float: | |
| return (time.perf_counter_ns() - start_ns) / 1.0e6 | |
| def stats(values: Iterable[float]) -> dict[str, float]: | |
| items = [float(value) for value in values] | |
| if not items: | |
| return {"count": 0} | |
| mean = float(sum(items) / len(items)) | |
| variance = float(sum((value - mean) ** 2 for value in items) / len(items)) | |
| return { | |
| "count": len(items), | |
| "mean": mean, | |
| "min": float(min(items)), | |
| "max": float(max(items)), | |
| "sum": float(sum(items)), | |
| "var": variance, | |
| } | |
| def _torchvision_inverse_affine_matrix(center: tuple[float, float], angle: float) -> list[float]: | |
| rot = math.radians(angle) | |
| a = math.cos(rot) | |
| b = -math.sin(rot) | |
| c = math.sin(rot) | |
| d = math.cos(rot) | |
| matrix = [d, -b, 0.0, -c, a, 0.0] | |
| cx, cy = center | |
| matrix[2] += matrix[0] * (-cx) + matrix[1] * (-cy) | |
| matrix[5] += matrix[3] * (-cx) + matrix[4] * (-cy) | |
| matrix[2] += cx | |
| matrix[5] += cy | |
| return matrix | |
| def rotate_prev_bev_like_torchvision( | |
| previous_bev: np.ndarray, | |
| angle: float, | |
| rotate_center: tuple[float, float] = ROTATE_CENTER, | |
| ) -> np.ndarray: | |
| bev = np.ascontiguousarray(previous_bev, dtype=np.float32).reshape(SHAPES["bev"]) | |
| grid = bev[0].reshape(50, 50, 256) | |
| height, width, channels = grid.shape | |
| center_f = ( | |
| float(rotate_center[0]) - width * 0.5, | |
| float(rotate_center[1]) - height * 0.5, | |
| ) | |
| theta = np.asarray( | |
| _torchvision_inverse_affine_matrix(center_f, -float(angle)), | |
| dtype=np.float32, | |
| ).reshape(2, 3) | |
| x_base = np.linspace(-width * 0.5 + 0.5, width * 0.5 - 0.5, width, dtype=np.float32) | |
| y_base = np.linspace(-height * 0.5 + 0.5, height * 0.5 - 0.5, height, dtype=np.float32) | |
| x_grid, y_grid = np.meshgrid(x_base, y_base) | |
| src_x = (x_grid * theta[0, 0] + y_grid * theta[0, 1] + theta[0, 2]) / (0.5 * width) | |
| src_y = (x_grid * theta[1, 0] + y_grid * theta[1, 1] + theta[1, 2]) / (0.5 * height) | |
| ix = np.rint(((src_x + 1.0) * width - 1.0) * 0.5).astype(np.int64) | |
| iy = np.rint(((src_y + 1.0) * height - 1.0) * 0.5).astype(np.int64) | |
| valid = (ix >= 0) & (ix < width) & (iy >= 0) & (iy < height) | |
| out = np.zeros((height, width, channels), dtype=np.float32) | |
| out[valid] = grid[iy[valid], ix[valid]] | |
| return np.ascontiguousarray(out.reshape(SHAPES["bev"]), dtype=np.float32) | |
| def rotate_prev_bev(previous_live_bev: np.ndarray, rotation_can_bus: np.ndarray) -> np.ndarray: | |
| angle = float(rotation_can_bus.reshape(-1)[-1]) | |
| previous_native = np.ascontiguousarray(previous_live_bev, dtype="<f2") | |
| previous_semantic = previous_native.astype(np.float32).reshape(SHAPES["bev"]) | |
| rotated = rotate_prev_bev_like_torchvision(previous_semantic, angle) | |
| rotated_native = np.ascontiguousarray(rotated, dtype="<f2") | |
| return np.ascontiguousarray(rotated_native.astype(np.float32).reshape(SHAPES["bev"])) | |
| def save_raw_outputs(output_dir: Path, frame_index: int, cls_scores: np.ndarray, bbox_preds: np.ndarray) -> dict[str, Any]: | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| cls_path = output_dir / f"frame{frame_index:03d}_cls_scores_fp32.raw" | |
| bbox_path = output_dir / f"frame{frame_index:03d}_bbox_preds_fp32.raw" | |
| np.ascontiguousarray(cls_scores, dtype="<f4").tofile(cls_path) | |
| np.ascontiguousarray(bbox_preds, dtype="<f4").tofile(bbox_path) | |
| return { | |
| "cls_scores": {"path": str(cls_path), "sha256": sha256_file(cls_path)}, | |
| "bbox_preds": {"path": str(bbox_path), "sha256": sha256_file(bbox_path)}, | |
| } | |
| def save_final_coordinates( | |
| output_dir: Path, | |
| frame_index: int, | |
| boxes: np.ndarray, | |
| scores: np.ndarray, | |
| labels: np.ndarray, | |
| ) -> dict[str, Any]: | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| path = output_dir / f"frame{frame_index:03d}_final_coordinates.npz" | |
| np.savez( | |
| path, | |
| boxes=np.ascontiguousarray(boxes, dtype=np.float32), | |
| scores=np.ascontiguousarray(scores, dtype=np.float32), | |
| labels=np.ascontiguousarray(labels, dtype=np.int64), | |
| ) | |
| return { | |
| "path": str(path), | |
| "sha256": sha256_file(path), | |
| "box_count": int(len(scores)), | |
| } | |