PIL(pillow)画像をPython配列に変換してみる

画像オブジェクトを配列に変換

画像読み込み

from PIL import Image
im = Image.open('sample20x15.bmp')
im

f:id:chiyoh:20190504095104p:plain

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

f:id:chiyoh:20190504095100p:plain

バイト列から画像オブジェクトに戻った

im2 = Image.frombytes('RGB',(20,15),bytes(data2))
im2

f:id:chiyoh:20190504095058p:plain

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) 四倍精度浮動小数点数