前言
1.这是一个是检测人员是否佩戴口罩的的安卓NDK项目,只检测人脸上是否有口罩是,是于检测是否佩戴标准,比如是否盖住嘴巴和鼻子,是否标准的医用的口罩的模型还在优化训练中。项目源码地址:https://download.youkuaiyun.com/download/matt45m/85252239
2.开发环境是win10,IDE是Android studio 北极狐,用到的库有NCNN,OpenCV,NCNN库可以用官方编译好的releases库,也可以按官方文档自己编译。OpenCV用的是nihui大佬简化过的opencv-mobile,大小只有10多M,如果不嫌大也可以用OpenCV官方的版本。测试使用的安卓手机是mate 30 pro ,有测试CPU和GPU两个性能。
3.项目的各种依赖版本:
一、模型训练
1.使用的数据集是开源的人脸口罩数据集:https://github.com/X-zhangyang/Real-World-Masked-Face-Dataset。数据集基本都是标佩戴。
2.所用的模型训练框架是 yolov5 lite ,在移动端上在保证精度的同时,如果能用上GPU加速,速度能达到10FPS以上。
3.把训练好的模型先onnx,简化之后再转ncnn的模型,这个可以参考ncnn的官网,如果觉得麻烦,也可以直接转pnnx之后直接转ncnn。
二、项目部署
1.创建一个Native C++项目
2.把下载好的NCNN和OpenCV so库粘贴到cpp目录下,这里只要arm64-v8a和armeadbi-v7a这两个库,这样的话,项目会小很多,但就是不能在安卓的虚拟机上运行,毕竟ncnn在虚拟机也是跑不动的。
3.添加推理代码
3.1 faceMask.h
#ifndef _FACE_FACEMASK_H_
#define _FACE_FACEMASK_H_
#include "net.h"
#include <opencv2/opencv.hpp>
#include "cpu.h" //安卓上有cpu推理优化
struct FaceInfo
{
cv::Rect location_;
float score_;
bool mask_;
float mask_prod;
};
class FaceMask
{
public:
FaceMask(AAssetManager *mgr,bool use_gpu); //如果机子有GPU ,这里可以设置成true,就可以使用GPU进行推理
~FaceMask();
int loadModel(AAssetManager *mgr,bool use_gpu);
int detectFace(const cv::Mat& img_src, std::vector<FaceInfo> *faces);
static FaceMask* face_mask;
private:
ncnn::Net mask_net;
std::vector<std::vector<cv::Rect>> anchors_generated;
bool initialized_;
const int RPNs_[3] = {
32, 16, 8 };
const cv::Size inputSize_ = {
640, 640 };
const float iouThreshold_ = 0.4f;
const float scoreThreshold_ = 0.8f;
const float maskThreshold_ = 0.2f;
};
#endif
3.2 faceMask.cpp
#include "faceMaks.h"
#include <iostream>
FaceMask::FaceMask(AAssetManager *mgr,bool use_gpu)
{
loadModel(mgr,use_gpu);
}
FaceMask::~FaceMask()
{
}
int RatioAnchors(const cv::Rect& anchor,const std::vector<float>& ratios,std::vector<cv::Rect>* anchors)
{
anchors->clear();
cv::Point center = cv::Point(anchor.x + (anchor.width - 1) * 0.5f,
anchor.y + (anchor.height - 1) * 0.5f);
float anchor_size = anchor.width * anchor.height;
#if defined(_OPENMP)
#pragma omp parallel for num_threads(threads_num)
#endif
for (int i = 0; i < static_cast<int>(ratios.size()); ++i) {
float ratio = ratios.at(i);
float anchor_size_ratio = anchor_size / ratio;
float curr_anchor_width = std::sqrt(anchor_size_ratio);
float curr_anchor_height = curr_anchor_width * ratio;
float curr_x = center.x - (curr_anchor_width - 1) * 0.5f;
float curr_y = center.y - (curr_anchor_height - 1) * 0.5f;
cv::Rect curr_anchor = cv::Rect(curr_x, curr_y,
curr_anchor_width - 1, curr_anchor_height - 1);
anchors->push_back(curr_anchor);
}
return 0;
}
int ScaleAnchors(const std::vector<cv::Rect>& ratio_anchors,const std::vector<float>& scales, std::vector<cv::Rect>* anchors)
{
anchors->clear();
#if defined(_OPENMP)
#pragma omp parallel for num_threads(threads_num)
#endif
for (int i = 0; i < static_cast<int>(ratio_anchors.size()); ++i)
{
cv::Rect anchor = ratio_anchors.at(i);
cv::Point2f center = cv::Point2f(anchor.x + anchor.width * 0.5f,
anchor.y + anchor.height * 0.5f);
for (int j = 0; j < static_cast<int>(scales.size()); ++j)
{
float scale = scales.at(j);
float curr_width = scale * (anchor.width + 1);
float curr_height = scale * (anchor.height + 1);
float curr_x = center.x - curr_width * 0.5f;
float curr_y = center.y - curr_height * 0.5f;
cv::Rect curr_anchor = cv::Rect(curr_x, curr_y,
curr_width, curr_height);
anchors->push_back(curr_anchor);
}
}
return 0;
}
int GenerateAnchors(const int& base_size,const std::vector<float>& ratios,const std::vector<float> scales,std::vector<cv::Rect>* anchors)
{
anchors->clear();
cv::Rect anchor = cv::Rect(0, 0, base_size, base_size);
std::vector<cv::Rect> ratio_anchors;
RatioAnchors(anchor, ratios, &ratio_anchors);
ScaleAnchors(ratio_anchors, scales, anchors);
return 0;
}
int FaceMask::loadModel(AAssetManager *mgr,bool use_gpu)
{
bool has_gpu = false;
#if NCNN_VULKAN
ncnn::create_gpu_instance();
has_gpu = ncnn::get_gpu_count() > 0;
#endif
bool to_use_gpu = has_gpu && use_gpu;
mask_net.opt.use_vulkan_compute = to_use_gpu;
ncnn::set_cpu_powersave(2);
ncnn::set_omp_num_threads(ncnn::get_big_cpu_count());
mask_net.opt.num_threads = ncnn::get_big_cpu_count();
mask_net.opt.use_int8_inference = true;
int rp = mask_net.load_param(mgr, "mask.param");
int rm = mask_net.load_model(mgr, "mask.bin");
// generate anchors
for (int i = 0; i < 3; ++i)
{
std::vector<cv::Rect> anchors;
if (0 == i)
{
GenerateAnchors(16, {
1.0f }, {
32, 16 }, &anchors)