2013-01-22 43 views
6

我有一個圖像,存儲在uint8的一個numpy數組中,形狀爲(planes, rows, cols)。我需要將其與存儲在形狀爲(mask_rows, mask_cols)的掩碼(也是uint8s)中的值進行比較。雖然圖像可能非常大,但面具通常較小,通常爲(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)稍大。我認爲這樣做,這樣做我有三個ginormous陣列共存在內存中: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,這樣不僅是有沒有記憶保存,但由於它是int32秒的陣列,它實際上正在四倍空間商店...

任何想法如何去做到這一點?要饒你麻煩,下面的一些沙箱代碼玩弄:

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

我幾乎都指出你的rolling window型把戲,但對於這個簡單的非重疊的東西,正常的重塑確實只是以及。 (這裏的重塑是安全的,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