C++基於檔案流和armadillo讀取mnist

c艹使用者發表於2021-05-09

發現網上大把都是用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;
}

相關文章