5.18. Histogram och statistik¶
Vid sidan av de operationer som ändrar en bilds pixlar har klassen Image en familj av metoder som mäter dem – sammanfattar fördelningen av pixelvärden, returnerar medel- och medianljusstyrkan, hittar den optimala gränsen mellan mörka och ljusa pixlar och rapporterar spridningen i färgkanalerna. Mätningarna är till nytta för applikationer på två sätt: som indata till koden som avgör vilket tröskelvärde som ska användas, vilken förstärkning som ska ställas in och hur scenens tonprofil ser ut; och som diagnostiska signaler – ”är scenen tillräckligt ljus?” – som en applikation kan agera på utan att fatta något beslut om någon enskild pixel.
Utgångspunkten för nästan varje mätning är histogrammet.
5.18.1. Histogrammet¶
Ett histogram över en bild är en räkning av hur många pixlar som har varje möjligt ljusstyrkevärde. För en gråskalebild är det en lista med antal indexerade av värdena 0 till 255. För en färgbild är det tre sådana listor – en per kanal.
get_histogram() beräknar ett:
h = img.get_histogram()
Det returnerade objektet är ett histogram-resultat som exponerar bin-listorna per kanal och några högnivåförfrågningar på dem. Bin-antalen är normaliserade så att de summerar till 1.0 – histogrammet beskriver fördelningens profil snarare än det absoluta pixelantalet, vilket gör mätningarna jämförbara mellan bilder av olika storlek.
För gråskalebilder har histogrammet en kanal med bins, tillgänglig som h.bins() (eller motsvarande h[0]). För RGB565-bilder beräknas histogrammet i färgrymden LAB som introducerades på sidan om binär tröskling, med tre bin-kanaler tillgängliga som h.l_bins(), h.a_bins(), h.b_bins() (eller h[0], h[1], h[2]). LAB är samma val som tröskel- och spårningsmetoderna använder; histogram är överens med trösklar om i vilken rymd färg mäts.
5.18.2. Bins och antalet bins¶
Standardhistogrammet har en bin per möjligt pixelvärde – 256 bins för en 8-bitarskanal. Ibland är det finare upplösning än vad applikationen behöver. En klassificerare som bara bryr sig om fördelningens grova profil kan vara bättre betjänt av ett mindre antal bins – 32 eller till och med 8 bins – vilket både körs snabbare och ger ett renare resultat mot brus. Nyckelordet bins (och de kanalvisa l_bins, a_bins, b_bins för färg) ställer in antalet:
h = img.get_histogram(bins=32)
ROI- och tröskelavgränsning fungerar på samma sätt som på alla andra mätmetoder. Skicka en roi för att begränsa histogrammet till en rektangel av pixlar; skicka en thresholds-lista för att endast inkludera pixlar som matchar dessa intervall. Tröskelformen är det som gör ”beräkna histogrammet endast för de matchande pixlarna” till en operation med ett enda anrop – ett vanligt mönster när en applikation vill karaktärisera texturen i ett redan upptäckt område utan att själv behöva traversera pixlarna.
Ett gråskalehistogram med tre sammanfattande mätningar som överlägg: Otsus tröskelvärde (den gräns som bäst delar de mörka och ljusa klustren), medelvärdet och medianen. Varje mätning säger något olika om samma fördelning.¶
5.18.3. Statistik¶
Ett histogram är en beskrivning av varje värdes förekomst; statistik är de numeriska sammanfattningar som härleds från det. Objektet statistics som returneras av get_statistics() bär standarduppsättningen:
mean– det aritmetiska medelvärdet av pixelvärdena.median– värdet under vilket hälften av pixlarna ligger.mode– det vanligaste enskilda värdet.stdev– standardavvikelsen, ett mått på spridningen kring medelvärdet.minochmax– de ljusaste och mörkaste pixelvärdena som förekommer.lqochuq– de undre och övre kvartilgränserna.
För en RGB565-bild ger de kanalvisa formerna (l_mean, a_median, b_mode och så vidare) samma mätningar kanal för kanal.
De flesta av dessa tal dyker upp i specifika sammanhang. mean och stdev ger tillsammans en brusuppskattning: en scen som borde vara enhetlig har liten stdev, medan en brusig sensor ger samma scen en större stdev. min och max ger bildens kontrast: ju närmare de ligger, desto plattare är scenen; ju längre isär, desto mer dynamiskt omfång har algoritmen att arbeta med. median är den robusta mittpunkten när fördelningen har avvikare (några få mycket ljusa pixlar drar inte medianen så som de drar medelvärdet). mode är det enskilt vanligaste värdet, användbart för att hitta bakgrundsnivån i en bild vars bakgrund täcker de flesta av pixlarna.
get_statistics() kör histogramgenomgången internt och sammanfattar den sedan; att skicka samma thresholds- och roi-argument som ett tidigare beräknat histogram ger statistiken för samma uppsättning pixlar.
5.18.4. Percentiler och CDF-uppslagningar¶
Objektet histogram exponerar en metod get_percentile() som omvandlar en bråkdel till ett pixelvärde – värdet under vilket den begärda bråkdelen av pixlarna ligger. h.get_percentile(0.5) är medianen; h.get_percentile(0.05) och h.get_percentile(0.95) ger tillsammans ett robust min/max som ignorerar de nedersta och översta 5 % som avvikare.
Det är den form en applikation använder när den vill karaktärisera omfånget av pixelvärden utan att låta en handfull strökande ljusa eller mörka pixlar förvränga svaret. Det robusta min/max från den 5:e och 95:e percentilen är också den naturliga indatan till ett kontraststräckningspass – den pixelvisa ommappningen som Tonala korrigeringar täcker.
5.18.5. Otsus metod¶
Histogram besvarar en annan fråga som är värd att lyfta fram för sig själv: givet en bild vars pixlar delar upp sig i ett ”mörkt” och ett ”ljust” kluster, var går gränsen mellan dem? Tröskelsidan namngav redan mekanismen genom dess resultat – ett enda globalt tröskelvärde som applikationen kan lämna till binary() – men sköt upp hur. Hur är Otsus metod, och den finns på histogrammet.
Intuitionen: en bild med en tydlig förgrund och bakgrund har två kluster i sitt ljusstyrkehistogram, med en dal mellan dem. Rätt plats att tröskla är botten av dalen – värdet där de två klustren är bäst åtskilda. Otsus metod söker igenom varje möjlig gräns och väljer den där varianserna inom klustren är minst (vilket är samma sak som att säga att variansen mellan klustren är störst), och resultatet är den optimala binära uppdelningen för just den bildens fördelning.
Objektet histogram exponerar Otsu via get_threshold:
h = img.get_histogram()
t = h.get_threshold()
Det returnerade threshold-objektet har attributen value (för gråskala) eller l_value / a_value / b_value (för färg) som bär den valda gränsen. Att mata resultatet rakt tillbaka in i binary() ger ett självjusterande globalt tröskelvärde vars gräns väljs av bilden själv:
img.binary([(t.value, 255)])
Det mönstret löser inte problemet med ojämn belysning som den filterbaserade adaptiva tröskeln löser; vad det löser är frågan ”vilket värde ska jag klippa vid?” när global tröskling redan är rätt tillvägagångssätt. För en scen vars förgrunds-/bakgrundsskillnad är väldefinierad ligger värdet som Otsu väljer vanligtvis inom några få enheter från vad en människa skulle välja med ögat.
5.18.6. Beräkning på en differensbild¶
En användbar detalj om get_histogram() och get_statistics(): båda accepterar ett nyckelord difference som tar en annan Image och beräknar histogrammet (eller statistiken) för den pixelvisa differensen mellan källan och den bilden, utan att allokera en separat differensbild. Det är det billiga sättet att fråga ”hur mycket har scenen ändrats sedan referensbildrutan?” utan att betala för ett explicit anrop till difference() för att producera en bild vars enda syfte är att mätas. För ett kontinuerligt körande skript för rörelsedetektering blir besparingen märkbar.