目錄
原文鏈接:http://www.juzicode.com/image-mnist-get-access-apply
mnist是一個包含0~9的阿拉伯數字手寫字體數據集,它由60000個樣本組成的訓練集和10000個樣本組成的測試集。其中每種數據集又分別包含2個文件,分別是圖片文件和標簽文件。
1、獲取mnist數據
數據集下載地址:http://yann.lecun.com/exdb/mnist/,包含4個文件:
train-images-idx3-ubyte.gz: 60000個訓練圖片
train-labels-idx1-ubyte.gz: 60000個訓練標簽
t10k-images-idx3-ubyte.gz: 10000個測試圖片
t10k-labels-idx1-ubyte.gz: 10000個測試標簽
網站上提供的是壓縮版本,下載到本地后先進行解壓。
2、mnist文件格式
每個mnist文件的格式如下圖所示:
dimension N的值由magic numbe決定,N=LSB(magic number)-1,也就是magic number最后一各字節減1得到。其中dimension0的值為樣本數量。文件頭大小為1個4字節的magic number+(N+1)個4字節的dimension。這里先減一再加一,繞來繞去是為了符合起始從0開始的習慣,實際上magic number最后一個字節就是dimension的數量。
前面講的是通用格式,下面是對應到圖片和標簽文件的格式。
圖片文件train-images-idx3-ubyte和t10k-images-idx3-ubyte的magic number都是0x00000803,其中0x08表示data部分的類型為uchar型,0x03表示dimension的數量為3,其中dimension0表示數據的個數,train-images-idx3-ubyte有60000個,t10k-images-idx3-ubyte有10000個,2個圖片文件的dimension1和dimension2都為28,表示圖片的高寬(行列數),這樣文件頭長度head_len=4字節*1(magic number個數)+4字節*3(dimension個數)=16字節。
剩下的內容為數據部分,每28*28個字節為一張圖片,這樣第m張圖片的起始位置就是:16(文件頭長度)+28(高)*28(寬)*m。
標簽文件train-labels-idx1-ubyte.和t10k-labels-idx1-ubyte的magic number都是0x00000801,其中0x08表示data部分的類型為uchar型,0x01表示dimension的數量為1,其中dimension0表示數據的個數,train-labels-idx1-ubyte有60000個,10k-labels-idx1-ubyte有10000個。標簽文件的文件頭長度head_len=4字節*1(magic number個數)+4節*1(dimension個數)=8字節。
剩下的內容為數據部分,每個字節表示一個標簽。
有了前面對文件的分析,下面根據分析結果對文件做讀出操作。
3、從文件讀出數據
3.1、讀出文件頭
首先是獲取文件頭中的樣本數量和文件頭的長度,有了文件頭長度就可以根據文件頭長度偏移到相應位置獲取某一個圖片或標簽的值。
def get_head_info(filename):
dimension = []
with open(filename,'rb') as pf:
data = pf.read(4)#獲取magic number
magic_num = int.from_bytes(data,byteorder='big')#bytes格式大尾端模式轉換為int型
dimension_cnt = magic_num & 0xff #獲取dimension的長度,magic number最后一個字節
for i in range(dimension_cnt):
data = pf.read(4) #獲取dimension數據,dimension[0]表示圖片的個數,圖片文件中dimension[1][2]分別表示其行/列數值
dms = int.from_bytes(data,byteorder='big')
dimension.append(dms)
sample_count = dimension[0]
head_length = 4*len(dimension)+4
return head_length ,sample_count
入參filename是圖片或標簽的文件名稱。
打開文件后,首先讀出的4個字節是其magic number,需要將大尾端模式的字節數據轉換為int類型。然后將magic number和0xff相與得到其最后一個字節,表示dimension的數量,然后循環讀出所有的dimension的值。其中dimension[0]表示樣本的數量,文件頭的長度為4*len(dimension)+4。最后返回文件頭長度和樣本數量。
3.2、讀出圖片數據
有了文件頭信息,利用這些信息就可以獲取某個指定偏移位置的圖片或者標簽,下面我們現來看獲取圖片數據:
#mnist單個圖片的大小
IMAGE_ROW = 28
IMAGE_COL = 28
def read_image_p(pf,head_len,offset):
image = np.zeros((IMAGE_ROW,IMAGE_COL),dtype=np.uint8)#創建空白數組
pf.seek(head_len+IMAGE_ROW*IMAGE_COL*offset) #指向offset個圖片的位置
for row in range(IMAGE_ROW):
for col in range(IMAGE_COL):
data = pf.read(1)#單個字節讀
pix = int.from_bytes(data,byteorder='big')#byte轉為int
image[row][col] = pix
return image
入參pf為打開文件對象,head_len為get_head_info()返回的文件頭長度,offset表示要獲取的第offset個圖像。
該函數中首先創建一個28×28大小的numpy數組,然后將文件指針指向文件的head_len+IMAGE_ROW*IMAGE_COL*offset處,也就是第offset個圖像的起始位置。接下來讀出IMAGE_ROW*IMAGE_COL個數據,依次賦值給numpy數組,然后返回該數組,也就得到了第offset個圖像的數據。
3.3、讀取標簽數據
接下來看如何獲取標簽數據:
def read_label_p(pf,head_len,offset):
pf.seek(head_len+offset) #指向offset個標簽的位置
data = pf.read(1)
label = int.from_bytes(data,byteorder='big')#由byte轉換為int類型,
return label
入參pf為打開文件對象,head_len為get_head_info()返回的文件頭長度,offset表示要獲取的第offset個標簽。
首先將文件指針指向文件的head_len+offset處,也就是第offset個標簽的起始位置。接下來讀出1個字節的數據,轉換為int類型后返回。
4、應用例子
4.1、顯示標簽和圖片
下面這個例子利用前面寫的幾個函數顯示指定的某個偏移開始的圖像:
#juzicode.com/VX公眾號:桔子code
import os,sys
import matplotlib.pyplot as plt
import mnist #包含讀文件頭,讀圖像文件,讀標簽文件
def main_show(file_path,offset,sample_count=10,type='train'):
image_file = file_path+'train-images.idx3-ubyte'
label_file = file_path+'train-labels.idx1-ubyte'
if type!='train':
image_file = file_path+'t10k-images.idx3-ubyte'
label_file = file_path+'t10k-labels.idx1-ubyte'
#解析文件頭
image_head_len,image_amout = mnist.get_head_info(image_file)
label_head_len,label_amout = mnist.get_head_info(label_file)
#檢查樣本數量
if sample_countimage_amout:
sample_count = image_amout-offset
#讀出文件顯示圖片和標簽
with open(image_file,'rb') as pf_image, open(label_file,'rb') as pf_label:
for __ind in range(sample_count):
ind = __ind + offset
img = mnist.read_image_p(pf_image, image_head_len, ind)
label= mnist.read_label_p(pf_label, label_head_len, ind)
plt.imshow(img)
plt.title('offset=%d,label=%d'%(ind,label))
plt.show()
if __name__ == '__main__':
main_show('mnist\\',offset=9996,sample_count=5,type='test')
首先根據type決定是訪問訓練集還是測試集,數據集文件放在file_path子目錄下。然后讀取文件頭信息,根據讀出的文件頭和讀出的樣本數量和傳入的想顯示的樣本數量做對比,檢查樣本數量並限制其范圍。接下來打開圖片文件和標簽文件,並讀出數據在matplotlib里顯示offset、標簽值、圖片。
4.2、將圖片數據集轉換為tif圖片文件
下面這個例子從圖片文件中第offset個偏移開始,讀出sample_count個圖片,並生成tif文件:
#juzicode.com/VX公眾號:桔子code
import os,sys
import mnist #包含讀文件頭,讀圖像文件,讀標簽文件
import imageio
#生成tif圖片
def main_gen_tif(file_path,tif_name,offset,sample_count=10,type='train'):
image_file = file_path+'train-images.idx3-ubyte'
if type!='train':
image_file = file_path+'t10k-images.idx3-ubyte'
#解析文件頭
image_head_len,image_amout = mnist.get_head_info(image_file)
#檢查樣本數量
if sample_countimage_amout:
sample_count = image_amout-offset
#讀出文件
if not tif_name.endswith('tif') or not tif_name.endswith('tiff'):
tif_name += '.tif'
writer= imageio.get_writer(tif_name)
with open(image_file,'rb') as pf_image:
for __ind in range(sample_count):
ind = __ind + offset
img = mnist.read_image_p(pf_image, image_head_len, ind)
writer.append_data(img)
writer.close()
return 0
if __name__ == '__main__':
main_gen_tif('mnist\\','mnist-train',offset=1000,sample_count=10,type='train')
這個例子首先仍然根據type決定是訪問訓練集還是測試集,數據集文件放在file_path子目錄下。然后讀取文件頭信息,根據讀出的文件頭和讀出的樣本數量,檢查樣本數量並限制其范圍。接下來打開圖片文件,並從文件中讀出單張圖片再使用imageio生成tif文件。
推薦閱讀: