Good mass extraction example
https://github.com/myleott/mnist_png had been previously mentioned on a now deleted link-only answer by the OP user11141180. Here are some more details.
https://github.com/myleott/mnist_png/blob/400fe88faba05ae79bbc2107071144e6f1ea2720/convert_mnist_to_png.py contains a good PNG extraction example, licensed under GPL 2.0. Should be easy to adapt to other output formats with a library like Pillow.
They also have a pre-extracted archive at: https://github.com/myleott/mnist_png/blob/master/mnist_png.tar.gz?raw=true
Usage:
wget \
http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz \
http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz \
http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz \
http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
gunzip --keep *-ubyte.gz
python3 -m pip install pypng==0.20220715.0
./convert_mnist_to_png.py . out
And now out/
contains files such as:
out/training/0/1.png
out/training/0/21.png
out/training/1/3.png
out/training/1/6.png
out/testing/0/10.png
out/testing/0/13.png
convert_mnist_to_png.py
#!/usr/bin/env python
import os
import struct
import sys
from array import array
from os import path
import png
# source: http://abel.ee.ucla.edu/cvxopt/_downloads/mnist.py
def read(dataset = "training", path = "."):
if dataset is "training":
fname_img = os.path.join(path, 'train-images-idx3-ubyte')
fname_lbl = os.path.join(path, 'train-labels-idx1-ubyte')
elif dataset is "testing":
fname_img = os.path.join(path, 't10k-images-idx3-ubyte')
fname_lbl = os.path.join(path, 't10k-labels-idx1-ubyte')
else:
raise ValueError("dataset must be 'testing' or 'training'")
flbl = open(fname_lbl, 'rb')
magic_nr, size = struct.unpack(">II", flbl.read(8))
lbl = array("b", flbl.read())
flbl.close()
fimg = open(fname_img, 'rb')
magic_nr, size, rows, cols = struct.unpack(">IIII", fimg.read(16))
img = array("B", fimg.read())
fimg.close()
return lbl, img, size, rows, cols
def write_dataset(labels, data, size, rows, cols, output_dir):
# create output directories
output_dirs = [
path.join(output_dir, str(i))
for i in range(10)
]
for dir in output_dirs:
if not path.exists(dir):
os.makedirs(dir)
# write data
for (i, label) in enumerate(labels):
output_filename = path.join(output_dirs[label], str(i) + ".png")
print("writing " + output_filename)
with open(output_filename, "wb") as h:
w = png.Writer(cols, rows, greyscale=True)
data_i = [
data[ (i*rows*cols + j*cols) : (i*rows*cols + (j+1)*cols) ]
for j in range(rows)
]
w.write(h, data_i)
if __name__ == "__main__":
if len(sys.argv) != 3:
print("usage: {0} <input_path> <output_path>".format(sys.argv[0]))
sys.exit()
input_path = sys.argv[1]
output_path = sys.argv[2]
for dataset in ["training", "testing"]:
labels, data, size, rows, cols = read(dataset, input_path)
write_dataset(labels, data, size, rows, cols,
path.join(output_path, dataset))
Inspecting the generated PNGs with:
identify out/testing/0/10.png
gives:
out/testing/0/10.png PNG 28x28 28x28+0+0 8-bit Gray 256c 272B 0.000u 0:00.000
so they appear to be Grayscale and 8-bit, and therefore should faithfully represent the original data.
Tested on Ubuntu 22.10.