5.9. Aritmeettiset operaatiot¶
Edellisen osion piirtoperhe maalaa kuvan sisään. Aritmeettinen perhe yhdistää kaksi kuvaa kolmanneksi – laskemalla niiden pikseliarvot yhteen, vähentämällä toisen toisesta, ottamalla minimin tai maksimin jokaisessa paikassa. Tuo pieni joukko pikselikohtaisia aritmeettisia operaatioita on se, jonka päälle kehyserotus, taustan vähennys, valotuksen pinoaminen ja kourallinen muita klassisia kuvioita rakentuvat.
Image-luokan aritmeettinen perhe on tarpeeksi pieni lueteltavaksi kerralla:
add()– pikselikohtainenself + other, leikattuna formaatin maksimiin.sub()– pikselikohtainenself - other, leikattuna arvoon0alareunassa.rsub()– pikselikohtainenother - self, leikattuna arvoon0(sama aritmetiikka kuinsub, operandit käännettyinä).min()– pikselikohtainen kahden arvon minimi.max()– pikselikohtainen maksimi.difference()– pikselikohtainen|self - other|, itseisarvoinen erotus.
Lisäksi kaksi siihen liittyvää yhden kuvan operaatiota:
invert()– korvaa jokaisen pikselin arvolla255 - pixel(tai formaatin vastaavalla maksimilla).
Kaksi lähdegradienttia A ja B sekä kunkin niihin sovelletun parikohtaisen operaation tulos. Jokainen operaatio suoritetaan paikka paikalta – se mitä tuloksessa näkyy jossakin sijainnissa riippuu vain kahdesta lähdepikselistä kyseisessä sijainnissa.¶
5.9.1. Kaksi operandimuotoa¶
Kukin kahden kuvan menetelmä hyväksyy kumman tahansa muodon toisena operandinaan:
Toinen samankokoinen
Image. Aritmetiikka suoritetaan paikka paikalta – tulos kohdassa(x, y)on operaatio sovellettuna molempien kuvien lähdepikseleihin kohdassa(x, y).Skalaariarvo – kokonaisluku harmaasävylle,
(r, g, b)-monikko RGB565:lle. Sama skalaari sovelletaan jokaiseen paikkaan.
Skalaarimuoto on hyödyllinen, kun sovellus haluaa siirtää jokaista pikseliä vakiomäärällä. img.add(40) kirkastaa koko kuvaa 40:llä; img.sub((20, 20, 20)) tummentaa jokaista pikseliä 20:llä kanavaa kohden; img.max(50) nostaa jokaisen alle 50:n pikselin arvoon 50 ja jättää loput rauhaan – tällainen operaatio muuttaa lähes mustan sensorin pohjan tasaiseksi tummanharmaaksi, jota vasten seuraavat vaiheet voivat työskennellä.
5.9.2. Leikkaaminen¶
Pikseliarvot pysyvät formaatin alueella jokaisen operaation läpi. 8-bittiselle kanavalle se tarkoittaa väliä 0 – 255: kaikki, mikä olisi vuotanut yli arvon 255, leikataan takaisin arvoon 255, ja kaikki, mikä olisi mennyt alle arvon 0, leikataan ylös arvoon 0. Ympärimenoa ei tapahdu.
Tuolla valinnalla on käytännön merkitystä. add kirkastaessaan pikseleitä ei koskaan tuota äkillistä tummenemisartefaktaa kirkkaassa päässä, jossa matematiikka muuten vuotaisi yli; sub tummentaessaan pikseleitä ei koskaan tuota äkillistä kirkastumisartefaktaa tummassa päässä, jossa se muuten vuotaisi ali. Tulokset pysyvät visuaalisesti merkityksellisinä jonkin verran tiedonhäviön kustannuksella kyllästyneissä ääripäissä.
Leikkaaminen on myös syy siihen, miksi sub ja rsub palauttavat toisistaan erilaiset tulokset. img_a.sub(img_b) antaa sen osan kuvasta a, joka on kirkkaampi kuin b, ja nollan kaikkialla muualla; img_a.rsub(img_b) antaa sen osan kuvasta b, joka on kirkkaampi kuin a. Kumpikin on hyödyllinen yksipuoliseen muutoksentunnistukseen – jos sovellus välittää vain pikseleistä, jotka kirkastuivat, tai vain pikseleistä, jotka tummenivat – mutta kumpikaan ei kaappaa kaikkea muutosta kahden kehyksen välillä.
5.9.3. Erotusoperaatio¶
Kaksipuoliseen muutoksentunnistukseen tartuttava operaatio on difference(), joka laskee |self - other| jokaisessa paikassa – itseisarvoisen erotuksen, etumerkittömänä. Jokainen pikseli, joka muuttui kumpaan tahansa suuntaan, näkyy nollasta poikkeavana arvona tuloksessa, ja suuruus on verrannollinen siihen, kuinka paljon se muuttui kyseisessä paikassa.
Tuo ominaisuus – nollasta poikkeava juuri siellä, missä kaksi kuvaa eroavat toisistaan – on se, mikä tekee difference-operaatiosta kehys kehykseltä -muutoksentunnistuksen työjuhdan. Käynnistyksessä tallennettu vertailukehys ja tuore kaappaus, difference-operaation läpi ajettuina, tuottavat kuvan, jonka nollasta poikkeavat pikselit merkitsevät jokaisen paikan, jossa jokin näkymässä liikkui tai muutti kirkkauttaan.
5.9.4. Rajaaminen maskilla¶
Kaikki aritmeettiset menetelmät hyväksyvät mask-avainsana-argumentin, joka esiteltiin alueet ja maskit -sivulla. Kun maski annetaan, operaatio suoritetaan vain niissä paikoissa, joissa maski on nollasta poikkeava; kaikkialla muualla kohdekuva jätetään rauhaan.
Tuo yhdistely näkyy kahdessa kuviossa. Ensimmäinen on operaation rajoittaminen tunnettuun alueeseen: esimerkiksi kahden kehyksen yhteenlaskeminen vain havaitun merkin rajauslaatikon sisällä. Toinen on yhdistelmäkehyksen rakentaminen pala palalta – min kehysten sarjan yli etualan maskin sisällä, max saman sarjan yli täydentävän maskin sisällä – tämänkaltainen kuvio.
5.9.5. Paikan päällä, ja syötteet säilyttäen¶
Aritmeettiset menetelmät noudattavat kaikki aiemmin vakiintunutta toimintatapaa: kukin muokkaa lähdekuvaa paikan päällä ja palauttaa saman kuvan ketjutusta varten. Lähteen pikselit ovat poissa kutsun jälkeen – korvattuina operaation tuloksella sitä vastaan, mikä välitettiin toisena operandina.
Kun sovelluksen on säilytettävä molemmat syötteet, turvallinen kuvio on kopioida toinen niistä ensin:
diff = current.copy() # leaves current intact
diff.difference(reference) # diff now holds the absolute difference
Tuo kuvio – kopioi, sitten operoi – on minkä tahansa kehyserotusputken selkäranka, jossa vertailukehyksen on selvittävä vertailusta, jotta sitä voidaan käyttää uudelleen seuraavassa kaapatussa kehyksessä.
Kuudella yhdistelyoperaatiolla, kahdella yhden kuvan operaatiolla, itseisarvoisen erotuksen työjuhdalla ja rajaamiseen tarkoitetulla mask-avainsanalla pikseliaritmetiikan työkalupakki kattaa kirkkaus- ja kanavayhdistelmät, joita klassinen konenäkö tarvitsee. Jäljellä olevat aritmetiikan kaltaiset työkalut toimivat pinnalla bitti kerrallaan arvo kerrallaan sijaan.