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() – pikselikohtainen self + other, leikattuna formaatin maksimiin.

  • sub() – pikselikohtainen self - other, leikattuna arvoon 0 alareunassa.

  • rsub() – pikselikohtainen other - self, leikattuna arvoon 0 (sama aritmetiikka kuin sub, 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 arvolla 255 - pixel (tai formaatin vastaavalla maksimilla).

  • negate()invert()-menetelmän alias.

Kaksi vaakasuoraa gradienttipalkkia ylhäällä edustamassa lähdekuvia A ja B -- A menee tummasta kirkkaaseen vasemmalta oikealle, B menee kirkkaasta tummaan vasemmalta oikealle. Niiden alapuolella viisi gradienttipalkkia edustamassa kunkin parikohtaisen operaation tulosta kuviin A ja B sovellettuna: A.add(B) näkyy tasaisen valkoisena, koska jokainen paikka summautuu yli arvon 255 ja leikkautuu; A.sub(B) on nolla vasemmalla puoliskolla ja kirkastuu oikealle päin; A.difference(B) näyttää V-muodon, kirkas molemmissa päissä ja tumma keskellä; A.min(B) on tumma päissä ja kirkkaampi keskellä; A.max(B) on kirkas päissä ja harmaa keskellä.

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ä 0255: 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.