• yolov5+bytetrack算法在华为NPU上进行端到端开发


            自从毕业后开始进入了华为曻腾生态圈,现在越来越多的公司开始走国产化路线了,现在国内做AI芯片的厂商比如:寒武纪、地平线等,虽然我了解的不多,但是相对于瑞芯微这样的AI开发板来说,华为曻腾的生态比瑞芯微好太多了,参考文档非常多,学习资料也有很多,也容易上手开发。

    华为曻腾官网:昇腾AI应用案例-昇腾社区 (hiascend.com)

            直接步入正题,现在的目标检测已经很成熟了,所以越来越多的公司会用到基于检测的跟踪算法,这样不仅起到了单一检测功能,还有跟踪目标或者计数的功能;

            现在应用较广泛的目标检测算法从最开始的yolov5一直到现在的yolov8,虽然只是简单的看了一下算法的原理,整体来说yolo的更新还是针对神经网络在GPU上的优化加速,而对比曻腾NPU,yolov5的速度还是在其他yolo算法中速度最快的一个;

            目标跟踪算法以前是sort+yolo,deepsort+yolo,bytetrack,fairmot等算法,本章主要介绍如何利用华为的ACL语言+ffmpeg推流进行整个业务的开发流程,大家可以借鉴下面的开发代码,首先你要具备基本的ACL语言知识,以及yolov5的后处理逻辑,跟踪方面直接借鉴开源作者的卡尔曼滤波进行预测更新即可:参考主函数代码如下:

    1. //1.先测试yolov5_nms可以泡桐?
    2. //使用dvpp+aipp编解码再使用opencv进行
    3. #include
    4. #include"acl/acl.h"
    5. #include "opencv2/opencv.hpp"
    6. #include "opencv2/imgproc/types_c.h"
    7. #include "acllite/AclLiteUtils.h"
    8. #include "acllite/AclLiteError.h"
    9. #include "acllite/AclLiteResource.h"
    10. #include "acllite/AclLiteModel.h"
    11. #include "acllite/AclLiteImageProc.h"
    12. #include "AclLiteVideoProc.h"
    13. #include "AclLiteVideoCapBase.h"
    14. #include "BYTETracker.h"
    15. #include
    16. extern"C" {
    17. #include
    18. #include
    19. #include "libavcodec/avcodec.h"
    20. #include "libavformat/avformat.h"
    21. #include "libswscale/swscale.h"
    22. #include "libavutil/imgutils.h"
    23. #include "libavutil/opt.h"
    24. };
    25. using namespace std;
    26. using namespace cv;
    27. typedef struct box {
    28. float x;
    29. float y;
    30. float w;
    31. float h;
    32. float score;
    33. size_t classIndex;
    34. size_t index; // index of output buffer
    35. } box;
    36. namespace{
    37. int a = 0;
    38. }
    39. int main()
    40. {
    41. //1.定义初始化变量dvpp\model\acl\rtsp解码接口cap
    42. AclLiteResource aclDev;
    43. aclrtRunMode g_runMode_;
    44. AclLiteVideoProc* cap_;
    45. AclLiteImageProc g_dvpp_;
    46. AclLiteModel g_model_;
    47. string streamName_;
    48. streamName_ = "rtsp://admin:ascend666@10.1.16.108/LiveMedia/ch1/Media1";
    49. //ffmpeg初始化
    50. AVFormatContext* g_fmtCtx;
    51. AVCodecContext* g_codecCtx;
    52. AVStream* g_avStream;
    53. AVCodec* g_codec;
    54. AVPacket* g_pkt;
    55. AVFrame* g_yuvFrame;
    56. uint8_t* g_yuvBuf;
    57. AVFrame* g_rgbFrame;
    58. uint8_t* g_brgBuf;
    59. int g_yuvSize;
    60. int g_rgbSize;
    61. struct SwsContext* g_imgCtx;
    62. //参数初始化
    63. //rtsp初始化
    64. g_avStream = NULL;
    65. g_codec = NULL;
    66. g_codecCtx = NULL;
    67. g_fmtCtx = NULL;
    68. g_pkt = NULL;
    69. g_imgCtx = NULL;
    70. g_yuvSize = 0;
    71. g_rgbSize = 0;
    72. int picWidth = 416;
    73. int picHeight = 416;
    74. string rtsp_url = "rtsp://192.168.3.38:8554/stream";
    75. int channelId = 0;
    76. string g_outFile = rtsp_url + to_string(channelId);
    77. //rtsp初始化
    78. avformat_network_init();
    79. if (avformat_alloc_output_context2(&g_fmtCtx, NULL, g_avFormat.c_str(), g_outFile.c_str()) < 0) {
    80. ACLLITE_LOG_ERROR("Cannot alloc output file context");
    81. return ACLLITE_ERROR;
    82. }
    83. av_opt_set(g_fmtCtx->priv_data, "rtsp_transport", "tcp", 0);
    84. av_opt_set(g_fmtCtx->priv_data, "tune", "zerolatency", 0);
    85. av_opt_set(g_fmtCtx->priv_data, "preset", "superfast", 0);
    86. //获取编码器的ID返回一个编码器
    87. g_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    88. if (g_codec == NULL) {
    89. ACLLITE_LOG_ERROR("Cannot find any endcoder");
    90. return ACLLITE_ERROR;
    91. }
    92. g_codecCtx = avcodec_alloc_context3(g_codec);
    93. if (g_codecCtx == NULL) {
    94. ACLLITE_LOG_ERROR("Cannot alloc context");
    95. return ACLLITE_ERROR;
    96. }
    97. //创建流
    98. g_avStream = avformat_new_stream(g_fmtCtx, g_codec);
    99. if (g_avStream == NULL) {
    100. ACLLITE_LOG_ERROR("failed create new video stream");
    101. return ACLLITE_ERROR;
    102. }
    103. //设置帧率
    104. g_avStream->time_base = AVRational{1, g_frameRate};
    105. //设置编码参数
    106. AVCodecParameters* param = g_fmtCtx->streams[g_avStream->index]->codecpar;
    107. param->codec_type = AVMEDIA_TYPE_VIDEO;
    108. param->width = picWidth;
    109. param->height = picHeight;
    110. avcodec_parameters_to_context(g_codecCtx, param);
    111. //参数绑定设置
    112. g_codecCtx->pix_fmt = AV_PIX_FMT_NV12;
    113. g_codecCtx->time_base = AVRational{1, g_frameRate};
    114. g_codecCtx->bit_rate = g_bitRate;
    115. g_codecCtx->gop_size = g_gopSize;
    116. g_codecCtx->max_b_frames = 0;
    117. if (g_codecCtx->codec_id == AV_CODEC_ID_H264) {
    118. g_codecCtx->qmin = 10;
    119. g_codecCtx->qmax = 51;
    120. g_codecCtx->qcompress = (float)0.6;
    121. }
    122. if (g_codecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
    123. g_codecCtx->mb_decision = 2;
    124. //初始化code
    125. if (avcodec_open2(g_codecCtx, g_codec, NULL) < 0) {
    126. ACLLITE_LOG_ERROR("Open encoder failed");
    127. return ACLLITE_ERROR;
    128. }
    129. //g_codecCtx参数传递给codecpar
    130. avcodec_parameters_from_context(g_avStream->codecpar, g_codecCtx);
    131. //指定输出数据的形式
    132. av_dump_format(g_fmtCtx, 0, g_outFile.c_str(), 1);
    133. //写文件头
    134. int ret1 = avformat_write_header(g_fmtCtx, NULL);
    135. if (ret1 != AVSTREAM_INIT_IN_WRITE_HEADER) {
    136. ACLLITE_LOG_ERROR("Write file header fail");
    137. return ACLLITE_ERROR;
    138. }
    139. g_pkt = av_packet_alloc();
    140. //传输数据初始化
    141. g_rgbFrame = av_frame_alloc();
    142. g_yuvFrame = av_frame_alloc();
    143. g_rgbFrame->width = g_codecCtx->width;
    144. g_yuvFrame->width = g_codecCtx->width;
    145. g_rgbFrame->height = g_codecCtx->height;
    146. g_yuvFrame->height = g_codecCtx->height;
    147. g_rgbFrame->format = AV_PIX_FMT_BGR24;
    148. g_yuvFrame->format = g_codecCtx->pix_fmt;
    149. g_rgbSize = av_image_get_buffer_size(AV_PIX_FMT_BGR24, g_codecCtx->width, g_codecCtx->height, 1);
    150. g_yuvSize = av_image_get_buffer_size(g_codecCtx->pix_fmt, g_codecCtx->width, g_codecCtx->height, 1);
    151. g_brgBuf = (uint8_t*)av_malloc(g_rgbSize);
    152. g_yuvBuf = (uint8_t*)av_malloc(g_yuvSize);
    153. //内存分配
    154. int ret2 = av_image_fill_arrays(g_rgbFrame->data, g_rgbFrame->linesize,
    155. g_brgBuf, AV_PIX_FMT_BGR24,
    156. g_codecCtx->width, g_codecCtx->height, 1);
    157. ret2 = av_image_fill_arrays(g_yuvFrame->data, g_yuvFrame->linesize,
    158. g_yuvBuf, g_codecCtx->pix_fmt,
    159. g_codecCtx->width, g_codecCtx->height, 1);
    160. g_imgCtx = sws_getContext(
    161. g_codecCtx->width, g_codecCtx->height, AV_PIX_FMT_BGR24,
    162. g_codecCtx->width, g_codecCtx->height, g_codecCtx->pix_fmt,
    163. SWS_BILINEAR, NULL, NULL, NULL);
    164. //2.类变量初始化
    165. AclLiteError ret = aclDev.Init();
    166. if (ret) {
    167. ACLLITE_LOG_ERROR("Init resource failed, error %d", ret);
    168. return ACLLITE_ERROR;
    169. }
    170. if (ACLLITE_OK != OpenVideoCapture()) {
    171. return ACLLITE_ERROR;
    172. }
    173. ret = g_dvpp_.Init();
    174. if (ret) {
    175. ACLLITE_LOG_ERROR("Dvpp init failed, error %d", ret);
    176. return ACLLITE_ERROR;
    177. }
    178. cap_ = nullptr;
    179. ret = g_model_.Init();
    180. if (ret) {
    181. ACLLITE_LOG_ERROR("Model init failed, error %d", ret);
    182. return ACLLITE_ERROR;
    183. }
    184. //3.创建模型img_info的输入以及数据拷贝操作
    185. g_runMode_ = g_aclDev_.GetRunMode();
    186. const float imageInfo[4] = {(float)g_modelInputWidth, (float)g_modelInputHeight,
    187. (float)g_modelInputWidth, (float)g_modelInputHeight};
    188. g_imageInfoSize_ = sizeof(imageInfo);
    189. g_imageInfoBuf_ = CopyDataToDevice((void *)imageInfo, g_imageInfoSize_,
    190. g_runMode_, MEMORY_DEVICE);
    191. if (g_imageInfoBuf_ == nullptr) {
    192. ACLLITE_LOG_ERROR("Copy image info to device failed");
    193. return ACLLITE_ERROR;
    194. }
    195. //4.获取视频源
    196. cap_ = new AclLiteVideoProc(streamName_);
    197. //5.视频流解码以及dvpp硬件-resize
    198. int i =0;
    199. while(true)
    200. {
    201. //6.获取解码图片(在device侧的YUV420图片)(存放在ImageDta结构体中)
    202. // struct ImageData {
    203. // acldvppPixelFormat format;
    204. // uint32_t width = 0;
    205. // uint32_t height = 0;
    206. // uint32_t alignWidth = 0;
    207. // uint32_t alignHeight = 0;
    208. // uint32_t size = 0;
    209. // std::shared_ptr data = nullptr;
    210. // };
    211. i++;
    212. ImageData image;
    213. ret = cap_->Read(image);
    214. ImageData resizedImage;
    215. ret = g_dvpp_.Resize(resizedImage, image, 640, 640);
    216. //7.创建模型输入进行模型推理
    217. ret = g_model_.CreateInput(resizedImage.data.get(), resizedImage.size,
    218. g_imageInfoBuf_, g_imageInfoSize_);
    219. if (ret != ACLLITE_OK) {
    220. ACLLITE_LOG_ERROR("Create mode input dataset failed, error:%d", ret);
    221. return ACLLITE_ERROR;
    222. }
    223. std::vector inferenceOutput;
    224. ret = g_model_.Execute(inferenceOutput);
    225. if (ret != ACLLITE_OK) {
    226. g_model_.DestroyInput();
    227. ACLLITE_LOG_ERROR("Execute model inference failed, error: %d", ret);
    228. return ACLLITE_ERROR;
    229. }
    230. g_model_.DestroyInput();
    231. //8.将YUV图像转换为opencv图像
    232. ImageData yuvImage;
    233. ret = CopyImageToLocal(yuvImage, image, g_runMode_);
    234. if (ret == ACLLITE_ERROR) {
    235. ACLLITE_LOG_ERROR("Copy image to host failed");
    236. return ACLLITE_ERROR;
    237. }
    238. cv::Mat yuvimg(yuvImage.height * 3 / 2, yuvImage.width, CV_8UC1, yuvImage.data.get());
    239. cv::Mat origImage;
    240. cv::cvtColor(yuvimg, origImage, CV_YUV2BGR_NV12);
    241. //模型后处理(根据目标跟踪需要的输入进行获取xywh)
    242. float* detectData = (float *)inferenceOutput[0].data.get();
    243. float* boxNum = (float *)inferenceOutput[1].data.get();
    244. uint32_t totalBox = boxNum[0];
    245. //获取(x,y,w,h)
    246. std::vector obj;
    247. float widthScale = (float)(origImage.cols) / 640.0;
    248. float heightScale = (float)(origImage.rows) / 640.0;
    249. vector detectResults;
    250. for (uint32_t i = 0; i < totalBox; i++) {
    251. box boundBox;
    252. boundBox.score = float(detectData[totalBox * SCORE + i]);
    253. boundBox.x = detectData[totalBox * TOPLEFTX + i] * widthScale;
    254. boundBox.y = detectData[totalBox * TOPLEFTY + i] * heightScale;
    255. boundBox.w = detectData[totalBox * BOTTOMRIGHTX + i] * widthScale;
    256. boundBox.h = detectData[totalBox * BOTTOMRIGHTY + i] * heightScale;
    257. boundBox.classIndex = (uint32_t)detectData[totalBox * LABEL + i];
    258. detectResults.emplace_back(boundBox);
    259. }
    260. for (size_t i = 0; i < detectResults.size(); i++){
    261. if (res[i].classId != class_id){ continue; }
    262. obj[i].label = detectResults[i].classIndex;
    263. obj[i].rect.x = detectResults[i].x;
    264. obj[i].rect.y = detectResults[i].y;
    265. obj[i].rect.height = detectResults[i].h;
    266. obj[i].rect.width = detectResults[i].w;
    267. obj[i].prob = detectResults[i].score;
    268. }
    269. std::vector output_stracks = tracker.update(obj);
    270. for (size_t i = 0; i < output_stracks.size(); i++){
    271. std::vector<float> tlwh = output_stracks[i].tlwh;
    272. cv::Scalar __color = tracker.get_color(output_stracks[i].track_id);
    273. cv::putText(origImage, std::to_string(output_stracks[i].track_id), cv::Point(tlwh[0], tlwh[1] - 10), cv::FONT_ITALIC, 0.75, __color, 2);
    274. cv::rectangle(origImage, cv::Rect(tlwh[0], tlwh[1], tlwh[2], tlwh[3]), __color, 2);
    275. }
    276. //跟踪完成后写推流
    277. memcpy(g_brgBuf, origImage.data, g_rgbSize);
    278. sws_scale(g_imgCtx,
    279. g_rgbFrame->data,
    280. g_rgbFrame->linesize,
    281. 0,
    282. g_codecCtx->height,
    283. g_yuvFrame->data,
    284. g_yuvFrame->linesize);
    285. g_yuvFrame->pts = i;
    286. if (avcodec_send_frame(g_codecCtx, g_yuvFrame) >= 0) {
    287. // cout<
    288. while (avcodec_receive_packet(g_codecCtx, g_pkt) >= 0) {
    289. cout<<"avcodec_receive_packet"<
    290. g_pkt->stream_index = g_avStream->index;
    291. av_packet_rescale_ts(g_pkt, g_codecCtx->time_base, g_avStream->time_base);
    292. g_pkt->pos = -1;
    293. int ret = av_interleaved_write_frame(g_fmtCtx, g_pkt);
    294. if (ret < 0) {
    295. ACLLITE_LOG_ERROR("error is: %d", ret);
    296. }
    297. }
    298. }
    299. }
    300. av_packet_free(&g_pkt);
    301. avcodec_close(g_codecCtx);
    302. if (g_fmtCtx) {
    303. avio_close(g_fmtCtx->pb);
    304. avformat_free_context(g_fmtCtx);
    305. }
    306. if (cap_ != nullptr) {
    307. cout << "cap is not open" << endl;
    308. cap_->Close();
    309. delete cap_;
    310. }
    311. dvpp_.DestroyResource();
    312. return 0;
    313. }
    314. 跟踪器方面的函数,可以搜索开源代码yolov5-bytetrack-main.cpp截取内部跟踪部分,检测部分使用华为ACL编写的推理代码进行检测;

      可以加入学习讨论:1076799627

    315. 相关阅读:
      java 企业工程管理系统软件源码+Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis
      C++ Reference: Standard C++ Library reference: C Library: cstring: memchr
      CTFshow-PWN-栈溢出(pwn43)
      ABAP 调用HTTP上传附件
      Github每日精选(第36期):find简单、快速和用户友好的替代工具fd
      Excel快速定位sheet
      WMI Provider Host占用CPU过高
      react生命周期新旧对比
      国泰君安期货:基于分布式架构的智能推送系统,满足单日亿级消息处理量
      Leetcode—189.轮转数组【中等】
    316. 原文地址:https://blog.csdn.net/qq_52859223/article/details/133675505