參考了秋風細雨的文章:http://blog.csdn.net/candyforever/article/details/8564746
花了點時間編寫出了程序,先看看效果吧。

識別效果大概都能正確。
好了,開始正題:
因為本程序是提取HOG特征,使用SVM進行分類的,所以大概了解下HOG的一些知識,其中我覺得怎么計算圖像HOG特征的維度會對程序了解有幫助
關於HOG,我們可以參考:
http://gz-ricky.blogbus.com/logs/85326280.html
http://blog.csdn.net/raodotcong/article/details/6239431
關於手寫的數字0-9的數據庫下載地址和如何生成此數據庫HOG特征的xml文件可以參考文章開頭的參考博客。
本人提供一個已經訓練好的關於此庫我生成的xml文件,下載地址:
http://pan.baidu.com/s/1qXSYp
訓練模型
#include <iostream>
#include <opencv2/opencv.hpp>
#include <fstream>
using namespace std;
using namespace cv;
int main()
{
vector<string> img_path;//輸入文件名變量
vector<int> img_catg;
int nLine = 0;
string buf;
ifstream svm_data( "D:/project/HOG/t10k-images-bmp/t10k-images/result.txt" );//訓練樣本圖片的路徑都寫在這個txt文件中,使用bat批處理文件可以得到這個txt文件
unsigned long n;
while( svm_data )//將訓練樣本文件依次讀取進來
{
if( getline( svm_data, buf ) )
{
nLine ++;
if( nLine % 2 == 0 )//注:奇數行是圖片全路徑,偶數行是標簽
{
img_catg.push_back( atoi( buf.c_str() ) );//atoi將字符串轉換成整型,標志(0,1,2,...,9),注意這里至少要有兩個類別,否則會出錯
}
else
{
img_path.push_back( buf );//圖像路徑
}
}
}
svm_data.close();//關閉文件
CvMat *data_mat, *res_mat;
int nImgNum = nLine / 2; //nImgNum是樣本數量,只有文本行數的一半,另一半是標簽
data_mat = cvCreateMat( nImgNum, 324, CV_32FC1 ); //第二個參數,即矩陣的列是由下面的descriptors的大小決定的,可以由descriptors.size()得到,且對於不同大小的輸入訓練圖片,這個值是不同的
cvSetZero( data_mat );
//類型矩陣,存儲每個樣本的類型標志
res_mat = cvCreateMat( nImgNum, 1, CV_32FC1 );
cvSetZero( res_mat );
IplImage* src;
IplImage* trainImg=cvCreateImage(cvSize(28,28),8,3);//需要分析的圖片,這里默認設定圖片是28*28大小,所以上面定義了324,如果要更改圖片大小,可以先用debug查看一下descriptors是多少,然后設定好再運行
//處理HOG特征
for( string::size_type i = 0; i != img_path.size(); i++ )
{
src=cvLoadImage(img_path[i].c_str(),1);
if( src == NULL )
{
cout<<" can not load the image: "<<img_path[i].c_str()<<endl;
continue;
}
cout<<"deal with\t"<<img_path[i].c_str()<<endl;
cvResize(src,trainImg);
HOGDescriptor *hog=new HOGDescriptor(cvSize(28,28),cvSize(14,14),cvSize(7,7),cvSize(7,7),9);
vector<float>descriptors;//存放結果
hog->compute(trainImg, descriptors,Size(1,1), Size(0,0)); //Hog特征計算
cout<<"HOG dims: "<<descriptors.size()<<endl;
n=0;
for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++)
{
cvmSet(data_mat,i,n,*iter);//存儲HOG特征
n++;
}
cvmSet( res_mat, i, 0, img_catg[i] );
cout<<"Done !!!: "<<img_path[i].c_str()<<" "<<img_catg[i]<<endl;
}
CvSVM svm;//新建一個SVM
CvSVMParams param;//這里是SVM訓練相關參數
CvTermCriteria criteria;
criteria = cvTermCriteria( CV_TERMCRIT_EPS, 1000, FLT_EPSILON );
param = CvSVMParams( CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria );
svm.train( data_mat, res_mat, NULL, NULL, param );//訓練數據
//保存訓練好的分類器
svm.save( "HOG_SVM_DATA.xml" );
cout<<"HOG_SVM_DATA.xml is saved !!! \n exit program"<<endl;
cvReleaseMat( &data_mat );
cvReleaseMat( &res_mat );
cvReleaseImage(&trainImg);
return 0;
}
D:/project/HOG/t10k-images-bmp/t10k-images/result.txt 的生成方法
使用createpath.py腳本
import os, sys
def get_filepaths(directory):
file_paths = [] # List which will store all of the full filepaths.
for root, directories, files in os.walk(directory):
for filename in files:
# Join the two strings in order to form the full filepath.
filepath = os.path.join(root, filename)
file_paths.append(filepath) # Add it to the list.
return file_paths # Self-explanatory.
lists = get_filepaths(os.path.dirname(os.path.abspath(__file__)))
with open('result.txt', 'a') as f:
for url in lists:
if (os.path.basename(url).startswith('0_')):
f.write(url)
f.write('\n')
f.write('0\n')
if (os.path.basename(url).startswith('1_')):
f.write(url)
f.write('\n')
f.write('1\n')
if (os.path.basename(url).startswith('2_')):
f.write(url)
f.write('\n')
f.write('2\n')
if (os.path.basename(url).startswith('3_')):
f.write(url)
f.write('\n')
f.write('3\n')
if (os.path.basename(url).startswith('4_')):
f.write(url)
f.write('\n')
f.write('4\n')
if (os.path.basename(url).startswith('5_')):
f.write(url)
f.write('\n')
f.write('5\n')
if (os.path.basename(url).startswith('6_')):
f.write(url)
f.write('\n')
f.write('6\n')
if (os.path.basename(url).startswith('7_')):
f.write(url)
f.write('\n')
f.write('7\n')
if (os.path.basename(url).startswith('8_')):
f.write(url)
f.write('\n')
f.write('8\n')
if (os.path.basename(url).startswith('9_')):
f.write(url)
f.write('\n')
f.write('9\n')

生成result.txt
![]()
使用模型
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { IplImage *test; char result[300]; //存放預測結果 CvSVM svm; svm.load("d:\\HOG_SVM_DATA.xml");//加載訓練好的xml文件,這里訓練的是10K個手寫數字 //檢測樣本 test = cvLoadImage("d:\\test.bmp", 1); //待預測圖片,用系統自帶的畫圖工具隨便手寫 if (!test) { cout<<"not exist"<<endl; return -1; } cout<<"load image done"<<endl; IplImage* trainTempImg=cvCreateImage(cvSize(28,28),8,3); cvZero(trainTempImg); cvResize(test,trainTempImg); HOGDescriptor *hog=new HOGDescriptor(cvSize(28,28),cvSize(14,14),cvSize(7,7),cvSize(7,7),9); vector<float>descriptors;//存放結果 hog->compute(trainTempImg, descriptors,Size(1,1), Size(0,0)); //Hog特征計算 cout<<"HOG dims: "<<descriptors.size()<<endl; //打印Hog特征維數 ,這里是324 CvMat* SVMtrainMat=cvCreateMat(1,descriptors.size(),CV_32FC1); int n=0; for(vector<float>::iterator iter=descriptors.begin();iter!=descriptors.end();iter++) { cvmSet(SVMtrainMat,0,n,*iter); n++; } int ret = svm.predict(SVMtrainMat);//檢測結果 sprintf(result, "%d\r\n",ret ); cvNamedWindow("dst",1); cvShowImage("dst",test); cout<<"result:"<<result<<endl; waitKey (); cvReleaseImage(&test); cvReleaseImage(&trainTempImg); return 0; }
工程源碼(MFC):
http://pan.baidu.com/s/1rDQbO
程序下載(裸機可運行,無需環境):
http://pan.baidu.com/s/1byQeX
QT控制台版本(包含手寫數據庫,訓練模型,使用模型)
http://pan.baidu.com/s/1pJ45bwZ
