JAI este un API care face parte din grupul Java Media API si este clasificat ca fiind o extensie standard a platformei Java. JAI furnizeaza functionalitati utile în procesarea de imagini dincolo de posibilitatile oferite de Java Foundation Classes (setul de clase de baza din Java), fiind totodata compatibil cu acesta.
JAI ofera mai multe avantaje pentru dezvoltatorii de aplicatii, în comparatie cu alte solutii de prelucrare a imaginilor. Cele mai importante dintre acestea vor fi expuse în cele ce urmeaza.
Prelucrare de imagini independenta de platforma. Majoritatea API-urilor specializate pe prelucrarea de imagini sunt destinate pentru un singur sistem de operare, însa JAI urmareste un model bazat pe biblioteci Java, ceea ce îi confera independenta de platforma. Implementari ale aplicatiilor JAI vor putea sa ruleze pe orice calculator cu Java VM. Acest lucru face ca JAI sa fie cu adevarat un API de prelucrari de imagini independent de platforma si care furnizeaza o interfata standard la posibilitatiile de prelucrari de imagini ale unei platforme. Asta înseamna ca se poate scrie o aplicatie o singura data si se va putea rula oriunde (conform sintagmei "Write once, run anywhere").
Prelucrare de imagini distribuita. JAI este de asemenea foarte potrivit pentru prelucrari de imagini în aplicatii client-server prin mijloacele oferite de arhitectura de retea a platformei Java si prin tehnologiile de executie la distanta. Executia la distanta se bazeaza pe Java RMI (Remote Method Invocation). Java RMI permite ca codul Java de la client sa invoce metode din obiecte care se afla pe un alt calculator fara a muta aceste obiecte la client.
API orientat pe obiecte. Ca si Java, JAI este total orientat pe obiecte. În JAI, imaginile si operatiile de procesare de imagini sunt definite ca obiecte. JAI unifica notiunea de imagine si operator prin faptul ca amândoua sunt subclase ale aceluiasi parinte.
Un obiect operator este instantiat cu una sau mai multe imagini sursa si alti parametri. Acest obiect operator poate apoi deveni o imagine sursa pentru urmatorul obiect operator. Conexiunea dintre obiecte defineste un flux de date procesate. Graful editabil rezultat de operatii de procesare de imagini poate fi definit si instantiat dupa necesitati.
Flexibil si extensibil. Orice API pentru procesari de imagini trebuie sa suporte cel putin câteva tehnologii de baza, cum ar fi achizitia si afisarea imaginilor, manipulari de baza, îmbunatatiri de imagini, manipulari geometrice si analiza de imagini. JAI furnizeaza un set de baza de operatori necesari pentru a sustine tehnologiile de procesari de imagini. Acesti operatori suporta multe dintre functiile necesare într-o aplicatie de procesare de imagini. Deoarece anumite aplicatii necesita operatii speciale de procesari de imagini care sunt foarte rar folosite, pentru aceste aplicatii, JAI furnizeaza o infrastructura care permite ca solutii particulare sa poata fi adaugate la nucleul JAI API.
JAI detine de asemenea un set de standarde de metode de compresie si decopresie de imagini. Setul de baza se bazeaza pe standarde internationale pentru cele mai comune tipuri de fisiere comprimate. De asemenea JAI suporta adaugarea de codoare si decodoare mai speciale, care sunt folosite de aplicatii.
Independenta de dispozitivul de reprezentare. Procesarea de imagini poate fi specificata de coordonate independente de dispozitiv. Modul de reprezentare JAI trateaza toate imaginile sursa ca fiind independente de afisare. Se poate obtine astfel un graf (sau lant) de operatii de vizualizare fara a ne preocupa de rezolutia sau dimensiunile imaginii sursa, JAI ocupându-se de toate aceste aspecte.
Puternic si performant. JAI suporta formate de imagine complexe, incluzând imagini cu pâna la trei dimensiuni si un numar arbitrar de benzi, precum si multi algoritmi de procesare de imagini si operatori de procesare.
Pentru a spori performanta în procesarea imaginilor, pentru anumite platforme (actual pentru Solaris si Windows), JAI este implementat cu cod nativ, incluzând implementari ultra-optimizate care se bazeaza pe accelerare hardware si posibilitatile multimedia ale platformei, cum ar fi instructiuni MMX pentru procesoarele Intel si VIS pentru UltraSparc.
Interoperabilitate. JAI este integrat cu celelalte API-uri Java Media, fiind posibil astfel dezvoltarea de aplicatii multimedia foarte avansate pe platforma Java. JAI functioneaza bine cu celelalte API-uri, cum ar fi Java 3D sau tehnologiile bazate pe componente JavaBeans.
JAI API este construit pe fundatia Jav 333g69d a 2D API pentru a permite aplicatii mai puternice si generale de procesari de imagini. JAI API adauga urmatoarele concepte:
Imagini multisegmentate
Executie amânata
Procesare de imagini în retea
Gestiunea proprietatilor imaginilor
Operatori imagine cu surse multiple
Date imagine tridimensionale
Combinatia dintre segmentare si executie amânata permite optimizari de rulare cu un model destul de simplu. Noi operatori pot fi adaugati si noi operatori pot participa ca obiecte de prima clasa în modelul de executie amânata.
JAI API de asemenea furnizeaza un grad destul de mare de compatibilitate cu modelele de procesare Java AWT si Java 2D. Operatorii JAI pot sa lucreze direct cu obiectele Java 2D BufferedImage sau cu orice alte obiecte imagine care implementeaza interfata RenderedImage. JAI suporta acelasi model independent de reprezentare ca si Java 2D API, folosind coordonate independente de dispozitiv. JAI suporta de asemenea stilul de desenare de la Java 2D pe amândoua tipurile de imagini, Rendered si Renderable folosind interfata Graphics.
JAI API nu utilizeaza interfetele de producator/consumator de imagine care au fost introduse în Java AWT si folosite mai apoi în Java 2D. În schimb, JAI API necesita ca imaginile sursa sa participe la modelul de procesare de tragere prin raspunsul la cereri pentru arii arbitrare, facând imposibil de instantiat un ImageProducer direct ca sursa. Este posibil, totusi, sa instantiem un ImageProducer ceea ce face ca datele imagine JAI API sa fie disponibile la aplicatii AWT mai vechi.
Similitudinile JAI cu Java 2D API sunt evidente, deoarece este dependent de abstractizarile definite în Java 2D API. În general, întregul mecanism pentru tratarea imaginilor de tip Renderable si Rendered, esantioanele pixelilor si stocarea datelor este sustinuta si de JAI. Principalele puncte comune ale celor doua API (JAI si Java 2D) sunt:
Interfetele RenderableImage si RenderedImage definite în Java 2D API sunt folosite ca baza pentru abstractizari de nivel înalt. JAI permite crearea si manipularea grafurilor directionale aciclice de obiecte implementând aceste interfete.
Obiectul principal de date, TiledImage, implementeaza interfata WritableRenderedImage si poate contine o grila regulata de segmente de obiecte Raster. Spre deosebire de BufferedImage din Java 2D API, TiledImage nu necesita sa fie prezent un obiect ColorModel pentru interpretare fotometrica a imaginii sale.
Obiectele operator din JAI sunt mult mai sofisticate decât în Java 2D API. Obiectul operator fundamental, OpImage, furnizeaza suport foarte bogat pentru extensibilitate spre noi operatori dincolo de posibilitatile Java 2D API. JAI are un mecanism bazat pe registri care automatizeaza selectia de operatii asupra unei RenderedImage.
Obiectele SampleModel, DataBuffer si Raster din Java 2D API sunt portate în JAI fara nici o modificare, cu exceptia ca se permite folosirea tipurilor double si float ca tipuri fundamentale de date a unui DataBuffer în adaos la tipurile existente, byte, short si int.
Clasele de date JAI. JAI introduce doua noi tipuri de clase de date care extind clasa de date DataBuffer din Java 2D. În Tabelul 7.6. sunt prezentate aceste clase.
Clasa |
Descriere |
DataBufferFloat |
Stocheaza date intern sub forma de float. |
DataBufferDouble |
Stocheaza date intern sub forma de double. |
Tabelul 7.6. Clasele utilizate pentru datele imagine din JAI
O operatie de procesare de imagini în JAI poate fi descrisa pe scurt în urmatorii patru pasi (JAI Programming, 1999):
Obtinerea unei s-au unor imagini sursa. Imaginile pot fi obtinute prin trei modalitati:
Încarcarea dintr-un fisier imagine cum ar fi GIF, TIFF, JPEG, etc.
Obtinerea unei imagini de la o alta sursa de date, cum ar fi un server distant.
Generarea unei imagini intern.
Definirea unui graf de prelucrari de imagini. Acest lucru este un proces în doua etape:
Definirea operatorilor imagine.
Definirea unei relatii parinte/copil între surse si destinatii.
Evaluarea grafului folosind unul din urmatoarele trei modele de executie:
Modelul de executie Rendered (modul imediat).
Modelul de executie Renderable (modul amânat).
Modelul de executie la distenta (modul distant).
Procesarea rezultatului. Aici avem patru posibilitati:
Salvarea imaginii într-un fisier
Afisarea imaginii pe ecran
Tiparirea imaginii la imprimanta sau la alt dispozitiv
Trimiterea imaginii la un alt API, de exemplu Swing.
În JAI orice operatie este definita ca obiect. Un obiect operator este instantiat cu nici una sau cu mai multe imagini sursa si alti parametri care definesc operatia. Doi sau mai multi operatori pot fi cuplati, astfel încât primul operator devine imaginea sursa pentru urmatorul operator. Prin legarea unui operator de un altul, se creeaza un graf de procesare de imagini sau lant.
În forma cea mai simpla, un graf de procesare de imagini este un lant de obiecte operator cu una sau mai multe imagini sursa la un capat si o imagine destinatie la celalalt. Graful astfel creat este cunoscut sub denumirea de graf aciclic directionat (DAG - directed acyclic graph), unde fiecare obiect este un nod în graf, iar referintele la obiecte formeaza ramuri. (Figura 7.5.)
Structura unui graf poate fi considerata ca fiind produsa de operatii, iar forma grafului este folositoare la vizualizarea acestor operatii. Un DAG este un graf care nu contine cicluri. Acest lucru înseamna ca pe o ruta de la un nod A spre un nod B nu mai exista o cale de întoarcere.
JAI extinde independenta de reprezentare, care a fost introdusa în Java 2D API. Cu ajutorul independentei de reprezentare se poate descrie o imagine asa cum dorim noi sa apara, independent de orice instanta specifica. JAI prezinta un mod Renderable prin felul în care trateaza toate sursele imagine ca si independente de reprezentare. Se poate construi un graf (sau lant) de operatii Renderable fara a ne interesa rezolutia sau dimensiunile imaginii sursa, JAI ocupându-se de toate aceste detalii.
JAI a introdus doua tipuri de grafuri: Rendered si Renderable.
Grafuri Rendered. Grafurile Rendered sunt forma cea mai simpla de reprezentare în JAI. Grafurile Renderable au avantajul independentei de reprezentare, eliminând nevoia de a lucra direct cu pixeli, în schimb grafurile Rendered sunt utile atunci când este necesar sa lucram direct cu pixeli. Un graf Rendered proceseaza imaginea în modul imediat. Pentru orice nod din graf, imaginea sursa este considerata ca o evaluare la momentul în care este instantiata si adaugata la graf, sau ca o noua operatie adaugata la lant.
Un graf Rendered este compus din noduri de obiecte Rendered. Aceste noduri sunt de obicei instante ale clasei RenderedOp, dar poate fi si o subclasa a clasei PlanarImage, care este versiunea JAI a clasei RenderedImage.
Imaginile sursa sunt obiecte care implementeaza interfata RenderedImage. Aceste surse sunt specificate ca parametri în constructia unor noi obiecte imagine. Fragmentul de cod urmator (Exemplul 7.10.) exemplifica construirea unui lant Rendered.
Exemplul 7.10. Exemplu de lant Rendered |
import javax.jai.*; import javax.jai.widget.*; import java.awt.Frame; public class AddExample extends Frame |
Primele trei linii din exemplu specifica pachetele si clasele care se importa. Clasele care încep cu javax.jai sunt clasele JAI. Acest exemplu defineste un obiect de tip cadru (Frame) care contine un obiect pentru afisarea imaginilor (ScrollingImagePanel). Pentru simplicitatea prezentarii, toate actiunile executate de aceasta clasa sunt definite în constructor. Acest constructor primeste doua obiecte de tip ParameterBlock, utilizate în constructia a doua imagini de tip constant, care vor fi adunate împreuna pentru a crea imaginea destinatie. În cele din urma se afiseaza imaginea destinatie într-o fereastra cu bare de defilare si apoi este atasata la cadrul principal.
Din momentul în care pixelii încep sa "curga", graful acestei procesari v-a arata ca în Figura 7.6., întregul proces fiind condus de catre componenta de afisare. Imaginile sursa nu sunt încarcate si nici un pixel nu este produs pâna când componenta de afisare le cere.
Grafuri Renderable. Grafurile Renderable nu sunt evaluate la momentul în care se specifica. Evaluarea este amânata pâna când apare o cerere specifica pentru o reprezentare. Acest proces este cunoscut sub numele de executie amânata (deferred execution).
Într-un graf Renderable, daca o imagine sursa trebuie sa se schimbe înainte sa fie o cerere pentru reprezentare, schimbarea va fi reflectata la iesire. Acest proces este posibil datorita modelului de "tragere", unde procesul care cere imaginea, trage imaginea prin lant, lucru care este în opozitie cu modelul de împingere din procesarile AWT.
Un graf Renderable este alcatuit din obiecte noduri care implementeaza interfata RenderableImage, dar de obicei se foloseste obiecte de tipul RenderableOp. Pe masura ce se construieste graful Renderable, sursele din fiecare nod sunt specificate prin topologia grafului. Sursa unui graf Renderable este un obiect de tip RenderableImage.
Fragmentul de cod urmator (Exemplul 7.11.) reprezinta un exemplu de graf Renderable. Acest exemplu citeste un fisier în format TIFF, inverseaza valorile pixelilor si aduna o valoare constanta la pixeli.
Exemplul 7.11. Exemplu de graf Renderable |
// Preia o sursa Rendered din sursa fisier TIFF // obiectul ParameterBlock `pb0' contine numele // sursei (fisier, URL, etc.). Obiectele `hints0', // `hints1' si `hints2' contin indiciile de reprezentare si se // presupune ca sunt create în afara acestui fragment de cod. RenderedOp sourceImg = JAI.create("TIFF", pb0); // Se obtine obiectul RenderableImage // din sursa RenderedImage. ParameterBlock pb = new ParameterBlock(); pb.addSource(sourceImg); pb.add(null).add(null).add(null).add(null).add(null); // creeaza o operatie Renderable. RenderableImage ren = JAI.createRenderable("renderable", pb); // creeaza blocul de parametrii pentru primul operand. ParameterBlock pb1 = new ParameterBlock(); pb1.addSource(ren); // Creeaza primul operand din lantul Renderable ca // operatie de inversare. RenderableOp Op1 = JAI.createRenderable("invert", pb1); // creeaza blocul de parametrii pentru cel de-al doilea operand. // constanta cu care se aduna este "2". ParameterBlock pb2 = new ParameterBlock(); pb2.addSource(Op1); // Op1 ca si sursa pb2.add(2.0f); // 2.0f ca si constanta // Creeaza al doilea operand ca si operatie de adunare // cu o constanta. RenderableOp Op2 = JAI.createRenderable("addconst", pb2); // Creeaza contextul de reprezentare. AffineTransform screenResolution = ...; RenderContext rc = new RenderContext(screenResolution); // creeaza o reprezentare. RenderedImage rndImg1 = Op2.createRendering(rc); // Afiseaza reprezentarea folosind screenResolution. imagePanel1 = new ScrollingImagePanel(rndImg1, 100, 100); |
Graful asociat lantului Renderable este prezentat în Figura 7.7.
Graful Renderable poate fi gândit ca si un model care, atunci când este reprezentat, provoaca instantierea unui graf Rendered paralel pentru îndeplinirea procesarilor actuale. În graful din exemplul de mai sus se produc urmatoarele:
Atunci când este apelata metoda Op2.createRendering, aceasta apeleaza recursiv metoda Op1.createRendering cu argumentul RenderContext rc.
Operatia Op1 apeleaza apoi metoda sourceImg.getImage, cu rc ca argument. Obiectul sourceImg creeaza un nou obiect RenderedImage pentru pastrarea pixelilor sursa la rezolutia necesara si îi introduce în lant. Acesta returneaza la Op1 o referinta spre acest obiect.
Op1 foloseste apoi OperationRegistry ca sa gaseasca un obiect de tipul ContextualRenderedImageFactory (CRIF) care poate sa execute o operatie "invert". Obiectul rezultat RenderedOp returnat de catre CRIF este introdus în lant cu referinta returnata de sourceImg ca si sursa.
Referinta catre obiectul "invert" de tip RenderedImage este returnat la Op2, care repeta procesul, creând un obiect "addconst" de tip RenderedOp, introducându-l în lant si returnând o referinta catre rndImg1.
La sfârsit, obiectul rndImg1 este utilizat la apelul lui ScrollingImagePanel sa afiseze rezultatul pe ecran.
Dupa crearea obiectului ScrollingImagePanel, lanturile Renderable si Rendered vor arata ca si în Figura 7.8.
În acest moment în lant, nu se proceseaza nici un pixel. Doar atunci când obiectul ScrollingImagePanel necesita sa afiseze pixeli pe ecran se creeaza obiectele OpImage si pixelii sunt trasi prin lantul Rendered, lucru executat în ultima linie de cod a exemplului:
imagePanel1 = new ScrollingImagePanel(rndImg1, 100, 100);
Reutilizarea grafurilor. De cele mai multe ori, este mai potrivit sa modificam un graf existent si sa-l reutilizam, decât sa cream un graf aproape identic. Amândoua tipurile de grafuri, Rendered si Renderable, sunt editabile cu anumite restrictii.
Initial, un nod dintr-un graf Rendered este modificabil, în sensul ca pot fi atribuite noi surse, care sunt considerate ca vor fi evaluate în momentul când sunt atribuite, iar valorile parametrilor lor pot fi modificati. Cu toate acestea, odata ce reprezentarea are loc, nodul va fi "înghetat" iar sursele si parametrii lui nu pot fi schimbati.
Un lant de noduri Rendered poate fi clonat fara ca nodurile lui sa fie înghetate cu ajutorul metodei RenderedOp.createInstance. Folosind aceasta metoda, un graf Rendered poate fi configurat si reutilizat dupa dorinta si de asemenea serializat si transmis prin retea.
Clasa RenderedOp furnizeaza câteva metode pentru reconfigurarea unui nod Rendered. Metodele setParameter pot fi folosite pentru modificarea parametrilor din noduri. Metoda setOperationName poate fi utilizata la schimbarea numelui operatiei. Metoda setParameterBlock poate fi folosita la schimbarea obiectului de tip ParameterBlock din noduri.
Deoarece grafurile Renderable nu sunt evaluate pâna când exista o cerere specifica pentru reprezentare, nodurile pot fi editate în orice moment. Singura problema în editarea grafurilor Renderable este introducerea de cicluri, care trebuie evitata.
Clasa RenderableOp furnizeaza câteva metode pentru reconfigurarea unui nod Renderable. Metodele setParameter pot fi folosite pentru modificarea parametrilor nodurilor. Metoda setParameterBlock poate fi folosita la schimbarea obiectelor PrameterBlock din noduri. Metoda setProperty poate fi folosita la modificarea proprietatii locale a unui nod. Metoda setSource poate fi folosita la modificarea sursei pentru un nod.
JAI este compus din mai multe clase grupate în cinci pachete:
javax.media.jai - contine interfetele si clasele de baza.
jaxax.media.jai.iterator - contine clase si interfete iterator, care sunt utile pentru scrierea operatiilor extensie.
javax.media.jai.operator - contine clase care descriu toti operatorii de procesare a imaginilor.
javax.media.jai.widget - contine interfete si clase pentru crearea de suprafete si ferestre cu bare de derulare simple pentru afisarea imaginilor.
com.sun.media.jai.codec - contine clase si interfete utile în decodarea si codarea fisierelor imagine.
Clasa JAI. Clasa JAI nu poate fi instantiata, ea este formata dintr-o colectie de metode statice care asigura o sintaxa simpla pentru crearea de grafuri de tip Renderable si Rendered. Majoritatea metodelor din aceasta clasa sunt folosite pentru a crea un RenderedImage, luând ca argumente un ParameterBlock si un RenderingHints. Exista o singura metoda pentru a crea un RenderableImage, cu argumente de tip ParameterBlock si RenderingHints.
Aceasta clasa detine câteva variante de metode create, toate acestea preiau surse si parametrii în mod direct si construiesc un obiect ParameterBlock automat.
Clasa PlanarImage. Clasa PlanarImage este clasa principala pentru descrierea imaginilor bidimensionale în JAI. Aceasta clasa implementeaza interfata RenderedImage din Java 2D API, TiledImage si OpImage.
Interfata RenderedImage descrie o imagine segmentata, disponibila doar pentru citire cu un model al pixelului descris de tipurile SampledModel si DataBuffer. Fiecare segment este o suprafata dreptunghiulara de dimensiune identica, asezata pe o grila regulata. Toate segmentele se folosesc de un acelasi obiect SampleModel. În plus, fata de posibilitatile oferite RenderedImage, PlanarImage mentine conexiunile dintre sursa si destinatie, dintre nodurile grafurilor Rendered. Deoarece nodurile grafului sunt conectate bidirectional, garbage-collector-ul necesita un mic ajutor pentru a detecta atunci când o portiune din graf nu mai este referentiata de codul utilizator si poate fi eliberata. Clasa PlanarImage se ocupa de acest lucru prin folosirea referintelor slabe (Weak References API) din Java 2.
Orice obiecte RenderedImage din exteriorul API sunt "acoperite" pentru a produce o instanta de PlanarImage. Acest lucru permite ca API sa utilizeze functionalitatile suplimentare din PlanarImage pentru toate imaginile.
Clasa CollectionImage. CollectionImage este o superclasa abstracta pentru patru clase si reprezinta colectii de obiecte PlanarImage:
ImageStack - reprezinta imagini bidimensionale care sunt asezate într-un spatiu tridimensional. Imaginile necesita sa fie asezate paralel una cu cealalta.
ImageSequence - reprezinta o secventa de imagini cu marcajul de timp asociat si pozitiile camerei. Este folosita pentru a reprezenta imagini video sau fotografii dependente de timp.
ImagePyramid - reprezinta o serie de imagini de rezolutie progresiv mai mica, fiecare derivata din ultima prin mijloacele unui operator de procesare.
ImageMIPMap - reprezinta o stiva de imagini cu o relatie operationala fixa între partile adiacente.
Clasa TiledImage. Clasa TiledImage reprezinta imagini continând mai multe segmente aranjate într-o grila. Segmentele formeaza o grila regulata, care poate sa ocupe orice regiune dreptunghiulara din plan.
Clasa implementeaza interfata WritableRenderedImage din Java 2D API, de asemenea extinde clasa PlanarImage. Clasa permite ca segmentele sa poata fi validate pentru scriere, dupa ce datele pixelului vor fi accesate direct. De asemenea aceasta are o metoda createGraphics care permite ca continutul sa fie modificat utilizând apeluri de desenare din Java 2D API.
Aceasta clasa contine initial o grila care este goala, pe masura ce fiecare segment este solicitat, se initializeaza cu date dintr-o sursa PlanarImage. Dupa ce un segment este initializat, continutul sau poate fi modificat. O regiune de interes (ROI) arbitrara poate fi umpluta cu date copiate dintr-o sursa PlanarImage.
Clasa contine o metoda care permite desenarea unui obiect Graphics2D într-un obiect TiledImage. Acest lucru este util la adaugarea de text, linii, sau a altor obiecte grafice simple la o imagine.
Clasa OpImage. OpImage este clasa parinte pentru toate operatiile de procesare a imaginilor, cum ar fi:
AreaOpImage - pentru operatori imagine care necesita doar o regiune dreptunghiulara fixa în jurul pixelului sursa pentru a calcula fiecare pixel destinatie.
PointOpImage - pentru operatori imagine care necesita doar un singur pixel sursa pentru a calcula fiecare pixel destinatie.
SourcelessOpImage - pentru operatori imagine care nu au imagine sursa.
StatisticsOpImage - pentru operatori imagine care calculeaza statistica pe o regiune data dintr-o imagine, cu o frecventa de esantionare data.
UntiledOpImage - pentru operatii uni-sursa în care valorile tuturor pixelilor din imaginea sursa contribuie la valoarea fiecarui pixel din imaginea destinatie.
WrapOpImage - pentru operatori imagine care executa o acoperire de imagine.
ScaleOpImage - pentru operatori extensie care executa scalare si necesita mapare regresiva rectilinie si adaugare prin dimensiunile filtrului de reesantionare.
Clasa OpImage este capabila sa determine care arii sursa sunt suficiente pentru calcularea unei arii date din destinatie prin mijloace furnizate de utilizator si anume metoda mapDestRect. Pentru majoritatea operatiilor, aceasta metoda este furnizata de o subclasa standard a clasei OpImage, cum ar fi PointOpImage sau AreaOpImage.
Clasa RenderableOp. Aceasta clasa aduce o reprezentare usoara (lightweight) a unei operatii in spatiul Renderable. Obiectele de tipul RenderableOp sunt create în mod normal folosind metoda createRenderable din clasa JAI si poate fi modificata dupa dorinta. De asemenea, clasa implementeaza interfata RenderableImage, asa ca poate fi interogata pentru dimensiunile sale independente de reprezentare.
Atunci când un obiect RenderableOp trebuie sa fie reprezentat, acesta utilizeaza un obiect OperationRegistry pentru localizarea unui obiect de tipul ContextualRenderedImageFactory pentru a executa conversia din spatiul Renderable în RenderedImage.
Clasa RenderedOp. Este un obiect lightweight similar cu RebderableOp care stocheaza un nume de operatie, ParameterBlock si RenderingHints si poate fi adaugat într-un graf Rendered. Exista doua metode pentru producerea si reprezentarea unui RenderedOp:
Implicita - orice apel la o metoda RenderedImage din RenderedOp provoaca crearea reprezentarii. Aceasta reprezentare este în mod obisnuit compusa dintr-un lant de obiecte OpImage cu o geometrie similara cu cea a lantului RenderedOp.
Explicita - un apel la metoda createInstance cloneaza obiectul RenderedOp si sursa lui, rezultând un nou lant Rendered cu aceleasi surse non-RenderedOp (de exemplu, obiecte TiledImage) ca si lantul original.
Obiectele RenderedOp care nu au fost reprezentate pot avea sursele si parametrii modificati. Sursele sunt considerate evaluate din momentul în care sunt conectate la un obiect RenderedOp.
JAI API specifica un set de baza de operatori de procesare de imagini. Categoriile generale de operatori de procesare de imagini suportati includ:
Operatori punctuali
Operatori de arie
Operatori geometrici
Operatori de cuantizare a culorii
Operatori de fisier
Operatori în frecventa
Operatori statistici
Operatori de extragere a muchiilor
Alti operatori
De asemenea JAI API suporta abstractizari pentru mai multe tipuri de colectii de imagini, cum ar fi imagini dependente de timp si piramide de imagini. Acestea au ca scop simplificarea operatiilor asupra colectiilor de imagini si permite dezvoltarea de operatori care lucreaza direct cu aceste abstractizari.
Operatori punctuali. Operatorii punctuali permit modificarea modului în care datele ocupa un domeniu disponibil de nivele de gri. Operatorii punctuali transforma imaginea de intrare într-o imagine de iesire în asa fel încât fiecare pixel de iesire depinde doar de pixelul corespunzator de la intrare. Operatorii punctuali nu modifica relatia spatiala dintr-o imagine.
Câteva exemple de operatori punctuali sunt descrise în Tabelul 7.7.
Operator |
Descriere |
Add |
Preia doua imagini sursa Rendered sau Renderable si aduna fiecare pereche de pixeli, câte un pixel de la fiecare imagine sursa de la pozitia si banda corespunzatoare. |
BandCombine |
Preia o imagine sursa Rendered sau Renderable si calculeaza un set de combinatii liniare arbitrare asupra benzilor folosind o matrice specifica. |
Composite |
Preia doua imagini sursa Rendered sau Renderable si combina cele doua imagini bazându-se pe valorile alfa a fiecarui pixel. |
Invert |
Preia o imagine sursa Rendered sau Renderable si inverseaza valorile pixelilor. |
Tabelul 7.7. Exemple de operatori punctuali din JAI
Operatori de arie. Operatorii de arie executa transformari geometrice care are ca rezultat repozitionarea pixelilor dintr-o imagine. Pixelii sunt repozitionati de la coordonatele spatiale initiale x si y din imaginea de intrare la noi coordonate în imaginea de iesire folosind transformari matematice.
Exista doua tipuri de operatii de arie de baza: liniare si neliniare. Operatiile liniare sunt translatia, rotatia si scalarea. Operatiile neliniare, cunoscute si sub numele de transformari de acoperire, sunt de curbare si de deformare. Câteva dintre aceste operatii sunt prezentate în Tabelul 7.8.
Operator |
Descriere |
Border |
Preia o imagine sursa Rendered si îi adauga o bordura în jurul ei. |
BoxFilter |
Preia o imagine sursa Rendered si determina intensitatea unui pixel din imagine prin medierea pixelilor sursa din interiorul unei arii dreptunghiulare din jurul acelui pixel. |
Convolve |
Preia o imagine sursa Rendered si executa o operatie spatiala care calculeaza fiecare esantion de iesire prin înmultirea elementelor unui nucleu cu esantioanele înconjuratoare unui esantion particular. |
Crop |
Preia o imagine sursa Rendered sau Renderable si ajusteaza imaginea la o arie specificata. |
MedianFilter |
Preia o imagine sursa Rendered si o trece printr-un filtru neliniar care este util pentru eliminarea linilor sau pixelilor izolati, pastrând în mare înfatisarea imaginii. |
Tabelul 7.8. Operatori de arie
Operatori geometrici. Operatorii geometrici permit modificarea orientarii, marimii si formei unei imagini. Câteva exemple de astfel de operatori JAI se gasesc în Tabelul 7.9.
Operator |
Descriere |
Affine |
Preia o sursa Rendered sau Renderable si executa o alocare affine |
Rotate |
Preia o imagine sursa Rendered sau Renderable si o roteste în jurul unui punct dat cu un unghi dat, specificat în radiani. |
Scale |
Preia o imagine sursa Rendered sau Renderable si translateaza si redimensioneaza imaginea. |
Shear |
Preia o imagine sursa Rendered sau Renderable si executa o "întindere" (shearing) spatiala pe orizontala si verticala |
Translate |
Preia o imagine sursa Rendered sau Renderable si copiaza imaginea la o noua locatie din plan. |
Transpose |
Preia o imagine sursa Rendered sau Renderable si o întoarce sau o roteste. |
Wrap |
Preia o imagine sursa Rendered sau Renderable si executa o operatie de întindere generala a imaginii |
Tabelul 7.9. Operatori geometrici
Operatori de cuantizare a culorii. Cuantizarea culorii este des folosita pentru a micsora conturarea amplitudinii pentru cadre bufer cu mai putin de 8 biti adâncime sau pentru cadre bufer de culoare cu mai putin de 24 biti adâncime. Operatori de cuantizare a culorii sunt prezentati în Tabelul 7.10.
Operator |
Descriere |
ErrorDiffusion |
Preia o sursa Rendered sau Renderable si executa cuantizarea culorii prin cautarea celei mai apropiate culori pentru fiecare pixel dintr-o paleta de culori data si difuzarea erorii de cuantizare a culorii în partea de jos si dreapta a pixelului |
OrderedDither |
Preia o sursa Rendered sau Renderable si executa cuantizarea culorii prin cautarea celei mai apropiate culori pentru fiecare pixel dintr-un cub de culori dat si translatarea valorii indexului rezultat cu o cantitate pseudoaleatoare determinata de valorile date de masca dither |
Tabelul 7.10. Operatorii de cuantizare a culorii
Operatori de fisier. Operatorii de fisier sunt utilizati pentru a citi si a scrie fisiere imagine. Câteva exemple de operatori de fisier sunt prezentati în Tabelul 7.11.
Operator |
Descriere |
AWTImage |
Converteste un obiect standard java.awt.Image într-o imagine Rendered |
BMP |
Citeste un flux de intrare standard BMP |
Encode |
Preia o imagine sursa Rendered si scrie imaginea la un obiect OutputStream în formatul specificat folosind parametrii de codare dati. |
FileLoad |
Citeste o imagine dintr-un fisier |
FileStore |
Preia o imagine sursa Rendered si scrie imaginea într-un fisier dat în formatul specificat folosind parametrii de codare furnizati. |
TIFF |
Citeste date de tip TIFF 6.0 dintr-un SeekableStream |
URL |
Creeaza o imagine de iesire a carui sursa este specificata de un URL |
Tabelul 7.11. Exemple de operatori de fisier
Operatori în frecventa. Operatorii în frecventa sunt folositi pentru a descompune o imagine din forma spatiala într-o forma din domeniul frecventa. Operatorii sunt de asemenea disponibili pentru a executa operatia inversa a transformarii în frecventa, în care imaginea este convertita din domeniul frecventa înapoi în forma ei spatiala.
JAI suporta mai multe tipuri de transformari în frecventa. Cea mai utilizata transformare este transformta Fourier, dar JAI utilizeaza forma discreta a acestei transformate cunoscuta sub numele de transformata Fourier discreta, precum si transformata discreta cosinus împreuna cu variantele lor de transformari inverse. Câteva exemple de operatori în domeniul frecventa sunt prezentati în Tabelul 7.12.
Operator |
Descriere |
DCT |
Preia o imagine sursa Rendered sau Renderable si calculeaza transformata discreta cosinus (DCT) para a imaginii. Fiecare banda a imaginii destinatie este derivata prin executarea unei transformari DCT bidimensionale asupra benzii corespunzatoare din imaginea sursa. |
DFT |
Preia o imagine sursa Rendered sau Renderable si calculeaza transformata discreta Fourier a imaginii. |
Phase |
Preia o imagine sursa Rendered sau Renderable continând date complexe si calculeaza unghiul fazei pentru fiecare pixel. |
Tabelul 7.12. Exemple de operatori în domeniul frecventa
Operatori statistici. Operatorii statistici furnizeaza mijloace de analiza a continutului unei imagini. Operatorii statistici din JAI sunt prezentati în Tabelul 7.13.
Operator |
Descriere |
Extrema |
Preia o sursa imagine Rendered, citeste o regiune specifica din imagine si gaseste valorile minime si maxime ale valorilor pixelilor pentru fiecare banda din acea regiune de imagine. Datele imagine trec prin acest operator nemodificate. |
Histogram |
Preia o imagine sursa Rendered, citeste o regiune specifica din imagine si genereaza o histograma bazându-se pe valorile pixelilor din acea regiune de imagine. Datele histogramei sunt stocate într-un obiect de tip javax.media.jai.Histogram obtinut de la utilizator, acesta putându-se obtine printr-un apel la metoda getProperty asupra acestei operatii cu numele proprietatii "histogram". Valoarea de retur va fi de tipul javax.media.jai.Histogram. Datele imagine trec neschimbate prin aceasta operatie. |
Mean |
Preia o sursa imagine Rendered, citeste o regiune specifica si calculeaza valoarea medie pentru fiecare banda din interiorul unei regiuni dintr-o imagine. Datele imagine trec neschimbate prin aceasta operatie. |
Tabelul 7.13. Operatori statistici
Operatori de extragere a muchiilor. Operatorii de extragere a muchiilor permit îmbunatatirea muchiilor imaginii, reducând imaginea doar la detaliile muchiilor. Îmbunatatirea muchiilor se face prin intermediul unor filtre spatiale care detecteaza o denivelare de stralucire a pixelilor specifica din interiorul unui grup de pixeli dintr-o imagine. O denivelare precipitata de stralucire indica prezenta unei muchii.
Operatorul GradientMagnitude preia o imagine sursa Rendered si calculeaza intensitatea vectorului gradient a imaginii pe doua directii ortogonale.
Alti operatori. Acestia sunt operatori care nu se încadreaza în categoriile de mai sus. Un astfel de operator din JAI este Renderable, care preia o imagine sursa Rendered si produce un obiect RenderableImage constituind o "piramida" de obiecte RenderedImage la valori ale rezolutiei progresiv mai mica.
Obiectele operatii sunt create în cele mai multe cazuri folosind metodele (metode create din clasa JAI) care vor fi expuse în cele ce urmeaza.
Pentru grafurile Renderable exista patru variatii pentru crearea de operatii în modul Renderable. De exemplu:
RenderableOp im = JAI.createRenderable("operationName",
paramBlock);
Metoda JAI.createRenderable creeaza un nod operatie Renderable care preia doi parametrii:
Un nume de operatie
O sursa si un set de parametrii pentru operatia continuta în blocul de parametrii
Pentru grafurile Rendered exista mult mai multe variatii ale metodelor de creare (metode JAI.create) a operatiilor în modul Rendered.
Exista doua versiuni ale metodei create care nu sunt statice, identificate ca createNS. Aceste metode pot fi utilizate cu o instanta specifica a clasei JAI si ar trebui utilizata doar atunci când rezultatul final returnat este un singur obiect RenderedImage. Sursa sau sursele furnizate pot fi o colectie de imagini sau o colectie de colectii. În cele ce urmeaza se prezinta un exemplu de utilizare a acestei metode:
RenderedOp im = JAI.createNS("operationName",
source, param1, param2)
Indiciile de reprezentare asociate cu aceasta instanta a JAI sunt suprapuse cu indiciile furnizate la aceasta metoda, indiciile vor fi o uniune dintre indiciile instantei si indiciile date prin parametru.
Numele operatiei descrie operatorul care va fi creat si este un string.
Blocul de parametrii contine sursa operatiei si un set de parametrii utilizati de operatie. Continutul blocului de parametri depinde de operatia care se creeaza si poate fi doar numele imaginii sursa sau poate contine toti parametrii operatorului. Blocurile de parametrii încapsuleaza toate informatiile despre sursele si parametrii utilizati de operatie. Parametrii specificati de catre blocul de parametri sunt obiecte.
Aceste obiecte de control, parametri si surse, pot fi editate prin metoda setParameterBlock pentru a afecta operatii specifice sau chiar structura lantului de reprezetare. Modificarile afecteaza viitoarele obiecte RenderedImage derivate din punctele lantului din aval de unde s-a facut modificarea.
Exista doua clase separate pentru a specifica blocurile de parametrii:
java.awt.image.renderable.ParameterBlock - clasa principala pentru specificarea si schimbarea blocurilor de parametrii.
javax.media.jai.ParameterBlockJAI - extinde ParameterBlock prin permiterea utilizarii a valorilor implicite a parametrilor si utilizarii numelor de parametrii.
Blocul de parametrii trebuie sa contina acelasi numar de surse si parametri în functie de necesitatea operatiei, cu exceptia când se foloseste ParameterBlockJAI care furnizeaza valori implicite. Pentru unele operatii, valorile implicite ale parametrilor nu sunt disponibile si de aceea ele trebuie furnizate.
Sursele sunt adaugate la blocul de parametrii cu metoda addSource. În exemplul urmator se creeaza un nou ParameterBlock numit pb si apoi metoda addSource este utilizata la adaugarea imaginii sursa im0 la blocul de parametrii.
ParameterBlock pb = new ParameterBlock();
pb.addSource(im0);
Pentru adaugarea a doua surse la un bloc de parametrii, se utilizeaza doua apeluri la metoda addSource.
ParameterBlock pb = new ParameterBlock();
pb.addSource(im0);
pb.addSource(im1);
În cazul folosirii clasei ParameterBlock, parametrii operatiei sunt adaugati la ParameterBlock cu metoda ParameterBlock.add. Exemplul urmator adauga doua valori la obiectul ParameterBlock numit pb, care a fost creat în exemplul anterior.
pb.add(150);
pb.add(200);
Metoda add poate fi utilizata cu toate tipurile de date suportate: byte, short, integer, long, float si double. Atunci când se utilizeaza obiectul ParameterBlock, toti parametrii pe care o operatie are nevoie trebuie sa fie adaugati, altfel operatia va esua.
La utilizarea clasei ParameterBlockJAI, deoarece obiectul contine deja valori implicite pentru parametrii la momentul constructiei, parametrii trebuie schimbati cu metodele ParameterBlockJAI.set(value, index).
Exemplul urmator (Exemplul 7.12.) arata crearea unui Parameter-BlockJAI cu intentia de a fi furnizat unei operatii de rotire. Operatia de rotire preia patru parametrii: xOrigin, yOrigin, angle si interpolation. Valorile implicite pentru xOrigin si yOrigin sunt 0.0F pentru amândoua. În acest exemplu, aceste doua valori nu sunt modificate, deoarece valorile implicite sunt suficiente pentru operatie. Ceilalti doi parametri (angle si interpolation) au valoarea implicita null si astfel necesita modificarea valorilor. Imaginea sursa trebuie de asemenea specificata.
Exemplul 7.12. Utilizarea obiectelor ParameterBlockJAI pentru efectuarea operatiilor |
// Specifica metoda de interpolatie utilizata interp = Interpolation.create(Interpolation.INTERP_NEAREST); // creeaza ParameterBlockJAI si adauga interpolatia la el ParameterBlockJAI pb = new ParameterBlockJAI(); pb.addSource(im); // imaginea sursa pb.set(1.2F, "angle"); // unghiul de rotatie în radiani pb.set(interp, "interpolation"); // metoda de interpolatie |
Indiciile de reprezentare reprezinta un set de indicii care descriu modul în care obiectele sunt reprezentate. Indiciile de reprezentare sunt întotdeauna optionale pentru orice operatie.
Indiciile de reprezentare specifica diferiti algoritmi de reprezentare cum ar fi antialiasing, interpolatie alfa si dithering. Multe dintre indicii permit alegerea dintre calitatea reprezentarii si viteza, alte indicii pot sa porneasca sau sa opreasca diferite optiuni de reprezentare, cum ar fi antialiasing si metrica fractionala. Exista doua clase separate pentru specificarea indiciilor de reprezentare:
java.awt.RenderingHints - contine indiciile de reprezentare care pot fi folosite de catre clasa Graphics2D si clasele care implementeaza Raster si BufferedImageOp.
javax.media.jai.JAI - furnizeaza metode pentru definirea cheilor pentru indicii de reprezentare specifice JAI.
Clasa RenderingHints creeaza indicii de reprezentare specifice Java AWT. Pentru a modifica indiciile de reprezentare, se creeaza un obiect RenderingHints si se transmite unei metode JAI.create asupra careia dorim sa actioneze. Modificând un indiciu de reprezentare nu se garanteaza ca un anume algoritm de reprezentare va fi folosit, deoarece nu toate platformele suporta modificarea codului de reprezentare.
În codul urmator, indiciile de reprezentare sunt setate pe calitate.
qualityHints = new
RenderingHints(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Odata ce un obiect RenderingHints a fost creat, indiciile pot fi utilizate într-o operatie folosind o metoda JAI.create.
Fiecare instanta a unui obiect JAI contine un set de indicii de reprezentare care va fi folosit pentru toate crearile de imagini sau colectii de imagini. Indiciile sunt combinate cu orice alte indicii furnizate metodei JAI.create. Indiciile furnizate direct au precedenta asupra indicilor obisnuite. Atunci când o noua instanta JAI este construita, indiciile ei sunt initializate la o copie a indiciilor asociate cu instanta implicita. Indiciile asociate cu alte instante, incluzând instanta implicita, pot fi manipulate folosind metodele getRenderingHints, setRenderingHints si clearRenderingHints. Pentru manipularea individuala a indiciilor se folosesc metodele getRenderingHint, setRenderingHint si removeRenderingHint.
Codul urmator (Exemplul 7.13.) este un exemplu de reprezentare a unei imagini cu indicii de reprezentare pentru o operatie de scalare. Indiciul de reprezentare specifica originea imaginii destinatie modificata la 200x200.
Exemplul 7.13. Utilizarea indiciilor de reprezentare |
// creeaza blocul de parametrii pentru operatia de scalare. ParameterBlock pb = new ParameterBlock(); pb.addSource(im0); // imaginea sursa pb.add(4.0F); // factorul de scalare x pb.add(4.0F); // factorul de scalare y pb.add(interp); // metoda de interpolatie // Specificarea indiciilor de reprezentare layout = new ImageLayout(); layout.setMinX(200); layout.setMinY(200); RenderingHints rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); // crearea operatiei de scalare. PlanarImage im2 = (PlanarImage)JAI.create("scale", pb, layout) |
Toate aplicatiile de procesare a imaginilor trebuie sa execute niste operatii de baza, cum ar fi achizitia, afisarea si înregistrarea imaginilor. Imaginile pot fi achizitionate de la multe surse, care includ fisier de pe disc, retea, etc. Imaginile pot fi achizitionate, procesate si imediat afisate, sau scrise într-un fisier pe disc pentru a putea fi afisate mai târziu.
JAI ofera programatorului flexibilitatea de a procesa si afisa o imagine în mod imediat sau sa amâne afisarea imaginii procesate pâna când apare o cerere specifica pentru aceasta.
Achizitia si afisarea imaginilor folosind mecanismele din JAI este prezentata în Exemplul 7.14. Acest exemplu este o aplicatie care preia un singur argument care este calea si numele fisierului care trebuie citit, iar aplicatia citeste acest fisier imagine folosind operatorul "FileLoad" si afiseaza imaginea cu ajutorul unei componente de afisare de tipul ScrollingImagePanel.
Exemplul 7.14. Program pentru citirea si afisarea unui fisier imagine |
// se specifca clasele care se importa import java.awt.image.renderable.ParameterBlock; import java.io.File; import javax.media.jai.JAI; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import javax.media.jai.widget.ScrollingImagePanel; public class FileTest extends WindowContainer else if(args.length == 1) new FileTest(fileName); } public FileTest(String fileName) |
Datele imagine reprezinta o arie tridimensionala de pixeli, asa cum arata Figura 7.9., fiecare din aceste trei arii formeaza câte o banda. Numarul de linii specifica înaltimea benzii imaginii, iar numarul de coloane specifica latimea benzii imaginii.
Imaginile monocrome, de exemplu imaginile grayscale, au doar o singura banda. Imaginile color au trei sau mai multe benzi, dar benzile nu neaparat reprezinta o culoare, de exemplu imaginile din satelit pot fi achizitionate având mai multe benzi spectrale, de exemplu rosu, verde, albastru si infrarosu.
O imagine este esantionata într-o grila dreptunghiulara de pixeli. Fiecare pixel are o coordonata (x, y) care corespunde locatiei de pe imagine. În JAI, locatia pixelului (0, 0) este în coltul din stânga sus al imaginii, cu valorile coordonatelor x crescatoare spre dreapta si valorile coordonatelor y crescatoare în jos.
În JAI API, unitatea de baza pentru stocarea imaginilor sunt obiectele DataBuffer. Acestea sunt un fel de obiecte de stocare brute, care stocheaza toate esantioanele din compozitia unei imagini, dar nu contin nici o informatie despre felul cum aceste esantioane sunt organizate ca pixeli. Aceasta informatie este stocata într-un obiect SampleModel. Clasa SampleModel contine metode pentru derivarea datelor pixel dintr-un DataBuffer.
JAI suporta mai multe tipuri de date imagine, asa ca clasa DataBuffer are urmatoarele subclase, fiecare reprezentând un tip diferit de date:
DataBufferByte - stocheaza date intern ca si bytes (valori pe 8 biti).
DataBufferShort - stocheaza date intern ca si short (valori pe 16 biti)
DataBufferUShort - stocheaza date intern ca si unsigned short (valori pe 16 biti)
DataBufferInt - stocheaza date intern ca si întregi (valori pe 32 biti)
DataBufferFloat - stocheaza date intern ca si valori flotante în precizie simpla.
DataBufferBuffer - stocheaza date intern ca si valori flotante în precizie dubla.
JAI suporta si alte formate de date imagine, pe lânga cele oferite de pachetul java.awt.image (vezi paragraful 7.3.4.), dar clasa SampleModel furnizeaza si urmatoarele tipuri de modele de esantioane:
ComponentSampleModelJAI - utilizat la extragerea pixelilor din imagini care stocheaza date esantion astfel încât fiecare esantion al unui pixel ocupa un element data din DataBuffer.
FloatComponentSampleModel - stocheaza n esantioane care formeaza un pixel în n arii de elemente data separate, toate acestea sunt stocate în acelasi segment a obiectului DataBuffer.
Combinatia dintre un obiect DataBuffer, un obiect SampleModel si un element origine constituie o unitate de stocare a imaginilor de tip multi-pixel si este concretizata prin clasa Raster (vezi paragraful 7.3.2.).
JAI API furnizeaza o serie de clase pentru descrierea datelor imagine de diferite tipuri. Aceste clase sunt organizate într-o ierarhie care este expusa în Figura 7.10.
Clasa PlanarImage. Aceasta clasa este clasa principala pentru definirea imaginilor bidimensionale. PalnarImage implementeaza interfata java.awt.image.RenderedImage, care descrie o imagine segmentata, disponibila doar pentru citire, cu o dispunere a pixelilor descrisa de un obiect SampleModel si unul de tip DataBuffer. Subclasele TiledImage si OpImage manipuleaza variabilele instanta pe care le mostenesc de la PlanarImage, cum ar fi marimea imaginii, originea, dimensiunea segmentului, deplasamentul grilei segmentului, obiectele de tip Vector care pastreaza sursa si destinatia imaginii.
Toate obiectele care nu apartin JAI de tip RenderedImage, dar care se utilizeaza în JAI trebuie convertite în PlanarImage prin mijloace oferite de clasa RenderedImageAdapter si clasa WriteableRenderedImageAdapter.
Clasa TiledImage. JAI API extinde conceptul de segmentare a datelor imagine introdus în Java 2D API. În Java 2D, un segment este una dintre regiunile dreptunghiulare care împarte o imagine într-o grila regulata. JAI API extinde segmentarea imaginilor prin clasa TiledImage.
Un segment (tile) reprezinta totalitatea elementelor de stocare pentru o regiune dintr-o imagine. Daca o imagine este compusa din trei benzi, fiecare segment contine componente din toate cele trei benzi din datele stocate. Utilizarea imaginilor segmentate îmbunatateste performanta aplicatiei prin faptul ca permite ca aplicatia sa proceseze o regiune de imagine dintr-un segment fara sa fie nevoie sa aducem întreaga imagine în memorie.
Clasa TiledImage este o implementare a interfetei WriteableRendered-Image, beneficiind de avantajul interfetei de a descrie imaginea cu segmente multiple. Segmentele de tipul WriteableRenderedImage trebuie sa împarta un SampleModel, care determina latimea, înaltimea si formatul pixelului.
Segmentele formeaza o grila regulata care poate sa ocupe orice regiune dreptunghiulara din plan. Segmentele de pixeli care depasesc limitele imaginii au valori nedefinite.
Continutul unui obiect TiledImage este definit de o singura sursa PlanarImage, furnizata fie la momentul constructiei fie cu ajutorul metodei set. Metoda set asigura un mod de suprascriere selectiva a unei portiuni din TiledImage.
TiledImage suporta manipularea directa a pixelilor prin mijloacele oferite de metoda getWriteableTile. Aceasta metoda returneaza un WritableRaster care poate fi modificat direct.
Un alt mod de a modifica continutul unui TiledImage este apelarea metodei createGraphics. Aceasta metoda returneaza un obiect GraphicsJAI care poate fi folosit pentru desenarea de grafica, text si imagini în maniera AWT.
TiledImage v-a cauza calcularea segmentelor sale doar atunci când continutul este solicitat. Odata ce un segment este calculat, continutul sau poate fi eliminat daca se determina ca poate fi recalculat în mod identic din sursa. Metoda lockTile forteaza calcularea unui segment si mentinerea lui pe durata de existenta a obiectului TiledImage.
Interfata TileCache. Aceasta interfata este o zona cache comuna pentru segmentele calculate detinute de obiectele OpImage. Acest obiect care se creeaza (de tipul TileCache) are o capacitate data, masurata în segmente. În mod implicit, capacitatea unui TileCache este 300 segmente. Capacitatea implicita a memoriei rezervata pentru un obiect TileCache este 20M bytes.
Un obiect TileCache care va fi utilizat de o operatie anume poate fi conceput în momentul constructiei operatiei, sau printr-un apel la metoda JAI.setTileCache. Aceasta face ca obiectul TileCache sa fie adaugat la setul de indicii de reprezentare.
Interfata TileScheduler permite segmentelor sa fie programate pentru calculare. În diferite implementari, calcularea segmentului se poate realiza prin utilizarea de multithreading si conexiuni în retea multiple în mod simultan pentru a îmbunatati performanta.
Clasa SnapshotImage. Aceasta clasa reprezinta componenta principala a motorului de executie amânata. Un SnapshotImage furnizeaza un numar arbitrar de variante sincrone a unui posibil obiect WritableRendered-Image care se modifica. SnapshotImage este responsabila pentru stabilizarea surselor care se modifica pentru a permite executia amânata a operatiilor dependente de astfel de surse.
Orice RenderedImage poate fi utilizat ca sursa pentru un Snapshot-Image. Daca sursa este un WritableRenderedImage, SnapshotImage se va înregistra ca TileObserver si va face copii pentru segmentele care sunt pe cale sa se modifice.
Mai multe versiuni ale fiecarui segment sunt pastrate intern, atât timp cât se cere. SnapshotImage permite monitorizarea acestor cereri si poate sa simplifice urmatoarele cereri pentru segmente, fara sa fie nevoie sa facem o copie.
Atunci când se utilizeaza ca si sursa, apelurile la metoda getTile vor fi transmise spre sursa, cu alte cuvinte obiectele SnapshotImage sunt complet transparente. Prin apelul createSnapshot o instanta a unei subclase PlanarImage care nu este publica va fi creata si returnata. Obiectul PlanarImage va returna întotdeauna segmente de imagini cu continut ca si în momentul constructiei lui.
Clasa RemoteImage. Aceasta clasa este o subclasa a PlanarImage care reprezinta o imagine pe un server distant. Un obiect RemoteImage poate fi construit dintr-un RenderedImage sau dintr-un lant de procesare a imaginilor care este fie de tip Rendered, fie Renderable.
Clasa CollectionImage. Aceasta clasa este o superclasa abstracta pentru clasele care reprezinta grupuri de imagini. Exemple de grupuri de imagini sunt piramidele (ImagePyramid), secventele temporale (TimeSequence) si feliile plane suprapuse pentru a forma un volum (ImageStack).
Clasa ImageSequence. Aceasta clasa reprezinta o secventa de imagini asociata cu niste indecsi temporali si o pozitie a camerei, cu scopul de a reprezenta secvente video sau fotografie dependenta de timp.
Imaginile sunt de tipul ImageJAI, indecsii temporali sunt de tipul long, iar pozitiile camerei sunt de tipul Point. Acest triplet este reprezentat printr-un obiect de tipul SequentialImage.
Clasa ImageStack. Clasa ImageStack reprezinta o stiva de imagini, fiecare cu o orientare spatiala definita într-un sistem de coordonate.
Imaginile sunt de tipul PlanarImage, coordonatele sunt de tipul javax.media.jai.Coordinate. Aceste date sunt reprezentate printr-un obiect javax.media.jai.CoordinateImage.
Clasa ImageMIPMap. O imagine de tip harta MIP este o stiva de imagini cu o relatie fixa între feliile adiacente. Fiind data felia cu cea mai mare rezolutie, celelalte pot fi obtinute prin derivare folosind operatii specifice. Datele pot fi extrase felie cu felie sau prin iteratori speciali.
O imagine harta MIP (MIP vine din lmba latina "multim im parvo" care înseamna "multe lucruri în spatii mici") este similara cu o alocare de textura. La alocarea texturilor, imaginea harta MIP contine versiuni de diferite marimi ale aceleiasi imagini în aceeasi locatie. Pentru utilizarea acestei alocari a texturilor, trebuie furnizate toate marimile imaginii în putere de 2 din cea mai mare imagine la o harta de 1x1.
Clasa ImageMIPMap preia sursa originala la cel mai mare nivel de rezolutie, considerat a fi nivelul 0 si un lant RenderedOp care defineste modul în care imaginea de la nivelul de rezolutie imediat inferior este derivata din nivelul de rezolutie curent.
Lantul RenderedOp poate sa aiba mai multe operatii, dar prima operatie din lant trebuie sa preia doar o singura imagine sursa, care este imaginea de la nivelul de rezolutie curent.
Clasa ImagePyramid. Aceasta clasa implementeaza o operatie piramida (pyramid) asupra unui obiect RenderedImage. Prin aceasta clasa se poate genera imagini aditionale prin medierea succesiva a unui bloc de 2x2 pixeli, fiecare data se elimina toate celelalte coloane si rânduri de pixeli. De exemplu, pentru o imagine de tip RenderedImage de 1024x1024, vom avea imagini de 512x512, 256x256 si tot asa pâna la 1x1.
În practica, imaginile de rezolutie mai mica pot fi derivate prin executia unui lant de operatii în mod repetat care subesantioneaza felia de imagine cu rezolutie mai mare. În mod similar, odata ce o felie de imagine este obtinuta, feliile de imagine cu rezolutia mai mare pot fi derivate prin executia unui alt lant de operatiuni în mod repetat care supraesantioneaza felia de imagine cu rezolutia mai mica. De asemenea, un al treilea lant de operatii poate fi folosit pentru gasirea diferentelor dintre felia originala a imaginii si felia rezultata obtinuta prin prima subesantionare apoi supraesantionând felia originala.
Clasa MultiResolutionRenderableImage. Aceasta clasa produce reprezentari bazate pe un set de obiecte RenderedImage de diferite rezolutii. Obiectul este construit dintr-o dimensiune specificata si un vector de obiecte RenderdImage care reprezinta imagini cu rezolutii progresiv mai mici.
JAI API extinde tipurile de streamuri din Java prin adaugarea de sapte clase de tip stream "seekable" (vezi Figura 7.11.). Tabelul 7.14. descrie pe scurt fiecare dintre aceste noi clase.
Noile clase de tip seekable sunt utilizate pentru aducerea în avans (în cache) a datelor imagine care sunt citite, metodele lor pot fi folosite la cautarea prin date înapoi sau înainte fara a fi nevoie sa recitim datele. Acest lucru este important pentru tipuri de date imagine care sunt segmentate sau nu pot fi recitite usor pentru a localiza o anume informatie importanta.
Clasa |
Descriere |
SeekableStream |
Clasa abstracta care combina functionalitatile lui InputStream si RandomAccessFile, cu abilitatile de a citi tipuri de date primitive în format little-endian. |
FileSeekableStream |
Furnizeaza functionalitate SeekableStream asupra datelor stocate într-un obiect File |
ByteArraySeekable Stream |
Implementeaza functionalitate SeekableStream asupra datelor stocate într-o arie de bytes |
SegmentedSeekable Stream |
Furnizeaza informatii despre un subset al altui SeekableStream care contine o serie de segmente cu o pozitie de start data în fluxul sursei si lungime. Fluxul rezultat se comporta ca si un SeekableStream obisnuit. |
ForwardSeekable Stream |
Furnizeaza functionalitate SeekableStream asupra datelor dintr-un InputStream cu adaugare de date minima, dar nu permite cautari înapoi. Poate fi utilizat cu formate de intrare care suporta streaming, evitând nevoia de a aduce în avans (cache) datele de intrare. |
FileCacheSeekable Stream |
Furnizeaza functionalitate SeekableStream asupra datelor dintr-un InputStream cu adaugare de date minima, dar nu permite cautari înapoi. Poate fi utilizat cu formate de intrare care suporta streaming, evitând nevoia de a aduce în avans (cache) datele de intrare. În circumstantele în care nu permit crearea unui fisier temporar (de exemplu, datorita securitatii sau absenta unui disc local), poate fi utilizata MemoryCache-SeekableStream. |
MemoryCache SeekableStream |
Furnizeaza functionalitate SeekableStream asupra datelor dintr-un InputStream, folosind un cache în memorie pentru a permite cautarea înapoi. Aceasta clasa se utilizeaza atunci când securitatea sau lipsa accesului la un disc local nu poate fi facuta cu FileCacheSeekableStream. |
Tabelul 7.14. Clasele stream din JAI
Pentru a citi date imagine dintr-un fisier este nevoie de abilitatea de cautare înainte si înapoi prin date si de a citi informatiile care descriu imaginea. Cea mai buna cale pentru a face datele sa poata fi cautate este printr-un cache, un fisier temporar stocat pe discul local sau în memorie. Cea mai preferata metoda de a stoca datele este discul local, dar nu întotdeauna este posibil. Din motive de securitate, sau la sistemele fara disc, crearea unui fisier cache pe disc nu este întotdeauna permisa. Atunci când un fisier cache nu este permis, se poate utiliza un cache în memorie.
Clasa SeekableStream permite cautarea în datele de intrare, în mod similar cu clasa RandomAccessFile. Interfata DataInput este suportata si extinsa pentru a include suport pentru reprezentare little-endian a tipurilor de date fundamentale.
SeekableStream adauga câteva metode read la cele existente din clasa java.io.DataInput, incluzând metode pentru citirea datelor în ordine little-endian, deoarece în Java toate valorile sunt scrise în stilul big-endian. Cu toate acestea, JAI necesita metode pentru citirea de date care nu sunt produse de Java, date care sunt produse pe alte platforme si care sunt în stilul little-endian.
Pe lânga metodele obisnuite furnizate de clasa InputStream, clasa RandomAccessFile implementeaza metodele getFilePointer si seek. Metoda canSeekBackwards returneaza true daca este posibila cautarea înapoi în stream fata de valoarea curenta obtinuta cu metoda getFilePointer.
Exista câteva subclase ale SeekableStream care se gasesc în pachetul com.sun.media.jai.codec. Trei clase sunt furnizate cu scopul de adaptare a unui obiect standard InputStream la interfata SeekableStream. Clasa Forward-SeekableStream nu permite cautarea înapoi, dar este foarte performanta. Clasa FileCacheSeekableStream mentine o copie a tuturor datelor citite de la intrare într-un fisier temporar. Acest fisier va fi eliminat automat atunci când FileSeekableStream este finalizat, sau când JVM iese din program în mod normal.
Clasa FileCacheSeekableStream are scopul de a fi eficienta în limite rezonabile în relatie cu utilizarea spatiului de pe disc. Atunci când crearea unui fisier temporar nu este posibila, se foloseste clasa MemoryCacheSeekable-Stream. Aceasta clasa creeaza un bufer foarte mare în memorie pentru a stoca datele din stream si este de preferat sa fie evitata. Clasa FileSeekableStream acopera clasa File sau clasa RandomAccessFile si executa o operatie limitata de aducere în avans a datelor (caching) pentru a evita operatiile I/O prea dese.
Metoda wrapInputStream este furnizata pentru a construi o instanta potrivita a SeekableStream a carui date sunt furnizate de un InputStream dat. Cel care apeleaza, prin mijloace ale parametrului canSeekBackwards, determina daca este necesar suportul pentru cautari în urma.
Arhitectura de codare JAI este compusa din codoare si decodoare capabile de a citi si scrie câteva formate de fisiere imagine rastru.
Exista mai multe formate de fisiere imagine rastru, cele mai multe dintre ele au fost create pentru a suporta stocarea si interschimbarea imaginilor. Unele formate au ajuns sa fie utilizate pe scara larga si sunt considerate standarde de facto. Alte formate, care sunt foarte importante pentru anumiti vendori software, sunt mai putin utilizate.
JAI suporta în mod direct câteva dintre cele mai utilizate formate de fisiere imagine (listate în Tabelul 7.15.). Pentru alte formate, se pot adauga noi codoare de fisiere, pe baza arhitecturii de extindere a JAI API.
Un fisier imagine are în mod obisnuit cel putin doua parti: un header de fisier si datele imagine. Headerul contine câmpuri cu informatii despre datele imagine care urmeaza. Acest header trebuie sa furnizeze toate informatiile necesare pentru reconstructia imaginii originale din datele imagine stocate. Datele imagine pot sau nu sa fie comprimate.
Numele formatului de fisier |
Descriere |
BMP |
Fisier imagine de tip Microsoft Windows bitmap |
FPX |
Format FlashPix |
GIF |
Format Graphics Interchange Format |
JPEG |
Format de fisier imagine dezvoltat de Joint Photographic Experts Group |
PNG |
Format Portable Network Graphics |
PNM |
Format de fisier Portable aNy Map. Include formatele PBM, PGM si PPM. |
TIFF |
Format de fisier imagine Tag Image File Format |
Tabelul 7.15. Formate de fisiere imagine suportate de JAI
Principala clasa pentru decodarea si codarea imaginilor este ImageCodec. Subclasele acestei clase pot sa execute recunoasterea unui format de fisier fie prin inspectia a unui header de lungime fixa sau prin accesul arbitrar la streamul de date sursa. Fiecare subclasa ImageCodec implementeaza una din cele doua metode de recunoastere a fisierelor imagine. Codecul la început apeleaza metoda getNumHeaderBytes, care fie returneaza 0 daca accesul arbitrar la stream este necesar, sau returneaza numarul de bytes din header necesari pentru a recunoaste formatul. În functie de ce returneaza metoda getNumHeaderBytes, codorul citeste streamul sau headerul.
Odata ce codecul a determinat formatul de imagine, prin metoda citirii streamului sau cea a headerului, el returneaza numele codecului asociat cu formatul de imagine detectat. Daca nu este înregistrat nici un codec cu acel nume, se returneaza null. Numele codecului defineste subclasa care este apelata, care decodeaza imaginea.
Pentru cele mai multe tipuri de imagine, JAI ofera optiunea de a citi fisierul cu date imagine ca obiect java.io.File sau ca una din subclasele de tipul java.io.InputStream.
JAI ofera câtiva operatori de fisier pentru citirea fisierelor cu date imagine, listati în Tebelul 7.16.
Operator |
Descriere |
AWTImage |
Importa o imagine AWT standard în JAI |
BMP |
Citeste date BMP dintr-un stream de intrare |
FileLoad |
Citeste o imagine dintr-un fisier |
FPX |
Citeste date FlashPix dintr-un stream de intrare |
FPXFile |
Citeste un fisier standard FlashPix |
GIF |
Citeste date GIF dintr-un stream de intrare |
JPEG |
Citeste un fisier standard JPEG (JFIF) |
PNG |
Citeste un stream de intrare PNG |
PNM |
Citeste un fisier standard PNM, inclusiv imagini PBM, PGM si PPM în format ASCII sau brut. |
Stream |
Citeste obiecte de tip java.io.InputStream |
TIFF |
Citeste date TIFF 6.0 dintr-un stream de intrare. |
URL |
Creeaza o imagine sursa care este specificata de un URL |
Tabelul 7.16. Operatori pentru fisiere imagine
În exemplul urmator (Exemplul 7.15.) se prezinta câteva fragmente de program în care se exemplifica utilizarea diferitilor operatori pentru fisiere.
Exemplul 7.15. Utilizarea diferitilor operatori pentru fisiere |
// exemplu operatie stream // încarcarea imaginii sursa dintr-un stream RenderedImage im = JAI.create("stream", stream); // exemplu de operatie fileload // încarca imaginea sursa dintr-un fisier RenderedImage src = (RenderedImage)JAI.create("fileload", fileName) // exemplu de citire a unei imagini FPX // specifica numele fisierului File file = new File(filename); // specifica rezolutia fisierului ImageDecodeParam param = new FPXDecodeParam(resolution); // creeaza operatia FPX pentru citirea fisierului. ImageDecoder decoder = ImageCodec.createImageDecoder("fpx", file, param); RenderedImage im = decoder.decodeAsRenderedImage(); ScrollingImagePanel p = new ScrollingImagePanel(im, Math.min(im.getWidth(), 800) + 20, Math.min(im.getHeight(), 800) + 20); // exemplu de citire a imaginilor BMP // acopera un obiect InputStream dintr-un SeekableStream. InputStream is = new FileInputStream(filename); SeekableStream s = SeekableStream.wrapInputStream(is, false); // creeaza obiectul ParameterBlock si este adaugat SeekableStream. ParameterBlock pb = new ParameterBlock(); pb.add(s); // executa operatia BMP op = JAI.create("BMP", pb); // exemplu de citire a fisierelor imagine PNG // crearea obiectului ParameterBlock. InputStream image = new FileInputStream(filename); ParameterBlock pb = new ParameterBlock(); pb.add(image); // creeaza operatia PNG. op = JAI.create("PNG", pb); // exemplu de citire a fisierelor imagine PNM // creeaza obiectul ParameterBlock. InputStream image = new FileInputStream(filename); ParameterBlock pb = new ParameterBlock(); pb.add(image); // creeaza operatia PNM op = JAI.create("PNM", pb); // exemplu de citire a unei imagini AWT // creeaza ParameterBlock. ParameterBlock pb = new ParameterBlock(); pb.add(image); // creeaza o operatie AWTImage. PlanarImage im = (PlanarImage)JAI.create("awtImage", pb); // exemplu de citire a unei imagini de tip URL // defineste o imagine URL. url = new URL("https://www/img/k.gif"); // citeste imaginea de la URL-ul specificat. RenderedOp src = JAI.create("url", url); |
Operatia Format reformateaza o imagine printr-o operatie cast asupra pixelilor unei imagini, la un anumit tip de data, înlocuind obiectele SampleModel si ColorModel a unei imagini si restructurând stilul de grila de segmente. Valorile pixelilor din imaginea destinatie sunt definite de urmatorul pseudocod:
dst[x][y][b] = cast(src[x][y][b], dataType)
where dataType is one of the constants
DataBuffer.TYPE_BYTE,
DataBuffer.TYPE_SHORT,
DataBuffer.TYPE_USHORT,
DataBuffer.TYPE_INT,
DataBuffer.TYPE_FLOAT,
or DataBuffer.TYPE_DOUBLE.
Obiectele de iesire SampleModel, ColorModel si stilul grilei de segmente sunt specificate prin transmiterea unui obiect ImageLayout ca si RenderingHint numit ImageLayout. Atunci când este posibil, imaginea de iesire va avea un SampleModel compatibil cu acel specificat în indiciul de reprezentare. Pentru tipurile de datele de iesire float si double va fi folosit un obiect ComponentSampleModel cu privire la valoarea parametrului hint.
Pentru a utiliza un DAG de tip Renderable cu un tip de imagine non-Renderable, imaginea trebuie mai întâi convertita din tipul Rendered în tipul Renderable. De exemplu, pentru a folosi o imagine obtinuta de la un server distant într-un lant Renderable, trebuie tratata imaginea sursa ca si RenderedImage, apoi convertita la RenderableImage pentru urmatoarele procesari.
Operatia Renderable produce un obiect RenderableImage dintr-o sursa RenderedImage. Obiectul RenderableImage care este produs este format dintr-o "piramida" de obiecte RenderedImage la rezolutii progresiv mai mici. Imaginile cu rezolutiile mai mici sunt produse prin invocarea lantului de operatii specificat prin parametrul downSampler asupra imaginii de la nivelul de rezolutie imediat superior al piramidei. Lantul de operatii downSampler trebuie sa adere la specificatiile descrise pentru constructorii clasei ImageMIPMap, care accepta acest tip de parametru.
Numarul de nivele din piramida va fi astfel încât cea mai mare dimensiune (latime sau înaltime) a nivelului de piramida cu cea mai mica rezolutie sa fie mai mic sau egal cu valoarea parametrului maxLowResDim, care trebuie sa fie pozitiv. Valoarea implicita pentru maxLowResDim este 64, ceea ce înseamna ca nivelul de piramida cu rezolutia cea mai mica va fi imaginea cu dimensiunea cea mai mare egala cu 64 pixeli sau mai mica.
Parametrii operatiei Renderable sunt descrisi în Tabelul 7.17.
Parametru |
Tip |
Descriere |
Valoare implicita |
downSamples |
RenderedOp |
Lantul de operatii utilizat pentru derivarea imaginilor cu rezolutie mai mica |
Filtru trece jos |
maxLowResDim |
Integer |
Dimensiunea maxima a nivelului de piramida cu rezolutia cea mai mica. | |
minX |
Float |
Coordonata independenta de reprezentare X minima a destinatiei |
0.0F |
minZ |
Float |
Coordonata independenta de reprezentare Y minima a destinatiei |
0.0F |
height |
Float |
Înaltimea independenta de reprezentare |
1.0F |
Tabelul 7.17. Parametrii operatiei Renderable
Exemplul 7.16. demonstreaza o operatie Renderable. Valorile implicite sunt folosite pentru toti cei cinci parametrii ai operatiei. Rezultatul operatiei poate fi transmis la urmatoarea operatie din graf.
Exemplul 7.16. Convertirea unui RenderedImage la Renderable |
// deriva obiectul RenderableImage din sursa RenderedImage. ParameterBlock pb = new ParameterBlock(); pb.addSource(src); pb.add(null).add(null).add(null).add(null).add(null); // creeaza operatia Renderable. RenderableImage ren = JAI.createRenderable("renderable", pb); |
Operatia constant defineste o imagine Rendered multi-banda si segmentata, unde toate valorile pixelilor din aceeasi banda au valoare constanta. Numarul de benzi a imaginii este determinat de numarul de valori de pixeli constanti furnizati ca parametrii. Tipul de data este determinat de tipul constantei din prima intrare.
Exemplul urmator (Exemplul 7.17.) ilustreaza o operatie constant.
Exemplul 7.17. Program pentru citirea si afisarea unui fisier imagine |
// creeaza obiectul ParameterBlock. Byte[] bandValues = new Byte[1]; bandValues[0] = alpha1; pb = new ParameterBlock(); pb.add(new Float(src1.getWidth())); // latimea pb.add(new Float(src1.getHeight())); // înaltimea pb.add(bandValues); // valorile benzilor // creeaza operatia constant. PlanarImage afa1 = (PlanarImage)JAI.create("constant", pb); |
JAI utilizeaza modelul BufferedImage din Java2D pentru a afisa imaginile. Clasa BufferedImage gestioneaza o imagine în memorie si furnizeaza modalitati de stocare a datelor pixel, interpreteaza datele pixel si reprezinta datele pixel la un context Graphics2D.
Afisarea imaginilor în JAI poate fi facuta prin mai multe moduri. Pentru început, apelul metodei drawRenderedImage asupra obiectului Graphics2D poate fi folosit pentru a produce o reprezentare imediata. O alta metoda este instantierea unei componente de afisare care raspunde la cererile utilizatorului cum ar fi derularea sau translatarea si de asemenea expunerea evenimentelor si obtinerea datelor imagine dintr-o sursa RenderedImage. Aceasta tehnica permite ca datele imagine sa fie calculate la cerere.
Pentru acest scop JAI furnizeaza o componenta de afisare, disponibila în pachetul javax.media.jai.widget, numita ScrollingImagePanel. Aceasta clasa preia un obiect RenderedImage si o latime si înaltime specifica si creeaza un panou cu bare de derulare, iar imaginea este asezata în centrul panoului.
Odata ce ScrollingImagePanel este creat, acesta poate fi asezat oriunde într-un Frame, la fel ca si orice alt panou din AWT. Exemplul 7.18. demonstreaza utilizarea unui astfel de panou.
Exemplul 7.18. Exemplu de utilizare a unui ScrollingImagePanel |
// preia înaltimea si latimea imaginii int width = image.getWidth(); int height = image.getHeight(); // ataseaza imaginea la panoul derulabil pentru afisare. ScrollingImagePanel panel = new ScrollingImagePanel( image, width, height); // creeaza un obiect Frame care va contine panoul. Frame window = new Frame("Examplu cu ScrollingImagePanel "); window.add(panel); window.pack(); window.show(); |
JAI extinde clasa java.awt.Canvas cu clasa ImageCanvas, care permite reprezentarea unei imagini într-o suprafata de desenare (canvas). Aceasta clasa mosteneste metode de la java.awt.Component, permitând folosirea acelorasi metode la tratarea evenimentelor pentru tastatura si mouse, ca si la clasa Canvas.
Clasa ImageCanvas este o componenta de afisare simpla pentru un RenderedImage si poate fi utilizata în orice context în care este nevoie de un Canvas. Clasa ImageCanvas monitorizeaza evenimentele de redimensionare si reîmprospatare si la nevoie cere în mod automat segmente de la sursa sa.
JAI utilizeaza trei clase de baza pentru gestiunea culorilor:
ColorModel - descrie un mod particular prin care valorile pixelilor sunt mapate la culori. Un obiect ColorModel este de obicei asociat cu un obiect Image sau BufferedImage si furnizeaza informatia necesara pentru a interpreta corect valorile pixelilor. Aceasta clasa este definita în pachetul java.awt.image.
ColorSpace - reprezinta un sistem pentru masurarea culorilor, în mod obisnuit folosind trei valori sau componente separate. Clasa ColorSpace contine metode pentru conversia între spatiul de culoare original si una sau doua spatii de culoare standard, CIEXYZ si RGB. Aceasta clasa este definita în pachetul java.awt.color.
Color - reprezinta o culoare fixa, definita prin componentele sale într-un obiect ColorSpace. Aceasta clasa este definita în pachetul java.awt.
Un obiect ColorModel este utilizat pentru interpretarea datelor pixel dintr-o imagine. Aceasta include:
Maparea componentelor din benzile unei imagini la componentele unui spatiu de culoare particular.
Extragerea componentelor pixelului din datele pixel împachetate.
Obtinerea de componente multiple dintr-o singura banda folosind masti.
Conversia datelor pixel printr-un tabel de cautare.
Pentru a determina valoarea culorii a unui pixel particular dintr-o imagine, trebuie cunoscut modul în care informatia de culoare este codata în fiecare pixel. Obiectul ColorModel asociat unei imagini încapsuleaza datele si metodele necesare pentru translatarea valorii unui pixel spre si de la componentele sale de culoare constituente.
JAI suporta cinci modele de culoare:
DirectColorModel - lucreaza cu valorile pixelilor care reprezinta informatie despre culori RGB si canal alfa ca si esantioane separate si împacheteaza toate esantioanele pentru un singur pixel într-un singur int, short, sau byte. Aceasta clasa poate fi utilizata doar cu spatii de culoare de tipul ColorSpace.TYPE_RGB.
IndexColorModel - lucreaza cu valorile pixelilor care reprezinta un singur esantion care este un index într-o harta de culori fixa în spatiul de culoare RGB implicit. Harta de culori specifica componentele de rosu, verde, albastru si optional alfa corespunzatoare pentru fiecare index.
ComponentColorModel - poate sa manevreze un obiect arbitrar ColorSpace si o arie de componente de culoare pentru a se potrivi cu spatiul de culoare. Acest model poate fi folosit pentru a reprezenta cele mai utilizate modele culoare pe cele mai utilizate dispozitive grafice (GraphicsDevice).
PackedColorModel - clasa de baza pentru modele care reprezinta valorile pixelilor în care sunt încorporate componentele de culoare în mod direct în bitii unui pixel întreg. Un astfel de obiect stocheaza informatia de împachetare care descrie modul în care componentele de culoare si alfa sunt extrase din canal. Obiectele DirectColorModel sunt de tipul PackedColorModel.
FloatDoubleColorModel - lucreaza cu valorile pixelilor care reprezinta informatie de culoare si alfa ca si esantioane separate, folosind elemente float si double.
Urmatorul exemplu (Exemplul 7.19.) prezinta constructia unui obiect ComponentColorModel pentru un model de culoare RGB.
Exemplul 7.19. Crearea unui model de culoare RGB |
// Creeaza un model de culoare RGB int[] bits = ; ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance( ColorSpace.CS_sRGB), bits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); |
Cantitatea de transparenta a unei imagini este specificata de valoarea alfa. O valoare alfa de 0.0 specifica o transparenta totala, iar o valoare alfa de 1.0 specifica o opacitate totala.
Imaginile pot sa transporte informatie de transparenta, cunoscuta sub denumirea de canal alfa, pentru fiecare pixel din imagine. Valoarea alfa este importanta atunci când culorile se suprapun. Valoarea alfa specifica cât de mult din culoarea reprezentata anterior trebuie sa fie afisata.
Procesarea imaginilor utilizând JAI API cuprinde operatii de manipulare, îmbunatatire, transformare geometrica si analiza a imaginilor.
Obiectele si metodele JAI pentru manipularea imaginilor sunt utilizate pentru îmbunatatire si modificare geometrica a imaginilor si pentru a extrage informatie din imagini. Manipularea imaginilor cuprinde:
Controlul regiunilor de interes ROI (Region of Interest)
Operatori relationali
Operatori logici
Operatori aritmetici
Efect de vibratie (dithering)
Fixarea (clamping) valorilor pixelilor
Copierea benzilor
Controlul regiunilor de interes. De obicei, orice îmbunatatire a unei imagini se produce asupra întregii imagini. Dar operatia de îmbunatatire a imaginii poate sa îmbunatateasca portiuni dintr-o imagine, celelalte portiuni ale imaginii pot sa piarda detalii, de aceea operatia de îmbunatatire trebuie limitata la o regiune specifica a imaginii.
Pentru a restrictiona operatia de îmbunatatire a imaginii la o regiune specifica din imagine, se creeaza o masca regiune de interes. O regiune de interes (Region of Interest - ROI) este conceptual o masca de valori true sau false. Masca ROI controleaza care pixel sursa din imagine trebuie procesat si care pixel destinatie trebuie înregistrat.
JAI suporta doua tipuri diferite de masti ROI: masca booleana si valoare de prag. Clasa ROIShape utilizeaza o masca booleana, care permite efectuarea de operatii rapid si folosind moduri de stocare compacte. Clasa ROI permite specificarea unei valori de prag, valorile pixelilor mai mari sau egale cu o valoare de prag sunt incluse în ROI. Valorile pixelilor mai mici decât valoarea pragului sunt excluse din ROI.
Regiunea de interes este definita de cele mai multe ori folosind un obiect ROIShape, care stocheaza aria folosind clase java.awt.Shape. Aceste clase definesc o arie ca si o descriere geometrica a conturului ei. Clasa ROI stocheaza o arie ca si o imagine cu o singura banda.
Un ROI poate fi atasat unei imagini ca si proprietate.
Operatori relationali. Operatorii relationali ai JAI permit efectuarea urmatoarelor operatii, folosind doua imagini sursa si o imagine destinatie:
Gasirea pixelilor celor mai mari din doua imagini sursa si stocarea rezultatelor în destinatie (Max).
Gasirea pixelilor celor mai mici din doua imagini sursa si stocarea rezultatelor în destinatie (Min).
Operatorii relationali necesita ca amândoua imaginile sursa si destinatie sa aiba acelasi tip de data si numar de benzi. Marimea celor doua imagini (înaltimea si latimea), nu trebuie sa fie neaparat la fel.
Atunci când se determina maximul si minimul pixelilor în doua imagini, JAI executa o comparare banda cu banda.
Operatori logici. JAI suporta operatori logici monadici, diadici si unari. Operatorii logici monadici includ operatii pixel-cu-pixel AND, OR si XOR între o imagine sursa si o constanta pentru a produce o imagine destinatie. Operatiile logice diadice includ operatii pixel-cu-pixel AND, OR si XOR între doua imagini sursa pentru a produce o imagine destinatie. Operatia logica unara este operatia NOT (complementare imagine) asupra fiecarui pixel a unei imagini sursa pe baza benzilor.
JAI suporta urmatoarele operatii logice:
And - preia rezultatul unei operatii pe biti AND asupra a doua imagini sursa si stocheaza rezultatul într-o destinatie.
AdConst - preia rezultatul unei operatii pe biti AND asupra unei imagini sursa si a unei constante dintr-un set al benzilor.
Or - preia rezultatul unei operatii pe biti OR asupra a doua imagini sursa si stocheaza rezultatul într-o destinatie.
OrConst - preia rezultatul unei operatii pe biti OR asupra unei imagini sursa si a unei constante dintr-un set al benzilor.
Xor - preia rezultatul unei operatii pe biti XOR asupra a doua imagini sursa si stocheaza rezultatul într-o destinatie.
XorConst - preia rezultatul unei operatii pe biti XOR asupra unei imagini sursa si a unei constante dintr-un set al benzilor.
Not - preia rezultatul unei operatii pe biti NOT asupra fiecarui pixel din benzi a unei imagini sursa.
Asemenea operatorilor relationali, operatorii logici necesita ca amândoua imaginile sursa si imaginea destinatie sa aiba acelasi tip de data si numar de benzi. Marimea celor doua imagini (înaltime si latime) poate sa difere.
Operatori aritmetici. JAI suporta operatori aritmetici monadici si diadici. Operatiile aritmetice monadice includ adunarea pe banda, scaderea, divizarea si multiplicarea între o imagine sursa si o constanta pentru a produce o imagine destinatie. Operatiile aritmetice diadice includ adunarea pe banda, scaderea, divizarea si multiplicarea între doua imagini sursa pentru a produce o imagine destinatie.
JAI suporta urmatorii operatori aritmetici:
Add - adunarea a doua imagini sursa si stocarea rezultatului în imaginea destinatie
AddConst - adunarea unei valori constante la pixelii dintr-o imagine sursa si stocarea rezultatelor într-o imagine destinatie.
AddCollection - aduna o colectie de imagini si stocheaza rezultatele într-o imagine destinatie.
AddConstToCollection - aduna o arie de constante double la o colectie de imagini Rendered.
Substract - scade o imagine sursa din alta si stocheaza rezultatele într-o imagine destinatie.
SubstractConst - scade o valoare constanta din pixelii unei imagini sursa si stocheaza rezultatele într-o imagine destinatie.
Divide - împarte o imagine sursa cu alta si stocheaza rezultatele într-o imagine destinatie.
DivideComplex - împarte doua imagini sursa cu date complexe si stocheaza rezultatele într-o imagine destinatie.
DivideByConst - împarte o imagine sursa cu o valoare constanta.
Multiply - înmulteste doua imagini sursa si stocheaza rezultatele într-o imagine destinatie.
MultiplyConst - înmulteste o imagine sursa cu o valoare constanta.
MultiplyComplex - înmulteste doua imagini reprezentând date complexe.
Absolute - gaseste valoarea absoluta a pixelilor într-o imagine sursa si stocheaza rezultatele într-o imagine destinatie.
Exp - preia exponentul unei imagini si stocheaza rezultatele într-o imagine destinatie.
Asemenea operatorilor relationali si logici, operatorii aritmetici necesita ca amândoua imaginile sursa si destinatie sa aiba acelasi tip de data si numar de benzi. Marimea celor doua imagini (înaltime si latime) poate sa difere.
Atunci când JAI aduna doua imagini, preia valoarea de la locatia 0,0 dintr-o imagine sursa, aduna la valoarea de la locatia 0,0 din cea de-a doua imagine sursa si scrie suma în imaginea destinatie la locatia 0,0. Pentru toate celelalte puncte din imagine se procedeaza la fel. Scaderea, înmultirea si împartirea sunt tratate în mod similar.
Operatiile aritmetice asupra imaginilor cu mai multe benzi se executa pe benzile corespunzatoare din imaginile sursa.
Efect de vibratie (dithering). Afisarea unei imagini color de 24 biti pe un bufer cadru de 8 biti necesita o operatie numita dithering. Aceasta operatie comprima cele trei benzi ale unei imagini RGB la o imagine byte cu o singura banda.
Operatia dithering utilizeaza un tabel de cautare prin care imaginea sursa este trecuta pentru a produce imaginea destinatie. Cea mai utilizata facilitate ale acestei operatii este conversia imaginilor true-color (byte cu trei benzi) în imagini pseudocolor (byte cu o singura banda).
Fixarea (clamping) valorilor pixelilor. Operatia clamp restrictioneaza intervalul valorilor pixelilor pentru o imagine sursa prin restrângerea intervalului pixelilor la valori "low" si "high" definite. Operatia preia o imagine sursa Rendered sau Renderable si seteaza toti pixelii a caror valoare este sub o valoare inferioara la acea valoare inferioara si toti pixelii a caror valoare este peste o valoare superioara la acea valoare superioara. Pixelii a caror valoare este între valoarea inferioara si valoarea superioara sunt lasati nemodificati.
Copierea benzilor. Operatia BandSelect alege N benzi din o imagine sursa Rendered sau Renderable si copiaza datele pixelilor ale acestor benzi la imaginea destinatie în ordinea specificata.
Îmbunatatirea imaginilor prin operatii JAI cuprinde urmatoarele:
Adaugare de borduri
Decuparea unei imagini
Rescalare de amplitudine
Egalizare de histograma
Modificare prin tabele de cautare
Filtrare prin convolutie
Filtrare mediana
Procesare în domeniul frecventa
Procesare punctuala
Binarizare (threshold)
Rescalare de amplitudine. Rescalarea de amplitudine reprezinta o transformare liniara a valorilor pixelilor de intrare la valorile pixelilor de iesire. Aceasta operatie poate fi folosita pentru îmbunatatirea imaginilor care au contrast insuficient între valorile cele mai luminoase si cele mai întunecoase, cum ar fi cele cauzate prin subexpunerea sau supraexpunerea imaginii originale.
Egalizarea de histograma. O histograma a unei imagini este un instrument analitic utilizat pentru a masura distributia amplitudinilor pixelilor dintr-o imagine. Prin analizarea distributiei amplitudinilor pixelilor, se poate obtine informatii despre aparitia vizuala a imaginii. O imagine cu contrast mare contine o distributie larga a contorizarilor pixelilor acoperind întregul interval de amplitudini. O imagine cu contrast mic are cele mai multe dintre amplitudinile pixelilor adunate într-un spatiu relativ îngust.
Exemplul urmator (Exemplul 7.20.) demonstreaza crearea unui obiect histograma si o operatie de egalizare de histograma folosind acest obiect. Operatia de egalizare de histograma se realizeaza utilizând operatorul MatchCDF.
Exemplul 7.20. Operatie de egalizare de histograma |
PlanarImage img; // imaginea sursa // obtine histograma pentru imagine. int binCount=256; // obtine numarul de benzi int numBands = img.getSampleModel().getNumBands(); // Aloca memorie pentru histograma int[] numBins = new int[numBands]; double[] lowValue = new double[numBands]; double[] highValue = new double[numBands]; for(int i = 0; i < numBands; i++) // creeaza un obiect Histogram. Histogram hist = new Histogram(numBins, lowValue, highValue); ROIShape roi; if(frame.isGotSelected())// specifica ROI roi = frame.getSelection(); else roi = new ROIShape(img.getBounds()); // creeaza o operatie histogram. RenderedOp histImage = JAI.create("histogram", img, hist, roi, new Integer(1), new Integer(1)); // creeaza un CDF (cumulative distribution function) pentru egalizare float[][] CDFeq = new float[numBands][]; for(int b = 0; b < numBands; b++) // creeaza o imagine cu histograma egalizata frame.setImage(JAI.create("matchcdf", histImage, CDFeq, roi)); |
Modificare prin tabele de cautare. Modificarea prin tabele de cautare (lookup table) furnizeaza o transformare de amplitudine neliniara. Transformarile de amplitudine neliniare sunt utile daca avem o diferenta de raspuns neliniar a amplitudinii dintre un senzor care captureaza datele imagine si afisare.
Mecanismul de modificare prin tabela de cautare permite conversia arbitrara între imaginea sursa cu valorile pixelilor de tipul byte, short, sau integer si una sau mai multe valori de iesire ale pixelilor. Valoarea de iesire a pixelului imagine poate sa fie byte, short, integer, float, sau double.
Valoarea de intrare a pixelului de intrare se comporta ca si o adresa în intrarea în tabela de cautare (vezi Figura 7.12.). Fiecare locatie din tabela de cautare stocheaza valoarea de iesire dorita pentru o adresa particulara.
Tabela de cautare este la început încarcata cu datele necesare. Tabelul 7.18. defineste o lista partiala dintr-un exemplu de tabela de cautare.
În acest exemplu, valorile de intrare variaza între 0 si 255. Valorile de iesire furnizeaza o transformare între intrare si iesire, conform cu urmatoarea ecuatie:
Intrare |
Iesire |
Tabelul 7.18. Exemplu de tabela de cautare
Exemplul 7.21. este un fragment de cod care ilustreaza constructia si utilizarea unei tabele de cautare.
Exemplul 7.21. Utilizarea tabelei de cautare |
PlanarImage image; // imaginea sursa private byte lut[][]; private byte newlut[][]; private int brightness = 0; lut = new byte[3][256]; newlut = new byte[3][256]; int i; // initializarea tabelei de cautare for ( i = 0; i < 256; i++ ) for ( i = 0; i < 256; i++ ) // creeaza tabela de cautare LookupTableJAI lookup = new LookupTableJAI(newlut); ParameterBlock pb = new ParameterBlock(); pb.addSource(image); pb.add(lookup); // aplica operatia de cautare în tabela PlanarImage dst = JAI.create("lookup", pb, null); final private byte clamp(int v) else if ( v < 0 ) else |
Filtrare prin convolutie. Filtrarea prin convolutie este utilizata pentru a reduce efectele de zgomot din imagini ori pentru a accentua detaliile din imagini. Aceasta filtrare este o forma de filtrare spatiala care calculeaza fiecare esantion de iesire prin înmultirea elementelor unui nucleu cu esantioanele încunjuratoare a unui esantion sursa.
Operatiile de filtrare prin convolutie se realizeaza în JAI cu ajutorul operatorului convolve.
Filtrarea mediana. Un filtru median este utilizat pentru a elimina zgomotul de impuls din imagine si de a netezi imaginea.
Efectul de reducere a zgomotului pe care îl are filtrul median asupra imaginii depinde de extinderea spatiala a vecinatatii (masca) si numarul de pixeli implicati în procesare. Operatia MedianFilter suporta trei forme de masti diferite: patrata, plus si forma de X.
Modificarile geometrice ale imaginii include urmatoarele:
Transformarea geometrica
Transformarea de perspectiva
Transpunerea
Shear
Rasucire
Câtiva dintre operatiile de manipulare geometrica a imaginii, cum ar fi Affine, Rotate, Scale, Shear, Translate si Warp, utilizeaza o transformare geometrica pentru a calcula coordonatele punctului dintr-o imagine sursa pentru fiecare pixel destinatie din imagine. În cele mai multe cazuri, pixelul destinatie nu se afla la locatia pixelului sursa, dar se afla undeva printre pixelii vecini. Valoarea estimata pentru fiecare pixel este stabilita într-un proces numit re-esantionarea imaginii.
Re-esantionarea este actiunea de calculare a valorii unui pixel la o pozitie pe cât posibil ne-întreaga a imaginii. Imaginea defineste valorile pixelilor în puncte întregi ale laticei si este la latitudinea re-esantionatorului sa produca o valoare rezonabila pentru pozitii care nu cad în latice. Tehnicile utilizate pentru aceasta sunt urmatoarele:
Cel mai apropiat vecin, care preia valoarea celui mai apropiat punct din latice.
Bilinear, care interpoleaza linear între patru puncte ale laticei cele mai apropiate.
Bicubic, care aplica o functie polinomiala pe o vecinatate a punctului de 4x4.
JAI furnizeaza clase care suporta operatii de desenare în plus fata de clasa Graphics2D. Sunt oferite trei tipuri de reprezentari grafice: grafica 2D simpla, grafica de tipul Renderable si grafica de imagine segmentata.
Clasa Graphics2D extinde clasa Graphics pentru a furniza mai mult control asupra geometriei, transformarilor de coordonate, gestiunii culorilor si afisare de text.
Clasa RenderableGraphics este o implementare a Graphics2D cu semnificatie de RenderableImage. Aceasta clasa permite stocarea unei secvente de comenzi de desenare si replicarea la o rezolutie de iesire arbitrara.
Metodele clasei RenderableGraphics rescriu metodele din clasele Graphics si Graphics2D. Acest lucru înseamna ca se pot utiliza metode în RenderableGraphics pentru a aplica culori si fonturi, pentru a crea forme grafice si text, etc.
Clasa Graphics2D permite desenarea de linii, forme geometrice, imagini si text. Aceste obiecte pot fi "pictate" deasupra unui TiledImage.
Procesarea client-server asigura abilitatea de calcul distribuit între un set de noduri de procesare. De exemplu, este posibil sa se construiasca un server puternic care furnizeaza servicii de procesare a imaginilor catre mai multi clienti care nu poseda putere de calcul prea mare. Cu JAI , este posibil pentru un client sa construiasca un lant de procesare a imaginilor complex, incluzând referinte la imagini sursa care se afla pe alte calculatoare din retea si sa ceara o reprezentare de iesire de la server.
JAI utilizeaza Java Remote Method Invocation (RMI) pentru a implementa procesarea de imagini client-server. Pentru a comunica folosind RMI, clientul si serverul trebuie sa ruleze Java. Un obiect stub este instantiat la client. Acest obiect transmite apelurile la metode la un obiect server corespunzator. Argumentele apelurilor la metode si valorile returnate sunt transmise între client si server prin mijloacele oferite de Java Development Environment si anume posibilitatea de serializare a obiectelor.
Calculatorul gazda si portul depind de configurarile locale. Calculatorul gazda trebuie sa ruleze un proces RMI registry si sa aiba un obiect RemoteImageServer care asculta portul dorit.
Un apel va duce la crearea pe partea de server a unui obiect de tip RMIImageImpl si la partea de client a unui obiect stub. Obiectul client stub serializeaza argumentele metodelor sale si le transfera la sever peste un socket, serverul serializeaza valorile returnate si le trimite la client în acelasi mod.
Constructorul clasei RemoteImage necesita un parametru serverName care reprezinta un nume de calculator gazda si un numar de port, în formatul urmator:
host:port
De exemplu:
hfovi.go.to:1099
Numarul portului este optional si trebuie sa fie furnizat doar daca numele calculatorului gazda a fost furnizat. Daca parametrul serverName este null, în mod implicit se va cauta serviciul RMIImage de pe calculatorul local de la portul rmiregistry (implicit 1099).
O eroare în retea sau o întârziere cauzata de serverul care esueaza în a raspunde la o cerere de imagine este tratata prin reîncercari. Daca la prima încercare, serverul nu raspunde, programul v-a astepta un anumit timp specificat si apoi va face o noua cerere pentru imagine. Atunci când limita reîncercarilor este depasita, este returnat un Raster null.
Timpul de asteptare dintre reîncercari este în mod implicit de 1 secunda. Metoda getTimeout din RemoteImage este folosita pentru a obtine timpul dintre reîncercari, în milisecunde. Metoda setTimeout este utilizata la stabilirea timpului dintre reîncercari.
Numarul de reîncercari pe care programul le v-a executa în încercarea de a citi imaginea distanta poate fi citit cu metoda getNumRetries. Metoda setNumRetries este utilizata pentru stabilirea numarului maxim de reîncercari.
Aceasta sectiune contine doua programe de procesare a imaginilor la distanta.
Exemplul 7.22. ilustreaza codul complet al unui exemplu de utilizare a clasei RemoteImage. Acest exemplu afiseaza o grila 2x2 de obiecte ScrollingImagePanel, cu fiecare fereastra afisând suma a doua imagini de tip byte care au fost rescalate în limitele [0, 127] înainte de a fi adunate. Panourile afiseaza urmatoarele rezultate:
Sus stânga: reprezentare locala
Sus dreapta: rezultatul procesarii la distanta a grafului RenderedOp
Jos stânga: rezultatele încarcarii la distanta a RenderedImage
Jos dreapta: rezultatele procesarii distante a grafului RenderableOp
Imaginea din partea de jos-dreapta este o versiune cu efect de vibratie (dithered) a imaginii suma transmisa printr-un lookup table cub de culoare si poate sa apara în mod diferit de celelalte trei imagini, care ar trebui sa arate identic.
Exemplul 7.22. Program de procesare la distanta a imaginilor |
import java.awt.*; import java.awt.event.WindowEvent; import java.awt.geom.*; import java.awt.image.*; import java.awt.image.renderable.*; import java.util.*; import javax.media.jai.*; import javax.media.jai.operator.*; import javax.media.jai.widget.*; public class RemoteImagingTest extends WindowContainer // configureaza numele serverului. String serverName = null; if(args.length == 0 || args.length == 2) else // seteaza numele fisierelor. if(args.length == 2) else if(args.length == 3) else RemoteImagingTest riTest = new RemoteImagingTest(serverName, fileName1, fileName2); * ruleaza un test de procesare a imaginilor distant. * * @param serverName numele serverului distant * @param fileName1 prima imagine adaugata. * @param fileName2 a doua imagine adaugata. RemoteImagingTest(String serverName, String fileName1, String fileName2) ).add(new double[] ); RenderedOp addend1 = JAI.create("rescale", pb, rh); pb = (new ParameterBlock()); pb.addSource(ti2); pb.add(new double[] ).add(new double[] ); RenderedOp addend2 = JAI.create("rescale", pb, rh); // aduna imaginile rescalate. pb = (new ParameterBlock()).addSource(addend1).addSource(addend2); RenderedOp sum = JAI.create("add", pb, rh); // adauga un efect de vibratie (dither) // sumei imaginilor rescalate. pb = (new ParameterBlock()).addSource(sum); pb.add(ColorCube.BYTE_496).add(KernelJAI.DITHER_MASK_443); RenderedOp dithered = JAI.create("ordereddither", pb, rh); // Construieste un obiect RemoteImage din lantul RenderedOp. RemoteImage remoteImage = new RemoteImage(serverName, sum); // defineste segmentul de afisare si modelul de fereastra. setTitle(getClass().getName()); setLayout(new GridLayout(2, 2)); // reprezentare locala add(new ScrollingImagePanel(sum, sum.getWidth(), sum.getHeight())); // reprezentare distanta RenderedOp. add(new ScrollingImagePanel(remoteImage, remoteImage.getWidth(), remoteImage.getHeight())); // reprezentare distanta RenderedImage PlanarImage sumImage = sum.getRendering(); remoteImage = new RemoteImage(serverName, sumImage); add(new ScrollingImagePanel(remoteImage, remoteImage.getWidth(), remoteImage.getHeight())); // reprezentare distanta RenderableOp. pb = new ParameterBlock(); pb.addSource(dithered); RenderableOp absImage = JAI.createRenderable("absolute",pb); pb = new ParameterBlock(); pb.addSource(absImage).add(ColorCube.BYTE_496); RenderableOp lutImage = JAI.createRenderable("lookup", pb); AffineTransform tf = AffineTransform.getScaleInstance(384/dithered.getWidth(), 256/dithered.getHeight()); Rectangle aoi = new Rectangle(128, 128, 384, 256); RenderContext rc = new RenderContext(tf, aoi, rh); remoteImage = new RemoteImage(serverName, lutImage, rc); add(new ScrollingImagePanel(remoteImage, remoteImage.getWidth(), remoteImage.getHeight())); // la sfârsit afisam totul pack(); show(); } |
Urmatorul exemplu (Exemplul 7.23.) reprezinta un lant Remote-Imaging raspândit dealungul a doua noduri distante si afiseaza rezultatele local.
Exemplul 7.23. Lant de tipul RemoteImage |
import java.awt.image.*; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.*; import javax.media.jai.widget.*; * creeaza un lant de procesare raspândit de-a lungul a * doua noduri distante si afiseaza rezultatul local. public class MultiNodeTest extends WindowContainer new MultiNodeTest(args[0], args[1], args[2]); } public MultiNodeTest(String fileName, String node1, String node2) |
Pentru a executa o procesare de imagini distanta utilizând JAI, trebuie parcurse urmatoarele etape:
Crearea unui fisier care specifica tipul de securitate (security policy file).
Pornirea registrilor RMI
Pornirea serverului de procesare a imaginilor distant
Rularea aplicatiei locale
Acesti patru pasi sunt explicati în detaliu în cele ce urmeaza.
Pas 1: Crearea unui security policy file. Implementarea implicita pentru RMI security policy este specificata în unul sau mai multe fisiere de configurare a securitatii. Aceste fisiere de configurare specifica permisiunile care sunt asociate codului din diferite surse. Exista un fisier general implicit security policy file si un singur fisier utilizator policy file.
Fisierul policy este situat în directorul de baza unde JAI este instalat. Daca $JAI este directorul de baza unde JAI este instalat, se poate crea (din motive de securitate este de preferat sa fie doar pentru testare) cu ajutorul unui editor text un fisier numit $JAI/policy care contine urmatoarele:
grant ;
Pas 2: Pornirea registrilor RMI. RMI registry este un server de nume simplu care permite clientilor distanti sa primeasca o referinta la un obiect distant. În mod obisnuit, registrul este utilizat doar pentru localizarea primului obiect distant de care o aplicatie are nevoie pentru a conlucra. Apoi acel obiect în schimb furnizeaza suport specific aplicatiei pentru gasirea altor obiecte.
Pentru pornirea registrilor pe server, trebuie întâi efectuata operatia de autentificare la sistemul distant unde serverul de procesare a imaginilor v-a rula si executa comanda rmiregistry.
Pas 3: Pornirea serverului de procesare a imaginilor distant. Dupa efectuarea operatiei de autentificare la serverul distant, se porneste severul de procesare a imaginilor distant. De exemplu:
$ CLASSPATH=$JAI/lib/jai.jar:\
$JAI/lib/mlibwrapper_jai.jar
$ export CLASSPATH
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAI/lib
$ export LD_LIBRARY_PATH
$ java \
-Djava.rmi.server.codebase=\
file:$JAI/lib/jai.jar \
-Djava.rmi.server.useCodebaseOnly=false \
-Djava.security.policy=file:$JAI/policy \
com.sun.media.jai.rmi.RMIImageImpl
Când pasii de mai sus sunt executati pe o masina cu adresa IP 193.226.6.174 se afiseaza urmatoarele:
Server: using host 193.226.6.174 port 1099
Registering image server as
"rmi://193.226.6.174:1099/RemoteImageServer".
Server: Bound RemoteImageServer into the registry.
Pas 4: Rularea aplicatiei locale. Dupa efectuarea pasilor 1 la 3, aplicatia locala poate fi pornita. Atunci când se ruleaza aplicatia locala, trebuie ca parametrul serverName pentru orice constructor RemoteImage sa corespunda masinii pe care ruleaza serverul de procesare a imaginilor distant. De exemplu, daca avem masina cu adresa de IP 193.226.6.174, denumita myserver, atunci parametrul serverName al oricarui constructor RemoteImage trebuie sa fie "myserver".
Internet Imaging Protocol (IIP) este un standard utilizat în procesarile de imagini client/server. Exista doua operatii JAI care suporta operatii IIP. Aceste doua operatii separate furnizeaza suport pentru partea client a IIP. Aceste operatii, IIP si IIPResolution, solicita o imagine de pe un server IIP apoi creeaza un obiect RenderedImage sau RenderableImage.
Operatia IIP. Aceasta operatie furnizeaza suport pentru partea client a IIP în modurile Rendered si Renderable. Aceasta creeaza un obiect RenderedImage sau RenderableImage bazându-se pe datele receptionate de la serverul IIP si optional aplica o secventa de operatii asupra imaginii create.
Operatiile suportate sunt urmatoarele:
Filtrare (blur si sharpen)
Corectie de tonalitate si culoare ("color twist")
Modificare de contrast
Selectare a unei surse de interes dreptunghiulare
Orientare spatiala (transformare prin alocare independenta de reprezentare)
Selectarea unei destinatii de interes dreptunghiulare
Transformare de reprezentare (doar în mod Renderable)
Transpunere (rotire si/sau oglindire)
Dupa cum se observa, transformarea de reprezentare este executata doar în modul de procesare Renderable. Aceasta transformare este derivata dintr-un obiect AffineTransform furnizat în RenderContext atunci când reprezentarea se produce. Modul de procesare Rendered creeaza un obiect RenderedImage care este reprezentarea implicita a obiectului RenderableImage creat în modul de procesare Renderable.
Exemplul 7.24. reprezinta o operatie IIP.
Exemplul 7.24. Operatie IIP |
public class IIPTest } String url = SERVER + "FIF=" + imagePath; new IIPTest(url); } public IIPTest (String url) ; pb.set(colorTwist, 3); //color-twist pb.set(2.0F, 4); // contrast pb.set(new Rectangle2D.Float(0.10F, 0.10F, 0.80F*aspectRatioSource, 0.80F), 5); // ROI sursa AffineTransform afn = AffineTransform.getShearInstance(0.2, 0.1); pb.set(afn, 6); // transformare Rectangle2D destBounds = null; try catch(Exception e) float aspectRatio = (float)destBounds.getHeight(); pb.set(aspectRatio, 7); // raportul de aspect al destinatiei pb.set(new Rectangle2D.Float(0.0F, 0.0F, 0.75F*aspectRatio, 0.75F), 8); // ROI destinatie pb.set(90, 9); // unghiul de rotatie pb.set("x", 10); // axa oglinzii // ramâne: profilul ICC implicit // calitatea JPEG implicita // tabela index JPEG implicita int height = DEFAULT_HEIGHT; AffineTransform at = AffineTransform.getScaleInstance( height*aspectRatioSource, height); RenderContext rc = new RenderContext(at); // creeaza un obiect RenderableImage. RenderableImage renderable = JAI.createRenderable("iip", pb); } |
Operatia IIPResolution. Aceasta operatie furnizeaza suport pentru partea client a IIP în modul de procesare Rendered. Aceasta operatie este cu specific legat de rezolutie, necesitând de la serverul IIP o imagine la un nivel de rezolutie particular si creeaza un obiect RenderedImage bazându-se pe datele receptionate de la server. Odata ce este creat acest obiect RenderedImage, nivelul de rezolutie nu poate fi schimbat.
Exemplul 7.25. prezinta codul pentru o operatie IIPResolution.
Exemplul 7.25. Aplicarea operatiei IIPResolution |
public class IIPResolutionTest } else if(args[i].equalsIgnoreCase("-res")) } String url = SERVER + "FIF=" + imagePath; new IIPResolutionTest(url, resolution); } public IIPResolutionTest (String url, int resolution) |
Sistemul de codoare JAI suporta o mare varietate de formate de imagine pentru scrierea imaginilor spre un fisier sau spre un obiect OutputStream pentru urmatoarele procesari.
Pentru scrierea unei imagini într-un fisier specificat, în formatul si cu parametrii de codare specificati, se foloseste operatia FileStore. Formatele suportate de operatia FileStore sunt BMP, JPEG, PNG, PNM si TIFF.
Pentru a coda o imagine spre un OutputStream în formatul specificat utilizând parametrii de codare furnizati de un parametru operatie ImageEncodeParam, se foloseste operatia Encode.
Exemplul 7.26. este un fragment de cod care demonstreaza utilizarea operatiilor Encode si FileStore.
Exemplul 7.26. Scrierea unui obiect OutputStream si a unui File |
// defineste numele fisierelor sursa si destinatie String inputFile = /img/p1.tif String outputFile = /img/p2.bmp // încarca imaginea de intrare RenderedOp src = JAI.create("fileload", inputFile); // codeaza fisierul ca si imagine BMP FileOutputStream stream = new FileOutputStream(outputFile); JAI.create("encode", src, stream, BMP, null); // stocheaza imaginea în format BMP. JAI.create("filestore", src, outputFile, BMP, null); |
Pentru a coda în format BMP se poate utiliza operatia BMP împreuna cu alti parametrii de codare (versiune BMP, modul de asezare a datelor în fisier), ceea ce se exemplifica în Exemplul 7.27.
Exemplul 7.27. Codarea si scrierea în format BMP |
File f; // fisierul în care se va stoca imaginea PlanarImage img; // imaginea sursa // OutputStream-ul spre care se scrie FileOutputStream fos = new FileOutputStream(f); // clasa JAI, specifica diferiti parametrii de codare BMP BMPEncodeParam param = new BMPEncodeParam(); // specifica versiunea BMP param.setVersion(BMPEncodeParam.VERSION_3); // specifica formatul de scriere a datelor de sus în jos param.setTopDown(true); // specifica posibilitatea de compresie a datelor param.setCompressed(false); // creeaza un codor BMP ImageEncoder enc = ImageCodec.createImageEncoder( "BMP", fos,param); // codeaza imaginea si o scrie la OutputStream enc.encode(img); fos.close(); |
La codarea în formatul JPEG se pot specifica diferiti parametrii si diferite optiuni de codare, cum ar fi posibilitatea de eliminare a header-ului JFIF, modificarea parametrilor de codare DCT (Disrete Cosine Transform), modificarea tabelei de cuantizare, subesantionare pe orizontala si verticala, controlul compresiei si al calitatii imaginii, etc. Fragmentul de cod urmator (Exemplul 7.28.) exemplifica codarea unei imagini în format JPEG.
Exemplul 7.28. Codare JPEG |
// tabela de cuatizare private int[] qTable=new int[]= ; File f; // fisierul în care se va stoca imaginea PlanarImage img; // imaginea sursa // OutputStream-ul spre care se scrie FileOutputStream fos = new FileOutputStream(f); // clasa JAI, specifica diferiti parametrii de codare JPEG JPEGEncodeParam encodeParam = new JPEGEncodeParam(); // creeaza un nou tabel de cuantizare encodeParam.setQTable(0, 1, qTable); encodeParam.setQTable(1, 1, qTable); encodeParam.setQTable(2, 1, qTable); // tabelul de cuantizare pentru datele de luminanta encodeParam.setLumaQTable(qTable); // tabelul de cuantizare pentru datele de crominanta encodeParam.setChromaQTable(qTable); // creaza un nou tabel de cuantizare si îl înlocuieste pe cel existent // si configureaza nivelul de calitate a imaginii encodeParam.setQuality(0.87F); // subesantionare pe orizontala aplicata pentru fiecare banda encodeParam.setHorizontalSubsampling(0, 1); encodeParam.setHorizontalSubsampling(1, 1); encodeParam.setHorizontalSubsampling(2, 2); // subesantionare pe verticala aplicata pentru fiecare banda encodeParam.setVerticalSubsampling(0, 1); encodeParam.setVerticalSubsampling(1, 1); encodeParam.setVerticalSubsampling(2, 2); // specifica intervalul de restart în Minimum Coded Units (MCU) encodeParam.setRestartInterval(res); // scrie doar imaginea si nu scrie tabelele (JPEG abreviat) encodeParam.setWriteTablesOnly(false); encodeParam.setWriteImageOnly(true); // scrie headerul JFIF encodeParam.setWriteJFIFHeader(true); // creeaza un codor JPEG ImageEncoder encoder = ImageCodec.createImageEncoder( "JPEG", fos, encodeParam); encoder.encode(img); // codeaza JPEG si scrie spre OutputStream fos.close(); |
În Exemplul 7.29. se prezinta codarea unei imagini în format PNG folosind o codare pe 16 biti si întretesere Adam7.
Exemplul 7.29. Codare PNG |
File f; // fisierul în care se va stoca imaginea PlanarImage img; // imaginea sursa // OutputStream-ul spre care se scrie FileOutputStream fos = new FileOutputStream(f); // clasa JAI, specifica diferiti parametrii de codare PNG PNGEncodeParam param = PNGEncodeParam.getDefaultEncodeParam(img); // specifica numarul de biti adâncime doriti param.setBitDepth(16); // codeaza folosind întretesere Adam7 param.setInterlacing(true); // creeaza un codor PNG având parametrii specificati ImageEncoder enc = ImageCodec.createImageEncoder( "PNG", fos, param); // codare si scriere în fisier enc.encode(img); fos.close(); |
Exemplul urmator (Exemplul 7.30.) ilustreaza modul de codare si scriere într-un fisier de format PNM, folosind un format ASCII de reprezentare a datelor.
Exemplul 7.30. Codare în format PNM |
File f; // fisierul în care se va stoca imaginea PlanarImage img; // imaginea sursa // OutputStream-ul spre care se scrie FileOutputStream fos = new FileOutputStream(f); // clasa JAI, specifica diferiti parametrii de codare PNM PNMEncodeParam param = new PNMEncodeParam(); // specifica modul de codare ASCII param.setRaw(true); // creeaza codorul ImageEncoder enc = ImageCodec.createImageEncoder( "PNM", fos, param); // codeaza imaginea apoi o scrie în fisier enc.encode(img); fos.close(); |
Exemplul 7.31. reprezinta un fragment de cod care codeaza o sursa Rendered în format TIFF.
Exemplul 7.31. Codare în format TIFF |
File f; // fisierul în care se va stoca imaginea PlanarImage img; // imaginea sursa // OutputStream-ul spre care se scrie FileOutputStream fos = new FileOutputStream(f); // clasa JAI, specifica diferiti parametrii de codare TIFF TIFFEncodeParam param = new TIFFEncodeParam(); // scrie datele în format segmentat param.setWriteTiled(true); // creeaza codorul ImageEncoder enc = ImageCodec.createImageEncoder( "TIFF", fos, param); // codeaza si scrie în fisier enc.encode(img); fos.close(); |
Desi nici un API de procesare a imaginilor nu spera sa captureze enorma varietate de operatii care se pot executa asupra imaginilor digitale, JAI API suporta un numar foarte mare de operatii, fiind conceput de la început ca sa încurajeze programatorii sa scrie extensii decât sa manipuleze datele imagine în mod direct. JAI permite în mod virtual ca orice algoritm de procesare a imaginilor sa fie adaugat la API si sa fie folosit ca si cum ar fi fost o parte nativa a acestuia.
Mecanismul pentru adaugarea diverselor functionalitati la API pate fi prezentat la mai multe nivele de încapsulare si complexitate. Aceasta permite programatorilor care doresc sa adauge operatii simple la API sa se confrunte cu concepte simple, în timp ce extensiile mai complexe au un control total asupra mediului lor la cel mai jos nivel de abstractizare.
JAI API suporta o mare varietate de stiluri de programare, care include modul de executie imediat si amânat pentru tipuri diferite de aplicatii de procesare a imaginilor.
Toate extensiile la JAI necesita adaugarea de noi clase. Toate clasele trebuie grupate în pachete ca mod de organizare eficient si pentru separare a acestora de pachetele oferite de altii.
Pentru a extinde JAI API prin crearea de noi operatii, este nevoie sa scriem o noua subclasa a OpImage. Aceasta se poate face prin subclasarea a uneia sau mai multe clase utilitare pentru a automatiza unele dintre detaliile a operatorului pe care dorim sa-l implementam.
Odata creati, noii operatori pot fi facuti disponibili pentru utilizatori în mod transparent si fara modificarea codului sursa de la utilizator, folosind mecanismul de registrii JAI.
Pentru a crea un nou operator, trebuie create urmatoarele clase:
O clasa care extinde clasa OpImage sau oricare dintre subclasele acesteia. Aceasta noua clasa este cea care executa procesarea.
O clasa care extinde clasa OperationDescriptor. Aceasta noua clasa descrie operatia prin nume, lista de parametrii, etc.
O clasa care implementeaza java.awt.image.renderable.Rendered-ImageFactory, daca operatorul va functiona doar în modul Rendered.
Operatiile care sunt create folosind una dintre metodele JAI.create trebuie definite în registryFile, care este inclus în arhiva jai_core.jar. Fiecare operatie are asociat un obiect OperationDesciptor (marcata prin "odesc" în registryFile), care furnizeaza o descriere textuala a operatiei si specifica numarul si tipul sursei precum si parametrii. Acest descriptor specifica de asemenea daca operatia suporta modul Rendered, sau Renderable, sau amândoua.
Toate numele de operatii de nivel înalt (de exemplu, Rotate, Convolve si AddConst) sunt mapate la instante de tipul RenderedImageFactory (RIF) si/sau ContextualRenderedImageFactory (CRIF) care sunt capabile de instantierea de lanturi OpImage pentru executia operatiei specificate. Obiectele RIF sunt utilizate pentru operatii în modul Rendered, iar obiectele CRIF sunt utilizate pentru operatii în modul Renderable sau în modurile Rendered si Renderable.
Pentru evitarea problemelor asociate cu editarea directa a registryFile si reîmpachetarea lui, se pot înregistra obiectele OperationDescriptor, RIF si CRIF utilizând metodele registerOperationDescription, registerRIF si registerCRIF din clasa OperationRegistry. Singurul dezavantaj al acestei metode de înregistrare este faptul ca noul operator nu va fi în mod automat reîncarcat de fiecare data când un program JAI este executat, deoarece operatia nu este înregistrata în registryFile. Noua operatie va trebui înregistrata de fiecare data înainte de a putea fi folosita.
Pentru a înregistra temporar o noua operatie trebuie parcursi urmatorii pasi:
Înregistrarea numelui operatiei
Numele operatiei la nivel înalt, denumit si operation descriptor (descriptor de operatie), este înregistrat printr-un apel la metoda registerOperationByName sau la metoda registerOperationDescriptor. Numele descriptorului de operatie trebuie sa fie unic.
Odata ce un descriptor de operatie este înregistrat, acestea pot fi obtinute prin nume prin apelul metodei getOperationDescriptor.
Înregistrarea setului de obiecte RIF
Obiectele RIF sunt înregistrate folosind metoda registerRIF. Fiecare RIF este înregistrat cu un nume de operatie specific. Exista de asemenea metode similare pentru înregistrarea obiectelor CRIF.
Exemplul
7.32.
prezinta câteva fragmente dintr-un program în
care se înregistreaza temporar o noua operatie. Dupa
înregistrarea operatiei MyThreshold se aplica aceasta
operatie asupra unei imagini Rendered. În exemplu se prezinta
si codul sursa al claselor care descriu operatia.
Exemplul 7.32. Înregistrarea si utilizarea unei noi operatii JAI |
//înregistreaza un nou OperationDescriptor MyThresholdDescriptor thDescriptor = new MyThresholdDescriptor(); OperationDescriptor odesc = thDescriptor; RenderedImageFactory rif = thDescriptor; String operationName = "MyThreshold"; String productName = "medimaging"; OperationRegistry or = JAI.getDefaultInstance().getOperationRegistry(); or.registerOperationDescriptor(odesc,operationName); or.registerRIF(operationName,productName,rif); PlanarImage image; // sursa // utilizarea operatorului care tocmai a fost înregistrat // construirea blocului de parametrii ParameterBlock pb = new ParameterBlock(); pb.addSource(image); pb.add(55); pb.add(174); // aplicarea operatorului PlanarImage img = JAI.create("MyThreshold", pb, renderHints); * O clasa de tipul OperationDescriptor si RenderedImageFactory si o clasa de tipul OpImage pentru crearea unei * operatieii threshold modificat. public class MyThresholdDescriptor extends OperationDescriptorImpl implements RenderedImageFactory , , , , , , , }; * Numele parametrilor pentru operatia "MyThreshold". */ private static final String[] paramNames = ; * Tipurile de clase pentru parametrii. */ private static final Class[] paramClasses = ; * Parametrii impliciti private static final Object[] paramDefaults = ; /** Constructorul. */ public MyThresholdDescriptor() * creeaza un MyThresholdOpImage cu ParameterBlock dat daca * MyThresholdOpImage accepta acest ParameterBlock. public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) return new MyThresholdOpImage(paramBlock.getRenderedSource(0), new ImageLayout(), (Integer)paramBlock.getObjectParameter(0), (Integer)paramBlock.getObjectParameter(1)); * verifica daca toti parametrii din ParameterBlock au * tipul corect înainte de construirea MyThresholdOpImage public boolean validateParameters(ParameterBlock paramBlock) if (!(arg instanceof Integer)) } return true; * MyThresholdOpImage este o extensie a PointOpImage care preia * doi parametri întregi si o sursa si executa threshold asupra sursei class MyThresholdOpImage extends PointOpImage * Executa o operatie threshold modificata asupra pixelilor în * suprafata dreptunghiulara data. Valorile esantioanelor sub o * limita inferioara sunt limitate la 0, în timp ce acele peste o limita * superioara sunt limitate la 255. Rezultatele sunt returnate în * WritableRaster ca destinatie. protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) if (dstAccessor.isDataCopy()) * Calculeaza o arie dintr-un Raster tip byte folosind o sursa * RasterAccessor si o destinatie RasterAccesor. private void byteLoop(RasterAccessor src, RasterAccessor dst) else if (pixel > param2) else srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } * Calculeaza o arie dintr-un Raster de tip int folosind o sursa * RasterAccessor si o destinatie RasterAccesor. private void intLoop(RasterAccessor src, RasterAccessor dst) else if (pixel > param2) else srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } |
Pentru a crea un decodor si un codor pentru un anumit format specific este nevoie de crearea a trei clase:
O clasa de tipul ImageCodec pentru controlul fisierelor imagine în formatul pentru care se doreste codorul.
O clasa de tipul ImageDecoder necesara pentru citirea fisierelor imagine în formatul pentru care se doreste decodorul
O clasa de tipul ImageEncoder necesara pentru scrierea fisierelor imagine în formatul pentru care se doreste codorul.
Clasele de codoare care acompaniaza pachetele JAI API sunt furnizate programatorului doar ca suport convenabil pentru lucrul cu fisiere imagine. Aceste clase nu fac parte în mod oficial din JAI API, functionalitatile oferite de aceste clase sunt implementate în Java Image I/O API, care este disponibil în J2SE începând cu versiunea 1.4. Cu toate acestea, clasele pentru lucrul cu fisiere imagine din JAI sunt înca disponibile în continuare, dar este posibil ca ele sa fie eliminate în urmatoarele editii JAI. Aplicatiile care utilizeaza operatii I/O cu imagini este de preferat sa lucreze cu Java Image I/O API.
Clasa ImageCodec permite crearea de codoare si decodoare de imagini. Instantele clasei ImageCodec pot fi înregistrate prin nume. Metoda registerCodec asociaza un obiect ImageCodec cu un nume dat. Orice codor care a fost înainte asociat cu acel nume este eliminat. Odata ce un codor este înregistrat, numele asociat cu acesta poate fi folosit ca parametru name în metodele createImageEncoder si createImageDecoder.
Clasa ImageCodec mentine un registru cu obiecte FormatRecognizer care examineaza un InputStream si determina daca acesta adera la un format controlat de un ImageCodec particular. Un FormatRecognizer este adaugat la registrii cu metoda registerFormatRecognizer.
Metoda getCodec returneaza obiectul ImageCodec asociat cu un nume dat. Daca nu este înregistrat nici un codor cu acel nume, se returneaza null.
|