简体   繁体   中英

Most pythonic way to index each tuple contained within a list

Sort of a Python beginner, sorry if this is a basic question.

I have tuples of the form (i, j) contained inside a list of variable length. This list is output by a function that is selecting clusters of pixels inside an image and averaging their RGB values, so the tuples are indices. The function is recursive, here is the return statement:

return ((red,blue,green), tuple_list, sum_n)
# [0] (r,g,b) values [1] list of indices as tuples [2] number of pixels gathered

I take the output from this function and write the new RGB values to my image matrix, which is a numpy array, like this by using a for loop:

cluster = rgb_avg_cluster(depth = 0, row = i, column = j, image = newimg)
    # cluster[1] is the list of tuple indices
    for x in cluster[1]: 
          newimg[x[0],x[1],0] = int(cluster[0][0])
          newimg[x[0],x[1],1] = int(cluster[0][1])
          newimg[x[0],x[1],2] = int(cluster[0][2])

My numpy array is [width, height, rgb], so for a 1200x600 image it would be (1200,600,3). Is there some quicker way to index the rgb values at each tuple index and change them to the new values?

As an example, if my output is ((150,40,40), [(35,35), (95,42)], 2) is there a better/faster way to change pixels (35,35) and (95,42) in my numpy array to rgb = (150,40,40) ? Like this:

input: 
        ((150,40,40), [(35,35), (95,42)], 2)

result: 
        newimg[35,35,0] = 150
        newimg[35,35,1] = 40
        newimg[35,35,2] = 40
        newimg[95,42,0] = 150
        newimg[95,42,1] = 40
        newimg[95,42,2] = 40

I know vectorized operations are the way to go with numpy but I don't know how to implement it in this case. This function takes a while to execute as-is.

Thanks!

It's easy enough to avoid the iteration on the RGB dimension. We still have to iterate on pixel indexing tuples:

In [10]: atup = ((150,40,40), [(35,35), (95,42)], 2)
In [11]: newimg = np.zeros((1200,600,3),int)
In [12]: vals = atup[0]
In [13]: idx = atup[1]
In [14]: for x in idx:
    ...:     newimg[x[0],x[1],:] = vals
    ...: 

testing a few values:

In [15]: newimg[35,35,1]
Out[15]: 40
In [16]: newimg[95,42,2]
Out[16]: 40

The first improvement is that when you call rgb_avg_cluster , save its results decomposed into 3 components:

rgb, ind, sum_n = rgb_avg_cluster(...)

This way you avoid index access to the composite returned value (so far cluster ).

Then you can save rgb into respective pixels eg in the following loop:

for r, c in ind:
    newimg[r, c, :] = rgb

To test the above code, I created a zero-filled array of size 2 rows by 4 columns by 3 colors :

newimg = np.zeros((2, 4, 3), dtype='int')

and set a "surrogate" rgb_avg_cluster result as:

rgb, ind, sum_n = ((150,40,40), [(1,1), (0,0)], 2)

After I ran the above loop, the result is:

array([[[150,  40,  40],
        [  0,   0,   0],
        [  0,   0,   0],
        [  0,   0,   0]],

       [[  0,   0,   0],
        [150,  40,  40],
        [  0,   0,   0],
        [  0,   0,   0]]])

Edit

It is possible to save your results even without any explicit loop.

To perform a test on just the same array, run:

newimg = np.zeros((2, 4, 3), dtype='int')
rgb, ind, sum_n = ((150,40,40), [(1,1), (0,0)], 2)
ind2 = np.array(ind).T
newimg[ind2[0], ind2[1], :] = rgb

Details:

  1. ind2 contains an array with 2 rows:

     array([[1, 0], [1, 0]])

    The first row contains x coordinates of pixels to save, and the second row - y coordinates,

  2. newimg[ind2[0], ind2[1]] - provides access to the indicated pixels. ind2[0] specifies x coordinates and ind2[1] - y coordinates. The third index is actually not needed. You access all elements in this dimension, just like you wrote " , : " (as the third index).

  3. newimg[...] = rgb - saves rgb at the given indices, in one go .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM