Python - 查找图像中的显性/最常见颜色

时间:2010-07-13 22:05:56

标签: python image image-processing colors

我正在寻找一种方法,使用python在图像中找到最主要的颜色/色调。 RGB的平均阴影或最常见的颜色都可以。我查看了Python Imaging库,在他们的手册中找不到与我想要的内容有关的内容,也简要介绍了VTK。

然而,我确实找到了一个可以满足我需要的PHP脚本,here(需要登录才能下载)。该脚本似乎将图像大小调整为150 * 150,以显示主色。然而,在那之后,我相当失落。我确实考虑过写一些可以将图像调整到一个小尺寸的东西,然后检查每个其他像素左右它的图像,虽然我想这会非常低效(尽管将这个想法作为C python模块实现可能是一个想法)。 / p>

然而,在所有这些之后,我仍然感到难过。所以我转向你,所以。是否有一种简单有效的方法来查找图像中的主色。

11 个答案:

答案 0 :(得分:52)

以下是使用PillowScipy's cluster package的代码。

为简单起见,我将文件名硬编码为“image.jpg”。调整图像大小是为了提高速度:如果您不介意等待,请注释调整大小调用。当在sample image of blue peppers上运行时,它通常表示主色是#d8c865,它大致对应于两个辣椒左下方的亮黄色区域。我说“通常”是因为使用的clustering algorithm具有一定程度的随机性。有多种方法可以改变这种情况,但为了您的目的,它可能很适合。 (如果您需要确定性结果,请查看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')

答案 1 :(得分:13)

Python Imaging Library在Image对象上有方法getcolors:

  

im.getcolors() =>列表(计数,   颜色)元组或无

我猜你仍然可以在此之前尝试调整图像大小,看看它是否表现得更好。

答案 2 :(得分:7)

如果您仍在寻找答案,那么这对我有用,虽然效率不高:

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)

答案 3 :(得分:6)

您可以使用PIL在每个维度中将图像重复调整大小2倍,直到达到1x1。我不知道PIL使用什么算法通过大因子进行缩减,因此在单个调整大小中直接转换为1x1可能会丢失信息。它可能不是最有效的,但它会给你图像的“平均”颜色。

答案 4 :(得分:3)

要添加到Peter的答案,如果PIL为您提供模式为“P”的图像或几乎任何不是“RGBA”的模式,则需要应用alpha蒙版将其转换为RGBA。您可以通过以下方式轻松完成:

if im.mode == 'P':
    im.putalpha(0)

答案 5 :(得分:3)

尝试Color-thief。它基于PIL,效果很棒。

安装

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)

它也可以找到颜色调色板

palette = color_thief.get_palette(color_count=6)

答案 6 :(得分:2)

不必像彼得建议的那样使用k均值来找到主要颜色。这使一个简单的问题复杂化了。您还受到选择的群集数量的限制,因此基本上,您需要了解所要查找的内容。

正如您所提到的和zvone的建议,使用Pillow库可以快速找到最常见/最主要的颜色。我们只需要按像素数对它们进行排序即可。

from PIL import Image

    def 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

唯一的问题是,getcolors()方法在颜色数量大于256时返回None。您可以通过调整原始图像的大小来处理它。

总而言之,这可能不是最精确的解决方案,但可以完成工作。

答案 7 :(得分:2)

您可以通过许多不同的方式执行此操作。而且,您真的不需要scipy和k均值,因为当您调整图像大小或将图像缩小到特定的调色板时,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

解决方案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

两种解决方案均得出相似的结果。后一种解决方案可能会为您提供更高的准确性,因为我们在调整图像大小时会保持宽高比。此外,由于您可以调整pallete_size,因此您将获得更多控制权。

答案 8 :(得分:1)

  

下面是一个基于c ++ Qt的例子来猜测主要的图像颜色。您可以使用PyQt并将其转换为Python等效项。

#include <Qt/QtGui>
#include <Qt/QtCore>
#include <QtGui/QApplication>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QPixmap pixmap("logo.png");
    QImage image = pixmap.toImage();
    QRgb col;
    QMap<QRgb,int> rgbcount;
    QRgb greatest = 0;

    int width = pixmap.width();
    int height = pixmap.height();

    int count = 0;
    for (int i = 0; i < width; ++i)
    {
        for (int j = 0; j < height; ++j)
        {
            col = image.pixel(i, j);
            if (rgbcount.contains(col)) {
                rgbcount[col] = rgbcount[col] + 1;
            }
            else  {
                rgbcount[col] = 1;
            }

            if (rgbcount[col] > count)  {
                greatest = col;
                count = rgbcount[col];
            }

        }
    }
    qDebug() << count << greatest;
    return app.exec();
}

答案 9 :(得分:0)

这是我根据彼得·汉森的解决方案改编的。有所不同,因为它使用kmeans ++来选择初始聚类中心,这似乎可以提供更好的结果。您也可以为确定性输出添加random_state。

import scipy.cluster
import sklearn.cluster
import numpy
from PIL import Image

def dominant_colors(image):  # PIL image input

    num_clusters = 10

    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.KMeans(
        n_clusters=num_clusters,
        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

答案 10 :(得分:0)

这是一个带有函数compute_average_image_color()的完整脚本。

只需复制并粘贴它,然后更改图像的路径。

我的图片是 img_path='./dir/image001.png'

#AVERANGE COLOR, MIN, MAX, STANDARD DEVIATION
#SELECT ONLY NOT TRANSPARENT COLOR


from PIL import Image
import sys
import os
import os.path
from os import path
import numpy as np
import math 



def compute_average_image_color(img_path):

    if not os.path.isfile(img_path):
        print(path_inp_image, 'DONT EXISTS, EXIT')
        sys.exit()

    
    #load image
    img = Image.open(img_path).convert('RGBA')
    img = img.resize((50,50))  # Small optimization


    #DEFINE SOME VARIABLES
    width, height = img.size
    r_total = 0
    g_total = 0
    b_total = 0
    count = 0
    red_list=[]
    green_list=[]
    blue_list=[]
    
    
    #READ AND CHECK PIXEL BY PIXEL
    for x in range(0, width):
        for y in range(0, height):
            r, g, b, alpha = img.getpixel((x,y))
            
            if alpha !=0:
                red_list.append(r)
                green_list.append(g)
                blue_list.append(b)
            
                r_total += r
                g_total += g
                b_total += b
                count += 1

            
    #CALCULATE THE AVRANGE COLOR, MIN, MAX, ETC             
    average_color=(round(r_total/count), round(g_total/count), round(b_total/count))
    print(average_color)
    
    red_list.sort()
    green_list.sort()
    blue_list.sort()

    
    red_min_max=[]
    green_min_max=[]
    blue_min_max=[]


    
    
    red_min_max.append(min(red_list))
    red_min_max.append(max(red_list))
    green_min_max.append(min(green_list))
    green_min_max.append(max(red_list))
    blue_min_max.append(min(blue_list))
    blue_min_max.append(max(blue_list))
    
    print('red_min_max: ', red_min_max)
    print('green_min_max: ', green_min_max)
    print('blue_min_max: ', blue_min_max)



    #variance and standard devietion
    red_stddev=round(math.sqrt(np.var(red_list)))
    green_stddev=round(math.sqrt(np.var(green_list)))
    blue_stddev=round(math.sqrt(np.var(blue_list)))

    print('red_stddev: ', red_stddev)
    print('green_stddev: ', green_stddev)
    print('blue_stddev: ', blue_stddev)






img_path='./dir/image001.png'
compute_average_image_color(img_path)