2013-01-22 5 views
6

(planes, rows, cols)の形のuint8のnumpy配列で保存された画像があります。私はそれをuint8のマスクに格納された値と比較する必要があります。形状は(mask_rows, mask_cols)です。イメージは非常に大きいかもしれませんが、マスクは通常小さいですが、通常(256, 256)であり、imageにタイルを張ります。コードを簡略化するには、rows = 100 * mask_rowscols = 100 * mask_colsのふりをしましょう。定期マスクのための `numpy.tile`の代わりに

私は現在、このしきい値を処理しています方法は、このようなものです:

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

私はMemoryErrorで顔に平手打ち取得する前にこの方法で処理できる最大の配列は(3, 11100, 11100)よりも少し大きくなっています。私が考えたやり方では、このようにして、メモリに共存する最大3つのginormous配列を持っています:image、タイル状のmask、私の返信outです。しかし、タイル張りのマスクは、10000回以上コピーされた同じ小さな配列です。私はそのメモリを余裕があれば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のは、実際にそう何メモリの節約がないだけでなく、それはint32の配列であることから、それは実際に4倍以上のスペースを取っている、インデックスmask(planes, rows, cols)配列を構築していることです店舗...

これについてどうやったらいいですか?あなたの悩みを惜しまするには、で遊ぶためにいくつかのサンドボックスコードの下に見つける:私はほとんどのトリックのrolling windowタイプにあなたを指摘しているだろうが、この単純な非重複の事のために、通常のリシェイプはちょうどそれをしない

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) 

答えて

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 
+0

美しいです!私はこのような解決策に頭を抱えていましたが、余分な次元の順序を正しく取ることができませんでした。もしできれば、ローリングウインドウのトリックへのリンクに+1を与えるでしょう。これは、マスクが画像を正確に分割することを要求します。そうでない場合は、画像がパディングされるまでそれが行く方法であり、画像で埋め込むことができると推測しています。サイズ変更 '、そう? – Jaime

+0

@Jaime、本当ですが、パッドする必要がある場合は、コピーする必要があります(同じデータセグメント内をコピーすることは可能ですが、現実的なままにしておくことができます)。それを避ける唯一のことは、いくつかの 'scipy 'を使用することです。ndimageツールを使用するか、境界を明示的に処理します。おそらく、ndimageのものはすべてマスクに重なるでしょう。 – seberg

+0

@Jaime OK、nvm ...リサイズが実際にそのメモリを動かしているようです(あなたが運が良ければ) – seberg

関連する問題