SVM实现手写数字识别

SVM简介

    知乎上的一个回答我认为是史上最NB最形象的SVM含义解释,想看介绍戳这里(里面的第一个回答),再看看百科就能知道个大概了。

开发环境

    Windows10 + VS2013 + Qt580 + OpenCV300

主要代码

    利用opencv-SVM算法和Mnist数据集封装成一个单例模式的数字识别检测器。

DigitsDetector.h
#ifndef DIGITSDETECTOR
#define DIGITSDETECTOR

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/core/core.hpp> 
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/flann/flann.hpp>
#include <opencv.hpp>
#include <opencv2/ml/ml.hpp>

#include <iostream>
#include <string>
#include <fstream>

using namespace std;
using namespace cv;
using namespace cv::ml;

class DigitsDetector
{
public:
	static DigitsDetector *I();

public:
	Mat ReadMnistImage(const string pathName);
	Mat ReadMnistLabel(const string pathName);
	void CreateSVM();
	bool Train(Mat data, Mat label);
	float Predict(Mat testdata);

protected:
	int CvtToLittleEndian(int i);

private:
	DigitsDetector();
	~DigitsDetector();
	static DigitsDetector *inst;

	Ptr<SVM> svm;
};

#endif // !DIGITSDETECTOR

DigitsDetector.cpp
#include "DigitsDetector.h"

DigitsDetector *DigitsDetector::inst = new DigitsDetector();

DigitsDetector::DigitsDetector(){ 

}

DigitsDetector::~DigitsDetector(){

	if (inst != NULL){

		delete inst;
		inst = NULL;
	}
}

DigitsDetector *DigitsDetector::I(){

	if (inst == NULL) inst = new DigitsDetector();
	return inst;
}

Mat DigitsDetector::ReadMnistImage(const string pathName){

	int magicNumber = 0;
	int imageNumber = 0;
	int rows = 0;
	int cols = 0;
	Mat dataMat;
	ifstream file(pathName, ios::binary);
	if (file.is_open() == true){

		file.read((char*)&magicNumber, sizeof(magicNumber));
		file.read((char*)&imageNumber, sizeof(imageNumber));
		file.read((char*)&rows, sizeof(rows));
		file.read((char*)&cols, sizeof(cols));
		magicNumber = CvtToLittleEndian(magicNumber);
		imageNumber = CvtToLittleEndian(imageNumber);
		rows = CvtToLittleEndian(rows);
		cols = CvtToLittleEndian(cols);

		// 每张数字图像为一个一维向量,构成imageNumber * (rows * cols)的矩阵
		dataMat = Mat::zeros(imageNumber, rows * cols, CV_32FC1);
		for (int i = 0; i < imageNumber / 1; i++){

			for (int j = 0; j < rows * cols; j++){

				unsigned char temp = 0;
				file.read((char*)&temp, sizeof(temp));
				float value = float((temp + 0.0) / 255.0);
				dataMat.at<float>(i, j) = value;
			}
		}
	}
	file.close();
	return dataMat;
}

Mat DigitsDetector::ReadMnistLabel(const string pathName){

	int magicNumber;
	int labelNumber;
	Mat labelMat;
	ifstream file(pathName, ios::binary);
	if (file.is_open() == true){

		file.read((char*)&magicNumber, sizeof(magicNumber));
		file.read((char*)&labelNumber, sizeof(labelNumber));
		magicNumber = CvtToLittleEndian(magicNumber);
		labelNumber = CvtToLittleEndian(labelNumber);

		labelMat = Mat::zeros(labelNumber, 1, CV_32SC1);
		for (int i = 0; i < labelNumber / 1; i++){

			unsigned char temp = 0;
			file.read((char*)&temp, sizeof(temp));
			labelMat.at<unsigned int>(i, 0) = (unsigned int)temp;
		}
	}
	file.close();
	return labelMat;
}

int DigitsDetector::CvtToLittleEndian(int i){

	unsigned char c1, c2, c3, c4;
	c1 =i & 255;
	c2 = (i >> 8) & 255;
	c3 = (i >> 16) & 255;
	c4 = (i >> 24) & 255;
	return ((int)c1 << 24) + ((int)c2 << 16) + ((int)c3 << 8) + c4;
}

void DigitsDetector::CreateSVM(){

	svm = SVM::create();
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::RBF);
	svm->setGamma(0.01);
	svm->setC(10.0);
	svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));
}

bool DigitsDetector::Train(Mat data, Mat label){

	return svm->train(data, ROW_SAMPLE, label);
}
float DigitsDetector::Predict(Mat testdata){

	return svm->predict(testdata);
}

实验效果

训练Mnist训练集过程

    训练过程时间有些长,需要耐心等待。我的测试环境为:windows10+i7-6700+8GRAM,debug版本的训练时间大约10分钟,release版本大约5分钟,训练过程如下图:


测试Mnist测试集的过程和结果

    测试10000张Mnist的测试集,也需要一小段时间,测试结果的准确率为98.33%,如下图。


测试现场手写数字

    在画板上利用鼠标手写测试数字,点击“开始识别当前数字”识别,实验效果如下图:


总结

    训练过程比较耗时,但是识别率比较高(相比于KNN好不少,KNN实现可以参考上一篇文章《KNN实现手写数字识别》)。和KNN的区别可以应用知乎上的一句神总结:svm, 就像是在河北和北京之间有一条边界线,如果一个人居住在北京一侧就预测为北京人,在河北一侧,就预测为河北人。但是住在河北的北京人和住在北京的河北人就会被误判。knn,就是物以类聚,人以群分。如果你的朋友里大部分是北京人,就预测你也是北京人。

附件

    源代码工程戳这里(注:release里面的可执行程序可以直接运行)。

更多参考

    SVM百度百科:https://baike.baidu.com/item/svm/4385807?fr=aladdin

    SVM笔试题:https://blog.csdn.net/szlcw1/article/details/52259668

    SVM最牛逼的解释:https://www.zhihu.com/question/21094489

    SVM车牌识别:https://blog.csdn.net/u010429424/article/details/75335212

    支持向量机通俗导论(理解SVM的三层境界):https://blog.csdn.net/v_july_v/article/details/7624837

    opencv3-svm参数详解:http://livezingy.com/svm-in-opencv3-1/ 


  • 3
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值