2013-07-08 3 views
5

मैं काम करने की कोशिश कर रहा हूं कि पाइथन फ़ंक्शन को कैसे बढ़ाया जाए जो numpy का उपयोग करता है। उत्पादन मैं lineprofiler से प्राप्त हुआ है नीचे है, और यह पता चलता है कि समय के विशाल बहुमत लाइन ind_y, ind_x = np.where(seg_image == i) पर खर्च किया जाता है।पूर्णांक को गतिमान करने के लिए numpy.where गति?

seg_image एक पूर्णांक सरणी है जो एक छवि को विभाजित करने का परिणाम है, इस प्रकार पिक्सल ढूंढना जहां seg_image == i एक विशिष्ट सेगमेंट ऑब्जेक्ट निकालता है। मैं इन वस्तुओं के बहुत सारे के माध्यम से पाशन कर रहा हूँ (कोड में नीचे मैं सिर्फ परीक्षण के लिए 5 के माध्यम से पाशन हूँ, लेकिन मैं वास्तव में 20,000 से अधिक के माध्यम से पाशन हो जाएगा), और यह एक लंबे समय से चलाने के लिए ले जाता है!

वहाँ किसी भी तरह से, जिसमें np.where कॉल को गति जा सकती है? या, वैकल्पिक रूप से, कि अंतिम रेखा (जो समय का एक अच्छा अनुपात भी लेती है) तेज हो सकती है?

आदर्श समाधान लूपिंग के बजाए एक बार में पूरे सरणी पर कोड चलाने के लिए होगा, लेकिन मुझे नहीं लगता कि यह संभव है क्योंकि कुछ कार्यों के लिए दुष्प्रभाव हैं जिन्हें मैं चलाने की आवश्यकता है उदाहरण के लिए, सेगमेंट ऑब्जेक्ट को फैलाने से यह अगले क्षेत्र के साथ 'टकरा' कर सकता है और इस प्रकार बाद में गलत परिणाम देता है)।

क्या किसी के पास कोई विचार है?

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    5           def correct_hot(hot_image, seg_image): 
    6   1  239810 239810.0  2.3  new_hot = hot_image.copy() 
    7   1  572966 572966.0  5.5  sign = np.zeros_like(hot_image) + 1 
    8   1  67565 67565.0  0.6  sign[:,:] = 1 
    9   1  1257867 1257867.0  12.1  sign[hot_image > 0] = -1 
    10           
    11   1   150 150.0  0.0  s_elem = np.ones((3, 3)) 
    12           
    13            #for i in xrange(1,seg_image.max()+1): 
    14   6   57  9.5  0.0  for i in range(1,6): 
    15   5  6092775 1218555.0  58.5   ind_y, ind_x = np.where(seg_image == i) 
    16           
    17             # Get the average HOT value of the object (really simple!) 
    18   5   2408 481.6  0.0   obj_avg = hot_image[ind_y, ind_x].mean() 
    19           
    20   5   333  66.6  0.0   miny = np.min(ind_y) 
    21             
    22   5   162  32.4  0.0   minx = np.min(ind_x) 
    23             
    24           
    25   5   369  73.8  0.0   new_ind_x = ind_x - minx + 3 
    26   5   113  22.6  0.0   new_ind_y = ind_y - miny + 3 
    27           
    28   5   211  42.2  0.0   maxy = np.max(new_ind_y) 
    29   5   143  28.6  0.0   maxx = np.max(new_ind_x) 
    30           
    31             # 7 is + 1 to deal with the zero-based indexing, + 2 * 3 to deal with the 3 cell padding above 
    32   5   217  43.4  0.0   obj = np.zeros((maxy+7, maxx+7)) 
    33           
    34   5   158  31.6  0.0   obj[new_ind_y, new_ind_x] = 1 
    35           
    36   5   2482 496.4  0.0   dilated = ndimage.binary_dilation(obj, s_elem) 
    37   5   1370 274.0  0.0   border = mahotas.borders(dilated) 
    38           
    39   5   122  24.4  0.0   border = np.logical_and(border, dilated) 
    40           
    41   5   355  71.0  0.0   border_ind_y, border_ind_x = np.where(border == 1) 
    42   5   136  27.2  0.0   border_ind_y = border_ind_y + miny - 3 
    43   5   123  24.6  0.0   border_ind_x = border_ind_x + minx - 3 
    44           
    45   5   645 129.0  0.0   border_avg = hot_image[border_ind_y, border_ind_x].mean() 
    46           
    47   5  2167729 433545.8  20.8   new_hot[seg_image == i] = (new_hot[ind_y, ind_x] + (sign[ind_y, ind_x] * np.abs(obj_avg - border_avg))) 
    48   5  10179 2035.8  0.1   print obj_avg, border_avg 
    49           
    50   1   4  4.0  0.0  return new_hot 

उत्तर

4

संपादित मैं रिकार्ड के लिए तल पर मेरी मूल जवाब छोड़ दिया है, लेकिन मैं वास्तव में खाने पर और अधिक विस्तार में अपने कोड में देखा है, और मुझे लगता है कि np.where का उपयोग कर एक बड़ी गलती है:

In [63]: a = np.random.randint(100, size=(1000, 1000)) 

In [64]: %timeit a == 42 
1000 loops, best of 3: 950 us per loop 

In [65]: %timeit np.where(a == 42) 
100 loops, best of 3: 7.55 ms per loop 

आप एक बूलियन सरणी (आप अनुक्रमण के लिए उपयोग कर सकते हैं कि) समय आप अंक की वास्तविक निर्देशांक प्राप्त करने की आवश्यकता का 1/8 में मिल सकता है !!!

निश्चित रूप से नहीं है सुविधाएँ आपको करना है कि फसल, लेकिन ndimage एक find_objects समारोह है कि enclosing स्लाइस देता है, और बहुत तेजी से प्रतीत होता है:

In [66]: %timeit ndimage.find_objects(a) 
100 loops, best of 3: 11.5 ms per loop 

यह स्लाइस की tuples की सूची लौटाता है 50% अधिक समय में सभी अपने वस्तुओं की enclosing, यह एक ही वस्तु के सूचकांकों को खोजने के लिए ले जाता है thn।

यह बॉक्स से बाहर काम नहीं कर सकता के रूप में मैं अभी परीक्षण नहीं कर सकते, लेकिन मैं निम्नलिखित की तरह कुछ में अपने कोड पुनर्गठन होगा:

def correct_hot_bis(hot_image, seg_image): 
    # Need this to not index out of bounds when computing border_avg 
    hot_image_padded = np.pad(hot_image, 3, mode='constant', 
           constant_values=0) 
    new_hot = hot_image.copy() 
    sign = np.ones_like(hot_image, dtype=np.int8) 
    sign[hot_image > 0] = -1 
    s_elem = np.ones((3, 3)) 

    for j, slice_ in enumerate(ndimage.find_objects(seg_image)): 
     hot_image_view = hot_image[slice_] 
     seg_image_view = seg_image[slice_] 
     new_shape = tuple(dim+6 for dim in hot_image_view.shape) 
     new_slice = tuple(slice(dim.start, 
           dim.stop+6, 
           None) for dim in slice_) 
     indices = seg_image_view == j+1 

     obj_avg = hot_image_view[indices].mean() 

     obj = np.zeros(new_shape) 
     obj[3:-3, 3:-3][indices] = True 

     dilated = ndimage.binary_dilation(obj, s_elem) 
     border = mahotas.borders(dilated) 
     border &= dilated 

     border_avg = hot_image_padded[new_slice][border == 1].mean() 

     new_hot[slice_][indices] += (sign[slice_][indices] * 
            np.abs(obj_avg - border_avg)) 

    return new_hot 

आप अभी भी यह पता लगाने की आवश्यकता होगी टक्कर न हो, लेकिन आप सभी सूचकांकों एक साथ एक np.unique आधारित दृष्टिकोण का उपयोग कर की गणना के द्वारा एक 2x गति-अप के बारे में मिल सकता है:

a = np.random.randint(100, size=(1000, 1000)) 

def get_pos(arr): 
    pos = [] 
    for j in xrange(100): 
     pos.append(np.where(arr == j)) 
    return pos 

def get_pos_bis(arr): 
    unq, flat_idx = np.unique(arr, return_inverse=True) 
    pos = np.argsort(flat_idx) 
    counts = np.bincount(flat_idx) 
    cum_counts = np.cumsum(counts) 
    multi_dim_idx = np.unravel_index(pos, arr.shape) 
    return zip(*(np.split(coords, cum_counts) for coords in multi_dim_idx)) 

In [33]: %timeit get_pos(a) 
1 loops, best of 3: 766 ms per loop 

In [34]: %timeit get_pos_bis(a) 
1 loops, best of 3: 388 ms per loop 

ध्यान दें कि प्रत्येक के लिए पिक्सल ओ इंजेक्शन को एक अलग क्रम में वापस कर दिया जाता है, इसलिए आप समानता का आकलन करने के लिए दोनों कार्यों के रिटर्न की तुलना नहीं कर सकते हैं। लेकिन उन्हें दोनों को वापस करना चाहिए।

+0

यह अद्भुत, अद्भुत और अद्भुत है - धन्यवाद! पहली बार मैंने इसे चलाया, मैंने पाया कि यह वास्तव में मेरे मूल कोड से धीमा था, लेकिन फिर मैंने आपके कुछ कोड को संशोधित किया ताकि यह विशाल सरणी के बजाय एक छोटे सरणी में सभी काम (फैलाव, सीमाएं इत्यादि) कर सके - संशोधित करके कि new_shape की गणना कैसे की गई थी। अब मुझे गति में भारी वृद्धि हुई है। जिन छवियों के साथ मैं काम कर रहा हूं उनमें से एक पर पुराने संस्करण में ढाई घंटे लग गए, नए में 11 सेकंड लग गए! – robintw

+0

ओह! हां, ऐसा लगता है कि जनरेटर अभिव्यक्ति 'new_shape = tuple (dim_image_view.shape में मंद के लिए मंद +6) होना चाहिए, और' new_shape = tuple (dim_image.shape में मंद के लिए मंद +6) नहीं होना चाहिए। क्या तुमने क्या बदल दिया? कृपया, कार्य कोड को प्रतिबिंबित करने के लिए मेरे उत्तर को संपादित करने के लिए स्वतंत्र महसूस करें। – Jaime

2

एक बात आप एक ही समय का एक छोटा सा करने के लिए कर सकता है seg_image == i का परिणाम को बचाने के लिए इतना है कि आप इसे दो बार की गणना करने की आवश्यकता नहीं है। आप तर्ज पर यह कंप्यूटिंग रहे 15 & 47, आप seg_mask = seg_image == i जोड़ने और फिर उस परिणाम का पुन: उपयोग कर सकता है (यह भी उन उद्देश्यों की रूपरेखा के लिए उस टुकड़े को अलग करने के लिए अच्छा हो सकता है)।

जबकि कुछ अन्य मामूली चीजें जो आप थोड़ा प्रदर्शन करने के लिए कर सकते हैं, मूल मुद्दा यह है कि आप ओ (एम * एन) एल्गोरिदम का उपयोग कर रहे हैं जहां एम सेगमेंट और एन की संख्या है आपकी छवि का आकार है। यह आपके कोड से मुझे स्पष्ट नहीं है कि एक ही चीज़ को पूरा करने के लिए एक तेज एल्गोरिदम है, लेकिन यह पहला स्थान है जिसे मैं कोशिश करता हूं और एक गति की तलाश करता हूं।

संबंधित मुद्दे

 संबंधित मुद्दे