機械学習などに使用される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