【バイナリデータ】VBAでBMP画像を読み込みExcel上でドット絵を作成する方法

今回はExcel VBAで画像ファイルを読み込み、その画像ファイルの各ピクセルをExcelのセルに書き出し、ドット絵を作成する方法を紹介します。

この方法を理解することで、選択した画像ファイルを数値(CSVファイル)に変換したり、サイズや高さ/幅などを取得することができるようになります。

 

画像データの構造

画像データは多くのピクセルが集合することで1つの画像となっています。

ピクセルにはR(赤),G(緑),B(青)の各成分があり、それぞれ0〜255の256段階のいずれかの値を持ちます。これらの成分が合わさることで1つの色を表します。
(つまり256×256×256通りの色が再現できる)

これはVBAでセルに色を付けるときを考えればイメージがつくと思います。
下のコードではA1セルにR=243,G=152,B=0の色(オレンジ色)を塗ることができます。

 icon-code VBAでセルに色付け 

Range(“A1").Interior.Color = RGB(243, 152, 0)

つまり、画像すべてのピクセルのそれぞれのRGB値さえ取得できれば、同じようにセルに色を塗るだけでその画像を再現できるということです。
 

要はピクセルの代わりにExcelのセルだけで表現しようってお話ですね!

 

ピクセルのRGB値を取得する

Excel上でセルを使って画像を表現する考え方はわかりましたが、どのようにしてVBAで画像データの各ピクセルのRGB値を取得するのでしょうか。

その答えは『バイナリデータ』です。

バイナリデータとは簡単に言えばコンピュータが読み込むための二進数のデータのことをいいます。
私たち人類には日本語をはじめ英語、ドイツ語、ロシア語、中国語などの様々な言語がありますが、これらは”翻訳”することで意味を理解することができます。

コンピュータもこれと同じように”人間語”という言語を理解することが出来ないため、人間語をコンピュータ語に翻訳して言葉を理解しています。このコンピュータ語こそが『バイナリデータ』です

ただ、コンピュータ語といってもイメージが付きづらいと思うので実際に見てみましょう。
適当に”テキストデータ以外のファイル”をメモ帳で開いてみてください
(画像や動画ファイルを右クリック>[プログラムから開く]>[別のプログラムを選択]>[メモ帳])

すると上画像のように文字化けした訳の分からない文字列がつらつらと書き連ねられています。

これがコンピュータ語、つまりはバイナリデータです。
我々には文字化けして何が書かれているのか理解できないデータですが、コンピュータはここに書かれているデータから動画や画像ファイルなどを読み取っています

つまり、このバイナリデータさえ取得できれば、画像のRGB値を取得することも可能であるというわけです。

 

バイナリデータの取得

では、実際にVBAで選択した画像ファイルのバイナリデータを取得していきます。

ここでは画像ファイルとして、「BMPファイル」を使用します。(BMPファイルは圧縮が行われないため、ファイル構造が非常に分かりやすく、バイナリデータで読み込む際に都合がいいためです)

読み込ませる画像データが大きすぎると時間がかかるので、はじめのうちは512×512(px)以下のサイズの画像を使うことをオススメします。

Dim bData() As Byte
'―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Sub Main()

    Application.ScreenUpdating = False

    
    'ファイル選択ダイアログでファイルを指定
    Dim vFilePath As Variant
    vFilePath = Application.GetOpenFilename
    If vFilePath = False Then
        Application.ScreenUpdating = True
        End
    End If

    'ファイルサイズが0バイトの場合は処理終了
    Dim nFileLen As Long
    nFileLen = FileLen(vFilePath)
    If nFileLen = 0 Then
        Application.ScreenUpdating = True
        End
    End If
    
    ReDim bData(0 To nFileLen - 1)


    '選択されたbmp画像をバイナリデータで取得
    Dim iFile As Integer
    iFile = FreeFile
    
    Open vFilePath For Binary As #iFile
    Get #iFile, , bData
    Close #iFile
  
    
    If Not (bData(0) = 66 And bData(1) = 77) Then
        MsgBox "bmp画像を選択してください。"
        Exit Sub
    End If
    
    
    Dim BinarySheet As Worksheet
    Set BinarySheet = Sheets.Add(After:=Sheets(Sheets.count))
    BinarySheet.Name = "Binary"

    Dim i As Long
    'バイナリデータを16進ダンプ表示
    For i = 0 To nFileLen - 1
        y = i \ 16 + 1
        x = i Mod 16 + 1
        BinarySheet.Cells(y, x) = "'" & Right("0" & Hex(bData(i)), 2)
    Next i
    
    Application.ScreenUpdating = True

End Sub

 
上記のコードを実行してBMPファイルを選択すると「Binary」というシートが作成され、選択した画像のバイナリデータが16進ダンプで表示されます。BMPファイルを16進ダンプで表示することで、BMPファイルの構造を目視で読み取ることが可能になります。

BMPファイルの構造について詳しく知りたい方は下記ページを参照して下さい。下記ページの内容と「Binary」シートの内容を見比べることで各データが何を表しているかを理解することができます。

 

上記コードを実行することで配列「bData」に選択した画像のバイナリデータが格納されます。
(これを16進数に変換し可視化したのが「Binary」シート)

この配列の54番目以降の要素(Binaryシートの「G4セル」以降)が画像の各ピクセルのRGB値を表しています。(ちなみに0番目から〜53番目までは画像の種類や幅/高さ、ビットレートなどの情報が格納されています。詳しくは上記リンクページより確認ください)

格納されているRGB値は以下のようになっています。
RGBではなくBGRの順でピクセルの各成分値が格納されているので注意しましょう。

bData(54)=1つ目のピクセルの青の成分
bData(55)=1つ目のピクセルの緑の成分
bData(56)=1つ目のピクセルの赤の成分
bData(57)=2つ目のピクセルの青の成分
bData(58)=2つ目のピクセルの緑の成分
bData(59)=2つ目のピクセルの赤の成分
         :
bData(UBound(bData)-2)=最後のピクセルの青の成分
bData(UBound(bData)-1)=最後のピクセルの緑の成分
bData(UBound(bData))=最後のピクセルの赤の成分

上記コードにより各ピクセルの各成分の値が取得できたので、あとはこれらを使ってセルに色をつければドットの完成です。

ここまで理解できれば後は簡単ですね!

 

BMP画像をエクセルに書き出す

それでは実際に画像ファイルを読み込み、セルに色を出力していきます。

ここではサンプル画像として画像処理では同じみのレナさんの画像を使用します。
表示されている画像はpng形式なので、使用する際は下記リンクからダウンロードして下さい。

標準画像データベース – 神奈川工科大学 情報学部 情報工学科
(リンク先ページの最後でレナさんを含めたいくつかのBMP画像のダウンロードが可能です)
 

画像が保存できたらExcelで新規ブックを作成し、以下のコードを実行して下さい。
実行すると画像選択のウィンドウが立ち上がるので先ほど保存したBMP画像を選択して下さい。

※2度目の実行は想定していないのでエラーが発生します。
 再度実行したい場合は「Binary」「Export」シートを削除してから再実行してください。

Dim bData() As Byte
'―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Sub Main()

    Application.ScreenUpdating = False

    
    'ファイル選択ダイアログでファイルを指定
    Dim vFilePath As Variant
    vFilePath = Application.GetOpenFilename
    If vFilePath = False Then
        Application.ScreenUpdating = True
        End
    End If

    'ファイルサイズが0バイトの場合は処理終了
    Dim nFileLen As Long
    nFileLen = FileLen(vFilePath)
    If nFileLen = 0 Then
        Application.ScreenUpdating = True
        End
    End If
    
    ReDim bData(0 To nFileLen - 1)


    '選択されたbmp画像をバイナリデータで取得
    Dim iFile As Integer
    iFile = FreeFile
    
    Open vFilePath For Binary As #iFile
    Get #iFile, , bData
    Close #iFile
  
    
    If Not (bData(0) = 66 And bData(1) = 77) Then
        MsgBox "bmp画像を選択してください。"
        Exit Sub
    End If
    
    
    Dim BinarySheet As Worksheet
    Set BinarySheet = Sheets.Add(After:=Sheets(Sheets.Count))
    BinarySheet.Name = "Binary"

    Dim i As Long
    
    'バイナリデータを16進ダンプ表示
    For i = 0 To nFileLen - 1
        y = i \ 16 + 1
        x = i Mod 16 + 1
        BinarySheet.Cells(y, x) = "'" & Right("0" & Hex(bData(i)), 2)
    Next i
    
    CreateImage
    
    Application.ScreenUpdating = True

End Sub
'―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Sub CreateImage()

    Dim ExportSheet As Worksheet
    Set ExportSheet = Sheets.Add(After:=Sheets(Sheets.Count))
    ExportSheet.Name = "Export"
    
    Dim ImageRow As Long
    Dim ImageCol As Long
    Dim k As Long
    Dim R As Integer
    Dim G As Integer
    Dim B As Integer
    
    ImageCol = bData(18) + (bData(19) * 256) + (bData(20) * 256) + (bData(21) * 256)
    ImageRow = bData(22) + (bData(23) * 256) + (bData(24) * 256) + (bData(25) * 256)
    
    Dim elm As Long
    elm = ImageCol * ImageRow * 3
    
    Dim RGBDatas()
    ReDim RGBDatas(elm) '0番目は空にする

    'バイナリデータのデータ部情報を格納
    Dim i As Long
    For i = 1 To UBound(RGBDatas)
        RGBDatas(i) = bData(i + 53)
    Next i
    
    k = 1
    
    With ExportSheet
        .Cells.ClearFormats
        .Range(Columns(1), Columns(ImageCol)).ColumnWidth = 0.77 '3
        .Range(Rows(1), Rows(ImageRow)).RowHeight = 7.5 '21
    
        For i = ImageRow To 1 Step -1
            For j = 1 To ImageCol
            
            R = Int(RGBDatas((k - 1) * 3 + 1 + 2))
            G = Int(RGBDatas((k - 1) * 3 + 1 + 1))
            B = Int(RGBDatas((k - 1) * 3 + 1))
            
            .Cells(i, j).Interior.Color = RGB(R, G, B)
            k = k + 1
            
            Next j
        Next i
    
    End With

End Sub

 
うまく実行できれば以下のようにセルが着色され、読み込ませた画像が「Export」というシートに出力されます。(画像だとわかりづらいですがセル1つ1つに色が塗られて画像が表現されています)

コードでやっていることは単純で、取得したRGB値と冒頭にも出てきた基本コードの「.Interior.Color = RGB(r, g, b)」を使って順にセルに色を塗っていくだけです。

ただバイナリデータではRGBがBGRの順になるのと同じくピクセルの順番も逆転しています。
そのため54番目のデータから順にセルに書き出していくと、画像の上下が反転したものになってしまうので注意しましょう。(上記コードでは「Step -1」を使って反転に対応させています)

※※※ 注意 ※※※
画像のサイズによってはうまく結果が表示されない場合があります。

具体的にいうと「(画像の幅×3) ÷ 4」でキレイに割りきれない(余りが出る)場合です。つまりは「512×512」のサイズはうまく表示できますが「511×511」や「513×513」のサイズの画像はうまく表示されません。(斜めにズレるような画像が出力されます)

 

まとめ

今回は『VBAでBMP画像を読み込みExcel上でドット絵を作成する方法』についての内容でした。
ドット絵の作成方法とはいっていますが、実質的な中身は「バイナリデータとして画像を取得し、RGB値を取得する方法」の紹介でした。

画像を読み取ってセルに出力するマクロと聞くと「画像をExcelのセルに書き出す必要なんてあるの?」「画像として貼り付ければいいじゃん」という意見が出てくると思います。

しかし、今回の内容で重要なのは「VBAで画像ファイルをバイナリデータとして読み込み画像のデータを取得することが可能である」という点です。セルでドット絵を作成しるのは、しっかりと画像データを取得できていることが確認するためです。

今回メインとして使ったのはRGB値だけでしたが、他にも画像の高さや幅、サイズなども取得できています。これらのデータをうまく使うことで、VBAの可能性がさらに広がるので知識として覚えておくのもいいと思います。

本サイトではExcel VBAを使って文字認識の機械学習を行う方法を紹介していますが、今回やった内容がそちらで非常に重要なものとなってきます。興味のある方はぜひそちらのページも読んでみてください。

2021年3月26日VBA,画像処理