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

时间:2015-03-11 18:20:05

标签: python image opencv image-processing

我有一张图片:

enter image description here

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

1 个答案:

答案 0 :(得分:1)

这是一个相当暴力的尝试,我将如何解决这个问题。请注意,我在RGB空间中找到了颜色距离,但众所周知,RGB中的颜色距离并不能很好地模仿人类对颜色的感知......但这可以帮助您入门。请注意,您需要安装numpymatplotlibmatplotlib允许将直方图绘制为茎图。

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

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

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

现在,为了创建插值RGB查找,我在红色,绿色和蓝色通道关键点上调用numpy.interp,其中输出(y)值来自红色的那些关键点值,您定义的绿色和蓝色值,输入(x)值是虚拟输入值,从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()

我们获得的图表是:

enter image description here

上图是有道理的。在色谱的开头,有许多红色和黄色像素,它们是在光谱开始附近定义的。绿色和白色像素更接近末端并且不包括大多数像素。您需要使用垃圾箱来满足您的口味。

祝你好运!