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\\%5d.png", i + 1);
fread_s(img.data, imgdatalen, 1, imgdatalen, f);//读取数据集图像列数
imwrite(savepath, img);
if ((i+1)%100==0)
{
printf("%5d.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