Один JPEG стал меньше на 21%. Другой способ увеличил его на 78%

Мы проверили JPEG XL на трёх графических изображениях проекта и двух фотографиях NASA. На обеих фотографиях обратимое преобразование JPEG уменьшило файл — на 20,93% и 23,4%. Кодирование декодированных RGB-пикселей, наоборот, увеличило файлы на 78,4% и 49,24%. Ниже — размеры и проверки восстановления.

Что сравнивали

Взяли три собственных изображения: аватар, мем и иллюстрированную карточку поиска фотографий. Все три — 1254×1254 пикселя. Это графические материалы проекта, а не репрезентативная подборка фотографий.

Каждый образец сначала преобразовали в RGB. Затем из одного массива пикселей получили два файла: PNG с оптимизацией и уровнем сжатия 9 и JPEG XL в режиме без потерь. Для JPEG XL использовали imagecodecs 2026.8.16, effort=7 и два потока.

Аватар: PNG — 1 234 239 байт; JPEG XL — 892 740 байт; меньше на 27,67%. Мем: PNG — 2 075 539 байт; JPEG XL — 1 353 747 байт; меньше на 34,78%. Карточка: PNG — 2 098 487 байт; JPEG XL — 1 401 414 байт; меньше на 33,22%.

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

Как проверяли «без потерь»

Каждый JPEG XL декодировали обратно в массив RGB и сравнили его с массивом, который поступал в кодировщик. Использовали точное сравнение numpy.array_equal, а не оценку «на глаз». Для всех трёх файлов результат — True: совпали все RGB-пиксели.

Исходные прозрачность и метаданные в сравнении не участвовали. Поэтому говорить, что сохранено абсолютно всё содержимое первоначального файла, нельзя. Мы проверили сохранение пикселей после преобразования в RGB.

Две фотографии и два режима JPEG XL

Взяли публичную фотографию NASA: 3912×3936 пикселей, исходный JPEG — 4 815 461 байт. Обратимое преобразование JPEG-потока в JPEG XL дало 3 807 692 байта — на 20,93% меньше. Затем восстановили JPEG и сравнили все байты с оригиналом: они совпали, SHA-256 тоже. В другом режиме сначала декодировали этот JPEG в RGB, затем закодировали пиксели в JPEG XL без потерь. Получили 8 590 110 байт — на 78,4% больше исходного JPEG. После декодирования RGB-пиксели совпали. Это разные проверки: восстановление исходного JPEG и сохранение его декодированных пикселей. Фото: https://commons.wikimedia.org/wiki/File:Aldrin_Apollo_11_original.jpg

Затем повторили опыт на втором JPEG — фотографии Земли с Apollo 17, 3000×3002 пикселя. Исходный файл: 6 511 067 байт. Обратимое преобразование: 4 987 524 байта, меньше на 23,4%; восстановленный JPEG совпал с оригиналом побайтно. Кодирование декодированных RGB-пикселей: 9 716 939 байт, больше на 49,24%; RGB-пиксели после декодирования совпали. Фото: https://commons.wikimedia.org/wiki/File:The_Earth_seen_from_Apollo_17.jpg

AVIF, HDR, анимацию и работу на телефоне мы не тестировали. Кодирование и декодирование выполнялись локально через библиотеку. Дополнительно проверили отображение одного примера: JPEG и JPEG XL открылись во встроенном браузере с размером 3912×3936 пикселей. JPEG XL передавался как image/jxl внутри локальной страницы. Это проверка одной среды; версия браузера, прямое открытие файла и другие приложения отдельно не проверены. Равенство пикселей в браузере не измерялось. В пакет для повторения опыта добавлена страница такой проверки.

Что это даёт владельцу сайта

Практический следующий шаг — повторить сравнение на собственных изображениях и отдельно проверить нужные браузеры. В официальном анонсе команда Chrome указывает поддержку декодирования JPEG XL начиная с версии 155. Это не подтверждает наличие поддержки в каждом установленном браузере.

Как повторить наш опыт. Работайте с копией JPEG. В imagecodecs 2026.8.16 передайте исходные байты в jpegxl_encode_jpeg (usecontainer=True, numthreads=2), восстановите через jpegxl_decode_jpeg и сравните все байты. Отдельно откройте тот же JPEG через Pillow, преобразуйте в RGB, получите массив NumPy и закодируйте через jpegxl_encode (lossless=True, effort=7, numthreads=2). Результат jpegxl_decode сравните с входным массивом через numpy.array_equal.

Запишите размеры исходного JPEG и двух полученных JPEG XL в байтах. Изменение считайте относительно исходного JPEG отдельно для каждого режима. Успешное восстановление не гарантирует уменьшения файла. Оригиналы сохраняйте до проверки восстановления и совместимости приложений.

Старые PNG и JPEG сами не станут меньше после обновления браузера: другой формат нужно подготовить и использовать там, где он подходит.

На трёх графических образцах JPEG XL оказался меньше PNG. На двух выбранных фотографиях уменьшение или увеличение относительно JPEG зависело от режима преобразования. Это не репрезентативный тест фотоархива: перед преобразованием собственных файлов проверяйте копии, восстановление и совместимость нужных приложений.

Библиотека эксперимента: https://github.com/cgohlke/imagecodecs

В «Завтра пригодится» проверяем новые инструменты на конкретных задачах и показываем границы результата: https://t.me/+sHppvS3zGlozNzQ6

Что у вас занимает больше места: фотографии, скриншоты или картинки для работы?