Een koloniepikker is een instrument waarmee microbiële kolonies die op een vaste voedingsbodem groeien automatisch kunnen worden gelocaliseerd, opgepikt en gedupliceerd op een vaste of vloeibare voedingsbodem. Doorgaans wordt een petrischaal aangebracht op de lichtplaat van de koloniepikker, waarna algoritmen voor beeldanalyse de juiste kolonies uitkiezen en een robotarm aansturen om die kolonies te bemonsteren. Dergelijke toestellen worden zowel in onderzoekslaboratoria als in industriële omgevingen gebruikt, bijvoorbeeld om voedings- of bloedstalen te analyseren.
De beeldverwerkingssoftware krijgt de foto van een petriplaat te zien onder de vorm van een bitmap. Dit is niets anders dan een rechthoekig rooster waarvan de vakjes bits genoemd worden omdat ze slechts twee toestanden kunnen aannemen. Elke bit van het rooster is ofwel leeg (voorgesteld door een spatie) of wordt bedekt door een kolonie (voorgesteld door een hekje: #). Als voorbeeld zie je hieronder een bitmap waarop een aantal kolonies te zien zijn. In een bitmap wordt een kolonie gevormd door een gebied van aangrenzende bedekte bits. Bits worden hierbij aangrenzend genoemd als ze een gemeenschappelijke zijde hebben. De software zal een gebied van aangrenzende bedekte bits echter nooit als een kolonie beschouwen, als het bits op de buitenste rand van de bitmap bevat.
Definieer een klasse Petrischaal waarmee kolonies op een petrischaal kunnen geanalyseerd worden. De objecten van deze klasse moeten minstens de volgende methoden hebben:
Een initialisatiemethode __init__ waaraan de locatie van een tekstbestand moet doorgegeven worden. Dit tekstbestand bevat een bitmap van de petrischaal die moet geanalyseerd worden.
Een methode __str__ die een stringvoorstelling van de petrischaal teruggeeft. Deze stringvoorstelling geeft het rechthoekig rooster weer zoals het uit het opgegeven bitmapbestand werd gelezen. Naast lege bits (voorgesteld door spaties) en bits die bedekt worden door kolonies (voorgesteld door hekjes: #), kunnen bits daarnaast ook verwerkt zijn (voorgesteld door punten). Dit verwerken gebeurt door het aanroepen van de methode kolonies (zie verder).
Een methode kolonie waaraan het rij- en kolomnummer van een bedekte bit in de bitmap moeten doorgegeven worden. De rijen van een bitmap worden genummerd van boven naar onder, de kolommen van links naar rechts, en de nummering start telkens vanaf nul. Indien de bit op de opgegeven positie niet bedekt is, moet de methode een AssertionError opwerpen met de tekst geen kolonie gevonden op positie (rij, kolom). Hierbij moeten de cursieve fragmenten ingevuld worden op basis van de argumenten die aan de methode doorgegeven werden. Anders moeten alle bits van het gebied van aangrenzende bedekte bits waartoe de gegeven bit behoort als verwerkt gemarkeerd worden (ze worden dus niet langer als bedekt beschouwd nadat de methode werd aangeroepen), en moet de methode teruggeven uit hoeveel bits dit gebied bestaat.
Een methode ongedaan_maken die alle verwerkte bits van de bitmap terug als bedekt markeert. Merk op dat de verwerkte bits in de bitmap mogelijks tot meerdere kolonies kunnen behoren, bijvoorbeeld als de methode kolonie verschillende keren werd aangeroepen voordat de methode ongedaan_maken wordt aangeroepen.
Gebruik de methoden kolonie en ongedaan_maken om een methode aantal te schrijven die het aantal kolonies teruggeeft die niet kleiner zijn dan de opgegeven minimale grootte. De minimale grootte kan doorgegeven worden aan de optionele parameter minimum (standaardwaarde: 1). De grootte van een kolonie wordt uitgedrukt als het aantal bits in het overeenkomstige gebied van aaneengesloten bedekte bits in de bitmap. Het gebruik van een minimale grootte is belangrijk om kleine artefacten uit te sluiten die kunnen ontstaan bij het omzetten van de foto van de petriplaat naar de overeenkomstige bitmap. We merken hier opnieuw op dat gebieden van aaneengesloten bedekte bits die bits op de buitenste rand van de bitmap bevatten niet als kolonie mogen beschouwd worden. Deze gebieden moeten dus genegeerd worden als de methode de kolonies telt. Zorg er voor dat de bitmap geen verwerkte bits heeft nadat de methode werd aangeroepen.
Gebruik de methoden kolonie en ongedaan_maken om een methode grootte te schrijven die de gemiddelde grootte teruggeeft van alle kolonies die niet kleiner zijn dan de opgegeven minimale grootte. De minimale grootte kan doorgegeven worden aan de optionele parameter minimum (standaardwaarde: 1). Net zoals de vorige methode mag ook deze methode de gebieden van aaneengesloten bedekte bits die bits op de buitenste rand van de bitmap bevatten niet als kolonies beschouwen, en mag de bitmap ook geen verwerkte bits vertonen na het aanroepen van de methode. De methode moet de waarde None teruggeven indien er geen kolonies zijn die groter of gelijk zijn dan de opgegeven minimale grootte.
Bij onderstaande voorbeeldsessie gaan we ervan uit dat het tekstbestand schaal.txt12 zich in de huidige directory bevindt. Dit bestand bevat de bitmap van een petriplaat die hierboven grafisch werd weergegeven.
>>> schaal = Petrischaal('schaal.txt')
>>> print(schaal)
<BLANKLINE>
###
#####
#######
#######
######
###### ##
#### #####
## ###### ####
# ###### ####
### ########## #####
####### #### ## ######
######### ## # #####
# #### ### ###
##### #### # ## ##
##### ###### #
###### ########
#### ########
#######
#######
>>> schaal.kolonie(10, 35)
42
>>> print(schaal)
<BLANKLINE>
###
#####
#######
#######
######
###### ..
#### .....
## ...... ####
# ...... ####
### .......... #####
####### .... .. ######
######### .. . #####
# #### ... ###
##### #### . ## ##
##### ###### #
###### ########
#### ########
#######
#######
>>> schaal.kolonie(10, 40)
Traceback (most recent call last):
AssertionError: geen kolonie gevonden op positie (10, 40)
>>> schaal.kolonie(10, 45)
30
>>> print(schaal)
<BLANKLINE>
###
#####
#######
#######
######
###### ..
#### .....
## ...... ....
# ...... ....
### .......... .....
####### .... .. ......
######### .. . .....
# #### ... ...
##### #### . ## ..
##### ###### .
###### ########
#### ########
#######
#######
>>> schaal.ongedaan_maken()
>>> print(schaal)
<BLANKLINE>
###
#####
#######
#######
######
###### ##
#### #####
## ###### ####
# ###### ####
### ########## #####
####### #### ## ######
######### ## # #####
# #### ### ###
##### #### # ## ##
##### ###### #
###### ########
#### ########
#######
#######
>>> schaal.kolonies()
5
>>> schaal.koloniegrootte()
32.2