【Matlab编程实现常见小问题之三】Matlab如何实现把MNIST数据库中的二进制文件形式的样本集转化为标准图像格式

手写数字的MNIST数据库包含60000个训练集以及10000个测试集,该数据库将其训练集和测试集保存为二进制文件格式而非标准图像格式,所以本篇文章中,旨在解决如何用Matlab编程实现MNIST数据库中下载下来的二进制文件转化为标准图片格式并保存下来。编程环境是Matlab2012a,MNIST数据库链接是http://yann.lecun.com/exdb/mnist/

一、问题描述

手写数字识别是图像识别的应用之一,很多学习图像识别,机器学习的人,项目实践时,都会把手写数字识别项目作为入门项目,而实践此类项目时样本集来源是个关键,幸运的是我们前人为了后人的方便,把他们辛苦收集到的样本共享出来放在了网上,也就是MNIST数据库,链接如上,里面内容丰富,详细介绍了数据库的内容,有兴趣的人可以通篇浏览一下。手写数字的样本集出自250多人之手,每张图片是28*28,60000测试集,10000测试集。进入链接后可以找到四个压缩包文件,如下图1-1所示。

 

图 1-1

把这四个压缩包文件下载下来并解压,解压后,满心欢喜的以为能看到世界各地的人手写的0-9,且是我们熟悉的图片格式,却只有四个不知道什么格式的文件,打也打不开,一脸懵,解压后如下图1-2所示。

 

图 1-2

经过查阅相关资料以及阅读MNIST数据库的介绍后,了解到这是四个二进制文件,图片就藏在里面,所以本文的目的是用Matlab编程把这二进制文件转化为一个个我们肉眼可见的熟悉的图片格式。

二、编程实现

2.1 二进制文件介绍

上面如图1-2可以看到有四个二进制文件,在MNIST数据库链接中有对这四个文件的详细介绍。四个文件的内容:

    t10k-images.idx3-ubyte:测试集图片。

    t10k-labels.idx1-ubyte:测试集标签。

    train-images.idx3-ubyte:训练集图片。

    train-labels.idx1-ubyte:训练集标签。

每个二进制文件中,到底怎么存储的信息,链接中也有详细的介绍。由于训练集的两个文件和测试集的两个文件是类似的,所以本文选择训练集的两个二进制文件进行详细介绍与分析,如下图2-1所示

 图 2-1

这是我们编程实现的主要依据。理解这里,就很容易编程实现了。以下对两个文件分别介绍:

上图2-1可以看到train-images.idx3-ubyte文件中前16字节(0000~0015)存的是说明信息,后面就是数据信息了,即图片的像素值,每一个字节表示一个灰度值(0~255)。说明信息中,16字节介绍如下:

    0000~0003:IDX文件格式说明,该值为2051。

    0004~0007:该位置的值说明了该文件中包含了多少张图片,该值是60000。

    0008~0011:该位置的值说明了每张图片的行是多大,该值是28。

    0012~0015:该位置的值说明了每张图片的 列是多大,该值是28。

看到上图2-1的最下面有一句话,说像素排列是按行,0代表背景(白色),255代表是前景(黑色),即二进制文件中对图片保存时是先行后列,也就是从上到下,从左到右,和正常不一样,所以编程是需要注意要记得转置一下;图片是白纸黑字,0代表的白,255代表的黑,和正常不一样,所以到时候变一下。

上图2-1可以看到train-labels.idx1-ubyte文件中前8字节(0000~0007)存的是说明信息,后面就是数据信息了,即对应的图像的标签(取值0~9),每个字节一个标签,编程需要根据对应的标签的值把图片存在相应的文件夹下。

2.2 编程实现

经过上面一节的介绍,现在就可以进行编程实现了。首先打开相应的文件,读取文件说明信息部分,根据说明部分判断文件选择是否正确。

%读取训练集图片文件

[FileName,PathName] = uigetfile('*.*','选择训练集图片数据文件train-images.idx3-ubyte');

TrainFile = fullfile(PathName,FileName);

fid = fopen(TrainFile,'r'); %打开文件

%读取文件说明信息

a = fread(fid,16,'uint8'); %前十六字节的数据

%说明信息是用32位整型表述,所以需要组合

MagicNum = ((a(1)*256+a(2))*256+a(3))*256+a(4);%前四字节是idx文件格式说明

ImageNum = ((a(5)*256+a(6))*256+a(7))*256+a(8);%图片数量

rows = ((a(9)*256+a(10))*256+a(11))*256+a(12);%行数(四字节,用32位整型表述):此值为28

cols = ((a(13)*256+a(14))*256+a(15))*256+a(16);%列数(四字节,用32位整型表述):此值为28

%读取训练集标签文件

[FileName1,PathName1] = uigetfile('*.*','选择训练集图片数据文件train-label.idx1-ubyte');

TrainFile1 = fullfile(PathName1,FileName1);

fid1 = fopen(TrainFile1,'r');

%读取文件说明信息

a1 = fread(fid1,8,'uint8'); %前八个字节

MagicNum1 = ((a1(1)*256+a1(2))*256+a1(3))*256+a1(4);%idx文件格式说明

ImageNum1 = ((a1(5)*256+a1(6))*256+a1(7))*256+a1(8);%图像数量(四字节,用32位整型表述)

%根据说明信息判断文件选择是否正确

if (ImageNum~=60000)

    error('不是 MNIST train-images.idx3-ubyte 文件!');

    fclose(fid);    

    return;    

end 

if (ImageNum1~=60000)

    error('不是 MNIST train-label.idx1-ubyte 文件!');

    fclose(fid1);    

    return;    

end 

下一步就是读取数据信息,代码如下:

for i=1:ImageNum

    b = fread(fid,rows*cols,'uint8');   %每次读取一个图片大小的数据,28*28,为向量

    label= fread(fid1,1,'uint8');%对应的标签,该值表示图片中的数字是0~9的哪个数字                                           

    c = reshape(b,[rows cols]); %转化为矩阵

    d = c'; %文件中是图片是按行存储的,转置

    e = 255-d; %文件中0表示白,255表示黑,转变一下。

    e = uint8(e);%格式变换

然后按标签值将每张图片保存在对应文件夹下。代码如下:

    switch label

        case 0

            savepath = fullfile(savedirectory,'0',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 1

            savepath = fullfile(savedirectory,'1',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 2

            savepath = fullfile(savedirectory,'2',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 3

            savepath = fullfile(savedirectory,'3',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 4

            savepath = fullfile(savedirectory,'4',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 5

            savepath = fullfile(savedirectory,'5',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 6

            savepath = fullfile(savedirectory,'6',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 7

            savepath = fullfile(savedirectory,'7',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 8

            savepath = fullfile(savedirectory,'8',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

        case 9

            savepath = fullfile(savedirectory,'9',['TrainImage_' num2str(i,'%d') '.bmp']);

            imwrite(e,savepath,'bmp');

    end

最后关闭文件即可。由于图片数量上万,运行时间有点长,笔者在此就不运行,不贴运行结果图了。此外,测试集的文件转化和训练集类似,只需要稍微修改一下代码即可,所以在此不做赘述。

三、总结

本次问题的解决,重点是要理解二进制文件每个位置存储的是什么信息这个理解后,编程实现很容易。对于测试集文件转化来说,测试集不需要把每张图片按标签将其保存在指定文件夹下,测试集的标签文件是用于检验识别率时用的,在本文用处不大。所有代码的文件夹内容如下:


 本篇文章的配套程序请点击这里下载:

【Matlab编程实现常见小问题之三】Matlab如何实现把MNIST数据库中的二进制文件形式的样本集转化为标准图像格式

 

 

猜你喜欢

转载自blog.csdn.net/qq_18995069/article/details/80471415
今日推荐