Example 7: Measuring worm length

Body length is a commonly measured trait. However, live animals are typically not well behaved in front of camera: typically the body is not aligned straight, which makes length measure difficult and sometimes time consuming. Some computer vision can help here, such as skeletonization (https://en.wikipedia.org/wiki/Topological_skeleton) for an acurate semi automatic approach, and polyline drawing for a less acurate, manual aproach.

Here I demonstrate how to automatically measure the length of california blackworms (Lumbriculus variegatus) in 6 well plates by dragging a mask around them. A future implementation will like include a variant of Hough transformation to automatically detect the circles of the wellplates for a full automatic workflow.

Note: For details on the differen workflows (prototyping-, low throughput- and high throughout) see Tutorial 2. Not all examples include the prototyping workflow.

Before

Input - Worms placed in 6 well plates

After

Results - extracted body contour (green area) and body length (red line) as a topological skeleton

Low throughput

First we load the image as a container. If you have saved data before with a specific save_suffix (recommended for reproducibility and to not overwrite files) you can reload it if you have provided it at the point of container creation in load_image:

[1]:
import phenopype as pp

image_path = r"./images/worms.jpg"
custom_save_path = r"../_temp/output/ex7"
ct = pp.load_image(image_path, # path to image
                   cont=True,  # create a phenopype container
                   save_suffix="worms1",      # indicate a suffix for save- and loading operations
                   dirpath=custom_save_path)  # supply an (existing) directory where files get stored and loaded from
Directory to save files set at - E:\git_repos\phenopype\_temp\output
[2]:
ct.load() # to reload any saved masks or polylines
Nothing loaded.
[4]:
ct.reset() # reset canvas and images, if you run repeatedly

First we drag a rectangle around each worm in each well. Future releases will include “circle”-shaped mask tools, and the opportunity to automatically find circles using hough transform - OpenCV is already set up to do so: https://docs.opencv.org/3.4/d4/d70/tutorial_hough_circle.html

[5]:
pp.preprocessing.create_mask(ct)
- create mask

Next step is to perform the segmentation. To adjust vor variable brightness inside the wells we use the "adaptive" threshold method, and afterwards, find_contours. If the worms touch the border of the well, or their own body, you can directly manipulate the binary image using the draw function (two clicks draw a line, or use the argument tool="rectangle". Sometimes a worm may overlap with itself - in that case, use polylines to measure it manually.

Be aware that low- and high-throughput workflows are designed for consecutive application of functions, meaning that repeated execution of thresholding will be on an already thresholded image. To restart that process, reset the container with ct.reset()(see above)

[6]:
pp.segmentation.threshold(ct, method="adaptive", blocksize=49, constant=5)
## pp.segmentation(draw) # uncomment this if you need to manually separate worms from the well-border
pp.segmentation.find_contours(ct, min_diameter=50)
## pp.visualization.polylines(ct) # uncomment this if you need to manually measure a worm
pp.measurement.skeletonize(ct) # this will measure the longest distance in a curvy object
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels

Next we draw the output, and save the results (including a visualization).

[7]:
pp.visualization.select_canvas(ct, canvas="raw") # first select a background (try "binary")
pp.visualization.draw_contours(ct) # this draws both the contour (green) and the skeleton
pp.visualization.draw_masks(ct) # will be skipped if loaded before
- raw image
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
[8]:
pp.export.save_masks(ct, dirpath=custom_save_path) #
pp.export.save_contours(ct, dirpath=custom_save_path)
pp.export.save_canvas(ct, dirpath=custom_save_path, resize=1) # resize=1 to have the original sized picture
- masks saved under ../_temp/output\masks_worms1.csv.
- contours saved under ../_temp/output\contours_worms1.csv.
- canvas saved under ../_temp/output\canvas_worms1.jpg.
[9]:
pp.show_image(ct.canvas)

Should a worm superimpose itself it is not possible for the skeletonization algorithm to follow the shape through from beginning to end (e.g. in worm 3 in this example). In that case one has to manually draw a line along the worm using the polylines tool.

Canvas masking

[11]:
pp.measurement.polylines(ct)
pp.visualization.draw_polylines(ct)
pp.export.save_polylines(ct)
- polylines already drawn (overwrite=False)
- polylines saved under ../_temp/output\polylines_worms1.csv (overwritten).
[12]:
pp.show_image(ct.canvas)

Alternatively to the polylines tool, one can also use the drawing tool if you just need to separte an endpoint or if the worm touches the border. This should be done on the binary image, before the contour are detected.

High throughput

As for the other examples I have created a preset (ex7) with appropriate settings for the example. The template can be passed to the pype using config_preset="ex7" - see below.

[13]:
import phenopype as pp

image_path = r"./images/worms.jpg"

print(pp.presets.ex7)

preprocessing:
- create_mask
segmentation:
- threshold:
    method: adaptive
    blocksize: 49
    constant: 5
# - draw
- find_contours:
    retrieval: ccomp
    min_diameter: 50
    min_area: 0
measurement:
- skeletonize
# - polylines
visualization:
- select_canvas:
    canvas: image
- draw_contours:
    line_width: 2
    label_width: 1
    label_size: 1
    fill: 0.3
- draw_masks
# - draw_polylines
export:
- save_contours:
    overwrite: true

[14]:
pp.pype(image_path, name="worms2", dirpath=r"../_temp/output/ex7", config_preset="ex7")
../_temp/output/ex7\pype_config_worms2.yaml


------------+++ new pype iteration 2020:04:28 18:36:50 +++--------------


AUTOLOAD
- masks_worms2.csv
PREPROCESSING
create_mask
- mask with label mask1 already created (overwrite=False)
SEGMENTATION
threshold
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
- include mask "mask1" pixels
find_contours
MEASUREMENT
skeletonize
VISUALIZATION
NoneType
select_canvas
- invalid selection - defaulting to raw image
ndarray
draw_contours
ndarray
draw_masks
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
 - show mask: mask1.
EXPORT
save_contours
- contours saved under ../_temp/output/ex7\contours_worms2.csv (overwritten).
AUTOSAVE
save_canvas
- canvas saved under ../_temp/output/ex7\canvas_worms2.jpg (overwritten).
save_masks
- masks not saved - file already exists (overwrite=False).


TERMINATE
[14]:
<phenopype.main.pype at 0x281ac3af0c8>
[ ]: