H.264 is magie: een technische walkthrough van een bijzondere technologie
H.264 is een opmerkelijk stuk technologie. Het is het resultaat van meer dan 30 jaar werk met één enkel doel: de bandbreedte die nodig is voor de transmissie van video met vloeiende bewegingen (full-motion video) verminderen.
Technisch gezien is het zeer interessant. Dit artikel geeft inzicht in enkele van de details op een hoog niveau. Let op dat veel van de hier uitgelegde concepten gelden voor videocompressie in het algemeen, en niet alleen voor H.264.
Waarom überhaupt iets comprimeren?
Een eenvoudig ongecomprimeerd videobestand bevat een array van 2D-buffers met pixelgegevens voor elk frame. Het is dus een 3D-array (twee ruimtelijke dimensies en één temporele dimensie) van bytes. Elke pixel vereist 3 bytes voor opslag: één byte voor elk van de drie primaire kleuren (rood, groen en blauw).
Berekening voor 1080p @ 60 Hz: 1920 x 1080 x 60 x 3 $\approx$ 370 MB/sec aan ruwe data.
Dit is vrijwel onmogelijk om te verwerken. Een Blu-ray disk van 50 GB zou slechts ongeveer 2 minuten van dit materiaal kunnen bevatten. Je kunt het niet snel versturen. Zelfs SSD's hebben moeite om dit rechtstreeks vanuit het RAM naar de schijf te schrijven[^1].
Compressie is dus noodzakelijk.
Waarom H.264-compressie?
Om dit te illustreren, kijken we naar de homepage van Apple. Er zijn twee bestanden gemaakt van deze pagina:
- PNG-screenshot van de Apple homepage: 1015 KB
- H.264-video (5 seconden, 60 fps) van dezelfde homepage: 175 KB
Op het eerste gezicht lijken deze bestandsgroottes omgewisseld. De H.264-video, die uit 300 frames bestaat, is slechts 175 KB, terwijl één enkel frame in PNG-formaat 1015 KB is.
Het lijkt alsof we in de video 300 keer zoveel data opslaan, maar de bestandsgrootte is juist een vijfde van het screenshot. H.264 lijkt hierdoor 1500 keer efficiënter dan PNG. Hoe is dit mogelijk?
H.264 gebruikt een breed scala aan technieken. Laten we de belangrijkste bespreken.
Gewicht verliezen: Lossy compressie
Stel dat je een auto bouwt voor straatraces. Om sneller te gaan, is het eerste wat je doet het weggooien van onnodig gewicht. Je gooit de achterbanken, de subwoofer en de airconditioning eruit. Je behoudt alleen de zaken die er echt toe doen.
Het concept van het weggooien van bits die je niet nodig hebt om ruimte te besparen, wordt lossy compressie genoemd. H.264 is een lossy codec: hij gooit minder belangrijke bits weg en behoudt alleen de essentiële informatie.
PNG is daarentegen een lossless codec. Dit betekent dat er niets wordt weggegooid; bit voor bit kan het originele bronbeeld worden hersteld uit een PNG-gecodeerde afbeelding.
Hoe bepaalt het algoritme wat belangrijk is?
H.264 verwijdert detailinformatie. Als je inzoomt op een originele afbeelding vergeleken met een afbeelding na compressie, zie je dat de fijne details (zoals de gaatjes in de luidsprekerroosters van een MacBook Pro) verdwijnen. Als je niet inzoomt, merk je het verschil niet eens. De gecomprimeerde afbeelding is slechts 7% van de oorspronkelijke grootte.
Informatie-entropie
Om te begrijpen hoe detailinformatie wordt verwijderd, is het concept van informatie-entropie essentieel. Informatie-entropie is het aantal bits dat nodig is om bepaalde informatie weer te geven. Het is niet simpelweg de grootte van een dataset, maar het minimum aantal bits dat moet worden gebruikt om alle informatie in een dataset te representeren.
Bijvoorbeeld:
- Eén muntworp vereist 1 bit aan entropie.
- Twee muntworpen vereisen 2 bits.
Stel dat je een vreemde munt hebt die 10 keer op kop is gevallen. Je zou dit niet beschrijven als "KKKKKKKKKK", maar als "10 worpen, allemaal kop". Hiermee heb je data gecomprimeerd door redundantie te verminderen. De informatie-entropie is niet veranderd, maar je hebt de representatie omgezet. Dit type encoder wordt een entropie-encoder genoemd; een lossless encoder die voor elk type data werkt.
Het frequentiedomein
Data kan worden getransformeerd naar een andere coördinatenruimte. In plaats van X-Y coördinaten, kunnen we gebruikmaken van frequentiecoördinaten (freqX en freqY). Dit wordt een representatie in het frequentiedomein genoemd. Volgens een wiskundige stelling[^2] kan dit voor elke dataset worden gedaan via een perfecte lossless transformatie, mits de frequenties hoog genoeg zijn.
In het frequentiedomein geldt:
- Fijne details (zoals het rooster van een MacBook) = hoge frequentiecomponenten.
- Geleidelijke variaties (zoals kleurgradiënten) = lage frequentiecomponenten.
De lage frequenties bevinden zich in het centrum van de representatie, terwijl de hoge frequenties zich aan de randen bevinden.
Quantisatie
Door in het frequentiedomein een masker toe te passen over de randen, kun je de informatie met hoge frequenties weggooien. Wanneer je dit terugzet naar normale X-Y coördinaten, ziet het beeld er bijna hetzelfde uit, maar zijn de fijne details verdwenen.
Door de grootte van het masker te bepalen, kun je precies instellen hoe gedetailleerd de output moet zijn. In de praktijk kan een afbeelding worden teruggebracht tot 2% van de oorspronkelijke entropie zonder dat dit bij normale zoom zichtbaar is. Dit proces in lossy compressie wordt quantisatie genoemd[^3].
Chroma Subsampling
Het menselijk oog en brein zijn niet erg goed in het waarnemen van fijne details in kleur, maar kunnen variaties in helderheid wel zeer gemakkelijk detecteren.
In een videosignaal worden R+G+B kleurgegevens getransformeerd naar Y+Cb+Cr:
- Y: Luminantie (in essentie zwart-wit helderheid).
- Cb en Cr: Chrominantie (kleurcomponenten).
Bij Chroma Subsampling[^4] wordt de Y-component op volledige resolutie gecodeerd, terwijl de C-componenten slechts op een kwart van de resolutie worden gecodeerd. Omdat wij kleurvariaties slecht waarnemen, is het visuele verschil minimaal, maar wordt de totale bandbreedte gehalveerd.
Bewegingscompensatie (Motion Compensation)
Tot nu toe hebben we gekeken naar ruimtelijke compressie binnen één frame. Nu volgt de temporele compressie: het kijken naar een groep frames over een tijdsperiode.
H.264 is een standaard voor bewegingscompensatie. In plaats van elk frame volledig op te slaan, analyseert H.264 welke delen van het beeld statisch zijn en welke delen bewegen. De afbeelding wordt opgedeeld in macro-blocks (meestal blokken van 16x16 pixels).
Er worden verschillende soorten frames gebruikt:
- I-frames (Intra frames): Een volledig frame dat alle bits bevat die nodig zijn om het beeld te construeren. Dit is een statisch referentiebeeld.
- P-frames (Predicted): Deze frames coderen alleen een bewegingsvector voor elk macro-block ten opzichte van het vorige frame.
- B-frames (Bi-directionally predicted): Deze frames voorspellen beweging op basis van zowel voorgaande als toekomstige frames.
Waarom pauzeert YouTube bij het terugspoelen?
Wanneer je een video terugspoelt naar een specifiek tijdstip, pauzeert de speler vaak kort. Dit komt omdat de decoder vanaf het dichtstbijzijnde I-frame opnieuw alle berekeningen moet maken en de bewegingsvector-delta's van de P- en B-frames moet optellen om het huidige frame te kunnen tonen. Dit is rekentechnisch zwaar.
De laatste stap: de entropie-coder
Na alle lossy stappen bevatten de I-frames en de bewegingsvectoren in de P- en B-frames nog steeds redundante informatie. Daarom wordt als laatste stap een traditionele lossless entropie-encoder toegepast om de overgebleven redundantie te verwijderen zonder dat er extra kwaliteitsverlies optreedt.
Conclusie: De eindsom
Om de efficiëntie te illustreren, kijken we naar de oorspronkelijke video van de Apple-homepage (resolutie 1232x1154):
- Ruwe data:
5 sec @ 60 fps = 1232 x 1154 x 60 x 3 x 5$\approx$ 1,2 GB - Gecomprimeerde video: 175 KB
Als we deze ratio toepassen op de analogie van de auto van 3000 lbs, zou het uiteindelijke gewicht slechts 0,4 lbs (ongeveer 180 gram) zijn.
Dat is waar de magie van H.264 ligt.
***
[^1]: SSD Benchmarks [^2]: Nyquist-Shannon Sampling Theorem [^3]: Quantization) [^4]: Chroma Subsampling
Groetjes,