基于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

这是他写的关于神经网络识别车牌号的代码

posted @ 2017-03-13 15:01  靑い鳥  阅读(7268)  评论(1编辑  收藏  举报