MNIST画像情報を読み込む

MNIST画像情報をPerlで読み込むサンプルプログラムです。MNISTは、ディープラーニングで利用できる画像のサンプルです。

THE MNIST DATABASE of handwritten digitsを読み込むサンプルです。

use strict;
use warnings;
use FindBin;

# MNIST画像情報を読み込む
my $mnist_image_file = "$FindBin::Bin/data/train-images-idx3-ubyte";

open my $mnist_image_fh, '<', $mnist_image_file
  or die "Can't open file $mnist_image_file: $!";

# マジックナンバー
my $image_buffer;
read($mnist_image_fh, $image_buffer, 4);
my $magic_number = unpack('N1', $image_buffer);
if ($magic_number != 0x00000803) {
  die "Invalid magic number expected " . 0x00000803 . "actual $magic_number";
}

# 画像数
read($mnist_image_fh, $image_buffer, 4);
my $items_count = unpack('N1', $image_buffer);

# 画像の行ピクセル数
read($mnist_image_fh, $image_buffer, 4);
my $rows_count = unpack('N1', $image_buffer);

# 画像の列ピクセル数
read($mnist_image_fh, $image_buffer, 4);
my $columns_count = unpack('N1', $image_buffer);

# 画像の読み込み
my $image_data;
my $all_images_length = $items_count * $rows_count * $columns_count;
my $read_length = read $mnist_image_fh, $image_data, $all_images_length;
unless ($read_length == $all_images_length) {
  die "Can't read all images";
}

# 画像情報
my $image_info = {};
$image_info->{items_count} = $items_count;
$image_info->{rows_count} = $rows_count;
$image_info->{columns_count} = $columns_count;
$image_info->{data} = $image_data;

MNIST画像情報を読み込むプログラムの簡易解説

「data」以下に「train-images-idx3-ubyte」を配置します。

ファイルをオープンしてread関数で読み込んでいきます。

マジックナンバーのチェックを行います。ビッグエンディアンの32bit整数なので、unpackに「N1」を指定します。

画像数を取得します。ビッグエンディアンの32bit整数なので、unpackに「N1」を指定します。

画像の行ピクセル数を取得します。ビッグエンディアンの32bit整数なので、unpackに「N1」を指定します。

画像の列ピクセル数を取得します。ビッグエンディアンの32bit整数なので、unpackに「N1」を指定します。

MNIST画像情報では、すべての画像データで、行ピクセル数と列ピクセル数は共通です。

すべての画像を読み込みます。

訓練用の画像情報である「train-images-idx3-ubyte」を読み込んでいますが、検証用の画像情報「t10k-images-idx3-ubyte」も同様の方法で読み込めます。

MNIST画像情報を出力する方法

メモリ上にMNIST画像情報を読み込みましたが、これを確認する方法を解説します。ビットマップ画像として出力すれば、内容を確認できます。

Imagerというモジュールを使います。CPANモジュールですので、cpanmあるいはcpanでインストールしてください。

以下は、最初の5つの画像をビットマップ画像として出力するサンプルです。

use strict;
use warnings;
use FindBin;
use Imager;

# MNIST画像情報を読み込む
my $mnist_image_file = "$FindBin::Bin/data/train-images-idx3-ubyte";

open my $mnist_image_fh, '<', $mnist_image_file
  or die "Can't open file $mnist_image_file: $!";

# マジックナンバー
my $image_buffer;
read($mnist_image_fh, $image_buffer, 4);
my $magic_number = unpack('N1', $image_buffer);
if ($magic_number != 0x00000803) {
  die "Invalid magic number expected " . 0x00000803 . "actual $magic_number";
}

# 画像数
read($mnist_image_fh, $image_buffer, 4);
my $items_count = unpack('N1', $image_buffer);

# 画像の行ピクセル数
read($mnist_image_fh, $image_buffer, 4);
my $rows_count = unpack('N1', $image_buffer);

# 画像の列ピクセル数
read($mnist_image_fh, $image_buffer, 4);
my $columns_count = unpack('N1', $image_buffer);

# 画像の読み込み
my $image_data;
my $all_images_length = $items_count * $rows_count * $columns_count;
my $read_length = read $mnist_image_fh, $image_data, $all_images_length;
unless ($read_length == $all_images_length) {
  die "Can't read all images";
}

# 画像情報
my $image_info = {};
$image_info->{items_count} = $items_count;
$image_info->{rows_count} = $rows_count;
$image_info->{columns_count} = $columns_count;
$image_info->{data} = $image_data;

# 画像情報の出力
for (my $i = 0; $i < 5; $i++) {

  # 画像オフセット
  my $offset = $i * $rows_count * $columns_count;

  # キャンバス(モノクロ)
  my $img = Imager->new(xsize => $rows_count, ysize => $columns_count, channels => 1);
  
  # 画像情報を順番に出力
  for (my $row = 0; $row < $rows_count; $row++) {
    for (my $column = 0; $column < $columns_count; $column++) {
      
      # 色(白黒がRGBと逆なので反転)
      my $pos = $offset + ($column * $rows_count) + $row;
      my $color_bin = substr($image_data, $pos, 1);
      my $color_value = unpack('C1', $color_bin);
      my $color_value_neg = $color_value ^ 0xFF;
      my $color = Imager::Color->new($color_value_neg, $color_value_neg, $color_value_neg);
      
      # ピクセル描画
      $img->setpixel(x => $row, y => $column, color => $color);
    }
  }
  # Web表示できるようにPNGとして保存
  my $bitmap_file = "$FindBin::Bin/tmp_images/number$i.png";
  $img->write(file => $bitmap_file);
}

出力した画像です。

MNIST画像情報を出力するプログラムの簡易解説

Imagerオブジェクトを幅と高さとチャンネル数を指定して作成します。モノクロなので1を指定しています。

画像情報を読み込んでいきます。行列の順で色の濃さが並んでいるので、取得します。データは、符号なし8bit整数で格納されているのでunpackに「C1」を指定しています。

MNISTの色の濃さはRGBと逆なので、反転させています。

Imager::Colorオブジェクトを作成しています。

ピクセルに描画します。

最後に、Webで表示できるようにpngで保存しています。拡張子から自動判定されます。

C言語のlibpngライブラリがOSのインストールされていることと、Imager::File::PNGが必要かもしれません。

関連情報