<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          項(xiàng)目實(shí)踐 | 從零開始邊緣部署輕量化人臉檢測(cè)模型——EAIDK310部署篇

          共 18981字,需瀏覽 38分鐘

           ·

          2021-06-01 07:02


          繼續(xù)上一章的話題,前面我們主要聊到關(guān)于人臉檢測(cè)模型UltraFace的訓(xùn)練任務(wù),本文將和大家討論在開發(fā)板上如何部署UltraFace模型,并進(jìn)行實(shí)時(shí)視頻人臉檢測(cè),或者圖片流人臉檢測(cè)。

          1Tengine簡(jiǎn)介

          Tengine 由 OPEN AI LAB 主導(dǎo)開發(fā),該項(xiàng)目實(shí)現(xiàn)了深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)模型在嵌入式設(shè)備上的快速、高效部署需求。為實(shí)現(xiàn)在眾多 AIoT 應(yīng)用中的跨平臺(tái)部署,本項(xiàng)目基于原有 Tengine 項(xiàng)目使用 C 語言進(jìn)行重構(gòu),針對(duì)嵌入式設(shè)備資源有限的特點(diǎn)進(jìn)行了深度框架裁剪。同時(shí)采用了完全分離的前后端設(shè)計(jì),有利于 CPU、GPU、NPU 等異構(gòu)計(jì)算單元的快速移植和部署,同時(shí)降低評(píng)估和遷移成本。

          Tengine推理流程

          依照順序調(diào)用Tengine核心API如下:

          2模塊實(shí)現(xiàn)

          1 模型轉(zhuǎn)換

          第1步:轉(zhuǎn)換到onnx模型

          model_path = "models/pretrained/version-RFB-320.pth"
          net = create_Mb_Tiny_RFB_fd(len(class_names), is_test=True)
          net.load(model_path)
          net.eval()
          net.to("cuda")

          model_name = model_path.split("/")[-1].split(".")[0]
          model_path = f"models/onnx/{model_name}.onnx"

          dummy_input = torch.randn(13240320).to("cuda")
          torch.onnx.export(net, dummy_input, model_path, verbose=False, input_names=['input'], output_names=['scores''boxes'])

          第2步:編譯Tengine模型轉(zhuǎn)換工具

          依賴庫(kù)安裝

          sudo apt install libprotobuf-dev protobuf-compiler

          源碼編譯

          mkdir build && cd build
          cmake ..
          make -j`nproc` && make install

          編譯完成后,生成的可行性文件tm_convert_tool存放在 ./build/install/bin/ 目錄下。

          第3步:轉(zhuǎn)換onnx模型為tmfile模型

          ./tm_convert_tool -m xxx.onnx -o xxx.tmfile
          • -m 為*.caffemodel, *.params, *.weight, *.pb, *.onnx, *.tflite等模型;
          • -o 為output fp32 tmfile

          2 NMS計(jì)算

          偽代碼:

          • 1 將各組box按照score降序排列;

          • 2 從score最大值開始,置為當(dāng)前box,保存idex,然后依次遍歷后面的box,計(jì)算與當(dāng)前box的IOU值,若大于閾值,則抑制,不會(huì)輸出;

          • 3 完成一輪遍歷后,繼續(xù)選擇下一個(gè)非抑制的box作為當(dāng)前box,重復(fù)步驟2;

          • 4 返回沒有被抑制的index即符合條件的box;

          python版本

          def NMS(dects,threshhold):
              """
              detcs:二維數(shù)組(n_samples,5)
              5列:x1,y1,x2,y2,score
              threshhold: IOU閾值
              """

              x1=dects[:,0]
              y1=dects[:,1]
              x2=dects[:,2]
              y2=dects[:,3]
              score=dects[:,4]
              ndects=dects.shape[0]#box的數(shù)量
              area=(x2-x1+1)*(y2-y1+1)
              order=score.argsort()[::-1#score從大到小排列的indexs,一維數(shù)組
              keep=[] #保存符合條件的index
              suppressed=np.array([0]*ndects) #初始化為0,若大于threshhold,變?yōu)?,表示被抑制
              
              for _i in range(ndects):
                  i=order[_i]  #從得分最高的開始遍歷
                  if suppressed[i]==1:
                      continue
                  keep.append(i) 
                  for _j in range(i+1,ndects):
                      j=order[_j]
                      if suppressed[j]==1#若已經(jīng)被抑制,跳過
                          continue
                      xx1=np.max(x1[i],x1[j])#求兩個(gè)box的交集面積interface
                      yy1=np.max(y1[i],y1j])
                      xx2=np.min(x2[i],x2[j])
                      yy2=np.min(y2[i],y2[j])
                      w=np.max(0,xx2-xx1+1)
                      h=np.max(0,yy2-yy1+1)
                      interface=w*h
                      overlap=interface/(area[i]+area[j]-interface) #計(jì)算IOU(交/并)
                      
                      if overlap>=threshhold:#IOU若大于閾值,則抑制
                          suppressed[j]=1
              return keep

          C++版本

          void UltraFace::nms(std::vector<FaceInfo> &input, std::vector<FaceInfo> &output, int type) {
              //根據(jù)score對(duì)候選框進(jìn)行 sort 排序操作
              std::sort(input.begin(), input.end(), [](const FaceInfo &a, const FaceInfo &b) { return a.score > b.score; });

              int box_num = input.size();

              std::vector<intmerged(box_num, 0);

              for (int i = 0; i < box_num; i++) {
                  if (merged[i])
                      continue;
                  std::vector<FaceInfo> buf;

                  buf.push_back(input[i]);
                  merged[i] = 1;

                  float h0 = input[i].y2 - input[i].y1 + 1;
                  float w0 = input[i].x2 - input[i].x1 + 1;

                  float area0 = h0 * w0;

                  for (int j = i + 1; j < box_num; j++) {
                      if (merged[j])
                          continue;
                      //確立每個(gè)候選框的坐標(biāo)以及寬高
                      float inner_x0 = input[i].x1 > input[j].x1 ? input[i].x1 : input[j].x1;
                      float inner_y0 = input[i].y1 > input[j].y1 ? input[i].y1 : input[j].y1;

                      float inner_x1 = input[i].x2 < input[j].x2 ? input[i].x2 : input[j].x2;
                      float inner_y1 = input[i].y2 < input[j].y2 ? input[i].y2 : input[j].y2;

                      float inner_h = inner_y1 - inner_y0 + 1;
                      float inner_w = inner_x1 - inner_x0 + 1;

                      if (inner_h <= 0 || inner_w <= 0)
                          continue;

                      float inner_area = inner_h * inner_w;

                      float h1 = input[j].y2 - input[j].y1 + 1;
                      float w1 = input[j].x2 - input[j].x1 + 1;

                      float area1 = h1 * w1;

                      float score;
                      //計(jì)算IOU
                      score = inner_area / (area0 + area1 - inner_area);
                      //根據(jù)閾值進(jìn)行極大值抑制的篩選
                      if (score > iou_threshold) {
                          merged[j] = 1;
                          buf.push_back(input[j]);
                      }
                  }
              }
          }

          2.3 獲取候選框

          //獲取候選框
          void UltraFace::generateBBox(std::vector<FaceInfo> &bbox_collection, tensor_t scores, tensor_t boxes) {
              float* scores_blob = ( float* )get_tensor_buffer(scores);
              float* boxes_blob = ( float* )get_tensor_buffer(boxes);
              for (int i = 0; i < num_anchors; i++) {
                  if (scores_blob[i * 2 + 1] > score_threshold) {
                      FaceInfo rects;
                      //確定坐標(biāo)中心以及box的寬高
                      float x_center = boxes_blob[i * 4] * center_variance * priors[i][2] + priors[i][0];
                      float y_center = boxes_blob[i * 4 + 1] * center_variance * priors[i][3] + priors[i][1];
                      float w = exp(boxes_blob[i * 4 + 2] * size_variance) * priors[i][2];
                      float h = exp(boxes_blob[i * 4 + 3] * size_variance) * priors[i][3];
                      //截取坐標(biāo)結(jié)果
                      rects.x1 = clip(x_center - w / 2.01) * image_w;
                      rects.y1 = clip(y_center - h / 2.01) * image_h;
                      rects.x2 = clip(x_center + w / 2.01) * image_w;
                      rects.y2 = clip(y_center + h / 2.01) * image_h;
                      rects.score = clip(scores_blob[i * 2 + 1], 1);
                      bbox_collection.push_back(rects);
                  }
              }
          }

          2.4 模型檢測(cè)函數(shù)

          //模型檢測(cè)函數(shù)
          int UltraFace::detect(cv::Mat &raw_image, std::vector<FaceInfo> &face_list) {
              if (raw_image.empty()) {
                  std::cout << "image is empty ,please check!" << std::endl;
                  return -1;
              }

              image_h = raw_image.rows;
              image_w = raw_image.cols;

              int img_size      = in_w * in_h * 3;
              float* input_data = ( float* )malloc(img_size * sizeof(float));
              // 獲取來自opencv讀取的圖片或者視頻數(shù)據(jù),并返回一個(gè)適應(yīng)模型輸入的結(jié)果
              get_input_data_cv(raw_image, input_data, in_w, in_h, mean_vals, norm_vals, 0);

              if (set_tensor_buffer(input_tensor, input_data, (in_w * in_h * 3) * 4) < 0)
              {
                  printf("Set input tensor buffer failed\n");
                  return -1;
              }
              //開始計(jì)時(shí)?
              auto start = chrono::steady_clock::now();


              // 6、Run網(wǎng)絡(luò)
              if (run_graph(graph, 1) < 0)
              {
                  printf("Run graph failed\n");
                  return -1;
              }

              // 獲取輸出結(jié)果
              string scores = "scores";
              string boxes = "boxes";

              //7.1、獲取分類得分結(jié)果
              tensor_t tensor_scores = get_graph_tensor(graph, scores.c_str());
              //7.2、獲取檢測(cè)框坐標(biāo)結(jié)果
              tensor_t tensor_boxes = get_graph_tensor(graph, boxes.c_str());

              std::vector<FaceInfo> bbox_collection;

              //結(jié)束計(jì)時(shí),然后計(jì)算推理時(shí)間
              auto end = chrono::steady_clock::now();
              chrono::duration<double> elapsed = end - start;
              cout << "inference time:" << elapsed.count() << " s" << endl;
              //后處理操作,主要是獲取BBox以及NMS操作
              generateBBox(bbox_collection, tensor_scores, tensor_boxes);
              nms(bbox_collection, face_list);

              free(input_data);

              return 0;
          }

          2.5 主函數(shù)

          #include "UltraFace.hpp"
          #include <iostream>
          #include <opencv2/opencv.hpp>
          #include <opencv2/highgui.hpp>
          #include <opencv2/imgproc.hpp>

          using namespace std;

          int main() {
              string tengine_path = "/home/chaucer/Tengine_Tutorial/2_FaceDetector/models/version-RFB-320_simplified.tmfile";
              UltraFace ultraface(tengine_path, 32024040.65)// config model input

              cv::Mat frame;
              //cv::VideoCapture capture(0);
              cv::VideoCapture capture("/home/chaucer/face_detect/test_1.mp4");

              //cv::Mat frame = cv::imread(image_file);
              while(1)
              {
                  capture >> frame;
                  auto start = chrono::steady_clock::now();
                  vector<FaceInfo> face_info;
                  ultraface.detect(frame, face_info);

                  cout << "face_info " << face_info.size() << endl;

                  for (auto face : face_info) {
                      cv::Point pt1(face.x1, face.y1);
                      cv::Point pt2(face.x2, face.y2);
                      cv::rectangle(frame, pt1, pt2, cv::Scalar(02550), 2);
                  }

                  auto end = chrono::steady_clock::now();
                  chrono::duration<double> elapsed = end - start;
                  cout << "all time: " << elapsed.count() << " s" << endl;
                  cv::imshow("UltraFace", frame);
                  cv::waitKey(1);
                  string result_name = "result" + to_string(2) + ".jpg";
                  cv::imwrite(result_name, frame);
              }
              return 0;
          }

          3輸出結(jié)果

          3.1 圖片檢測(cè)結(jié)果

          3.2 視頻檢測(cè)結(jié)果

          4參考

          [1].https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB
          [2].https://github.com/OAID/Tengine
          [3].https://github.com/jiangzhongbo/Tengine_Tutorial

          5推薦閱讀

          Google新作 | 詳細(xì)解讀 Transformer那些有趣的特性(建議全文背誦)


          極品Trick | 在ResNet與Transformer均適用的Skip Connection解讀


          Transformer又一城 | Swin-Unet:首個(gè)純Transformer的醫(yī)學(xué)圖像分割模型解讀


          輕量化卷積:TBC,不僅僅是參數(shù)共享組卷積,更具備跨通道建模


          最快ViT | FaceBook提出LeViT,0.077ms的單圖處理速度卻擁有ResNet50的精度(文末附論文與源碼)

          本文論文原文獲取方式,掃描下方二維碼

          回復(fù)【UltraFaceC】即可獲取項(xiàng)目代碼

          長(zhǎng)按掃描下方二維碼添加小助手并加入交流群,群里博士大佬云集,每日討論話題有目標(biāo)檢測(cè)、語義分割、超分辨率、模型部署、數(shù)學(xué)基礎(chǔ)知識(shí)、算法面試題分享的等等內(nèi)容,當(dāng)然也少不了搬磚人的扯犢子

          長(zhǎng)按掃描下方二維碼添加小助手。

          可以一起討論遇到的問題

          聲明:轉(zhuǎn)載請(qǐng)說明出處

          掃描下方二維碼關(guān)注【集智書童】公眾號(hào),獲取更多實(shí)踐項(xiàng)目源碼和論文解讀,非常期待你我的相遇,讓我們以夢(mèng)為馬,砥礪前行!

          瀏覽 115
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕免费视频在线观看 | 亚拍欧美| 在线看一区 | 一级片国产 | 美日韩一区二区三区 |