A colony picker is an instrument that is used to automatically localize, pick up or duplicate microbial colonies that grow on a fixed or fluid medium. Usually, a Petri dish is inserted on the light plate of the colony picker, after which algorithms for image analysis choose the right colonies and a operate a robot arm to sample those colonies. Such appliances are used in both research laboratories and industrial surroundings, e.g. to analyze food or blood samples.
The image processing software sees the photo of the Petri dish in the format of a bitmap. This is nothing but a rectangle grid of which the boxes are called bits because they can only take two forms. Every bit of the grid is either empty (represented by a space) or covered by a colony (represented by a hash: #). As an example you can see a bitmap below on which a number of colonies can be seen. In a bitmap, a colony is formed by an area of neighbouring covered bits. Bits are here called neighbouring if they have a side in common. However, the software will never see an area of neighbouring covered bits as a colony, if it contains bits on the outer rim of the bitmap.
Define a class Petridish which can be used to analyze colonies on a Petri dish. the objects of this class must contain at least the following methods:
An initializing method __init__ to which the location of a text file must be given. This text file contains a bitmap of the Petri dish that needs to be analyzed.
A method __str__ that prints the string representation of the Petri dish. This string representation gives the rectangle gird as it is read in the bitmap file. Apart from empty bits (represented by spaces) and bits that are covered by colonies (represented by hashes: #), the bits can also be processed (represented by full stops). This processing happens by calling the method colonies (see below).
A method colony to which the row number and column number of a covered bit in the bitmap must be given. The rows of a bitmap are numbered from top to bottom, the columns from left to right, and the numbering always starts from zero. If the bit on the given position isn't covered, the method must raise an AssertionError with the text no colony found on position(row, column). Here, the fragments in italics must be filled out based on the arguments that are given to the method. Otherwise, all bits of the area of neighbouring covered bits to which the given bit belongs must be marked as processed (they are no longer seen as covered after the method was called), and the method must print the amount of bits this area consists of.
A method undo that marks all processed bits of a bitmap as covered again. Note that the processed bits in a bitmap could possibly belong to multiple colonies, for example if the method colony was called multiple times before the method undo is called.
Use the methods colony and undo to write a method count that prints the number of colonies that are not smaller than the given minimal size. The minimal size can be given to the optional parameter minimum (standard value: 1). The size of a colony is expressed as the number of bits in the corresponding area of neighbouring covered bits in the bitmap. The use of a minimal size is important for excluding smaller artifacts that can arise when converting of the photo of the Petri dish to the corresponding bitmap. We notice again that the areas of neighbouring covered bits that contain bits on the outer rim of the bitmap may not be see as colonies. These areas must be neglected when the method counts the colonies. Make sure that the bitmap does not have any processed bits after the method was called.
Use the methods colony and undo to write a method size that prints the size of all colonies that are not smaller than the given minimal size. The minimal size can be given to the optional parameter minimum (standard value: 1). As in the method above, this method may not see the areas of neighbouring covered bits that contain bits on the outer rim of the bitmap as colonies, and the bitmap may not show any processed bits after the method was called. The method must print the value None if there are no colonies that are larger than or equal to the given minimal size.
In the example session below we assume that the text file dish.txt12 is situated in the current directory. This file contains the bitmap of a Petri dish that was graphically shown above.
>>> dish = Petridish('dish.txt')
>>> print(dish)
<BLANKLINE>
###
#####
#######
#######
######
###### ##
#### #####
## ###### ####
# ###### ####
### ########## #####
####### #### ## ######
######### ## # #####
# #### ### ###
##### #### # ## ##
##### ###### #
###### ########
#### ########
#######
#######
>>> dish.colony(10, 35)
42
>>> print(dish)
<BLANKLINE>
###
#####
#######
#######
######
###### ..
#### .....
## ...... ####
# ...... ####
### .......... #####
####### .... .. ######
######### .. . #####
# #### ... ###
##### #### . ## ##
##### ###### #
###### ########
#### ########
#######
#######
>>> dish.colony(10, 40)
Traceback (most recent call last):
AssertionError: no colony was found on position (10, 40)
>>> dish.colony(10, 45)
30
>>> print(dish)
<BLANKLINE>
###
#####
#######
#######
######
###### ..
#### .....
## ...... ....
# ...... ....
### .......... .....
####### .... .. ......
######### .. . .....
# #### ... ...
##### #### . ## ..
##### ###### .
###### ########
#### ########
#######
#######
>>> dish.undo()
>>> print(dish)
<BLANKLINE>
###
#####
#######
#######
######
###### ##
#### #####
## ###### ####
# ###### ####
### ########## #####
####### #### ## ######
######### ## # #####
# #### ### ###
##### #### # ## ##
##### ###### #
###### ########
#### ########
#######
#######
>>> dish.colonies()
5
>>> dish.colonysize()
32.2