跳到主要内容

推理

推理失败时模型不会被关掉,SDK 只把错误写进 last_error,phase 仍然是 OPENED。

快速开始​

char* json = NULL;
if (darra_model_infer_file(h, "photo.jpg", &json) == 0) printf("%s\n", json);
if (json) darra_string_free(json);
类别属性类型读写说明
推理darra_model_infer_file / _ex函数只写对磁盘上的图片文件推理,结果写成 JSON 返回
darra_model_infer函数只写按文件路径推理的统一入口
darra_model_infer_bytes函数只写直接喂图片字节,格式自动识别(AUTO)
darra_model_infer_buffer / _ex函数只写喂内存里的像素缓冲推理,结果写成 JSON 返回
darra_model_infer_image / _ex函数只写跟 infer_buffer 参数含义完全一样
darra_model_infer_tensors函数只写喂张量推理,返回输出张量(用完调 darra_tensor_free 释放)

方法​

darra_model_infer_file() / darra_model_infer_file_ex()​

DARRA_API int32_t darra_model_infer_file(darra_model* m,
const char* image_path,
char** out_results_json);
DARRA_API int32_t darra_model_infer_file_ex(darra_model* m,
const char* image_path,
const double* thresholds,
size_t threshold_count,
char** out_results_json);

对磁盘上的单张图片(jpg/png/bmp)做推理,返回 DARRA_OK(0) 表示成功,结果写进 *out_results_json;失败时看 darra_model_last_error。结果 JSON 是堆上的字符串,必须调 darra_string_free 释放。

参数:

  • m (darra_model*) — 已经打开的模型句柄
  • image_path (const char*) — 图片文件路径(UTF-8)
  • thresholds (const double*) — 这次调用要用的分数下限表,只在 _ex 版本有。按类别下标给值,范围 [0,1]。传 NULL 表示用 default_thresholds
  • threshold_count (size_t) — 表里有几个元素(_ex 用),要跟 class_count 对得上
  • out_results_json (char**) — 成功时把结果 JSON 的地址写到这里。失败时这个指针不会被改动
/* out_results_json 返回的 JSON 是堆上的字符串,用完必须调 darra_string_free 释放。 */
/* 外层字段固定这几项: */
/* schema:int 契约版本号;task:string 任务短名,拿不到就写 null */
/* provider:string 本次实际用的 Provider;elapsedMs:double 本次耗时(毫秒) */
/* image:{width,height} 图片解码后、送进模型前的尺寸 */
/* results[] 里每个元素的字段随 task 和 Provider 不同: */
/* detect: label/class_id/score/box:[x,y,w,h],有的还带 polygon */
/* classify: label/class_id/score(没有 box) */
/* pose: detect 的四个键 + keypoints:[[x,y,conf],...] */
/* obb: detect 的四个键 + angle */
/* segment(ONNX 系): detect 的四个键 + mask/mask_width/mask_height */
/* segment(paddle 系): maskRle:{size:[H,W],values:int[],counts:int[]} */
/* ocr 检测: {label:"text",score,box,polygon};ocr 识别: {text,score} */
/* anomaly 分数图: anomalyScore+anomaly_width/anomaly_height */
/* anomaly 图像级: 只有 score */
/* depth: depth+depth_width/depth_height(这三个键要么都有,要么都没有) */
/* embedding: embedding+embeddingDim(这两个键要么都有,要么都没有) */
/* class_id 是类别身份的权威值(就是类别表里的下标)。要归类请用 class_id,不要靠 label 反推 */
/* 坐标跟 image 用的是同一个坐标空间 */

示例:

char* json = NULL;
int32_t rc = darra_model_infer_file(m, "a.jpg", &json);
if (rc == DARRA_OK) {
printf("%s\n", json);
darra_string_free(json); /* 结果 JSON 必须释放 */
} else {
printf("infer failed %d: %s\n", rc, darra_model_last_error(m));
}

darra_model_infer()​

DARRA_API int32_t darra_model_infer(darra_model* h, const char* image_path, char** out_results_json);

按文件路径推理的统一入口,含义、参数和错误都跟 darra_model_infer_file 一样。

参数: h / image_path / out_results_json。都跟 darra_model_infer_file 一样。

char* json = NULL;
if (darra_model_infer(h, "a.jpg", &json) == DARRA_OK) { puts(json); darra_string_free(json); }

darra_model_infer_bytes()​

DARRA_API int32_t darra_model_infer_bytes(darra_model* h,
const uint8_t* bytes,
size_t len,
char** out_results_json);

按字节推理的统一入口,不用填 desc。

参数: h (darra_model*) — 已经打开的模型句柄。bytes (const uint8_t*) — 图片的编码字节。

char* json = NULL;
if (darra_model_infer_bytes(h, jpg, jpg_len, &json) == DARRA_OK) { puts(json); darra_string_free(json); }

darra_model_infer_buffer() / darra_model_infer_buffer_ex()​

DARRA_API int32_t darra_model_infer_buffer(darra_model* m,
const uint8_t* data,
size_t len,
const darra_image_desc* desc,
char** out_results_json);
DARRA_API int32_t darra_model_infer_buffer_ex(darra_model* m,
const uint8_t* data,
size_t len,
const darra_image_desc* desc,
const double* thresholds,
size_t threshold_count,
char** out_results_json);

直接对内存里的图像缓冲做推理,不经过文件读写。

参数:

  • m (darra_model*) — 已经打开的模型句柄
  • data (const uint8_t*) — 图像数据。内存归调用方持有,函数不负责释放
  • len (size_t) — 数据的字节长度
  • desc (const darra_image_desc*) — 图像描述(size/format/width/height/stride)。
  • thresholds (const double*) — 这次调用要用的分数下限表(只在 _ex 版本有)。传 NULL 表示用 default_thresholds,这点跟 infer_file_ex 一样
  • threshold_count (size_t) — 表里有几个元素(_ex 用),要跟 class_count 对得上
  • out_results_json (char**) — 成功时结果 JSON 的地址写到这里

返回 DARRA_OK(0) 表示成功,结果 JSON 用 darra_string_free 释放。

结果 JSON 的外层字段,跟 infer_file 那一节写的一样。results 里每个元素的字段也都跟 infer_file 那一节写的一样。

示例:

darra_image_desc desc = {0};
desc.size = sizeof(desc);
desc.format = DARRA_IMAGE_RGB888; /* 裸缓冲必须显式给格式 */
desc.width = 640;
desc.height = 480;
char* json = NULL;
if (darra_model_infer_buffer(m, pixels, len, &desc, &json) == DARRA_OK) {
printf("%s\n", json);
darra_string_free(json);
}

format 传 DARRA_IMAGE_AUTO 时自动识别图片格式。

darra_model_infer_image() / darra_model_infer_image_ex()​

DARRA_API int32_t darra_model_infer_image(darra_model* m,
const uint8_t* data,
size_t len,
const darra_image_desc* desc,
char** out_results_json);
DARRA_API int32_t darra_model_infer_image_ex(darra_model* m,
const uint8_t* data,
size_t len,
const darra_image_desc* desc,
const double* thresholds,
size_t threshold_count,
char** out_results_json);

darra_model_infer_buffer / _ex 换了个名字的版本。参数和含义完全一样。

参数: 同 darra_model_infer_buffer / _ex。

char* json = NULL;
if (darra_model_infer_image(m, pixels, len, &desc, &json) == DARRA_OK) { puts(json); darra_string_free(json); }

darra_model_infer_tensors()​

DARRA_API int32_t darra_model_infer_tensors(darra_model* h,
const darra_tensor_t* inputs,
size_t input_count,
darra_tensor_t** out_tensors,
size_t* out_count);

张量推理,支持多个输入和多个输出。

参数: h 是已经打开的模型句柄。inputs / input_count 是输入张量数组和它的个数。

darra_tensor_t in = {0};
in.size = (uint32_t)sizeof(in);
darra_tensor_t* outs = NULL; size_t n = 0;
if (darra_model_infer_tensors(h, &in, 1, &outs, &n) == DARRA_OK) { /* ... */ darra_tensor_free(outs, n); }

基础用法​

批量​

顺序跑多张图就是一个循环,每轮拿到的 JSON 都要释放:

for (;;) {
rc = darra_model_infer_file(h, path, &json);
if (rc != 0) break;
darra_string_free(json);
json = NULL;
}
darra_model_close(h);

阈值​

default_thresholds 是这个实例的默认阈值表。下标是类别序号,值是分数下限。

double def[2] = {0.5, 0.8};
cfg.default_thresholds = def; /* 实例默认 */
cfg.default_thresholds_count = 2;
double one[1] = {0.9};
rc = darra_model_infer_file_ex(h, "a.jpg", one, 1, &json); /* 这个阈值只覆盖这一次调用 */

如果传的值超出范围,或者表的长度跟 class_count 对不上,调用会直接失败。

并发推理​

并发用池:cfg.workers 设大于 1。池入口 darra_pool_submit / darra_pool_wait。

空闲机实测(197,det.onnx,3 轮中位):

  • 单任务基线:1×(约 0.2s)
  • 池 workers=2 双投:两任务同时完成,各约 1–1.5 倍单任务
  • 多实例 2–4 个:总吞吐约等于串行(0.88–0.94×) 并发收益随机器负载变化。

darra_model_submit() / darra_model_wait()​

DARRA_API int32_t darra_model_submit(darra_model* h, const uint8_t* data, size_t len,
const darra_image_desc* desc_or_null, uint64_t* out_ticket);
DARRA_API int32_t darra_model_wait(darra_model* h, uint64_t ticket, char** out_results_json);

异步提交,wait 取结果。那次推理失败,wait 原样带回失败。

示例:

uint64_t ticket = 0;
if (darra_model_submit(h, jpg, jpg_len, NULL, &ticket) == DARRA_OK) {
char* json = NULL;
if (darra_model_wait(h, ticket, &json) == DARRA_OK) { puts(json); darra_string_free(json); }
}

darra_pool_submit / darra_pool_wait​

int32_t darra_pool_submit(darra_pool* pool, const uint8_t* image_bytes, size_t len,
const darra_image_desc* desc, uint64_t* out_ticket);
int32_t darra_pool_wait(darra_pool* pool, uint64_t ticket, char** out_results_json);

提交成功 *out_ticket 大于 0,之后可释放缓冲区。wait 等到结果出来。

示例:

uint64_t t = 0;
darra_pool_submit(pool, jpeg, len, &desc, &t);
char* json = NULL;
if (darra_pool_wait(pool, t, &json) == DARRA_OK) { puts(json); darra_string_free(json); }

槽与份额​

每次推理占 1 个槽。

DARRA_API void darra_sched_set_gpu_slots(int32_t gpu_device, int32_t slots);

这个函数限制一张 GPU 上同时在跑的推理张数。slots 填 0 表示不限制。

示例:

darra_sched_set_gpu_slots(0, 4);   /* 卡 0 最多同时跑 4 个推理 */

日志出口​

进程级日志回调。级别 0 是 ERROR,1 是 WARN,2 是 INFO,3 是 DEBUG。

typedef void (*darra_model_log_fn)(int level, const char* msg, void* user);
DARRA_API int32_t darra_global_set_log_callback(darra_model_log_fn cb, void* user_or_null);

后注册的会覆盖先注册的,cb 传 NULL 表示注销。msg 只在回调执行期间有效,如果想留着以后用,要先拷贝一份。

示例:

darra_global_set_log_callback(my_log, NULL);   /* 注销传 NULL */

推理结果​

推理结果的 JSON 外层字段是固定的。schema / task / provider。

  • detect → {label, class_id, score, box:[x,y,w,h]}。有的还另带 polygon
  • classify → {label, class_id, score}(没有 box)
  • anomaly 有两种形态。分数图是 {anomalyScore, anomaly_width, anomaly_height}。按行存,长度等于宽×高。图像级只带 score
  • pose → {label, class_id, score, box}。keypoints 是 [[x,y,conf],...]
  • obb → {label, class_id, score, box, angle}
  • ocr 有两种形态。det 是 {label:"text", score, box, polygon}。不带 class_id。rec 是 {text, score}
  • depth → {depth, depth_width, depth_height}。embedding → {embedding, embeddingDim}。每组里的键要么都有要么都没有
  • segment 按 Provider 分两种。ONNX 系返回 {label, class_id, score, box} 加三个掩码键。mask/mask_width/mask_height
  • Paddle 系返回 maskRle:{size:[H,W], values, counts}。是整张类别图的游程编码(sum(counts)==H*W,H、W 按模型输出的尺寸算)
  • onnx-directml / trt-native 这两种不出掩码
  • 遇到不认识的键就忽略。task / image 拿不到就写 null,不编造