機械学習などに使用されるMNIST手書き文字データをピクチャーボックスに表示する方法を記載します。
MNIST手書き文字データは、手書き文字データファイルとラベルファイルの2つのファイルに分かれています。
文字データファイルの構造
先頭の16バイトがヘッダー部で、それ以降がデータ部分になっています。
- マジックナンバー:4バイト
- 文字データ数:4バイト
- イメージ高さ:4バイト
- イメージ幅 :4バイト
- 文字データ:784バイト
- 文字データ:784バイト
- ・・・・
ラベルデータの構造
先頭の8バイトがヘッダー部で、それ以降がラベルになっています。
- マジックナンバー:4バイト
- ラベル数:4バイト
- ラベル:1バイト
- ラベル:1バイト
- ・・・・
MNIST手書き数字データは、THE MNIST DATABASEからダウンロードできます。
MNIST手書き文字データを画面に表示させる
フォームにボタン、ピクチャーボックス、リストボックスを配置します。
Const cImageSize = 28 'イメージのザイズ 24bit color 32 * 32 * 3 = 3072 Const cImageDataLength = cImageSize * cImageSize 'チャネルあたりのバイト数 (784 Byte) <StructLayout(LayoutKind.Sequential, Pack:=1)> Friend Structure MNISTImageHead <VBFixedArray(4 - 1)> Public MagicNumber() As Byte 'マジックナンバー <VBFixedArray(4 - 1)> Public Images() As Byte 'データ個数 <VBFixedArray(4 - 1)> Public Rows() As Byte 'イメージ高さ <VBFixedArray(4 - 1)> Public Columns() As Byte 'イメージ幅 End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Friend Structure MNISTImageData <VBFixedArray(cImageDataLength - 1)> Public Pixel() As Byte '1つのイメージデータ End Structure Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button3.Click Dim FilePath As String = "C:\work\train-images.idx3-ubyte" 'レコード長を取得する Dim ImageHedLen As Long = Len(New MNISTImageHead()) 'ヘッダーレコード長 Dim ImageDataLen As Long = Len(New MNISTImageData()) 'データレコード長 Dim ImageHead = New MNISTImageHead() 'ヘッダー読み込み領域 Dim ImageData = New MNISTImageData() 'データ読み込み領域 'ファイルをオープンする Dim FileNo As Integer = FileSystem.FreeFile FileSystem.FileOpen(FileNo, FilePath, OpenMode.Random, OpenAccess.Read, OpenShare.Default, 1) 'イメージデータ数を取得する Dim fileBytes As Long = FileSystem.LOF(FileNo) Dim recordMax As Long = (fileBytes - ImageHedLen) \ ImageDataLen FileSystem.FileGet(FileNo, ImageHead) 'ヘッダー読み込み For rec As Integer = 1 To recordMax Dim pos As Long = ImageDataLen * (rec - 1) + ImageHedLen FileSystem.Seek(FileNo, pos) FileSystem.FileGet(FileNo, ImageData) 'イメージデータ読み込み Dim bmp = MNISTToBitmap(ImageData) 'イメージデータをビットマップ変換 PictureBox1.Image = bmp PictureBox1.Refresh() MNISTImageDataDisp(ImageData) 'バイナリーデータを画面表示 System.Threading.Thread.Sleep(100) Next 'ファイルをクローズする FileSystem.FileClose(FileNo) End Sub 'イメージデータをビットマップ変換 Private Function MNISTToBitmap(ImageData As MNISTImageData) Dim Data As Byte() = New Byte(cImageDataLength - 1) {} For i = 0 To cImageDataLength - 1 Data(i) = ImageData.Pixel(i) Next Dim bmp As Bitmap = New Bitmap(cImageSize, cImageSize, PixelFormat.Format8bppIndexed) Dim rect = New Rectangle(0, 0, cImageSize, cImageSize) Dim bitmapData = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed) Marshal.Copy(Data, 0, bitmapData.Scan0, Data.Length) bmp.UnlockBits(bitmapData) Return bmp End Function 'バイナリーデータを画面表示 Private Sub MNISTImageDataDisp(ImageData As MNISTImageData) ListBox1.Items.Clear() Dim pnt As Integer = 0 For i = 0 To cImageSize - 1 Dim s As String = "" For j = 0 To cImageSize - 1 s = s + " " + String.Format("{0:X2}", ImageData.Pixel(pnt)) pnt = pnt + 1 Next ListBox1.Items.Add(s) Next ListBox1.Refresh() End Sub
ラベルデータを画面に表示させる
フォームにボタン、ラベルを配置します。
<StructLayout(LayoutKind.Sequential, Pack:=1)> Friend Structure MNISTLabelHead <VBFixedArray(4 - 1)> Public MagicNumber() As Byte 'マジックナンバー <VBFixedArray(4 - 1)> Public Images() As Byte 'データ個数 End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Friend Structure MNISTLabelData <VBFixedArray(cImageDataLength - 1)> Public Label() As Byte 'ラベル End Structure Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button4.Click Dim FilePath As String = "C:\work\train-labels.idx1-ubyte" 'レコード長を取得する Dim LabelHedLen As Long = Len(New MNISTLabelHead()) 'ヘッダーレコード長 Dim LabelDataLen As Long = Len(New MNISTLabelData()) 'データレコード長 Dim LabelHead = New MNISTLabelHead() 'ヘッダー読み込み領域 Dim LabelData = New MNISTLabelData() 'データ読み込み領域 'ファイルをオープンする Dim FileNo As Integer = FileSystem.FreeFile FileSystem.FileOpen(FileNo, FilePath, OpenMode.Random, OpenAccess.Read, OpenShare.Default, 1) 'イメージデータ数を取得する Dim fileBytes As Long = FileSystem.LOF(FileNo) Dim recordMax As Long = (fileBytes - LabelHedLen) \ LabelDataLen FileSystem.FileGet(FileNo, LabelHead) 'ヘッダー読み込み For rec As Integer = 1 To recordMax Dim pos As Long = LabelDataLen * (rec - 1) + LabelHedLen FileSystem.Seek(FileNo, pos) FileSystem.FileGet(FileNo, LabelData) 'イメージデータ読み込み Label1.Text = String.Format("{0:X2}", LabelData.Label(0)) Label1.Refresh() System.Threading.Thread.Sleep(100) Next 'ファイルをクローズする FileSystem.FileClose(FileNo) End Sub