Reaalaja videotöötluse jõudluse parendamine Eesti Infotehnoloogia Kolledži Robootikaklubi Botmaster platvormi baasil
Enhancement of Real-time Video Processing Performance based on Estonian Information Technology College Robotics Club Botmaster Platform

Wednesday, September 19, 2012

Chapter 3 - little smarty singleton


Tööl olid vahepeal karmid ajad, olime graafikust maas ja töötasime 10h päevas. Rootsis asjad ilmselt käivadki  nii - ühel hommikusel miitingul lihtsalt öeldi, et koodivabrik hakkab täispööretel tööle.. ja nii ongi :D Ja see kestis üle kahe nädala järjest ning oli isegi päris fun, aga lõputöö ja botmasteriga tegelemiseks ei jäänud eriti aega. Nüüd on kiired ajad jälle möödas :)

Seoses lõputöö administratiivse poolega.
  • Lõputöö deklareerisin ÕISis ära (Ei maganudki maha). 
  • Juhendaja andis ametliku kinnituse, et nõustub juhendama mu diplomitööd. Juhendajaks Valdur Kaldvee!

Vahepeal kohendasin veidi ja kompileerisin uuesti ov534 kaamera draiveri, sest esimesel korral olin teinud näpuka ja punane led tuluke läks videopildi edastamisel põlema, kuid ei kustunud enam. (Kui kaamera USBist välja tõmmata, siis muidugi kustus) Ja see pisiasi häiris.

VisionTime.cpp & VisionTime.h in Botmasters NOW!

Olemus ja Ehitus
Sain valmis oma fancy tööriista, mille disainisin antud olukorras spetsiaalselt videotöötluse jõudluse mõõtmiseks. Samas saab seda kasutada kõikjal, kus protsesside täidesaatmine toimub tsükliliselt. Panin sellele nimeks VisionTime.
Tool ise on singleton disainimustriga, mis sarnaste lahenduste puhul üldlevinud:
  • Klassi kuuluva(te) objekti(de) arv on alati rangelt piiratud ühele & alati ainult üks ligipääsupunkt
  • "Global point of access" - globaalselt ligipääsetav 
  • “Just-in-time initialization”
Ja selle saavutamiseks on kasutusel privaatne konstruktor ning privaatne pointer staatilisele objekti instantsile. (Java kutina on mul C++'is pidev peavalu nende pointeritega, no ei oska kunagi otsustada, kas kasutada või mitte :P) Instantsi "getter" on avalik ning esimasel väljakutsumisel algul loob objekti ja siis tagastab, järgnevatel kordadel lihtsalt tagastab olemasoleva. 
Paljud loggerid on taolise ülesehitusega.  Väga lihtne ja basic ning teatud funktsionaalsuse puhul ka päris vajalik. Näiteks andmebaasisessioonide puhul, kui on vaja vältida paralleelseid ühendusi.  Mul on olnud kokkupuuteid singletoniga küll, javas ka levinud, kuid üldiselt reeglina ei soovitata andmeid ja objekte kasutada globaalse kehtivusega. Aga hetkel see vastas nõudmistele ideaalselt.

Alustasin tegelikult sellega, et mõtlesin täiustada Timer.cpp klassi, mille Silver oli kirjutanud 2010 aastal oma lõputöö arvutuste jaoks. Mingi hetk sain aru, et minu tool-i funktsionaalsus hakkab vanast timer.cpp skoobist väga kaugele eemalduma. Otsustasingi teha eraldiseisvaks. Vanast timerist on kasutusel vaid samasugune ajahetke "püüdmise" printsiip.

Kuidas töötab
Koodis pannakse lipukesed enne ja pärast mõõdetavaid protsesse. Samuti seadistatakse paika, mitmendast kaadrist alates algab mõõtmine ning, mitu järjestikust kaadrit arvestatakse. 
Sel hetkel kui toimub videotöötlus, VisionTime ise mingeid kalkulatsioone ei tee, vaid salvestab Time Stamp Counter väärtusi eraldi csv faili  - (edit: järgmises versioonis loobusin csv faili kirjutamisest ning hakkasin salvestama otse RAMi, sest see on muidugi veelgi kiirem) üritab olla botmasteri videotöötlemise ajal nii lightweight kui võimalik. Väärtuste hankimiseks kasutatakse POSIX programmiliidese funktsiooni clock_gettime määrates ära parameetrina monotoonse kella (CLOCK_MONOTONIC). See loendur on nanosekundilise täpsusega ja üliväikese üldkuluga - võtab mõned protsessori taktid, et loenduri väärtus lugeda.

1 nanosekund on 1/1000000000 sekundit ehk äärmiselt väike sekundi murdosa.
Näiteks 3.3 nanosekundiga liigub valgus vaakumis kõigest ühe meetri. (vees ja atmosfääris veel aeglasemalt)

Kaks VisionTime Tool'i ajasalvestust (sündmuse algus ja lõpp) võtavad aega umbes ~50000ns(edit: 2000ns), ehk on võimelised salvestama 20 000 (edit: 500000) korda sekundis.

Kogu tulemuste arvutamine toimub alles lõpufaasis, kui on kogutud ajasalvestused soovitud arvu kaadrite kohta. Põhimõtteliselt tehakse ära "tagantjärgi arvutamine". VisionTime teeb tuhandeid arvutusi, rehkendab iga mõõdetava protsessi keskmised ja lisatab muud statistikat, mida saadud numbrite alusel saab kokku panna.

Pärast seda läheb automaatselt käima Open-Office Calc, mis kuvab tabeli koos tulemustega. (Mu lemmik featuur :D)

Umbes sellise tabeli annab välja:
(hetkel mõõdetud suht suvalised protsessid)

<... umbes 600 sarnast rida nagu järgmised kolm>
procfunc_goalB e 183564720
procfunc e 183707475
frame e 193506229




-------------------------------------------


MEASUREMENT OVERVIEW


Started_measuring_from_frame: 90

Measured_nr_of_frames: 30





-----


RESULTS


Process Total_Nanoseconds Occurrences Avg_nanoseconds
fps_calc 4345991 30 144866
Gauss 227982470 30 7599415
Lut 368607550 30 12286918
procfunct_cvline 1410093 30 47003
procfunc_goalB 185440320 30 6181344
frame 1705898552 30 56863285
procfunct_stor_crt 1106627 30 36887
procfunc_goalY 116735493 30 3891183
procfunc_ball 70547238 30 2351574
procfunc 389610809 30 12987026




-----


ACCORDING_TO_CALCULATIONS


Average_FPS: 17

Slowest_process:(procfunc) 12987026 ns (Avg.)
Fastest_process:(fps_calc) 144866 ns (Avg.)

DEMO!

Väikeses aknas captionid ei tööta päris nii nagu vaja :P


Kuidas saab panna tööle
Iseenesest mõistetav, et..
#include "visiontime.h"
VisionTime liidese juhtimine käib kujul VisionTime::DO()->funktsiooniNimi(Parameeter);
Kokku on 6 avalikku juhtimisfunktsiooni.

Esimesed kolm on mõistlik käivitada testitava programmi(nt Botmasteri) main() funktsioonis.

1. turnOn() funktsioon lülitab sisse VisionTime'i. Kui see välja kommenteerida, siis ülejäänud VisionTime'i funktsioonid ei tee mitte midagi (pole lõplikult valmis):
VisionTime::DO()->turnOn();
2. setMeasureNumberOfFrames() - määrab mitme kaadri kohta tulemused arvutatakse. Nimelt ei ole pilditöötluse kiirus konstantne, see muutub ajas pidevalt. Ühe ainsa kaadri mõõtmine on mõttetu, sest tegu võib olla kaadriga, tegelikust keskmisest parem/halvem. VisionTime kogub info etteantud arvu kaadrite kohta ning arvutab keskmised.
VisionTime::DO()->setMeasureNumberOfFrames(30);
3. setMeasutingFromFrame() - määrab mitmendast kaadrist alates alustatakse mõõtmist. Jällegi pilditöötluse kiirus pole konstante ja kaamera videovoo edastamise alguses on jõudlus mõnevõrra kehvem ning paari sekundi pärast stabiliseerub.
VisionTime::DO()->setStartMeasuringFromFrame(90);
Järgmised kolm lähevad sinna, kus toimub mõõdetav töötlemistsükkel.

4. newFrame() - lipuke, mis määrab momendi, mil algab uus kaader. Ehk siis selline rida tuleb panna koodis uue kaadri funktsiooni vahetusse lähedusse.
VisionTime::DO()->newFrame();
5. markBegin() - lipuke, mis määrab momendi, mil algab üks mõõdetavatest protsessidest. Funktsiooni argumendiks on protsessi nimetus või lühend - seda kasutatakse lõpus tulemuste kokkuvõttes.
VisionTime::DO()->markBegin("Lut");
// Mõõdetav protsess nt 'Lut' vms
6. markEnd() - lipuke, mis määrab momendi, mil lõpeb üks mõõdetavatest protsessidest. Argument on sama nagu oli protsessi alguse lipukese puhul.
VisionTime::DO()->markEnd("Lut");

VisionTime aktsepteerib ka "alamprotsesse":
VisionTime::DO()->markBegin("Process");
VisionTime::DO()->markBegin("Sub-process1");
//alamprotsess1
VisionTime::DO()->markEnd("Sub-process1"); 
VisionTime::DO()->markBegin("Sub-process2");
//alamprotsess2
VisionTime::DO()->markEnd("Sub-process2"); 
//miskit muud
VisionTime::DO()->markEnd("Process");
Kuid alamprotsess ei tohi olla sama nimega:
VisionTime::DO()->markBegin("Nimi");
//mingi protsess
VisionTime::DO()->markBegin("Nimi");
//mõõdetav protsess
VisionTime::DO()->markEnd("Nimi"); //Kumb lõpeb?
VisionTime::DO()->markEnd("Nimi"); //Kumb lõpeb?

Samuti ei pea mõõdetavad protsessid esinema sama arv kordi. Iga protsessi keskmine arvutatakse täpselt selle järgi kui tihti selle käivitamiseni programm jõudis. Ainus nõue on see, et viimase mõõdetava kaadri lõpuks ei tohi olla ühtegi pooleliolevat protsessi. Videotöötluse puhul on see loomulik, sest uue kaadri alguses peab olema kogu eelmise kaadri töötlemine juba "kauge minevik" :)

Mõned issued veel on (seoses turnOn() funktsionaalsuse ja käskude kujuga), mida tahaks muuta, aga põhiline funktsionaalsus on sellel tükil valmis. Hea mini-projektike c++'ga harjumiseks enne suuremaid vägitegusid ning lähitulevikus see jupp kannab ka vilja kuna säästab kindlasti palju aega. Igasuguse koodimuudatuse peale saab asja lihtsalt käima lükata ning vähem kui minuti pärast on ees kena tabel väljaarvutatud tulemustega. Ning kohe saab võrrelda, kuidas muudatused mõjutasid jõudlust. Elu on päris lill, kui keegi teeb alati sinu eest tülika mõõtmistöö :)

Source v1.0:
visiontime.cpp


Nüüd saan keskenduda mõõtmiste analüüsile ning alustan suurte Botmasteri uuendustöödega.

2 comments:

  1. normps update timerile, sa võid vana asendada :P respection :)

    ReplyDelete
  2. Aitäh :) Aga ei maksa vana arvelt võtta.
    Eks neil on erinev ampluaa :P
    Vana timer sobib paremini ühekordseteks mõõtmisteks vms.

    ReplyDelete