5.5. Regioner och masker

Varje operation i modulen image påverkar som standard varenda pixel i sin källbild. Det är det enklaste beteendet att beskriva, och det rätta när algoritmens uppgift verkligen omfattar hela bildrutan – en enhetlig färgkorrigering, ett globalt histogram, en kodningspass för överföring. Men de flesta algoritmer vill i praktiken titta på mindre än så. En blob-spårare som bevakar en färgad markör bryr sig om den del av scenen där markören kan dyka upp, inte väggen bakom den. En morfologisk uppstädningspass är bara säker över de pixlar som ett tidigare steg markerat som kandidater. En ansiktsdetektor kanske bara körs inuti den begränsningsruta som en grövre detektor redan smalnat av. Modulen image stöder det arbetet genom två mekanismer som begränsar en operation till en delmängd pixlar: rektangulära intresseområden, och binära masker. De kombineras fritt, och nästan varje metod som rör pixlar accepterar den ena eller den andra – eller båda – som ett nyckelordsargument.

5.5.1. Intresseområden

Ett intresseområde är en rektangel av pixlar som anges av fyrtupeln (x, y, w, h) som introducerades på koordinatsidan. Ungefär trettio metoder i gränssnittet accepterar ett roi-nyckelordsargument; när det finns med körs operationen enbart på pixlarna inuti den rektangeln och lämnar resten av bilden orörd. När roi är None eller utelämnas körs operationen över hela bilden – precis som om roi=(0, 0, width, height) hade angetts.

I kod står nyckelordet vid sidan av vilka övriga argument operationen än tar:

# Compute a histogram over a centred crop of the image.
h = img.get_histogram(roi=(64, 64, 128, 128))

Det första ROI:er ger är kontroll över falska positiva. En färgspårare som bara tittar på bordet kommer aldrig att utlösas av skjortan som går förbi; en kantdetektor som bara körs inuti det definierade arbetsområdet kommer aldrig att rapportera kanterna på själva kamerafästet. Att skära ner sökområdet till den del av scenen som algoritmen faktiskt bryr sig om är den billigaste förbättring en pipeline kan göra för sin egen tillförlitlighet.

Det andra de ger är grov-till-fin-pipelinen. Detekteringsresultatobjekt – en blob, en rect, en apriltag och så vidare – exponerar sina begränsningsrutor som samma fyrtupel (x, y, w, h) som roi accepterar. Så ett grovt första steg kan returnera en begränsningsruta, rutan placeras direkt i nästa stegs roi, och det andra steget körs över det smalare området. Varje progressiv avsmalning både snabbar upp nästa steg och gör dess resultat mer tillförlitliga, eftersom sökrymden redan har filtrerats.

5.5.2. Binära masker

En rektangel är rätt form när intresseområdet är axeljusterat. När det inte är det – ett krökt område, ett icke-konvext, de pixlar som något tidigare steg klassificerat som ”träffar” – måste operationen i stället instrueras att begränsa sig till ett godtyckligt pixelmönster. Mekanismen för det är en binär mask: en separat Image, med samma dimensioner som källan, använd som en på/av-brytare per pixel. En pixel som inte är noll i masken säger ”inkludera den matchande källpixeln”; en nollpixel säger ”lämna källpixeln orörd.”

En mask är vanligtvis en BINARY-bild – formatet med en bit per pixel som finns till exakt detta ändamål – men vilken enkanalsbild som helst fungerar, eftersom konsumenten behandlar varje värde som inte är noll som på.

Metoder för filtrering, tröskling och aritmetik accepterar ett mask-nyckelordsargument. Formen är densamma för var och en: en separat allokerad binär bild, med samma dimensioner som källan, som skickas in.

ROI:er och masker kombineras. Skicka in båda, så körs operationen enbart på pixlar som är inuti ROI:n och på i masken. De två mekanismerna ger applikationskoden oberoende reglage – ett för det rektangulära intresseområdet, ett för det godtyckliga mönstret inom det – utan att någondera formen ärver begränsningar från den andra.

Ett litet rutnät som representerar en bild. En streckad rektangel ritad över den övre-mellersta delen av rutnätet märker ROI:n: endast pixlar inuti denna rektangel beaktas. Inuti ROI:n märker en ungefär cirkulär uppsättning ifyllda celler masken: endast dessa ifyllda celler modifieras faktiskt. De återstående cellerna är lätt skuggade för att indikera att de är orörda.

En ROI begränsar en operation till en axeljusterad rektangel. En mask smalnar av den ytterligare till ett godtyckligt pixelmönster. De två kombineras: endast pixlar inuti ROI:n och på i masken modifieras.

5.5.3. Bygga masker

Tre Image-metoder bygger vanliga maskgeometrier på plats genom att nolla pixlarna utanför det valda området:

Var och en tar (x, y, w, h) (för rektangeln och ellipsen) eller (x, y, radius) (för cirkeln). Att anropa någon av dem utan argument centrerar geometrin och dimensionerar den så att den fyller bilden, vilket är den form en applikation tar till när målet är en enkel helbildsoval eller -cirkel som inte döljer något annat än hörnen.

mask = image.Image(img.width(), img.height(), image.BINARY)
mask.clear()              # start from all zeros
mask.mask_ellipse()       # centred, full-size oval

De intressanta maskerna kommer sällan från enbart mask_*-metoderna. De kommer från tidigare steg i pipelinen: en trösklingspass producerar en binär bild vars pixlar som inte är noll markerar träffarna, exakt rätt form att mata in i nästa stegs mask=-argument. En morfologisk uppstädningspass förfinar den masken utan att ändra dess form. Allt som slutar som en enkanalsbild är i sig en giltig mask.

5.5.4. Hur operationer modifierar bilden

Ett mönster som syns i varje kodavsnitt på de senaste sidorna – operationen som returnerar samma img för kedjning – är värt att lyfta fram explicit så att det inte behöver upprepas varje gång en ny metod introduceras. Tre familjer av metoder förekommer i Image-gränssnittet, var och en behandlar källbilden på olika sätt:

  • Operationsmetoder modifierar källans pixlar på plats och returnerar samma bild för kedjning. Familjerna för ritning, aritmetik, tröskling och filter beter sig alla på detta sätt. img.gaussian(1) suddar img och returnerar samma img; att tilldela om – img = img.gaussian(1) – är harmlöst men onödigt.

  • Konverteringsmetoder opererar som standard på plats på samma sätt som operationsmetoder gör, men de accepterar copy=True och copy_to_fb=True för att allokera en separat resultatbild när källan behöver bevaras. Formatkonverteringarna och de geometriska kopiorna är de huvudsakliga medlemmarna i denna familj.

  • Inspektionsmetoder läser pixlarna och returnerar ett resultatobjekt – en lista över detekterade särdrag, ett histogram, en uppsättning statistik – utan att modifiera källbilden alls.

Den trikotomin är konsekvent över hela gränssnittet. Att veta vilken familj en metod tillhör talar om för applikationen vad den kan förvänta sig av ett anrop: huruvida källans pixlar kommer att överleva intakta, huruvida en separat resultatbild kommer att allokeras, och huruvida returvärdet är källan själv eller något annat.