一.环境

  1. QT5.14
  2. OpenCv 4.2.0

训练器使用的是OpenCV-3.4.1-x64版本的opencv_createsamples.exe和opencv_traincascade.exe文件。
高版本去除了这两个文件,有些低版本使用的时候会出现”应用程序无法正常启动(0xc000007b)。“的错误。(我之前使用OpenCv-3.3.1的时候就出现过)
下载链接:https://github.com/huihut/OpenCV-MinGW-Build

或者百度云:https://pan.baidu.com/s/1QnGZcaBoyGIIgCC_CvCR5g
提取码:hr3c

下载完成后,到对应路径的文件夹下找到bin文件夹,双击直接运行opencv_createsamples.exe和opencv_traincascade.exe这两个文件。要是没有弹出错误窗口的话就说明可以使用。
我的路径是这样子的D:\OpenCv\OpenCV-MinGW-Build-OpenCV-3.4.1-x64\x64\mingw\bin。

不能使用的话只能换其他低的版本。要是可以使用,就将bin文件夹的路径添加到环境变量(不会请百度),添加环境变量后就能在其他地方使用这两个exe文件了。

将opencv_createsamples.exe和opencv_traincascade.exe两个文件复制到一个新的文件夹中。改变了环境变量后一般需要重启。

opencv 训练原理 opencv训练器_xml文件


二.样本准备

正样本(需要识别的物体)

负样本(不包含需要识别的物体的照片)

正样本需要同样大小,并且为灰度图;负样本只需要为灰度图即可。

在刚才的文件夹中建立pos、neg、xml三个文件夹,分别用于存放正样本、负样本和训练出来的xml文件。

opencv 训练原理 opencv训练器_txt文件_02


我的正样本是使用电脑摄像头拍摄,然后opencv处理成8080大小的灰度图;负样本是使用手机的相册照片,经过opencv处理成500500的灰度图。

我的简单的处理代码如下:

Point pt(45,45);
int flag_neg = 0;   // 0 是80*80正样本,1 是负样本, 2 为自动从目录中读取
void on_mouse( int event, int x, int y, int , void* )//鼠标的回调函数
{
    if(event == EVENT_LBUTTONUP      )
    {
        pt.x = x;
        pt.y = y;
        //cvCircle( src, pt, 2,cvScalar(255,0,0,0) ,CV_FILLED, CV_AA, 0 );
    }
}

int main(int argc, char *argv[])
{
    readTemplate();
    tryFindresule();

    VideoCapture v(0);  //默认分辨率640*480
//    v.set(CAP_PROP_FRAME_WIDTH, 1280.0);    //设置分辨率
//    v.set(CAP_PROP_FRAME_HEIGHT, 960.0);
    Mat src, draw;
    char c = 0;
    int count = 666;
    namedWindow("src",CV_WINDOW_AUTOSIZE);

    cvSetMouseCallback( "src", on_mouse, &src );    
    while(v.read(src)){

        Point p1(pt.x-45, pt.y-45);
        Point p2(pt.x+45, pt.y+45);
        if(flag_neg == 0)
        {
            //rectangle(src, p1, p2, Scalar(0,0,255), 1, LINE_4, 0);
            rectangle(src, Point(pt.x-80, pt.y-80), Point(pt.x+80, pt.y+80), Scalar(0,0,255), 1, LINE_4, 0);
        }

        imshow("src",src);
        c = waitKey(30);
        if(c == 27)
        {
            break;
        }
        else if(c == 's')
        {
            if(flag_neg == 0)
            {
                if((pt.x - 40 > 0)&&(pt.y - 40 > 0))
                {
                    Mat image = src(Rect(pt.x-40, pt.y-40, 80, 80));
                    cvtColor(image, image, CV_BGR2GRAY);
                    imwrite(format("D:/QTopencv/work2/pos/lll%d.jpg",count),image);
                    count++;
                }
            }
            else if(flag_neg == 1)
            {

                Mat image;
                cvtColor(src, image, CV_BGR2GRAY);
                //resize(image,image,Size(500,500));
                imwrite(format("D:/QTopencv/work2/neg/rrr%d.jpg",count),image);
                count++;
            }
            else if(flag_neg == 2)
            {
                ifstream in("F:/meizu16x/data.txt");
                if(in)
                {
                    string line;

                    for(int ii=0; ii<400; ii++)
                    {
                        getline(in, line);
                        Mat image = imread(line);
                        Mat imagel;
                        cvtColor(image, imagel, CV_BGR2GRAY);
                        resize(imagel,imagel,Size(500,500));
                        imwrite(format("D:/QTopencv/work2/neg/rrr%d.jpg",count),imagel);
                        cout<<"image "<<count<<" is ok"<<endl;
                        count++;
                    }
                }
                else
                {
                    cout<<"no file"<<endl;
                }

            }

        }
    }

    v.release();
    destroyAllWindows();
    return 0;

}

其中,flag_neg = 2从目录中读取的方法,需要生成一个里面存有路径的txt文件。生成方法为同pos和neg的txt文件,下面会讲到。

三.样本处理

进入命令行输入命令:

d:

进入d盘

cd QTopencv\train\pos

打开文件夹,注意修改成你的pos文件夹路径

dir /s/b >pos.txt

生成目录文件

opencv 训练原理 opencv训练器_txt文件_03

同样的办法就能生成neg.txt和上面代码中的data.txt文件。

opencv 训练原理 opencv训练器_xml文件_04


opencv 训练原理 opencv训练器_xml文件_05


到pos文件夹里,打开pos.txt文件

opencv 训练原理 opencv训练器_xml文件_06


删掉最后一行的txt的路径,利用记事本的替换功能,删去前面的路径,并在后面加上 1 0 0 80 80。其中,1表示图片内有一个待识别的对象,0,0表示图片的左上角坐标,80,80表示右下角坐标。

opencv 训练原理 opencv训练器_txt文件_07


打开neg文件夹,neg.txt只需要删去txt文件的路径和图片的前半部分的路径。

opencv 训练原理 opencv训练器_xml文件_08


把处理好的pos.txt和neg.txt放到两个.exe文件的同级目录下。

opencv 训练原理 opencv训练器_opencv 训练原理_09


四.生成vec文件

从命令行进入opencv_createsamples.exe文件的目录下。

输入opencv_createsamples.exe -info pos.txt -vec pos.vec -num 56 -w 80 -h 80

其中,num 为正样本的数量,w,h为正样本的大小。

opencv 训练原理 opencv训练器_txt文件_10


此时的文件夹应该是这样子的

opencv 训练原理 opencv训练器_txt文件_11


五.训练xml文件

在命令行中输入

opencv_traincascade.exe -data xml -vec pos.vec -bg neg.txt -numPos 35 -numNeg 300 -numStages 15 -featureType LBP -w 80 -h 80

其中,numPos、numNeg是正负样本的数量,numStages是训练层数,featureType是训练方法。训练方法有HAAR 、 LBP 、 HOG。但是我使用其他的两种会报错,所以我只能用LBP方法进行训练。

由于我的样本数量比较少,所以并没有训练到15层,就出现了

Required leaf false alarm rate achieved. Branch training terminated.

大概就是说虚警率超标了,不再训练了。不是说训练出来的xml文件不能用,只是可能识别率不高。

opencv 训练原理 opencv训练器_opencv 训练原理_12

在xml文件夹中也生成了可以用于识别的xml文件

opencv 训练原理 opencv训练器_txt文件_13


最后,载入cascade.xml,使用detectMultiScale函数就能进行检测了。