Сжатое изображение стало больше? Три реальные причины
Скорее всего, средство сжатия исправно. Когда сжатие изображения не работает — на выходе тот же размер или файл заметно больше исходного, — причина почти всегда одна из трех, и сломанный инструмент в этот список не входит.
Первая причина: любой инструмент, который сжимает через элемент canvas браузера, выбрасывает color type исходного PNG и заново кодирует каждый пиксель как 32-битный RGBA. Мы прогнали через canvas.toBlob('image/png') в Chrome 151.0.0.0 все 83 PNG-карточки Open Graph этого сайта. Все 83 стали больше. Медианный рост — +76,6%, минимальный — +35,6%, максимальный — +237,2%.
Вторая причина: JPEG на входе уже сжат. Прогоните его пять раз подряд с качеством 0.8, и размер перестанет меняться после второго прохода, хотя каждый проход продолжит портить картинку.
Третья: PNG уже был квантован. Цветовой избыточности, которую мог бы убрать второй проход, там больше нет.
Ни один из этих случаев не лечится сдвигом ползунка качества вниз. Лечение — другой формат или другой кодировщик. Те же 83 PNG, сконвертированные в WebP с качеством 0.8, уменьшились все до одного, медиана — −94,2%.
Как получены эти числа. Chrome 151.0.0.0 под управлением Playwright, macOS 26.5.2, Node v25.8.2, upng-js 2.1.0, ImageMagick. Набор из 83 файлов — это все PNG в каталоге
public/ogэтого сайта, а не выборка из них. Еще четыре контрольных файла размером 1200×630 и 800×600 покрывают по отдельности случаи фотографии, графики, палитры и JPEG.
1. Сжатие не работает: диагностика за тридцать секунд
Найдите свою строку и переходите к разделу, на который она указывает.
| Что подали на вход | Что получили | Первопричина | Читать |
|---|---|---|---|
| PNG-скриншот или графика | Больше оригинала | canvas перекодировал его в 32-битный RGBA | Раздел 2 |
| JPEG-фотография | Больше оригинала | Файл сохранили как PNG | Раздел 2 |
| JPEG-фотография | Почти без изменений | Размер уже вышел на плато | Раздел 3 |
| PNG, уже прошедший через сжатие | Без изменений или чуть больше | Запаса не осталось | Раздел 4 |
| Любой файл | Меньше, но размыто или с искаженным цветом | Потери поколений или срезанный ICC-профиль | Разделы 3 и 6 |
Строки не исключают друг друга. JPEG-фотография, брошенная в PNG-экспортер на основе canvas, попадает сразу в первые две, и именно так файл на 47 828 байт превращается в файл на 809 415 байт.
Если диагностика не нужна и хочется просто получить файл поменьше, наше бесплатное сжатие изображений использует квантующий PNG-кодировщик вместо прогона через canvas и выбрасывает собственный результат всякий раз, когда он не меньше загруженного файла.
2. Причина первая: canvas браузера всегда пишет 32-битный RGBA
Что canvas.toBlob() делает с PNG
В прогоне через canvas нет шага сжатия. Есть декодирование и повторное кодирование, а между ними теряется всё, что не является значением пикселя.
Нарисуйте изображение на canvas — и браузер декодирует его в плоский буфер RGBA: четыре байта на пиксель. Палитра, пониженная глубина цвета и метаданные до этого буфера не доходят. Затем HTMLCanvasElement.toBlob() кодирует этот буфер с нуля. Спецификация PNG описывает шесть color types, и кодировщик волен выбрать самый дешевый из них, который способен представить изображение. Кодировщик canvas в Chrome не выбирает. Он всегда выдает color type 6.
| Color type на входе | Что возвращает canvas.toBlob('image/png') |
|---|---|
| RGB (color type 2) | RGBA (color type 6) |
| Палитра (color type 3) | RGBA (color type 6) |
| JPEG (у него нет PNG color type) | RGBA (color type 6) |
Это читается прямо из байтов. 25-й байт PNG-файла — глубина цвета, 26-й — color type, оба лежат внутри чанка IHDR:
xxd -s 24 -l 2 -p suspect.png
# 0806 -> глубина 8 бит, color type 6 (RGBA)
Все три типа входных данных выше дали depth=8 type=6. Изображение с палитрой из 64 цветов хранит один байт на пиксель плюс небольшую таблицу; после прогона оно хранит четыре байта на пиксель, а таблицы больше нет. Deflate отыгрывает часть этой разницы, но никогда не всю.
Повторить это в своем браузере можно консольным сниппетом: он выбирает файл, прогоняет его через canvas и печатает оба размера вместе с color type:
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/png,image/jpeg';
input.onchange = async () => {
const file = input.files[0];
const bitmap = await createImageBitmap(file);
const canvas = document.createElement('canvas');
canvas.width = bitmap.width;
canvas.height = bitmap.height;
canvas.getContext('2d').drawImage(bitmap, 0, 0);
const blob = await new Promise((r) => canvas.toBlob(r, 'image/png'));
const head = new Uint8Array(await blob.slice(0, 26).arrayBuffer());
console.log(file.name, file.size, '->', blob.size);
console.log('bit depth', head[24], 'color type', head[25]);
};
input.click();
Аргумент качества у toBlob для image/png игнорируется. PNG — формат без потерь, обменивать число качества здесь не на что, и ползунок, который в инструменте на основе canvas будто бы управляет сжатием PNG, не управляет ничем.
Измерено 83 файла: каждое сжатое изображение вернулось больше оригинала
Весь прогон, без отбора удобных случаев:
| Показатель | Значение |
|---|---|
| Проверено файлов | 83 (все PNG в public/og, оба color type — RGB и палитра) |
| Файлов стало больше | 83 / 83 (100%) |
| Минимальный прирост | +35,6% |
| Медианный прирост | +76,6% |
| Максимальный прирост | +237,2% |
Один файл для наглядности: aes-decrypt.png вырос с 505 516 B до 898 014 B, прирост +77,6%. Тот же файл, закодированный в WebP с качеством 0.8, весит 27 188 B, −94,6%.
Границы выборки здесь важны. Эти 83 файла — карточки Open Graph: 1200×630, плоские фоны, крупный текст, несколько фирменных цветов. Это ровно та графика, с которой хороший PNG-кодировщик справляется отлично, и именно поэтому прогон через canvas бьет по ней так сильно. Результат убедителен для PNG графического типа; это не утверждение, что любой PNG на свете растет после прогона через canvas. Фотографический PNG, который и так хранился полным RGBA, теряет куда меньше.
Зато вывод держится и за пределами такой выборки: если сжатое изображение больше оригинала, а инструмент работает во вкладке браузера, смотреть надо в первую очередь на кодировщик, а не на настройки.
Почему JPEG, сохраненный как PNG, растет в 16,9 раза
Это самая большая разница во всем наборе данных. Четыре контрольных файла, все четыре прогнаны по одному и тому же пути через canvas:
| Файл | Характер | Оригинал | canvas PNG | Изменение | JPEG q92 | JPEG q80 | WebP q80 |
|---|---|---|---|---|---|---|---|
og-a.png | Графика, 2 351 цвет, RGB | 306 302 | 607 481 | +98,3% | 56 459 | 37 695 | 13 852 |
quantized.png | Палитра из 64 цветов | 59 843 | 184 856 | +208,9% | 78 388 | 44 442 | 16 046 |
photo.png | Фото, 479 373 цвета | 498 639 | 867 763 | +74,0% | 77 321 | 45 050 | 24 068 |
photo.jpg | JPEG q82 | 47 828 | 809 415 | +1 592% (16,9×) | 58 762 (+22,9%) | 47 152 | 23 862 |
В последней строке: на входе 47 828 байт, на выходе 809 415 байт.
Механизм здесь объясняет целое семейство жалоб вида «сжатие сделало файл больше». JPEG — кодек с потерями, работающий в частотной области: он переводит блоки 8×8 в коэффициенты DCT, агрессивно их квантует и хранит выживших. PNG — кодек без потерь, работающий в пространственной области: он предсказывает каждый пиксель по соседям и сжимает остатки алгоритмом deflate. Декодируйте JPEG — и получите пиксели, которые несут все артефакты, внесенные квантователем: звон у краев, блочность в плавных градиентах, едва заметный шум там, где в оригинале его не было.
Сохраните эти пиксели как PNG — и вы просите кодек без потерь сохранить артефакты в точности. Он соглашается. Тот самый шум, который JPEG создал, чтобы файл стал маленьким, теперь и делает PNG большим: шум — ровно то, что предсказывающий кодировщик без потерь сжать не может.
Это происходит везде, где перед фотографией стоит умолчание «сохранить как PNG»: инструменты для скриншотов, экспорт из дизайн-редакторов, мессенджеры, некоторые виджеты загрузки.
Те же пиксели, разница в 3,46 раза, оба варианта без потерь
Кодировщик canvas измеримо слаб. Возьмем буфер пикселей из og-a.png и закодируем его двумя способами, оба полностью без потерь:
| Кодировщик | Выход | Color type |
|---|---|---|
Chrome canvas toBlob('image/png') | 607 481 B | RGBA (принудительное расширение) |
upng-js encode(..., cnum=0) | 175 491 B | RGB (исходный color type сохранен) |
3,46× на идентичных пикселях. Утверждение про отсутствие потерь мы проверили, а не приняли на веру: декодирование вывода upng-js при cnum=0 возвращает буфер, побайтово совпадающий с исходным буфером RGBA. Эти 3,46× не стоили ничего.
Это ответ на вопрос, который задают постоянно: почему два онлайн-сервиса сжатия, оба бесплатные, оба обещающие одно и то же, дают несопоставимые результаты? Потому что «сжатие в браузере» описывает две совершенно разные реализации. Одна отдает пиксели в canvas.toBlob и отгружает то, что вернулось. Другая несет в себе настоящий PNG-кодировщик и управляет color type. Тот же вход, тот же браузер, разница в 3,46 раза.
3. Причина вторая: вашему JPEG больше нечего отдавать
Пять раундов пересжатия, и размер перестает двигаться
Возьмем photo.jpg, уже сохраненный с качеством 82, и пересожмем его с качеством 0.8 пять раз подряд, подавая каждое поколение на вход следующему:
| Поколение | Байты | К предыдущему |
|---|---|---|
| 0 (оригинал, q82) | 47 828 | — |
| 1 | 47 152 | −1,4% |
| 2 | 47 164 | +0,0% |
| 3 | 47 156 | −0,0% |
| 4 | 47 156 | 0,0% |
| 5 | 47 156 | 0,0% |
Первый проход дает 1,4%. Начиная со второго поколения размер файла заперт в полосе ±12 байт, а поколения 4 и 5 совпадают по размеру с третьим байт в байт.
Вот так выглядит «сжатие не работает», когда на входе JPEG. Инструмент отработал. Кодировщик отработал. Убирать было просто нечего: таблицы квантования на качестве 80 обнуляли примерно те же коэффициенты, которые качество 82 оставило. Однажды исчезнувший коэффициент нельзя убрать повторно.
Проверить это можно локально:
cp photo.jpg gen0.jpg
for i in 1 2 3 4 5; do
magick "gen$((i-1)).jpg" -quality 80 "gen$i.jpg"
done
stat -f%z gen0.jpg gen1.jpg gen2.jpg gen3.jpg gen4.jpg gen5.jpg
В Linux вместо этого нужен stat -c%s. Точные значения в байтах зависят от того, с каким кодировщиком слинкована сборка ImageMagick, так что не ждите, что таблица выше воспроизведется цифра в цифру, — важна форма кривой. Одно осмысленное падение, дальше прямая линия.
Поднять число качества — это не сжатие
Посмотрите еще раз на строку photo.jpg в разделе 2. Пересжатие этого оригинала с качеством 82 на качестве 92 дало 58 762 байта: +22,9%.
Это удивляет тех, кто считает параметр качества регулятором от «маленького» к «большому», который можно ставить куда угодно. Это не абсолютная цель по качеству. Параметр выбирает таблицу квантования, и прогон уже декодированного изображения через более мелкую таблицу, чем та, что его породила, сохраняет имеющиеся артефакты точнее и сверху добавляет новый круг потерь. Файл больше, картинка хуже — и то и другое сразу.
Отсюда правило: никогда не пересжимайте JPEG с качеством выше того, с которым он был сохранен. Если это значение неизвестно, не пересжимайте вовсе. Вернитесь к исходнику.
Потери поколений: та потеря качества при пересжатии JPEG, которую не видно
В таблице с плато спрятана ловушка. Размер перестал меняться после второго поколения, а изображение меняться не перестало. Каждый проход декодирует в пиксели, заново выполняет преобразование и заново квантует. Коэффициенты, которые в одном поколении удержались на границе, в следующем через нее переваливают.
Ущерб проявляется не там, где его ищут. В размере миниатюры пятое поколение неотличимо от первого. Увеличьте до 100% и проверьте места, где JPEG сдает первым: резкие края на плоском фоне, текст и плавные градиенты, где блочность видна как плитки 8×8. В сборочном пайплайне, который пересжимает на каждом деплое, это молча копится месяцами.
Здесь мы измеряли размеры файлов, а не воспринимаемое качество, поэтому цифру PSNR или SSIM для пяти поколений мы не приведем. Мы ее не измеряли. Данных по размеру и без того достаточно: начиная со второго поколения вся цена платится качеством, а выгода равна нулю.
Сдвиг цвета — отдельная поломка с тем же спусковым крючком. Canvas не переносит метаданные, поэтому прогон через toBlob выбрасывает блок EXIF, а вместе с ним и ICC-профиль. Изображение с меткой Display P3 или Adobe RGB на входе выходит без метки, и просмотрщики истолкуют его как sRGB. Значения пикселей не изменились. Изменилась инструкция, как их читать.
4. Причина третья: размер PNG не уменьшается, потому что его уже квантовали
Если PNG уже проходил через сжатие один раз, второму проходу работать не с чем. Это строка quantized.png — файл на 59 843 байта, уже сведенный к палитре из 64 цветов и прогнанный по трем разным путям:
| Путь | Результат | К оригиналу |
|---|---|---|
Перекодирование без потерь (upng cnum=0) | 61 377 | +2,6% |
| Квантование до 256 цветов | 61 366 | +2,5% |
| Квантование до 64 цветов | 61 366 | +2,5% |
Каждый путь дал результат больше оригинала. Ненамного, но больше — включая квантование до 64 цветов файла, в котором и так было 64 цвета.
Сжатие PNG устроено как удаление избыточности: повторяющиеся цвета, предсказуемые соседи, небольшая палитра. Предыдущий проход всё это уже собрал. Остаток почти не сжимаем, а крошечный прирост — это собственные накладные расходы кодировщика: чуть иной порядок палитры, другой выбор фильтра для каждой строки развертки, чуть менее удачный deflate.
Практическое следствие: «сэкономлено 0%» на уже оптимизированном PNG — это правильный результат, а не сбой. Инструмент, который сообщает о небольшом приросте и оставляет исходный файл, ведет себя как надо. Инструмент, который всё равно отдает файл побольше, — нет.
5. Настоящий рычаг для PNG — квантование, а не перекодирование
Перекодирование без потерь против 256 цветов и против 64 цветов
Три файла, три стратегии:
| Файл | Оригинал | Без потерь (cnum=0) | 256 цветов | 64 цвета |
|---|---|---|---|---|
og-a.png | 306 302 | 175 491 (−42,7%) | 110 772 (−63,8%) | 76 230 (−75,1%) |
photo.png | 498 639 | 587 863 (+17,9%) | 109 284 (−78,1%) | 61 397 (−87,7%) |
quantized.png | 59 843 | 61 377 (+2,6%) | 61 366 (+2,5%) | 61 366 (+2,5%) |
Перекодирование без потерь — самый слабый доступный инструмент. На графике оно выиграло 42,7%, на фотографии отдало обратно 17,9%, на уже квантованном файле проиграло 2,6%. Фотографический материал побеждает даже грамотный PNG-кодировщик без потерь: палитру там не найти, а соседние пиксели плохо предсказывают друг друга.
Настоящее уменьшение дает квантование, и отрыв большой: 63,8% против 42,7% на той же графике при 256 цветах и 75,1% при 64. В командной строке:
magick logo.png -colors 64 PNG8:logo-64.png
magick logo.png -colors 256 PNG8:logo-256.png
Префикс PNG8: принудительно дает PNG с палитрой. Без него ImageMagick может сократить число цветов и всё равно записать truecolor-файл, а это съедает большую часть выигрыша.
Когда палитра безопасна, а когда появляются полосы
Квантование — операция с потерями. Оно сопоставляет каждому пикселю ближайшую запись ограниченной палитры, поэтому вопрос в том, достаточно ли в материале различных цветов, чтобы это стало заметно.
Безопасно: иконки, логотипы, скриншоты интерфейса, плоские иллюстрации, схемы — всё, где есть большие области однородного цвета и резкие границы. В таких изображениях обычно от силы несколько сотен различных цветов, поэтому палитра на 256 записей достается почти даром, а нередко выживают и 64.
Рискованно: фотографии, плавные градиенты, мягкие тени и полупрозрачные наложения. Сведение градиента к 64 ступеням дает видимые полосы, а dithering меняет эти полосы на шум, который потом отъедает часть выигрыша в размере. Частичная прозрачность поверх градиента — самый тяжелый случай из всех.
Прозрачность заслуживает отдельной проверки: сколько ее уцелеет, зависит от кодировщика, а не от самого квантования. PNG8: в ImageMagick пишет бинарную прозрачность — пиксель либо полностью непрозрачен, либо полностью прозрачен, и мягкий сглаженный край возвращается резким. Специализированный квантователь PNG сохраняет полный альфа-канал, а вместе с ним и мягкий край. Если в материале есть тень или растушеванные края, сравните оба варианта, прежде чем выбирать.
Проверяйте результат на 100%, а не в миниатюре. Полосы — тот самый артефакт, который уменьшенный предпросмотр надежно прячет.
6. Решение: менять формат, а не пересжимать
Таблица выбора формата
| Материал | Что использовать | Почему |
|---|---|---|
| Фотографии | WebP, а ради максимальной совместимости JPEG | Фотографиям нужно частотное кодирование с потерями |
| Скриншоты, графика интерфейса | Квантованный PNG или WebP | Плоские цвета, резкие границы, небольшие палитры |
| Иконки и логотипы | SVG, если вектор есть, иначе квантованный PNG | У векторов нет проблемы разрешения |
| Всё, где нужна прозрачность | WebP или PNG | Оба несут полный альфа-канал |
| Анимация | WebP | Один формат вместо GIF |
| Попиксельно точный архив | PNG без потерь | Единственный случай, где отсутствие потерь — требование |
Это намеренно краткая версия. Эффективности кодирования и поддержке современных форматов в браузерах посвящена отдельная статья: WebP vs AVIF vs JPEG.
Что WebP сделал с теми же 83 файлами
Тот же набор из 83 файлов, который через canvas PNG рос в 100% случаев, сконвертированный вместо этого в WebP с качеством 0.8: 83 из 83 стали меньше, медиана −94,2%. Уже знакомый файл aes-decrypt.png уменьшился с 505 516 B до 27 188 B, −94,6%.
Контрольные файлы согласуются. og-a.png: 306 302 в PNG и 13 852 в WebP q80. photo.png: 498 639 в PNG и 24 068 в WebP q80. photo.jpg: 47 828 в JPEG и 23 862 в WebP q80.
К последнему сравнению одна оговорка. WebP с качеством 0.8 — формат с потерями, так что с PNG он соревнуется не на равных, а перекодирование существующего JPEG в WebP всё равно стоит одного поколения. Сравнивать надо с тем, что вы собирались выкладывать, а не с гипотетическим идеальным оригиналом.
Когда PNG всё же стоит оставить
Иногда отсутствие потерь — требование, а не предпочтение. Оставляйте PNG для материалов, которые вернутся в дизайн-процесс и будут редактироваться снова; для скриншотов в тестах попиксельного сравнения; для нарезки интерфейса, где один сдвинутый цвет ломает визуальный diff; и для всего, что позже будут накладывать слоями, — там артефакты квантования складываются. В таких случаях запускайте нормальный квантующий кодировщик, если материал это позволяет, и смиритесь с размером файла, если не позволяет.
Еще один вид «стало больше», не связанный со сжатием
Если встраивать изображения как data URI, размер в CSS или HTML — это не размер на диске. Base64 кодирует каждые 3 байта четырьмя символами плюс padding, поэтому текст арифметически на +33% больше самих байтов. И это еще до всякого сжатия на транспорте. Идеально оптимизированное изображение всё равно вырастает на треть в момент встраивания. Наше руководство по встраиванию через data URI разбирает, когда такой размен оправдан.
7. На практике: браузер, командная строка, сборочный пайплайн
В браузере
При выборе браузерного инструмента значение имеет одно различие: проходит ли PNG через canvas. В нашем бесплатном сжатии изображений — не проходит. PNG на входе квантуется до цветовой палитры и записывается обратно настоящим PNG с целым альфа-каналом, поэтому прозрачность и мягкие края выживают; ползунок качества отображается на размер палитры, а не на аргумент toBlob, который PNG всё равно проигнорировал бы. Качество 100 соответствует перекодированию без потерь. JPEG и WebP через canvas проходят — там параметр качества настоящий и делает ровно то, чего от него ждут.
Одно поведение выглядит багом, но им не является: если сжатый результат не меньше загруженного файла, инструмент выбрасывает собственный вывод и оставляет исходные байты. На уже оптимизированном PNG вы увидите «сэкономлено 0%». Это раздел 4, работающий как задумано.
Обработка идет в браузере, файлы никуда не уходят.
В командной строке
cwebp поставляется вместе с libwebp и быстрее всего позволяет проверить, решает ли смена формата вашу задачу:
# WebP с потерями, качество 0-100
cwebp -q 80 photo.png -o photo.webp
# WebP без потерь, уровень усилий сжатия 0-9
cwebp -z 9 logo.png -o logo.webp
ImageMagick 7 закрывает случаи конвертации и квантования:
# PNG в JPEG с выбранным качеством
magick photo.png -quality 80 photo.jpg
# Квантование в PNG с палитрой из 64 цветов
magick logo.png -colors 64 PNG8:logo-64.png
# Убрать EXIF и прочие метаданные
magick photo.jpg -strip photo-clean.jpg
В macOS утилита sips уже установлена и не требует зависимостей:
# Конвертация в JPEG; formatOptions принимает 0-100 или low/normal/high/best
sips -s format jpeg -s formatOptions 80 photo.png --out photo.jpg
# Ресайз до 1200 px по длинной стороне с сохранением пропорций
sips -Z 1200 photo.jpg --out photo-1200.jpg
# Прочитать размеры обратно
sips -g pixelWidth -g pixelHeight photo-1200.jpg
Сначала ресайз, потом сжатие. Кодировщики работают с пикселями, а самый дешевый пиксель — тот, которого нет.
В сборочном пайплайне
Когда процесс автоматизирован, а не выполняется руками, важным становится другое: где идет работа и какая библиотека ее делает. Компромиссы там совсем иные, и разбирает их отдельная статья — сжатие изображений: браузер против Node.js. Единственное правило, которое переносится сюда из этой статьи: сжимайте на каждой сборке из исходного файла, а не из вывода предыдущей сборки. Именно так пайплайн сам себя сводит вниз по кривой потерь поколений, пока размеры файлов выглядят идеально стабильными.
8. Пять убеждений, которые замеры не подтверждают
«Сжать дважды — станет меньше». Первое поколение дало 1,4%. Со второго по пятое держались в пределах ±12 байт, а картинка при этом продолжала деградировать. Второй проход — чистые издержки.
«PNG без потерь, значит, формат лучше». Отсутствие потерь — свойство, а не достоинство. Наша тестовая фотография весит 498 639 байт в PNG и 24 068 в WebP q80. Побитовое сохранение фотографии, которую всё равно будут смотреть только на экране, не дает ничего и стоит большей части файла.
«Качество 100 — безопасный выбор». Пересжатие JPEG с качеством 82 на качестве 92 дало +22,9% и картинку хуже. Выше исходного значения качества это число перестает означать «безопаснее» и начинает означать «больше».
«Файл большой, потому что высокое разрешение». Разрешение важно, но при одном и том же разрешении формат важнее. og-a.png в обоих случаях 1200×630: 607 481 байт как canvas PNG и 13 852 байта как WebP q80. Число пикселей идентично.
«Онлайн-сервисы сжатия все одинаковые». Те же пиксели, тот же браузер, оба без потерь: 607 481 байт из canvas и 175 491 из upng-js. Разброс в 3,46 раза между двумя инструментами, которые описывают себя одинаково.
9. Частые вопросы
Почему сжатое изображение больше оригинала?
Потому что инструмент не сжал его, а перекодировал. Вывод браузерного canvas — всегда 32-битный RGBA PNG: палитры отбрасываются, на пиксель уходит четыре байта. В нашем тесте на 83 реальных PNG выросли все 83, медианный прирост — +76,6%. Вместо этого конвертируйте в WebP или JPEG.
Почему PNG не уменьшается при сжатии?
PNG — формат без потерь, поэтому ползунку качества нечего обменивать. Реальное уменьшение дает сокращение числа цветов, а если файл уже квантован, сокращать нечего. Наш тестовый PNG на 64 цвета после повторного квантования до 64 цветов вернулся с приростом +2,5%.
Теряет ли JPEG качество при повторном сжатии?
Да, и взамен вы почти ничего не получаете. Пять раундов с качеством 0.8: 47 828 байт превратились в 47 152 на первом проходе, а следующие четыре держались в пределах 12 байт. Размер перестал двигаться, а каждый проход продолжал заново квантовать картинку. Храните оригиналы.
Что выбрать для уменьшения файла — PNG или JPEG?
Для фотографий — JPEG или WebP, без исключений. Наша тестовая фотография весит 498 639 байт в PNG, 45 050 в JPEG q80 и 24 068 в WebP q80. PNG оставьте для плоской графики, четкого текста и прозрачности, где работу по сжатию выполняет небольшая палитра.
Почему изображение стало размытым после сжатия?
Причин две. Кодировщики с потерями на низком качестве дают заметную блочность вокруг границ и текста. Повторные проходы добавляют потери поколений, даже когда размер файла перестал меняться. А если цвета не размылись, а сдвинулись, то прогон через canvas выбросил ICC-профиль: canvas не переносит метаданные вообще.
Можно ли сжать изображение без потери качества?
Да, но ждите куда меньшего. Перекодирование без потерь всего лишь записывает те же самые пиксели эффективнее: с 306 302 до 175 491 байта на нашей графике, с проверкой побайтового совпадения после декодирования. Тот же подход на фотографии сработал в обратную сторону — +17,9%. Ради реальной экономии без видимых потерь берите WebP с качеством 80.
Почему PNG со скриншотом такой большой?
Снимки экрана сохраняются как полноценный RGBA PNG — четыре байта на пиксель до сжатия, — а Retina-дисплей удваивает число пикселей по каждой стороне. Плоское содержимое хорошо отзывается на сокращение палитры: наша графика карточки Open Graph похудела на 63,8% при 256 цветах и на 75,1% при 64.
Уменьшает ли ресайз размер файла сильнее, чем сжатие?
Обычно да, и эффекты складываются. Уменьшение обеих сторон вдвое убирает три четверти пикселей еще до старта кодировщика, а размер файла в целом следует за числом пикселей. Сначала приведите картинку к тем размерам, в которых она реально показывается, затем сожмите один раз. Файл с камеры в полном разрешении, вставленный в слот миниатюры, тратит впустую оба прохода.