基於OpenCV的交通標志識別(SVM+Hu不變矩, 部分測試源代碼)


最近跟着老師做一個交通識別的項目, 總算明白了一個道理, 這水啊, 不去親自蹚上一遭就不知道有多深, 更根本的原因當然還是自己學的不夠扎實, 不夠好.

經過了一個寒假的折磨,終於做出了一個原型來, 想到了自己當時被折磨的頭疼的樣子,想着將一部分源代碼發上來, 希望可以幫助到別人.

呵呵,廢話不多說了

這里我發的是一個手寫字符識別的程序(這是在編寫交通標志的過程中產生的,因為當時手頭的交通標志的樣本夠,所以從網上下載了手寫字符的樣本庫來測試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

這是他寫的關於神經網絡識別車牌號的代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM