Morphology sub-package functions can be used with a clean mask of a plant (see VIS tutorial for examples of masking background. This tutorial will start with a binary mask (after object segmentation has been completed) but in a complete workflow users will need to use other functions to achieve plant isolation. Skeletonizing is very sensitive to any pepper noise remaining within a binary mask. Morphology functions are intended to be one type of object analysis. These functions can potentially return information about leaf length, leaf angle, and leaf curvature.
# Import libraries
from plantcv import plantcv as pcv
class options:
def __init__(self):
self.image = "./img/mask.png"
self.debug = "plot"
self.writeimg= False
self.result = "morphology_tutorial_results.txt"
self.outdir = "."
# Get options
args = options()
# Set debug to the global parameter
pcv.params.debug = args.debug
# Read image (sometimes you need to run this line twice to see the image)
# Inputs:
# filename - Image file to be read in
# mode - How to read in the image; either 'native' (default), 'rgb', 'gray', or 'csv'
img, path, filename = pcv.readimage(filename=args.image)
# Crop the mask
cropped_mask = img[1150:1750, 900:1550]
# Skeletonize the mask
#%matplotlib notebook
# To enable the zoom feature to better see fine lines, uncomment the line above ^^
# Inputs:
# mask = Binary image data
skeleton = pcv.morphology.skeletonize(mask=cropped_mask)
# Prune the skeleton
# Generally, skeletonized images will have barbs (this image is particularly ideal,
# that's why it's the example image in the tutorial!),
# representing the width, that need to get pruned off.
# Inputs:
# skel_img = Skeletonized image
# size = Size to get pruned off each branch
# mask = (Optional) binary mask for debugging. If provided, debug image will be overlaid on the mask.
img1, seg_img, edge_objects = pcv.morphology.prune(skel_img=skeleton, size=10, mask=cropped_mask)
# Fill in segments (also stores out area data)
# Inputs:
# mask = Binary image, single channel, object = 1 and background = 0
# objects = List of contours
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects, label="default")
# Identify branch points
# Inputs:
# skel_img = Skeletonized image
# mask = (Optional) binary mask for debugging. If provided, debug image will be overlaid on the mask.
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
branch_pts_mask = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=cropped_mask, label="default")
# Identify tip points
# Inputs:
# skel_img = Skeletonized image
# mask = (Optional) binary mask for debugging. If provided, debug
# image will be overlaid on the mask
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
tip_pts_mask = pcv.morphology.find_tips(skel_img=skeleton, mask=None, label="default")
# Adjust line thickness with the global line thickness parameter (default = 5),
# and provide binary mask of the plant for debugging. NOTE: the objects and
# hierarchies returned will be exactly the same but the debugging image (segmented_img)
# will look different.
pcv.params.line_thickness = 3
# Sort segments into primary (stem) objects and secondary (leaf) objects.
# Downstream steps can be performed on just one class of objects at a time,
# or all objects (output from segment_skeleton)
# Inputs:
# skel_img = Skeletonized image
# objects = List of contours
# mask = (Optional) binary mask for debugging. If provided, debug image will be overlaid on the mask.
leaf_obj, stem_obj = pcv.morphology.segment_sort(skel_img=skeleton,
objects=edge_objects,
mask=cropped_mask)
# Identify segments
# Inputs:
# skel_img = Skeletonized image
# objects = List of contours
# mask = (Optional) binary mask for debugging. If provided,
# debug image will be overlaid on the mask.
segmented_img, labeled_img = pcv.morphology.segment_id(skel_img=skeleton,
objects=leaf_obj,
mask=cropped_mask)
# Similar to line thickness, there are optional text size and text thickness parameters
# that can be adjusted to better suit images or varying sizes.
pcv.params.text_size=.8 # (default text_size=.55)
pcv.params.text_thickness=3 # (defaul text_thickness=2)
segmented_img, labeled_img = pcv.morphology.segment_id(skel_img=skeleton,
objects=leaf_obj,
mask=cropped_mask)
# Measure path lengths of segments
# Inputs:
# segmented_img = Segmented image to plot lengths on
# objects = List of contours
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
labeled_img = pcv.morphology.segment_path_length(segmented_img=segmented_img,
objects=leaf_obj, label="default")
# Measure euclidean distance of segments
# Inputs:
# segmented_img = Segmented image to plot lengths on
# objects = List of contours
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
labeled_img = pcv.morphology.segment_euclidean_length(segmented_img=segmented_img,
objects=leaf_obj, label="default")
# Measure curvature of segments
# Inputs:
# segmented_img = Segmented image to plot curvature on
# objects = List of contours
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
labeled_img = pcv.morphology.segment_curvature(segmented_img=segmented_img,
objects=leaf_obj, label="default")
# Measure the angle of segments
# Inputs:
# segmented_img = Segmented image to plot angles on
# objects = List of contours
# label = Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`)filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects)
labeled_img = pcv.morphology.segment_angle(segmented_img=segmented_img,
objects=leaf_obj, label="default")