Недавно я завершил проект, который создает слайд-шоу фотографий на основе текущего времени суток и цветов, которые появляются на наборе фотографий. Я назвал это цифровое произведение «Циркадный пейзаж». Он был создан с использованием PHP, MySQL, HTML/CSS и JavaScript.

Этот пост является частью 2 в серии статей, объясняющих, почему и как я его построил (Часть 1, где я обсуждаю цели, а также то, как я планировал и готовился к этому проекту). В этом посте я опишу, как я думал над проектом в то время, когда я им занимался. Я подробно расскажу о своих решениях и результатах в части 3, но это должен быть обзор решений, эмоций и проблем, возникающих при работе над новым проектом.

В этом разделе я поделюсь:

  • Пошаговый процесс перехода от анализа цвета к слайд-шоу
  • Препятствия, с которыми я столкнулся, и методы, которые я использовал для поиска альтернативных решений
  • Разбивка фактических результатов каждого этапа этого процесса, включая некоторое объяснение кода и того, что он делает.

Репозиторий кода: https://bitbucket.org/alightergreen/circadian-app

Проанализируйте фотографии

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

Как указано в моем Документе о требованиях к продукту, я изначально планировал продвигаться по этапам линейно: начать с анализа фотографий и продвигаться по каждому этапу, пока не смогу отобразить динамическое слайд-шоу.

Моим первым шагом была обработка фотографий с помощью найденной библиотеки PHP Color Analysis. Я начал с краткого обзора кода, чтобы понять его возможности. Библиотека поставлялась с файлом-примером (index.php), который возвращал и отображал результаты анализа на экране, как вы видите на сайте CoolPHPTools. Я начал свои эксперименты с того, что сделал копию этого файла примера и внес небольшие изменения в код, чтобы он работал с фотографиями, которые я хотел использовать.

Кодирование

Самым очевидным ограничением кода, которое мне нужно было устранить, был тот факт, что вы могли выбрать только один файл фотографии за раз для анализа. У меня была целая папка с фотографиями, которые я хотел проанализировать за один проход.

Чтобы компенсировать это ограничение, я добавил код для заполнения массива имен файлов на основе содержимого выбранной папки. Затем код просматривал этот массив и вставлял каждое имя в исходную логику обработки «по одному за раз».

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

Вторая проблема заключалась в том, что исходный файл просто отображал результаты на экране. Я мог бы просто расширить логику «фото за фото», чтобы получить эти результаты и выполнить дополнительную обработку. Однако я хотел, чтобы между различными действиями было больше швов, поэтому я также добавил фрагмент для отправки результатов в базу данных. Таким образом, если бы я изменил способ работы действия, пока оно помещало окончательные данные в те же поля данных, я знал, что мои более поздние действия будут продолжать работать. Это также позволило мне легко останавливаться между действиями, смотреть на результаты и спрашивать: «Это делает то, что я хочу? Это возвращает те данные, которые мне нужны?» Хранение результатов в базе данных было самым быстрым и простым в реализации решением, которое дало бы мне постоянную запись результатов без необходимости каждый раз перезапускать файл.

Вывод

Результатом этой работы является файл Выберите каталог для извлечения (seldir4extract.php). Этот файл открывает форму, которая позволяет вам установить переменные, которые использует библиотека анализа фотографий:

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

Результатом являются шестнадцатеричные коды цветов и процентная доля фотографии, которую составляет этот цвет. Результаты возвращаются отсортированными по процентам (от большего к меньшему), поэтому вы всегда будете получать наиболее распространенные цвета, независимо от того, какое количество результатов вы выберете.

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

В зависимости от размера ваших фотографий и того, сколько вы обрабатываете за один раз, есть большая вероятность, что это время истечет. (Или это может быть просто мой компьютер. Я использовал свой древний персональный Mac при создании этого, и он легко складывается.) Если вы столкнетесь с этим, не беспокойтесь. Прежде чем он обработает фотографию, я добавил проверку, чтобы увидеть, есть ли имя файла уже в базе данных. Если время истекло, вам просто нужно обновить страницу и снова нажать «Выполнить» (не забудьте сбросить настройки, если вы изменили их по умолчанию). Он пропустит все фотографии, которые уже были обработаны и имеют записи в базе данных, и начнет со следующего нового изображения. У этого есть свои плюсы и минусы с точки зрения итерации, но это был самый быстрый способ обойти проблему тайм-аута с помощью того, что я умел делать хорошо.

Задание цвета, раунд 1

Я предполагал, что если я могу анализировать фотографии на наличие цветов, то результаты будут немедленно полезны.

Поскольку у меня был цветовой анализ всех моих фотографий, теперь мне нужно было выбрать фотографии по этим цветам. Что оказывается очень сложным процессом!

Самая большая проблема заключается в том, что нет хороших, «удобных» имен, по которым можно было бы выбирать большинство цветов, существующих в спектре RGB. Поскольку я не хотел становиться экспертом по теории цвета или кодированию RGB, чтобы закончить, я был готов ввести в процесс некоторую ручную работу, чтобы закончить быстро. Если бы я мог получить цвета для каждой фотографии до 1 или 2 результатов, я мог бы назвать цвета «синий» или «розовый» вручную за разумное время. Тогда было бы легко запрограммировать слайд-шоу на «Покажи мне розовые фотографии на рассвете».

Первым шагом в этом было найти способ автоматически получить 1 или 2 цвета, которые действительно представляют фотографию из результатов анализа. Вот тут и начались блокпосты. Я предполагал, что если я могу анализировать фотографии на наличие цветов, то результаты будут немедленно полезны. Я не нашел, что это так.

Кодирование

Я опробовал несколько идей о том, как заставить это работать.

  1. Необработанные результаты. В первом просто брался верхний цвет из результатов и использовался в качестве «цвета» фотографии. Но это было не особенно точно. Например, скажем, на фотографии много черного, может быть, 25%, и на фотографии также много разных цветов синего, которые составляют 75% фотографии. Если ни один отдельный синий цвет не превышает 25%, то фотография будет указана как «черная», даже если вы, глядя на нее, скажете: «Эта фотография «синяя».

Любой, кто когда-либо делал что-либо с компьютерным зрением, вероятно, сейчас смеется…

2. Цветовые группы. Чтобы решить эту проблему, я решил объединить все похожие цвета, которые собирал, в группы цветов. Тогда весь синий цвет фотографии будет сложен в виде синего. В приведенном выше примере результаты будут такими, что фотография будет на 75% синей и на 25% черной, и фотография будет указана как «синяя». Для этого цвета должны быть помечены как «синий» или «черный», чтобы их можно было сгруппировать.

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

Я снова начал с нескольких предположений, которые постепенно проверялись, отбрасывались, улучшались и снова проверялись.

  1. Пожалуйста, просто сопоставьте! Я надеялся, что результаты, которые я получил в результате анализа, будут довольно близки или точно соответствуют цветам, перечисленным на листе из Википедии, поэтому я мог просто выполнить простой процесс сопоставления. Это была гипотеза, вызванная отчаянием, и я действительно не хотел выяснять, как выполнять математические операции с шестнадцатеричными кодами. Но… в спектре RGB возможны миллионы результатов в шестнадцатеричном коде, и только немногим более 100 результатов в таблице, с которой я сопоставлялся. Только 6 из более чем 200 цветовых результатов совпали при первом запуске, и все они были идеально черными (#000000) или идеально белыми (#FFFFFF). Не очень полезно.
  2. ОТЛИЧНО! Я посчитаю… Казалось, что другого выхода нет. Мне нужно было написать логику, чтобы выяснить, какой из цветов таблицы цветовых групп ближе всего к результату. Это требует математических вычислений в шестнадцатеричном коде — вычисления общего расстояния двух цветов друг от друга путем просмотра расстояний между их тремя различными красными, зелеными и синими элементами — чего я действительно хотел избежать и почему я пошел искать библиотека, которая в первую очередь сделала бы для меня анализ цвета.

Вывод

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

Поскольку я надеялся вообще не иметь дело с шестнадцатеричными кодами, я не проводил никаких исследований их логики или того, как работать с ними в PHP. Даже после всего этого мне все еще нужно было вернуться к началу, чтобы иметь возможность двигаться вперед. Я не был знаком с программированием с шестнадцатеричными кодами за пределами их основных интерфейсных приложений (сделать ссылки синими, сделать заголовок2 оранжевым и т. д.).

Оглядываясь назад, я понимаю, что необходимость вернуться к исследовательской фазе меня так расстроила. Мне казалось, что я уже завершил этап исследования и планирования, и теперь я должен был делать что-то. Необходимость снова обратиться к книгам, чтобы решить эту проблему, казалась движением назад. Это было похоже на провал. (Оставайтесь с нами для части 3 для более подробного обсуждения этого.)

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

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

Я записал, что я пробовал, что у меня получалось и чего, как я знал, мне все еще не хватает, и решил перейти к следующим шагам — сделать так, чтобы фотографии были выбраны по времени суток и отображались в слайд-шоу. Я также поставил себе жесткий дедлайн еще на одну неделю, прежде чем мне придется отложить проект. У меня был поиск работы, мне нужно было вернуться к полной занятости!

Поиск лучших цветов фотографии

Конечно, это не собиралось хорошо масштабироваться. Это было обходным решением. Он сделал свою работу, и теперь я смог завершить построение процесса.

Я обошел тот факт, что не мог сразу закончить математическую задачу с шестнадцатеричным кодом, вернувшись к своему исходному решению — сделать это вручную. Однако вместо того, чтобы просто посмотреть на 1 или 2 лучших цвета, я сохранил их все. Я загрузил результаты своих 12 тестовых фотографий в Excel, а также закинул их в быстрый файл HTML, чтобы я мог увидетьцвет в шестнадцатеричном формате. Затем я установил цветовую группу для всех их более чем 200 цветовых результатов вручную и импортировал этот столбец обратно в базу данных.

Теперь, по крайней мере, мне было от чего отталкиваться. Это был отличный обходной путь на этом начальном этапе! Учитывая, что у меня было более 100 фотографий, которые я уже купил для слайд-шоу, хотя это НЕ собиралось хорошо масштабироваться…

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

Кодирование

Поскольку теперь у меня было несколько цветовых групп для работы, я смог вернуться к сглаживанию результатов каждого изображения с 19 видов синего и 1 черного до общего процента синего и общего процента черного.

Я начал с копии моего файла «Выбор каталога», потому что для завершения этого шага мне также требовался массив списка файлов, а в файле «Выбор каталога» уже был рабочий код для его получения. Когда у меня был список файлов, я смог извлечь результаты каждого файла из базы данных, сложить вместе все, у которых была одна и та же цветовая группа, и сохранить «сжатые» результаты обратно в базу данных в отдельной таблице.

Сопоставление и математическая логика здесь просты, но требуют вложенных циклов for и операторов if. Это доставило мне немного хлопот заранее, особенно продумывая, что должно было произойти в каждой из ветвей «тогда» или «ошибка». В отличие от ситуации с шестнадцатеричным кодом, это не было чем-то, что не могла бы обнаружить тупая отладка.

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

Вывод

Это было выполнено в файле Top Colors (topcolors.php).

Как и в исходном файле Select Directory, вы можете выбрать папку, с которой хотите работать. В этом случае мы просто используем папку для создания списка файлов, которые мы хотим проверить в базе данных, и уплотняем наши цвета. На самом деле мы ничего не делаем с самими файлами.

Сопоставление фотографий по цвету и времени суток

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

Я сделал простую таблицу, в которой в одном столбце были перечислены часы дня, используя значения от 0 до 23, а в другом — названия цветовых групп из моей таблицы Википедии. Какие часы получили, какие цвета были просто основаны на моей интуиции, и то, что я думал, будет выглядеть красиво.

Кодирование

Все остальное, что нужно для завершения процесса, находится в файле Слайд-шоу (slideshow.php). Этот файл делает больше вещей, чем любой другой файл:

  • Показывает текущее время суток
  • Находит время суток в таблице базы данных
  • Получает цветовую группу (группы), назначенную для этого времени суток.
  • Выбирает фотографии, первый или второй верхний цвет которых соответствует этой цветовой группе (группам).
  • Рандомизирует этот список фотографий
  • Выбирает несколько фотографий для отображения
  • Использует этот список для инициализации слайд-шоу

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

Подобно файлу примера в библиотеке анализа цвета, учебник по слайд-шоу, который я использовал, был сделан жестко закодированным — это означало, что вы должны создать файл слайд-шоу и ввести имена файлов, которые вы хотите слайд-шоу использовать. Поскольку я хотел, чтобы он мог динамически выбирать фотографии, мне пришлось разбить структуру, используемую для каждого слайда, и заменить жестко закодированные строки переменными, которые я мог передать. Затем я поместил это в цикл for, чтобы он мог записать слайд. для каждого файла в списке возвращаемый код PHP.

Вывод

Я достиг первоначальной цели! Я перешел от папки, полной файлов, к слайд-шоу фотографий в зависимости от их основных цветов и времени суток! ДА!

Однако я обнаружил две большие проблемы в коде слайд-шоу, который я использовал для создания своего слайд-шоу:

  • Хотя в то время мне это не пришло в голову, беглый просмотр показал, что все фотографии, использованные в руководстве, были изменены, чтобы иметь одинаковый размер и ориентацию. У меня есть как горизонтальные, так и вертикальные фотографии разного размера, и слайд-шоу не настроено так, чтобы это выглядело привлекательно.
  • Вторая трудность заключалась в том, чтобы слайд-шоу время от времени перепроверяло время суток и обновляло страницу при необходимости. В противном случае, если вы запустите его ночью, вы просто будете вечно видеть ночные фотографии, пока не обновите вручную.

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

Автоматическая группировка цветов

В этот момент я был довольно напуган. У меня был намечен функциональный конвейер, чтобы перейти от папки, полной фотографий, к слайд-шоу. Это по-прежнему требовало ручной помощи, но все части, которые я хотел, были там, по крайней мере, в их самой базовой форме. По крайней мере, я мог запустить слайд-шоу, используя мои 12 тестовых фотографий.

Я никоим образом не собирался вручную устанавливать цветовые группы для других 100 фотографий, которые я купил. 100 фотографий x 20 результатов каждая = 2000 цветов для обзора! Прежде чем мой срок истек, я хотел посмотреть, смогу ли я закончить автоматизацию процесса, чтобы я мог импортировать остальные свои фотографии.

Кодирование

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

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

Example:
05A6FF — 00AAFF
Split into components:
Red | Green | Blue
05 — | A6 — | FF -
00 | AA | FF
Change to Base 10:
Red | Green | Blue
5 — | 166 — | 255 -
0 | 170 | 255
Subtract Each Column (Color)
Red = | Green | Blue
5 | -4 | 0
Take the Absolute Values
Red = | Green | Blue
5 | 4 | 0
Add them all together to find the total distance
05A6FF is 9 units away from 00AAFF
05A6FF would be labeled with 00AAFF’s color group if 
9 is less than the result of the previous color 05A6FF 
was compared with.

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

Потребовалась тонна отладки и итераций, чтобы заставить это работать последовательно. В первую очередь из-за настройки forloops и операторов if, необходимых для построения логики.

Вывод

Выяснение способа решения этой проблемы и фактическое его выполнение по-прежнему является тем, чем я больше всего горжусь в этом приложении!

Результатом всей этой работы является файл Соответствие цветовой группы (colorgroupmatch.php). Этот файл используется между файлами Select Directory и Top Color. Это заменяет ручную работу, которую я проделал, чтобы пометить все цветовые результаты, возвращаемые анализом фотографий, с помощью удобного для пользователя названия цветовой группы, такого как розовый или синий.

Рефакторинг

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

Мне нужно было пройти и перепроверить весь процесс от начала до конца, потому что я нарушил некоторые соединения, чтобы заставить файл Color Group Match работать. Пока я что-то исправлял, я решил сделать несколько вещей более согласованными во всех файлах.

Кодирование

Я очистил то, как я делал вызовы базы данных, как были организованы мои циклы for и ошибки, и как я увеличивал свои циклы for.

Например, я изначально изучил forloops в Octave и просто включился в этот проект, используя этот синтаксис.

Затем мой муж, который на самом деле программирует на PHP, посмотрел на то, что я написала, и сказал: «ЧТО ТЫ ДЕЛАЕШЬ?! #phpterror».

Но эй, то, что я сделал, сработало! Стандартный метод PHP, однако, НАМНОГО чище, что делает его более приятным для вас, а мне приятнее через 6 месяцев, и я действительно не помню ничего из этого, чтобы выяснить, что я сделал.

Вывод

Большая часть очистки включала перемещение повторяющихся задач в отдельный файл и превращение их в вызываемые функции. Это означало, что когда вы открывали любой из исполняемых файлов .php для этого проекта, вы просто видели логику того, что этот файл пытался выполнить. Повторяющиеся вещи, связанные с открытием и закрытием соединений с базой данных, а также учет ошибок, были перенесены в photoapp.inc.php.

Уроки выучены

Не прошло и недели, как я успела даже добавить в папку все 100 фото, обработать их по всему алгоритму и посмотреть в слайд-шоу! Это означало, что весь процесс перехода от папки с фотографиями к слайд-шоу можно было завершить, просто нажав «Выполнить» на нескольких файлах всякий раз, когда я хотел добавить больше фотографий. Я также был идеально настроен, чтобы сжать весь процесс в один файл или вернуться, чтобы исправить проблемы со слайд-шоу, когда захочу.

Версия 0.1 официально завершена!

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

  • Много о том, как хорошо кодировать PHP
  • Поддержание кода в чистоте
  • Дистанцирование от проблемы может помочь вам решить ее
  • Рефакторить действительно проще, чем создавать
  • Всегда больше «тумана войны»

В части 3 я расскажу о том, чего я надеялся достичь, о целях, которые я изложил в своем PRD, и о том, как реальный опыт противоречил этому.