5.18. Histogrammen en statistieken

Naast de bewerkingen die de pixels van een afbeelding wijzigen, beschikt de Image-klasse over een familie van methoden die ze meten – de verdeling van pixelwaarden samenvatten, de gemiddelde en mediane helderheid teruggeven, de optimale grens tussen donkere en heldere pixels vinden, de spreiding van de kleurkanalen rapporteren. De metingen voeden toepassingen op twee manieren: als invoer voor de code die beslist welke drempelwaarde te gebruiken, welke versterking in te stellen, hoe het tonale profiel van de scène eruitziet; en als diagnostische signalen – “is de scène helder genoeg?” – waarop een toepassing kan reageren zonder een beslissing over een specifieke pixel te nemen.

Het uitgangspunt voor bijna elke meting is het histogram.

5.18.1. Het histogram

Een histogram van een afbeelding is een telling van hoeveel pixels elke mogelijke helderheidswaarde hebben. Voor een grijswaardenafbeelding is dat een lijst met tellingen geïndexeerd op de waarden 0 tot en met 255. Voor een kleurafbeelding zijn het drie van zulke lijsten – één per kanaal.

get_histogram() berekent er een:

h = img.get_histogram()

Het teruggegeven object is een histogram-resultaat dat de bin-lijsten per kanaal en enkele hoogniveau-query’s daarop blootstelt. De bin-tellingen worden genormaliseerd zodat ze sommeren tot 1.0 – het histogram beschrijft het profiel van de verdeling in plaats van het absolute aantal pixels, waardoor de metingen vergelijkbaar zijn tussen afbeeldingen van verschillende groottes.

Voor grijswaardenafbeeldingen heeft het histogram één kanaal met bins, beschikbaar als h.bins() (of equivalent h[0]). Voor RGB565-afbeeldingen wordt het histogram berekend in de LAB-kleurruimte die op de pagina over binaire drempelwaarden is geïntroduceerd, met drie bin-kanalen beschikbaar als h.l_bins(), h.a_bins(), h.b_bins() (of h[0], h[1], h[2]). LAB is dezelfde keuze die de drempelwaarde- en volgmethoden gebruiken; histogrammen zijn het met drempelwaarden eens over de ruimte waarin kleur wordt gemeten.

5.18.2. Bins en het aantal bins

Het standaardhistogram heeft één bin per mogelijke pixelwaarde – 256 bins voor een 8-bits kanaal. Soms is dat een fijnere resolutie dan de toepassing nodig heeft. Een classificator die alleen geeft om het ruwe profiel van de verdeling is wellicht beter af met een kleiner aantal bins – 32 of zelfs 8 bins – wat zowel sneller draait als een schoner resultaat oplevert tegen ruis. Het sleutelwoord bins (en de per-kanaal l_bins, a_bins, b_bins voor kleur) stelt het aantal in:

h = img.get_histogram(bins=32)

ROI- en drempelwaarde-afbakening werken op dezelfde manier als bij elke andere meetmethode. Geef een roi door om het histogram te beperken tot een rechthoek van pixels; geef een thresholds-lijst door om alleen pixels op te nemen die met die bereiken overeenkomen. De drempelwaardevorm is wat “bereken het histogram van alleen de overeenkomende pixels” tot een bewerking met één aanroep maakt – een veelvoorkomend patroon wanneer een toepassing de textuur van een reeds gedetecteerd gebied wil karakteriseren zonder zelf de pixels te hoeven doorlopen.

Een grijswaardenhistogram getekend als een rij balken over het helderheidsbereik 0 tot 255. De verdeling heeft twee pieken -- een kleinere donkere piek en een grotere heldere piek -- gescheiden door een duidelijk dal. Drie verticale lijnen zijn over elkaar gelegd: de Otsu-drempelwaarde in het dal, het gemiddelde verschoven naar de grotere heldere piek, en de mediaan verder naar rechts waar het cumulatieve aantal pixels de helft bereikt.

Een grijswaardenhistogram met drie samenvattende metingen over elkaar gelegd: Otsu’s drempelwaarde (de grens die de donkere en heldere clusters het best splitst), het gemiddelde en de mediaan. Elke meting zegt iets anders over dezelfde verdeling.

5.18.3. Statistieken

Een histogram is een beschrijving van de frequentie van elke waarde; statistieken zijn de numerieke samenvattingen die eruit worden afgeleid. Het statistics-object dat door get_statistics() wordt teruggegeven bevat de standaardset:

  • mean – het rekenkundig gemiddelde van de pixelwaarden.

  • median – de waarde waaronder de helft van de pixels ligt.

  • mode – de meest voorkomende enkele waarde.

  • stdev – de standaarddeviatie, een maat voor de spreiding rond het gemiddelde.

  • min en max – de aanwezige helderste en donkerste pixelwaarden.

  • lq en uq – de onderste en bovenste kwartielgrenzen.

Voor een RGB565-afbeelding leveren de per-kanaalvormen (l_mean, a_median, b_mode, enzovoort) dezelfde metingen kanaal voor kanaal.

De meeste van die getallen komen in specifieke contexten voor. mean en stdev geven samen een ruisschatting: een scène die uniform zou moeten zijn heeft een kleine stdev, terwijl een ruisende sensor dezelfde scène een grotere stdev geeft. min en max geven het contrast van de afbeelding: hoe dichter ze bij elkaar liggen, hoe vlakker de scène; hoe verder uit elkaar, hoe meer dynamisch bereik het algoritme heeft om mee te werken. median is het robuuste midden wanneer de verdeling uitschieters heeft (een paar zeer heldere pixels trekken de mediaan niet zoals ze het gemiddelde trekken). mode is de enkele meest voorkomende waarde, nuttig om het achtergrondniveau te vinden van een afbeelding waarvan de achtergrond de meeste pixels beslaat.

get_statistics() voert intern de histogram-doorloop uit en vat die vervolgens samen; het doorgeven van dezelfde thresholds- en roi-argumenten als een eerder berekend histogram levert de statistieken voor dezelfde verzameling pixels op.

5.18.4. Percentielen en CDF-opzoekingen

Het histogram-object stelt een get_percentile()-methode beschikbaar die een fractie omzet in een pixelwaarde – de waarde waaronder de gevraagde fractie van de pixels ligt. h.get_percentile(0.5) is de mediaan; h.get_percentile(0.05) en h.get_percentile(0.95) geven samen een robuuste min/max die de onderste en bovenste 5% als uitschieters negeert.

Dat is de vorm die een toepassing gebruikt wanneer ze het bereik van pixelwaarden wil karakteriseren zonder een handjevol verdwaalde heldere of donkere pixels het antwoord te laten scheeftrekken. De robuuste min/max uit het 5e en 95e percentiel is ook de natuurlijke invoer voor een contrast-stretching-doorloop – de per-pixel-remapping die Tonale correcties behandelt.

5.18.5. Otsu’s methode

Histogrammen beantwoorden nog een vraag die het waard is om apart te benoemen: gegeven een afbeelding waarvan de pixels uiteenvallen in een “donker” en een “helder” cluster, wat is de grens daartussen? De pagina over drempelwaarden noemde het mechanisme al bij zijn resultaat – een enkele globale drempelwaarde die de toepassing kan doorgeven aan binary() – maar stelde het hoe uit. Het hoe is Otsu’s methode, en die leeft op het histogram.

De intuïtie: een afbeelding met een duidelijke voorgrond en achtergrond heeft twee clusters in zijn helderheidshistogram, met een dal ertussen. De juiste plek om te drempelen is de bodem van het dal – de waarde waar de twee clusters het best gescheiden zijn. Otsu’s methode doorzoekt elke mogelijke grens en kiest die waar de variantie binnen de clusters het kleinst is (wat hetzelfde is als zeggen dat de variantie tussen de clusters het grootst is), en het resultaat is de optimale binaire splitsing voor de verdeling van die specifieke afbeelding.

Het histogram-object stelt Otsu beschikbaar via get_threshold:

h = img.get_histogram()
t = h.get_threshold()

Het teruggegeven threshold-object heeft een value-attribuut (voor grijswaarden) of l_value / a_value / b_value (voor kleur) die de gekozen grens dragen. Het resultaat rechtstreeks terugvoeren naar binary() geeft een zelfafstemmende globale drempelwaarde waarvan de grens door de afbeelding zelf wordt gekozen:

img.binary([(t.value, 255)])

Dat patroon lost niet het probleem van ongelijkmatige belichting op dat de filtergebaseerde adaptieve drempelwaarde oplost; wat het wel oplost is de vraag “bij welke waarde moet ik snijden?” wanneer globale drempelwaarde al de juiste aanpak is. Voor een scène met een goed gedefinieerd onderscheid tussen voorgrond en achtergrond ligt de waarde die Otsu kiest meestal binnen enkele eenheden van wat een mens met het oog zou kiezen.

5.18.6. Berekenen op een verschilafbeelding

Een nuttig detail bij get_histogram() en get_statistics(): beide accepteren een difference-sleutelwoord dat een andere Image neemt en het histogram (of de statistieken) berekent van het per-pixel-verschil tussen de bron en die afbeelding, zonder een aparte verschilafbeelding toe te wijzen. Dat is de goedkope manier om te vragen “hoeveel is de scène veranderd sinds het referentieframe?” zonder te betalen voor een expliciete difference()-aanroep om een afbeelding te produceren wiens enige doel is om gemeten te worden. Voor een continu draaiend bewegingsdetectiescript telt de besparing op.