美文网首页
用opencv训练一个模型用于识别特定物体

用opencv训练一个模型用于识别特定物体

作者: JIAWEIJIAWEI | 来源:发表于2019-12-31 16:10 被阅读0次

windows 下OpenCV的安装部署详细教程

一、下载OpenCV
  到OpenCV官网下载你需要的版本。
  点击RELEASES(发布)

图片.png
由于OpenCV支持好多平台,比如Windows, Android, Maemo, FreeBSD, OpenBSD, iOS, Linux和Mac OS,一般初学者都是用windows,所以在这里下载Win pack
图片.png
点击Win pack 后跳出下面界面,等待5s自动下载。
图片.png
下载后是这样的
图片.png
然后双击他,解压,就是大佬们说的安装,实质就是解压一下,解压完出来一个文件夹,其他什么也没发生。你把这个文件夹放在哪都行,不过你要记住他在哪。
图片.png
正在解压
图片.png
解压完打开文件夹是这样的
图片.png
其中build是OpenCV使用时要用到的一些库文件,而sources中则是OpenCV官方为我们提供的一些demo示例源码
二、配置环境变量
  把OpenCV文件夹放好地方后,依次选择计算机—>属性—>高级系统设置—>环境变量,找到Path变量,选中并点击编辑,然后新建把你的OpenCV执行文件的路径填进去,然后一路点确定,这样环境变量就配置完了。
图片.png
OpenCV执行文件的路径这样找:
找到你解压好的OpenCV文件夹,依次选择build—>x64—>vc15—>bin,
然后是这样的
图片.png
这个路径就是我的OpenCV执行文件的路径,你的应该和我的差不多吧。
这里注意,如果你下载的是OpenCV2.x版本,选择build后,还需要选择x86或x64,然后是vc12(为什么不是vc10或vc11,一般都是选最新的),其他步骤大同小异。

三、部署OpenCV
  前面说了,OpenCV是一个SDK,得使用工具开发它,比如Visual Studio(当然有些大佬只用记事本或神一样的Vim),接下来就是在Visual Studio中部署OpenCV了。

  1. 安装Visual Studio
      因为主题是OpenCV,这个这里不讲了,请自行Google。

  2. 打开Visual Studio,新建工程
      初学者最好是建一个控制台工程,没有其他问题的干扰。

  3. 添加包含目录
      依次选择项目—>属性—>VC++目录—>包含目录—>编辑
      找到你的包含目录添加就可以了,最好添加三个,我的是这样的:
      D:\opencv\build\include
      D:\opencv\build\include\opencv
      D:\opencv\build\include\opencv2
      


    图片.png

    3.添加库目录
      依次选择项目—>属性—>VC++目录—>库目录—>编辑
      我的是D:\opencv\build\x64\vc15\lib


    图片.png
    如果编译报错 image.png
    去掉打勾 image.png

4.添加附加依赖项
  依次选择项目—>属性—>链接器—>输入—>附加依赖项—>编辑
  添加你的库文件名


图片.png

库文件这样找:


图片.png
有两个文件opencv_world341d.lib和opencv_world341.lib
  如果配置为Debug,选择opencv_world341d.lib
  如果为Release,选择opencv_world341.lib
  这里注意,如果你下载的是OpenCV2.x版本,这里的库文件比较多,都填进去就可以了。

到这里OpenCV的所有安装部署就结束了,可以进行下一步的使用和学习了。
参考链接:https://blog.csdn.net/maizousidemao/article/details/81474834

OpenCV Tutorials

https://docs.opencv.org/master/d9/df8/tutorial_root.html

版本3.4.7,4.0版本缺少了一些训练用的exe文件。。

1、 准备正负样本

最好为1:3。

正样本

image.png

最好自己拍,让物体占满整个画面,这样就不用后续去裁剪。Opencv有个opencv_annotations.exe的接口用于标记物体在图像中的位置(如在命令行

opencv_annotations -a=F:\test\Project1\Project1\annotations.txt -i=F:\test\Project1\Project1\fortest

,-a要生成的描述文件,-i负样本路径),不过不好用,因为后续还需要对正样本进行缩小,加快训练速度。缩小后位置就变化了。白做。缩小后再标记不现实,太小了不好标记。
所以最好让物体占满整个画面,就不需要进行标记或裁剪,后面说原因。

如何是要识别固定的物体,比如商标之类的,目标不存在太多变化,可以用一张图片通过各种扭曲生成多个正样本。CD进入图片文件夹中,命令行输入:

opencv_createsamples -vec pos.vec  -num 1000 -w 30 -h 30 -bg neg.txt -img C:\obeject_detetion\xsamples\pos_1\0.jpg

-vec 要生成的vec文件
-num要生成的样本数
-w、-h 要生成样本的宽和高
-bg 背景文件的描述文件目录(不能用绝对路径,会出错,把该文件放在正样本同一目录下)
-img 使用的一张正样本(用于扭曲生成多个正样本)

负样本


image.png

[图片上传中...(image.png-3cc16c-1577779623303-0)]
负样本比较随意,只需要是与检测场景相关就好。

2、对样本进行预处理
分别对正负样本进行灰度化处理,其中正样本还需要进行缩放处理。如缩小为4040,加快训练进度。灰度化处理可以使检测效果更好,因为检测时读取的每帧图像也都要进行灰度化。
处理后如图所示
正样本

image.png
每张都是40
40

负样本


image.png

大小不需要统一
一个对图片进行灰度化和缩放的C++程序,也可以用一些软件来完成,如ACDsee,PS等。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"

using namespace cv;
using namespace std;

int main(int argc, char *argv[])
{
    //const char* filename = argc >= 2 ? argv[1] : "./posresize/1.jpg";
    //string strInFileName = parser.get<String>("image");
    vector<String> filenames;
    String folder = "F:\\程序\\obeject_detetion(C)\\xsamples\\pos"; //要处理的图片目录
    glob(folder, filenames);
    cout << filenames.size() << endl;
    for (size_t i = 0; i < filenames.size(); ++i)
    {
        //cout << filenames[i] << endl;
        string str = filenames[i].substr(folder.length()-1);
        //cout << str<< endl;
        Mat src = imread(filenames[i], IMREAD_COLOR);

        if (src.empty())
        {
            printf(" Error opening image\n");
            cout << filenames[i] << endl; //printf(" Usage:\n %s [image_name-- default lena.jpg] \n", argv[0]);
            return EXIT_FAILURE;
        }
    
        Mat gray;
        cvtColor(src, gray, COLOR_BGR2GRAY); //灰度化
        resize(gray, gray, Size(30, 30), 0, 0, INTER_LINEAR); //缩放处理,负样本不需要
        imwrite("F:\\程序\\obeject_detetion(C)\\xsamples\\pos_1\\"+to_string(i)+".jpg", gray);//处理后的图片存放目录
        
        
        
    }


    return 0;
}

或者拍一段视频,获取对每一帧图片作为样本。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/video.hpp>
#include "opencv2/imgcodecs.hpp"


using namespace cv;
using namespace std;

int main(int argc, char **argv) //把视频每一帧保存为图片
{
    const string keys =
        "{ h help |      | print this help message }"
        "{ @image |M4.mp4| path to image file }"; //要处理的视频目录
    CommandLineParser parser(argc, argv, keys);
    if (parser.has("help"))
    {
        parser.printMessage();
        return 0;
    }
    string filename = parser.get<string>("@image");
    if (!parser.check())
    {
        parser.printErrors();
        return 0;
    }

    VideoCapture capture(filename);
    if (!capture.isOpened()){
        //error in opening the video input
        cerr << "Unable to open file!" << endl;
        return 0;
    }

   
    /*Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0,
        -1, 5, -1,
        0, -1, 0);
    namedWindow("Input", WINDOW_AUTOSIZE);
    namedWindow("Output", WINDOW_AUTOSIZE);*/
    Mat frame;

    // Take first frame and find corners in it
    int i = 0;
    capture >> frame;
    while (!frame.empty())
    {
        /*imshow("frame", frame);
        waitKey();*/
        //cvtColor(frame, frame, COLOR_BGR2GRAY);
        //resize(frame, frame, Size(100, 100), 0, 0, INTER_LINEAR);
        /*imshow("Input", frame);
        GaussianBlur(frame, frame,Size(5,5),1.5);
        filter2D(frame, frame, frame.depth(), kernel);
        imshow("Output", frame);
        waitKey();*/
        static int j = 0;
        j++;
        if (j > 5) //每隔几帧保存一次
        {
            imwrite("./videoresult/" + to_string(i++) + ".jpg", frame);//保存目录
            j = 0;
        }
        capture >> frame;
    }
    return 0;
}

3、生成正负样本描述文件
然后在命令行下 CD 进入正负样本路径下,分别执行

dir /b/s/p/w *.jpg > pos.txt
 dir /b/s/p/w *.jpg > neg.txt    

如图
正样本描述文件


image.png

然后把所有jpg 替换成jpg 1 0 0 40 40,也就是在每一行最后加上 1 0 0 40 40(1物体,x左上角x坐标, y左上角y坐标, w宽, h高)也就是物体所在的矩形位置。因为整个画面都是物体,只有一个物体,而且每一张大小都是40*40,所以是1 0 0 40 40。如图


image.png
负样本描述文件
image.png
负样本不需要做任何替换。

4、生成正样本的vec文件
命令行下使用命令cd进入正样本目录下后,

opencv_createsamples -info pos.txt -vec pos.vec  -num 4200 -w 30 -h 30

(-info 正样本描述文件,-vec 要生成的vec文件,-num要生成的正样本数, -w生成样本的宽,-h生成样本的高)宽和高要和预处理的w,h对应。
如果报错OpenCV(3.4.7) Error: Assertion failed (0 <= roi.x && 0 <= roi.width &,说明有部分图片不是和-w -h对应,好好检查一下是哪些图片,改

5、训练
命令行下

opencv_traincascade -data C:\obeject_detetion\newsamples\classfier -vec C:\obeject_detetion\newsamples\pos2_1\pos.vec -bg C:\obeject_detetion\newsamples\neg_1\neg.txt -w 30 -h 30 -precalcValBufSize 16384 -precalcIdxBufSize 16384  -maxFalseAlarmRate 0.1 -minHitRate 0.999 -mode ALL -numStages 10 -maxWeakCount 200 -numPos 2950 -numNeg 5100 

(要替换成自己的路径,参数什么的)

-data 生成的分类器的目录
-vec 上一步4中生成的vec文件目录
-bg 负样本描述文件目录
-numPos 每一阶段使用的正样本数(越多效果越好)
-numNeg 每一阶段使用的负样本数(和正样本数1:3最好,或者1:4)
-w 样本的宽
-h 样本的高
-precalcValBufSize 分配给训练中每阶段中每样本的内存,越大训练越快,但不能太大,不然会报内存不足的错误,因numPos和numNeg太大引起内存不足报错就减小该参数,不报错前提下越大越好
-precalcIdxBufSize 分配给训练中每阶段中每样本的内存,越大训练越快,但不能太大,不然会报内存不足的错误,因numPos和numNeg太大引起内存不足报错就减小该参数,不报错前提下越大越好,两个加起来不能超过电脑内存
-numStages 训练的阶段数,默认20,太多会过拟合,一般13左右就差不多了
-maxFalseAlarmRate 每一阶段负样本被误判的最大错误率,越小越好,不过训练越久
-minHitRate 每一阶段正样本被正确识别的最小正确率,越大越好,不过训练越久,默认0.995,一般不用设置,设置后-numpos不能设置为全部正样本数,会报数说不正样本数不够。。
-mode 训练使用的特征,默认BASIC只使用垂直的特征,ALL使用垂直和旋转45度的特征,效果更好,不过训练越久。

接下来就是等待训练完成了


image.png
image.png

N为迭代次数,HR为最小正确率,FA为最大错误率,HR和FA分别达到设置的数值后进入下一阶段,等全部阶段都训练完或许提前收敛了就会生成一个.xml的分类器文件,然后就可以用来识别啦!

如果到后面阶段卡住了,负样本加载很慢,是因为错误率已经很低了,或者负样本不够了,这时也可以提前结束训练,Ctrl+C结束, 然后再次输入训练命令,修改-numStages,比如在第5阶段卡住了,那就修改为-numStages 5,然后就会生成一个5层的分类器。

相关文章

网友评论

      本文标题:用opencv训练一个模型用于识别特定物体

      本文链接:https://www.haomeiwen.com/subject/odzboctx.html