1

I have a NumPy matrix of shape (n, height, width) containing grayscale images in the uint8 range. Each layer (n in total) contains a prediction from a neural network. I would like to compare all n layers and for each pixel get the second most common value and create a new matrix of shape (height, width) with this new value. In other words, I use per-pixel voting to create a new 2D matrix from all the matrices in the layered one. I prefer the second most common value as the most common one is 3, for different reasons, and I would like to ignore this; this is why I search for the second most common value with mode(vote[vote != 3], axis=None)[0][0]. The following is a working solution. However, it is not very fast as I have to slowly iterate. I would like to use a vectorized solution to save time.

import numpy
from scipy.stats import mode

n = 84
height = 1872
width = 3128

layered_image = numpy.ndarray(shape=(n, height , width), dtype=numpy.uint8)
image = numpy.ndarray(shape=(height , width), dtype=numpy.uint8)

for i in range(0, height):
    for j in range(0, width):
        vote = []
        for k in range(len(layered_image)):
            vote.append(layered_image[k][i][j])
        vote = numpy.asarray(vote, dtype=numpy.uint8)
        image[i][j] = mode(vote[vote != 3], axis=None)[0][0]

Thank you for any suggestions.

  • If the highest count occurs at two places , eg : `[10,12,13,13]`, would you choose 13 or 12? – Divakar May 04 '20 at 12:33
  • 1
    In the case of `vote = [10,12,13,13]`, I would choose **13** because 13 has the highest count that is not **3** because I ignore 3 with `mode(vote[vote != 3], axis=None)[0][0]`. –  May 04 '20 at 12:41
  • The result can only be one of `[0, 1, 2, 3]`; it has to be the most common one of these three other than **3**. –  May 04 '20 at 12:50
  • Got it. So. you are saying `layered_image` can have only one of `[0,1,2,3]` values? – Divakar May 04 '20 at 12:52
  • Yes; each layer is slightly different from the rest and can only have one of these values: `[0, 1, 2, 3]`. I want to create a new matrix of shape (height, width), the values of which are the most common of the values of all layers other than the value 3. –  May 04 '20 at 12:57

2 Answers2

0

One way would be with outer broadcasting equality -

np.equal.outer(layered_image,np.arange(3)).sum(0).argmax(-1)

Another with mask processing -

# a is the input layered_image
c0,c1,c2 = (a==0).sum(0), (a==1).sum(0), (a==2).sum(0)
out = np.where(np.maximum(c0,c1)>c2, np.where(c0>c1,0,1), 2)

Another with 2D bincount -

# https://stackoverflow.com/a/46256361/ @Divakar
def bincount2D_vectorized(a):    
    N = a.max()+1
    a_offs = a + np.arange(a.shape[0])[:,None]*N
    return np.bincount(a_offs.ravel(), minlength=a.shape[0]*N).reshape(-1,N)

# a is the input layered_image
b = bincount2D_vectorized(a.reshape(a.shape[0],-1).T)
out = b[:,:-1].argmax(1).reshape(a.shape[1:])
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • 1
    @OmidKholmi Hence, you can use `Another with mask processing` code that uses slicing to keep memory in check. – Divakar May 06 '20 at 09:50
0

Thanks to Divakar, the bincounting method worked great for my purposes. To prevent excessive RAM usage when dealing with a huge number of layers, I added slicing to this method. This is a bit slower but it allows processing on machines with less memory; given that slice_coordinates is a list containing the top-left coordinates of all image tiles in tuples of (y, x), prediction_overlay the 3D layered image and prediction_mask the new 2D image, slicing works as follows:

for i in range(len(slice_coordinates)):
    to_process = prediction_overlay [:, slice_coordinates[i][0] : slice_coordinates[i][0] + tile_height, slice_coordinates[i][1] : slice_coordinates[i][1] + tile_width]

    b = bincount2D_vectorized(to_process.reshape(to_process.shape[0],-1).T)
    processed = b[:,:-1].argmax(1).reshape(to_process.shape[1:])

    prediction_mask [slice_coordinates[i][0] : slice_coordinates[i][0] + tile_height, slice_coordinates[i][1] : slice_coordinates[i][1] + tile_width] = processed