5.9. Aritmetiska operationer¶
Ritfamiljen i föregående avsnitt målar in i en bild. Den aritmetiska familjen kombinerar två bilder till en tredje – adderar deras pixelvärden, subtraherar det ena från det andra, tar minimum eller maximum på varje position. Den lilla uppsättningen pixelvisa aritmetiska operationer är vad bildrutsdifferentiering, bakgrundssubtraktion, exponeringsstapling och en handfull andra klassiska mönster bygger på.
Den aritmetiska familjen på Image-klassen är liten nog att räkna upp på en gång:
add()– pixelvisself + other, klippt till formatets maximum.sub()– pixelvisself - other, klippt till0i botten.rsub()– pixelvisother - self, klippt till0(samma aritmetik somsubmed operanderna omkastade).min()– pixelvis minimum av de två värdena.max()– pixelvis maximum.difference()– pixelvis|self - other|, den absoluta skillnaden.
Plus två relaterade operationer för enskilda bilder:
invert()– ersätt varje pixel med255 - pixel(eller motsvarande maximum för formatet).
Två källgradienter A och B, och resultatet av varje parvis operation tillämpad på dem. Varje operation körs position för position – vad som visas i resultatet på en given plats beror endast på de två källpixlarna på den platsen.¶
5.9.1. Två operandformer¶
Var och en av tvåbildsmetoderna accepterar någon av formerna för sin andra operand:
En annan
Imagemed samma dimensioner. Aritmetiken körs position för position – resultatet på(x, y)är operationen tillämpad på källpixlarna på(x, y)i båda bilderna.Ett skalärt värde – ett heltal för gråskala, en
(r, g, b)-tupel för RGB565. Samma skalär tillämpas på varje position.
Skalärformen är användbar när applikationen vill förskjuta varje pixel med ett konstant belopp. img.add(40) ljusnar hela bilden med 40; img.sub((20, 20, 20)) mörkar varje pixel med 20 per kanal; img.max(50) lyfter varje pixel under 50 upp till 50 och lämnar resten ifred – den sortens operation som förvandlar ett nästan svart sensorgolv till ett jämnt mörkgrått som efterföljande steg kan arbeta mot.
5.9.2. Klippning¶
Pixelvärden hålls inom formatets intervall genom varje operation. För en 8-bitarskanal betyder det 0 – 255: allt som annars skulle ha svämmat över förbi 255 klipps tillbaka till 255, och allt som skulle ha gått under 0 klipps upp till 0. Det finns ingen överlappning.
Det valet spelar roll i praktiken. add som ljusnar pixlar producerar aldrig en plötslig mörkningsartefakt i den ljusa änden där matematiken annars skulle svämma över; sub som mörkar pixlar producerar aldrig en plötslig ljusningsartefakt i den mörka änden där den annars skulle underskrida. Resultaten förblir visuellt meningsfulla till priset av viss informationsförlust vid de mättade ytterligheterna.
Klippningen är också anledningen till att sub och rsub returnerar olika resultat från varandra. img_a.sub(img_b) ger den del av a som är ljusare än b och noll överallt annars; img_a.rsub(img_b) ger den del av b som är ljusare än a. Endera är användbar för ensidig förändringsdetektering – om applikationen bara bryr sig om pixlar som blev ljusare, eller bara om pixlar som blev mörkare – men ingen av dem fångar all förändring mellan två bildrutor.
5.9.3. Differensoperationen¶
För tvåsidig förändringsdetektering är operationen att gripa till difference(), som beräknar |self - other| på varje position – den absoluta skillnaden, teckenlös. Varje pixel som ändrades i endera riktningen visar sig som ett nollskilt värde i resultatet, med magnituden proportionell mot hur mycket den ändrades på den positionen.
Den egenskapen – nollskild exakt där de två bilderna är oense – är vad som gör difference till arbetshästen för förändringsdetektering bildruta för bildruta. En referensbildruta lagrad vid uppstart och en ny infångning, körda genom difference, producerar en bild vars nollskilda pixlar markerar varje position där något i scenen rörde sig eller ändrade ljusstyrka.
5.9.4. Avgränsning med mask¶
Alla de aritmetiska metoderna accepterar nyckelordsargumentet mask som introducerades på sidan om områden och masker. När en mask skickas körs operationen endast på positioner där masken är nollskild; överallt annars lämnas destinationsbilden ifred.
Den kompositionen dyker upp i två mönster. Det första är att begränsa en operation till ett känt område: att till exempel addera två bildrutor endast inuti en detekterad markörs begränsningsruta. Det andra är att bygga upp en sammansatt bildruta bit för bit – min över en sekvens av bildrutor inuti en förgrundsmask, max över samma sekvens inuti den kompletterande masken – den sortens mönster.
5.9.5. På plats, och med bevarade indata¶
De aritmetiska metoderna följer alla den arbetskonvention som etablerades tidigare: var och en modifierar källbilden på plats och returnerar samma bild för kedjning. Källans pixlar är borta efter anropet – ersatta med resultatet av operationen mot vad som än skickades som andra operand.
När applikationen behöver bevara båda indata är det säkra mönstret att kopiera en av dem först:
diff = current.copy() # leaves current intact
diff.difference(reference) # diff now holds the absolute difference
Det mönstret – kopiera, sedan operera – är ryggraden i varje bildrutsdifferentieringspipeline, där referensbildrutan måste överleva jämförelsen så att den kan återanvändas på nästa infångade bildruta.
Med sex kombinerande operationer, två operationer för enskilda bilder, en arbetshäst för absolut skillnad, och nyckelordet mask för avgränsning täcker verktygslådan för pixelaritmetik de ljusstyrke- och kanalkombinationer som klassiskt maskinseende behöver. De återstående aritmetiklika verktygen på ytan arbetar bit för bit snarare än värde för värde.