Numpy - how to build a new image from 4 input images, picking the brightest pixels (max(R+G+B))

Viewed 168

There are four image arrays like this:

image1 = np.array([
    [
      # [R  G  B]
      #  |  |  |
        [1, 2, 3], [11, 22, 55], [12, 45, 56]
    ],
    [
        [1, 2, 3], [56, 55, 13], [12, 45, 56]
    ],
    [
        [11, 22, 55], [56, 55, 13], [12, 45, 56]
    ],
])

image2 = np.array([
    [
        [91, 72, 33], [111, 222, 155], [212, 245, 156]
    ],
    [
        [100, 200, 113], [56, 255, 213], [112, 145, 156]
    ],
    [
        [113, 223, 255], [156, 55, 113], [212, 245, 156]
    ],
])

image3 = np.array([
    [
        [9, 2, 3], [111, 222, 255], [22, 25, 16]
    ],
    [
        [10, 20, 13], [156, 25, 23], [12, 45, 16]
    ],
    [
        [13, 23, 155], [56, 255, 13], [222, 235, 216]
    ],
])

image4 = np.array([
    [
        [29, 22, 23], [111, 222, 255], [223, 125, 216]
    ],
    [
        [210, 220, 13], [156, 252, 232], [122, 145, 216]
    ],
    [
        [123, 232, 155], [56, 255, 213], [222, 235, 216]
    ],
])

For each rows and columns, I want to compute the max RGB value of sum of Red & Green & Blue channel of the image arrays and use the max to build a new image array.

So after tried the following code, It got duplicate indices, because in each rows and columns the max RGB of three image arrays may be duplicate. But I just want to get the first match index.

array = np.array([image1, image2, image3, image4])
array_sum = array.sum(axis=3)
# array_sum_max_index = array_sum.argmax(axis=0)
indices = np.where(array_sum == array_sum.max(axis=0))
print(indices)

Output

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

There is a similar answer in this link, but it cannot resolve my question. So how to get the index when it first match.

Edit:

The actual number of input images is more than 32. (Solutions involving numpy.choose() may not work because of this)

4 Answers

(Proposing an alternative solution that does not use numpy.choose(), since OP has indicated that the constraint of 32 choice arrays in the case of numpy.choose() is an issue)

array = np.array([image1, image2, image3, image4])
array_sum = array.sum(axis=3)
array_sum_max_index = array_sum.argmax(axis=0)

m = 3 # Number of pixel rows
n = 3 # Number of pixel columns

array[array_sum_max_index, np.arange(m)[:,None,], np.arange(n)[None,:]]

Output:

array([[[ 91,  72,  33],
        [111, 222, 255],
        [212, 245, 156]],

       [[210, 220,  13],
        [156, 252, 232],
        [122, 145, 216]],

       [[113, 223, 255],
        [ 56, 255, 213],
        [222, 235, 216]]])

In one line (based on @fountainhead 's answer):

np.take_along_axis(array, array.sum(3).argmax(0)[None, ..., None], 0)

Out[]: 
array([[[[ 91,  72,  33],
         [111, 222, 255],
         [212, 245, 156]],

        [[210, 220,  13],
         [156, 252, 232],
         [122, 145, 216]],

        [[113, 223, 255],
         [ 56, 255, 213],
         [222, 235, 216]]]])

You can do it using np.choose:

np.choose(array_sum_max_index, array_sum)

Which gives you the brightest (highest R+G+B) pixel at each location:

array([[196, 588, 613],
       [443, 640, 483],
       [591, 524, 673]])

The result is the same as array_sum.max(axis=0).

I think this is what you're looking for:

np.choose(array_sum_max_index[...,None], tuple(array))

Output:

array([[[ 91,  72,  33],
        [111, 222, 255],
        [212, 245, 156]],

       [[210, 220,  13],
        [156, 252, 232],
        [122, 145, 216]],

       [[113, 223, 255],
        [ 56, 255, 213],
        [222, 235, 216]]])

Explanation:

  1. array_sum_max_index has the shape (m,n), where each image is an array of shape (m,n,3)

  2. array is an array of shape (4,m,n,3), and each "element" of tuple(array) is a sub-array of shape (m,n,3)

  3. We're trying to use np.choose(), passing array_sum_max_index as a "selector" array, and tuple(array) as the sequence of sub-arrays from which the selector will select from. But as you can see in the documentation for numpy.choose(), the "selector" array, and the sub-arrays being chosen from, must all be broadcastable together. Since the "selector" array array_sum_max_index is of shape (m,n), to make it broadcastable with sub-arrays of shape (m,n,3), we append an extra dimension of unit length to array_sum_max_index, using the expression array_sum_max_index[...,None]

  4. This would still work if we used just array instead of tuple(array), but the documentation for numpy.choose() discourages that.

Related