Așa cum am făcut cel mai rapid redimensiona imagini. Partea 0 78
- 14.02.17 08:17 •
- HoMM •
- • # 321744
- • Habrahabr
- 66 •
- 14800
- cum ar fi Forbes, doar mai bine.
Bună ziua, numele meu este Sasha, am scris cel mai rapid redimensionați imaginile pentru procesoarele x86 de astăzi. Aș argumenta, ca tot restul bibliotecii, am fost în stare să găsească și de testare, au fost mai lente. Am preluat provocarea atunci când se lucrează la optimizarea redimensiona imagini pe zbor în Uploadcare. Am decis să deschidă codul, iar rezultatul a fost proiectul Pernă-SIMD. Oricine poate folosi cu ușurință în aplicația în Python.
Orice cod care se execută pe un anumit hardware și optimizare bună poate fi realizată numai prin înțelegerea arhitecturii sale. Tot ce am de gând să elibereze 4 sau 5 articole pe care le va spune cum să aplice cunoștințele de arhitectura de fier pentru a optimiza problema reală. Exemplul său Vreau să vă încurajez să optimizați alte aplicații. Primele două articole va fi lansat într-o săptămână, iar restul - atunci când este gata.
Sub „Resize Image“ Vreau să spun schimbarea dimensiunii imaginii folosind metoda reeșantionare parcelă. Reeșantionare se realizează pe o serie de 8 biți pixeli RGB în memorie fără a ține cont de codificare și de decodare a imaginilor, cu toate acestea, cu alocarea de memorie și imaginea finală cu pregătirea coeficienților necesare pentru o anumită operațiune.
E atât de stricte. Nu există trucuri (cum ar fi decodarea de dzhipega imagini mai mici) și nu are algoritmi care combină numai măsurare corectă a unui anumit algoritm. Trucuri și optimizarea cazuri particulare pot fi aplicate mai târziu, ele nu fac obiectul acestei serii de articole.
... folosind metoda reeșantionare a mănunchiului. Ce?
Pentru a face clar exact ceea ce trebuie să fie optimizat, să-ți spun convolutii ce reeșantionare. Convoluție (convoluția dreptul de a vorbi de valori discrete ca pixeli de imagine discrete.) - o operație matematică foarte simplă. Avem un anumit număr de valori №1 (coeficienți) și o serie de valori (date nr.2, în acest caz, intensitatea pixelilor de canale). Rezultatul convoluție a două serii este suma produselor tuturor membrilor în perechi. La fel ca și faptul că - suma produselor. Matan a terminat înainte de a începe.
Funcții de filtrare nu sunt infinite, valorile lor nu sunt egale cu zero numai în partea centrală: este o gamă de valori pentru filtrul biliniar -1-1; pentru bicubic 2 pana la 2, pentru Lanczos -3-3 (deși există și alte soiuri Lanczos).
Aceste numere sunt numite fereastra de filtru, după cum Filtrul se aplică numai în acest interval, și dincolo de zero. Prin urmare, numărul de pixeli de fundal necesare pentru sinuozitate este luată în mărimea razei filtrului ferestrei înmulțită cu raportul de reducere (sau de una dacă are loc creșterea). Cred că acest lucru este mai bine explicat cu un exemplu. Avem nevoie de a reduce lățimea imaginii la lățimea de 2560 pixeli 2048 folosind un filtru Bicubică. Să presupunem că vrem să găsim valoarea pixelului 33rd imaginea finală. In bicubic fereastra filtru dimensiune este egal cu doi, iar raportul de reducere se obține 2560/2048 = 1,25, deci trebuie să luăm un rând de pixeli ai imaginii originale de la podea ((33 -. 2) 1.25) la ceil ((33 + 2) . 1.25). Ie de la 38 la 44 mii de pixeli-lea. Pentru acești pixeli sunt valori calculate ale coeficienților.
Până în acest moment, am spus pe un număr de factori și numărul de pixeli, pierde din vedere faptul că imaginea - este de fapt o structură bidimensională. Și la fel ca logica, nu opriți linia, iar unele dintre zona imaginii originale. Cu toate acestea, o proprietate a pachetului este că operația poate fi efectuată separat atât pe verticală cât și pe orizontală, făcând două treceri. Aproximativ vorbind, reduce complexitatea unui convoluție cu O (n?) La O (2n) (de fapt, mai puțin, dar încă semnificativă).
De ce sunt toate la fel de convoluție
În general, expresia „redimensiona imaginea„poartă un minim de informații cu privire la ce să facă. Ea spune că avem de a obține dimensiunea finală a imaginii, utilizând originalul, păstrând geometria obiectelor prezentate. Dar pentru a utiliza imaginea originală poate fi diferită. Este posibil, de exemplu, pentru a pune fiecare capăt al pixel într-o singură linie pixel al sursei și ia-o fără modificări. Aceasta se numește cel mai apropiat vecin. Imaginea este aspră, zdrențăros, neplăcut:

Acest lucru se întâmplă pentru că o fracțiune foarte mică a pixelilor originali (în exemplul de mai sus, mai puțin de un procent) a fost utilizat în imaginea finală. Informațiile din aceste pixeli, care nu au fost incluse în imaginea finală, am pierdut.
Dar se pare ca reeșantionare folosind pachetul:

Reeșantionare folosind convolutii ia în considerare în mod corect contribuția fiecărui pixel sursă din imaginea finală. Este universală, deoarece dă rezultate la fel de bune și previzibile pentru o gamă largă de factori de scalare, nu conține nici o distorsiune la nivel local geometrie (cu condiția ca un filtru este utilizat, care nu prevede o astfel de denaturare, deoarece unele filtre da încă). Și oricum, e bine și atât de bine pe toate părțile cu excepția uneia: performanța.
Perna - o bibliotecă de manipulare a imaginii în Python, dezvoltat de comunitate, condus de Alex Clark și Eric Soroos. În Uploadcare pernă a fost folosit înainte, așa cum am venit la echipa. Atunci mi se părea ciudat - Lucrul cu imagini a fost una dintre principalele sarcini, de ce ti-a luat bibliotecă, este legat de limbaj. Nu e mai bine să ia la fel de ImageMagick, care are o tona de caracteristici, care este folosit de milioane de dezvoltatori, așa că, probabil, toți ar trebui să fie bine cu performanța. După câțiva ani, pot spune că a fost noroc pentru mine, și pentru pernă. După cum se dovedește, performanța ambelor biblioteci de la început a fost aproximativ la fel, dar mă îndoiesc foarte mult că mi-ar fi puterea de a face pentru ImageMagick este ceva ce am făcut pentru pernă.
Perna - este o furculiță de foarte vechi bibliotecă PIL. Punct de vedere istoric, pentru redimensionarea în PIL nu au fost utilizate convoluție. Prima punere în aplicare a convoluție redimensionare în PIL a apărut în versiunea 1.1.3 și a fost disponibilă atunci când se utilizează filtrul AntiAlias al cărui nume subliniază faptul că filtrele rămase utilizate algoritmi de calitate inferioară. Rețeaua poate fi în continuare de multe ori nu este găsit recomandări actuale pot fi utilizate la redimensionarea în PIL (în pernă, ca un receptor) numai filtru AntiAlias.
Din păcate, AntiAlias a fost o productivitate destul de scăzută. Am ajuns în codul sursă. pentru a vedea ce se poate face și sa dovedit că punerea în aplicare a redimensiona pentru AntiAlias (de exemplu, sinuozitate), acesta poate fi folosit și cu alte filtre. Un AntiAlias în sine constantă corespunde filtru Lanczos, care are o fereastră mare (± 3), și, prin urmare, este destul de lent. Chiar prima optimizare pe care am vrut să fac - rândul său, clapeta pentru biliniar și filtrele Bicubică. Deci, ar fi posibil în cererea mea de a utiliza filtru bicubic mai ieftin (cu o fereastră de ± 2) și nu pierde prea în calitate.
În continuare a fost interesant să se uite la codul redimensionării. L-am găsit cu ușurință în acest modul. Și, deși am scris cea mai mare parte în Python, imediat am observat câteva locuri discutabile în termeni de performanță. După mai multe optimizare am primit o creștere de 2,5 ori (acest lucru va fi discutat în articolul următor). Apoi am început să experimenteze cu SIMD, transferul tuturor calculelor pe numere întregi disloca agresiv cicluri și calcule de grup. Sarcina a fost extrem de interesant, în capul meu a fost întotdeauna un cuplu de idei cu privire la modul de îmbunătățire a performanței. Am aruncat în gaura de iepure mai adânc și mai adânc, verificând periodic următoarea ipoteză.
Treptat, codul devine mai mult și mai mare viteză. Partea din spate a predat evoluția pernă. Dar SIMD-cod a fost greu să se miște, pentru că este scris pentru o arhitectură specifică și o pernă - o bibliotecă de cross-platform. Prin urmare, sa decis să nu se facă o furculiță cross-platform Pernă-SIMD. Versiunile Pernă-SIMD respectă în totalitate cu versiunile originale ale perne și se adaugă accelerarea unor operațiuni.
Cele mai recente versiuni ale Pernă-SIMD AVX2 să redimensionați mergând de la 15 până la 30 de ori mai repede decât originalul PIL. Așa cum am spus la început, este punerea în aplicare rapidă a Redimensionarea de înaltă calitate, pe care am fost în stare să-l testeze. Puteți vizualiza pagina. care a adunat rezultatele de referință ale diferitelor biblioteci.

măsurătorile de performanță
A doua coloană este timpul în secunde, iar al treilea - lățimea de bandă a imaginii originale pentru această operațiune. Aceasta este, în cazul în care operațiunea a luat 0,2 secunde, debitul va fi 2560? 1600 / 0,2 = 20.48 megapixeli pe secundă.
În cazul în care un astfel de efect ar putea fi extrapolate la scara planetei, și să înlocuiască toate codul care se ocupă cu redimensionarea imaginilor pentru o utilizare mai eficientă ar fi enorme. Zeci de mii de servere salvate, sute de kilowați de energie electrică. Și aceasta este o milionime din consumul global. Da, s-ar putea salva planeta!
și apoi dintr-o dată acest patos?
rapidă - estimare hotărâre cu privire trebuie să fie cel mai rapid, este inutil, trebuie să fie suficient de rapid pentru a rezolva setul pentru provocările lingvistice și înțelegerea Python cu ei, recent mai mult succes și mai mult succes, iar dacă nu, atunci există posibilitatea de a adăuga la aceasta viteza și foarte semnificativ, cu alte cuvinte, este la sarcinile lor foarte bine, ca și cum ați înclinat ciocan, nu toate transformat în cuie
Poate ceva ce nu am înțeles, dar am luat OpenCV și pur și simplu numit resize:
2560 x 1600 -> 320 x 200 Bicubică = 3 ms
Și tu SIMD SSE4 - 7,7 ms, și SIMD AVX2 - 5,7 ms, care este de 2-2,5 ori mai lent.
Faptul că OpenCV pentru a redimensiona metoda care nu utilizează pachetul. Se folosește aceeași metodă ca și în pernă la versiunea 2.7 pentru biliniară și bicubic aceeași metodă care este utilizată în elementul de pânză în browsere. Vă trimit să citiți articolul Redimensionarea imaginilor în browser. Totul este foarte rău. diferență minimă - cu scăderea fereastra de imagine nu crește cu un factor de scădere ca convoluție. Dar aceasta afectează radical calitatea și viteza. De exemplu, eu iau aceeași imagine. că articolul (7000. 2926> 512 214).
După cum puteți vedea, este mult mai aproape de cel mai apropiat vecin. În funcție de sarcina vă puteți gândi la un astfel de algoritm, sau să ia în considerare nevalabilitatea.
Vă mulțumim pentru informativ, și cel mai important, metoda de înțeles poveste) este prezentată foarte lucid și clar, foarte faimos în scris.
Aș, cu toate acestea, să fie foarte recunoscător dacă mi spui unele link-ul în cazul în care același principiu este clar descris pentru a simplifica convoluția a două treceri. Pentru că în acest moment nu sunt clar modul în care acest lucru poate da un rezultat identic (nu putem pentru că, de fapt, într-un astfel de scenariu să se ia în considerare efectul pixelului diagonală a ferestrei de filtrare).
Fiecare pixel după convoluției orizontală devine o combinație a unei multitudini de pixeli vecine pe orizontală. trecerea ulterioară combină convoluția verticală între pixelii nu mai este imaginea originală, iar aceste „combinație de mai mulți pixeli.“ Spune în filtrul 5x5 dacă ne interesează unghi (-2, 2) la etapa vom calcula o p_horizontal convoluție pixel orizontală [2] = weighted_sum ([-2,2], [1,2], [0,2], [ 1.2], [2.2]) și apoi se substituie în p = weighted_sum (p_horizontal [-2], [-1], [0], [1], [2]); Este ușor de văzut că această sumă va veni și [-2.2] cu unele greutate.
Ea nu funcționează pentru toate posibile convoluție 2D, dar numai pentru subclasă lor specială numită convolutii cu nuclee „separabile“. Din fericire pentru redimensionarea aproape toate kernel separabile.
Mulțumesc, pare să înțeleagă)
Cu convoluție verticale noi „adăugați“ pixeli „linie“ vertical cu raportul dorit, fiecare dintre care a rezumat-o deja pe orizontală „linia“. Și în plus „propria“ linie este deja reprezentat. Da, totul este super.
Singurul lucru pe care - dacă ajungi la factorii, pot exista unele comune.
De exemplu, vreau să iau un filtru de 3x3 pentru simplitate și de pixeli extreme să ia un coeficient de 0,4. Apoi, după convoluție orizontală vom adăuga 0,4 la elementele extreme în linia de jos, iar daca adaugam verticala are 0,4 * 0,4 = 0,16, adică, la pătrat coeficientul vom avea. In timp ce logic, probabil, utilizați rădăcina pătrată (cum ar fi distanța). În cazul în care filtrul nu este un bi-liniară, și nu există cu siguranță nu este proporțională cu distanța, dar vreau să spun că toți aceiași coeficienți ai acestei matrice ar trebui să fie numărate în această metodă va fi :)