mirror of
https://github.com/sipeed/MaixCDK.git
synced 2026-11-09 17:04:38 -06:00
714 lines
26 KiB
C++
714 lines
26 KiB
C++
/**
|
|
* @author neucrack@sipeed
|
|
* @copyright Sipeed Ltd 2023-
|
|
* @license Apache 2.0
|
|
* @update 2024.9.19: Add PP OCR support
|
|
*/
|
|
|
|
#pragma once
|
|
#include "maix_basic.hpp"
|
|
#include "maix_nn.hpp"
|
|
#include "maix_image.hpp"
|
|
#include "maix_nn_F.hpp"
|
|
#include "maix_nn_object.hpp"
|
|
#include "maix_nn_ocr_object.hpp"
|
|
|
|
namespace maix::nn
|
|
{
|
|
/**
|
|
* PP_OCR class
|
|
* @maixpy maix.nn.PP_OCR
|
|
*/
|
|
class PP_OCR
|
|
{
|
|
public:
|
|
/**
|
|
* Constructor of PP_OCR class
|
|
* @param model model path, default empty, you can load model later by load function.
|
|
* @throw If model arg is not empty and load failed, will throw err::Exception.
|
|
* @maixpy maix.nn.PP_OCR.__init__
|
|
* @maixcdk maix.nn.PP_OCR.PP_OCR
|
|
*/
|
|
PP_OCR(const string &model = "")
|
|
{
|
|
_model = nullptr;
|
|
_rec_model = nullptr;
|
|
this->det = false;
|
|
this->rec = false;
|
|
if (!model.empty())
|
|
{
|
|
err::Err e = load(model);
|
|
if (e != err::ERR_NONE)
|
|
{
|
|
throw err::Exception(e, "load model failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
~PP_OCR()
|
|
{
|
|
if (_model)
|
|
{
|
|
delete _model;
|
|
_model = nullptr;
|
|
}
|
|
if (_rec_model)
|
|
{
|
|
delete _rec_model;
|
|
_rec_model = nullptr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load model from file
|
|
* @param model Model path want to load
|
|
* @return err::Err
|
|
* @maixpy maix.nn.PP_OCR.load
|
|
*/
|
|
err::Err load(const string &model)
|
|
{
|
|
if (_model)
|
|
{
|
|
delete _model;
|
|
_model = nullptr;
|
|
}
|
|
if (_rec_model)
|
|
{
|
|
delete _rec_model;
|
|
_rec_model = nullptr;
|
|
}
|
|
|
|
_model = new nn::NN(model, false);
|
|
if (!_model)
|
|
{
|
|
return err::ERR_NO_MEM;
|
|
}
|
|
_extra_info = _model->extra_info();
|
|
if (_extra_info.find("model_type") != _extra_info.end())
|
|
{
|
|
if (_extra_info["model_type"] != "pp_ocr")
|
|
{
|
|
log::error("model_type not match, expect 'pp_ocr', but got '%s'", _extra_info["model_type"].c_str());
|
|
return err::ERR_ARGS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log::error("model_type key not found");
|
|
return err::ERR_ARGS;
|
|
}
|
|
log::info("model info:\n\ttype: pp_ocr");
|
|
if (_extra_info.find("input_type") != _extra_info.end())
|
|
{
|
|
std::string input_type = _extra_info["input_type"];
|
|
if (input_type == "rgb")
|
|
{
|
|
_input_img_fmt = maix::image::FMT_RGB888;
|
|
log::print("\tinput type: rgb\n");
|
|
}
|
|
else if (input_type == "bgr")
|
|
{
|
|
_input_img_fmt = maix::image::FMT_BGR888;
|
|
log::print("\tinput type: bgr\n");
|
|
}
|
|
else
|
|
{
|
|
log::error("unknown input type: %s", input_type.c_str());
|
|
return err::ERR_ARGS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log::error("input_type key not found");
|
|
return err::ERR_ARGS;
|
|
}
|
|
if (_extra_info.find("det") != _extra_info.end())
|
|
{
|
|
std::string det_str = _extra_info["det"];
|
|
if(det_str == "true")
|
|
{
|
|
this->det = true;
|
|
this->rec = false;
|
|
log::print("\tdet: true\n");
|
|
}
|
|
else
|
|
{
|
|
this->det = false;
|
|
this->rec = true;
|
|
log::print("\tdet: false\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log::error("det key not found");
|
|
return err::ERR_ARGS;
|
|
}
|
|
if (_extra_info.find("mean") != _extra_info.end())
|
|
{
|
|
std::string mean_str = _extra_info["mean"];
|
|
std::vector<std::string> mean_strs = split(mean_str, ",");
|
|
log::print("\tmean:");
|
|
for (auto &it : mean_strs)
|
|
{
|
|
try
|
|
{
|
|
if(this->det)
|
|
this->mean.push_back(std::stof(it));
|
|
else
|
|
this->rec_mean.push_back(std::stof(it));
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
log::error("mean value error, should float");
|
|
return err::ERR_ARGS;
|
|
}
|
|
log::print("%f ", this->det ? this->mean.back() : this->rec_mean.back());
|
|
}
|
|
log::print("\n");
|
|
}
|
|
else if(!this->rec)
|
|
{
|
|
log::error("mean key not found");
|
|
return err::ERR_ARGS;
|
|
}
|
|
if (_extra_info.find("scale") != _extra_info.end())
|
|
{
|
|
std::string scale_str = _extra_info["scale"];
|
|
std::vector<std::string> scale_strs = split(scale_str, ",");
|
|
log::print("\tscale:");
|
|
for (auto &it : scale_strs)
|
|
{
|
|
try
|
|
{
|
|
if(this->det)
|
|
this->scale.push_back(std::stof(it));
|
|
else
|
|
this->rec_scale.push_back(std::stof(it));
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
log::error("scale value error, should float");
|
|
return err::ERR_ARGS;
|
|
}
|
|
log::print("%f ", this->det ? this->scale.back() : this->rec_scale.back());
|
|
}
|
|
log::print("\n");
|
|
}
|
|
else if(!this->rec)
|
|
{
|
|
log::error("scale key not found");
|
|
return err::ERR_ARGS;
|
|
}
|
|
std::vector<nn::LayerInfo> inputs = _model->inputs_info();
|
|
if(inputs[0].shape[3] <= 4) // nhwc
|
|
_input_size = image::Size(inputs[0].shape[2], inputs[0].shape[1]);
|
|
else
|
|
_input_size = image::Size(inputs[0].shape[3], inputs[0].shape[2]);
|
|
log::print("\tinput size: %dx%d\n\n", _input_size.width(), _input_size.height());
|
|
|
|
// rec model
|
|
if (_extra_info.find("rec_model") != _extra_info.end())
|
|
{
|
|
if (this->rec)
|
|
{
|
|
log::error("model is rec model, shouldn't have rec_model key");
|
|
return err::ERR_ARGS;
|
|
}
|
|
std::string rec_model = fs::dirname(model) + "/" + _extra_info["rec_model"];
|
|
log::print("\trec_model: %s\n", rec_model.c_str());
|
|
_rec_model = new nn::NN(rec_model, false);
|
|
if (!_rec_model)
|
|
{
|
|
log::error("load rec model %s failed", rec_model.c_str());
|
|
return err::ERR_ARGS;
|
|
}
|
|
log::info("rec_model info:");
|
|
this->rec = true;
|
|
}
|
|
else
|
|
{
|
|
log::info("only det");
|
|
}
|
|
if(this->rec)
|
|
{
|
|
if (_extra_info.find("rec_mean") != _extra_info.end())
|
|
{
|
|
std::string mean_str = _extra_info["rec_mean"];
|
|
std::vector<std::string> mean_strs = split(mean_str, ",");
|
|
log::print("\trec_mean:");
|
|
for (auto &it : mean_strs)
|
|
{
|
|
try
|
|
{
|
|
this->rec_mean.push_back(std::stof(it));
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
log::error("rec_mean value error, should float");
|
|
return err::ERR_ARGS;
|
|
}
|
|
log::print("%f ", this->rec_mean.back());
|
|
}
|
|
log::print("\n");
|
|
}
|
|
if (_extra_info.find("rec_scale") != _extra_info.end())
|
|
{
|
|
std::string scale_str = _extra_info["rec_scale"];
|
|
std::vector<std::string> scale_strs = split(scale_str, ",");
|
|
log::print("\trec_scale:");
|
|
for (auto &it : scale_strs)
|
|
{
|
|
try
|
|
{
|
|
this->rec_scale.push_back(std::stof(it));
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
log::error("rec_scale value error, should float");
|
|
return err::ERR_ARGS;
|
|
}
|
|
log::print("%f ", this->rec_scale.back());
|
|
}
|
|
log::print("\n");
|
|
}
|
|
if(this->rec_scale.empty() || this->rec_mean.empty())
|
|
{
|
|
log::error("mean and scale key not found");
|
|
return err::ERR_ARGS;
|
|
}
|
|
|
|
err::Err e = _model->extra_info_labels(labels);
|
|
if(e == err::Err::ERR_NONE)
|
|
{
|
|
log::print("\tlabels num: %ld\n", labels.size());
|
|
}
|
|
else
|
|
{
|
|
log::error("labels key not found: %s", err::to_str(e).c_str());
|
|
return err::ERR_ARGS;
|
|
}
|
|
|
|
if(!this->det)
|
|
{
|
|
_rec_model = _model;
|
|
_model = nullptr;
|
|
}
|
|
std::vector<nn::LayerInfo> rec_inputs = _rec_model->inputs_info();
|
|
if(rec_inputs[0].shape[3] <= 4) // nhwc
|
|
_rec_input_size = image::Size(rec_inputs[0].shape[2], rec_inputs[0].shape[1]);
|
|
else
|
|
_rec_input_size = image::Size(rec_inputs[0].shape[3], rec_inputs[0].shape[2]);
|
|
std::vector<nn::LayerInfo> rec_outputs = _rec_model->outputs_info();
|
|
size_t labels_num = rec_outputs[0].shape[2] - 2;
|
|
if(labels_num != labels.size())
|
|
{
|
|
log::error("labels size not match, model need: %lld, labels: %lld", labels_num, labels.size());
|
|
return err::ERR_ARGS;
|
|
}
|
|
if(_use_space_char)
|
|
labels.push_back(" ");
|
|
_prob_num = rec_outputs[0].shape[2];
|
|
_max_ch_num = rec_outputs[0].shape[1];
|
|
if(_max_ch_num * 8 != _rec_input_size.width())
|
|
{
|
|
log::error("input width not match, model need: %lld, actual: %lld", _max_ch_num * 8, _rec_input_size.width());
|
|
return err::ERR_ARGS;
|
|
}
|
|
}
|
|
return err::ERR_NONE;
|
|
}
|
|
|
|
/**
|
|
* Detect objects from image
|
|
* @param img Image want to detect, if image's size not match model input's, will auto resize with fit method.
|
|
* @param thresh Confidence threshold where pixels have charactor, default 0.3.
|
|
* @param box_thresh Box threshold, the box prob higher than this value will be valid, default 0.6.
|
|
* @param fit Resize method, default image.Fit.FIT_CONTAIN.
|
|
* @param char_box Calculate every charactor's box, default false, if true then you can get charactor's box by nn.OCR_Object's char_boxes attribute.
|
|
* @throw If image format not match model input format or no memory, will throw err::Exception.
|
|
* @return nn.OCR_Objects type. In C++, you should delete it after use.
|
|
* @maixpy maix.nn.PP_OCR.detect
|
|
*/
|
|
nn::OCR_Objects *detect(image::Image &img, float thresh = 0.3, float box_thresh = 0.6, maix::image::Fit fit = maix::image::FIT_CONTAIN, bool char_box = false)
|
|
{
|
|
if(!this->det)
|
|
{
|
|
throw err::Exception(err::ERR_ARGS, "detect method not for only rec model, please use recognize method");
|
|
}
|
|
this->_thresh = thresh;
|
|
this->_box_thresh = box_thresh;
|
|
if (img.format() != _input_img_fmt)
|
|
{
|
|
throw err::Exception("image format not match, input_type: " + image::fmt_names[_input_img_fmt] + ", image format: " + image::fmt_names[img.format()]);
|
|
}
|
|
tensor::Tensors *outputs;
|
|
outputs = _model->forward_image(img, this->mean, this->scale, fit, true);
|
|
if (!outputs) // not ready, return empty result.
|
|
{
|
|
return new nn::OCR_Objects();
|
|
}
|
|
nn::OCR_Objects *res = _post_process(img, outputs, img.width(), img.height(), fit);
|
|
delete outputs;
|
|
if(res == NULL)
|
|
{
|
|
throw err::Exception("post process failed, please see log before");
|
|
}
|
|
// rec
|
|
return res;
|
|
}
|
|
|
|
|
|
/**
|
|
* Only recognize, not detect
|
|
* @param img image to recognize chractors, can be a stanrd cropped charactors image,
|
|
* if crop image not standard, you can use box_points to assgin where the charactors' 4 corner is.
|
|
* @param box_points list type, length must be 8 or 0, default empty means not transfer image to standard image.
|
|
* 4 points postiion, format: [x1, y1, x2, y2, x3, y3, x4, y4], point 1 at the left-top, point 2 right-top...
|
|
* @param char_box Calculate every charactor's box, default false, if true then you can get charactor's box by nn.OCR_Object's char_boxes attribute.
|
|
* @maixpy maix.nn.PP_OCR.recognize
|
|
*/
|
|
nn::OCR_Object *recognize(image::Image &img, const std::vector<int> &box_points = std::vector<int>())
|
|
{
|
|
nn::OCR_Box box(0, 0, img.width(), 0, img.width(), img.height(), 0, img.height());
|
|
bool crop = false;
|
|
if(!box_points.empty())
|
|
{
|
|
box.x1 = box_points[0];
|
|
box.y1 = box_points[1];
|
|
box.x2 = box_points[2];
|
|
box.y2 = box_points[3];
|
|
box.x3 = box_points[4];
|
|
box.y3 = box_points[5];
|
|
box.x4 = box_points[6];
|
|
box.y4 = box_points[7];
|
|
crop = true;
|
|
}
|
|
std::vector<int> idx_list;
|
|
std::vector<std::string> char_list;
|
|
std::vector<int> char_pos;
|
|
nn::OCR_Object *obj = new nn::OCR_Object(box, idx_list, char_list, 1.0, char_pos);
|
|
if(!obj)
|
|
{
|
|
throw err::Exception(err::ERR_NO_MEM);
|
|
}
|
|
_recognize(img, box, obj->idx_list, char_list, obj->char_pos, crop);
|
|
obj->update_chars(char_list);
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* Draw segmentation on image
|
|
* @param img image object, maix.image.Image type.
|
|
* @param seg_mask segmentation mask image by detect method, a grayscale image
|
|
* @param threshold only mask's value > threshold will be draw on image, value from 0 to 255.
|
|
* @maixpy maix.nn.PP_OCR.draw_seg_mask
|
|
*/
|
|
void draw_seg_mask(image::Image &img, int x, int y, image::Image &seg_mask, int threshold = 127)
|
|
{
|
|
if (seg_mask.format() != image::FMT_GRAYSCALE)
|
|
{
|
|
throw err::Exception(err::ERR_ARGS, "seg_mask only support grascale");
|
|
}
|
|
if (img.format() != image::FMT_RGB888 &&
|
|
img.format() != image::FMT_BGR888 &&
|
|
img.format() != image::FMT_RGBA8888 &&
|
|
img.format() != image::FMT_BGRA8888 &&
|
|
img.format() != image::FMT_GRAYSCALE)
|
|
{
|
|
throw err::Exception(err::ERR_ARGS, "img not support");
|
|
}
|
|
int fmt_size = image::fmt_size[img.format()];
|
|
uint8_t *to = (uint8_t*)img.data();
|
|
uint8_t *from = (uint8_t*)seg_mask.data();
|
|
log::info("w: %d, h: %d, mask w: %d, h: %d", img.width(), img.height(), seg_mask.width(), seg_mask.height());
|
|
for (int i = 0; i < seg_mask.height(); ++i)
|
|
{
|
|
for (int j = 0; j < seg_mask.width(); ++j)
|
|
{
|
|
uint8_t data = from[i * seg_mask.width() + j];
|
|
if(data > threshold)
|
|
{
|
|
to[((y + i) * img.width() + x + j) * fmt_size] = data;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get model input size
|
|
* @return model input size
|
|
* @maixpy maix.nn.PP_OCR.input_size
|
|
*/
|
|
image::Size input_size()
|
|
{
|
|
return _input_size;
|
|
}
|
|
|
|
/**
|
|
* Get model input width
|
|
* @return model input size of width
|
|
* @maixpy maix.nn.PP_OCR.input_width
|
|
*/
|
|
int input_width()
|
|
{
|
|
return _input_size.width();
|
|
}
|
|
|
|
/**
|
|
* Get model input height
|
|
* @return model input size of height
|
|
* @maixpy maix.nn.PP_OCR.input_height
|
|
*/
|
|
int input_height()
|
|
{
|
|
return _input_size.height();
|
|
}
|
|
|
|
/**
|
|
* Get input image format
|
|
* @return input image format, image::Format type.
|
|
* @maixpy maix.nn.PP_OCR.input_format
|
|
*/
|
|
image::Format input_format()
|
|
{
|
|
return _input_img_fmt;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* Get mean value, list type
|
|
* @maixpy maix.nn.PP_OCR.mean
|
|
*/
|
|
std::vector<float> mean;
|
|
|
|
/**
|
|
* Get scale value, list type
|
|
* @maixpy maix.nn.PP_OCR.scale
|
|
*/
|
|
std::vector<float> scale;
|
|
|
|
/**
|
|
* Get mean value, list type
|
|
* @maixpy maix.nn.PP_OCR.rec_mean
|
|
*/
|
|
std::vector<float> rec_mean;
|
|
|
|
/**
|
|
* Get scale value, list type
|
|
* @maixpy maix.nn.PP_OCR.rec_scale
|
|
*/
|
|
std::vector<float> rec_scale;
|
|
|
|
/**
|
|
* labels (charactors)
|
|
* @maixpy maix.nn.PP_OCR.labels
|
|
*/
|
|
std::vector<std::string> labels;
|
|
|
|
/**
|
|
* model have detect model
|
|
* @maixpy maix.nn.PP_OCR.det
|
|
*/
|
|
bool det;
|
|
|
|
/**
|
|
* model have recognize model
|
|
* @maixpy maix.nn.PP_OCR.rec
|
|
*/
|
|
bool rec;
|
|
|
|
private:
|
|
image::Size _input_size;
|
|
image::Size _rec_input_size;
|
|
image::Format _input_img_fmt;
|
|
nn::NN *_model;
|
|
nn::NN *_rec_model;
|
|
std::map<string, string> _extra_info;
|
|
float _thresh = 0.3;
|
|
float _box_thresh = 0.6;
|
|
int _max_candidates = 1000;
|
|
float _unclip_ratio = 1.5;
|
|
bool _use_dilation = false;
|
|
bool _use_space_char = true;
|
|
std::string _score_mode = "fast";
|
|
int _max_ch_num;
|
|
int _prob_num;
|
|
|
|
private:
|
|
err::Err _load_labels_from_file(std::vector<std::string> &labels, const std::string &label_path)
|
|
{
|
|
// load labels from labels file
|
|
labels.clear();
|
|
fs::File *f = fs::open(label_path, "r");
|
|
if (!f)
|
|
{
|
|
log::error("open label file %s failed", label_path.c_str());
|
|
return err::ERR_ARGS;
|
|
}
|
|
std::string line;
|
|
while (f->readline(line) > 0)
|
|
{
|
|
// strip line
|
|
line.erase(0, line.find_first_not_of(" \t\r\n"));
|
|
line.erase(line.find_last_not_of(" \t\r\n") + 1);
|
|
if(!line.empty())
|
|
labels.push_back(line);
|
|
}
|
|
f->close();
|
|
delete f;
|
|
return err::ERR_NONE;
|
|
}
|
|
|
|
nn::OCR_Objects *_post_process(image::Image &img, tensor::Tensors *outputs, int img_w, int img_h, maix::image::Fit fit);
|
|
|
|
void _recognize(image::Image &img, const nn::OCR_Box &box, std::vector<int> &idx_list, std::vector<std::string> &char_list, std::vector<int> &char_pos, bool crop);
|
|
|
|
// void _get_layer_objs(std::vector<nn::Object> &objs, tensor::Tensor &output, int layer_i, int layer_num)
|
|
// {
|
|
// int h = output.shape()[2];
|
|
// int w = output.shape()[3];
|
|
// int class_num = this->labels.size();
|
|
// int box_len = class_num + 5;
|
|
// int s = w * h;
|
|
// int anchor_stride = box_len * s;
|
|
// int s4 = 4 * s;
|
|
// int s3 = 3 * s;
|
|
// int s2 = 2 * s;
|
|
// float *data = (float *)output.data();
|
|
// int anchor_num = this->anchors.size() / 2 / layer_num;
|
|
// int anchor_start = anchor_num * layer_i * 2;
|
|
// float scale_x = _input_size.width() / w;
|
|
// float scale_y = _input_size.height() / h;
|
|
// for (int a = 0; a < anchor_num; ++a)
|
|
// {
|
|
// for (int y = 0; y < h; ++y)
|
|
// {
|
|
// for (int x = 0; x < w; ++x)
|
|
// {
|
|
// float *p = data + a * anchor_stride + y * w + x + s4;
|
|
// float obj_score = _sigmoid(*p);
|
|
// if (obj_score <= _conf_th)
|
|
// continue;
|
|
// float *cls_scores = p + s;
|
|
// int class_id = _argmax(cls_scores, class_num, s);
|
|
// obj_score *= _sigmoid(cls_scores[class_id * s]);
|
|
// if (obj_score <= _conf_th)
|
|
// continue;
|
|
// float bbox_x = (_sigmoid(*(p - s4)) * 2 + x - 0.5) * scale_x;
|
|
// float bbox_y = (_sigmoid(*(p - s3)) * 2 + y - 0.5) * scale_y;
|
|
// float bbox_w = pow(_sigmoid(*(p - s2)) * 2, 2) * this->anchors[anchor_start + a * 2];
|
|
// float bbox_h = pow(_sigmoid(*(p - s)) * 2, 2) * this->anchors[anchor_start + a * 2 + 1];
|
|
// bbox_x -= bbox_w * 0.5; // center x to left top x
|
|
// bbox_y -= bbox_h * 0.5; // center y to left top y
|
|
// Object obj(bbox_x, bbox_y, bbox_w, bbox_h, class_id, obj_score);
|
|
// objs.push_back(obj);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// std::vector<nn::Object> *_nms(std::vector<nn::Object> &objs)
|
|
// {
|
|
// std::vector<nn::Object> *result = new std::vector<nn::Object>();
|
|
// std::sort(objs.begin(), objs.end(), [](const nn::Object &a, const nn::Object &b) {
|
|
// return a.score > b.score;
|
|
// });
|
|
// for(size_t i=0; i < objs.size(); ++i)
|
|
// {
|
|
// nn::Object &a = objs.at(i);
|
|
// if(a.score == 0)
|
|
// continue;
|
|
// for(size_t j = i + 1; j < objs.size(); ++j)
|
|
// {
|
|
// nn::Object &b = objs.at(j);
|
|
// if(b.score != 0 && a.class_id == b.class_id && _calc_iou(a, b) > this->_iou_th)
|
|
// {
|
|
// b.score = 0;
|
|
// }
|
|
// }
|
|
// }
|
|
// for(nn::Object &a :objs)
|
|
// {
|
|
// if(a.score != 0)
|
|
// {
|
|
// if (a.x < 0)
|
|
// {
|
|
// a.w += a.x;
|
|
// a.x = 0;
|
|
// }
|
|
// if (a.y < 0)
|
|
// {
|
|
// a.h += a.y;
|
|
// a.y = 0;
|
|
// }
|
|
// if (a.x + a.w > _input_size.width())
|
|
// {
|
|
// a.w = _input_size.width() - a.x;
|
|
// }
|
|
// if (a.y + a.h > _input_size.height())
|
|
// {
|
|
// a.h = _input_size.height() - a.y;
|
|
// }
|
|
// result->push_back(a);
|
|
// }
|
|
// }
|
|
// return result;
|
|
// }
|
|
|
|
void _correct_bbox(nn::OCR_Objects &objs, int img_w, int img_h, maix::image::Fit fit);
|
|
|
|
inline static float _sigmoid(float x) { return 1.0 / (1 + expf(-x)); }
|
|
|
|
inline static float _calc_iou(Object &a, Object &b)
|
|
{
|
|
float area1 = a.w * a.h;
|
|
float area2 = b.w * b.h;
|
|
float wi = std::min((a.x + a.w), (b.x + b.w)) -
|
|
std::max(a.x, b.x);
|
|
float hi = std::min((a.y + a.h), (b.y + b.h)) -
|
|
std::max(a.y, b.y);
|
|
float area_i = std::max(wi, 0.0f) * std::max(hi, 0.0f);
|
|
return area_i / (area1 + area2 - area_i);
|
|
}
|
|
|
|
template <typename T>
|
|
static int _argmax(const T *data, size_t len, size_t stride = 1)
|
|
{
|
|
int maxIndex = 0;
|
|
for (size_t i = 1; i < len; i++)
|
|
{
|
|
int idx = i * stride;
|
|
if (data[maxIndex * stride] < data[idx])
|
|
{
|
|
maxIndex = i;
|
|
}
|
|
}
|
|
return maxIndex;
|
|
}
|
|
|
|
static void split0(std::vector<std::string> &items, const std::string &s, const std::string &delimiter)
|
|
{
|
|
items.clear();
|
|
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
|
|
std::string token;
|
|
|
|
while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos)
|
|
{
|
|
token = s.substr(pos_start, pos_end - pos_start);
|
|
pos_start = pos_end + delim_len;
|
|
items.push_back(token);
|
|
}
|
|
|
|
items.push_back(s.substr(pos_start));
|
|
}
|
|
|
|
static std::vector<std::string> split(const std::string &s, const std::string &delimiter)
|
|
{
|
|
std::vector<std::string> tokens;
|
|
split0(tokens, s, delimiter);
|
|
return tokens;
|
|
}
|
|
};
|
|
|
|
} // namespace maix::nn
|