include steps for ai-hub
This commit is contained in:
92
examples/meter-detection/prepare_aihub_inputs.py
Normal file
92
examples/meter-detection/prepare_aihub_inputs.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Prepare Qualcomm AI Hub calibration and validation inputs for the meter detector."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png"}
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--dataset-dir",
|
||||
type=Path,
|
||||
default=Path("examples/meter-detection/data/electric-meter-detection"),
|
||||
help="Root of the extracted Roboflow dataset.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--calibration-dir",
|
||||
type=Path,
|
||||
default=Path("examples/meter-detection/data/aihub_calibration"),
|
||||
help="Directory where .npy calibration samples will be written.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input-file",
|
||||
type=Path,
|
||||
default=Path("examples/meter-detection/data/inputs.npz"),
|
||||
help="Validation .npz input file for qc-cli ai-hub validate.",
|
||||
)
|
||||
parser.add_argument("--input-name", default="images", help="ONNX input name.")
|
||||
parser.add_argument("--image-size", type=int, default=640, help="Square image size used for ONNX export.")
|
||||
parser.add_argument("--samples", type=int, default=16, help="Number of calibration samples to write.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def preprocess_image(path: Path, image_size: int) -> np.ndarray:
|
||||
"""Apply Ultralytics-style letterboxing and produce an NCHW float32 tensor."""
|
||||
with Image.open(path) as source:
|
||||
image = source.convert("RGB")
|
||||
|
||||
scale = min(image_size / image.width, image_size / image.height)
|
||||
resized_width = round(image.width * scale)
|
||||
resized_height = round(image.height * scale)
|
||||
image = image.resize((resized_width, resized_height), Image.Resampling.BILINEAR)
|
||||
|
||||
canvas = Image.new("RGB", (image_size, image_size), (114, 114, 114))
|
||||
left = round((image_size - resized_width) / 2 - 0.1)
|
||||
top = round((image_size - resized_height) / 2 - 0.1)
|
||||
canvas.paste(image, (left, top))
|
||||
|
||||
array = np.asarray(canvas, dtype=np.float32) / 255.0
|
||||
return np.transpose(array, (2, 0, 1))[None, ...].astype(np.float32)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
if args.image_size < 1:
|
||||
raise SystemExit("--image-size must be at least 1")
|
||||
if args.samples < 1:
|
||||
raise SystemExit("--samples must be at least 1")
|
||||
|
||||
images = sorted(
|
||||
path
|
||||
for path in args.dataset_dir.rglob("*")
|
||||
if path.is_file() and path.suffix.lower() in IMAGE_EXTENSIONS and path.parent.name == "images"
|
||||
)
|
||||
if not images:
|
||||
raise SystemExit(f"No images found under {args.dataset_dir}")
|
||||
|
||||
args.calibration_dir.mkdir(parents=True, exist_ok=True)
|
||||
args.input_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
for stale_sample in args.calibration_dir.glob("sample_*.npy"):
|
||||
stale_sample.unlink()
|
||||
|
||||
prepared: list[np.ndarray] = []
|
||||
for index, image_path in enumerate(images[: args.samples]):
|
||||
sample = preprocess_image(image_path, args.image_size)
|
||||
np.save(args.calibration_dir / f"sample_{index:03d}.npy", sample)
|
||||
prepared.append(sample)
|
||||
|
||||
np.savez(args.input_file, **{args.input_name: prepared[0]}) # pyright: ignore[reportArgumentType]
|
||||
print(f"Wrote {len(prepared)} calibration samples to {args.calibration_dir}")
|
||||
print(f"Wrote validation input to {args.input_file}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user