# NeoTracker SDK ## 1. 范围 目前对标准跟踪器的解耦方案如下: ![images](doc/images/image.png) 本文档描述的NeoTracker SDK为上图蓝色部分,包含对空对地搜索、传统跟踪算法逻辑,并与NPU设备运行的AI识别与AI跟踪算法进行交互。 ## 2.图像 接口中图像使用平台定义的GD_VIDEO_FRAME_S。 ```C++ #ifndef GD_VIDEO_FRAME_S #define GD_VIDEO_FRAME_S typedef enum _gd_pixel_format_e { GD_PIXEL_FORMAT_NONE = 0, GD_PIXEL_FORMAT_YUV420P, GD_PIXEL_FORMAT_YUV422P, GD_PIXEL_FORMAT_NV12, GD_PIXEL_FORMAT_NV21, GD_PIXEL_FORMAT_NV16, GD_PIXEL_FORMAT_NV61, GD_PIXEL_FORMAT_RGB_PLANAR, GD_PIXEL_FORMAT_BGR_PLANAR, GD_PIXEL_FORMAT_RGB_PACKED, GD_PIXEL_FORMAT_BGR_PACKED, GD_PIXEL_FORMAT_GRAY_Y8, GD_PIXEL_FORMAT_GRAY_Y16, GD_PIXEL_FORMAT_GRAY_Y16Y8, GD_PIXEL_FORMAT_BUTT } GD_PIXEL_FORMAT_E; typedef struct _gd_video_frame_s { unsigned int u32Width; unsigned int u32Height; GD_PIXEL_FORMAT_E enPixelFormat; unsigned int u32Stride[3]; unsigned long long u64PhyAddr[3]; unsigned char* u64VirAddr[3]; unsigned int u32TimeRef; unsigned int u32FrameCnt; } GD_VIDEO_FRAME_S; #endif ``` 图像数据结构引用平台室《图像格式要求》,该格式相对于传统算法输入图像格式,特点是内存可以不连续 (1)yuv、RGB不同通道的地址可以不连续 (2)数据带有stride属性,目前海思平台上广泛使用这种数据类型,用于字节对齐加快处理速度。 目前对数据结构的支持如下: | 模块 | Y16 | Y8 | NV12 | RGB_PACKED | 其他 | | ---- | ---- | ---- | ---- | ---------- | ---- | | DST | 支持 | x | x | x | x | | DAT | 支持 | 支持 | x | x | x | | KCF | 支持 | 支持 | 支持 | 支持 | x | | TLD | 支持 | 支持 | 支持 | 支持 | x | | OCC | 支持 | 支持 | 支持 | 支持 | x | >**目前算法内部对数据的处理基于连续内存,因此暂不支持stride特性。如图像通道不满足内存连续,算法会直接退出。需要在赋值时将stride赋值为实际占用内存长度,如Y16数据位 w * 2,RGB数据为w * 3** ## 3 主接口与调用时序控制 相对于之前一个主接口打天下的方式,现阶段需要调用的接口增加。 - 接口使用前需要显式调用构造函数和初始化函数,使用完毕后手动析构函数。 - 由于AI识别技术的广泛使用, 参考S727的实现,将传统小面目标检测接口单独暴露出来供调用。目标列表融合部分需要针对每个项目需求编写。检测部分也受到整个算法模块的状态控制,因此也需要在初始化完成后调用,如对空对地模式参数。 框架如何响应外部指令。外部指令发出是瞬时的,在跟踪流程的任意时刻都有可能,但多目标跟踪过程中,批号、算法资源等都严重依赖上下帧关系,不能实时处理指令,从框架设计简易考虑, 需要在每一帧开始前进行统一的指令计算与响应。 因此本框架仍不支持实时响应,需要在下一帧统一处理。 ### 3.4 锁定逻辑 跟踪算法最重要的外部接口是锁定逻辑,本框架中将锁定指令进行如下划分,作不同响应。 接口1:普通锁定 ``` void ARIDLL_LockCommand(ArithHandle hArithSrc, int nLockX, int nLockY, int nLockW, int nLockH) ``` - LockMode::LOCK_POINT 如果不传大小,则为点锁定,点锁定位置附近若存在目标,则自动锁定该目标,若点锁定附近无目标,则根据需求可执行不同操作,例如对空可进行强制提取,对地可进行固定波门锁定。 - LockMode::LOCK_RECT: >如果传入完整的锁定框,那么仅执行强制面目标锁定。 接口2:引导锁定 ``` void ARIDLL_GuideLockMultiCommand(ArithHandle hArithSrc, TargetGuide* guideList, int num) ``` 给出稳定系角度进行目标锁定,属于某种自动锁定逻辑,要求算法根据先验对视场内的目标进行智能化选择与锁定。同样支持多个目标。 引导锁定在接口形式上与普通锁定的点选比较接近,但一般点选是有人在回路的,锁错代价低,要求快速响应。引导锁定一般是个连续的自动流程,要求一次性锁对,可以延迟多帧处理。 ```C++ STD_TRACKER_API void ARIDLL_AdjustTrackRect(ArithHandle hArithSrc,int dx,int dy,int dw,int dh); ``` 修改锁定波门接口,可以在目标静止时微调已跟踪的目标框。接口是通过重复下发锁定实现的,因此不适合跟踪运动目标时使用。此外,由于在多目标场景下下发锁定一般会实现为再次初始化新目标, 因此本接口限制为仅单目标跟踪模式下有效。 **关于延迟跟踪机制** 该机制在跟踪调用端实现,可以降低算法设计的复杂度。 对于无线传输项目,显控端收到的图像往往是延迟的,用户在延迟的数据上下发跟踪,指令传到设备端后已无法确认指令要求的位置和图像。因此针对延迟跟踪项目,需要使用缓存队列+延迟跟踪设计。 正常跟踪时,算法调用者(综控)始终缓存图像帧(含内容与帧编号),显控端下发锁定指令时需附带帧编号,如果帧编号滞后于当前最新帧编号,则在缓存队列中查找用户锁定帧,并将送算法的队列指针指向该帧,同时根据帧差自适应跳帧速度,跟踪到最新帧后,则恢复正常跟踪。 ## 4 状态与模式 本SDK目前定义了如下算法及需求相关的状态/模式,解释如下。 ### 4.1外部系统模式 **GLB_SYS_MODE** 通常用于响应用户产品模式,如S338用户定义的【战斗】【模靶】等,S731自主定义的【无人值守】【扫描】等,不一定都会影响算法状态和运行,但通常都需将该模式传递给算法便于灵活响应,不同的项目区别较大,可能涉及接口增加。 ### 4.2 算法内部状态 **GLB_STATUS** 算法根据用户需求,将自身工作状态进行划分。同时会针对不同控制指令自行切换。通常我司的光电设备具有 搜索 - 单目标跟踪 - 周扫 - 扇扫 - 多目标跟踪 - 丢失等状态。 根据下发的锁定指令或引导指令,算法会从不同的搜索状态转为跟踪,同时会输出自身当前状态,需要调用者外部及时接收并配合做出不同的响应(控制伺服、上报等)。 【搜索】 算法执行目标检测,并进行时空域滤波,送出具有固定批号的目标列表,该列表可以在如下结构中体现。 ```c++ //*****目标检测*****(短时航迹点,用于用户指示) int nAlarmObjCnts; //当前帧告警目标总个数 ARIDLL_OBJINFO stAlarmObjs[ST_OBJ_NUM]; //检测目标信息数组 ``` 【周扫】【扇扫】 与搜索状态基本一致,仅对目标出视场处理有所区别。 【跟踪】 跟踪一般是接收到锁定、引导或自动切换进入,进入跟踪状态后,将在如下结构中体现 ```c++ //*****目标跟踪*****(长时航迹点,第0个为主目标送伺服跟踪) int nTrackObjCnts; //跟踪目标个数 ARIDLL_OBJINFO stTrackers[LT_OBJ_NUM]; //跟踪器输出数组 ``` 【待命】 一般不执行算法 【丢失】 跟踪状态下的子状态,表明跟踪器暂时无法发现目标,但没有放弃目标跟踪,仍保留了目标批号,此时需要将该状态透传给伺服控制,用于执行记忆跟踪。 一旦重捕目标成功,将从丢失重新回到跟踪状态。 ### 4.3 场景模式 **GLB_SCEN_MODE** 指对空、对地、对海、实验室等场景的跟踪算法,由于场景不同,算法很难用相同的策略进行跟踪,因此为了保持良好的跟踪能力,在不同的场景下算法区分了实现,需要外部根据需要进行切换。通常项目中都允许进行手动切换或模式保持某种场景模式。 ### 5 版本查询 NeoTacker的版本信息填写在不同模块的Version.h.in 文件中。包含构建时间和版本号。 ```C++ #pragma once #include std::string BUILD_TIME = "BUILD_TIME @build_time@"; std::string VERSION = "BUILD_VERSION 0.9.0"; ``` 其中版本号手动填写更新,编译时间自动生成。利用Cmake进行构造时,会自动将Version.h.in文件转为Version.h,其中会将编译时间更换为当前时间。 编译完成后,通过如下指令可获取版本信息和编译时间。 ![alt text](doc/images/imageVersion.png) ## 6.API 说明 ### 6.1 构造 ```C++ STD_TRACKER_API ArithHandle STD_CreatEOArithHandle(); ``` ArithHandle 类型为void*,表示算法实例的指针,需要主动调用构造与析构,以此完成算法实例的生成与资源释放。 ### 6.2 初始化 ```C++ STD_TRACKER_API void ARIDLL_EOArithInit(ArithHandle hArith,int nWidth, int nHeight, GD_PIXEL_FORMAT_E nPixelType); STD_TRACKER_API void ARIDLL_EOArithInitWithMode(ArithHandle hArith, int nWidth, int nHeight, GD_PIXEL_FORMAT_E nPixelType, GLB_SYS_MODE nSysMode,GLB_SCEN_MODE nScenMode); ``` 在调用后续接口前,需要完成初始化,否则后续接口将不能正常工作。可以调用默认初始化以及指定工作模式的初始化。 ### 6.3 检测接口 ```C++ STD_TRACKER_API TARGET_OBJECT* ARIDLL_SearchFrameTargets(ArithHandle hArithSrc, GD_VIDEO_FRAME_S img, int* nFrmTargetNum); ``` 调用接口获取当前帧图像的传统目标检测队列,返回目标个数,以及目标队列。 传统检测算法与AI识别并行运行完成后,调用者在 此处调用下面的接口可以合并二者输出。【可选】 ```C++ STD_TRACKER_API int ARIDLL_MergeAITargets(ArithHandle hArithSrc, TARGET_OBJECT* pTargetArray,int num,obj_res* aiDetectArray,int aiNum); ``` 合并后将AI识别与传统目标检测放在统一的数据结构中送入跟踪模块,这里主要是处理字段的拷贝与溢出异常。 由于检测器通常与跟踪器并行,因此可能存在同时读写冲突,算法内部无锁,需调用者自行处理。 ### 6.4 主接口 ```C++ STD_TRACKER_API int ARIDLL_RunController(ArithHandle hArithSrc, GD_VIDEO_FRAME_S img, ARIDLL_INPUTPARA stInputPara, ARIDLL_OUTPUT* pstOutput); ``` ```C++ #ifndef _ARIDLL_INPUTPARA_ #define _ARIDLL_INPUTPARA_ //输入【系统参数】结构体 typedef struct tagARIDLL_INPUTPARA { int nTimeStamp; //当前帧采集时刻时间戳,单位毫秒 int unFrmId; //当前帧图像帧编号 ServoInfo stServoInfo; //传感器伺服信息 CamInfo stCameraInfo; //相机信息 AirCraftInfo stAirCraftInfo; //载体信息 GuideInfo stGuideInfo; //外部引导信息 // 外部目标列表 int nInputTargetNum; TARGET_OBJECT stInputTarget[INPUT_OBJ_NUM]; // 外部输入目标列表,包含AI识别结果 // AI跟踪器结果 AIT_OUTPUT stAITrackerInfo; }ARIDLL_INPUTPARA; #endif ``` 输入参数基本不需要介绍。 ```C++ #ifndef _ARIDLL_OUTPUT_ #define _ARIDLL_OUTPUT_ //跟踪目标输出结构体 typedef struct tagARIDLL_OUTPUT { int nTimeStamp;//当前帧时间戳(透传),单位:毫秒 // 系统工作模式(透传)// by wcw04046 @ 2021/12/06 int nSysMode; int nFrmNum;//处理帧计数 //*****工作状态***** int nStatus; //待命/检测/跟踪/丢失状态信息等 //*****目标检测*****(短时航迹点,用于用户指示) int nAlarmObjCnts; //当前帧告警目标总个数 ARIDLL_OBJINFO stAlarmObjs[ST_OBJ_NUM]; //检测目标信息数组 //*****目标跟踪*****(长时航迹点,第0个为主目标送伺服跟踪) int nTrackObjCnts; //跟踪目标个数 ARIDLL_OBJINFO stTrackers[LT_OBJ_NUM]; //跟踪器输出数组 // 以下为调试内容 // 事件处理信息 TRACK_EVENT stEventInfo; // 算法控制指令计算透传(来自API调用) GLB_PCCOMMAND stCommand; // AI跟踪器协同控制指令输出,用于控制端侧NPU程序 AIT_Command stAI_TkCmd; // 自定义输出--------- ARIDLL_DEBUG_OUTPUT stDebugInfo; }ARIDLL_OUTPUT; #endif ``` 注意主接口返回的nSysMode表示的是凝视/扫描/自主工作等系统模式,nStatus表示算法内部的搜索/跟踪/多目标跟踪等状态。 ### 6.5 切换扫描/凝视/等系统模式 ```C++ STD_TRACKER_API void ARIDLL_SetSysMode(ArithHandle hArithSrc, GLB_SYS_MODE nSysMode); ``` 将算法在凝视/扇描/周扫等模式进行切换,算法行为有一定差异。 ### 6.6 对空/对地场景模式切换 ```C++ STD_TRACKER_API void ARIDLL_SetScenMode(ArithHandle hArithSrc, GLB_SCEN_MODE nScenMode); ``` 算法切换为对空/对地/对海等场景模式,切换后原跟踪目标自动解锁,算法自动切入SEARCH状态。 ### 6.7 锁定接口 ```C++ STD_TRACKER_API void ARIDLL_LockCommand(ArithHandle hArithSrc, int nLockX,int nLockY,int nLockW,int nLockH); ``` 调用后算法将从凝视尝试切换为跟踪状态,并输出锁定目标的位置。通过传参宽高可以选择点选或者框选,分别按照不同的项目需求进行锁定逻辑实现。 ```C++ STD_TRACKER_API ARIDLL_OBJINFO ARIDLL_LockTarget(ArithHandle hArithSrc, GD_VIDEO_FRAME_S img, int nLockX, int nLockY, int nLockW, int nLockH); ``` 该接口功能与ARIDLL_LockCommand 一致,区别在于算法在当前帧完成锁定,并将锁定目标传出来。传该目标的原因在锁定波门一般与用户设置或者识别结果相关,传统跟踪器有最全面的参数和用户信息,这样传统锁定结果可以供AI跟踪使用,避免同时向AI跟踪传递重复的识别列表或者自定义波门等信息。 用该接口比上一个快1帧完成锁定,适合目标快速运动场景捕获目标。 ```C++ STD_TRACKER_API void ARIDLL_GuideLockMultiCommand(ArithHandle hArithSrc, TargetGuide* guideList,int num); ``` 外引导转跟踪,可支持多批次的引导目标自动锁定。调用接口后算法等待目标进入视场(自动判断),并按照目标先验自动选择最佳目标进行跟踪。外部需监控算法状态,进入跟踪后即表明已锁定目标。 ### 6.8 解除锁定 ```C++ STD_TRACKER_API void ARIDLL_unLockCommand(ArithHandle hArithSrc); ``` 调用后主接口返回的状态将变为搜索,跟踪器解锁。 ### 6.9 算法参数设置 ```C++ STD_TRACKER_API bool ARIDLL_SetRunTimeParam(ArithHandle hArithSrc, ARIDLL_PARMA config); STD_TRACKER_API bool ARIDLL_GetRunTimeParam(ArithHandle hArithSrc, ARIDLL_PARMA* pConfig); STD_TRACKER_API bool ARIDLL_ReadSetParamFile(ArithHandle hArithSrc, const char* configFilePath); STD_TRACKER_API bool ARIDLL_ReadSetParamStream(ArithHandle hArithSrc,const char* configsstream); ``` 算法参数获取与设置接口,支持json配置文件(文件路径或字符流)下发以及结构体下发。 ### 6.10 运行TLD接口 TLD在算法中通常占用了较长时间,在某些帧频较高的系统中,需要将TLD与主接口并行,提高跟踪频率,这样处理的代价是TLD的结果要在下一帧才能传递给主跟踪,可能引起细微跟踪效果下降。标准基线中本接口是不调用的,算法自行在内部运行了TLD。 一旦在外部调用该接口,需要关闭算法内部的TLD运行,因此调用该接口时需将输入的flag标记置为true以关闭内部调用。 ```C++ STD_TRACKER_API void ARIDLL_RunTLDTracker(ArithHandle hArithSrc,GD_VIDEO_FRAME_S img); ``` ### 6.11 析构 ```C++ STD_TRACKER_API void STD_DeleteEOArithHandle(ArithHandle hArith); ```