Sådan fungerer JPG

Sådan fungerer JPG

JPG-filformatet var en af ​​de mest teknologisk imponerende fremskridt inden for billedkomprimering, der kom på scenen i 1992. Siden da har det været en dominerende kraft i repræsentationen af ​​billeder i fotokvalitet på internettet. Og med god grund. Meget af teknologien bag, hvordan JPG fungerer, er usædvanligt kompleks og kræver en klar forståelse af, hvordan det menneskelige øje tilpasser sig opfattelsen af ​​farver og kanter.

Og da jeg er vild med de slags ting (og det er du også, hvis du læser dette), ville jeg nedbryde, hvordan JPG-kodning fungerer, så vi bedre kan forstå, hvordan man laver mindre JPG-filer.

GISTEN

JPG-komprimeringsskemaet er opdelt i flere faser. Billedet nedenfor beskriver dem på et højt niveau, og vi gennemgår hver fase nedenfor.

Konvertering af farverum

Et af nøgleprincipperne for tabt datakomprimering er, at menneskelige sensorer ikke er så nøjagtige som computersystemer. Videnskabeligt har det menneskelige øje kun den fysiske evne til at skelne omkring 10 millioner forskellige farver. Der er dog mange ting, der kan påvirke, hvordan det menneskelige øje opfatter en farve; perfekt fremhævet med farve illusioner eller det faktum, at denne kjole brød internettet. Kernen er, at det menneskelige øje kan manipuleres pænt med hensyn til de farver, det opfatter.

Kvantisering er en form for denne effekt i tabsfri billedkomprimering, men JPG tager en anden tilgang til dette: farvemodeller . Et farverum er en bestemt organisering af farver, og dens farvemodel repræsenterer den matematiske formel for, hvordan disse farver er repræsenteret (f.eks. Tredobler i RGB eller firdobler i CMYK).

Hvad der er stærkt ved denne proces er, at du kan konvertere fra en farvemodel til en anden , hvilket betyder, at du kan ændre den matematiske repræsentation af en given farve med et helt andet sæt numeriske værdier.

For eksempel er nedenfor en bestemt farve, og den er repræsentation i RGB- og CMYK-farvemodeller, de har samme farve for det menneskelige øje, men kan repræsenteres med et andet sæt numeriske værdier.

JPG konverterer fra RGB til Y, Cb, Cr farvemodel; Hvilket består af Luminance (Y), Chroma Blue (Cb) og Chroma Red (Cr). Årsagen til dette er, at psyko-visuelle eksperimenter (aka hvordan hjernen arbejder med information, øjet ser) viser, at det menneskelige øje er mere følsomt over for luminans end krominans, hvilket betyder, at vi kan forsømme større ændringer i krominansen uden at påvirke vores opfattelse af billedet. Som sådan kan vi foretage aggressive ændringer i CbCr-kanaler, før det menneskelige øje bemærker.

Nedsampling

Et af de interessante resultater af YCbCr-farveområdet er, at de resulterende Cb / Cr-kanaler har mindre detaljerede detaljer; de indeholder mindre information end Y-kanalen gør.

Som et resultat ændrer JPG-algoritmen størrelsen på Cb- og Cr-kanaler, så de er omkring ¼ deres oprindelige størrelse (bemærk, der er en vis nuance i, hvordan dette gøres, som jeg ikke dækker her ...), hvilket kaldes downsampling .

Det, der er vigtigt at bemærke her, er, at nedprøvning er en tabsfri komprimeringsproces (du vil ikke være i stand til at gendanne de nøjagtige kildefarver, men kun en tæt tilnærmelse), men den samlede indflydelse på de visuelle komponenter i den menneskelige visuelle cortex er minimal. Luma (Y) er, hvor de interessante ting er, og da vi kun nedmonterer CbCr-kanalerne, er påvirkningen på det visuelle system lav.

Billede opdelt i 8x8 blokke af pixels

Herefter udfører JPG alle operationer på 8x8 pixelblokke. Dette gøres, fordi vi generelt forventer, at der ikke er meget forskel på 8x8 blokke, selv i meget komplekse fotos, er der tendens til at være en vis selvlighed i lokale områder; denne lighed er, hvad vi vil drage fordel af under vores komprimering senere.

Det er værd at bemærke, at vi på dette tidspunkt introducerer en af ​​de første almindelige "artefakter" i JPG-kodning. "Farveblødning" er hvor farver langs skarpe kanter kan "bløder" på den anden side. Dette skyldes, at krominanskanalerne, som udtrykker pixelfarven, har haft hver blok på 4 pixels i gennemsnit til en enkelt farve, og nogle af disse blokke krydser den skarpe kant.

Diskret cosinustransformation

Indtil dette tidspunkt har tingene været temmelig tamme. Farverum, nedprøvetagning og blokering er enkle ting i billedkomprimeringens verden. Men nu… nu dukker den rigtige matematik op.

Nøglekomponenten i DCT-transformeringen er, at den antager, at ethvert numerisk signal kan genskabes ved hjælp af en kombination af cosinusfunktioner.

For eksempel, hvis vi har denne graf nedenfor:

Du kan se, at det faktisk er en sum af cos (x) + cos (2x) + cos (4x)

Måske er en bedre visning af dette den faktiske afkodning af et billede, givet en række cosinusfunktioner over et 2D-rum. For at vise dette præsenterer jeg en af ​​de mest fantastiske GIF'er på internettet: kodning af en 8x8 pixelblok ved hjælp af cosinus i et 2D-rum:

Det, du ser her, er rekonstruktionen af ​​et billede (panelet til venstre). Hver ramme tager vi en ny basisværdi (højre panel) og ganger den med en vægtværdi (højre paneltekst) for at producere bidraget til billedet (centerpanelet).

Som du kan se, ved at summere forskellige cosinusværdier mod en vægt, kan vi rekonstruere vores originale billede (temmelig godt ...)

Dette er den grundlæggende baggrund for, hvordan Discrete Cosine Transform fungerer. Ideen er, at enhver 8x8-blok kan repræsenteres som en sum af vægtede cosinustransformationer ved forskellige frekvenser. Tricket med hele denne ting er at finde ud af, hvilke cosinusindgange der skal bruges, og hvordan de skal vægtes sammen.

Viser sig, at " hvilke cosinus skal man bruge" problemet er ret let; Efter en masse test blev et sæt cosinusværdier valgt til at give de bedste resultater, de er vores basisfunktioner og visualiseret i nedenstående billede.

For så vidt angår “hvordan de skal vægtes sammen”, skal du ganske enkelt (HA!) Anvende denne formel.

Jeg sparer dig for, hvad alle disse værdier betyder, du kan slå dem op på wikipedia-siden.

Det grundlæggende resultat er, at anvendelse af ovenstående formel og basisfunktioner for en 8x8 pixelblok i hver farvekanal vil generere en ny 8x8 matrix, som repræsenterer de vægte, der skal bruges under genopbygning. Her er en grafik af processen:

Denne matrix, G, repræsenterer basisvægtene, der skal bruges til at rekonstruere billedet (den lille decimalværdi i nederste højre side af animationen ovenfor). Dybest set multiplicerer vi det for hver basis med vægten i denne matrix, summerer det hele og får vores resulterende billede.

På dette tidspunkt arbejder vi ikke længere i farverum, men snarere direkte med G Matrix (basisvægte), al yderligere kompression udføres direkte på denne matrix.

Problemet her er dog, at vi nu har konverteret bytejusterede heltalværdier til reelle tal. Hvilket effektivt spreder vores information (bevæger sig fra 1 byte til 1 float (4 bytes)). For at løse dette og begynde at producere mere signifikant kompression går vi videre til kvantiseringsfasen.

Kvantisering

Så vi ønsker ikke at komprimere flydende punktdata. Dette ville svulme op i vores strøm og ikke være effektiv. Til dette formål vil vi gerne finde en måde at konvertere vægte-matrixen tilbage til værdier i rummet [0,255]. Direkte kunne vi gøre dette ved at finde min / max-værdien for matrixen (henholdsvis -415,38 og 77,13) og dividere hvert tal i dette interval for at give os en værdi mellem [0,1], som vi multiplicerer med 255 for at få vores endelige værdi.

For eksempel: [34.12- -415.38] / [77.13 - -415.38] * 255 = 232

Dette fungerer, men kompromisen er en betydelig reduktion af præcision. Denne skalering vil producere en ujævn fordeling af værdier, hvis resultat er et betydeligt visuelt tab for billedet.

I stedet tager JPG en anden rute. I stedet for at bruge værdiområdet i matrixen, da den skalerer værdi, bruger den i stedet en forberegnet matrix af kvantiseringsfaktorer. Disse QF'er behøver ikke at være en del af strømmen, de kan snarere være en del af selve codec'en.

Dette eksempel viser en almindeligt anvendt matrix af kvantiseringsfaktorer, en for hvert basisbillede,

Vi bruger nu Q- og G-matricerne til at beregne vores kvantiserede DCT-koefficientmatrix:

Brug f.eks. G [0,0] = - 415,37 og Q [0,0] = 16 værdier:

Resultatet i en endelig matrix på:

Vær opmærksom på, hvor meget enklere matrixen bliver - den indeholder nu et stort antal poster, der er små eller nul, hvilket gør det meget lettere at komprimere.

Som en hurtig til side anvender vi denne proces på Y-, CbCr-kanaler uafhængigt, og som sådan har vi brug for to forskellige matricer: en til Y og den anden til C-kanaler:

Kvantisering komprimerer billedet på to vigtige måder: den ene begrænser det effektive vægteområde og reducerer antallet af bits, der kræves for at repræsentere dem. To, mange af vægtene bliver identiske eller nul, hvilket forbedrer kompression i det tredje trin, entropikodning.

Som sådan er kvantisering den primære kilde til JPEG-artefakter. Fordi billederne nederst til højre har de største kvantiseringsdelere, vil JPEG-artefakter have en tendens til at ligne kombinationer af disse billeder. Matrixen med kvantiseringsfaktorer kan styres direkte ved at ændre JPEGs “kvalitetsniveau”, som skalerer dens værdier op eller ned (vi dækker det om et minut)

Kompression

Nu er vi tilbage i en helhedsværdis verden og kan gå videre med at anvende et tabsfri kompressionstrin på vores blokke. Når du ser på vores transformerede data, skal du dog bemærke noget interessant:

Når du bevæger dig fra øverst til venstre til nederst til højre, øges hyppigheden af ​​nuller. Dette ligner en hovedmistænkt for Run Length Encoding. Men rækkefølge og kolonne-større ordrer er ikke ideelle her, da det ville blande disse nuller, snarere end at pakke dem alle sammen.

I stedet starter vi med det øverste venstre hjørne og zig-zag i et diagonalt mønster på tværs af matricen og går frem og tilbage, indtil vi når det nederste højre hjørne.

Resultatet af vores luma-matrix bliver i denne rækkefølge:

−26, −3,0, −3, −2, −6,2, −4,1, −3,1,1,5,1,2, −1,1, −1,2,0,0 , 0,0,0, -1, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Når dataene er i dette format, er de næste trin ligetil: udfør RLE på sekvensen, og anvend derefter nogle statistiske kodere (Huffman / Arithmetic / ANS) på resultaterne.

Og Boom. Din blok er nu JPG-kodet.

Forståelse af kvalitetsparameteren

Nu hvor du forstår, hvordan JPG-filer faktisk oprettes, er det værd at gennemgå begrebet kvalitetsparameter , som du normalt ser, når du eksporterer JPG-billeder fra Photoshop (eller hvad der ikke er).

Denne parameter, som vi kalder q, er et heltal fra 1 til 100. Du bør tænke på q som et mål for billedets kvalitet: højere værdier af q svarer til billeder af højere kvalitet og større filstørrelser.

Denne kvalitetsværdi bruges i kvantiseringsfasen til at skalere kvantiseringsfaktorerne korrekt. Så pr. Basisvægt ligner kvantiseringstrinnet nu rundt (Gi, k / alfa * Qi, k)

Hvor alfasymbolet oprettes som et resultat af kvalitetsparameteren.

Når enten alpha eller Q [x, y] øges (husk at store værdier af alpha svarer til mindre værdier i kvalitetsparameteren q), går mere information tabt, og filstørrelsen falder .

Som sådan, hvis du vil have en mindre fil på bekostning af flere visuelle artefakter, kan du indstille en lavere kvalitetsværdi under eksportfasen.

Bemærk ovenfor, i billedet med den laveste kvalitet, hvordan vi ser klare tegn på blokeringsfasen såvel som kvantiseringsfasen.

Det vigtigste er sandsynligvis, at kvalitetsparameteren varierer afhængigt af billedet . Da hvert billede er unikt og præsenterer forskellige typer visuelle artefakter, vil Q-værdien også være unik.

Konklusion

Når du først har forstået, hvordan JPG-algoritmen fungerer, vises et par ting:

  1. At få kvalitetsværdien rigtig pr. Billede er vigtig for at finde en kompromis mellem visuel kvalitet og filstørrelse.
  2. Da denne proces er blokbaseret, vil artefakter have tendens til at forekomme i blokitet eller "ringe"
  3. Da forarbejdede blokke ikke blander sig med hinanden, ignorerer JPG generelt muligheden for at komprimere store skår af lignende blokke sammen. At tackle denne bekymring er noget, WebP-formatet er godt til at gøre.

Og hvis du vil lege med alt dette alene, kan al denne vanvid koges ned til en ~ 1000 linjefil.

HEJ!

Vil du vide, hvordan du gør dine JPG-filer mindre?

Vil du vide, hvordan PNG-filer fungerer, eller hvordan man gør dem mindre?

Vil du have mere datakomprimerings godhed? Køb min bog!