遇到的几个大坑
1.官方主页给出了每个文件的字节数是个玄幻数据,training set images (9912422 bytes) ,这个字节数是解压前的,解压后字节数应该为47,040,016,这个数等于4 + 4 + 4 + 4 + 60000 * 28 * 28。
2.windows下的fgetc是个玄幻函数,以文本方式"r"读取时会错误判断EOF标志,改成"rb",以字节流方式读取即可。
3.virtual studio2017默认的堆栈大小为1MB,若不修改程序会出现栈溢出错误,属性->链接器->系统->修改堆栈大小和堆栈保留大小为100000000。


#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

const int MnistTrainNumber = 6000;
const int MnistTestNumber = 1000;

//存储像素信息
struct Image
{
	cv::Mat pixs;
	Image()
	{
		pixs.create(Size(28, 28), CV_8U);
	}
};
struct MnistImage
{
	//检验值
	int magicNumber;
	//图片数量
	int number;
	//图片行数
	int rows;
	//图片列数
	int cols;
	//图片数组
	vector<Image> images;
};
struct MnistLabel
{
	//检验值
	int magicNumber;
	//标签数量
	int number;
	//标签数组
	vector<int> labels;
};
//训练集
struct MnistTrainSet
{
	MnistImage trainImages;
	MnistLabel trainLabels;
};
//测试集
struct MnistTestSet
{
	MnistImage trainImages;
	MnistLabel trainLabels;
};
//从file当前指针开始,连续读取length个字节,返回读取到的整数
int readData(FILE *file, int length)
{
	int ans = 0;
	for (int i = 0; i < length; i++)
	{
		ans = ans * 256 + fgetc(file);
	}
	return ans;
}
//解析图片字节流文件
int parseMnistImage(const char *fileName, MnistImage &mnistImage, int imagesNumber)
{
	FILE *out = fopen(fileName, "rb");
	if (out == NULL) return -1;
	mnistImage.magicNumber = readData(out, 4);
	mnistImage.number = readData(out, 4);
	mnistImage.rows = readData(out, 4);
	mnistImage.cols = readData(out, 4);
	for (int k = 0; k < imagesNumber; k++)
	{
		Image image;
		for (int i = 0; i < 28; i++)
		{
			for (int j = 0; j < 28; j++)
			{
				int x = fgetc(out);
				image.pixs.at<uchar>(i, j) = x;
			}
		}
		mnistImage.images.push_back(image);
	}
	fclose(out);
	return mnistImage.magicNumber;
}
//解析标签字节流文件
int parseMnistLabel(const char *fileName, MnistLabel &mnistLabel, int labelNumber)
{
	FILE *out = fopen(fileName, "rb");
	if (out == NULL) return -1;
	mnistLabel.magicNumber = readData(out, 4);
	mnistLabel.number = readData(out, 4);
	for (int i = 0; i < labelNumber; i++)
	{
		int x = fgetc(out);
		mnistLabel.labels.push_back(x);
	}
	fclose(out);
	return mnistLabel.magicNumber;
}
void virtualizeData(Mat &mat)
{
	imshow("virtualizeData", mat);
	waitKey();
}
int main()
{
	MnistTrainSet mnistTrainSet;
	MnistTestSet mnistTestSet;
	//magic number分别为2051,2049,2051,2049,与官方提供的检验值比对以确定解析程序是否有误
	cout << parseMnistImage("train-images.idx3-ubyte", mnistTrainSet.trainImages, MnistTrainNumber) << endl;
	cout << parseMnistLabel("train-labels.idx1-ubyte", mnistTrainSet.trainLabels, MnistTrainNumber) << endl;
	cout << parseMnistImage("t10k-images.idx3-ubyte", mnistTestSet.trainImages, MnistTestNumber) << endl;
	cout << parseMnistLabel("t10k-labels.idx1-ubyte", mnistTestSet.trainLabels, MnistTestNumber) << endl;
	//可视化训练集中第k张图片
	int k = 5;
	virtualizeData(mnistTrainSet.trainImages.images[k].pixs);
	return 0;
}


08-31 12:17