По работе делал листалку фотографий. Сопровождающий текст было решено положить на усреднённый цвет фото. Тема среднего цвета заинтересовала, и я решил
посмотреть какие ещё варианты можно использовать в верстке.
Фон
Рассчитываем средний цвет фотографии и устанавливаем цвет подложки. Пример
Градиент
Средний цвет высчитывается у верхней или нижней части картинки и используется в подложке для текста. Между картинкой и подложкой установлен плавный градиент. Стиль Яндекс.Дзена. Пример
Градиент в стиле Minecraft — средний цвет высчитывается частями (горизональными полосками). Пример
Рамка
Как багет у картины, средний цвет высчитывается отдельно у каждой из сторон фотографии.
Пример
Тень
Вычисленный средний цвет используется в задании цвета падающей тени. Пример
В CSS у элемента можно задать несколько теней. Для каждой из сторон фотографии вычислим средний цвет и установим отдельную тень. Пример
Видео
Предыдущий пример применим в динамике для видео. Пример
Разделим стороны экрана на большее количество частей (30), в которых вычислим средний цвет для отбрасываемой тени, совсем как у Philips Ambilight. Пример
Текстовая фотография
Фотографию заполняем текстом, под каждым символом вычисляем средний цвет и применяем его к символу. Пример
Использование
Средний цвет в примерах вычисляется с помощью небольшого пакета «fast-average-color». При подсчёте цвета учитывается прозрачность. По умолчанию используется квадратичный алгоритм, т.к. при простом усреднении цвет становится более тёмным.
npm i -D fast-average-color
Примеры
Для получения среднего цвета из загруженных картинок, видео и canvas’a используется метод .getColor():
<html>
<body>
...
<div class="image-container">
<img src="image.png" />
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</div>
<script src="https://unpkg.com/fast-average-color/dist/index.min.js"></script>
<script>
window.addEventListener('load', function() {
var
fac = new FastAverageColor(),
container = document.querySelector('.image-container'),
color = fac.getColor(container.querySelector('img'));
container.style.backgroundColor = color.rgba;
container.style.color = color.isDark ? '#fff' : '#000';
console.log(color);
// {
// error: null,
// rgb: 'rgb(255, 0, 0)',
// rgba: 'rgba(255, 0, 0, 1)',
// hex: '#ff0000',
// hexa: '#ff0000ff',
// value: [255, 0, 0, 255],
// isDark: true,
// isLight: false
// }
}, false);
</script>
</body>
</html>
А для картинок, которые находятся в процессе загрузки — .getColorAsync():
<html>
<body>
...
<div class="image-container">
<img src="image.png" />
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</div>
<script src="https://unpkg.com/fast-average-color/dist/index.min.js"></script>
<script>
var
fac = new FastAverageColor(),
container = document.querySelector('.image-container');
fac.getColorAsync(container.querySelector('img'), function(color) {
container.style.backgroundColor = color.rgba;
container.style.color = color.isDark ? '#fff' : '#000';
console.log(color);
// {
// error: null,
// rgb: 'rgb(255, 0, 0)',
// rgba: 'rgba(255, 0, 0, 1)',
// hex: '#ff0000',
// hexa: '#ff0000ff',
// value: [255, 0, 0, 255],
// isDark: true,
// isLight: false
// }
});
</script>
</body>
</html>
Для картинок и видео с разных доменов стоит не забывать про CORS.
Ссылки:
Комментарии (17)
dom1n1k
05.08.2018 23:50Насколько я понимаю, «квадратичный» алгоритм — это переход в «почти линейное» пространство RGB с грубым учетом гаммы. Гамма принимается равной 2, что конечно же дает очень приблизительный результат, но гораздо лучше, чем если не учитывать её вовсе.
AngReload
06.08.2018 06:05Странно что в примерах сравниваются настройки simple и sqrt с усреднением цветов в lab. Lab для этого не подходит, лучше бы показали сравнение настоящим линейным усреднением.
dom1n1k
06.08.2018 09:24Почему lab не подходит?
AngReload
06.08.2018 10:47Цель у всех этих рамок создать environment, как бы свет идущий с вне поля чёткого зрения, но из той же среды.
По этому усредняться цвет должен так же как это произошло бы в реальном оптическом эксперименте, то есть линейно.
Lab же пытались сделать идеально соответствующим человеческим ощущениям от цвета. Он не линеен, не могу точно сказать за эволюцию, но наверное он лучше подходит для классификации объектов и устранении влияния освещения, и это совсем противоположная задача.
Так, например, сравним гамму: у линейного света — 1, у SRGB ? 2.2, у Lab — 3. И в первом примере, где смешиваются черный и белый, это видно: у Lab получается даже более тёмный цвет, чем у SRGB. Далее, при смешивании синего и жёлтого у Lab получается розовый.
cruzo
06.08.2018 11:12На первый взгляд скрипт круче color-thief получился. Здорово, кстати, у вас выглядит amdient light.
shybovycha
06.08.2018 13:06+2Есть разница между "средним цветом" и "цветом, встречающимся наиболее часто" — так же, как и между медианой, средним и модой: https://www.purplemath.com/modules/meanmode.htm
В вашем алгоритме используется среднее значение. Но, как и написано в указанной мною статье, среднее значение может даже и не появляться в выборке:
data = [ 13, 13, 13, 13, 14, 14, 16, 18, 21 ] mean(data) = 15 mode(data) = 13
Поэтому у вас на примерах небо темнее полоски на фоне, а на картинке с листиками фоном вообще болотный цвет.
Я понимаю, для разных задач — разные решения, и в некоторых случаях подобное решение может быть оправдано затратами времени. Но тем не более, может стоит попробовать и немного другой подход?
anprs
06.08.2018 14:56Прочитал заголовок «Градиент». Посмотрел на картинку, не понял. Прочитал абзац «Средний цвет высчитывается у верхней или нижней части картинки и используется в подложке для текста. Между картинкой и подложкой установлен плавный градиент». Присмотрелся к картинкам и понял что эту подложку я считал частью фотографии. Как мне кажется, это наиболее органичное применение «среднего цвета» из всех приведённых примеров
amarao
06.08.2018 15:50Варианты с градиентами и рамочками уходят в практику, а вот обсуждение, какой цвет для фотографии средний — это же потрясающе интересный вопрос.
Вот несколько интересных идей:
1. Минимальное расстояние (сумма расстояний) по цвету до каждого пиксела картинки. Высчитываем цветовое расстояние (например, как квадратный корень из суммы квадратов H, S, L) для пиксела, суммируем его, минимизируем, получаем «самый близкий ко всем». Для разноцветья — серый, для фотографий с общим цветом (например, синеватых) — усреднённо синеватый.
2. Минимизируем расстояние в пятимерном пространстве, где три кординаты — HSL, а ещё две — расстояние на картинке. Таким образом, чем дальше пиксел от рамочки, тем меньше он влияет на её цвет.
Можно придумать кучу вариантов. Уход в градиентики и прочие «нарисовать рамочку» уже не так интересно.
Kuorell
06.08.2018 21:15А для чего вы умножение цвет на alpha? (выглядит так, будто вы и сходите из предположегия, что любая картинка на черном фоне (мб стоит передавать предполагаемый цвет фона и смешивать?)
hcodes Автор
06.08.2018 21:20AngReload
06.08.2018 22:02На хабре есть статья про premultiplied alpha — https://habr.com/post/328386/
GCU
06.08.2018 21:15По логике sqrt алгоритм — это гамма-коррекция 2. Почему бы не использозать страндартную 2.2?
AngReload
06.08.2018 22:14Наверное, побоялись что будет медленно. Но сравнения скорости нету. И можно ведь заранее посчитать все 256 значений, а потом просто доставать результат по индексу массива.
Кстати, ести точно, то стандартная не просто 2.2, а такая формула:
GCU
07.08.2018 13:15Можно считать количество каждого встречающегося значения 0-255 в массиве и по этой статистике уже пересчитывать разные варианты «усреднения», не сканируюя всю картинку заново, раз «усреднение» всё равно поканальное. Хотя это скорее оптимизация для больших размеров картинки.
Поскольку это производные от изображения данные, их можно рассчитать заранее (Каким-нибудь ImageMagick, например) и передавать уже готовые рядом с картинкой. Онлайн-рассчёт это хорошо, но пользователи устройств с питанием от аккумулятора, возможно, будут недовольны раходом батареи :(
ThisMan
Пример с рамкой выглядит клево, только если у сторон близкие цвета, иначе получается 4 вариант с костром, который смотрится не очень