Cum funcționează JPG

Cum funcționează JPG

Formatul de fișier JPG a fost unul dintre cele mai impresionante progrese din punct de vedere tehnologic în ceea ce privește compresia de imagini care a apărut în scenă în 1992. De atunci, a fost o forță dominantă în reprezentarea imaginilor de calitate a fotografiilor pe internet. Și din motive întemeiate. O mare parte din tehnologia din spatele modului în care funcționează JPG este extrem de complexă și necesită o înțelegere fermă a modului în care ochiul uman se adaptează la percepția culorilor și a marginilor.

Și din moment ce mă interesează chestia asta (și tu ești și tu, dacă citești asta), am vrut să descompun cum funcționează codificarea JPG, astfel încât să putem înțelege mai bine cum să creăm fișiere JPG mai mici.

ESENȚIALUL

Schema de compresie JPG este împărțită în mai multe faze. Imaginea de mai jos le descrie la un nivel înalt și vom parcurge fiecare fază de mai jos.

Conversia spațiului de culori

Unul dintre principiile cheie ale compresiei de date cu pierderi este că senzorii umani nu sunt la fel de exacți ca sistemele de calcul. Științific, ochiul uman are abilitatea fizică de a distinge aproximativ 10 milioane de culori diferite. Cu toate acestea, există o mulțime de lucruri care pot influența modul în care ochiul uman percepe o culoare; perfect evidențiată cu iluzii de culoare, sau prin faptul că această rochie a spart internetul. Esența este că ochiul uman poate fi manipulat frumos în raport cu culorile pe care le percepe.

Cuantificarea este o formă a acestui efect în compresia imaginii cu pierderi, cu toate acestea JPG adoptă o abordare diferită în acest sens: modelele color . Un spațiu de culoare este o organizare specifică a culorilor, iar modelul său de culoare reprezintă formula matematică pentru modul în care aceste culori sunt reprezentate (de exemplu, tripluri în RGB sau cvadrupluri în CMYK).

Ceea ce este puternic în acest proces este că puteți converti de la un model de culoare la altul , ceea ce înseamnă că puteți schimba reprezentarea matematică a unei culori date, cu un set complet diferit de valori numerice.

De exemplu, mai jos este o culoare specifică și este reprezentată în modelele de culori RGB și CMYK, acestea sunt de aceeași culoare pentru ochiul uman, dar pot fi reprezentate cu un set diferit de valori numerice.

JPG convertește din modelul de culoare RGB în Y, Cb, Cr; Care cuprinde Luminance (Y), Chroma Blue (Cb) și Chroma Red (Cr). Motivul pentru aceasta este că experimentele psiho-vizuale (alias cum funcționează creierul cu informațiile pe care ochiul le vede) demonstrează că ochiul uman este mai sensibil la luminanță decât la crominanță, ceea ce înseamnă că putem neglija modificări mai mari ale crominanței fără a afecta percepția imaginii. Ca atare, putem face schimbări agresive la canalele CbCr înainte ca ochiul uman să observe.

Prelevare de probe

Unul dintre rezultatele interesante ale spațiului de culoare YCbCr este că canalele Cb / Cr rezultate au detalii mai puțin fine; conțin mai puține informații decât canalul Y.

Ca rezultat, algoritmul JPG redimensionează canalele Cb și Cr pentru a avea aproximativ size dimensiunea lor originală (rețineți, există unele nuanțe în modul în care se face acest lucru, pe care nu le acoper aici ...), care se numește downsampling .

Ceea ce este important de remarcat aici este că eșantionarea este un proces de compresie cu pierderi (nu veți putea recupera culorile sursei exacte, ci doar o aproximare strânsă), dar impactul general asupra componentelor vizuale ale cortexului vizual uman este minim. Luma (Y) este locul în care se află lucrurile interesante și din moment ce suntem doar de eșantionare a canalelor CbCr, impactul asupra sistemului vizual este redus.

Imagine împărțită în 8 x 8 blocuri de pixeli

De aici înainte, JPG face toate operațiunile pe blocuri de 8x8 pixeli. Acest lucru se face pentru că, în general, ne așteptăm să nu existe o mare diferență între blocurile 8x8, chiar și în fotografiile foarte complexe, tinde să existe o oarecare similitudine în zonele locale; această similitudine este ceea ce vom profita în timpul compresiei noastre mai târziu.

Este demn de remarcat faptul că, în acest moment, introducem unul dintre primele „artefacte” comune ale codificării JPG. „Sângerarea culorii” este locul în care culorile de-a lungul marginilor ascuțite pot „sângera” pe cealaltă parte. Acest lucru se datorează faptului că canalele de crominanță, care exprimă culoarea pixelilor, au avut fiecare bloc de 4 pixeli mediat într-o singură culoare, iar unele dintre aceste blocuri traversează marginea ascuțită.

Transformarea discretă a cosinusului

Până în prezent, lucrurile au fost destul de blânde. Spațiile de culori, eșantionarea și blocarea sunt lucruri simple în lumea comprimării imaginilor. Dar acum ... acum apare adevărata matematică.

Componenta cheie a transformării DCT este că presupune că orice semnal numeric poate fi recreat folosind o combinație de funcții cosinus.

De exemplu, dacă avem acest grafic mai jos:

Puteți vedea că este de fapt o sumă de cos (x) + cos (2x) + cos (4x)

Poate că o afișare mai bună a acestui lucru este decodarea reală a unei imagini, având în vedere o serie de funcții ale cosinusului pe un spațiu 2D. Pentru a arăta acest lucru, vă prezint unul dintre cele mai uimitoare GIF-uri de pe internet: codificarea unui bloc de pixeli de 8x8 folosind cosinusuri într-un spațiu 2D:

Ce urmăriți aici este reconstrucția unei imagini (panoul din stânga). Fiecare cadru, luăm o nouă valoare de bază (panoul din dreapta) și îl multiplicăm cu o valoare a greutății (textul din panoul din dreapta) pentru a produce contribuția la imagine (panoul central).

După cum puteți vedea, prin însumarea diferitelor valori ale cosinusului în raport cu o greutate, ne putem reconstrui imaginea originală (destul de bine ...)

Acesta este fundalul fundamental pentru modul în care funcționează Transformarea discretă a cosinusului. Ideea este că orice bloc 8x8 poate fi reprezentat ca o sumă de transformate ponderate ale cosinusului, la diferite frecvențe. Trucul cu toată această chestiune este să ne dăm seama ce intrări de cosinus să folosim și cum ar trebui ponderate împreună.

Rezultă că problema „ ce cosinus se folosește” este destul de ușoară; După o mulțime de teste, un set de valori ale cosinusului au fost alese pentru a produce cele mai bune rezultate, acestea sunt funcțiile noastre de bază și vizualizate în imaginea de mai jos.

În ceea ce privește problema „cum ar trebui să fie ponderate împreună”, pur și simplu (HA!) Aplicați această formulă.

Vă scutesc ce înseamnă toate aceste valori, le puteți căuta pe pagina wikipedia.

Rezultatul de bază este că pentru un bloc de 8x8 de pixeli în fiecare canal de culoare, aplicarea formulei de mai sus și a funcțiilor de bază va genera o nouă matrice 8x8, care reprezintă greutățile care trebuie utilizate în timpul reconstrucției. Iată un grafic al procesului:

Această matrice, G, reprezintă greutățile de bază pentru a reconstitui imaginea (valoarea zecimală mică din partea dreaptă jos a animației de mai sus). Practic, pentru fiecare bază, o înmulțim cu greutatea din această matrice, însumăm totul împreună și obținem imaginea rezultată.

În acest moment, nu mai lucrăm în spații de culori, ci mai degrabă direct cu matricea G (greutăți de bază), toate comprimările ulterioare se fac direct pe această matrice.

Totuși, problema aici este că am convertit acum valori întregi aliniate la octeți în numere reale. Ceea ce ne umflă efectiv informațiile (trecând de la 1 octet la 1 float (4 octeți)). Pentru a rezolva acest lucru și a începe să producem o compresie mai semnificativă, trecem la faza de cuantificare.

Cuantizare

Deci, nu vrem să comprimăm datele în virgulă mobilă. Acest lucru ne-ar umfla fluxul și nu ar fi eficient. În acest scop, am dori să găsim o modalitate de a converti greutatea-matrice înapoi la valori în spațiul de [0,255]. În mod direct, am putea face acest lucru găsind valoarea min / max pentru matrice (-415.38 și respectiv 77.13) și împărțind fiecare număr din acest interval pentru a ne oferi o valoare între [0,1] la care înmulțim cu 255 pentru a obține valoarea noastră finală.

De exemplu: [34.12- -415.38] / [77.13 - -415.38] * 255 = 232

Acest lucru funcționează, dar compromisul reprezintă o reducere semnificativă a preciziei. Această scalare va produce o distribuție inegală a valorilor, al cărei rezultat este o pierdere vizuală semnificativă a imaginii.

În schimb, JPG urmează un traseu diferit. Mai degrabă decât să utilizeze gama de valori din matrice ca valoare de scalare, folosește în schimb o matrice precalculată de factori de cuantificare. Aceste QF-uri nu trebuie să facă parte din flux, ci pot face parte din codecul în sine.

Acest exemplu arată o matrice utilizată în mod obișnuit de factori de cuantificare, câte unul pentru fiecare imagine de bază,

Acum folosim matricile Q și G pentru a calcula matricea coeficientului DCT cuantificat:

De exemplu, folosind valorile G [0,0] = - 415,37 și Q [0,0] = 16:

Rezultând într-o matrice finală de:

Observați cât de simplă devine matricea - conține acum un număr mare de intrări mici sau zero, ceea ce face mult mai ușor comprimarea.

Deoparte, aplicăm acest proces canalelor Y, CbCr în mod independent și, ca atare, avem nevoie de două matrice diferite: una pentru Y și cealaltă pentru canalele C:

Cuantizarea comprimă imaginea în două moduri importante: unul, limitează domeniul efectiv al greutăților, scăzând numărul de biți necesari pentru a le reprezenta. Două, multe dintre greutăți devin identice sau zero, îmbunătățind compresia în al treilea pas, codificarea entropiei.

Deoarece o astfel de cuantificare este sursa principală a artefactelor JPEG. Deoarece imaginile din dreapta jos tind să aibă cei mai mari divizori de cuantificare, artefactele JPEG vor tinde să semene cu combinații ale acestor imagini. Matricea factorilor de cuantificare poate fi controlată direct prin modificarea „nivelului de calitate” al JPEG, care îi scalează valorile în sus sau în jos (vom acoperi asta într-un minut)

Comprimare

Până acum, ne-am întors în lumea valorilor întregi și putem merge mai departe aplicând o etapă de compresie fără pierderi blocurilor noastre. Când vă uitați la datele noastre transformate, ar trebui să observați ceva interesant:

Pe măsură ce treceți de la stânga sus la dreapta jos, frecvența zerourilor crește. Acesta pare a fi un suspect principal pentru codificarea pe lungime. Dar ordinele rând-majore și coloane-majore nu sunt ideale aici, deoarece acest lucru ar antrela aceste runde de zerouri, mai degrabă decât să le împachetăm pe toate.

În schimb, începem cu colțul din stânga sus și zig-zag într-un model diagonal peste matrice, mergând înainte și înapoi până ajungem în colțul din dreapta jos.

Rezultatul matricei noastre Luma, în această ordine, devine:

−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,0,0,0,0,0

Odată ce datele sunt în acest format, pașii următori sunt simpli: executați RLE pe secvență și apoi aplicați un codificator statistic (Huffman / Arithmetic / ANS) pe rezultate.

Și Boom. Blocul dvs. este acum codificat JPG.

Înțelegerea parametrului de calitate

Acum că înțelegeți cum sunt create de fapt fișierele JPG, merită să revizuiți conceptul parametrului de calitate pe care îl vedeți în mod normal atunci când exportați imagini JPG din Photoshop (sau ce nu).

Acest parametru, pe care îl vom numi q, este un număr întreg de la 1 la 100. Ar trebui să vă gândiți la q ca la o măsură a calității imaginii: valori mai mari ale q corespund unor imagini de calitate superioară și dimensiuni mai mari de fișiere.

Această valoare a calității este utilizată în timpul fazei de cuantificare, pentru a scala factorii de cuantificare în mod corespunzător. Deci, pe baza greutății, pasul de cuantizare seamănă acum cu rotund (Gi, k / alfa * Qi, k)

În cazul în care simbolul alfa este creat ca urmare a parametrului de calitate.

Atunci când alfa sau Q [x, y] sunt mărite (amintiți-vă că valorile mari ale alfa corespund unor valori mai mici ale parametrului de calitate q), se pierd mai multe informații și dimensiunea fișierului scade .

Ca atare, dacă doriți un fișier mai mic, cu prețul mai multor artefacte vizuale, puteți seta o valoare de calitate mai mică în timpul fazei de export.

Observați mai sus, în imaginea de cea mai mică calitate, cum vedem semne clare ale stadiului de blocare, precum și stadiul de cuantificare.

Probabil cel mai important este că parametrul de calitate variază în funcție de imagine . Deoarece fiecare imagine este unică și prezintă diferite tipuri de artefacte vizuale, valoarea Q va fi, de asemenea, unică.

Concluzie

Odată ce ați înțeles cum funcționează algoritmul JPG, câteva lucruri devin evidente:

  1. Obținerea corectă a valorii calității, pe imagine, este importantă pentru a găsi diferența dintre calitatea vizuală și dimensiunea fișierului.
  2. Deoarece acest proces este bazat pe blocuri, artefactele vor avea tendința să apară în blocuri sau „sunete”
  3. Deoarece blocurile procesate nu se amestecă unul cu celălalt, JPG ignoră, în general, oportunitatea de a comprima zone mari de blocuri similare împreună. Abordarea acestei preocupări este un lucru pe care formatul WebP îl face bine.

Și dacă vrei să te joci singur cu toate acestea, toată nebunia asta poate fi redusă într-un fișier de ~ 1000 linii.

HEI!

Doriți să știți cum să vă micșorați fișierele JPG?

Doriți să știți cum funcționează fișierele PNG sau cum să le micșorați?

Doriți mai multe bunătăți de compresie a datelor? Cumpără-mi cartea!