簡體   English   中英

Numpy 數組數學以復制像素,而不是通過 Python 中的 For 循環進行迭代

[英]Numpy Array Math to copy pixels instead of iterating through a For loop in Python

我正在拉入地球 ( src_filename ) 的 4000 寬 x 2000 高平面 map 並預先計算了存儲在numpy.ndarray ( transform_array , 2).ndarray (transform_array, 2).ndarray 中的像素重定位 x, y 值。

重新定位的像素應該從srcimg_array復制到 2000x2000 polarimg_array ,然后保存到圖像中。

我有一個 for 循環遍歷transform_array ,讀取每個位置的位置值,然后將像素 RGB 值從源復制到極坐標數組 - 即: transform_array [0, 0] 包含 [1414, 1500] 這意味着[0, 0] 處的源圖像顏色值被復制到polarimg_array中的 [1414, 1500],[0, 1] 值被復制到 [1413, 1500] 等。

這個過程每張圖像大約需要 30 秒,我正在努力加快這個過程。

如何使用 Python 來實現這一點? (即:不是 C 或 Cython 等)。

import numpy as np
import imageio

global transform_array
transform_array = np.zeros((2000, 2000, 2), dtype='i')
# transform_array values are pre-calculated already

srcimg_array = np.array(imageio.imread(src_filename))

polarimg_array = np.zeros((outputHeight, outputWidth, 3), dtype='uint8')

for item in np.ndindex((2000, 2000)):
    XYgrid = transform_array[item[0], item[1]]
    polarimg_array[[item[1]], [outputHeight - item[0]]] = srcimg_array[XYgrid[0], XYgrid[1]]

首先,就像sai他的評論中提到的那樣,如果您想根據您在問題中解釋的基於transform_array將像素從srcimg_array重新定位到polarimg_array

我有一個循環遍歷 transform_array,讀取每個位置的位置值,然后將像素 RGB 值從源復制到極坐標數組 - 即: transform_array [0, 0] 包含 [1414, 1500] 這意味着[0, 0] 處的源圖像顏色值被復制到 polarimg_array 中的 [1414, 1500],[0, 1] 值被復制到 [1413, 1500] 等。

然后你會像這樣使用 for 循環:

for item in np.ndindex((2000, 2000)):
    XYgrid = transform_array[item[0], item[1]]
    polarimg_array[item[0], item[1]] = srcimg_array[XYgrid[0], XYgrid[1]]

我不明白你想用你的 for 循環做什么:

for item in np.ndindex((2000, 2000)):
    XYgrid = transform_array[item[0], item[1]]
    polarimg_array[[item[1]], [outputHeight - item[0]]] = srcimg_array[XYgrid[0], XYgrid[1]]

似乎您正在嘗試進行某種額外的轉換。 如果是這種情況,那么您應該在transform_array上應用該轉換,以便transform_array包含您需要的所有轉換,然后使用上面編寫的 for 循環基於transform_array重新定位像素(我的回答中提到的第一個 for 循環)。

此外,在這一行中:

polarimg_array[[item[1]], [outputHeight - item[0]]] = srcimg_array[XYgrid[0], XYgrid[1]]

您已將索引item[1]outputHeight - item[0]放在一個數組中(因此您將[item[1]][outputHeight - item[0]]作為索引),這不僅是不必要的,而且(對於某些奇怪的原因)它顯着減慢了 for 循環的執行速度。 (在我的計算機上,當我刪除不必要的方括號時,它的速度大約快了 5 倍)。 所以,這一行應該是這樣的:

polarimg_array[item[1], outputHeight - item[0]] = srcimg_array[XYgrid[0], XYgrid[1]]

最后回到主要問題,在這個 for 循環中復制像素可以更快:

for item in np.ndindex((2000, 2000)):
    XYgrid = transform_array[item[0], item[1]]
    polarimg_array[item[0], item[1]] = srcimg_array[XYgrid[0], XYgrid[1]]

您可以在 NumPy 中使用數組索引( sai他的評論中也提到過)。 您可以在此處的 NumPy 中找到數組索引的詳細信息和示例。 簡而言之:

Integer 數組索引允許根據它們的 N 維索引選擇數組中的任意項。 每個 integer 數組代表該維度的多個索引。

示例

>>> x = np.array([[ 0,  1,  2],
                  [ 3,  4,  5], 
                  [ 6,  7,  8], 
                  [ 9, 10, 11]])
>>> rows = np.array([[0, 0],
                     [3, 3]], dtype=np.intp) 
>>> columns = np.array([[0, 2],
                        [0, 2]], dtype=np.intp)
>>> x[rows, columns]
array([[ 0,  2],
       [ 9, 11]])

下一行將做我們想要的:

polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]

我將在一個只有 6 個像素的圖像上的簡單示例中解釋它是如何工作的。

6 像素的示例圖像

對於這個圖像srcimg_array將是:

array([[[200,   0,   0],
        [150,   0,   0],
        [100,   0,   0]],

       [[  0,   0, 200],
        [  0,   0, 150],
        [  0,   0, 100]]], dtype=uint8)

如果我們想水平翻轉圖像,那么transform_array將是:

>>> transform_array = np.array([[[j, 2 - i] for i in range(3)] for j in range(2)])
>>> transform_array
array([[[0, 2],
        [0, 1],
        [0, 0]],

       [[1, 2],
        [1, 1],
        [1, 0]]])

變量transform_array[:,:,0]將包含變換位置的高度,而transform_array[:,:,1]將包含變換位置的寬度。 ( ::: :,:,0表示我們從第一維選擇所有內容( : ),從第二維選擇所有內容( : ),在第三維選擇 position 0上的元素。)

>>> transform_array[:,:,0]
array([[0, 0, 0],
       [1, 1, 1]])

>>> transform_array[:,:,1]
array([[2, 1, 0],
       [2, 1, 0]])

最后:

>>> polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]
>>> polarimg_array
array([[[100,   0,   0],
        [150,   0,   0],
        [200,   0,   0]],

       [[  0,   0, 100],
        [  0,   0, 150],
        [  0,   0, 200]]], dtype=uint8)

例如, polarimg_array[1, 2][ 0, 0, 200]因為transform_array[:,:,0][1, 2]1transform_array[:,:,1][1, 2]0所以polarimg_array[1, 2]等於srcimg_array[1, 0]

生成的圖像是:

生成的圖像

這是完整的代碼示例:

import numpy as np
import imageio

transform_array = np.zeros((2000, 2000, 2), dtype='i')
# transform_array values are pre-calculated already

srcimg_array = np.array(imageio.imread(src_filename))

polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]

在我的計算機上,大小為 4000x2000 的圖像上,創建polarimg_array所需的時間約為 0.1 秒。

編輯(在cpuguru的評論之后):

我查看了您的代碼並設法創建了您需要的轉換。 (我寫了 function getTransformation創建transform_array所以我們可以使用srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]來獲取polarimg_array 。)這是完全簡化的代碼示例。 (我使用 numba 刪除了參數解析、日志記錄、多處理和性能改進):

import os
import time
import numpy as np
import imageio

def getTransformation(height, outputHeight, outputWidth):
    ts_rows, ts_columns = np.indices((outputHeight, outputHeight))
    ts_rows = ts_rows - outputHeight / 2
    ts_columns = ts_columns - outputHeight / 2
    transform_src = np.zeros((outputHeight, outputHeight, 2), dtype='int')
    transform_src[:,:,0] = np.arctan2(ts_columns, ts_rows) * height / np.pi
    transform_src[:,:,1] = np.sqrt(ts_rows*ts_rows + ts_columns*ts_columns) \
                           * height / outputHeight
        
    tn_columns, tn_rows = np.indices((outputHeight, outputHeight))
    tn_rows = outputHeight - 1 - tn_rows
    transform_north = transform_src[tn_rows, tn_columns]    
    transform_north = transform_north[:,:,::-1]

    ts_columns, ts_rows = np.indices((outputHeight, outputHeight))
    ts_rows = ts_rows - outputHeight
    transform_south = transform_src[ts_rows, ts_columns]
    transform_south[:,:,1] = height - transform_south[:,:,1] - 1
    transform_south = transform_south[:,:,::-1]

    transform_array = np.zeros((outputHeight, outputWidth, 2), dtype='int')
    transform_array[:,:outputHeight,:] = transform_north
    transform_array[:,outputHeight-4:,:] = transform_south
    return transform_array
        

def EquirectangularToPolar(src_filename, transform_array):
    srcimg_array = np.array(imageio.imread(src_filename))

    polarimg_array = srcimg_array[transform_array[:,:,0], transform_array[:,:,1]]

    name = os.path.splitext(os.path.basename(src_filename))[0]
    dst_filename = ''.join([name, '_pp.', 'jpg'])
    imageio.imsave(dst_filename, polarimg_array)
    print("Saving " + dst_filename)
    return


def main():
    starttime = time.time()
    filename = 'wind_waves_0001.jpg'

    height = 2000
    width = 4000
    outputHeight = height
    outputWidth = width - 4

    starttime = time.time()
    
    transform_array = getTransformation(height, outputHeight, outputWidth)

    EquirectangularToPolar(filename, transform_array)

    print('File conversion took {0:.2f} seconds'.format(time.time() - starttime))

if __name__ == '__main__':
    main()

Function getTransformation創建transform_array ,然后在 function EquirectangularToPolar中使用,將變換應用於每個圖像。 在上面的代碼中,我假設您的所有圖像都具有相同的大小,因此您可以在所有圖像上使用相同的transform_array (或至少在相同大小的圖像上使用相同的transform_array ),從而為您帶來額外的性能提升。

function getTransformation工作原理的說明:

  • 第一部分創建transform_src transform_src是一個大小為(outputHeight, outputHeight, 2)的數組,使得transform_src[x][y]等於findSrcXY(x, y, height, outputHeight) 我們不是為每個坐標(x, y)計算findSrcXY ,而是為所有 x 坐標一起計算它,並為所有 y 坐標一起計算。 NumPy 在對整個數組應用操作而不是對數組的每個元素單獨應用該操作時非常快。 在第一行:

     ts_rows, ts_columns = np.indices((outputHeight, outputHeight))

    啟動ts_rowsts_columns以便ts_rows包含 x 坐標, ts_columns包含大小數組(outputHeight,outputHeight)中所有位置的 y 坐標。

    接下來兩行:

     ts_rows = ts_rows - outputHeight / 2 ts_columns = ts_columns - outputHeight / 2

    替換此代碼:

     x = x - outputHeight / 2 y = y - outputHeight / 2

    最后兩行:

     transform_src[:,:,0] = np.arctan2(ts_columns, ts_rows) * height / np.pi transform_src[:,:,1] = np.sqrt(ts_rows*ts_rows + ts_columns*ts_columns) * height / outputHeight

    與函數findSrcXfindSrcY做同樣的事情。

  • 在 function getTransformation的第二和第三部分中,創建了 arrays transform_northtransform_south transform_north是生成圖像左側所需的變換,而圖像右側需要transform_south 數組transform_north替換了這一行:

     polarimg_array[coord_target[1], outputHeight - coord_target[0]] = srcimg_array[coord_src[1], coord_src[0]]

    再次在第一行啟動ts_rowsts_columns

     tn_columns, tn_rows = np.indices((outputHeight, outputHeight))

    這次ts_rowsts_columns顛倒了,因為coord_targetpolarimg_array[coord_target[1], outputHeight - coord_target[0]]中顛倒了(第一個索引是coord_target[1] ,第二個索引是coord_target[0] 。)

    然后下一行:

     tn_rows = outputHeight - 1 - tn_rows

    替換outputHeight - coord_target[0] polarimg_array[coord_target[1], outputHeight - coord_target[0]] (我添加了-1以便生成的圖像從第一列(索引為零)開始,如果您查看圖像(以 png 格式),您會看到第一列只是黑色,我認為那不是期望的結果。)

    在下一行:

     transform_north = transform_src[tn_rows, tn_columns]

    我們從右邊 position 處的transform_src獲得值。

    在最后一行:

     transform_north = transform_north[:,:,::-1]

    我們反轉位置(從 (x, y) 到 (y, x)),因為coord_src srcimg_array[coord_src[1], coord_src[0]]中的 coord_src 位置是反轉的。

    數組transform_south的創建類似於transform_north

  • function getTransformation的最后一部分將transform_northtransform_south組合在一個數組transform_array中。

就像我說的,function getTransformation比以前的解決方案快得多,因為我們將所有操作應用於整個 arrays。 這些是我電腦上部分程序所花費的(大約)時間:

  • 0.8 秒創建transform_array (使用getTransformation函數)
  • 0.2 秒創建srcimg_array (打開圖像)
  • 0.3 秒創建polarimg_array (應用變換)
  • imageio.imsave function 為 0.2 秒(保存生成的圖像)

使用您的代碼(具有性能改進)轉換大約需要 1 分鍾。 您可以嘗試使用多處理和 numba 改進我的解決方案,但如果您的所有圖像大小相同,那么您只創建一次transform_array (調用getTransformation )並為每個圖像調用一次 function EquirectangularToPolar (大約需要 0.7 秒)。 鑒於打開和保存圖像大約需要 0.4 秒,只有當您可以加快打開和保存圖像的過程時,性能改進才有意義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM