博客
关于我
C++基于文件流和armadillo读取mnist
阅读量:530 次
发布时间:2019-03-08

本文共 3217 字,大约阅读时间需要 10 分钟。

读取MNIST文件的C++实现:基于文件流的简洁方法

在数据科学和机器学习领域,MNIST是常用的手写数字数据集。虽然大部分开发者习惯使用Python或TensorFlow处理MNIST,但使用C++与其标配OpenCV可能显得复杂。以下是手动实现读取MNIST文件的方法,这种方法省去了依赖外部库的麻烦,仅通过标准的C++文件操作和Armadillo矩阵库完成。

1. MNIST文件格式

MNIST文件由文件头和图像/标签数据组成。文件头包含四个32位无符号字节(unsigned char),即整数。具体来说:

  • 第一个4字节是魔数(magic number),默认值为2051。
  • 接下来的4字节是数量(num_img),表示MNIST文件中图像的数量。
  • 然后是高度(num_row)和宽度(num_col),确定每张图像的尺寸。
  • 最后是一个大小(size_img),即num_col * num_row。

文件尾部包含所有图像和标签数据。在处理时,建议先验证文件头的魔数,以确保文件格式正确。

2. 读取文件头

文件头读取相对简单。通过文件指针读取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];

这段代码读取出魔数并将其转换为整数,验证文件格式。读取完文件头后,接下来的数据即为图像和标签部分。

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矩阵库,便于后续的训练和处理。

4. 阅读完整代码

完整的代码实现如下:

#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/

你可能感兴趣的文章
Objective-C实现异或加密(附完整源码)
查看>>
Objective-C实现异或加密(附完整源码)
查看>>
Objective-C实现异或密码算法(附完整源码)
查看>>
Objective-C实现异步编程(附完整源码)
查看>>
Objective-C实现弧度到度算法 (附完整源码)
查看>>
Objective-C实现循环移位(附完整源码)
查看>>
Objective-C实现循环链表(附完整源码)
查看>>
Objective-C实现循环队列算法(附完整源码)
查看>>
Objective-C实现循环队列链表算法(附完整源码)
查看>>
Objective-C实现快速fibonacci斐波那契算法(附完整源码)
查看>>
Objective-C实现快速傅立叶变换FFT算法(附完整源码)
查看>>
Objective-C实现快速傅里叶变换FFT(附完整源码)
查看>>
Objective-C实现快速傅里叶变换FFT(附完整源码)
查看>>
Objective-C实现快速排序(附完整源码)
查看>>
Objective-C实现快速排序(附完整源码)
查看>>
Objective-C实现快速排序算法(附完整源码)
查看>>
Objective-C实现恩尼格玛密码机算法(附完整源码)
查看>>
Objective-C实现感知哈希算法(附完整源码)
查看>>
Objective-C实现感知哈希算法(附完整源码)
查看>>
Objective-C实现截留雨水问题的动态编程方法算法(附完整源码)
查看>>