画像データの扱い方についてのメモということで、”入門以前” としました。
僕の場合はまずByteArrayについて調べるところから始めないと。。
扱う画像について
カラー(RGB各チャンネル8ビット)のraw画像を扱います。
(RAW画像ではなくベタ画像の方)
よって画像データは R0G0B0 R1G1B1 R2G2B2 ・・・の順で格納されているものとします。
画像データの扱い
ByteArray上の画像データを参照する場合、
・画像の横幅 (width)
・1画素あたりのバイト数 (bytesPerPixel)
の2つは最低限必要な情報です。
ここではこれらの情報は既知であるものとします。
(厳密にはチャンネル数も必要ですが、ここでは条件より3で固定されます)
ByteArrayに格納された画像データにおいて座標(x,y)の各色成分を取得するには、
1 2 3 |
img[width*bytesPerPixel*y + bytesPerPixel*x] //R img[width*bytesPerPixel*y + bytesPerPixel*x + 1] //G img[width*bytesPerPixel*y + bytesPerPixel*x + 2] //B |
のように書きます。
二次元の画像を一次元のバイト配列で扱っているという事を意識すると理解が早まるかと。
BitmapDataとByteArrayのそれぞれにおいて座標(x,y)の画素値を取得するには、
BitmapDataの場合
1 2 |
/* imgはBitmapData */ var pixelA:uint = img.getPixel(x,y); |
ByteArrayの場合
1 2 |
/* imgはByteArray */ var pixelB:uint = img[width*bytesPerPixel*y + bytesPerPixel*x] << 16 | img[width*bytesPerPixel*y + bytesPerPixel*x + 1] << 8 | img[width*bytesPerPixel*y + bytesPerPixel*x + 2]; |
pixelAとpixelBは同じ値になります。
同様に、座標(x,y)の画素値を黒色(0x000000)に変更するには、
BitmapDataの場合
1 2 |
/* imgはBitmapData */ img.setPixel(x,y,0x000000); |
ByteArrayの場合
1 2 3 4 5 6 7 8 9 10 11 12 |
/* imgはByteArray */ img[width*bytesPerPixel*y + bytesPerPixel*x] = 0; img[width*bytesPerPixel*y + bytesPerPixel*x + 1] = 0; img[width*bytesPerPixel*y + bytesPerPixel*x + 2] = 0; /* ByteArrayがバイト配列であるということを意識すれば、 以下の記述で上と同じ処理をしていることが理解できると思います。 (ただし、この場合は処理がただ遅くなるだけですが;) */ img[width*bytesPerPixel*y + bytesPerPixel*x] >>= 8; img[width*bytesPerPixel*y + bytesPerPixel*x + 1] >>= 8; img[width*bytesPerPixel*y + bytesPerPixel*x + 2] >>= 8; |
このように、C/C++でポインタを使って画像データへ直接アクセスするのと似た感覚で書けます。
ただ、ASにunsigned charのような1バイトの型がないのが惜しいところです。
ここで注意しないといけないのは、BitmapData.getPixel()では画像の境界外を参照すると0が返ってきますが、ByteArrayで画像データを扱う場合、座標の範囲チェックはプログラマの責任になります。
画像の境界外を参照した場合の処理は次の3つの方法が一般的です。
1. 境界外の画素値は固定値で構成されていると考える (多くの場合、黒か白)
2. 参照された座標に最も近い境界内の画素を参照する
3. 境界外には鏡像イメージがあるものと仮定して画素を参照する
BitmapData.getPixel()の場合は、一番エコな1.の方法を採用しているようです。
今回は画像データの参照方法までを扱いました。
次はなにしよう。
3 Thoughts