在ubuntu16.04+opencv3.0环境下使用mnist手写体数据集编写相关程序

 因为最近可能做项目需要,因此搜索了手写体数字检测博客,在查看了大量博客后总结了一些自己的学习小体会。但是但是-----敲黑板、划重点了。呵呵,就是还是先把参考的几篇好博客的分享给大家。
 http://www.itnose.net/detail/6525586.html,这篇文章里面最可取的就是分析了mnist数据集存在的坑。很多坑我也不是太明白,哈哈。
 http://blog.csdn.net/xuan_zizizi/article/details/71102018,这篇博客,对就是这篇,上一个网址我就是在这里找到的。所以这篇博客挺好的。
 首先介绍下mnist数据集吧,这个数据集至少在现在看来在手写体方面使用的还是比较多的。下载它可以在这里`http://yann.lecun.com/exdb/mnist/`,打开后你可以看到四个可以下载的包,但是你会发现网传的训练60000张图片,测试10000张图片,下载下来的包怎么这么小,解压下来一看,我靠居然是二进制文件。不着急,因为很多前辈已经给了很多详细的参考,特别是在http://www.itnose.net/detail/6525586.html下,写的非常详细,如果感兴趣可以认真看下。
 有3个函数需要先贴出来.
 1.大小端转换`int reverseInt(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;//左移
}`
因为MNIST是大端存储,然而大部分的Intel处理器都是小端存储,所以对于int、long、float这些多字节的数据类型,就要一个一个byte地翻转过来,才能正确显示。
2.读取训练图片

Mat read_mnist_image(const char* fileName)
{
int magic_number = 0;
int number_of_images = 0;
int n_rows = 0;
int n_cols = 0;
Mat DataMat;
ifstream file;
file.open(fileName, std::ifstream::binary);


//fstream file;//(fileName, ios_base::binary);
//file.open(fileName,  ios_base::binary);
if (file.is_open())
{
cout << "成功打开图像集\n";
file.read((char*)&magic_number, sizeof(magic_number));
file.read((char*)&number_of_images, sizeof(number_of_images));
file.read((char*)&n_rows, sizeof(n_rows));
file.read((char*)&n_cols, sizeof(n_cols));
cout << "magic_number=/n"<<magic_number << "number_of_images=\n " << number_of_images << " " << n_rows << " " << n_cols << endl;
magic_number = reverseInt(magic_number);
number_of_images = reverseInt(number_of_images);
n_rows = reverseInt(n_rows);
n_cols = reverseInt(n_cols);
cout << "MAGIC NUMBER = " << magic_number<< " ;NUMBER OF IMAGES = " << number_of_images<< " ; NUMBER OF ROWS = " << n_rows << " ; NUMBER OF COLS = " << n_cols << endl;
//-test-
//输出第一张和最后一张图,检测读取数据无误
Mat s = Mat::zeros(n_rows, n_rows * n_cols, CV_32FC1);
Mat e = Mat::zeros(n_rows, n_rows * n_cols, CV_32FC1);
cout << "开始读取Image数据......\n";
DataMat = Mat::zeros(number_of_images, n_rows * n_cols, CV_32FC1);
for (int i = 0; i < number_of_images; i++)
{
for (int j = 0; j < n_rows * n_cols; j++)
{
unsigned char temp = 0;
file.read((char*)&temp, sizeof(temp));
float pixel_value = float((temp + 0.0) / 255.0);
DataMat.at<float>(i, j) = pixel_value;

//打印第一张和最后一张图像数据
if (i == 0)
{
s.at<float>(j / n_cols, j % n_cols) = pixel_value;
}
else if (i == number_of_images - 1)
{
e.at<float>(j / n_cols, j % n_cols) = pixel_value;
}
}
}
//imshow("first image", s);
//cout << "first image = " << s << endl;
//imshow("last image", e);
waitKey(0);
}
file.close();
return DataMat;
}

3.读取标签

Mat read_mnist_label(const char* fileName)
{
int magic_number;
int number_of_items;
Mat LabelMat;

ifstream file;
file.open(fileName, std::ifstream::binary);

//fstream file;//(fileName, ios_base::binary);
//file.open(fileName, ios_base::binary);
if (file.is_open())
{
cout << "成功打开标签\n";
file.read((char*)&magic_number, sizeof(magic_number));
file.read((char*)&number_of_items, sizeof(number_of_items));
magic_number = reverseInt(magic_number);
number_of_items = reverseInt(number_of_items);
cout << "MAGIC NUMBER = " << magic_number << "  ; NUMBER OF ITEMS = " << number_of_items << endl;
//-test-
//number_of_items = testNum;
//记录第一个label和最后一个label
unsigned int s = 0, e = 0;
cout << "开始读取Label数据......\n";
LabelMat = Mat::zeros(number_of_items, 1, CV_32SC1);
for (int i = 0; i < number_of_items; i++)
{
unsigned char temp = 0;
file.read((char*)&temp, sizeof(temp));
LabelMat.at<unsigned int>(i, 0) = (unsigned int)temp;
//打印第一个和最后一个label
if (i == 0) s = (unsigned int)temp;
else if (i == number_of_items - 1) e = (unsigned int)temp;
}
cout << "first label = " << s << endl;
   cout << "last label = " << e << endl;
}
file.close();
return LabelMat;
}

其中在读图片和读标签函数中做了一点修改,不知道是因为什么原因,当把文件名以string 类型赋值的时候,file.open()一直报错,所以最后只能改成char testImage[] = “t10k-images.idx3-ubyte”。
最后就是在主函数中进行训练数据集和测试数据集。

int main()
{
//读取训练数据
Mat trainData;
Mat labels;
trainData = read_mnist_image(trainImage);
labels = read_mnist_label(trainLabel);
cout << " 训练数据行数:" << trainData.rows << "训练数据列数:" << trainData.cols << endl;
cout << " 训练标签行数:" << labels.rows << "训练数据列数: " << labels.cols << endl;
cout << "训练数据读取完成" << endl;
//训练参数
Ptr<SVM> 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));
cout << "参数设置完成" << endl;
//训练分类器
cout << "开始训练分类器" << endl;
svm->train(trainData, ROW_SAMPLE, labels);
cout << "分类器训练完成" << endl;
//保存训练器
svm->save("./mnist_svm.xml");
cout << "save as ./mnist_svm.xml" << endl;
//下载分类器
//cout << "开始导入SVM文件...\n";
//Ptr<SVM> svm1 = StatModel::load<SVM>("mnist_dataset/mnist_svm.xml");
//cout << "成功导入SVM文件...\n";
//读取测试数据
Mat testData;
Mat tLabel;
testData = read_mnist_image(testImage);
tLabel = read_mnist_label(testLabel);
cout << "测试数据读取完成" << endl;

float count = 0;
for (int i = 0; i < testData.rows; i++) 
{
Mat sample = testData.row(i);
float res=0.0;
res=svm->predict(sample);
res = abs(res - tLabel.at<unsigned int>(i, 0)) <= FLT_EPSILON ? 1.f : 0.f;
count += res;
}

cout << "正确的识别个数: " << count << endl;
cout << "错误率为:" << (10000 - count + 0.0) / 10000 * 100.0 << "%\n";
system("pause");





return 0;
}
 那么最后程序成功运行完成后就会在build文件夹或者项目的根目录下存在一个mnist_svm.xml。这就是我们训练出来的模型了。以后需要预测手写体数字直接加载这个模型就可以了,不用每次都训练数据集。
 好了这就是训练模型的程序了。
 -------------------------------我是分割线
 哈哈,但是此时,如果你要读取自己的视频或者本地图片进行预测,你会发现很多坑的,哈哈。
 例如预测结果......en  ,都是些什么玩意儿。完全对不上,说好准确率98%以上的,结果错误率。具体原因,请听下回分解。

(mnisit读出来的图片是黑底白字)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

城墙郭外斜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值