如何在 OpenCV 中创建自定义调色板的颜色直方图?

How do I create a colour histogram of a custom colour palette in OpenCV?

我有一张图片:

并且图像中的颜色范围是通过这些 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

如何创建 graph/histogram,其中 x 轴是颜色范围,值是具有该像素颜色的图像的百分比。

这是我如何解决这个问题的蛮力尝试。请注意,我正在寻找 RGB space 中的颜色距离,但众所周知,RGB 中的颜色距离并不能很好地模仿人类对颜色的感知……但这是让您入门的东西。请注意,您需要安装 numpy and matplotlibmatplotlib 允许将直方图绘制为柱状图。

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

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

要计算最小距离,请使用 numpy.sum as well as subtracting each pixel you get in the input image with each location in the lookup table via broadcasting. We square each of the distances, sum them up, then determine the location in the lookup table that gives us the minimum by 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()

我们得到的图表是:

上图说得有道理。在你的色谱开始处,有很多红色和黄色像素,它们被定义在你的色谱开始附近。绿色和白色像素更靠近末端,不包含大部分像素。您需要根据自己的喜好调整垃圾箱的数量。

祝你好运!