0,0
рейтинг
9 февраля 2012 в 00:06

Разработка → Определение доминирующих тонов на изображении [v 1.1]

После публикации прошлой статьи, я полностью забил на попытку выполнить алгоритм при помощи HSV или Lab координат. Забил на использовании библиотек цветов и вообще на сам скрипт забил.

Но что-то стало скучно и опять зачесались руки поработать с изображениями и одновременно захотелось исправить уже имеющийся алгоритм.
Скрипт: link


Решение

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

Возникало это все вот почему. Уникальный идентификатор цвета определялся как (r-g)*1000000+(r-b)*1000+(g-b). Соответственно цвета имеющие одинаковые идентификаторы являлись подобными. При обработке постоянно вносилась все большая погрешность в r, g и b составляющие цвета. Вносилось простейшим округлением: round(round( (r-g)/$error ) * $error) итд.

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

Также из-за разбиения тонов на интервалы яркости, пришлось увеличить количество обрабатываемых цветов. Теперь вместо 60000, алгоритм обрабатывает 500000 цветов и шаг проверки уменьшился с 20px до 10px. Так что возможно замедление работы.

Результат

В результате, картина очень сильно изменилась в лучшую сторону! Скрипт выдает более менее адекватные цвета на большинство изображений. Также работают и ч/б изображения, хотя с ними возможны баги.
Ниже несколько примеров работы:






Дополнительно

Не стал публиковать в блог «Алгоритмы», т.к. это всего лишь небольшое изменение в работу предыдущей версии.
Многие спрашивали скрипт для определения цветов на изображении. Собственно вот. Внутри есть описание.

UPD_1 По просьбе некоторых пользователей, добавил вывод цветов в hex формате после обработки изображения. RGB нужен?
UPD_2 Добавил поддержку PNG.
UPD_3 Автор фотографии рыжей девушки: Елена Серебрякова.

UPD_4 Пересчет яркости по формуле 0.299*R + 0.587*G + 0.114*B. Изменена погрешность. Добавлена возможность перевода цвета из HEX в RGB формат.
UPD_5 Уменьшил пороговую погрешность. Добавил возможность определения цветов на загруженной фотографии. Просто кликните в нужном месте на изображении.
Спартак Каграманян @Assorium
карма
37,0
рейтинг 0,0
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (76)

  • +3
    Хорошая работа, молодец
    • 0
      На фото у девушки 2 цвета с красным оттенком (видимо с губ), но нет цвета лица.
  • +4
    А есть ещё фотографии, похожие на последнюю?

    Кстати, вот на этом сайте (не реклама, это не мой) на каждое пришедшее изображение уже реализовывается подборка основных цветов.
  • +11
    Оффтопом — рыжие круты.

    Спасибо за скрипт, думаю, пригодится.

    ЗЫ Никто не знает, с помощью чего можно быстро отделить цветные фото от ч/б? Есть, к примеру, большая папка с кучей фоток, где есть и те, и те. Нужно быстрое решение, позволяющее выделить (переместить) только цветные или только ч/б.

    Всем спасибо, автору плюсы.
    • –2
      Это тоже не реклама, но совемую посмотреть на поиск по цветам (и не только) вот здесь.
      • 0
        Ммм, ну да, там есть поиск по цветам, только как это поможет мне разделить мои собственные фотки?))
        • +1
          гистограммы по каналам посчитай.
          для чернобелых они должны быть одинаковы либо очень близки.

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

          готового «пакетного» софта — что-то не припомню.
    • +3
      Переводить в HSV и строить гистограмму H. Если получим только один тон (узкую полосу), изображение явно монохромное (черно-белое, либо подкрашенное).
      • 0
        Спасибо, будем посмотреть.
      • 0
        Если рассматривать решение на PHP, то построение гистограммы не самое идеальное решение.
        Тут вполне можно дополнить выложенный мною класс. Ч/б это по сути цвета у которых r=g=b. Собственно ставится условие, бежит цикл до первого нарушения, иначе изображение цветное.
        • 0
          Ну, на сях это делается просто. С пыхпыхом я не знаком, да и не представляю, зачем локальное приложение на пыхпыхе писать.
          А вообще, можно попробовать эту задачу решить при помощи простого скриптика: посредством ImageMagic'овского identify получить информацию о статистике по изображению; затем grep'ом выделить статистику по цветам; а затем сравнить средние и экстремальные значения RGB, а также их дисперсии. Если они будут примерно одинаковыми, изображение наверняка черно-белое. Правда, монохромные (окрашенные) изображения так не определить.
    • 0
      Можно попытаться так:
      www.imagemagick.org/Usage/compare/
      искать там «Gray-scale vs Color»
  • +15
    Почему тут не выдаёт бирюзовый цвет губ?



    пс: и да. это первая попавшаяся картинка на моём компьютере.
    • 0
      Спасибо, буду проверять.

      п.с. в прошлой версии скрипта я наполучал более 1600 «первых попавшихся» картинок. По ним вполне можно ставить удручающий диагноз)
      • 0
        И у вашего кота вылез бирюзовый.
        Вероятно это серые штаны?
  • +3
    Также работают и ч/б изображения, хотя с ними возможны баги.

    Например, такие?

    • +2
      Да, например такие.
      Но я в принципе не вижу смысла определять тона черно-белого изображения. Тут вполне можно обойтись максимальными вхождениями серых цветов.
      • +7
        А вот такие мы искушенные пользователи.
      • 0
        не согласен в крайней степени, если брать рентгеновские фото там очень важно знать доминирующие цвета для пороговой сегментации и это в свою очередь может даст возможность разделять объекты
  • +22
    Какая девушка красивая…
  • +1
    А скрипт на выходе дает превалирующие цвета именно из самого изображения, или просто выводит похожие?
    • +1
      Я забыл упомянуть, что алгоритм был описан в прошлой статье.
      Цвета он берет из самого изображения, но их он сглаживает при наличии подобных цветов. Т.е. если есть градиент на изображении, то он выдаст некое среднее из этого градиента (с учетом яркости само собой).
      Если, к примеру, изображение наполнено белым (255,255,255) цветом на 90% и серым (200,200,200) на 10%, то в итоге он должен выдать серый (250,250,250)
      • +1
        Ясно, спасибо. Просто вот это немного смутило.

        image

        Вроде, на лого только два цвета, а выдал шесть.
        • +2
          на границах картинки из за сглаживания образуются цветовые переходы
          скрипт это и показал
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Может кто-нибудь сделает сайт-сервис? Загрузил фотку, на выходе получил результат. Я бы пользовался!
    • 0
      полно таких уже, и выдают сразу коды для html и прочего).
  • –2
    skifcha популярен я смотрю, даже группа вконтакте есть)
  • 0
    В статье нехватает ссылки на реализацию алгоритма, исходник.
  • 0
    * PHP versions 4 and 5

    и

    public $error = 1;

    Как-то не совместимы :)
    • 0
      В исходном варианте переменная погрешности была объявлена внутри цикла. Я решил вынести ее. Пользуюсь PHP 5.3 =)
      • 0
        Я имел введу, что в PHP4 не было области видимости :)
  • 0
    Сделайте поддержку png, плиз.
    • 0
      Добавил.
      • 0
        Спасибо!
  • +1


    Голубое небо потерялось…
    • 0
      Я писал про 2 допущения, которые делаются при анализе изображения.
      1. Шаг выборки пикселей (10px)
      2. Количество возвращаемых цветов

      На изображении куда больше бежевых оттенков (особенно учитывая градиент), также возможно голубые оттенки просто не попали в шаг в том количестве, в котором попали бежевые.

      На локалке пробежался по каждому 2 пикселю и увеличил количество яркостных интервалов до 4. Вот, что получил:
      • +3
        Все-таки, по-моему, построение гистограммы изображения дало бы более правильный результат: строим гистограмму H, находим положение нескольких локальных максимумов, выдаем результат…
        • 0
          Я постараюсь поподробнее рассмотреть этот вариант, хотя локальные максимумы еще ни о чем не говорят. Доминирующие тона — это не просто самые часто встречающиеся цвета, а в то же время и контрастные. Этими контрастными цветами может быть цвет губной помады, маленький зеленый листок посреди снега итд. Он не обязательно будет превалировать в количестве, но будет тут же заметен глазам и поэтому в идеале он должен попасть в выдачу. Собственно я именно эту цель и преследовал, хотя работать еще много есть над чем…
          • +1
            Такие явно контрастирующие цвета и дадут локальные максимумы (пусть даже небольшие) на гистограмме. Например, если у вас монохромное голубовато-черное изображение с ярко красной розой, то гистограмма даст всего два максимума: главный — для голубого и маленький локальный — для красного. Если мы прогоним гистограмму через лапласиан гауссианы, получим две области с отрицательными значениями, соответствующие локальному и глобальному максимуму. Сортируя затем эти области по значению на гистограмме, мы сможем выделить нужные цвета.
            Да, близкие цвета можно объединять (т.е., например, если у нас уйма локальных минимумов в синей области, мы просто расширяем ее и суммируем в один синий цвет).
        • +1
          Гистограмма мало что дает:







          Брались максимумы в пространстве (r/s, g/s, b/s, s/768), где s=r+g+b
          • 0
            Неправильно делаете: стройте гистограмму только для H и ищите локальные максимумы.
            • +1
              И получить ярко-оранжевый и бледно-коричневый в одной клетке? Не хочу.
  • 0
    А если попробовать найти алгоритмы расчета палитр при оптимизации gif файлов: там же есть несколько разных и ими на сегодня пользуются Очень многие.
  • 0
    Ограничение 6тью цветами — все портит. Ну и зачастую «тон» совсем не тот что ожидается. Как напримере последней фото с девушкой. Цвет лица «ушел» а появился какойто непонятный розовый.
    • 0
      Это цвет губ и теней.
      А собственно сколько Вам необходимо цветов?
      • 0
        в зависимости от картинки естественно. На одной и двух достаточно, на другой и 7 мало.
      • 0
        а про губы и тени, площадь лица ммм несколько больше чем площадь губ… но ваш метод выбрал почемуто губы
  • +1
    Отлично! Поможет юзерам кастомизировать свои интерфейсы, раскрасив панели и виджеты в цвета, выданные скриптом, которому передана ему обоину. Лично у меня всегда вызывает небольшое затруднение подбор цветовой темы для wm, это значительно упрощает процесс «стилизации».
  • +1
    Notice: Unable to open image file in /var/www/u1063004/data/www/blog.assorium.ru/jpeg/getImageColor.php on line 59

    Warning: Invalid argument supplied for foreach() in /var/www/u1063004/data/www/blog.assorium.ru/jpeg/index.php on line 18

    Warning: arsort() expects parameter 1 to be array, null given in /var/www/u1063004/data/www/blog.assorium.ru/jpeg/index.php on line 101


    на этой картинке сломалось:

    • –1


      а вот на этом вообще странно — откуда такие цвета?
      • +2
        ClearType?
        • 0
          Типа того. Сразу и не въехал — откуда.
  • 0
    интересный материал, кстати у адоба есть очень интересный инструмент на флеше, с ч/б отлично справляется, при чем есть возможность самому подкорректировать «точку сбора» и он показывает откуда берет цвет, самое то для дизайнера
  • 0
    Не всегда верно работает (нет розового и сиреневого): image
    Но за старание — большой плюс, конечно)
    • 0
      Поубирал ограничения.


      Исчез зеленый. В общем я уже нашел, как сделать этот алгоритм еще более крутым и адекватным, но на это требуется еще одна чесотка рук =)
  • 0
    Notice: Unable to open image file in /var/www/u1063004/data/www/blog.assorium.ru/jpeg/getImageColor.php on line 59

    Warning: Invalid argument supplied for foreach() in /var/www/u1063004/data/www/blog.assorium.ru/jpeg/index.php on line 18

    Warning: arsort() expects parameter 1 to be array, null given in /var/www/u1063004/data/www/blog.assorium.ru/jpeg/index.php on line 101
  • +1
    Я построил карту распределения цвета (проекция на плоскость r+g+b=1) для фотографии дерева:


    и «незабудки»:


    красный цвет — справа внизу, синий — слева внизу, зеленый — вверху.

    Я сочувствую программам, которые пытаются найти в этом тумане «доминирующие цвета» :)

    • 0
      Яркость точки определятся количеством вхождений цвета?

      Определить доминирующий цвет или цвета на самом деле не трудная задача. Куда труднее выбрать контрастные цвета. Возможно изображение будет изобиловать какими-то 4 превалирующими оттенками, которые будут занимать 98% изображения, но если где-то посередине появится цвет, контрастный остальным, окружающим его и он займет оставшиеся 2% изображения, то он не попадет в список «доминирующих», но должен попасть в список основных тонов изображения.
      Я решаю это проблему практически в лоб и каждый раз лишь заостряю инструмент, но не создаю нового подхода… Соответственно, если в следующий раз меня опять припрет усовершенствовать приложение, т.е. я его буду переписывать с 0.
  • 0
    Да, разрешение картинки примерно 1024 точки на расстояние от «синего» до «красного», чем больше цветов пикселей попало в данную точку, тем она темнее.
    Контрастные цвета на гистограмме будут выглядеть как изолированные облака точек. На этих картинках их не видно, но на других встречаются. Но вообще, видно, что такая гистограмма, не привязанная к реальным 2D-координатам — это неправильный инструмент. Нужна какая-то двойная статистика: одна найдет цветные пятна (сгущения в 5-мерном пространстве x,y,r,g,b?), а вторая сгруппурует их по цветам и выделит «основные». Я тут вижу сразу два или три подхода, но они наверняка тоже работать не будут :(
    • 0
      Ну если абстрагироваться до твоего варианта, то я по сути, пытаюсь найти центр облака подобных и выделить центровой цвет, тем самым убиваю большое количество точек вокруг облака. После нескольких итераций, остаются довольно отчетливые отдельно-стоящие островки, которые как раз и необходимы.

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

      Т.к. я все преследую умеренную ресурсоемкость, то и решение должно быть крайне простым, поэтому я даже и не пытаюсь лезть в дебри подобных алгоритмов. Проще всего задействовать человеческий фактор по окончании обработки, пусть человек доделывает все, как надо.
      Но я думаю, что следующим шагом обработки я возьму HSV координаты, т.к. с помощью них крайне легко определить похожесть цвета и яркость цвета.
      • 0
        Я сначала думал, что основная проблема будет в «побочном пике на склоне горы» в гистограмме. Но картинки показали, что никаких склонов в фотографиях, в общем, нет — есть острые пики разной высоты там и сям разбросанные по плато. Про поглощение одного цвета другим казалась разумной такая идея: если по пути от цвета A к цвету B по гистограмме (хорошо сглаженной, т.е. с крупными ячейками) нам придется пройти через точку, высота которой ниже, чем сколько-то процентов от минимума из высот A, B, то эти цвета различны. Таким образом, чтобы проверить, что локальный максимум высотой H окажется «цветом», мы возьмем множество клеток гистограммы со значением не меньше cf*H, выделим связную область, содержащую нашу точку, и проверим, является ли точка в ней общим максимумом. Если да — то она есть «цвет», и можно искать центр области вокруг нее? либо брать среднее от всех пискселей, попавших в область, либо работать только с одной клеткой гистограммы.
        А что, попробую. У меня все готово, надо только поправить пару строчек в поиске связных компонент…
  • +4
    Попробуйте вначале перевести цвет пикселя в цветовое пространоство L*a*b. Оно спроектировано таким образом, чтобы растояние между похожими цветами было линейным для восприятия.

    Если же говорить про RGB, то большинство файлов созданы для гаммы 2.2. Поэтому, перед обработкой, их надо перевести обратно в линейное цветовое пространство.
    • 0
      Я так и не понял, почему автор не использовал CIE L*a*b. По-моему, это очень удобно. Когда-то делал проект кластеризации, так вот в матлабе сначала переводил в CIELAB, а потом уже использовал кластеризацию по методу k-средних для сегментации изображений. Т.к. пространство содержит две хроматических составляющих цвета и одну управляющую светлостью, то светлость выкидывал и искал расстояние по двум координатам.
      • 0
        Мне становится страшно при одном взгляде на формулы пересчета. Кроме того, смущает огромное количество точек, не имеющих RGB-представления. Да и если посмотреть в фотошопе на кары цветов при фиксированной одной из координат, то видно, что ничего не понятно — какие-то странные радужные переливы, с огромными областями, которые заполнены, на взгляд, одним цветом. Непонятно. А если непонятно, как можно им пользоваться?
        • 0
          Считать действительно получается немало, однако не все так сложно. Для начала делаем нормальное преобразование RGB -> XYZ



          После преобразования получим данные в пространстве XYZ. Потом делаем преобразование XYZ -> L*a*b, как это сделать есть википедия.

          Ну а насчет того что Вы далее написали, то L*a*b имеет огромный цветовой охват и перевод в него не связан с потерями. Если перевести RGB -> LAB -> RGB, то потерь не будет.
          • +1
            Кстати тут даже и написано
    • 0
      Если переводить в линейное пространство, все получается гораздо хуже. Основным цветом практически любой картинки становится черный (если брать метрику в линейном RGB-пространстве) или оттенки черного (если отделять яркость от оттенка/насыщенности). Там слишком большая плотность точек. Пожалуй, лучше, наоборот, в качестве координаты взять логарифм яркости.
  • +1
    В конечном итоге, через гистограмму все получилось:





    Другие примеры здесь

    А программа здесь (для .NET)

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

    Чем меньше первые два параметра (до определенных пределов), тем больше оттенков «затянет» каждый найденный цвет, и тем больше шансов будет пробиться у более редких цветов.
    • 0
      Очень даже хорошо справляется Ваш алгоритм. Правда на выходе почти у каждого оттенка есть сильно похожий на него…
      Да и просмотрев Ваши работы, заметил ту же пропасть, что и у меня. Некоторые, даже довольно часто встречающиеся цвета он совсем не выводит… Я откомментировал в пикассе 2 самые выделяющиеся работы.
      • 0
        Поигравшись с параметрами, иногда можно заставить его найти цвета, встречающиеся реже (чуть позже попробую построить и выгрузить примеры). Но без участия человека здесь трудно.
        Обидно, что в итоге у меня получился самый примитивный алгоритм — не помогли ни k-d деревья, ни вычисление фрактальной размерности, ни прочие способы автоматической оценки параметров. С одной стороны, чем примитивнее, тем, казалось бы, больше возможностей куда-нибудь его развивать, с другой — если все делается одним волшебным параметром, до дополнительные ручки управления прикручивать может оказаться некуда.
        • 0
          На самом деле я почти в самом начале разработки отчаялся и хотел выдать юзер скрипт, чтобы пользователь сам подбирал удачные оттенки… Но все же это не гуд. Нужен полностью автономный алгоритм.
  • 0
    Оказывается, мой алгоритм работал из-за ошибки в программе: я случайно запретил ему объединять цвета с разной яркостью. После того, как я ошибку исправил, он радостно объявил все цвета на картинке оттенками черного :(
    • 0
      Довольно печально такие вещи обнаруживать… Искренне сочувстую

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