Visual Studio 2019のVisual Basic・Windows10環境にて、指定した画像中に任意のファイルを埋め込む。コードのサンプルです。

表題の通り
Visual Studio 2019のVisual Basicにて、
直接バイナリを叩いて指定した画像中に任意のファイル情報を埋め込む。
ステガノグラフィーに関する。コードのサンプルでございます。

このQiitaに投稿している、
[Visual Studio 2019のC#・Android環境にて、指定した画像中に任意のファイルを埋め込む。コードのサンプルです。](https://qiita.com/oyk3865b/items/4729a9e094ba767462f0)
を、
Visual Basic・Windows10環境で動作することを想定し
書き直したものの記事でございます。

<font color="Red">なので、重複している部分が多々ございます。</font>

また、この記事で出力される画像も、
[Visual Studio 2019のC#・Android環境にて、指定した画像中に任意のファイルを埋め込む。コードのサンプルです。](https://qiita.com/oyk3865b/items/4729a9e094ba767462f0)
にて出力されるものと、互換がございます。


#概要
[以前の記事](https://qiita.com/oyk3865b/items/4729a9e094ba767462f0)と同じ内容で
任意のファイルを、画像に埋め込む際に
バイナリを以前のように、直接叩く方法を採りたかったのですが、

<font color="Red">VB.NETにてBitmapを直接BitmapからByte配列に変換した場合
[各幅の行にあたるバイト数が4の倍数となるまで、空白の00が挟まる](https://oyk3865b.blog.fc2.com/blog-entry-1394.html)
という現象が発生します。</font>
元々、画像の幅が4の倍数なら問題ないのですが、そうでない場合
計算が面倒で、そして、計算して回避するコードを組んでも力不足で上手くいきませんでした。

そこで、
更新前の画像のバイナリを叩く方法ではなく、
画像のピクセルを叩く方法に変更いたしました。

**1.**埋め込む任意のファイルのバイナリを、すべて読み込む。
**2.**指定した画像をRGB565形式に変換。
**3.**画像の各1ドットのRGB値を取得し、RGB値それぞれを二進数とする。
**4.**各8桁の数からRGB565と、各色が割り当てられたビット数となるように<br>　　R:<font color="Red">11111</font> G:<font color="Green">111111</font> B:<font color="Blue">11111</font> とのように必要桁数の切り出しをする。
**5.**各色の下位ビットを切り捨てて<br>　　R:<font color="Red">111</font>-- G:<font color="Green">111</font>--- B:<font color="Blue">11</font>--- とのようにする。
**5.**埋め込むファイルの各バイナリを8桁の2進数にして、<font color="Brown">00000000</font>とのようにする。
**6.**8桁の2進数の内、2桁、3桁、3桁に分割して、<font color="Brown">00/000/000</font>とのようにする。
**7.**画像の各色のバイナリ上位の2進数に、<br>　　埋め込むファイルの2進数を下位に結合して、<br>　　R:<font color="Red">111</font><font color="Brown">00</font> G:<font color="Green">111</font><font color="Brown">000</font> B:<font color="Blue">11</font><font color="Brown">000</font>とのようにする。
**8.**上記のように、画像の1ドットにつき、埋め込むファイル1バイトが埋まっていく。
**9.**埋め込みが終わった色の末尾に0{今回はXと表現}を追加して、RGB888色に戻し<br>　　R:<font color="Red">111</font><font color="Brown">00</font>XXX G:<font color="Green">111</font><font color="Brown">000</font>XX B:<font color="Blue">11</font><font color="Brown">000</font>XXXとのようにしてから、<br>　　色情報にして画像に戻す。
**10.**可逆圧縮であるpng形式で、出力する。

そして、埋め込んだファイルを取り出す際は、
この逆をしています。

今回は、RGB565形式の画像に埋め込んでいます。
今回の方法なら　(幅x高さ) バイトの情報が入ります。


尚、
復元の際も、埋め込みと同じく
BitmapをRGB565形式で読み込み、直接Byte配列に変換する方式を採ろうとしましたが、
<font color="Red">画像読み込みの際に、誤差が生じるようで
復元したファイルのバイナリに変化が生じ
復元がうまくいきませんでした。</font>

このため、
手動で各ピクセルのRGB値を取って、
RGB565領域を取り出して、さらに、そこから復元する方法となっています。

#注意事項
<font color="Red">**※**この記事でのコードは、
最低限の記述にとどめているため、解放など不十分な部分や、
記述を省略している箇所があります。
また、逆に不必要なコードも記述しています。</font>

**※**コード中のURLは、参考にさせていただきましたサイト様のものでございます。
勝手ながら、参考にさせていただいたサイト様には、この場にて厚く御礼致します。

#下準備
今回は、Windowsフォームアブリケーションの
新規作成した状態からの作成を想定しています。

#本題
標題にある機能のための
Visual Basic 2019での
Windows10開発環境でのコードは、以下の通りでございます。

```VB:Form1.vb
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Text

Public Class Form1
    'RGB565モードで扱う
    Const bitmapConfig As Imaging.PixelFormat = Imaging.PixelFormat.Format16bppRgb565

    '出力先のパスを格納
    Dim Output_Path As String = System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)

    '表示更新スパン
    Const hyouji_refresh_span As Integer = 10000

    Public Sub Output_Sukashi_Bitmap_Making(Umekomi_FileName As String)
        '画像を埋め込むところ
        Dim BaseImage As Bitmap
        Dim err_str As String

        '画像の取得
        Using webClient As New System.Net.WebClient()
            '今回は、ネット空間からDLする。
            err_str = "DownloadDataTaskAsync"
            Using fc As Stream = webClient.OpenRead(New Uri("https://blog-imgs-110.fc2.com/o/y/k/oyk3865b/2017_05_03_290.jpg"))
                BaseImage = New Bitmap(fc)
                fc.Close()
            End Using
        End Using

        '埋め込む冒頭のファイルの名前の取得
        Dim FileName As String = System.IO.Path.GetFileName(Umekomi_FileName)

        '埋め込むファイルのバイナリ格納
        Dim bs As New List(Of Byte)

        Using stream As System.IO.Stream = File.OpenRead(Umekomi_FileName)
            Dim buffer(1023) As Byte                ' 読み込んだデータを格納するためのバッファ

            Do
                'ストリームから一時バッファに読み込む
                Dim read As Integer = stream.Read(buffer, 0, buffer.Length)

                If (read > 0) Then
                    '一時バッファの内容をメモリ・ストリームに書き込む
                    Dim ary2 As Byte() = New Byte(read - 1) {}
                    Array.Copy(buffer, 0, ary2, 0, read)
                    bs.AddRange(ary2)
                    Array.Clear(ary2, 0, ary2.Length)

                Else
                    Exit Do
                End If
            Loop

            Array.Clear(buffer, 0, buffer.Length)
            stream.Close()
        End Using


        'RGB565で、再作成する。
        'https://qiita.com/Nuits/items/da8c11e5b284ad6cb90a
        Using bmp As Bitmap = BaseImage.Clone(New Rectangle(0, 0, BaseImage.Width, BaseImage.Height), bitmapConfig)
            '先の画像を解放しておく
            BaseImage.Dispose()

            'これから何を埋め込むかの種類番号を格納
            Dim mode_flg As Integer = 0
            Dim data_cnt As Integer = 0
            Dim input_data As Byte() = Nothing
            Dim data_size As Byte() = Nothing

            '本体バイナリがすべて埋められたか？
            Dim data_complete_flg As Boolean = False

            '末尾を乱数で埋める
            Dim rnd As New System.Random()
            '各ピクセルに埋め込む
            For y = 0 To bmp.Height - 1
                For x = 0 To bmp.Width - 1
                    If (mode_flg = 0 OrElse mode_flg = 2) _
                        AndAlso data_cnt = 4 Then
                        'データ長の埋め込み(4バイト固定)後は、データ本体の埋め込みに進む
                        mode_flg += 1
                        data_cnt = 0

                    ElseIf mode_flg = 0 And data_cnt = 0 Then
                        '名前の埋め込み準備
                        input_data = Encoding.UTF8.GetBytes(FileName)
                        data_size = BitConverter.GetBytes(CInt(input_data.Length))

                    ElseIf mode_flg = 1 And data_cnt = input_data.Length Then
                        '本体バイナリの埋め込み準備
                        Array.Clear(data_size, 0, data_size.Length)
                        Array.Clear(input_data, 0, input_data.Length)

                        data_size = BitConverter.GetBytes(CInt(bs.Count))
                        mode_flg += 1
                        data_cnt = 0

                    ElseIf mode_flg = 3 And data_cnt = bs.Count Then
                        '乱数の埋め込み準備
                        Array.Clear(data_size, 0, data_size.Length)
                        bs.Clear()

                        mode_flg += 1
                        data_cnt = 0

                        '埋め込み完遂フラグ
                        data_complete_flg = True
                    End If

                    Select Case mode_flg
                        Case 0, 2
                            'データ長を埋め込む場合
                            bmp.SetPixel(x, y, data_umekomi_color(bmp.GetPixel(x, y), data_size(data_cnt)))
                        Case 1
                            '名前データ本体を埋め込む場合
                            bmp.SetPixel(x, y, data_umekomi_color(bmp.GetPixel(x, y), input_data(data_cnt)))
                        Case 3
                            '本体バイナリを埋め込む場合
                            bmp.SetPixel(x, y, data_umekomi_color(bmp.GetPixel(x, y), bs(data_cnt)))
                        Case 4
                            '乱数を埋め込む場合
                            bmp.SetPixel(x, y, data_umekomi_color(bmp.GetPixel(x, y), rnd.Next(256)))

                    End Select
                    data_cnt += 1
                Next
            Next

            If data_complete_flg Then
                '埋め込んだ画像の出力
                bmp.Save(System.IO.Path.Combine(Output_Path, "test.png"), Imaging.ImageFormat.Png)
                'デバッグ用
                MessageBox.Show("埋め込みEND")
            Else
                MessageBox.Show("指定ファイルが大きく埋め込めませんでした。")
            End If
        End Using




    End Sub

    Private Function data_umekomi_color(base_color As Color, umekomi_byte As Byte) As Color
        '指定されたバイト数値にデータを埋め込む所 

        '基礎画像の下位4ビットをカットする。
        Dim base1_str As String = Convert.ToString(base_color.R, 2).PadLeft(8, "0") '8桁の二進数にする。
        Dim base2_str As String = Convert.ToString(base_color.G, 2).PadLeft(8, "0") '8桁の二進数にする。
        Dim base3_str As String = Convert.ToString(base_color.B, 2).PadLeft(8, "0") '8桁の二進数にする。

        'RGB565なので、各色のビットを変換する
        Dim color_r As String = base1_str.Substring(0, 5)
        Dim color_g As String = base2_str.Substring(0, 6)
        Dim color_b As String = base3_str.Substring(0, 5)

        '空いた下位4ビットにデータを埋め込む
        Dim umekomi_str As String = Convert.ToString(umekomi_byte, 2).PadLeft(8, "0") '8桁の二進数にする。
        color_r = color_r.Substring(0, 3) + umekomi_str.Substring(0, 2) 'R下位2ビットに埋め込む
        color_g = color_g.Substring(0, 3) + umekomi_str.Substring(2, 3) 'G下位3ビットに埋め込む
        color_b = color_b.Substring(0, 2) + umekomi_str.Substring(5, 3) 'B下位3ビットに埋め込む

        '8ビット表記にする
        color_r &= "000"
        color_g &= "00"
        color_b &= "000"

        '元の色表現に戻す
        '2進数からInt32に変換
        Return Color.FromArgb(255, Convert.ToInt32(color_r, 2), Convert.ToInt32(color_g, 2), Convert.ToInt32(color_b, 2))

    End Function


    Public Sub Input_Fukugo_Bitmap(FileName As String)
        '★埋めたファイルを復号するところ

        'これから取り出すデータのサイズを格納
        Dim data_size As Int32 = 0
        '埋め込むファイルのバイナリ格納
        Dim bs As New List(Of Byte)

        '名前の格納
        Dim load_filepath As String = ""

        '画像の取得
        Using fs As New FileStream(FileName, FileMode.Open)
            Using bm_2 As New Bitmap(System.Drawing.Image.FromStream(fs))
                '埋め込みモードの取得

                Dim fff As String = ""
                Dim data_mode As Integer = 0

                For y As Integer = 0 To bm_2.Height - 1
                    For x As Integer = 0 To bm_2.Width - 1
                        '指定位置のピクセルの色を取得
                        Dim clr As Color = bm_2.GetPixel(x, y)

                        'RGB565各ビットに変換して取得
                        fff &= Convert.ToString(clr.R, 2).PadLeft(8, "0").Substring(0, 5)
                        fff &= Convert.ToString(clr.G, 2).PadLeft(8, "0").Substring(0, 6)
                        fff &= Convert.ToString(clr.B, 2).PadLeft(8, "0").Substring(0, 5)

                        '2バイトの文字列からデータを取り出す
                        bs.Add(data_toridashi_str(fff))
                        fff = ""

                        If data_mode = 0 AndAlso bs.Count = 4 Then
                            '名前のデータサイズ
                            data_size = BitConverter.ToInt32(bs.ToArray(), 0)
                            data_mode += 1
                            bs.Clear()

                        ElseIf data_mode = 1 AndAlso bs.Count = data_size Then
                            '名前の取得
                            'UTF8にてバイナリを文字列に戻す
                            '出力先パスの完成";
                            load_filepath = System.Text.Encoding.UTF8.GetString(bs.ToArray())
                            load_filepath = System.IO.Path.Combine(Output_Path, load_filepath)
                            load_filepath = System.IO.Path.Combine(Output_Path, System.IO.Path.GetFileNameWithoutExtension(load_filepath) + "[t]" + System.IO.Path.GetExtension(load_filepath))
                            data_mode += 1
                            bs.Clear()

                        ElseIf data_mode = 2 AndAlso bs.Count = 4 Then
                            '本体のデータサイズ
                            data_size = BitConverter.ToInt32(bs.ToArray(), 0)
                            data_mode += 1
                            bs.Clear()

                        ElseIf data_mode = 3 AndAlso bs.Count = data_size Then
                            '本体の取得
                            'ファイルに書き出す";
                            System.IO.File.WriteAllBytes(load_filepath, bs.ToArray())
                            bs.Clear()

                            '復号したファイルを開く
                            Process.Start(load_filepath)

                            MessageBox.Show(load_filepath & Environment.NewLine & "のファイルを取り出す処理が終わりました", "処理完了", MessageBoxButtons.OK, MessageBoxIcon.Information)
                            Exit Sub
                        End If
                    Next
                Next
            End Using
        End Using
    End Sub

    Private Function data_toridashi_str(color_bits_str As String) As Byte
        '指定された2バイト文字列からデータを取り出す所 

        'RGB565なので、各色のビットに分ける
        '→リトルエンディアンとする。
        'https://www.argocorp.com/software/sdk/ICImagingControl/users_guide_c++/tutorial/pixformat/PixelformatRGB565.htm
        Dim color_r As String = color_bits_str.Substring(0, 5)
        Dim color_g As String = color_bits_str.Substring(5, 6)
        Dim color_b As String = color_bits_str.Substring(11, 5)

        '下位ビットを取り出す
        color_bits_str = color_r.Substring(3, 2) + color_g.Substring(3, 3) + color_b.Substring(2, 3)


        '2進数からbyteに変換
        Dim ret As Integer = Convert.ToInt32(color_bits_str, 2)

        Try
            Return CByte(ret)

        Catch
            Return 0
        End Try
    End Function

    Dim btn1 As Button, btn2 As Button

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        btn1 = New Button
        btn1.Location = New Point(13, 25)
        btn1.Size = New Size(150, 100)
        btn1.Text = "ファイル埋め込み"
        btn1.Name = "btn1"
        Me.Controls.Add(btn1)
        AddHandler btn1.Click, AddressOf btn1_Click

        btn2 = New Button
        btn2.Location = New Point(180, 25)
        btn2.Size = New Size(150, 100)
        btn2.Text = "ファイル取り出し"
        btn2.Name = "btn2"
        Me.Controls.Add(btn2)
        AddHandler btn2.Click, AddressOf btn2_Click

    End Sub
    Private Sub btn1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Using OpenFileDialog1 As New OpenFileDialog
            '■対応している拡張子一覧  
            OpenFileDialog1.Filter = "埋め込むファイル|*.*"
            OpenFileDialog1.FilterIndex = 1
            OpenFileDialog1.Title = "これから埋め込むファイルを選択してください"
            OpenFileDialog1.Multiselect = False

            'ダイアログを表示する  
            If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
                'OKボタンがクリックされたとき  
                '■出力先の設定2  
                Output_Sukashi_Bitmap_Making(OpenFileDialog1.FileName)

            End If
        End Using
    End Sub
    Private Sub btn2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Using OpenFileDialog1 As New OpenFileDialog
            '■対応している拡張子一覧  
            OpenFileDialog1.Filter = "埋め込んである画像ファイル|*.png"
            OpenFileDialog1.FilterIndex = 1
            OpenFileDialog1.Title = "これから扱う画像ファイルを選択してください"
            OpenFileDialog1.Multiselect = False

            'ダイアログを表示する  
            If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
                'OKボタンがクリックされたとき  
                '■出力先の設定2  
                Input_Fukugo_Bitmap(OpenFileDialog1.FileName)

            End If
        End Using
    End Sub

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
        RemoveHandler btn1.Click, AddressOf btn1_Click   'イベントを関連付けの解除
        btn1.Dispose()

        RemoveHandler btn2.Click, AddressOf btn2_Click   'イベントを関連付けの解除
        btn2.Dispose()

    End Sub
End Class


```

なお、埋め込んだファイルが確実に復号できているかを確認するために
CRC-32等のハッシュ値を併せて埋め込んでおくと、より確実に思います。

#サンプル

上記のコードで、
画像に、私作成であり
Vector様のサイトで公開している
[『オフタイマー弐式』](https://www.vector.co.jp/soft/winnt/personal/se491845.html)という、
Windowsソフトを
埋め込んでおります。
<img src="https://blog-imgs-132.fc2.com/o/y/k/oyk3865b/2020_01_20_test.png" width=50%>

exeファイルは、直接だったり
zipに圧縮してメールでやり取りしようとしても、
セキュリティにて、止められたりしますが

画像に埋め込んであるので
途中で加工さえされなければ、
そこは、すり抜けます。

人に見られたくないファイルの
保存にも向きますが、

消えると困る大切なファイルの埋め込みには向かないと
私は感じております。



#没案
Androidの記事と同じく、
バイナリを直接触って埋め込もうとして作成したコードの残骸です。
埋め込み部分だけ示します。

幅が4の倍数でない画像だと、
末尾に黒い領域が生じ、復元も上手くいかなくなります。

```VB:FormBotsu.vb
    'RGB565モードで扱う
    Const bitmapConfig As Imaging.PixelFormat = Imaging.PixelFormat.Format16bppRgb565

    Dim base_cnt As Integer = 0

    '出力先のパスを格納
    Dim Output_Path As String = System.Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)


    '表示更新スパン
    Const hyouji_refresh_span As Integer = 10000

    Public Sub Output_Sukashi_Bitmap_Making(Umekomi_FileName As String)
        '画像を埋め込むところ
        Dim BaseImage As Bitmap
        Dim err_str As String

        '画像の取得
        Using webClient As New System.Net.WebClient()
            '今回は、ネット空間からDLする。
            err_str = "DownloadDataTaskAsync"
            Using fc As Stream = webClient.OpenRead(New Uri("https://blog-imgs-110.fc2.com/o/y/k/oyk3865b/2017_05_03_290.jpg"))
                BaseImage = New Bitmap(fc)
                fc.Close()
            End Using
        End Using

        '埋め込む冒頭のファイルの名前の取得
        Dim FileName As String = System.IO.Path.GetFileName(Umekomi_FileName)

        '埋め込むファイルのバイナリ格納
        Dim bs As New List(Of Byte)

        Using stream As System.IO.Stream = File.OpenRead(Umekomi_FileName)
            Dim buffer(1023) As Byte                ' 読み込んだデータを格納するためのバッファ

            Do
                'ストリームから一時バッファに読み込む
                Dim read As Integer = stream.Read(buffer, 0, buffer.Length)

                If (read > 0) Then
                    '一時バッファの内容をメモリ・ストリームに書き込む
                    Dim ary2 As Byte() = New Byte(read - 1) {}
                    Array.Copy(buffer, 0, ary2, 0, read)
                    bs.AddRange(ary2)
                    Array.Clear(ary2, 0, ary2.Length)

                Else
                    Exit Do
                End If
            Loop

            Array.Clear(buffer, 0, buffer.Length)
            stream.Close()
        End Using


        'RGB565で、Bitmapを再作成する。
        'https://qiita.com/Nuits/items/da8c11e5b284ad6cb90a
        Dim bmparr As Byte() '画像バイナリを格納するところ
        Using bmp As Bitmap = BaseImage.Clone(New Rectangle(0, 0, BaseImage.Width, BaseImage.Height), bitmapConfig)
            'Bitmapをロックし、BitmapDataを取得する
            Dim srcBitmapData As System.Drawing.Imaging.BitmapData =
                bmp.LockBits(
                    New System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
                    System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat)

            '変換対象のカラー画像の情報をバイト列へ書き出す
            ReDim bmparr(bmp.Width * bmp.Height * 2 - 1)
            System.Runtime.InteropServices.Marshal.Copy(srcBitmapData.Scan0, bmparr, 0, bmparr.Length)
            bmp.UnlockBits(srcBitmapData)
        End Using

        '画像の大きさを記憶しておく
        Dim bmp_width As Integer = BaseImage.Width
        Dim bmp_height As Integer = BaseImage.Height
        BaseImage.Dispose()

        '基礎の埋め込み開始位置の初期化
        base_cnt = 0

        '冒頭には、埋め込んだファイル情報を入れる
        '名前データ
        Dim name_data As Byte() = Encoding.UTF8.GetBytes(FileName)

        '名前のサイズ4バイト
        Dim size_data As Byte() = BitConverter.GetBytes(CInt(name_data.Length))

        '名前を埋め込んでいく"
        For i As Integer = 0 To (size_data.Length - 1)
            'データ埋め込み
            data_umekomi(bmparr(base_cnt), bmparr(base_cnt + 1), size_data(i))
        Next
        '続いて、名前バイナリを埋め込む
        For i As Integer = 0 To (name_data.Length - 1)
            'データ埋め込み
            data_umekomi(bmparr(base_cnt), bmparr(base_cnt + 1), name_data(i))
        Next

        'いったん初期化
        Array.Clear(size_data, 0, size_data.Length)
        Array.Clear(name_data, 0, name_data.Length)

        '次に、埋め込むファイル本体のサイズ
        size_data = BitConverter.GetBytes(CInt(bs.Count))
        For i As Integer = 0 To (size_data.Length - 1)
            'データ埋め込み
            data_umekomi(bmparr(base_cnt), bmparr(base_cnt + 1), size_data(i))
        Next

        '埋め込み側のバイナリ本体を埋め込む
        For i As Integer = 0 To (bs.Count - 1)
            '安全装置
            If (base_cnt + 1 > bmparr.Length - 1) Then
                MessageBox.Show("埋め込められる容量 " + i.ToString() + "B を超えました。", "容量オーバー", MessageBoxButtons.OK, MessageBoxIcon.Information)
                Exit For
            ElseIf i Mod hyouji_refresh_span = 0 Then
                'たまには休む
                Threading.Thread.Sleep(5)
                Application.DoEvents()
            End If

            'データ埋め込み
            data_umekomi(bmparr(base_cnt), bmparr(base_cnt + 1), bs(i))
        Next

        bs.Clear()
        Array.Clear(size_data, 0, size_data.Length)


        '末尾を乱数で埋める
        Dim rnd As New System.Random()
        '乱数を配列に埋め込む
        ReDim size_data(bmparr.Length - base_cnt - 1)
        rnd.NextBytes(size_data)
        For i As Integer = 0 To (size_data.Length - 1)
            '安全装置       
            If (base_cnt + 1 > bmparr.Length - 1) Then
                Exit For
            ElseIf (i Mod hyouji_refresh_span = 0) Then
                'たまには休む
                Threading.Thread.Sleep(5)
                Application.DoEvents()
            End If

            'データ埋め込み
            data_umekomi(bmparr(base_cnt), bmparr(base_cnt + 1), size_data(i))
        Next
        Array.Clear(size_data, 0, size_data.Length)

        'バイト配列をBitmapオブジェクトに変換
        'https://qiita.com/Nuits/items/da8c11e5b284ad6cb90a
        'http://mapw.elte.hu/elek/image2bytearray.html
        Using out_bmp As New Bitmap(bmp_width, bmp_height, bitmapConfig)

            Dim bmpData As BitmapData = out_bmp.LockBits(New Rectangle(0, 0, bmp_width, bmp_height), ImageLockMode.WriteOnly, bitmapConfig)
            Dim ptr As IntPtr = bmpData.Scan0
            Dim psize As Int32 = bmparr.Length ' bmpData.Stride * bmp_height
            System.Runtime.InteropServices.Marshal.Copy(bmparr, 0, ptr, psize)
            out_bmp.UnlockBits(bmpData)

            '画像をファイルに出力
            out_bmp.Save(System.IO.Path.Combine(Output_Path, "test.png"), Imaging.ImageFormat.Png)
        End Using

        'デバッグ用
        MessageBox.Show("埋め込みEND")

    End Sub

    Private Sub data_umekomi(ByRef base1 As Byte, ByRef base2 As Byte, umekomi_byte As Byte)
        '指定されたバイト数値にデータを埋め込む所 

        '基礎画像の下位4ビットをカットする。
        Dim base1_str As String = Convert.ToString(base1, 2).PadLeft(8, "0") '8桁の二進数にする。
        Dim base2_str As String = Convert.ToString(base2, 2).PadLeft(8, "0") '8桁の二進数にする。

        'RGB565なので、各色のビットに分ける
        '→リトルエンディアンとする。
        'https://www.argocorp.com/software/sdk/ICImagingControl/users_guide_c++/tutorial/pixformat/PixelformatRGB565.htm
        Dim color_bits_str As String = base2_str + base1_str
        Dim color_r As String = color_bits_str.Substring(0, 5)
        Dim color_g As String = color_bits_str.Substring(5, 6)
        Dim color_b As String = color_bits_str.Substring(11, 5)

        '空いた下位4ビットにデータを埋め込む
        Dim umekomi_str As String = Convert.ToString(umekomi_byte, 2).PadLeft(8, "0") '8桁の二進数にする。
        color_r = color_r.Substring(0, 3) + umekomi_str.Substring(0, 2) 'R下位2ビットに埋め込む
        color_g = color_g.Substring(0, 3) + umekomi_str.Substring(2, 3) 'G下位3ビットに埋め込む
        color_b = color_b.Substring(0, 2) + umekomi_str.Substring(5, 3) 'B下位3ビットに埋め込む

        '元のビット文字列に戻す
        color_bits_str = color_r + color_g + color_b


        '2進数からbyteに変換
        base1 = Convert.ToByte(color_bits_str.Substring(8, 8), 2)
        base2 = Convert.ToByte(color_bits_str.Substring(0, 8), 2)

        '基礎側のバイナリは2つ進む
        base_cnt += 2
    End Sub
```