]> Panopticon :: 2008年2月 Archive

トルコへ行ってきます。
エジプトから帰ってきます。
しばらくさようなら。

http://www.panopticon.jp/eblog/

Pythonで多次元リストを作るときに、どうすれば見栄えがよいか、ということを小一時間考えていました。

一次元の簡単なリストであれば、

>>> map(lambda x: 0, range(5))
[0, 0, 0, 0, 0]
>>> [0]*5
[0, 0, 0, 0, 0]

[0]*5は非常に簡単でいいのですが、参照「先」に対する操作を行うときには少し気を付ける必要があります。この方法で生成されたリストの要素は、すべて同じインスタンスを指しているからです。

>>> a = [[]]*5
>>> a
[[], [], [], [], []]
>>> a[1].append(1)
>>> a
[[1], [1], [1], [1], [1]]

したがって、この記法で多次元リストを作ることはできません。多次元リストのようなものはできますが、多次元リストとして使うことはできません。すべての行が、ただ一つの実体を参照しているからです。

>>> a = [[0]*3]*2
>>> a
[[0, 0, 0], [0, 0, 0]]
>>> a[1][1] = 3
>>> a
[[0, 3, 0], [0, 3, 0]]

こんなときは、おそらくリスト内包表記を使うのが一番簡単だと思います。ただ、これは次元が増えると大変です。入れ子となった内包表記を全て一行におさめる必要があるからです。

>>> a = [ [ [] for j in range(0,3) ] for i in range(0,2) ]
>>> a
[[[], [], []], [[], [], []]]
>>> a[1][1] = 3
>>> a
[[[], [], []], [[], 3, []]]

結局、考えていたことというのは、次元が増えたときにはdeepcopy()を使うしかないのか?ということです。何か代替があってもよさそうな気がするんですが。

import copy

def hyperlist(dimension=(), baselist=[]):
   if dimension:
      return hyperlist(dimension[1:], [ copy.deepcopy(baselist) for i in range(0, dimension[0]) ])
   else:
      return baselist

# dimension : dimensions (x, y, z, ...)
# beselist : Initial Entity (You can use it individually)
>>> a = hyperlist((4,3,2))
>>> a
[[[[], [], [], []], [[], [], [], []], [[], [], [], []]],
 [[[], [], [], []], [[], [], [], []], [[], [], [], []]]]
>>> a[1][1][1].append(3)
>>> a
[[[[], [], [], []], [[], [], [], []], [[], [], [], []]],
 [[[], [], [], []], [[], [3], [], []], [[], [], [], []]]]
>>> a[1][2][2] = 4
>>> a
[[[[], [], [], []], [[], [], [], []], [[], [], [], []]],
 [[[], [], [], []], [[], [3], [], []], [[], [], 4, []]]]
>>> a[0][0][0].append([1,2])
>>> a
[[[[[1, 2]], [], [], []], [[], [], [], []], [[], [], [], []]],
 [[[], [], [], []], [[], [3], [], []], [[], [], 4, []]]]

PIL(Python Imaging Library) は、Pythonインタープリタ用の画像処理ライブラリ群。これを使うとPythonで多くの形式のファイルを読み取って相互に変換できたり、非常に便利。

例えば、RGB画像を開いてその画素値を配列で取得するには、

import Image

filename = "xxx.xxx"
im = Image.open(filename)
print list(im.getdata())

輝度表現に変換。

im = im.convert("L")
print list(im.getdata())

輪郭検出フィルタを適用して保存。

import ImageFilter

im_contour = im.filter(ImageFilter.CONTOUR)
savefilename = "yyy.yyy"
im_contour.save(savefilename)

フィルタを自分で定義することもできます。ImageFilter.CONTOURは8方向ラプラシアンフィルタ(画素値の変化分の変化分を検出)ですが、4方向のものが使いたいときには、

from ImageFilter import BuiltinFilter

class LAP4CONTOUR(BuiltinFilter):
    name = "Lap4Contour"
    filterargs = (3, 3),1, 255, (
        0, -1,  0,
        -1, 4, -1,
        0, -1,  0
        )

im_l4contour = im.filter(LAP4CONTOUR)
im_l4contour.save(savefilename)

また、簡単なフィルタはクラスにしなくても作れます。下は4方向ラプラシアンを用いた鮮鋭化フィルタ。

from ImageFilter import Kernel

f = Kernel((3,3), (0, -1, 0, -1, 5, -1, 0, -1, 0), 1, 0)
im_l4edge = im.filter(f)
im_l4edge.save(savefilename)

それぞれの値の意味は、

class SomeFilter(BuiltinFilter):
    name = "SomeFilter"
    filterargs = matrixsize, scale, offset, matrix

f = Kernel(matrixsize, matrix, scale, offset)

# matrixsize フィルタ行列の大きさをあらわすタプル (3,3) または (5,5)
# matrix : フィルタ行列
# scale : フィルタ後の画素値はscaleで除算される 省略された場合はフィルタ行列の和
# offset : scaleで除算した後、この値を足す 下地の画素値