文章目录
MNIST数据集简介
MNIST是一个手写数字的数据库,它由一个包含有60000个样本的训练集和一个包含有10000个样本的测试集组成。数据集中所有的手写数字都已经被标准化,并且以固定大小的图像形式保存。
完整数据集可从MNIST数据集的官网THE MNIST DATABASE of handwritten digits上进行下载。
下载完成后,解压即可得到我们所需的数据集。
MNIST数据集文件格式
数据集用字节形式进行存储,采用MSB(Most Significant Bit) first模式,即大端模式。
MNIST数据集由四个文件组成:
| 文件名 | 说明 |
|---|---|
| train-images-idx3-ubyte | 训练集图片 |
| train-labels-idx1-ubyte | 训练集标签 |
| t10k-images-idx3-ubyte | 测试集图片 |
| t10k-labels-idx1-ubyte | 测试集标签 |
train-images-idx3-ubyte
| 字节偏移量 | 数据类型 | 数值 | 说明 |
|---|---|---|---|
| 0000 | 32 bit integer | 0x00000803(2051) | 魔数 |
| 0004 | 32 bit integer | 60000 | 图片数量 |
| 0008 | 32 bit integer | 28 | 图片像素的行数(row) |
| 0012 | 32 bit integer | 28 | 图片像素的列数(column) |
| 0016 | unsigned byte | ?? | 像素的值 |
| 0017 | unsigned byte | ?? | 像素的值 |
| … | … | … | … |
| xxxx | unsigned byte | ?? | 像素的值 |
像素按行排列。 像素值为0到255。0表示背景(白色),255表示前景(黑色)。
train-labels-idx1-ubyte
| 字节偏移量 | 数据类型 | 数值 | 说明 |
|---|---|---|---|
| 0000 | 32 bit integer | 0x00000801(2049) | 魔数 (MSB first) |
| 0004 | 32 bit integer | 60000 | 标签数量 |
| 0008 | unsigned byte | ?? | 标签值 |
| 0009 | unsigned byte | ?? | 标签值 |
| … | … | … | … |
| xxxx | unsigned byte | ?? | 标签值 |
标签的值为0到9的整数。
t10k-images-idx3-ubyte
| 字节偏移量 | 数据类型 | 数值 | 说明 |
|---|---|---|---|
| 0000 | 32 bit integer | 0x00000803(2051) | 魔数 |
| 0004 | 32 bit integer | 10000 | 图片数量 |
| 0008 | 32 bit integer | 28 | 图片像素的行数(row) |
| 0012 | 32 bit integer | 28 | 图片像素的列数(column) |
| 0016 | unsigned byte | ?? | 像素的值 |
| 0017 | unsigned byte | ?? | 像素的值 |
| … | … | … | … |
| xxxx | unsigned byte | ?? | 像素的值 |
像素按行排列。 像素值为0到255。0表示背景(白色),255表示前景(黑色)。
t10k-labels-idx1-ubyte
| 字节偏移量 | 数据类型 | 数值 | 说明 |
|---|---|---|---|
| 0000 | 32 bit integer | 0x00000801(2049) | 魔数 (MSB first) |
| 0004 | 32 bit integer | 10000 | 标签数量 |
| 0008 | unsigned byte | ?? | 标签值 |
| 0009 | unsigned byte | ?? | 标签值 |
| … | … | … | … |
| xxxx | unsigned byte | ?? | 标签值 |
标签的值为0到9的整数。
读取方法
open() 函数
文件以二进制格式进行存储,我们同样的也就需要用二进制格式进行读取。所以我们在使用Python的open()函数时,需指明模式为rb,即以二进制格式打开一个文件用于只读。
f = open(file_name, "rb")
open()将打开一个文件,创建一个file对象,这时调用相关方法便可以对文件进行读写。
file.read(size)
size指定了需要读取的字符数或字节数,若未指定,则返回整个文件,如果文件大小大于 倍内存则有问题,file.read(size)读到文件尾时返回""(空字串)。
file.close()
关闭文件是个好习惯。
int.from_bytes() 函数
我们直接读取到数据的类型为bytes,并不是我们所希望的数据类型,因此需要进行转换。由于所有的数都是整数,所以我们可以直接用int.from_bytes()完成。
res = int.from_bytes(s, byteorder="big", signed=False)
s为需要转换的数。
byteorder指定了为大端模式"big"或者小端模式"little"。
signed表示是否要区分二进制的正负数含义。
struct.unpack() 函数
struct.unpack()也能完成将字节流转换为Python指定的数据类型的操作。
推荐博客:python之struct详解
完整代码
import os
import cv2
import numpy as np
TRAIN_IMAGES_DIR = "train_images/"
TEST_IMAGES_DIR = "test_images/"
TRAIN_IMAGE_DATASET_PATH = "train-images.idx3-ubyte"
TRAIN_LABEL_DATASET_PATH = "train-labels.idx1-ubyte"
TEST_IMAGE_DATASET_PATH = "t10k-images.idx3-ubyte"
TEST_LABEL_DATASET_PATH = "t10k-labels.idx1-ubyte"
def convert_to_image(dataset_type):
if dataset_type == "train":
images_dir = TRAIN_IMAGES_DIR
image_dataset = open(TRAIN_IMAGE_DATASET_PATH, "rb")
label_dataset = open(TRAIN_LABEL_DATASET_PATH, "rb")
elif dataset_type == "test":
images_dir = TEST_IMAGES_DIR
image_dataset = open(TEST_IMAGE_DATASET_PATH, "rb")
label_dataset = open(TEST_LABEL_DATASET_PATH, "rb")
else:
print("Invalid type.")
return
counter = [0] * 10
image_magic_number = int.from_bytes(image_dataset.read(4), byteorder='big', signed=False)
image_num = int.from_bytes(image_dataset.read(4), byteorder='big', signed=False)
image_row = int.from_bytes(image_dataset.read(4), byteorder='big', signed=False)
image_col = int.from_bytes(image_dataset.read(4), byteorder='big', signed=False)
label_magic_number = int.from_bytes(label_dataset.read(4), byteorder='big', signed=False)
label_num = int.from_bytes(label_dataset.read(4), byteorder='big', signed=False)
for i in range(10):
if not os.path.exists(images_dir + str(i)):
os.makedirs(images_dir + str(i))
for i in range(image_num):
image = []
for j in range(image_row * image_col):
image.append(int.from_bytes(image_dataset.read(1), byteorder='big', signed=False))
image = np.array(image, dtype=np.uint8).reshape((image_row, image_col))
label = int.from_bytes(label_dataset.read(1), byteorder='big', signed=False)
counter[label] += 1
image_path = images_dir + str(label) + "/" + str(label) + "." + str(counter[label]) + ".jpg"
cv2.imwrite(image_path, image)
if (i + 1) % 1000 == 0:
print("Running, " + dataset_type + " images: " + str(i + 1) + "/" + str(image_num))
# cv2.imshow('image', image)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
image_dataset.close()
label_dataset.close()
print(dataset_type + " dataset finished.")
if __name__ == "__main__":
convert_to_image("train")
convert_to_image("test")
print("All finished.")
这篇博客介绍了MNIST手写数字数据集,包括其组成、文件格式和读取方法。通过Python的open()函数以二进制模式读取数据,结合int.from_bytes()和struct.unpack()函数进行数据转换,将数据集中的图像保存为图片。
5460





