linaro 211341cbbb feat: 按算法生成模板并支持从 Git LFS 下载模型
- init 新增 yolo26/resnet/arcface/lprnet/face-recognition 模板(多模型人脸检测+识别)
- 新增 embedding/text 输出类型与模板资源(COCO、字符表等)
- 新增 algpkg download-models:从 Gitea LFS 下载 models/BM1688 并做 SHA256 校验
- 补充单元测试与 README
2026-10-17 20:33:00 +08:00

algpkg-sdk —— 算法包开发 SDK

独立项目:算法作者只依赖本项目,即可完成「写算法 → 本地跑通 → 打包 → 自检 → 交付 .mbank」, 不需要 worker 仓库,也不需要了解 worker 内部结构。

算法包开发(本项目)                       设备运行(worker)
──────────────────────                    ──────────────────
写代码 / 本地测试 / 打包 / 签名    →  .mbank  →  上传 → 挂载 → 推理
   用的接口 = algpkg_sdk.api                     同一份接口(别名 video_algpkg_api)

1. 安装

本项目是独立仓库(2026-10-16 从 sophon-edge 仓库拆出): git clone <本仓库地址> && cd algpkg-sdk(目录名即项目名;下文命令都在本项目根执行)。

# 从本项目根安装(开发态;也可打成 wheel 分发给第三方)
uv pip install -e .              # 只做静态检查/打包的机器
uv pip install -e '.[device]'    # 设备上(含 sophon-arm,可跑推理)

配套的 worker(视频推理系统)仓库只在使用 algpkg 工具链做集成联调时才需要: worker 侧 workers/video/pyproject.toml 以路径依赖指向本项目(path = "../../../algpkg-sdk"), 交付打包(deploy/pack_worker.py)会把本项目打成 wheel 放进 worker 交付包的 vendor/; 路径可用环境变量 ALGPKG_SDK_DIR 覆盖(三个开发脚本与打包脚本都认它)。

依赖很轻:module-bank(.mbank 读写)、orjson、numpy。 设备推理包(sophon-arm)是可选的 —— 没有 NPU 的机器照样能打包与做静态校验。

2. 三步上手

# ① 生成脚手架(目录骨架 + manifest 模板 + 可运行示例 + 自测说明)
algpkg init /tmp/smoke-detect --id demo.smoke-detect --name 抽烟检测 --version 1.0.0

# 按算法生成(推荐):YOLO26 模板已内置完整 SAIL 前后处理与 COCO 80 类标签
algpkg init /tmp/yolo26-demo --id demo.yolo26 --name YOLO26检测 --algorithm yolo26

# ② 实现 /tmp/smoke-detect/<包内代码目录>/__init__.py 的 load() / infer()
#    yolo26 模板已实现,无需再写;模型放 /tmp/yolo26-demo/models/
#    或打包时用 --model 指定

# ③ 打包 + 自检(不需要设备)
algpkg build  /tmp/smoke-detect --out demo.smoke-detect-1.0.0.mbank
algpkg inspect demo.smoke-detect-1.0.0.mbank           # 清单/模型/快照/签名
algpkg verify  demo.smoke-detect-1.0.0.mbank           # 结构与模型校验

2.1 算法模板(algpkg init --algorithm/--template)

模板 生成内容
generic(默认) 目录骨架 + 占位 load()/infer(),自己按模型补前后处理
yolo26 可直接运行的 YOLO26 检测包:load() 构造 SAIL 引擎;infer() 完成 letterbox 前处理 → 推理 → 置信度过滤/坐标反变换;manifest 预置 yolo26s_fp16_1b 模型、conf/interval/draw 参数与 COCO 80 类 labels
resnet 可直接运行的 ResNet 图像分类包:resize + ImageNet mean/std 归一化 → 推理 → softmax/top-1;分类结果用整帧框承载(score/类别),绘制与告警链路无需改动;manifest 预置 resnet50_fp32_1b 模型、conf/interval/draw 参数与 1000 类占位 labels
arcface ArcFace 人脸特征提取包:RGB → resize 112×112 → 推理 → L2 归一化;输出 512 维 embedding 放在 AlgoResult.meta["embedding"],output.type=embedding;manifest 预置 arcface_resnet50_fp32_1b 模型与 interval 参数
lprnet LPRNet 车牌识别包:BGR → resize 到模型输入 → (x-127.5)/128 → CTC 贪心解码;车牌字符串放在 AlgoResult.meta["text"],output.type=text;manifest 预置 lprnet_fp32_1b 模型、interval 参数与字符表 assets/chars.txt
face-recognition 人脸检测 + 识别双模型包:RetinaFace 检测人脸 → 按框外扩裁脸 → ArcFace 提取 512 维特征;boxes 为人脸框,meta["embeddings"] 与框逐项对应;manifest 同时声明 face_detector 与 arcface 两份模型

--algorithm 也接受 yolov26、yolo26-detect、resnet50、classifier、arc-face、 face-embedding、lpr、license-plate、face、retinaface-arcface 等别名; --kind 缺省沿用模板(yolo26/face-recognition → detector,其余 → classifier), 显式给出可覆盖。模型放到模板提示的路径即可打包,不在源码目录时用 --model <模型名>=<磁盘路径> 指定实际文件:

algpkg init  /tmp/yolo26-demo --id demo.yolo26 --name YOLO26检测 --algorithm yolo26
algpkg build /tmp/yolo26-demo --out demo.yolo26-1.0.0.mbank \
    --model yolo26s_fp16_1b=/path/to/yolo26s_fp16_1b.bmodel

algpkg init  /tmp/resnet-demo --id demo.resnet --name ResNet分类 --algorithm resnet
algpkg build /tmp/resnet-demo --out demo.resnet-1.0.0.mbank \
    --model resnet50_fp32_1b=/path/to/resnet50_fp32_1b.bmodel

algpkg init  /tmp/arcface-demo --id demo.arcface --name ArcFace特征 --algorithm arcface
algpkg build /tmp/arcface-demo --out demo.arcface-1.0.0.mbank \
    --model arcface_resnet50_fp32_1b=/path/to/arcface_resnet50_fp32_1b.bmodel

algpkg init  /tmp/lprnet-demo --id demo.lprnet --name 车牌识别 --algorithm lprnet
algpkg build /tmp/lprnet-demo --out demo.lprnet-1.0.0.mbank \
    --model lprnet_fp32_1b=/path/to/lprnet_fp32_1b.bmodel

algpkg init  /tmp/face-demo --id demo.face --name 人脸检测识别 --algorithm face-recognition
algpkg build /tmp/face-demo --out demo.face-1.0.0.mbank \
    --model face_detector=/path/to/retinaface_mobilenet0.25_fp32_1b.bmodel \
    --model arcface=/path/to/arcface_resnet50_fp32_1b.bmodel

2.2 下载演示模型(algpkg download-models)

仓库里的 5 个 BM1688 演示模型(YOLO26 / ResNet50 / ArcFace / LPRNet / RetinaFace, 共约 299 MB)通过 Git LFS 存放在 models/BM1688/。没有 git-lfs、或不想拉取 整个仓库时,可直接从 Gitea 的 LFS 端点下载(自动 SHA256 校验;已存在且正确的文件跳过):

algpkg download-models                              # 全部 5 个 → models/BM1688
algpkg download-models --model yolo26 --model resnet
algpkg download-models --dest /tmp/models --force   # 自定义目录 / 强制重下
# 私有仓库或自定义地址:
algpkg download-models --url http://<gitea>/<owner>/<repo>.git --token <TOKEN>

默认仓库地址可用 ALGPKG_REPO_URL 覆盖,token 也可用 GITEA_TOKEN 环境变量提供。 模型名支持 yolo26、resnet、arcface、lprnet、retinaface 及常用别名 (yolov26、resnet50、lpr、face-detector 等)。模型需已 push 到远端 LFS。

3. 本地测试(不需要 worker)

# 静态检查:manifest 合法性 / 包结构 / 模型文件 / 代码可导入(试加载)
algpkg check demo.smoke-detect-1.0.0.mbank
algpkg check demo.smoke-detect-1.0.0.mbank --deep      # 再实例化入口类(需设备)

# 设备上真跑:图片或视频,warmup 后统计单帧耗时,可画框存图
algpkg run demo.smoke-detect-1.0.0.mbank --source clip.mp4 --frames 5 --save-annotated out/
algpkg run a-1.0.0.mbank --source a.jpg --params '{"conf":0.3}'

# 两个包/版本对拍(逐帧检出数 + 逐位一致性)
algpkg compare a-1.0.0.mbank b-1.0.0.mbank --source clip.mp4

同样能力在 Python 里直接调(适合写进自己的 pytest):

from algpkg_sdk import testkit

report = testkit.check("demo.smoke-detect-1.0.0.mbank")          # 任意机器
runs = testkit.run("demo.smoke-detect-1.0.0.mbank", source="clip.mp4",
                   params={"conf": 0.3}, save_annotated="out/")   # 设备
print(runs.summary())
print(testkit.compare(runs, testkit.run("old.mbank", source="clip.mp4")))

4. 写算法:接口契约

包内代码只应导入 video_algpkg_api(接口契约名,= algpkg_sdk.api 的同一批对象)+ 设备框架:

装好本 SDK 后,这个导入名在任何工具里都直接可用:IDE 补全 / mypy / pyright / 本地 python -c "import video_algpkg_api"(SDK 自带同名真实模块 + PEP 561 py.typed)。 设备运行时 worker 仍注册同名别名,两条解析路径拿到的是同一批对象; 不要自己 vendor/复制一份同名模块(两份实现分叉 = "本地过、设备挂")。

from video_algpkg_api import Algorithm, AlgoContext, AlgoResult, AlgoError

class Algorithm(Algorithm):                 # 类名与 manifest.entry 一致
    kind = "detector"

    def load(self) -> None:
        data = self.ctx.model_bytes()       # 包内模型字节(不落盘,直接给引擎)
        conf = float(self.ctx.param("conf", 0.25))
        self._labels = self.ctx.read_asset("assets/labels.txt").decode().splitlines()
        self._engine = MyEngine(data, self.ctx.dev_id, conf)   # 例如 sail.Engine(bytes, …)

    def infer(self, frame) -> AlgoResult:   # 单帧;抽帧由运行方控制
        return AlgoResult(boxes=self._engine(frame))           # (N,6): x1,y1,x2,y2,score,cls

    def draw(self, frame, result):          # 可选:自定义叠加
        return None

    def close(self) -> None:                # 实例回收时调用
        self._engine = None
用法 说明
ctx.model_bytes(name) / ctx.read_asset(rel) 读包内文件(模型/类别表/阈值表),不落盘;不给名 = 选中模型,给名 = 任意已声明模型(多模型包,见下)
ctx.param(key, default) 任务参数(params_schema 默认值已合并,越界在装载时被拒)
ctx.dev_id / ctx.logger / ctx.meta 设备号 / 日志 / 选中模型的元信息(精度、分辨率…)
infer 返回 AlgoResult(boxes=(N,6), counts={"person": n}, value=n, labels=[...])
load() 抛异常 任务转 failed,错误信息带包 id/版本(先在本地 algpkg check --deep 拦住)

约定:同一实例不会被并发调用(运行方串行化),但 infer 不要长时间阻塞; 线程与内存预算见《算法包开发指南》§5。

多模型包(如"检测 + 二次判定/误报过滤"):一个包声明多份模型(manifest.models[]), 代码里 ctx.model_bytes("fp_filter") 按名字取第二份——不必硬编码包内路径:

def load(self):
    self._det = ctx.model_bytes()               # 选中模型(manifest 里 default 的那份)
    self._cls = ctx.model_bytes("fp_filter")    # 第二份:裁检测框 → 分类判误报

两级管线写在 infer() 里:返回过滤后的 boxes 即可,告警/统计/叠加图自动跟着走 (运行时不做级联,这就是"包内级联"的标准形态;细节与算力旋钮见《算法包开发指南》§5)。

5. 打包选项

algpkg build <源目录> --out x.mbank \
    --model <模型名>=<磁盘路径> \      # 模型不在源码目录时显式指定(包内路径以 manifest 为准)
    --encrypt <Fernet KEY> \          # 加密包内代码(明文源码会被删除;模型不加密)
    --pyarmor \                       # PyArmor 混淆(需授权)
    --sign <KEY> --key-id <ID>        # HMAC-SHA256 签名(对称;设备侧配相同密钥验签)
algpkg list <目录>                     # 列目录内的包(id/版本/大小)

6. 目录结构

algpkg-sdk/
├── pyproject.toml            项目定义(scripts: algpkg)
├── src/algpkg_sdk/
│   ├── api.py                接口契约(Algorithm / AlgoContext / AlgoResult)+ 别名注册
│   ├── manifest.py           清单字段与校验(models / params_schema / alarm / legacy…)
│   ├── models.py             download-models:从 Git LFS 拉取 models/BM1688 演示模型
│   ├── loader.py             AlgoPackage:读 svfs、挂代码(试加载)、构造实例
│   ├── builder.py            打包(module_bank 打代码 + svfs 放模型/资源 + 快照=版本 + 签名)
│   ├── testkit.py            本地测试台:check / run / compare(无需 worker)
│   ├── templates.py          init 算法模板(generic / yolo26 / resnet / arcface / lprnet / face-recognition)
│   └── cli.py                algpkg build|inspect|verify|init|list|check|run|compare|download-models
├── models/BM1688/            Git LFS:5 个 BM1688 演示模型(可 download-models 拉取)
├── examples/person-count/    完整示例:人员计数(自带实现,只依赖本 SDK)
│   └── …
└── tests/                    pytest(无设备跑静态,有设备跑推理,自动跳过)

7. 自测

cd algpkg-sdk
uv run pytest                 # 静态用例;标记为 device 的用例在无 NPU 机器上自动跳过
uv run pytest -m device       # 设备上跑推理用例

8. 与设备的对接

  1. algpkg build 产出单文件 .mbank;
  2. 在设备「算法管理」页上传(或 POST /api/v1/video/algorithm/install 分片上传 → commit);
  3. 任务里选择该算法(参数表单由 manifest.params_schema 生成),可与其他算法并行;
  4. 现场排障与完整字段表见仓库文档《算法包开发指南》;设计依据见《算法包与多算法任务设计》。
S
Description
No description provided
Readme
298 MiB
Languages
Python 100%