Отримайте середній колір зображення за допомогою Javascript


118

Не впевнений, що це можливо, але прагнучи написати сценарій, який би повернув середнє значення hexrgb значення або значення для зображення. Я знаю, що це можна зробити в AS, але хочу зробити це в JavaScript.


19
мій друг Боаз раніше пішов по цьому шляху (сподіваємось, він задзвенів), але середній типово залишає тебе з блювотним кольором. Те, що вам справді хочеться, - це «домінуючий колір». Є кілька способів досягти цього (гістограма відтінку з / динамічне кодування тощо), але я думаю, що це, мабуть, більш точно для вашого наміченого результату.
Пол Ірландський

Відповіді:


149

AFAIK, єдиний спосіб зробити це за допомогою <canvas/>...

DEMO 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 ознайомтеся з есканвами .


1
Чи є спосіб отримати кольорову гістограму для зображення, що лежить на іншому домені?
Dan Loewenherz

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

84

Подумав, що я опублікував проект, який я нещодавно зіткнувся, щоб отримати домінуючий колір

Кольоровий злодій

Сценарій захоплення домінуючого кольору або репрезентативної кольорової палітри зображення. Використовує javascript та полотно.

Інші рішення, що згадують та пропонують домінуючий колір, ніколи насправді не відповідають на питання у відповідному контексті ("у javascript"). Сподіваємось, цей проект допоможе тим, хто хоче зробити саме це.


55

"Домінантний колір" хитрий. Що ви хочете зробити, це порівняти відстань між кожним пікселем та кожним іншим пікселем у кольоровому просторі (евклідова відстань), а потім знайти піксель, колір якого найближчий до кожного іншого кольору. Цей піксель є домінуючим кольором. Середній колір зазвичай буде грязюкою.

Я б хотів, щоб я мав тут MathML, щоб показати вам евклідову відстань. Google це.

Я виконав вищезазначене виконання в кольоровому просторі RGB, використовуючи PHP / GD тут: https://gist.github.com/cf23f8bddb307ad4abd8

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

Будь ласка, дайте мені знати, якщо ви прогресуєте на цьому фронті. Я був би радий це бачити. Я зроблю те саме.

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

Інша річ, про яку слід подумати, - це те, що RGB не є хорошим кольоровим простором для цього, оскільки евклідова відстань між кольорами в просторі RGB не дуже близька до зорової відстані. Кращий колірний простір для цього може бути LUV, але я не знайшов для цього гарної бібліотеки або будь-яких алгоритмів для перетворення RGB в LUV.

Зовсім інший підхід полягає в сортуванні кольорів за веселкою та побудові гістограми з толерантністю для врахування різних відтінків кольору. Я цього не пробував, тому що сортувати кольори в веселці важко, так само як і кольорові гістограми. Я можу спробувати це далі. Знову, дайте мені знати, якщо ви досягнете будь-якого прогресу тут.


6
Це одна з тих відповідей, на яку я хотів би зробити більше, ніж просто +1 :-)
Девід Джонстоун

3
Відмінна відповідь, хоча чіткого рішення не було винесено. Це допоможе мені вирішити свій наступний крок ...
Брест

Це прекрасна відповідь. Я написав декілька вузлових модулів для обчислення евклідової відстані не в LUV, а в кольоровому просторі L a b *. Дивіться github.com/zeke/color-namer і github.com/zeke/euclidean-distance
Zeke

1
@user: Мені було просто цікаво: чи не скажемо, скажімо, 1% вибірки для великих зображень, щоб прискорити обчислення?
jackthehipster

Звучить як геометрична медіана. Може, це може допомогти? stackoverflow.com/questions/12934213/… і якщо потрібна точка повинна існувати в оригінальному наборі: en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Lawrence Weru

16

По-перше: це можна зробити без HTML5 Canvas або SVG.
Насправді, хтось просто зумів генерувати файли PNG на стороні клієнта за допомогою JavaScript , без полотна або SVG, використовуючи схему URI даних .

По-друге: вам може взагалі не знадобитися Canvas, SVG або будь-яке з перерахованого вище.
Якщо вам потрібно лише обробляти зображення на стороні клієнта, не змінюючи їх, все це не потрібно.

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


+1 для розчину bytestream. Це добре, якщо єдиний варіант - це зробити це на клієнтській програмі.
loretoparisi

11

Я б сказав через тег HTML canvas.

Ви можете знайти тут повідомлення від @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, потім округлити червоний, зелений та синій масиви і, нарешті, перетворити їх назад у код HEX.


є шанс, що існує альтернатива IE для рішення полотна?
більше,

@ Ben4Himv: Є попередній перегляд платформи IE9 ;-), але серйозно, боюся, ні.
Енді Е


4

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


2

Рішення "все в одному"

Я б особисто поєднав Color Thief разом із цією модифікованою версією Name the 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


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);
}

1

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


-2

Існує інтернет-інструмент pickimagecolor.com, який допоможе вам знайти середній або домінуючий колір зображення. Вам просто потрібно завантажити зображення зі свого комп'ютера, а потім натиснути на зображення. Він дає середній колір у HEX, RGB та HSV. Він також знаходить кольорові відтінки, які відповідають цьому кольорові на вибір. Я використовував його кілька разів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.