推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

OpenCV编程:OpenCV3.X训练自己的分类器

   2023-08-21 网络整理佚名1990
核心提示:的官方已经提供了很多训练好的分类器文件,在的安装目录下有。下面编写QT程序,调用的级联分类器进行测试。下面的QT界面很简单,主要是为了测试分类器文件。这份QT代码只是为了简单的测试,就没有开线程去识别,如果识别耗时比较久的话,识别过程中UI界面会卡住,等一会即可。如果自己实际要检测的物体在自带的分类器里没有,或者自带的分类器识别精度不满足要求,就可以使用自带的分类器程序自己训练。

1.环境介绍

操作系统:64位

QT版本:5.12.6(在我的程序中,QT+主要用于图像处理和显示)

版本:.4.7

2.下载并安装

不需要下载源码,直接从官网下载编译好的文件并解压即可使用。

官网下载地址:下载后解压到指定目录即可。 这里我直接解压到C盘。

因为官网下载的版本是VC版本,而我的QT使用的是MinGW编译器,所以上面官网下载的安装包中的库无法使用,需要另外下载一个MinGW版本。 下载链接:

为什么我需要下载两个版本? 其实主要原因是MinGW版本中的两个训练分类器(.exe)文件在我的电脑上无法使用。 可能存在库冲突。 具体问题没有调查,所以干脆下载了另一个VC版本,就是VC版本中的.exe。 该文件可供正常使用。 其实下载的VC版本主要是使用这两个文件(.exe、.exe)

3.测试内置分类器 3.1 内置分类器文件介绍

官方已经提供了很多训练好的分类器文件,在安装目录下。

上面的文件提供了常见的人脸检测、眼睛检测、猫脸检测、行人检测等,可以看到XML文件的名称。

下面编写QT程序,调用级联分类器进行测试。

3.2 QT示例代码

下面的QT界面很简单,主要是测试分类器文件。

xxx.cpp文件代码:

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    dir="C:/";
    ui->label_source->setAlignment(Qt::AlignVCenter);
    ui->label_2->setAlignment(Qt::AlignVCenter);
}
Widget::~Widget()
{
    delete ui;
}
QImage Widget::Mat2QImage(const Mat& mat)
{
    // 8-bits unsigned, NO. OF CHANNELS = 1
    if(mat.type() == CV_8UC1)
    {
        QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
        // Set the color table (used to translate colour indexes to qRgb values)
        image.setColorCount(256);
        for(int i = 0; i < 256; i++)
        {
            image.setColor(i, qRgb(i, i, i));
        }
        // Copy input Mat
        uchar *pSrc = mat.data;
        for(int row = 0; row < mat.rows; row ++)
        {
            uchar *pDest = image.scanLine(row);
            memcpy(pDest, pSrc, mat.cols);
            pSrc += mat.step;
        }
        return image;
    }
    // 8-bits unsigned, NO. OF CHANNELS = 3
    else if(mat.type() == CV_8UC3)
    {
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        return image.rgbSwapped();
    }
    else if(mat.type() == CV_8UC4)
    {
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
        return image.copy();
    }
    else
    {
        return QImage();
    }
}
//打开本地图片
void Widget::on_pushButton_open_clicked()
{
    filename=QFileDialog::getOpenFileName(this,"选择打开的文件",dir,tr("*.bmp *.jpg *.png"));
    if(filename.isEmpty())return;
    QFileInfo info(filename);
    dir=info.path(); //保存当前路径
}
//人脸检测代码
void Widget::opencv_face(QImage qImage)
{
    QTime time;
    time.start();
    //定义级联分类器
    CascadeClassifier face_cascade;
    //加载分类文件
    //
    if( !face_cascade.load("C:/OpenCV_3.4.7/OpenCV-MinGW-Build-OpenCV-3.4.7/etc/haarcascades/haarcascade_frontalcatface.xml") )
    {
        qDebug()<<"haarcascade_frontalface_alt.xml 分类器加载错误";
        return;
    }
    Mat frame=QImage2cvMat(qImage);
    cvtColor( frame, frame, COLOR_BGR2GRAY );//转换成灰度图像
    std::vector faces;
    //正脸检测
    face_cascade.detectMultiScale(frame,faces);
    qDebug()<rame, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4 );
        rectangle(frame,
                  cvPoint(cvRound(faces[i].x), cvRound(faces[i].y)),
                  cvPoint(cvRound((faces[i].x + faces[i].width-1)),
                  cvRound((faces[i].y + faces[i].height-1))),
                  Scalar(255, 255, 255), 3, 8, 0);
        #endif
        //提取识别结果
        Mat frame1;
        for(size_t i=0;irame1= frame(Rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height));
        }
        
        ui->label_2->setPixmap(QPixmap::fromImage(Mat2QImage(frame1)));
    }
    
    QImage display_image=Mat2QImage(frame);
    ui->label_source->setPixmap(QPixmap::fromImage(display_image));
}
Mat Widget::QImage2cvMat(QImage image)
{
    Mat mat;
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
        break;
    case QImage::Format_RGB888:
        mat = Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
        cvtColor(mat, mat, CV_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
        mat = Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
        break;
    }
    return mat;
}
void Widget::on_pushButton_start_clicked()
{
      opencv_face(QImage(filename).scaled(ui->label_source->size(),Qt::KeepAspectRatio));
}

复制

xxx.h文件代码:

#ifndef WIDGET_H
#define WIDGET_H
#include 
#include "opencv2/core/core.hpp"
#include "opencv2/core/core_c.h"
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include 
#include 
#include 
using namespace cv;
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    QImage Mat2QImage(const Mat& mat);
    void opencv_face(QImage qImage);
    Mat QImage2cvMat(QImage image);
    QString dir;
    QString filename;
private slots:
    void on_pushButton_open_clicked();
    void on_pushButton_start_clicked();
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

复制

xxx.pro 文件

QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
ConFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
    main.cpp \
    widget.cpp
HEADERS += \
    widget.h
FORMS += \
    widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
#linu平台的路径设置
linux {
message('运行linu版本')
#添加opencv头文件的路径,需要根据自己的头文件路径进行修改
INCLUDEPATH+=/home/wbyq/work_pc/opencv-3.4.9/_install/install/include\
             /home/wbyq/work_pc/opencv-3.4.9/_install/install/include/opencv\
             /home/wbyq/work_pc/opencv-3.4.9/_install/install/include/opencv2
LIBS+=/home/wbyq/work_pc/opencv-3.4.9/_install/install/lib/libopencv_*
}
win32
{
    message('运行win32版本')
    #添加opencv头文件的路径,需要根据自己的头文件路径进行修改
    INCLUDEPATH+=C:/OpenCV_3.4.7/OpenCV-MinGW-Build-OpenCV-3.4.7/include \
                 C:/OpenCV_3.4.7/OpenCV-MinGW-Build-OpenCV-3.4.7/include/opencv \
                 C:/OpenCV_3.4.7/OpenCV-MinGW-Build-OpenCV-3.4.7/include/opencv2
    LIBS+=C:/OpenCV_3.4.7/OpenCV-MinGW-Build-OpenCV-3.4.7/x86/mingw/bin/libopencv_*.dll
}

复制

xxx.ui 文件:

3.3 测试人脸分类器的效果

将代码中的分类器文件替换为:.xml

这段QT代码只是为了简单测试,并没有线程来识别。 如果识别时间较长,识别过程中UI界面会卡住,稍等片刻即可。

3.4 测试猫脸分类器的效果

将代码中的分类器文件替换为:.xml

3.5 测试行人检测分类器的效果

将代码中的分类器文件替换为:.xml

4. 训练自己的分类器 4.1 前言

如果你真正要检测的物体不在内置分类器中,或者内置分类器的识别精度达不到要求,你可以使用内置分类器程序自行训练。 网上关于训练方法的教程有很多,我们就重复一下造轮子的过程,简单描述一下训练过程。

说明:因为下面的内容主要是简单描述训练过程,所以我准备的样本数量比较少。 如果您需要增加实际训练的样本数量,请参阅以下说明。

4.1 训练正负样本材料准备说明

如果想让计算器识别指定的物体,首先要让计算器知道你要识别的物体是什么样子的,需要提前学习。 在学习过程中,需要准备正样本和负样本。 正样本为待识别对象; 负样本用于与正样本进行比较。 负样本不包含正样本中的图片或相似图片,但不能随机选取。 最好与阳性样本处于同一环境。 ,效果最好,减少误识别。

样本图片最好使用灰度图像(即黑白图像); 样本越多越好,尽量高于1000,样本之间的差异越大越好,正负样本的比例可以是1:3,官方推荐的训练样本大小是最好的为20x20,示例图片的名称中不能有特殊字符,使用普通名称即可。

所有正样本的图像必须具有相同的尺寸。 如果不一致或者尺寸较大,可以先将所有样本缩放到20*20。

推荐一个简单的图像处理工具:

大小决定了训练时间的长短。 大尺寸也可以训练。 如果图片太小,很多细节就会丢失。 大小可以根据实际情况权衡,但是用太大的图片样本训练可能会导致内存不足。 可以根据训练效果和情况进行调整。

注意,为了提高训练精度,不能随机选择负样本。

例如:如果检测的正样本是车轮,那么负样本最好是车身、道路、护栏等环境。

4.2 正样本图像示例

以下是用于识别汽车的正样本图像。 您可以创建一个文件夹来存储正样本图像。

4.3 负样本图像示例

负样本图像可以存储在文件夹中。 负样本图像对样本大小没有要求,但必须大于或等于正样本大小; 并且负样本不能重复,必须增加负样本的差异。 负样本也应该进行灰度化,这与正样本相同。

4.4 创建工作目录

在计算机的任意目录下,创建一个工作目录,将存放正负样本的目录复制到该目录中,然后创建一个XML目录来存放生成的训练文件。

3.2 创建正样本描述文件

打开计算机命令行终端。

使用cd命令进入正样本目录。

执行命令如下:

命令1-进入到正样本目录下:cd /d D:\linux-share-dir\OpenCV_TrainingData\PositiveSample
命令2-将目录下所有图片名字和路径输出到pos.txt文件:dir /b/s/p/w *.jpg > pos.txt

复制

打开生成的pos.txt文件,内容如下。

稍微修改文件内容,添加检测目标数量、目标图像左上位置坐标、图像宽高参数。

我这里准备的样本图片尺寸都是40x40,所以要填写的代码:1 0 0 40 40

修改后的效果如下:

如果图片数量较多,手动修改比较麻烦,只需使用文本编辑器搜索替换即可。

3.3 创建负样本描述文件

负样本描述文件的创建方法与正样本描述文件的创建方法相同。 进入负样本图片所在目录,生成neg.txt文件。 代码如下:

命令1:cd /d D:\linux-share-dir\OpenCV_TrainingData\NegativeSample
命令2:dir /b/s/p/w *.jpg > neg.txt

复制

注意:负样本neg.txt文件不需要修改。 以下是最终文件。

3.4 生成正样本的.vec文件

为了方便填写路径,将生成的正负样本描述文件pos.txt和neg.txt复制到上级目录。

生成正样本的.vec文件,执行命令如下:

命令1:cd /d D:\linux-share-dir\OpenCV_TrainingData
命令2:C:\OpenCV_3.4.7\opencv-vc-3.4.7\build\x64\vc15\bin\opencv_createsamples.exe -vec pos.vec -info pos.txt -num 54 -w 40 -h 40

复制

参数介绍:

.exe:生成样本描述文件的可执行程序(自带),前面的路径是我的电脑。

-vec pos.vec 指定生成的vec文件

-info pos.txt 指定源样本的描述文件

-num 54 指定校准目标样本总数,即样本描述文件中第二列所有数字的总和。

-w 40 指定缩放后样本的宽度。 如果上一张图片不是40,那么就会缩放到40。有了这个参数,就可以省略前面的图片处理过程。

-h 40 指定缩放后样本的高度。 如果上一张图片不是40,那么就会缩放到40。有了这个参数,就可以省略前面的图片处理过程。

我电脑上的安装路径:

生成结果如下:

执行成功后,在当前目录下生成pos.vec文件。

注意:负样本不需要生成vec文件。

3.5 开始训练样本

命令1:cd /d D:\linux-share-dir\OpenCV_TrainingData
命令2:C:\OpenCV_3.4.7\opencv-vc-3.4.7\build\x64\vc15\bin\opencv_traincascade.exe -data XML -vec pos.vec -bg neg.txt -numPos 50 -numNeg 133 -numStages 20 -w 40 -h 40 -mode ALL

复制

参数介绍:

-data 指定输出目录,训练生成的xml文件放在该目录下

-vec 指定正样本生成的vec文件

-bg 指定负样本数据文件,即之前生成的neg.txt文件

- 指定阳性样本的数量。 该值必须小于准备的阳性样本数,否则将无法得到新的报告。

参考理由:影响各个强分类器的阈值。 当它设置为0.95时,如果正训练样本的数量为10,000,那么其中500个可能会背叛另一个作为负样本。 你必须在第二次选择中选择更多 对于接下来的 500 个,根据这个规则,我们为后续的每个级别添加*更多的正样本。 根据训练级别数,可以得到以下公式

+(-1)**(1-)《=准备好的训练样本

上式只是根据训练序列和准备的正样本之和来设置参与训练的正样本数量。 它只能用作估计。 小于计算出的数可能没有问题,但大于该数就一定有问题。

现在解释一下“可能有问题”是如何理解的:因为我们总是默认每次添加固定数量的正训练样本,但有时后面固定数量的正训练样本中可能存在不满足条件的样本,这些样本和我们排除的样本类似,所以比如说我们计划添加500个样本就足够了,但实际上需要添加600个,那么就会出现问题。

从上面例子的结果可以看出,我们允许每个级别丢弃12000*0.001个正样本=12。需要注意的是,如果第11个或第10个的阈值与第12个相同,那么我们前面的10个或者9个都丢失了,所以每次添加的数量可能会小于12个,超过12个的情况就是上面提到的“可能有问题”。

- 指定训练级别的数量

- 指定负样本的数量

-w 40 -h 40 指定样本图像的大小

-mode指定haar特征的类型,basio仅使用垂直特征,al1表示使用垂直和45度旋转特征

开始训练:

训练成功后,会在XML目录下生成.xml文件。 该文件是最终成功训练的文件,可以替换到上面的代码中进行测试。

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON