【极市平台】口罩识别封装经验分享

1、配置环境并导出onnx模型

执行位置:模型开发实例中

1)下载yolov5模型:

yolov5仓库地址:https://github.com/XiaFire/cvmart-yolov5
下载该模型,并放置到/project/train/src_repo路径下,并改名为v5

2)创建export_onnx.sh文件

文件放置位置:/project/train/src_repo/v5/yolov5/
文件内容:

cd /project/train/src_repo/v5/yolov5

# 安装环境
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple onnx onnx-simplifier

# 导出命令
python export.py \
--data data/cvmart.yaml \
--weights /project/train/models/exp5/weights/best.pt \
--simplify \
--include onnx
  • –data data/cvmart.yaml # 该参数修改为自己的数据配置文件路径
  • –weights /project/train/models/exp5/weights/best.pt # 该参数修改为自己的模型路径
  • onnx>=1.9.0 onnx-simplifier>=0.3.6即可
    在模型开发 - 训练 - 新建训练任务 - 运行export_onnx.sh文件即可

3) 可视化onnx

工具网址: https://netron.app
输出维度: box(x_center,y_center,width,height) + box_score + 类别信息

2、下载封装代码并修改

执行位置:算法开发实例

1)下载封装代码并使用命令将其复制到现有的ev_sdk文件夹

gitee仓库地址:https://gitee.com/cvmart/ev_sdk_demo4.0_pedestrian_intrusion_yolov5.git

cp -r ev_sdk_demo4.0_pedestrian_intrusion_yolov5/* ./ev_sdk/

2) 修改配置文件

  • config/algo_config.json #与训练时配置文件编号顺序对应
"thresh": 0.01,
"mark_text_en": ["front_wear","front_no_wear","front_under_nose_wear","front_under_mouth_wear","front_unknown","side_wear","side_no_wear","side_under_nose_wear","side_under_mouth_wear","side_unknown","side_back_head_wear","side_back_head_no_wear","back_head","mask_front_wear","mask_front_under_nose_wear","mask_front_under_mouth_wear","mask_side_wear","mask_side_under_nose_wear","mask_side_under_mouth_wear","strap"],
"mark_text_zh": ["正面佩戴口罩包住鼻子","正面未佩戴口罩","正面佩戴口罩在鼻子下且在嘴巴上面","正面佩戴口罩在嘴巴下面","不能很明确知道正面是否佩戴口罩","侧面佩戴口罩包住鼻子","侧面未佩戴口罩","侧面佩戴口罩在鼻子下且在嘴巴上面","侧面佩戴口罩在嘴巴下面","不能很明确知道侧面是否佩戴口罩","带了口罩","没有戴口罩","背面人头","口罩包住鼻子","口罩在鼻子下且在嘴巴上面","口罩在嘴巴下面","口罩包住鼻子","口罩在鼻子下且在嘴巴上面","口罩在嘴巴下面","口罩的带子"],
  • src/Configuration.hpp
struct Configuration
{
    private:
    Json::Value mJConfigValue;
    public: 
    // 添加新变量alarmType、warningType
    std::vector<int> alarmType = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};//定义需检测的类别
    std::vector<int> warningType = {1,2,3,6,7,8};//定义需报警的类别
    // 修改现有变量targetRectTextMap
    std::map<std::string, std::vector<std::string> > targetRectTextMap = { {"en",{"front_wear","front_no_wear","front_under_nose_wear","front_under_mouth_wear","front_unknown","side_wear","side_no_wear","side_under_nose_wear","side_under_mouth_wear","side_unknown","side_back_head_wear","side_back_head_no_wear","back_head","mask_front_wear","mask_front_under_nose_wear","mask_front_under_mouth_wear","mask_side_wear","mask_side_under_nose_wear","mask_side_under_mouth_wear","strap"}}, {"zh", {"正面佩戴口罩包住鼻子","正面未佩戴口罩","正面佩戴口罩在鼻子下且在嘴巴上面","正面佩戴口罩在嘴巴下面","不能很明确知道正面是否佩戴口罩","侧面佩戴口罩包住鼻子","侧面未佩戴口罩","侧面佩戴口罩在鼻子下且在嘴巴上面","侧面佩戴口罩在嘴巴下面","不能很明确知道侧面是否佩戴口罩","带了口罩","没有戴口罩","背面人头","口罩包住鼻子","口罩在鼻子下且在嘴巴上面","口罩在嘴巴下面","口罩包住鼻子","口罩在鼻子下且在嘴巴上面","口罩在嘴巴下面","口罩的带子"}}};// 检测目标框顶部文字
  • src/SampleAlgorithm.cpp
    模型路径可在算法开发 - 调试 - 发起调试 - 模型列表中找到
//修改模型路径
STATUS SampleAlgorithm::Init(){
	mDetector->Init("/usr/local/ev_sdk/model/yolov5s.onnx", mConfig.algoConfig.thresh);
}

//修改业务逻辑 将过滤出行人部分代码替换为以下代码
STATUS SampleAlgorithm::Process(const cv::Mat &inFrame, const char *args, JiEvent &event){
	//过滤出目标
    for(auto iter = detectedObjects.begin(); iter != detectedObjects.end();)
    {
        SDKLOG_FIRST_N(INFO, 5) << "iter->label : " << iter->label; //输出当前正在迭代的标签
        if(find(mConfig.alarmType.begin(), mConfig.alarmType.end(), iter->label) != mConfig.alarmType.end())
        {
            iter++;
        }
        else
        {
            iter = detectedObjects.erase(iter);
        }
    }

    // 过滤出有效目标
    for (auto &obj : detectedObjects)
    {
        for (auto &roiPolygon : mConfig.currentROIOrigPolygons)
        {
            int mid_x = (obj.x1 + obj.x2) / 2;
            int mid_y = (obj.y1 + obj.y2) / 2;
            // 当检测的目标的中心点在ROI内的话,就视为闯入ROI的有效目标
            if (WKTParser::inPolygon(roiPolygon, cv::Point(mid_x, mid_y)) && (find(mConfig.warningType.begin(), mConfig.warningType.end(), obj.label) != mConfig.warningType.end()))
            {
                validTargets.emplace_back(obj);
            }           
        }
    }
}
  • src/SampleDetector.cpp
    runNms(DetObjs, 0.05); //修改置信度

3、编译测试

创建编译测试文件test.sh,并在算法开发 - 调试页运行该文件:
文件位置:与ev_sdk文件同级
文件内容:

# 删除已有的编译测试文件
rm -rf ./ev_sdk/build 
rm -rf ./ev_sdk/test/build
# 编译SDK库
mkdir -p /usr/local/ev_sdk/build
cd /usr/local/ev_sdk/build
cmake ..
make install 
# 编译测试工具
mkdir -p /usr/local/ev_sdk/test/build
cd /usr/local/ev_sdk/test/build
cmake ..
make install
# 测试
# /usr/local/ev_sdk/data/test1.jpeg 修改为自己的输入图片路径
# /usr/local/ev_sdk/data/result1.jpeg 修改为自己的输出图片路径
/usr/local/ev_sdk/bin/test-ji-api -f 1 -i /usr/local/ev_sdk/data/test1.jpeg -o /usr/local/ev_sdk/data/result1.jpeg

注意:每次修改代码后都需要删除之前的编译好的测试工具重新编译

4、提交封装测试

问题汇总

1)测试时onnx模型检测不到目标

解决方案:

1、可使用yolov5官方权重做预测,测试代码逻辑是否正确,需将类别0添加至报警类别,并使用包含多个人的图片作为输入图片,若可以检测到目标,则说明代码逻辑没有问题。
需修改如下代码:

  • src/SampleAlgorithm.cpp
mDetector->Init("/usr/local/ev_sdk/model/yolov5s.onnx", mConfig.algoConfig.thresh);
  • src/Configuration.hpp
std::vector<int> alarmType = {0};

2、使用编码环境训练出的权重做测试,测试导出的onnx模型是否有问题,需使用编码环境中训练集相似的图片作为输入图片,且将置信度与之调整为0.01
需修改代码如下

  • config/algo_config.json
"thresh": 0.01
  • src/SampleAlgorithm.cpp
mDetector->Init("/usr/local/ev_sdk/model/exp5/weights/best.onnx", mConfig.algoConfig.thresh);
  • src/Configuration.hpp
 std::vector<int> warningType = {1,2,3,6,7,8};

2)算法测试精度过低

如使用官方封装代码,需修改src/SampleAlgorithm.cpp文件中的业务逻辑,具体代码如修改配置文件一节所示。
口罩识别任务需要输出20个类别检测信息,其中有6个类别需要报警,因此需要先筛选出20个类别的有效目标,再从其中筛选出6个类别的需报警的目标。

你可能感兴趣的:(c++,目标检测)