C++基于文件流和armadillo读取mnist

本文介绍了一种使用C++和文件流读取MNIST数据集的方法,包括读取文件头、解析图像和标签数据的具体实现。该方法不依赖于OpenCV,适用于希望直接使用标准库进行数据读取的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

发现网上大把都是用python读取mnist的,用C++大都是用opencv读取的,但我不怎么用opencv,因此自己摸索了个使用文件流读取mnist的方法,armadillo仅作为储存矩阵的一种方式。

1. mnist文件

首先避坑,这些文件要解压。
没有肾么描述
官网截图可知,文件头很简单,只有若干个32位整数,MSB,像素和标签均是无符号字节(即unsigned char)可以先读取文件头,再读取剩下的部分。

2. 读取文件头

我觉得没什么必要啊,直接跳过不行吗
文件头都是32位,那就整四个unsigned char呗。

	FILE *File = fopen(fileName, "r");
	fseek(File, 0, 0);
	uchar a[4];
	fread(a, 4, 1, File);

这样a字符串就保存了一个整数。

	x = ((((a[0] * 256) + a[1]) * 256) + a[2]) * 256 + a[3];

然后就得到了呗。
看每个文件有多少文件头,就操作几次(并可以顺便与官方的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++)
	{
		fread(a, 1, 1, File);
		image(i, j) = double(a);
	}

这样就读取了一张图片。其余以此类推吧。

4. 完整代码

可以复制,可以修改,也可以用于商业和学术,但是请标注原作者(就是我)。
mnist.h

#ifndef MNIST_H  
#define MNIST_H
#include<iostream>
#include<fstream>
#include<armadillo>
#include<vector>
#include<cstdio>

#define uchar unsigned char

using namespace std;
using namespace arma;

//小端存储转换
int reverseInt(uchar *a);

//读取image数据集信息
mat read_mnist_image(const char *fileName);

//读取label数据集信息
mat read_mnist_label(const char* fileName);
#endif

mnist.cpp

//mnist.cpp
//作者:C艹
#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, "r");
	fseek(File, 0, 0);
	mat image;
	uchar a[4];
	fread(a, 4, 1, File);
	int magic = reverseInt(a);
	if (magic != 2051) //magic number wrong
	{
		cout << magic;
		return mat(0, 0, fill::zeros);
	}
	fread(a, 4, 1, File);
	const int num_img = reverseInt(a);
	fread(a, 4, 1, File);
	const int num_row = reverseInt(a);
	fread(a, 4, 1, File);
	const int num_col = reverseInt(a);
	const int size_img = num_col * num_row;
	// 文件头读取完毕
	image.reshape(num_img, size_img);
	uchar img[784];
	for (int i = 0; i < num_img; i++)
	{
		fseek(File, i*784+16, SEEK_SET);
		fread(img, size_img, 1, File);
		for (int j = 0; j < size_img; j++)
		{
			image(i, j) = double(img[j])/256;
		}
	}
	fclose(File);
	return image;
}

mat read_mnist_label(const char *fileName)
{
	FILE* File = fopen(fileName, "r");
	fseek(File, 0, 0);
	uchar a[4];
	fread(a, 4, 1, File);
	int magic = reverseInt(a);
	if (magic != 2049) //magic number wrong
	{
		cout << magic;
		return mat(0, 0, fill::zeros);
	}
	fread(a, 4, 1, File);
	const int num_lab = reverseInt(a);
	// 文件头读取完毕
	mat label(num_lab, 10, fill::zeros);
	uchar lab[1];
	for (int i = 0; i < num_lab; i++)
	{
		fread(lab, 1, 1, File);
		label(i, int(lab[0])) = 1;
	}
	fclose(File);
	return label;
}


### 使用Armadillo C++库加载解析MATLAB .mat文件 为了实现这一目标,可以利用Armadillo内置的功能以及第三方库的支持。具体来说,Armadillo本身并不直接支持.mat文件的读写操作;然而,可以通过链接HDF5或其他专门处理.mat文件的库来完成此任务。 #### 方法一:使用hdf5接口(推荐) 由于较新版本的.mat文件基于HDF5格式存储数据,因此可以直接借助HDF5 API进行读取。下面展示了一个简单的例子,展示了如何设置环境并编写代码以读取.mat文件中的矩阵[^1]: 首先,在项目中引入必要的头文件,并确保编译器能够找到HDF5库路径及其相应的include目录。 ```cpp #include <armadillo> #include "H5Cpp.h" using namespace arma; using namespace H5; void load_mat_file(const std::string& filename, mat& output_matrix) { Exception::dontPrint(); // 打开文件 H5File file(filename.c_str(), H5F_ACC_RDONLY); // 获取根组内的所有对象名 Group rootGroup(file.openGroup("/")); StrType strdatatype(0, H5T_VARIABLE_LENGTH); LinkInfo linkinfo; ssize_t numObjs = rootGroup.getNumObjs(); for (int i=0; i<numObjs; ++i){ char objname[256]; rootGroup.getObjnameByIdx(i,objname); DataSet dataset = rootGroup.openDataSet(objname); DataSpace dataspace = dataset.getSpace(); hsize_t dims_out[2]; int rank = dataspace.getSimpleExtentNdims(); dataspace.getSimpleExtentDims(dims_out,NULL); if(rank==2){ // 只考虑二维数组即matrix的情况 float* buffer = new float[dims_out[0]*dims_out[1]]; FloatType filetype(dataset.getTypeClass()); dataset.read(buffer,filetype); output_matrix.set_size(dims_out[0],dims_out[1]); for(size_t row=0;row<output_matrix.n_rows;++row){ for(size_t col=0;col<output_matrix.n_cols;++col){ output_matrix(row,col)=buffer[row*dims_out[1]+col]; } } delete[] buffer; }else{ continue; } } } ``` 这段程序片段定义了一个名为`load_mat_file`的函数,它接受两个参数——`.mat`文件的名字字符串一个待填充的目标arma::mat类型的变量。该函数会遍历给定.mat文件里的每一个dataset,并尝试将其转换成arma::mat形式存入到指定位置上。 请注意,这只是一个基础示例,实际应用可能需要更复杂的错误处理机制其他特性支持。 #### 方法二:采用其他专用工具链 如果不想自己动手集成HDF5,则可以选择一些已经封装好的解决方案,比如matio库。MATIO是一个开源C/C++库,专为读/写旧版v7.3之前的.mat文件而设计。虽然这种方法对于某些特定情况可能是有效的,但对于现代应用程序而言,建议优先选用前一种方案因为其更好的兼容性性能表现[^2]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值