Содержание статьи +
- TL;DR
- Зачем это нужно
- Что значит «in-loop»
- Deblocking – сглаживание сетки
- Sample-Adaptive Offset (SAO) – мелкие правки везде
- Adaptive Loop Filter (ALF) – Wiener-фильтр внутри кодека
- CDEF – направленный «sharpener» от AV1
- Сквозной пример
- Сравнительная таблица
- Типичная ловушка – deblocking, отключённый в транскоде
- Где в этом Фора Софт
- Главное
- Что почитать дальше
TL;DR
In-loop фильтрация – стадия видеокодека, которая сглаживает артефакты блочного сжатия: видимую сетку 8×8 или 16×16 на границах блоков, ringing-ореол вокруг резких переходов и слабый шум в плоских участках. Она работает внутри петли кодирования: и кодер, и декодер обязаны применять одни и те же фильтры в одном и том же порядке, иначе их опорные кадры расходятся, и поток на экране быстро рассыпается. Четыре фильтра покрывают почти всё, что использует индустрия в 2026 году: deblocking, унаследованный от H.264 и улучшенный в HEVC; Sample-Adaptive Offset (SAO) и Adaptive Loop Filter (ALF), пришедшие с HEVC и развившиеся в VVC; и Constrained Directional Enhancement Filter (CDEF), которым AV1 заменил SAO. Вместе они дают 5–12% экономии битрейта при том же визуальном качестве – и именно благодаря им современный кодек на 2 Mbps больше не выглядит как H.264 2003 года.
Зачем это нужно
Если вы когда-нибудь видели низкобитрейтный стрим, на котором по лицу человека вдруг проступает сетка из квадратов 8×8 – вы видели именно тот failure mode, ради подавления которого существуют in-loop фильтры. Продакт-менеджер, который знает, что современный декодер применяет четыре пост-квантовых фильтра внутри петли кодирования, перестанет считать «более мягкую» картинку худшей: это может быть нормальная работа deblocking-фильтра. Стриминг-инженер, отличающий SAO от ALF, читает лог энкодера без догадок. Основатель, понимающий, почему AV1 отказался от SAO в пользу CDEF, увидит, почему у AV1-декодера в железе другой on-chip пайплайн фильтров, чем у HEVC. В этой статье мы пройдём по каждому фильтру, покажем арифметику на проработанном примере и закончим сравнением, какой кодек что отгружает и какой вклад это даёт в битрейт.
Что значит «in-loop»
Любой современный кодек построен на гибридной архитектуре: предсказать следующий блок по соседям или по предыдущему кадру, преобразовать остаток, квантовать коэффициенты и энтропийно закодировать результат. Декодер обращает эти шаги, чтобы восстановить блок. Восстановление – приближённое, потому что квантование намеренно отбрасывает часть информации. Эта ошибка приближения проявляется в виде видимых разрывов на границах блоков, слабых отголосков вокруг резких переходов и низкоамплитудного шума на плоских участках.
Очистить это можно двумя путями. Пост-фильтрация работает после декодера и никак не влияет на предсказание следующего кадра. Она может быть агрессивной – от её результата ничто не зависит. In-loop фильтрация, наоборот, работает внутри петли предсказания. В качестве опорного кадра, на который кодер опирается при предсказании следующего P или B кадра, берётся именно отфильтрованный кадр, а не сырой результат восстановления. Поэтому каждый декодер в мире обязан применять один и тот же фильтр в одном и том же порядке: расхождение хотя бы на один пиксель с любой стороны превращается в drift, который накапливается с каждым кадром с компенсацией движения.
In-loop фильтрация окупает себя, потому что более чистый опорный кадр улучшает motion estimation на следующем P-кадре. Остатки в нижестоящих кадрах уменьшаются, на них нужно меньше бит, и эта экономия с лихвой перекрывает стоимость самих фильтров. Вендорские бенчмарки начиная с H.264 сходятся на 5–10% экономии битрейта при том же PSNR от одного только deblocking, и ещё 1–4% сверху от каждого из SAO, ALF и CDEF там, где они есть.
Deblocking – сглаживание сетки
Первый фильтр, который применяет любой современный кодек, – это deblocking. Он борется с blocking artefact: видимым скачком в значениях пикселей на границе двух coded-блоков, вызванным независимым квантованием каждого блока. На низком битрейте скачок достигает 8–20 уровней luma и прорисовывает сетку блоков по всему кадру.
Deblocking-фильтр проверяет каждую горизонтальную и вертикальную границу блоков и решает по каждой, насколько сильно через неё сглаживать. В решение входят три параметра: boundary strength (был ли один из соседей intra-coded? разные ли у двух сторон motion vectors или reference frames?), активность на каждой стороне (текстурный участок терпит больший скачок, чем плоский) и quantisation parameter (более агрессивное квантование даёт большие скачки, и фильтр усиливается с ростом QP). Само ядро короткое и симметричное: H.264 использовал 4-tap фильтр, модифицирующий до 2 пикселей с каждой стороны; HEVC расширил его до 3 пикселей с каждой стороны на сильных границах; VVC дошёл до 7 пикселей с каждой стороны на самых длинных границах блоков.
Пример с числами. Два соседних 4×4 luma-блока на плоской поверхности встречаются на вертикальной границе. Четыре пикселя слева в одной строке – 148, 150, 151, 152; четыре справа – 162, 161, 160, 159. На границе скачок 11 уровней – видно на хорошем мониторе. Deblocking применяет нормализованное симметричное ядро, которое подтягивает два пограничных пикселя на каждой стороне к середине:
- новый левый пограничный пиксель = round((152 × 1 + 162 × 3) / 4) = round(159,5) = 160
- новый правый пограничный пиксель = round((152 × 3 + 162 × 1) / 4) = round(154,5) = 155
11-уровневый скачок становится плавным 5-уровневым переходом, а следующая пара пикселей с каждой стороны получает меньший толчок в том же направлении. На выходе – визуально непрерывная граница, а не ступенька. Что важно: фильтр консервативен – он отступает, когда любая сторона имеет высокую активность, и поэтому никогда не размывает реальную текстуру в кашу.
Deblocking – самый дешёвый из четырёх фильтров. Он трогает только пиксели рядом с границей блока, ядро короткое, решения локальные. По публикациям HEVC и AV1 типичная пропускная способность декодера на deblocking – 1–2% общего числа циклов декодирования, а экономия битрейта – 5–10%. Это хрестоматийная «дешёвая сделка» современного видеокодирования.
Sample-Adaptive Offset (SAO) – мелкие правки везде
После deblocking HEVC и VVC применяют Sample-Adaptive Offset, или SAO. Артефакт, на который нацелен SAO, – это остаточная ошибка квантования, оставшаяся после deblocking: небольшой DC-сдвиг в плоских областях и ringing-ореол вокруг резких переходов, где высокочастотные базисные функции преобразования были «пере-квантованы».
SAO работает по одной CTU за раз. Для каждой CTU энкодер выбирает один из двух режимов – Edge Offset (EO) или Band Offset (BO) – и сигнализирует в битстриме четыре маленьких оффсета. Edge Offset классифицирует каждый пиксель в одну из пяти категорий по значениям двух соседей вдоль выбранного направления (горизонталь, вертикаль, 45°, 135°): локальный минимум, локальный максимум, начало edge, конец edge или часть плоского участка. Дальше энкодер передаёт по одному оффсету на каждую не-плоскую категорию – обычно ±1 или ±2 уровня luma – и декодер прибавляет его ко всем пикселям этой категории. Band Offset делит диапазон luma на 32 равные полосы и передаёт оффсеты для четырёх соседних полос.
Пример с числами. CTU содержит плоский участок со средним luma 132, и преобразование оставило после квантования DC-сдвиг −2: восстановленное среднее 130 вместо 132. Энкодер замечает это в своём rate-distortion поиске, устанавливает SAO для этой CTU в Band Offset, выбирает четыре полосы, покрывающие luma 128–143, и сигнализирует оффсеты +2 для каждой. Декодер прибавляет +2 ко всем пикселям этих полос, восстанавливая среднее до 132, не трогая соседние полосы. Стоимость в битах битстрима – около 30–60 на CTU.
SAO дёшев и в кодировании, и в декодировании. Энкодер запускает небольшой rate-distortion поиск на CTU – обычно 5–8 альтернатив, – а декодеру нужны только lookup-таблица и одно целочисленное сложение на пиксель. Экономия битрейта поверх deblocking в HEVC – 1–3% при том же PSNR, а на контенте с плавными градиентами и резкими краями (анимация, motion graphics) – больше.
Adaptive Loop Filter (ALF) – Wiener-фильтр внутри кодека
VVC добавляет третий фильтр поверх deblocking и SAO – Adaptive Loop Filter, ALF. Артефакт, на который нацелен ALF, – это всё, что осталось после SAO: малоамплитудная высокочастотная ошибка, разбросанная по всему восстановленному кадру, такая, которую никакой per-CTU оффсет не закроет, потому что у неё нулевое среднее.
ALF – это Wiener-фильтр, классический инструмент signal processing, который находит линейный фильтр, минимизирующий в среднем квадратичную ошибку между фильтрованной реконструкцией и оригиналом. Коэффициенты фильтра энкодер оценивает у себя по сравнению реконструкции и оригинала; коэффициенты квантуются и отправляются в битстриме; декодер применяет те же коэффициенты к своей реконструкции.
В VVC ALF использует 7-tap diamond luma-фильтр и 5-tap diamond chroma-фильтр; коэффициенты выбираются на CTU из пула до 25 альтернатив. «Адаптивная» часть – per-CTU классификация: энкодер метит каждый 4×4 sub-блок по локальной активности градиента и направлению, и декодер переключается между формами фильтров. Плоский sub-блок получает более гладкий фильтр; sub-блок с доминирующим вертикальным edge получает фильтр, сохраняющий edge и сглаживающий вдоль него; 45°-edge sub-блок – фильтр, повёрнутый на 45°. Стоимость сигнализации небольшая – несколько бит на CTU плюс глобальные коэффициенты фильтра на кадр.
VVC также добавляет Cross-Component ALF (CC-ALF), использующий канал luma как дополнительный вход при фильтрации chroma-каналов. Идея в том, что текстура в luma обычно соответствует активности в chroma, и luma-управляемый фильтр может вернуть chroma-детали, утраченные из-за chroma sub-sampling и квантования. CC-ALF приносит около 0,5–1% дополнительной экономии битрейта в chroma в VVC при типичных стриминговых QP.
Совместный эффект ALF и CC-ALF в VVC – 3–6% экономии битрейта поверх deblocking и SAO при том же PSNR при более тяжёлой стоимости декода: Karczewicz et al. (2021) сообщают, что ALF занимает около 5–8% runtime VVC-декодера, самый дорогой из любого одиночного фильтра в цепочке. AVS3 отгружает похожий Wiener-based ALF; AV1 – нет.
CDEF – направленный «sharpener» от AV1
AV1 пошёл другим путём и отказался от SAO. После deblocking AV1 применяет Constrained Directional Enhancement Filter (CDEF), придуманный Steinar Midtskogen и Jean-Marc Valin в Cisco и Mozilla и доработанный в рамках AOMedia. CDEF нацелен на ringing-ореол вокруг резких краёв и на направленное размытие, которое deblocking иногда оставляет на наклонной текстуре.
CDEF работает на каждом 8×8 блоке. На блок энкодер оценивает доминирующее направление edge из восьми возможных (каждые 22,5°) и выбирает primary силу фильтра вдоль этого направления и secondary силу поперёк. Primary-фильтр сглаживает вдоль edge; secondary – заостряет поперёк. Вторая пара сил обрабатывает chroma. Совместный эффект – сохранить резкость на реальных краях и подавить точечный ringing-паттерн, идущий от sparse квантованных высокочастотных коэффициентов.
Дизайн CDEF подчёркнуто простой. Восемь направлений, четыре primary-силы, две secondary-силы, никаких Wiener-обученных коэффициентов – фильтр это фиксированное ядро, применённое с выбранными силами, а не полноценный адаптивный Wiener-фильтр. В этой простоте и есть смысл: CDEF идёт примерно за такую же стоимость декодера, как SAO в HEVC, и даёт 2–4% экономии битрейта на натуральном контенте. Статья Midtskogen/Valin 2017 года сообщает, что CDEF обходит SAO по PSNR-Y в среднем на 0,85% на тестовом сете AOMedia и сильнее – по перцептивной метрике VMAF, что и убедило AOMedia отгрузить его вместо SAO.
AV1 также применяет Loop Restoration после CDEF, с двумя суб-режимами: Wiener-style фильтр и self-guided фильтр. Loop Restoration по духу ближе к VVC ALF, но проще – максимум 7-tap Wiener-фильтр или guided-image filter с двумя параметрами. Вклад – около 1–2% экономии битрейта поверх CDEF. От AV2 ждут, что все три AV1 in-loop фильтра будут дополнены нейросетевыми альтернативами, которые сейчас находятся в экспериментальных треках.
Сквозной пример
1080p HEVC-стрим кодирует медленную футбольную сцену на 4 Mbps с QP 28. Внутри одной CTU на зелёном газоне восстановленные значения luma – ровно 137; оригинал был ровно 140. Deblocking ничего не делает: на границе блоков нет скачка. Энкодер SAO запускает per-CTU поиск, выбирает Band Offset, видит, что полосы 128–143 на 3 уровня ниже, чем нужно, сигнализирует четыре оффсета +3 и платит 36 бит на сигнализацию в битстриме. Декодер прибавляет +3 ко всем пикселям этих полос, возвращая участок к 140. Если бы был активен ALF (в VVC он был бы, в HEVC – нет), он добавил бы небольшую высокочастотную коррекцию сверху, заплатив примерно 60–100 бит на кадр за глобальные коэффициенты фильтра. Вся цепочка фильтров переводит участок с заметно потускневшего 137 обратно к исходному 140, и следующее P-кадровое предсказание энкодера стартует из исправленной опорной картинки. Без фильтров каждый последующий P-кадр предсказывал бы от 137 и либо принимал 3-уровневую ошибку (и тянул её дальше), либо тратил дополнительные биты на её исправление. Фильтры окупаются на третьем кадре и продолжают окупаться дальше.
Сравнительная таблица
История по пяти кодекам, доминирующим в 2026 году, – это про всё больше фильтров и всё большую компрессию при медленно растущей стоимости декода.
| Кодек (год) | Deblocking | SAO | ALF | CDEF | Loop Restoration | Экономия цепочки (типично) | Доля в декоде |
|---|---|---|---|---|---|---|---|
| H.264 / AVC (2003) | да | нет | нет | нет | нет | ~5–8% | ~3% |
| HEVC (2013) | да | да | нет | нет | нет | ~7–11% | ~5% |
| AV1 (2018) | да | нет | нет | да | да | ~9–13% | ~6% |
| VVC (2020) | да | да | да (+CC-ALF) | нет | нет | ~10–14% | ~8–10% |
| AVS3 (2019) | да | да | да | нет | нет | ~9–12% | ~7% |
Два наблюдения. Во-первых, каждый кодек после H.264 сохранил deblocking – это самая дешёвая прибавка в цепочке, и никто не нашёл повода её выкинуть. Во-вторых, выбор того, что ставить после deblocking, – один из самых явных архитектурных «отпечатков» кодека: HEVC взял offset-based коррекцию (SAO), AV1 – направленный sharpener (CDEF), VVC добавил поверх SAO обученный Wiener-фильтр (ALF), и каждый выбор уходит корнями в приоритеты той standards body, которая проектировала кодек.
Типичная ловушка – deblocking, отключённый в транскоде
Самый частый баг in-loop фильтрации в транскодинговом пайплайне – это deblocking, отключённый из командной строки и забытый. FFmpeg, x264, x265 и SVT-AV1 принимают опцию, выключающую deblocking, – обычно --no-deblock или -deblock 0:0. Инженеры хватаются за неё при отладке, чтобы посмотреть «сырой» вывод сетки блоков, а потом забывают убрать перед выкатыванием конфига энкодера в прод.
Видимый симптом тонкий. Энкодер работает чуть быстрее, выход проходит базовые проверки воспроизведения, а первый VMAF на тестовом высокобитрейтном клипе выглядит примерно как при включённом deblocking. Но на низкобитрейтном контенте, как только в кадр входит движение, по плоским участкам начинают маршировать блоки. На клиентских дашбордах капают жалобы «видео пикселит», а баг трудно поймать: энкодер не помечает выключенный фильтр как ошибку в логе.
Лечится contract-тестом в CI: закодировать референсный клип с прод-конфигом, декодировать, и удостовериться, что deblocking_filter_disabled_flag (H.264 / HEVC) или эквивалент (loop_filter_level > 0 в AV1) в битстриме имеет ожидаемое значение. Мы добавили однострочный ffprobe-чек в каждый клиентский пайплайн, который сопровождаем, – он стоит миллисекунду на кодирование и за два года поймал тот же баг дважды у трёх клиентов.
Где в этом Фора Софт
Мы отгружаем энкодерские конфигурации и инструменты bitstream-анализа для пайплайнов видеоконференций, OTT, видеонаблюдения, e-learning, телемедицины и AR/VR. В каждом нашем пайплайне есть in-loop-filter аудит в CI-сьюте – deblocking включён, SAO включён там, где это разрешает спецификация, ALF-коэффициенты присутствуют в VVC-стримах, AV1-стримы имеют ненулевые CDEF-силы. Когда клиент жалуется на видимые блоки на 720p мобильном фиде, первое, что мы проверяем, – состояние deblocking-фильтра в свежем encode. Двадцать лет отладки битстримов через H.263, MPEG-2, MPEG-4 Part 2, H.264, HEVC, VP8, VP9 и AV1 научили нас, что цепочка in-loop фильтров – то самое место, где мелкие misconfigurations порождают большую клиентскую боль; поэтому аудит стоит нам миллисекунды и экономит саппорт-тикет каждые две недели.
Главное
- In-loop фильтры работают внутри петли предсказания, поэтому энкодер и декодер делят одни и те же опорные кадры.
- Deblocking сглаживает скачок на границе блоков, идёт дёшево и экономит 5–10% битрейта – есть в каждом современном кодеке.
- SAO правит мелкие per-CTU оффсеты после deblocking, отгружается в HEVC, VVC и AVS3, даёт 1–3% сверху.
- ALF – Wiener-фильтр, отгружаемый в VVC и AVS3; даёт 3–6% сверху, стоит 5–8% runtime декодера.
- AV1 заменил SAO на CDEF (направленный) плюс Loop Restoration (Wiener-подобный), получив другой пайплайн декодера и 3–5% экономии.
- Выключенный deblocking – самый частый баг in-loop фильтров; однострочный ffprobe contract-тест ловит его всегда.
Что почитать дальше
- Квантование: где теряется качество – upstream-стадия, создающая артефакты, которые in-loop фильтры чистят.
- H.265 / HEVC – кодек, представивший SAO рядом с deblocking.
- AV1: где он находится в 2026 – кодек, заменивший SAO на CDEF.