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

(Поскольку Medium не поддерживает математический рендеринг, я использовал изображения, чтобы показать более сложные уравнения. Для более доступной версии, пожалуйста, прочтите сообщение на моем личном веб-сайте.)

Резюме изменения размера изображения с учетом содержимого

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

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

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

Точный алгоритм подробно описан с примером кода в моем предыдущем посте.

Когда энергетическая функция недостаточно хороша

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

Уже сейчас мы видим одну проблему с определением функции энергии. Как я уже говорил в своем предыдущем посте:

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

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

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

Технически я не реализовал эту энергетическую функцию. Вместо этого я должен был извлечь квадратный корень из горизонтальной и вертикальной составляющих энергии по отдельности, прежде чем складывать их. На Github пользователь aryann попытался использовать квадратный корень из реализованной мной функции энергии, получив таким образом квадратный корень после сложения двух компонентов. Вывод:

Я использовал уменьшенную версию серфера для моего первоначального тестирования […], и в этих версиях функция энергии квадратного корня работала лучше.

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

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

Смотрим на энергию после снятия шва

Авторы оригинальной статьи в сотрудничестве с Майклом Рубинштейном через год выпустили новую статью под названием Улучшенная обработка швов для ретаргетинга видео. (Опять же, статья доступна в свободном доступе в Интернете.) В этой статье была поставлена ​​цель обобщить алгоритм вырезания швов на видео, и при этом авторы определили концепцию прямой энергии.

(Спасибо участнику Hacker News andreareina за указание мне на эту дополнительную статью.)

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

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

Определение прямой энергии в каждом пикселе

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

В предыдущем посте мы рассмотрели разницу между двумя соседними пикселями для любого заданного пикселя:

Однако после удаления шва нам придется учитывать пиксели, которые раньше не были смежными, но теперь являются смежными. Итак, начнем с определения концепции разницы в цвете между двумя произвольными пикселями p0 и p1:

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

  • Пиксель слева от текущего с координатами (x − 1, y) теперь касается пикселя справа от текущего с координатами (x + 1, у).
  • Пиксель над текущим, с координатами (x, y − 1), теперь касается пикселя слева от текущего, с координатами (x − 1, y) .

Эти новые кромки показаны на схеме ниже вместе с новыми кромками, образованными при соединении с другими швами.

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

Это означает, что для случая, когда мы подключаемся к шву в верхнем левом углу, новая cost - это разница в цвете между пикселями (x − 1, y) и (x + 1, y) вместе с разницей в цвете между пикселями (x, y − 1) и (x − 1, y). Мы называем эту стоимость CL (x, y).

Точно так же у нас есть затраты CU (x, y) для соединения с верхним швом и CR (x, y) для соединения с правым верхним швом. Итак, вместо того, чтобы связывать с каждым пикселем одну энергию, теперь у нас есть три стоимости:

Обратите внимание, поскольку текущий пиксель удаляется во всех трех случаях, первый член появляется во всех трех уравнениях. Пиксели слева и справа от текущего пикселя всегда сдвигаются вместе.

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

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

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

Например, три стоимости для левого края станут:

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

Динамическое программирование для поиска шва с наименьшей энергией

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

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

Опять же, с верхним рядом нужно обращаться особым образом. Нет смысла думать о соединении с другим швом из предыдущего ряда. Вместо этого мы смотрим только на эффект удаления текущего пикселя, который я описал в последнем разделе как захватываемый CU:

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

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

  • Вместо вычисления одного значения энергии для каждого пикселя, для каждого пикселя вычисляются три значения стоимости.
  • Основной цикл алгоритма выполняет другой расчет, который включает эти три значения стоимости.

Если вы хотите поэкспериментировать с моей реализацией и посмотреть на код C, прямая реализация энергии находится на Github.

Полученные результаты

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

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

Наконец, мы видим, как со временем улучшается процесс удаления швов:

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

Применение прямой энергии дало гораздо лучший результат:

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

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

Есть два вывода:

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

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