UWSCで作業を効率化しよう【画像の中心座標取得編】

UWSC

UWSCでは画像のサイズなど画像に関する情報を取得する関数がないため自分で作成しようと思います。

とりあえず今回は、画像の中心座標を計算してマウスを移動させて見ようと思います!

UWSCで扱える画像のフォーマットについて

  • UWSCでは「ビットマップ形式」で画像を扱うことができます。
  • 拡張子は「bmp

ビットマップファイルの構造

ビットマップファイルは先頭から

  1. ファイルヘッダ
  2. 情報ヘッダ
  3. カラーマスク
  4. カラーパレット
  5. ビットマップデータ
  6. カラープロファイル

の順で構成されています。
画像サイズとかは「情報ヘッダ」に格納されているのでそこから取得していきたいと思います!

情報ヘッダの構成

wikipediaより引用

オフセットサイズ格納する情報値・備考
0x000e4バイトヘッダサイズ40
0x00124バイトビットマップの横幅単位はピクセル
0x00164バイトビットマップの縦幅単位はピクセル。値が負の場合はトップダウン画像となる
0x001a2バイトプレーン数常に1
0x001c2バイト1ピクセルあたりのビット数0,1,4,8,16,24,32
0x001e4バイト圧縮形式0,1,2,3,4,5 ※1
0x00224バイト画像データサイズ単位はバイト
0x00264バイト水平方向の解像度単位はピクセル/m
0x002a4バイト垂直方向の解像度単位はピクセル/m
0x002e4バイト使用する色数ビットマップで実際に使用するカラーパレット内のカラーインデックスの数。
0x00324バイト重要な色数ビットマップを表示するために必要なカラーインデックスの数。

ちなみにここで言うオフセットというのは16進数で表された先頭から数えたバイト数のことです。
例えば、ヘッダサイズの「0x000e」なら先頭から15バイト目ということになります!

実際に覗いてみる!

今回はこの画像について扱っていきたいと思います!

f:id:reishisu:20180715133411j:plain

画像のサイズは「400×400」となっております。

まずは、バイナリエディタで画像を開いてほんとに情報があるか見てみます!

f:id:reishisu:20180715135115p:plain
つまり、前半の26byteさえ取得できれば画像のサイズは取得できるのです!!!!

画像から「え?400なんてね~よ!!」と怒る人もいると思います?
ちょっと待ってください!なぜ400じゃないのか!?
なぜなら、これが16進数で表されているからです!

ちなみに、「90 01」と書かれていますが左から1桁であらわされているので実際は「01 90」を10進数に直して普段扱っている数字に直してあげます。
16進数から→10進数の計算方法は右から「16^(桁数-1)×数字」をそれぞれ計算して最後に全部足し合わせたものになります。
なので、この場合は「0190」で先頭の0は計算しても0なので「190」を計算します。右から
「16^(1-1) * 0」=「1×0」=0
「16^(2-1) * 9」=「16×9」=144
「16^(3-1) * 1」=「256×1」=256
となり、全部足すと「0+144+256 = 400」となりしっかりとサイズを確認できてると思います!

ちなみに、Windows10標準の電卓をプログラマーモードにして「Hex」の所に計算したい16進数の値を入れても計算することができます!

f:id:reishisu:20180715141030p:plain

UWSCで実現するには?

まずは、どの画像を読み込むか教えるための画像のパスを用意します。
例えば、Cドライブ直下の「野獣先輩.bmp」という画像を読み込むなら

ImageFilePath = "C:/野獣先輩.bmp"

これで、「ImageFilePath」に画像までのパスが入りました!

画像のサイズを大きさを保存する配列を作成します。

Dim WHSize[1]

次に画像をバイナリ形式で扱えるCOMオブジェクトを用意します。

stream = CreateOleObj("ADODB.Stream")

これで、バイナリデータを扱ってくれる「stream」が準備できました!!

次に、実際に読み込んでもらうためにstreamを開いてあげましょう。

stream.Open()

これで、streamがいつでもデータを受け入れるようになりました!!

次に、デフォルトではテキストファイルとして読み込んでしまうのでしっかりとバイナリデータを扱うようにstreamに教えてあげましょう。

stream.Type = 1

ちなみに、1が「バイナリデータ」で2が「テキストデータ」として明示的に指定することができます。
これでstreamがファイルをバイナリデータとして扱えるようになりました!!

では、実際にstreamを使って画像をバイナリデータとして読み込んでみましょう!

stream.LoadFromFile(ImageFilePath)

これでエラーが出なければstreamに画像のバイナリデータが読み込まれているはずです!!

次に、読み込まれたバイナリデータを先頭から26byte取得しましょう!!

ImagePartBinary = stream.Read(26)

これでエラーが出なければstreamから先頭分のバイナリデータがImagePartBinaryに入ったはずです!!

もう、データを開き終わったのでstreamは閉じてあげましょう。

stream.Close()

ImagePartBinaryには嬉しいことにもともと16進数が10進数に変換されて1byteずつ配列に保存されています!!
今回ほしいのは、画像の中心の座標なので19バイト目から計算すればいいことになります。
なので19バイト目から26バイト目までをFor文を利用して横幅と縦幅を計算していこうと思います!!
計算を楽にするために計算を2バイトしか計算しない前提で処理を書いていきます。言い訳としては
超高精細映像の8Kでも8192×4320で
また、16Kの16384×8640でも
2バイトで表現できる65535×65535で十分足りるためですw
(64K以上だとちゃんと処理を書く必要がありますが)

// 配列の添え字は0から始まるため要素の19番目にアクセスするには
// ImagePartBinary[18]と書く
// Power(a, b)  aのb乗を返す
for i=18 to length(ImagePartBinary)-1
// 横幅の計算
if (i<22) then WHSize[0] = WHSize[0] + ImagePartBinary[i]*Power(256, i-18)
// 縦幅を計算
if (i>21) then WHSize[1] = WHSize[1] + ImagePartBinary[i]*Power(256, i-22)
next

1回目のループではPower(256, 0)となり配列の要素に影響はないのですが、
2回目のループではPower(256, 1)となり配列の要素に256をかけることで16進数の2桁目の処理を行います。

これで、WHSize[0]には横幅が入って、WHSize[1]に縦幅が入りました!!

使いやすくするために関数にしましょう!

上の処理だと、毎度毎度この処理を書くのは面倒なのでパス名を引数にとって大きさを配列で返す関数を作成しましょう!
解説はソースコードのコメントに書きます。

実際に作った関数を利用して画像の中心にマウスを移動させよう!!

これで画像の中心座標を無事計算することができました!!

お疲れ様でした!!

実行したソースコードなど

使用したファイルなどは、こちらからDLして実行できますので良かったらどうぞ。
github.com

コメント

タイトルとURLをコピーしました