From f3977b1183a4e762d35356b08c01286987602f21 Mon Sep 17 00:00:00 2001 From: Neucrack Date: Mon, 27 May 2024 17:34:44 +0800 Subject: [PATCH] add face recognizer --- components/basic/include/maix_fs.hpp | 7 + components/basic/src/maix_fs.cpp | 10 + .../nn/include/maix_nn_face_recognizer.hpp | 637 ++++++++++++++++++ .../nn/src/libmaix_nn_decoder_retinaface.cpp | 48 +- components/vision/include/maix_image.hpp | 2 +- components/vision/src/maix_image.cpp | 2 +- examples/key_demo/main/src/main.cpp | 3 +- examples/nn_face_detector/main/src/main.cpp | 2 +- examples/nn_face_recognizer/.gitignore | 9 + examples/nn_face_recognizer/README.md | 12 + .../nn_face_recognizer/main/CMakeLists.txt | 74 ++ examples/nn_face_recognizer/main/Kconfig | 0 .../nn_face_recognizer/main/include/main.h | 3 + examples/nn_face_recognizer/main/src/main.cpp | 75 +++ 14 files changed, 855 insertions(+), 29 deletions(-) create mode 100644 components/nn/include/maix_nn_face_recognizer.hpp create mode 100644 examples/nn_face_recognizer/.gitignore create mode 100644 examples/nn_face_recognizer/README.md create mode 100644 examples/nn_face_recognizer/main/CMakeLists.txt create mode 100644 examples/nn_face_recognizer/main/Kconfig create mode 100644 examples/nn_face_recognizer/main/include/main.h create mode 100644 examples/nn_face_recognizer/main/src/main.cpp diff --git a/components/basic/include/maix_fs.hpp b/components/basic/include/maix_fs.hpp index fd9128a0..f0075077 100644 --- a/components/basic/include/maix_fs.hpp +++ b/components/basic/include/maix_fs.hpp @@ -252,6 +252,13 @@ namespace maix::fs */ std::string *readline(); + /** + * End of file or not + * @return 0 if not reach end of file, else eof. + * @maixpy maix.fs.File.eof + */ + int eof(); + /** * Write data to file * @param buf buffer to write diff --git a/components/basic/src/maix_fs.cpp b/components/basic/src/maix_fs.cpp index b705dfe7..65af2698 100644 --- a/components/basic/src/maix_fs.cpp +++ b/components/basic/src/maix_fs.cpp @@ -338,6 +338,16 @@ namespace maix::fs return line; } + /** + * End of file or not + * @return 0 if not reach end of file, else eof. + * @maixpy maix.fs.File.eof + */ + int File::eof() + { + return std::feof((FILE*)_fp); + } + int File::write(const void *buf, int size) { // write data to file use std::fwrite diff --git a/components/nn/include/maix_nn_face_recognizer.hpp b/components/nn/include/maix_nn_face_recognizer.hpp new file mode 100644 index 00000000..e8ab77d1 --- /dev/null +++ b/components/nn/include/maix_nn_face_recognizer.hpp @@ -0,0 +1,637 @@ +/** + * @author neucrack@sipeed + * @copyright Sipeed Ltd 2023- + * @license Apache 2.0 + * @update 2024.5.17: Create this file. + */ + +#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 +// #include "maix_nn_face_detector.hpp" +#include "maix_nn_retinaface.hpp" + +namespace maix::nn +{ + /** + * Face object + * @maixpy maix.nn.FaceObject + */ + class FaceObject + { + private: + /* data */ + public: + /** + * Constructor + * @maixpy maix.nn.FaceObject.__init__ + * @maixcdk maix.nn.FaceObject.FaceObject + */ + FaceObject(int x = 0, int y = 0, int w = 0, int h = 0, int class_id = 0, float score = 0, std::vector points = std::vector(), std::vector feature = std::vector(), image::Image face = image::Image()) + : x(x), y(y), w(w), h(h), class_id(class_id), score(score), points(points), feature(feature), face(face) + { + } + + ~FaceObject() + { + } + + /** + * FaceObject info to string + * @return FaceObject info string + * @maixpy maix.nn.FaceObject.__str__ + * @maixcdk maix.nn.FaceObject.to_str + */ + std::string to_str() + { + return "x: " + std::to_string(x) + ", y: " + std::to_string(y) + ", w: " + std::to_string(w) + ", h: " + std::to_string(h) + ", class_id: " + std::to_string(class_id) + ", score: " + std::to_string(score); + } + + /** + * FaceObject left top coordinate x + * @maixpy maix.nn.FaceObject.x + */ + int x; + + /** + * FaceObject left top coordinate y + * @maixpy maix.nn.FaceObject.y + */ + int y; + + /** + * FaceObject width + * @maixpy maix.nn.FaceObject.w + */ + int w; + + /** + * FaceObject height + * @maixpy maix.nn.FaceObject.h + */ + int h; + + /** + * FaceObject class id + * @maixpy maix.nn.FaceObject.class_id + */ + int class_id; + + /** + * FaceObject score + * @maixpy maix.nn.FaceObject.score + */ + float score; + + /** + * keypoints + * @maixpy maix.nn.FaceObject.points + */ + std::vector points; + + /** + * feature, float list type + * @maixpy maix.nn.FaceObject.feature + */ + std::vector feature; + + /** + * face image + * @maixpy maix.nn.FaceObject.face + */ + image::Image face; + + private: + }; + + /** + * FaceRecognizer class + * @maixpy maix.nn.FaceRecognizer + */ + class FaceRecognizer + { + public: + public: + /** + * Constructor of FaceRecognizer class + * @param detect_model face detect model path, default empty, you can load model later by load function. + * @param feature_model feature extract model + * @throw If model arg is not empty and load failed, will throw err::Exception. + * @maixpy maix.nn.FaceRecognizer.__init__ + * @maixcdk maix.nn.FaceRecognizer.FaceRecognizer + */ + FaceRecognizer(const string &detect_model = "", const string &feature_model = "") + { + _model_feature = nullptr; + labels.push_back("unknown"); + _facedetector = nullptr; + if (!detect_model.empty() && !feature_model.empty()) + { + err::Err e = load(detect_model, feature_model); + if (e != err::ERR_NONE) + { + throw err::Exception(e, "load face detect model failed"); + } + } + } + + ~FaceRecognizer() + { + if (_facedetector) + { + delete _facedetector; + _facedetector = nullptr; + } + if (_model_feature) + { + delete _model_feature; + _model_feature = nullptr; + } + } + + /** + * Load model from file + * @param detect_model face detect model path, default empty, you can load model later by load function. + * @param feature_model feature extract model + * @return err::Err + * @maixpy maix.nn.FaceRecognizer.load + */ + err::Err load(const string &detect_model, const string &feature_model) + { + _facedetector = new Retinaface(); + err::Err e = _facedetector->load(detect_model); + if (e != err::ERR_NONE) + { + log::info("load detect model failed"); + return e; + } + _input_size = _facedetector->input_size(); + + // feature extract model + if (_model_feature) + { + delete _model_feature; + _model_feature = nullptr; + } + _model_feature = new nn::NN(feature_model); + if (!_model_feature) + { + delete _facedetector; + _facedetector = nullptr; + return err::ERR_NO_MEM; + } + _extra_info2 = _model_feature->extra_info(); + if (_extra_info2.find("model_type") != _extra_info2.end()) + { + if (_extra_info2["model_type"] != "face_feature") + { + log::error("model_type not match, expect 'face_feature', but got '%s'", _extra_info2["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: face_feature"); + if (_extra_info2.find("input_type") != _extra_info2.end()) + { + std::string input_type = _extra_info2["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_info2.find("mean") != _extra_info2.end()) + { + std::string mean_str = _extra_info2["mean"]; + std::vector mean_strs = split(mean_str, ","); + log::print("\tmean:"); + for (auto &it : mean_strs) + { + try + { + this->mean_feature.push_back(std::stof(it)); + } + catch (std::exception &e) + { + log::error("mean value error, should float"); + return err::ERR_ARGS; + } + log::print("%f ", this->mean_feature.back()); + } + log::print("\n"); + } + else + { + log::error("mean key not found"); + return err::ERR_ARGS; + } + if (_extra_info2.find("scale") != _extra_info2.end()) + { + std::string scale_str = _extra_info2["scale"]; + std::vector scale_strs = split(scale_str, ","); + log::print("\tscale:"); + for (auto &it : scale_strs) + { + try + { + this->scale_feature.push_back(std::stof(it)); + } + catch (std::exception &e) + { + log::error("scale value error, should float"); + return err::ERR_ARGS; + } + log::print("%f ", this->scale_feature.back()); + } + log::print("\n"); + } + else + { + log::error("scale key not found"); + return err::ERR_ARGS; + } + std::vector inputs = _model_feature->inputs_info(); + _feature_input_size = inputs[0].shape[2]; + _std_points = { + (int)(38.2946f * _feature_input_size / 112), + (int)(51.6963f * _feature_input_size / 112), + (int)(73.5318f * _feature_input_size / 112), + (int)(51.5014f * _feature_input_size / 112), + (int)(56.0252f * _feature_input_size / 112), + (int)(71.7366f * _feature_input_size / 112), + (int)(41.5493f * _feature_input_size / 112), + (int)(92.3655f * _feature_input_size / 112), + (int)(70.7299f * _feature_input_size / 112), + (int)(92.2041f * _feature_input_size / 112), + }; + 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 conf_th Detect confidence threshold, default 0.5. + * @param iou_th Detect IoU threshold, default 0.45. + * @param compare_th Compare two face score threshold, default 0.8, if two faces' score < this value, will see this face fas unknown. + * @param get_feature return feature or not, if true will copy features to result, if false will not copy feature to result to save time and memory. + * @param get_face return face image or not, if true result object's face attribute will valid, or face sttribute is empty. Get face image will alloc memory and copy image, so will lead to slower speed. + * @param fit Resize method, default image.Fit.FIT_CONTAIN. + * @throw If image format not match model input format, will throw err::Exception. + * @return FaceObject list. In C++, you should delete it after use. + * @maixpy maix.nn.FaceRecognizer.recognize + */ + std::vector *recognize(image::Image &img, float conf_th = 0.5, float iou_th = 0.45, float compare_th = 0.8, bool get_feature = false, bool get_face = false, maix::image::Fit fit = maix::image::FIT_CONTAIN) + { + this->_conf_th = conf_th; + this->_iou_th = iou_th; + std::vector *objs = _facedetector->detect(img, _conf_th, _iou_th, fit); + std::vector *faces = new std::vector(); + for (size_t i = 0; i < objs->size(); ++i) + { + // get std face + image::Image *std_img = img.affine(objs->at(i).points, _std_points, _feature_input_size, _feature_input_size); + // img.save("/root/test0.jpg"); + // std_img->save("/root/test.jpg"); + tensor::Tensors *outputs = _model_feature->forward_image(*std_img, this->mean_feature, this->scale_feature, fit, false); + if (!outputs) + { + delete std_img; + throw err::Exception("forward image failed"); + } + tensor::Tensor *out = outputs->tensors[outputs->get_names()[0]]; + int fea_len = out->size_int(); + float *feature = (float *)out->data(); + // compare feature from DB + float max_score = 0; + int max_i = -1; + for (size_t i = 0; i < features.size(); ++i) + { + float score = _feature_compare(feature, features[i].data(), fea_len); + if (score > max_score && score > compare_th) + { + max_score = score; + max_i = i; + } + } + nn::Object &obj = objs->at(i); + nn::FaceObject face(obj.x, obj.y, obj.w, obj.h, max_i + 1, max_score); + faces->push_back(face); + nn::FaceObject &face1 = faces->at(faces->size() - 1); + face1.points = obj.points; + if(get_feature) + { + face1.feature = std::vector(feature, feature + fea_len); + } + if(get_face) + { + face1.face = *std_img; + } + delete std_img; + delete outputs; + } + return faces; + } + + /** + * Add face to lib + * @param face face object, find by recognize + * @param label face label(name) + * @maixpy maix.nn.FaceRecognizer.add_face + */ + err::Err add_face(nn::FaceObject *face, const std::string &label) + { + if (face->feature.empty()) + { + log::error("face no feature"); + return err::ERR_ARGS; + } + labels.push_back(label); + features.push_back(face->feature); + return err::ERR_NONE; + } + + /** + * remove face from lib + * @param idx index of face in lib, default -1 means use label, idx and label must have one, idx have high priotiry. + * @param label which face to remove, default to empty string mean use idx, idx and label must have one, idx have high priotiry. + * @maixpy maix.nn.FaceRecognizer.remove_face + */ + err::Err remove_face(int idx = -1, const std::string &label = "") + { + if (idx == -1 && label.empty()) + { + log::info("idx and label must have one"); + return err::ERR_ARGS; + } + if (!label.empty()) + { + for (size_t i = 0; i < labels.size(); ++i) + { + if (labels[i] == label) + { + idx = i; + break; + } + } + } + if (idx >= 0 && (size_t)idx < features.size()) + { + features.erase(features.begin() + idx); + labels.erase(labels.begin() + idx + 1); + return err::ERR_NONE; + } + log::info("idx value error: %d", idx); + return err::ERR_ARGS; + } + + /** + * Save faces info to a file + * @param path where to save, string type. + * @return err.Err type + * @maixpy maix.nn.FaceRecognizer.save_faces + */ + err::Err save_faces(const std::string &path) + { + std::string dir = fs::dirname(path); + err::Err e = fs::mkdir(dir); + if (e != err::ERR_NONE) + { + return e; + } + fs::File *f = fs::open(path, "w"); + if (!f) + { + return err::ERR_IO; + } + for (size_t i = 0; i < features.size(); ++i) + { + // name + \0 + fea_len(2B) + feature + f->write(labels[i + 1].c_str(), (int)labels[i + 1].size()); + f->write("\0", 1); + uint16_t len = (uint16_t)features[i].size(); + f->write(&len, 2); + f->write(features[i].data(), features[i].size() * sizeof(float)); + } + f->flush(); + f->close(); + delete f; + return err::ERR_NONE; + } + + /** + * Load faces info from a file + * @param path from where to load, string type. + * @return err::Err type + * @maixpy maix.nn.FaceRecognizer.load_faces + */ + err::Err load_faces(const std::string &path) + { + // Open the file + fs::File *f = fs::open(path, "r"); + if (!f) + { + return err::ERR_IO; + } + + // Clear current data + features.clear(); + labels.clear(); + labels.push_back("unknown"); + + // Read from the file + while (!f->eof()) + { + std::string label; + char ch; + // Read label until '\0' + while (f->read(&ch, 1) && ch != '\0') + { + label += ch; + } + + // Read the length of the feature vector + uint16_t len; + if (f->read(&len, 2) != 2) + { + // Error handling if we cannot read length + f->close(); + delete f; + return err::ERR_IO; + } + + // Read the feature vector + std::vector feature(len); + if (f->read(feature.data(), len * sizeof(float)) != (int)(len * sizeof(float))) + { + // Error handling if we cannot read feature data + f->close(); + delete f; + return err::ERR_IO; + } + + // Add the data to the vectors + labels.push_back(label); + features.push_back(feature); + } + + // Close the file and clean up + f->close(); + delete f; + return err::ERR_NONE; + } + + /** + * Get model input size + * @return model input size + * @maixpy maix.nn.FaceRecognizer.input_size + */ + image::Size input_size() + { + return _input_size; + } + + /** + * Get model input width + * @return model input size of width + * @maixpy maix.nn.FaceRecognizer.input_width + */ + int input_width() + { + return _input_size.width(); + } + + /** + * Get model input height + * @return model input size of height + * @maixpy maix.nn.FaceRecognizer.input_height + */ + int input_height() + { + return _input_size.height(); + } + + /** + * Get input image format + * @return input image format, image::Format type. + * @maixpy maix.nn.FaceRecognizer.input_format + */ + image::Format input_format() + { + return _input_img_fmt; + } + + public: + /** + * Get mean value, list type + * @maixpy maix.nn.FaceRecognizer.mean_detector + */ + std::vector mean_detector; + + /** + * Get scale value, list type + * @maixpy maix.nn.FaceRecognizer.scale_detector + */ + std::vector scale_detector; + + /** + * Get mean value, list type + * @maixpy maix.nn.FaceRecognizer.mean_feature + */ + std::vector mean_feature; + + /** + * Get scale value, list type + * @maixpy maix.nn.FaceRecognizer.scale_feature + */ + std::vector scale_feature; + + /** + * labels, list type, first is "unknown" + * @maixpy maix.nn.FaceRecognizer.labels + */ + std::vector labels; + + /** + * features + * @maixpy maix.nn.FaceRecognizer.features + */ + std::vector> features; + + private: + image::Size _input_size; + image::Format _input_img_fmt; + nn::NN *_model_feature; + std::map _extra_info; + std::map _extra_info2; + float _conf_th = 0.5; + float _iou_th = 0.45; + std::vector> _anchor; // [[dense_cx, dense_cy, s_kx, s_ky],] + std::vector _variance; + Retinaface *_facedetector; + int _feature_input_size; + std::vector _std_points; + + private: + float _feature_compare(float *ftr0, float *ftr1, int len) + { + double sumcorr = 0; + double sumftr0 = 0; + double sumftr1 = 0; + + for (int i = 0; i < len; i++) + { + sumftr0 += ftr0[i] * ftr0[i]; + sumftr1 += ftr1[i] * ftr1[i]; + sumcorr += ftr0[i] * ftr1[i]; + } + return (0.5 + 0.5 * sumcorr / sqrt(sumftr0 * sumftr1)); + } + + static void split0(std::vector &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 split(const std::string &s, const std::string &delimiter) + { + std::vector tokens; + split0(tokens, s, delimiter); + return tokens; + } + }; + +} // namespace maix::nn diff --git a/components/nn/src/libmaix_nn_decoder_retinaface.cpp b/components/nn/src/libmaix_nn_decoder_retinaface.cpp index 4b6687b5..a3f1d4dc 100644 --- a/components/nn/src/libmaix_nn_decoder_retinaface.cpp +++ b/components/nn/src/libmaix_nn_decoder_retinaface.cpp @@ -225,30 +225,30 @@ nn::ObjectFloat* retinaface_get_priorboxes(libmaix_nn_decoder_retinaface_config_ return boxes; } -static void softmax(float *data, int stride, int n ) -{ - int i; - // int diff; - // float e; - float sum = 0; - float largest_i = data[0]; - - for (i = 0; i < n; ++i) - { - if (data[i + stride] > largest_i) - largest_i = data[i + stride]; - } - for (i = 0; i < n; ++i) - { - float value = expf(data[i + stride] - largest_i); - sum += value; - data[i + stride] = value; - } - for (i = 0; i < n; ++i) - { - data[i + stride] /= sum; - } -} +// static void softmax(float *data, int stride, int n ) +// { +// int i; +// // int diff; +// // float e; +// float sum = 0; +// float largest_i = data[0]; + +// for (i = 0; i < n; ++i) +// { +// if (data[i + stride] > largest_i) +// largest_i = data[i + stride]; +// } +// for (i = 0; i < n; ++i) +// { +// float value = expf(data[i + stride] - largest_i); +// sum += value; +// data[i + stride] = value; +// } +// for (i = 0; i < n; ++i) +// { +// data[i + stride] /= sum; +// } +// } int retinaface_decode(float* net_out_loc, float* net_out_conf, float* net_out_landmark, nn::ObjectFloat* prior_boxes, std::vector *faces, int* boxes_num, bool chw, libmaix_nn_decoder_retinaface_config_t* config) { diff --git a/components/vision/include/maix_image.hpp b/components/vision/include/maix_image.hpp index bf924c51..f0fd6f3b 100644 --- a/components/vision/include/maix_image.hpp +++ b/components/vision/include/maix_image.hpp @@ -67,7 +67,7 @@ namespace maix::image } ~Image(); - void operator=(image::Image &img); + void operator=(const image::Image &img); //************************** get and set basic info **************************// diff --git a/components/vision/src/maix_image.cpp b/components/vision/src/maix_image.cpp index 8ee87fef..42a54517 100644 --- a/components/vision/src/maix_image.cpp +++ b/components/vision/src/maix_image.cpp @@ -333,7 +333,7 @@ namespace maix::image } } - void Image::operator=(image::Image &img) + void Image::operator=(const image::Image &img) { _format = img._format; _width = img._width; diff --git a/examples/key_demo/main/src/main.cpp b/examples/key_demo/main/src/main.cpp index c6c33f32..8fe08a04 100644 --- a/examples/key_demo/main/src/main.cpp +++ b/examples/key_demo/main/src/main.cpp @@ -25,8 +25,7 @@ int _main(int argc, char* argv[]) display::Display screen = display::Display(); log::info("screen size: %dx%d", screen.width(), screen.height()); - // Init key will cancel the default ok button function(exit app) - // key::Key key = key::Key(on_key); + key::Key key = key::Key(on_key); // If you want to exit app when press ok button, you can just call add_default_listener(), more see it's definition. // key::add_default_listener(); log::info("Init key ok"); diff --git a/examples/nn_face_detector/main/src/main.cpp b/examples/nn_face_detector/main/src/main.cpp index 454a6e63..8fdd7ec1 100644 --- a/examples/nn_face_detector/main/src/main.cpp +++ b/examples/nn_face_detector/main/src/main.cpp @@ -3,7 +3,7 @@ #include "maix_vision.hpp" #include "main.h" -#define USE_RETINAFACE 0 +#define USE_RETINAFACE 1 #if USE_RETINAFACE #include "maix_nn_retinaface.hpp" diff --git a/examples/nn_face_recognizer/.gitignore b/examples/nn_face_recognizer/.gitignore new file mode 100644 index 00000000..7171eaac --- /dev/null +++ b/examples/nn_face_recognizer/.gitignore @@ -0,0 +1,9 @@ +build +dist +.config.mk +.flash.conf.json +data + +/CMakeLists.txt + +__pycache__ diff --git a/examples/nn_face_recognizer/README.md b/examples/nn_face_recognizer/README.md new file mode 100644 index 00000000..3457324d --- /dev/null +++ b/examples/nn_face_recognizer/README.md @@ -0,0 +1,12 @@ +face detect based on MaixCDK +==== + +Model download from: + + https://maixhub.com/model/zoo/377 (face_detector https://github.com/biubug6/Face-Detector-1MB-with-landmark) + https://maixhub.com/model/zoo/378 (retinafate https://github.com/biubug6/Pytorch_Retinaface) + + + +Build method please visit [MaixCDK](https://github.com/sipeed/MaixCDK). + diff --git a/examples/nn_face_recognizer/main/CMakeLists.txt b/examples/nn_face_recognizer/main/CMakeLists.txt new file mode 100644 index 00000000..0334c61e --- /dev/null +++ b/examples/nn_face_recognizer/main/CMakeLists.txt @@ -0,0 +1,74 @@ +############### Add include ################### +list(APPEND ADD_INCLUDE "include" + ) +list(APPEND ADD_PRIVATE_INCLUDE "") +############################################### + +############ Add source files ################# +# list(APPEND ADD_SRCS "src/main.c" +# "src/test.c" +# ) +append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS +# list(REMOVE_ITEM COMPONENT_SRCS "src/test2.c") +# FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") +# FILE(GLOB EXTRA_SRC "src/*.c") +# list(APPEND ADD_SRCS ${EXTRA_SRC}) +# aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS +# append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS +# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") +# set(ADD_ASM_SRCS "src/asm.S") +# list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) +# SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language +# SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") +############################################### + +###### Add required/dependent components ###### +list(APPEND ADD_REQUIREMENTS basic nn vision) +############################################### + +###### Add link search path for requirements/libs ###### +# list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") +# list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here +# set (OpenCV_DIR opencv/lib/cmake/opencv4) +# find_package(OpenCV REQUIRED) +############################################### + +############ Add static libs ################## +# list(APPEND ADD_STATIC_LIB "lib/libtest.a") +############################################### + +#### Add compile option for this component #### +#### Just for this component, won't affect other +#### modules, including component that depend +#### on this component +# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) + +#### Add compile option for this component +#### and components denpend on this component +# list(APPEND ADD_DEFINITIONS -DAAAAA222=1 +# -DAAAAA333=1) +############################################### + +############ Add static libs ################## +#### Update parent's variables like CMAKE_C_LINK_FLAGS +# set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) +############################################### + +######### Add files need to download ######### +# list(APPEND ADD_FILE_DOWNLOADS "{ +# 'url': 'https://*****/abcde.tar.xz', +# 'urls': [], # backup urls, if url failed, will try urls +# 'sites': [], # download site, user can manually download file and put it into dl_path +# 'sha256sum': '', +# 'filename': 'abcde.tar.xz', +# 'path': 'toolchains/xxxxx', +# 'check_files': [] +# }" +# ) +# +# then extracted file in ${DL_EXTRACTED_PATH}/toolchains/xxxxx, +# you can directly use then, for example use it in add_custom_command +############################################## + +# register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib +register_component() diff --git a/examples/nn_face_recognizer/main/Kconfig b/examples/nn_face_recognizer/main/Kconfig new file mode 100644 index 00000000..e69de29b diff --git a/examples/nn_face_recognizer/main/include/main.h b/examples/nn_face_recognizer/main/include/main.h new file mode 100644 index 00000000..45dcbb04 --- /dev/null +++ b/examples/nn_face_recognizer/main/include/main.h @@ -0,0 +1,3 @@ +#pragma once + + diff --git a/examples/nn_face_recognizer/main/src/main.cpp b/examples/nn_face_recognizer/main/src/main.cpp new file mode 100644 index 00000000..22595589 --- /dev/null +++ b/examples/nn_face_recognizer/main/src/main.cpp @@ -0,0 +1,75 @@ + +#include "maix_basic.hpp" +#include "maix_vision.hpp" +#include "main.h" + +#include "maix_nn_face_recognizer.hpp" + +using namespace maix; + +int _main(int argc, char *argv[]) +{ + log::info("Program start"); + std::string model_type = "unknown"; + int ret = 0; + err::Err e; + char tmp_chars[64] = {0}; + + std::string help = "Usage: " + std::string(argv[0]) + " detect_model_path feature_model_path"; + + if (argc < 2) + { + log::info(help.c_str()); + return -1; + } + + const char *detect_model_path = argv[1]; + const char *feature_model_path = argv[2]; + float conf_threshold = 0.4; + float iou_threshold = 0.45; + + nn::FaceRecognizer recognizer; + + e = recognizer.load(detect_model_path, feature_model_path); + err::check_raise(e, "load model failed"); + log::info("open camera now"); + maix::image::Size input_size = recognizer.input_size(); + camera::Camera cam = camera::Camera(input_size.width(), input_size.height(), recognizer.input_format()); + log::info("open camera success"); + display::Display disp = display::Display(); + while (!app::need_exit()) + { + uint64_t t = time::time_ms(); + maix::image::Image *img = cam.read(); + err::check_null_raise(img, "read camera failed"); + std::vector *result = recognizer.recognize(*img, conf_threshold, iou_threshold); + for (auto &r : *result) + { + img->draw_rect(r.x, r.y, r.w, r.h, maix::image::Color::from_rgb(255, 0, 0)); + snprintf(tmp_chars, sizeof(tmp_chars), "%s:%.2f", recognizer.labels[r.class_id].c_str(), r.score); + img->draw_string(r.x, r.y, tmp_chars, maix::image::Color::from_rgb(255, 0, 0)); + int radius = ceil(r.w / 10); + img->draw_keypoints(r.points, image::COLOR_RED, radius > 4 ? 4 : radius); + } + disp.show(*img); + delete result; + delete img; + log::info("time: %d ms", time::time_ms() - t); + } + + log::info("Program exit"); + + return ret; +} + +int main(int argc, char *argv[]) +{ + // Catch SIGINT signal(e.g. Ctrl + C), and set exit flag to true. + signal(SIGINT, [](int sig) + { app::set_exit_flag(true); }); + + // Use CATCH_EXCEPTION_RUN_RETURN to catch exception, + // if we don't catch exception, when program throw exception, the objects will not be destructed. + // So we catch exception here to let resources be released(call objects' destructor) before exit. + CATCH_EXCEPTION_RUN_RETURN(_main, -1, argc, argv); +}