合并图像直方图的相似容器
Merge Similar Bins of Image Histogram
我正在使用 Python(没有 AI,只有经典工具)和 运行 解决一个问题。
我正在尝试从手放在白色背景上的已知 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()
用于直方图。
我确实尝试删除白色 运行ge 像素的总和,其他颜色仍然存在问题:
for color in sortedColors:
if isInWhiteRange(color[1]) or color[1] == config.BLUE:
toRemove.append(color)
for color in toRemove:
sortedColors.remove(color)
谢谢!
通常使用直方图,您将颜色放入 bins。例如,如果您有 256 个强度和 32 个箱,这意味着每个箱的宽度为 4。0-3 范围内的强度集中到第一个箱中,4-7 范围内的强度集中到第二个箱中,依此类推. 这就是正式的统一颜色量化,我们对像素进行量化,使其进入一组预定的 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 的映射位置。这是一个独特的元组,将映射到 1D 直方图中的单个位置。要计算最终的 1D bin 编号,请考虑红色导航此 space 的行,绿色导航此 space 的列。假设我们只需要处理红色和绿色,每次我们需要使用红色的新 space 时,我们都必须跳过 num_green_bins
,所以这就是我们有 num_green_bins * bin_red
的原因。每次我们转到绿色的新 space 时,我们只需要偏移列,以便我们可以将 bin_green
添加到 num_green_bins * bin_red + bin_green
。最后,如果我们想要变成蓝色,我们需要为每个我们想要的蓝色 space 跳过 num_red_bins * num_green_bins
,因为我们现在要变成 3D,因此我们现在也添加 num_red_bins * num_green_bins * bin_blue
。然后我们使用 numpy.bincount
根据我们刚刚计算的 1D bins 计算最终的直方图。
现在您有了这个 1D 直方图,您可以使用任何直方图相似性度量来查看您期望从手上看到的颜色分布是否与感兴趣的色块相匹配。最后一点,如果您想看看这个量化图像是什么样子,只需取您的 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 数量是您需要调整的参数。 bin 的数量越多,您获得的颜色就越细粒度,从而增加了您所表示的动态范围,但是使用粒度颜色会以将非常相似的 RGB 像素视为不同为代价。同样,bin 的数量越少,在更广泛的值范围内看起来越相似的颜色,这将使您的分类的判别力变弱。我建议更改 bin 的数量,以便在反映人类肤色 (red/green) 的 bin 上放置更多夸张,而不太强调不反映人类肤色的颜色(蓝色)。
我正在使用 Python(没有 AI,只有经典工具)和 运行 解决一个问题。
我正在尝试从手放在白色背景上的已知 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()
用于直方图。 我确实尝试删除白色 运行ge 像素的总和,其他颜色仍然存在问题:
for color in sortedColors:
if isInWhiteRange(color[1]) or color[1] == config.BLUE:
toRemove.append(color)
for color in toRemove:
sortedColors.remove(color)
谢谢!
通常使用直方图,您将颜色放入 bins。例如,如果您有 256 个强度和 32 个箱,这意味着每个箱的宽度为 4。0-3 范围内的强度集中到第一个箱中,4-7 范围内的强度集中到第二个箱中,依此类推. 这就是正式的统一颜色量化,我们对像素进行量化,使其进入一组预定的 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 的映射位置。这是一个独特的元组,将映射到 1D 直方图中的单个位置。要计算最终的 1D bin 编号,请考虑红色导航此 space 的行,绿色导航此 space 的列。假设我们只需要处理红色和绿色,每次我们需要使用红色的新 space 时,我们都必须跳过 num_green_bins
,所以这就是我们有 num_green_bins * bin_red
的原因。每次我们转到绿色的新 space 时,我们只需要偏移列,以便我们可以将 bin_green
添加到 num_green_bins * bin_red + bin_green
。最后,如果我们想要变成蓝色,我们需要为每个我们想要的蓝色 space 跳过 num_red_bins * num_green_bins
,因为我们现在要变成 3D,因此我们现在也添加 num_red_bins * num_green_bins * bin_blue
。然后我们使用 numpy.bincount
根据我们刚刚计算的 1D bins 计算最终的直方图。
现在您有了这个 1D 直方图,您可以使用任何直方图相似性度量来查看您期望从手上看到的颜色分布是否与感兴趣的色块相匹配。最后一点,如果您想看看这个量化图像是什么样子,只需取您的 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 数量是您需要调整的参数。 bin 的数量越多,您获得的颜色就越细粒度,从而增加了您所表示的动态范围,但是使用粒度颜色会以将非常相似的 RGB 像素视为不同为代价。同样,bin 的数量越少,在更广泛的值范围内看起来越相似的颜色,这将使您的分类的判别力变弱。我建议更改 bin 的数量,以便在反映人类肤色 (red/green) 的 bin 上放置更多夸张,而不太强调不反映人类肤色的颜色(蓝色)。