PIL(pillow)画像をPython配列に変換してみる
画像オブジェクトを配列に変換
画像読み込み
from PIL import Image im = Image.open('sample20x15.bmp') im
PIL画像データ配列構造
左上を原点とするXY座標(Xは、右側に進む。Yは下側に進む)とすると横書きのノートのように上から左から右に端まで行ったら次の行まで行きY=0から1にしてX=0に戻りまた左から右にというデータ配列でデータをImage.tobytes()
とImage.frombytes(mode, size, data
でデータのやり取りができる
data = im.tobytes() print("データの種類 = ",type(data)) print("データの長さ = ",len(data)) print("データの単位 = ",type(data[0])) print("画像モード = ",im.mode) print("幅、高さ、チャンネル数 = ",im.size[0],im.size[1],len(im.getpixel((0,0)))) print("幅x高さxチャンネル数 = ",im.size[0]*im.size[1]*len(im.getpixel((0,0))))
データの種類 = <class 'bytes'>
データの長さ = 900
データの単位 = <class 'int'>
画像モード = RGB
幅、高さ、チャンネル数 = 20 15 3
幅x高さxチャンネル数 = 900
RGBからBGRの並びに変えてみる
data2 = bytearray() for xy in range(im.size[0]*im.size[1]): data2 += bytearray( (data[3*xy+2],data[3*xy+1],data[3*xy])) print("RGB(0,0) = ",list(data[0:3])) print("BGR(0,0) = ",list(data2[0:3]))
RGB(0,0) = [200, 36, 179]
BGR(0,0) = [179, 36, 200]
byte配列の並びが変換できた
im1 = Image.frombytes('RGB',(20,15),data) im1
バイト列から画像オブジェクトに戻った
im2 = Image.frombytes('RGB',(20,15),bytes(data2)) im2
R⇔B入れ替えたデータで画像オブジェクトにした
def getBytes2pixel(im,xy): x,y = xy w,h = im.size dat = im.tobytes() print(xy," = ",dat[3*(w*y+x)],dat[3*(w*y+x)+1],dat[3*(w*y+x)+2]) print("--im1--") getBytes2pixel(im1,(0,0)) getBytes2pixel(im1,(19,14)) print("--im2--") getBytes2pixel(im2,(0,0)) getBytes2pixel(im2,(19,14))
--im1--
(0, 0) = 200 36 179
(19, 14) = 49 53 230
--im2--
(0, 0) = 179 36 200
(19, 14) = 230 53 49
pythonのbytes,bytearrayを使って画像オブジェクトは作成できる
listとも互換なので普通の配列として使える。ただし、byte単位なので0~255のRGBや、L(グレー)やP(パレット)でしかできない? I(32int)やF(32float)の場合どうなる?
I(32int)やF(32float)の場合
import struct im_i32 = Image.new('I',(20,15)) im_i32.putpixel((0,0),1) # (0,0) =1 im_i32.putpixel((2,0),-1) # (2,0) = -1 符号表記 print("mode = ",im_i32.mode) print("size = ",im_i32.size) print("getbands = ",im_i32.getbands()) print("pixel(0,0) = ",im_i32.getpixel((0,0))) print("pixel(0,0) = ",type(im_i32.getpixel((0,0)))) data_i32 = im_i32.tobytes() print("データの種類 = ",type(data_i32)) print("データの長さ = ",len(data_i32)) print("データの単位 = ",type(data_i32[0])) print("データ始めの16bytes = ",data_i32[0:16].hex()) print("int32にデコード = ", struct.unpack('iiii',data_i32[0:16]))
mode = I
size = (20, 15)
getbands = ('I',)
pixel(0,0) = 1
pixel(0,0) = <class 'int'>
データの種類 = <class 'bytes'>
データの長さ = 1200
データの単位 = <class 'int'>
データ始めの16bytes = 0100000000000000ffffffff00000000
int32にデコード = (1, 0, -1, 0)
im_f32 = Image.new('F',(20,15)) im_f32.putpixel((0,0),1) # (0,0) =1 im_f32.putpixel((2,0),-1) # (2,0) = -1 符号表記 im_f32.putpixel((3,0),0.003) # (3,0) = 0.003 小数点 im_f32.putpixel((4,0),1.234e5) # (4,0) = 1.234e5 指数表示 print("mode = ",im_f32.mode) print("size = ",im_f32.size) print("getbands = ",im_f32.getbands()) print("pixel(0,0) = ",im_f32.getpixel((0,0))) print("pixel(0,0) = ",type(im_f32.getpixel((0,0)))) data_f32 = im_f32.tobytes() print("データの種類 = ",type(data_f32)) print("データの長さ = ",len(data_f32)) print("データの単位 = ",type(data_f32[0])) print("データ始めの32bytes = ",data_f32[0:32].hex()) print("float32にデコード = ", struct.unpack('ffffffff',data_f32[0:32]))
mode = F
size = (20, 15)
getbands = ('F',)
pixel(0,0) = 1.0
pixel(0,0) = <class 'float'>
データの種類 = <class 'bytes'>
データの長さ = 1200
データの単位 = <class 'int'>
データ始めの32bytes = 0000803f00000000000080bfa69b443b0004f147000000000000000000000000
float32にデコード = (1.0, 0.0, -1.0, 0.003000000026077032, 123400.0, 0.0, 0.0, 0.0)
WxHx(CHx32bit/8bit)
か20*15*1*32/8=1200
でIもFも1200bytesなのね。
並び順序は他と同じでピクセルデータがint32とfloatになっているということね
デコードして普通の配列にして処理かけてstruct.pack
かければもとに戻せるかな
おさらいで内部表現しらべた
64bit長のデータって改めてみると実に大きいのね。大きいといえば天文単位
天文単位1auは、149,597,870,700 mみたいだけど64bit長じゃないとメートル換算できないのか
GPSシステムとかの細かい計算とかに使うのかな?
32bit整数(int,uint)
符号付き -2,147,483,648 ~ 2,147,483,647
符号なし 0 ~ 4,294,967,295
64bit整数(long,ulong)
符号付き -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807
符号なし 0 ~ 18,446,744,073,709,551,615
32bit浮動小数点値(float) 単精度浮動小数点数
64bit浮動小数点値(double) 倍精度浮動小数点数
128bit浮動小数点値(decimal) 四倍精度浮動小数点数