Convert multi-dimensional Numpy array to 2-dimensional array based on color values

前端 未结 3 1585
猫巷女王i
猫巷女王i 2021-01-23 04:21

I have an image which is read as a uint8 array with the shape (512,512,3). Now I would like to convert this array to a uint8 array of shape (512,512,1)

相关标签:
3条回答
  • 2021-01-23 05:17

    Here's one based on views -

    # https://stackoverflow.com/a/45313353/ @Divakar
    def view1D(a, b): # a, b are arrays
        # This function gets 1D view into 2D input arrays
        a = np.ascontiguousarray(a)
        b = np.ascontiguousarray(b)
        void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[-1]))
        return a.view(void_dt).ravel(),  b.view(void_dt).ravel()
    
    def img2label(a, maps):
        # Get one-dimension reduced view into input image and map arrays.
        # We need to reshape image to 2D, then feed it to view1D to get 1D
        # outputs and then reshape 1D image to 2D 
        A,B = view1D(a.reshape(-1,a.shape[-1]),maps)
        A = A.reshape(a.shape[:2])
    
        # Trace back positions of A in B using searchsorted. This gives us
        # original order, which is the final output.
        sidx = B.argsort()
        return sidx[np.searchsorted(B,A,sorter=sidx)]
    

    Given that your labels start from 1, you might want to add 1 to the output.

    Sample run -

    In [100]: # Mapping array
         ...: maps = np.array([[0, 0, 0],[0, 0, 255],\
         ...:                  [255, 0, 0],[150, 30, 150]],dtype=np.uint8)
         ...: 
         ...: # Setup random image array
         ...: idx = np.array([[0,2,1,3],[1,3,2,0]])
         ...: img = maps[idx]
    
    In [101]: img2label(img, maps) # should retrieve back idx
    Out[101]: 
    array([[0, 2, 1, 3],
           [1, 3, 2, 0]])
    
    0 讨论(0)
  • 2021-01-23 05:19

    You could use giant lookup table. Let cls be [[0,0,0], [0,0,255], ...] of dtype=np.uint8.

    LUT = np.zeros(size=(256,256,256), dtype='u1')
    LUT[cls[:,0],cls[:,1],cls[:,2]] = np.arange(cls.shape[1])+1
    img_as_cls = LUT[img[...,0],img[...,1], img[...,2]]
    

    This solution is O(1) per pixel. It is also quite cache efficient because a small part of entries in LUT are actually used. It takes circa 10ms to process 1000x1000 image on my machine.

    The solution can be slightly improved by converting 3-color channels to 24-bit integers. Here is the code

    def scalarize(x):
        # compute x[...,2]*65536+x[...,1]*256+x[...,0] in efficient way
        y = x[...,2].astype('u4')
        y <<= 8
        y +=x[...,1]
        y <<= 8
        y += x[...,0]
        return y
    LUT = np.zeros(2**24, dtype='u1')
    LUT[scalarize(cls)] = 1 + np.arange(cls.shape[0])
    simg = scalarize(img)
    img_to_cls = LUT[simg]
    

    After optimization it takes about 5ms to process 1000x1000 image.

    0 讨论(0)
  • 2021-01-23 05:22

    One way: separately create the boolean arrays with True values where the input's pixel value matches one of the palette values, and then use arithmetic to combine them. Thus:

    palette = [
        [0, 0, 0], 
        [0, 0, 255], 
        [255, 0, 0],
        # etc.
    ]
    
    def palettized(data, palette):
        # Initialize result array
        shape = list(data.shape)
        shape[-1] = 1
        result = np.zeros(shape)
        # Loop and add each palette index component.
        for value, colour in enumerate(palette, 1):
            result += (data == colour).all(axis=2) * value
        return result
    
    0 讨论(0)
提交回复
热议问题