opencv调用yolov5模型进行目标检测(c++版)

一、环境安装

1. miniconda

(1)安装准备

在终端中输入以下命令

sudo apt-get update         //更新包列表
sudo apt-get install build-essential     //安装构建必需工具
sudo apt-get install wget      //安装wget
(2)下载

官网:https://docs.conda.io/en/latest/miniconda.html

或者

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh

然后给文件添加运行权限

chmod +x Miniconda3-latest-Linux-x86_64.sh
(3)安装
./Miniconda3-latest-Linux-x86_64.sh

照着提示按步骤安装,过程中可以修改安装路径

(4)配置
sudo vim ~/.bashrc   //打开.bashrc文件

在文件末尾处添加

export PATH="/home/你的用户名/miniconda/bin:$PATH"  //这个是默认路径下的

保存退出后,使配置生效

source ~/.bashrc

可以添加清华的conda源来使用

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes
(5)使用
conda create -n yolov5 python=3.8  //创建名为yolov5且python版本3.8的虚拟环境
conda activate yolov5    //用来激活环境的

在该环境下可以使用conda install来安装包

2. yolov5

(1)下载

官网:https://github.com/ultralytics/yolov5

 点击全部发行版,选择自己需要的yolov5版本的源代码和预训练权重文件下载

注:我所使用opencv版本为v4.5.0,一开始使用yolov5-v7.0版本出现无法调用该模型的错误,后经过调试发现opencv4.5.0无法读取ai.onnx版本超过v12的onnx模型,后来yolov5版本改为v6.2可以正常使用
(2)安装依赖

把预训练模型放入yolov5文件夹后,打开requirements.txt。

#onnx>=1.9.0  # ONNX export
#onnx-simplifier>=0.4.1  # ONNX simplifier

将这两行开头的的#号去掉,保存,然后在之前创建的conda环境中执行

pip install -r requirements.txt
(3)将.pt文件转化为.onnx文件
python export.py --weights yolov5m6.pt --include onnx --imgsz 640 640

二、调用代码

我用一个json文件存放模型的对应参数信息,调用时将从文件里读取存入到一个封装的struct结构体里。

struct Net5_config
{
    std::vector CONFIDENCE_THRESHOLD; // 置信度
    std::vector NMS_THRESHOLD;        // nms阙值
    std::vector SCORE_THRESHOLD;        //得分阙值
    std::vector INPUT_WIDTH;  // 输入图像的宽度
    std::vector INPUT_HEIGHT; // 输入图像的长度
    std::vector classesFile;   //coco.name等物品分类名称文件路径
    std::vector modelWeights;   //模型权重文件路径
    std::vector netname;    //模型权重文件名称
    int id = 0;   //用于判断当前使用哪个模型
};

然后又写了一个类来实现调用

class Recognizerv5
{
public:

    Recognizerv5(Net5_config config);  //yolo初始化配置

    bool IdentifyTarget(cv::Mat &frame, Result &output); //yolo模型推理

    bool drawRect(cv::Mat &frame, Result &output);  //将识别结果画出来

private:

    float confThreshold;   //置信度阙值
    float nmsThreshold;   //nms阙值
    float SCORE_THRESHOLD;  //得分阙值
    int inpWidth;   //图片输入宽度
    int inpHeight;  //图片输入高度
    char netname[20];  //使用的模型的名字
    std::vector classes; //物品类别目录
    cv::dnn::Net net;  //用于模型推理
};

//存放识别的定位信息
struct Result
{
    std::vector x;
    std::vector y;
    std::vector width;
    std::vector height;
    std::vector name;
};

//清空定位信息
void Result_clear(struct Result &p)
{
    p.name.clear();
    p.x.clear();
    p.y.clear();
    p.height.clear();
    p.width.clear();
}

//用于存放识别出来的物品框的信息
struct Detection
{
    int class_id;  //物品类别下标
    float confidence;  //置信度
    cv::Rect box;   //方框信息
};

//用于复制图像
cv::Mat format_yolov5(const cv::Mat &source)
{
    int col = source.cols;
    int row = source.rows;
    int _max = MAX(col, row);
    cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);
    source.copyTo(result(cv::Rect(0, 0, col, row)));
    return result;
}

//yolo初始化配置
Recognizerv5::Recognizerv5(Net5_config config)
{
    //将数据从struct结构体里传入class里
    std::cout << "Net use " << config.netname[config.id] << std::endl;
    this->confThreshold = config.CONFIDENCE_THRESHOLD[config.id];
    this->nmsThreshold = config.NMS_THRESHOLD[config.id];
    this->inpWidth = config.INPUT_WIDTH[config.id];
    this->inpHeight = config.INPUT_HEIGHT[config.id];
    this->SCORE_THRESHOLD = config.SCORE_THRESHOLD[config.id];
    strcpy(this->netname, config.netname[config.id].c_str());
    std::ifstream ifs(config.classesFile[config.id].c_str());
    std::string line;
    while (getline(ifs, line))
        this->classes.push_back(line);
    //使用opencv的dnn模块读取模型文件
    auto result = cv::dnn::readNet(config.modelWeights[config.id]);
    //设置运行方式
    std::cout << "Running on CPU\n";
    result.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
    result.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
    //将读取结果存入net
    this->net = result;
}

//yolo模型推理
bool Recognizerv5::IdentifyTarget(cv::Mat &frame, Result &output)
{
    std::vector p;
    cv::Mat blob;
    auto input_image = format_yolov5(frame);
    //为推理模型设置参数
    cv::dnn::blobFromImage(input_image, blob, 1. / 255., cv::Size(this->inpWidth, this->inpHeight), cv::Scalar(), true, false);
    this->net.setInput(blob);
    //网络推理
    std::vector outputs;
    this->net.forward(outputs, net.getUnconnectedOutLayersNames());

    float x_factor = input_image.cols / this->inpWidth;
    float y_factor = input_image.rows / this->inpHeight;

    float *data = (float *)outputs[0].data;

    const int dimensions = 85;
    const int rows = 25200;

    std::vector class_ids;
    std::vector confidences;
    std::vector boxes;
    //对推理的结果进行检测
    for (int i = 0; i < rows; ++i)
    {

        float confidence = data[4];
        if (confidence >= this->confThreshold)  //如果置信度大于设置的阙值说明是可信的
        {

            float *classes_scores = data + 5;
            cv::Mat scores(1, this->classes.size(), CV_32FC1, classes_scores);
            cv::Point class_id;
            double max_class_score;
            minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
            if (max_class_score > SCORE_THRESHOLD)  //如果检测的分数大于阙值,则是可信的
            {

                confidences.push_back(confidence);

                class_ids.push_back(class_id.x);

                float x = data[0];
                float y = data[1];
                float w = data[2];
                float h = data[3];
                int left = int((x - 0.5 * w) * x_factor);
                int top = int((y - 0.5 * h) * y_factor);
                int width = int(w * x_factor);
                int height = int(h * y_factor);
                boxes.push_back(cv::Rect(left, top, width, height));
            }
        }

        data += 85;
    }

    std::vector nms_result;
    //进行极大值抑制nms算法
    cv::dnn::NMSBoxes(boxes, confidences,SCORE_THRESHOLD,this->nmsThreshold, nms_result,0.8);//eta这个参数默认1.0,范围0.1-1.0,设置代表启用soft-nms算法,top_k表示保留框的个数
    //将结果存入output
    for (int i = 0; i < nms_result.size(); i++)
    {
        int idx = nms_result[i];
        Detection result;
        result.class_id = class_ids[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx];
        p.push_back(result);
    }
    int detections = p.size();
    for (int i = 0; i < detections; ++i)
    {

        auto detection = p[i];
        auto box = detection.box;
        auto classId = detection.class_id;
        output.name.push_back(this->classes[classId]);
        output.x.push_back(box.x);
        output.y.push_back(box.y);
        output.width.push_back(box.width);
        output.height.push_back(box.height);
    }

    return true;
}

//将识别结果画出来
bool Recognizerv5::drawRect(cv::Mat &frame, Result &output)
{
    for (int i = 0; i < output.name.size(); i++)
    {
        cv::Rect box(output.x[i], output.y[i], output.width[i], output.height[i]);
        cv::rectangle(frame, box, cv::Scalar(0, 255, 0), 3);
        cv::rectangle(frame, cv::Point(box.x, box.y - 20), cv::Point(box.x + box.width, box.y), cv::Scalar(0, 255, 0), cv::FILLED);
        cv::putText(frame, output.name[i].c_str(), cv::Point(box.x, box.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
    }
    return true;
}

以上可以分装在namespcae里便于使用

main函数

int main(int argc, char const *argv[]){
    cv::VideoCapture camera=cv::VideoCapture(0);
    if (camera.isOpened() == false)
    {
        std::cout<< "camera couldn't open" <> image;
        if (image.empty() == true)
        {
            break;
        }
        cv::namedWindow("test",cv::WINDOW_NORMAL);
        YOLO::Result_clear(yolo_result);
        recognizer.IdentifyTarget(image, yolo_result);
        recognizer.drawRect(image, yolo_result);
        cv::imshow("test", image);
        if (cv::waitKey(1) > 0)
        {
            break;
        }
    }
    camera.release();
    cv::destroyAllWindows();
    return 0;
}

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