6.1. Miksi taulukoita

Image-luokka on oikea työkalu pikselityöhön, koska jokainen sen metodi toimii suoraan kameran natiivin pikselipuskurin parissa yhdessä nopeassa kutsussa. Suurin osa siitä, mitä sovellus tekee kehykselle – kynnysarvostus, blobien etsintä, AprilTag-tunnistus, reunasuodattimet – elää jo siellä.

Mitä kuvakirjasto ei tarjoa, on loput numeerisesta työstä, johon OpenMV-sovellus törmää:

  • sensoripuskurit, jotka eivät ole pikseleitä – ADC-näytteet, IMU:n (inertiamittausyksikön) akselit, mikrofoniääni,

  • kuvasta johdetut numerot, joita mikään sisäänrakennettu metodi ei palauta – histogrammisarake, kahden kehyksen mukautettu sekoitus, pikselikohtainen muunnos, jota luettelo ei kata,

  • pieni lineaarialgebra – linssin oikaiseva kalibrointimatriisi, IMU:n yhdistävä kierto,

  • signaalinkäsittelymatematiikka – värähtelypuskurin taajuussisältö, sensorin tulosteeseen sovellettu tasoitus, piirrevektori, jonka luokitin haluaa syötteeksi.

Kaikki nämä haluavat saman muodon: numeropuskurin, jonka jokaiseen alkioon sovelletaan yhtä operaatiota. Python-for-silmukka on ilmeinen tapa kirjoittaa se:

for i in range(len(samples)):
    samples[i] = samples[i] * cal

Silmukka toimii. Se on myös hidas. Python on tulkattu kieli, ja jokainen Python-silmukan kierros kantaa kustannuksen tulkin ajamisesta kerran: etsi samples, lue alkio i, kerro, kirjoita takaisin, edistä silmukkalaskuria, tarkista silmukan ehto. Tuhannen sensorinäytteen puskurissa nuo tulkkikustannukset kasvavat kymmeniksi millisekunneiksi pohjimmiltaan nopealle operaatiolle.

Tuo ylikuorma puree joka kerta, kun skripti tavoittaa puskurin. QVGA-harmaasävykehys on 76 800 pikseliä; kiihtyvyysanturi 100 Hz:llä toimittaa sata kolmiakselista näytettä sekunnissa; mikrofoni täyttää 1024-näytteisen puskurin 64 ms:n välein. Puhdas Python-for-silmukka minkä tahansa näiden yli muuttaa työn, jonka pitäisi viedä muutama mikrosekunti, sellaiseksi, joka vie kymmeniä millisekunteja – ja kuvan kokoisella puskurilla taas suunnilleen kymmenen kertaa kauemmin.

6.1.1. Kirjastofunktiot ovat silmukoita nopeampia

Korjaus on ilmaista operaatio yhtenä funktiokutsuna koko puskuria vastaan, sen sijaan että käytettäisiin Python-silmukkaa sen alkioiden yli. numpy on tarkalleen sitä: taulukkomatematiikan kirjasto, jossa jokainen operaatio on yksi jo optimoitu funktio, joka kävelee puskurin läpi kerran alusta loppuun. np.multiply(samples, cal) kertoo jokaisen samples-taulukon alkion cal-arvolla yhden kutsun sisällä – sama aritmetiikka kuin silmukka teki, ilman kierroskohtaista tulkkikustannusta. Sama 1000-alkioinen kertolasku, joka vei kymmeniä millisekunteja Python-silmukkana, vie kymmeniä mikrosekunteja numpy-kutsuna.

Tämä on sopimus, jota numpy tarjoaa kautta linjan: summa, keskiarvo, sini, eksponentti, matriisikertolasku, signaalinkäsittelyn peruslohkot – jokainen on yksi kirjastofunktio, joka toimii koko puskuriin kerralla. Vastineeksi datan on elettävä numpyn taulukkotyypissä ja operaatio on ilmaistava tuota taulukkoa vastaan, ei sen alkioita vastaan yksi kerrallaan.

6.1.2. Miksi lista ei kelpaa

Python-list ei voi astua tilalle. Lista voi pitää mitä tahansa objektien sekoitusta – kokonaislukuja, liukulukuja, merkkijonoja, muita listoja – ja sitä lukevan kirjastofunktion on silti katsottava jokaista lokeroa selvittääkseen, mitä siinä on, ja vedettävä arvo ulos ennen kuin mitään aritmetiikkaa tapahtuu. Tuo lokerokohtainen ylikuorma on tarkalleen se kustannus, jonka Python-silmukka maksaa. Listat sopivat huonosti nopeaan taulukkomatematiikkaan.

6.1.3. Miksi bytearray ei myöskään riitä

bytearray on oikea muoto – yksi tyypitetty puskuri, yksi tavu alkiota kohti, kaikki yhdessä yhtenäisessä lohkossa. Se on se, mitä useimmat tavupohjaiset oheislaite-API:t antavat takaisin. Mitä siltä puuttuu on matematiikka. bytearray * 2 toistaa puskurin sen sijaan että kaksinkertaistaisi kunkin arvon, eikä bytearray + bytearray -operaatiolle ole järkevää alkiokohtaista merkitystä.

Tietorakenne, joka yhdistää tyypitetyn puskurin alkiokohtaiseen matematiikkaan, on ndarray. Mitä laatikon sisällä on ja kuinka kukin kenttä muovaa nopean polun käyttäytymistä, ovat perustat, joiden päälle luvun loppuosa nojaa.