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

Python 如何在OpenCV中创建自定义调色板的颜色直方图?,python,image,opencv,image-processing,Python,Image,Opencv,Image Processing,我有一个形象: 图像中的颜色范围通过这些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

我有一个形象:

图像中的颜色范围通过这些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轴是颜色范围,值是具有该像素颜色的图像的百分比。

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

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

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

要计算最小距离,请使用并减去输入图像中的每个像素以及查找表中的每个位置。我们将每个距离平方,求和,然后确定查找表中的位置,该位置为我们提供最小值

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

[0, 1, 2, 3, ..., N-1]
N
是关键点的总数,输出关键点分别是红色、绿色和蓝色的关键点值。为了创建64个值的查找,我们需要在
0
N-1
之间创建64个点,我们可以使用

现在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()

我们得到的图表是:

上面的数字是有道理的。在你的光谱开始时,有很多红色和黄色的像素,它们是在你的光谱开始时定义的。绿色和白色像素更靠近末端,不包含大多数像素。你需要摆弄一下箱子的数量,才能让它符合你的口味


祝你好运

您的颜色比例在HSV图像中看起来是色调通道的缩放版本。您的
x轴是否必须根据这些颜色而变化?例如,你可以从绿色开始,朝红色走,然后在橙色/白色结束吗?@rayryeng我想x轴的实际顺序在这一点上并不重要。一旦我知道了色标是如何排序的,我就对列进行排序。@bicker色标是红色和绿色之间的线性渐变。我在edit@bicker中添加了更多关于色标的细节