Module cppcpyutils.roi
Expand source code
import os
from plantcv import plantcv as pcv
import cppcpyutils as cppc
import re
import json
import numpy as np
def copy_metadata(args):
"""Copy metadata for each image so it can be used for each ROI
Parameters
----------
args : dict
commandline info from running the workflow
Returns
-------
updated args to include filename metadata : dict
"""
# The result file should exist if plantcv-workflow.py was run
if os.path.exists(args.result):
# Open the result file
results = open(args.result, "r")
# The result file would have image metadata in it from plantcv-workflow.py, read it into memory
args.metadata = json.load(results)
# Close the file
results.close()
# Delete the file, we will create new ones
os.remove(args.result)
args.plantbarcode = args.metadata['metadata']['plantbarcode']['value']
print('\n')
print(args.plantbarcode,
args.metadata['metadata']['timestamp']['value'],
sep=' - ')
else:
# If the file did not exist (for testing), initialize metadata as an empty string
args.metadata = {}
regpat = re.compile(args.regex)
args.plantbarcode = re.search(regpat, args.image).groups()[0]
return args
def write_output(args, i):
"""Write Outputs for a single ROI
Parameters
----------
args : dict
commandline arguments and image metadata
i : int
ROI number
Returns
-------
Notes
-----
Side effect of printing a json file to disk
"""
# Here I will name the results file with the ROI ID combined with the original result filename
basename, ext = os.path.splitext(args.result)
filename = basename + "-roi" + str(i) + ext
# Save the existing metadata to the new file
with open(filename, "w") as r:
json.dump(args.metadata, r)
pcv.print_results(filename=filename)
# The results are saved, now clear out the observations so the next loop adds new ones for the next plant
pcv.outputs.clear()
def iterate_rois(img, c, h, rc, rh, args, masked=True, gi=False, shape=False, hist=True, hue=False):
"""Analyze each ROI separately and store results
Parameters
----------
img : ndarray
rgb image
c : list
object contours
h : list
object countour hierarchy
rc : list
roi contours
rh : list
roi contour hierarchy
threshold_mask : ndarray
binary image (mask) from threshold steps
args : dict
commandline arguments and metadata from running the workflow
masked : boolean
whether to print masked rgb images for each roi
gi : boolean
whether to print greenness index false color
shape : boolean
whether to print object shapes on an image
hist : boolean
whether to print color histogram
hue : boolean
whether to save hsv color info and print the hue false color image
Returns
-------
binary image of plant mask that includes both threshold and roi filter steps : ndarray
"""
final_mask = np.zeros(shape=np.shape(img)[0:2], dtype='uint8')
# Compute greenness
if gi:
img_gi = cppc.compute.greenness_index(img=img, mask=final_mask+1)
if hue:
img_h = pcv.rgb2gray_hsv(img, 'h')
for i, rc_i in enumerate(rc):
rh_i = rh[i]
# Add ROI number to output. Before roi_objects so result has NA if no object.
pcv.outputs.add_observation(
sample='default',
variable='roi',
trait='roi',
method='roi',
scale='int',
datatype=int,
value=i,
label='#')
roi_obj, hierarchy_obj, submask, obj_area = pcv.roi_objects(
img, roi_contour=rc_i, roi_hierarchy=rh_i, object_contour=c, obj_hierarchy=h, roi_type='partial')
if obj_area == 0:
print('\t!!! No object found in ROI', str(i))
pcv.outputs.add_observation(
sample='default',
variable='plantarea',
trait='plant area in sq mm',
method='observations.area*pixelresolution^2',
scale=cppc.pixelresolution,
datatype="<class 'float'>",
value=0,
label='sq mm')
else:
# Combine multiple objects
# ple plant objects within an roi together
plant_object, plant_mask = pcv.object_composition(
img=img, contours=roi_obj, hierarchy=hierarchy_obj)
final_mask = pcv.image_add(final_mask, plant_mask)
if gi:
# Save greenness for individual ROI
grnindex = cppc.utils.mean(img_gi, plant_mask)
grnindexstd = cppc.utils.std(img_gi, plant_mask)
pcv.outputs.add_observation(
sample='default',
variable='greenness_index',
trait='mean normalized greenness index',
method='g/sum(b+g+r)',
scale='[0,1]',
datatype="<class 'float'>",
value=float(grnindex),
label='/1')
pcv.outputs.add_observation(
sample='default',
variable='greenness_index_std',
trait='std normalized greenness index',
method='g/sum(b+g+r)',
scale='[0,1]',
datatype="<class 'float'>",
value=float(grnindexstd),
label='/1')
# Analyze all colors
if hist:
colorhist = pcv.analyze_color(img, plant_mask, 'all')
elif hue:
_ = pcv.analyze_color(img, plant_mask, 'hsv')
# Analyze the shape of the current plant (always do this even if shape is False so you can get plant_area)
img_shape = pcv.analyze_object(img, plant_object, plant_mask)
plant_area = pcv.outputs.observations['default']['area']['value'] * cppc.pixelresolution**2
pcv.outputs.add_observation(
sample='default',
variable='plantarea',
trait='plant area in sq mm',
method='observations.area*pixelresolution^2',
scale=cppc.pixelresolution,
datatype="<class 'float'>",
value=plant_area,
label='sq mm')
# end if-else
# At this point we have observations for one plant
# We can write these out to a unique results file
write_output(args, i)
if args.writeimg and obj_area != 0:
if shape:
imgdir = os.path.join(args.outdir, 'shape_images', args.plantbarcode)
os.makedirs(imgdir, exist_ok=True)
pcv.print_image(img_shape, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-shape.png'))
if hist:
imgdir = os.path.join(args.outdir, 'colorhist_images', args.plantbarcode)
os.makedirs(imgdir, exist_ok=True)
pcv.print_image(colorhist, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-colorhist.png'))
if masked:
# save masked rgb image for entire tray but only 1 plant
imgdir = os.path.join(args.outdir, 'maskedrgb_images')
os.makedirs(imgdir, exist_ok=True)
img_masked = pcv.apply_mask(img, plant_mask, 'black')
pcv.print_image(
img_masked,
os.path.join(imgdir,
args.imagename + '-roi' + str(i) + '-masked.png'))
if hue:
# save hue false color image for entire tray but only 1 plant
imgdir = os.path.join(args.outdir, 'hue_images')
os.makedirs(imgdir, exist_ok=True)
fig_hue = pcv.visualize.pseudocolor(img_h*2, obj=None,
mask=plant_mask,
cmap=cppc.viz.get_cmap('hue'),
axes=False,
min_value=0, max_value=179,
background='black', obj_padding=0)
fig_hue = cppc.viz.add_scalebar(fig_hue,
pixelresolution=cppc.pixelresolution,
barwidth=10,
barlabel='1 cm',
barlocation='lower left')
fig_hue.set_size_inches(6, 6, forward=False)
fig_hue.savefig(os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-hue.png'),
bbox_inches='tight',
dpi=300)
fig_hue.clf()
if gi:
# save grnness image of entire tray but only 1 plant
imgdir = os.path.join(args.outdir, 'grnindex_images')
os.makedirs(imgdir, exist_ok=True)
fig_gi = pcv.visualize.pseudocolor(img_gi,
obj=None,
mask=plant_mask,
cmap='viridis',
axes=False,
min_value=0.3,
max_value=0.6,
background='black',
obj_padding=0)
fig_gi = cppc.viz.add_scalebar(
fig_gi,
pixelresolution=cppc.pixelresolution,
barwidth=10,
barlabel='1 cm',
barlocation='lower left')
fig_gi.set_size_inches(6, 6, forward=False)
fig_gi.savefig(os.path.join(
imgdir,
args.imagename + '-roi' + str(i) + '-greenness.png'),
bbox_inches='tight',
dpi=300)
fig_gi.clf()
# end roi loop
return final_mask
Functions
def copy_metadata(args)
-
Copy metadata for each image so it can be used for each ROI
Parameters
args : dict commandline info from running the workflow
Returns
updated args to include filename metadata : dict
Expand source code
def copy_metadata(args): """Copy metadata for each image so it can be used for each ROI Parameters ---------- args : dict commandline info from running the workflow Returns ------- updated args to include filename metadata : dict """ # The result file should exist if plantcv-workflow.py was run if os.path.exists(args.result): # Open the result file results = open(args.result, "r") # The result file would have image metadata in it from plantcv-workflow.py, read it into memory args.metadata = json.load(results) # Close the file results.close() # Delete the file, we will create new ones os.remove(args.result) args.plantbarcode = args.metadata['metadata']['plantbarcode']['value'] print('\n') print(args.plantbarcode, args.metadata['metadata']['timestamp']['value'], sep=' - ') else: # If the file did not exist (for testing), initialize metadata as an empty string args.metadata = {} regpat = re.compile(args.regex) args.plantbarcode = re.search(regpat, args.image).groups()[0] return args
def iterate_rois(img, c, h, rc, rh, args, masked=True, gi=False, shape=False, hist=True, hue=False)
-
Analyze each ROI separately and store results
Parameters
img
:ndarray
- rgb image
c
:list
- object contours
h
:list
- object countour hierarchy
rc
:list
- roi contours
rh
:list
- roi contour hierarchy
threshold_mask
:ndarray
- binary image (mask) from threshold steps
args
:dict
- commandline arguments and metadata from running the workflow
masked
:boolean
- whether to print masked rgb images for each roi
gi
:boolean
- whether to print greenness index false color
shape
:boolean
- whether to print object shapes on an image
hist
:boolean
- whether to print color histogram
hue
:boolean
- whether to save hsv color info and print the hue false color image
Returns
binary image of plant mask that includes both threshold and roi filter steps : ndarray
Expand source code
def iterate_rois(img, c, h, rc, rh, args, masked=True, gi=False, shape=False, hist=True, hue=False): """Analyze each ROI separately and store results Parameters ---------- img : ndarray rgb image c : list object contours h : list object countour hierarchy rc : list roi contours rh : list roi contour hierarchy threshold_mask : ndarray binary image (mask) from threshold steps args : dict commandline arguments and metadata from running the workflow masked : boolean whether to print masked rgb images for each roi gi : boolean whether to print greenness index false color shape : boolean whether to print object shapes on an image hist : boolean whether to print color histogram hue : boolean whether to save hsv color info and print the hue false color image Returns ------- binary image of plant mask that includes both threshold and roi filter steps : ndarray """ final_mask = np.zeros(shape=np.shape(img)[0:2], dtype='uint8') # Compute greenness if gi: img_gi = cppc.compute.greenness_index(img=img, mask=final_mask+1) if hue: img_h = pcv.rgb2gray_hsv(img, 'h') for i, rc_i in enumerate(rc): rh_i = rh[i] # Add ROI number to output. Before roi_objects so result has NA if no object. pcv.outputs.add_observation( sample='default', variable='roi', trait='roi', method='roi', scale='int', datatype=int, value=i, label='#') roi_obj, hierarchy_obj, submask, obj_area = pcv.roi_objects( img, roi_contour=rc_i, roi_hierarchy=rh_i, object_contour=c, obj_hierarchy=h, roi_type='partial') if obj_area == 0: print('\t!!! No object found in ROI', str(i)) pcv.outputs.add_observation( sample='default', variable='plantarea', trait='plant area in sq mm', method='observations.area*pixelresolution^2', scale=cppc.pixelresolution, datatype="<class 'float'>", value=0, label='sq mm') else: # Combine multiple objects # ple plant objects within an roi together plant_object, plant_mask = pcv.object_composition( img=img, contours=roi_obj, hierarchy=hierarchy_obj) final_mask = pcv.image_add(final_mask, plant_mask) if gi: # Save greenness for individual ROI grnindex = cppc.utils.mean(img_gi, plant_mask) grnindexstd = cppc.utils.std(img_gi, plant_mask) pcv.outputs.add_observation( sample='default', variable='greenness_index', trait='mean normalized greenness index', method='g/sum(b+g+r)', scale='[0,1]', datatype="<class 'float'>", value=float(grnindex), label='/1') pcv.outputs.add_observation( sample='default', variable='greenness_index_std', trait='std normalized greenness index', method='g/sum(b+g+r)', scale='[0,1]', datatype="<class 'float'>", value=float(grnindexstd), label='/1') # Analyze all colors if hist: colorhist = pcv.analyze_color(img, plant_mask, 'all') elif hue: _ = pcv.analyze_color(img, plant_mask, 'hsv') # Analyze the shape of the current plant (always do this even if shape is False so you can get plant_area) img_shape = pcv.analyze_object(img, plant_object, plant_mask) plant_area = pcv.outputs.observations['default']['area']['value'] * cppc.pixelresolution**2 pcv.outputs.add_observation( sample='default', variable='plantarea', trait='plant area in sq mm', method='observations.area*pixelresolution^2', scale=cppc.pixelresolution, datatype="<class 'float'>", value=plant_area, label='sq mm') # end if-else # At this point we have observations for one plant # We can write these out to a unique results file write_output(args, i) if args.writeimg and obj_area != 0: if shape: imgdir = os.path.join(args.outdir, 'shape_images', args.plantbarcode) os.makedirs(imgdir, exist_ok=True) pcv.print_image(img_shape, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-shape.png')) if hist: imgdir = os.path.join(args.outdir, 'colorhist_images', args.plantbarcode) os.makedirs(imgdir, exist_ok=True) pcv.print_image(colorhist, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-colorhist.png')) if masked: # save masked rgb image for entire tray but only 1 plant imgdir = os.path.join(args.outdir, 'maskedrgb_images') os.makedirs(imgdir, exist_ok=True) img_masked = pcv.apply_mask(img, plant_mask, 'black') pcv.print_image( img_masked, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-masked.png')) if hue: # save hue false color image for entire tray but only 1 plant imgdir = os.path.join(args.outdir, 'hue_images') os.makedirs(imgdir, exist_ok=True) fig_hue = pcv.visualize.pseudocolor(img_h*2, obj=None, mask=plant_mask, cmap=cppc.viz.get_cmap('hue'), axes=False, min_value=0, max_value=179, background='black', obj_padding=0) fig_hue = cppc.viz.add_scalebar(fig_hue, pixelresolution=cppc.pixelresolution, barwidth=10, barlabel='1 cm', barlocation='lower left') fig_hue.set_size_inches(6, 6, forward=False) fig_hue.savefig(os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-hue.png'), bbox_inches='tight', dpi=300) fig_hue.clf() if gi: # save grnness image of entire tray but only 1 plant imgdir = os.path.join(args.outdir, 'grnindex_images') os.makedirs(imgdir, exist_ok=True) fig_gi = pcv.visualize.pseudocolor(img_gi, obj=None, mask=plant_mask, cmap='viridis', axes=False, min_value=0.3, max_value=0.6, background='black', obj_padding=0) fig_gi = cppc.viz.add_scalebar( fig_gi, pixelresolution=cppc.pixelresolution, barwidth=10, barlabel='1 cm', barlocation='lower left') fig_gi.set_size_inches(6, 6, forward=False) fig_gi.savefig(os.path.join( imgdir, args.imagename + '-roi' + str(i) + '-greenness.png'), bbox_inches='tight', dpi=300) fig_gi.clf() # end roi loop return final_mask
def write_output(args, i)
-
Write Outputs for a single ROI
Parameters
args
:dict
- commandline arguments and image metadata
i
:int
- ROI number
Returns
Notes
Side effect of printing a json file to disk
Expand source code
def write_output(args, i): """Write Outputs for a single ROI Parameters ---------- args : dict commandline arguments and image metadata i : int ROI number Returns ------- Notes ----- Side effect of printing a json file to disk """ # Here I will name the results file with the ROI ID combined with the original result filename basename, ext = os.path.splitext(args.result) filename = basename + "-roi" + str(i) + ext # Save the existing metadata to the new file with open(filename, "w") as r: json.dump(args.metadata, r) pcv.print_results(filename=filename) # The results are saved, now clear out the observations so the next loop adds new ones for the next plant pcv.outputs.clear()