| from __future__ import annotations |
|
|
| import argparse |
| import sys |
| from pathlib import Path |
|
|
| from alphabet.utils import setup_logging |
|
|
|
|
| def main() -> None: |
| parser = argparse.ArgumentParser(description="Alphabet (A-Z) classifier CLI") |
| parser.add_argument("--log-level", default="INFO") |
| subparsers = parser.add_subparsers(dest="command", required=True) |
|
|
| p_train = subparsers.add_parser("train", help="Download EMNIST and train the model") |
| p_train.add_argument("--config", default="config/config.yaml") |
| p_train.add_argument("--device", default=None, help="cuda | cpu (auto-detects if omitted)") |
|
|
| p_export = subparsers.add_parser("export", help="Export a checkpoint to ONNX and TorchScript") |
| p_export.add_argument("--config", default="config/config.yaml") |
| p_export.add_argument("--checkpoint", required=True, help="Path to best.pt") |
| p_export.add_argument("--device", default="cpu") |
|
|
| p_infer = subparsers.add_parser("infer", help="Run inference on a single image (for testing)") |
| p_infer.add_argument("--model", required=True, help="Path to .onnx model") |
| p_infer.add_argument("--image", required=True, help="Path to a letter crop image") |
| p_infer.add_argument("--config", default="config/config.yaml") |
|
|
| args = parser.parse_args() |
| setup_logging(args.log_level) |
|
|
| if args.command == "train": |
| from alphabet.train import train_from_config |
| summary = train_from_config(args.config, device=args.device) |
| print(f"\nBest val accuracy: {summary['best_val_accuracy']:.4f}") |
| return |
|
|
| if args.command == "export": |
| from alphabet.export import export_from_config |
| exported = export_from_config(args.config, args.checkpoint, device=args.device) |
| print(exported) |
| return |
|
|
| if args.command == "infer": |
| import cv2 |
| import numpy as np |
| from alphabet.dataset import get_class_list |
| from alphabet.infer import OnnxBackend, predict_crops |
| from alphabet.utils import load_yaml |
|
|
| cfg = load_yaml(args.config) |
| infer_cfg = cfg.get("infer", {}) |
| model_cfg = cfg["model"] |
| class_list = get_class_list(cfg) |
|
|
| crop = cv2.imread(args.image, cv2.IMREAD_COLOR) |
| if crop is None: |
| print(f"Error: could not read image at {args.image}", file=sys.stderr) |
| sys.exit(1) |
|
|
| backend = OnnxBackend(args.model) |
| results = predict_crops( |
| backend, |
| [crop], |
| img_size=int(model_cfg.get("img_size", 64)), |
| mean=float(model_cfg.get("mean", 0.5)), |
| std=float(model_cfg.get("std", 0.5)), |
| min_confidence=float(infer_cfg.get("min_confidence", 0.60)), |
| class_list=class_list, |
| ) |
| r = results[0] |
| flag_str = " [FLAGGED for review]" if r["flag"] else "" |
| print(f"Prediction : {r['character']}{flag_str}") |
| print(f"Confidence : {r['confidence']:.4f}") |
| top3 = sorted(enumerate(r["probabilities"]), key=lambda x: -x[1])[:3] |
| for idx, prob in top3: |
| print(f" {class_list[idx]}: {prob:.4f}") |
| return |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|