6

sの多数の配列に格納されたuint8形状の画像があり(planes, rows, cols)ます。uint8これを、 sの形状のマスクに格納されている値と比較する必要があります(mask_rows, mask_cols)。画像は非常に大きい場合がありますが、マスクは通常小さく、通常(256, 256)は上に並べて表示されimageます。コードを単純化するために、そのふりをしてみましょrows = 100 * mask_rowscols = 100 * mask_cols

私が現在このしきい値を処理している方法は、次のようなものです。

out = image >= np.tile(mask, (image.shape[0], 100, 100))

で顔を平手打ちする前にこの方法で処理できる最大の配列MemoryErrorは、より少し大きいです(3, 11100, 11100)。私がそれを理解した方法で、このように物事を行うと、メモリ内に最大3つの巨大な配列が共存します:image、タイルmask、およびリターンout。しかし、タイル状のマスクは、10,000回以上コピーされた同じ小さな配列です。したがって、そのメモリを節約できれば、メモリの2/3しか使用せず、3/2大きい画像を処理できるはずなので、サイズは約(3, 13600, 13600)です。ちなみに、これは、しきい値処理を適切に実行した場合に得られるものと一致しています。

np.greater_equal(image, (image.shape[0], 100, 100), out=image)

より大きな配列を処理するための周期的な性質を利用する私の(失敗した)試みは、周期的な線形配列でmaskインデックスを付けることでした:mask

mask = mask[None, ...]
rows = np.tile(np.arange(mask.shape[1], (100,))).reshape(1, -1, 1)
cols = np.tile(np.arange(mask.shape[2], (100,))).reshape(1, 1, -1)
out = image >= mask[:, rows, cols]

小さなアレイの場合、20倍の速度低下(!!!)がありますが、他のアレイと同じ結果が得られますが、大きなサイズでは実行に失敗します。代わりにMemoryError、他のメソッドが問題なく処理する値に対しても、最終的にpythonをクラッシュさせます。

私が起こっていると思うのは、numpyが実際(planes, rows, cols)にインデックスを作成する配列を構築してmaskいるため、メモリを節約できないだけでなく、int32sの配列であるため、実際に格納するのに4倍のスペースが必要です...

これについてどうやって行くかについてのアイデアはありますか?トラブルを回避するために、以下のサンドボックスコードを試してみてください。

import numpy as np

def halftone_1(image, mask) :
    return np.greater_equal(image, np.tile(mask, (image.shape[0], 100, 100)))

def halftone_2(image, mask) :
    mask = mask[None, ...]
    rows = np.tile(np.arange(mask.shape[1]),
                   (100,)).reshape(1, -1, 1)
    cols = np.tile(np.arange(mask.shape[2]),
                   (100,)).reshape(1, 1, -1)
    return np.greater_equal(image, mask[:, rows, cols])

rows, cols, planes = 6000, 6000, 3
image = np.random.randint(-2**31, 2**31 - 1, size=(planes * rows * cols // 4))
image = image.view(dtype='uint8').reshape(planes, rows, cols)
mask = np.random.randint(256,
                         size=(1, rows // 100, cols // 100)).astype('uint8')

#np.all(halftone_1(image, mask) == halftone_2(image, mask))
#halftone_1(image, mask)
#halftone_2(image, mask)

import timeit
print timeit.timeit('halftone_1(image, mask)',
                    'from __main__ import halftone_1, image, mask',
                    number=1)
print timeit.timeit('halftone_2(image, mask)',
                    'from __main__ import halftone_2, image, mask',
                    number=1)
4

1 に答える 1

6

私はほとんどあなたにローリングウィンドウタイプのトリックを指摘したでしょうが、この単純な重複しないものについては、通常の形状変更も同様に行います。(ここでの形状変更は安全です。numpyがそれらのコピーを作成することはありません)

def halftone_reshape(image, mask):
    # you can make up a nicer reshape code maybe, it is a bit ugly. The
    # rolling window code can do this too (but much more general then reshape).
    new_shape = np.array(zip(image.shape, mask.shape))
    new_shape[:,0] /= new_shape[:,1]
    reshaped_image = image.reshape(new_shape.ravel())

    reshaped_mask = mask[None,:,None,:,None,:]

    # and now they just broadcast:
    result_funny_shaped = reshaped_image >= reshaped_mask

    # And you can just reshape it back:
    return result_funny_shaped.reshape(image.shape)

そして、タイミングがすべてなので(実際にはそうではありませんが...):

In [172]: %timeit halftone_reshape(image, mask)
1 loops, best of 3: 280 ms per loop

In [173]: %timeit halftone_1(image, mask)
1 loops, best of 3: 354 ms per loop

In [174]: %timeit halftone_2(image, mask)
1 loops, best of 3: 3.1 s per loop
于 2013-01-22T21:28:27.747 に答える