预备知识 html
下面两个都不是必备知识,可是若是你想了解更多内容,可参考这两篇文章。 学习
OpenCV 2.4+ C++ SVM线性不可分处理 .net
SVM划分的意义 code
到此,咱们已经对SVM有必定的了解了。但是这有什么用呢?回到上一篇文章结果图: xml
这个结果图的意义在于,他成功从二维划分了分类的区域。因而若是之后,有一个新的样本在绿色区域,那么咱们就能够把他当成是绿色的点。 htm
因为这能够像更高维度推广,因此若是咱们能对样品映射成高维度空间的点,当有足够多的样品时,咱们一样能够找到一个高维度的超平面划分,使得同一类样品的映射点在同一区域,因而当有新样品落在这些区域是,咱们能够把它当成是这一类型的样本。 blog
通俗一点 图片
可能咱们能更加通俗一点。好比咱们来识别男性和女性。 ci
咱们发现男性和女性可能头发长度不同,可能胸围不同,因而咱们对样本个体产生这样的一种映射:
人 —> (头发长度, 胸围)
因而咱们将每一个样品映射到二维平面,其中“头发的长度”和“胸围的长度”分别是x轴和y轴。咱们把这些样品丢给SVM学习,则他会寻找出一个合理的x和y的区域来划分男性和女性。
固然,也有可能有些男的头发比女的还长,有的男性的胸围比女性还大,这些就是错分点,它们也影响着划分。
最后,当咱们把一我的映射到这个二维空间时,SVM就能够根据以往的学习,猜一猜这我的究竟是什么性别。
咱们学到了什么呢?
好吧,特征要找准一点,不然可能遇到下面的悲剧……
若是这是老板,你就可死翘翘了……
简单的文字识别
固然计算机没那么厉害能看出你的胸围或者头发长短。他须要一些他能读懂的东西,特别计算机一般“看到”的是下面的这种东东……
咱们须要对文字找到他的特征,来映射到高维空间。
还记得小学时候练字的米字格么?这彷佛暗示了咱们,虽然每一个人写的字千差万别,可是他们却具备必定的特色。
咱们尝试这样作,取一个字,选取一个包含该字的正方形区域,将这个正方形区域分割成8*8个小格,统计每一个小格中像素的数量,以这些数量为维度进行映射。
OK,明白了原理让咱们开始吧。
样本获取
因为一般文字样本都是白底黑字的,而手写也能够直接获取写入的数据而无视背景,因此咱们并不须要对样本进行提取,但咱们须要对他定位,并弄成合适的大小。
好比,你无法避免有人这么写字……
坑爹啊,好好的那么大地方你就躲在左上角……
开始定位
void getROI(Mat& src, Mat& dst){ int left, right, top, bottom; left = src.cols; right = 0; top = src.rows; bottom = 0; //获得区域 for(int i=0; i<src.rows; i++) { for(int j=0; j<src.cols; j++) { if(src.at<uchar>(i, j) > 0) { if(j<left) left = j; if(j>right) right = j; if(i<top) top = i; if(i>bottom) bottom = i; } } } int width = right - left; int height = bottom - top; //建立存储矩阵 dst = Mat::zeros(width, height, CV_8UC1); Rect dstRect(left, top, width, height); dst(dstRect); }
这段代码经过遍历全部图像矩阵的元素,来获取该样本的定位和大小。并把样本提取出来。
从新缩放
Mat dst = Mat::zeros(8, 8, CV_8UC1); resize(src, dst, dst.size());
进行缩放,把全部样本变成8*8的大小。为了简便,咱们把像素多少变成了像素的灰度值。
resize的API:
调整图片大小
C++: void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
参数
- src – 输入图像。
- dst – 输出图像;它有一个dsize (当其不为0时) 或者这个size由 src.size(),fx和fy算出。dst的类型和src相同。
- dsize –
输出图像的大小,若是取值为0,则:
![]()
dsize或者fx和fy必须有一种大小决定方法不为0。
- fx –
水平轴缩放因子,当取值为0时,则为:
![]()
- fy –
垂直轴缩放因子,当取值为0时,则为:
![]()
- interpolation –
插值方法
- INTER_NEAREST - 最近邻值插入方法。
- INTER_LINEAR - 双线性插值(默认方式)。
- INTER_AREA - 使用象素关系重采样。当图像缩小时候,该方法能够避免波纹出现。当图像放大时,相似于 CV_INTER_NN 方法。
- INTER_CUBIC - 立方插值。
- INTER_LANCZOS4 - 8x8的Lanczos插入方法。
准备样本数据
Mat data = Mat::zeros(total, 64, CV_32FC1); //样本数据矩阵 Mat res = Mat::zeros(total, 1, CV_32SC1); //样本标签矩阵 res.at<double>(k, 1) = label; //对第k个样本添加分类标签 //对第k个样本添加数据 for(int i = 0; i<8; i++) { for(int j = 0; j<8; j++) { res.at<double>(k, i * 8 + j) = dst.at<double>(i, j); } }
将刚刚的结果,输入样本,并加上标签。
训练
CvSVM svm = CvSVM(); CvSVMParams param; CvTermCriteria criteria; criteria= cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON); param= CvSVMParams(CvSVM::C_SVC, CvSVM::RBF, 10.0, 8.0, 1.0, 10.0, 0.5, 0.1, NULL, criteria); svm.train(data, res, Mat(), Mat(), param); svm.save( "SVM_DATA.xml" );
开始训练并保存训练数据。
使用
CvSVM svm = CvSVM(); svm.load( "SVM_DATA.xml" ); svm.predict(m); //对样本向量m检测
参考资料
使用OPENCV训练手写数字识别分类器 . firefight . 2011-05-28