【问题标题】:How do I create a colour histogram of a custom colour palette in OpenCV?如何在 OpenCV 中创建自定义调色板的颜色直方图?
【发布时间】:2015-03-11 18:20:05
【问题描述】:

我有一张图片:

并且图像中的颜色范围通过这些 RGB 值作为线性插值生成:

rgb = [165,0,38], w = 0.0
rgb = [222,63,46], w = 0.125 
rgb = [248,142,82], w = 0.25
rgb = [253,212,129], ...
rgb = [254,254,189]
rgb = [203,232,129]
rgb = [132,202,102]
rgb = [42,159,84]
rgb = [0,104,55], w = 1.0

如何创建一个图表/直方图,其中 x 轴是颜色范围,值是具有该像素颜色的图像的百分比。

【问题讨论】:

  • 您的色阶看起来是 HSV 图像中 Hue 通道的缩放版本。
  • 您的x-axis 会根据这些颜色而变化吗?例如,您是否可以从绿色开始,走向红色,然后以橙色/白色结束?
  • @rayryeng 我想此时 x 轴的实际顺序并不重要。一旦我发现色阶是如何排序的,我就对列进行排序。
  • @beaker 色标是红色和绿色之间的线性渐变。
  • 我在编辑@beaker 中添加了更多关于色阶的细节

标签: python image opencv image-processing


【解决方案1】:

这是一个相当蛮力的尝试,我将如何解决这个问题。请注意,我正在寻找 RGB 空间中的颜色距离,但众所周知,RGB 中的颜色距离并不能很好地模拟人类对颜色的感知......但这是让你开始的东西。请注意,您需要安装 numpymatplotlibmatplotlib 允许将直方图绘制为茎图。

基本上,您定义的那些 RGB 值我们可以视为关键点。从这里开始,我们需要定义直方图中需要计算的 bin 总数。我将其设置为 64 开始。您首先需要做的是为您定义的那些值插入红色、绿色和蓝色值,以便我们可以创建一个 RGB 查找表。因此,我们需要使用您定义的那些关键点生成从开始 RGB 元组到结束 RGB 元组的 64 个 RGB 值,我们将对这些 RGB 值进行线性插值。

此 RGB 查找表将是一个 64 x 3 数组,基本算法是从您的图像中提取一个 RGB 像素,并从该像素中确定与查找表最接近的像素。我们找到产生最小距离的这个索引,我们将增加直方图中相应的 bin。我通过欧几里得距离的平方来计算。取平方根来获得欧几里得距离是没有意义的,因为我们想要找到最小距离。每个术语的平方根不会改变哪个像素颜色最接近查找表中的哪个条目。我们将对图像中的其余像素重复此操作。

要计算最小距离,请使用numpy.sum 以及通过broadcasting 减去查找表中每个位置在输入图像中获得的每个像素。我们将每个距离平方,将它们相加,然后确定查找表中的位置,该位置为我们提供numpy.argmin 的最小值。

现在,为了创建插值 RGB 查找,我在红色、绿色和蓝色通道关键点上调用了 numpy.interp,其中输出 (y) 值来自红色、绿色和蓝色值的那些关键点值你定义的,输入 (x) 值是 dummy 输入值,从 0 线性增加到我们减去 1 的控制点。所以我们的输入 x 关键点是:

[0, 1, 2, 3, ..., N-1]

N是关键点的总数,输出的关键点分别是红色、绿色和蓝色的关键点值。为了创建 64 个值的查找,我们需要在 0N-1 之间创建 64 个点,我们可以通过 numpy.linspace 实现这一点。

现在,OpenCV 的一个复杂之处在于以 BGR 格式读取图像。因此,我翻转通道使它们为 RGB,并且我还将图像投射为float32,以便我们在计算距离时可以保持精度。此外,一旦我计算出直方图,因为您想要 百分比,我将直方图转换为百分比,方法是除以直方图中的值总数(即图像中的像素数)并相乘100% 以得到这个百分比。

事不宜迟,这是我的代码尝试。您的图片看起来像麦田,所以我将您的图片命名为 wheat.png,但将其重命名为您的图片名称:

import numpy as np # Import relevant libraries
import cv2
import matplotlib.pyplot as plt

# Read in image    
img = cv2.imread('wheat.png')

# Flip the channels as the image is in BGR and cast to float
img = img[:,:,::-1].astype('float32')

# control points for RGB - defined by you
rgb_lookup = np.array([[165,0,38], [222,63,46], [248,142,82],
                      [253,212,129], [254,254,189], [203,232,129],
                      [132,202,102], [42,159,84], [0,104,55]])

# Define number of bins for histogram
num_bins = 64

# Define dummy x keypoint values
x_keypt = np.arange(rgb_lookup.shape[0])

# Define interpolating x values
xp = np.linspace(x_keypt[0], x_keypt[-1], num_bins)

# Define lookup tables for red, green and blue
red_lookup = np.interp(xp, x_keypt, rgb_lookup[:,0])
green_lookup = np.interp(xp, x_keypt, rgb_lookup[:,1])
blue_lookup = np.interp(xp, x_keypt, rgb_lookup[:,2])

# Define final RGB lookup
rgb_final_lookup = np.column_stack([red_lookup, green_lookup, blue_lookup])

# Brute force
# For each pixel we have in our image, find the closest RGB distance
# from this pixel to each pixel in our lookup.  Find the argmin,
# then log into histogram accordingly
hist = np.zeros(num_bins)

# Get the rows and columns of the image
rows = img.shape[0]
cols = img.shape[1]

# For each pixel
for i in np.arange(rows):
  for j in np.arange(cols):
    # Get colour pixel value
    val = img[i,j,:]

    # Find closest distance to lookup
    dists = np.sum((rgb_final_lookup - val)**2.0, axis=1)

    # Get location for histogram
    ind = np.argmin(dists)

    # Increment histogram
    hist[ind] += 1

# Get percentage calculation
hist = 100*hist / (rows*cols)

# Plot histogram
plt.stem(np.arange(num_bins), hist)
plt.title('Histogram of colours')
plt.xlabel('Bin number')
plt.ylabel('Percentage')
plt.show()

我们得到的图表是:

上图很有道理。在您的色谱开始处,有很多红色和黄色像素,它们在您的色谱开始附近定义。绿色和白色像素更接近末端,不包含大部分像素。您需要调整垃圾箱的数量,以使其符合您的口味。

祝你好运!

【讨论】:

    猜你喜欢
    • 2019-06-17
    • 1970-01-01
    • 2021-06-15
    • 2020-07-28
    • 2015-07-04
    • 2012-08-08
    • 2018-10-08
    • 1970-01-01
    • 2012-03-10
    相关资源
    最近更新 更多