Делаем превью сайтов в стиле Yandex Браузера

yandex browserПрошло уже почти два месяца как Yandex порадовал некоторых пользователей новым продуктом — Yandex Браузером. Несмотря на невероятную динамику развития продуктов в этой области (Chrome и Firefox), Яндексу удалось привнести в свой браузер ряд новых идей.

Из всех особенностей этого браузера больше всего меня зацепило их дизайнерское решение относительно изображений сайтов в «быстрых закладках» (Speed dial). Люди любят глазами и поэтому приятно видеть у себя в новом табе не пустую белую страницу, а красочные картинки. Беда только в том, что лично я, чаще всего, смотрю на подпись под этой картинкой или же на favicon, так как по скриншоту сайта бывает очень сложно его узнать. Эту проблему дизайнеры яндекса, на мой взгляд, решили очень элегантно. В данном посте мы посмотрим, как реализовать эту идею на клиентской стороне.



Суть идеи заключается в следующем:

  1. Получаем favicon сайта
  2. Определяем доминирующий в favicon цвет
  3. Рисуем прямоугольник с доминирующим цветом и вставляем в него favicon
  4. Для пущей привлекательности сверху накладывается градиент.

Звучит действительно очень просто. Наиболее сложным моментом здесь является определение доминирующего цвета в favicon-е. Чтобы это сделать, нам нужно решить три задачи:

  1. Получить favicon.
  2. Получить доступ к значению каждого пикселя изображения.
  3. Определиться с алгоритмом определения доминирующего цвета.


Получение favicon


Для того чтобы получить favicon, можно либо написать на сервере некий обработчик, который по домену будет искать и возвращать favicon, либо можно подглядеть как это делает Яндекс браузер… А делает он это при помощи запроса на одноименный сервис яндекса. Например такой запрос:

GET favicon.yandex.net/favicon/habrahabr.ru

Вернет вот такую вот картинку:

image


Доступ к пикселям изображения


Относительно второй задачи — единственным способом получить доступ к значениям пикселей изображения на клиентской стороне является использование элемента . Загрузив изображение в canvas мы сможем получить значение произвольного пикселя.

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

Таким образом, чтобы воспользоваться элементом canvas для поиска доминирующего цвета в favicon-е, необходимо организовать у себя на сервере некий прокси, который скачивал бы favicon (например с сервиса favicon.yandex.ru) и возвращал бы его назад к вам на страницу.

В связи с этим реализация такого превью сайтов чисто на клиентской стороне, увы, не получится. На самом деле раз уж нам все равно нужен сервер в качестве прокси для изображений, то мы могли бы перенести на сервер и вычисление доминирующего цвета, получая назад на страницу уже не иконку, а цвет. Однако то, как это реализовать на серверной стороне, не так интересно и сильно разнородно, так как в зависимости от используемого на сервере языка (Python, PHP, Java) реализация будет разная. Поэтому мы рассмотрим как это сделать на клиенте с помощью элемента canvas.

Алгоритм определения доминирующего цвета


Существует множество алгоритмов определения доминирующего цвета изображения. Общая идея этих алгоритмов следующая. Каждый пиксель изображения представляет собой четверку чисел R, G, B, A. Мы идем по всем пикселям и особым образом анализируем их составляющие:

// создаем элемент canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// отрисовываем в canvas наш favicon
ctx.drawImage( faviconImage, 0, 0, 18, 18);
// получаем данные о пикселях
var data = ctx.getImageData(0, 0, 18, 18);
// Обходим каждый пиксель
for ( i = 0; i<data.data.length; i=i+4 ) {
       rgb = {};
       rgb.r = data.data[i];
       rgb.g = data.data[i + 1];
       rgb.b = data.data[i + 2];       
}


В нашем случае при реализации следует учитывать две особенности:

I. У многих favicon-ов существуют прозрачные пиксели, которые при отрисовке в элемент canvas представляются в виде rgba( 0, 0, 0, 0 ). Так как мы игнорируем альфа-канал, то для нас эти пиксели будут выглядеть как черные (#000000), что не соответствует действительности. Чтобы исправить это, просто закрасим canvas в белый перед тем как отрисовывать на ней favicon.

// Заливаем canvas белым прямоугольником
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, 18, 18);
// Отрисовываем favicon
ctx.drawImage( faviconImage, 0, 0, 18, 18);


II. Так как по задумке favicon будет вписываться в белую окружность, то в случае, если преобладающим цветом в изображении окажется белый, мы не увидим ни окружности, ни границ нашего элемента. Чтобы избежать этого, мы будем просто в каждом алгоритме игнорировать белые пиксели.

Я рассмотрю три различных алгоритма определения доминирующего цвета по возрастанию их сложности.

Алгоритм 1. Среднее значение цвета

Первый и наиболее простой алгоритм заключается в следующем. Мы просто проходим по всем пикселям и считаем среднее арифметическое соответствующих составляющих их цветов. Чтобы не загромождать статью кодом — все исходники доступны на gihub-е, я буду демонстрировать только результаты их работы:

image

Алгоритм 2. Евклидово расстояние

Данный алгоритм чуть более сложен и заключается в следующем. Каждый цвет представляет собой вектор в трехмерном пространстве (r, g, b). Мы проходим по каждому пикселю и считаем его расстояние до всех остальных пикселей (Евклидово расстояние между двумя векторами). Затем ищем тот пиксель, который находится ближе всех ко всем остальным. Цвет этого пикселя и есть наш искомый цвет. Здесь следует отметить, что данный алгоритм, в отличие от предыдущего, не создает новый цвет, а лишь выбирает из уже существующих в данном изображении.

image

Алгоритм 3. Метод кластеризации k-средних

Суть данного алгоритма заключается в следующем. Произвольно выбирается k пикселей (центров) изображения различного цвета. Проходим по всем остальным пикселям и относим каждый из них к одному из центров на основании их близости друг к другу (считаем Евклидово расстояние как и в алгоритме 2). Затем пересчитываем центры — устанавливаем для них значение равное среднему среди всех пикселей, отнесенных к нему (как в алгоритме 1). Вновь проходим по всем пикселям и распределяем их по новым центрам. Проделываем все это до тех пор, пока значение центров не перестанет изменятся. Искомым цветом будет значение центра с наибольшим количеством пикселей. Следует отметить, что данный метод используется для тех же целей в браузере Chrome. Результат его работы для k = 3 следующий:

image


Для k = 5:

image


(для остальных значений параметра k результаты не столь показательны)

Заключение


Вопрос о том какой из данных алгоритмов лучше — спорный.

  • Алгоритм средних значений невероятно прост и в общем работает, но может давать довольно грязный результат.
  • Расчет через Евклидово расстояние, на мой вкус, даёт довольно симпатичный результат и при этом прост в реализации.
  • Алгоритм кластеризации наиболее сложен. Дает наиболее яркое изображение и требует больше всего вычислений.


P. S. Github-юзер static-lab предложил и реализовал еще один алгоритм «Усреднённое по YUV с контрастом», который тоже дает весьма неплохой результат:

image

Пример можно запустить у себя в браузере (favicons закодированы в Base64): DEMO
Исходники тут: github
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 28
  • +2
    Вроде пустяк, а жить помогает.
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        А можно и не только для хрома.
        • +2
          Гуглохром, кстати, в спид-дайле нечто похожее сам делает, только цветом выделяет не весь прямоугольник, а только нижнюю грань. Там, кстати, цвет естественнее подбирается, на мой взгляд, чем в топике.
        • 0
          Градиента нет :) А так интересно получилось. Правда более интересно как они логотипы выдёргивают.
          • 0
            Вы имеете ввиду лого а не favicon? Они их не выдергивают, а просто отрисовывают для всех топовых ресурсов.
            • 0
              Для хабра тогда ужасно сделано…
          • 0
            Вопрос на засыпку… А у Фавикона ли он берет цвет?
            Почему у меня в Яндекс.Браузере Форум AmDm.Ru в черном цвете? Протестируйте своим мм… «алгоритмом». Может я ошибаюсь.

            • 0
              А вот это очень интересно. У меня форум AmDm в Я.Браузере выглядит сильно светлее. Слева цвет полученный алгоритмом кластеризации при k=7, справа — как это выглядит у меня в Я.Б:

              image

              Если отбросить наложение градиента, то цвета, вроде, близкие.
              • 0
                Фантастика!
                Теперь у меня так:



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

                Цвет теперь действительно похож на средний.
                • 0
                  Видимо используются технологии схожие с Яндекс «генератором погоды» =). Других догадок пока нет. У себя такого поведения вроде не замечал.
                  • 0
                    Опять черный… Странно все это.
              • 0
                А почему не сделать так: собрать палитру цветов, потом сгруппировать все цвета по похожести цвета и определить самую крупную группу. Можно от неё взять средний цвет всех цветов, которые в неё вошли, или как-то выбрать значение внутри группы.
                • 0
                  Если я правильно вас понял, то это как раз алгоритм под номером 3. Кластеризация цветов.
                • 0
                  Вопрос из любопытства: какой цвет дадут все 3 алгоритма, если половина изображения будет белой, а вторая чёрной?
                  • 0
                    Кажется я задал очень глупый вопрос )
                    • 0
                      В моем случае все алгоритмы дадут черный цвет, так как белый я просто игнорирую (чтобы избежать белого как основного).
                    • 0
                      В Хроме на его стандартной новой странице под миниатюрой сайта тоже есть полоска цвета фавикона.

                      Кэп
                      • 0
                        Да, там как раз и используется алгоритм кластеризации.
                      • +4
                        В стиле «Яндекс Браузер»? Не смешите такими громкими словами.
                        • 0
                          Размытие по Гауссу с радиусом, равным стороне иконки, даёт хорошую картину.
                          Как понимаю, это близко к третьему алгоритму.
                          • 0
                            Второй алгоритм дает самый адекватный результат, как лично мне показалось.
                            • 0
                              неужели я единственный, кому приятно видеть в новой вкладке «пустую белую страницу» вместо отвлекающих «красочных картинок»?
                              • +1
                                Кому-то нравятся мороженное, кому-то пироженное. Кому-то девочки, кому-то мальчики. Это нормально. Не вижу ничего плохого ни в «пустой белой странице», ни в «красочных картинках».
                                • 0
                                  Как бы это сделать в хроме?
                                • +1
                                  Седьмая винда кнопки на панели задач тоже по доминирующему цвету раскрашивает. Только все оттенки серого за цвет не считает, а не только белый.
                                  • +1
                                    Да, это называлось Color Hot-Track.

                                    image

                                    В Windows 8, кстати, тоже есть.
                                  • 0
                                    В восьмой винде очень интересный алгоритм подбора цвета хрома окна в зависимости от картинки на обоях рабочего стола.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Интересные публикации