In het vorige artikel over contrast stretching, hebben we percentile contrast stretching onderzocht en hoe dit kan worden toegepast om betere prestaties te verkrijgen in objectdetectiemodellen. Percentile contrast stretching wordt ook wel (histogram)normalisatie genoemd, omdat we het bereik van de pixelintensiteiten normaliseren.
In dit artikel zullen we een andere methode voor contrast stretching onderzoeken, histogram equalization genaamd. Naast het vergelijken van de modelprestaties, zullen we ook de preprocessing snelheid voor histogramnormalisatie in SAS vergelijken met onze eigen geschreven percentile contrast stretching in Python.
Een kleine samenvatting van het vorige artikel; we willen een objectdetectiemodel (Faster R-CNN) trainen om tenten in vluchtelingenkampen te detecteren. In figuur 1 is de mAP-plot van ons basisscenario te zien, samen met het rolling mean (window=100) van een maat voor de oppervlaktegrootte van de detecties (sqrt(Area)). We merken twee dingen op: hoewel er veel tenten worden gevonden (recall=0.7), hebben we veel false positives (precisie=0.2). De mAP-score is 31%. We willen dit resultaat verbeteren door contrast stretching toe te passen, en in dit artikel kijken we specifiek naar histogram equalization.
Figuur 1: Precision-Recall plot voor contrast stretching, 10 epochs
We zullen eerst de wiskunde achter histogram equalization uitleggen voor alle wiskunde-enthousiastelingen, maar voel je vrij om direct naar de resultaten te gaan!
De wiskunde
Met histogram equalization kan men alle pixelintensiteiten uniform verdelen over het bereik [0,255]. In plaats van deze waarden simpelweg meer uit te spreiden, wat we deden met percentile contrast stretching, kiezen we nu nieuwe waarden voor elke pixelintensiteit op zo'n manier dat het histogram uniform wordt verdeeld. Om dit te doen, zoeken we naar de transformatie y=f(k), waarbij y een nieuwe pixelintensiteit is op basis van de oude pixelintensiteit k.
We zullen de histogram benaderen met pixelintensiteiten vanuit een probabilistisch oogpunt. Dan kunnen we alle pixels N met intensiteit xi samenvatten als tekeningen van een stochast X. We kunnen het voorkomen van één waarde van k uitdrukken als een kans met
waar I{xi=k} een indicator functie is:
Dan is de discrete cumulatieve distributiefunctie (CDF)
Om y uniform verdeeld te maken over het bereik [0,255], gebruiken we de transformatie
We bewijzen dat deze transformatie als volgt zal resulteren in een uniforme verdeling. Aangezien we pX(k) hebben geïntroduceerd, leidt deze transformatie Y=f(X) tot een nieuwe verdeling pY(k), die kan worden afgeleid met behulp van de inverse CDF-methode:
Het nemen van de afgeleiden met betrekking tot y van beide zijden geeft
Als f-1(y)=k per definitie krijgen we
Als we y=f(k) substitueren, krijgen we
wat gelijk is aan de kansdichtheidsfunctie van een uniforme verdeling op het domein [0,255].
Resultaten
In de eerste rij van figuur 2 is een voorbeeld van een image slice voor (links) en na histogram equalization (rechts) te vinden. In de middelste rij van deze figuur worden de histogrammen van de 400×400 pixelwaarden van het originele slice getoond, samen met de histogrammen van de twee stretched slices. Het verschil in stretching methoden is vooral duidelijk in de staarten. Voor de 2-98 percentile contrast stretching hebben we een groter aantal pixels met pixelwaarde 0, aangezien alle pixels met een oorspronkelijke waarde lager dan de 2 percentielwaarde deze waarde zullen bereiken. Hetzelfde geldt voor de waarde 255.
Figuur 2: Bovenste rij: Image Slices (Bron: Google Earth, Maxar Technologies, tweede en derde slice zijn bewerkt. Middelste rij: Histogram van pixeldistributie van slice hierboven. Onderste rij: verdeling van de standaarddeviatie per image slice voor elke image band (GBR) en het gemiddelde van de standaarddeviatie van de drie kanalen (in oranje)
Ook vergelijken we opnieuw de standaarddeviatie van pixelwaarden in elk beeldsegment. De standaarddeviaties van alle 58.163 image slices zijn uitgezet in de onderste rij van figuur 2. Een opvallende observatie is het feit dat de spreiding van sigma een stuk kleiner is voor de reeks beelden na Histogram Equalization. Dit is logisch, omdat we proberen de pixelwaarden van elk beeldsegment uniform te verdelen. Om deze reden zal de variantie van de waarden van elk segment ook de theoretische waarde van een uniforme verdeling benaderen:
Met a=0 en b=255 krijgen we
Dit leidt tot een standaarddeviatie
dat is precies waar je de piek in het histogram kunt vinden.
In figuur 3 zien we dat de mAP is gestegen van 31,03% naar 40,93%. Het effect is iets groter dan de percentile contrast stretching van onze vorige post (mAP=40,58%).
Figuur 3: Precision-Recall plot na histogram equalization, 10 epochs
Verwerkingssnelheid vergelijken
Hoewel onze twee methoden niet aanzienlijk van elkaar verschillen in termen van het verhogen van de modelprestaties, zijn we geïnteresseerd of de ene methode significant beter presteert dan de andere op snelheid. Daarom draaien we beide methoden serieel op dezelfde virtuele machine met 16 cores van 2,4 GHz (8 CPU's met elk 2 cores). Het totale geheugen van deze virtuele machine is 264 GB.
Om de percentile contrast stretching in Python uit te voeren, hebben we ons eigen algoritme geschreven omdat we geen functie konden vinden die dit voor ons zou doen. Hoe je dit stukje code schrijft, is erg belangrijk voor de snelheidsprestaties. Ons eerste algoritme kon ongeveer 50 beeldsegmenten per minuut verwerken. Door meer numpy-packages en minder for-loops te gebruiken, hebben we de snelheid verhoogd tot 2500 image slices per minuut.
Bij het coderen in SAS heb je over het algemeen minder flexibiliteit in vergelijking met het coderen in Python. Dat wil zeggen, in SAS zijn er misschien minder dan 5 manieren om tot hetzelfde resultaat te komen, terwijl er in Python gemakkelijk meer dan 20 manieren zijn, allemaal afhankelijk van verschillende soorten pakketten. Deze flexibiliteit (van gemakkelijk leesbare code tot zeer efficiënte codering) gaat echter gepaard met een compromis in snelheid. Een resultaat behalen in Python is niet zo moeilijk als in SAS, maar om snel een resultaat te verkrijgen kan het veel moeilijker zijn in Python vanwege de grote hoeveelheid opties die je hebt bij het coderen van een bepaald programma. Daarom zou een SAS-programma een snelheidsvoordeel kunnen hebben. Dit lijkt inderdaad het geval. De histogram-egalisatie van 58.163 beeldsegmenten van elk 400×400 pixels duurt 7 minuten, wat betekent dat we een snelheid bereiken van 8.300 beeldsegmenten per minuut, wat meer dan 3 keer sneller is dan onze Python-code.
Hoewel de contrastmethoden verschillen en het snelheidsverschil dus niet helemaal kan worden toegeschreven aan het verschil in Python en SAS, geeft het wel een indicatie van hoe snel SAS kan zijn.
Als je de programmacodes van zowel Python als SAS wilt ontvangen, neem dan gerust contact op!