Получить средний цвет изображения через Javascript

118

Не уверен, что это возможно, но хочу написать сценарий, который бы возвращал среднее значение hexили rgbзначение для изображения. Я знаю, что это можно сделать в AS, но хочу сделать это в JavaScript.

bgreater
источник
19
мой друг Вооз уже проходил этот путь раньше (надеюсь, он подхватывает), но средний цвет обычно оставляет у вас цвет, похожий на рвоту. На самом деле вам нужен «доминирующий цвет». Есть несколько способов добиться этого (гистограмма оттенков с динамическим ведением и т. Д.), Но я думаю, что это, вероятно, более точно для вашего предполагаемого результата.
Paul Irish,

Ответы:

149

AFAIK, единственный способ сделать это - с <canvas/>...

ДЕМО V2 : http://jsfiddle.net/xLF38/818/

Обратите внимание, это будет работать только с изображениями в том же домене и в браузерах, поддерживающих холст HTML5:

function getAverageRGB(imgEl) {

    var blockSize = 5, // only visit every 5 pixels
        defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
        canvas = document.createElement('canvas'),
        context = canvas.getContext && canvas.getContext('2d'),
        data, width, height,
        i = -4,
        length,
        rgb = {r:0,g:0,b:0},
        count = 0;

    if (!context) {
        return defaultRGB;
    }

    height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
    width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

    context.drawImage(imgEl, 0, 0);

    try {
        data = context.getImageData(0, 0, width, height);
    } catch(e) {
        /* security error, img on diff domain */
        return defaultRGB;
    }

    length = data.data.length;

    while ( (i += blockSize * 4) < length ) {
        ++count;
        rgb.r += data.data[i];
        rgb.g += data.data[i+1];
        rgb.b += data.data[i+2];
    }

    // ~~ used to floor values
    rgb.r = ~~(rgb.r/count);
    rgb.g = ~~(rgb.g/count);
    rgb.b = ~~(rgb.b/count);

    return rgb;

}

Для IE проверьте excanvas .

Джеймс
источник
1
Есть ли способ получить гистограмму цвета для изображения, лежащего в другом домене?
Дэн Лёвенгерц,
1
Дэн: Я думаю, вы столкнетесь с ограничением перекрестного происхождения, если попытаетесь получить доступ к данным изображения на изображении из другого домена. Это облом.
8
Я думаю, что есть рабочее место для синего и зеленого значения, вы пишете, 'rgb('+rgb.r+','+rgb.b+','+rgb.g+')'и это должно быть 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'. странно, когда преобладает синий цвет, а получается зеленый :).
Ариона Риан
7
Нашел отличный обходной путь для ограничений перекрестного происхождения: просто добавьте img.crossOrigin = '';перед установкой srcатрибута. Найдено на coderwall.com/p/pa-2uw
mhu
Не нужно считать, так как count === length / 4 / blockSize;)
yckart 05
84

Подумал, что опубликую проект, с которым недавно столкнулся, чтобы получить доминирующий цвет:

Цвет вор

Скрипт для захвата доминирующего цвета или репрезентативной цветовой палитры изображения. Использует javascript и холст.

Другие решения, в которых упоминается и предлагается доминирующий цвет, на самом деле никогда не отвечают на вопрос в надлежащем контексте («в javascript»). Надеюсь, этот проект поможет тем, кто хочет именно этого.

Дж. Чейз
источник
55

«Доминирующий цвет» - это сложно. Что вы хотите сделать, так это сравнить расстояние между каждым пикселем и каждым другим пикселем в цветовом пространстве (Евклидово расстояние), а затем найти пиксель, цвет которого ближе всего к любому другому цвету. Этот пиксель является доминирующим цветом. Обычно средний цвет грязи.

Хотел бы я иметь здесь MathML, чтобы показать вам евклидово расстояние. Поищи в Гугле.

Я выполнил вышеуказанное выполнение в цветовом пространстве RGB с помощью PHP / GD здесь: https://gist.github.com/cf23f8bddb307ad4abd8

Однако это очень затратно с точки зрения вычислений. Это приведет к сбою вашей системы на больших изображениях и определенно приведет к сбою вашего браузера, если вы попробуете его в клиенте. Я работал над рефакторингом своего выполнения, чтобы: - сохранять результаты в таблице поиска для будущего использования в итерации по каждому пикселю. - разделить большие изображения на сетки 20px 20px для локализованного доминирования. - использовать евклидово расстояние между x1y1 и x1y2, чтобы вычислить расстояние между x1y1 и x1y3.

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

Canvas - определенно лучший способ сделать это в клиенте. SVG - нет, SVG - векторный. После того, как я завершу выполнение, следующее, что я хочу сделать, - это запустить его на холсте (возможно, с помощью веб-мастера для расчета общего расстояния каждого пикселя).

Еще одна вещь, о которой следует подумать, заключается в том, что RGB не является хорошим цветовым пространством для этого, потому что евклидово расстояние между цветами в пространстве RGB не очень близко к визуальному расстоянию. Лучшим цветовым пространством для этого может быть LUV, но я не нашел для этого хорошей библиотеки или каких-либо алгоритмов для преобразования RGB в LUV.

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


источник
6
Это один из тех ответов, для которых я хотел бы сделать больше, чем просто +1 :-)
Дэвид Джонстон
3
Отличный ответ, хотя четкого решения не было. Это поможет мне определиться с моим следующим шагом ...
bgreater
Это отличный ответ. Я написал модуль с несколькими узлами, чтобы вычислять евклидово расстояние не в LUV, а в цветовом пространстве L a b *. См. Github.com/zeke/color-namer и github.com/zeke/euclidean-distance
Зик
1
@user: Мне просто интересно: а не хватит ли, скажем, 1% выборки на больших изображениях для ускорения вычислений?
jackthehipster 01
Похоже на геометрическую медиану. Может это поможет? stackoverflow.com/questions/12934213/… и если искомая точка должна существовать в исходном наборе: en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Лоуренс Веру,
16

Во-первых: это можно сделать без HTML5 Canvas или SVG.
Фактически, кому-то просто удалось сгенерировать клиентские файлы PNG с использованием JavaScript , без холста или SVG, с использованием схемы URI данных. .

Во-вторых: вам может вообще не понадобиться Canvas, SVG или что-либо из вышеперечисленного.
Если вам нужно обрабатывать изображения только на стороне клиента, без изменяя их, все это не нужно.

Вы можете получить адрес источника из тега img на странице, сделать для него запрос XHR - он, скорее всего, будет поступать из кеша браузера - и обработать его как поток байтов из Javascript.
Вам потребуется хорошее понимание формата изображения. (Вышеупомянутый генератор частично основан на источниках libpng и может стать хорошей отправной точкой.)

Андраш Васс
источник
+1 для решения с байтовым потоком. Это хорошо, если единственный вариант - сделать это в клиентском приложении.
loretoparisi
11

Я бы сказал через HTML-тег холста.

Вы можете найти здесь сообщение от @Georg, в котором говорится о небольшом коде разработчика Opera:

// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;

// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i  ] = 255 - pix[i  ]; // red
    pix[i+1] = 255 - pix[i+1]; // green
    pix[i+2] = 255 - pix[i+2]; // blue
    // i+3 is alpha (the fourth element)
}

// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);

Это инвертирует изображение, используя значения R, G и B каждого пикселя. Вы можете легко сохранить значения RGB, затем округлить массивы Red, Green и Blue и, наконец, преобразовать их обратно в HEX-код.

rnaud
источник
есть ли шанс, что есть альтернатива IE решению холста?
bgreater
@ Ben4Himv: Есть предварительная версия платформы IE9 ;-) но если серьезно, боюсь, что нет.
Andy E
4

Javascript не имеет доступа к данным цвета отдельных пикселей изображения. По крайней мере, до html5 ... в этот момент логично, что вы сможете нарисовать изображение на холсте, а затем проверить холст (возможно, я никогда не делал этого сам).

Джоэл Мартинес
источник
2

Универсальное решение

Я лично совмещал бы Color Thief с этой модифицированной версией Name that Color, чтобы получить более чем достаточный массив результатов доминирующего цвета для изображений.

Пример:

Рассмотрим следующее изображение:

введите описание изображения здесь

Вы можете использовать следующий код для извлечения данных изображения, относящихся к доминирующему цвету:

let color_thief = new ColorThief();
let sample_image = new Image();

sample_image.onload = () => {
  let result = ntc.name('#' + color_thief.getColor(sample_image).map(x => {
    const hex = x.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
    
  }).join(''));
  
  console.log(result[0]); // #f0c420     : Dominant HEX/RGB value of closest match
  console.log(result[1]); // Moon Yellow : Dominant specific color name of closest match
  console.log(result[2]); // #ffff00     : Dominant HEX/RGB value of shade of closest match
  console.log(result[3]); // Yellow      : Dominant color name of shade of closest match
  console.log(result[4]); // false       : True if exact color match
};

sample_image.crossOrigin = 'anonymous';
sample_image.src = document.getElementById('sample-image').src;
Грант Миллер
источник
1

Речь идет о «квантовании цвета», которое имеет несколько подходов, таких как MMCQ (модифицированное квантование с медианным отсечением ) или OQ (квантование октодерева). Другой подход использует K-средние для получения кластеров цветов.

Я собрал все вместе здесь, так как я нашел решение для того, tvOSгде есть подмножество XHTML, в котором нет <canvas/>элемента:

Генерация доминирующих цветов для изображения RGB с помощью XMLHttpRequest

loretoparisi
источник
1

Менее точный, но самый быстрый способ получить средний цвет изображения с datauriподдержкой:

function get_average_rgb(img) {
    var context = document.createElement('canvas').getContext('2d');
    if (typeof img == 'string') {
        var src = img;
        img = new Image;
        img.setAttribute('crossOrigin', ''); 
        img.src = src;
    }
    context.imageSmoothingEnabled = true;
    context.drawImage(img, 0, 0, 1, 1);
    return context.getImageData(1, 1, 1, 1).data.slice(0,3);
}
350D
источник
1

Как указано в других ответах, часто вам действительно нужен доминирующий цвет, а не средний цвет, который имеет тенденцию быть коричневым. Я написал сценарий, который получает наиболее распространенный цвет, и разместил его в этой статье.

dctalbot
источник
-2

Существует онлайн-инструмент pickimagecolor.com, который поможет вам найти средний или доминирующий цвет изображения. Вам просто нужно загрузить изображение со своего компьютера, а затем щелкнуть изображение. Он дает средний цвет в HEX, RGB и HSV. Он также находит цветовые оттенки, соответствующие этому цвету, на выбор. Я использовал его несколько раз.

Шубхам Агравал
источник