前言
发现网上大把都是用python读取mnist的,用C++大都是用opencv读取的,但我不怎么用opencv,因此自己摸索了个使用文件流读取mnist的方法,armadillo仅作为储存矩阵的一种方式。
1. mnist文件
首先避坑,这些文件要解压。
官网截图可知,文件头很简单,只有若干个32位整数,MSB,像素和标签均是无符号字节(即unsigned char)可以先读取文件头,再读取剩下的部分。
2. 读取文件头
我觉得没什么必要啊,直接跳过不行吗
文件头都是32位,那就整四个unsigned char呗。
uchar a, b, c, d; File >> a >> b >> c >> d;
这样a、b、c、d就保存了一个整数。
x = ((((a * 256) + b) * 256) + c) * 256 + d;
然后就得到了呗。
看每个文件有多少文件头,就操作几次(并可以顺便与官方的magic number进行对比),剩下的就是文件的内容了。
3. 读取内容
这部分可以依照之前的方法,一次读取一个字符,再保存至矩阵当中。例如:
uchar a; mat image(28, 28, fill::zeros); // 这是个矩阵! for(int i = 0; i < 28; i++) //28行28列的图像懒得改了 for(int j = 0; j < 28; j++) { File >> a; image(i, j) = double(a); }
这样就读取了一张图片。其余以此类推吧。
4. 完整代码
可以复制,可以修改,也可以用于商用和学术,但是请标注原作者(就是我)。
mnist.h
#ifndef MNIST_H #define MNIST_H #include<iostream> #include<fstream> #include<armadillo> #define uchar unsigned char using namespace std; using namespace arma; //小端存储转换 int reverseInt(uchar a, uchar b, uchar c, uchar d); //读取image数据集信息 mat read_mnist_image(const string fileName); //读取label数据集信息 mat read_mnist_label(const string fileName); #endif
mnist.cpp
//mnist.cpp //作者:C艹 #include "mnist.h" int reverseInt(uchar a, uchar b, uchar c, uchar d) { return ((((a * 256) + b) * 256) + c) * 256 + d; } mat read_mnist_image(const string fileName) { fstream File; mat image; File.open(fileName); if (!File.is_open()) // cannot open file { cout << "文件打不开啊" << endl; return mat(0, 0, fill::zeros); } uchar a, b, c, d; File >> a >> b >> c >> d; int magic = reverseInt(a, b, c, d); if (magic != 2051) //magic number wrong { cout << magic; return mat(0, 0, fill::zeros); } File >> a >> b >> c >> d; int num_img = reverseInt(a, b, c, d); File >> a >> b >> c >> d; int num_row = reverseInt(a, b, c, d); File >> a >> b >> c >> d; int num_col = reverseInt(a, b, c, d); // 文件头读取完毕 image = mat(num_img, num_col * num_row, fill::zeros); for(int i = 0; i < num_img; i++) for (int j = 0; j < num_col * num_row; j++) { File >> a; image(i, j) = double(a); } return image; } mat read_mnist_label(const string fileName) { fstream File; mat label; File.open(fileName); if (!File.is_open()) // cannot open file { cout << "文件打不开啊" << endl; return mat(0, 0, fill::zeros); } uchar a, b, c, d; File >> a >> b >> c >> d; int magic = reverseInt(a, b, c, d); if (magic != 2049) //magic number wrong { cout << magic; return mat(0, 0, fill::zeros); } File >> a >> b >> c >> d; int num_lab = reverseInt(a, b, c, d); // 文件头读取完毕 label = mat(num_lab, 10, fill::zeros); for (int i = 0; i < num_lab; i++) { File >> a; label(i, int(a)) = 1; } return label; }