如何将图像集合中的像素(R、G、B)映射到不同的像素颜色值索引?

Mik*_*nen 4 python performance numpy image image-processing

假设有 600 个带注释的语义分割掩模图像,其中包含 10 种不同的颜色,每种颜色代表一个实体。这些图像位于形状 (600, 3, 72, 96) 的 numpy 数组中,其中 n = 600、3 = RGB 通道、72 = 高度、96 = 宽度。

如何将 numpy 数组中的每个 RGB 像素映射到颜色索引值?例如,颜色列表将为 [(128, 128, 0), (240, 128, 0), ...n],并且 numpy 数组中的所有 (240, 128, 0) 像素将转换为索引唯一映射中的值 (= 1)。

如何用更少的代码高效地做到这一点?这是我想出的一种解决方案,但速度相当慢。

# Input imgs.shape = (N, 3, H, W), where (N = count, W = width, H = height)
def unique_map_pixels(imgs):
  original_shape = imgs.shape

  # imgs.shape = (N, H, W, 3)
  imgs = imgs.transpose(0, 2, 3, 1)

  # tupleview.shape = (N, H, W, 1); contains tuples [(R, G, B), (R, G, B)]
  tupleview = imgs.reshape(-1, 3).view(imgs.dtype.descr * imgs.shape[3])

  # get unique pixel values in images, [(R, G, B), ...]
  uniques = list(np.unique(tupleview))

  # map uniques into hashed list ({"RXBXG": 0, "RXBXG": 1}, ...)
  uniqmap = {}
  idx = 0
  for x in uniques:
    uniqmap["%sX%sX%s" % (x[0], x[1], x[2])] = idx
    idx = idx + 1
    if idx >= np.iinfo(np.uint16).max:
      raise Exception("Can handle only %s distinct colors" % np.iinfo(np.uint16).max)

  # imgs1d.shape = (N), contains RGB tuples
  imgs1d = tupleview.reshape(np.prod(tupleview.shape))

  # imgsmapped.shape = (N), contains uniques-index values
  imgsmapped = np.empty((len(imgs1d))).astype(np.uint16)

  # map each pixel into unique-pixel-ID
  idx = 0
  for x in imgs1d:
    str = ("%sX%sX%s" % (x[0], x[1] ,x[2]))
    imgsmapped[idx] = uniqmap[str]
    idx = idx + 1

  imgsmapped.shape = (original_shape[0], original_shape[2], original_shape[3]) # (N, H, W)
  return (imgsmapped, uniques)
Run Code Online (Sandbox Code Playgroud)

测试它:

import numpy as np
n = 30
pixelvalues = (np.random.rand(10)*255).astype(np.uint8)
images = np.random.choice(pixelvalues, (n, 3, 72, 96))

(mapped, pixelmap) = unique_map_pixels(images)
assert len(pixelmap) == mapped.max()+1
assert mapped.shape == (len(images), images.shape[2], images.shape[3])
assert pixelmap[mapped[int(n*0.5)][60][81]][0] == images[int(n*0.5)][0][60][81]
print("Done: %s" % list(mapped.shape))
Run Code Online (Sandbox Code Playgroud)

Div*_*kar 5

这是一种没有这些错误检查的紧凑矢量化方法 -

def unique_map_pixels_vectorized(imgs):
    N,H,W = len(imgs), imgs.shape[2], imgs.shape[3]
    img2D = imgs.transpose(0, 2, 3, 1).reshape(-1,3)
    ID = np.ravel_multi_index(img2D.T,img2D.max(0)+1)
    _, firstidx, tags = np.unique(ID,return_index=True,return_inverse=True)
    return tags.reshape(N,H,W), img2D[firstidx]
Run Code Online (Sandbox Code Playgroud)

运行时测试和验证 -

In [24]: # Setup inputs (3x smaller than original ones)
    ...: N,H,W = 200,24,32
    ...: imgs = np.random.randint(0,10,(N,3,H,W))
    ...: 

In [25]: %timeit unique_map_pixels(imgs)
1 loop, best of 3: 2.21 s per loop

In [26]: %timeit unique_map_pixels_vectorized(imgs)
10 loops, best of 3: 37 ms per loop ## 60x speedup!

In [27]: map1,unq1 = unique_map_pixels(imgs)
    ...: map2,unq2 = unique_map_pixels_vectorized(imgs)
    ...: 

In [28]: np.allclose(map1,map2)
Out[28]: True

In [29]: np.allclose(np.array(map(list,unq1)),unq2)
Out[29]: True
Run Code Online (Sandbox Code Playgroud)