在OpenCV的安装路径下,搜索digits.png
,可以得到一张图片,图片大小为1000* 2000,有0-9的10个数字,每5行为一个数字,总共50行,共有5000个手写数字,每个数字块大小为20* 20。 如下图所示:
下面将把这些数字中的0和1作为二分类的准备数据。其中0有500张,1有500张。
代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
//char ad[128] = { 0 };
int filename = 0, filenum = 0;
Mat img = imread("digits.png");
if (!img.data)
{
cout << "src image load failed!" << endl;
system("pause");
return -1;
}
/*imshow("原始图像", img);*/
Mat gray;
cvtColor(img, gray, CV_BGR2GRAY);
int b = 20;
int m = gray.rows / b; //原图为1000*2000,裁剪为50行100列共计5000个20*20的小图
int n = gray.cols / b;
for (int i = 0; i < m; i++)
{
int offsetRow = i * b; //行上的偏移量
if (i % 5 == 0 && i != 0)
{
filename++;
filenum = 0;
}
for (int j = 0; j < n; j++)
{
int offsetCol = j * b; //列上的偏移量
stringstream ss;
// 将多个字符串放入 ss 中
ss << "E:\\data\\" << filename<<"\\"<<filenum++<<".png" ;
string file = ss.str();
ss.str("");//清空ss
//截取20*20的小块
Mat tmp;
gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
imshow("tuxiang",tmp);
/*waitKey(100);*/
imwrite(file, tmp);
}
}
waitKey();
return 0;
}
【注意】:在运行命令前,需要在E盘data文件夹下新建两个子文件夹,命名为0和1。这样才能把图片存入。
我们可以手动将1000张图片划分为训练集和测试集,其中训练数据800张,0,1各400张;测试数据200张,0,1各100张。
(至于更简单的方法,暂时还没想到,读者可以自行探索)
(关于这一部分,可参考博客:利用C++和OpenCV3设计支持向量机SVM分类器)
由于只识别数字0和数字1,因此定义了两个子函数:
get_1(trainingImages, trainingLabels);
get_0(trainingImages, trainingLabels);
主要功能为:
get_1()用来将1文件夹中的图像Mat类型和标签写入训练集。
get_0()用来将0文件夹中的图像Mat类型和标签写入训练集。
这样就保证了特征和标签是一一对应的关系push_back(0)或者push_back(1)其实就是我们贴标签的过程。
在主函数中,将get_1()与get_0()写好的包含特征的矩阵拷贝给trainingImages,将包含标签的vector容器进行类型转换后拷贝到trainingLabels里,至此,数据准备工作完成,trainingImages与trainingLabels就是我们要训练的数据。
子函数代码如下:
void get_1(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "E:\\data\\train_image\\1";
int number =400;
for (int i = 0; i < number; i++)
{
stringstream ss;
// 将多个字符串放入 ss 中
ss << filePath <<"\\"<< i << ".png";
string files = ss.str();
ss.str("");//清空ss
Mat SrcImage = imread(files);
SrcImage = SrcImage.reshape(1, 1);//转换为单通道行向量
trainingImages.push_back(SrcImage);
trainingLabels.push_back(1);
}
}
void get_0(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "E:\\data\\train_image\\0";
vector<string> files;
int number = 400;
for (int i = 0; i < number; i++)
{
stringstream ss;
// 将多个字符串放入 ss 中
ss << filePath << "\\" << i << ".png";
string files = ss.str();
ss.str("");//清空ss
Mat SrcImage = imread(files);
SrcImage = SrcImage.reshape(1, 1);//转换为单通道列向量
trainingImages.push_back(SrcImage);
trainingLabels.push_back(0);
}
除了这两个子函数,需要注意的是,
①样本数据必须是CV_32FC1类型。这是由opencv3版本决定的;
②样本标签必须是CV_32SC1,opencv3后从int数组转换为CV_32SC1类型,而opencv2是从float数据转换。
特征提取和数据的准备是同步完成的。这里我们简单粗暴将整个图的所有像素作为了特征,因为我们关注更多的是整个的训练过程,所以选择了最简单的方式完成特征提取工作,除此中外,特征提取的方式有很多,比如LBP,HOG等等。
上面是行向量,这里注释写错了
程序如下:
//配置SVM训练器参数
Ptr<SVM> svm = SVM::create();//创建一个svm对象
svm->setType(SVM::C_SVC);//设置SVM公式类型
svm->setKernel(SVM::RBF);//设置SVM核函数类型
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10000, 1e-6));//设置SVM训练时迭代终止条件
svm->train(trainingData, ROW_SAMPLE, classes);//训练数据
程序如下:
//保存模型
svm->save("svm.xml");
cout << "训练好了!!!" << endl;
ok,这样,SVM就训练完成了。
下面开始测试。
核心代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::ml;
int main()
{
int result = 0;
int number = 100;
Ptr<SVM> svm = SVM::load("svm.xml");//加载svm对象
string filePath = "E:\\data\\train_image\\";
for (int i = 0; i < number; i++)
{
stringstream ss;
// 将多个字符串放入 ss 中
ss << filePath << "0\\" << i << ".png";
string files = ss.str();
ss.str("");//清空ss
Mat inMat = imread(files);
Mat p = inMat.reshape(1, 1);
p.convertTo(p, CV_32FC1);
int response = (int)svm->predict(p);
if (response == 0)
{
result++;
}
}
cout << result << endl;
getchar();
return 0;
}
可以发现,识别正确率还是很高的;当然,也可以加入下列代码进行测试:
/test
stringstream sss;
// 将多个字符串放入 ss 中
sss << filePath << "1\\" << 56 << ".png";
string filess = sss.str();
sss.str("");//清空ss
Mat src = imread(filess);
Mat p = src.reshape(1, 1);
p.convertTo(p, CV_32FC1);
int test = (int)svm->predict(p);
cout << "测试一下:" << test << endl;
//
ok,大功告成!
如果想要全部的三个文件的代码,可以从下列链接中下载:
https://download.csdn.net/download/didi_ya/15561230
如果对你有所帮助,记得点个赞哟~