资讯分类
全部资讯
最新活动
商城公告
行业信息
新品推荐
元器件知识
资讯标签
艾迈斯欧司朗(96) 英伟达NVIDIA(70) 艾为awinic(45) 飞腾派V3(37) 萤火工场(30) 罗彻斯特电子(28) Jetson Orin Nano SUPER 开发套件(21) 资料下载专区(18) 进迭时空(15) 艾为 awinic(15) 兆易创新(14) 赛昉科技(StarFive)(12) 灵动微电子(11) 小华半导体(9) 日清纺微电子(9) NVIDIA(8) Seeed矽递科技(8) 上海雷卯Leiditech(8) 微源半导体 LPSemi(8) 赛昉科技(8) 龙芯中科(7) GD32VW553-IOT(7) 物奇WuQi(7) 日清纺微电子Nisshinbo(5) 庆科(5) 开源口碑分享(5) 飞腾派(4) 创捷电子(4) TROQ创捷电子(4) Geehy极海半导体(4) 航顺芯片(4) CEM5826-M11(4) FTHR-G0001(3) 罗彻斯特(3) 飞腾派eMMC(3) iCEasy商城(3) MM32SPIN060G(3) 一件包邮专区(3) 英伟达初创加速计划(2) 极海(2) 中国星坤(2) 极海半导体(2) 小华EVB-HC32F4A0开发板(2) 国产开源硬件(2) 野火(2) 杭晶(HCI)(2) Mini-SPIN0230(2) NVIDIA品牌专区(2) 华润微电子(2) 口碑评测(2) EVB-L0130(2) 教育优惠申请链接(2) 启明云端(2) 飞腾官网飞腾派(2) 弈安云(2) 飞腾派下载专区(2) 村田(2) 飞腾派调研问卷链接(1) 微雪电子(1) 一件免邮专区(1) SFH 7018B(1) 极海半导体(Geehy)(1) Arduino品牌页(1) 英伟达 NVIDIA品牌专区(1) 小脚丫(1) 618大促专区(1) 艾为(AWINIC)(1) 飞虹半导体(1) MUSE Paper(1) 8月7日竞拍竞买(1) 飞腾派V3版本上新(1) 萤火工场GD32VW553-IOT(1) 野火RK3566开发板(1) 乐动机器人(1) 海凌科电子(Hi-Link)(1) VF202040-A0(1) 教育优惠(1) 松科智能(1) 润石(1) 格力新元(1) CEM5825F(1) COMBO模组 EMC3020-PZI5(1) 附件(1) Jetson AGX Thor 开发套件/模组(1) Mini-F5333(1) 微源半导体(1) 周年抽奖活动(1) 庆科信息(MXCHIP)品牌专区(1) 附件下载地址(1) 迈巨微(1) 华大北斗(1) GD32无线生态专区(1) JETSON_AGX_ORIN_64GB_DEVELOPER_KIT(1) QCS6490 SOM EVB开发板(1) 申请样片:高光效红光OSCONIQ® P 3030, GR QSSPA1.23(1) 台容积电(1) 雷克沙(1) GD32VW553-IOT V2版本(1) QCS8550 SOM EVB开发板(1) 商城下载专区(1) GD32VW553-IOT V2(1) CEM5881-M11(1) NVIDIA Jetson Orin Nano 开发套件(1) 萤火工场品牌专区(1) 进迭时空品牌页(1) 飞腾人才赋能(1) Jetson AGX Orin 64GB开发套件(1) 荣湃半导体(2Pai Semi)品牌专区(1) 下载专区(1) B站风扇视频(1) Jetson AGX Xavier(32GB)(1) BeagleBoard.org®(1) 社区评奖活动(1) Arduino UNO Rev 4 Minima开发板(1) 国产自主可控(1) 英伟达 NVIDIA(1) CEM5861G-M11(1) 赛昉科技9折活动券(1) NVIDIA Jetson Orin Nano 开发套件(1) Arduino品牌专区(1) 24GHz毫米波雷达模块(1) NVIDIA DGX™ Spark:桌面级AI超级计算机(1) 品牌清仓(1) QCS8550 SOM EVB(1) Jetson AGX Orin 64GB开发套件(1) 国产毫米波雷达模块(1) 技术问答专区(1) 英特尔(1) NVIDIA Jetson Nano 模组(1) QCS6490 SOM EVB(1) 天微电子商家页(1) 迈巨微电子品牌专区(1) 飞腾派专链(1) 瑞隆源产品(1) 龙芯教育派(1) CEK8902-S905D3(1) EV Board (MM32L0136C7P)(1) BeagleBone Black快速入门指南(1) 样片申请(1) NVIDIA NIM(1) 世野(Khadas)(1) 618大促(1) 竞买活动(1) 雷卯(1) 飞腾派试用申请链接(1)

VisionFive 2 机器视觉入门 2: 人脸检测

发布时间:2024-10-25

在前面的文章中介绍了`MNN`以及`OpenCV`的交叉编译,本篇文章将基于这两个库完成人脸检测。人脸检测(Face Detection),就是给一幅图像,找出图像中的所有人脸位置,通常用一个矩形框框起来。传统的人脸检测,通常使用`Haar`特征可以快速的检测人脸,在OpenCV中可以通过`CascadeClassifier`函数使用此分类器。然而在VisionFive中,`CascadeClassifier`检测效率并不高,在这里本文使用[Ultra-Light-Fast-Generic-Face-Detector-1MB](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB) 简称(Ultra)模型对人脸进行检测。Ultra模型是针对边缘计算设备设计的轻量人脸检测模型,模型较小,同时计算速度快。

代码时间

1. 设置编译器

$ export PATH=$PATH:${YOUR_PATH}/riscv/bin$ export CC=riscv64-unknown-linux-gnu-gcc$ export CXX=riscv64-unknown-linux-gnu-g++

2. 模型部分

MNN的使用较为简单,使用方法类似TensorFlow 1.x,简要的流程为

// 创建会话createSession();// 设置输入getSessionInput();// 运行会话runSession();// 获取输出getSessionOutput();

在该逻辑下,将模型的运行预处理,推理,后处理封装为一个模块:

// ultraFace.hpp#ifndef UltraFace_hpp#define UltraFace_hpp#pragma once#include "MNN/Interpreter.hpp"#include "MNN/MNNDefine.h"#include "MNN/Tensor.hpp"#include "MNN/ImageProcess.hpp"#include <opencv2/opencv.hpp>#include <algorithm>#include <iostream>#include <string>#include <vector>#include <memory>#include <chrono>#define num_featuremap 4#define hard_nms 1#define blending_nms 2 /* mix nms was been proposaled in paper blaze face, aims to minimize the temporal jitter*/typedef struct FaceInfo {    float x1;    float y1;    float x2;    float y2;    float score;} FaceInfo;class UltraFace {public:    UltraFace(const std::string &mnn_path,              int input_width, int input_length, int num_thread_ = 4, float score_threshold_ = 0.7, float iou_threshold_ = 0.3,              int topk_ = -1);    ~UltraFace();    int detect(cv::Mat &img, std::vector<FaceInfo> &face_list);private:    void generateBBox(std::vector<FaceInfo> &bbox_collection, MNN::Tensor *scores, MNN::Tensor *boxes);    void nms(std::vector<FaceInfo> &input, std::vector<FaceInfo> &output, int type = blending_nms);private:    std::shared_ptr<MNN::Interpreter> ultraface_interpreter;    MNN::Session *ultraface_session = nullptr;    MNN::Tensor *input_tensor = nullptr;    int num_thread;    int image_w;    int image_h;    int in_w;    int in_h;    int num_anchors;    float score_threshold;    float iou_threshold;    const float mean_vals[3] = {127, 127, 127};    const float norm_vals[3] = {1.0 / 128, 1.0 / 128, 1.0 / 128};    const float center_variance = 0.1;    const float size_variance = 0.2;    const std::vector<std::vector<float>> min_boxes = {            {10.0f,  16.0f,  24.0f},            {32.0f,  48.0f},            {64.0f,  96.0f},            {128.0f, 192.0f, 256.0f}};    const std::vector<float> strides = {8.0, 16.0, 32.0, 64.0};    std::vector<std::vector<float>> featuremap_size;    std::vector<std::vector<float>> shrinkage_size;    std::vector<int> w_h_list;    std::vector<std::vector<float>> priors = {};};#endif /* UltraFace_hpp */
// ultraFace.cpp#define clip(x, y) (x < 0 ? 0 : (x > y ? y : x))#include "UltraFace.hpp"using namespace std;UltraFace::UltraFace(const std::string &mnn_path,                     int input_width, int input_length, int num_thread_,                     float score_threshold_, float iou_threshold_, int topk_) {    num_thread = num_thread_;    score_threshold = score_threshold_;    iou_threshold = iou_threshold_;    in_w = input_width;    in_h = input_length;    w_h_list = {in_w, in_h};    for (auto size : w_h_list) {        std::vector<float> fm_item;        for (float stride : strides) {            fm_item.push_back(ceil(size / stride));        }        featuremap_size.push_back(fm_item);    }    for (auto size : w_h_list) {        shrinkage_size.push_back(strides);    }    /* generate prior anchors */    for (int index = 0; index < num_featuremap; index++) {        float scale_w = in_w / shrinkage_size[0][index];        float scale_h = in_h / shrinkage_size[1][index];        for (int j = 0; j < featuremap_size[1][index]; j++) {            for (int i = 0; i < featuremap_size[0][index]; i++) {                float x_center = (i + 0.5) / scale_w;                float y_center = (j + 0.5) / scale_h;                for (float k : min_boxes[index]) {                    float w = k / in_w;                    float h = k / in_h;                    priors.push_back({clip(x_center, 1), clip(y_center, 1), clip(w, 1), clip(h, 1)});                }            }        }    }    /* generate prior anchors finished */    num_anchors = priors.size();    ultraface_interpreter = std::shared_ptr<MNN::Interpreter>(MNN::Interpreter::createFromFile(mnn_path.c_str()));    MNN::ScheduleConfig config;    config.numThread = num_thread;    MNN::BackendConfig backendConfig;    backendConfig.precision = (MNN::BackendConfig::PrecisionMode) 2;    config.backendConfig = &backendConfig;    ultraface_session = ultraface_interpreter->createSession(config);    input_tensor = ultraface_interpreter->getSessionInput(ultraface_session, nullptr);}UltraFace::~UltraFace() {    ultraface_interpreter->releaseModel();    ultraface_interpreter->releaseSession(ultraface_session);}int UltraFace::detect(cv::Mat &raw_image, std::vector<FaceInfo> &face_list) {    if (raw_image.empty()) {        std::cout << "image is empty ,please check!" << std::endl;        return -1;    }    image_h = raw_image.rows;    image_w = raw_image.cols;    cv::Mat image;    cv::resize(raw_image, image, cv::Size(in_w, in_h));    ultraface_interpreter->resizeTensor(input_tensor, {1, 3, in_h, in_w});    ultraface_interpreter->resizeSession(ultraface_session);    std::shared_ptr<MNN::CV::ImageProcess> pretreat(            MNN::CV::ImageProcess::create(MNN::CV::BGR, MNN::CV::RGB, mean_vals, 3,                                          norm_vals, 3));    pretreat->convert(image.data, in_w, in_h, image.step[0], input_tensor);    auto start = chrono::steady_clock::now();    // run network    ultraface_interpreter->runSession(ultraface_session);    // get output data    string scores = "scores";    string boxes = "boxes";    MNN::Tensor *tensor_scores = ultraface_interpreter->getSessionOutput(ultraface_session, scores.c_str());    MNN::Tensor *tensor_boxes = ultraface_interpreter->getSessionOutput(ultraface_session, boxes.c_str());    MNN::Tensor tensor_scores_host(tensor_scores, tensor_scores->getDimensionType());    tensor_scores->copyToHostTensor(&tensor_scores_host);    MNN::Tensor tensor_boxes_host(tensor_boxes, tensor_boxes->getDimensionType());    tensor_boxes->copyToHostTensor(&tensor_boxes_host);    std::vector<FaceInfo> bbox_collection;    auto end = chrono::steady_clock::now();    chrono::duration<double> elapsed = end - start;    cout << "inference time:" << elapsed.count() << " s" << endl;    generateBBox(bbox_collection, tensor_scores, tensor_boxes);    nms(bbox_collection, face_list);    return 0;}void UltraFace::generateBBox(std::vector<FaceInfo> &bbox_collection, MNN::Tensor *scores, MNN::Tensor *boxes) {    for (int i = 0; i < num_anchors; i++) {        if (scores->host<float>()[i * 2 + 1] > score_threshold) {            FaceInfo rects;            float x_center = boxes->host<float>()[i * 4] * center_variance * priors[i][2] + priors[i][0];            float y_center = boxes->host<float>()[i * 4 + 1] * center_variance * priors[i][3] + priors[i][1];            float w = exp(boxes->host<float>()[i * 4 + 2] * size_variance) * priors[i][2];            float h = exp(boxes->host<float>()[i * 4 + 3] * size_variance) * priors[i][3];            rects.x1 = clip(x_center - w / 2.0, 1) * image_w;            rects.y1 = clip(y_center - h / 2.0, 1) * image_h;            rects.x2 = clip(x_center + w / 2.0, 1) * image_w;            rects.y2 = clip(y_center + h / 2.0, 1) * image_h;            rects.score = clip(scores->host<float>()[i * 2 + 1], 1);            bbox_collection.push_back(rects);        }    }}void UltraFace::nms(std::vector<FaceInfo> &input, std::vector<FaceInfo> &output, int type) {    std::sort(input.begin(), input.end(), [](const FaceInfo &a, const FaceInfo &b) { return a.score > b.score; });    int box_num = input.size();    std::vector<int> merged(box_num, 0);    for (int i = 0; i < box_num; i++) {        if (merged[i])            continue;        std::vector<FaceInfo> buf;        buf.push_back(input[i]);        merged[i] = 1;        float h0 = input[i].y2 - input[i].y1 + 1;        float w0 = input[i].x2 - input[i].x1 + 1;        float area0 = h0 * w0;        for (int j = i + 1; j < box_num; j++) {            if (merged[j])                continue;            float inner_x0 = input[i].x1 > input[j].x1 ? input[i].x1 : input[j].x1;            float inner_y0 = input[i].y1 > input[j].y1 ? input[i].y1 : input[j].y1;            float inner_x1 = input[i].x2 < input[j].x2 ? input[i].x2 : input[j].x2;            float inner_y1 = input[i].y2 < input[j].y2 ? input[i].y2 : input[j].y2;            float inner_h = inner_y1 - inner_y0 + 1;            float inner_w = inner_x1 - inner_x0 + 1;            if (inner_h <= 0 || inner_w <= 0)                continue;            float inner_area = inner_h * inner_w;            float h1 = input[j].y2 - input[j].y1 + 1;            float w1 = input[j].x2 - input[j].x1 + 1;            float area1 = h1 * w1;            float score;            score = inner_area / (area0 + area1 - inner_area);            if (score > iou_threshold) {                merged[j] = 1;                buf.push_back(input[j]);            }        }        switch (type) {            case hard_nms: {                output.push_back(buf[0]);                break;            }            case blending_nms: {                float total = 0;                for (int i = 0; i < buf.size(); i++) {                    total += exp(buf[i].score);                }                FaceInfo rects;                memset(&rects, 0, sizeof(rects));                for (int i = 0; i < buf.size(); i++) {                    float rate = exp(buf[i].score) / total;                    rects.x1 += buf[i].x1 * rate;                    rects.y1 += buf[i].y1 * rate;                    rects.x2 += buf[i].x2 * rate;                    rects.y2 += buf[i].y2 * rate;                    rects.score += buf[i].score * rate;                }                output.push_back(rects);                break;            }            default: {                printf("wrong type of nms.");                exit(-1);            }        }    }}

使用detect接口即可轻松获得输入图片中所有被检测脸的检测框

#include "UltraFace.hpp"#include <iostream>#include <opencv2/opencv.hpp>using namespace std;int main(int argc, char **argv) {    if (argc <= 2) {        fprintf(stderr, "Usage: %s <mnn .mnn> [image files...]\n", argv[0]);        return 1;    }    string mnn_path = argv[1];    UltraFace ultraface(mnn_path, 320, 240, 4, 0.65); // config model input    for (int i = 2; i < argc; i++) {        string image_file = argv[i];        cout << "Processing " << image_file << endl;        cv::Mat frame = cv::imread(image_file);        auto start = chrono::steady_clock::now();        vector<FaceInfo> face_info;        ultraface.detect(frame, face_info);        auto end = chrono::steady_clock::now();        for (auto face : face_info) {            cv::Point pt1(face.x1, face.y1);            cv::Point pt2(face.x2, face.y2);            cv::rectangle(frame, pt1, pt2, cv::Scalar(0, 255, 0), 2);        }        chrono::duration<double> elapsed = end - start;        cout << "all time: " << elapsed.count() << " s" << endl;        // cv::imshow("UltraFace", frame);        // cv::waitKey();        string result_name = "result" + to_string(i) + ".jpg";        cv::imwrite(result_name, frame);    }    return 0;}

运行结果

编译运行

$ cmake -Bbuild -S .$ cmake --build build

检测时间及检测效果如下:

user@starfive:~/Documents/u_face_bin$ sudo ./u_face_detect ./assets/slim/slim-320.mnn 3.bmpThe device support i8sdot:0, support fp16:0, support i8mm: 0Processing 3.bmpinference time:0.055068 sall time: 0.0691885 s

当然还可以检测视频中人脸,视频链接见下

https://mp.weixinbridge.com/mp/wapredirect?url=https%3A%2F%2Fwww.bilibili.com%2Fvideo%2FBV1mCbFeyEWQ%2F%3Fspm_id_from%3D333.999.0.0&action=appmsg_redirect&uin=MzgwODcwMTM2&biz=MzU1NDk5MzY1Nw==&mid=2247527275&idx=5&type=0&scene=0