利用OpenCV自带的haar training程序训练一个分类器,需要经过以下几个步骤:
(1)收集训练样本:
训练样本包括正样本和负样本。正样本,通俗点说,就是图片中只有你需要的目标。而负样本的图片只要其中不含有目标就可以了。但需要说明的是,负样本也并非随便选取的。例如,你需要检测的目标是汽车,那么正样本就应该是仅仅含有汽车的图片,而负样本显然不能是一些包含天空的,海洋的,风景的图片。因为你最终训练分类器的目的是检测汽车,而汽车应该出现在马路上。也就是说,分类器最终检测的图片应该是那些包含马路,交通标志,建筑物,广告牌,汽车,摩托车,三轮车,行人,自行车等在内的图片。很明显,这里的负样本应该是包含摩托车、三轮车、自行车、行人、路面、灌木丛、花草、交通标志、广告牌等。
另外,需要提醒的是,adaboost方法也是机器学习中的一个经典算法,而机器学习算法的前提条件是,测试样本和训练样本独立同分布。所谓的独立同分布,可以简单理解为:训练样本要和最终的应用场合非常接近或者一致。否则,基于机器学习的算法并不能保证算法的有效性。此外,足够的训练样本(至少得几千张正样本、几千张负样本)也是保证训练算法有效性的一个前提条件。
这里,假设所有的正样本都放在f:/pos文件夹下,所有的负样本都放在f:/neg文件夹下;
(2)对所有的正样本进行尺寸归一化:
上一步收集到的正样本,有很多的尺寸大小,有的是200*300,有的是500*800...尺寸归一化的目的,就是把所有的图片都缩放到同一大小。比如,都缩放到50*60的大小。
(3)生成正样本描述文件:
所谓的正样本描述文件,其实就是一个文本文件,只不过,很多人喜欢将这个文件的后缀改成.dat而已。正样本描述文件中的内容包括:文件名 目标个数 目标缓腊在图片中的位置(x,y,width,height)
典型的正样本描述文件如下所示:
0.jpg 1 0 0 30 40
1.jpg 1 0 0 30 40
2.jpg 1 0 0 30 40
.....
不难发现,正样本描述文件中,每一个正样本占一行,每一行以正样本图片开头,后面紧跟着该图片中正样本的数量(通常为1),以及正样本在图片中的位置
假如,f:\pos文件夹下有5000个正样本图片,每个图片中仅有一个目标。那么,我们可以写程序(遍历文件夹中的所有图片文件,将文件名写入到文件中,将正样本在图片中的位置,大小都写入文件中)生成一个pos.dat文件作为正样本描述文件。
(4)创建正样本vec文件
由于haarTraining训练的时候需要输入的正样本是vec文件,所以需要使用createsamples程序来将正乎昌样本转换为vec文件。
打开OpenCV安装目录下bin文件夹里面的名为createSamples(新版本的OpenCV里面改名为opencv_createSamples)的可执行程序。需要提醒的是,该程序应该通过命令行启动(可以参考我的另一篇博客:http://blog.csdn.net/carson2005/article/details/6704589 )。并设置正样本所在的路径以及生成的正样本文件保存路劲(例如:f:\pos\pos.vec)。
Createsamples程序的命令行参数:
命令行参数:
-vec <vec_file_name>
训练好的正样本的输出文件名。
-img<image_file_name>
源目标图片(例如:一个公司图标)
-bg<background_file_name>
背景描述文件。
-num<number_of_samples>
要产生的正样本的数量,和正样本图片数目相同。
-bgcolor<background_color>
背景色(假定当前图片为灰度图)。背景岁哪扒色制定了透明色。对于压缩图片,颜色方差量由bgthresh参数来指定。则在bgcolor-bgthresh和bgcolor+bgthresh中间的像素被认为是透明的。
-bgthresh<background_color_threshold>
-inv
如果指定,颜色会反色
-randinv
如果指定,颜色会任意反色
-maxidev<max_intensity_deviation>
背景色最大的偏离度。
-maxangel<max_x_rotation_angle>
-maxangle<max_y_rotation_angle>,
-maxzangle<max_x_rotation_angle>
最大旋转角度,以弧度为单位。
-show
如果指定,每个样本会被显示出来,按下"esc"会关闭这一开关,即不显示样本图片,而创建过程继续。这是个有用的debug选项。
-w<sample_width>
输出样本的宽度(以像素为单位)
-h《sample_height》
输出样本的高度,以像素为单位。
(5) 创建负样本描述文件
在保存负样本的文件夹下生成一个负样本描述文件,具体步骤同(3),此处不再赘叙;
(6)进行样本训练
该步骤通过调用OpenCV\bin目录下的haartraining程序(新版本的opencv改名为opencv_haartraining)来完成。其中,Haartraining的命令行参数为:
-data<dir_name>
存放训练好的分类器的路径名。
-vec<vec_file_name>
正样本文件名(由trainingssamples程序或者由其他的方法创建的)
-bg<background_file_name>
背景描述文件。
-npos<number_of_positive_samples>,
-nneg<number_of_negative_samples>
用来训练每一个分类器阶段的正/负样本。合理的值是:nPos = 7000nNeg = 3000
-nstages<number_of_stages>
训练的级联分类器层数。
-nsplits<number_of_splits>
决定用于阶段分类器的弱分类器。如果1,则一个简单的stump classifier被使用。如果是2或者更多,则带有number_of_splits个内部节点的CART分类器被使用。
-mem<memory_in_MB>
预先计算的以MB为单位的可用内存。内存越大则训练的速度越快。
-sym(default)
-nonsym
指定训练的目标对象是否垂直对称。垂直对称提高目标的训练速度。例如,正面部是垂直对称的。
-minhitrate《min_hit_rate》
每个阶段分类器需要的最小的命中率。总的命中率为min_hit_rate的number_of_stages次方。
-maxfalsealarm<max_false_alarm_rate>
没有阶段分类器的最大错误报警率。总的错误警告率为max_false_alarm_rate的number_of_stages次方。
-weighttrimming<weight_trimming>
指定是否使用权修正和使用多大的权修正。一个基本的选择是0.9
-eqw
-mode<basic(default)|core|all>
选择用来训练的haar特征集的种类。basic仅仅使用垂直特征。all使用垂直和45度角旋转特征。
-w《sample_width》
-h《sample_height》
训练样本的尺寸,(以像素为单位)。必须和训练样本创建的尺寸相同。
一个训练分类器的例子:
"D:\Program Files\OpenCV\bin\haartraining.exe" -data data\cascade -vec data\pos.vec -bg negdata\negdata.dat -npos 49 -nneg 49 -mem 200 -mode ALL -w 20 -h 20
训练结束后,会在目录data下生成一些子目录,即为训练好的分类器。
(7) 生成xml文件
上一步在进行haartraining的时候,会在data目录下生成一些目录及txt文件,我们需要调用opencv\bin\haarconv.exe将这些txt文件转换为xml文件,也就是所谓的分类器。
至此,分类器的训练工作已完成。剩下的,就是在程序中加载xml文件,并调用相应的函数接口来实现分类检测的作用了。
这几天继续在看Lowe大神的SIFT神作,看的眼花手脚抽筋。也是醉了!!!!实在看不下去,来点干货。我们知道opencv下自带SIFT特征检测以及MATCH匹配的库,这些库完全可以让我们进行傻瓜似的 *** 作。但实际用起来的时候还不是那么简单。下文将对一个典型的基于OPENCV的SIFT特征点提取以及匹配的例程进行分析,并由此分析详细的对OPENCV中SIFT算法的使用进行一个介绍。OPENCV下SIFT特征点提取与匹配的大致流程如下:
读取图片-》特征点检测(位置,角度,层)肆尺-》特征点描述的提取(16*8维的特征向量)-》匹配-》显示
其中,特征点提取主要有两个步骤,见上行黄子部分。下面做具体分析。
1、使用opencv内置的库读取两幅图片
2、生成一个SiftFeatureDetector的对象,这个对象顾名思义就是SIFT特征的探测器,用它来探测衣服图片中SIFT点的特征,存到一个KeyPoint类型的vector中。这里有必要说keypoint的数据结构,涉及内容较多,具体分析查看opencv中keypoint数据结构分析,里面讲的自认为讲的还算详细(表雹蔽打我……)。简而言之最重要的一点在于:
keypoint只是保存了opencv的sift库检测到的特征点的一些基本信息,但sift所提取出来的特征向量其实不是在这个里面,特征向量通过SiftDescriptorExtractor 提取,结果放在一个Mat的数据结构中。这个数据结构才真正保存了该特征点所对应的特征向量。具体见后文对SiftDescriptorExtractor 所生成的对象的详解。
就因为这点没有理解明白耽误了一上午的时间。哭死!
3、对图像所有KEYPOINT提取其特征向量:
得到keypoint只是达到了关键点的位置,方向等信息,并无该特征点的特征向量,要想提取得到特征向量就还要进行SiftDescriptorExtractor 的工作,建立了SiftDescriptorExtractor 对象后,通过该对象,对之前SIFT产生的特征点进行遍历,找到该特征点所对应的128维特征向量。具体方法参见opencv中SiftDescriptorExtractor所做的SIFT特征向量提取工作简单分析。通过这一步后,所有keypoint关键点的特征向量被保存到了一个MAT的数据结构中,作为特征。
4、对两幅图的特征向量进行匹配,得到匹配值。
两幅图片的特征向量被提取出来后,我们就可以使用BruteForceMatcher对象对两幅图片的descriptor进行匹配,得到匹配的结果到matches中,这其中具体的匹配方法暂没细看,过段时间补上。
至此,SIFT从特征点的探测到最后的匹配都已经完成,虽然匹配部分不甚了解,只扫对于如何使用OPENCV进行sift特征的提取有了一定的理解。接下来裂肆高可以开始进行下一步的工作了。
附:使用OPENCV下SIFT库做图像匹配的例程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// opencv_empty_proj.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include opencv.hpp>
#include features2d/features2d.hpp>
#includenonfree/nonfree.hpp>
#includelegacy/legacy.hpp>
#include
using namespace std
using namespace cv
int _tmain(int argc, _TCHAR* argv[])
{
const char* imagename = "img.jpg"
//从文件中读入图像
Mat img = imread(imagename)
Mat img2=imread("img2.jpg")
//如果读入图像失败
if(img.empty())
{
fprintf(stderr, "Can not load image %s\n", imagename)
return -1
}
if(img2.empty())
{
fprintf(stderr, "Can not load image %s\n", imagename)
return -1
}
//显示图像
imshow("image before", img)
imshow("image2 before",img2)
//sift特征检测
SiftFeatureDetector siftdtc
vectorkp1,kp2
siftdtc.detect(img,kp1)
Mat outimg1
drawKeypoints(img,kp1,outimg1)
imshow("image1 keypoints",outimg1)
KeyPoint kp
vector::iterator itvc
for(itvc=kp1.begin()itvc!=kp1.end()itvc++)
{
cout<<"angle:"<angle<<"\t"<class_id<<"\t"<octave<<"\t"<pt<<"\t"<response<<endl
}
siftdtc.detect(img2,kp2)
Mat outimg2
drawKeypoints(img2,kp2,outimg2)
imshow("image2 keypoints",outimg2)
SiftDescriptorExtractor extractor
Mat descriptor1,descriptor2
BruteForceMatcher<L2>matcher
vector matches
Mat img_matches
extractor.compute(img,kp1,descriptor1)
extractor.compute(img2,kp2,descriptor2)
imshow("desc",descriptor1)
cout<<endl<<descriptor1<<endl
matcher.match(descriptor1,descriptor2,matches)
drawMatches(img,kp1,img2,kp2,matches,img_matches)
imshow("matches",img_matches)
//此函数等待按键,按键盘任意键就返回
waitKey()
return 0
}
您好,1、检查涉及MFC特性的代码有多少,与linux下不兼容的API有哪些。2、能用宏定义的进行宏定义,如win32下Sleep(1000)==linux下sleep(1),处理方法如下。
#ifndef WIN32
#defin Sleep(i) sleep(i/1000)
#endif
这样,Sleep(1000)这个WIN32下的函数在linux下也就是正常的调用。
3、数据类简好型尽量使用标C/C++中的拦前铅,CString用悔早sting代替,或用char []代替。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)