初识SVM(验证码识别)

之前读了一堆关于SVM的论文,最近终于开始用opencv的SVM来实战了,事实证明果然不能停留于理论,真正实践又花了一定时间,把自己的历程记录如下:

首先来看一下我的main函数:

int main() {
    Mat data_mat, res_mat;
    Mat res;
    vector<string> img_path;
    vector<int> img_catg;
    int nLine = 0;

    //loadCatePath(img_path, img_catg, nLine);

    //生成测试数据
    //createData(img_path, data_mat, res_mat, res, nLine);

    //生成xml模型
    //createXML(img_path, img_catg, data_mat, res_mat, res, nLine);

    //测试
    testSVM();

    //system("pause");
    return 0;
}

由于是第一次写SVM的代码,刚开始写的很乱,后来自己又整理了一下,大致就分为如上流程.
img_path存的是图片的路径,img_catg存的是图片对应的label,然后就先回到loadCatePath函数:

void loadCatePath(vector<string> &img_path, vector<int> &img_catg,  int &nLine) {

    string buf;
    char c[10];

    for (int i = 2; i < 10; ++i) {

        _itoa(i, c, 10);
        string tmp(c);
        string addre = filename + tmp + "/trainpath.txt";
        ifstream svm_data(addre);
        while (svm_data) {
            if (getline(svm_data, buf)) {
                nLine++;
                img_catg.emplace_back(i);
                img_path.emplace_back(buf);
            }
        }
        svm_data.close();
    }
}

我的图片存放在多个文件夹下,文件夹名称从1、2、3一直到9,filename是公共目录,因此我们就可以用如上的方法实现依次读取文件夹,并用对应的label给图片“赋值”。

生成测试数据部分就因人而异了,针对各项目预处理方法都不同,就不多说,然后是生成XML模型部分:

void createXML(vector<string> &img_path, vector<int> &img_catg, Mat &data_mat, Mat &res_mat, Mat &res, int nImgNum) {
    Mat trainImg = Mat::zeros(IMGHEIGHT, IMGWIDTH, CV_8UC3);
    int hogImgWidht = 64, hogImgHeight = 64, n;
    for (string::size_type i = 0; i < img_path.size(); ++i) {
        res = imread(img_path[i].c_str(), 1);
        cout << " processing " << img_path[i].c_str() << endl;
        resize(res, trainImg, cv::Size(hogImgWidht, hogImgHeight), 0, 0, INTER_CUBIC);
        HOGDescriptor *hog = new HOGDescriptor(cvSize(hogImgWidht, hogImgHeight), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9);
        vector<float> descriptors;
        hog->compute(trainImg, descriptors, Size(1, 1), Size(0, 0));
        if (i == 0) {
            data_mat = Mat::zeros(nImgNum, descriptors.size(), CV_32FC1);
        }
        cout << "HOG dims: " << descriptors.size() << endl;
        n = 0;
        for (vector<float>::iterator iter = descriptors.begin(); iter != descriptors.end(); ++iter) {
            data_mat.at<float>(i, n) = *iter;
            n++;
        }
        res_mat.at<float>(i, 0) = img_catg[i];
        cout << " end processing " << img_path[i].c_str() << " " << img_catg[i] << endl;
    }

    CvSVM svm;
    CvSVMParams param;
    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, Mat(), Mat(), param);
    svm.save("F:/compiler/opencv/forfun/SVM_DATA.xml");
}

都是调用了ML.h中的方法,那几个参数具体的功能还没测试过,之后需要多看一下,最后通过svm.train和svm.svm就在对应路径下生成了xml文件。

既然模型有了,接下来就可以针对我们自己的图片进行测试了:

void testSVM() {
    IplImage *srcImg = cvLoadImage("D:\\other\\verifycode2\\2835.jpg");
    showImage("2835", srcImg);
    IplImage *grayImg = cvCreateImage(cvGetSize(srcImg), IPL_DEPTH_8U, 1);
    //灰度
    grayImage(srcImg, grayImg);
    binary(grayImg);
    cvSmooth(grayImg, grayImg, CV_MEDIAN);
    drawContours(srcImg, grayImg);
    cvReleaseImage(&srcImg);
    cvReleaseImage(&grayImg);
}

这里有几个showImage、grayImage等函数,看名字就知道意思了,重点是在drawContours这里,将原图和处理后的二值图传递过去,因为我这里做的是验证码识别,用到了分割,这一部分的实现过程如下:

    CvSeq* contours = NULL;
    CvMemStorage* storage = cvCreateMemStorage(0);
    cvFindContours(grayImg, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL);

    int count = 0;
    int idx = 0;
    int tempCount = 0;

    cvSeqSort(contours, cmp_func);

    int z = 0;
    vector<float> descriptors;
    vector<CvRect> v;
    for (CvSeq* c = contours; c != NULL; c = c->h_next) {
        v.emplace_back(cvBoundingRect(c, 0));
    }

    sort(v.begin(), v.end(), mycmp);
    for (int i = 0; i < v.size(); ++i) {
        CvRect rc = v[i];
        if (rc.height > srcImg->width / 10) {
            cvDrawRect(srcImg, cvPoint(rc.x, rc.y), cvPoint(rc.x + rc.width, rc.y + rc.height), CV_RGB(100, 100, 100));
            count++;
            IplImage* resImg = cvCreateImage(cvSize(rc.width, rc.height), srcImg->depth, srcImg->nChannels);
            cvSetImageROI(srcImg, rc);
            cvCopyImage(srcImg, resImg);
            cvResetImageROI(srcImg);
            Mat dstImg = (Mat)resImg;
            resize(dstImg, dstImg, Size(64, 64));
            //测试部分

            CvSVM svm;
            svm.load("SVM_DATA.xml");
            HOGDescriptor *hog = new HOGDescriptor(cvSize(64, 64), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9);      
            vector<float>descriptors;//结果数组  
            Mat trainImg = Mat::zeros(64, 64, CV_8UC3);
            resize(dstImg, trainImg, cv::Size(64, 64), 0, 0, INTER_CUBIC);
            hog->compute(trainImg, descriptors, Size(1, 1), Size(0, 0)); //调用计算函数开始计算   
            Mat SVMtrainMat = Mat::zeros(1, descriptors.size(), CV_32FC1);
            int n = 0;
            for (vector<float>::iterator iter = descriptors.begin(); iter != descriptors.end(); iter++)
            {
                SVMtrainMat.at<float>(0, n) = *iter;
                n++;
            }

            int ret = svm.predict(SVMtrainMat);
            cout << ret;
    }
    cvReleaseMemStorage(&storage);
}

以上即为我与SVM的第一次亲密接触,接下来还有许多研究的机会,希望能有更多进展,最后附上我的结果图:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/u013700358/article/details/52892090