Järjestikuse keskmise kvantimise teisendamise (SMQT) algoritm on mittelineaarne teisendus, mis paljastab andmete organisatsiooni või struktuuri ning eemaldab sellised omadused nagu võimendus ja eelarvamused. Paberis pealkirjaga Järjestikune keskmine kvantimise teisendus , SMQT on “rakendatud kõnetöötluses ja pilditöötluses”. Piltidele rakendatuna saab algoritmi 'näha progressiivse fookusena pildi detailidele'.
Vastavalt teisele paberile pealkirjaga SMQT-põhised heli kaardistamise operaatorid suure dünaamilise ulatusega piltide jaoks , annab see 'täiustatud detailidega pildi'. Artiklis kirjeldatakse kaht meetodit selle teisenduse rakendamiseks pildile.
Rakendage SMQT iga piksli heledusele - see kirjeldab valemit heleduse saamiseks iga piksli RGB-st.
Rakendage SMQT RGB igale kanalile eraldi - kanalite R, G ja B jaoks eraldi.
Järgmine pilt näitab kahe tehnika tulemust:
Rakendades teist tehnikat öösel Bruini teatri pildile, näeme, kuidas algoritm suumib pildi üksikasju järk-järgult ja paljastab üksikasju, mida pimedus algselt varjab:
Selles artiklis vaatleme lähemalt, kuidas see algoritm töötab, ja uurime lihtsat optimeerimist, mis võimaldab algoritmil olla praktilistes pilditöötlusrakendustes palju tulemuslikum.
Originaaldokumentides kirjeldatakse algoritmi abstraktselt. SMQT paremaks mõistmiseks tutvustame lihtsaid näiteid.
Oletame, et meil on järgmine vektor (võite seda mõelda nagu massiivi):
32 | 48 | 60 | 64 | 59 | 47 | 31 | viisteist | 4 | 0 | 5 | 18 |
Värvipildi puhul võime mõelda sellest kui kolmest eraldi vektorist, millest igaüks tähistab kindlat värvikanalit (RGB), ja vektori iga element, mis tähistab vastava piksli intensiivsust.
SMQT algoritmi rakendamise etapid on järgmised:
Arvutage vektori keskmine, mis on antud juhul 29,58.
Jagage vektor kaheks, jättes vasakule poolele numbrid, mis on väiksemad või võrdsed kui 29,58, ja paremal poolel arvud, mis on suuremad:
viisteist | 4 | 0 | 5 | 18 | 32 | 48 | 60 | 64 | 59 | 47 | 31 |
0 | 0 | 0 | 0 | 0 | üks | üks | üks | üks | üks | üks | üks |
Teine rida on kood, mille loome igale väärtusele. Väärtused, mis on väiksemad või võrdsed 29,58, saavad oma koodiks 0 ja arvud, mis on suuremad kui 29,58, saavad 1 (see on binaarne).
Nüüd jagatakse mõlemad saadud vektorid eraldi, rekursiivselt, järgides sama reeglit. Meie näites on esimese vektori keskmine (15 + 4 + 0 + 5 + 18) / 5 = 8,4 ja teise vektori keskmine (32 + 48 + 60 + 64 + 59 + 47 + 31) / 7 = 48,7. Igaüks neist kahest vektorist jaguneb veel kaheks vektoriks ja mõlemale lisatakse teine koodibitt, sõltuvalt selle võrdlusest keskmisega. See on tulemus:
4 | 0 | 5 | viisteist | 18 | 32 | 47 | 31 | 48 | 60 | 64 | 59 |
00 | 00 | 00 | 01 | 01 | 10 | 10 | 10 | üksteist | üksteist | üksteist | üksteist |
Pange tähele, et numbrile, mis on väiksem või võrdne keskmisega (iga vektori jaoks), lisati jällegi 0 ja keskmisest suuremate numbrite korral 1.
Seda algoritmi korratakse rekursiivselt:
0 | 4 | 5 | viisteist | 18 | 32 | 31 | 47 | 48 | 60 | 64 | 59 |
000 | 001 | 001 | 010 | 011 | 100 | 100 | 101 | 110 | 111 | 111 | 111 |
0 | 4 | 5 | viisteist | 18 | 31 | 32 | 47 | 48 | 60 | 59 | 64 |
0000 | 0010 | 0011 | 0100 | 0110 | 1000 | 1001 | 1010 | 1100 | 1110 | 1110 | 1111 |
0 | 4 | 5 | viisteist | 18 | 31 | 32 | 47 | 48 | 59 | 60 | 64 |
00000 | 00100 | 00110 | 01000 | 01100 | 10 000 | 10010 | 10100 | 11000 | 11100 | 11101 | 11110 |
Sel hetkel on igal vektoril ainult üks element. Nii et iga järgneva sammu jaoks lisatakse 0, kuna arv on alati iseendaga võrdne (0 eelduseks on, et arv peab olema väiksem või võrdne keskmisega, mis on iseenesest).
Siiani on meil kvantimistase L = 5. Kui kavatseme kasutada L = 8, peame igale koodile lisama kolm 0-d. Iga koodi binaarsest täisarvuks teisendamise tulemus (L = 8 korral) oleks:
0 | 4 | 5 | viisteist | 18 | 31 | 32 | 47 | 48 | 59 | 60 | 64 |
0 | 32 | 48 | 64 | 96 | 128 | 144 | 160 | 192 | 224 | 232 | 240 |
Lõplik vektor sorteeritakse kasvavas järjekorras. Väljundvektori saamiseks peame sisendvektori algväärtuse asendama selle koodiga.
32 | 48 | 60 | 64 | 59 | 47 | 31 | viisteist | 4 | 0 | 5 | 18 |
144 | 192 | 232 | 240 | 224 | 160 | 128 | 64 | 32 | 0 | 48 | 96 |
Pange tähele, et saadud vektoris:
Antud pilt on nagu RGB pikslite vektor, kusjuures iga punkt on R jaoks 8, G ja 8 B jaoks; saame sellest välja tõmmata kolm vektorit, ühe igale värvile, ja rakendada algoritmi igale vektorile. Seejärel moodustame nendest kolmest väljundvektorist uuesti RGB-vektori ja meil on SMQT-ga töödeldud pilt, nagu on tehtud Bruini teatri omaga.
Algoritm nõuab, et iga kvantimise taseme (L) puhul tuleks esimese käiguga kontrollida N elementi, et leida iga vektori keskmine, ja seejärel teisel läbimisel tuleb kõiki neid N elemente võrrelda vastava keskmisega et iga vektor omakorda jagada. Lõpuks tuleb N elementi asendada nende koodidega. Seetõttu on algoritmi keerukus O ((L * 2 * N) + N) = O ((2 * L + 1) * N), mis on O (L * N).
Paberist eraldatud graafik on kooskõlas O (L * N) keerukusega. Mida madalam on L, seda kiiremini töödeldakse ligikaudu lineaarsel viisil (suurem kaadrite arv sekundis). Töötlemiskiiruse parandamiseks soovitab artikkel langetada L väärtusi: „L taseme madalam valimine võib olla otsene viis töötlemiskiiruse vähendamiseks, kuid vähendatud pildikvaliteediga”.
Siit leiame võimaluse kiirust parandada ilma pildikvaliteeti vähendamata. analüüsime teisendusprotsessi ja leiame kiirema algoritmi. Selle terviklikuma ülevaate saamiseks vaadake korduvate numbritega näidet:
16 | 25 | 31 | 31 | 25 | 16 | 7 | üks | üks | 7 |
16 | 16 | 7 | üks | üks | 7 | 25 | 31 | 31 | 25 |
0 | 0 | 0 | 0 | 0 | 0 | üks | üks | üks | üks |
7 | üks | üks | 7 | 16 | 16 | 25 | 25 | 31 | 31 |
00 | 00 | 00 | 00 | 01 | 01 | 10 | 10 | üksteist | üksteist |
üks | üks | 7 | 7 | 16 | 16 | 25 | 25 | 31 | 31 |
000 | 000 | 001 | 001 | 010 | 010 | 100 | 100 | 110 | 110 |
Vektoreid ei saa enam jagada ja nullid tuleb lisada, kuni jõuame soovitud L-ni.
Oletame lihtsuse huvides, et sisend võib minna vahemikku 0 kuni 31 ja väljund 0 kuni 7 (L = 3), nagu näites näha.
Pange tähele, et esimese vektori (16 + 25 + 31 + 31 + 25 + 16 + 7 + 1 + 1 + 7) / 10 = 16 keskmise arvutamine annab sama tulemuse kui kogu viimase vektori keskmise arvutamine, kuna see on lihtsalt sama vektor koos elementidega erinevas järjekorras: (1 + 1 + 7 + 7 + 16 + 16 + 25 + 25 + 31 + 31) / 10 = 16.
Teises etapis peame arvutama iga vektor rekursiivselt. Seega arvutame hallide sisendite keskmise, mis on esimesed 6 elementi ((16 + 16 + 7 + 1 + 1 + 7) / 6 = 8), ja tühja sisendi keskmise, mis on 4 viimast elementi ((25 + 31 + 31 + 25) / 4 = 28):
16 | 16 | 7 | üks | üks | 7 | 25 | 31 | 31 | 25 |
Pange veel kord tähele, et kui kasutame viimast vektorit, mis on täielikult järjestatud, on tulemused samad. Esimese kuue elemendi jaoks on meil: ((1 + 1 + 7 + 7 + 16 + 16) / 6 = 8) ja viimase 4 elemendi puhul: ((25 + 25 + 31 + 31) / 4 = 28) . Kuna see on vaid liitmine, pole suvede järjestus oluline.
üks | üks | 7 | 7 | 16 | 16 | 25 | 25 | 31 | 31 |
Sama kehtib järgmise etapi kohta:
7 | üks | üks | 7 | 16 | 16 | 25 | 25 | 31 | 31 |
Arvutused on samad mis viimase vektoriga: ((7 + 1 + 1 + 7) / 4 = 4) võrdub ((1 + 1 + 7 + 7) / 4 = 4).
Ja sama kehtib ka ülejäänud vektorite ja sammude kohta.
Keskmise arvutamine on lihtsalt intervalli elementide väärtuste summa, jagatuna intervalli elementide arvuga. See tähendab, et saame kõik need väärtused ette arvutada ja vältida selle arvutuse kordamist L korda.
Vaatame samme selle kohta, mida nimetame FastSMQT algoritmiks meie näites:
Looge 32 veeru ja 3 reaga tabel, nagu näete allpool. Tabeli esimene rida tähistab tabeli indeksit (0 kuni 31) ja seda pole vaja tabeli kodeerimisel lisada. Kuid see on selgesõnaliselt näidatud, et näidet oleks lihtsam järgida.
Kordage N elementi üks kord, loendades iga väärtuse sagedust. Meie näites on elementide 1, 7, 16, 25 ja 31 sagedus 2. Kõigil teistel elementidel on sagedus 0. Sellel etapil on O (N) keerukus.
Nüüd, kui sagedusvektor on täidetud, peame selle vektori kordama (sagedusvektor, mitte sisendvektor). Me ei korda enam N elementi, vaid R (R on vahemikus: 2 ^ L), mis on antud juhul 32 (0 kuni 31). Pikslite töötlemisel võib N olla mitu miljonit (megapikslit), kuid R on tavaliselt 256 (üks värv iga värvi jaoks). Meie näites on see 32. me täidame järgmised kaks tabelirida korraga. Neist esimestel (tabeli teisel) on seniste sageduste summa. Teisel (tabeli kolmandal) on kõigi seni ilmunud elementide väärtuse summa.
Meie näites paneme 1-ni jõudes tabeli teise rea 2, kuna seni on ilmunud kaks 1-d. Ja me panime ka tabeli kolmandasse ritta 2, sest 1 + 1 = 2. Jätkame nende kahe väärtuse kirjutamist igale järgmisele positsioonile, kuni jõuame 7-ni. Kuna number 7 ilmub kaks korda, lisame 2 teise rea akumulaator, sest nüüd ilmus siiani 4 numbrit (1, 1, 7, 7). Ja lisame kolmandale reale 7 + 7 = 14, mille tulemuseks on 2 + 14 = 16, sest 1 + 1 + 7 + 7 = 16. Jätkame selle algoritmiga, kuni lõpetame nende elementide kordamise. Selle etapi keerukus on O (2 ^ L), mis meie puhul on O (32) ja RGB pikslitega töötades on see O (256).
i | 0 | üks | 2 | ... | 6 | 7 | 8 | ... | viisteist | 16 | 17 | ... | 24 | 25 | 26 | ... | 30 | 31 |
n | 0 | 2 | 0 | ... | 0 | 2 | 0 | ... | 0 | 2 | 0 | ... | 0 | 2 | 0 | ... | 0 | 2 |
n-kumulatiivne | 0 | 2 | 2 | ... | 2 | 4 | 4 | ... | 4 | 6 | 6 | ... | 6 | 8 | 8 | ... | 8 | 10 |
summa | 0 | 2 | 2 | ... | 2 | 16 | 16 | ... | 16 | 48 | 48 | ... | 48 | 98 | 98 | ... | 98 | 160 |
Järgmine samm on eemaldada tabelist veerud elementidega, mille sagedus on 0, ja näite selgemaks nägemiseks eemaldame ka teise rea, kuna see pole enam asjakohane, nagu näete.
i | üks | 7 | 16 | 25 | 31 |
n | 2 | 4 | 6 | 8 | 10 |
summa | 2 | 16 | 48 | 98 | 160 |
Pidage meeles, et iga sammu arvutuste tegemiseks võiksime kasutada viimast (täielikult järjestatud) vektorit ja tulemused jäävad samaks. Pange tähele, et siin, selles tabelis, on meil viimane vektor koos kõigi juba tehtud eelarvutustega.
Põhimõtteliselt peame sellel väikesel vektoril tegema SMQT algoritmi, kuid erinevalt algse vektoriga, millest me alustasime, on see üks palju lihtsam.
Kõigepealt peame arvutama kõigi proovide keskmise. Leiame selle hõlpsalt järgmiselt: summa [31] / n [31] = 160/10 = 16. Nii et jagame tabeli kaheks vektoriks ja hakkame igaühele kirjutama binaarkoodi:
i | üks | 7 | 16 | 25 | 31 |
n | 2 | 4 | 6 | 8 | 10 |
summa | 2 | 16 | 48 | 98 | 160 |
kood | 0 | 0 | 0 | üks | üks |
Nüüd jagasime iga vektori uuesti. Esimese vektori keskmine on: summa [16] / n [16] = 48/6 = 8. Ja teise vektori keskmine on: (summa [31] - summa [16]) / (n [31] - n [16]) = (160 - 48) / (10 - 6) = 112/4 = 28.
i | üks | 7 | 16 | 25 | 31 |
n | 2 | 4 | 6 | 8 | 10 |
summa | 2 | 16 | 48 | 98 | 160 |
kood | 00 | 00 | 01 | 10 | üksteist |
Jagunemiseks on jäänud ainult üks vektor. Keskmine on: summa [7] / n [7] = 16/4 = 4.
i | üks | 7 | 16 | 25 | 31 |
n | 2 | 4 | 6 | 8 | 10 |
summa | 2 | 16 | 48 | 98 | 160 |
kood | 000 | 001 | 010 | 100 | 110 |
Nagu näete, on iga elemendi kood sama, kui oleksime järginud algset algoritmi. Ja me tegime SMQT-d palju väiksemal vektoril kui N-elemendiga vektoril ning oleme keskmise väärtuse leidmiseks ka kõik vajalikud väärtused eelnevalt välja arvutanud, nii et saadud vektori arvutamine on lihtne ja kiire.
Selle etapi keerukus on O (L * (2 ^ L)), mis L = 8 ja praktiliste pilditöötlusvajaduste korral on see O (8 * (256)) = O (2048) = konstant.
Viimane samm on N elemendi kordamine, asendades elemendi uuesti iga elemendi koodiga: element [i] = kood [i]. Keerukus on O (N). FastSMQT keerukus on O (N) + O (2 ^ L) + O (L * (2 ^ L)) + O (N) = O (2 * N) + O ((L + 1) * (2 ^ L)) = O (N + L * (2 ^ L)).
Mõlemat algoritmi saab paralleelselt muuta, kuigi tõhusam on ühe algoritmi käitamine südamiku kohta, kui teisendada tuleb mitu vektorit. Näiteks piltide töötlemisel saame iga kolme arvutuse paralleelimise asemel töödelda iga RGB kanalit erinevas tuumas.
SMQT algoritmi paralleelseks muutmiseks võime vektorit kaheks jagades töödelda iga alavektorit uues südamikus, kui tuum on saadaval (tegelikult üks alavektor praeguses tuumas ja teine uues tuumas). Seda saab teha rekursiivselt, kuni jõuame saadaolevate südamike koguarvuni. Väärtuste asendamist koodidega saab teha ka paralleelselt väärtusega.
FastSMQT algoritmi saab ka paralleelselt muuta. Esimeses etapis tuleb sisendvektor jagada olemasolevate südamike arvuks (C). Kõigi nende osade jaoks tuleb luua üks sagedusloenduse vektor ja see tuleb täita paralleelselt. Seejärel lisame nende vektorite väärtused ühte saadud sageduste vektorisse. Järgmine samm, mida saab paralleelselt muuta, on viimane, kui asendame väärtused nende koodidega. Seda saab teha paralleelselt.
Algse SMQT algoritmi kogu keerukus on O ((2 * L + 1) * N), mis on O (L * N).
FastSMQT keerukus on O (N) + O (2 ^ L) + O (L * (2 ^ L)) + O (N) = O (2 * N) + O ((L + 1) * (2 ^ L)) = O (N + L * (2 ^ L)).
Fikseeritud L korral saab keerukus mõlema jaoks lihtsalt O (N). Kuid konstant, mis korrutab N, on FastSMQT jaoks palju väiksem ja see muudab töötlemisaja suureks erinevuseks, nagu näeme.
Järgmisel graafikul võrreldakse nii SMQT kui ka FastSMQT algoritmide jõudlust L = 8 korral, mis on piltide töötlemise puhul. Graafik näitab aega (millisekundites), mis kulub N elemendi töötlemiseks. Pange tähele, et L = 8 jaoks on SMQT keerukus (koos konstantidega) O (17 * N), samas kui FastSMQT puhul on O (2 * N + 2304).
Mida suurem on N, seda kauem kulub pildi töötlemiseks SMQT-l. Seevastu FastSMQT puhul on kasv palju aeglasem.
Veel suuremate N väärtuste korral on jõudluse erinevus palju ilmsem:
Siin võtab SMQT teatud piltide töötlemiseks kuni mitu sekundit aega, samal ajal kui FastSMQT jääb endiselt „mõne millisekundi“ tsooni.
Isegi mitme südamikuga (antud juhul kaks) on FastSMQT selgelt parem kui lihtsalt SMQT:
FastSMQT ei sõltu L. SMQT-st, seevastu sõltub lineaarselt L. väärtusest. Näiteks kui N = 67108864, suureneb SMQT tööaeg L väärtuse suurenemisega, samas kui FastSMQT mitte:
Kõik optimeerimisvõtted pole kõigile rakendatavad algoritmid ja algoritmi jõudluse parandamise võti on sageli peidetud algoritmi toimimise kohta. See FastSMQT algoritm kasutab ära väärtuste eelarvutamise võimalusi ja keskmiste arvutuste olemust. Seetõttu võimaldab see kiiremat töötlemist kui algne SMQT. L suurenemine ei mõjuta kiirust, kuigi L ei saa olla palju suurem kui tavaline 8, kuna mälukasutus on 2 ^ L, mis 8 puhul on vastuvõetav 256 (veergude arv meie sagedustabelis), kuid jõudluse kasv on ilmselge praktiline kasu.
See kiiruse paranemine võimaldas MiddleMatteril algoritmi rakendada iOS-i rakendus (ja Androidi rakendus ), mis parandab hämaras valguses pildistatud pilte ja videoid. Selle täiustuse abil oli võimalik videotöötlus läbi viia rakenduses, mis oleks muidu liiga aeglane iOS seadmeid.
SMQT ja FastSMQT algoritmide C ++ kood on saadaval GitHubis .