This tutorial was generated from an Jupyter notebook. You can download the notebook here.
import glob import numpy as np import pandas as pd import skimage.io import bi1x import bokeh.io import bokeh.plotting bokeh.io.output_notebook() notebook_url = 'localhost:8888'
In this tutorial, we will will work out an image processing pipeline for processing the time lapse movies of the delay oscillator. It is important that we all follow this pipeline exactly, because we will all share data.
Movies of bacterial colonies growing and divided are typically segmented using more sophisticated techniques that what we used for the E. coli growth module. We do not have time to discuss these techniques, but will instead do segmentation by hand. To do this, you can use some Bokeh-based tools in the
bi1x package. First, we can load in an image.
# Load in image im = skimage.io.imread('test_oscillator_images/img_000000011_Brightfield_000.tif')
Next, we can record clicks to make an ROI, or region of interest. To do so, we use the
bi1x.viz.draw_rois() function. Note the two keyword arguments in its function call below. The
flip=False keyword argument keeps the coordinates of the image matrix-like, which is what we expect further down our image processing pipeline. Next, the
notebook_url keyword argument specifies the URL of the notebook you are using. This is important to know so that the clicks are appropriately recorded. Once the function is called, a Bokeh plot appears with extra tools. To find out the URL, look in your browser. You will enter something like
'localhost:8888'. For convenience, I set a variable with the notebook URL in the import commands at the beginning of the notebook.
To select an ROI, select the polygon tool. To begin drawing an ROI, double-click the first part of the image. Each subsequent click designates a vertex of a polygon. To finish the polygon, double-click again. (In the HTML version of this notebook, the interactive image will not appear below.)
rois = bi1x.viz.draw_rois(im, flip=False, notebook_url=notebook_url)
The polygon is stored in a Bokeh object that we should convert to a Pandas DataFrame. To do so, use the
df_roi = bi1x.viz.roicds_to_df(rois) df_roi
Finally, you should store the file in a text file with a name like
img_000000000_Brightfield_000_cell_0.csv (see the naming convention below). For ease of loading in the ROI vertices in the next step, be sure to only write out columns
'y' without a header of an index.
df_roi[["x", "y"]].to_csv( "img_000000011_Brightfield_000_cell_0.csv", header=None, index=False )
Note that we will be doing our segmentation using the brightfield channel. It is generally a good idea to use a channel different from the one you are quantifying for segmentation. Then, you are not relying on a possible weak signal to locate the cells. Nonetheless, for the delay oscillator experiment, the fluorescence is almost always high enough to segment in the fluorescent channel.
You will segment cells from a single growing colony. It is important to keep track of the lineage of cells. To this end, we will use a unique binary identifier code for each cell in a time lapse movie. The binary code is constructed as follows.
0, and right-or-bottom-most is
As an example, a third-generation cell that is the left-most daughter of a division that came from the right-most daughter of the original mother is
You would store the coordinates, for example for frame number 15, as
img_000000015_Brightfield_000_cell_010.csv. If you want to use the function that follows, the cell binary code must be between the last underscore and dot in the file name.
For each cell we segment, we want to compute the median fluorescence intensity. We want to compute the median, as opposed to total, intensity because as the cells grow, the plasmids multiply, so we are interested in an approximate per-plasmid fluorescence. We choose the median instead of the mean because the median is less sensitive to noise and to errors in segmentation (for example, making the boundary around a cell too large or too small.
Given the stored coordinates, we need to determine what pixels belong to a given cell. The
bi1x.image.verts_to_roi() function does this. For example, let's consider the image
img_000000011_Brightfield_000.csv with the set of vertices of a polygon defining an ROI stored in
img_000000011_Brightfield_000_cell_01.csv. First, let's plot the image with the stored vertices. I will use the
flip=False kwarg when I show the image using
bi1x.viz.imshow() because we stored the ROIs with the origin at the top left of the image. We also keep the units in pixels because this is the units that the coordinates of the segmentation are stored in.
# Load in image im = skimage.io.imread( "test_oscillator_images/img_000000011_Brightfield_000.tif" ) # Load in the vertices defining the ROI (note comma delimiter) verts = np.loadtxt( "test_oscillator_images/img_000000011_Brightfield_000_cell_01.csv", delimiter=",", ) # Display the image p = bi1x.viz.imshow(im, flip=False) p.line(*verts.T, color="orange") bokeh.io.show(p)