最近跟着老師做一個交通識別的項目, 總算明白了一個道理, 這水啊, 不去親自蹚上一遭就不知道有多深, 更根本的原因當然還是自己學的不夠扎實, 不夠好.
經過了一個寒假的折磨,終於做出了一個原型來, 想到了自己當時被折磨的頭疼的樣子,想着將一部分源代碼發上來, 希望可以幫助到別人.
呵呵,廢話不多說了
這里我發的是一個手寫字符識別的程序(這是在編寫交通標志的過程中產生的,因為當時手頭的交通標志的樣本夠,所以從網上下載了手寫字符的樣本庫來測試SVM)
//添加使用到的頭文件 #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/core/core.hpp> #include <opencv2/ml/ml.hpp> #include <iostream> #include <fstream> #include "stdlib.h" //聲明命名空間 using namespace std; using namespace cv; using namespace cv::ml; //!訓練數據參數 const int sample_num_perclass = 40; //訓練每類圖片數量 const int class_num = 3; //訓練類數 //!所有圖片尺寸歸一化 const int image_cols = 16; //定義圖片尺寸 const int image_rows = 28; //定義圖片尺寸 //!生成的訓練文件保存位置 char SVMName[40] = "SVM.xml"; //分類器的訓練生成的名字,讀取時也按照這個名字來 #define RW 1 //0為讀取現有的分類器,1表示重新訓練一個分類器 //!讀取的圖像的路徑 char path[40] = "/home/aimer/Desktop/test2.png"; //!程序入口 double Hu[7]; //存儲得到的Hu矩陣 Moments mo; //矩變量 cv::Size size = cv::Size(image_cols, image_rows); int main(void) { #if RW //!讀取訓練數據 Mat trainingData = Mat::zeros(sample_num_perclass*class_num, 7, CV_32FC1); //填入圖像的7個Hu矩 Mat trainingLabel = Mat::zeros(sample_num_perclass*class_num, 1, CV_32SC1); char buf[50]; //字符緩沖區 for(int i=0;i<class_num;i++) //不同了類的循環 { for(int j=0;j<sample_num_perclass;j++) //一個類中的圖片數量 { //!生成圖片的路徑(不同類的圖片被放在了不同的文件夾下) sprintf(buf, "/home/aimer/Desktop/charSamples/%d/%d.png", i, j+1); //!讀取 Mat src = imread(buf, 0); //!重設尺寸(歸一化) Mat reImg; resize(src, reImg, size, CV_INTER_CUBIC); Mat canny; Canny(reImg, canny, 200, 120); //!求Hu矩 mo = moments(canny); HuMoments(mo, Hu); //!將Hu矩填入訓練數據集里 float *dstPoi = trainingData.ptr<float>(i*sample_num_perclass+j); //指向源的指針 for(int r=0;r<7;r++) dstPoi[r] = (float)Hu[r]; //!添加對該數據的分類標簽 int *labPoi = trainingLabel.ptr<int>(i*sample_num_perclass+j); labPoi[0] = i; } } imwrite("res.png", trainingData); //!創建SVM支持向量機並訓練數據 Ptr<SVM> svm = SVM::create(); svm->setType(SVM::C_SVC); svm->setC(0.01); svm->setKernel(SVM::LINEAR); svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10000, 1e-6)); svm->train(trainingData, ROW_SAMPLE, trainingLabel); svm->save(SVMName); #else //讀取xml文件 Ptr<SVM> svm=SVM::load<SVM>(SVMName); #endif //!讀取一副圖片進行測試 Mat temp = imread("/home/aimer/Desktop/test2.png", 0); Mat dst; resize(temp, dst, size, CV_INTER_CUBIC); Mat canny; Canny(dst, canny, 200, 120); mo = moments(canny); HuMoments(mo, Hu); Mat pre(1, 7, CV_32FC1); float *p = pre.ptr<float>(0); for(int i=0;i<7;i++) p[i] = Hu[i]; float res = svm->predict(pre); cout<<res<<endl; return 0; }
整體思路是:讀取圖片(程序沒有做灰度處理,因為讀取到的時候就是灰度圖了,與imread的參數有關),然后歸一化,求出七個不變矩,然后填充樣進行訓練,訓練完成后讀取一幅圖片進行測試.
環境為:Ubuntu 16.04 64位+OpenCV 3.1.0+Qt 4.8
注意一點的是:素材文件夾下面的圖片名字要改成數字(每個文件下的文件名從1開始,例如:1.png;並且不能斷開,例如:1.png,3.png這是不允許的,程序中被沒有加入這些判斷檢測程序,可以選擇自己加上這些代碼,這樣就免去了對文件重命名的煩惱)
樣本的鏈接:
鏈接:http://pan.baidu.com/s/1pLPeZkZ 密碼:26eb
感謝這位網上大咖, 看了他的文章對望幫助很大, 再次感謝
http://www.cnblogs.com/ronny/p/opencv_road_more_01.html
這是他寫的關於神經網絡識別車牌號的代碼