LoginSignup
2
5

More than 5 years have passed since last update.

MNISTの手書き数字画像データをJavaでベクトル化、画像化する

Last updated at Posted at 2017-02-14

はじめにMNISTの手書き数字画像データの仕様を説明した後、ベクトル化したり、画像として表示・保存するJavaのコードを説明します。

MNISTの手書き数字画像データセットとは

ネットに公開されている0から9までの手書き数字画像データセットです。
個々の画像の大きさは28ピクセル四方で、色は255階調のグレースケールです。
機械学習の学習および評価によく利用されています。

データ仕様

MNISTの手書き数字データの仕様について説明します。

訓練データセット

訓練(学習)に用いる0から9までの6万枚の画像データと正解ラベルデータがあります。

訓練用画像データ

train-images-idx3-ubyte.gz
訓練用画像データが独自のバイナリフォーマットで格納されています。

オフセット データ型 説明
0 32bit整数 2051 マジックナンバー(MSB First)
4 32bit整数 60000 画像数
8 32bit整数 28 画像の縦のピクセル数
12 32bit整数 28 画像の横のピクセル数
16 符号なしバイト 0から255 1枚目の画像の1行1列目のピクセルのグレースケール値
17 符号なしバイト 0から255 1枚目の画像の1行2列目のピクセルのグレースケール値
・・・ 符号なしバイト 0から255 6万枚目の画像の28行目28列目のピクセルのグレースケール値

訓練用正解ラベルデータ

train-labels-idx1-ubyte.gz
訓練データの正解ラベルが独自のバイナリフォーマットで格納されています。

オフセット データ型 説明
0 32bit整数 2049 マジックナンバー(MSB First)
4 32bit整数 60000 画像数
8 符号なしバイト 0から9 1枚目の画像の正解ラベル
9 符号なしバイト 0から9 2枚目の画像の正解ラベル
・・・ 符号なしバイト 0から9 6万枚目の画像の正解ラベル

テストデータセット

テスト(評価)に用いる0から9までの1万枚の画像データと正解ラベルデータがあります。

テスト用画像データ

t10k-images-idx3-ubyte.gz
テスト用画像データが独自のバイナリフォーマットで格納されています。

オフセット データ型 説明
0 32bit整数 2051 マジックナンバー(MSB First)
4 32bit整数 10000 画像数
8 32bit整数 28 画像の縦のピクセル数
12 32bit整数 28 画像の横のピクセル数
16 符号なしバイト 0から255 1枚目の画像の1行1列目のピクセルのグレースケール値
17 符号なしバイト 0から255 1枚目の画像の1行2列目のピクセルのグレースケール値
・・・ 符号なしバイト 0から255 1万枚目の画像の28行目28列目のピクセルのグレースケール値

テスト用正解ラベル

t10k-labels-idx1-ubyte.gz
テストデータの正解ラベルが独自のバイナリフォーマットで格納されています。

オフセット データ型 説明
0 32bit整数 2049 マジックナンバー(MSB First)
4 32bit整数 10000 画像数
8 符号なしバイト 0から9 1枚目の画像の正解ラベル
9 符号なしバイト 0から9 2枚目の画像の正解ラベル
・・・ 符号なしバイト 0から9 1万枚目の画像の正解ラベル

Javaによるベクトル化、画像化

GitHub上のソースコード
簡単な使い方はREADME.mdをご覧ください。

画像データの読み込み

DataInputStreamを使ってデータを読み込みます。
readIntでマジックナンバー、画像数、画像の縦のピクセル数、画像の横のピクセル数を読み込みます。
次元数は28*28=784になります。
double型の2次元配列featuresにreadUnsignedByteに画像データを読み込みます。
1次元目が画像のインデックスで、2次元目が次元のインデックスです。
機械学習での利用を考慮して、値を255.0で割って正規化(normalize)しています。

ImageDataSet.java
    private void loadFeatures() throws IOException {
        System.out.println("Loading feature data from " + fileName + " ...");
        DataInputStream is = new DataInputStream(new GZIPInputStream(new FileInputStream(Const.BASE_PATH + fileName)));
        is.readInt();
        numImages = is.readInt();
        numDimensions = is.readInt() * is.readInt();

        features = new double[numImages][numDimensions];
        for (int i = 0; i < numImages; i++) {
            for (int j = 0; j < numDimensions; j++) {
                features[i][j] = (double) is.readUnsignedByte() / 255.0;
            }
        }
    }

ラベルデータの読み込み

DataInputStreamを使ってデータを読み込みます。
readIntでマジックナンバー、画像数を読み込みます。
int型の配列labelsにreadUnsignedByteで正解ラベルを読み込みます。

LabelDataSet.java
    private void loadLabels() throws IOException {
        System.out.println("Loading label data from " + fileName + " ...");
        DataInputStream is = new DataInputStream(new GZIPInputStream(new FileInputStream(Const.BASE_PATH + fileName)));

        is.readInt();
        numLabels = is.readInt();

        labels = new int[numLabels];
        for (int i = 0; i < numLabels; i++) {
            labels[i] = is.readUnsignedByte();
        }
    }

画像の表示

テキストで表示

引数で画像のインデックスを指定して、コンソールに画像の概形をテキストで表示します。

ImageViewer.java
    public void showImageAsText(int index) {
        System.out.println("Label: " + labels[index]);
        for (int i = 0; i < 28; i++) {
            for (int j = 0; j < 28; j++) {
                double value = images[index][i * 28 + j];
                if (value > 0.0) {
                    System.out.print("**");
                } else {
                    System.out.print("  ");
                }
            }
            System.out.println();
        }
    }

BufferedImageを作成

ベクトル化された画像データを復元して、BufferedImageを作成します。
値は正規化されているので、255.0を掛けて元のグレースケール値に戻します。

ImageViewer.java
    private BufferedImage makeImage(int index) {
        BufferedImage image =
                new BufferedImage(28, 28, BufferedImage.TYPE_INT_RGB);

        for (int i = 0; i < 28; i++) {
            for (int j = 0; j < 28; j++) {
                int value = (int) (images[index][i * 28 + j] * 255.0);
                image.setRGB(j, i, 0xff000000 | value << 16 | value << 8 | value);
            }
        }

        return image;
    }

ダイアログに表示

makeImageで読み込んだBufferedImageをダイアログに表示します。

ImageViewer.java
    public void showImage(int index) {
        BufferedImage image = makeImage(index);
        Icon icon = new ImageIcon(image);
        JOptionPane.showMessageDialog(null, labels[index], "MnistImageViewer", JOptionPane.PLAIN_MESSAGE, icon);
    }

画像ファイルに保存

makeImageで読み込んだBufferedImageをgifファイルに保存します。

ImageViewer.java
    public void saveImage(String dir, String prefix, int index) throws IOException {
        BufferedImage image = makeImage(index);
        File file = new File(dir + "/" + prefix + "_" + String.format("%05d", index) + "_" + labels[index] + ".gif");
        if (file.exists()) file.delete();
        ImageIO.write(image, "gif", file);
    }
2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5