Python - поиск доминирующего/наиболее распространенного цвета в изображении

Я ищу способ найти наиболее доминирующий цвет/тон изображения с помощью python. Будет либо средний оттенок, либо самый распространенный из RGB. Я просмотрел библиотеку Python Imaging и не нашел ничего, что касалось того, что я искал в их руководстве, а также кратко в VTK.

Однако я нашел PHP script, который делает то, что мне нужно, здесь (логин требуется для загрузки). script, кажется, изменяет размер изображения до 150 * 150, чтобы выявить доминирующие цвета. Однако после этого я довольно потерян. Я подумал о том, чтобы написать что-то, что изменит размер изображения до небольшого размера, а затем проверит каждый пиксель или его изображение, хотя я предполагаю, что это будет очень неэффективно (хотя реализация этой идеи как модуля C python может быть идеей).

Однако, после всего этого, я все еще в тупике. Поэтому я обращаюсь к вам, ТАК. Есть простой и эффективный способ поиска доминирующего цвета в изображении.

Ответ 1

Здесь код использует кластерный пакет Pillow and Scipy.

Для простоты я жестко закодировал имя файла как "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')

Ответ 2

Библиотека изображений Python имеет метод getcolors для объектов Image:

im.getcolors() = > список (count, цвет) или Нет

Я думаю, вы все равно можете попробовать изменить размер изображения до этого и посмотреть, будет ли он работать лучше.

Ответ 3

Если вы все еще ищете ответ, вот что сработало для меня, хотя и не очень эффективно:

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)

Ответ 4

Вы можете использовать PIL для многократного изменения размера изображения в два раза в каждом измерении до достижения 1x1. Я не знаю, какой алгоритм использует PIL для уменьшения масштаба большими факторами, поэтому переход непосредственно к 1x1 при одном изменении размера может потерять информацию. Он может быть не самым эффективным, но он даст вам "средний" цвет изображения.

Ответ 5

Чтобы добавить к Питеру ответ, если PIL дает вам изображение с режимом "P" или почти любым режимом, который не является "RGBA", тогда вам нужно применить альфа-маску, чтобы преобразовать ее в RGBA. Вы можете сделать это довольно легко с помощью:

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

Ответ 6

Попробуйте 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)

Ответ 7

Нет необходимости использовать k-средства, чтобы найти доминирующий цвет, как предлагает Питер. Это усложняет простую проблему. Вы также ограничиваете количество выбранных вами кластеров, поэтому в основном вам нужно знать, на что вы смотрите.

Как вы упомянули и как предлагает zvone, быстрое решение для поиска наиболее распространенного/доминирующего цвета - использование библиотеки подушек. Нам просто нужно отсортировать пиксели по их количеству.

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() возвращает None, когда количество цветов превышает 256. Вы можете справиться с этим, изменив размер исходного изображения.

В целом, это может быть не самое точное решение, но оно выполняет свою работу.

Ответ 8

Ниже приведен пример С++ на основе 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();
}