本文共 3217 字,大约阅读时间需要 10 分钟。
读取MNIST文件的C++实现:基于文件流的简洁方法
在数据科学和机器学习领域,MNIST是常用的手写数字数据集。虽然大部分开发者习惯使用Python或TensorFlow处理MNIST,但使用C++与其标配OpenCV可能显得复杂。以下是手动实现读取MNIST文件的方法,这种方法省去了依赖外部库的麻烦,仅通过标准的C++文件操作和Armadillo矩阵库完成。
MNIST文件由文件头和图像/标签数据组成。文件头包含四个32位无符号字节(unsigned char),即整数。具体来说:
文件尾部包含所有图像和标签数据。在处理时,建议先验证文件头的魔数,以确保文件格式正确。
文件头读取相对简单。通过文件指针读取4字节的整数部分,可以提取文件的基本信息。例如:
FILE *File = fopen(fileName, "r");fseek(File, 0, 0);uchar a[4];fread(a, 4, 1, File);int magic = ((((a[0] * 256) + a[1]) * 256) + a[2]) * 256 + a[3];
这段代码读取出魔数并将其转换为整数,验证文件格式。读取完文件头后,接下来的数据即为图像和标签部分。
每张图像以28x28的大小存储,共784个字节。在读取时,可以逐行读取:
mat image;intValue magic = reverseInt(a);// 验证魔数并读取文件头参数size_t size_img = num_col * num_row;image.reshape(num_img, size_img);// 读取图像数据for (int i = 0; i < num_img; ++i) { fseek(File, i * 784 + 16, SEEK_SET); // 跳过文件头后的每张图像起始位置 uchar img[784]; fread(img, size_img, 1, File); for (int j = 0; j < size_img; ++j) { image(i, j) = double(img[j]) / 256.0; }} 通过文件流逐个读取图像数据并存入矩阵中,这种方式兼容Armadillo矩阵库,便于后续的训练和处理。
完整的代码实现如下:
#ifndef MNIST_H#define MNIST_H#include#include #include #include #include #include #include #include using namespace std;using namespace arma;// 魔数转换函数inline int reverseInt(uchar *a) { return ((((a[0] * 256) + a[1]) * 256) + a[2]) *256 + a[3];}// 读取MNIST图像数据mat read_mnist_image(const char *fileName);// 读取MNIST标签数据mat read_mnist_label(const char *fileName);#endif
#include "mnist.h"int reverseInt(uchar *a) { return ((((a[0] * 256) + a[1]) * 256) + a[2]) * 256 + a[3];}mat read_mnist_image(const char *fileName) { FILE *File = fopen(fileName, "rb"); fseek(File, 0, 0); uchar a[4]; fread(a, 4, 1, File); int magic = reverseInt(a); if (magic != 2051) { cout << magic << endl; return mat(0, 0, fill::zeros); } fread(a, 4, 1, File); int num_img = reverseInt(a); fread(a, 4, 1, File); int num_row = reverseInt(a); fread(a, 4, 1, File); int num_col = reverseInt(a); int size_img = num_col * num_row; mat image(num_img, size_img, fill::zeros); uchar img[784]; for (int i = 0; i < num_img; ++i) { fseek(File, i * 784 + 16, SEEK_SET); // 跳过前面的16字节(文件头) fread(img, size_img, 1, File); for (int j = 0; j < size_img; ++j) { image(i, j) = double(img[j])/256.0; } } fclose(File); return image;}mat read_mnist_label(const char *fileName) { FILE *File = fopen(fileName, "rb"); fseek(File, 0, 0); uchar a[4]; fread(a, 4, 1, File); int magic = reverseInt(a); if (magic != 2049) { cout << magic << endl; return mat(0, 0, fill::zeros); } fread(a, 4, 1, File); int num_lab = reverseInt(a); mat labels(num_lab, 10, fill::zeros); uchar lab[1]; for (int i = 0; i < num_lab; ++i) { fread(lab, 1, 1, File); labels(i, static_cast (lab[0])) = 1; } fclose(File); return labels;} 以上实现利用文件流逐个读取MNIST文件,结合Armadillo矩阵处理,大大简化了读取过程,同时避免了外部依赖。
转载地址:http://fgwiz.baihongyu.com/