OpenCV OpenGL手写字符识别
另外一篇文章地址:这个比较详细,但是程序略显简单,现在这个程序是比较复杂的
http://blog.csdn.net/wangyaninglm/article/details/17091901
整个项目下载地址:
http://download.csdn.net/detail/wangyaninglm/8244549
实现效果:
Finger.h
#ifndef __TOUCHSCREEN_FINGER__ #define __TOUCHSCREEN_FINGER__ #include <cxcore.h> #include <vector> class Finger { public: Finger() { area = 0.0f; w=h=0; }; public: CvPoint center; float area; float w; float h; }; //typedef std::vector<Finger> FingerTrack; class FingerTrack { public: FingerTrack() { states=0; lostCount =0; } std::vector<Finger> track; int states; int lostCount; }; #endif
MachineLearning.h
#include <cxcore.h> #include <cv.h> #include <ml.h> #include <string> using namespace std ; class MachineLearning { enum TraningMethod { RandomTrees, Boosting, NeuralNetworks, SVM }; public: bool getCrossFeature(IplImage *img,float featureData[]); bool predict(char &shape_type,float featureData[]); bool load(const char *training_filename); bool train(const char *data_filename,const char *save_filename); MachineLearning(void); void ExtractDFT(float pcadata[],const int featureData[],const int &dataWidth,const int &DFTwidth); int DataCount; private: static const int CROSS_COLS = 50; static const int CROSS_ROWS = 50; void getCrossFeatureData(IplImage *img_cross,int featureData[],const int &cols,const int &rows ); void getDistanceFeatureData(IplImage *img_cross,int featureData[],const int &cols,const int &rows ); void getCrossCenter(IplImage *img_cross,int &cx,int &cy); void getCrossSpecifyArea(IplImage *img_cross,CvRect &specifie_rect); void ExtractPCA(float pcadata[],const int featureData[],const int &dataWidth ); bool is_load_training_model; TraningMethod traning_method; int read_num_class_data( const char* filename, int var_count,CvMat** data, CvMat** responses); int build_rtrees_classifier(const char* data_filename,const char* filename_to_save,const char* filename_to_load); int build_boost_classifier( char* data_filename,char* filename_to_save, char* filename_to_load ); int build_mlp_classifier( char* data_filename,char* filename_to_save, char* filename_to_load ); int build_svm_classifier( char* data_filename,char* filename_to_save, char* filename_to_load ); CvRTrees forest; int predict_rtrees_classifier(CvMat *sample_data,char &shape_type); };
machinelearning.cpp
/************************************************* Copyright (C) File name: Author: Hardy Version: 1.0 Date: 2007-3-5 Description: 模式识别部分,提取特征数据,训练模型,预测结果 Others: Function List: History: 1. Date: Author: Modification: 2. ... ************************************************/ #include "stdafx.h" #include "MachineLearning.h" #include <highgui.h> #include <iostream> #include <fstream> MachineLearning::MachineLearning(void) { is_load_training_model = false; traning_method = RandomTrees; } bool MachineLearning::getCrossFeature(IplImage *img,float pcaData[]) /************************************************* Function: Description: 样本数据载入 Date: 2007-3-5 Author: Input: Output: Return: Others: *************************************************/ { assert(img); ////计算图形所在矩形 //int cx,cy; //getCrossCenter(img,cx,cy); //CvRect roiRect; //getCrossSpecifyArea(img,roiRect); //assert(roiRect.x>0); //assert(roiRect.y>0); //assert(roiRect.height>0 && roiRect.height < img->width); //assert(roiRect.width>0 && roiRect.width < img->width ); //cvSetImageROI(img,roiRect); //IplImage *img_copy = cvCreateImage(cvSize(100,100) , 8, 1 ); //img_copy->origin = img->origin; //cvZero(img_copy); //cvResize(img,img_copy,CV_INTER_NN); //cvResetImageROI(img); //计算形心 int cx,cy; getCrossCenter(img,cx,cy); assert(cx<img->width); assert(cx>0); assert(cy<img->height); assert(cy>0); int shift_x = img->width/2 - cx; int shift_y = img->height/2 - cy; IplImage *img_copy = cvCreateImage(cvGetSize(img) , 8, 1 ); img_copy->origin = img->origin; cvZero(img_copy); //移动图形到中心 for(int i = 0; i<img->width;i++) { for(int j = 0; j<img->height;j++) { CvScalar c = cvGetAt(img,j,i); int v = (int)c.val[0]; if(v==255) { int nj=j+shift_y; int ni=i+shift_x; if(nj<img->height && ni<img->width) if(nj>=0 && ni>=0) cvSet2D(img_copy,nj,ni,c); } } } //计算密度特征数据-------------- //int featureData[CROSS_ROWS + CROSS_COLS]; //memset(featureData,-1,sizeof(featureData)); //getCrossFeatureData(img_copy,featureData,CROSS_COLS,CROSS_ROWS); ////std::cout<<"--------------------------------------------"<<std::endl; ////cvShowImage("WIN1",img_copy); ////cvWaitKey(0); //float CrossData[10]; //ExtractPCA(CrossData,featureData,CROSS_COLS+CROSS_ROWS); // //计算距离特征数据 int featureDisData[2*CROSS_ROWS + CROSS_COLS]; memset(featureDisData,-1,sizeof(featureDisData)); getDistanceFeatureData(img_copy,featureDisData,CROSS_COLS,CROSS_ROWS); float DistanceData[10]; ExtractPCA(DistanceData,featureDisData,CROSS_COLS+2*CROSS_ROWS); //合并特征数据 //for(int i=0;i<5;i++) pcaData[i] = CrossData[i]; //for(int i=5;i<10;i++) pcaData[i] = DistanceData[i-5]; for(int i=0;i<10;i++) pcaData[i] = DistanceData[i]; cvReleaseImage(&img_copy); return true; } void MachineLearning::getCrossFeatureData(IplImage *img_cross,int featureData[],const int &cols,const int &rows) /************************************************* Function: Description: 穿线得到特征数据 Date: 2007-3-5 Author: Input: Output: Return: Others: *************************************************/ { const int CROSS_VALID_LENGTH = 6; //在6个象素内不计算穿越数目,避免噪音 CvScalar c; for(int cross_index=0;cross_index<rows;cross_index++) { int y = (int)(img_cross->height*((float)cross_index/rows)); //按照比例决定位置 int cross_count = 0; int pre_v = -1; int pre_x = 0; for(int x =0;x<img_cross->width;x++) { c = cvGetAt(img_cross,y,x); int v = (int)c.val[0]; if(pre_v==255 && v==0) if((x-pre_x)>CROSS_VALID_LENGTH) { cross_count++; pre_x = x; } pre_v = v; } //cout<<cross_count<<","; featureData[cross_index] = cross_count; } for(int cross_index=0;cross_index<cols;cross_index++) { int x = (int)(img_cross->width*((float)cross_index/cols)); int cross_count = 0; int pre_v = -1; int pre_y = 0; for(int y =0;y<img_cross->height;y++) { c = cvGetAt(img_cross,y,x); int v = (int)c.val[0]; if(pre_v==255 && v==0) if((y-pre_y)>CROSS_VALID_LENGTH) { cross_count++; pre_y = y; } pre_v = v; } //cout<<cross_count<<","; featureData[rows+cross_index] = cross_count; } } void MachineLearning::getDistanceFeatureData(IplImage *img_cross,int featureData[],const int &cols,const int &rows) /************************************************* Function: Description: 穿线得到距离特征数据 Date: 2007-3-9 Author: Input: Output: Return: Others: *************************************************/ { CvScalar c; //从左向右穿线 for(int cross_index=0;cross_index<rows;cross_index++) { int y = (int)(img_cross->height*((float)cross_index/rows)); //按照比例决定位置 int meet_x = 0; for(int x =0;x<img_cross->width;x++) { c = cvGetAt(img_cross,y,x); int v = (int)c.val[0]; if(v==255) { meet_x = x; break; } } //cout<<meet_x<<","; featureData[cross_index] = meet_x; } //从右向左穿线 for(int cross_index=rows;cross_index<2*rows;cross_index++) { int y = (int)(img_cross->height*((float)(cross_index-rows)/rows)); //按照比例决定位置 int meet_x = 0; for(int x =(img_cross->width-1);x>-1;x--) { c = cvGetAt(img_cross,y,x); int v = (int)c.val[0]; if(v==255) { meet_x = x; break; } } //cout<<meet_x<<","; featureData[cross_index] = meet_x; } //从下向上穿线 for(int cross_index=0;cross_index<cols;cross_index++) { int x = (int)(img_cross->width*((float)cross_index/cols)); int meet_y = 0; for(int y =(img_cross->height-1);y>-1;y--) { c = cvGetAt(img_cross,y,x); int v = (int)c.val[0]; if(v==255) { meet_y = y; break; } } //cout<<meet_y<<","; featureData[2*rows+cross_index] = meet_y; } } void MachineLearning::getCrossSpecifyArea(IplImage *img,CvRect &specifie_rect) /************************************************* Function: Description: 获得图像矩形 Date: 2007-3-7 Author: Input: Output: Return: Others: *************************************************/ { CvRect res_rect = cvRect(0,0,0,0); const int fix =0; CvMemStorage* mt_storage = cvCreateMemStorage(0); CvSeq* mt_contour = NULL; int ApproxCount = 2; //轮廓优化等级 IplImage *frame_copy = cvCreateImage(cvGetSize(img) , 8, 1 ); frame_copy->origin = img->origin; cvCopy(img,frame_copy,0); cvFindContours( frame_copy, mt_storage, &mt_contour, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) ); if(mt_contour) { CvSeqReader reader; int i; CvPoint left_top_pt=cvPoint(img->width,img->height); CvPoint right_bottom_pt=cvPoint(0,0); CvPoint pt; CvSeq *contour2 = mt_contour; for (; contour2 != NULL; contour2 = contour2->h_next) { cvStartReadSeq(contour2, &reader); int N = contour2->total; if(N<10) continue; for (i = 0; i < N; i++) { CV_READ_SEQ_ELEM(pt, reader); if(left_top_pt.x>pt.x)left_top_pt.x = pt.x; if(left_top_pt.y>pt.y)left_top_pt.y = pt.y; if(right_bottom_pt.x<pt.x)right_bottom_pt.x = pt.x; if(right_bottom_pt.y<pt.y)right_bottom_pt.y = pt.y; } res_rect = cvRect(abs(left_top_pt.x-fix),abs(left_top_pt.y-fix),(right_bottom_pt.x-left_top_pt.x+2*fix),(right_bottom_pt.y-left_top_pt.y+2*fix)); specifie_rect = res_rect; break; } } cvClearMemStorage(mt_storage); cvReleaseImage(&frame_copy); } void MachineLearning::getCrossCenter(IplImage *img,int &cx,int &cy) /************************************************* Function: Description: 获得图像平移到中心 Date: 2007-3-5 Author: Input: Output: Return: Others: *************************************************/ { CvMemStorage* mt_storage = cvCreateMemStorage(0); CvSeq* mt_contour = NULL; int ApproxCount = 2; //轮廓优化等级 IplImage *frame_copy = cvCreateImage(cvGetSize(img) , 8, 1 ); frame_copy->origin = img->origin; cvCopy(img,frame_copy,0); cvFindContours( frame_copy, mt_storage, &mt_contour, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) ); if(mt_contour) { CvSeqReader reader; int i; int total_x = 0; int total_y = 0; CvPoint pt; CvSeq *contour2 = mt_contour; for (; contour2 != NULL; contour2 = contour2->h_next) { cvStartReadSeq(contour2, &reader); int N = contour2->total; if(N<10) continue; for (i = 0; i < N; i++) { CV_READ_SEQ_ELEM(pt, reader); total_x += pt.x; total_y += pt.y; } cx = total_x/N; cy = total_y/N; break; } } cvReleaseMemStorage(&mt_storage); cvReleaseImage(&frame_copy); } void MachineLearning::ExtractPCA(float pcadata[],const int featureData[],const int &dataWidth ) /************************************************* Function: Description: 采用fourier transfer 得到降维的数据 Date: 2007-3-5 Author: Input: Output: Return: Others: *************************************************/ { //int dataWidth = cols + rows; //CvMat* pData = cvCreateMat(2,dataWidth, CV_32FC1); //for(int i = 0; i < dataWidth; i++) //{ // cvSet2D(pData, 0, i,cvRealScalar(i)); // cvSet2D(pData, 1, i,cvRealScalar(featureData[i])); //} //CvMat* pMean = cvCreateMat(2, dataWidth, CV_32FC1); //CvMat* pEigVals = cvCreateMat(2, dataWidth, CV_32FC1); //CvMat* pEigVecs = cvCreateMat(2, dataWidth, CV_32FC1); //cvCalcPCA(pData, pMean, pEigVals, pEigVecs, CV_PCA_DATA_AS_ROW ); //float pp[100]; //memcpy(pp,pEigVals->data.fl,100 ); //memcpy(pp,pEigVecs->data.fl,100 ); //memcpy(pp,pMean->data.fl,100 ); CvMat* s = cvCreateMat(1,dataWidth,CV_32FC1); memcpy(s->data.i,featureData,sizeof(featureData)); for(int i=0;i<dataWidth;i++) cvSetReal2D(s,0,i,featureData[i]); //for(int i=0;i<dataWidth;i++) // printf("%6.2f\t",cvGetReal2D(s,0,i)); //printf("\n"); CvMat* d = cvCreateMat(1,dataWidth,CV_32FC1); cvDFT(s,d,CV_DXT_FORWARD|CV_DXT_SCALE); //for(int i=0;i<dataWidth;i++) // printf("%6.2f\t",cvGetReal2D(d,0,i)); //printf("\n"); for(int i=0;i<10;i++) { pcadata[i] = (float)cvGetReal2D(d,0,i); } cvReleaseMat(&s); cvReleaseMat(&d); } void MachineLearning::ExtractDFT(float pcadata[],const int featureData[],const int &dataWidth,const int &DFTwidth ) /************************************************* Function: Description: 采用fourier transfer 得到降维的数据 Date: 2007-3-5 Author: Input: Output: Return: Others: *************************************************/ { CvMat* s = cvCreateMat(1,dataWidth,CV_32FC1); memcpy(s->data.i,featureData,sizeof(featureData)); for(int i=0;i<dataWidth;i++) cvSetReal2D(s,0,i,featureData[i]); //for(int i=0;i<dataWidth;i++) // printf("%6.2f\t",cvGetReal2D(s,0,i)); //printf("\n"); CvMat* d = cvCreateMat(1,dataWidth,CV_32FC1); cvDFT(s,d,CV_DXT_FORWARD|CV_DXT_SCALE); //for(int i=0;i<dataWidth;i++) // printf("%6.2f\t",cvGetReal2D(d,0,i)); //printf("\n"); for(int i=0;i<DFTwidth;i++) { pcadata[i] = (float)cvGetReal2D(d,0,i); } cvReleaseMat(&s); cvReleaseMat(&d); } int MachineLearning::read_num_class_data( const char* filename, int var_count,CvMat** data, CvMat** responses ) { const int M = 1024; FILE* f = fopen( filename, "rt" ); CvMemStorage* storage; CvSeq* seq; char buf[M+2]; float* el_ptr; CvSeqReader reader; int i, j; if( !f ) return 0; el_ptr = new float[var_count+1]; storage = cvCreateMemStorage(); seq = cvCreateSeq( 0, sizeof(*seq), (var_count+1)*sizeof(float), storage ); for(;;) { char* ptr; if( !fgets( buf, M, f ) || !strchr( buf, ',' ) ) break; el_ptr[0] = buf[0]; ptr = buf+2; for( i = 1; i <= var_count; i++ ) { int n = 0; sscanf( ptr, "%f%n", el_ptr + i, &n ); ptr += n + 1; } if( i <= var_count ) break; cvSeqPush( seq, el_ptr ); } fclose(f); *data = cvCreateMat( seq->total, var_count, CV_32F ); *responses = cvCreateMat( seq->total, 1, CV_32F ); cvStartReadSeq( seq, &reader ); for( i = 0; i < seq->total; i++ ) { const float* sdata = (float*)reader.ptr + 1; float* ddata = data[0]->data.fl + var_count*i; float* dr = responses[0]->data.fl + i; for( j = 0; j < var_count; j++ ) ddata[j] = sdata[j]; *dr = sdata[-1]; CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); } cvReleaseMemStorage( &storage ); delete el_ptr; return 1; } int MachineLearning::build_rtrees_classifier(const char* data_filename, const char* filename_to_save, const char* filename_to_load ) { CvMat* data = 0; CvMat* responses = 0; CvMat* var_type = 0; CvMat* sample_idx = 0; // Create or load Random Trees classifier if( filename_to_load ) { // load classifier from the specified file forest.load( filename_to_load ); if( forest.get_tree_count() == 0 ) { printf( "Could not read the classifier %s\n", filename_to_load ); return -1; } printf( "The classifier %s is loaded.\n", data_filename ); } else { int ok = read_num_class_data( data_filename, DataCount, &data, &responses ); int nsamples_all = 0, ntrain_samples = 0; int i = 0; double train_hr = 0, test_hr = 0; CvMat* var_importance = 0; if( !ok ) { printf( "Could not read the database %s\n", data_filename ); return -1; } printf( "The database %s is loaded.\n", data_filename ); nsamples_all = data->rows; ntrain_samples = (int)(nsamples_all*0.8); int ntrain_tests = -1; // create classifier by using <data> and <responses> printf( "Training the classifier ..."); // 1. create type mask var_type = cvCreateMat( data->cols + 1, 1, CV_8U ); cvSet( var_type, cvScalarAll(CV_VAR_ORDERED) ); cvSetReal1D( var_type, data->cols, CV_VAR_CATEGORICAL ); //00000000001 // 2. create sample_idx sample_idx = cvCreateMat( 1, nsamples_all, CV_8UC1 ); { CvMat mat; cvGetCols( sample_idx, &mat, 0, nsamples_all ); cvSet( &mat, cvRealScalar(1) ); for(int i=0;i<nsamples_all;i++) { if((i%5)==0) { cvSet2D(sample_idx,0,i,cvRealScalar(0)); ntrain_tests++; } } } // 3. train classifier forest.train( data, CV_ROW_SAMPLE, responses, 0, sample_idx, var_type, 0, CvRTParams(10,10,0,false,15,0,true,4,100,0.01f,CV_TERMCRIT_ITER)); printf( "\n"); // compute prediction error on train and test data int test_count=0; int train_count=0; for(int i = 0; i < nsamples_all; i++ ) { double r; CvMat sample; cvGetRow( data, &sample, i ); r = forest.predict( &sample ); double abs_r = fabs((float)r - responses->data.fl[i]) <= FLT_EPSILON ? 1.0 : 0.0; if(abs_r < FLT_EPSILON) { printf( "data error with lines %d '%c' %f \n",i,(char)responses->data.fl[i],fabs((float)r - responses->data.fl[i])); } if((i%5)==0) { test_hr += abs_r; } else { train_hr += abs_r; } } test_hr /= (double)(ntrain_tests); train_hr /= (double)(nsamples_all-ntrain_tests); printf( "Recognition rate: train = %.1f%%, test = %.1f%%\n", train_hr*100., test_hr*100. ); //printf( "Number of trees: %d\n", forest.get_tree_count() ); } //// Save Random Trees classifier to file if needed if( filename_to_save ) forest.save( filename_to_save ); //forest.save("..//data//rTreeResult.xml"); cvReleaseMat( &sample_idx ); cvReleaseMat( &var_type ); cvReleaseMat( &data ); cvReleaseMat( &responses ); return 0; } int MachineLearning::build_boost_classifier( char* data_filename, char* filename_to_save, char* filename_to_load ) { const int class_count = 3; CvMat* data = 0; CvMat* responses = 0; CvMat* var_type = 0; CvMat* temp_sample = 0; CvMat* weak_responses = 0; int ok = read_num_class_data( data_filename, 13, &data, &responses ); int nsamples_all = 0, ntrain_samples = 0; int var_count; int i, j, k; double train_hr = 0, test_hr = 0; CvBoost boost; if( !ok ) { printf( "Could not read the database %s\n", data_filename ); return -1; } printf( "The database %s is loaded.\n", data_filename ); nsamples_all = data->rows; ntrain_samples = (int)(nsamples_all*0.9); var_count = data->cols; // Create or load Boosted Tree classifier if( filename_to_load ) { // load classifier from the specified file boost.load( filename_to_load ); ntrain_samples = 0; if( !boost.get_weak_predictors() ) { printf( "Could not read the classifier %s\n", filename_to_load ); return -1; } printf( "The classifier %s is loaded.\n", data_filename ); } else { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // As currently boosted tree classifier in MLL can only be trained // for 2-class problems, we transform the training database by // "unrolling" each training sample as many times as the number of // classes (26) that we have. // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CvMat* new_data = cvCreateMat( ntrain_samples*class_count, var_count + 1, CV_32F ); CvMat* new_responses = cvCreateMat( ntrain_samples*class_count, 1, CV_32S ); // 1. unroll the database type mask printf( "Unrolling the database...\n"); for( i = 0; i < ntrain_samples; i++ ) { float* data_row = (float*)(data->data.ptr + data->step*i); for( j = 0; j < class_count; j++ ) { float* new_data_row = (float*)(new_data->data.ptr + new_data->step*(i*class_count+j)); for( k = 0; k < var_count; k++ ) new_data_row[k] = data_row[k]; new_data_row[var_count] = (float)j; new_responses->data.i[i*class_count + j] = responses->data.fl[i] == j+'A'; } } // 2. create type mask var_type = cvCreateMat( var_count + 2, 1, CV_8U ); cvSet( var_type, cvScalarAll(CV_VAR_ORDERED) ); // the last indicator variable, as well // as the new (binary) response are categorical cvSetReal1D( var_type, var_count, CV_VAR_CATEGORICAL ); cvSetReal1D( var_type, var_count+1, CV_VAR_CATEGORICAL ); // 3. train classifier printf( "Training the classifier (may take a few minutes)..."); boost.train( new_data, CV_ROW_SAMPLE, new_responses, 0, 0, var_type, 0, CvBoostParams(CvBoost::REAL, 100, 0.95, 5, false, 0 )); cvReleaseMat( &new_data ); cvReleaseMat( &new_responses ); printf("\n"); } temp_sample = cvCreateMat( 1, var_count + 1, CV_32F ); weak_responses = cvCreateMat( 1, boost.get_weak_predictors()->total, CV_32F ); // compute prediction error on train and test data for( i = 0; i < nsamples_all; i++ ) { int best_class = 0; double max_sum = -DBL_MAX; double r; CvMat sample; cvGetRow( data, &sample, i ); for( k = 0; k < var_count; k++ ) temp_sample->data.fl[k] = sample.data.fl[k]; for( j = 0; j < class_count; j++ ) { temp_sample->data.fl[var_count] = (float)j; boost.predict( temp_sample, 0, weak_responses ); double sum = cvSum( weak_responses ).val[0]; if( max_sum < sum ) { max_sum = sum; best_class = j + 'A'; } } r = fabs(best_class - responses->data.fl[i]) < FLT_EPSILON ? 1 : 0; if( i < ntrain_samples ) train_hr += r; else test_hr += r; } test_hr /= (double)(nsamples_all-ntrain_samples); train_hr /= (double)ntrain_samples; printf( "Recognition rate: train = %.1f%%, test = %.1f%%\n", train_hr*100., test_hr*100. ); printf( "Number of trees: %d\n", boost.get_weak_predictors()->total ); // Save classifier to file if needed if( filename_to_save ) boost.save( filename_to_save ); cvReleaseMat( &temp_sample ); cvReleaseMat( &weak_responses ); cvReleaseMat( &var_type ); cvReleaseMat( &data ); cvReleaseMat( &responses ); return 0; } int MachineLearning::build_mlp_classifier( char* data_filename, char* filename_to_save, char* filename_to_load ) { const int class_count = 3; CvMat* data = 0; CvMat train_data; CvMat* responses = 0; CvMat* mlp_response = 0; int ok = read_num_class_data( data_filename, 13, &data, &responses ); int nsamples_all = 0, ntrain_samples = 0; int i, j; double train_hr = 0, test_hr = 0; CvANN_MLP mlp; if( !ok ) { printf( "Could not read the database %s\n", data_filename ); return -1; } printf( "The database %s is loaded.\n", data_filename ); nsamples_all = data->rows; ntrain_samples = (int)(nsamples_all*0.9); // Create or load MLP classifier if( filename_to_load ) { // load classifier from the specified file mlp.load( filename_to_load ); ntrain_samples = 0; if( !mlp.get_layer_count() ) { printf( "Could not read the classifier %s\n", filename_to_load ); return -1; } printf( "The classifier %s is loaded.\n", data_filename ); } else { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // MLP does not support categorical variables by explicitly. // So, instead of the output class label, we will use // a binary vector of <class_count> components for training and, // therefore, MLP will give us a vector of "probabilities" at the // prediction stage // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CvMat* new_responses = cvCreateMat( ntrain_samples, class_count, CV_32F ); // 1. unroll the responses printf( "Unrolling the responses...\n"); for( i = 0; i < ntrain_samples; i++ ) { int cls_label = cvRound(responses->data.fl[i]) - 'A'; float* bit_vec = (float*)(new_responses->data.ptr + i*new_responses->step); for( j = 0; j < class_count; j++ ) bit_vec[j] = 0.f; bit_vec[cls_label] = 1.f; } cvGetRows( data, &train_data, 0, ntrain_samples ); // 2. train classifier int layer_sz[] = { data->cols, 100, 100, class_count }; CvMat layer_sizes = cvMat( 1, (int)(sizeof(layer_sz)/sizeof(layer_sz[0])), CV_32S, layer_sz ); mlp.create( &layer_sizes ); printf( "Training the classifier (may take a few minutes)..."); mlp.train( &train_data, new_responses, 0, 0, CvANN_MLP_TrainParams(cvTermCriteria(CV_TERMCRIT_ITER,300,0.01), CvANN_MLP_TrainParams::RPROP,0.01)); cvReleaseMat( &new_responses ); printf("\n"); } mlp_response = cvCreateMat( 1, class_count, CV_32F ); // compute prediction error on train and test data for( i = 0; i < nsamples_all; i++ ) { int best_class; CvMat sample; cvGetRow( data, &sample, i ); CvPoint max_loc = {0,0}; mlp.predict( &sample, mlp_response ); cvMinMaxLoc( mlp_response, 0, 0, 0, &max_loc, 0 ); best_class = max_loc.x + 'A'; int r = fabs((double)best_class - responses->data.fl[i]) < FLT_EPSILON ? 1 : 0; if( i < ntrain_samples ) train_hr += r; else test_hr += r; } test_hr /= (double)(nsamples_all-ntrain_samples); train_hr /= (double)ntrain_samples; printf( "Recognition rate: train = %.1f%%, test = %.1f%%\n", train_hr*100., test_hr*100. ); // Save classifier to file if needed if( filename_to_save ) mlp.save( filename_to_save ); cvReleaseMat( &mlp_response ); cvReleaseMat( &data ); cvReleaseMat( &responses ); return 0; } int MachineLearning::build_svm_classifier( char* data_filename, char* filename_to_save, char* filename_to_load ) { CvMat* data = 0; //CvMat train_data; CvMat* responses = 0; CvMat* mlp_response = 0; CvMat* var_type = 0; CvMat* sample_idx = 0; int ok = read_num_class_data( data_filename, 10, &data, &responses ); float kk[100]; memcpy(kk,data->data.fl,100); int nsamples_all = 0, ntrain_samples = 0; int i; double train_hr = 0, test_hr = 0; CvSVM svm; if( !ok ) { printf( "Could not read the database %s\n", data_filename ); return -1; } printf( "The database %s is loaded.\n", data_filename ); nsamples_all = data->rows; ntrain_samples = (int)(nsamples_all*0.9); // Create or load svm classifier if( filename_to_load ) { // load classifier from the specified file svm.load( filename_to_load ); ntrain_samples = 0; if( !svm.get_support_vector_count() ) { printf( "Could not read the classifier %s\n", filename_to_load ); return -1; } printf( "The classifier %s is loaded.\n", filename_to_load ); } else { printf( "The classifier is in tranning...\n" ); // 1. create type mask var_type = cvCreateMat( data->cols, 1, CV_8U ); cvSet( var_type, cvScalarAll(CV_VAR_CATEGORICAL) ); //1111111111 // 2. create sample_idx sample_idx = cvCreateMat( 1, nsamples_all, CV_8UC1 ); { CvMat mat; cvGetCols( sample_idx, &mat, 0, ntrain_samples ); cvSet( &mat, cvRealScalar(1) ); cvGetCols( sample_idx, &mat, ntrain_samples, nsamples_all ); cvSetZero( &mat ); } //1111111000 // 3. train classifier svm.train( data,responses,var_type,sample_idx, CvSVMParams( CvSVM::C_SVC, CvSVM::RBF ,0,0.3,0,0.1, 0, 0, 0, cvTermCriteria(CV_TERMCRIT_ITER,300,0.01) )); printf( "\n"); } // compute prediction error on train and test data for( i = 0; i < nsamples_all; i++ ) { double r; CvMat sample; cvGetRow( data, &sample, i ); r = svm.predict( &sample ); r = fabs((double)r - responses->data.fl[i]) <= FLT_EPSILON ? 1 : 0; if( i < ntrain_samples ) train_hr += r; else test_hr += r; } test_hr /= (double)(nsamples_all-ntrain_samples); train_hr /= (double)ntrain_samples; printf( "Recognition rate: train = %.1f%%, test = %.1f%%\n", train_hr*100., test_hr*100. ); printf( "Number of support_vector_count: %d\n", svm.get_support_vector_count() ); //printf( "value of svm.get_support_vector(0): %f\n", svm.get_support_vector(0) ); // Save classifier to file if needed //if( filename_to_save ) //svm.save( filename_to_save ); svm.save("../data/svmResult.xml"); cvReleaseMat( &mlp_response ); cvReleaseMat( &data ); cvReleaseMat( &responses ); return 0; } bool MachineLearning::load(const char *training_filename) /************************************************* Function: Description: 训练数据载入 Date: 2007-3-6 Author: Input: Output: Return: Others: *************************************************/ { switch( traning_method ) { case RandomTrees: forest.clear(); build_rtrees_classifier(NULL,NULL,training_filename); break; default: ; } is_load_training_model = true; return true; } bool MachineLearning::train(const char *data_filename,const char *save_filename) /************************************************* Function: Description: 样本数据训练 Date: 2007-3-6 Author: Input: Output: Return: Others: *************************************************/ { switch( traning_method ) { case RandomTrees: forest.clear(); build_rtrees_classifier(data_filename,save_filename,NULL); break; default: ; } is_load_training_model = true; return true; } bool MachineLearning::predict(char &shape_type,float featureData[]) /************************************************* Function: Description: 样本数据预测 Date: 2007-3-6 Author: Input: Output: Return: Others: *************************************************/ { if(is_load_training_model) { //float featureData[10]; //getCrossFeature(img,featureData); //to do build sample CvMat *sample_data= cvCreateMat( 1, DataCount, CV_32F ); //cvSet2D(sample_data,0,0,cvRealScalar(0)); for(int i=0;i<DataCount;i++) { cvSet2D(sample_data,0,i,cvRealScalar(featureData[i])); } //float ss[23]; //memcpy(ss,sample_data->data.fl,sizeof(float)*23); switch( traning_method ) { case RandomTrees: predict_rtrees_classifier(sample_data,shape_type); break; default: ; } cvReleaseMat(&sample_data); return true; } else return false; } int MachineLearning::predict_rtrees_classifier(CvMat *sample_data,char &shape_type) { double r = forest.predict( sample_data ); shape_type = (char)r; return 0; }
trainingtools.cpp
// TrainingTools.cpp : 定义控制台应用程序的入口点。 // // 26个字母要按一定笔划顺序书写。书写的规律有以下几点: //联机大写字母书写规则 //1. C J L O S U V W Z 一笔划完成 //2. B D G K M N P Q T X Y 两笔划完成 //3. A E F H I R 三笔划完成 //online upper letter rule //1. C J L O S U V W Z finish in one stroke //2. B D G K M N P Q T X Y finish in two stroke //3. A E F H I R finish in three stroke #include "stdafx.h" #include "windows.h" #include <iostream> #include <string.h> #include <cxcore.h> #include <cv.h> #include <highgui.h> #include <fstream> #include "Finger.h" #include "MachineLearning.h" #pragma comment(lib,"opencv_core2410d.lib") #pragma comment(lib,"opencv_highgui2410d.lib") #pragma comment(lib,"opencv_ml2410d.lib") #pragma comment(lib,"opencv_imgproc2410.lib") IplImage *image = 0 ; //原始图像 IplImage *image2 = 0 ; //原始图像 using namespace std; const int SCALE_MAX = 500; const DWORD IDLE_TIME_SPAN = 1000; //间隔一秒内没有输入,开始写入数据 const int SAMPLE_COUNT = 50; //每条曲线 五十个特征点 const int SAMPLE_COUNT_OPT = 5; //每条曲线只取五维 DWORD start_time =0; DWORD idle_time =0; bool InRecongnize = true; //0 训练 1 预测 char pre_letter =0; MachineLearning ml; std::vector< FingerTrack > FingerTrackList; std::vector <Finger>::iterator Itr_Finger; std::vector< FingerTrack >::iterator Itr_FingerTrack; std::vector< FingerTrack > FingerTrackListOpt;//优化轮廓 bool inTrack =false; void WriteData(float featureData[]); int DFT(); void toNormalSize(); int traing_data =0; char letter='A'; CvFont mycvFont; //归一化处理 void toNormalSize() { int max_temp_x=0; int max_temp_y=0; int min_temp_x=10000; int min_temp_y=10000; for(int i=0;i<(int)FingerTrackListOpt.size();i++) { int ListObjSize = (int)FingerTrackListOpt[i].track.size(); for(int j=0;j<(int)ListObjSize;j++) { //FingerTrackListOpt[i].track[j].center.x -=FingerTrackListOpt[i].track[0].center.x; //FingerTrackListOpt[i].track[j].center.y -=FingerTrackListOpt[i].track[0].center.y; max_temp_x = max((FingerTrackListOpt[i].track[j].center.x),max_temp_x); max_temp_y = max((FingerTrackListOpt[i].track[j].center.y),max_temp_y); min_temp_x = min((FingerTrackListOpt[i].track[j].center.x),min_temp_x); min_temp_y = min((FingerTrackListOpt[i].track[j].center.y),min_temp_y); } } for(int i=0;i<(int)FingerTrackListOpt.size();i++) { int ListObjSize = (int)FingerTrackListOpt[i].track.size(); for(int j=0;j<(int)ListObjSize;j++) { FingerTrackListOpt[i].track[j].center.x -=min_temp_x; FingerTrackListOpt[i].track[j].center.y -=min_temp_y; } } int MaxW = max(max_temp_x-min_temp_x,max_temp_y-min_temp_y); //最大的 for(int i=0;i<(int)FingerTrackListOpt.size();i++) { int ListObjSize = (int)FingerTrackListOpt[i].track.size(); for(int j=0;j<(int)ListObjSize;j++) { FingerTrackListOpt[i].track[j].center.x =(int)((float)FingerTrackListOpt[i].track[j].center.x/MaxW*SCALE_MAX); FingerTrackListOpt[i].track[j].center.y =(int)((float)FingerTrackListOpt[i].track[j].center.y/MaxW*SCALE_MAX); } } } void analysis() { FingerTrackListOpt.clear(); for(int i=0;i<(int)FingerTrackList.size();i++) { //创建FingerTrack 加入FingerTrackListOpt FingerTrack ft; FingerTrackListOpt.push_back(ft); CvPoint start_pt = FingerTrackList[i].track[0].center; Finger fg; fg.center = start_pt; FingerTrackListOpt[i].track.push_back(fg); //求取距离总和 long total_dis =0; int ListObjSize = (int)FingerTrackList[i].track.size(); for(int j=0;j<ListObjSize-1;j++) { CvPoint pt = FingerTrackList[i].track[j].center; CvPoint pt_next = FingerTrackList[i].track[j+1].center; long distance = (pt_next.x - pt.x)*(pt_next.x - pt.x) + (pt_next.y - pt.y)*(pt_next.y - pt.y); total_dis+=(long)sqrt((float)distance); } int search_len = total_dis/(SAMPLE_COUNT+2); //确定分割长度,取20等份 assert(search_len>0); //插值 for(int j=0;j<ListObjSize;j++) { CvPoint pt = FingerTrackList[i].track[j].center; long distance = (start_pt.x - pt.x)*(start_pt.x - pt.x) + (start_pt.y - pt.y)*(start_pt.y - pt.y); distance = (long)sqrt((float)distance); if(distance>search_len) { //在轨迹上计算一个插值虚拟点 float radio = (float)search_len/distance; start_pt.x = (int)(start_pt.x + (pt.x - start_pt.x)*radio); start_pt.y = (int)(start_pt.y + (pt.y - start_pt.y)*radio); Finger fg; fg.center = start_pt; FingerTrackListOpt[i].track.push_back(fg); j--; } } } //归一化处理 toNormalSize(); }; //写入特征数据到文件或者数组 void WriteData(float featureData[]) { std::fstream logfile("data.txt",std::ios::app); int Tracksize = (int)FingerTrackListOpt.size(); if(!InRecongnize) { logfile<<letter<<","; logfile<<Tracksize; } featureData[0] = (float)Tracksize; int f_index = 0; for(int i=0;i<Tracksize;i++) { int ListObjSize = (int)FingerTrackListOpt[i].track.size(); assert(ListObjSize>=SAMPLE_COUNT); float pcadata[SAMPLE_COUNT_OPT]; int fData[SAMPLE_COUNT]; //X DFT for(int j=0;j<SAMPLE_COUNT;j++) { fData[j] = FingerTrackListOpt[i].track[j].center.x; } ml.ExtractDFT(pcadata,fData,SAMPLE_COUNT,SAMPLE_COUNT_OPT); for(int k=0;k<SAMPLE_COUNT_OPT;k++) { if(!InRecongnize) logfile<<","<<pcadata[k]; f_index++; featureData[f_index] = pcadata[k]; } //Y DFT for(int j=0;j<SAMPLE_COUNT;j++) { fData[j] = FingerTrackListOpt[i].track[j].center.y; } ml.ExtractDFT(pcadata,fData,SAMPLE_COUNT,SAMPLE_COUNT_OPT); for(int k=0;k<SAMPLE_COUNT_OPT;k++) { if(!InRecongnize) logfile<<","<<pcadata[k]; f_index++; featureData[f_index] = pcadata[k]; } } for(int i=Tracksize;i<3;i++) //用0填充 { for(int j=0;j<SAMPLE_COUNT_OPT;j++) { if(!InRecongnize) logfile<<","<<0; if(!InRecongnize) logfile<<","<<0; f_index++; featureData[f_index] = 0.0f; f_index++; featureData[f_index] = 0.0f; } } if(!InRecongnize) logfile<<"\n"; logfile.close(); } static void on_mouse( int event, int x, int y, int flags, void *param ) { if( event == CV_EVENT_LBUTTONDOWN ) { if(!inTrack) { FingerTrack ft; FingerTrackList.push_back(ft); inTrack = true; } } else if ( event == CV_EVENT_MOUSEMOVE ) { if(inTrack) { Finger fg; fg.center = cvPoint(x,y); FingerTrackList.back().track.push_back(fg); idle_time =0; } } else if ( event == CV_EVENT_LBUTTONUP ) { inTrack = false; //analysis(); start_time = timeGetTime(); analysis(); //DFT(); } }; void OnChangeData(int pos) { letter = pos+'A'; } int main(int argc, char* argv[]) { std::cout<<" == The upper letter online handwriting recongnize ==" << std::endl; std::cout<<" 1. there two state (recongnizing and traning) for the app,In recongnizing mode,use your mouse write upper letter on 'Win' window,after 1 second,then will get result on Win2" << std::endl; std::cout<<" 2. you can press 'm' key to change mode from recongnizing to traning or back." << std::endl; std::cout<<" 3. In traning mode,change the value of letter, then you can write upper letter on 'Win' window, and app will write data into data.txt." << std::endl; std::cout<<" 4. you can retrain the traning data by press 't' without restart program." << std::endl; std::cout<<" 5. you can modify the traning data 'data.txt' by hand if you want. " << std::endl<< std::endl; std::cout<<" enjoy it.:)" << std::endl<< std::endl; std::cout<<" ===============================================================" << std::endl; CvSize image_sz = cvSize( 1000,1000); image = cvCreateImage(image_sz , 8, 3 ); image2 = cvCreateImage(image_sz , 8, 3 ); cvNamedWindow("Win",0); cvNamedWindow("Win2",0); cvSetMouseCallback( "Win", on_mouse, 0 ); cvResizeWindow("Win",500,500); cvResizeWindow("Win2",500,500); cvCreateTrackbar("Letter", "Win2", &traing_data, 25, OnChangeData); mycvFont = cvFont(5,2); ml.DataCount = 1 + SAMPLE_COUNT_OPT*2*3; ml.train("data.txt",0); for(;;) { //set Timer idle_time = timeGetTime()-start_time; if(idle_time>IDLE_TIME_SPAN && FingerTrackList.size()>0 && !inTrack) { float featureData[31]; //记录训练数据 WriteData(featureData); idle_time = 0; FingerTrackList.clear(); FingerTrackListOpt.clear(); if(InRecongnize) { pre_letter = 0; ml.predict(pre_letter,featureData); } } cvZero(image); cvZero(image2); for(int i=0;i<(int)FingerTrackList.size();i++) { for(int j=0;j<(int)FingerTrackList[i].track.size();j++) cvCircle(image,FingerTrackList[i].track[j].center,10,CV_RGB(0,255,0),1,8,0); } for(int i=0;i<(int)FingerTrackListOpt.size();i++) { for(int j=0;j<(int)FingerTrackListOpt[i].track.size();j++) { CvPoint newpt = FingerTrackListOpt[i].track[j].center; newpt.x =newpt.x/2+image2->width/2; newpt.y =newpt.y/2+image2->height/2; cvLine(image2,cvPoint(image2->width/2,0),cvPoint(image2->width/2 ,image2->height),CV_RGB(255,255,0),2,8,0); cvLine(image2,cvPoint(0,image2->height/2),cvPoint(image2->width ,image2->height/2),CV_RGB(255,255,0),2,8,0); cvCircle(image2,newpt,10,CV_RGB(255,0,0),1,8,0); } } CvPoint pt_info; if(InRecongnize) { pt_info = cvPoint(20,920); mycvFont = cvFont(2,2); cvPutText(image2,"recongnizing result = ",pt_info,&mycvFont,CV_RGB(20,250,250)); if(pre_letter!=0) { mycvFont = cvFont(5,2); pt_info = cvPoint(400,920); cvPutText(image2,&pre_letter,pt_info,&mycvFont,CV_RGB(255,0,0)); } } else { mycvFont = cvFont(5,2); pt_info = cvPoint(290,920); cvPutText(image2,&letter,pt_info,&mycvFont,CV_RGB(20,250,250)); mycvFont = cvFont(2,2); pt_info = cvPoint(20,920); cvPutText(image2,"is traning...",pt_info,&mycvFont,CV_RGB(20,250,250)); } cvShowImage("Win",image); cvShowImage("Win2",image2); int keyCode = cvWaitKey(10); if (keyCode==27) break; if (keyCode=='c') { FingerTrackList.clear(); FingerTrackListOpt.clear(); } if (keyCode=='t') { ml.train("data.txt",0); } if (keyCode=='m') { InRecongnize = InRecongnize^1; } } return 0; } int DFT() { for(int k=0;k<(int)FingerTrackListOpt.size();k++) { int ListObjSize = (int)FingerTrackListOpt[k].track.size(); //if(ListObjSize==20) break; printf("\n\nListObjSize %d ",ListObjSize); CvMat* s = cvCreateMat(1,ListObjSize,CV_32FC1); CvMat* d = cvCreateMat(1,ListObjSize,CV_32FC1); CvMat* s2 = cvCreateMat(1,ListObjSize,CV_32FC1); long avg_x =0; long avg_y =0; for(int j=0;j<(int)ListObjSize;j++) { CvPoint pt = FingerTrackListOpt[k].track[j].center; avg_x +=pt.x; avg_y +=pt.y; } avg_x = avg_x/ListObjSize; avg_y = avg_y/ListObjSize; for(int j=0;j<(int)ListObjSize;j++) { CvPoint pt = FingerTrackListOpt[k].track[j].center; float dis =(float)((pt.x-avg_x)* (pt.x-avg_x) + (pt.y-avg_y)* (pt.y-avg_y)); dis = sqrt(dis); cvSetReal2D(s,0,j,dis); } //for(int j=0;j<(int)ListObjSize;j++) //{ // printf("%6.2f ",cvGetReal2D(s,0,j)); //} printf(" \n"); //DFT 离散傅立叶变换 cvDFT(s,d,CV_DXT_FORWARD); //CV_DXT_FORWARD 代表了正变换:空域-〉频域 printf("\n The result of DFT: "); for(int j=0;j<(int)ListObjSize;j++) printf("%6.2f ",cvGetReal2D(d,0,j)); //printf(" \n"); ////DFT 离散傅立叶逆变换 //cvDFT(d,s2,CV_DXT_INV_SCALE); //逆变换 //printf("\n The result of IDFT: "); //for(int j=0;j<(int)ListObjSize;j++) // printf("%6.2f ",cvGetReal2D(s2,0,j)); //printf(" "); cvReleaseMat(&s); cvReleaseMat(&d); cvReleaseMat(&s2); } return 0; }
实现效果: