Faster way than concatenate of combining multiple cv2 images (numpy arrays) in python?

假如想象 提交于 2021-01-21 10:19:08

问题


I have 100 small images which I want to combine into one large (10x10) grid image for display with imshow. Each image (as a numpy array) is within the variable of a cell object. At the moment I'm using concatenate to first create vertical strips then using concatenate to connect all of those strips but it seems kinda clunky. Is there a better way to do this? I feel like I should be able to just create a numpy array the size of the final image (800 x 600) then plop each image in, but it seems to be beyond my ability to wrap my head around the syntax.

def stackImages(im1, im2, axisToStack):
    newImage = np.concatenate((im1, im2), axis = axisToStack)
    return newImage

def compileCells():
    #Make a list of strips
    strips = [(np.zeros((0,cellWidth,3), np.uint8)) for i in range(numberOfCells)]

    for x in range(numberOfCells):
        for i in range(numberOfCells):
            strips[x] = stackImages(cellArray[i+(x*numberOfCells)].image, strips[x], 0)

    display = strips[0]
    for c in range(1,numberOfCells):
        display = stackImages(strips[c], display, 1)
    return display

回答1:


Copying arrays can be a real speed killer when working with NumPy. Every time np.concatenate is called, space for a new array is allocated, and all the old data is copied into the new array. A way to make your code faster is to reduce the amount of copying.

So as you suggested, the faster way is to allocate space for the final array, display from the very the beginning:

display = np.empty((cellHeight*nrows, cellWidth*ncols, 3), dtype=np.uint8)

and then copy the data from cellArray into display just once:

for i, j in IT.product(range(nrows), range(ncols)):
    arr = cellArray[i*ncols+j].image  
    x, y = i*cellHeight, j*cellWidth
    display[x:x+cellHeight, y:y+cellWidth, :] = arr

For example,

import numpy as np
import matplotlib.pyplot as plt
import itertools as IT

def compileCells(cellArray, nrows, ncols, cellHeight, cellWidth):
    display = np.empty((cellHeight*nrows, cellWidth*ncols, 3), dtype=np.uint8)
    for i, j in IT.product(range(nrows), range(ncols)):
        # arr = cellArray[i*ncols+j].image  # you may need this
        arr = cellArray[i*ncols+j]          # my simplified cellArray uses this
        x, y = i*cellHeight, j*cellWidth
        display[x:x+cellHeight, y:y+cellWidth, :] = arr
    return display

cellHeight, cellWidth = 80, 60
nrows = ncols = numberOfCells = 10

cellArray = [np.full((cellHeight, cellWidth, 3), i) 
             for i in np.linspace(0, 255, nrows*ncols)]
display = compileCells(cellArray, nrows, ncols, cellHeight, cellWidth)
plt.imshow(display)
plt.show()

yields

enter image description here

Note, your code implies that cellArray is a list of objects whose image attributes are NumPy arrays. To make the example code above runnable and simple(r), I've defined cellArray above to be a list of NumPy arrays. You may need to uncomment

# arr = cellArray[i*ncols+j].image  

and comment out

arr = cellArray[i*ncols+j] 

to fit your definition of cellArray.


Let's compare the amount of copying done by the two approaches:

Using the original approach, if we say that an image array has size 1, then to build a strip requires allocating arrays of size 1, 2, ..., 10. So one strip requires allocating arrays of total size 1+2+...+10 = 10(11)/2 = 55. To build display requires allocating arrays of total size 55(1+2+..+10) = 55*55 = 3025. Each allocation of space is accompanied by a copy operation. The amount of copying grows quadratically with the number of cells in the final array.

In contrast, if we allocate space for the final display just once, then we only need to allocate of total size 10*10 = 100. Here, the amount of copying grows linearly with the number of cells.



来源:https://stackoverflow.com/questions/29956677/faster-way-than-concatenate-of-combining-multiple-cv2-images-numpy-arrays-in-p

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!