如何将一组图像中的像素点(R,G,B)映射到不同的像素颜色值索引?

4
假设有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)

测试它:

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))

嗯,你为什么要这样做?似乎只是增加了一个没有必要的步骤。如果你想对这些颜色索引进行任何操作,你将不得不搜索字典并将它们转换回RGB元组,对吧?编辑:算了,我明白了。如果你正在存储大量图像,那么存储整数比存储一堆元组更有效率,因为你预计会有一定数量的颜色(10),对吧? - pretzlstyle
是的,颜色数量是有限的。我需要唯一的索引,因为我将像素馈送到算法以预测像素类别,而不是像素颜色。灰度图像(具有强度例如0-10)也可以使用,但这些图像不能轻松地被标准工具(如图像查看器、编辑器等)可视化。最终,在预测之后,需要映射回RGB值。 - Mika Vatanen
3个回答

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]

运行时测试和验证 -

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

60倍的提升,一个数据集的预处理时间从4小时缩短到了4分钟 :) 谢谢!ravel_multi_index的文档非常稀少,我真的不太理解它是做什么的。作为起点的最大像素值是什么意思?据我所知,它以某种方式将这些3元素数组压缩成唯一的整数表示,但是这些(大)整数代表什么以及如何代表呢? - Mika Vatanen
1
@Mika,你真是太幸运了!看看这篇帖子:https://dev59.com/-1kT5IYBdhLWcg3wZehe#38674038 - Divakar

0

我有一张三通道的图像。我有三个通道的像素值,如果一个像素在这三个通道中具有这三个值,则它属于'A'类。 基本上,我想生成一个通道数等于类数的通道数组,每个类别在特定通道中分开。 这可以完成。

seg_channel = np.zeros((image.shape[0], image.shape[1], num_classes))
pixel_class_dict={'1': [128, 64, 128]. '2': [230, 50, 140]} #num_classes=2
for channel in range(num_classes):
    pixel_value= pixel_class_dict[str(channel)]
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            if list(image[i][j])==pixel_value:
                classes_channel[i,j,channel]=1

还有一种更高效的方法可以实现这个功能

import numpy as np
import cv2
for class_id in self.pixel_class_dict:
      class_color = np.array(self.pixel_class_dict:[class_id])
      seg_channel[:, :, class_id] = cv2.inRange(mask, class_color, class_color).astype('bool').astype('float32')

0

这是我的工作内容:

def rgb2mask(img): 
    if img.shape[0] == 3:
       img = img.rollaxis(img, 0, 3) 

    W = np.power(256, [[0],[1],[2]])

    img_id = img.dot(W).squeeze(-1) 
    values = np.unique(img_id)

    mask = np.zeros(img_id.shape)
    cmap = {}

    for i, c in enumerate(values):
        idx = img_id==c
        mask[idx] = i 
        cmap[tuple(img[idx][0])] = i
    return mask, cmap

如果您想根据已有的字典映射值,请查看我在此线程中的回答:将RGB图像转换为索引图像


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接