NumPyのarrayとPILの変換

Python Imaging LibraryのImageクラスのデータをNumpyのarrayとして扱うための方法について。
Numpyの関数を使って直接pixel値を書き換えることが目標です。
まずは両方のライブラリをインポートしておきます。

import numpy
import Image

PILからNumpyのarrayへの変換

numpyで用意されているasarray関数を使うと、PILのImageオブジェクトを配列に変換することができます。

imgArray = numpy.asarray(pilImg)

さらに配列の値を書き変えられるようにするためには、writeableフラグをTrueにする必要があるようです。

imgArray.flags.writeable = True

NumpyのarrayからPILへの変換

PILのfromarrayメソッドによって、配列の各値を1byte整数型(0〜255)として画像のpixel値に変換することができます。

pilImg = Image.fromarray(numpy.uint8(imgArray))

RGBモードの画像処理

3×4pixのRGB画像を読み込んだ配列を表示させると下のように値が格納されています。

import numpy
import Image

pilIN = Image.open("small.bmp")
imgArray = numpy.asarray(pilIN)
print imgArray


[ [ [100 161 217] ←[0][0]
[104 163 218] ←[0][1]
[114 169 221] ←[0][2]
[105 164 218] ] ←[0][3]

[ [169 203 234] ←[1][0]
[160 197 231] ←[1][1]
[117 171 220] ←[1][2]
[167 201 233] ] ←[1][3]

[ [124 175 221] ←[2][0]
[158 195 230] ←[2][1]
[ 80 147 209] ←[2][2]
[158 195 230] ] ] ←[2][3]

1pixの情報が[R,G,B]というリストで与えられていて、それが列方向に3,行方向に4だけ格納されていることがわかります。
この情報を扱うには、サイズを取得してfor文の中で[列][行][色]のようにアクセスすると良いです。
下の例は入力画像をネガ変換(RGB全てのモードで反転)して、PIL画像に戻して表示した例です。

import numpy
import Image

pilIN = Image.open("small.bmp")
imgArray = numpy.asarray(pilIN) #PILToNumpy

maxcol , maxrow = pilIN.size #get size
imgArray.flags.writeable = True
for i in range(maxrow):
    for j in range(maxcol):
        imgArray[i,j][0] = 255-imgArray[i,j][0] #R
        imgArray[i,j][1] = 255-imgArray[i,j][1] #G
        imgArray[i,j][2] = 255-imgArray[i,j][2] #B
pilOUT = Image.fromarray(numpy.uint8(imgArray)) #NumpyToPIL
pilOUT.show()

RGBAモードの画像処理

RGBAモードでは透過情報が含まれているため、1pixelに4つの情報が保存されていることになります。
3×4pixのRGBA画像を読み込み、その配列を表示させると下のように[R,G,B,A]という4つの値が格納されていることが分かります。

import numpy
import Image

pilIN = Image.open("small.bmp").convert("RGBA")#RGBAに変換
imgArray = numpy.asarray(pilIN)
print imgArray


[ [ [100 161 217 255]
[104 163 218 255]
[114 169 221 255]
[105 164 218 255] ]

[ [169 203 234 255]
[160 197 231 255]
[117 171 220 255]
[167 201 233 255] ]

[ [124 175 221 255]
[158 195 230 255]
[ 80 147 209 255]
[158 195 230 255] ] ]

RGBAの値にアクセスするには次のようにそれぞれのリストの値を取得するとよいでしょう。添え字を指定するには[i][j][k],[i,j][k],[i,j,k]などいくつかの書き方があります。

imgArray[i,j][0] #or imgArray[i,j,0] #R
imgArray[i,j][1] #or imgArray[i,j,1] #G
imgArray[i,j][2] #or imgArray[i,j,2] #B
imgArray[i,j][3] #or imgArray[i,j,3] #A

グレースケール画像処理

3×4pixのグレースケール画像を配列として表示させると下のようになります。

import numpy
import Image

pilIN = Image.open("small.bmp").convert("L")#グレースケールに変換
imgArray = numpy.asarray(pilIN)
print imgArray


[ [149 151 158 152]
[196 189 160 194]
[164 187 134 187] ]
この場合は直に[列][行]としてアクセスすれば値を書き換えられます。
グレースケール画像に対して50を閾値として2値化を行うコードです。

import numpy
import Image
pilIN = Image.open("small.bmp").convert("L")
imgArray = numpy.asarray(pilIN)
print imgArray
maxcol , maxrow = pilIN.size
imgArray.flags.writeable = True
for i in range(maxrow):
    for j in range(maxcol):
        if(imgArray[i,j] > 50):
            imgArray[i,j] = 1
        else:
            imgArray[i,j] = 0
pilOUT = Image.fromarray(numpy.uint8(imgArray))

pilOUT.show()

2値画像

2値画像の場合はbool型で値が格納されています。numpyではbool型はnumpy.bool8として定義されています。
下の例は一度配列に変換して戻した画像を、もう一度配列に直して出力したものです。

import numpy
import Image

pilIN = Image.open("small.bmp").convert("1")
imgArray = numpy.asarray(pilIN,numpy.bool8)
print imgArray
pilOUT = Image.fromarray(numpy.uint8(imgArray))
imgArray2 = numpy.asarray(pilOUT,numpy.bool8)
print imgArray2


[ [ True True True False]
[ True True True True]
[ True True True True] ]

[ [ True True True False]
[ True True True True]
[ True True True True] ]

配列に読み込む際には読み込みモードとしてbool8を指定します。画像に変換する場合はfromarrayでuint8を指定するとうまくいくみたいです。