推理入门
最小可跑的推理调用链只有三步:打开会话 → 推理一张图 → 关闭。本案例把这三步写成能直接编译运行的代码,六种语言调用的是同一套 C ABI。
本案例的几个要点:
- 一次打开,反复推理:打开成本只付一次,之后每帧循环调推理;不要每张图重新打开。
- 每次只喂一张图:没有批量或视频流接口,连续帧就是循环调用同一函数。
- 激活码只在打开时用一次:推理函数永不收码;已绑机的模型打开时不再要码。
分层见 架构。后端选择见 运行时 Provider。缺包与离线部署见 环境准备。失败码与 hint 见 错误码。
适用条件
| 项 | 要求 |
|---|---|
| 交付物 | 任一语言包,内含 x64 / ARM64 native 库;缺 native 只能编译引用,不能运行 |
| 模型 | 加密 .darmodel(钥匙内嵌),或明文 .onnx、.pdmodel、含二者的目录 |
| 不接受 | .pt / .pth(先在 Studio 转 ONNX);Windows 上的 rknn 载荷 |
| 运行时 | 对应 Provider 已在本机部署并通过本地校验;SDK 不下载不安装 |
| 图像输入 | 编码字节流(JPG / PNG / BMP),或裸像素缓冲(RGB888 / BGR888 等) |
| 平台 | Windows x64 / Linux x64 / Linux ARM64。macOS 不在 v1 |
加密模型还额外要求与 Core 信任根匹配的正式签名 Provider;单纯下载或登记成功不等于可运行。
应用场景
- 第一次接入本 SDK:用一个模型和一张图把整条链路跑通,再往里加业务。
- 部署后验收:现场机器装完语言包和 Provider,跑一遍本页判断能不能推理,而不是只看安装登记。
- 手动试跑:产线换模型或换卡后,单张图确认会话能打开、结果 JSON 正常。
关键思路
一个会话 = 一份已加载模型 + 一个已选定的 Provider。多模型或多份同模型就是多开几个会话,接口完全相同;没有模型组或全局引擎入口。
打开 open / open_plain 读容器头或明文探测 → 选 Provider → 验票 / 解密(明文跳过)
│
▼
推理 infer_image 每次一张图,结果 JSON;连续帧循环调这里
│
▼
关闭 close / Dispose 释放并清零明文窗口,之后句柄失效
三处最容易踩空的地方:
- 打开只做一次。 同路径同选项多次打开会驻留共用一份权重,不会加载多份模型。
- 推理是阻塞调用。 耗时随 Provider 与硬件而定,不承诺硬实时;PLC 侧必须在扫描周期外进程 / 线程调用。
- 关闭要等在飞推理结束。 禁止与进行中的推理并发调用。
六语言的三步函数名对照:
| 语言 | 打开 | 推理 | 关闭 |
|---|---|---|---|
| C | darra_session_open / open_plain | darra_session_infer_image | darra_session_close |
| C++ | 同 C,共用 darra_ai.h | 同 C | 同 C |
| C# | AiSession.Open / OpenPlain | InferImage | Dispose |
| Python | Session.open / open_plain | infer_image | close |
| Java | AiSession.tryOpen / open | inferImage | close |
| Rust | Session::open / open_plain | infer_image | Drop |
各语言完整 API 见 C · C++ · C# · Python · Java · Rust。
完整示例
下面每段都是完整可跑的最小程序:打开一个已绑机的加密模型,推理一张编码图片,关闭会话。未激活的出厂包先问激活状态再要一次码,见 初始化。
C
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "darra_ai.h"
int main(void)
{
/* 第一件事:核 ABI。不等 = 装错了库,禁止继续 */
if (DARRA_VERSION_DECODE_MAJOR(darra_version()) != DARRA_VERSION_MAJOR) {
fprintf(stderr, "ABI 不匹配: %s\n", darra_version_string());
return 1;
}
/* 打开:已绑机的 .darmodel 后三个参数都传 NULL */
darra_session* s = NULL;
int32_t rc = darra_session_open("model.darmodel", NULL, NULL, NULL, &s);
if (rc != DARRA_OK) {
darra_error_t err;
err.size = sizeof(err);
darra_last_error(&err);
fprintf(stderr, "[%d] %s\n建议: %s\n", err.code, err.message, err.hint);
return (int)rc;
}
/* 读一张编码图 */
FILE* f = fopen("photo.jpg", "rb");
if (f == NULL) { darra_session_close(s); return 1; }
fseek(f, 0, SEEK_END);
long flen = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t* jpg = (uint8_t*)malloc((size_t)flen);
size_t len = fread(jpg, 1, (size_t)flen, f);
fclose(f);
/* 推理:一张图,结果 JSON 由 SDK 分配 */
darra_image_desc desc;
memset(&desc, 0, sizeof(desc));
desc.size = sizeof(darra_image_desc);
desc.format = DARRA_IMAGE_AUTO; /* 编码字节流按魔数自嗅探 */
char* json = NULL;
rc = darra_session_infer_image(s, jpg, len, &desc, &json);
if (rc == DARRA_OK) {
printf("%s\n", json);
darra_string_free(json); /* 堆字符串必须用 darra_string_free */
}
free(jpg);
darra_session_close(s); /* 释放;NULL 安全 */
return (int)rc;
}
MSVC 编译记得加 /utf-8,运行目录必须带 DarraAI.Core.dll。结构体首字段 size 一律填 sizeof(...),漏填返回 DARRA_INTERNAL。
C++
与 C 共用同一份 darra_ai.h 导出,不另包一层 C++ 类库,直接把上面的函数换成 extern "C" 调用即可:
#include "darra_ai.h"
extern "C" int run_once() {
darra_session* s = nullptr;
if (darra_session_open("model.darmodel", nullptr, nullptr, nullptr, &s) != DARRA_OK)
return 1;
/* darra_session_infer_image(...) */
darra_session_close(s);
return 0;
}
C#
using Darra.AI.Runtime;
try
{
// 打开:已绑机的 .darmodel 不收激活码
using var session = AiSession.Open("model.darmodel");
// 推理:一张编码图(JPG / PNG / BMP)
byte[] jpg = File.ReadAllBytes("photo.jpg");
string json = session.InferImage(jpg, ImageDesc.Auto());
Console.WriteLine(json);
}
catch (AiException ex)
{
// SDK 不弹窗;错误码与 hint 原样透传,展示与重试由应用负责
Console.Error.WriteLine(ex.Message);
Console.Error.WriteLine(ex.Hint);
}
// Dispose 即关闭,释放并清零明文窗口
明文模型换成 AiSession.OpenPlain("model.onnx", preferProvider: "onnx-cpu") 即可,其余不变。参数与返回值见 推理。
Python
from pathlib import Path
from darra_ai import IMAGE_AUTO, Session
from darra_ai.errors import DarraError
jpg = Path("photo.jpg").read_bytes()
try:
with Session.open("model.darmodel") as s: # 已绑机不收码;with 退出即 close
print(s.task, s.provider_id)
print(s.infer_image(jpg, format=IMAGE_AUTO))
except DarraError as ex:
print(ex)
import darra_ai 不加载 native,第一次碰 Core 的 API 才加载。属性与异常见 Session。
Java
AiSession.OpenResult r = AiSession.tryOpen("model.darmodel", null);
if (!r.ok()) {
System.err.println(r.info().display());
return;
}
try (AiSession s = r.session()) { // AutoCloseable,退出即 close
byte[] jpg = java.nio.file.Files.readAllBytes(java.nio.file.Path.of("photo.jpg"));
String json = s.inferImage(jpg, ImageDesc.auto());
System.out.println(json);
}
出厂包要一次激活码走 activate / tryActivate,之后本机 open 免密。见 会话。
Rust
use darra_ai::{ImageDesc, Session};
fn main() -> Result<(), darra_ai::Error> {
let session = Session::open("model.darmodel", None)?;
let jpeg = std::fs::read("photo.jpg").map_err(|e| darra_ai::Error::IoNotFound {
message: format!("读图片失败:photo.jpg ({e})"),
hint: String::new(),
})?;
let json = session.infer_image(&jpeg, &ImageDesc::auto())?;
println!("{json}");
Ok(())
} // Drop 即 close
操作步骤
- 核版本:加载后比较 ABI 主版本,不等就是装错了库,停下来查安装目录。
- 准备环境:用 Studio 下载中心或离线整合包部署所需 Provider,见 运行时清单。
- 问激活状态(只有加密容器需要):先 probe,
NeedsCode时向用户要一次激活码再 activate;已绑机直接打开。 - 推理:复用同一个会话反复喂图,读结果 JSON 里随
session.Task变化的results。 - 关闭:所有任务结束后 close / Dispose,等在飞推理结束再关。
- 激活码不是推理密码,只在打开那一步用一次;不要每次推理都弹输入框,也不要把码写进日志。
- 结果 JSON 的结构随任务类型变化(detect 给 box、classify 给 top-k、segment 给掩码),元数据缺字段是空值,不要按默认 640 / 80 / 17 去补。
- 裸像素缓冲必须给宽高,且禁止用 Auto(嗅探不出通道序)。
- 赶时间别在这里压并发:同会话推理内部互斥排队,真要并发走 并行推理 的推理池,或多开会话。