如何找出图像中最主要/最常见的颜色?
我想用Python找出一张图片中最显眼的颜色或色调。无论是平均色调还是RGB中最常见的颜色都可以。我查过Python图像库,但在他们的手册里没找到相关的内容,也简单看过VTK。
不过,我找到了一段PHP脚本,可以实现我需要的功能,在这里(下载需要登录)。这个脚本似乎会把图片缩小到150*150的尺寸,以突出主要颜色。不过,之后我就有点迷茫了。我考虑过写一个程序,把图片缩小到小尺寸,然后检查每隔一个像素的颜色,但我想这样效率会很低(不过把这个想法做成C语言的Python模块可能是个不错的主意)。
但是,经过这些尝试,我还是不知道该怎么做。有没有简单又高效的方法来找出图片中的主色调呢?
11 个回答
你可以用很多不同的方法来做到这一点。其实你并不需要使用scipy和k-means,因为Pillow库在你调整图片大小或者把图片减少到某种调色板时,内部已经帮你处理好了这些事情。
解决方案1: 把图片缩小到1个像素。
def get_dominant_color(pil_img):
img = pil_img.copy()
img = img.convert("RGBA")
img = img.resize((1, 1), resample=0)
dominant_color = img.getpixel((0, 0))
return dominant_color
解决方案2: 把图片的颜色减少到一个调色板。
def get_dominant_color(pil_img, palette_size=16):
# Resize image to speed up processing
img = pil_img.copy()
img.thumbnail((100, 100))
# Reduce colors (uses k-means internally)
paletted = img.convert('P', palette=Image.ADAPTIVE, colors=palette_size)
# Find the color that occurs most often
palette = paletted.getpalette()
color_counts = sorted(paletted.getcolors(), reverse=True)
palette_index = color_counts[0][1]
dominant_color = palette[palette_index*3:palette_index*3+3]
return dominant_color
这两种解决方案的结果差不多。第二种方案可能更准确,因为在调整图片大小时我们保持了宽高比。而且你可以更好地控制,因为你可以调整palette_size
。
补充说明: 获取一份主要颜色的列表。
def get_dominant_colors(pil_img, palette_size=16, num_colors=10):
# Resize image to speed up processing
img = pil_img.copy()
img.thumbnail((100, 100))
# Reduce colors (uses k-means internally)
paletted = img.convert('P', palette=Image.ADAPTIVE, colors=palette_size)
# Find the color that occurs most often
palette = paletted.getpalette()
color_counts = sorted(paletted.getcolors(), reverse=True)
dominant_colors = []
for i in range(num_colors):
palette_index = color_counts[i][1]
dominant_colors.append(palette[palette_index*3:palette_index*3+3])
return dominant_colors
试试 Color Thief
,可以通过 PyPI 安装。唯一的要求是 Pillow
。
安装
pip install colorthief
使用方法
from colorthief import ColorThief
color_thief = ColorThief('/path/to/imagefile')
获取主色调
dominant_color = color_thief.get_color(quality=1)
输出结果是一个元组 (r, g, b)
,表示颜色的红、绿、蓝值。
注意:参数 quality
是可选的,可以在质量和速度之间做选择。设置为1时质量最高。数字越大,返回颜色的速度越快,但可能不是视觉上最突出的颜色。
构建调色板
palette = color_thief.get_palette(color_count=6, quality=1)
输出结果是一个元组列表 (r, g, b)
,每个元组代表一种颜色。
参数 color_count
是调色板的大小,也就是最多可以有多少种颜色。
注意:参数 quality
是可选的,可以在质量和速度之间做选择。设置为1时质量最高。数字越大,生成调色板的速度越快,但可能会漏掉一些颜色。
为了简单起见,我把文件名写死了,定为“image.jpg”。调整图片大小是为了加快处理速度:如果你不介意等待,可以把调整大小的那行代码注释掉。当在这个示例图片上运行时,
它通常会显示主导颜色是 #d8c865,这大致对应于两个甜椒左下角的亮黄色区域。我说“通常”是因为使用的聚类算法有一定的随机性。你可以通过不同的方法来改变这个结果,但对于你的需求来说,这个结果可能就足够了。(如果你需要确定的结果,可以看看kmeans2()的选项。)
from __future__ import print_function
import binascii
import struct
from PIL import Image
import numpy as np
import scipy
import scipy.misc
import scipy.cluster
NUM_CLUSTERS = 5
print('reading image')
im = Image.open('image.jpg')
im = im.resize((150, 150)) # optional, to reduce time
ar = np.asarray(im)
shape = ar.shape
ar = ar.reshape(scipy.product(shape[:2]), shape[2]).astype(float)
print('finding clusters')
codes, dist = scipy.cluster.vq.kmeans(ar, NUM_CLUSTERS)
print('cluster centres:\n', codes)
vecs, dist = scipy.cluster.vq.vq(ar, codes) # assign codes
counts, bins = scipy.histogram(vecs, len(codes)) # count occurrences
index_max = scipy.argmax(counts) # find most frequent
peak = codes[index_max]
colour = binascii.hexlify(bytearray(int(c) for c in peak)).decode('ascii')
print('most frequent is %s (#%s)' % (peak, colour))
注意:当我把要找的聚类数量从5增加到10或15时,结果经常会出现偏绿色或偏蓝色的情况。考虑到输入的图片,这些结果也是合理的……我也无法判断那张图片中哪个颜色是真正的主导色,所以我不怪这个算法!
还有一个小额外:保存缩小后的图片,并只保留N种最常见的颜色:
# bonus: save image using only the N most common colours
import imageio
c = ar.copy()
for i, code in enumerate(codes):
c[scipy.r_[scipy.where(vecs==i)],:] = code
imageio.imwrite('clusters.png', c.reshape(*shape).astype(np.uint8))
print('saved clustered image')