Содержание статьи +
- TL;DR
- Зачем это вам
- Блок, который должен покинуть кодер
- Что такое DCT на самом деле
- Пример на одном блоке
- Целочисленные transform – почему декодеры никогда не расходятся
- DST и ADST – лучший инструмент для краёв
- AV1 – шестнадцать преобразований и обучаемый выбор
- VVC – Multiple Transform Selection и вторичная стадия
- AV2 – data-driven ядра и intra/inter secondary transforms
- Как transform взаимодействует со всем остальным
- Частая ошибка – высокий QP и обвинения в адрес transform
- Где Фора Софт в этой картине
- Главное
- Что читать дальше
TL;DR
Видеокодер тратит почти ноль битов на сами пиксели – он тратит их на остаточную ошибку после предсказания, и эту ошибку «перетасовывает» математическая операция под названием transform, чтобы кодер мог отбросить ту часть, которую глаз не видит. Каждый современный кодек с 1988 года использует одни и те же инструменты: DCT (дискретное косинусное преобразование) для гладкого содержимого, DST и ADST (асимметричный DST) для краёв и целочисленные приближения обоих, чтобы все декодеры на планете приходили к одинаковому числу до последнего бита. Различия между H.264, HEVC, VP9, AV1 и VVC – не в самой идее, а в размере блоков, количестве типов transform на выбор и в вторичных трюках сверху. Сделайте transform-стадию правильно – и 4K-поток в 2026 году поместится в ту же трубу, в которой пять лет назад едва помещалось 1080p.
Зачем это вам
Если у вас видеосервис, главная причина того, что 4K-поток сегодня стоит вдвое дешевле, чем в 2019-м, – это transform-стадия кодека, а не сеть, не плеер, не экраны. Продакт-менеджер, который умеет сказать «кодер размазывает ошибку по нескольким крупным коэффициентам и выбрасывает мелкие», может читать даташит и задать правильный вопрос: какие размеры transform-блоков, какие типы transform, есть ли вторичный transform. Основатель, который ставит под сомнение вендорское «30% экономии битрейта» и спрашивает, на каком контенте тестировали и какие transform-ы включали, экономит четверть на счёте за storage и CDN. Технический лид, который понимает, зачем VVC держит сразу DCT-II, DCT-VIII и DST-VII, собирает транскодер, который обгоняет вендорскую коробку при половинной цене.
Блок, который должен покинуть кодер
Каждый современный кодек работает с маленькими прямоугольниками пикселей – мы разбирали их в статье про block-based прогнозирование. Для каждого блока кодер сначала угадывает пиксели – из соседних внутри того же кадра или из похожего блока в более раннем кадре; мы разобрали эти этапы в статьях intra-frame coding и inter-frame coding и motion estimation. Угадывание почти никогда не идеально. Попиксельная разница между догадкой и истиной называется residual – остаточная ошибка.
Residual – это маленькая картинка ошибок. На типичном спортивном клипе значения в residual-блоке сгруппированы около нуля, с редкими всплесками там, где догадка промахнулась мимо края или движущегося объекта. Передавать residual в виде сырых чисел расточительно – большая часть его энергии живёт в медленных, гладких градиентах, и горсть коэффициентов может описать эту энергию, если поменять точку зрения с «пикселей на сетке» на «весов заранее заданных образцов».
Эта смена точки зрения – то, что делает transform. Он берёт residual-блок и переписывает его как сумму фиксированных волнообразных образцов, которые называются basis functions – базисными функциями. У каждой базисной функции своя фиксированная частота – медленная, средняя, быстрая – и своё направление – плоское, вертикальное, диагональное. Выход transform – одно число на каждую базисную функцию, говорящее кодеру «сколько» этого образца в блоке. Эти числа называются коэффициентами.
Фокус в том, что реальные residual-ы выглядят в основном как несколько низкочастотных образцов плюс длинный хвост близких к нулю высокочастотных. Хорошо подобранный transform проталкивает почти всю энергию в верхний левый угол сетки коэффициентов – низкочастотный «гладкий» угол – и оставляет остальную сетку заполненной маленькими числами, которые quantization (мы разберём её в следующей статье блока, Quantization) может выбросить с минимальным видимым ущербом. Эта концентрация энергии в нескольких коэффициентах называется energy compaction – компактизация энергии – и это и есть единственная причина существования transform-ов.
Что такое DCT на самом деле
Дискретное косинусное преобразование (DCT) – это рецепт, как разложить любой блок чисел в сумму косинусоид фиксированных частот. Идею предложил математик Насир Ахмед (Nasir Ahmed) в 1972 году в Университете Техаса в Арлингтоне, затем он со своими учениками Т. Раджем Натараджаном и К. Р. Рао опубликовал формальный алгоритм в январской статье 1974 года. Первое видео-применение последовало в 1975 году, когда Джон А. Роуз и Гюнер С. Робинсон использовали DCT внутри motion-compensated inter-frame кодера и сообщили, что он умеет сжимать данные до 0.25 бита на пиксель – число, которое спустя пятьдесят лет до сих пор задаёт форму каждого кодека в интернете.
DCT работает сначала построчно, потом столбец за столбцом. Для блока 4×4 transform записывает каждую строку как взвешенную сумму четырёх косинусоид: одна волна вообще не меняется (среднее), одна идёт от плюса к минусу по строке (первый косинус), одна с двумя нулями, одна с тремя. Тот же рецепт прогоняется по столбцам. На выходе – сетка 4×4 коэффициентов, где верхнее левое число – DC-коэффициент – среднее значение пикселей, названо по аналогии с постоянным током (direct current) в электротехнике, а остальные – AC-коэффициенты на возрастающих частотах.
Почему именно косинусы, а не какой-то другой набор волн? Потому что для сигналов, какие встречаются в естественных изображениях и видео-residual-ах, косинусы попадают в пределах долей процента от теоретически оптимального преобразования – преобразования Карунена-Лоэва (KLT), которое доказуемо лучшее по energy compaction, но требует пересчёта для каждого блока – слишком медленно для видеокодера. DCT – это фиксированная замена, достаточно хорошая для естественного контента и достаточно дешёвая, чтобы запускаться миллиард раз в секунду на телефоне.
В математической литературе DCT существует в восьми вариантах, DCT-I – DCT-VIII. В видео используется DCT-II – тот же самый рецепт, что и в JPEG с 1992 года. Современные кодеки добавляют второй вариант, DCT-VIII, который нам встретится дальше при обсуждении VVC и AV1.
Пример на одном блоке
Чтобы математика стала видимой, возьмём 4×4 residual-блок, в котором каждое значение равно 8:
8 8 8 8
8 8 8 8
8 8 8 8
8 8 8 8В блоке нет деталей – только среднее. Если прогнать его через 4×4 DCT-II, только один коэффициент будет ненулевым: DC-коэффициент со значением, пропорциональным среднему по блоку, умноженному на размер блока. Все остальные коэффициенты – точно ноль. Мы начинали с 16 равноценных чисел и закончили одним числом, которое несёт всю энергию. Кодер передаёт одно число (несколько бит), декодер восстанавливает блок, картинка точная.
Теперь смешаем residual с вертикальным краем:
8 8 -8 -8
8 8 -8 -8
8 8 -8 -8
8 8 -8 -8DCT-II распределяет энергию между двумя коэффициентами: DC теперь ноль (среднее – ноль), а коэффициент (0,1) – первая колонка, второй ряд, узор с горизонтальной частотой один – несёт край. Два ненулевых из шестнадцати, и quantization выбьет остальной шум, и никто этого не увидит.
Теперь шумный блок – residual после промаха motion prediction:
4 -3 2 1
-2 5 -1 3
1 -2 4 -1
-3 2 -1 5Выход DCT-II распределяется по многим коэффициентам, причём самые крупные по-прежнему сгруппированы вверху слева. Energy compaction слабее для шума, чем для гладкого сигнала, – и это ровно то поведение, которое нужно: случайному шуму негде спрятаться, и кодеру приходится тратить биты.
Причина, по которой каждый кодек использует DCT как «дефолтный» transform, – в том, что естественные residual-ы выглядят как первые два примера в 90% случаев, а не как третий.
Целочисленные transform – почему декодеры никогда не расходятся
Стандартный DCT-II использует значения косинусов с плавающей точкой, а плавающая точка не bit-exact на разном железе. Телефон, телевизор и браузер на ноутбуке должны декодировать один и тот же битстрим в одни и те же пиксели, кадр за кадром, часами. Если кто-то из них считает обратный DCT хоть чуть-чуть иначе – пусть и в последнем десятичном знаке – маленькая ошибка накапливается кадр за кадром, и декодеры расходятся. Это был реальный баг в ранних MPEG-кодеках под названием inverse transform drift – он давал банды и «призраки» на длинных последовательностях.
H.264, утверждённый в 2003 году, починил это раз и навсегда, заменив floating-point DCT на целочисленный transform – масштабированное приближение DCT-II, использующее только сложение, вычитание и сдвиг вправо. Элементы матрицы – маленькие целые (в основном 1 и 2), подобранные так, что transform ведёт себя как настоящий DCT-II с точностью до доли коэффициента, но каждый шаг определён в целочисленной арифметике. Цена – небольшая потеря energy compaction (целочисленный transform примерно на 0.1 dB хуже истинного DCT-II в среднем); выигрыш – каждый декодер на Земле выдаёт идентичный байт-за-байтом результат.
Transform-слой H.264 содержит четыре куска. Базовый transform 4×4 – это и есть целочисленное DCT-II-приближение, которое мы только что описали. Hadamard-преобразование 4×4 использует только сложение и вычитание – даже проще, чем базовый – и применяется к DC-коэффициентам блоков 16×16 при intra-предсказании, чтобы выжать ещё один процент из плоских областей. Hadamard 2×2 делает то же для chroma DC. И целочисленный transform 8×8, добавленный в High profile в 2005-м, – это более крупная версия базового transform-а для блоков с более гладким содержимым, где 4×4 слишком мал, чтобы быть эффективным.
Вторая дизайн-идея H.264 – transform разбит на core часть и scaling часть. Core работает на кодировании и декодировании; scaling сложен в quantizer. Эта разбивка делает и кодер, и декодер простыми и объясняет, почему H.264-transform-ы выполняются только сложениями, вычитаниями и сдвигами – умножения растворены в другом месте.
Каждый кодек после H.264 повторяет этот шаблон: определить целочисленные матрицы, доказать, что обратное преобразование точно возвращает исходник, сложить scaling в quantizer. Числа в матрицах разные, но дисциплина общая.
DST и ADST – лучший инструмент для краёв
DCT-II хорош для гладкого контента, где residual мягко спадает от центра. Он не так хорош для residual-блоков, выглядящих как односторонний наклон – пиксели маленькие с одной стороны, большие с другой – а это ровно та форма, которую принимает ошибка intra-prediction. Intra-prediction копирует пиксели сверху и слева от блока; предсказатель почти идеален у самой границы и постепенно хуже по мере удаления. Residual маленький у верхнего левого угла и больший у нижнего правого.
Для этой формы дискретное синусное преобразование (DST) математически подходит лучше. DST – та же идея, что DCT, но использует синусы, которые обращаются в ноль на одном краю блока, вместо косинусов, которые на одном краю плоские. Его базисные функции наклонены так, что компактизируют асимметричный residual в меньшее число коэффициентов.
HEVC, утверждённый в 2013-м, первым из стандартов выпустил DST в продакшен. HEVC определяет 4×4 целочисленный DST-VII – седьмой из семейства DST – и кодер по правилу обязан применить его к 4×4 luma-residual-ам внутри intra-предсказанных областей. Авторы HEVC ограничили DST-VII блоками 4×4 luma intra, потому что именно там выигрыш наибольший; на больших блоках или на chroma прирост сжимался до уровня ниже стоимости поддержки двух типов transform-а. Объединённая команда JCT-VC замерила примерно 1% снижения битрейта при той же субъективной картинке только от этого правила (Sze et al., 2014).
Следующий кодек, VP9, утверждённый Google в 2013-м, взял то же наблюдение и пошёл дальше. VP9 ввёл Asymmetric Discrete Sine Transform (ADST) – асимметричный DST – как соседа DCT. ADST близок к DST-VII, но реализован так, чтобы аккуратно ложиться на целочисленную арифметику и интегрироваться с intra-prediction-режимами VP9. Кодер VP9 использует ADST для intra-residual-ов из directional-предсказателя и DCT для inter-residual-ов и для плоских направлений. Декодер выбирает transform по флагам в битстриме.
Причина, почему ADST помогает, та же, что и у DST-VII в HEVC – его базисные функции наклонены в ту же сторону, что и типичный intra-residual, поэтому energy compaction плотнее. Слово «asymmetric» означает, что базисные функции не симметричны относительно центра блока – они растут к одной стороне, повторяя асимметричную форму residual.
AV1 – шестнадцать преобразований и обучаемый выбор
AV1, утверждённый Альянсом за открытые медиа (AOMedia) в марте 2018-го, оставил идею множественных типов transform и довёл её до логического предела. AV1 определяет четыре 1D-ядра: DCT, ADST, FLIPADST и IDTX.
DCT – стандартный косинусный transform, который мы уже встречали. ADST – асимметричный синусный transform, унаследованный и доработанный от VP9. FLIPADST – перевёрнутая версия ADST: те же базисные функции, но прогнанные снизу вверх, а не сверху вниз; нужны для residual-ов, чья энергия наклонена в обратную сторону. IDTX, сокращённо от identity transform, оставляет вход без изменений. IDTX полезен для screen content – текста, line art, резкой компьютерной графики, – где residual уже выглядит как несколько изолированных «всплесков», и настоящий частотный transform размазал бы их.
AV1 сочетает эти четыре ядра в горизонтальном и вертикальном направлениях, давая до 16 различных 2D-transform на блок. Для блоков 4×4 и 8×8 доступны все 16 комбинаций; для 16×16, 32×32 и 64×64 кодер выбирает из сокращённого набора, чтобы держать rate-distortion search управляемым. Тип transform сигнализируется в битстриме на каждый блок – одна из причин, почему битстрим AV1 содержит так много флагов на блок по сравнению с H.264.
Цена шестнадцати transform – сложность кодера. Кодер должен попробовать каждого кандидата, прогнать quantization, оценить битовую стоимость и выбрать победителя – процесс, называемый rate-distortion optimization (RDO), мы разбираем его в статье Mode decision и RDO. Выгода – примерно 5–7% снижения битрейта при той же картинке на естественном контенте против single-transform baseline, и до 15–20% на screen content, где блистает IDTX.
| Transform | Где лучше всего | Доступен в | Размеры блоков |
|---|---|---|---|
| DCT-II | Гладкие residual, inter | H.261, MPEG-2, H.264, HEVC, VP9, AV1, VVC | 4×4 – 64×64 |
| DST-VII | 4×4 intra luma | HEVC, VVC | 4×4 (HEVC), 4×4 – 32×32 (VVC) |
| ADST | Directional intra | VP9, AV1 | 4×4 – 64×64 |
| FLIPADST | Intra с обратным наклоном | AV1 | 4×4 – 64×64 |
| IDTX (identity) | Screen content, всплески | AV1 (и VVC TS-mode) | 4×4 – 32×32 |
| DCT-VIII | Малые intra-блоки | VVC | 4×4 – 32×32 |
Таблица 1. Меню transform-ов современных кодеков. Каждое поколение кодеков добавляет либо новое ядро, либо новый размер блока – лежащая в основе идея не менялась с 1972 года.
VVC – Multiple Transform Selection и вторичная стадия
VVC, ратифицированный как ITU-T H.266 в июле 2020-го, переработал transform-стадию двумя параллельными нововведениями: Multiple Transform Selection (MTS) и Low-Frequency Non-Separable Transform (LFNST).
MTS даёт кодеру VVC три типа transform – DCT-II, DCT-VIII, DST-VII – и позволяет выбирать лучший для каждого блока независимо по горизонтали и вертикали. Размеры блоков идут от 4×4 до 64×64 для DCT-II и до 32×32 для DCT-VIII и DST-VII. Это та же идея, что у AV1, только с чуть другим меню ядер.
LFNST – то, что делает VVC особенным. Он работает после первичного transform-а и перед quantization, только над низкочастотными коэффициентами (верхний левый угол 4×4 или 8×8 выхода первичного transform). LFNST – non-separable: он не делится на проход по строкам и по столбцам; он применяет одну 2D-матрицу к развёрнутому 16- или 64-элементному вектору коэффициентов. Non-separable transform-ы способны схватить корреляции, которые structure «строки потом столбцы» обычного separable DCT упускает, особенно в directional intra-residual-ах, где энергия лежит вдоль диагонали.
Цена – non-separable transform-ы стоят больше арифметики. LFNST ограничивает ущерб тем, что работает только в низкочастотном углу – максимум 64 коэффициента на блок – и хранит лишь горстку маленьких матриц, выбираемых по intra-prediction-режиму. Команда JVET намерила ещё ~1–2% снижения битрейта от одного LFNST на intra-контенте (Wang et al., 2021).
У LFNST есть одно важное взаимодействие: когда LFNST включён, MTS принудительно равен DCT-II. Эти два нововведения не стекаются – они чередуются, выбор на каждый блок.
AV2 – data-driven ядра и intra/inter secondary transforms
AV2, наследник AV1 от AOMedia, в активном черновом виде по состоянию на май 2026 года. Transform-стадия – одна из областей с самыми крупными запланированными изменениями. Опубликованные технические отчёты указывают на четыре направления.
Во-первых, AV2 перепроектирует первичные ядра DCT, DST и ADST, чтобы целочисленные матрицы точнее соответствовали статистике residual-ов, измеренной на крупных современных видеокорпусах. Во-вторых, AV2 вводит data-driven transforms (DDTs) – ядра, обученные офлайн на реальном видео и вшитые в стандарт; идея похожа на обучаемые компоненты neural codec, но закодированы они как фиксированные матрицы, чтобы декодеры оставались детерминированными. В-третьих, AV2 добавляет intra/inter secondary transforms (IST) – non-separable вторую стадию, аналог VVC LFNST, но доступную и для intra, и для inter блоков. В-четвёртых, AV2 расширяет фреймворк partitioning-а transform-ов, чтобы кодер мог выбрать более мелкие разбиения transform-блоков внутри одного coded-блока, подгоняя размер transform под локальную структуру residual (Nadir et al., arXiv:2601.02712, 2026).
Ожидаемый совместный прирост только от transform-стадии – диапазон 3–6% снижения битрейта над AV1 при той же картинке на естественном контенте, с большим выигрышем на screen content. Общая цель AV2 против AV1 со всеми инструментами – 30–40% снижения битрейта; цифра подвижна, поскольку стандарт ещё не заморожен.
Как transform взаимодействует со всем остальным
Transform не стоит в одиночку. Три downstream-стадии зависят от его решений.
Quantization делит каждый коэффициент на шаг и округляет. Больший шаг – меньше выживших ненулевых коэффициентов, меньше бит. Transform решает, как выглядит вход для quantization; чем плотнее energy compaction, тем больше коэффициентов можно безопасно занулить. Это мы разберём в следующей статье: Quantization.
Reordering и run-length coding проходят по сетке коэффициентов зигзагом – следуя возрастающей частоте от верхнего левого угла наружу. Цель – собрать длинный хвост нулей в один прогон, который энтропийный кодер потом сожмёт почти до ничего. Разные transform-ы дают разный порядок зигзага; для ADST и FLIPADST порядок повёрнут под асимметрию базисных функций. Это разобрано в статье Reordering, zig-zag и run-length.
Entropy coding – CABAC в H.264/HEVC/VVC, арифметический кодер в AV1 – превращает квантованные коэффициенты и их позиции в финальные биты. Компактные, в основном нулевые сетки коэффициентов дают entropy-кодеру контент, который он жмёт агрессивно; «размазанные» сетки – нет. Это разобрано в статье Entropy coding в деталях.
Четыре стадии – transform, quantization, reordering, entropy – спроектированы как цепочка. Изменение одной без других возвращает её прирост обратно; вот почему каждое новое поколение кодеков переделывает все четыре сразу, а не подкручивает один блок.
Частая ошибка – высокий QP и обвинения в адрес transform
Инженеры, новые в видео-тюнинге, иногда видят банды или блочность в перекодировке и решают, что виноват transform. Почти никогда. Сам transform математически без потерь, когда его масштабный коэффициент равен единице и округление симметрично – каждый коэффициент сохранён точно. Потери возникают на следующей стадии – quantization – когда кодер делит коэффициенты на шаг и округляет.
Если картинка блочная – transform сделал своё дело, а quantizer выставлен слишком агрессивно. Если видите ringing вокруг краёв – transform выдал законные высокочастотные коэффициенты, а quantizer их убил: явление Гиббса, предсказуемо, лечится снижением QP или добавлением битов на края. Если видите цветовые банды – transform отдал чистые коэффициенты quantizer-у со слишком малым числом уровней; лечится более высокой битовой глубиной или 10-битным кодированием. Transform – фотограф; quantizer – корзина, в которую выкидываются неудачные снимки. Целите фиксы в корзину.
Где Фора Софт в этой картине
В стриминговых, surveillance, конференц- и AR/VR-продуктах, которые мы делаем, выбор transform-стадии формирует треть результата по картинке и четверть бюджета по времени кодирования. В live OTT-пайплайнах мы опираемся на DST-VII HEVC для intra-тяжёлого контента и на полное меню из шестнадцати transform-ов AV1 для премиального VOD, где время кодирования платится один раз и окупается миллионами проигрываний. В WebRTC SFU мы держим целочисленный 4×4 transform H.264, потому что bit-exact декодеры и крошечная стоимость на блок здесь важнее последних нескольких процентов сжатия. В surveillance-композитах мы используем MTS VVC с выключенным LFNST, потому что прирост от non-separable transform-ов схлопывается на обрезанных под-кадрах. Смысл – transform это кнопка под нагрузку, а не дефолт, унаследованный из вендорского пресета.
Главное
- Transform переписывает пиксельный residual как веса фиксированных волновых узоров; энергия концентрируется, чтобы quantization могла выбросить мелочь.
- DCT-II – дефолт для гладкого контента; DST-VII и ADST лучше ложатся на intra-residual-ы со скосом по блоку.
- Целочисленные transform-ы – только сложения, вычитания и сдвиги – устраняют inverse-transform drift и делают каждый декодер bit-exact.
- HEVC выпускает DST-VII на 4×4 luma intra; VP9 и AV1 – ADST широко; AV1 добавляет FLIPADST и identity transform для screen content; VVC добавляет DCT-VIII и вторичную стадию LFNST; AV2 кладёт сверху data-driven transform-ы.
- Приросты transform-стадии складываются с quantization, reordering и entropy – их нужно переделывать вместе, чтобы получить реальный coding gain.
- Выбор правильного transform на каждый блок – одна из самых крупных кнопок, которыми владеет кодер; современные кодеки тратят сложность на хороший выбор.