Excel VBAでファイル解析(BMPファイル編)
■BMPファイルの概要
BMP(Microsoft Windows Bitmap Image)は、MicrosoftとIBMが共同で開発した画像ファイルの形式で、ファイルの拡張子は「.bmp」です。
今となっては古典的なファイル形式ですが、構造がシンプルでわかり易く、個人的には好きなフォーマットなので、次項で詳細を説明します。
⇒詳細はMicrosoftのサイト(ビットマップ)を参照して下さい。
BMPファイルの構成
BMPファイルの構成は下図のようになっており、「ファイルヘッダ」「情報ヘッダ」「カラーパレット」「ビットマップデータ」の順に配置されています。
ファイル ヘッダ |
情報ヘッダ | カラー パレット |
ビットマップ データ |
ファイルヘッダのフォーマット
ファイルヘッダのフォーマットは、下表の通りです。
オフセット | 長さ | 説明 |
---|---|---|
0 | 2 | ファイルタイプ('BZ’固定) |
2 | 4 | ファイルサイズ |
6 | 2 | 予約領域1(0固定) |
8 | 2 | 予約領域2(0固定) |
10 | 4 | ビットマップデータのオフセット |
情報ヘッダのフォーマット
情報ヘッダのフォーマットは、下表の通りです。
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | ヘッダサイズ(40固定) |
4 | 4 | 画像の幅 |
8 | 4 | 画像の高さ |
12 | 2 | プレーン数(1固定) |
14 | 2 | 1ピクセルあたりのビット数 (0,1,4,8,16,24,32のいずれか) |
16 | 4 | 圧縮形式 以下のいずれか(通常は0) 0:無圧縮 1:8ビット/ピクセル RLE(連長圧縮) 2:4ビット/ピクセル RLE(連長圧縮) 3:ビットフィールド 4:JPEG画像 5:PNG画像 |
20 | 4 | 画像データサイズ |
24 | 4 | 水平解像度 |
28 | 4 | 垂直解像度 |
32 | 4 | 使用色数(0で全色使用) |
36 | 4 | 重要色数(0で全色使用) |
カラーパレットのフォーマット
カラーパレットは、情報ヘッダの「1ピクセルあたりのビット数」が『1』『4』『8』の時に存在し、『1』であれば2色、『4』であれば16色、『8』であれば256色をRGB形式の配列で定義したものです。
それぞれの色は、以下の4バイトで定義されています。
オフセット | 長さ | 説明 |
---|---|---|
0 | 1 | 青 |
1 | 1 | 緑 |
2 | 1 | 赤 |
3 | 1 | 予約領域 |
ビットマップデータのフォーマット
画像イメージのデータを左から右、下から上に向ってピクセルごとに格納しますが、行単位で4バイト境界に揃える必要があるため、端数の部分はパディング・バイト(X’00’)を埋めて調整します。
各ピクセルの色は、情報ヘッダの「1ピクセルあたりのビット数」が『1』『4』『8』の時はカラーパレットの番号、『16』『24』『32』の時は(カラーパレットが存在しないため)RGB形式で指定します。
RGBの値を1バイト(8ビット)で表す方式をRGB888と呼び、32bitの場合はRGBQUAD(青、緑、赤、予約領域の4バイト)、24bitの場合はRGBTRIPLE(青、緑、赤の3バイト)でRGB値を格納します。
16bitの場合はRGBを2バイトで表すため、RGB565(R:5bit,G:6bit,B:5bit)やRGB555(R:5bit,G:5bit,B:5bit,予備:1bit)等の形式で格納されており、これを実現するため、カラーパレットの代りにカラーマスクを保有しています(詳細な説明は割愛します)。
■BMPファイルのヘッダ情報をExcelシートに表示する処理
BMPファイルのヘッダを読み込み、Excelシートに「オフセット」「長さ」「項目」「値」を表示するプログラムです。
⇒サンプル・プログラムで使用している、Binary Fileクラスの詳細については、Excel VBAでファイル解析(事前準備編)を参照して下さい。
処理の概要
処理の流れは以下の通りです。
①Binary Fileオブジェクトをインスタンス化し、BMPファイルを読み込み
②BMPファイルのヘッダを解析し、必要な情報を取得してExcelシートにセット
③使用済のオブジェクトを破棄
サンプル・プログラム
行番号8~14で見出し項目の設定を行った後、行番号15でBinary Fileオブジェクトをインスタンス化し、行番号16でBMPファイルを読み込んでいます。
行番号18~45でファイルヘッダ、行番号46~103で情報ヘッダの内容を表示し、行番号104~110でカラーパレット、行番号111~115でビットマップデータの概要(「オフセット」と「長さ」飲み)を表示しています。
(※)BMPファイルの数値データ(サイズ、オフセット等)は全てリトル・エンディアンで格納されているため、計算に使用する場合はエンディアン変換が必要です。
- Dim sht, bf As Object
- Dim wOff, wLen As String
- Dim lcnt As Long
- Private Sub Sample1()
- Set sht = ActiveSheet
- sht.Cells.NumberFormatLocal = “@"
- sht.Range(“A1:D1").Interior.Color = RGB(127, 127, 127)
- sht.Range(“A1:D1").Font.Bold = True
- sht.Range(“A1:D1").HorizontalAlignment = xlCenter
- sht.Range(“A1") = “オフセット"
- sht.Range(“B1") = “長さ"
- sht.Range(“C1") = “項目"
- sht.Range(“D1") = “値"
- Set bf = New BinaryFile
- bf.InputFile (“C:\work\xxx.bmp")
- lcnt = 2
- sht.Cells(lcnt, 1) = “[ファイルヘッダ]"
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(2), 8)
- sht.Cells(lcnt, 3) = “ファイルタイプ"
- sht.Cells(lcnt, 4) = bf.GetDataHex(2)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “ファイルサイズ"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(2), 8)
- sht.Cells(lcnt, 3) = “予約領域1"
- sht.Cells(lcnt, 4) = bf.GetDataHex(2)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(2), 8)
- sht.Cells(lcnt, 3) = “予約領域2"
- sht.Cells(lcnt, 4) = bf.GetDataHex(2)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “ビットマップデータのオフセット"
- wOff = bf.GetDataHex(4)
- sht.Cells(lcnt, 4) = wOff
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = “[情報ヘッダ]"
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “ヘッダサイズ"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “画像の幅"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “画像の高さ"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(2), 8)
- sht.Cells(lcnt, 3) = “プレーン数"
- sht.Cells(lcnt, 4) = bf.GetDataHex(2)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(2), 8)
- sht.Cells(lcnt, 3) = “1ピクセルあたりのビット数"
- sht.Cells(lcnt, 4) = bf.GetDataHex(2)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “圧縮形式"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “画像データサイズ"
- wLen = bf.GetDataHex(4)
- sht.Cells(lcnt, 4) = wLen
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “水平解像度"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “垂直解像度"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “使用色数"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(4), 8)
- sht.Cells(lcnt, 3) = “重要色数"
- sht.Cells(lcnt, 4) = bf.GetDataHex(4)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = “[カラーパレット]"
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(CLng(“&H" & bf.ConvertEndian(wOff)) – 54), 8)
- bf.SkipData (CLng(“&H" & bf.ConvertEndian(wOff)) – 54)
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = “[ビットマップデータ]"
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = bf.GetPositionHex
- sht.Cells(lcnt, 2) = Right(“0000000" & Hex(CLng(“&H" & bf.ConvertEndian(wLen))), 8)
- bf.SkipData (CLng(“&H" & bf.ConvertEndian(wLen)))
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = “[ファイル終端]"
- lcnt = lcnt + 1
- sht.Cells(lcnt, 1) = Right(“0000000" & Hex(bf.CurrentPosition – 1), 8)
- Set bf = Nothing
- End Sub
実行結果(イメージ)
8bitのBMPファイルに対して前項のExcelツールを実行すると、下図のような結果が表示されます。
オフセット | 長さ | 項目 | 値 |
---|---|---|---|
[ファイルヘッダ] | |||
00000000 | 00000002 | ファイルタイプ | 424D |
00000002 | 00000004 | ファイルサイズ | 36D40200 |
00000006 | 00000002 | 予約領域1 | 0000 |
00000008 | 00000002 | 予約領域2 | 0000 |
0000000A | 00000004 | ビットマップデータのオフセット | 36040000 |
[情報ヘッダ] | |||
0000000E | 00000004 | ヘッダサイズ | 28000000 |
00000012 | 00000004 | 画像の幅 | 80010000 |
00000016 | 00000004 | 画像の高さ | E0010000 |
0000001A | 00000002 | プレーン数 | 0100 |
0000001C | 00000002 | 1ピクセルあたりのビット数 | 0800 |
0000001E | 00000004 | 圧縮形式 | 00000000 |
00000022 | 00000004 | 画像データサイズ | 00D00200 |
00000026 | 00000004 | 水平解像度 | 00000000 |
0000002A | 00000004 | 垂直解像度 | 00000000 |
0000002E | 00000004 | 使用色数 | 00000000 |
00000032 | 00000004 | 重要色数 | 00000000 |
[カラーパレット] | |||
00000036 | 00000400 | ||
[ビットマップデータ] | |||
00000436 | 0002D000 | ||
[ファイル終端] | |||
0002D435 |
■8ビットのBMPファイルを32ビットのBMPファイルに変換する処理
8ビットBMPファイルを読み込み、32ビットのBMPファイルに変換するプログラムです。
画質が変らないにも拘らず、ファイルのサイズが大きくなるので、全く意味のないプログラムですが、BMPファイルに関する理解を深めるためのエクササイズとして掲載します。
⇒サンプル・プログラムで使用している、Binary Fileクラスの詳細については、Excel VBAでファイル解析(事前準備編)を参照して下さい。
処理の概要
処理の流れは以下の通りです。
①Binary Fileオブジェクトをインスタンス化し、BMPファイルを読み込み
②BMPファイルのヘッダをコピーしたうえ、ビットマップデータの値をカラーパレットの内容に置換えてファイルに出力
③使用済のオブジェクトを破棄
サンプル・プログラム
行番号10でBinary Fileオブジェクトをインスタンス化し、行番号11でBMPファイルを読み込んでいます。
行番号12で入力ファイルのサイズを取得し、行番号13で変換後の画像データサイズを求め、行番号14で出力ファイルのサイズを算出して、行番号15で出力エリアを確保しています。
行番号16~18で「ファイルヘッダ」と「情報ヘッダ」を出力エリアにコピーし、行番号19~23で「カラーパレット」を退避しています。
行番号25~40は出力エリアのヘッダ情報を更新する処理になっており、行番号25~28が「ファイルサイズ」、行番号29~32が「ビットマップデータのオフセット」、行番号33~36が「1ピクセルあたりのビット数」、行番号37~40が「画像データサイズ」に関する処理です。
行番号43~50は「ビットマップデータ」の変換処理になっており、元データのカラーパレット番号を退避したカラーパレット情報のRGB値(RGBQUAD)に置換えています。
- Dim bf As Object
- Dim ColorPalette(0 To 255, 0 To 3) As Byte
- Dim OUTlen As Long
- Dim OUTnum As Integer
- Dim OUTbuf() As Byte
- Dim wStr As String
- Dim wSize, wLen, i, j As Long
- Private Sub Sample2()
- Set bf = New BinaryFile
- bf.InputFile (“C:\work\xxx-8bit.bmp")
- wLen = bf.FileSize
- wSize = (wLen – (14 + 40 + 1024)) * 4
- OUTlen = 14 + 40 + wSize
- ReDim OUTbuf(0 To OUTlen – 1)
- For i = 0 To 53
- OUTbuf(i) = bf.GetData(1)
- Next i
- For i = 0 To 255
- For j = 0 To 3
- ColorPalette(i, j) = bf.GetData(1)
- Next j
- Next i
- wStr = bf.ConvertEndian(Right(“0000000" & Hex(OUTlen), 8))
- For i = 2 To 5
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 1) * 2 – 1, 2))
- Next i
- wStr = bf.ConvertEndian(Right(“0000000" & Hex(54), 8))
- For i = 10 To 13
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 9) * 2 – 1, 2))
- Next i
- wStr = bf.ConvertEndian((Right(“000" & Hex(32), 4)))
- For i = 28 To 29
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 27) * 2 – 1, 2))
- Next i
- wStr = bf.ConvertEndian(Right(“0000000" & Hex(wSize), 8))
- For i = 34 To 37
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 33) * 2 – 1, 2))
- Next i
- i = 54
- Do While bf.CurrentPosition < bf.FileSize – 1
- j = bf.GetData(1)
- OUTbuf(i) = ColorPalette(j, 0)
- OUTbuf(i + 1) = ColorPalette(j, 1)
- OUTbuf(i + 2) = ColorPalette(j, 2)
- OUTbuf(i + 3) = ColorPalette(j, 3)
- i = i + 4
- Loop
- OUTnum = FreeFile
- Open “C:\work\xxx-32bit.bmp" For Binary Access Write As #OUTnum
- Put #OUTnum, , OUTbuf
- Close #OUTnum
- Set bf = Nothing
- End Sub
実行結果(イメージ)
32bitのBMPファイルに対して前掲のExcelツールを実行すると、下図のような結果が表示されます。
オフセット | 長さ | 項目 | 値 |
---|---|---|---|
[ファイルヘッダ] | |||
00000000 | 00000002 | ファイルタイプ | 424D |
00000002 | 00000004 | ファイルサイズ | 36400B00 |
00000006 | 00000002 | 予約領域1 | 0000 |
00000008 | 00000002 | 予約領域2 | 0000 |
0000000A | 00000004 | ビットマップデータのオフセット | 36000000 |
[情報ヘッダ] | |||
0000000E | 00000004 | ヘッダサイズ | 28000000 |
00000012 | 00000004 | 画像の幅 | 80010000 |
00000016 | 00000004 | 画像の高さ | E0010000 |
0000001A | 00000002 | プレーン数 | 0100 |
0000001C | 00000002 | 1ピクセルあたりのビット数 | 2000 |
0000001E | 00000004 | 圧縮形式 | 00000000 |
00000022 | 00000004 | 画像データサイズ | 00400B00 |
00000026 | 00000004 | 水平解像度 | 00000000 |
0000002A | 00000004 | 垂直解像度 | 00000000 |
0000002E | 00000004 | 使用色数 | 00000000 |
00000032 | 00000004 | 重要色数 | 00000000 |
[カラーパレット] | |||
00000036 | 00000000 | ||
[ビットマップデータ] | |||
00000036 | 000B4000 | ||
[ファイル終端] | |||
000B4035 |
■8ビットのBMPファイルを24ビットのBMPファイルに変換する処理
8ビットBMPファイルを読み込み、24ビットのBMPファイルに変換するプログラムです。
くどいようですが、前項に引続き、BMPファイルに関する理解を深めるためのエクササイズです。
⇒サンプル・プログラムで使用している、Binary Fileクラスの詳細については、Excel VBAでファイル解析(事前準備編)を参照して下さい。
処理の概要
処理の流れは以下の通りです。
①Binary Fileオブジェクトをインスタンス化し、BMPファイルを読み込み
②BMPファイルのヘッダをコピーしたうえ、ビットマップデータの値をカラーパレットの内容に置換えてファイルに出力
③使用済のオブジェクトを破棄
サンプル・プログラム
行番号10でBinary Fileオブジェクトをインスタンス化し、行番号11でBMPファイルを読み込んでいます。
行番号12で入力ファイルのサイズを取得し、行番号13で変換後の画像データサイズを求め、行番号14で出力ファイルのサイズを算出して、行番号15で出力エリアを確保しています。
行番号16~18で「ファイルヘッダ」と「情報ヘッダ」を出力エリアにコピーし、行番号19~24で「カラーパレット」を退避しています。
行番号26~41は出力エリアのヘッダ情報を更新する処理になっており、行番号26~29が「ファイルサイズ」、行番号30~33が「ビットマップデータのオフセット」、行番号34~37が「1ピクセルあたりのビット数」、行番号38~41が「画像データサイズ」に関する処理です。
行番号44~50は「ビットマップデータ」の変換処理になっており、元データのカラーパレット番号を退避したカラーパレット情報のRGB値(RGBTRIPLE)に置換えています。
- Dim bf As Object
- Dim ColorPalette(0 To 255, 0 To 2) As Byte
- Dim OUTlen As Long
- Dim OUTnum As Integer
- Dim OUTbuf() As Byte
- Dim wStr As String
- Dim wSize, wLen, i, j As Long
- Private Sub Sampl3()
- Set bf = New BinaryFile
- bf.InputFile (“C:\work\xxx-8bit.bmp")
- wLen = bf.FileSize
- wSize = (wLen – (14 + 40 + 1024)) * 3
- OUTlen = 14 + 40 + wSize
- ReDim OUTbuf(0 To OUTlen – 1)
- For i = 0 To 53
- OUTbuf(i) = bf.GetData(1)
- Next i
- For i = 0 To 255
- For j = 0 To 2
- ColorPalette(i, j) = bf.GetData(1)
- Next j
- bf.SkipData (1)
- Next i
- wStr = bf.ConvertEndian(Right(“0000000" & Hex(OUTlen), 8))
- For i = 2 To 5
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 1) * 2 – 1, 2))
- Next i
- wStr = bf.ConvertEndian(Right(“0000000" & Hex(54), 8))
- For i = 10 To 13
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 9) * 2 – 1, 2))
- Next i
- wStr = bf.ConvertEndian((Right(“000" & Hex(24), 4)))
- For i = 28 To 29
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 27) * 2 – 1, 2))
- Next i
- wStr = bf.ConvertEndian(Right(“0000000" & Hex(wSize), 8))
- For i = 34 To 37
- OUTbuf(i) = CByte(“&H" & Mid(wStr, (i – 33) * 2 – 1, 2))
- Next i
- i = 54
- Do While bf.CurrentPosition < bf.FileSize – 1
- j = bf.GetData(1)
- OUTbuf(i) = ColorPalette(j, 0)
- OUTbuf(i + 1) = ColorPalette(j, 1)
- OUTbuf(i + 2) = ColorPalette(j, 2)
- i = i + 3
- Loop
- OUTnum = FreeFile
- Open “C:\work\xxx-24bit.bmp" For Binary Access Write As #OUTnum
- Put #OUTnum, , OUTbuf
- Close #OUTnum
- Set bf = Nothing
- End Sub
実行結果(イメージ)
24bitのBMPファイルに対して前掲のExcelツールを実行すると、下図のような結果が表示されます。
オフセット | 長さ | 項目 | 値 |
---|---|---|---|
[ファイルヘッダ] | |||
00000000 | 00000002 | ファイルタイプ | 424D |
00000002 | 00000004 | ファイルサイズ | 36700800 |
00000006 | 00000002 | 予約領域1 | 0000 |
00000008 | 00000002 | 予約領域2 | 0000 |
0000000A | 00000004 | ビットマップデータのオフセット | 36000000 |
[情報ヘッダ] | |||
0000000E | 00000004 | ヘッダサイズ | 28000000 |
00000012 | 00000004 | 画像の幅 | 80010000 |
00000016 | 00000004 | 画像の高さ | E0010000 |
0000001A | 00000002 | プレーン数 | 0100 |
0000001C | 00000002 | 1ピクセルあたりのビット数 | 1800 |
0000001E | 00000004 | 圧縮形式 | 00000000 |
00000022 | 00000004 | 画像データサイズ | 00700800 |
00000026 | 00000004 | 水平解像度 | 00000000 |
0000002A | 00000004 | 垂直解像度 | 00000000 |
0000002E | 00000004 | 使用色数 | 00000000 |
00000032 | 00000004 | 重要色数 | 00000000 |
[カラーパレット] | |||
00000036 | 00000000 | ||
[ビットマップデータ] | |||
00000036 | 00087000 | ||
[ファイル終端] | |||
00087035 |
出版社:インプレス
発売日:2022/3/23
単行本(ソフトカバー):A5判/912ページ
出版社:技術評論社
発売日:2021/1/9
単行本(ソフトカバー):A5判/800ページ
出版社:技術評論社
発売日:2019/11/25
単行本(ソフトカバー):B5変形判/576ページ
ディスカッション
コメント一覧
まだ、コメントがありません