2022-12-19 来源:华纳网 责任编辑:王双双 人气:
核心提示:本节,我们使用C++来调用Paddle使用Yolo5进行图像分类经谷雨老师测试,完全可以用于实用的以下是部分核心代码
大家好,欢迎来到谷雨课堂

本节,
我们使用C++来调用Paddle使用Yolo5进行图像分类
经谷雨老师测试,
完全可以用于实用的
以下是部分核心代码
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "Pipeline.h"
#include <algorithm>
#include <map>
#include <utility>

Detector::Detector(const std::string &modelDir, const std::string &labelPath,
                   const int cpuThreadNum, const std::string &cpuPowerMode,
                   int inputWidth, int inputHeight,
                   const std::vector<float> &inputMean,
                   const std::vector<float> &inputStd, float scoreThreshold)
    : inputWidth_(inputWidth), inputHeight_(inputHeight), inputMean_(inputMean),
      inputStd_(inputStd), scoreThreshold_(scoreThreshold) {
  paddle::lite_api::MobileConfig config;
  config.set_model_from_file(modelDir + "/model.nb");

  LOGD("--->model path: %s", modelDir.c_str());

  config.set_threads(cpuThreadNum);
  config.set_power_mode(ParsePowerMode(cpuPowerMode));
  predictor_ =
      paddle::lite_api::CreatePaddlePredictor<paddle::lite_api::MobileConfig>(
          config);
  labelList_ = LoadLabelList(labelPath);
  colorMap_ = GenerateColorMap(labelList_.size());
  channelLength_ = inputWidth_ * inputHeight_;
  isInited_ = false;
}

void Detector::InitParams(const int &width, const int &height) {
  if (isInited_)
    return;

  float r_w = inputWidth_ / (width * 1.0);
  float r_h = inputHeight_ / (height * 1.0);
  if (r_h > r_w) {
    inputW = inputWidth_;
    inputH = r_w * height;
    inputX = 0;
    inputY = (inputHeight_ - inputH) / 2;
    ratio_ = r_w;
  } else {
    inputW = r_h * width;
    inputH = inputHeight_;
    inputX = (inputWidth_ - inputW) / 2;
    inputY = 0;
    ratio_ = r_h;
  }
  isInited_ = true;
}

std::vector<std::string> Detector::LoadLabelList(const std::string &labelPath) {
  std::ifstream file;
  std::vector<std::string> labels;
  file.open(labelPath);
  while (file) {
    std::string line;
    std::getline(file, line);
    labels.push_back(line);
  }
  file.clear();
  file.close();
  return labels;
}

std::vector<cv::Scalar> Detector::GenerateColorMap(int numOfClasses) {
  std::vector<cv::Scalar> colorMap = std::vector<cv::Scalar>(numOfClasses);
  for (int i = 0; i < numOfClasses; i++) {
    int j = 0;
    int label = i;
    int R = 0, G = 0, B = 0;
    while (label) {
      R |= (((label >> 0) & 1) << (7 - j));
      G |= (((label >> 1) & 1) << (7 - j));
      B |= (((label >> 2) & 1) << (7 - j));
      j++;
      label >>= 3;
    }
    colorMap[i] = cv::Scalar(R, G, B);
  }
  return colorMap;
}

void Detector::Preprocess(const cv::Mat &rgbaImage) {
  InitParams(rgbaImage.cols, rgbaImage.rows);

  // Feed the input tensor with the data of the preprocessed image
  auto inputTensor = predictor_->GetInput(0);
  std::vector<int64_t> inputShape = {1, 3, inputHeight_, inputWidth_};
  inputTensor->Resize(inputShape);
  auto inputData = inputTensor->mutable_data<float>();

  cv::Mat img;
  cv::cvtColor(rgbaImage, img, cv::COLOR_BGRA2RGB);
  cv::Mat re(inputH, inputW, CV_8UC3);
  cv::resize(img, re, cv::Size(inputW, inputH));
  cv::Mat out(inputHeight_, inputWidth_, CV_8UC3, cv::Scalar(128, 128, 128));
  re.copyTo(out(cv::Rect(inputX, inputY, re.cols, re.rows)));

  // split channels
  out.convertTo(out, CV_32FC3, 1. / 255.);
  cv::Mat input_channels[3];
  cv::split(out, input_channels);
  for (int j = 0; j < 3; j++) {
    memcpy(inputData + channelLength_ * j, input_channels[j].data,
           channelLength_ * sizeof(float));
  }
}

void Detector::ExtractBoxes(int seq_id, const float *in,
                            std::map<int, std::vector<Object>> *outs,
                            const std::vector<int64_t> &shape) {
  int cls_num = shape[3] - 5;
  int xdim = static_cast<int>(inputWidth_ / strides_[seq_id]);
  for (int c = 0; c < shape[1]; c++) {
    int step = c * shape[2] * shape[3];
    for (int r = 0; r < shape[2]; r++) {
      int offset = step + r * shape[3];
      float score = in[offset + 4];
      if (score < confThresh_)
        continue;

      int max_cls_id = 0;
      float max_cls_val = 0;
      for (int i = 0; i < cls_num; i++) {
        if (in[offset + 5 + i] > max_cls_val) {
          max_cls_val = in[offset + 5 + i];
          max_cls_id = i;
        }
      }

      score *= max_cls_val;
      if (score < confThresh_)
        continue;

      Object obj;
      int y = static_cast<int>(r / xdim);
      int x = static_cast<int>(r % xdim);
      int cx = static_cast<int>(
          ((in[offset] * 2 - 0.5 + x) * strides_[seq_id] - inputX) / ratio_);
      int cy = static_cast<int>(
          ((in[offset + 1] * 2 - 0.5 + y) * strides_[seq_id] - inputY) /
          ratio_);
      int w = static_cast<int>(pow(in[offset + 2] * 2, 2) *
                               anchors_[seq_id][2 * c] / ratio_);
      int h = static_cast<int>(pow(in[offset + 3] * 2, 2) *
                               anchors_[seq_id][2 * c + 1] / ratio_);
      int left = cx - w / 2.0;
      int top = cy - h / 2.0;

      obj.rec = cv::Rect(left, top, w, h);
      obj.prob = score;
      obj.class_id = max_cls_id;

      if (outs->count(obj.class_id) == 0)
        outs->emplace(obj.class_id, std::vector<Object>());
      (*outs)[obj.class_id].emplace_back(obj);
    }
  }
}

static float iou_calc(const cv::Rect &rec_a, const cv::Rect &rec_b) {
  cv::Rect u = rec_a | rec_b;
  cv::Rect s = rec_a & rec_b;
  float s_area = s.area();
  if (s_area < 20)
    return 0.f;
  return u.area() * 1.0 / s_area;
}

static bool cmp(const Object &a, const Object &b) { return a.prob > b.prob; }

void Detector::Nms(const std::map<int, std::vector<Object>> &src,
                   std::vector<Object> *res) {
  for (auto it = src.begin(); it != src.end(); it++) {
    auto dets = it->second;
    std::sort(dets.begin(), dets.end(), cmp);
    for (size_t m = 0; m < dets.size(); ++m) {
      auto &item = dets[m];
      item.class_name = item.class_id >= 0 && item.class_id < labelList_.size()
                            ? labelList_[item.class_id]
                            : "Unknow";
      item.fill_color = item.class_id >= 0 && item.class_id < colorMap_.size()
                            ? colorMap_[item.class_id]
                            : cv::Scalar(0, 0, 0);
      res->push_back(item);
      for (size_t n = m + 1; n < dets.size(); ++n) {
        if (iou_calc(item.rec, dets[n].rec) > nmsThresh_) {
          dets.erase(dets.begin() + n);
          --n;
        }
      }
    }
  }
}

void Detector::Postprocess(std::vector<Object> *results) {
  std::map<int, std::vector<Object>> raw_outputs;
  for (int k = 0; k < 3; k++) {
    std::unique_ptr<const paddle::lite_api::Tensor> output_tensor(
        std::move(predictor_->GetOutput(k)));
    auto *outptr = output_tensor->data<float>();
    auto shape_out = output_tensor->shape();
    ExtractBoxes(k, outptr, &raw_outputs, shape_out);
  }
  Nms(raw_outputs, results);
}

void Detector::Predict(const cv::Mat &rgbaImage, std::vector<Object> *results,
                       double *preprocessTime, double *predictTime,
                       double *postprocessTime) {
  auto t = GetCurrentTime();
  Preprocess(rgbaImage);
  *preprocessTime = GetElapsedTime(t);
  LOGD("Detector postprocess costs %f ms", *preprocessTime);

  t = GetCurrentTime();
  predictor_->Run();
  *predictTime = GetElapsedTime(t);
  LOGD("Detector predict costs %f ms", *predictTime);

  t = GetCurrentTime();
  Postprocess(results);
  *postprocessTime = GetElapsedTime(t);
  LOGD("Detector postprocess costs %f ms", *postprocessTime);
}

Pipeline::Pipeline(const std::string &modelDir, const std::string &labelPath,
                   const int cpuThreadNum, const std::string &cpuPowerMode,
                   int inputWidth, int inputHeight,
                   const std::vector<float> &inputMean,
                   const std::vector<float> &inputStd, float scoreThreshold) {
  detector_.reset(new Detector(modelDir, labelPath, cpuThreadNum, cpuPowerMode,
                               inputWidth, inputHeight, inputMean, inputStd,
                               scoreThreshold));
}

void Pipeline::VisualizeResults(const std::vector<Object> &results,
                                cv::Mat *rgbaImage) {
  int oriw = rgbaImage->cols;
  int orih = rgbaImage->rows;
  for (int i = 0; i < results.size(); i++) {
    Object object = results[i];
    cv::Rect boundingBox = object.rec & cv::Rect(0, 0, oriw - 1, orih - 1);
    // Configure text size
    std::string text = object.class_name + ": ";
    std::string str_prob = std::to_string(object.prob);
    text += str_prob.substr(0, str_prob.find(".") + 4);
    int fontFace = cv::FONT_HERSHEY_PLAIN;https://mp.weixin.qq.com/s?__biz=MzU1MDEyNDk5OQ==&mid=2247494638&idx=1&sn=8a7e0fca04089978f23ccbd21c0f416c&chksm=fba7c746ccd04e5016209d77c0ebf5ac8b530e930edcd45c170d7979c6e70da7f6455f432991&token=94696696&lang=zh_CN#rd
    double fontScale = 1.5f;
    float fontThickness = 1.0f;
    cv::Size textSize =
        cv::getTextSize(text, fontFace, fontScale, fontThickness, nullptr);
    // Draw roi object, text, and background
    cv::rectangle(*rgbaImage, boundingBox, object.fill_color, 2);
    cv::rectangle(*rgbaImage,
                  cv::Point2d(boundingBox.x,
                              boundingBox.y - round(textSize.height * 1.25f)),
                  cv::Point2d(boundingBox.x + boundingBox.width, boundingBox.y),
                  object.fill_color, -1);
    cv::putText(*rgbaImage, text, cv::Point2d(boundingBox.x, boundingBox.y),
                fontFace, fontScale, cv::Scalar(255, 255, 255), fontThickness);
  }
}

void Pipeline::VisualizeStatus(double preprocessTime, double predictTime,
                               double postprocessTime, cv::Mat *rgbaImage) {
  char text[255];
  cv::Scalar fontColor = cv::Scalar(255, 255, 255);
  int fontFace = cv::FONT_HERSHEY_PLAIN;
  double fontScale = 1.f;
  float fontThickness = 1;
  sprintf(text, "Preprocess time: %.1f ms", preprocessTime); // NOLINT
  cv::Size textSize =
      cv::getTextSize(text, fontFace, fontScale, fontThickness, nullptr);
  textSize.height *= 1.25f;
  cv::Point2d offset(10, textSize.height + 15);
  cv::putText(*rgbaImage, text, offset, fontFace, fontScale, fontColor,
              fontThickness);
  sprintf(text, "Predict time: %.1f ms", predictTime); // NOLINT
  offset.y += textSize.height;
  cv::putText(*rgbaImage, text, offset, fontFace, fontScale, fontColor,
              fontThickness);
  sprintf(text, "Postprocess time: %.3f ms", postprocessTime); // NOLINT
  offset.y += textSize.height;
  cv::putText(*rgbaImage, text, offset, fontFace, fontScale, fontColor,
              fontThickness);
}

bool Pipeline::Process(cv::Mat &rgbaImage, std::string savedImagePath) {
  double preprocessTime = 0, predictTime = 0, postprocessTime = 0;

  // Feed the image, run inference and parse the results
  std::vector<Object> results;
  detector_->Predict(rgbaImage, &results, &preprocessTime, &predictTime,
                     &postprocessTime);

  // Visualize the objects to the origin image
  VisualizeResults(results, &rgbaImage);

  // Visualize the status(performance data) to the origin image
  VisualizeStatus(preprocessTime, predictTime, postprocessTime, &rgbaImage);

  // Dump modified image if savedImagePath is set
  if (!savedImagePath.empty()) {
    cv::Mat bgrImage;
    cv::cvtColor(rgbaImage, bgrImage, cv::COLOR_RGBA2BGR);
    imwrite(savedImagePath, bgrImage);
  }

  return true;
}



完整的源代码可以登录【华纳网】下载。
https://www.worldwarner.com/







免责声明:本文仅代表作者个人观点,与华纳网无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。