如何使用 OpenCV 在 Python 中找到图像的平均颜色?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43111029/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 22:32:06  来源:igfitidea点击:

How to find the average colour of an image in Python with OpenCV?

pythonopencvnumpyimage-processingscikit-image

提问by Nikhith Tayambhath

I tried this code:

我试过这个代码:

import cv2
image = cv2.imread("sample.jpg")
pixel = image[200, 550]
print pixel

But I am getting error as:

但我收到错误如下:

'Nonetype' no attributes error getitem

'Nonetype' 没有属性错误getitem

This error is getting displayed after executing the third line of code.

执行第三行代码后显示此错误。

回答by Tonechas

How to fix the error

如何修复错误

There are two potential causes for this error to happen:

发生此错误的潜在原因有两个:

  1. The file name is misspelled.
  2. The image file is not in the current working directory.
  1. 文件名拼写错误。
  2. 图像文件不在当前工作目录中。

To fix this issue you should make sure the filename is correctly spelled (do case sensitive check just in case) and the image file is in the current working directory (there are two options here: you could either change the current working directory in your IDE or specify the full path of the file).

要解决此问题,您应该确保文件名拼写正确(为了以防万一,请进行区分大小写的检查)并且图像文件位于当前工作目录中(这里有两个选项:您可以更改 IDE 中的当前工作目录或指定文件的完整路径)。

Average colour vs. dominant colour

平均颜色与主色

Then to calculate the "average colour" you have to decide what you mean by that. In a grayscale image it is simply the mean of gray levels across the image. Colours are usually represented through 3-dimensional vectors whilst gray levels are scalars.

然后要计算“平均颜色”,您必须确定您的意思。在灰度图像中,它只是整个图像灰度的平均值。颜色通常通过 3 维向量表示,而灰度级是标量。

The average colour is the sum of all pixels divided by the number of pixels. However, this approach may yield a colour different to the most prominent visual color. What you might really want is dominant colorrather than average colour.

平均颜色是所有像素的总和除以像素数。但是,这种方法可能会产生与最突出的视觉颜色不同的颜色。您可能真正想要的是主色而不是平均色。

Implementation

执行

Let's go through the code slowly. We start by importing the necessary modules and reading the image:

我们慢慢看代码。我们首先导入必要的模块并读取图像:

import cv2
import numpy as np
from skimage import io

img = io.imread('https://i.stack.imgur.com/DNM65.png')[:, :, :-1]

Then we can calculate the mean of each chromatic channel following a method analog to the one proposed by @Ruan B.:

然后我们可以按照类似于@Ruan B. 提出的方法计算每个彩色通道的平均值:

average = img.mean(axis=0).mean(axis=0)

Next we apply k-means clusteringto create a palette with the most representative colours of the image (in this toy example n_colorswas set to 5).

接下来,我们应用k-means 聚类来创建具有图像最具代表性的颜色的调色板(在这个玩具示例n_colors中设置为5)。

pixels = np.float32(img.reshape(-1, 3))

n_colors = 5
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
flags = cv2.KMEANS_RANDOM_CENTERS

_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, flags)
_, counts = np.unique(labels, return_counts=True)

And finally the dominant colour is the palette colour which occurs most frequently on the quantized image:

最后,主色是量化图像上最常出现的调色板颜色:

dominant = palette[np.argmax(counts)]

Comparison of results

结果比较

To illustrate the differences between both approaches I've used the following sample image:

为了说明两种方法之间的差异,我使用了以下示例图像:

lego

乐高

The obtained values for the average colour, i.e. a colour whose components are the means of the three chromatic channels, and the dominant colour calculated throug k-means clustering are rather different:

获得的平均颜色值,即颜色的分量是三个色度通道的均值,与通过 k-means 聚类计算的主色有很大不同:

In [30]: average
Out[30]: array([91.63179156, 69.30190754, 58.11971896])

In [31]: dominant
Out[31]: array([179.3999  ,  27.341282,   2.294441], dtype=float32)

Let's see how those colours look to better understand the differences between both approaches. On the left part of the figure below it is displayed the average colour. It clearly emerges that the calculated average colour does not properly describe the colour content of the original image. In fact, there's no a single pixel with that colour in the original image. The right part of the figure shows the five most representative colours sorted from top to bottom in descending order of importance (occurrence frequency). This palette makes it evident that the dominant color is the red, which is consistent with the fact that the largest region of uniform colour in the original image corresponds to the red Lego piece.

让我们看看这些颜色如何更好地理解两种方法之间的差异。在下图的左侧部分,它显示了平均颜色。很明显,计算出的平均颜色没有正确描述原始图像的颜色内容。实际上,原始图像中没有具有该颜色的单个像素。该图的右侧部分显示了按重要性(出现频率)降序从上到下排序的五种最具代表性的颜色。这个调色板清楚地表明主色是红色,这与原始图像中最大的均匀颜色区域对应于红色乐高积木的事实是一致的。

Results

结果

This is the code used to generate the figure above:

这是用于生成上图的代码:

import matplotlib.pyplot as plt

avg_patch = np.ones(shape=img.shape, dtype=np.uint8)*np.uint8(average)

indices = np.argsort(counts)[::-1]   
freqs = np.cumsum(np.hstack([[0], counts[indices]/counts.sum()]))
rows = np.int_(img.shape[0]*freqs)

dom_patch = np.zeros(shape=img.shape, dtype=np.uint8)
for i in range(len(rows) - 1):
    dom_patch[rows[i]:rows[i + 1], :, :] += np.uint8(palette[indices[i]])

fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12,6))
ax0.imshow(avg_patch)
ax0.set_title('Average color')
ax0.axis('off')
ax1.imshow(dom_patch)
ax1.set_title('Dominant colors')
ax1.axis('off')
plt.show(fig)

TL;DR answer

TL; DR 答案

In summary, despite the calculation of the average colour - as proposed in @Ruan B.'s answer - is correct, the yielded result may not adequately represent the colour content of the image. A more sensible approach is that of determining the dominant colour through vector quantization (clustering).

总而言之,尽管平均颜色的计算 - 正如@Ruan B. 的回答所提议的 - 是正确的,但产生的结果可能无法充分代表图像的颜色内容。一种更明智的方法是通过矢量量化(聚类)来确定主色。

回答by Ruan B.

I was able to get the average color by using the following:

我能够通过使用以下方法获得平均颜色:

import cv2
import numpy
myimg = cv2.imread('image.jpg')
avg_color_per_row = numpy.average(myimg, axis=0)
avg_color = numpy.average(avg_color_per_row, axis=0)
print(avg_color)

Result:

结果:

[ 197.53434769  217.88439451  209.63799938]

Great Resource which I referenced

我参考的很棒的资源

回答by nathancy

Another approach using K-Means Clusteringto determine the dominant colors in an image with sklearn.cluster.KMeans()

另一种使用K-Means Clustering来确定图像中的主色的方法sklearn.cluster.KMeans()



Input image

输入图像

Results

结果

With n_clusters=5, here are the most dominant colors and percentage distribution

随着n_clusters=5,这里是最占优势的颜色和比例分布

[76.35563647 75.38689122 34.00842057] 7.92%
[200.99049989  31.2085501   77.19445073] 7.94%
[215.62791291 113.68567694 141.34945328] 18.85%
[223.31013152 172.76629675 188.26878339] 29.26%
[234.03101989 217.20047979 229.2345317 ] 36.03%

Visualization of each color cluster

每个颜色簇的可视化

enter image description here

在此处输入图片说明

Similarity with n_clusters=10,

n_clusters=10,相似

[161.94723762 137.44656853 116.16306634] 3.13%
[183.0756441    9.40398442  50.99925105] 4.01%
[193.50888866 168.40201684 160.42104169] 5.78%
[216.75372674  60.50807092 107.10928817] 6.82%
[73.18055782 75.55977818 32.16962975] 7.36%
[226.25900564 108.79652434 147.49787087] 10.44%
[207.83209569 199.96071651 199.48047163] 10.61%
[236.01218943 151.70521203 182.89174295] 12.86%
[240.20499237 189.87659523 213.13580544] 14.99%
[235.54419627 225.01404087 235.29930545] 24.01%

enter image description here

在此处输入图片说明

import cv2, numpy as np
from sklearn.cluster import KMeans

def visualize_colors(cluster, centroids):
    # Get the number of different clusters, create histogram, and normalize
    labels = np.arange(0, len(np.unique(cluster.labels_)) + 1)
    (hist, _) = np.histogram(cluster.labels_, bins = labels)
    hist = hist.astype("float")
    hist /= hist.sum()

    # Create frequency rect and iterate through each cluster's color and percentage
    rect = np.zeros((50, 300, 3), dtype=np.uint8)
    colors = sorted([(percent, color) for (percent, color) in zip(hist, centroids)])
    start = 0
    for (percent, color) in colors:
        print(color, "{:0.2f}%".format(percent * 100))
        end = start + (percent * 300)
        cv2.rectangle(rect, (int(start), 0), (int(end), 50), \
                      color.astype("uint8").tolist(), -1)
        start = end
    return rect

# Load image and convert to a list of pixels
image = cv2.imread('1.png')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
reshape = image.reshape((image.shape[0] * image.shape[1], 3))

# Find and display most dominant colors
cluster = KMeans(n_clusters=5).fit(reshape)
visualize = visualize_colors(cluster, cluster.cluster_centers_)
visualize = cv2.cvtColor(visualize, cv2.COLOR_RGB2BGR)
cv2.imshow('visualize', visualize)
cv2.waitKey()

回答by CopyPasteIt

If you put the image into OpenCV's BGR format, you can run this code that puts each pixel into one of four classifications:

如果将图像放入 OpenCV 的 BGR 格式,则可以运行以下代码,将每个像素放入以下四种分类之一:

blue-green-red-gray

蓝绿红灰

In the code that follows we process the image used by Tonechas,

在接下来的代码中,我们处理 Tonechas 使用的图像,

colored lego pieces

彩色乐高积木

PROGRAM

程序

import cv2 as cv
import numpy as np
from imageio import imread

image = imread('https://i.stack.imgur.com/DNM65.png')
img  = cv.cvtColor(np.array(image), cv.COLOR_RGB2BGR)
rows, cols, _ = img.shape

color_B = 0
color_G = 0
color_R = 0
color_N = 0 # neutral/gray color

for i in range(rows):
    for j in range(cols):
        k = img[i,j]
        if k[0] > k[1] and k[0] > k[2]:
            color_B = color_B + 1
            continue
        if k[1] > k[0] and k[1] > k[2]:
            color_G = color_G + 1
            continue        
        if k[2] > k[0] and k[2] > k[1]:
            color_R = color_R + 1
            continue
        color_N = color_N + 1

pix_total = rows * cols
print('Blue:', color_B/pix_total, 'Green:', color_G/pix_total, 'Red:',  color_R/pix_total, 'Gray:',  color_N/pix_total)

OUTPUT

输出

Blue: 0.2978447577378059 Green: 0.21166979188369564 Red: 0.48950158575827024 Gray: 0.0009838646202282567