表題の通り
Visual Studio 2019のVisual Basicにて、
直接バイナリを叩いて指定した画像中に任意のファイル情報を埋め込む。
ステガノグラフィーに関する。コードのサンプルでございます。
このQiitaに投稿している、
Visual Studio 2019のC#・Android環境にて、指定した画像中に任意のファイルを埋め込む。コードのサンプルです。
を、
Visual Basic・Windows10環境で動作することを想定し
書き直したものの記事でございます。
なので、重複している部分が多々ございます。
また、この記事で出力される画像も、
Visual Studio 2019のC#・Android環境にて、指定した画像中に任意のファイルを埋め込む。コードのサンプルです。
にて出力されるものと、互換がございます。
#概要
以前の記事と同じ内容で
任意のファイルを、画像に埋め込む際に
バイナリを以前のように、直接叩く方法を採りたかったのですが、
VB.NETにてBitmapを直接BitmapからByte配列に変換した場合
各幅の行にあたるバイト数が4の倍数となるまで、空白の00が挟まる
という現象が発生します。
元々、画像の幅が4の倍数なら問題ないのですが、そうでない場合
計算が面倒で、そして、計算して回避するコードを組んでも力不足で上手くいきませんでした。
そこで、
更新前の画像のバイナリを叩く方法ではなく、
画像のピクセルを叩く方法に変更いたしました。
**1.**埋め込む任意のファイルのバイナリを、すべて読み込む。
**2.**指定した画像をRGB565形式に変換。
**3.**画像の各1ドットのRGB値を取得し、RGB値それぞれを二進数とする。
**4.**各8桁の数からRGB565と、各色が割り当てられたビット数となるように
R:11111 G:111111 B:11111 とのように必要桁数の切り出しをする。
**5.**各色の下位ビットを切り捨てて
R:111-- G:111--- B:11--- とのようにする。
**5.**埋め込むファイルの各バイナリを8桁の2進数にして、00000000とのようにする。
**6.**8桁の2進数の内、2桁、3桁、3桁に分割して、00/000/000とのようにする。
**7.**画像の各色のバイナリ上位の2進数に、
埋め込むファイルの2進数を下位に結合して、
R:11100 G:111000 B:11000とのようにする。
**8.**上記のように、画像の1ドットにつき、埋め込むファイル1バイトが埋まっていく。
**9.**埋め込みが終わった色の末尾に0{今回はXと表現}を追加して、RGB888色に戻し
R:11100XXX G:111000XX B:11000XXXとのようにしてから、
色情報にして画像に戻す。
**10.**可逆圧縮であるpng形式で、出力する。
そして、埋め込んだファイルを取り出す際は、
この逆をしています。
今回は、RGB565形式の画像に埋め込んでいます。
今回の方法なら (幅x高さ) バイトの情報が入ります。
尚、
復元の際も、埋め込みと同じく
BitmapをRGB565形式で読み込み、直接Byte配列に変換する方式を採ろうとしましたが、
画像読み込みの際に、誤差が生じるようで
復元したファイルのバイナリに変化が生じ
復元がうまくいきませんでした。
このため、
手動で各ピクセルのRGB値を取って、
RGB565領域を取り出して、さらに、そこから復元する方法となっています。
#注意事項
**※**この記事でのコードは、
最低限の記述にとどめているため、解放など不十分な部分や、
記述を省略している箇所があります。
また、逆に不必要なコードも記述しています。
**※**コード中のURLは、参考にさせていただきましたサイト様のものでございます。
勝手ながら、参考にさせていただいたサイト様には、この場にて厚く御礼致します。
#下準備
今回は、Windowsフォームアブリケーションの
新規作成した状態からの作成を想定しています。
#本題
標題にある機能のための
Visual Basic 2019での
Windows10開発環境でのコードは、以下の通りでございます。
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様のサイトで公開している
『オフタイマー弐式』という、
Windowsソフトを
埋め込んでおります。
exeファイルは、直接だったり
zipに圧縮してメールでやり取りしようとしても、
セキュリティにて、止められたりしますが
画像に埋め込んであるので
途中で加工さえされなければ、
そこは、すり抜けます。
人に見られたくないファイルの
保存にも向きますが、
消えると困る大切なファイルの埋め込みには向かないと
私は感じております。
#没案
Androidの記事と同じく、
バイナリを直接触って埋め込もうとして作成したコードの残骸です。
埋め込み部分だけ示します。
幅が4の倍数でない画像だと、
末尾に黒い領域が生じ、復元も上手くいかなくなります。
'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