1 Grafikus interfész adatfolyam gráf alapú algoritmusok tervezéséhez és futtatásához Pázmány Péter Katolikus Egyetem Információs Technológiai Kar Mérn...
Grafikus interfész adatfolyam gráf alapú algoritmusok tervezéséhez és futtatásához
Pázmány Péter Katolikus Egyetem Információs Technológiai Kar Mérnök informatikus BSc
Buzsáky Andrea Témavezető: Cserey György Konzulens: Soós Balázs Gergely
Budapest, 2011.
2
Nyilatkozat Alulírott Buzsáky Andrea, a Pázmány Péter Katolikus Egyetem Információs Technológiai Karának hallgatója kijelentem, hogy ezt a szakdolgozatot meg nem engedett segítség nélkül, saját magam készítettem, és a szakdolgozatban csak a megadott forrásokat használtam fel. Minden olyan részt, melyet szó szerint, vagy azonos értelemben, de átfogalmazva más forrásból átvettem, egyértelműen a forrás megadásával megjelöltem. Ezt a szakdolgozatot más szakon még nem nyújtottam be. …....................................................... Buzsáky Andrea
Tartalmi összefoglaló
3
Tartalmi összefoglaló Szoftverfejlesztés során előszeretettel használunk különféle modellezési módszereket. Ezek közül az egyik legelterjedtebb a folyamatgráfok rajzolása, mely különösen jelfeldolgozási, illetve egyes képfeldolgozó algoritmusok esetén hasznos. Munkám során képfeldolgozó algoritmusok tervezéséhez készítettem egy olyan eszközt, mellyel megrajzolhatjuk a kívánt algoritmust, majd a program ezt a rajzot értelmezi, és futtatja is. Ehhez egy már kész, nyílt forrású diagramok készítésére használt szoftvert, a Diát választottam grafikus megjelenítő környezetnek, mivel a grafikus felület elkészítése további, hosszas fejlesztést igényelt volna. Dolgozatomban ezt a fejlesztést szeretném bemutatni, a tervezés és a kutatómunka kezdeti fázisaitól.
Abstract
4
Abstract In software development, different modeling methods are used often. One of these, possibly the most frequent one is drawing flow graphs. These flow graphs are the most useful in signal processing and some image processing algorithms. During my work I produced an instrument for designing image processing algorithms, that is able to draw the graph chosen algorithm, interpret it, generate the source, then compile and run. For the graphic user interface I chose Dia, an opensource software designed to draw diagrams, because creating it my own would need even more development. Besides, Dia has a good interface to integrate different scripts I needed. In my thesis I would like to introduce this development from the beginning of designing and research.
Tartalomjegyzék
5
Tartalomjegyzék Nyilatkozat.................................................................................................................................2 Tartalmi összefoglaló.................................................................................................................3 Abstract......................................................................................................................................4 Tartalomjegyzék.........................................................................................................................5 1 Bevezetés................................................................................................................................7 2 Grafikus programozás.............................................................................................................9 2.1 Unified Modeling Language...........................................................................................9 2.2 Control Flow Graph......................................................................................................12 2.3 Data Flow Graph...........................................................................................................13 3 Grafikus programozást támogató rendszerek........................................................................14 3.1 SAP NetWeaver Developer...........................................................................................14 3.2 NI LabView...................................................................................................................16 3.3 Harpia...........................................................................................................................17 4 Rendszerterv.........................................................................................................................20 4.1 Frontend........................................................................................................................20 4.2 Middle-end....................................................................................................................21 4.3 Backend........................................................................................................................22 5 A Dia felépítése.....................................................................................................................23 5.1 Saját sheet létrehozása..................................................................................................24 5.2 Fordítás.........................................................................................................................25 6 Az ImageProcess frontend modul..........................................................................................28 6.1 Telepítés: configure – make – make install...................................................................28 6.2 Modulterv.....................................................................................................................29 6.3 Megvalósítás.................................................................................................................30 7 Az ImageProcess middle-end modul.....................................................................................38 7.1 Az osztályok..................................................................................................................38 7.2 Az algoritmus................................................................................................................40 7.3 Példa.............................................................................................................................40 8 Összegzés, kitekintés............................................................................................................42
legelterjedtebb az objektumközpontú (objektum-orientált) megközelítés. Ennek egyik oka az objektum-orientált szemlélet (OOPi) elterjedése, melyet az OOP-t támogató programozási nyelvek térnyerése is támogat. Az objektumközpontú módszereket segítik számítógépes szoftverek, az úgynevezett CASEii eszközök is1, melyek nagyban megkönnyítik a fejlesztők munkáját. Ezek olyan eszközök, melyek a tervezés során használt modellek elkészítésében segítenek, majd ezek alapján a rendszer főbb részei automatikusan létrehozhatók. Tipikusan ilyen feladat például a diagramok készítése, adatbázisok tervezése, de az adminisztráció, a projektmenedzsment folyamata is. A fejlesztést legtöbb esetben valamely elterjedt programtervezési minta alapján végzik 2, melyek egyike az úgynevezett „rapid (throwaway) prototyping”, vagyis az eldobható prototípus készítése. Ilyenkor a feladat specifikálása során készítünk egy olyan „kezdetleges” szoftverprototípust, melyet a későbbi felhasználóknak meg lehet mutatni, ám a végleges rendszerben már nem fog szerepelni. A forrásnak nem kell jól strukturáltnak lennie, ezúttal csak a végeredmény számít, és hogy gyorsan elkészüljön, hisz a végső változatban már más kód fog szerepelni. Tipikusan ilyen feladat például egy grafikus felület (GUI iii) megtervezése, melyhez léteznek már olyan eszközök, melyekkel megrajzolhatjuk az egyes elemek helyét – mint például a Microsoft Visual Studio3 fejlesztői környezet. Különösen nagyobb projektek tervezése esetén használják előszeretettel az úgynevezett UML iv modellezési rendszert, mely szabványos szintaktikát definiál a szoftver különböző szempontból való vizsgálatára. Vannak szoftverek, melyek ezekből a modellekből képesek automatikusan kódot generálni. Az automatikus kódgenerálás csökkenti az implementálási hibákat, és felgyorsítja a tervezés-megvalósítás ciklusokat. Az elérhető megoldások az objektumok statikus vázát (skeletont) készítik el, a dinamikus viselkedés transzformációja annak bonyolultsága miatt nem megoldott. Egy algoritmus tervezésekor is hasznos lehet egy olyan eszköz, mellyel megrajzolhatjuk az [i] [ii] [iii] [iv]
Object-oriented programming, l. 44. oldal Computer Aided Software Engineering, l. 44. oldal Graphic User Interface, l. 44. oldal Unified Modeling Language, l. 44. oldal
Bevezetés
8
algoritmus folyamatgráfját, amit az eszköz értelmezni, majd futtatni is tud, így az eredményt azonnal láthatjuk. Az általam tervezett program az algoritmusok egy speciális osztályához, a képfeldolgozó algoritmusokhoz ajánl grafikus reprezentációt, és automatikus kódváz generálást. Dolgozatomban először áttekintek néhány ismert, általánosan használt modellezési módszert, köztük az én munkámban is fontos szerepet kapó adatfolyam gráfokat. Utána bemutatok három olyan programot, mely a grafikusan elkészített diagramokból generál forrást, és azt futtatja is, ezek az SAP Netweaver Developer Studiója, az NI LabView, valamint a Harpia. Ez utóbbi azért is különösen érdekes, mert szintén az általam használt képfeldolgozó könyvtárat használja, s a tervezés kezdeti szakaszában az ő neve is felmerült a Dia mellett, mint grafikus környezet. A dolgozat további részében az általam készített rendszer részletezése következik. Előbb a rendszerterv, majd egy részletesebb ismertető a Dia környezetből, végül az általam elkészített modulok bemutatása, és a fejlesztés egyes lépései.
Grafikus programozás
9
2 Grafikus programozás Grafikus programozásnak nevezzük azt, amikor programozás során nem programkódot írunk, hanem a program egyes elemeit egy grafikus felületen rakjuk össze. Léteznek kifejezetten grafikus programozási nyelvek (VPLi), erre mutatok is majd egy példát a 3.2. fejezetben. Szoftverek tervezése során sokszor használunk különféle diagramokat a rendszer többféle szempontból való modellezéséhez. Modellezhetjük az egyes modulok időrendisége szempontjából, az adatok állapotváltozásainak szempontjából, az egyes felhasználói interakciók alapján. Egy szoftverfejlesztési folyamat során a tisztánlátás érdekében valamennyi ilyen szempontot meg kell vizsgálni. Ezekhez a modellezési feladatokhoz definiál szabványos szintaktikát az UML.
2.1 Unified Modeling Language A tervezés folyamatában, főleg nagyobb projektek tervezése során használják előszeretettel az UML modellezési rendszert4. Kialakulása a 80-as, 90-es évekre tehető, mikorra több objektumközpontú modellezési irányzat is kialakult, végül 1997. novemberében vette át az 1.1-es verziót az OMGii, mely azóta is fejleszti. Több diagramtípust is magába foglal: •
struktúra diagramok: például osztály-, komponens-, és objektumdiagramok
•
viselkedési diagramok: például use-case, állapotgép diagram
•
interakció diagramok: például kommunikációs diagram, szekvencia diagram
Az évek során több kritika is érte az UML-t, ezek közül az egyik az, hogy túl nagy és bonyolult, túl sok diagramot tartalmaz, melyek egy részét alig használják, egy más része pedig redundáns, s míg egy kisebb rendszert megtervezünk UML-lel, az idő alatt a program bőven megírható5. Másik nagy probléma az UML-lel, hogy nincs szigorúan definiálva, szemantikája pontatlan. Mégis, nagy projektek esetén nélkülözhetetlen a megfelelő dokumentációhoz, s szintén nagy előny, hogy az elkészített diagramok segítségével a kódolás egy része könnyen és pontosan automatizálható. Erre vannak különféle szoftverek, a drága, üzleti rendszerektől kezdve (pl. az [i] Visual Programming Language, l. 44. oldal [ii] Object Management Group, l. .oldal
Grafikus programozás
10
IBM Rational Rose-a6) a szintén kiváló opensourcei megoldásokig, melyek nagy része szinten képes diagramokból kódot generálni (pl. Umbrello7). Az UML-nek vannak úgynevezett profiljai, bővítményei is. Ilyen az executable (futtatható) UML (xUML, xtUML) is, mellyel a modellt egy kevésbé absztrakt nyelvre fordíthatjuk.
2.1.1 Használati eset diagram Használati eset (use case) diagrammal azt modellezhetjük, az egyes felhasználók számára milyen funkciókat hajt végre a rendszer. A használati eset diagramban három (egyes források szerint kettő) objektum szerepel: •
Szereplők (actors): egy felhasználó, személy, szervezet, vagy egy külső rendszer, ami valamilyen interakciót hajt végre a rendszerben. A szereplők egymással való interakcióit nem jelöljük a használati eset diagramban.
•
Használati esetek (use cases): folyamatok egy sorozatát írja le, melyeknek valamiféle eredménye van valamelyik szereplő számára. Egy vízszintes ellipszissel jelöljük.
•
Rendszerdoboz (system boundary boxes): opcionális jelölés, a rendszert jelöli. Azok az esetek, melyek a dobozon belül vannak, a rendszer hatálya alá tartoznak, a többi azon kívül.
Használati eset diagramban is tudjuk ábrázolni az egyes esetek közti relációkat. Pontosabban négyféle relációt jelölhetünk: •
Include: az interakciók egyes lépéseiben egyik eset magába foglalhat egy másik esetet, ezt nevezzük include-nak. Jelölése egy szaggatott nyíl a külső esettől a belsőig, <> címkével
•
Extend: egy eset lehet egy másik eset kibővítése, ezt nevezzük extendnek. Jelölése szintén szaggatott nyíl, azonban most <<extend>> címkével látjuk el. Gyakran használjuk opcionális esetekben, amikor egy használati eset nem hajtódik végre minden esetben.
•
Általánosítás/specializálás: a használati eseteknek lehetnek közös viselkedési formái, feltételei egy másik, általánosabb esettel. Ilyenkor nevezzük őket a közös, általánosított esetnek a specializációinak. Jelölése egyenes vonal, üres háromszöggel, mely a specializációtól mutat az általános objektum felé (vagyis a megszokott, általánosítás-specializálás jelölés).
•
Asszociációk, kapcsolatok: kapcsolatok a szereplők és használati esetek között egybefüggő vonallal, esetleg nyíllal (természetesen nem az előbb említett általánosítás/specializálást jelölő nyíllal) az egyik végén jelöljük.
[i] Opensource, l. 44.oldal
Grafikus programozás
11
Az 1. képen egy használati eset diagramot látunk.
Kép 1: use case diagram
2.1.2 Szekvencia diagram A szekvencia diagram párhuzamos, függőleges vonalakként jelöli az egymással parallel futó folyamatokat, objektumokat, s ezekre merőleges, vízszintes vonalként a közöttük zajló kommunikációt. Erre látunk egy példát a 2. képen.
Kép 2: szekvencia diagram A szekvencia diagramnak egy speciális fajtája, amikor nem folyamatokat ábrázolunk, hanem adatokat, s ezeknek a változását követjük nyomon a diagramon.
2.1.3 Osztálydiagram Osztálydiagrammal objektumorientált módon tervezhetjük meg a programunkat. Könnyen átláthatóvá teszi a programstruktúrát, ezáltal a későbbi karbantartást, továbbfejlesztést is megkönnyíti. Az objektumok (osztályok) jelölése úgy történik, hogy az osztály nevén kívül feltüntetjük az osztály változóit, valamint a függvényeket. Változók és függvények esetén azok láthatóságát is
Grafikus programozás
12
jelöljük (Kép 3):
Kép 3: class •
public (mindenki láthatja): +
•
protected (csak önmaga, illetve leszármazottak által látható): #
•
private (csak önmaga által látható): -
•
package (csak az ugyanabban a package-ben lévők láthatják): ~
Az osztályok közötti relációkat is jelezhetjük. Az UML 2.0-ban a következő öt relációt különböztetjük meg: •
asszociáció: az osztály elemei egy másik osztály
Kép 4: asszociáció
elemeivel működnek együtt (Kép 4) •
öröklődés: az osztály egy ősosztály leszármazottja (Kép 5)
•
megvalósítási reláció: interfész, absztrakt osztály
Kép 5: öröklődés
megvalósítása (Kép 6) Az utolsó két reláció rész-egész viszonyt fejez ki: •
aggregáció: az osztály tartalmazza egy másik osztály elemeit, a rész önállóan is létezhet (Kép 7)
•
Kép 6: megvalósítás
kompozíció: az osztály tartalmazza egy másik osztály elemeit, de a rész nem létezhet az egész nélkül (Kép 8)
Több olyan környezet is létezik, amellyel a megrajzolt
Kép 7: aggregáció
osztálydiagramból legenerálhatjuk a program vázát, ilyen például az ArgoUML8 vagy az Umbrello Kép 8: kompozíció
2.2 Control Flow Graph A CFGi egy irányított, összefüggő gráf. CFG = (N,P), ahol N a csúcsok halmaza (például [i] Control Flow Graph, l. 44. oldal
Grafikus programozás
13
hozzárendelés, összeadás, logikai műveletek), míg a P az élek halmaza (például precedencia relációk). Egy él (n1,n2)∈P jelenti, hogy n2 következik n1 végrehajtása után. A csúcsok jelentik az egyes műveleteket, míg az élek a közöttük fennálló relációkat. Ábrázolható feltételes végrehajtás. Egy művelet után pontosan egy újabb soron következő művelet hajtódhat végre, melynek kijelölése függ a feltételektől (mely élekhez kapcsolódik). A feltételeket az élek mutatják. A CFG-k tartalmazhatnak ciklusokat, amelyek a folyamat iteratív viselkedését jelzik (hurkok). A gráf összefüggő kell legyen. Ha a gráf nem összefüggő, az azt jelenti, hogy az a blokk a végrehajtás során elérhetetlen, tehát a kód sem fog lefutni, vagyis nyugodtan törölhető. Ez a kód optimalizálását segíti. Ha a kilépési blokk a kiindulási blokkból elérhetetlen, a kód végtelen ciklusba fut.
2.3 Data Flow Graph A DFGi szintén irányított gráf, adatok, transzformációk és összeköttetések. Az egyes transzformációk lehetnek elemi utasítások vagy magasabb szintű átalakítások. A transzformációk operandusai és a transzformációk eredményei lehetnek adattárolók, vagy kapcsolódhat közvetlenül a következő transzformációhoz is. Az utasításokat egyéb, konstans paraméterek is befolyásolhatják, melyek a program futása közben nem változnak. Ezeket nem tüntetjük föl a gráfban. A DFG egy programrész összes lehetséges adatútját tartalmazza, míg a CFG a vezérlési szerkezeteket: adatfüggő elágazást, ciklust. Jellemzően jelfeldolgozó algoritmusokat szoktunk DFG-vel ábrázolni, illetve egyes képfeldolgozó algoritmusokat. A jelfeldolgozó algoritmusok egy része szűrő jellegű, a bemenet-kimenet transzformáció teljesen statikus. Ezek működése tisztán adatfolyam jellegű. Az egyes műveleteket úgy is lehet ábrázolni, hogy nincs köztük kapcsolódási pont, vagyis a DFG nem feltétlenül kell összefüggő legyen.
[i] Data Flow Graph, l. Error: Reference source not found. oldal
Grafikus programozást támogató rendszerek
14
3 Grafikus programozást támogató rendszerek 3.1 SAP NetWeaver Developer A német SAP AG. jelenleg a világ egyik vezető üzleti szoftver szolgáltatója, az általa kínált vállalatirányítási rendszereket széles körben használják. Az SAP rendszer programozási nyelve az ABAP. Az SAP-hoz a felhasználók klienseken keresztül csatlakozhatnak. Erre egy példa az SAP GUI, de böngészőn keresztül, HTTP, HTTPS protokollon is elérhetjük a rendszert. A lekérdezések grafikus felületeit az ABAP dynpróknak nevezi, ebből jött a web dynpro elnevezés azokra a GUI-kra, amiket már nem a SAP GUI-ban, hanem egy vékony kliensen, például böngészőn keresztül érhet el a felhasználó. A web dynprókat már nem csak ABAPban, hanem Javában is el lehet készíteni az SAP NWDS i környezetben. Az NWDS egy Eclipse alapú fejlesztői környezet. A cél az volt, hogy minimális programozási tudás nélkül is képesek legyenek üzleti folyamatokat elkészíteni. Nagyban megkönnyíti a feladatot, hogy az üzleti folyamatot a Visual Composer felületen egy folyamatábrával lemodellezve a NWDS legenerálja az egész háttérben futó kódot, a felhasználók által látott kezelőfelületet a szükséges form mezőkkel (ez utóbbit természetesen lehet módosítani). Az NWDS-ben a többféle felületen (view-n) ugyanazt a projektet több nézőpontból is megközelíthetjük, több „irányból” szerkeszthetjük. A Web Dynpro felületen a web dynprót szerkeszthetjük, például a View Editorban itt tudjuk szerkeszteni a felhasználók által látott felületet, míg a Composite Designerben például a folyamatgráfot, illetve magát a processzt szerkeszthetjük (sajnos épp az ekkora mértékű „mindent tudni akarás” az egyik legnagyobb nehézsége a környezetnek: nagyon sok modul van benne, melyeket átlátni nem kevés munka és nagyfokú hozzáértést igényel – azonban, ha az embernek sikerült ezeket átlátnia, nagyon megkönnyíti a munkát). Ha kész az alkalmazásunk, feltöltjük (Deploy) a NetWeaver Application Serverre, s aktiválás után futtathatjuk is.
3.1.1 A folyamat elkészítése [i] NetWeaver Developer Studio, l. 44. oldal
Grafikus programozást támogató rendszerek
15
Kép 9: Composite Designer felület. Balra a rajzvászon, jobbra az eszközpaletta. A folyamatgráfot a Composite Designer (Kép 9) felületen rajzolhatjuk meg. Kapunk egy rajzvásznat, s a jobb oldalon egy listát, ahonnan behúzhatjuk a kívánt elemet. A gráf megrajzolása egy poollal kezdődik, ez lesz maga a processz. A processzbe létrehozhatunk egy vagy több „oszlopot”, úgynevezett lane-t, egyet-egyet a process minden egyes szereplőjének, ahogy az a 10. képen látható9. Tipikus példa, a hivatalos SAP bemutatókban és példákban gyakran találkozunk az engedélyezés folyamatával. Van az alkalmazott, aki egy
bizonyos
üzemanyagköltséget
összeget, szeretne
például
elszámoltatni
a
vállalattal. Amennyiben ez az összeg nem halad meg egy bizonyos határt, a rendszer automatikusan elfogadja a kérést. Ha meghaladja, a kérést továbbítja az alkalmazott felettesének, aki vagy elfogadja, vagy elutasítja, majd a rendszer értesíti a válaszról az alkalmazottat. Ha a folyamat egy eleme az adott szereplő lane-jében van, a folyamatnak azt a pontját ő fogja végrehajtani. Kép 10: engedélyezési folyamat pool-ja három lane-nel Fontos megemlíteni, hogy a szereplők nem csak felhasználók lehetnek! Szereplő lehet egy másik SAP rendszermodul, sőt, teljesen idegen, külső rendszer is, például egy webáruház esetén külső rendszer lehet például a banki rendszer, ami, miután az ügyfél megrendelte a kívánt terméket, és a rajta keresztül fizetett is, jelzi a fizetés teljesültét az SAP-nak, ami csak ezután indítja el a soron következő (postázás előkészítése, raktáron lévő mennyiség módosítása, stb.) folyamatokat.
Grafikus programozást támogató rendszerek
16
3.2 NI LabView Az 1976-ban alapított National Instruments meghatározó név az informatika világában 10. Központja a texasi Austinban van, több, mint negyven országban vannak jelen világszerte, több, mint 30000 partnercéggel. Dr. James Truchard alapította, s tíz év múlva, 1986-ban megjelentették első grafikus fejlesztő eszközüket, az azóta is piacvezető LabView-ti. Az első, 1.0-s verzió 1986-ban jelent meg, a legújabb 2010. augusztusában, LabView 2010. A 2005-ös 8.0-s verzió óta rendszeresen augusztus első heteiben jelentetik meg az újabb verziókat, melyet a következő év februárjában egy, a hibákat javító bug-fix kiadás követ. 2009. óta a verziókat a kiadás éve alapján számozzák. Széles körben használt eszköz, mérnökök és tudósok világszerte használják mérések, tesztek elvégzésére, ellenőrző rendszerek fejlesztésére. Hardvereszközök integrációja is megoldott vele, az elérhető több száz beépített könyvtár magas szintű analizálást, jelfeldolgozást tesz lehetővé11. A LabView programokat, függvényeket Virtual Instrument-nek, VI-nek nevezzük, az elmentett programjaink is a .vi kiterjesztést kapják. A diagramon a megfelelő ikonokat „drótokkal” összekötjük, majd ezt a LabView közvetlenül gépi kódra fordítja le. Ugyan szöveges kód helyett most grafikusan ábrázoljuk a kódot, ugyanazokat a programozási elemeket tartalmazza, mint a klasszikus nyelvek. Ugyanúgy van ciklus, elágazás, változó, mint akármelyik más nyelven. Egy VI két részből áll: egy front panelből és egy blokk diagramból. A front panel, mint a neve is mutatja, a program kezelőfelülete, ezen láthatjuk az eredményeket, itt tudjuk beállítani a paramétereket (Kép 11). Ha megnyitjuk a LabView-t, ezen a felületen találjuk magunkat.
Kép 11: LabView front panel
[i] Laboratory Virtual Instrumentation Engineering Workbench, l. 44. oldal
Grafikus programozást támogató rendszerek
17
Az oldalsó panelről tudjuk behúzogatni a szükséges elemeket. Találunk itt többféle kijelzőt, bemeneti mezőket, ha számokat akarunk beadni a programnak, választhatunk potmétert, csúszkát, tulajdonképpen egy valódi műszert is le tudunk modellezni vele. Ha egy jelet akarunk vizsgálni, akkor kirakhatunk oszcilloszkópot, vagy bármit, amit szeretnénk. A blokk diagramon tudjuk „összedrótozni” a szükséges eszközöket (l. Kép 12).
Kép 12: A 11. képen látott front panelhez tartozó blokk diagram Azok az elemek, amiket kiraktunk a front panelre, itt is megjelennek egy ikon képében, így összeköthetjük őket, megadhatunk nekik értéket, vagy éppen a róluk érkező adatokkal dolgozhatunk.
3.3 Harpia 3.3.1 Harpia Project A Harpia Project a brazil S2i kutatócsoport fejlesztése 12 képfeldolgozó algoritmusok tervezéséhez és futtatásához. Az S2i egy multidiszciplináris kutatócsoport, mérnöki, informatikai és marketinges témákkal foglalkozik, munkatársai között a Systems and Automation Department (DAS) és Statistics and Informatics Department (INE) hallgatóit és professzorait is megtalálhatjuk. Brazil és német cégekkel áll partnerségben, innovatív és versenyképes technológiai kutatásokat végez. A projekt célja egy grafikus eszköz készítése volt, mely képes kezelni blokkdiagramokat, ahol a rendszer minden egyes lépését egy blokknak feleltetjük meg. Ezek a blokkok megfelelően paraméterezhetők, konfigurálhatók. Egyes modulok hardverelemekkel kapcsolatosak, így például kameráról is érkezhet a feldolgozandó kép. A Harpia C kódot generál és futtat, a képfeldolgozó feladatokat az OpenCV 13 könyvtárral végzi. A grafikus eszköz egy rajzvászonból, canvas-ből áll, ahova a baloldali panelről lehet behúzni a szükséges blokkokat (l. Kép 22). A blokkokat lehet paraméterezni, konfigurálni a megfelelő
hardvereszközökhöz
(pl.
kamera),
fényképet
készíthetünk,
amit
egyből
Grafikus programozást támogató rendszerek
18
analizálhatunk, feldolgozhatunk, és végül kitehetjük a kívánt kimenetre. A blokkokhoz létezik egy megjelenés fül is, ahol a színeket állíthatjuk, valamint egy help is.
Kép 13: Harpia grafikus felülete: balra az eszközpaletta, jobbra a rajzvászon a példaprogramok egyikével (arcfelismerés). A Harpia opensource, vagyis nyílt forrású program, mégis inkább egy saját rendszer fejlesztése mellett döntöttem a Harpia továbbfejlesztése helyett. Erre több okom is volt: a Harpia fejlesztését 2006. környékén felfüggesztették, azóta nincs róla több hír. Másik probléma, hogy angol nyelvű dokumentáció alig van hozzá, csak egy felhasználói leírás, részletes fejlesztői dokumentáció csak portugál nyelven van. A Harpia az egyes függvények (blokkok) adatait XML fájlokban tárolja. Az AND függvény XML fájlja a következő: <properties> <property name='state' value='true' />
Ugyanezt a sémát követi a többi függvénynél is, és hasonló szintaktikát használtam én is a programomban, azzal a különbséggel, hogy az egyes blokkokat egy fájlba fűztem össze. A tulajdonságok grafikus megjelenítéséhez glade fájlokat használ. A Glade14 egy RADi eszköz, mellyel a GTK+ könyvtár használatával egyszerűen készíthetünk grafikus felületet. A felület a Glade Interface Designer programban megtervezhető, melyet a program egy .glade [i] Rapid Application Development, l.
Grafikus programozást támogató rendszerek
19
kiterjesztésű XML fájlba ment el. Ezt a glade fájlt a GTKBuilder könyvtár segítségével tudjuk használni számos nyelven köztük C, C++, Java, Python nyelven is.
3.3.2 A blokkok A felhasználható blokkokat (OpenCV függvényeket) a következő csoportokba sorolja a program: •
Morfológiai műveletek: erode, dilate, stb.
•
Matematikai műveletek: logaritmus, exponenciális, hatvány
•
Szűrők és színkonverziók: színkonverzió, RGB műveletek, treshold, elmosás
•
Tulajdonság (feature) detekció: maximum, minimum, arckeresés, adott színű objektum keresése, stb.
•
Gradiens, sarkok, szélek: Sobel, Laplace és Canny szűrők
•
Hisztogramok
•
Aritmetikai és logikai műveletek: Not, And, Or, Xor, összeadás, kivonás, szorzás és osztás
•
Általános műveletek: mentés, betöltés, komment, stb.
•
Egyéb (experimental): méret lekérdezése, kép vágása, átméretezés, stb.
Rendszerterv
20
4 Rendszerterv A program célja képfeldolgozó algoritmusok tervezésének és végrehajtásának megkönnyítése. Az ehhez szükséges operátorokat több függvénykönyvtár is tartalmazza, melyek közül én az OpenCV-t választottam. A tervező egy folyamatgráfban felrajzolja az algoritmus által végrehajtott utasításokat, majd a rendszer ezt értelmezi, és futtatja. A futtatás menete a következő: az eltárolt forráskönyvtárból legeneráljuk a program forrását, majd ezt fordítjuk és futtatjuk. A programot három modulra, három rétegre bontottam: 1. A legfelső réteg a frontend, mely a grafikus felületet foglalja magában. 2. A középső réteg a middle-end, melynek feladata a frontendről érkező adatok feldolgozása és értelmezése. 3. A harmadik, legalsó réteg futtatja a kódot. Ez már rendszerspecifikus, a program az operációs rendszer (Linux) által nyújtott eszközöket használja az algoritmus futtatásához. A moduláris dekompozíció lehetővé teszi az egyes rétegek esetleges cseréjét. A modulok egymásra rétegződését a 14. ábra mutatja.
Kép 14: rendszermodulok
4.1 Frontend A rendszer grafikus felületét a Dia 15 biztosítja. A Dia opensource diagramok készítéséhez
Rendszerterv
21
használatos program. Alexander Larsson kezdte fejleszteni, jelenleg Hans Breuer felügyeli a fejlesztést. A Win32 verziót Steffen Macke felügyeli. Saját, .dia nevű XML formátumban menti a diagramokat, s többféle formátumba is tudja exportálni őket, például SVG, PNG, EPS formátumokba. Fejlesztése folyamatos, a hivatalos oldalon olvashatók a tervek 0.98-as és 1.0-s verziókhoz. Tervek vannak az OpenOffice.org és OmniGraffle formátumok támogatására, LaTeX-hez és folyton visszatérő kérdés a Visio .vsd fájlok kezelése. A Harpiával ellentétben a Dia fejlesztése nem ért véget, és megfelelő dokumentáció is van hozzá, ezért választottam ezt a Harpia helyett. Mivel a Dia már egy kész, jól használható, átlátható felületet ad a rajzoláshoz, úgy döntöttem, nem készítek külön egy új felületet, hanem használom a Diát. Több hasonló szoftvert is találtam erre a célra (például a már említett Harpiát), de a Dia bizonyult a legmegfelelőbbnek jól dokumentáltsága, átlátható forráskódja és platformfüggetlensége miatt. A Dia C nyelven íródott, grafikus megjelenítéshez a GTK16 könyvtárat használja. Ez egy opensource könyvtár, mely a GUI-k létrehozásához kínál platformfüggetlen, jól használható interfészt. A különböző típusú diagramokhoz használható objektumokat úgynevezett sheetekben tárolja. Ahhoz, hogy az általunk használt algoritmusokhoz megfelelő objektumaink legyenek, saját sheetet kell létrehozni saját objektumokkal.
4.2 Middle-end A middle-end rendszer azon része, mely összeköti a front- és backendet. Ez lehet tetszőleges nyelven, én most a Pythont választottam, mert kényelmes, és jól integrálható a Dia környezetbe. Dia scripteket írhatunk Python nyelven, melyhez az szükséges, hogy a telepítés során a Python plugint is telepítsük. Ennek módját a 6.1.1 -es fejezetben ismertetem. A Python pluginhoz a dokumentációt a Dia projekt oldalán találhatjuk 17, ezen kívül a Dia levelezőlista archívumában találhatunk hasznos forrásokat. A middle-end algoritmusának futtatását a Tools/DiaCV menüpontba ágyaztam be. A middle-end feladata, hogy értelmezze és végrehajtsa a frontendről érkező algoritmusgráfot. Meg kell kapnia magát a gráfot, majd értelmeznie és validálnia kell. Ellenőriznie kell, hogy a gráf összefüggő legyen, különben a folyamat megszakad. Ugyanakkor a gráf irányított és körmentes is kell, hogy legyen, ezt hívjuk Directed Acyclic Graph-nak, röviden DAG-nak. Az interpreter az elejétől a végéig bejárja a gráfot, értelmezi a csomópontokon álló objektumokat, az esetleges paramétereket, majd legenerálja belőle a megfelelő kódot. A gráfnak ezen a ponton három reprezentációja is van:
Rendszerterv •
A frontendről érkező gráf, melyet a felhasználó megrajzolt.
•
A middle-end által a frontendről érkező rajzból létrehozott belső reprezentációja.
•
A belső reprezentációból létrehozott kód, melyet a middle-end átad a backendnek.
22
A belső reprezentáció egy tömb, melyben a kapott függvények adatait eltároljuk, míg végigjárjuk a teljes gráfot. Ezalatt a végigjárás alatt kell ellenőriznünk is, hogy megfelelő objektumokat kaptunk-e, melyeknek mindegyike megfeleltethető-e az általunk ismert függvényeknek. Ugyancsak ellenőriznünk és tárolnunk kell a kapott paramétereket is. A belső reprezentációból létrehozzuk a megfelelő kódot, ezt lefordítjuk, majd átadjuk a backendnek azzal, hogy futtatjuk is.
4.3 Backend A backend a legalsó réteg, mely végrehajtja a fölötte levő réteg, a middle end által generált kódot. Futtat interpretál. A felette levő rétegekkel ellentétben a backend már nem platformfüggetlen. Én az OpenCV grafikus könyvtárat használtam, mely elérhető több nyelvhez, többek között Chez, C++-hoz, Pythonhoz is. A backend jelenleg a Python nyelvet és a hozzá szükséges OpenCV könyvtárat használja. Ezek megléte szükséges a program megfelelő működéséhez.
A Dia felépítése
23
5 A Dia felépítése Mivel a Diához fejlesztői dokumentáció csak korlátozottan áll rendelkezésre, úgy vélem, először érdemes röviden áttekinteni, miből áll ez a szoftver, hogyan épül fel, s csak ezután belemenni a részletes fejlesztésbe.
Kép 15: a Dia grafikus felülete A Dia program elindításával a Harpiához hasonlóan egy rajzvásznat kapunk, melyre a panelről húzhatjuk be a kívánt objektumokat. A Harpiához hasonlóan a Dia is a GTK canvas felületét használja a rajzolásra. Alapértelmezetten a program különálló panelekkel indul el, tehát az eszközpanel és a rajzvászon külön ablakokban jelenik meg – ha több diagramot nyitunk meg, azok is külön ablakban lesznek. Ezt a --integrated kapcsolóval változtathatjuk meg, ekkor egy ablakban nyílik meg, s a diagramokat lapokon jeleníti meg (l. Kép 15). Az egyes diagramtípusokat, a felhasználható grafikus elemeket a Dia úgynevezett sheetekben tartja, a sheetek elemeit objektumoknak nevezi. Vannak benne előre definiált sheetek, mint például az egyed-kapcsolat (ER) diagramok, UML, folyamatábra, vagy éppen Cisco, illetve Sybase. A legördülő menüben kiválaszthatjuk a kívánt sheetet, majd az objektumokat behúzogatva, és őket összekötögetve megrajzoljuk a diagramunkat.
A Dia felépítése
24
5.1 Saját sheet létrehozása Ha saját sheetet szeretnénk létrehozni, több lehetőségünk van. Ha csak lokálisan, magunknak szeretnénk létrehozni egyet, a legegyszerűbb ezt a Dián belül megtenni. Ezt a fájl menün belül a Sheets and objects menüpontban találjuk. Miután létrehoztuk, ugyanitt tudunk hozzáadni objektumokat, vagy, ha szeretnénk, akár meg is rajzolhatjuk őket a Dián belül (ez esetben a rajzot Dia shape fájlként kell exportálni, s azt a Sheets and objectsben tudjuk betölteni, és hozzáadni a sheethez). Ehhez a weben is sokfelé találunk jól érthető, világos, részletes leírást, például a Dia projekt hivatalos oldalán is18. A sheet a felhasználó saját home könyvtárán belül a .dia/sheets könyvtárban jön létre (mind Windows, mind Linux rendszer esetében). A sheet szintén egy XML fájl lesz, melynek formátuma a következő: <sheet xmlns="http://www.lysator.liu.se/~alla/dia/diasheetns"> ujsheet <description>ez egy uj sheet
Tehát a példában a sheet neve „ujsheet”, és két objektumot tartalmaz: az egyik az ER Entity objektuma, a másik az UML-ből a Class. Ezzel a módszerrel azonban több probléma is van, ami miatt jelen esetben nem tudtam használni. Az egyik, abból következik, amit már említettem: a sheet a felhasználó saját könyvtárában, lokálisan jön létre. Ha a könyvtárat töröljük, vagy újratelepítjük a programot, az adat elveszik, illetve, ha több számítógépen, több felhasználónak szeretnénk a sheetet használni (például egy oktatási intézmény géptermeiben), a Dia telepítése után egyesével kell bemásolni a .sheet fájlt a megfelelő helyre, ami idő, és kényelmetlenség, még, ha a folyamat automatizálható is. Sokkal egyszerűbb lenne, ha a telepítés során ezt is feltelepítenénk. Másik probléma, hogy az XML-ben létrehozott objektumok tulajdonságait nem lehet testre szabni. Ezeknek az objektumoknak a Dia egy beépített tulajdonságmenüt hoz létre, melyben
A Dia felépítése
25
csak a megjelenését (betűtípus, színek, méret, stb.) lehet változtatni, ami sokszor nem elég. Ezt a problémát a Dia kézzel történő fordításával tudjuk megoldani.
5.2 Fordítás A Dia forrása letölthető a projekt oldaláról, a live.gnome.org/dia-ról, jelenleg a 2009. májusában kijött 0.97-es verzió a legfrissebb hivatalos kiadás. Ennél frissebb verziót a Dia Git repositoryból tudunk letölteni. Fordítani így tudjuk (Linux rendszer alatt): •
a forrás könyvtárban futtatjuk a configure szkriptet, ami elkészíti a Makefile-t – mivel nekünk kell a python modul, ezért ezzel kapcsolóval: ./configure --with-python
•
a make paranccsal elkészítjük a futtatható állományt
•
végül telepítjük a programot: sudo make install
A make clean parancs törli az összes, a configure szkript által készített fájlt (ez hasznos lehet, ha valamit módosítottunk, és újra akarjuk make-elni a programot, vagy, ha csak a forrásra van szükségünk).
5.2.1 Objektum írása XML-ben Ahhoz, hogy telepítéskor feltelepítsük a saját sheetünket, nincs nehéz dolgunk. A dia forrását megnézve a következő könyvtárakat találjuk: app bindings data doc installer lib objects plugins po samples shapes sheets tests
Ami számunkra most lényeges, az a sheets, objects, shapes, valamint esetleg a po. Ez utóbbi tartalmazza a különböző nyelvű fordításokat, ami akkor lehet számunkra érdekes, ha több nyelven is elérhetővé szeretnénk tenni az általunk készített modult. Én most nem foglalkozom vele, mivel a fejlesztés során végig angol nyelven dolgoztam. A sheetsben találjuk a sheetek létrehozásához szükséges sheet.in fájlokat, a shapesben az XML
A Dia felépítése
26
nyelven írt objektumokat, míg az objectsben a C nyelven írt objektumokat. A fent említett két probléma közül az elsőt könnyen megoldhatjuk, XML-lel ugyanúgy készíthetünk
új
sheetet.
Ehhez
a
sheets
könyvtárban
létre
kell
hoznunk
egy
[sheet neve].sheet.in nevű XML fájlt. Formátuma egészen egyszerű, mint azt láthatjuk is, ha megnézzük egy beépített sheet sheet.in fájlját: <sheet xmlns="http://www.lysator.liu.se/~alla/dia/diasheetns"> <_name>ER <_description>Editor for Entity Relations Diagrams ...
Ennek mintájára megírhatjuk a saját fájlunkat, majd a sheetsben lévő Makefile.am-ben meg kell mondanunk, hogy ezt a fájlt is szeretnénk használni: a sheet_in_files változóba írjuk bele a [sheet neve].sheet.in fájl nevét is. Ha saját objektumokat is szeretnénk készíteni XML-ben, arra is megvan a lehetőség: a shapes könyvtárban kell létrehoznunk a sheetünk nevével megegyező könyvtárat, s ebbe rakjuk a kívánt objektumok .shape fájljait. A .shape fájlok SVG 19 objektumokat definiálnak. Az SVG egy W3C20 szabvány kétdimenziós grafikus alkalmazások és képek leírására. <shape xmlns="http://www.daa.com.au/~james/diashapens" xmlns:svg="http://www.w3.org/2000/svg"> Jigsaw – part_iiiipart_iiii.png<point x="1.5" y="1.5" main="yes"/> <svg:svg width="3" height="3"> ...
A fenti példa a Jigsaw egyik objektuma. A tagben az objektum neve található. Ez fontos, mivel a fentebb látott sheet.in fájlba is ezt a nevet kell értékül adni az object name attribútumának. Az icon tagben azt tudjuk megadni, milyen képpel ábrázoljuk az objektumot az eszközpanelen. Ez egy 22*22 pixeles png kép, melyet ugyanebben a könyvtárban, a shape fájlok között találunk. A connection point tagben az objektum kapcsolódási pontjait adhatjuk meg. Ezek azok a
A Dia felépítése
27
pontok, melyekkel az objektumot más objektumokhoz tudjuk kötni a diagramon. Megadhatunk köztük egy main pontot is (main="yes"), ez az a pont, melyhez a Dia a kapcsolatot kötni fogja, ha nem kötjük egyértelműen egy másik ponthoz. Ha megírtuk a fájlt, itt is meg kell mondani a fordítónak, hogy használja is azt, ezt az ebben a könyvtárban lévő Makefile.am-ben tehetjük meg: a SHAPES változónak kell értékül adni mind a .shape, mind a .png fájlt. Valamint, ha teljesen új sheetet hoztunk létre, a shapes könyvtárban található Makefile.am SUBDIRS változójának is meg kell adnunk a sheet könyvtár nevét. Ezeknek az objektumoknak a készítéséhez, az SVG objektumokhoz a dia-installer.de oldalon találhatunk hasznos leírást21.
5.2.2 Objektum írása C-ben Ahhoz, hogy az 5.1 -es fejezetben említett második problémát is megoldjuk, nem elegendő az XML, C-ben kell lekódolnunk a kívánt objektumot. Az 5.2.1 -as fejezetben leírt módon megírt sheet.in fájlra ez esetben is szükségünk lesz, a különbség annyi, hogy most az objektumokat C nyelven írjuk meg. Erre a kérdésre még visszatérek részletesen a 6.2 -es fejezetben.
Az ImageProcess frontend modul
28
6 Az ImageProcess frontend modul A modul fejlesztése során az Ubuntu 10.10 (Maverick Meerkat) Linux disztribúciót használtam, a következőkben az itt használt techikákat részletezem.
6.1 Telepítés: configure – make – make install A fordításhoz nézzük meg a forrás gyökérkönyvtárában található fájlokat. Amik számunkra most érdekesek, hárman vannak: autogen.sh, configure.in és makefile.am. A fordításához ezeket a fájlokat fogjuk használni.
6.1.1 Autoconf Ha újra szeretnénk generálni a telepítőt, egy make clean paranccsal kell kezdenünk. Ezzel töröljük az eddig generált .la, .lo fájlokat, melyek a .c és .h forrásfájlok fordítása során készültek. A ./autogen.sh paranccsal futtatjuk az autogen.sh shell scriptet. Ehhez szükségünk van a libtool nevű általános building könyvtárra22. A script egyrészt legenerálja a makefile.am fájlból a makefile.in-t, valamint a configure.in file-ból a configure scriptet, s ez utóbbit futtatja is. Tehát, ha a makefile-okat újra akarjuk generálni, nem elég csak a make clean parancs, az autogen.sh script futtatása is szükséges. A Makefile.am-be kerülnek a Makefile-hoz szükséges paraméterek, mint a telepítéskor szükséges fájlok elérési helyei, és a flagek. A configure.in fájlból generálja le a configure scriptet, tehát a számunkra szükséges kapcsolókat ebbe kell beleírni. Mint már említettem, nekünk szükségünk van a --with-python kapcsolóra, hogy a python modult is telepítsük a Diába, ezért megkerestem, hol állítja ennek a kapcsolónak a default értékét: AC_ARG_WITH(python, [ withpython compile python plugin],,with_python=no)
Annyi változtatás volt szükséges, hogy a with_python változó értékét átírtam: AC_ARG_WITH(python, [ withpython compile python plugin],,with_python=yes)
Az ImageProcess frontend modul
29
6.1.2 Configure Az autogen.sh lefuttatja ezt a scriptet is, de ha a configure.in illetve Makefile.am fájlokat nem változtattuk, a ./configure paranccsal tudjuk lefuttatni. A script a Makefile.in fájlokból generálja a Makefile-okat. A --prefix kapcsolóval azt is meg tudjuk mondani neki, hogy hova telepítse a szoftvert (pl. ./configure --prefix=/home/user/folder)
6.1.3 Make, make install A make23 egy utility szoftver, mely a forrásfájlokból építi fel a szoftvert a Makefile-ok segítségével. Ha a make végzett, a (sudo) make install paranccsal telepíthetjük a szoftvert (mivel szoftver telepítéséhez többnyire root jog kell, szükséges a sudo parancs is – értelemszerűen a jelszót ismernünk kell).
6.2 Modulterv Az 5.2.2 -es fejezetben már említettem az objektumok C nyelven történő megírását, itt is erre volt szükségem. Ehhez az objects könyvtárban kell létrehoznunk egy könyvtárat, melynek a sheet nevét adjuk névként, ez most az ImageProcess. Az objects könyvtárban lévő Makefile.am-ben lévő SUBDIRS-nek természetesen most is meg kell adnunk a könyvtár nevét. A sheet a következőképpen fog kinézni: szükségünk lesz a blokkokra, melyek az egyes OpenCV függvényeknek feleltethetők meg, valamint az őket összekötő vonalakra, élekre.
6.2.1 Blokkok Az egyes blokkok az OpenCV függvényeknek felelnek meg. Két blokkot külön ki kell emelnem, ez a Load image (kép betöltése) és a Save image (kép mentése) blokk, melyek a fájlkezelő műveleteket végzik. Ez a két blokk azért különleges, mert tulajdonságai nem változtathatók, egy bemeneti (mentés) illetve egy kimeneti (betöltés) paramétere van. A többi blokk tulajdonságai változhatnak. Ennek megoldásához a Harpia módszerét használtam: az egyes függvényeket és paramétereiket XML fájlokban tárolja. Ezeket a blokkokat egy nagy XML fájlba tettem. A program futása során először a függvények nevét kell kiszelektálni az XML-ből, ezek a nevek lesznek a jobb gombos menü menüpontjai. A felhasználó ezek közül választ egyet,
Az ImageProcess frontend modul
30
ekkor a program kiválasztja az adott függvényhez tartozó tulajdonságokat, melyek a blokk properties menüjének pontjai lesznek (l. Kép 16).
Kép 16: menü
6.2.2 Összekötők Az egyes blokkokat összekötő elemekkel kötjük össze. Ezekkel a vonalak a hálózati folyamoknál, irányított gráfoknál látott módon az objektum paraméterek haladását modellezzük.
6.3 Megvalósítás Első lépésként a sheetet kell létrehozni. Ez a sheet könyvtárban tudtam megtenni, egy ImageProcess.sheet.in XML fájllal, melybe a sheet blokkjait listáztam a 25. oldalon látott módon. Emellett a Makefile.am sheet_in_files változójához is hozzá kell fűzni az újonnan létrehozott sheet.in fájlt. Ezután létrehoztam az objects könyvtárban az ImageProcess könyvtárat, s a következőkben itt dolgoztam. Az ImageProcess könyvtárban szükség volt egy Makefile.am fájlra, s ahhoz, hogy ebből egy Makefile készüljön, a configure.in-ben az AC_OUTPUT-hoz hozzá kell adnunk az ebben a könyvtárban létrehozandó objects/ImageProcess/Makefile-t is. Ahhoz,
hogy
a
programban
az
egyes
objektumoknál
megjelenjek
ikonok,
az
objects/ImageProcess könyvtárban létre kell hozni egy pixmaps könyvtárat. Itt ugyanazok az ikonok találhatók, mint az XML-lel létrehozott objektumok tagjében, azzal a különbséggel, hogy most nem png, hanem xpm, vagyis pixmap 24 fájlokat használunk. A pixmap fájlokat ikonok megjelenítésére használjuk, az xpm ezeknek a szöveges, C nyelvhez hasonló szintaktikájú reprezentációi.
Az ImageProcess frontend modul
31
Kép 17: ImageProcess sheet, rajta a négy objektummal: Load Image, Save Image, Process (mely több függvényt is magába foglal), végül az összekötő Line A Makefile.am nem túl bonyolult, az általam írt ImageProcess sheeté a következőképpen néz ki: ## Makfile.am for ImageProcess objects ## Process this file with automake to produce Makefile.in pkglib_LTLIBRARIES = libimageprocess_objects.la ## Sources of the objects libimageprocess_objects_la_SOURCES = \ load.c \ save.c \ line.c \ process.c \ imageprocess.c libimageprocess_objects_la_LDFLAGS = \ exportdynamic module \ avoidversion $(NO_UNDEFINED) \ `xml2config cflags libs` libimageprocess_objects_la_LIBADD = $(top_builddir)/lib/libdia.la INCLUDES = I$(top_srcdir)/intl I$(srcdir)/../../lib \ $(DEBUG_FLAGS) $(GTK_CFLAGS) $(GNOME_CFLAGS) \ $(PANGOFT2_CFLAGS) $(UNICODE_CFLAGS) ## Extra resources, eg. Pixmaps EXTRA_DIST = \ pixmaps/line.xpm \ pixmaps/load.xpm \ pixmaps/save.xpm \ pixmaps/process.xpm
A libimageprocess_objects_la_SOURCES nevű változónak kell megadni a felhasznált .c fájlokat. A libimageprocess_objects_la_LDFLAGS-ben a fordításhoz szükséges kapcsolók (flagek) vannak. Itt egy extra kapcsolót is találunk, az `xml2-config --cflags --libs` az XML parsoláshoz használt libxml library használatához szükséges. Az objektumokhoz szükséges xpm fájlokat az EXTRA_DIST-nek kellett megadni. A modul c fájljai az objects/ImageProcess könyvtárban kaptak helyet. A három objektum (load
Az ImageProcess frontend modul
32
image, save image, process), a sheet, és a vonal egy-egy külön c fájlban található. Az imageprocess.c-ben regisztráljuk a sheetben lévő blokkokat. A vonal a line.c-ben található, ez lényegében az FS sheet Flow nevű blokkjára épül. A line-nak változtatható a típusa, image és data között választhatunk. Ennek azért van jelentősége, mert láthatjuk, a blokkok között milyen típusú adat „áramlik”. Ezáltal könnyebben ellenőrizhető és értelmezhető a későbbiekben a gráf. A blokkokat később, a 6.3.2 -as fejezetben részletezem.
6.3.1 XML A modul egy külön része az XML fájl parsolása. Az egyes blokkok konstans paramétereinek leírásához egy általános reprezentációt választottam, az elemeket XML fájlban definiálom. Az XML azért jó, mert szabványos, de a feladattól függően tetszőlegesen bővíthető formátum, parsolására több könyvtár is létezik. Az általam használt XML fájl a Harpiában is használt felépítéshez hasonló, a következőképpen néz ki: <properties> <property name='state' value='true' /> <properties> <property name='state' value='true' /> ...
Tehát első körben a block tageket kell kiszelektálni a jobb gombos menühöz. A következő lépés a properties menühöz a kiválasztott block alá tartozó property tageket. Az XML parsoláshoz a libxml225 könyvtárat használtam. A libxml2 C nyelven íródott, használható többek között C, C++, C#, és Python nyelveken is. Az XML parser függvény a következő: int execute_xpath_expression (const char* filename, const xmlChar* xpathExpr) { xmlDocPtr doc; xmlXPathContextPtr xpathCtx; xmlXPathObjectPtr xpathObj;
Az xmlDocPtr doc változóba az xmlParseFile függvénnyel beolvassuk az XML fájlt. Paraméterként
annak
elérési
útját
adjuk
meg
egy
char*
tömbben.
Ebből
egy
xmlXPathContextPtr típusú változóba olvassuk át, majd az xmlXPathEvalExpression függvénnyel végrehajtjuk rajta a kívánt műveletet. Ez esetben egy egy XPath kifejezést hajtunk végre, melyet az xpathExpr paraméterként adunk át a függvénynek. Az Xpath egy, a W3C által definiált nyelv az XML fájlokban való keresésre. Az XPath kifejezés most így néz ki: "/functions/properties/block". Látható, hogy a function blokkon belül, a properties blokkban található block tageket szelektáljuk ki az xmlNodeSetPtr típusú xpathObj változóba. Ebből később, a print_xpath_nodes függvényben egy objektumtömböt készítünk, mely block típusú struktúrákból áll: typedef struct block { char* type; char* name; char* id; int nr_props; property my_props[5]; } block;
Minden blokkhoz tartozik egy property tömb: typedef struct property { char* name; char* value; } property;
Mivel C-ben programozok, a tömböt elsőként fix mérettel kell létrehoznom. Ha a tömb később, futás közben kicsinek bizonyul, új, az előzőnél nagyobb tömböt kell létrehozni, a régi tömb elemeit átmásolni ebbe, annak a helyét a memóriában felszabadítani, majd az új tömböt visszadni. Pszeudokóddal ez így néz ki: int old_size = 5; type old_array[old_size]; …
Az ImageProcess frontend modul
34
int new_size = old_size*2; type new_array[new_size]; for (int i=0; i
6.3.2 Blokkok
Kép 18: Save image tulajdonságai A két „különleges” blokkal, a Load és Save image-dzsel most nem kívánnék részletesebben foglalkozni, a többi blokknál lényegesen egyszerűbbek, az eltéréseket majd a helyükön megemlítem. Egyetlen paraméterük van, ez a bemeneti és kimeneti képek elérési útja (Kép 18). A többi függvény, melyeknek egy be- és kimenete van, a process nevű blokkban kapott helyet. A Dia, bár C-ben íródott, egy osztályöröklődéshez hasonló felépítést valósít meg. Ezt struktúrákkal, azok egymásba ágyazásával, valamint virtuális függvényekkel oldja meg. Példaként vegyük a Process blokkot. A Process ősosztálya az Element (l. Kép 19), tehát a
Kép 19: objektumok osztálydiagramja Process struktúrán belül lesz egy Element típusú változó. Az Element szintén egy struktúra, az ő őse a DiaObject, tehát az Element struktúrában is lesz egy DiaObject változó. Minden blokknak tartalmaznia kell két fontos függvénycsoport függvényeit: ezek az
Az ImageProcess frontend modul
35
ObjectOps és az ObjectTypeOps függvények. Mind definiálva van a lib/object.h header fájlban, implementálva viszont nincsenek, külön-külön kell őket megírni minden egyes blokkra. Az ObjectOps függvények az objektum létrehozásához és a mentéssel kapcsolatos műveletekhez szükséges függvények. •
CreateFunc: ez a függvény hozza létre az objektumot
•
LoadFunc: megnyitott diagramból betölti az objektumot
•
SaveFunc: elmenti az objektumot
•
GetDefaultsFunc: az objektum alapértelmezett értékeit tölti be (akkor hívódik meg, amikor a felhasználó kétszer kattint az objektumra az eszközpalettán)
•
ApplyDefaultsFunc: az objektum alapértelmezett értékeinek változtatását alkalmazza az objektumra (akkor hívódik meg, mikor az előbb megnyitott ablakon az Apply gombra kattint a felhasználó)
Az ObjectTypeOps függvények jóval többen vannak, ezek az objektum kezeléséhez, manipulálásához szükséges egyéb függvények: •
DestroyFunc: az objektum törlésekor meghívott függvény. Meghívja az ősosztály DestroyFunc függvényét, felszabadítja a memóriaterületet.
•
DrawFunc: az objektum rajzolásáért felelős függvény. Minden rajzolási műveletet a DiaRenderer osztályon keresztül hajtunk végre, így ennek is egyik paramétere egy DiaRenderer objektum.
•
DistanceFunc: a paraméterként megadott DiaObject objektum és a pont távolságát adja vissza.
•
SelectFunc: akkor hívódik meg ez a függvény, amikor kiválasztjuk az objektumot. Csak frissíti az objektumot (handle-k pozícióját, stb.), de nem rajzolja újra.
•
CopyFunc: Az objektum egy másolatát adja vissza. „Depth-copy”, vagyis teljes másolat, pointereket is másol, tehát az eredeti objektumot kitörölhetjük, a másolat hiánytalanul megmarad.
•
MoveFunc: az objektum mozgatásáért felelős függvény.
•
MoveHandleFunc: az objektumhoz kapcsolódó “handle”-k mozgatásáért felelős függvény. Visszatérési értéke egy ObjectChange objektum, mely tartalmazza a változások listáját, és a visszavonáshoz szükséges információkat. Az ObjectChange szintén egy struktúra.
•
GetPropertiesFunc: ha kétszer kattintunk egy DiaObjectre (vagyis egy objektumra a Dia rajzvásznon), vagy a jobbgombos menüben a Properties menüpontra kattintunk
Az ImageProcess frontend modul
36
hívódik meg ez a függvény. Az objektum tulajdonságait adja vissza. •
ApplyPropertiesDialogFunc: ha a felhasználó az “Apply” gombra kattint, hívjuk meg. Általában ő hívja meg az ApplyPropertiesListFunc függvényt.
•
ApplyPropertiesListFunc:
tulajdonságok
egy
listáját
alkalmazza
az
aktuális
objektumra. •
ObjectMenuFunc: egy objektum-specifikus menüt hoz létre. Lehet null is.
•
DescribePropsFunc: az objektum tulajdonságai.
•
GetPropsFunc: az adott tulajdonságok aktuális értékei.
•
SetPropsFunc: beállítja az objektum tulajdonságait a megadott értékekre.
•
TextEditFunc: az objektum szövegét frissíti. Ha nem null, minden alkalommal meghívódik, amikor a szöveg szerkesztését elkezdjük vagy abbahagyjuk.
Ezeket a függvényeket így definiáljuk az egyes blokkokban: static ObjectTypeOps process_type_ops = { (CreateFunc)
process_create,
(LoadFunc)
process_load,
(SaveFunc)
process_save
};
Majd később létrehozzuk őket: static DiaObject* process_create(Point *startpoint, void *user_data, Handle **handle1, Handle **handle2) { ... }
A blokkok context menüjét egy DiaMenu típusú objektumokat tartalmazó statikus tömbben határozza meg. Ez most nem járható út, mivel a függvényeket egy XML fájlból olvassuk be, és azt szeretnénk, hogy tudjunk újabb függvényeket is hozzáadni a modulhoz, csupán az XML fájl módosításával. Miután kiválasztottuk a megfelelő típusú függvényt, szeretnénk annak paramétereit, tulajdonságait is látni. Ezek a context menü Properties menüpontja alatt találhatók, és egy PropDescription típusú tömbben tároljuk őket. Ezt a tömböt is dinamikusan kell felépítenünk, mivel az egyes függvényeknek más tulajdonságai és paraméterei vannak. Szintén futás közben, a felhasználó választásától függően dől el, melyik függvény tulajdonságait kell megjelenítenünk, ezt is kezelni kell. Ezt azzal oldottam meg, hogy az XML beolvasásakor létrehozok két tömböt. Egyikük a
Az ImageProcess frontend modul
37
függvények neveit tárolja el, ezeket a tömbben elfoglalt indexük alapján azonosítom. A másik tömbbe melybe a függvények tulajdonságai kerülnek, szintén tömbökként a megfelelő indexszel. Ezzel, ha a felhasználó kiválasztja a menüben a 3. indexű függvényt, a tulajdonságok közül is a 3. indexű tömböt kell visszaadnunk.
Az ImageProcess middle-end modul
38
7 Az ImageProcess middle-end modul A middle end a DiaCV plugin, mely értelmezi és végrehajtja a grafikus felületen megrajzolt algoritmust. A plugint Python nyelven írtam, a PyDia, illetve a python OpenCV könyvtárak segítségével. Az átláthatóság és kezelhetőség kedvéért döntöttem osztályok írása mellett, melyek osztálydiagramja a 20. képen látható. A 7.1. fejezetben ezeket az osztályokat fejtem ki részletesebben.
Kép 20: python osztályok
7.1 Az osztályok 7.1.1 Pont.py A pont.py-ban két classt írtam meg: a point és a line osztályt. Mindkét osztály arra van, hogy megkönnyítse a diagramban tárolt koordináták kezelését és a kód értelmezését. A point osztályban két értéket tárolunk csupán, a pont x és y koordinátáit, valamint az ezekhez tartozó getter függvényeket (getX, getY).
Az ImageProcess middle-end modul
39
A line osztály a diagramban használt „ImageProcess – Line” objektumokat jelenti. Ennek két pont attribútuma van, a vonal két végpontja, két pont objektum formájában. A Dia a line objektumok esetén az XML fájlban eltárolja a köré rajzolt „bounding box” paramétereit obj_bb property néven:
Ezt könnyen le tudjuk kérdezni a properties.get('obj_bb') függvénnyel, és értékül adni a line osztály konstruktorának.
7.1.2 Myobject osztály A myobject osztályban a diagramon lévő objektumokat tároljuk el. Az objektumban először is eltároljuk az objektum nevét és típusát (name és mytype), ezzel megkönnyítjük a későbbiekben az összehasonlítási műveleteket. Tároljuk az objektum kapcsolódási pontjait is (mycp lista). A kapcsolódási pontok azok a pontok, melyeken keresztül kapcsolatokat tudunk kötni az objektumhoz. Végül valamennyi objektumban eltároljuk azoknak az objektumoknak, melyeknek ő a szülője. Két függvényünk van, a getmyconnpoints()-t a konstruktor hívja meg, visszatérési értékét a mycp lista kapja értékül. A getconns() függvényt kívülről, az algo osztály konstruktora hívja meg.
7.1.3 Algo osztály Az algo osztályban az algoritmus gráfot tároljuk. Az osztályban három listánk van, az egyikben a diagram objektumait (myobjects), a másikban az őket összekötő vonalakat (mylines) tároljuk, valamint egy __loadedImage és __savedImage objektum. Mivel most képfeldolgozó könyvtárral van dolgunk, feltételezhetjük, hogy a diagramban szerepelni fog a képbetöltő és -mentő függvény, melyeknek attribútumaként megadjuk a betöltendő, valamint a feldolgozott kép nevét. Ezt a két nevet tároljuk ebben a két objektumban. A harmadik lista, a thecode már sorba rendezve tárolja az objektumok neveit. Ez alapján generáljuk le a forráskódot.
7.1.4 Printer osztály Az utolsó osztály a printer osztály. Ez az algo osztálytól megkapja a betöltött és elmentett képek neveit, valamint a listát, mely alapján a forrást legenerálja. A source attribútum egy string, a forrás, melyet majd fájlba írunk. A printmyalgo függvény fut végig a listán és írja meg
Az ImageProcess middle-end modul
40
a forrást, s a run függvénnyel futtatjuk.
7.2 Az algoritmus Ha futtatjuk a plugint, mely a Dia Tools/DiaCV menübe van beágyazva, a handleImage függvény fut le. Ez a DiaCV.py fájlban található. A függvény először lekérdezi a diagramon található objektumok listáját, ezt a következő módon tudjuk megtenni: dia.active_display().diagram.data.layers[0].objects
Ezekből az objektumokból példányosítjuk az algo osztály egy objektumát. Az algo osztály felépíti az objektumok és a vonalak listáját, valamint lekérdezi a két szükséges kép paramétert. Ehhez a getmyobjects függvényt használja, mellyel végigjárja az objektumlistát, és valamennyi objektumnál ellenőrzi annak típusát. A Dia a vonalakat is objektumként tárolja, ezért is szükséges ellenőrizni az összes típust. Ha az objektum „ImageProcess – Line” típusú, akkor a vonalakat tartalmazó listába tesszük bele line típusú objektumként, ha nem, akkor az objektumok listájába, myobjectként. A myobject objektum példányosítása során még nem kérdezzük le azokat az objektumokat, melyeknek ő szülőobjektuma, ezt csak azután tesszük meg, hogy felépítettük a két listát. Ekkor végigjárjuk a nyilak listáját, és megkeressük, melyik melyik objektumhoz kapcsolódik. Megnézzük, melyik objektumból indul, s ennek az objektumnak a conns listájába beletesszük a nyíl másik végén lévő objektum nevét. A rendezett listát úgy kapjuk meg, hogy újra végigmegyünk mindegyik objektumon, és megkeressük azt, amelyiknek nincs szülője, vagyis nem szerepel egyik objektum conns listájában sem. Ha megtaláltuk, kitöröljük őt az objektumlistából, beillesztjük a rendezett listába (ezúttal az objektumok típusára van szükségünk), s továbblépünk a következő objektumra, és ezt folytatjuk, amíg végig nem értünk a listán. Hogy megkaptuk a rendezett listát, őt is végigjárjuk, és a forrásba beillesztjük valamennyi objektumhoz a megfelelő kódrészletet. Végül a következő paranccsal futtatjuk: command = "python " command += self.__source os.popen(command)
7.3 Példa Példaként egy egyszerűbb algoritmus objektumait és magát az algoritmust készítettem el. A diagram a 21. képen látható. Ehhez az ImageProcess sheet objektumaira volt szükségem: a Load image, Save image, Process, valamint a Line objektumokra. Mindhármat C-ben írtam
Az ImageProcess middle-end modul
41
meg, ahogy azt a 4.1 . fejezetben leírtam. Mindhárom objektumnak van egy név attribútuma, a Load image és a Save image ezen felül rendelkezik egy-egy image tulajdonsággal – a Load image-ben azt a fájlt adjuk meg, melyet betölteni szeretnénk, a Save image-ben azt, amilyen néven a feldolgozott képet el akarjuk menteni.
Kép 21: arcfelismerés diagramja A haarDetect függvény az OpenCV dokumentáció példafüggvényei között szerepelt, ezt használtam fel a példa során. Mivel ez egy hosszabb kódrészlet, egy eltárolt fájlból olvassuk be, és írjuk ki a saját fájlunkba. A kiindulási képet a 22., az eredményt a 23 képen láthatjuk.
Kép 22: példaprogram kiindulási képe
Kép 23: példaprogram eredménye
Összegzés, kitekintés
42
8 Összegzés, kitekintés A munka során egy olyan rendszer létrehozása volt a célom, mellyel megtervezhetjük, megrajzolhatjuk, és le is futtathatjuk a kívánt algoritmust. A rendszer grafikus felületének a Dia diagram rajzoló programot használtam, s ehhez írtam olyan plugint Python nyelven, mely a megrajzolt diagramot értelmezni tudja, legenerálja hozzá a forrást, majd le is futtatja. A felhasznált grafikus könyvtárhoz (OpenCV) megírtam a használni kívánt Dia objektumokat, és saját objektumgyűjteményt hoztam létre hozzá. Ezekhez részleges dokumentációt is készítettem, melyet átdolgozva rövidesen elérhetővé is kívánok tenni. Demonstráltam a technikai megvalósítást néhány konkrét műveletre. A rendszer további fejlesztések előtt áll. Bővíteni szeretném az OpenCV-ből felhasználható függvények számát, melyhez megadtam azt a leíró formátumot, amivel a további blokkok a rendszerbe integrálhatók. A middle-end algoritmust is fejleszteni szeretném, mivel akadnak még benne hiányosságok. Jelenleg a hibakezelés és a felhasználóknak szóló visszajelzés nem megoldott.
Köszönetnyilvánítás
43
Köszönetnyilvánítás Köszönöm a sok segítséget és a hatalmas türelmet konzulensemnek, Soós Balázs Gergelynek. Köszönöm a kitartást és a szintén nagy türelmet Verner Gábornak, a bátorítást és biztatást Kurczina Gergőnek. És nem utolsó sorban a sok-sok segítséget, türelmet és hasznos tanácsokat Kiss Tímeának, aki nélkül ez a munka soha nem ért volna véget.
Rövidítések, kifejezések jegyzéke
44
Rövidítések, kifejezések jegyzéke OOP: Object-oriented programming, objektum-orientált programozás CASE: Computer Aided Software Engineering, számítógéppel támogatott szoftverfejlesztés GUI: Graphical User Interface, grafikus felhasználói felület VPL: Visual Programming Language, vizuális programozási nyelv UML: Unified Modeling Language, szabványos, általános célú modellező nyelv OMG: Object Management Group, 1989-ben alakult konzorcium, célja az elosztott, objektumorientált rendszerek elterjedésének elősegítése. Tevékenységi köre időközben kibővült a modell-alapú tervezéssel, illetve a modell-alapú szabványok készítésével is. 26 Opensource: ingyenes, nyílt forrású CFG: Control Flow Graph, vezérlésfolyam gráf DFG: Data Flow Graph, adatfolyam gráf (DFD: Data Flow Diagram) SAP: a világ egyik vezető üzleti szoftver szolgáltatója, nevének jelentése „Systems, Applications, and Products in Data Processing” NWDS: NetWeaver Developer Studio, Eclipse alapú fejlesztői környezet ABAP: Advanced Business Application Programming, az SAP rendszerek által használt programozási nyelv LabView: Laboratory Virtual Instrumentation Engineering Workbench 27 DAG: Directed Acyclic Graph, irányított, körmentes gráf RAD: Rapid Application Development, gyors alkalmazásfejlesztés 28 SVG: Scalable Vector Graphics W3C: World Wide Web Consortium
Irodalomjegyzék
Irodalomjegyzék [1] Dr. Kondorosi Károly, Dr. László Zoltán, Dr. Szirmay-Kalos László, Objektum-orientált szoftverfejlesztés, Computer Books, 1999. [2] Erich Gamma, Ralph Johnson, Richard Helm, John Vlissides, Programtervezési minták, Kiskapu, 2004. [3] Microsoft Visual Studio, http://msdn.microsoft.com/en-us/vstudio/aa718325 [4] Kim Hamilton, Russell Miles, Learning UML 2.0, O'Reilly, 2006 [5] UML, Wikipedia, http://hu.wikipedia.org/wiki/Unified_Modeling_Language [6] IBM Software, Rose, http://www-01.ibm.com/software/awdtools/developer/rose/# [7] Umbrello UML Modeler, http://uml.sourceforge.net/ [8] ArgoUML, http://argouml.tigris.org/ [9] Eng Kit LUM , Venky Shankararaman, Create a Process-Driven Composite Application with CE 7.2, 2010. 05. 01. [10] National Instruments, http://www.ni.com/company [11] National Instruments Corporation , Getting Started with LabVIEW , April 2003 Edition [12] Harpia Project, http://s2i.das.ufsc.br/harpia [13] OpenCV, http://opencv.willowgarage.com/wiki/ [14] Glade User Interface Designer, http://glade.gnome.org/ [15] Dia projekt, http://projects.gnome.org/ [16] GTK, http://www.gtk.org/ [17] Dia Python plugin, http://live.gnome.org/Dia/Python [18] Dia tutorial, http://projects.gnome.org/dia/diatut/all/all.html [19] Scalable Vector Graphics, http://www.w3.org/Graphics/SVG/ [20] World Wide Web Consortium, http://www.w3.org/ [21] Dia-installer, http://dia-installer.de/doc/en/custom-shapes-chapter.html [22] GNU libtool, http://www.gnu.org/software/libtool/ [23] GNU make, http://www.gnu.org/software/make/ [24] Pixmap XPM, http://en.wikipedia.org/wiki/X_PixMap (ide normális linket keresni:) [25] Libxml2, http://www.xmlsoft.org/