PIL(pillow)画像をnumpyにしたり戻したり
画像オブジェクトをnumpy配列に変換
import numpy as np from PIL import Image im = Image.open('sample20x15.bmp') im
im_paltte = im.quantize() # パレットカラー 1chカラー im_CMYK = im.convert('CMYK') # CMYK 4chカラー im_grey = im.convert('L') # グレースケール 1chカラー im_bw = im.convert('1') # 白黒 1chカラー im_i = im.convert('I') # どのように変換されるかわからんが int型に変換 im_f = im.convert('F') # どのように変換されるかわからんが float型に変換 print("--RGB--") print("画像モード = ",im.mode) print("省略") print("--paltte--") print("画像モード = ",im_paltte.mode) print("省略") print("--CMYK--") print("画像モード = ",im_CMYK.mode) print("画像サイズ(x,y) = ",im_CMYK.size) print("pixel格納形式 = ",im_CMYK.getbands()) print("pixel(0,0) = ",im_CMYK.getpixel((0,0))) print("pixel(0,0)の形式 = ",type(im_CMYK.getpixel((0,0)))) print("各チャンネルの形式 = ",type(im_CMYK.getpixel((0,0))[0]),type(im_CMYK.getpixel((0,0))[1]), type(im_CMYK.getpixel((0,0))[2]),type(im_CMYK.getpixel((0,0))[3])) print("--grey--") print("画像モード = ",im_grey.mode) print("画像サイズ(x,y) = ",im_grey.size) print("pixel格納形式 = ",im_grey.getbands()) print("pixel(0,0) = ",im_grey.getpixel((0,0))) print("pixel(0,0)の形式 = ",type(im_grey.getpixel((0,0)))) print("各チャンネルの形式 = ",type(im_grey.getpixel((0,0)))) # 1chなのでそのまま print("--binary--") print("画像モード = ",im_bw.mode) print("画像サイズ(x,y) = ",im_bw.size) print("pixel格納形式 = ",im_bw.getbands()) print("pixel(0,0) = ",im_bw.getpixel((0,0))) print("pixel(0,0)の形式 = ",type(im_bw.getpixel((0,0)))) print("各チャンネルの形式 = ",type(im_bw.getpixel((0,0)))) # 1chなのでそのまま print("--int--") print("画像モード = ",im_i.mode) print("省略") print("--float--") print("画像モード = ",im_f.mode) print("省略")
--RGB--
画像モード = RGB
省略
--paltte--
画像モード = P
省略
--CMYK--
画像モード = CMYK
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('C', 'M', 'Y', 'K')
pixel(0,0) = (55, 219, 76, 0)
pixel(0,0)の形式 = <class 'tuple'>
各チャンネルの形式 = <class 'int'> <class 'int'> <class 'int'> <class 'int'>
--grey--
画像モード = L
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('L',)
pixel(0,0) = 101
pixel(0,0)の形式 = <class 'int'>
各チャンネルの形式 = <class 'int'>
--binary--
画像モード = 1
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('1',)
pixel(0,0) = 0
pixel(0,0)の形式 = <class 'int'>
各チャンネルの形式 = <class 'int'>
--int--
画像モード = I
省略
--float--
画像モード = F
省略
読み込んだ画像RGBオブジェクトを各モードの画像に変換した。いままで見てなかったCMYKとLグレスケと1白黒については内部データも確認しておく
numpy配列に変換
dat_RGB = np.asarray(im) dat_paltte = np.asarray(im_paltte) dat_CMYK = np.asarray(im_CMYK) dat_grey = np.asarray(im_grey) dat_bw = np.asarray(im_bw) dat_i = np.asarray(im_i) dat_f = np.asarray(im_f)
dat_paltte += dat_paltte/2
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-4-d0f6116c4334> in <module>
----> 1 dat_paltte += dat_paltte/2
ValueError: output array is read-only
書き換えNGのようだ
dat_paltte.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : False
WRITEABLE : False
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
dat_paltte.flags.writeable = True dat_paltte += dat_paltte/2
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-6-612dcc4be249> in <module>
----> 1 dat_paltte.flags.writeable = True
2 dat_paltte += dat_paltte/2
ValueError: cannot set WRITEABLE flag to True of this array
なるほど、np.asarray()で変換した配列は参照ようなのね
np.array()を使って変換すると
dat2_paltte = np.array(im_paltte) dat2_paltte.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
np.asarray()
で変換すると配列書き換え不可、np.array()
で行えば書き換え可能
だけど大体numpyの場合演算すると新しいオブジェクト生成されてしまうのでnp.asarray()
で変換して速度優先にするって感じか
numpy配列に変換した中身
print("--RGB--") print("オブジェクト型 = ",type(dat_RGB)) print("配列要素 = ",dat_RGB.dtype) print("配列形 = ",dat_RGB.shape) print("配列要素サイズ = ",dat_RGB.size) print("シリーズに並べた時の隣軸との距離(Y軸,X軸,ch間) = ",dat_RGB.strides) print("次元数 = ",dat_RGB.ndim) print("1つの配列要素の長さ(バイト単位) = ",dat_RGB.itemsize) print("使用されている総バイト数 = ",dat_RGB.nbytes) print("メモリレイアウト情報 = ",dat_RGB.flags)
--RGB--
オブジェクト型 = <class 'numpy.ndarray'>
配列要素 = uint8
配列形 = (15, 20, 3)
配列要素サイズ = 900
シリーズに並べた時の隣軸との距離(Y軸,X軸,ch間) = (60, 3, 1)
次元数 = 3
1つの配列要素の長さ(バイト単位) = 1
使用されている総バイト数 = 900
メモリレイアウト情報 = C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : False
WRITEABLE : False
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
基本のRGBを見るとRGBの各チャンネルに1軸を使っていて、3次元配列になっているのね
print("ベースになるオブジェクト = ",dat_RGB.base) print("len(base) = ",len(dat_RGB.base)) print("配列の先頭のオブジェクト = ",dat_RGB.data) print("type(data) = ",type(dat_RGB.data)) print("len(data) = ",len(dat_RGB.data))
ベースになるオブジェクト = b"\xc8$\xb3\xc5%\xb5\xc0%\xb6\xbc%\xb7\xb8&\xb9\xb3&\xbb\xad'\xbc\xa9(\xbe\xa4(\xbf\x9e)\xc2\x99)\xc4\x94*\xc5\x8e+\xc7\x89+\xc9\x84,\xcb~,\xccx-\xcfs.\xd0n.\xd2h.\xd4\xc5$\xb4\xc1$\xb6\xbd%\xb7\xb8&\xb9\xb3'\xba\xae'\xbb\xa9(\xbe\xa4(\xbf\x9f(\xc1\x9a)\xc3\x94*\xc5\x90*\xc7\x8a+\xc8\x85+\xca\x7f,\xcdz-\xcet-\xd0n.\xd2i/\xd4c/\xd6\xc2$\xb5\xbe%\xb6\xb9&\xb8\xb5&\xba\xaf'\xbc\xaa(\xbd\xa6(\xbf\xa1(\xc1\x9c)\xc2\x96*\xc4\x90+\xc6\x8b+\xc8\x86+\xca\x80,\xcc{-\xceu-\xd0p.\xd1j.\xd4e/\xd5_0\xd7\xbe%\xb6\xba%\xb8\xb5&\xba\xb1'\xbb\xac'\xbc\xa7'\xbf\xa2)\xc0\x9c)\xc2\x97*\xc4\x91*\xc6\x8c*\xc8\x86+\xca\x81,\xcc|,\xcdv-\xcfq.\xd2k.\xd3f/\xd5a0\xd7\\0\xd8\xbb%\xb8\xb6&\xba\xb2'\xbb\xac'\xbd\xa8'\xbe\xa3(\xc0\x9d(\xc2\x98)\xc3\x92*\xc5\x8e+\xc7\x87+\xc9\x83,\xcb}-\xcdw.\xcfr-\xd0l.\xd3h/\xd5a0\xd6]1\xd8W1\xda\xb7&\xb9\xb3'\xbb\xad&\xbc\xa9'\xbe\xa4(\xc0\x9e(\xc1\x99*\xc3\x93*\xc5\x8f*\xc7\x89+\xc9\x83,\xcb~,\xccy-\xcfs.\xd0n/\xd2h.\xd4b/\xd6^0\xd7X0\xd9S1\xdb\xb4&\xba\xaf'\xbc\xaa'\xbe\xa4(\xbf\xa0)\xc2\x9a)\xc3\x95*\xc5\x8f+\xc6\x8a+\xc9\x85,\xca\x7f,\xccz-\xcet-\xd0o.\xd2j/\xd3d/\xd5_0\xd7Y0\xdaT1\xdbO2\xdd\xb0'\xbb\xab'\xbd\xa5'\xbf\xa0)\xc0\x9c)\xc2\x96)\xc4\x90*\xc6\x8c*\xc8\x85+\xca\x80,\xcc{-\xceu-\xcfp.\xd1k/\xd3e0\xd5_0\xd7[0\xd9U1\xdaP1\xddK2\xde\xab'\xbd\xa6(\xbf\xa2(\xc0\x9d)\xc2\x97)\xc4\x92*\xc6\x8c+\xc8\x87+\xc9\x82,\xcc|,\xcdw-\xd0q.\xd1l/\xd3f/\xd5a0\xd7\\1\xd9W1\xdaQ2\xdcL2\xdeG3\xdf\xa8(\xbe\xa3)\xc0\x9e)\xc2\x99)\xc4\x92*\xc6\x8d+\xc8\x88+\xc9\x82,\xcb},\xcdx-\xcfr-\xd0m.\xd3g/\xd4b/\xd6]0\xd8X1\xd9R1\xdcM2\xddH2\xdfC3\xe1\xa4(\xbf\x9e(\xc1\x99*\xc3\x94*\xc6\x8f+\xc6\x89+\xc9\x84,\xca\x7f,\xcdy-\xces.\xd0n.\xd2h/\xd4c/\xd6^0\xd8X0\xd9S2\xdbN2\xddI2\xdfD3\xe0?3\xe3\x9f(\xc1\x9a)\xc3\x95*\xc5\x90+\xc7\x8b*\xc9\x85+\xca\x7f,\xccz-\xceu.\xd0o.\xd2j.\xd4d/\xd6_0\xd7Z0\xd9T1\xdbO2\xdcJ2\xdfE2\xe0A3\xe2<4\xe3\x9c)\xc2\x96*\xc4\x91*\xc6\x8c+\xc8\x86+\xca\x81,\xcc{-\xceu-\xcfq.\xd2j/\xd3f/\xd5`0\xd7Z1\xd8U1\xdaP2\xdcK2\xdeF3\xe0B3\xe1=4\xe384\xe5\x97)\xc4\x92*\xc6\x8d+\xc7\x87,\xca\x81,\xcb|-\xcdw-\xcfq.\xd1k/\xd3f/\xd5a/\xd7\\0\xd8V1\xdbR1\xdcL2\xdeG2\xdfB3\xe1>4\xe394\xe455\xe5\x93*\xc5\x8e+\xc7\x89,\xc9\x82,\xca},\xcdx-\xcer.\xd0m.\xd2h/\xd4b/\xd6]0\xd8X0\xdaR1\xdcN1\xddI2\xdfC3\xe1?3\xe3;4\xe465\xe615\xe6"
len(base) = 900
配列の先頭のオブジェクト = <memory at 0x000001AEDC7E67C8>
type(data) = <class 'memoryview'>
len(data) = 15
なるほど、baseはim.tobytes()
な感じかな。dataは列要素に入っている行要素が15という感じで行要素にはch要素の3があるって感じなのかな。
各要素にアクセス
print("pixel(0,0) = ",im.getpixel((0,0))) print("numpy配列(0,0) = ",dat_RGB[0,0]) print("pixel(1,0) = ",im.getpixel((1,0))) print("numpy配列(1,0) = ",dat_RGB[1,0]) print("numpy配列(0,1) = ",dat_RGB[0,1]) print("numpy配列(0,1,:) = ",dat_RGB[0,1,:]) print("numpy配列(0,1)R = ",dat_RGB[0,1,0]) print("numpy配列(0,1)G = ",dat_RGB[0,1,1]) print("numpy配列(0,1)B = ",dat_RGB[0,1,2]) print("numpy配列(Y軸=0) = ",dat_RGB[0])
pixel(0,0) = (200, 36, 179)
numpy配列(0,0) = [200 36 179]
pixel(1,0) = (197, 37, 181)
numpy配列(1,0) = [197 36 180]
numpy配列(0,1) = [197 37 181]
numpy配列(0,1,:) = [197 37 181]
numpy配列(0,1)R = 197
numpy配列(0,1)G = 37
numpy配列(0,1)B = 181
numpy配列(Y軸=0) = [[200 36 179]
[197 37 181]
[192 37 182]
[188 37 183]
[184 38 185]
[179 38 187]
[173 39 188]
[169 40 190]
[164 40 191]
[158 41 194]
[153 41 196]
[148 42 197]
[142 43 199]
[137 43 201]
[132 44 203]
[126 44 204]
[120 45 207]
[115 46 208]
[110 46 210]
[104 46 212]]
PILでは、(x,y)のタプルで表現していたがnumpy配列に変換すると[y,x,ch]
の順番になる。x,yの順番が逆なので注意
[y,x]
と表現すれば[R G B]
の3要素が表現できる。[0]
1次元表記すると(X軸とch)の要素すべてが表現できるのね
1チャンネルのパレットカラーの場合はどうか?
print("--paltte--") print("オブジェクト型 = ",type(dat_paltte)) print("配列要素 = ",dat_paltte.dtype) print("配列形 = ",dat_paltte.shape) print("配列要素サイズ = ",dat_paltte.size) print("シリーズに並べた時の隣軸との距離(Y軸,X軸,ch間) = ",dat_paltte.strides) print("次元数 = ",dat_paltte.ndim) print("1つの配列要素の長さ(バイト単位) = ",dat_paltte.itemsize) print("使用されている総バイト数 = ",dat_paltte.nbytes) print("メモリレイアウト情報 = ",dat_paltte.flags)
--paltte--
オブジェクト型 = <class 'numpy.ndarray'>
配列要素 = uint8
配列形 = (15, 20)
配列要素サイズ = 300
シリーズに並べた時の隣軸との距離(Y軸,X軸,ch間) = (20, 1)
次元数 = 2
1つの配列要素の長さ(バイト単位) = 1
使用されている総バイト数 = 300
メモリレイアウト情報 = C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : False
WRITEABLE : False
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
その他の画像
def print_np_type(dat): print("配列要素 = ",dat.dtype) print("次元数 = ",dat.ndim) print("配列形 = ",dat.shape) print("配列要素サイズ = ",dat.size) print("--CMYK--") print_np_type(dat_CMYK) print("--gray--") print_np_type(dat_grey) print("--白黒--") print_np_type(dat_bw) print("--int--") print_np_type(dat_i) print("--float--") print_np_type(dat_f)
--CMYK--
配列要素 = uint8
次元数 = 3
配列形 = (15, 20, 4)
配列要素サイズ = 1200
--gray--
配列要素 = uint8
次元数 = 2
配列形 = (15, 20)
配列要素サイズ = 300
--白黒--
配列要素 = bool
次元数 = 2
配列形 = (15, 20)
配列要素サイズ = 300
--int--
配列要素 = int32
次元数 = 2
配列形 = (15, 20)
配列要素サイズ = 300
--float--
配列要素 = float32
次元数 = 2
配列形 = (15, 20)
配列要素サイズ = 300
なるほど、色要素としてチャンネルがあるものは3次元、無いのは縦横の2次元
要素タイプは種別ごとに違う感じか
numpy配列からPIL(pillow)オブジェクトに
im = Image.frombytes()でなく、Image.fromarray()のほうがいろいろとケアしてくれるみたいです 配列情報からサイズや画像モードなんかを読み取ってくれるみたいです
im2_RGB = Image.fromarray(dat_RGB) im2_paltte = Image.fromarray(dat_paltte) im2_CMYK = Image.fromarray(dat_CMYK) im2_grey = Image.fromarray(dat_grey) im2_bw = Image.fromarray(dat_bw) im2_i = Image.fromarray(dat_i) im2_f = Image.fromarray(dat_f) def print_image_type(im): print("画像モード = ",im.mode) print("画像サイズ(x,y) = ",im.size) print("pixel格納形式 = ",im.getbands()) print("pixel(0,0) = ",im.getpixel((0,0))) print("pixel(0,0)の形式 = ",type(im.getpixel((0,0)))) def print_image_type2(im): print_image_type(im) print("pixel要素形式 = ",type(im.getpixel((0,0))[0])) print("--RGB--") print_image_type2(im2_RGB) print("--CMYK--") print_image_type2(im2_CMYK) print("--パレット--") print_image_type(im2_paltte) print("--グレスケ--") print_image_type(im2_grey) print("--白黒--") print_image_type(im2_bw) print("--int--") print_image_type(im2_i) print("--float--") print_image_type(im2_f)
--RGB--
画像モード = RGB
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('R', 'G', 'B')
pixel(0,0) = (200, 36, 179)
pixel(0,0)の形式 = <class 'tuple'>
pixel要素形式 = <class 'int'>
--CMYK--
画像モード = RGBA
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('R', 'G', 'B', 'A')
pixel(0,0) = (55, 219, 76, 0)
pixel(0,0)の形式 = <class 'tuple'>
pixel要素形式 = <class 'int'>
--パレット--
画像モード = L
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('L',)
pixel(0,0) = 0
pixel(0,0)の形式 = <class 'int'>
--グレスケ--
画像モード = L
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('L',)
pixel(0,0) = 101
pixel(0,0)の形式 = <class 'int'>
--白黒--
画像モード = 1
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('1',)
pixel(0,0) = 0
pixel(0,0)の形式 = <class 'int'>
--int--
画像モード = I
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('I',)
pixel(0,0) = 101
pixel(0,0)の形式 = <class 'int'>
--float--
画像モード = F
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('F',)
pixel(0,0) = 101.33799743652344
pixel(0,0)の形式 = <class 'float'>
結果は、'P'と'L','CMYK'と'RGBA'が元に戻らない感じですね
チャンネルの数と要素タイプで区別しているみたいです
チャンネル 1 タイプ boolが '1'
チャンネル 1 タイプ uint8が 'L'
チャンネル 1 タイプ intが 'I'
チャンネル 1 タイプ floatが 'F'
チャンネル 3 タイプ uint8が 'RGB'
チャンネル 4 タイプ uint8が 'RGBA'
に自動変換でしょうか
Image.fromarray(obj, mode=None)
なのでパラメータでモードを指定すれば問題無し
im3_paltte = Image.fromarray(dat_paltte,'P') im3_CMYK = Image.fromarray(dat_CMYK,'CMYK') print("--パレット--") print_image_type(im3_paltte) print("--CMYK--") print_image_type2(im3_CMYK)
--パレット--
画像モード = P
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('P',)
pixel(0,0) = 0
pixel(0,0)の形式 = <class 'int'>
--CMYK--
画像モード = CMYK
画像サイズ(x,y) = (20, 15)
pixel格納形式 = ('C', 'M', 'Y', 'K')
pixel(0,0) = (55, 219, 76, 0)
pixel(0,0)の形式 = <class 'tuple'>
pixel要素形式 = <class 'int'>
パレットカラーモードについて
dat_paltte = np.asarray(im_paltte)
で配列に変換してもこれだけではIndexカラー番号のみが変換されて
色情報がありません。パレットカラーを取り扱う必要があります
data=im.getpalette()
とim.putpalette(data)
でパレットの情報の出し入れが出来る
パレット情報はリスト形式の[R0,G0,B0,R1,G1,B1, ... R255,G255,B255]
なので(R+G+B)*256 = 768要素になる
pc_list = im_paltte.getpalette() print("形式 = ",type(pc_list)) print("要素数 = ",len(pc_list)) print("要素数形式 = ",type(pc_list[0]))
形式 = <class 'list'>
要素数 = 768
要素数形式 = <class 'int'>
pythonが通常扱えるリストで、整数形式の768要素になっている。numpy配列にする
pc_array = np.array(im_paltte.getpalette()) print_np_type(pc_array) print("頭から16要素 = ",pc_array[0:16]) print("--画像っぽくRGBを分ける--") pc_array2 = pc_array.reshape((256,3)) print_np_type(pc_array2) print("インデックス番号5の色 = ",pc_array2[5])
配列要素 = int32
次元数 = 1
配列形 = (768,)
配列要素サイズ = 768
頭から16要素 = [200 36 179 197 37 181 197 36 180 194 36 181 193 36 182 192]
--画像っぽくRGBを分ける--
配列要素 = int32
次元数 = 2
配列形 = (256, 3)
配列要素サイズ = 768
インデックス番号5の色 = [192 37 182]
reshapeすれば扱いやすくなる。例えばインデックスカラーの1番を黒にするとかカラーオブジェクトを使っていろいろできるかも
pc_array2[1]= (0,0,0) #インデックス2を黒に pc_list = pc_array2.tolist() print("頭から8 = ",pc_list[0:8]) pc_list2 = pc_array2.flatten().tolist() print("頭から8 = ",pc_list2[0:8])
頭から8 = [[200, 36, 179], [0, 0, 0], [197, 36, 180], [194, 36, 181], [193, 36, 182], [192, 37, 182], [190, 37, 182], [189, 37, 183]]
頭から8 = [200, 36, 179, 0, 0, 0, 197, 36]
numpy配列にしたものも、flatten()
とtolist()
を使えば768要素の1次元配列にリストに戻せるので
その後im.putpalette(data)すればよい