Need help with dev-docs?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

ExtremeMart
122 Stars 36 Forks 273 Commits 4 Opened issues

Description

服务于极市平台开发者的项目,提供SDK接口文件规范与常见问题示例代码。欢迎所有开发者一起参与示例代码编写。

Services available

!
?

Need anything else?

Contributors list

EV_SDK

说明

EV_SDK的目标

开发者专注于算法开发及优化,最小化业务层编码,即可快速部署到生产环境,共同打造商用级高质量算法。

极市平台做了哪些

  1. 统一定义算法接口:针对万千视频和图片分析算法,抽象出接口,定义在
    include
    目录下的
    ji.h
    文件中
  2. 提供工具包:比如算法授权(必须实现)、模型加密,在
    3rd
    目录下
  3. 应用层服务:此模块不在ev_sdk中,比如视频处理服务、算法对外通讯的http服务等

开发者需要做什么

  1. 模型的训练和调优
  2. 实现
    ji.h
    约定的接口,同时包括授权、支持分析区域等功能
  3. 实现约定的输入输出
  4. 其他后面文档提到的功能

目录

代码目录结构

ev_sdk
|-- 3rd             # 第三方源码或库目录,发布时请删除
|   |-- wkt_parser          # 针对使用WKT格式编写的字符串的解析器
|   |-- cJSON               # c版json库,简单易用
|   |-- ev_encrypt_module   # 模型加密库及相关工具
|   |-- darknet             # 示例项目依赖的库
|   `-- license             # SDK授权库及相关工具
|-- CMakeLists.txt          # 本项目的cmake构建文件
|-- README.md       # 本说明文件
|-- model           # 模型数据存放文件夹
|-- config          # 程序配置目录
|   |-- README.md   # algo_config.json文件各个参数的说明和配置方法
|   `-- algo_config.json    # 程序配置文件
|-- doc
|-- include         # 库头文件目录
|   `-- ji.h        # libji.so的头文件,理论上仅有唯一一个头文件
|-- lib             # 本项目编译并安装之后,默认会将依赖的库放在该目录,包括libji.so
|-- src             # 实现ji.cpp的代码
`-- test            # 针对ji.h中所定义接口的测试代码,请勿修改!!!

使用示例

作为示例,我们提供了一个使用

darknet
实现的图像检测器,并将其使用
EV_SDK
规范进行封装,需要实现的业务逻辑是当检测到时,需要返回相关的报警信息。使用如下步骤尝试编译和测试该项目:

下载
EV_SDK

git clone https://github.com/ExtremeMart/dev-docs
mv dev-docs /usr/local/ev_sdk

编译

编译和安装

libji.so
mkdir -p /usr/local/ev_sdk/build
cd /usr/local/ev_sdk/build
cmake ..
make install

测试示例程序和接口规范

执行完成之后,

/usr/local/ev_sdk/lib
下将生成
libji.so
和相关的依赖库,以及
/usr/local/ev_sdk/bin/
下的测试程序
test-ji-api
  1. 要使用
    /usr/local/ev_sdk/bin/test-ji-api
    测试
    EV_SDK
    的接口,需要重新生成授权所使用的参考码
    reference.txt
    ,并使用私钥对其进行加密后重新生成授权文件
    license.txt
   # 生成公私钥以及公钥对应的头文件
   bash /usr/local/ev_sdk/3rd/license/bin/oneKeyAuth.sh
   # 生成硬件参考码文件和授权文件
   bash /usr/local/ev_sdk/3rd/license/bin/oneKeyTest.sh
  1. 使用
    test-ji-api
    测试
    ji_calc_frame
    接口,测试添加了一个
    ROI
    参数
   /usr/local/ev_sdk/bin/test-ji-api -f ji_calc_frame -i /usr/local/ev_sdk/data/dog.jpg -o /tmp/output.jpg -l /usr/local/ev_sdk/authorization/license.txt -a '{"roi":["POLYGON((0.2 0.2,0.6 0.1,0.8 0.7,0.4 0.9,0.1 0.8,0.2 0.25))"]}'

输出内容样例:

    code: 0
    json: {
    "alert_flag":   1,
    "dogs": [{
            "x": 129,
            "y": 186,
            "width": 369,
            "height": 516,
            "confidence":   0.566474
        }]
    }

使用
EV_SDK
快速封装算法

假设项目需要检测输入图像中是否有,如果检测到,就需要输出报警信息,以下示例开发算法与使用

EV_SDK
进行封装的流程

实现自己的模型

假设我们使用

darknet
开发了针对的检测算法,程序需要在检测到狗时输出报警信息。

下载
EV_SDK

git clone https://github.com/ExtremeMart/dev-docs
mv dev-docs /usr/local/ev_sdk

生成授权功能所依赖的文件

  1. 使用
    EV_SDK
    提供的工具一键生成公钥和私钥、以及公钥对应的头文件
    pubKey.hpp
   bash /usr/local/ev_sdk/3rd/license/bin/oneKeyAuth.sh

执行成功后将在

/usr/local/ev_sdk
下生成公钥
authorization/pubKey.perm
和私钥
authorization/privateKey.pem
,以及头文件
include/pubKey.hpp
  1. ji_init(int argc, char **argv)
    的接口实现中,添加校验授权文件的功能。

注:这部分代码在示例代码

ji.cpp
中已经实现,可以无需变动,直接使用。

   // 使用公钥校验授权信息
   int ret = ji_check_license(pubKey, license, url, activation, timestamp, qps, version);
   return ret == EV_SUCCESS ? JISDK_RET_SUCCEED : JISDK_RET_UNAUTHORIZED;

更多授权功能的原理,请参考算法授权

添加模型加密功能

  1. 使用
    EV_SDK
    提供的工具加密模型,并生成
    C++
    头文件,这里仅仅示例加密
    yolov3-tiny.cfg
    文件,请根据实际需要,对重要的模型/权重文件进行加密
   mkdir -p /usr/local/ev_sdk/model_encryption/
   cd /usr/local/ev_sdk/model_encryption/
   /usr/local/3rd/ev_encrypt_module/bin/encrypt_tool /usr/local/ev_sdk/model/yolov3-tiny.cfg

执行成功后会生成加密后的文件

model_str.hpp
encrypt_tool
程序支持在加密模型时指定一个混淆字符串,具体方法请执行
encrypt_tool
参考帮助文档。将头文件移动到代码区
   mv /usr/local/ev_sdk/model_encryption/model_str.hpp /usr/local/ev_sdk/include

这个加密后的模型将被硬编码

libji.so
  1. ji_create_predictor(int)
    的接口实现中,添加模型解密的功能。

注:示例代码

ji.cpp
里面提供了解密的方法,对于加密文本类型的模型文件的场景可以直接使用,无需更改。

   // 创建解密句柄
   void *h = CreateEncryptor(model_str.c_str(), model_str.size(), key.c_str());
   // 获取解密后的字符串
   int fileLen = 0;
   model_struct_str = (char *) FetchBuffer(h, fileLen);
   // 获取解密后的文件句柄
   // file *file = (file *) FetchFile(h);
   DestroyEncrtptor(h);

模型解密的详细接口函数请参考其头文件encrypt_wrapper.h

实现
ji.h
中的接口

ji.h
中定义了所有
EV_SDK
规范的接口,详细的接口定义和实现示例,请参考头文件ji.h和示例代码ji.cpp

将代码编译成

libji.so
mkdir -p /usr/local/ev_sdk/build
cd /usr/local/ev_sdk/build
cmake ..
make install

编译完成后,将在

/usr/local/ev_sdk/lib
下生成
libji.so
和其他依赖的库。

测试接口功能

测试

libji.so
的授权功能是否正常工作以及
ji.h
的接口规范
  1. 使用
    EV_SDK
    提供的程序
    oneKeyTest.sh
    一键生成授权文件
   bash /usr/local/ev_sdk/3rd/license/bin/oneKeyTest.sh

oneKeyTest.sh
会执行:
  • 检查

    authorization/pubKey.pem
    authorization/privateKey.pem
    的有效性;
  • 生成硬件参考码文件

    authorization/reference.txt
    和授权文件
    authorization/license.txt
  1. 检查授权功能和
    ji.h
    的接口规范性

EV_SDK
代码中提供了测试所有接口的测试程序,编译并安装
libji.so
之后,会在
/usr/local/ev_sdk/bin
下生成
test-ji-api
可执行文件,
test-ji-api
用于测试
ji.h
的接口实现是否正常,例如,测试
ji_calc_frame
接口以及授权功能是否正常:
   /usr/local/ev_sdk/bin/test-ji-api -f ji_calc_frame \
   -i /usr/local/ev_sdk/data/dog.jpg \
   -o /tmp/output.jpg \
   -l /usr/local/ev_sdk/authorization/license.txt \
   -a '{"roi":["POLYGON((0.2 0.2,0.6 0.1,0.8 0.7,0.4 0.9,0 0.8,0.2 0.2))"]}'

接口测试程序的详细功能请查阅

test-ji-api --help
的帮助文档及其代码test.cpp

哪些内容必须完成才能通过测试?

按照需求实现接口(由项目经理告知)

  1. ji_calc_frame
    ,用于实时视频流分析
  2. ji_calc_buffer
    ,用于分析图片
    buffer
  3. ji_calc_file
    ,用于分析图片文件
  4. ji_calc_video_file
    :用于极市平台测试组测试和开发者自测视频文件

规范要求

规范测试大部分内容依赖于内置的

/usr/local/ev_sdk/test
下面的代码,这个测试程序会链接
/usr/local/ev_sdk/lib/libji.so
库,
EV_SDK
封装完成提交后,极市方会使用
test-ji-api
程序测试
ji.h
中的所有接口。测试程序与
EV_SDK
的实现没有关系,所以请请不要修改
/usr/local/ev_sdk/test
目录下的代码!!!
  1. 接口功能要求
  • 确定

    test-ji-api
    能够正常编译,并且将
    test-ji-api
    license.txt
    移动到任意目录,都需要能够正常运行;
  • 在提交算法之前,请自行通过

    /usr/local/ev_sdk/bin/test-ji-api
    测试接口功能是否正常;
  • 未实现的接口需要返回

    JISDK_RET_UNUSED
  • 实现的接口,如果传入参数异常时,需要返回

    JISDK_RET_INVALIDPARAMS
  • 对于实现多个接口的情况,请确保每个接口对同样的输入数据保持一致的算法分析结果,比如

    ji_calc_frame
    ji_calc_file
    两个接口对于同样的输入图片数据,应该保持一样的分析结果;
  • 输入图片和输出图片的尺寸应保持一致;

  • 对于接口中传入的参数

    args
    (如,
    ji_calc_frame(void *, const JI_CV_FRAME *, const char *args, JI_CV_FRAME *, JI_EVENT *)
    中中
    args
    ),根据项目需求,算法实现需要支持
    args
    实际传入的参数。

    例如,如果项目需要支持在

    args
    中传入
    roi
    参数,使得算法只对
    roi
    区域进行分析,那么算法内部必须实现只针对
    roi
    区域进行分析的功能
  • 对于接口

    ji_calc_video
    接口,其保存的
    json
    文件格式必须与
    ji_calc_frame
    ji_calc_file
    ji_calc_buffer
    中的
    JI_EVENT.json
    格式保持一致;
  • 通常输出图片中需要画

    roi
    区域、目标框等,请确保这一功能正常,包括但不仅限于:
    • args
      中输入的
      roi
      需要支持多边形
    • 算法默认分析区域必须是全尺寸图,如当
      roi
      传入为空时,算法对整张图进行分析;
  • 为了保证多个算法显示效果的一致性,与画框相关的功能必须优先使用

    ji_utils.hpp.h
    中提供的工具函数;
  1. test-ji-api
    的使用方法可以参考上面的使用示例以及运行
    test-ji-api --help
  2. 以上要求在示例程序
    ji.cpp
    中有实现;
  1. 业务逻辑要求

针对需要报警的需求,算法必须按照以下规范输出结果: * 报警时输出:

JI_EVENT.code=JISDK_CODE_ALARM
JI_EVENT.json
内部填充
"alert_flag"=1
; * 未报警时输出:
JI_EVENT.code=JISDK_CODE_NORMAL
JI_EVENT.json
内部填充
"alert_flag"=0
; * 处理失败的接口返回
JI_EVENT.code=JISDK_CODE_FAILED
  • 算法输出的
    json
    数据必须与项目需求保持一致;
  1. 授权功能要求

需要实现授权功能,并且在调用接口(比如

ji_calc_frame
)时,如果授权没有通过,必须返回
JISDK_RET_UNAUTHORIZED

注:授权功能已经在示例代码实现,基本不需要修改

  1. 算法配置选项要求
  • 配置文件必须遵循
    EV_SDK
    配置协议
  • 所有算法与
    SDK
    可配置参数必须存放在统一的配置文件:
    /usr/local/ev_sdk/config/algo_config.json
    中;
  • 配置文件中必须实现的参数项:

    • draw_roi_area
      true
      或者
      false
      ,是否在输出图中绘制
      roi
      分析区域;
    • roi_line_thickness
      :ROI区域的边框粗细;
    • roi_fill
      :是否使用颜色填充ROI区域;
    • roi_color
      roi
      框的颜色,以BGRA表示的数组,如
      [0, 255, 0, 0]
      ,参考model/README.md
    • roi
      :针对图片的感兴趣区域进行分析,如果没有此参数或者此参数解析错误,则roi默认值为整张图片区域; 注:多个点、线、框有两种实现方式:
      • 使用WKT格式,如:
        "roi": "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))"
      • 使用数组形式,如:
        "roi":["POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"]

    config/README.md
    内必须说明使用的是哪一种格式。 -

    thresh
    :算法阈值,需要有可以调整算法灵敏度、召回率、精确率的阈值参数,如果算法配置项有多个参数,请自行扩展,所有与算法效果相关并且可以变动的参数必须
    /usr/local/ev_sdk/config/README.md
    中提供详细的配置方法和说明(包括类型、取值范围、建议值、默认值、对算法效果的影响等); -
    draw_result
    true
    或者
    false
    ,是否绘制分析结果,比如示例程序中,如果检测到狗,是否将检测框和文字画在输出图中; -
    draw_confidence
    true
    或者
    false
    ,是否将置信度画在检测框顶部,小数点后保留两位; -
    language
    :所显示文字的语言,需要支持
    en
    zh
    两种选项,分别对应英文和中文; - 所有
    json
    内的键名称必须是小写字母,并且单词间以下划线分隔,如上面几个示例。
  • 必须支持参数实时更新。所有

    /usr/local/ev_sdk/config/algo_config.json
    内的可配置参数必须支持能够在调用
    ji_calc_frame
    ji_calc_buffer
    ji_calc_file
    ji_calc_video_file
    四个接口时,进行实时更新。也就是必须要在
    ji_calc_*
    等接口的
    args
    参数中,加入这些可配置项。

    根据算法的实际功能和使用场景,参数实时更新功能可能只能够使部分参数有效,其中

 1. 可以通过`ji_calc_frame`等接口的`args`参数传入并实时更新的参数,比如示例代码中检测框的颜色`target_rect_color`,这些配置项称为**动态参数**(即可动态变更);

  1. 其他无法通过args参数传入并进行实时更新的参数称为静态参数,通常这些参数需要重启算法实例才能生效;

    > 静态参数的名称规范: > > 静态参数必须以static作为前缀,例如static_detect_thresh

  • 算法开发完成后,必须按照
    config/README.md
    的模版,修改成当前算法的配置说明
  1. 算法输出规范要求

算法输出结果,即

JI_EVENT.json
必须是使用
json
格式填充的字符串,
json
字符串内所有的键名称必须是小写字母,并且单词之间使用下划线分隔,如
alert_flag
  1. 文件结构规范要求
  • 授权功能生成的公私钥必须放在
    /usr/local/ev_sdk/bin
    ,且一次生成后,请勿再次更新公私钥(极市方会保存第一版的私钥),如果在后续更新中,重新生成了公私钥,会导致公私钥不匹配;
  • 与模型相关的文件必须存放在
    /usr/local/ev_sdk/model
    目录下,例如权重文件、目标检测通常需要的名称文件
    coco.names
    等。
  • 最终编译生成的
    libji.so
    必须自行链接必要的库,
    test-ji-api
    不会链接除
    /usr/local/ev_sdk/lib/libji.so
    以外的算法依赖库;
  • 如果
    libji.so
    依赖了系统动态库搜索路径(如
    /usr/lib
    /lib
    等)以外的库,必须将其安装到
    /usr/local/ev_sdk/lib
    下,可以使用
    ldd /usr/local/ev_sdk/lib/libji.so
    查看
    libji.so
    是否正确链接了所有的依赖库。
  • 授权文件位置
    • 请务必将生成的私钥
      privateKey.pem
      和公钥
      publicKey.pem
      放到
      /usr/local/ev_sdk/authorization
      下。并请自行保存一份,后续算法迭代过程都会使用第一次提交的公私钥,不能重新生成

FAQ

如何使用接口中的
args

通常,在实际项目中,外部需要将多种参数(例如

ROI
)传入到算法,使得算法可以根据这些参数来改变处理逻辑。
EV_SDK
接口(如
int ji_calc_frame(void *, const JI_CV_FRAME *, const char *args, JI_CV_FRAME *, JI_EVENT *)
中的
args
参数通常由开发者自行定义和解析,但只能使用JSON格式。格式样例:
{
    "cid": "1000",
    "roi": [
        "POLYGON((0.0480.357,0.1660.0725,0.3930.0075,0.3920.202,0.2420.375))",
        "POLYGON((0.5130.232,0.790.1075,0.9280.102,0.9530.64,0.7590.89,0.510.245))",
        "POLYGON((0.1150.497,0.5920.82,0.5810.917,0.140.932))"
    ],
    "cross_line": ["LINESTRING(0.070.21,0.360.245,0.580.16,0.970.27)"],
    "point": [
            "POINT(0.38 0.10)",
            "POINT(0.47 0.41)"
    ]
}

例如当算法支持输入

ROI
参数时,那么开发者需要在
EV_SDK
的接口实现中解析上面示例中
roi
这一值,提取其中的
ROI
参数,并使用
WKTParser
对其进行解析,应用到自己的算法逻辑中。

如何在
algo_config.json
内添加一个自定义配置项?

假定需要在配置文件中添加一个额外的算法阈值参数

nms_thresh
,则需要:
  1. algo_config.json
    中加入默认配置参数:
   "nms_thresh": 0.4
  1. Configuration.hpp
    中的
    Configuration
    结构体中添加这一参数对应的变量:
   float nmsThresh = 0.4;
  1. Configuration.hpp
    Configuration.parseAndUpdateArgs
    方法中添加对该参数的解析代码:
   cJSON *nmsThreshObj = cJSON_GetObjectItem(confObj, "nms_thresh");
   if (nmsThreshObj != nullptr && nmsThreshObj->type == cJSON_Number) {
    nmsThresh = nmsThreshObj->valuedouble;     // 获取默认的阈值
     algoConfig.thresh = newThresh;
   }

为什么不能且不需要修改
/usr/local/ev_sdk/test
下的代码?

  1. /usr/local/ev_sdk/test
    下的代码是用于测试
    ji.h
    接口在
    libji.so
    中是否被正确实现,这一测试程序与
    EV_SDK
    的实现无关,且是极市方的测试标准,不能变动;
  2. 编译后
    test-ji-api
    程序只会依赖
    libji.so
    ,如果
    test-ji-api
    无法正常运行,很可能是
    libji.so
    没有按照规范进行封装;

为什么运行
test-ji-api
时,会提示找不到链接库?

由于

test-ji-api
对于算法而言,只链接了
/usr/local/ev_sdk/lib/libji.so
库,如果
test-ji-api
运行过程中,找不到某些库,那么很可能是
libji.so
依赖的某些库找不到了。此时
  1. 可以使用
    ldd /usr/local/ev_sdk/lib/libji.so
    检查是否所有链接库都可以找到;
  2. 请按照规范将系统动态库搜索路径以外的库放在
    /usr/local/ev_sdk/lib
    目录下。

如何使用
test-ji-api
进行测试?

  1. 输入单张图片和授权文件,并调用
    ji_calc_frame
    接口:
   ./test-ji-api -f ji_calc_frame -i /path/to/test.jpg -l /path/to/license.txt
  1. 输入
    json
    格式的
    roi
    参数到
    args
    参数:
   ./test-ji-api \
   -f ji_calc_frame \
   -i /path/to/test.jpg \
   -l /path/to/license.txt \
   -a '{"roi":["POLYGON((0.2 0.2,0.7 0.13,0.9 0.7,0.4 0.9,0.05 0.8,0.2 0.25))"]}'
  1. 保存输出图片:
   ./test-ji-api -f ji_calc_frame -i /path/to/test.jpg -l /path/to/license.txt -o /path/to/out.jpg

更多选项,请参考

test-ji-api --help

EV_SDK配置协议的实现样例

配置协议规定:

  1. 只能有一级KEY-VALUE
  2. VALUE的类型有两种:
    1. JSON格式定义的非数组和非对象类型,如
      string
      number
      false
      true
    2. 由配置协议所定义的数据类型;

举例:

  1. 算法支持多个ROI时的配置,VALUE可以使用协议所定义的多个
    POLYGON
    类型:
   {
     "roi": ["POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))", "POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))"]
   }
  1. 算法需要多个ROI,并且多个每个ROI表示不同逻辑含义时:
   {
     "roi_1": "POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))",
     "roi_2": "POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))"
   }

算法内部根据

roi_1
roi_2
进行逻辑区分。
  1. 如果算法需要以组合的形式配置算法,且组合的数量不设限制时,可用如下配置:
   {
     "roi_1": "POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))",
     "line_1": "LINESTRING (0.1 0.1, 0.12 0.15, 0.2 0.3)",
     "roi_2": "POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))",
     "line_2": "LINESTRING (0.1 0.1, 0.12 0.15, 0.2 0.3)"
     ......
   }

算法内部需要:

  • 针对字段名称将所设置的值进行组合,例如将
    roi_1
    line_1
    组合为一组,从而合成自己所需的格式;
  • 对于数量,通过字段的数字后缀来遍历得到,例如当外部传入
    roi_3
    line_3
    时,算法内部需要自行通过遍历获得这第三组配置

以上配置方法必须在实现时写入

config/README.md
配置文档。
  1. 人员闯入算法通常需要配置一个ROI和一条闯入边界线,可以使用以下配置:
   {
     "roi": "POLYGON ((0.1 0.1, 0.1 0.1, 0.2 0.3, 0.9 0.9))",
     "cross_line": "LINESTRING (0.1 0.1, 0.12 0.15, 0.2 0.3)"
   }

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.