工业缺陷检测项目实战(二)——基于深度学习框架yolov5的钢铁表面缺陷检测

2023-05-16

基于深度学习框架yolov5-6.0的钢铁表面缺陷检测

首先说明:
我做这个的主要目的,是利用opencv4.5.0的C++代码,加载深度学习模型框架进行检测识别。也就是说,在yolov5训练完后,用C++部署训练好的权重和网络模型,然后进行检测。这个过程,遇到特别多的坑,这里要给大家一一列出来,也防止我以后自己忘记哈哈哈哈。。。。
注意,方法适用于版本是最新的6.0版本

一. yolov5的模型学习
这里直接参考大佬们讲解,向大佬学习:
Yolov5 系列1— Yolo发展史以及Yolov5模型详解.
Yolov5 模型详解.

二. yolov5的训练过程
1.yolov5下载
https://github.com/ultralytics/yolov5
文件内容:
在这里插入图片描述
2. 安装环境
pip install -r requirements.txt

3.数据集整理
使用yolov5的模型进行训练,首先要配置好数据集,数据的格式如下:
在这里插入图片描述
其中必须的是:train,valid,data.yaml
一个是训练集,一个是验证集,一个是加载数据集(训练+验证)的配置文件,其实就是写个路径进去。我们分别看看里面的样子:
(1) train:
在这里插入图片描述
images就不用说了,都是.jpg文件,我们这里都是钢材表面带有缺陷的图片:
在这里插入图片描述
labels标签是txt文件,这是yolov5特定的:
在这里插入图片描述
注意的是,每个标签文件的名字一定要根图片的名字对应上。我的data文件夹里配置有xml文件转txt文件的py文件,即xml2yolo,py。我们看txt文件的内容:
在这里插入图片描述
第一列为类别,剩余的四个列分别为x,y,w,h。
每一行表示缺陷个数。

(2) valid:
在这里插入图片描述
和train一样。
(3) data.yaml:
内容:

train: /home/cg/机器视觉与机器学习/工业缺陷检测项目/钢材表面缺陷检测/NEU-DET/train/images
val: /home/cg/机器视觉与机器学习/工业缺陷检测项目/钢材表面缺陷检测/NEU-DET/valid/images

nc: 6
names: ['crazing', 'inclusion', 'patches', 'pitted_surface', 'rolled-in_scale', 'scratches']

train和val改成自己的路径
nc表示类别,我这里有六个缺陷类别,所以是6。names很明显是类别的名称。

4.开始训练
数据准备完毕了。我们开始训练。首先选择训练的模型,在moudles文件夹里:
在这里插入图片描述
假设我们选择yolov5s.yaml,打开之后,需要改的参数是那个nc,依然改成我们的分类数量。

接着运行

python3 train.py --data ../NEU-DET/data.yaml --cfg models/yolov5s.yaml --weights '' --batch-size 8

等待训练完成。

训练完成之后,会在runs文件夹生成train/exp文件夹,里面是这样:
在这里插入图片描述
里面的weights权重文件夹里的best.pt,是我们要用的权重文件。
值得注意的是,和yolov3和yolov4的权重文件.weight和模型文件.cfg都不同的是,yolov5的.pt同时包含了模型以及权重参数。

5.opencv4.5.0的C++部署yolov5的训练模型与权重。
5.1 opencv4.5.0里面配置的函数,没有直接读取.pt文件的,所以,我们要进行转化,将best.pt转为best.onnx。转化的方法:

python3 export.py --weights best.pt --opset 12

注意这只适用于6.0版本,要将参数opset由原来的13改为12,可以代码改,也可以参数改。

5.2 读取.onnx文件

string modelFile = "/home/cg/机器视觉与机器学习/工业缺陷检测项目/钢材表面缺陷检测/project/best";
modelFile += ".onnx";
this->net = readNet(modelFile);

当然啦路径要改。
5.3 检测函数

void YOLO::detect(Mat &frame)
{
    Mat blob;
    blobFromImage(frame, blob, 1 / 255.0, Size(this->inpWidth, this->inpHeight), Scalar(0, 0, 0), true, false);
    this->net.setInput(blob);
    vector<Mat> outs;
    this->net.forward(outs, this->net.getUnconnectedOutLayersNames());

    //generate proposals
    vector<int> classIds;
    vector<float> confidences;
    vector<Rect> boxes;
    cout << "classes:" << classes.size() << endl;

    float ratioh = (float)frame.rows / this->inpHeight, ratiow = (float)frame.cols / this->inpWidth;
    int n = 0, q = 0, i = 0, j = 0, nout = this->classes.size() + 5, row_ind = 0;
    for (n = 0; n < 3; n++) //stride
    {
        int num_grid_x = (int)(this->inpWidth / this->stride[n]);
        int num_grid_y = (int)(this->inpHeight / this->stride[n]);
        cout << "num_grid_x:" << num_grid_x << endl;
        cout << "num_grid_y:" << num_grid_y << endl;
        for (q = 0; q < 3; q++) ///anchors
        {
            const float anchor_w = this->anchors[n][q * 2];
            const float anchor_h = this->anchors[n][q * 2 + 1];
            for (i = 0; i < num_grid_y; i++)
            {
                for (j = 0; j < num_grid_x; j++)
                {
                    float *pdata = (float *)outs[0].data + row_ind * nout;
                    float box_score = pdata[4]; //sigmoid_x(pdata[4])
                    if (box_score > this->objThreshold)
                    {
                        //评分框
                        cv::Mat scores(1, classes.size(), CV_32FC1, pdata + 5);
                        Point classIdPoint;
                        double max_class_socre;
                        // 获取最高分数的值和位置
                        minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
                        max_class_socre = max_class_socre; // sigmoid_x((float)max_class_socre);
                        if (max_class_socre > this->confThreshold)
                        {
                            float cx = pdata[0]; //(sigmoid_x(pdata[0]) * 2.f - 0.5f + j) * this->stride[n]; ///cx
                            float cy = pdata[1]; // (sigmoid_x(pdata[1]) * 2.f - 0.5f + i) * this->stride[n]; ///cy
                            float w = pdata[2];  //powf(sigmoid_x(pdata[2]) * 2.f, 2.f) * anchor_w; ///w
                            float h = pdata[3];  //powf(sigmoid_x(pdata[3]) * 2.f, 2.f) * anchor_h; ///h

                            int left = (cx - 0.5 * w) * ratiow;
                            int top = (cy - 0.5 * h) * ratioh;

                            classIds.push_back(classIdPoint.x);
                            confidences.push_back(max_class_socre);
                            boxes.push_back(Rect(left, top, (int)(w * ratiow), (int)(h * ratioh)));
                        }
                    }
                    row_ind++;
                }
            }
        }
    }

    // Perform non maximum suppression to eliminate redundant overlapping boxes with
    // lower confidences

    vector<int> indices;
    NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices);

    cout << "num_grid_y:" << indices.size() << endl;

    for (size_t i = 0; i < indices.size(); ++i)
    {
        int idx = indices[i];
        Rect box = boxes[idx];
        this->drawPred(classIds[idx], confidences[idx], box.x, box.y,
                       box.x + box.width, box.y + box.height, frame);
    }
}

主要函数:
blobFromImage主要是用来对图片进行预处理。包含两个主要过程:
整体像素值减去平均值(mean)
通过缩放系数(scalefactor)对图片像素值进行缩放
详细参考:OpenCV中blobFromImage函数详细解释.

前向计算,检测结果存入outs

this->net.forward(outs, this->net.getUnconnectedOutLayersNames());

5.4 绘制锚框

void YOLO::drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat &frame) // Draw the predicted bounding box
{
    //Draw a rectangle displaying the bounding box
    rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 0, 255), 3);

    //Get the label for the class name and its confidence
    string label = format("%.2f", conf);
    label = this->classes[classId] + ":" + label;

    //Display the label at the top of the bounding box
    int baseLine;
    Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
    top = max(top, labelSize.height);
    //rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
    putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
}

效果:

在这里插入图片描述

因为机器视觉开发企业都要求C++,所以我用C++去读取best.pt获取识别结果。
需要代码请私聊

Cmake编译,记得有关加载文件的路径都要改。
此外,使用的时候,要更改下面四个地方:
(1) best文件的位置,即参数modelFile
在这里插入图片描述(2)识别的图像路径,即参数imgpath
在这里插入图片描述(3) classesFile
在这里插入图片描述(4)模型文件的选择
在这里插入图片描述在这里插入图片描述这里选择4。

目前,已经将算法封装为一个界面识别系统:
链接: 钢材缺陷检测系统-ui界面

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

工业缺陷检测项目实战(二)——基于深度学习框架yolov5的钢铁表面缺陷检测 的相关文章

随机推荐