H.264 е стандарт за кодек за видео компресия. Той е повсеместен - интернет видео, Blu-ray, телефони, охранителни камери, дронове, всичко. Всичко използва H.264 сега.

забележителна

H.264 е забележителна технология. Това е резултат от 30+ години работа с една единствена цел: Намаляване на честотната лента, необходима за предаване на видео с пълно движение.

Технически е много интересно. Тази публикация ще даде представа за някои детайли на високо ниво - надявам се да не ви отегчавам твърде много с тънкостите. Също така имайте предвид, че много от концепциите, обяснени тук, се отнасят за видео компресията като цяло, а не само за H.264.

Един прост некомпресиран видео файл ще съдържа масив от 2D буфери, съдържащ пикселни данни за всеки кадър. Това е 3D (2 пространствени измерения и 1 временен) масив от байтове. Всеки пиксел отнема 3 байта за съхранение - по един байт за трите основни цвята (червен, зелен и син).

1080p @ 60 Hz = 1920x1080x60x3 =>

370 MB/сек на сурови данни.

С това е почти невъзможно да се справим. 50 GB Blu-ray диск ще побере само

2 минути. Не можете да го преместите никъде бързо. Дори SSD устройствата имат проблеми с изхвърлянето на това направо от RAM на диск [^ 1].

Така че, да. Нуждаем се от компресия.

Да, ще отговоря на това. Но първо да ви покажа нещо. Ето началната страница на Apple:

Заснех екрана на тази начална страница и създадох два файла:

Ех Какво? Тези размери на файлове изглеждат променени.

Не, те са прави. Видеото H.264 с дължина 300 кадъра е 175KB. Един кадър от това видео в PNG е 1015KB.

Изглежда, че съхраняваме 300 пъти количеството данни във видеото. Но размерът на файла е една пета. Така че H.264 изглежда е 1500 пъти по-ефективен от PNG.

Как е възможно това? Добре, какъв е трикът?

Има много много трикове! H.264 използва всички трикове, за които се сещате (и тонове, за които не се сещате). Нека да преминем през важните.

Тегло на проливането

Представете си, че правите кола за улични състезания. Трябва да вървите по-бързо. Какво е първото нещо, което правите? Отслабвате малко. Колата ви тежи 3000 кг. Изхвърляте ненужни неща. Тези задни седалки? pfft. Чък тези. Този субуфер? Си отиде. Няма музика за теб. Климатик? Да, махни го. Предаване? Да.не. Изчакайте! Това ще ни трябва.

Премахвате всичко, освен важните неща.

Тази концепция за изхвърляне на битове, които не са ви необходими, за да спестите място, се нарича загубен компресия. H.264 е кодек със загуби - изхвърля по-малко важни битове и запазва само важните битове.

PNG е без загуби кодек. Означава, че нищо не се изхвърля. Бит за бит, оригиналното изходно изображение може да бъде възстановено от PNG кодирано изображение.

Важни битове? Как алгоритъмът знае кои битове в моята рамка са важни?

Има няколко очевидни начина за изрязване на изображения. Може би горният десен квадрант е безполезен през цялото време. Така че може би можем да нулираме тези пиксели и да отхвърлим този квадрант. Бихме използвали само 3/4 от необходимото пространство.

2200 lbs сега. Или може би можем да изрежем дебела граница около краищата на рамката, важните неща така или иначе са в средата. Да, можете да направите това. Но H.264 не прави това.

H.264, подобно на други алгоритми със загубени изображения, изхвърля подробна информация. Ето един близък план на оригинала в сравнение с изображението след изхвърляне.

Вижте как компресираният не показва дупките в решетките на високоговорителите в MacBook Pro? Ако не увеличите мащаба, дори ще забележите разликата. Изображението отдясно тежи на 7% размера на оригинала - и дори не сме компресирали изображението в традиционния смисъл. Представете си, че колата ви тежи само 200 кг!

7% уау! Как изхвърляте подробна информация по този начин?

За това се нуждаем от бърз урок по математика.

Информационна ентропия

Сега стигаме до сочните късчета! Ха каламбури! Ако сте ходили на курс по теория на информацията, може би си спомняте информационна ентропия. Информационната ентропия е броят на битовете, необходими за представяне на някаква информация. Имайте предвид, че това не е просто размерът на някои набори от данни. Минималният брой битове трябва да се използва за представяне на цялата информация, съдържаща се в набор от данни.

Например, ако вашият набор от данни е резултат от едно хвърляне на монета, имате нужда от 1 бит ентропия. Ако имате записани две хвърляния на монети, ще ви трябват 2 бита. Има смисъл?

Да предположим, че имате някаква странна монета - хвърляли сте я 10 пъти и всеки път тя кацне на главите. Как бихте описали тази информация на някого? Не бихте казали HHHHHHHHH. Просто бихте казали „10 хвърляния, всички глави“ - бам! Току-що сте компресирали някои данни! Лесно. Спестих ви часове умопомрачителни лекции. Това очевидно е прекалено опростяване, но сте трансформирали някои данни в друго по-кратко представяне на същата информация. Намалили сте данните съкращаване. Ентропията на информацията в този набор от данни не се е променила - току-що сте преобразували между представяния. Този тип кодер се нарича ентропиен кодер - това е кодер без загуби с общо предназначение, който работи за всякакъв вид данни.

Честотен домейн

Сега, когато разбирате ентропия на информацията, нека преминем към трансформации на данни. Можете да представите данни в някои основни единици. Ако използвате двоичен файл, имате 0 и 1. Ако използвате шестнадесетичен, имате 16 знака. Можете лесно да трансформирате между двете системи. Те са по същество еквивалентни. Дотук добре? Добре!

Сега, малко въображение! Представете си, че можете да трансформирате всеки набор от данни, който варира в зависимост от пространството (или времето) - нещо като стойността на яркостта на изображението, в друго координатно пространство. Така че вместо координати x-y, да кажем, че имаме честотни координати. freqX и freqY са осите сега. Това се нарича a честотен домейн представителство. Има още една умопомрачителна математическа теорема [^ 2], която гласи, че можете да направите това за всякакви данни и можете да постигнете идеална трансформация без загуби, стига freqX и freqY да са достатъчно високи.

Добре, но какви са честотите са freqX и freqY?

freqX и freqY са някакъв друг набор от базисни единици. Точно както когато превключваме от двоичен в шестнадесетичен, имаме различна основна единица, превключваме от познатия X-Y към freqX и freqY. Шестнадесетичен 'A' изглежда различно от двоичен '1010'. И двете означават едно и също нещо, но виж различен. Ето как изглежда нашето изображение в честотната област:

Фината скара на този MacBook pro има високо информационно съдържание в по-високочестотните компоненти на това изображение. Фино вариращо съдържание = високочестотни компоненти. Всякакви постепенни вариации в цвета и яркостта - като градиенти са нискочестотни компоненти на това изображение. Всичко между тях попада между тях. Толкова фини детайли = висока честота. Нежни градиенти = ниска честота. Има смисъл?

При представяне в честотната област компонентите с ниска честота са близо до центъра на това изображение. По-високочестотните компоненти са към краищата на изображението.

Добре. Някак си има смисъл. Но защо да правя всичко това?

Защото сега можете да направите това изображение в честотния домейн и след това да замаскирате краищата - да изхвърлите информация, която ще съдържа информацията с високочестотни компоненти. Сега, ако конвертирате обратно в обичайните си координати x-y, ще откриете, че полученото изображение изглежда подобно на оригинала, но е загубило някои от фините детайли. Но сега изображението заема само малка част от пространството. Като контролирате колко голяма е вашата маска, вече можете да настроите точно колко подробни искате да бъдат изходните ви изображения.

Ето и близък план на лаптопа в началната страница отново. Освен сега, има приложена маска с кръгла граница.

Числата представляват информационната ентропия на това изображение като част от оригинала. Дори при 2% няма да забележите разликата, освен ако не сте на това ниво на увеличение. 2%! - вашата кола вече тежи 60 lbs!

Така че вие ​​сваляте тегло. Този процес при компресия със загуби се нарича квантуване[^ 3].

Добре. Впечатляващо, предполагам. Какво друго имаш?

Подсимплиране на хроматизацията.

Мозъчната система човек/око не е много добра в разрешаването на по-фини детайли в цвета. Той може да открие незначителни вариации в яркостта много лесно, но не и в цвета. Така че трябва да има някакъв начин да се изхвърли цветната информация, за да се хвърли още повече тежест.

В телевизионен сигнал цветните данни R + G + B се трансформират в Y + Cb + Cr. Y е яркостта (по същество черно-бяла яркост), а Cb и Cr са цветните (цветни) компоненти. RGB и YCbCr са еквивалентни по отношение на информационната ентропия.

Защо ненужно усложнявам? RGB не е достатъчно добър за вас?

Преди да имаме цветен телевизор, имахме само Y сигнала. И когато цветните телевизори току-що започнаха да се появяват, инженерите трябваше да измислят начин за предаване на RGB цвят заедно с Y. Вместо да използват два отделни потока данни, те разумно решиха да кодират цветната информация в Cb и Cr и да я предадат заедно Y информация. По този начин телевизорите BW биха разгледали само Y компонента. Освен това цветните телевизори ще разглеждат компонентите за цветност и ще преобразуват вътрешно RGB.

Но вижте трика: Y компонентът се кодира с пълна разделителна способност. Компонентите C само с четвърт резолюция. Тъй като окото/мозъкът е ужасно да открива цветови вариации, можете да се измъкнете с това. По този начин намалявате общата честотна лента с половината, с много малка визуална разлика. Половината! Колата ви сега тежи 30 кг!

Този процес на изхвърляне на част от информацията за цвета се нарича Подсимплиране на хроматизацията[^ 4]. Въпреки че не е специфичен за H.264 и съществува от десетилетия, той се използва почти универсално.

Това са големите теглилки за компресия със загуби. Нашите рамки вече са малки - тъй като изхвърлихме по-голямата част от подробната информация и половината от цветовата информация.

Изчакайте. Това е? Можем ли да направим нещо повече?

Да. Отделянето на тегло е само първата стъпка. Засега разглеждаме само пространствените домейни в рамките на един кадър. Сега е време да изследваме времевата компресия - където разглеждаме група кадри във времето.

Компенсация на движение

H.264 е стандарт за компресия на компенсация на движението.

Представете си, че гледате тенис мач. Камерата е фиксирана под определен ъгъл. Единственото нещо, което се движи, е топката напред-назад. Как бихте кодирали тази информация? Правиш това, което винаги правиш, нали? Имате 3D масив от пиксели, две измерения в пространството и едно във времето. Нали?

Не Защо би? Повечето изображения така или иначе са еднакви. Съдът, мрежата, тълпите, всички са статични. Единственото истинско действие е движението на топката. Ами ако можете да имате само едно статично изображение на всичко на заден план и след това едно движещо се изображение само на топката? Това не би ли спестило много място? Виждате ли къде отивам с това? Вземи го? Вижте къде отивам? Оценка на движението?

Куцо шеги настрана, точно това прави H.264. H.264 разделя изображението на макроблокове - обикновено блокове 16x16 пиксела, които ще използва за оценка на движението. Той кодира едно статично изображение - обикновено се нарича I-рамка(Вътрешна рамка). Това е пълен кадър - съдържащ всички битове, необходими за изграждането на този кадър. И тогава следващите кадри са или P-рамки(прогнозирано) или B-рамки(двупосочно прогнозирано). P-кадрите са кадри, които ще кодират вектор на движение за всеки от макроблоковете от предишния кадър. Така че P-кадърът трябва да бъде конструиран от декодера въз основа на предишни кадри. Започва с последния I-кадър във видео потока и след това преминава през всеки следващ кадър - добавяйки делтата на вектора на движението, докато върви, докато стигне до текущия кадър.

B-кадрите са още по-интересни, когато прогнозирането се извършва двупосочно, както от минали, така и от бъдещи кадри. Така че сега можете да си представите защо видеото на началната страница на Apple е толкова добре компресирано. Защото това са всъщност само три I-кадъра, в които макро блоковете се въртят наоколо.

Да предположим, че сте пускали видеоклип в YouTube. Пропуснахте последните няколко секунди от диалоговия прозорец, така че скролирайте няколко секунди назад. Забелязали ли сте, че не започва незабавно да се възпроизвежда от току-що избрания от вас времеви код. Той прави пауза за няколко мига и след това играе. Вече са буферирани тези кадри от мрежата, тъй като току-що сте го възпроизвели, така че защо тази пауза?

Да, това ме дразни. Защо го прави това?

Тъй като сте помолили декодера да премине към някакъв произволен кадър, декодерът трябва да повтори всички изчисления - започвайки от най-близките I-кадри и добавяйки делтата на вектора на движението към кадъра, в който се намирате - и това е изчислително скъпо, а оттам и кратката пауза. Надяваме се, че сега ще се раздразните по-малко, знаейки, че всъщност върши тежка работа, а не просто да седите наоколо, само за да ви дразни.

Тъй като кодирате само делта на вектори на движение, тази техника е изключително ефективно пространство за всяко видео с движение, на цената на някои изчисления.

Сега сме обхванали както пространствено, така и времево компресиране! Засега имаме малко пространство, спестено при квантуване. Подсимплирането на хроматизацията допълнително намалява наполовина необходимото пространство. На всичкото отгоре имаме компенсация на движението, която съхранява само 3 действителни кадъра за

300, които имахме в това видео.

Изглежда ми доста добре. Сега какво?

Сега приключваме и подпечатваме сделката. Използваме традиционен ентропиен енкопий без загуби. Защото защо не? Нека просто го удряме там за добра мярка.

Ентропиен кодер

I-кадрите, след стъпките със загуби, съдържат излишна информация. Векторите на движение за всеки от макро блоковете в P и B-кадрите - има цели групи от тях с еднакви стойности - тъй като няколко макро блока се движат с една и съща сума, когато изображението панира в нашето тестово видео.

Ентропиен кодер ще се погрижи за това съкращение. И тъй като това е енкодер без загуби с общо предназначение, не е нужно да се притесняваме какви компромиси прави. Можем да възстановим всички данни, които влизат.

И готово! В основата на това е как работят кодеците за видео компресия като H.264. Това са неговите трикове.

ОК страхотно! Но ми е любопитно да разбера колко тежи нашата кола сега.

Оригиналното видео е заснето при странна резолюция 1232x1154. Ако приложим математиката тук, получаваме:

5 секунди @ 60 fps = 1232x1154x60x3x5 => 1.2 GB
Компресирано видео => 175 KB

Ако приложим същото съотношение към нашата кола от 3000 фунта, получаваме 0.4 lbs като крайно тегло. 6.5 унции!

Да Магия е!

Очевидно съм, че силно опростявам няколко десетилетия интензивни изследвания в тази област. Ако искате да знаете повече, Страницата на Уикипедия е доста описателна.

Имате коментари? Сбърках ли нещо? Не сте фен на куците шеги? Обиден от ругатните? Използвайте HackerNews или Reddit за изразяване на вашето мнение!