5.9. Rekenkundige bewerkingen¶
De tekenfamilie in de vorige sectie schildert in een afbeelding. De rekenkundige familie combineert twee afbeeldingen tot een derde – hun pixelwaarden bij elkaar optellen, de ene van de andere aftrekken, op elke positie het minimum of maximum nemen. Die kleine verzameling pixelgewijze rekenkundige bewerkingen vormt de basis waarop frame differencing, achtergrondaftrek, belichtingsstapeling en een handvol andere klassieke patronen zijn gebouwd.
De rekenkundige familie op de Image-klasse is klein genoeg om in één keer op te sommen:
add()– pixelgewijsself + other, afgekapt op het maximum van het formaat.sub()– pixelgewijsself - other, onderaan afgekapt op0.rsub()– pixelgewijsother - self, afgekapt op0(dezelfde rekenkunde alssubmet de operanden omgewisseld).min()– pixelgewijs minimum van de twee waarden.max()– pixelgewijs maximum.difference()– pixelgewijs|self - other|, het absolute verschil.
Plus twee verwante bewerkingen op één afbeelding:
invert()– vervang elke pixel door255 - pixel(of het equivalente maximum voor het formaat).
Twee brongradiënten A en B, en het resultaat van elke paarsgewijze bewerking die erop is toegepast. Elke bewerking verloopt positie voor positie – wat op een willekeurige plaats in het resultaat verschijnt, hangt alleen af van de twee bronpixels op die plaats.¶
5.9.1. Twee operandvormen¶
Elk van de twee-afbeeldingsmethoden accepteert beide vormen voor zijn tweede operand:
Een andere
Imagemet dezelfde afmetingen. De rekenkunde verloopt positie voor positie – het resultaat op(x, y)is de bewerking toegepast op de bronpixels op(x, y)van beide afbeeldingen.Een scalaire waarde – een geheel getal voor grijswaarden, een
(r, g, b)-tupel voor RGB565. Dezelfde scalair geldt op elke positie.
De scalaire vorm is nuttig wanneer de applicatie elke pixel met een constante hoeveelheid wil verschuiven. img.add(40) maakt de hele afbeelding 40 helderder; img.sub((20, 20, 20)) maakt elke pixel 20 donkerder per kanaal; img.max(50) tilt elke pixel onder 50 op tot 50 en laat de rest met rust – het soort bewerking dat een bijna-zwarte sensorbodem omzet in een vlak donkergrijs waartegen volgende fasen kunnen werken.
5.9.2. Afkappen¶
Pixelwaarden blijven bij elke bewerking binnen het bereik van het formaat. Voor een 8-bits kanaal betekent dat 0 – 255: alles wat boven 255 zou zijn overgelopen, wordt teruggekapt naar 255, en alles wat onder 0 zou zijn gegaan, wordt opgekapt naar 0. Er is geen wrap-around.
Die keuze is in de praktijk van belang. add die pixels helderder maakt, produceert nooit een plotseling verdonkeringsartefact aan de heldere kant waar de rekenkunde anders zou overlopen; sub die pixels donkerder maakt, produceert nooit een plotseling verlichtingsartefact aan de donkere kant waar het anders zou onderlopen. De resultaten blijven visueel betekenisvol ten koste van enig informatieverlies aan de verzadigde uitersten.
Het afkappen is ook waarom sub en rsub verschillende resultaten van elkaar teruggeven. img_a.sub(img_b) geeft het deel van a dat helderder is dan b en overal elders nul; img_a.rsub(img_b) geeft het deel van b dat helderder is dan a. Beide zijn nuttig voor eenzijdige veranderingsdetectie – als de applicatie alleen geïnteresseerd is in pixels die helderder werden, of alleen in pixels die donkerder werden – maar geen van beide legt alle verandering tussen twee frames vast.
5.9.3. De verschilbewerking¶
Voor tweezijdige veranderingsdetectie is de bewerking om naar te grijpen difference(), die op elke positie |self - other| berekent – het absolute, tekenloze verschil. Elke pixel die in beide richtingen veranderde, verschijnt als een niet-nulwaarde in het resultaat, met een grootte evenredig aan hoeveel deze op die positie veranderde.
Die eigenschap – niet-nul precies waar de twee afbeeldingen het oneens zijn – is wat difference het werkpaard maakt van frame-voor-frame-veranderingsdetectie. Een bij het opstarten opgeslagen referentieframe en een verse opname, door difference gehaald, leveren een afbeelding op waarvan de niet-nulpixels elke positie markeren waar iets in de scène bewoog of van helderheid veranderde.
5.9.4. Afbakenen met een mask¶
Alle rekenkundige methoden accepteren het mask-trefwoordargument dat op de pagina over regio’s en masks werd geïntroduceerd. Wanneer een mask wordt doorgegeven, draait de bewerking alleen op posities waar de mask niet-nul is; overal elders blijft de bestemmingsafbeelding ongemoeid.
Die samenstelling komt in twee patronen voor. Het eerste is het beperken van een bewerking tot een bekend gebied: bijvoorbeeld twee frames bij elkaar optellen alleen binnen het begrenzingsvak van een gedetecteerde marker. Het tweede is het stuk voor stuk opbouwen van een samengesteld frame – min over een reeks frames binnen een voorgrond-mask, max over diezelfde reeks binnen de complementaire mask – dat soort patroon.
5.9.5. Ter plaatse, met behoud van de invoer¶
De rekenkundige methoden volgen allemaal de eerder vastgelegde werkconventie: elk wijzigt de bronafbeelding ter plaatse en geeft diezelfde afbeelding terug voor chaining. De pixels van de bron zijn na de aanroep verdwenen – vervangen door het resultaat van de bewerking tegen wat er ook als tweede operand werd doorgegeven.
Wanneer de applicatie beide invoeren moet behouden, is het veilige patroon om er eerst een van te kopiëren:
diff = current.copy() # leaves current intact
diff.difference(reference) # diff now holds the absolute difference
Dat patroon – kopiëren en dan bewerken – is de ruggengraat van elke frame-differencing-pijplijn, waar het referentieframe de vergelijking moet overleven zodat het op het volgende vastgelegde frame opnieuw gebruikt kan worden.
Met zes combinerende bewerkingen, twee bewerkingen op één afbeelding, een absoluut-verschil-werkpaard en het mask-trefwoord voor afbakening dekt de pixel-rekenkundige gereedschapskist de helderheids- en kanaalcombinaties die klassieke machine vision nodig heeft. De resterende rekenkundig-achtige gereedschappen aan de oppervlakte werken bit voor bit in plaats van waarde voor waarde.