1. 程式人生 > >C++ —— 讀取MNIST資料集資料並轉存為影象

C++ —— 讀取MNIST資料集資料並轉存為影象

       在上一個部落格中,我們已經對MNIST資料集的資料格式有了一定的瞭解,這裡我們要完成的工作是將讀到的資料轉成圖片,存入資料夾中,以便日後使用。在開始之前,我們先對該資料庫的儲存格式進行一個具體的介紹:

MNIST(Mixed National Institute of Standards and Technology database)是一個計算機視覺資料集,它包含70000張手寫數字的灰度圖片,包括60000張訓練圖片(60000訓練集分拆為 55000 的訓練集和 5000 的驗證集)和10000張測試圖片,其中每一張圖片包含 28 X 28 個畫素點。官網給的資料集合並不是原始的影象資料格式,而是編碼後的二進位制格式: 

影象的編碼為:



典型的head+data模式:前16個位元組分為4個整型資料,每個佔據4位元組,分別代表:資料資訊des、影象數量(img_num),影象行數(row)、影象列數(col),之後的資料全部為畫素,每row*col個畫素構成一張圖,每個色素的值為(0-255)。即測試影象(rain-images-idx3-ubyte)與訓練影象(train-images-idx3-ubyte)由5部分組成:


標籤的編碼為:



模式和前面的一樣,不同的是head只有8位元組,分別為des和標籤的數量(label_num).之後每一個位元組代表一個標籤,值為(0-9)。即測試標籤(t10k-labels-idx1-ubyte)與訓練標籤(train-labels-idx1-ubyte)由3部分組成:


在這裡,我們將MNIST資料集的資料讀出後,存為jpg圖片格式。程式碼如下:

#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/core/core.hpp>  
#include <vector>  
#include <iostream>  
#include <fstream>  
#include <string>  
#include<inttypes.h>
using namespace std;
using namespace cv;

//把大端資料轉換為我們常用的小端資料  
uint32_t swap_endian(uint32_t val)
{
	val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
	return (val << 16) | (val >> 16);
}

void readAndSave(const string& mnist_img_path, const string& mnist_label_path)
{
	//以二進位制格式讀取mnist資料庫中的影象檔案和標籤檔案  
	ifstream mnist_image(mnist_img_path, ios::in | ios::binary);
	ifstream mnist_label(mnist_label_path, ios::in | ios::binary);
	if (mnist_image.is_open() == false)
	{
		cout << "open mnist image file error!" << endl;
		return;
	}
	if (mnist_label.is_open() == false)
	{
		cout << "open mnist label file error!" << endl;
		return;
	}

	uint32_t magic;//檔案中的魔術數(magic number)  
	uint32_t num_items;//mnist影象集檔案中的影象數目  
	uint32_t num_label;//mnist標籤集檔案中的標籤數目  
	uint32_t rows;//影象的行數  
	uint32_t cols;//影象的列數  
	//讀魔術數  
	mnist_image.read(reinterpret_cast<char*>(&magic), 4);
	magic = swap_endian(magic);
	if (magic != 2051)
	{
		cout << "this is not the mnist image file" << endl;
		return;
	}
	mnist_label.read(reinterpret_cast<char*>(&magic), 4);
	magic = swap_endian(magic);
	if (magic != 2049)
	{
		cout << "this is not the mnist label file" << endl;
		return;
	}
	//讀影象/標籤數  
	mnist_image.read(reinterpret_cast<char*>(&num_items), 4);
	num_items = swap_endian(num_items);
	mnist_label.read(reinterpret_cast<char*>(&num_label), 4);
	num_label = swap_endian(num_label);
	//判斷兩種標籤數是否相等  
	if (num_items != num_label)
	{
		cout << "the image file and label file are not a pair" << endl;
	}
	//讀影象行數、列數  
	mnist_image.read(reinterpret_cast<char*>(&rows), 4);
	rows = swap_endian(rows);
	mnist_image.read(reinterpret_cast<char*>(&cols), 4);
	cols = swap_endian(cols);
	//讀取影象  
	for (int i = 0; i != num_items; i++)
	{
		char* pixels = new char[rows * cols];
		mnist_image.read(pixels, rows * cols);
		char label;
		mnist_label.read(&label, 1);
		Mat image(rows, cols, CV_8UC1);
		for (int m = 0; m != rows; m++)
		{
			uchar* ptr = image.ptr<uchar>(m);
			for (int n = 0; n != cols; n++)
			{
				if (pixels[m * cols + n] == 0)
					ptr[n] = 0;
				else
					ptr[n] = 255;
			}
		}
		string saveFile = "D:\\C++_file\\SVM_DEAL\\MNIST DATABASE\\imagedatabase\\mnist_test\\" + to_string((unsigned int)label) + "_" + to_string(i) + ".jpg";
		imwrite(saveFile, image);
	}
}

int main()
{
	readAndSave("D:\\C++_file\\SVM_DEAL\\MNIST DATABASE\\t10k-images.idx3-ubyte", "D:\\C++_file\\SVM_DEAL\\MNIST DATABASE\\t10k-labels.idx1-ubyte");  
	//readAndSave("D:\\C++_file\\SVM_DEAL\\MNIST DATABASE\\train-images.idx3-ubyte", "D:\\C++_file\\SVM_DEAL\\MNIST DATABASE\\train-labels.idx1-ubyte");
	return 0;
}

執行後即可得到圖片檔案: