2014-09-30 49 views
2

我編寫了一個python腳本,它以獨特的方式爲OpenGL着色器組合了圖像。問題是我有大量非常大的地圖,需要很長時間來處理。有沒有辦法以更快的方式寫這個?Python - 對PNG的快速批量修改

import numpy as np 

    map_data = {} 
    image_data = {} 
    for map_postfix in names: 
    file_name = inputRoot + '-' + map_postfix + resolution + '.png' 
    print 'Loading ' + file_name 
    image_data[map_postfix] = Image.open(file_name, 'r') 
    map_data[map_postfix] = image_data[map_postfix].load() 


    color = mapData['ColorOnly'] 
    ambient = mapData['AmbientLight'] 
    shine = mapData['Shininess'] 

    width = imageData['ColorOnly'].size[0] 
    height = imageData['ColorOnly'].size[1] 

    arr = np.zeros((height, width, 4), dtype=int) 

    for i in range(width): 
     for j in range(height): 
      ambient_mod = ambient[i,j][0]/255.0 
      arr[j, i, :] = [color[i,j][0] * ambient_mod , color[i,j][1] * ambient_mod , color[i,j][2] * ambient_mod , shine[i,j][0]] 

    print 'Converting Color Map to image' 
    return Image.fromarray(arr.astype(np.uint8)) 

這僅僅是一個大量的批量處理的樣品,所以我更感興趣的是,如果有迭代和修改的圖像文件更快的方法。幾乎所有的時間都花在嵌套循環上,加載和保存。

+3

你熟悉'numpy'工作得更快,當您試圖在整個陣列上一次(或至少整個矢量在一次操作思路),而不是循環個別元素?這個問題似乎是這個問題的一個典型例子。 – Marius 2014-09-30 01:40:48

+0

我忘了在頂部顯示我的導入聲明。這是正確的用法。有更多我應該使用Numpy的? – David 2014-09-30 04:44:38

+3

你應該試着對整個'color'和'shine'數組執行你的乘法,除法等操作,而不是對它們的單個元素進行操作,並且同樣用類似'ambient_mod_arr = ambient [:,創建一個'ambient_mod'數組來創建' ,0]/255.0'。這種方法很難讓你的頭腦開始,也很難讓我在單一的答案中解釋,但這對於高效地使用numpy非常重要。 – Marius 2014-09-30 06:47:21

回答

2

矢量化,代碼示例 - 在你的timeit或測試效果zmq.Stopwatch()

Reported to have 22.14 seconds >> 0.1624 seconds speedup!

當你的代碼似乎環路剛剛超過RGBA [X,Y],讓我告訴了「矢量化「 - 代碼的語法,受益於numpy矩陣操作實用程序(忘記RGB/YUV操作(最初基於OpenCV而不是PIL),但重新使用矢量化語法方法以避免for-loop並使其適應您的微積分的高效工作。錯誤的操作順序可能會使處理時間增加一倍以上。

並使用測試/優化/重新測試循環加速。

如需測試,請使用標準python timeit如果[msec]分辨率就足夠了。

如果您需要進入[usec]分辨率,請轉到zmq.StopWatch()

# Vectorised-code example, to see the syntax & principles 
#       do not mind another order of RGB->BRG layers 
#       it has been OpenCV traditional convention 
#       it has no other meaning in this demo of VECTORISED code 

def get_YUV_U_Cb_Rec709_BRG_frame(brgFRAME): # For the Rec. 709 primaries used in gamma-corrected sRGB, fast, VECTORISED MUL/ADD CODE 
    out = numpy.zeros(   brgFRAME.shape[0:2]) 
    out -= 0.09991/255 *   brgFRAME[:,:,1] # // Red 
    out -= 0.33601/255 *   brgFRAME[:,:,2] # // Green 
    out += 0.436 /255 *   brgFRAME[:,:,0] # // Blue 
    return out 
# normalise to <0.0 - 1.0> before vectorised MUL/ADD, saves [usec] ... 
# on 480x640 [px] faster goes about 2.2 [msec] instead of 5.4 [msec] 

在你的情況下,使用dtype = numpy.int,猜測它應該更快MUL首先ambient[:,:,0]最後DIV正常化arr[:,:,:3] /= 255

# test if this goes even faster once saving the vectorised overhead on matrix DIV 
arr[:,:,0] = color[:,:,0] * ambient[:,:,0]/255 # MUL remains INT, shall precede DIV 
arr[:,:,1] = color[:,:,1] * ambient[:,:,0]/255 # 
arr[:,:,2] = color[:,:,2] * ambient[:,:,0]/255 # 
arr[:,:,3] = shine[:,:,0]       # STO alpha 

它那麼如何可以看看你的算法中?

一個不需要有彼得·傑克遜的令人印象深刻的預算和計劃的時間一次,跨區卷和執行的巨大的數字運算能力超過3年在新西蘭的機庫,由SGI工作站一羣人滿爲患,因爲他生產「指環王「全數字母版製作流水線,通過逐幀像素操作,意識到大規模生產管線中的毫秒級,微秒級甚至納秒級無關緊要。

因此,深呼吸並測試並重新測試,以便將您的實際圖像處理性能優化到您的項目所需的水平。

希望這可以幫助你在此:

# OPTIONAL for performance testing -------------# |||||||||||||||||||||||||||||||| 
from zmq import Stopwatch      # _MICROSECOND_ timer 
#            # timer-resolution step ~ 21 nsec 
#            # Yes, NANOSECOND-s 
# OPTIONAL for performance testing -------------# |||||||||||||||||||||||||||||||| 
arr  = np.zeros((height, width, 4), dtype = int) 
aStopWatch = zmq.Stopwatch()     # |||||||||||||||||||||||||||||||| 
# /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\# <<< your original code segment   
# aStopWatch.start()       # |||||||||||||__.start 
# for i in range( width ): 
#  for j in range(height): 
#   ambient_mod = ambient[i,j][0]/255.0 
#   arr[j, i, :] = [ color[i,j][0] * ambient_mod, \ 
#       color[i,j][1] * ambient_mod, \ 
#       color[i,j][2] * ambient_mod, \ 
#       shine[i,j][0]    \ 
#       ] 
# usec_for = aStopWatch.stop()     # |||||||||||||__.stop 
# print 'Converting Color Map to image' 
# print '   FOR processing took ', usec_for, ' [usec]' 
# /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\# <<< proposed alternative 
aStopWatch.start()        # |||||||||||||__.start 
# reduced numpy broadcasting one dimension less # ref. comments below 
arr[:,:, 0] = color[:,:,0] * ambient[:,:,0] # MUL ambient[0] * [{R}] 
arr[:,:, 1] = color[:,:,1] * ambient[:,:,0] # MUL ambient[0] * [{G}] 
arr[:,:, 2] = color[:,:,2] * ambient[:,:,0] # MUL ambient[0] * [{B}] 
arr[:,:,:3] /= 255        # DIV 255 to normalise 
arr[:,:, 3] = shine[:,:,0]      # STO shine[ 0] in [3] 
usec_Vector = aStopWatch.stop()    # |||||||||||||__.stop 
print 'Converting Color Map to image' 
print '   Vectorised processing took ', usec_Vector, ' [usec]' 
return Image.fromarray(arr.astype(np.uint8)) 
+0

我之前忘記在頂部顯示我的導入聲明。將numpy導入numpy。有更多我應該使用它? – David 2014-09-30 04:44:58

+0

沒問題,大衛。不需要其他輸入。 Numpy設計了內部能力來分析和加速迭代矩陣運算的順序/尺度,並考慮到它的內部數據表示(FORTRAN排序,C排序,實際數據單元的稀疏映射,以至於忘記了內部性並保持在numpy數組抽象的頂部)。另外,你使用字節編碼的RGBA,所以保持numpy.int中的大部分操作,這避免了將dtypes重新分配爲浮點或在舍入時丟失精度。 [:,:,0]足以說明[i,j] [0]中的「all i-s,j-s」。測試它。 – user3666197 2014-09-30 11:42:23

+0

@David如果發佈的食譜確實有助於圖像處理,那麼善良併發布你的性能輸出並與StackOverflow社區共享,好嗎?謝謝你這麼做,大衛。 – user3666197 2014-09-30 12:29:43