mnist是在图像机器学习占据“Hello World”地位的库。下载地址如下:http://yann.lecun.com/exdb/mnist/ train-images-idx3-ubyte(训练集数据,存放训练图像的集合) train-labels-idx1-ubyte(训练集标签,存放训练图像属于1~9中的哪一个) t10k-images-idx3-ubyte(测试集数据,存放测试图像的集合) t10k-labels-idx1-ubyte(测试集标签,存放测试图像属于1~9中的哪一个) 可见其是两类文件,数据和标签。 首先数据文件的存放格式如下: 第0~3字节:为魔数,其实就是校验位,告诉我们这个文件就是数据文件 第4~7字节:图像个数,告诉我们这个数据集里面存放了多少张图像 第8~11字节:每个图像有多少行 第12~15字节:每个图像有多少列 后面的数据是按先列后行的方式存储的图像数据,一张图像的占用空间为:(行×列)个字节 有了上面这些信息,就可以从数据文件中解析出图像了,代码如下:
#include <opencv2/opencv.hpp> using namespace cv; void main() { char savepath[30];//图像存储路径 uchar readbuf[4];//信息数据读取空间 FILE *f; fopen_s(&f, "train-images.idx3-ubyte","rb"); fread_s(readbuf, 4, 1, 4, f);//读取魔数,即文件标志位 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像个数 int numOfImg = (readbuf[0] << 24)+ (readbuf[1] << 16)+(readbuf[2]<<8)+ readbuf[3];//图像个数 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像行数 int imgheight= (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像行数 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像列数 int imgwidth = (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像列数 int imgdatalen = imgheight*imgwidth;//图像数据长度 Mat img(imgheight, imgwidth, CV_8UC1); for (int i = 0; i < numOfImg; i++) { sprintf_s(savepath, 30,"train-images\\].png", i + 1); fread_s(img.data, imgdatalen, 1, imgdatalen, f);//读取数据集图像列数 imwrite(savepath, img); if ((i+1)0==0) { printf("].png\n", i + 1); } } img.release(); fclose(f); }因为图像是用来训练测试的,因此光有图像数据本身不够,还要知道图像上面的字符到底是个啥,这也就是标签文件的作用。其格式如下: 第0~3字节:为魔数,也就是校验位,告诉我们这个文件就是标签文件 第4~7字节:标签个数,告诉我们这个数据集里面存放了多少个图像的标签 后面一个字节代表一个标签(0~9中的一个数) 有了上面这些信息,就可以从标签文件中解析出图像标签了,代码如下:
#include <opencv2/opencv.hpp> using namespace cv; void main() { uchar readbuf[4];//信息数据读取空间 FILE *f; fopen_s(&f, "train-labels.idx1-ubyte", "rb"); fread_s(readbuf, 4, 1, 4, f);//读取魔数,即文件标志位 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像个数 int numOfImg = (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像个数 for (int i = 1; i <= numOfImg; i++) { fread_s(readbuf, 1, 1, 1, f);//读取数据集图像列数 printf("%d ", readbuf[0]); } fclose(f); }对比解析出来的图像和标签,很容易发现他们是一一对应的,这也是其意义所在。
为了训练方便,我们一般用一个数组将标签和图像数据存在一起,代码如下:
#include "stdafx.h" #include <opencv2/opencv.hpp> using namespace cv; class numImg { public: Mat img; uchar tag; }; void main() { /**********************************/ /***********读取图片数据***********/ /**********************************/ char savepath[30];//图像存储路径 uchar readbuf[4];//信息数据读取空间 FILE *f; fopen_s(&f, "train-images.idx3-ubyte", "rb"); fread_s(readbuf, 4, 1, 4, f);//读取魔数,即文件标志位 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像个数 int sumOfImg = (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像个数 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像行数 int imgheight = (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像行数 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像列数 int imgwidth = (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像列数 int imgdatalen = imgheight * imgwidth;//图像数据长度 numImg* mNumImg=new numImg[sumOfImg]; for (int i = 0; i < sumOfImg; i++) { mNumImg[i].img = Mat(imgheight, imgwidth, CV_8UC1); fread_s(mNumImg[i].img.data, imgdatalen, 1, imgdatalen, f);//读取数据集图像列数 } fclose(f); /**********************************/ /***********读取标签数据***********/ /**********************************/ fopen_s(&f, "train-labels.idx1-ubyte", "rb"); fread_s(readbuf, 4, 1, 4, f);//读取魔数,即文件标志位 fread_s(readbuf, 4, 1, 4, f);//读取数据集图像个数 sumOfImg = (readbuf[0] << 24) + (readbuf[1] << 16) + (readbuf[2] << 8) + readbuf[3];//图像个数 for (int i = 0; i <= sumOfImg; i++) { fread_s(&mNumImg[i].tag, 1, 1, 1, f);//读取数据集图像列数 //printf("%d ", mNumImg[i].tag); //imshow("", mNumImg[i].img); //waitKey(3000); } fclose(f); //mNumImg 中存着图像和对应的标签,用完记得释放掉内存 }此外,因为python最近相当火,在这里也放上python读取mnist的代码
import numpy as np import struct def loadImageSet(filename): print ("load image set",filename) binfile= open(filename, 'rb') buffers = binfile.read() head = struct.unpack_from('>IIII' , buffers ,0) print ("head,",head) offset = struct.calcsize('>IIII') imgNum = head[1] width = head[2] height = head[3] #[60000]*28*28 bits = imgNum * width * height bitsString = '>' + str(bits) + 'B' #读取定长数据段,即字符集图片总和 imgs = struct.unpack_from(bitsString,buffers,offset) binfile.close() imgs = np.reshape(imgs,[imgNum,1,width*height])#将字符集图片分隔为单张图片 print ("load imgs finished") return imgs def loadLabelSet(filename): print ("load label set",filename) binfile = open(filename, 'rb') buffers = binfile.read() head = struct.unpack_from('>II' , buffers ,0) print ("head,",head) imgNum=head[1] offset = struct.calcsize('>II') numString = '>'+str(imgNum)+"B" labels = struct.unpack_from(numString , buffers , offset) binfile.close() labels = np.reshape(labels,[imgNum,1]) print ('load label finished') return labels