- PVSM.RU - https://www.pvsm.ru -
В приложениях по работе с изображениями довольно часто встречается задача ресайза джипегов (картинок, сжатых по алгоритму JPEG). В этом случае сразу сделать ресайз нельзя и нужно сначала декодировать исходные данные. Ничего сложного и нового в этом нет, но если это нужно делать много миллионов раз в сутки, то особую важность приобретает оптимизация производительности такого решения, которое должно быть очень быстрым.

Такая задача часто встречается при организации удалённого для хранилища картинок, поскольку большинство камер и телефонов снимают именно в формате JPEG. Ежедневно фото архивы ведущих веб-сервисов (социальных сетей, форумов, фото хостингов и многих других) пополняются значительным количеством таких изображений, поэтому вопрос о том, как именно хранить такие картинки, крайне важен. Для уменьшения размера исходящего трафика и для улучшения времени отклика на запрос пользователя, многие веб-сервисы хранят несколько десятков файлов для одного изображения в разных разрешениях. Скорость отклика получается хорошей, но места эти копии занимают много. Это основная проблема, хотя есть и другие минусы у такого подхода.
Идея решения этой проблемы состоит в том, чтобы не хранить на сервере много вариантов исходного изображения в разных разрешениях, а динамически создавать нужную картинку с заданными размерами из заранее приготовленного оригинала, причём как можно быстрее. Таким образом, в реальном времени можно создать изображение нужного разрешения и сразу его отправить пользователю. Очень важно, что разрешение этого изображения сразу можно сделать таким, чтобы устройство пользователя не делало экранный ресайз, так как в нём просто не будет необходимости.
Использование других форматов, отличных от JPEG, в качестве базовых для организации такого хранилища изображений не кажется оправданным. Конечно, есть стандартные, широко используемые форматы, которые дают лучшее сжатие при аналогичном качестве (JPEG2000, WebP), но скорость кодирования и декодирования таких изображений очень мала по сравнению с JPEG, поэтому имеет смысл выбрать именно JPEG качестве базового формата для хранения оригинальных фотографий, которые при необходимости будут отмасштабированы в реальном времени после получения запроса от пользователя.
Конечно, кроме джипегов на каждом сайте чаще всего есть изображения в форматах PNG и GIF, но обычно их относительное количество невелико, а фотографии в этих форматах хранятся крайне редко. Поэтому заметного влияния на рассматриваемую задачу эти форматы не внесут в большинстве случаев.
Итак, входными данными являются файлы формата JPEG, причём для достижения быстрого декодирования (это верно как для CPU, так и для GPU), сжатые изображения должны иметь встроенные рестарт-маркеры. Эти маркеры описаны в стандарте JPEG и часть кодеков умеет с ними работать, остальные умеют их не замечать. Если таких маркеров у джипегов нет, их можно заранее добавить с помощью утилиты jpegtran. При добавлении маркеров изображение не изменяется, но размер файла становится чуть больше. В итоге получаем следующую схему работы:
Можно сделать и более точное решение, когда перед ресайзом накладывается обратная гамма на каждую компоненту пиксела, чтобы ресайз был в линейном пространстве, а потом гамму применить снова, но уже после шарпа. Фактическая разница для пользователя невелика, но она существует, а вычислительные затраты для такой модификации минимальны. Нужно лишь вставить наложение обратной и прямой гаммы в общую схему обработки.
Также возможен вариант решения, когда декодирование джипегов выполняется на многоядерном CPU с помощью библиотеки libjpeg-turbo. В этом случае каждая картинка декодируется в отдельном потоке CPU, а все остальные действия выполняются на видеокарте. При наличии большого количества ядер CPU так может получиться даже быстрее, но будет серьёзный проигрыш в латентности. Если латентность при декодировании джипега на одном ядре CPU является допустимой, то этот вариант может оказаться очень быстрым, особенно для случая, когда исходные джипеги имеют небольшое разрешение. При увеличении разрешения исходного изображения, время декодирования джипега в одном потоке CPU будет увеличиваться, поэтому этот вариант может подойти только для небольших разрешений.
а) Если размер изображения не будет совпадать в размером окна, то устройство пользователя (телефон, планшет, ноутбук) перед выводом картинки на экран сделает аппаратный ресайз после декодирования. В OpenGL этот аппаратный ресайз делается только по билинейному алгоритму, который часто вызывает появление муара (разводов) и других артефактов на изображениях, содержащих мелкие детали.
б) Экранный ресайз дополнительно потребляет энергию устройства.
в) Если для решения задачи использовать серию заранее отмасштабированных изображений, то не всегда получится точно попасть в нужный размер, а значит, придётся посылать картинку большего разрешения. Увеличенный размер изображения приводит к большему трафику, чего тоже хотелось бы избежать.

Рестарт-маркеры. Мы можем быстро декодировать на видеокарте изображения в формате JPEG только в том случае, если внутри него находятся рестарт-маркеры. В официальном стандарте JPEG эти маркеры описаны, это стандартный параметр. Если рестарт-маркеров нет, то распараллелить декодирование картинки на видеокарте нельзя, что приведёт к очень малой скорости декодера. Поэтому нужна база приготовленных изображений, в которых есть эти маркеры.
Фиксированный алгоритм для кодека изображений. Декодирование и кодирование изображений по алгоритму JPEG является на сегодняшний день самым быстрым вариантом.
Разрешение изображений в приготовленной базе данных может быть любым, а в качестве вариантов мы рассмотрим 1К и 2К (можно взять и 4К). Также можно сделать не только уменьшение, но и увеличение изображений при ресайзе.
Мы протестировали приложение для быстрого ресайза из комплекта Fastvideo SDK на видеокарте NVIDIA Tesla V100 (OS Windows Server 2016, 64-bit, драйвер 24.21.13.9826) на 24-битных изображениях 1k_wild.ppm и 2k_wild.ppm с разрешением 1К и 2К (1280х720 и 1920х1080). Тесты проведены для разного количества потоков, запущенных на одной видеокарте. При этом требуется не более 110 МБайт памяти на видеокарте на один поток. На 4 потока нужно не более 440 МБайт.
Сначала сжимаем исходные изображения в JPEG c качеством 90%, с прореживанием 4:2:0 или 4:4:4. Затем декодируем и делаем ресайз в 2 раза по ширине и высоте, делаем шарп, потом опять кодируем c качеством 90% в 4:2:0 или в 4:4:4. Исходные данные лежат в оперативной памяти, финальная картинка размещается там же.
Время работы считается от начала загрузки исходной картинки из RAM до сохранения в RAM обработанного изображения. Время инициализации программы и выделения памяти на видеокарте не включены в измерения.
Пример командной строки для 24-битного изображения 1К
PhotoHostingSample.exe -i 1k_wild.90.444.jpg -o 1k_wild.640.jpg -outputWidth 640 -q 90 -s 444 -sharp_after 0.95 -repeat 200
Декодирование (включая пересылку данных на видеокарту): 0,70 мс
Ресайз в два раза (по ширине и по высоте): 0,27 мс
Шарп: 0,02 мс
Кодирование JPEG (включая пересылку данных с видеокарты): 0,20 мс
Суммарное время на один кадр: 1,2 мс
| Качество | Прореживание | Ресайз | Потоки | Частота кадров (Гц) | |
| 1 | 90% | 4:4:4 / 4:2:0 | 2 раза | 1 | 868 / 682 |
| 2 | 90% | 4:4:4 / 4:2:0 | 2 раза | 2 | 1039 / 790 |
| 3 | 90% | 4:4:4 / 4:2:0 | 2 раза | 3 | 993 / 831 |
| 4 | 90% | 4:4:4 / 4:2:0 | 2 раза | 4 | 1003 / 740 |
| Качество | Прореживание | Ресайз | Потоки | Частота кадров (Гц) | |
| 1 | 90% | 4:4:4 / 4:2:0 | 2 раза | 1 | 732 / 643 |
| 2 | 90% | 4:4:4 / 4:2:0 | 2 раза | 2 | 913 / 762 |
| 3 | 90% | 4:4:4 / 4:2:0 | 2 раза | 3 | 891 / 742 |
| 4 | 90% | 4:4:4 / 4:2:0 | 2 раза | 4 | 923 / 763 |
Прореживание 4:2:0 для исходного изображения уменьшает скорость работы, зато размеры исходного и конечного файлов становятся меньше. При переходе на 4:2:0 степень параллелизма падает в 4 раза, так как теперь блок 16х16 рассматривается как одно целое, поэтому в этом режиме скорость работы ниже, чем для 4:4:4.
Производительность в основном определяется стадией декодирования JPEG, поскольку на этой стадии картинка имеет максимальное разрешение, а вычислительная сложность этой стадии обработки выше, чем все остальные.
Результаты тестов показали, что для видеокарты NVIDIA Tesla V100 скорость обработки 1К и 2К изображений максимальна при запуске 2-4 потоков одновременно, и составляет от 800 до 1000 кадров в секунду на одну видеокарту. Обработка 1К картинок выполняется быстрее, чем 2К, а работа с изображениями 4:2:0 всегда выполняется медленнее, чем с 4:4:4. Для получения окончательного результата по производительности, нужно точно определить все параметры программы и оптимизировать её под конкретную модель видеокарты.
Латентность порядка одной миллисекунды — это неплохой результат. Насколько известно, такую латентность нельзя получить для аналогичной задачи ресайза на CPU (даже при отсутствии необходимости кодирования и декодирования джипегов), так что это ещё один важный аргумент в пользу применения видеокарт в высокопроизводительных решениях по обработке изображений.
Для обработки одного миллиарда джипегов в сутки с разрешениями 1K или 2K, может потребоваться до 16 видеокарт NVIDIA Tesla V100. Некоторые из наших заказчиков уже используют это решение, другие тестируют его в своих задачах.
Ресайз джипегов на видеокарте может быть очень полезен не только для веб-сервисов. Есть огромное количество высокопроизводительных приложений по обработке изображений, где такой функционал может быть востребован. Например, быстрый ресайз очень часто необходим практически для любой схемы обработки изображений, полученных от камер, перед выводом картинки на монитор. Такое решение может работать для Windows/Linux на любых видеокартах NVIDIA: Tegra K1/X1/X2/Xavier, GeForce GT/GTX/RTX, Quadro, Tesla.
Библиотека для быстрого ресайза джипегов может применяться в высоконагруженных веб-сервисах, больших онлайн-магазинах, социальных сетях, системах онлайн менеджмента фотографий, электронной коммерции, практически в любом программном обеспечении для управления предприятий большого размера.
Разработчики ПО могут использовать эту библиотеку, которая обеспечивает латентность порядка нескольких миллисекунд для ресайза джипегов с разрешением 1К, 2К и 4К на видеокарте.
По всей видимости, этот подход может оказаться более быстрым, чем решение NVIDIA DALI для быстрого декодирования джипегов, ресайза и подготовки изображений на этапе тренировки нейросетей для Deep Learning.
Автор: Серженко Фёдор
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/obrabotka-izobrazhenij/294171
Ссылки в тексте:
[1] хостинга: https://www.reg.ru/?rlink=reflink-717
[2] Источник: https://habr.com/post/424575/?utm_source=habrahabr&utm_medium=rss&utm_campaign=424575
Нажмите здесь для печати.