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.

Two horizontal gradient bars at the top representing source images A and B -- A going dark to bright left-to-right, B going bright to dark left-to-right. Below them, five gradient bars representing the result of each pairwise operation applied to A and B: A.add(B) appears uniform white because every position sums past 255 and clips; A.sub(B) is zero on the left half and brightens toward the right; A.difference(B) shows a V shape, bright on each end and dark in the middle; A.min(B) is dark on the ends and brighter in the middle; A.max(B) is bright on the ends and grey in the middle.

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.