(28) MNIST画像をPNG画像で出力する(Python版)

投稿者: | 2019年11月13日

4,913 views

この記事は最終更新から 1132日 が経過しています。

1. やりたいこと

過去記事 (3) MNIST画像をPNG画像で出力する(Octave版) では、MNISTテスト画像データファイル t10k-images-idx3-ubyte.gz から個々の画像をPNGファイル化して保存した。

Octaveはとっても高水準な言語で、複雑な処理を簡潔な命令で書けてとっても便利だ。

でも…
一般的な言語かと問われれば うむぅ~ と言った感じか。
Octaveは MATLABクローンな、どちらかと言えば一般的ではない言語なのだ。

そこで…
今回は同じことを人気の言語 Python で書いてみる。

2. やってみる

実は、過去記事 (11) cuda-convnet用MNISTデータを作る(その1) で既に同じことをやっている。
今回は当該記事から表題の機能だけを抜き出してリライトしたようなものだ。

(1) MNISTテスト画像データをダウンロードする。

全データのダウンロード手順は (1) MNIST画像データをダウンロード に書いてある。

$ wget http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz

(2) ダウンロードしたファイルを解凍する。

$ gunzip t10k-images-idx3-ubyte.gz

ここまでで、画像ファイルが取得できた。

$ ls -l
total 7660
-rw-rw-r--. 1 user user 7840016 Jul 22  2000 t10k-images-idx3-ubyte

(3) Pythonを起動する。

$ python
Python 3.6.2 |Anaconda custom (64-bit)| (default, Jul 20 2017, 13:51:32)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

(4) 必要なモジュールを読み込む。

>>> import numpy as np
>>> import struct
>>> from PIL import Image

(5) MNIST画像データファイルをバイナリで読み出す。

>>> f = open('./t10k-images-idx3-ubyte','rb')

rbread 且つ binary を指定している。

(6) ヘッダ部のデータをロードする。

データフォーマットは以下の通り。
http://yann.lecun.com/exdb/mnist/

1) 先頭 4バイト magic number を読み出し。

>>> magic_number = f.read( 4 )
>>> magic_number = struct.unpack('>i', magic_number)[0]
>>> magic_number
2051

>ibig endian 且つ 32bit integer を指定している。

structのパラメータ指定はこちらを参照のこと。
https://docs.python.org/ja/3/library/struct.html

2) 次の 4バイト number of images を読み出し。

>>> number_of_images = f.read( 4 )
>>> number_of_images = struct.unpack('>i', number_of_images)[0]
>>> number_of_images 
10000

3) 次の 4バイト number of rows を読み出し。

>>> number_of_rows = f.read( 4 )
>>> number_of_rows = struct.unpack('>i', number_of_rows)[0]
>>> number_of_rows 
28

4) 次の 4バイト number of columns を読み出し。

>>> number_of_columns = f.read( 4 )
>>> number_of_columns = struct.unpack('>i', number_of_columns)[0]
>>> number_of_columns 
28

(7) 画像データをロードする。

1) 画像データのバイト数を計算する。

>>> bytes_per_image = number_of_rows * number_of_columns
>>> bytes_per_image
784

2) 画像データの先頭一個をロードし Numpy配列に格納する。

>>> raw_img = f.read(bytes_per_image)
>>> format = '%dB' % bytes_per_image
>>> lin_img = struct.unpack(format, raw_img)
>>> np_ary = np.asarray(lin_img).astype('uint8')
>>> np_ary = np.reshape(np_ary, (28,28),order='C')

(8) PNG画像としてファイル出力する。

一旦 PIL画像化して PNGファイル出力する。
拡張子を PNGにすれば、勝手に PNGフォーマットの画像ファイルを作ってくれるという大サービスだ。

>>> pil_img = Image.fromarray(np_ary)
>>> pil_img.save("00000.png")

できた!

同じ手順で、先頭から 100個のデータをPNGファイル化した。

3. やったことをまとめる。

上述のプログラムを一つのプログラムファイル my_mnist.py にまとめる。
もちろん、ファイル名は何でもよい。

このプログラムを実行すると、指定した MNIST画像ファイル中の全画像が、個々に PNG画像として出力される。

import numpy as np
import struct
from PIL import Image

def output_mnist_img_png( source_mnist_img_file ):
    f = open(source_mnist_img_file, 'rb')
    magic_number      = struct.unpack('>i', f.read(4))[0]
    number_of_images  = struct.unpack('>i', f.read(4))[0]
    number_of_rows    = struct.unpack('>i', f.read(4))[0]
    number_of_columns = struct.unpack('>i', f.read(4))[0]
    bytes_per_image = number_of_rows * number_of_columns
    format = '%dB' % bytes_per_image
    for i in range(0, number_of_images):
        raw_img = f.read(bytes_per_image)
        lin_img = struct.unpack(format, raw_img)
        np_ary  = np.asarray(lin_img).astype('uint8')
        np_ary  = np.reshape(np_ary, (28,28),order='C')
        pil_img = Image.fromarray(np_ary)
        fpath   = '%05d.png' % i
        pil_img.save(fpath)

このモジュールを Python Shellから以下のように使えばよい。

>>> import my_mnist
>>> my_mnist.output_mnist_img_png('train-images-idx3-ubyte')    # 学習データを PNGファイル出力
>>> my_mnist.output_mnist_img_png('t10k-images-idx3-ubyte')     # テストデータを PNGファイル出力

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です