Jiale/test2_ort/lite/utils.cpp

485 lines
17 KiB
C++
Raw Normal View History

2024-05-15 18:06:07 +08:00
//
// Created by DefTruth on 2021/10/7.
//
#include "utils.h"
//*************************************** lite::utils **********************************************//
std::string lite::utils::to_string(const std::wstring &wstr)
{
unsigned len = wstr.size() * 4;
setlocale(LC_CTYPE, "");
char *p = new char[len];
wcstombs(p, wstr.c_str(), len);
std::string str(p);
delete[] p;
return str;
}
std::wstring lite::utils::to_wstring(const std::string &str)
{
unsigned len = str.size() * 2;
setlocale(LC_CTYPE, "");
wchar_t *p = new wchar_t[len];
mbstowcs(p, str.c_str(), len);
std::wstring wstr(p);
delete[] p;
return wstr;
}
// reference: https://github.com/DefTruth/headpose-fsanet-pytorch/blob/master/src/utils.py
void lite::utils::draw_axis_inplace(cv::Mat &mat_inplace,
const types::EulerAngles &euler_angles,
float size, int thickness)
{
if (!euler_angles.flag) return;
const float pitch = euler_angles.pitch * _PI / 180.f;
const float yaw = -euler_angles.yaw * _PI / 180.f;
const float roll = euler_angles.roll * _PI / 180.f;
const float height = static_cast<float>(mat_inplace.rows);
const float width = static_cast<float>(mat_inplace.cols);
const int tdx = static_cast<int>(width / 2.0f);
const int tdy = static_cast<int>(height / 2.0f);
// X-Axis pointing to right. drawn in red
const int x1 = static_cast<int>(size * std::cos(yaw) * std::cos(roll)) + tdx;
const int y1 = static_cast<int>(
size * (std::cos(pitch) * std::sin(roll)
+ std::cos(roll) * std::sin(pitch) * std::sin(yaw))
) + tdy;
// Y-Axis | drawn in green
const int x2 = static_cast<int>(-size * std::cos(yaw) * std::sin(roll)) + tdx;
const int y2 = static_cast<int>(
size * (std::cos(pitch) * std::cos(roll)
- std::sin(pitch) * std::sin(yaw) * std::sin(roll))
) + tdy;
// Z-Axis (out of the screen) drawn in blue
const int x3 = static_cast<int>(size * std::sin(yaw)) + tdx;
const int y3 = static_cast<int>(-size * std::cos(yaw) * std::sin(pitch)) + tdy;
cv::line(mat_inplace, cv::Point2i(tdx, tdy), cv::Point2i(x1, y1), cv::Scalar(0, 0, 255), thickness);
cv::line(mat_inplace, cv::Point2i(tdx, tdy), cv::Point2i(x2, y2), cv::Scalar(0, 255, 0), thickness);
cv::line(mat_inplace, cv::Point2i(tdx, tdy), cv::Point2i(x3, y3), cv::Scalar(255, 0, 0), thickness);
}
cv::Mat lite::utils::draw_axis(const cv::Mat &mat,
const types::EulerAngles &euler_angles,
float size, int thickness)
{
if (!euler_angles.flag) return mat;
cv::Mat mat_copy = mat.clone();
const float pitch = euler_angles.pitch * _PI / 180.f;
const float yaw = -euler_angles.yaw * _PI / 180.f;
const float roll = euler_angles.roll * _PI / 180.f;
const float height = static_cast<float>(mat_copy.rows);
const float width = static_cast<float>(mat_copy.cols);
const int tdx = static_cast<int>(width / 2.0f);
const int tdy = static_cast<int>(height / 2.0f);
// X-Axis pointing to right. drawn in red
const int x1 = static_cast<int>(size * std::cos(yaw) * std::cos(roll)) + tdx;
const int y1 = static_cast<int>(
size * (std::cos(pitch) * std::sin(roll)
+ std::cos(roll) * std::sin(pitch) * std::sin(yaw))
) + tdy;
// Y-Axis | drawn in green
const int x2 = static_cast<int>(-size * std::cos(yaw) * std::sin(roll)) + tdx;
const int y2 = static_cast<int>(
size * (std::cos(pitch) * std::cos(roll)
- std::sin(pitch) * std::sin(yaw) * std::sin(roll))
) + tdy;
// Z-Axis (out of the screen) drawn in blue
const int x3 = static_cast<int>(size * std::sin(yaw)) + tdx;
const int y3 = static_cast<int>(-size * std::cos(yaw) * std::sin(pitch)) + tdy;
cv::line(mat_copy, cv::Point2i(tdx, tdy), cv::Point2i(x1, y1), cv::Scalar(0, 0, 255), thickness);
cv::line(mat_copy, cv::Point2i(tdx, tdy), cv::Point2i(x2, y2), cv::Scalar(0, 255, 0), thickness);
cv::line(mat_copy, cv::Point2i(tdx, tdy), cv::Point2i(x3, y3), cv::Scalar(255, 0, 0), thickness);
return mat_copy;
}
cv::Mat lite::utils::draw_landmarks(const cv::Mat &mat, types::Landmarks &landmarks)
{
if (landmarks.points.empty() || !landmarks.flag) return mat;
cv::Mat mat_copy = mat.clone();
for (const auto &point: landmarks.points)
cv::circle(mat_copy, point, 2, cv::Scalar(0, 255, 0), -1);
return mat_copy;
}
void lite::utils::draw_landmarks_inplace(cv::Mat &mat, types::Landmarks &landmarks)
{
if (landmarks.points.empty() || !landmarks.flag) return;
for (const auto &point: landmarks.points)
cv::circle(mat, point, 2, cv::Scalar(0, 255, 0), -1);
}
void lite::utils::draw_boxes_inplace(cv::Mat &mat_inplace, const std::vector<types::Boxf> &boxes)
{
if (boxes.empty()) return;
for (const auto &box: boxes)
{
if (box.flag)
{
cv::rectangle(mat_inplace, box.rect(), cv::Scalar(255, 255, 0), 2);
if (box.label_text)
{
std::string label_text(box.label_text);
label_text = label_text + ":" + std::to_string(box.score).substr(0, 4);
cv::putText(mat_inplace, label_text, box.tl(), cv::FONT_HERSHEY_SIMPLEX,
0.6f, cv::Scalar(0, 255, 0), 2);
}
}
}
}
cv::Mat lite::utils::draw_boxes(const cv::Mat &mat, const std::vector<types::Boxf> &boxes)
{
if (boxes.empty()) return mat;
cv::Mat canva = mat.clone();
for (const auto &box: boxes)
{
if (box.flag)
{
cv::rectangle(canva, box.rect(), cv::Scalar(255, 255, 0), 2);
if (box.label_text)
{
std::string label_text(box.label_text);
label_text = label_text + ":" + std::to_string(box.score).substr(0, 4);
cv::putText(canva, label_text, box.tl(), cv::FONT_HERSHEY_SIMPLEX,
0.6f, cv::Scalar(0, 255, 0), 2);
}
}
}
return canva;
}
void lite::utils::draw_boxes_with_landmarks_inplace(cv::Mat &mat_inplace, const std::vector<types::BoxfWithLandmarks> &boxes_kps, bool text)
{
if (boxes_kps.empty()) return;
for (const auto &box_kps: boxes_kps)
{
if (box_kps.flag)
{
// box
if (box_kps.box.flag)
{
cv::rectangle(mat_inplace, box_kps.box.rect(), cv::Scalar(255, 255, 0), 2);
if (box_kps.box.label_text && text)
{
std::string label_text(box_kps.box.label_text);
label_text = label_text + ":" + std::to_string(box_kps.box.score).substr(0, 4);
cv::putText(mat_inplace, label_text, box_kps.box.tl(), cv::FONT_HERSHEY_SIMPLEX,
0.6f, cv::Scalar(0, 255, 0), 2);
}
}
// landmarks
if (box_kps.landmarks.flag && !box_kps.landmarks.points.empty())
{
for (const auto &point: box_kps.landmarks.points)
cv::circle(mat_inplace, point, 2, cv::Scalar(0, 255, 0), -1);
}
}
}
}
cv::Mat lite::utils::draw_boxes_with_landmarks(const cv::Mat &mat, const std::vector<types::BoxfWithLandmarks> &boxes_kps, bool text)
{
if (boxes_kps.empty()) return mat;
cv::Mat canva = mat.clone();
for (const auto &box_kps: boxes_kps)
{
if (box_kps.flag)
{
// box
if (box_kps.box.flag)
{
cv::rectangle(canva, box_kps.box.rect(), cv::Scalar(255, 255, 0), 2);
if (box_kps.box.label_text && text)
{
std::string label_text(box_kps.box.label_text);
label_text = label_text + ":" + std::to_string(box_kps.box.score).substr(0, 4);
cv::putText(canva, label_text, box_kps.box.tl(), cv::FONT_HERSHEY_SIMPLEX,
0.6f, cv::Scalar(0, 255, 0), 2);
}
}
// landmarks
if (box_kps.landmarks.flag && !box_kps.landmarks.points.empty())
{
for (const auto &point: box_kps.landmarks.points)
cv::circle(canva, point, 2, cv::Scalar(0, 255, 0), -1);
}
}
}
return canva;
}
cv::Mat lite::utils::draw_age(const cv::Mat &mat, types::Age &age)
{
if (!age.flag) return mat;
cv::Mat canva = mat.clone();
const unsigned int offset = static_cast<unsigned int>(
0.1f * static_cast<float>(mat.rows));
std::string age_text = "Age:" + std::to_string(age.age).substr(0, 4);
std::string interval_text = "Interval" + std::to_string(age.age_interval[0])
+ "_" + std::to_string(age.age_interval[1]);
std::string interval_prob = "Prob:" + std::to_string(age.interval_prob).substr(0, 4);
cv::putText(canva, age_text, cv::Point2i(10, offset),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 255, 0), 2);
cv::putText(canva, interval_text, cv::Point2i(10, offset * 2),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(255, 0, 0), 2);
cv::putText(canva, interval_prob, cv::Point2i(10, offset * 3),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 0, 255), 2);
return canva;
}
void lite::utils::draw_age_inplace(cv::Mat &mat_inplace, types::Age &age)
{
if (!age.flag) return;
const unsigned int offset = static_cast<unsigned int>(
0.1f * static_cast<float>(mat_inplace.rows));
std::string age_text = "Age:" + std::to_string(age.age).substr(0, 4);
std::string interval_text = "Interval" + std::to_string(age.age_interval[0])
+ "_" + std::to_string(age.age_interval[1]);
std::string interval_prob = "Prob:" + std::to_string(age.interval_prob).substr(0, 4);
cv::putText(mat_inplace, age_text, cv::Point2i(10, offset),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 255, 0), 2);
cv::putText(mat_inplace, interval_text, cv::Point2i(10, offset * 2),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(255, 0, 0), 2);
cv::putText(mat_inplace, interval_prob, cv::Point2i(10, offset * 3),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 0, 255), 2);
}
cv::Mat lite::utils::draw_gender(const cv::Mat &mat, types::Gender &gender)
{
if (!gender.flag) return mat;
cv::Mat canva = mat.clone();
const unsigned int offset = static_cast<unsigned int>(
0.1f * static_cast<float>(mat.rows));
std::string gender_text = std::to_string(gender.label) + ":"
+ gender.text + ":" + std::to_string(gender.score).substr(0, 4);
cv::putText(canva, gender_text, cv::Point2i(10, offset),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 255, 0), 2);
return canva;
}
void lite::utils::draw_gender_inplace(cv::Mat &mat_inplace, types::Gender &gender)
{
if (!gender.flag) return;
const unsigned int offset = static_cast<unsigned int>(
0.1f * static_cast<float>(mat_inplace.rows));
std::string gender_text = std::to_string(gender.label) + ":"
+ gender.text + ":" + std::to_string(gender.score).substr(0, 4);
cv::putText(mat_inplace, gender_text, cv::Point2i(10, offset),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 255, 0), 2);
}
cv::Mat lite::utils::draw_emotion(const cv::Mat &mat, types::Emotions &emotions)
{
if (!emotions.flag) return mat;
cv::Mat canva = mat.clone();
const unsigned int offset = static_cast<unsigned int>(
0.1f * static_cast<float>(mat.rows));
std::string emotion_text = std::to_string(emotions.label) + ":"
+ emotions.text + ":" + std::to_string(emotions.score).substr(0, 4);
cv::putText(canva, emotion_text, cv::Point2i(10, offset),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 255, 0), 2);
return canva;
}
void lite::utils::draw_emotion_inplace(cv::Mat &mat_inplace, types::Emotions &emotions)
{
if (!emotions.flag) return;
const unsigned int offset = static_cast<unsigned int>(
0.1f * static_cast<float>(mat_inplace.rows));
std::string emotion_text = std::to_string(emotions.label) + ":"
+ emotions.text + ":" + std::to_string(emotions.score).substr(0, 4);
cv::putText(mat_inplace, emotion_text, cv::Point2i(10, offset),
cv::FONT_HERSHEY_SIMPLEX, 0.6f, cv::Scalar(0, 255, 0), 2);
}
// reference: https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/
// blob/master/ncnn/src/UltraFace.cpp
void lite::utils::hard_nms(std::vector<types::Boxf> &input, std::vector<types::Boxf> &output,
float iou_threshold, unsigned int topk)
{
if (input.empty()) return;
std::sort(input.begin(), input.end(),
[](const types::Boxf &a, const types::Boxf &b)
{ return a.score > b.score; });
const unsigned int box_num = input.size();
std::vector<int> merged(box_num, 0);
unsigned int count = 0;
for (unsigned int i = 0; i < box_num; ++i)
{
if (merged[i]) continue;
std::vector<types::Boxf> buf;
buf.push_back(input[i]);
merged[i] = 1;
for (unsigned int j = i + 1; j < box_num; ++j)
{
if (merged[j]) continue;
float iou = static_cast<float>(input[i].iou_of(input[j]));
if (iou > iou_threshold)
{
merged[j] = 1;
buf.push_back(input[j]);
}
}
output.push_back(buf[0]);
// keep top k
count += 1;
if (count >= topk)
break;
}
}
void lite::utils::blending_nms(std::vector<types::Boxf> &input, std::vector<types::Boxf> &output,
float iou_threshold, unsigned int topk)
{
if (input.empty()) return;
std::sort(input.begin(), input.end(),
[](const types::Boxf &a, const types::Boxf &b)
{ return a.score > b.score; });
const unsigned int box_num = input.size();
std::vector<int> merged(box_num, 0);
unsigned int count = 0;
for (unsigned int i = 0; i < box_num; ++i)
{
if (merged[i]) continue;
std::vector<types::Boxf> buf;
buf.push_back(input[i]);
merged[i] = 1;
for (unsigned int j = i + 1; j < box_num; ++j)
{
if (merged[j]) continue;
float iou = static_cast<float>(input[i].iou_of(input[j]));
if (iou > iou_threshold)
{
merged[j] = 1;
buf.push_back(input[j]);
}
}
float total = 0.f;
for (unsigned int k = 0; k < buf.size(); ++k)
{
total += std::exp(buf[k].score);
}
types::Boxf rects;
for (unsigned int l = 0; l < buf.size(); ++l)
{
float rate = std::exp(buf[l].score) / total;
rects.x1 += buf[l].x1 * rate;
rects.y1 += buf[l].y1 * rate;
rects.x2 += buf[l].x2 * rate;
rects.y2 += buf[l].y2 * rate;
rects.score += buf[l].score * rate;
}
rects.flag = true;
output.push_back(rects);
// keep top k
count += 1;
if (count >= topk)
break;
}
}
// reference: https://github.com/ultralytics/yolov5/blob/master/utils/general.py
void lite::utils::offset_nms(std::vector<types::Boxf> &input, std::vector<types::Boxf> &output,
float iou_threshold, unsigned int topk)
{
if (input.empty()) return;
std::sort(input.begin(), input.end(),
[](const types::Boxf &a, const types::Boxf &b)
{ return a.score > b.score; });
const unsigned int box_num = input.size();
std::vector<int> merged(box_num, 0);
const float offset = 4096.f;
/** Add offset according to classes.
* That is, separate the boxes into categories, and each category performs its
* own NMS operation. The same offset will be used for those predicted to be of
* the same category. Therefore, the relative positions of boxes of the same
* category will remain unchanged. Box of different classes will be farther away
* after offset, because offsets are different. In this way, some overlapping but
* different categories of entities are not filtered out by the NMS. Very clever!
*/
for (unsigned int i = 0; i < box_num; ++i)
{
input[i].x1 += static_cast<float>(input[i].label) * offset;
input[i].y1 += static_cast<float>(input[i].label) * offset;
input[i].x2 += static_cast<float>(input[i].label) * offset;
input[i].y2 += static_cast<float>(input[i].label) * offset;
}
unsigned int count = 0;
for (unsigned int i = 0; i < box_num; ++i)
{
if (merged[i]) continue;
std::vector<types::Boxf> buf;
buf.push_back(input[i]);
merged[i] = 1;
for (unsigned int j = i + 1; j < box_num; ++j)
{
if (merged[j]) continue;
float iou = static_cast<float>(input[i].iou_of(input[j]));
if (iou > iou_threshold)
{
merged[j] = 1;
buf.push_back(input[j]);
}
}
output.push_back(buf[0]);
// keep top k
count += 1;
if (count >= topk)
break;
}
/** Substract offset.*/
if (!output.empty())
{
for (unsigned int i = 0; i < output.size(); ++i)
{
output[i].x1 -= static_cast<float>(output[i].label) * offset;
output[i].y1 -= static_cast<float>(output[i].label) * offset;
output[i].x2 -= static_cast<float>(output[i].label) * offset;
output[i].y2 -= static_cast<float>(output[i].label) * offset;
}
}
}
//*************************************** lite::utils **********************************************//