Содержание статьи +
- TL;DR
- Зачем это вам
- Что происходит в quantization, в одном абзаце
- Шаг квантования – что значит это число
- Quantization parameter – одна ручка, логарифмическая шкала
- Пример на одном блоке
- Dead zone – маленький приём, который много значит
- Quantization matrices – разная точность для разных частот
- Dependent quantization – полушаг VVC
- Adaptive quantization – тратить биты там, куда смотрит глаз
- Как сравниваются кодеки – одна таблица
- CRF, CBR, VBR – quantization под другими именами
- Частые артефакты от quantization
- Где здесь Фора Софт
- Ключевые выводы
- Что почитать дальше
TL;DR
Quantization – единственный этап видеокодека, где информация теряется навсегда: каждый размытый край, каждый «грязный» блок неба, каждый плоский участок, где должен быть градиент, а его нет, – всё ведёт к этому одному шагу. Кодер делит каждый transform-коэффициент на шаг квантования, округляет до ближайшего целого, и маленькие числа превращаются в нули, которые почти не стоят бит при передаче. У каждого кодека своя обёртка вокруг одной и той же идеи: H.264 использует quantization parameter от 0 до 51 с шагом, удваивающимся каждые шесть пунктов; HEVC и VP9 повторяют ту же шкалу на бо́льших блоках; AV1 называет это qindex и даёт 256 уровней; VVC добавляет dependent quantization, фактически дающий кодеру половину дополнительного шага. Освойте quantization – и одной ручкой вы будете управлять качеством, битрейтом и видимыми артефактами.
Зачем это вам
Главное число, которое настраивает оператор кодера, – это quantization parameter, обычно сокращаемое до QP (параметр, который контролирует жёсткость округления коэффициентов). Когда стриминговая платформа режет битрейт пополам, чтобы сэкономить на CDN, экономия идёт почти целиком из роста QP – не из «более умного кодека», не из нового алгоритма, не из новой железки. Продакт-менеджер, который умеет прочитать значение QP в логе FFmpeg и предсказать, выйдет ли результат смотрибельным или сломанным, отсекает половину плохих идей в encoding-роудмапе до того, как они дойдут до продакшена. Основатель, который объясняет инвесторам, почему «снижение битрейта на 30%» иногда выходит чистым, а иногда блочным, понимает единственный источник перцептивного риска во всём видеопайплайне. Технический лид, который знает, когда ставить плоскую quantization-матрицу, а когда отгружать кастомную, выигрывает 1–2 пункта VMAF на сервисе, который отдаёт миллион часов в сутки, – а это уже деньги.
Что происходит в quantization, в одном абзаце
После того как стадия предсказания произвела residual-блок, а transform размазал его энергию по сетке коэффициентов – мы разобрали эти шаги в intra-frame coding, inter-frame coding и motion estimation и transform coding, – кодер делит каждый коэффициент на шаг и округляет результат до ближайшего целого. Это и есть quantization. Шаг – одно число, и это число под управлением оператора: маленький шаг сохраняет большую часть точности и даёт большой битстрим, большой шаг обнуляет почти все коэффициенты и даёт маленький битстрим. Когда декодер восстанавливает блок, он умножает уцелевшие целые на тот же шаг, но ошибка округления исчезла навсегда. Эта ошибка округления – единственное место во всём кодеке, где информация уничтожается; всё остальное в принципе обратимо.
Шаг квантования – что значит это число
Шаг, обычно записываемый как Qstep, – это расстояние на числовой оси между двумя значениями, которые кодек может представить. Если Qstep равен 8, а коэффициент 21, кодер округляет 21 ÷ 8 до 3, передаёт целое число 3, а декодер умножает обратно и получает 24 – ошибка 3. Если Qstep равен 32, а тот же коэффициент 21, кодер округляет 21 ÷ 32 до 1, декодер выдаёт 32, ошибка – 11. Чем больше шаг, тем больше средняя ошибка на коэффициент, но тем меньше целые числа, которые приходится передавать энтропийному кодеру.
Арифметика короткая:
quantized = round(coefficient / Qstep) # кодер
recovered = quantized × Qstep # декодер
error = recovered − coefficient # всегда |error| ≤ Qstep / 2Подставим коэффициент 87 и Qstep = 16:
quantized = round(87 / 16) = round(5.4375) = 5
recovered = 5 × 16 = 80
error = 80 − 87 = −7Ошибка ограничена половиной шага – в данном случае половиной 16, то есть 8 – и граница точная. Каждая ячейка transform-блока получает свою независимую ошибку внутри этой границы, и сумма этих ошибок, пропущенная через обратный transform, – это и есть то, что зритель в итоге увидит как падение качества.
Почему это превращается в меньшие файлы? Потому что бо́льшая часть ячеек типичного transform-блока несёт маленькие числа, и как только Qstep становится больше двух таких чисел, они округляются в ноль. Энтропийный кодер тратит почти ноль бит на длинный ряд нулей. Большие числа в верхнем левом углу – структурная энергия блока – выживают, и стоимость блока схлопывается с «16 целых чисел» до «два ненулевых коэффициента плюс флаг, что остальное – нули».
Quantization parameter – одна ручка, логарифмическая шкала
Современные кодеки не показывают Qstep напрямую. Они показывают маленькое целое – quantization parameter – QP в H.264, HEVC, VP9, VVC и qindex в AV1, а Qstep выводится из фиксированной математической формулы. Связь логарифмическая, и это сделано специально: глаз реагирует на относительные изменения ошибки, не на абсолютные, поэтому удвоение Qstep должно соответствовать одному перцептивному «щелчку», а не пятидесяти.
В H.264, HEVC, VP9 и VVC правило одно и то же: Qstep удваивается каждые шесть пунктов QP. Конкретно:
Qstep(QP) = 2 ** ((QP − 4) / 6)Это фиксирует Qstep на 1 при QP = 4, на 2 при QP = 10, на 4 при QP = 16, на 8 при QP = 22, на 16 при QP = 28, на 32 при QP = 34 и так далее. Минимальный QP в стандарте – 0, для 8-битного контента диапазон H.264 / HEVC – от 0 до 51, с расширениями для 10-бит (0..63) и 12-бит (0..75). Каждый плюс один к QP увеличивает шаг примерно на 12,2%, и каждые шесть пунктов шаг ровно удваивается. Кодер выбирает QP, декодер читает QP, оба вычисляют Qstep одинаково.
Правило удвоения и делает QP интуитивно понятным, как только с ним поработаешь. Переход с QP 23 на QP 29 удваивает среднюю ошибку квантования; переход с QP 23 на QP 35 учетверяет её. Стриминг-инженер, который запоминает четыре опорные точки – QP 23 для «прозрачного качества», QP 28 для «хорошо», QP 33 для «смотрибельно на маленьком экране», QP 38 для «заметно мягкая картинка» – предсказывает результат транскодинга, ещё не запустив его.
AV1 использует более тонкую шкалу под названием qindex с диапазоном 0..255. Маппинг из qindex в Qstep – не чистая степень двойки, а таблица, откалиброванная для более плавного управления у high-end-конца и для более широкого диапазона, чем у 0..51-шкалы. Грубое правило: qindex ≈ 4 × QP, так что AV1 qindex 100 примерно соответствует HEVC QP 25. Таблицы у AV1 разделены по типу коэффициента – отдельно для DC и AC, и по цветовой плоскости (luma и chroma), что даёт AV1 более точный контроль над тем, сколько ошибки тратить на что.
Пример на одном блоке
Возьмём 4×4 transform-блок из гладкого куска неба после intra-предсказания. DCT-коэффициенты могут выглядеть так (числа подобраны читаемыми, в реальности значения обычно больше):
96 8 2 0
12 4 1 0
3 1 0 0
0 0 0 0При QP = 22 шаг 8. Делим каждую ячейку на 8 и округляем:
12 1 0 0
2 1 0 0
0 0 0 0
0 0 0 0Шестнадцать ячеек на входе; четыре ненулевых на выходе. Энтропийный кодер передаёт эти четыре числа плюс короткий флаг – примерно пятнадцать бит вместо нескольких сотен, которые потребовал бы сырой блок. Декодер умножает обратно:
96 8 0 0
16 8 0 0
0 0 0 0
0 0 0 0По сравнению с оригиналом нижний левый «12» стал «16» – ошибка 4; «4» во втором столбце стал «8»; «2» в верхней строке исчез; и так далее. Ошибки ограничены 4 (половина шага). После обратного transform блок для зрителя неотличим от оригинального неба.
Теперь тот же блок при QP = 34. Шаг 32:
3 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0Уцелела одна ячейка. Битстрим короче на пятнадцать бит. Декодер восстанавливает блок как плоский патч со значением 3 × 32 = 96 в DC-позиции и нулями всюду остальном – равномерный 4×4 субблок средней яркости. У оригинала была мягкая вариация по четырём строкам и четырём столбцам; эта вариация теперь потеряна навсегда. На плоском небе зритель этого не заметит. На лице тот же QP сделает щёку похожей на мазок краски. В этом и есть всё искусство выбора QP.
Dead zone – маленький приём, который много значит
Реальный кодек не пользуется учебным правилом quantized = round(coefficient / Qstep). Он пользуется асимметричной версией, где маленькие положительные коэффициенты сдвинуты в сторону нуля. Интервал [−Qstep/2, +Qstep/2], который в учебнике отображается в ноль, расширяется до чего-то вроде [−2·Qstep/3, +2·Qstep/3]. Этот расширенный интервал называется dead zone – мёртвая зона, – и внутри него любой коэффициент, маленький положительный или маленький отрицательный, округляется в ноль, а не в ±1.
Зачем асимметрия? Потому что ноль – особенный. Длинный ряд нулей стоит почти ноль бит; одиночная ±1 в середине ряда ломает run и стоит реальный бит. Затолкать маленькие коэффициенты в dead zone – это разменять чуть-чуть лишнего искажения на реальную экономию бит, и на естественном контенте обмен почти всегда стоит того.
Ширина dead zone – скрытый параметр кодера: декодер не знает и ему не важно, какую ширину выбрал кодер, потому что декодер только умножает целое обратно на Qstep. Reference-софт H.264 использует dead zone около Qstep × 5/6 для intra-блоков и Qstep × 2/3 для inter-блоков; у AV1 форма чуть другая. Тюнинг dead zone – одна из самых простых psy-визуальных ручек, доступных команде кодера, и выигрыш реальный – 5–10% битрейта при том же VMAF на хорошо настроенной dead zone.
Quantization matrices – разная точность для разных частот
Глаз более терпим к ошибке в высокочастотных коэффициентах, чем в низкочастотных. Quantization с плоским Qstep тратит точность на высокочастотные ячейки, где зритель её не видит. Каждый современный кодек позволяет кодеру отгрузить quantization matrix – поячеечный множитель поверх Qstep, который ослабляет шаг для высоких частот и ужесточает для низких.
Для блока 8×8 default-матрица в стиле JPEG может выглядеть так:
16 11 10 16 24 40 51 61
12 12 14 19 26 58 60 55
14 13 16 24 40 57 69 56
14 17 22 29 51 87 80 62
18 22 37 56 68 109 103 77
24 35 55 64 81 104 113 92
49 64 78 87 103 121 120 101
72 92 95 98 112 100 103 99В верхнем левом углу множитель 16, в нижнем правом – 99. При базовом Qstep = 8 эффективный шаг в нижнем правом углу – 8 × 99 / 16 ≈ 49, примерно в шесть раз грубее учебного шага, тогда как верхний левый остаётся близок к учебному значению. Зритель не видит потери в высокочастотных ячейках, потому что глаз на этих частотах деталь не разрешает.
H.264, HEVC и VVC дают кодеру выбор между плоскими матрицами (все ячейки умножаются на одно и то же значение, эквивалент «без матрицы»), default-матрицами, зашитыми в стандарт, и кастомными матрицами в битстриме. AV1 использует фиксированные матрицы из стандарта, зависящие от размера transform; кодер не может их переопределить, но может выбрать из небольшого встроенного набора. У VP9 – только плоский quantization.
Практический совет для стриминг-инженеров: плоские матрицы хороши для inter-кадров и черновых прогонов. Default-матрицы выигрывают на intra-кадрах и медленных пресетах. Кастомные матрицы выигрывают только тогда, когда у вас есть ground-truth контент конкретного типа – анимация, screen capture, спорт, – и измеряемая цель.
Dependent quantization – полушаг VVC
VVC, финализированный в 2020 году и принятый как ITU-T H.266 в 2020-м, добавил новый инструмент – dependent quantization – который даёт кодеру эффективно половину дополнительного шага точности без удвоения целочисленной шкалы. Идея, изначально предложенная Хайко Шварцем и коллегами из Heinrich Hertz Institute, в том, чтобы переключаться между двумя квантизаторами с уровнями реконструкции, сдвинутыми на полшага, на основе чётности предыдущих коэффициентов в порядке обхода.
В обычном кодеке уровни реконструкции при Qstep = 8 – это 0, ±8, ±16, ±24 и так далее. Dependent quantization добавляет второй набор, сдвинутый на полшага – 0, ±4, ±12, ±20 – и позволяет кодеру выбирать, к какому набору относится каждый коэффициент, с правилом «выбор зависит от предыдущего коэффициента». Маленький конечный автомат – четыре состояния – кодирует, какой квантизатор активен на каждом шаге.
Результат – более плотная решётка реконструкции. Кодер находит комбинации значений, которые аппроксимируют исходный блок точнее, чем умеет одиночный квантизатор, а энтропийный кодер тратит только один лишний бит на блок на сигнализацию состояния автомата. Dependent quantization экономит от 3% до 5% битрейта при том же качестве на типичном VVC-кодировании и включён по умолчанию в большинстве production-кодеров VVC.
«Pitfall. Некоторые команды выключают dependent quantization в VVC, прочитав спецификацию и решив, что это «дорого». На естественном контенте выигрыш реальный, а нагрузка на кодер – один-два процента, недостаточно, чтобы оправдать отключение. Выключайте только для маломощных декодеров, которые явно не поддерживают этот инструмент.»
Adaptive quantization – тратить биты там, куда смотрит глаз
Покадровый QP – слишком грубо. В кадре есть плоские области, где высокий QP невидим, и детализированные, где высокий QP виден сразу; тратить одинаковое число бит на оба типа – расточительство. Каждый современный кодер поддерживает adaptive quantization – поблочный сдвиг QP, который понижает QP в детализированных или перцептивно важных блоках и повышает в плоских.
Простейшая схема – variance-based adaptive quantization, обычно с флагом aq-mode=1. Кодер считает variance каждого макроблока (или CTU, или superblock) и понижает QP там, где variance высокий. Более тонкая схема, aq-mode=2, использует «spatial complexity»-модель, которая аккуратнее обрабатывает и очень плоские области (где появляется banding), и очень текстурированные (где глаз деталь не разрешает). x264, x265, libvpx, libaom и SVT-AV1 – все имеют варианты этих схем; названия и значения по умолчанию различаются, но принцип нет.
Вторая ось – temporal adaptive quantization, иногда называемая MB-tree в x264 и x265. Кодер делает обратный проход, оценивая, насколько часто каждый блок в reference-кадре будет упомянут более поздними блоками, и понижает QP на тех блоках, к которым будут обращаться чаще. Инвестиция окупается во многих кадрах. MB-tree – главная причина, почему современный x265-кодек при том же QP выглядит лучше, чем тот же кодек с выключенной фичей; выигрыш 10–20% по PSNR-эквивалентному битрейту – обычное дело.
Третья ось – psycho-visual quantization, семейство приёмов вокруг наблюдения, что глаз предпочитает сохранённую высокочастотную «энергию» над плоским сглаживанием. Флаги --psy-rd и --psy-rdoq в x264/x265 правят rate-distortion стоимость quantization-выборов так, чтобы кодер предпочитал блоки с сохранённой видимой текстурой, даже ценой чуть большего численного искажения. Результат хуже по PSNR, но лучше в субъективных тестах и в VMAF на настроенных моделях. Подробнее в метриках качества: PSNR, SSIM, VMAF.
Как сравниваются кодеки – одна таблица
В таблице ниже выровнены основные quantization-фичи кодеков, которые мы отгружали или видели в production в Фора Софт. Читать слева направо: кодек, QP-шкала, правило шага, поддержка матриц, dependent quantization, инструменты adaptive quantization.
| Кодек | QP-шкала | Правило шага | Матрицы | Dependent Q | Adaptive Q |
|---|---|---|---|---|---|
| H.264 / AVC | 0..51 (8-bit) | удваивается каждые 6 | Default + custom | Нет | На стороне кодера (x264) |
| H.265 / HEVC | 0..51 (8-bit), расширение до 63/75 | удваивается каждые 6 | Default + custom, поблочная delta | Нет | На стороне кодера (x265, MB-tree) |
| VP9 | 0..255 qindex | калиброванная таблица | Только плоский | Нет | На стороне кодера (libvpx) |
| AV1 | 0..255 qindex | калиброванная таблица, на DC/AC и на плоскость | Встроенный набор, без override | Нет | Встроенные segment-карты + логика кодера |
| VVC / H.266 | 0..63 (8-bit), расширение до 75 (10-bit) | удваивается каждые 6 | Default + custom + LFNST-aware | Да (по умолчанию) | На стороне кодера + tools-aware |
Паттерн поколений очевиден: правило шага практически не менялось с H.264, но каждое поколение добавляет более тонкие ручки – поблочная delta QP, таблицы на плоскость, частотозависимые матрицы, dependent quantization, – позволяющие кодеру тратить биты точнее. Quantization – одна и та же идея, применённая всё более хирургически.
CRF, CBR, VBR – quantization под другими именами
Бо́льшая часть флагов кодера, к которым реально прикасаются стриминг-инженеры – CRF (Constant Rate Factor), CBR (Constant Bit Rate), VBR (Variable Bit Rate), ABR (Average Bit Rate) – это контуры управления, которые подстраивают QP на лету под целевые показатели. Мы разбираем каждый в rate control: CBR, VBR, CRF.
CRF – проще всех в рассуждениях: кодер берёт базовый QP, нацеленный на постоянное перцептивное качество, и применяет adaptive quantization, чтобы варьировать QP поблочно. CRF 23 в x264 примерно соответствует среднему QP, на котором кодер бы остановился, если попросить его выдать фильм 1080p в «прозрачном качестве»; CRF 28 в x265 примерно соответствует той же точке. Цифра на ручке – не буквально QP, но управляет тем же подлежащим параметром.
CBR и VBR добавляют buffer-модель поверх CRF-логики. CBR ограничивает QP кодера любым значением, которое уложится в целевой битовый бюджет каждую секунду; VBR разрешает кодеру тратить больше бит на экшн-сцены и меньше на статичные, тоже через изменение QP. QP-шкала – субстрат; режим rate control – политика, выбирающая, где на шкале сидеть.
Следствие для продуктовых решений короткое. Каждое «снизить битрейт» – это под капотом «поднять QP». Если QP стал слишком высоким, блоки выходят плоскими, края – размытыми, градиенты – ступеньками. Никакой магии rate control, которая скрыла бы потерю; rate control только выбирает, в какое время её размазать.
Частые артефакты от quantization
Когда зритель жалуется на плохой стрим, причина почти всегда в quantization, тратящем биты не там. Пять симптомов в особенности сводятся к одному корню.
Blockiness – видимая граница между transform-блоками. Когда QP слишком высокий, каждый блок реконструируется как почти константный патч, и разрыв между соседними патчами становится виден. Современные кодеки запускают in-loop deblocking filter, который сглаживает эти границы – мы покрываем это в in-loop filtering, – но у фильтра есть предел. Выше QP 38 на H.264 фильтр не справляется.
Banding – видимая ступенька в градиенте – закат, дымка, синяя стена – где у оригинала был плавный переход, а в энкоде видно две-три равномерные полосы. Причина – quantization на низких частотах на гладком источнике; DC-коэффициент каждого блока округляется в другое целое, и шаг округления виден как полоса. Banding – каноническая проблема 8-бит и канонический аргумент за 10-бит кодирование даже на SDR; разбираем в 8-bit vs 10-bit encoding.
Blurring – потеря высокочастотной детали – текст становится «пушистым», волосы – мягкой массой, трава – краской. Причина – quantization matrix, проталкивающая высокочастотные коэффициенты в ноль. Выше определённого QP высокочастотные ячейки матрицы выдают ноль даже для легитимных краёв, и обратный transform реконструирует low-pass-версию блока.
Ringing – ореол волнистых артефактов вокруг резкого края. Причина – асимметричное квантование высокочастотных коэффициентов, которые DCT раскидал вокруг края; если некоторые из них выживают, а большинство обнулены, реконструированный блок показывает выжившие как видимую рябь. Ringing – канонический недостаток DCT-only-кодеков на мультфильмах и тексте и каноническая мотивация для DST и ADST, которые мы разобрали в transform coding.
Mosquito noise – быстро меняющиеся мелкие артефакты вокруг движущихся краёв. Причина – quantization-выбор различается от кадра к кадру, и поэтому уцелевшие высокочастотные коэффициенты вокруг края «мерцают». Чинится на стороне кодера – лучше rate control, меньше вариация QP между соседними кадрами – а не на стороне декодера.
Где здесь Фора Софт
Мы отгружаем quantization-тюнинг на production-кодирования в видеостриминге, OTT и интернет-ТВ, видеоконференциях и видеонаблюдении с 2010 года. Шаги, которые экономят деньги, почти всегда одни и те же: выбрать правильный анкер CRF или qindex под каждый класс контента, проверить, что adaptive quantization включён и настроен под этот контент (спорт, talking-head, анимация, screen capture), отгрузить маленькую кастомную quantization-матрицу для худшего типа контента, если цифры это оправдывают, и закрывать каждое изменение A/B-тестом на реальных клиентских устройствах. На WebRTC SFU мы получали 20–30% экономии трафика без измеримой потери качества, тюня только поведение кодера по QP на talking-head контенте.
Ключевые выводы
- Quantization – единственный этап кодека, где информация уничтожается навсегда.
- Quantization parameter (QP) управляет логарифмическим шагом: каждые шесть пунктов шаг удваивается.
- AV1 qindex 0..255 даёт в четыре раза более тонкий шаг, чем 0..51.
- Quantization-матрицы тратят точность там, куда смотрит глаз, и экономят там, куда не смотрит.
- Adaptive quantization перенаправляет биты к перцептивно важным блокам внутри кадра.
- Любой режим rate control (CRF, CBR, VBR) – это политика выбора QP на лету.