Newer
Older
lynxi-plugin / src / osd / carPlatePostProcess.cpp
/**
 *@file carPlatePostProcess.cpp
 *@author lynxi
 *@version v1.0
 *@date 2023-03-07
 *@par Copyright:
 *© 2022 北京灵汐科技有限公司 版权所有。
 * 注意:以下内容均为北京灵汐科技有限公司原创,未经本公司允许,不得转载,否则将视为侵权;对于不遵守此声明或者其他违法使用以下内容者,本公司依法保留追究权。\n
 *© 2022 Lynxi Technologies Co., Ltd. All rights reserved.
 * NOTICE: All information contained here is, and remains the property of Lynxi.
 *This file can not be copied or distributed without the permission of Lynxi
 *Technologies Co., Ltd.
 *@brief 车牌检测后处理
 */

#include <sys/time.h>
#include <algorithm>
#include <cstring>
#include <cmath>
#include "carPlatePostProcess.h"
#include "detect.h"
#include "drawTool.h"
#include "lyn_plugin_dev.h"
#include "osdPlugin.h"
namespace COMMON {

int16_t expBitNum = 5;
int16_t baseBitNum = 15 - expBitNum;
int maxExp = 32;     // pow(2, expBitNum);
int maxBase = 1024;  // pow(2, baseBitNum);
int biasExp = 15;    // maxExp / 2 - 1;
int sig[2] = {1, -1};
float result = 5.96046e-08;

float half2float(int16_t ib) {
    int16_t s, e, m;
    s = (ib >> 15) & 0x1;
    e = (ib >> 10) & 0x1f;
    m = ib & 0x3ff;

    // added by puyang.wang@lynxi.com
    {
        if (0 == e)
            return sig[s] * m * result;
        else {
            union {
                unsigned int u32;
                float f32;
            } ou;

            e = (0x1f == e) ? 0xff : (e - 15 + 127);
            ou.u32 = (s << 31) | (e << 23) | (m << 13);
            return ou.f32;
        }
    }
}

int16_t float2half(float value) {
    int16_t ob;
    //            uint16 s,e,m;
    int16_t s, m;
    int e;  // modified by puyang.wang@lynxi。修正接近于0的值不能正确转换的bug。
    // int16_t expBitNum = 5;
    // int16_t baseBitNum = 15 - expBitNum;
    int maxExp = 32;     // pow(2,expBitNum);
    int maxBase = 1024;  // pow(2,baseBitNum);
    int biasExp = 15;    // maxExp/2 - 1;
    s = value < 0;
    double thd2;
    // thd1 = (maxBase-1)*1.0/maxBase * pow(2,(1 - biasExp));
    thd2 = 1.0 / maxBase * pow(2, (1 - biasExp));
    double x;
    bool inf_flag = 0;
    x = s ? -value : value;
    x = (x > 65504) ? 65504 : x;
    int16_t indA;
    indA = x < thd2 / 2;
    // indB = x > thd2/2;
    if (indA) {
        e = 0;
        m = 0;
        s = 0;
    }
    // if (indB)
    else  // float为Nan转为half
    {
        union {
            float xl;
            unsigned int u32;
        } ou;
        ou.xl = log2(x);

        if (((ou.u32 >> 23) & 0xff) == 0xff)  // float为inf转为half
        {
            e = maxExp - 1;
            if ((ou.u32 & 0x7fffff) == 0)
                inf_flag = 1;
            else {
                inf_flag = 0;
                s = (ou.u32 >> 31);
            }
        } else {
            e = biasExp + floor(ou.xl);
        }

        if (e > (maxExp - 1))
            printf("[double2uint16]Error: out of e range\n");
    }
    int16_t ind1, ind2;
    ind1 = e <= 0;
    ind2 = e > 0;
    if (ind1) {
        e = 0;
        m = round(x * pow(2, (biasExp - 1)) * maxBase);
    }
    if (ind2) {
        if (31 == e) {
            if (inf_flag)
                m = 0;
            else
                m = 1;
        } else {
            double xr;
            xr = x / pow(2, (e - biasExp)) - 1;
            m = round(xr * maxBase);
        }
    }

    ob = (s & 0x1) << 15 | (((e & 0x1f) << 10) + m);
    return ob;
}

}   // namespace COMMON
namespace CarPlate {
template <class ForwardIterator>
inline size_t argmin(ForwardIterator first, ForwardIterator last) {
  return std::distance(first, std::min_element(first, last));
}

template <class ForwardIterator>
inline size_t argmax(ForwardIterator first, ForwardIterator last) {
  return std::distance(first, std::max_element(first, last));
}

typedef struct Bbox {
  float xmin;
  float ymin;
  float xmax;
  float ymax;

  Bbox() {}

  Bbox(float xmin, float ymin, float xmax, float ymax)
      : xmin(xmin), ymin(ymin), xmax(xmax), ymax(ymax) {}

  ~Bbox() {}
} Bbox;

typedef struct Detection {
  int id;
  float score;
  Bbox bbox;
  const char *class_name;
  Detection() {}

  Detection(int id, float score, Bbox bbox)
      : id(id), score(score), bbox(bbox), class_name("") {}

  Detection(int id, float score, Bbox bbox, const char *class_name)
      : id(id), score(score), bbox(bbox), class_name(class_name) {}

  friend bool operator>(const Detection &lhs, const Detection &rhs) {
    return (lhs.score > rhs.score);
  }

  ~Detection() {}
} Detection;
} // namespace CarPlate

using namespace COMMON;

using namespace CarPlate;

static void yolo5Nms(std::vector<Detection> &input, float iou_threshold,
                     int top_k, std::vector<Detection> &result, bool suppress) {
  std::stable_sort(input.begin(), input.end(), std::greater<Detection>());

  std::vector<bool> skip(input.size(), false);

  std::vector<float> areas;
  areas.reserve(input.size());
  for (size_t i = 0; i < input.size(); i++) {
    float width = input[i].bbox.xmax - input[i].bbox.xmin;
    float height = input[i].bbox.ymax - input[i].bbox.ymin;
    areas.push_back(width * height);
  }

  int count = 0;
  for (size_t i = 0; /*count < top_k && */ i < skip.size(); i++) {
    if (skip[i]) {
      continue;
    }
    skip[i] = true;
    ++count;

    for (size_t j = i + 1; j < skip.size(); ++j) {
      if (skip[j]) {
        continue;
      }
      if (suppress == false) {
        if (input[i].id != input[j].id) {
          continue;
        }
      }

      float xx1 = std::max(input[i].bbox.xmin, input[j].bbox.xmin);
      float yy1 = std::max(input[i].bbox.ymin, input[j].bbox.ymin);
      float xx2 = std::min(input[i].bbox.xmax, input[j].bbox.xmax);
      float yy2 = std::min(input[i].bbox.ymax, input[j].bbox.ymax);

      if (xx2 > xx1 && yy2 > yy1) {
        float area_intersection = (xx2 - xx1) * (yy2 - yy1);
        float iou_ratio =
            area_intersection / (areas[j] + areas[i] - area_intersection);
        if (iou_ratio > iou_threshold) {
          skip[j] = true;
        }
      }
    }
    result.push_back(input[i]);
  }
}

static void tensorpostProcess(const Yolov5sConfig *cfg, void *tensor,
                              YoloPostProcessInfo_t *post_info, int layer,
                              int *filter, int filterNum,
                              std::vector<Detection> &dets) {
  // auto *data = reinterpret_cast<uint16_t *>(tensor);
  void *data = tensor;
  int class_num = cfg->class_num;
  int stride = cfg->strides[layer];
  int num_pred = cfg->class_num + 4 + 1;

  std::vector<int16_t> class_pred(cfg->class_num, 0);

  const std::vector<std::pair<double, double>> &anchors =
      cfg->anchors_table[layer];

  double h_ratio = post_info->height * 1.0 / post_info->ori_height;
  double w_ratio = post_info->width * 1.0 / post_info->ori_width;
  double resize_ratio = std::min(w_ratio, h_ratio);
  if (post_info->is_pad_resize) {
    w_ratio = resize_ratio;
    h_ratio = resize_ratio;
  }

  int grid_height, grid_width;
  grid_height = post_info->height / stride;
  grid_width = post_info->width / stride;

  int16_t box_score_threshold = float2half(post_info->score_threshold);
  for (int h = 0; h < grid_height; h++) {
    for (int w = 0; w < grid_width; w++) {
      for (size_t k = 0; k < anchors.size(); k++) {
        int16_t *cur_data = (int16_t *)data + k * num_pred;

        int16_t objness = cur_data[4];
        if (objness < box_score_threshold /*post_info->score_threshold*/) {
          continue;
        }

#if 0
int16_t id = cur_data[5];
double confidence = half2float(objness) * half2float(cur_data[6]);
#else
        for (int index = 0; index < class_num; ++index) {
          class_pred[index] = (cur_data[5 + index]);
        }

        int16_t id = argmax(class_pred.begin(), class_pred.end());
        if (filterNum > 0) {
          int i = 0;
          for (; i < filterNum; i++) {
            if (id == *(filter + i)) {
              break;
            }
          }
          if (i == filterNum) {
            continue;
          }
        }

        double confidence = half2float(objness) * half2float(class_pred[id]);
#endif

        if (confidence < post_info->score_threshold) {
          continue;
        }

#if 1
        float center_x = half2float(cur_data[0]);
        float center_y = half2float(cur_data[1]);
        float scale_x = half2float(cur_data[2]);
        float scale_y = half2float(cur_data[3]);
#else
        float center_x = cur_data[0] * post_info->width;
        float center_y = cur_data[1] * post_info->height;
        float scale_x = cur_data[2] * post_info->width;
        float scale_y = cur_data[3] * post_info->height;
#endif

        double xmin = (center_x - scale_x / 2.0);
        double ymin = (center_y - scale_y / 2.0);
        double xmax = (center_x + scale_x / 2.0);
        double ymax = (center_y + scale_y / 2.0);
        double w_padding =
            (post_info->width - w_ratio * post_info->ori_width) / 2.0;
        double h_padding =
            (post_info->height - h_ratio * post_info->ori_height) / 2.0;

        double xmin_org = (xmin - w_padding) / w_ratio;
        double xmax_org = (xmax - w_padding) / w_ratio;
        double ymin_org = (ymin - h_padding) / h_ratio;
        double ymax_org = (ymax - h_padding) / h_ratio;

        if (xmax_org <= 0 || ymax_org <= 0) {
          continue;
        }

        if (xmin_org > xmax_org || ymin_org > ymax_org) {
          continue;
        }

        xmin_org = std::max(xmin_org, 0.0);
        xmax_org = std::min(xmax_org, post_info->ori_width - 1.0);
        ymin_org = std::max(ymin_org, 0.0);
        ymax_org = std::min(ymax_org, post_info->ori_height - 1.0);

        Bbox bbox(xmin_org, ymin_org, xmax_org, ymax_org);
        dets.push_back(Detection((int)id, confidence, bbox,
                                 cfg->class_names[(int)id].c_str()));
      }
      data = (int16_t *)data + num_pred * anchors.size();
    }
  }
}


void num_extractor_postprocess(void* pred, std::vector<int> &indexs,
                               float *confidence) {
  int16_t max = 0;
  int16_t max_idx = 0;
  float conf = 0.0f;
  int valid_count = 0;

  for (int h = 0; h < 20; h++) { // 20
    auto i = (int16_t*)pred + h * 84;
    for (int c = 0; c < 84; c++) { // 84
      int16_t current = i[c];
      if (current > max) {
        max = current;
        max_idx = c;
      }
    }

    indexs[h] = max_idx;
    if (max_idx < 83) {
      valid_count++;
      conf += half2float(max);
    }

    max = 0;
    max_idx = 0;
  }
  *confidence = conf / valid_count;
}

int carPlatePostProcess(YoloPostProcessInfo_t *post_info,
                                    int *filter, int filterNum) {
  auto output_tensor_virt_addr = lynPluginGetVirtAddr(post_info->output_tensor);
  if (output_tensor_virt_addr == nullptr) {
      LOG_PLUGIN_E("get virtual addr of output_tensor_virt_addr error\n");
      return -1;
  }            
  lynBoxesInfo * boxesInfo = (lynBoxesInfo *)lynPluginGetVirtAddr(post_info->post_process_result);
  if (boxesInfo == nullptr) {
      LOG_PLUGIN_E("get virtual addr of boxesInfo error\n");
      return -2;
  }
  auto cfg = &carplate_config;                        
  void *tensor0 = NULL;
  tensor0 = output_tensor_virt_addr;
  void *tensor1 = NULL;
  int grid_width1 = post_info->width / cfg->strides[0];
  int grid_height1 = post_info->height / cfg->strides[0];
  tensor1 = (int16_t *)tensor0 +
            1 * 3 * grid_width1 * grid_height1 * (cfg->class_num + 5);
  void *tensor2 = NULL;
  int grid_width2 = post_info->width / cfg->strides[1];
  int grid_height2 = post_info->height / cfg->strides[1];
  tensor2 = (int16_t *)tensor1 +
            1 * 3 * grid_width2 * grid_height2 * (cfg->class_num + 5);
  std::vector<Detection> dets;
  std::vector<Detection> det_results;

  tensorpostProcess(cfg, tensor0, post_info, 0, filter, filterNum, dets);
  tensorpostProcess(cfg, tensor1, post_info, 1, filter, filterNum, dets);
  tensorpostProcess(cfg, tensor2, post_info, 2, filter, filterNum, dets);

  yolo5Nms(dets, post_info->nms_threshold, post_info->nms_top_k, det_results,
           false);

  boxesInfo->boxesNum = 0;
  
  for (auto det_result : det_results) {
    if (det_result.score < 0.6) continue;
    boxesInfo->boxes[boxesInfo->boxesNum].xmax = det_result.bbox.xmax;
    boxesInfo->boxes[boxesInfo->boxesNum].xmin = det_result.bbox.xmin;
    boxesInfo->boxes[boxesInfo->boxesNum].ymax = det_result.bbox.ymax;
    boxesInfo->boxes[boxesInfo->boxesNum].ymin = det_result.bbox.ymin;
    boxesInfo->boxes[boxesInfo->boxesNum].score = det_result.score;
    boxesInfo->boxesNum += 1;
  }
  return 0;
}


int osdPostProcess(OSD_PARAM_T* osdParam) {
  lynBoxesInfo* boxInfo = (lynBoxesInfo *)lynPluginGetVirtAddr(osdParam->deviceBoxInfo);
  if (boxInfo == nullptr) {
    LOG_PLUGIN_E("get virtual addr of deviceBoxInfo error\n");
  }
  std::vector<TARGET_INFO> recog_result;
  auto baseAddr =  (uintptr_t*)lynPluginGetVirtAddr(osdParam->apuBuffers);
  if (boxInfo == nullptr) {
    LOG_PLUGIN_E("get baseAddr addr of apuBuffers error\n");
  }

  for (int i = 0; i < osdParam->targetNum; ++i) {
    auto addr = lynPluginGetVirtAddr((void*)baseAddr[i]);
    if (addr == nullptr)
      continue;;
    std::vector<int> indexes(20, 100);
    float confidence = 0.0f;
    num_extractor_postprocess(addr, indexes, &confidence);
    if (confidence < 0.85)
      continue;
    TARGET_INFO targetInfo;
    targetInfo.height = boxInfo->boxes[i].ymax - boxInfo->boxes[i].ymin;
    targetInfo.width = boxInfo->boxes[i].xmax - boxInfo->boxes[i].xmin;
    targetInfo.left = boxInfo->boxes[i].xmin;
    targetInfo.top = boxInfo->boxes[i].ymin;
    for (size_t j = 0; j<20; ++j) {
      targetInfo.info[j] = (char)indexes[j];
    }
    recog_result.push_back(targetInfo);
  }
  osdDraw(osdParam->imgW, osdParam->imgH, osdParam->imgData, recog_result);
  return 0;
}