【问题标题】:Merge Similar Bins of Image Histogram合并图像直方图的相似分箱
【发布时间】:2020-12-21 16:20:55
【问题描述】:

我正在使用 Python(没有 AI,只有经典工具)处理一个 CV 项目,但遇到了一个问题。 我正在尝试从已知 ROI 中检测手部和皮肤颜色,该 ROI 手部位于白色背景上(因为它使用网络摄像头,所以颜色可能不准确 - 例如,灰色对我来说可能被视为白色)。我试着做一个颜色的直方图,从那里我会提取手的颜色。 为了获得我使用Image.getcolors(width*height) 的颜色列表,并将其放入直方图中。不幸的是,我得到了一个巨大的颜色列表,其中很多颜色与其他颜色相似(例如,(255,0,0)和(255,0,1)在单独的箱中表示)(由于相机质量差、照明等)。我的问题是如何合并这些箱并获得一个可靠的直方图,我可以从中提取肤色。 这是我写的一些代码:

pilRoi = Image.fromarray(coloredRoi)
w,h = pilRoi.size
colorsInRoi = pilRoi.getcolors(w*h)
sortedColors = sorted(colorsInRoi, key=lambda tup: tup[0])[::-1]

用于对颜色进行排序。 并且:

    for idx, color in enumerate(sortedColors):
    if(idx<config.NUM_COLORS):
        plt.bar(idx, color[0], color=helper.toHex(color[1]),edgecolor=helper.toHex(color[1]))
    else:
        break
plt.show()

用于直方图。 我确实尝试删除白色范围像素的总和,问题仍然存在于其他颜色:

    for color in sortedColors:
    if isInWhiteRange(color[1]) or color[1] == config.BLUE:
        toRemove.append(color)

for color in toRemove:
    sortedColors.remove(color)

谢谢!

【问题讨论】:

  • 处理此问题的一种方法是在获取直方图之前将图像缩小到小尺寸(例如 50x50)。如果仍然有太多颜色,则使用 -kmeans num_colors 限制颜色数量,例如 256 种颜色或更少。
  • 看起来像是对颜色进行聚类,然后取聚类中心可能更适合您所描述的问题。看看tberg.dk/post/determining-dominant-colors

标签: python opencv image-processing computer-vision histogram


【解决方案1】:

通常使用直方图将颜色放入 bins。例如,如果您有 256 个强度和 32 个 bin,这意味着每个 bin 的宽度为 4。0-3 范围内的强度集中到第一个 bin 中,4-7 范围内的强度集中到第二个 bin 中,等等. 这就是所谓的统一颜色量化,我们对一个像素进行量化,使其进入一组预定的 bin 之一。

在您的特定情况下,您可以指定每个颜色通道的 bin 数量,然后您可以简单地计算一维直方图,这样对于每个颜色通道,您可以确定它属于该通道的哪个 bin,然后转换此序列将 3 个 bin 转换为单个值。我提倡一维直方图的原因是为了更容易计算图像之间的相似性度量。

由于您已经将图像以 NumPy 形式存储在 coloredRoi 中,因此我假设您已经在三个平面中拥有该图像,因此通道是最后一个维度的 3D 数组。我还假设您正在处理每个通道的 8 位无符号整数值。像这样简单的东西可以工作:

# Define number of bins per channel
num_red_bins = 8
num_green_bins = 8
num_blue_bins = 8

# Define threshold per bin
thresh_red = 256 // num_red_bins
thresh_green = 256 // num_green_bins
thresh_blue = 256 // num_blue_bins

# Extract planes
red = coloredRoi[..., 0]
green = coloredRoi[..., 1]
blue = coloredRoi[..., 2]

# Calculate bin number per location
bin_red = red // thresh_red
bin_green = green // thresh_green
bin_blue = blue // thresh_blue

# Calculate 1D bin locations
bins = num_red_bins * num_green_bins * bin_blue + num_green_bins * bin_red + bin_green

# Calculate histogram
histo = np.bincount(bins, minlength=num_red_bins * num_green_bins * num_blue_bins)

代码非常不言自明,但最后两行可能会令人困惑。在此之前,我们已经将 RGB 像素转换为它们在红色、绿色和蓝色通道中的 bin 位置。这些集合将为我们提供该像素相对于最终 3D bin 的映射位置。这是一个独特的元组,将映射到一维直方图中的单个位置。要计算最终的一维 bin 编号,请考虑红色导航此空间的行,绿色导航此空间的列。假设我们只需要处理红色和绿色,每次我们需要去一个红色的新空间时,我们都必须跳过num_green_bins,这就是我们有num_green_bins * bin_red的原因。每次我们去一个绿色的新空间,我们只需要偏移列,这样我们就可以将bin_green添加到num_green_bins * bin_red + bin_green。最后,如果我们想转为蓝色,我们需要为我们想要的每个蓝色空间跳过num_red_bins * num_green_bins,因为我们现在要转为 3D,因此我们现在还添加了num_red_bins * num_green_bins * bin_blue。然后我们使用numpy.bincount 根据我们刚刚计算的一维 bin 计算最终的直方图。

现在您已经有了这个一维直方图,您可以使用任何直方图相似性度量来查看您期望从手上得到的颜色分布是否与感兴趣的补丁匹配。最后一点,如果您想看看这个量化的图像是什么样子,只需将您的 bin 值乘以 bin 值乘以我上面概述的每个 bin 的阈值,然后将所有内容堆叠成最终图像。

out_img = np.dstack((thresh_red * bin_red, thresh_green * bin_green, thresh_blue * bin_blue))

numpy.dstack 采用 2D 数组并将它们堆叠在第三维中以生成合并的 3D 数组。如果你做对了,当你可视化存储在out_img 中的量化结果时,颜色的微小变化将会消失。请注意,每个颜色通道的 bin 数量是您需要调整的参数。箱的数量越多,颜色的细粒度就越细,从而增加了所代表的动态范围,但是使用细粒度颜色的代价是将非常相似的 RGB 像素视为不同的像素。同样,bin 的数量越少,在更广泛的值范围内看起来就越相似的颜色,这将使您的分类的辨别力更弱。我建议更改 bin 的数量,以便更夸张地突出反映人类肤色(红色/绿色)的 bin,而较少强调不反映人类肤色的颜色(蓝色)。

【讨论】:

    猜你喜欢
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多