Blu*_*ers 54 python image colors image-processing
我正在寻找一种方法,使用python在图像中找到最主要的颜色/色调.RGB的平均阴影或最常见的颜色都可以.我查看了Python Imaging库,但在他们的手册中找不到与我要查找的内容有关的内容,也简要介绍了VTK.
然而,我确实找到了一个PHP脚本,它可以满足我的需求,这里需要登录(需要登录才能下载).该脚本似乎将图像大小调整为150*150,以显示主色.然而,在那之后,我相当失落.我确实考虑过写一些可以将图像调整到一个小尺寸的东西,然后检查每个其他像素左右它的图像,虽然我想这会非常低效(虽然实现这个想法作为C python模块可能是一个想法).
然而,在所有这些之后,我仍然难过.所以我转向你,所以.是否有一种简单有效的方法来查找图像中的主色.
Pet*_*sen 56
为简单起见,我将文件名硬编码为"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))
Run Code Online (Sandbox Code Playgroud)
注意:当我将群集数量扩大到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')
Run Code Online (Sandbox Code Playgroud)
Art*_*kyi 33
试试色贼。它基于Pillow并且工作得很棒。
安装
pip install colorthief
用法
from colorthief import ColorThief
color_thief = ColorThief('/path/to/imagefile')
# get the dominant color
dominant_color = color_thief.get_color(quality=1)
Run Code Online (Sandbox Code Playgroud)
它还可以找到调色板
palette = color_thief.get_palette(color_count=6)
Run Code Online (Sandbox Code Playgroud)
zvo*_*one 14
Python Imaging Library在Image对象上有方法getcolors:
im.getcolors() =>(count,color)元组列表或None
我想你仍然可以尝试在此之前调整图像大小,看看它是否表现得更好.
这是我根据 Peter Hansen 的解决方案进行的改编。
import scipy.cluster
import sklearn.cluster
import numpy
from PIL import Image
def dominant_colors(image): # PIL image input
image = image.resize((150, 150)) # optional, to reduce time
ar = numpy.asarray(image)
shape = ar.shape
ar = ar.reshape(numpy.product(shape[:2]), shape[2]).astype(float)
kmeans = sklearn.cluster.MiniBatchKMeans(
n_clusters=10,
init="k-means++",
max_iter=20,
random_state=1000
).fit(ar)
codes = kmeans.cluster_centers_
vecs, _dist = scipy.cluster.vq.vq(ar, codes) # assign codes
counts, _bins = numpy.histogram(vecs, len(codes)) # count occurrences
colors = []
for index in numpy.argsort(counts)[::-1]:
colors.append(tuple([int(code) for code in codes[index]]))
return colors # returns colors in order of dominance
Run Code Online (Sandbox Code Playgroud)
它使用 kmeans++ 来选择初始聚类中心,从而提供更好的结果。(虽然 kmeans++ 可能不是选择聚类中心的最快方法)
使用速度sklearn.cluster.MiniBatchKMeans明显更快,并且提供与默认 KMeans 算法非常相似的颜色。您始终可以尝试较慢的速度sklearn.cluster.KMeans并比较结果并决定这种权衡是否值得。
我正在使用 random_state 来获得一致的输出(我相信原始版本scipy.cluster.vq.kmeans也有一个seed参数)。在添加随机状态之前,我发现某些输入可能具有显着不同的输出。
我决定对一些解决方案进行非常粗略的基准测试。
| 方法 | 时间(100 次迭代) |
|---|---|
| 彼得·汉森 (kmeans) | 58.85 |
| 阿乔姆·伯纳茨基(色彩小偷) | 61.29 |
| Artem Bernatskyi(色贼调色板) | 15.69 |
| Pithikos(PIL 调整大小) | 0.11 |
| Pithikos(调色板) | 1.68 |
| 我的(小批量kmeans) | 6.31 |
您可以通过许多不同的方式来做到这一点。而且您实际上并不需要 scipy 和 k-means,因为在您调整图像大小或将图像缩小到某个调色板时,Pillow 在内部已经为您做了这些。
解决方案 1:将图像大小调整为 1 像素。
def get_dominant_color(pil_img):
img = pil_img.copy()
img.convert("RGB")
img.resize((1, 1), resample=0)
dominant_color = img.getpixel((0, 0))
return dominant_color
Run Code Online (Sandbox Code Playgroud)
解决方案 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
Run Code Online (Sandbox Code Playgroud)
两种解决方案都给出了相似的结果。后一种解决方案可能会为您提供更高的准确性,因为我们在调整图像大小时保持纵横比。此外,您可以获得更多控制权,因为您可以调整pallete_size.
如果您仍在寻找答案,那么这对我有用,虽然不是非常有效:
from PIL import Image
def compute_average_image_color(img):
width, height = img.size
r_total = 0
g_total = 0
b_total = 0
count = 0
for x in range(0, width):
for y in range(0, height):
r, g, b = img.getpixel((x,y))
r_total += r
g_total += g
b_total += b
count += 1
return (r_total/count, g_total/count, b_total/count)
img = Image.open('image.png')
#img = img.resize((50,50)) # Small optimization
average_color = compute_average_image_color(img)
print(average_color)
Run Code Online (Sandbox Code Playgroud)
没有必要像 Peter 建议的那样使用 k-means 来找到主色。这使一个简单的问题变得过于复杂。您还受到您选择的集群数量的限制,因此基本上您需要了解您正在查看的内容。
正如您所提到的和 zvone 所建议的,找到最常见/主要颜色的快速解决方案是使用Pillow库。我们只需要按像素数对像素进行排序。
from PIL import Image
def find_dominant_color(filename):
#Resizing parameters
width, height = 150,150
image = Image.open(filename)
image = image.resize((width, height),resample = 0)
#Get colors from image object
pixels = image.getcolors(width * height)
#Sort them by count number(first element of tuple)
sorted_pixels = sorted(pixels, key=lambda t: t[0])
#Get the most frequent color
dominant_color = sorted_pixels[-1][1]
return dominant_color
Run Code Online (Sandbox Code Playgroud)
唯一的问题是getcolors()当颜色数量超过256时该方法返回None。您可以通过调整原始图像的大小来处理它。
总而言之,它可能不是最精确的解决方案,但它可以完成工作。
您可以使用PIL在每个维度中将图像重复调整大小2倍,直到达到1x1.我不知道PIL使用什么算法通过大因子进行缩减,因此在单个调整大小中直接转换为1x1可能会丢失信息.它可能不是最有效的,但它会给你图像的"平均"颜色.