diff --git a/docs/acute_vertex.md b/docs/acute_vertex.md index 8787ff509..d41b75088 100644 --- a/docs/acute_vertex.md +++ b/docs/acute_vertex.md @@ -3,7 +3,7 @@ Perform a heuristic search for sharp angles given an object contour and user specified parameters. The acute (sharp) angles are often associated with object tip points. Outputs a python list of points that meet criteria specified in parameters. -**plantcv.acute_vertex**(*img, obj, window, thresh, sep*) +**plantcv.acute_vertex**(*img, obj, window, thresh, sep, label="default"*) **returns** list of points that meet specified criteria, image with points selected @@ -13,6 +13,7 @@ angles are often associated with object tip points. Outputs a python list of poi - window - The pre and post point distances on which to calculate angle of focal point (a value of 30 worked well for a sample image) on which to calculate the angle - thresh - Threshold to set for acuteness; keep points with an angle more acute than the threshold (a value of 15 worked well for sample image) - sep - The number of contour points to search within for the most acute value + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to identify tip points based upon the angle between focal pixel and reference points on contour. - **Output data stored:** Data ('tip_coordinates') automatically gets stored to the [`Outputs` class](outputs.md) when this function is ran. @@ -32,11 +33,11 @@ pcv.params.debug = "print" # Identify acute vertices (tip points) of an object # Results in set of point values that may indicate tip points -list_of_acute_points, points_img = pcv.acute_vertex(img, obj, 30, 15, 100) -` +list_of_acute_points, points_img = pcv.acute_vertex(img=img, obj=obj, window=30, thresh=15, sep=100, label="default") + # Access data stored out from acute_vertex -vertices = pcv.outputs.observations['tip_coordinates']['value'] -` +vertices = pcv.outputs.observations['default']['tip_coordinates']['value'] + ``` **Image of points selected** diff --git a/docs/analyze_NIR_intensity.md b/docs/analyze_NIR_intensity.md index ae1f55f04..49cc86bd4 100644 --- a/docs/analyze_NIR_intensity.md +++ b/docs/analyze_NIR_intensity.md @@ -3,7 +3,7 @@ This function calculates the intensity of each pixel associated with the plant and writes the values out to the [Outputs class](outputs.md). Can also return/plot/print out a histogram plot of pixel intensity. -**plantcv.analyze_nir_intensity**(*gray_img, mask, bins=256, histplot=False*) +**plantcv.analyze_nir_intensity**(*gray_img, mask, bins=256, histplot=False, label="default"*) **returns** Histogram image (when histplot is `True`, otherwise returns `None` object) @@ -12,6 +12,7 @@ the values out to the [Outputs class](outputs.md). Can also return/plot/print ou - mask - Binary mask made from selected contours - bins - Number of NIR intensity value groups (default bins = 256) - histplot - If True plots histogram of intensity values (default histplot = False) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Near Infrared pixel frequencies within a masked area of an image. - **Example use:** @@ -33,10 +34,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Caclulates the proportion of pixels that fall into a signal bin and writes the values to a file. Also provides a histogram of this data -analysis_image = pcv.analyze_nir_intensity(gray_img, mask, 256, histplot=True) +analysis_image = pcv.analyze_nir_intensity(gray_img, mask, 256, histplot=True, label="default") # Access data stored out from analyze_nir_intensity -nir_frequencies = pcv.outputs.observations['nir_frequencies']['value'] +nir_frequencies = pcv.outputs.observations['default']['nir_frequencies']['value'] ``` diff --git a/docs/analyze_bound_horizontal.md b/docs/analyze_bound_horizontal.md index 9d3ba8f96..a8f670403 100644 --- a/docs/analyze_bound_horizontal.md +++ b/docs/analyze_bound_horizontal.md @@ -4,7 +4,7 @@ Set boundary line with boundary tool, this allows the user to find the extent-y above and below as well as the area above and below the boundary line. This tool functions best if the pot size/position of the plant remains relatively constant. -**plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position*) +**plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position, label="default"*) **returns** image with boundary data @@ -13,6 +13,7 @@ best if the pot size/position of the plant remains relatively constant. - obj - single or grouped contour object - mask - binary mask of selected contours - line_position - position of boundary line (a value of 0 would draw the line through the top of the image) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to define a boundary line for the image, to find the height above and below as well as area above and below a boundary line. - **Example use:** @@ -30,10 +31,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Set Boundary Line -boundary_image = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=bin_mask, line_position=300) +boundary_image = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=bin_mask, line_position=300, label="default") # Access data stored out from analyze_bound_horizontal -percent_area_below_reference = pcv.outputs.observations['percent_area_below_reference']['value'] +percent_area_below_reference = pcv.outputs.observations['default']['percent_area_below_reference']['value'] ``` diff --git a/docs/analyze_bound_vertical.md b/docs/analyze_bound_vertical.md index 65db1569b..ffbdc35ec 100644 --- a/docs/analyze_bound_vertical.md +++ b/docs/analyze_bound_vertical.md @@ -4,7 +4,7 @@ Set boundary line with boundary tool, this allows the user to find the extent-x to the right and to the left as well as the area to the right and to the left of the set boundary line. This tool functions best if the pot size/position of the plant remains relatively constant. -**plantcv.analyze_bound_vertical**(*img, obj, mask, line_position*) +**plantcv.analyze_bound_vertical**(*img, obj, mask, line_position, label="default"*) **returns** image with boundary data @@ -13,6 +13,7 @@ best if the pot size/position of the plant remains relatively constant. - obj - single or grouped contour object - mask - binary mask of selected contours - line_position - position of boundary line (a value of 0 would draw the line through the left of the image) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to define a boundary line for the image, to find the width to the right and to the left as well as area to the right and to the left of a boundary line. - **Example use:** @@ -30,10 +31,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Set Boundary Line -boundary_image = pcv.analyze_bound_vertical(img=img, obj=obj, mask=bin_mask, line_position=1000) +boundary_image = pcv.analyze_bound_vertical(img=img, obj=obj, mask=bin_mask, line_position=1000, label="default") # Access data stored out from analyze_bound_vertical -area_right_reference = pcv.outputs.observations['area_right_reference']['value'] +area_right_reference = pcv.outputs.observations['default']['area_right_reference']['value'] ``` diff --git a/docs/analyze_color.md b/docs/analyze_color.md index bdbb30761..8bba10043 100644 --- a/docs/analyze_color.md +++ b/docs/analyze_color.md @@ -2,7 +2,7 @@ Extract color data of objects and produce pseudocolored images, can extract data for RGB (Red, Green, Blue), HSV (Hue, Saturation, Value) and LAB (Lightness, Green-Magenta, Blue Yellow) channels. -**plantcv.analyze_color**(*rgb_img, mask, hist_plot_type=None*) +**plantcv.analyze_color**(*rgb_img, mask, hist_plot_type=None, label="default"*) **returns** Histogram image (if hist_plot_type is not `None`, otherwise returns `None` object) @@ -10,6 +10,7 @@ Extract color data of objects and produce pseudocolored images, can extract data - rgb_img - RGB image data - mask - binary mask of selected contours - hist_plot_type - None (default), 'all', 'rgb', 'lab', or 'hsv'. This can limit the data saved out. Hue data is still saved out when set to None. + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to extract color data from RGB, LAB, and HSV color channels. - Generates histogram of color channel data. @@ -35,10 +36,10 @@ pcv.params.debug = "print" # Analyze Color -analysis_image = pcv.analyze_color(rgb_img=rgb_img, mask=mask, hist_plot_type='all') +analysis_image = pcv.analyze_color(rgb_img=rgb_img, mask=mask, hist_plot_type='all', label="default") # Access data stored out from analyze_color -hue_circular_mean = pcv.outputs.observations['hue_circular_mean']['value'] +hue_circular_mean = pcv.outputs.observations['default']['hue_circular_mean']['value'] ``` diff --git a/docs/analyze_index.md b/docs/analyze_index.md index 9312712b2..e3bda3283 100644 --- a/docs/analyze_index.md +++ b/docs/analyze_index.md @@ -3,7 +3,7 @@ This function calculates the spectral index statistics and writes the values as observations out to the [Outputs class](outputs.md). -**plantcv.hyperspectral.analyze_index**(*index_array, mask, histplot=False, bins=100, min_bin=0, max_bin=1*) +**plantcv.hyperspectral.analyze_index**(*index_array, mask, histplot=False, bins=100, min_bin=0, max_bin=1, label="default"*) **returns** None @@ -14,7 +14,8 @@ This function calculates the spectral index statistics and writes the values as - bins - Optional, number of classes to divide spectrum into (default bins=100) - min_bin - Optional, minimum bin label. Default of 0 will be used for the smallest bin label while calculating pixel frequency data unless otherwise defined. `min_bin="auto"` will set minimum bin to the smallest observed pixel value within the masked index provided. - - max_bin - Optional, maximum bin label. Default of 1 will be used for the maximum bin label unless otherwise defined. `max_bin="auto"` will set maximum bin to the largest observed pixel value within the masked index provided. + - max_bin - Optional, maximum bin label. Default of 1 will be used for the maximum bin label unless otherwise defined. `max_bin="auto"` will set maximum bin to the largest observed pixel value within the masked index provided. + - label - Optional label parameter, modifies the variable name of observations recorded - **Context:** - Calculates data about mean, median, and standard deviation of an input index within a masked region. @@ -30,7 +31,7 @@ This function calculates the spectral index statistics and writes the values as from plantcv import plantcv as pcv -pcv.hyperspectral.analyze_index(index_array=ndvi_index, mask=leaf_mask, histplot=True, bins=100, min_bin=0, max_bin="auto") +pcv.hyperspectral.analyze_index(index_array=ndvi_index, mask=leaf_mask, histplot=True, bins=100, min_bin=0, max_bin="auto", label="default") ``` diff --git a/docs/analyze_shape.md b/docs/analyze_shape.md index 5d3110f3f..19c6a0daa 100644 --- a/docs/analyze_shape.md +++ b/docs/analyze_shape.md @@ -2,7 +2,7 @@ Shape analysis outputs numeric properties for an input object (contour or grouped contours), works best on grouped contours. -**plantcv.analyze_object**(*img, obj, mask*) +**plantcv.analyze_object**(*img, obj, mask, label="default"*) **returns** analysis_image @@ -10,6 +10,7 @@ Shape analysis outputs numeric properties for an input object (contour or groupe - img - RGB or grayscale image data for plotting. - obj - Single or grouped contour object. - mask - Binary image to use as mask for moments analysis. + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to output shape characteristics of an image, including height, object area, convex hull, convex hull area, perimeter, extent x, extent y, longest axis, centroid x coordinate, centroid y coordinate, in bounds QC (if object @@ -17,7 +18,7 @@ Shape analysis outputs numeric properties for an input object (contour or groupe - **Example use:** - [Use In VIS Tutorial](vis_tutorial.md) - [Use In NIR Tutorial](nir_tutorial.md) - - [Use In PSII Tutorial](psII_tutorial.md)
 + - [Use In PSII Tutorial](psII_tutorial.md) - **Output data stored:** Data ('area', 'convex_hull_area', 'solidity', 'perimeter', 'width', 'height', 'longest_path', 'center_of_mass, 'convex_hull_vertices', 'object_in_frame', 'ellipse_center', 'ellipse_major_axis', 'ellipse_minor_axis', 'ellipse_angle', 'ellipse_eccentricity') automatically gets stored to the [`Outputs` class](outputs.md) when this function is ran. @@ -38,13 +39,13 @@ pcv.params.debug = "print" # Characterize object shapes -shape_image = pcv.analyze_object(img, objects, mask) +shape_image = pcv.analyze_object(img=img, obj=objects, mask=mask, label="default") # Save returned images with more specific naming pcv.print_image(shape_image, '/home/malia/setaria_shape_img.png') # Access data stored out from analyze_object -plant_solidity = pcv.outputs.observations['solidity']['value'] +plant_solidity = pcv.outputs.observations['default']['solidity']['value'] ``` diff --git a/docs/analyze_spectral.md b/docs/analyze_spectral.md index 93bddbf90..8fec460b0 100644 --- a/docs/analyze_spectral.md +++ b/docs/analyze_spectral.md @@ -3,7 +3,7 @@ This function calculates the reflectance frequencies associated with a hyperspectral datacube and writes the values out as observations to get saved out. Can also print out a histogram of average reflectance intensity. -**plantcv.hyperspectral.analyze_spectral**(*array, mask, histplot=False*) +**plantcv.hyperspectral.analyze_spectral**(*array, mask, histplot=False, label="default"*) **returns** reflectance histogram (if `histplot=True`, otherwise returns None object) @@ -11,6 +11,7 @@ the values out as observations to get saved out. Can also print out a histogram - array - A hyperspectral datacube object, an instance of the `Spectral_data` class (read in with [pcv.readimage](read_image.md) with `mode='envi'`) - mask - Binary mask made from selected contours - histplot - If True plots histogram of reflectance intensity values (default histplot = False) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Example use:** - Below - **Output data stored:** Data ('max_reflectance', 'min_reflectance', 'median_reflectance', 'spectral_std', 'spectral_frequencies', 'global_mean_reflectance', 'global_median_reflectance', 'global_spectral_std') automatically gets stored to the @@ -27,10 +28,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Calculates reflectance frequencies and writes the values as observations. Also provides a histogram of this data -spectral_hist = pcv.hyperspectral.analyze_spectral(array=spectral_data, mask=mask, histplot=True) +spectral_hist = pcv.hyperspectral.analyze_spectral(array=spectral_data, mask=mask, histplot=True, label="default") # Access data stored -reflectance_range = max(pcv.outputs.observations['max_reflectance']['value']) - min(pcv.outputs.observations['min_reflectance']['value']) +reflectance_range = max(pcv.outputs.observations['default']['max_reflectance']['value']) - min(pcv.outputs.observations['default']['min_reflectance']['value']) ``` diff --git a/docs/analyze_stem.md b/docs/analyze_stem.md index 052f7ad45..574c7e34a 100644 --- a/docs/analyze_stem.md +++ b/docs/analyze_stem.md @@ -3,13 +3,14 @@ Primary, or stem, objects identified during workflows that examine the [morphology](morphology_tutorial.md) of plants or plant organs can have specific characteristics measured about the stem segments of a skeleton. -**plantcv.morphology.analyze_stem**(*rgb_img, stem_objects*) +**plantcv.morphology.analyze_stem**(*rgb_img, stem_objects, label="default"*) **returns** labeled_img - **Parameters:** - - rgb_img - RGB image data for plotting. + - rgb_img - RGB image data for plotting. - stem_objects - List of stem segments (output from [segment_sort](segment_sort.md) function) + - label - Optional label parameter, modifies the variable name of observations recorded - **Context:** - Used to output stem morphological characteristics, including height, angle, and length. - **Example use:** @@ -31,11 +32,12 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" -stem_debug_img1 = pcv.morphology.analyze_stem(rgb_img=img1, stem_objects=stem_objects1) -stem_debug_img2 = pcv.morphology.analyze_stem(rgb_img=img2, stem_objects=stem_objects2) - +stem_debug_img1 = pcv.morphology.analyze_stem(rgb_img=img1, stem_objects=stem_objects1, label="default") # Access data stored out from analyze_object -stem_angle = pcv.outputs.observations['stem_angle']['value'] +stem_angle = pcv.outputs.observations['default']['stem_angle']['value'] + +stem_debug_img2 = pcv.morphology.analyze_stem(rgb_img=img2, stem_objects=stem_objects2, label="rep1") +stem_angle = pcv.outputs.observations['rep1']['stem_angle']['value'] ``` diff --git a/docs/analyze_thermal_values.md b/docs/analyze_thermal_values.md index ed58a5b04..9569c887e 100644 --- a/docs/analyze_thermal_values.md +++ b/docs/analyze_thermal_values.md @@ -3,7 +3,7 @@ This function calculates the intensity of each pixel associated with the temperature and writes the values out to a file. Can optionally create a histogram of pixel intensity. -**plantcv.analyze_thermal_values**(*thermal_array, mask, histplot=False*) +**plantcv.analyze_thermal_values**(*thermal_array, mask, histplot=False, label="default""default"*) **returns** thermal histogram (if `histplot=True`, otherwise returns None object) @@ -11,6 +11,7 @@ the values out to a file. Can optionally create a histogram of pixel intensity. - thermal_array - Numpy array of thermal image data (most likely read in with [pcv.readimage](read_image.md) with `mode='csv'`) - mask - Binary mask made from selected contours - histplot - If True plots histogram of intensity values (default histplot = False) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Data about image temperature within a masked region. - **Example use:** @@ -33,10 +34,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Caclulates the proportion of pixels that fall into a signal bin and writes the values to a file. Also provides a histogram of this data -thermal_hist = pcv.analyze_thermal_values(thermal_img, mask, histplot=True) +thermal_hist = pcv.analyze_thermal_values(thermal_array=thermal_img, maks=mask, histplot=True, label="default") # Access data stored out from analyze_thermal_values -temp_range = pcv.outputs.observations['max_temp']['value'] - pcv.outputs.observations['min_temp']['value'] +temp_range = pcv.outputs.observations['default']['max_temp']['value'] - pcv.outputs.observations['default']['min_temp']['value'] ``` diff --git a/docs/check_cycles.md b/docs/check_cycles.md index 5c642a910..22b72df99 100644 --- a/docs/check_cycles.md +++ b/docs/check_cycles.md @@ -2,12 +2,13 @@ Check for cycles within a skeletonized image. -**plantcv.morphology.check_cycles**(*skel_img*) +**plantcv.morphology.check_cycles**(*skel_img, label="default"*) **returns** debugging cycle image - **Parameters:** - skel_img - Skeleton image (output from [plantcv.morphology.skeletonize](skeletonize.md)) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Identifies cycles in a skeleton image. - **Output data stored:** Data ('num_cycles') automatically gets stored to the [`Outputs` class](outputs.md) when this function is ran. @@ -29,10 +30,10 @@ pcv.params.debug = "print" # The cycle_img created for debugging purposes allows for line thickness # adjustments with the global line thickness parameter. Try setting # pcv.params.line_thickness = 8 for thicker lines (default 5) -cycle_img = pcv.morphology.check_cycles(skel_img=skeleton) +cycle_img = pcv.morphology.check_cycles(skel_img=skeleton, label="default") # Access data stored out from check_cycles -num_cycles = pcv.outputs.observations['num_cycles']['value'] +num_cycles = pcv.outputs.observations['default']['num_cycles']['value'] ``` diff --git a/docs/fill_segments.md b/docs/fill_segments.md index 2115eab18..7133bb93d 100644 --- a/docs/fill_segments.md +++ b/docs/fill_segments.md @@ -2,16 +2,17 @@ Propagate the labels of a segmented skeleton to fill the mask. -**plantcv.morphology.fill_segments**(*mask, objects, stem_objects=None*) +**plantcv.morphology.fill_segments**(*mask, objects, stem_objects=None, label="default""default"*) **returns** filled_img - **Parameters:** - - mask - Binary mask - - objects - Segment objects (output from either [plantcv.morphology.prune](prune.md), + - mask - Binary mask + - objects - Segment objects (output from either [plantcv.morphology.prune](prune.md), [plantcv.morphology.segment_skeleton](segment_skeleton.md), or [plantcv.morphology.segment_sort](segment_sort.md)). - stem_objects - Optional input for stem objects that will be combined into a single object before filling the mask. + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Uses the watershed algorithm to fill the mask propagating the objects' labels. - **Output data stored:** Data ('segment_area') automatically gets stored to the [`Outputs` class](outputs.md) when this function is ran. @@ -32,10 +33,10 @@ from plantcv import plantcv as pcv # or "plot" (Jupyter Notebooks or X11) pcv.params.debug = "print" -filled_img = pcv.morphology.fill_segments(mask=plant_mask, objects=obj) +filled_img = pcv.morphology.fill_segments(mask=plant_mask, objects=obj, label="default") # Access data stored out from fill_segments -segments_area = pcv.outputs.observations['segment_area']['value'] +segments_area = pcv.outputs.observations['default']['segment_area']['value'] ``` diff --git a/docs/find_branch_pts.md b/docs/find_branch_pts.md index a9faa06ce..bc758ab27 100644 --- a/docs/find_branch_pts.md +++ b/docs/find_branch_pts.md @@ -2,13 +2,14 @@ Find branch/junction points in a skeletonized image. -**plantcv.morphology.find_branch_pts**(*skel_img, mask=None*) +**plantcv.morphology.find_branch_pts**(*skel_img, mask=None, label="default"*) **returns** Binary mask of branch points - **Parameters:** - skel_img - Skeleton image (output from [plantcv.morphology.skeletonize](skeletonize.md)) - - mask - Binary mask used for debugging image (optional). If provided the debug image will be overlaid on the mask. + - mask - Binary mask used for debugging image (optional). If provided the debug image will be overlaid on the mask. + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Identifies branch/junction points in a skeleton image @@ -38,7 +39,7 @@ branch_points_img = pcv.morphology.find_branch_pts(skel_img=skeleton) pcv.params.line_thickness = 2 branch_points_img = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=None) -branch_points_img = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=plant_mask) +branch_points_img = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=plant_mask, label="rep1") ``` diff --git a/docs/find_color_card.md b/docs/find_color_card.md index 7ac9a1725..f1db3927b 100644 --- a/docs/find_color_card.md +++ b/docs/find_color_card.md @@ -2,7 +2,7 @@ Automatically detects a color card's location and size. Useful in workflows where color card positioning isn't constant in all images. -**plantcv.transform.find_color_card**(*rgb_img, threshold_type='adaptgauss', threshvalue=125, blurry=False, background='dark', record_chip_size='median'*) +**plantcv.transform.find_color_card**(*rgb_img, threshold_type='adaptgauss', threshvalue=125, blurry=False, background='dark', record_chip_size='median', label="default"*) **returns** df, start_coord, spacing @@ -13,6 +13,7 @@ Automatically detects a color card's location and size. Useful in workflows wher - blurry - Optional boolean, if True then image sharpening is applied (default blurry=False) - background - Optional, type of image background, either 'dark' or 'light' (default background='dark') - record_chip_size - Optional, for choosing chip size measurement to be recorded, either "median" (default), "mean", or None + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Returns** - df - Dataframe of all color card chips found. - start_coord - Two-element tuple of the first chip mask starting x and y coordinate. Useful in [create a color card mask](#create-a-labeled-color-card-mask) function. @@ -32,7 +33,8 @@ rgb_img, path, filename = pcv.readimage("target_img.png") df, start, space = pcv.transform.find_color_card(rgb_img=rgb_img) # Use these outputs to create a labeled color card mask -mask = pcv.transform.create_color_card_mask(rgb_img=img, radius=10, start_coord=start, spacing=space, ncols=6, nrows=4) +mask = pcv.transform.create_color_card_mask(rgb_img=img, radius=10, start_coord=start, spacing=space, ncols=6, nrows=4, label="prefix") +avg_chip_size = pcv.outputs.observations['prefix']['color_chip_size']['value'] ``` diff --git a/docs/find_tips.md b/docs/find_tips.md index 81d1dc6a4..2f26013ea 100644 --- a/docs/find_tips.md +++ b/docs/find_tips.md @@ -2,13 +2,15 @@ Find endpoints of a skeletonized image. -**plantcv.morphology.find_tips**(*skel_img, mask=None*) +**plantcv.morphology.find_tips**(*skel_img, mask=None, label="default"*) **returns** Binary mask of endpoints - **Parameters:** - skel_img - Skeleton image (output from [plantcv.morphology.skeletonize](skeletonize.md)) - - mask - Binary mask used for debugging (optional). If provided the debug image will be overlaid on the mask. + - mask - Binary mask used for debugging (optional). If provided the debug image will be overlaid on the mask. + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) + - **Context:** - Identifies endpoints/tips in a skeleton image @@ -36,7 +38,7 @@ pcv.params.debug = "plot" pcv.params.line_thickness = 3 tips_img = pcv.morphology.find_tips(skel_img=skeleton) -tips_img = pcv.morphology.find_tips(skel_img=skeleton, mask=plant_mask) +tips_img = pcv.morphology.find_tips(skel_img=skeleton, mask=plant_mask, label=rep1) ``` diff --git a/docs/hyperspectral_tutorial.md b/docs/hyperspectral_tutorial.md index 7226ad879..3989afc1d 100644 --- a/docs/hyperspectral_tutorial.md +++ b/docs/hyperspectral_tutorial.md @@ -226,7 +226,9 @@ Binary mask after [filtering objects by the region of interest](roi_objects.md) # array - Hyperspectral data instance # mask - Binary mask image data # hist_plot - If True plots histogram of reflectance intensity values - analysis_img = pcv.hyperspectral.analyze_spectral(array=spectral_array, mask=kept_mask, histplot=True) + # label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) + + analysis_img = pcv.hyperspectral.analyze_spectral(array=spectral_array, mask=kept_mask, histplot=True, label="default") ``` @@ -241,7 +243,9 @@ Binary mask after [filtering objects by the region of interest](roi_objects.md) # Inputs: # array - Hyperspectral index data instance # mask - Binary mask image data - pcv.hyperspectral.analyze_index(array=index_array_gdvi, mask=kept_mask) + # label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) + + pcv.hyperspectral.analyze_index(array=index_array_gdvi, mask=kept_mask, label="default") ``` diff --git a/docs/img/tutorial_images/morphology/filled_img_all_segments.jpg b/docs/img/tutorial_images/morphology/filled_img_all_segments.jpg new file mode 100644 index 000000000..279e2a494 Binary files /dev/null and b/docs/img/tutorial_images/morphology/filled_img_all_segments.jpg differ diff --git a/docs/img/tutorial_images/morphology/filled_segments_stem.jpg b/docs/img/tutorial_images/morphology/filled_segments_stem.jpg new file mode 100644 index 000000000..69d49e9b9 Binary files /dev/null and b/docs/img/tutorial_images/morphology/filled_segments_stem.jpg differ diff --git a/docs/landmark_reference_pt_dist.md b/docs/landmark_reference_pt_dist.md index feb537792..fc2395cc3 100644 --- a/docs/landmark_reference_pt_dist.md +++ b/docs/landmark_reference_pt_dist.md @@ -4,7 +4,7 @@ This is a function to measure the distance from user defined points to the centr along the x-axis and baseline coordinate (top of pot) along the y-axis. Calculating the vertical distance between leaf tip points to the centroid of the plant object in side-view images may provide a proxy measure of turgor pressure. -**plantcv.landmark_reference_pt_dist**(*points_r, centroid_r, bline_r*) +**plantcv.landmark_reference_pt_dist**(*points_r, centroid_r, bline_r, label="default"*) **returns** none @@ -12,6 +12,7 @@ to the centroid of the plant object in side-view images may provide a proxy meas - points_r - A list of tuples representing rescaled landmark points - centroid_r - A tuple representing the rescaled centroid point - bline_r - A tuple representing the rescaled baseline point + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to estimate the distance and angles of landmark points relative to shape reference landmarks (centroid and pot height aka baseline) - **Output data stored:** Data ('vert_ave_c', 'hori_ave_c', 'euc_ave_c', 'ang_ave_c', 'vert_ave_b', 'hori_ave_b', 'euc_ave_b', @@ -31,10 +32,10 @@ pcv.params.debug = "print" # Identify acute vertices (tip points) of an object # Results in set of point values that may indicate tip points -pcv.landmark_reference_pt_dist(points_r, centroid_r, bline_r) +pcv.landmark_reference_pt_dist(points_r=points_r, centroid_r=centroid_r, bline_r=bline_r, label="default") # Access data stored out from landmark_reference_pt_dist -avg_vert_distance = pcv.outputs.observations['vert_ave_c']['value'] +avg_vert_distance = pcv.outputs.observations['default']['vert_ave_c']['value'] ``` diff --git a/docs/morphology_tutorial.md b/docs/morphology_tutorial.md index 1e5a39a00..fb0b41eff 100644 --- a/docs/morphology_tutorial.md +++ b/docs/morphology_tutorial.md @@ -171,8 +171,8 @@ off. The function prunes only secondary segments; primary segments that are smal # Inputs: # skel_img = Skeletonized image # mask = (Optional) binary mask for debugging. If provided, debug image will be overlaid on the mask. - - branch_pts_mask = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=cropped_mask) + # label = (Optional) label parameter, modifies the variable name of observations recorded. (default `label="default"`) + branch_pts_mask = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=cropped_mask, label="default") ``` @@ -193,8 +193,9 @@ to remove all barbs. # 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"`) - tip_pts_mask = pcv.morphology.find_tips(skel_img=skeleton, mask=None) + tip_pts_mask = pcv.morphology.find_tips(skel_img=skeleton, mask=None, label="default") ``` @@ -262,9 +263,10 @@ For this tutorial we assume leaves are the objects of interest, and just pass th # 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"`) labeled_img = pcv.morphology.segment_path_length(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") ``` @@ -282,9 +284,10 @@ passed into the function. # 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"`) labeled_img = pcv.morphology.segment_euclidean_length(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") ``` @@ -302,9 +305,10 @@ euclidean distance of each segment passed to the function. # 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"`) labeled_img = pcv.morphology.segment_curvature(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") ``` @@ -324,9 +328,10 @@ is a straight line while larger values indicate the segment has more curvature. # 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"`) labeled_img = pcv.morphology.segment_angle(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") ``` @@ -345,9 +350,10 @@ by fitting a linear regression line to each segment. # segmented_img = Segmented image to plot tangent angles on # objects = List of contours # size = Size of ends used to calculate "tangent" lines + # label = (Optional) label parameter, modifies the variable name of observations recorded. (default `label="default"`) labeled_img = pcv.morphology.segment_tangent_angle(segmented_img=segmented_img, - objects=leaf_obj, size=15) + objects=leaf_obj, size=15, label="default") ``` @@ -370,12 +376,13 @@ leaves that are more "floppy" will have smaller angles. # leaf_objects = List of leaf contours # stem_objects = List of stem objects # size = Size of the inner portion of each leaf to find a linear regression line + # label = (Optional) label parameter, modifies the variable name of observations recorded. (default `label="default"`) labeled_img = pcv.morphology.segment_insertion_angle(skel_img=skeleton, segmented_img=segmented_img, leaf_objects=leaf_obj, stem_objects=stem_obj, - size=20) + size=20, label="default") ``` @@ -388,6 +395,45 @@ out from a stem will have larger insertion angles than those that grow upward. ![Screenshot](img/tutorial_images/morphology/insertion_angle_img.jpg) +```python + + # 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, label="all_segments") + + +``` + +**Figure 19.** Fill Segment Area + +The [plantcv.morphology.fill_segment](fill_segments.md) function aims to measure +area of segments filled in. By using watershed segmentation to flood fill the mask by +using the segments as markers. + +![Screenshot](img/tutorial_images/morphology/filled_img_all_segments.jpg) + + +```python + + # Fill in leaves (also stores out area data) + + filled_img2 = pcv.morphology.fill_segments(mask=cropped_mask, objects=leaf_obj, stem_objects=stem_obj, label="separate_leaves") + + +``` + +**Figure 20.** Fill Leaf/Stem Area + +The [plantcv.morphology.fill_segment](fill_segments.md) function can also measure segments +separately. When inputting sorted stem and leaf segments, the stem segments will be combined into +one object while the leaves remain separate. + +![Screenshot](img/tutorial_images/morphology/filled_segments_stem.jpg) + To deploy a workflow over a full image set please see tutorial on [workflow parallelization](pipeline_parallel.md). @@ -458,10 +504,10 @@ def main(): pruned, seg_img, edge_objects = pcv.morphology.prune(skel_img=skeleton, size=0, mask=cropped_mask) # Identify branch points - branch_pts_mask = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=cropped_mask) + branch_pts_mask = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=cropped_mask, label="default") # Identify tip points - tip_pts_mask = pcv.morphology.find_tips(skel_img=skeleton, mask=None) + tip_pts_mask = pcv.morphology.find_tips(skel_img=skeleton, mask=None, label="default") # Sort segments into leaf objects and stem objects leaf_obj, stem_obj = pcv.morphology.segment_sort(skel_img=skeleton, objects=edge_objects, @@ -473,29 +519,33 @@ def main(): # Measure path lengths of segments labeled_img2 = pcv.morphology.segment_path_length(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") # Measure euclidean distance of segments labeled_img3 = pcv.morphology.segment_euclidean_length(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") # Measure curvature of segments labeled_img4 = pcv.morphology.segment_curvature(segmented_img=segmented_img, - objects=leaf_obj) + objects=leaf_obj, label="default") # Measure the angle of segments - labeled_img5 = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=leaf_obj) + labeled_img5 = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=leaf_obj, label="default") # Measure the tangent angles of segments labeled_img6 = pcv.morphology.segment_tangent_angle(segmented_img=segmented_img, - objects=leaf_obj, size=15) + objects=leaf_obj, size=15, label="default") # Measure the leaf insertion angles labeled_img7 = pcv.morphology.segment_insertion_angle(skel_img=skeleton, segmented_img=segmented_img, leaf_objects=leaf_obj, stem_objects=stem_obj, - size=20) + size=20, label="default") + + # Fill in segments (also stores out area data) + filled_img = pcv.morphology.fill_segments(mask=cropped_mask, objects=edge_objects, label="all_segments") + filled_img2 = pcv.morphology.fill_segments(mask=cropped_mask, objects=leaf_obj, stem_objects=stem_obj, label="separate_leaves") # Write out data collected about angles and lengths diff --git a/docs/nir_tutorial.md b/docs/nir_tutorial.md index 4ae306cb2..13e88d217 100644 --- a/docs/nir_tutorial.md +++ b/docs/nir_tutorial.md @@ -456,8 +456,9 @@ Now we can perform the [analysis of pixelwise signal value](analyze_NIR_intensit # mask - Binary mask made from selected contours # bins - Number of classes to divide the spectrum into # histplot - If True, plots the histogram of intensity values + # label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) nir_hist = pcv.analyze_nir_intensity(gray_img=img, mask=kept_mask, - bins=256, histplot=True) + bins=256, histplot=True, label="default") # Pseudocolor the grayscale image to a colormap @@ -480,7 +481,8 @@ Now we can perform the [analysis of pixelwise signal value](analyze_NIR_intensit # img - RGB or grayscale image data # obj- Single or grouped contour object # mask - Binary image mask to use as mask for moments analysis - shape_imgs = pcv.analyze_object(img=rgb_img, obj=o, mask=m) + # label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) + shape_imgs = pcv.analyze_object(img=img, obj=o, mask=m, label="default") # Write shape and nir data to results file pcv.print_results(filename=args.result) @@ -634,13 +636,13 @@ def main(): outfile = os.path.join(args.outdir, img_name) # Perform signal analysis - nir_hist = pcv.analyze_nir_intensity(gray_img=img, mask=kept_mask, bins=256, histplot=True) + nir_hist = pcv.analyze_nir_intensity(gray_img=img, mask=kept_mask, bins=256, histplot=True, label="default") # Pseudocolor the grayscale image to a colormap pseudocolored_img = pcv.visualize.pseudocolor(gray_img=img, mask=kept_mask, cmap='viridis') # Perform shape analysis - shape_imgs = pcv.analyze_object(img=rgb_img, obj=o, mask=m) + shape_imgs = pcv.analyze_object(img=img, obj=o, mask=m, label="default") # Write shape and nir data to results file pcv.print_results(filename=args.result) diff --git a/docs/output_measurements.md b/docs/output_measurements.md index af002db3c..2f1cf9a5a 100644 --- a/docs/output_measurements.md +++ b/docs/output_measurements.md @@ -12,10 +12,11 @@ The JSON output file has two top-level sections: `variables` is a collection of dataset, and `entities` is a list of data blocks for each unit of analysis (typically an image, or a sub-region of an image) in the dataset. For each entity there are two data blocks: `metadata` is a set of key-value pairs of metadata keywords and their values (e.g. image or experimental metadata such as timestamp, treatment, etc.), and `observations` -is a set of data blocks of observational data or measurements. Each observation has the same set of information, -roughly following the [MIAPPE](https://www.miappe.org/) guidelines: `trait` is the name of the observation, `method` is generally the PlantCV -function name used (but it could be another method), `scale` is the observation units, `datatype` is the Python data -type the data are stored as, `value` is the observation output value(s), and `label` is the data/category label. +is a set of data blocks of observational data or measurements. Observations contain samples. Each sample has the same +set of information, roughly following the [MIAPPE](https://www.miappe.org/) guidelines: `trait` is the name of the +observation, `method` is generally the PlantCV function name used (but it could be another method), `scale` is the +observation units, `datatype` is the Python data type the data are stored as, `value` is the observation output +value(s), and `label` is the data/category label. Example (abbreviated) JSON data: @@ -90,21 +91,23 @@ Example (abbreviated) JSON data: } }, "observations": { - "pixel_area": { - "trait": "area", - "method": "plantcv.plantcv.analyze_object", - "scale": "pixels", - "datatype": "", - "value": 10000, - "label": "pixels" - }, - "hull_area": { - "trait": "convex hull area", - "method": "plantcv.plantcv.analyze_object", - "scale": "pixels", - "datatype": "", - "value": 100000, - "label": "pixels" + "sample1": { + "pixel_area": { + "trait": "area", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 10000, + "label": "pixels" + }, + "hull_area": { + "trait": "convex hull area", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 100000, + "label": "pixels" + } } } } @@ -150,6 +153,7 @@ Functions that automatically store data to the [`Outputs` class](outputs.md) are [morphology.segment_curvature](segment_curvature.md), [morphology.segment_euclidean_length](segment_euclidean_length.md), [morphology.segment_insertion_angle](segment_insertion_angle.md), [morphology.segment_path_length](segment_pathlength.md), [morphology.segment_tangent_angle](segment_tangent_angle.md), [report_size_marker_area](report_size_marker.md), [watershed_segmentation](watershed.md), -[within_frame](within_frame.md), [x_axis_pseudolandmarks](x_axis_pseudolandmarks.md), and [y_axis_pseudolandmarks](y_axis_pseudolandmarks.md). +[within_frame](within_frame.md), [x_axis_pseudolandmarks](x_axis_pseudolandmarks.md), and [y_axis_pseudolandmarks](y_axis_pseudolandmarks.md). All of these functions include an optional `label` parameter +that allows users to append custom prefixes to the unique variable identifier. For more detail about the traits measured by each function see the [Observation Traits Summary Table](https://docs.google.com/spreadsheets/d/1gk5VocBA-63gyF_vA6yPNvWreZ1R7-_z4vOfm37YBl8/edit?usp=sharing). diff --git a/docs/outputs.md b/docs/outputs.md index 8c534590d..113f1c181 100644 --- a/docs/outputs.md +++ b/docs/outputs.md @@ -28,7 +28,7 @@ functions: * `watershed` An instance of `Outputs` is created on import automatically as `plantcv.outputs`. The function -[pcv.print_results](print_results.md) will print out all the stored measurments data to a text file. +[pcv.print_results](print_results.md) will print out all the stored measurment data to a text file. ### Methods @@ -38,6 +38,8 @@ Methods are accessed as plantcv.outputs.*method*. **add_observation**: Add new measurement or other information +* sample: A sample name or label. Observations are organized by sample name. + * variable: A local unique identifier of a variable, e.g. a short name, that is a key linking the definitions of variables with observations. * trait: A name of the trait mapped to an external ontology; if there is no exact mapping, an informative description of the trait. @@ -63,10 +65,10 @@ from plantcv import plantcv as pcv ######## workflow steps here ######## # Find shape properties, output shape image (optional) -shape_img = pcv.analyze_object(img, obj, mask) +shape_img = pcv.analyze_object(img, obj, mask, label="default") # Look at object area data without writing to a file -plant_area = pcv.outputs.observations['pixel_area']['value'] +plant_area = pcv.outputs.observations['default']['pixel_area']['value'] # Write shape data to results file pcv.print_results(filename=args.result) @@ -76,8 +78,8 @@ pcv.outputs.clear() ######## More workflow steps here ######## -nir_imgs = pcv.analyze_nir_intensity(nir2, nir_combinedmask, 256) -shape_img = pcv.analyze_object(nir2, nir_combined, nir_combinedmask) +nir_imgs = pcv.analyze_nir_intensity(nir2, nir_combinedmask, 256, label="default") +shape_img = pcv.analyze_object(nir2, nir_combined, nir_combinedmask, label="default") # Write the NIR and shape data to a file pcv.print_results(filename=args.coresult) @@ -97,7 +99,8 @@ healthy_plant = np.count_nonzero(mask['plant']) percent_diseased = sick_plant / (sick_plant + healthy_plant) # Create a new measurement -pcv.outputs.add_observation(variable='percent_diseased', trait='percent of plant detected to be diseased', +pcv.outputs.add_observation(sample='default', variable='percent_diseased', + trait='percent of plant detected to be diseased', method='ratio of pixels', scale='percent', datatype=float, value=percent_diseased, label='percent') diff --git a/docs/photosynthesis_analyze_fvfm.md b/docs/photosynthesis_analyze_fvfm.md index 1181077e9..54cfac96e 100644 --- a/docs/photosynthesis_analyze_fvfm.md +++ b/docs/photosynthesis_analyze_fvfm.md @@ -2,7 +2,7 @@ Extract Fv/Fm data of objects. -**plantcv.photosynthesis.analyze_fvfm**(*fdark, fmin, fmax, mask, bins=256*) +**plantcv.photosynthesis.analyze_fvfm**(*fdark, fmin, fmax, mask, bins=256, label="default"*) **returns** PSII analysis images (Fv/Fm image, Fv/Fm histogram) @@ -12,6 +12,7 @@ Extract Fv/Fm data of objects. - fmax - image object, grayscale - mask - binary mask of selected contours - bins - number of grayscale bins (0-256 for 8-bit images and 0 to 65,536), if you would like to bin data, you would alter this number (default bins=256) + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Used to extract Fv/Fm or Fv'/Fm' per identified plant pixel. - Generates histogram of Fv/Fm data. @@ -41,10 +42,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Analyze Fv/Fm -fvfm_images = pcv.photosynthesis.analyze_fvfm(fdark, fmin, fmax, kept_mask, 256) +fvfm_images = pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=kept_mask, bins=256, label="fluor") # Access data stored out from fluor_fvfm -fvfm_median = pcv.outputs.observations['fvfm_median']['value'] +fvfm_median = pcv.outputs.observations['fluor']['fvfm_median']['value'] # Store the two images fvfm_img = fvfm_images[0] diff --git a/docs/psII_tutorial.md b/docs/psII_tutorial.md index b75ab2e55..d03498855 100644 --- a/docs/psII_tutorial.md +++ b/docs/psII_tutorial.md @@ -197,7 +197,8 @@ The next step is to analyze the plant object for traits such as [shape](analyze_ # img - RGB or grayscale image data # obj - Single or grouped contour object # mask - Binary image mask to use as mask for moments analysis - shape_img = pcv.analyze_object(img=fmax, obj=obj, mask=cleaned_mask) + # label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) + shape_img = pcv.analyze_object(img=fmax, obj=obj, mask=cleaned_mask, label="default") # Analyze fv/fm fluorescence properties @@ -207,7 +208,8 @@ The next step is to analyze the plant object for traits such as [shape](analyze_ # fmax - Grayscale image # mask - Binary mask of selected contours # bins - Number of grayscale bins (0-256 for 8-bit img, 0-65536 for 16-bit). Default bins = 256 - fvfm_images = pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=cleaned_mask, bins=256) + # label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) + fvfm_images = pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=cleaned_mask, bins=256, label="default") # Store the two fv_fm images fvfm_img = fvfm_images[0] @@ -305,10 +307,10 @@ def main(): obj, masked = pcv.object_composition(img=cleaned_mask, contours=id_objects, hierarchy=obj_hierarchy) # Find shape properties - shape_img = pcv.analyze_object(img=fmax, obj=obj, mask=cleaned_mask) + shape_img = pcv.analyze_object(img=fmax, obj=obj, mask=cleaned_mask, label="default") # Analyze fv/fm fluorescence properties - fvfm_images = pcv.pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=cleaned_mask, bins=256) + fvfm_images = pcv.pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=cleaned_mask, bins=256, label="default") # Store the two fv_fm images fvfm_img = fvfm_images[0] diff --git a/docs/report_size_marker.md b/docs/report_size_marker.md index 452b0ee0a..8adebc4a3 100644 --- a/docs/report_size_marker.md +++ b/docs/report_size_marker.md @@ -3,7 +3,7 @@ Get and record the size of a size marker or set an area as a size marker. **plantcv.report_size_marker_area**(*img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, - thresh=None*) + thresh=None, label="default"*) **returns** analysis_image @@ -16,6 +16,7 @@ Get and record the size of a size marker or set an area as a size marker. - objcolor = Object color is 'dark' (default) or 'light' (is the marker darker or lighter than the background) - thresh_channel = 'h', 's', or 'v' for hue, saturation or value, default set to None - thresh = Binary threshold value (integer), default set to None. + - label = Optional label parameter, modifies the variable name of observations recorded - **Context:** - Allows user to add size marker data, so that shape data can be normalized between images/cameras - **Output data stored:** Data ('marker_area', 'marker_ellipse_major_axis', 'marker_ellipse_minor_axis', 'marker_ellipse_eccentricity') @@ -37,13 +38,14 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Define an ROI for the marker -roi_contour, roi_hierarchy = pcv.roi.rectangle(img1, 3550, 850, 500, 500) +roi_contour, roi_hierarchy = pcv.roi.rectangle(img=img1, x=3550, y=850, h=500, w=500) # Detect and Measure Size Marker -image = pcv.report_size_marker_area(img1, roi_contour, roi_hierarchy, marker='detect', objcolor='light', thresh_channel='s', thresh=120) +image = pcv.report_size_marker_area(img=img1, roi_contour=roi_contour, roi_hierarchy=roi_hierarchy, + marker='detect', objcolor='light', thresh_channel='s', thresh=120, label="default") # Access data stored out from report_size_marker_area -marker_area = pcv.outputs.observations['marker_area']['value'] +marker_area = pcv.outputs.observations['default']['marker_area']['value'] ``` diff --git a/docs/segment_angle.md b/docs/segment_angle.md index c7ee7895c..dad595ca0 100644 --- a/docs/segment_angle.md +++ b/docs/segment_angle.md @@ -2,7 +2,7 @@ Measure angles of segments. -**plantcv.morphology.segment_angle**(*segmented_img, objects*) +**plantcv.morphology.segment_angle**(*segmented_img, objects, label="default"*) **returns** labeled image @@ -11,6 +11,7 @@ Measure angles of segments. or [plantcv.morphology.segment_id](segment_id.md)), used for creating the labeled image. - objects - Segment objects (output from either [plantcv.morphology.segment_skeleton](segment_skeleton.md), or [plantcv.morphology.segment_sort](segment_sort.md)). + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Calculates angles of segments (in degrees) by fitting a linear regression line to each segment. Users can pass only leaf objects (returned from [plantcv.morphology.segment_sort](segment_sort.md)) to only collect angles of leaves. @@ -30,10 +31,10 @@ from plantcv import plantcv as pcv # or "plot" (Jupyter Notebooks or X11) pcv.params.debug = "print" -labeled_img = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=obj) +labeled_img = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=obj, label="default") # Access data stored out from segment_angle -segment_angles = pcv.outputs.observations['segment_angle']['value'] +segment_angles = pcv.outputs.observations['default']['segment_angle']['value'] ``` diff --git a/docs/segment_curvature.md b/docs/segment_curvature.md index a53ed3ab8..65b715133 100644 --- a/docs/segment_curvature.md +++ b/docs/segment_curvature.md @@ -2,7 +2,7 @@ Measure the curvature of segments. -**plantcv.morphology.segment_curvature**(*segmented_img, objects*) +**plantcv.morphology.segment_curvature**(*segmented_img, objects, label="default"*) **returns** labeled image @@ -12,6 +12,7 @@ Measure the curvature of segments. - objects - Segment objects (output from either [plantcv.morphology.prune](prune.md), [plantcv.morphology.segment_skeleton](segment_skeleton.md), or [plantcv.morphology.segment_sort](segment_sort.md)). + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Calculates curvature of segments by taking the ratio of the geodesic distance ([plantcv.morphology.segment_path_length](segment_pathlength.md)) over the euclidean distance [plantcv.morphology.segment_euclidean_length](segment_euclidean_length.md)). Measurement of two-dimensional tortuosity. @@ -29,13 +30,14 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" labeled_img = pcv.morphology.segment_curvature(segmented_img=segmented_img, - objects=obj) + objects=obj, label="all") # Pass just leaf objects and hierarchies (output from pcv.morphology.segment_sort) labeled_img2 = pcv.morphology.segment_curvature(segmented_img=leaf_segmented, - objects=leaf_obj) + objects=leaf_obj, label="leaf") # Access data stored out from segment_curvature -segment_curvatures = pcv.outputs.observations['segment_curvature']['value'] +all_curvatures = pcv.outputs.observations['all']['segment_curvature']['value'] +leaf_curvatures = pcv.outputs.observations['leaf']['segment_curvature']['value'] ``` diff --git a/docs/segment_euclidean_length.md b/docs/segment_euclidean_length.md index ff6598d45..75712c39f 100644 --- a/docs/segment_euclidean_length.md +++ b/docs/segment_euclidean_length.md @@ -2,7 +2,7 @@ Measure Euclidean distance of segments. -**plantcv.morphology.segment_euclidean_length**(*segmented_img, objects*) +**plantcv.morphology.segment_euclidean_length**(*segmented_img, objects, label="default"*) **returns** labeled image @@ -12,6 +12,7 @@ Measure Euclidean distance of segments. - objects - Segment objects (output from either [plantcv.morphology.prune](prune.md), [plantcv.morphology.segment_skeleton](segment_skeleton.md), or [plantcv.morphology.segment_sort](segment_sort.md)). + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Calculates the euclidean distance of each segment. Users can pass only leaf objects (returned from [plantcv.morphology.segment_sort](segment_sort.md)) to only collect lengths of leaves. @@ -32,10 +33,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" labeled_img = pcv.morphology.segment_euclidean_length(segmented_img=segmented_img, - objects=obj) + objects=obj, label="default") # Access data stored out from segment_euclidean_length -euclidean_lengths = pcv.outputs.observations['segment_eu_length']['value'] +euclidean_lengths = pcv.outputs.observations['default']['segment_eu_length']['value'] ``` diff --git a/docs/segment_insertion_angle.md b/docs/segment_insertion_angle.md index fdf22e74a..a98a7ae65 100644 --- a/docs/segment_insertion_angle.md +++ b/docs/segment_insertion_angle.md @@ -2,7 +2,7 @@ Measure leaf insertion angles. -**plantcv.morphology.segment_insertion_angle**(*skel_img, segmented_img, leaf_objects, stem_objects, size*) +**plantcv.morphology.segment_insertion_angle**(*skel_img, segmented_img, leaf_objects, stem_objects, size, label="default"*) **returns** labeled image @@ -14,6 +14,7 @@ Measure leaf insertion angles. - leaf_objects - Leaf segment objects (output from [plantcv.morphology.segment_sort](segment_sort.md)). - stem_objects - Stem segment objects (output from [plantcv.morphology.segment_sort](segment_sort.md)). - size - Size of ends (number of pixels) used to calculate insertion point "tangent" lines + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Find "tangent" angles to leaf insertion points in degrees of skeleton segments compared to the stem angle. Use `size` pixels of the inner portion of each leaf to find a linear regression line, and calculate angle between insertion @@ -41,10 +42,10 @@ labeled_img = pcv.morphology.segment_insertion_angle(skel_img=skeleton, segmented_img=leaves_segment, leaf_objects=leaf_obj, stem_objects=stem_objs, - size=20) + size=20, label="default") # Access data stored out from segment_insertion_angle -segment_insertion_angles = pcv.outputs.observations['segment_insertion_angle']['value'] +segment_insertion_angles = pcv.outputs.observations['default']['segment_insertion_angle']['value'] ``` diff --git a/docs/segment_pathlength.md b/docs/segment_pathlength.md index 638177e42..25ea97394 100644 --- a/docs/segment_pathlength.md +++ b/docs/segment_pathlength.md @@ -2,7 +2,7 @@ Measure the geodesic distance of segments. -**plantcv.morphology.segment_path_length**(*segmented_img, objects*) +**plantcv.morphology.segment_path_length**(*segmented_img, objects, label="default"*) **returns** labeled_image @@ -12,6 +12,7 @@ Measure the geodesic distance of segments. - objects - Segment objects (output from either [plantcv.morphology.prune](prune.md), [plantcv.morphology.segment_skeleton](segment_skeleton.md), or [plantcv.morphology.segment_sort](segment_sort.md)). + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Calculates the geodesic distance of each segment. Users can pass only leaf objects (returned from [plantcv.morphology.segment_sort](segment_sort.md)) to only collect lengths of leaves only. @@ -32,10 +33,10 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" labeled_img = pcv.morphology.segment_path_length(segmented_img=segmented_img, - objects=obj) + objects=obj, label="default") # Access data stored out from segment_path_length -path_lengths = pcv.outputs.observations['segment_path_length']['value'] +path_lengths = pcv.outputs.observations['default']['segment_path_length']['value'] ``` diff --git a/docs/segment_tangent_angle.md b/docs/segment_tangent_angle.md index 18342b1de..c0a8e7f8b 100644 --- a/docs/segment_tangent_angle.md +++ b/docs/segment_tangent_angle.md @@ -2,7 +2,7 @@ Measure tangent angles of segments as a way to quantify leaf behavior. -**plantcv.morphology.segment_tangent_angle**(*segmented_img, objects, size*) +**plantcv.morphology.segment_tangent_angle**(*segmented_img, objects, size, label="default"*) **returns** labeled image @@ -13,6 +13,7 @@ Measure tangent angles of segments as a way to quantify leaf behavior. [plantcv.morphology.segment_skeleton](segment_skeleton.md), or [plantcv.morphology.segment_sort](segment_sort.md)). - size - Size of ends (number of pixels) used to calculate "tangent" lines + - label - Optional label parameter, modifies the variable name of observations recorded. (default `label="default"`) - **Context:** - Find 'tangent' angles in degrees of skeleton segments. Use `size` pixels on either end of each segment to find a linear regression line, and calculate angle between the two lines @@ -39,10 +40,10 @@ pcv.params.line_thickness = 3 labeled_img = pcv.morphology.segment_tangent_angle(segmented_img=leaves_segment, objects=leaf_obj, - size=15) + size=15, label="default") # Access data stored out from segment_tangent_angle -leaf_tangent_angles = pcv.outputs.observations['segment_tangent_angle']['value'] +leaf_tangent_angles = pcv.outputs.observations['default']['segment_tangent_angle']['value'] ``` diff --git a/docs/thermal_tutorial.md b/docs/thermal_tutorial.md index 87e21fa24..f1dc10760 100644 --- a/docs/thermal_tutorial.md +++ b/docs/thermal_tutorial.md @@ -217,7 +217,8 @@ Now that the plant has been separated from the background we can analyze the tem # img - Array of thermal values # mask - Binary mask made from selected contours # histplot - If True plots histogram of intensity values (default histplot = False) - analysis_img = pcv.analyze_thermal_values(thermal_array=thermal_data, mask=kept_mask, histplot=True) + # label - Optional label parameter, modifies the variable name of observations recorded + analysis_img = pcv.analyze_thermal_values(thermal_array=thermal_data, mask=kept_mask, histplot=True, label="default") ``` @@ -372,7 +373,8 @@ def main(): # img - Array of thermal values # mask - Binary mask made from selected contours # histplot - If True plots histogram of intensity values (default histplot = False) - analysis_img = pcv.analyze_thermal_values(thermal_array=thermal_data, mask=kept_mask, histplot=True) + # label - Optional label parameter, modifies the variable name of observations recorded + analysis_img = pcv.analyze_thermal_values(thermal_array=thermal_data, mask=kept_mask, histplot=True, label="default") # Pseudocolor the thermal data diff --git a/docs/updating.md b/docs/updating.md index c7f5721e9..de15d1a77 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -144,6 +144,7 @@ pages for more details on the input and output variable types. * pre v3.0dev2: device, acute = **plantcv.acute_vertex**(*obj, win, thresh, sep, img, device, debug=None*) * post v3.0dev2: acute = **plantcv.acute_vertex**(*obj, win, thresh, sep, img*) * post v3.2: acute, analysis_image = **plantcv.acute_vertex**(*img, obj, win, thresh, sep*) +* post v3.11: acute, analysis_image = **plantcv.acute_vertex**(**img, obj, win, thresh, sep, label="default"*) #### plantcv.adaptive_threshold @@ -156,7 +157,7 @@ pages for more details on the input and output variable types. * pre v3.0dev2: device, bound_header, bound_data, analysis_images = **plantcv.analyze_bound**(*img, imgname, obj, mask, line_position, device, debug=None, filename=False*) * post v3.0dev2: Deprecated, see: - * analysis_images = **plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position, filename=False*) + * analysis_images = **plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position, filename=False, label="default"*) #### plantcv.analyze_bound_horizontal @@ -164,6 +165,8 @@ pages for more details on the input and output variable types. * post v3.0dev2: bound_header, bound_data, analysis_images = **plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position, filename=False*) * post v3.0: bound_header, bound_data, analysis_images = **plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position*) * post v3.3: analysis_image = **plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position*) +* post v3.11: analysis_image = **plantcv.analyze_bound_horizontal**(*img, obj, mask, line_position, label="default"*) + #### plantcv.analyze_bound_vertical @@ -171,6 +174,8 @@ pages for more details on the input and output variable types. * post v3.0dev2: bound_header, bound_data, analysis_images = **plantcv.analyze_bound_vertical**(*img, obj, mask, line_position, filename=False*) * post v3.0.5: bound_header, bound_data, analysis_images = **plantcv.analyze_bound_vertical**(*img, obj, mask, line_position*) * post v3.3: analysis_image = **plantcv.analyze_bound_vertical**(*img, obj, mask, line_position*) +* post v3.11: analysis_image = **plantcv.analyze_bound_vertical**(*img, obj, mask, line_position, label="default"*) + #### plantcv.analyze_color @@ -178,6 +183,8 @@ pages for more details on the input and output variable types. * post v3.0dev2: hist_header, hist_data, analysis_images = **plantcv.analyze_color**(*rgb_img, mask, bins, hist_plot_type=None, pseudo_channel='v', pseudo_bkg='img', filename=False*) * post v3.0: hist_header, hist_data, analysis_images = **plantcv.analyze_color**(*rgb_img, mask, bins, hist_plot_type=None*) * post v3.3: analysis_image = **plantcv.analyze_color**(*rgb_img, mask, hist_plot_type=None*) +* post v3.11: analysis_image = **plantcv.analyze_color**(*rgb_img, mask, hist_plot_type=None, label="default"*) + #### plantcv.analyze_nir_intensity @@ -185,6 +192,8 @@ pages for more details on the input and output variable types. * post v3.0dev2: hist_header, hist_data, analysis_img = **plantcv.analyze_nir_intensity**(*gray_img, mask, bins, histplot=False, filename=False*) * post v3.0: hist_header, hist_data, nir_hist = **plantcv.analyze_nir_intensity**(*gray_img, mask, bins, histplot=False*) * post v3.3: nir_hist = **plantcv.analyze_nir_intensity**(*gray_img, mask, bins, histplot=False*) +* post v3.11: nir_hist = **plantcv.analyze_nir_intensity**(*gray_img, mask, bins, histplot=False, label="default"*) + #### plantcv.analyze_object @@ -192,11 +201,14 @@ pages for more details on the input and output variable types. * post v3.0dev2: shape_header, shape_data, analysis_images = **plantcv.analyze_object**(*img, obj, mask, filename=False*) * post v3.0: shape_header, shape_data, analysis_images = **plantcv.analyze_object**(*img, obj, mask*) * post v3.3: analysis_image = **plantcv.analyze_object**(*img, obj, mask*) +* post v3.11: analysis_image = **plantcv.analyze_object**(*img, obj, mask, label="default"*) + #### plantcv.analyze_thermal_values * pre v3.5: NA * post v3.5: thermal_histogram = **plantcv.analyze_thermal_values**(*thermal_array, mask, histplot=False*) +* post v3.11: thermal_histogram = **plantcv.analyze_thermal_values**(*thermal_array, mask, histplot=False, label="default"*) #### plantcv.apply_mask @@ -329,11 +341,14 @@ pages for more details on the input and output variable types. * pre v3.7: NA * post v3.7: **plantcv.hyperspectral.analyze_index**(*index_array, mask*) * post v3.8: index_histogram = **plantcv.hyperspectral.analyze_index**(*index_array, mask, histplot=False, bins=100, max_bin=0, min_bin=1*) +* post v3.11: index_histogram = **plantcv.hyperspectral.analyze_index**(*index_array, mask, histplot=False, bins=100, max_bin=0, min_bin=1, label="default"*) + #### plantcv.hyperspectral.analyze_spectral * pre v3.7: NA * post v3.7: spectral_histogram = **plantcv.hyperspectral.analyze_spectral**(*array, mask, histplot=True*) +* post v3.11: spectral_histogram =**plantcv.hyperspectral.analyze_spectral**(*array, mask, histplot=True, label="default"*) #### plantcv.hyperspectral.extract_index @@ -362,6 +377,8 @@ pages for more details on the input and output variable types. * post v3.0dev2: vert_ave_c, hori_ave_c, euc_ave_c, ang_ave_c, vert_ave_b, hori_ave_b, euc_ave_b, ang_ave_b = **plantcv.landmark_reference_pt_dist**(*points_r, centroid_r, bline_r*) * post v3.2: landmark_header, landmark_data = **plantcv.landmark_reference_pt_dist**(*points_r, centroid_r, bline_r*) * post v3.3: **plantcv.landmark_reference_pt_dist**(*points_r, centroid_r, bline_r*) +* post v3.11: **plantcv.landmark_reference_pt_dist**(*points_r, centroid_r, bline_r, label="default"*) + #### plantcv.laplace_filter @@ -394,21 +411,25 @@ pages for more details on the input and output variable types. * pre v3.8: NA * post v3.8: labeled_img = **plantcv.morphology.analyze_stem**(*rgb_img, stem_objects*) +* post v3.11: labeled_img = **plantcv.morphology.analyze_stem**(*rgb_img, stem_objects, label="default"*) #### plantcv.morphology.check_cycles * pre v3.3: NA * post v3.3: cycle_img = **plantcv.morphology.check_cycles**(*skel_img*) +* post v3.11: cycle_img = **plantcv.morphology.check_cycles**(*skel_img, label="default"*) #### plantcv.morphology.find_branch_pts * pre v3.3: NA * post v3.3: branch_pts_img = **plantcv.morphology.find_branch_pts**(*skel_img, mask=None*) +* post v3.11: branch_pts_img = **plantcv.morphology.find_branch_pts**(*skel_img, mask=None, label="default"*) #### plantcv.morphology.find_tips * pre v3.3: NA * post v3.3: tip_img = **plantcv.morphology.find_tips**(*skel_img, mask=None*) +* post v3.11: tip_img = **plantcv.morphology.find_tips**(*skel_img, mask=None, label="default"*) #### plantcv.morphology.prune @@ -420,16 +441,19 @@ pages for more details on the input and output variable types. * pre v3.3: NA * post v3.3: labeled_img = **plantcv.morphology.segment_angle**(*segmented_img, objects*) +* post v3.11: labeled_img = **plantcv.morphology.segment_angle**(*segmented_img, objects, label="default"*) -#### plantcv.morphology.curvature +#### plantcv.morphology.segment_curvature * pre v3.3: NA * post v3.3: labeled_img = **plantcv.morphology.segment_curvature**(*segmented_img, objects*) +* post v3.11: labeled_img = **plantcv.morphology.segment_curvature**(*segmented_img, objects, label="default"*) #### plantcv.morphology.segment_euclidean_length * pre v3.3: NA * post v3.3: labeled_img = **plantcv.morphology.segment_euclidean_length**(*segmented_img, objects*) +* post v3.11: labeled_img = **plantcv.morphology.segment_euclidean_length**(*segmented_img, objects, label="default"*) #### plantcv.morphology.segment_id @@ -440,6 +464,7 @@ pages for more details on the input and output variable types. * pre v3.3: NA * post v3.3: labeled_img = **plantcv.morphology.segment_path_length**(*segmented_img, objects*) +* post v3.11: labeled_img = **plantcv.morphology.segment_path_length**(*segmented_img, objects, label="default"*) #### plantcv.morphology.segment_skeleton @@ -455,6 +480,7 @@ pages for more details on the input and output variable types. * pre v3.3: NA * post v3.3: labeled_img = **plantcv.morphology.segment_tangent_angle**(*segmented_img, objects, size*) +* post v3.11: labeled_img = **plantcv.morphology.segment_tangent_angle**(*segmented_img, objects, size, label="default"*) #### plantcv.morphology.skeletontize @@ -490,6 +516,7 @@ pages for more details on the input and output variable types. #### plantcv.photosynthesis.analyze_fvfm * pre v3.10: see plantcv.fluor_fvfm * post v3.10: analysis_images = **plantcv.photosynthesis.analyze_fvfm**(*fdark, fmin, fmax, mask, bins=256*) +* post v3.11: analysis_images = **plantcv.photosynthesis.analyze_fvfm**(*fdark, fmin, fmax, mask, bins=256, label="default"*) #### plantcv.photosynthesis.read_cropreporter @@ -546,6 +573,7 @@ pages for more details on the input and output variable types. * post v3.0dev2: marker_header, marker_data, analysis_images = **plantcv.report_size_marker_area**(*img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, thresh=None, filename=False*) * post v3.1: marker_header, marker_data, analysis_image = **plantcv.report_size_marker_area**(*img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, thresh=None*) * post v3.3: analysis_image = **plantcv.report_size_marker_area**(*img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, thresh=None*) +* post v3.11: analysis_image = **plantcv.report_size_marker_area**(*img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, thresh=None, label="default"*) #### plantcv.resize @@ -824,7 +852,7 @@ pages for more details on the input and output variable types. * post v3.0: df, start_coord, spacing = **plantcv.transform.find_color_card**(*rgb_img, threshold='adaptgauss', threshvalue=125, blurry=False, background='dark'*) * post v3.3: df, start_coord, spacing = **plantcv.transform.find_color_card**(*rgb_img, threshold_type='adaptgauss', threshvalue=125, blurry=False, background='dark'*) * post v3.9: df, start_coord, spacing = **plantcv.transform.find_color_card**(*rgb_img, threshold_type='adaptgauss', threshvalue=125, blurry=False, background='dark', record_chip_size='median'*) - +* post v3.11: df, start_coord, spacing = **plantcv.transform.find_color_card**(*rgb_img, threshold_type='adaptgauss', threshvalue=125, blurry=False, background='dark', record_chip_size='median', label="default"*) #### plantcv.transform.get_color_matrix @@ -892,6 +920,7 @@ pages for more details on the input and output variable types. * post v3.0dev2: watershed_header, watershed_data, analysis_images = **plantcv.watershed_segmentation**(*rgb_img, mask, distance=10, filename=False*) * post v3.1: watershed_header, watershed_data, analysis_images = **plantcv.watershed_segmentation**(*rgb_img, mask, distance=10*) * post v3.3: analysis_image = **plantcv.watershed_segmentation**(*rgb_img, mask, distance=10*) +* post v3.11: analysis_image = **plantcv.watershed_segmentation**(*rgb_img, mask, distance=10, label="default"*) #### plantcv.white_balance @@ -903,15 +932,18 @@ pages for more details on the input and output variable types. * pre v3.3: NA * post v3.3: in_bounds = **plantcv.within_frame**(*mask*) * post v3.8: in_bounds = **plantcv.within_frame**(*mask, border_width=1*) +* post v3.11: in_bounds = **plantcv.within_frame**(*mask, border_width=1, label="default"*) #### plantcv.x_axis_pseudolandmarks * pre v3.0dev2: device, top, bottom, center_v = **plantcv.x_axis_pseudolandmarks**(*obj, mask, img, device, debug=None*) * post v3.0dev2: top, bottom, center_v = **plantcv.x_axis_pseudolandmarks**(*obj, mask, img*) * post v3.2: top, bottom, center_v = **plantcv.x_axis_pseudolandmarks**(*img, obj, mask*) +* post v3.11: top, bottom, center_v = **plantcv.x_axis_pseudolandmarks**(*img, obj, mask, label="default"*) #### plantcv.y_axis_pseudolandmarks * pre v3.0dev2: device, left, right, center_h = **plantcv.y_axis_pseudolandmarks**(*obj, mask, img, device, debug=None*) * post v3.0dev2: left, right, center_h = **plantcv.y_axis_pseudolandmarks**(*obj, mask, img*) * post v3.2: left, right, center_h = **plantcv.y_axis_pseudolandmarks**(*img, obj, mask*) +* post v3.11: left, right, center_h = **plantcv.y_axis_pseudolandmarks**(*img, obj, mask, label="default"*) diff --git a/docs/vis_nir_tutorial.md b/docs/vis_nir_tutorial.md index 7fa2806c4..27ab3c89a 100644 --- a/docs/vis_nir_tutorial.md +++ b/docs/vis_nir_tutorial.md @@ -322,7 +322,8 @@ The next step is to analyze the plant object for traits such as [horizontal heig # img - RGB or grayscale image data # obj- Single or grouped contour object # mask - Binary image mask to use as mask for moments analysis - shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask) + # label - Optional label parameter, modifies the variable name of observations recorded + shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask, label="default") # Shape properties relative to user boundary line (optional) @@ -332,8 +333,9 @@ The next step is to analyze the plant object for traits such as [horizontal heig # mask - Binary mask of selected contours # line_position - Position of boundary line (a value of 0 would draw a line # through the bottom of the image) + # label - Optional label parameter, modifies the variable name of observations recorded boundary_img1 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, - line_position=1680) + line_position=1680, label="default") # Determine color properties: Histograms, Color Slices, output color analyzed histogram (optional) @@ -341,7 +343,8 @@ The next step is to analyze the plant object for traits such as [horizontal heig # rgb_img - RGB image data # mask - Binary mask of selected contours # hist_plot_type - None (default), 'all', 'rgb', 'lab', or 'hsv' - color_histogram = pcv.analyze_color(rgb_img=img, mask=kept_mask, hist_plot_type='all') + # label - Optional label parameter, modifies the variable name of observations recorded + color_histogram = pcv.analyze_color(rgb_img=img, mask=kept_mask, hist_plot_type='all', label="default") # Pseudocolor the grayscale image @@ -442,10 +445,11 @@ The next step is to [get the matching NIR](get_nir.md) image, [resize](transform # mask - Binary mask made from selected contours # bins - Number of classes to divide spectrum into # histplot - If True then plots histogram of intensity values, (default False) + # label - Optional label parameter, modifies the variable name of observations recorded nir_hist = pcv.analyze_nir_intensity(gray_img=nir2, mask=nir_combinedmask, - bins=256, histplot=True) + bins=256, histplot=True, label="default") - nir_shape_image = pcv.analyze_object(img=nir2, obj=nir_combined, mask=nir_combinedmask) + nir_shape_image = pcv.analyze_object(img=nir2, obj=nir_combined, mask=nir_combinedmask, label="NIR") # Save out the NIR histogram pcv.print_image(img=nir_hist, filename=os.path.join(pcv.params.debug_outdir, 'nirhist.png')) @@ -559,14 +563,14 @@ def main(): ############### Analysis ################ # Find shape properties, output shape image (optional) - shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask) + shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask, label="default") # Shape properties relative to user boundary line (optional) boundary_img1 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, - line_position=1680) + line_position=1680, label="default") # Determine color properties: Histograms, Color Slices, output color analyzed histogram (optional) - color_histogram = pcv.analyze_color(rgb_img=img, mask=kept_mask, hist_plot_type='all') + color_histogram = pcv.analyze_color(rgb_img=img, mask=kept_mask, hist_plot_type='all', label="default") # Pseudocolor the grayscale image pseudocolored_img = pcv.visualize.pseudocolor(gray_img=s, mask=kept_mask, cmap='jet') @@ -594,8 +598,8 @@ def main(): hierarchy=nir_hierarchy) # Analyze NIR intensity and object shape - nir_hist = pcv.analyze_nir_intensity(gray_img=nir2, mask=nir_combinedmask, bins=256, histplot=True) - nir_shape_image = pcv.analyze_object(img=nir2, obj=nir_combined, mask=nir_combinedmask) + nir_hist = pcv.analyze_nir_intensity(gray_img=nir2, mask=nir_combinedmask, bins=256, histplot=True, label="default") + nir_shape_image = pcv.analyze_object(img=nir2, obj=nir_combined, mask=nir_combinedmask, label="NIR") # Save out the NIR histogram pcv.print_image(nir_imgs[0], os.path.join(pcv.params.debug_outdir, 'nirhist.png')) diff --git a/docs/vis_tutorial.md b/docs/vis_tutorial.md index efb9142f2..1beeff57e 100644 --- a/docs/vis_tutorial.md +++ b/docs/vis_tutorial.md @@ -380,8 +380,9 @@ The next step is to analyze the plant object for traits such as [horizontal heig # Inputs: # img - RGB or grayscale image data # obj- Single or grouped contour object - # mask - Binary image mask to use as mask for moments analysis - shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask) + # mask - Binary image mask to use as mask for moments analysis + # label - Optional label parameter, modifies the variable name of observations recorded + shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask, label="default") # Shape properties relative to user boundary line (optional) @@ -390,9 +391,10 @@ The next step is to analyze the plant object for traits such as [horizontal heig # obj - Single or grouped contour object # mask - Binary mask of selected contours # line_position - Position of boundary line (a value of 0 would draw a line - # through the bottom of the image) + # through the bottom of the image) + # label - Optional label parameter, modifies the variable name of observations recorded boundary_img1 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, - line_position=1680) + line_position=1680, label="default") # Determine color properties: Histograms, Color Slices, output color analyzed histogram (optional) @@ -400,8 +402,9 @@ The next step is to analyze the plant object for traits such as [horizontal heig # rgb_img - RGB image data # mask - Binary mask of selected contours # hist_plot_type - None (default), 'all', 'rgb', 'lab', or 'hsv' - # This is the data to be printed to the SVG histogram file - color_histogram = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type='all') + # This is the data to be printed to the SVG histogram file + # label - Optional label parameter, modifies the variable name of observations recorded + color_histogram = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type='all', label="default") # Pseudocolor the grayscale image @@ -590,13 +593,13 @@ def main(): outfile = os.path.join(args.outdir, filename) # Find shape properties, output shape image (optional) - shape_imgs = pcv.analyze_object(img=img, obj=obj, mask=mask) + shape_imgs = pcv.analyze_object(img=img, obj=obj, mask=mask, label="default") # Shape properties relative to user boundary line (optional) - boundary_img1 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, line_position=1680) + boundary_img1 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, line_position=1680, label="default") # Determine color properties: Histograms, Color Slices, output color analyzed histogram (optional) - color_histogram = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type='all') + color_histogram = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type='all', label="default") # Pseudocolor the grayscale image pseudocolored_img = pcv.visualize.pseudocolor(gray_img=s, mask=mask, cmap='jet') diff --git a/docs/watershed.md b/docs/watershed.md index a66e50436..e74c1fcdf 100644 --- a/docs/watershed.md +++ b/docs/watershed.md @@ -5,7 +5,7 @@ For more information see [https://github.com/lsx1980/Leaf_count](https://github. This function uses the watershed algorithm to detect boundary of objects. Needs a mask file which specifies area which is object is white, and background is black. -**plantcv.watershed_segmentation**(*rgb_img, mask, distance=10*) +**plantcv.watershed_segmentation**(*rgb_img, mask, distance=10, label="default"*) **returns** analysis_image @@ -13,6 +13,7 @@ Needs a mask file which specifies area which is object is white, and background - rgb_img - RGB image data - mask - Binary image, single channel, object in white and background black - distance - Minimum distance of local maximum, lower values are more sensitive, and segments more objects (default: 10) + - label - Optional label parameter, modifies the variable name of observations recorded - **Context:** - Used to segment image into parts - Data automatically gets stored into the [Outputs class](outputs.md). Users can look at the data collected at any point during @@ -32,7 +33,7 @@ from plantcv import plantcv as pcv pcv.params.debug = "print" # Segment image with watershed function -analysis_image = pcv.watershed_segmentation(rgb_img=crop_img, mask=bin_mask, distance=10) +analysis_image = pcv.watershed_segmentation(rgb_img=crop_img, mask=bin_mask, distance=10, label="default") ``` diff --git a/docs/within_frame.md b/docs/within_frame.md index 4ed31cff3..fbc442d96 100644 --- a/docs/within_frame.md +++ b/docs/within_frame.md @@ -3,13 +3,14 @@ This function tests whether an object (defined as nonzero pixels in a mask) falls completely within the bounds of an image, or if it touches the edge. -**plantcv.within_frame**(*mask, border_width=1*) +**plantcv.within_frame**(*mask, border_width=1, label="default"*) **returns** in_bounds - **Parameters:** - mask - a single channel image (i.e. binary or greyscale) - border_width - distance from border of image considered out of frame (default = 1) + - label - Optional label parameter, modifies the variable name of observations recorded - **Context:** - This function could be used to test whether the plant has grown outside the field of view. diff --git a/docs/x_axis_pseudolandmarks.md b/docs/x_axis_pseudolandmarks.md index 8804870c2..8f70414f4 100644 --- a/docs/x_axis_pseudolandmarks.md +++ b/docs/x_axis_pseudolandmarks.md @@ -3,7 +3,7 @@ Divide plant object into twenty equidistant bins and assign pseudolandmark points based upon their actual (not scaled) position. Once this data is scaled this approach may provide some information regarding shape independent of size. -**plantcv.x_axis_pseudolandmarks**(*img, obj, mask*) +**plantcv.x_axis_pseudolandmarks**(*img, obj, mask, label="default"*) **returns** landmarks_on_top (top), landmarks_on_bottom (bottom), landmarks_at_center_along_the_vertical_axis (center_V) @@ -11,6 +11,7 @@ Once this data is scaled this approach may provide some information regarding sh - img - A copy of the original image (RGB or grayscale) generated using np.copy - obj - A contour of the plant object (this should be output from the object_composition.py fxn) - mask - This is a binary image. The object should be white and the background should be black. + - label - Optional label parameter, modifies the variable name of observations recorded - **Context:** - Used to identify a set of sixty equidistant landmarks on the horizontal axis. Once scaled these can be used for shape analysis. - **Output data stored:** Data ('top_lmk', 'bottom_lmk', 'center_v_lmk') automatically gets stored to the [`Outputs` class](outputs.md) when this function is ran. @@ -28,10 +29,10 @@ pcv.params.debug = "plot" # Identify a set of land mark points # Results in set of point values that may indicate tip points -top, bottom, center_v = pcv.x_axis_pseudolandmarks(img, obj, mask) +top, bottom, center_v = pcv.x_axis_pseudolandmarks(img=img, obj=obj, mask=mask, label="default") # Access data stored out from x_axis_pseudolandmarks -bottom_landmarks = pcv.outputs.observations['bottom_lmk']['value'] +bottom_landmarks = pcv.outputs.observations['default']['bottom_lmk']['value'] ``` diff --git a/docs/y_axis_pseudolandmarks.md b/docs/y_axis_pseudolandmarks.md index da548736c..4dca0ad7d 100644 --- a/docs/y_axis_pseudolandmarks.md +++ b/docs/y_axis_pseudolandmarks.md @@ -4,7 +4,7 @@ Divide plant object into twenty equidistant bins along the y-axis and assign pse actual (not scaled) position. Once this data is scaled this approach may provide some information regarding shape independent of size. -**plantcv.y_axis_pseudolandmarks**(*img, obj, mask*) +**plantcv.y_axis_pseudolandmarks**(*img, obj, mask, label="default"*) **returns** landmarks_on_leftside (left), landmarks_on_right (right), landmarks_at_center_along_the_horizontal_axis (center_h) @@ -12,6 +12,7 @@ independent of size. - img - A copy of the original image (RGB or grayscale) generated using np.copy - obj - A contour of the plant object (this should be output from the object_composition.py fxn) - mask - This is a binary image. The object should be white and the background should be black. + - label - Optional label parameter, modifies the variable name of observations recorded - **Context:** - Used to identify a set of sixty equidistant landmarks on the vertical axis. Once scaled these can be used for shape analysis. - **Output data stored:** Data ('left_lmk', 'right_lmk', 'center_h_lmk') automatically gets stored to the [`Outputs` class](outputs.md) when this function is ran. @@ -31,10 +32,10 @@ pcv.params.debug = "plot" # Identify a set of land mark points # Results in set of point values that may indicate tip points -left, right, center_h = pcv.y_axis_pseudolandmarks(img, obj, mask) +left, right, center_h = pcv.y_axis_pseudolandmarks(img=img, obj=obj, mask=mask, label="default") # Access data stored out from y_axis_pseudolandmarks -left_landmarks = pcv.outputs.observations['left_lmk']['value'] +left_landmarks = pcv.outputs.observations['default']['left_lmk']['value'] ``` diff --git a/plantcv/parallel/process_results.py b/plantcv/parallel/process_results.py index 73c907aa9..7feb4d7e3 100644 --- a/plantcv/parallel/process_results.py +++ b/plantcv/parallel/process_results.py @@ -42,9 +42,10 @@ def process_results(job_dir, json_file): for var in obs["metadata"]: data["variables"][var] = {"category": "metadata", "datatype": ""} # Keep track of all observations variables stored - for othervars in obs["observations"]: - data["variables"][othervars] = {"category": "observations", - "datatype": obs["observations"][othervars]["datatype"]} + for sample in obs["observations"]: + for othervars in obs["observations"][sample]: + data["variables"][othervars] = {"category": "observations", + "datatype": obs["observations"][sample][othervars]["datatype"]} # Write out json file with info from all images with open(json_file, 'w') as datafile: diff --git a/plantcv/plantcv/__init__.py b/plantcv/plantcv/__init__.py index 0396acd9f..db681f0a2 100644 --- a/plantcv/plantcv/__init__.py +++ b/plantcv/plantcv/__init__.py @@ -8,7 +8,8 @@ class Params: """PlantCV parameters class.""" def __init__(self, device=0, debug=None, debug_outdir=".", line_thickness=5, dpi=100, text_size=0.55, - text_thickness=2, marker_size=60, color_scale="gist_rainbow", color_sequence="sequential", saved_color_scale=None): + text_thickness=2, marker_size=60, color_scale="gist_rainbow", color_sequence="sequential", + saved_color_scale=None): """Initialize parameters. Keyword arguments/parameters: @@ -66,9 +67,10 @@ def clear(self): self.observations = {} # Method to add observation to outputs - def add_observation(self, variable, trait, method, scale, datatype, value, label): + def add_observation(self, sample, variable, trait, method, scale, datatype, value, label): """ Keyword arguments/parameters: + sample = Sample name. Used to distinguish between multiple samples variable = A local unique identifier of a variable, e.g. a short name, that is a key linking the definitions of variables with observations. trait = A name of the trait mapped to an external ontology; if there is no exact mapping, an informative @@ -83,6 +85,7 @@ def add_observation(self, variable, trait, method, scale, datatype, value, label label = The label for each value (most useful when the data is a frequency table as in hue, or other tables) + :param sample: str :param variable: str :param trait: str :param method: str @@ -91,6 +94,7 @@ def add_observation(self, variable, trait, method, scale, datatype, value, label :param value: :param label: """ + self.sample = sample self.variable = variable self.trait = trait self.method = method @@ -99,7 +103,11 @@ def add_observation(self, variable, trait, method, scale, datatype, value, label self.value = value self.label = label - self.observations[variable] = { + # Create an empty dictionary for the sample if it does not exist + if sample not in self.observations: + self.observations[sample] = {} + # Save the observation for the sample and variable + self.observations[sample][variable] = { "trait": trait, "method": method, "scale": scale, diff --git a/plantcv/plantcv/acute_vertex.py b/plantcv/plantcv/acute_vertex.py index 6fe6941c2..3bf5a08e2 100755 --- a/plantcv/plantcv/acute_vertex.py +++ b/plantcv/plantcv/acute_vertex.py @@ -10,7 +10,7 @@ from plantcv.plantcv import outputs -def acute_vertex(img, obj, win, thresh, sep): +def acute_vertex(img, obj, win, thresh, sep, label="default"): """acute_vertex: identify corners/acute angles of an object For each point in contour, get a point before (pre) and after (post) the point of interest, @@ -23,6 +23,8 @@ def acute_vertex(img, obj, win, thresh, sep): thresh = an threshold to set for acuteness; keep points with an angle more acute than the threshold (a value of 15 worked well for sample image) sep = the number of contour points to search within for the most acute value + label = optional label parameter, modifies the variable name of observations recorded + Returns: acute_points = list of acute points @@ -33,6 +35,7 @@ def acute_vertex(img, obj, win, thresh, sep): :param win: int :param thresh: int :param sep: int + :param label: str :return acute_points: ndarray :return img2: ndarray """ @@ -102,7 +105,7 @@ def acute_vertex(img, obj, win, thresh, sep): plot_image(img2) # Store into global measurements - outputs.add_observation(variable='tip_coordinates', trait='tip coordinates', + outputs.add_observation(sample=label, variable='tip_coordinates', trait='tip coordinates', method='plantcv.plantcv.acute_vertex', scale='none', datatype=list, value=acute_points, label='none') diff --git a/plantcv/plantcv/analyze_bound_horizontal.py b/plantcv/plantcv/analyze_bound_horizontal.py index f3b774bfc..6079395b6 100755 --- a/plantcv/plantcv/analyze_bound_horizontal.py +++ b/plantcv/plantcv/analyze_bound_horizontal.py @@ -9,7 +9,7 @@ from plantcv.plantcv import outputs -def analyze_bound_horizontal(img, obj, mask, line_position): +def analyze_bound_horizontal(img, obj, mask, line_position, label="default"): """User-input boundary line tool Inputs: @@ -17,6 +17,7 @@ def analyze_bound_horizontal(img, obj, mask, line_position): obj = single or grouped contour object mask = Binary mask made from selected contours line_position = position of boundary line (a value of 0 would draw the line through the top of the image) + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_images = list of output images @@ -25,6 +26,7 @@ def analyze_bound_horizontal(img, obj, mask, line_position): :param obj: list :param mask: numpy.ndarray :param line_position: int + :param label: str :return analysis_images: list """ @@ -144,25 +146,26 @@ def analyze_bound_horizontal(img, obj, mask, line_position): plot_image(wback) plot_image(ori_img) - outputs.add_observation(variable='horizontal_reference_position', trait='horizontal reference position', + outputs.add_observation(sample=label, variable='horizontal_reference_position', + trait='horizontal reference position', method='plantcv.plantcv.analyze_bound_horizontal', scale='none', datatype=int, value=line_position, label='none') - outputs.add_observation(variable='height_above_reference', trait='height above reference', + outputs.add_observation(sample=label, variable='height_above_reference', trait='height above reference', method='plantcv.plantcv.analyze_bound_horizontal', scale='pixels', datatype=int, value=height_above_bound, label='pixels') - outputs.add_observation(variable='height_below_reference', trait='height_below_reference', + outputs.add_observation(sample=label, variable='height_below_reference', trait='height_below_reference', method='plantcv.plantcv.analyze_bound_horizontal', scale='pixels', datatype=int, value=height_below_bound, label='pixels') - outputs.add_observation(variable='area_above_reference', trait='area above reference', + outputs.add_observation(sample=label, variable='area_above_reference', trait='area above reference', method='plantcv.plantcv.analyze_bound_horizontal', scale='pixels', datatype=int, value=above_bound_area, label='pixels') - outputs.add_observation(variable='percent_area_above_reference', trait='percent area above reference', + outputs.add_observation(sample=label, variable='percent_area_above_reference', trait='percent area above reference', method='plantcv.plantcv.analyze_bound_horizontal', scale='none', datatype=float, value=percent_bound_area_above, label='none') - outputs.add_observation(variable='area_below_reference', trait='area below reference', + outputs.add_observation(sample=label, variable='area_below_reference', trait='area below reference', method='plantcv.plantcv.analyze_bound_horizontal', scale='pixels', datatype=int, value=below_bound_area, label='pixels') - outputs.add_observation(variable='percent_area_below_reference', trait='percent area below reference', + outputs.add_observation(sample=label, variable='percent_area_below_reference', trait='percent area below reference', method='plantcv.plantcv.analyze_bound_horizontal', scale='none', datatype=float, value=percent_bound_area_below, label='none') diff --git a/plantcv/plantcv/analyze_bound_vertical.py b/plantcv/plantcv/analyze_bound_vertical.py index e4868a5d7..7adfd863b 100755 --- a/plantcv/plantcv/analyze_bound_vertical.py +++ b/plantcv/plantcv/analyze_bound_vertical.py @@ -9,7 +9,7 @@ from plantcv.plantcv import outputs -def analyze_bound_vertical(img, obj, mask, line_position): +def analyze_bound_vertical(img, obj, mask, line_position, label="default"): """User-input boundary line tool Inputs: @@ -19,6 +19,7 @@ def analyze_bound_vertical(img, obj, mask, line_position): shape_header = pass shape header data to function shape_data = pass shape data so that analyze_bound data can be appended to it line_position = position of boundary line (a value of 0 would draw the line through the left side of the image) + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_images = output images @@ -27,6 +28,7 @@ def analyze_bound_vertical(img, obj, mask, line_position): :param obj: list :param mask: numpy.ndarray :param line_position: int + :param label: str :return analysis_images: list """ ori_img = np.copy(img) @@ -144,27 +146,28 @@ def analyze_bound_vertical(img, obj, mask, line_position): plot_image(wback) plot_image(ori_img) - outputs.add_observation(variable='vertical_reference_position', trait='vertical reference position', + outputs.add_observation(sample=label, variable='vertical_reference_position', trait='vertical reference position', method='plantcv.plantcv.analyze_bound_vertical', scale='none', datatype=int, value=line_position, label='none') - outputs.add_observation(variable='width_left_reference', trait='width left of reference', + outputs.add_observation(sample=label, variable='width_left_reference', trait='width left of reference', method='plantcv.plantcv.analyze_bound_vertical', scale='pixels', datatype=int, value=width_left_bound, label='pixels') - outputs.add_observation(variable='width_right_reference', trait='width right of reference', + outputs.add_observation(sample=label, variable='width_right_reference', trait='width right of reference', method='plantcv.plantcv.analyze_bound_vertical', scale='pixels', datatype=int, value=width_right_bound, label='pixels') - outputs.add_observation(variable='area_left_reference', trait='area left of reference', + outputs.add_observation(sample=label, variable='area_left_reference', trait='area left of reference', method='plantcv.plantcv.analyze_bound_vertical', scale='pixels', datatype=int, value=left_bound_area, label='pixels') - outputs.add_observation(variable='percent_area_left_reference', trait='percent area left of reference', - method='plantcv.plantcv.analyze_bound_vertical', scale='none', datatype=float, + outputs.add_observation(sample=label, variable='percent_area_left_reference', + trait='percent area left of reference', method='plantcv.plantcv.analyze_bound_vertical', + scale='none', datatype=float, value=percent_bound_area_left, label='none') - outputs.add_observation(variable='area_right_reference', trait='area right of reference', + outputs.add_observation(sample=label, variable='area_right_reference', trait='area right of reference', method='plantcv.plantcv.analyze_bound_vertical', scale='pixels', datatype=int, value=right_bound_area, label='pixels') - outputs.add_observation(variable='percent_area_right_reference', trait='percent area right of reference', - method='plantcv.plantcv.analyze_bound_vertical', scale='none', datatype=float, - value=percent_bound_area_right, label='none') + outputs.add_observation(sample=label, variable='percent_area_right_reference', + trait='percent area right of reference', method='plantcv.plantcv.analyze_bound_vertical', + scale='none', datatype=float, value=percent_bound_area_right, label='none') # Store images outputs.images.append(analysis_images) diff --git a/plantcv/plantcv/analyze_color.py b/plantcv/plantcv/analyze_color.py index df6bc819d..720977d19 100755 --- a/plantcv/plantcv/analyze_color.py +++ b/plantcv/plantcv/analyze_color.py @@ -9,12 +9,13 @@ from plantcv.plantcv import outputs -def analyze_color(rgb_img, mask, hist_plot_type=None): +def analyze_color(rgb_img, mask, hist_plot_type=None, label="default"): """Analyze the color properties of an image object Inputs: rgb_img = RGB image data mask = Binary mask made from selected contours hist_plot_type = None, 'all', 'rgb','lab' or 'hsv' + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_image = histogram output @@ -22,6 +23,7 @@ def analyze_color(rgb_img, mask, hist_plot_type=None): :param rgb_img: numpy.ndarray :param mask: numpy.ndarray :param hist_plot_type: str + :param label: str :return analysis_images: list """ if len(np.shape(rgb_img)) < 3: @@ -158,48 +160,49 @@ def analyze_color(rgb_img, mask, hist_plot_type=None): percent_values = [round((i / 255) * 100, 2) for i in range(0, 256)] # Diverging values on a -128 to 127 scale (green-magenta and blue-yellow) diverging_values = [i for i in range(-128, 128)] + if hist_plot_type is not None: if hist_plot_type.upper() == 'RGB' or hist_plot_type.upper() == 'ALL': - outputs.add_observation(variable='blue_frequencies', trait='blue frequencies', + outputs.add_observation(sample=label, variable='blue_frequencies', trait='blue frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["b"]["hist"], label=rgb_values) - outputs.add_observation(variable='green_frequencies', trait='green frequencies', + outputs.add_observation(sample=label, variable='green_frequencies', trait='green frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["g"]["hist"], label=rgb_values) - outputs.add_observation(variable='red_frequencies', trait='red frequencies', + outputs.add_observation(sample=label, variable='red_frequencies', trait='red frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["r"]["hist"], label=rgb_values) if hist_plot_type.upper() == 'LAB' or hist_plot_type.upper() == 'ALL': - outputs.add_observation(variable='lightness_frequencies', trait='lightness frequencies', + outputs.add_observation(sample=label, variable='lightness_frequencies', trait='lightness frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["l"]["hist"], label=percent_values) - outputs.add_observation(variable='green-magenta_frequencies', trait='green-magenta frequencies', + outputs.add_observation(sample=label, variable='green-magenta_frequencies', trait='green-magenta frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["m"]["hist"], label=diverging_values) - outputs.add_observation(variable='blue-yellow_frequencies', trait='blue-yellow frequencies', + outputs.add_observation(sample=label, variable='blue-yellow_frequencies', trait='blue-yellow frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["y"]["hist"], label=diverging_values) if hist_plot_type.upper() == 'HSV' or hist_plot_type.upper() == 'ALL': - outputs.add_observation(variable='hue_frequencies', trait='hue frequencies', + outputs.add_observation(sample=label, variable='hue_frequencies', trait='hue frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["h"]["hist"][0:180], label=hue_values) - outputs.add_observation(variable='saturation_frequencies', trait='saturation frequencies', + outputs.add_observation(sample=label, variable='saturation_frequencies', trait='saturation frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["s"]["hist"], label=percent_values) - outputs.add_observation(variable='value_frequencies', trait='value frequencies', + outputs.add_observation(sample=label, variable='value_frequencies', trait='value frequencies', method='plantcv.plantcv.analyze_color', scale='frequency', datatype=list, value=histograms["v"]["hist"], label=percent_values) # Always save hue stats - outputs.add_observation(variable='hue_circular_mean', trait='hue circular mean', + outputs.add_observation(sample=label, variable='hue_circular_mean', trait='hue circular mean', method='plantcv.plantcv.analyze_color', scale='degrees', datatype=float, value=hue_circular_mean, label='degrees') - outputs.add_observation(variable='hue_circular_std', trait='hue circular standard deviation', + outputs.add_observation(sample=label, variable='hue_circular_std', trait='hue circular standard deviation', method='plantcv.plantcv.analyze_color', scale='degrees', datatype=float, value=hue_circular_std, label='degrees') - outputs.add_observation(variable='hue_median', trait='hue median', + outputs.add_observation(sample=label, variable='hue_median', trait='hue median', method='plantcv.plantcv.analyze_color', scale='degrees', datatype=float, value=hue_median, label='degrees') diff --git a/plantcv/plantcv/analyze_nir_intensity.py b/plantcv/plantcv/analyze_nir_intensity.py index 095cf1e78..106bfd922 100755 --- a/plantcv/plantcv/analyze_nir_intensity.py +++ b/plantcv/plantcv/analyze_nir_intensity.py @@ -12,7 +12,7 @@ from plantcv.plantcv import outputs -def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False): +def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False, label="default"): """This function calculates the intensity of each pixel associated with the plant and writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. @@ -21,6 +21,7 @@ def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False): mask = Binary mask made from selected contours bins = number of classes to divide spectrum into histplot = if True plots histogram of intensity values + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_images = NIR histogram image @@ -29,6 +30,7 @@ def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False): :param mask: numpy array :param bins: int :param histplot: bool + :param label: str :return analysis_images: plotnine ggplot """ # apply plant shaped mask to image @@ -91,16 +93,16 @@ def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False): elif params.debug == "plot": print(fig_hist) - outputs.add_observation(variable='nir_frequencies', trait='near-infrared frequencies', + outputs.add_observation(sample=label, variable='nir_frequencies', trait='near-infrared frequencies', method='plantcv.plantcv.analyze_nir_intensity', scale='frequency', datatype=list, value=hist_nir, label=bin_labels) - outputs.add_observation(variable='nir_mean', trait='near-infrared mean', + outputs.add_observation(sample=label, variable='nir_mean', trait='near-infrared mean', method='plantcv.plantcv.analyze_nir_intensity', scale='none', datatype=float, value=masked_nir_mean, label='none') - outputs.add_observation(variable='nir_median', trait='near-infrared median', + outputs.add_observation(sample=label, variable='nir_median', trait='near-infrared median', method='plantcv.plantcv.analyze_nir_intensity', scale='none', datatype=float, value=masked_nir_median, label='none') - outputs.add_observation(variable='nir_stdev', trait='near-infrared standard deviation', + outputs.add_observation(sample=label, variable='nir_stdev', trait='near-infrared standard deviation', method='plantcv.plantcv.analyze_nir_intensity', scale='none', datatype=float, value=masked_nir_std, label='none') diff --git a/plantcv/plantcv/analyze_object.py b/plantcv/plantcv/analyze_object.py index 79770bd3a..1e166e814 100755 --- a/plantcv/plantcv/analyze_object.py +++ b/plantcv/plantcv/analyze_object.py @@ -10,13 +10,14 @@ from plantcv.plantcv import within_frame -def analyze_object(img, obj, mask): +def analyze_object(img, obj, mask, label="default"): """Outputs numeric properties for an input object (contour or grouped contours). Inputs: img = RGB or grayscale image data for plotting obj = single or grouped contour object mask = Binary image to use as mask + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_images = list of output images @@ -24,6 +25,7 @@ def analyze_object(img, obj, mask): :param img: numpy.ndarray :param obj: list :param mask: numpy.ndarray + :param label: str :return analysis_images: list """ # Valid objects can only be analyzed if they have >= 5 vertices @@ -46,7 +48,7 @@ def analyze_object(img, obj, mask): background2 = np.zeros(size1, dtype=np.uint8) # Check is object is touching image boundaries (QC) - in_bounds = within_frame(mask) + in_bounds = within_frame(mask=mask, label=label) # Convex Hull hull = cv2.convexHull(obj) @@ -156,49 +158,49 @@ def analyze_object(img, obj, mask): else: pass - outputs.add_observation(variable='area', trait='area', + outputs.add_observation(sample=label, variable='area', trait='area', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=area, label='pixels') - outputs.add_observation(variable='convex_hull_area', trait='convex hull area', + outputs.add_observation(sample=label, variable='convex_hull_area', trait='convex hull area', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=hull_area, label='pixels') - outputs.add_observation(variable='solidity', trait='solidity', + outputs.add_observation(sample=label, variable='solidity', trait='solidity', method='plantcv.plantcv.analyze_object', scale='none', datatype=float, value=solidity, label='none') - outputs.add_observation(variable='perimeter', trait='perimeter', + outputs.add_observation(sample=label, variable='perimeter', trait='perimeter', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=perimeter, label='pixels') - outputs.add_observation(variable='width', trait='width', + outputs.add_observation(sample=label, variable='width', trait='width', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=width, label='pixels') - outputs.add_observation(variable='height', trait='height', + outputs.add_observation(sample=label, variable='height', trait='height', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=height, label='pixels') - outputs.add_observation(variable='longest_path', trait='longest path', + outputs.add_observation(sample=label, variable='longest_path', trait='longest path', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=caliper_length, label='pixels') - outputs.add_observation(variable='center_of_mass', trait='center of mass', + outputs.add_observation(sample=label, variable='center_of_mass', trait='center of mass', method='plantcv.plantcv.analyze_object', scale='none', datatype=tuple, value=(cmx, cmy), label='none') - outputs.add_observation(variable='convex_hull_vertices', trait='convex hull vertices', + outputs.add_observation(sample=label, variable='convex_hull_vertices', trait='convex hull vertices', method='plantcv.plantcv.analyze_object', scale='none', datatype=int, value=hull_vertices, label='none') - outputs.add_observation(variable='object_in_frame', trait='object in frame', + outputs.add_observation(sample=label, variable='object_in_frame', trait='object in frame', method='plantcv.plantcv.analyze_object', scale='none', datatype=bool, value=in_bounds, label='none') - outputs.add_observation(variable='ellipse_center', trait='ellipse center', + outputs.add_observation(sample=label, variable='ellipse_center', trait='ellipse center', method='plantcv.plantcv.analyze_object', scale='none', datatype=tuple, value=(center[0], center[1]), label='none') - outputs.add_observation(variable='ellipse_major_axis', trait='ellipse major axis length', + outputs.add_observation(sample=label, variable='ellipse_major_axis', trait='ellipse major axis length', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=major_axis_length, label='pixels') - outputs.add_observation(variable='ellipse_minor_axis', trait='ellipse minor axis length', + outputs.add_observation(sample=label, variable='ellipse_minor_axis', trait='ellipse minor axis length', method='plantcv.plantcv.analyze_object', scale='pixels', datatype=int, value=minor_axis_length, label='pixels') - outputs.add_observation(variable='ellipse_angle', trait='ellipse major axis angle', + outputs.add_observation(sample=label, variable='ellipse_angle', trait='ellipse major axis angle', method='plantcv.plantcv.analyze_object', scale='degrees', datatype=float, value=float(angle), label='degrees') - outputs.add_observation(variable='ellipse_eccentricity', trait='ellipse eccentricity', + outputs.add_observation(sample=label, variable='ellipse_eccentricity', trait='ellipse eccentricity', method='plantcv.plantcv.analyze_object', scale='none', datatype=float, value=float(eccentricity), label='none') diff --git a/plantcv/plantcv/analyze_thermal_values.py b/plantcv/plantcv/analyze_thermal_values.py index 38d15f7ab..43fb86238 100755 --- a/plantcv/plantcv/analyze_thermal_values.py +++ b/plantcv/plantcv/analyze_thermal_values.py @@ -10,7 +10,7 @@ from plantcv.plantcv.threshold import binary as binary_threshold -def analyze_thermal_values(thermal_array, mask, histplot=False): +def analyze_thermal_values(thermal_array, mask, histplot=False, label="default"): """This extracts the thermal values of each pixel writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. @@ -19,6 +19,7 @@ def analyze_thermal_values(thermal_array, mask, histplot=False): array = numpy array of thermal values mask = Binary mask made from selected contours histplot = if True plots histogram of intensity values + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_img = output image @@ -26,6 +27,7 @@ def analyze_thermal_values(thermal_array, mask, histplot=False): :param thermal_array: numpy.ndarray :param mask: numpy.ndarray :param histplot: bool + :param label: str :return analysis_img: ggplot """ max_value = np.amax(thermal_array) @@ -58,19 +60,19 @@ def analyze_thermal_values(thermal_array, mask, histplot=False): mediantemp = np.median(masked_thermal) # Store data into outputs class - outputs.add_observation(variable='max_temp', trait='maximum temperature', + outputs.add_observation(sample=label, variable='max_temp', trait='maximum temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=maxtemp, label='degrees') - outputs.add_observation(variable='min_temp', trait='minimum temperature', + outputs.add_observation(sample=label, variable='min_temp', trait='minimum temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=mintemp, label='degrees') - outputs.add_observation(variable='mean_temp', trait='mean temperature', + outputs.add_observation(sample=label, variable='mean_temp', trait='mean temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=avgtemp, label='degrees') - outputs.add_observation(variable='median_temp', trait='median temperature', + outputs.add_observation(sample=label, variable='median_temp', trait='median temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=mediantemp, label='degrees') - outputs.add_observation(variable='thermal_frequencies', trait='thermal frequencies', + outputs.add_observation(sample=label, variable='thermal_frequencies', trait='thermal frequencies', method='plantcv.plantcv.analyze_thermal_values', scale='frequency', datatype=list, value=hist_percent, label=bin_labels) analysis_img = None diff --git a/plantcv/plantcv/hyperspectral/analyze_index.py b/plantcv/plantcv/hyperspectral/analyze_index.py index b77a858d3..fd5b55dbe 100644 --- a/plantcv/plantcv/hyperspectral/analyze_index.py +++ b/plantcv/plantcv/hyperspectral/analyze_index.py @@ -12,7 +12,7 @@ from plotnine import ggplot, aes, geom_line, scale_x_continuous -def analyze_index(index_array, mask, histplot=False, bins=100, min_bin=0, max_bin=1): +def analyze_index(index_array, mask, histplot=False, bins=100, min_bin=0, max_bin=1, label="default"): """This extracts the hyperspectral index statistics and writes the values as observations out to the Outputs class. @@ -23,6 +23,8 @@ def analyze_index(index_array, mask, histplot=False, bins=100, min_bin=0, max_bi bins = optional, number of classes to divide spectrum into min_bin = optional, minimum bin value ("auto" or user input minimum value) max_bin = optional, maximum bin value ("auto" or user input maximum value) + label = optional label parameter, modifies the variable name of observations recorded + :param index_array: __main__.Spectral_data @@ -31,6 +33,7 @@ def analyze_index(index_array, mask, histplot=False, bins=100, min_bin=0, max_bi :param bins: int :param max_bin: float, str :param min_bin: float, str + :param label: str :return analysis_image: ggplot, None """ params.device += 1 @@ -104,24 +107,24 @@ def analyze_index(index_array, mask, histplot=False, bins=100, min_bin=0, max_bi elif params.debug == 'plot': print(fig_hist) - outputs.add_observation(variable='mean_' + index_array.array_type, + outputs.add_observation(sample=label, variable='mean_' + index_array.array_type, trait='Average ' + index_array.array_type + ' reflectance', method='plantcv.plantcv.hyperspectral.analyze_index', scale='reflectance', datatype=float, value=float(index_mean), label='none') - outputs.add_observation(variable='med_' + index_array.array_type, + outputs.add_observation(sample=label, variable='med_' + index_array.array_type, trait='Median ' + index_array.array_type + ' reflectance', method='plantcv.plantcv.hyperspectral.analyze_index', scale='reflectance', datatype=float, value=float(index_median), label='none') - outputs.add_observation(variable='std_' + index_array.array_type, + outputs.add_observation(sample=label, variable='std_' + index_array.array_type, trait='Standard deviation ' + index_array.array_type + ' reflectance', method='plantcv.plantcv.hyperspectral.analyze_index', scale='reflectance', datatype=float, value=float(index_std), label='none') - outputs.add_observation(variable='index_frequencies_' + index_array.array_type, trait='index frequencies', - method='plantcv.plantcv.analyze_index', scale='frequency', datatype=list, - value=hist_percent, label=bin_labels) + outputs.add_observation(sample=label, variable='index_frequencies_' + index_array.array_type, + trait='index frequencies', method='plantcv.plantcv.analyze_index', scale='frequency', + datatype=list, value=hist_percent, label=bin_labels) if params.debug == "plot": plot_image(masked_array) diff --git a/plantcv/plantcv/hyperspectral/analyze_spectral.py b/plantcv/plantcv/hyperspectral/analyze_spectral.py index 3470a21ec..a6b52a7b3 100644 --- a/plantcv/plantcv/hyperspectral/analyze_spectral.py +++ b/plantcv/plantcv/hyperspectral/analyze_spectral.py @@ -8,7 +8,7 @@ from plotnine import ggplot, aes, geom_line, scale_x_continuous -def analyze_spectral(array, mask, histplot=True): +def analyze_spectral(array, mask, histplot=True, label="default"): """This extracts the hyperspectral reflectance values of each pixel writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. @@ -17,6 +17,7 @@ def analyze_spectral(array, mask, histplot=True): array = Hyperspectral data instance mask = Binary mask made from selected contours histplot = if True plots histogram of reflectance intensity values + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_img = output image @@ -24,6 +25,7 @@ def analyze_spectral(array, mask, histplot=True): :param array: __main__.Spectral_data :param mask: numpy array :param histplot: bool + :param label: str :return analysis_img: ggplot """ params.device += 1 @@ -68,28 +70,29 @@ def analyze_spectral(array, mask, histplot=True): wavelength_labels.append(i) # Store data into outputs class - outputs.add_observation(variable='global_mean_reflectance', trait='global mean reflectance', + outputs.add_observation(sample=label, variable='global_mean_reflectance', trait='global mean reflectance', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='reflectance', datatype=float, value=float(avg_reflectance), label='reflectance') - outputs.add_observation(variable='global_median_reflectance', trait='global median reflectance', + outputs.add_observation(sample=label, variable='global_median_reflectance', trait='global median reflectance', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='reflectance', datatype=float, value=float(median_reflectance), label='reflectance') - outputs.add_observation(variable='global_spectral_std', trait='pixel-wise standard deviation per band', + outputs.add_observation(sample=label, variable='global_spectral_std', + trait='pixel-wise standard deviation per band', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='None', datatype=float, value=float(std_reflectance), label='reflectance') - outputs.add_observation(variable='global_spectral_std', trait='pixel-wise standard deviation ', + outputs.add_observation(sample=label, variable='global_spectral_std', trait='pixel-wise standard deviation ', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='None', datatype=float, value=float(std_reflectance), label='reflectance') - outputs.add_observation(variable='max_reflectance', trait='maximum reflectance per band', + outputs.add_observation(sample=label, variable='max_reflectance', trait='maximum reflectance per band', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='reflectance', datatype=list, value=new_max_per_band, label=wavelength_labels) - outputs.add_observation(variable='min_reflectance', trait='minimum reflectance per band', + outputs.add_observation(sample=label, variable='min_reflectance', trait='minimum reflectance per band', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='reflectance', datatype=list, value=new_min_per_band, label=wavelength_labels) - outputs.add_observation(variable='spectral_std', trait='pixel-wise standard deviation per band', + outputs.add_observation(sample=label, variable='spectral_std', trait='pixel-wise standard deviation per band', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='None', datatype=list, value=new_std_per_band, label=wavelength_labels) - outputs.add_observation(variable='spectral_frequencies', trait='spectral frequencies', + outputs.add_observation(sample=label, variable='spectral_frequencies', trait='spectral frequencies', method='plantcv.plantcv.hyperspectral.analyze_spectral', scale='frequency', datatype=list, value=new_freq, label=wavelength_labels) diff --git a/plantcv/plantcv/landmark_reference_pt_dist.py b/plantcv/plantcv/landmark_reference_pt_dist.py index 5d8f0e4b7..0f2b8acc8 100755 --- a/plantcv/plantcv/landmark_reference_pt_dist.py +++ b/plantcv/plantcv/landmark_reference_pt_dist.py @@ -7,7 +7,7 @@ from plantcv.plantcv import outputs -def landmark_reference_pt_dist(points_r, centroid_r, bline_r): +def landmark_reference_pt_dist(points_r, centroid_r, bline_r, label="default"): """landmark_reference_pt_dist For each point in contour, get a point before (pre) and after (post) the point of interest. @@ -17,10 +17,12 @@ def landmark_reference_pt_dist(points_r, centroid_r, bline_r): points_r = a set of rescaled points (basically the output of the acute_vertex fxn after the scale_features fxn) centroid_r = a tuple that contains the rescaled centroid coordinates bline_r = a tuple that contains the rescaled boundary line - centroid coordinates + label = optional label parameter, modifies the variable name of observations recorded :param points_r: ndarray :param centroid_r: tuple :param bline_r: tuple + :param label: str """ params.device += 1 @@ -92,27 +94,29 @@ def landmark_reference_pt_dist(points_r, centroid_r, bline_r): euc_ave_b = np.mean(euc_dist_b) ang_ave_b = np.mean(angles_b) - outputs.add_observation(variable='vert_ave_c', trait='average vertical distance from centroid', + outputs.add_observation(sample=label, variable='vert_ave_c', trait='average vertical distance from centroid', method='plantcv.plantcv.landmark_reference_pt_dist', scale='pixels', datatype=float, value=vert_ave_c, label='pixels') - outputs.add_observation(variable='hori_ave_c', trait='average horizontal distance from centeroid', + outputs.add_observation(sample=label, variable='hori_ave_c', trait='average horizontal distance from centeroid', method='plantcv.plantcv.landmark_reference_pt_dist', scale='pixels', datatype=float, value=hori_ave_c, label='pixels') - outputs.add_observation(variable='euc_ave_c', trait='average euclidean distance from centroid', + outputs.add_observation(sample=label, variable='euc_ave_c', trait='average euclidean distance from centroid', method='plantcv.plantcv.landmark_reference_pt_dist', scale='pixels', datatype=float, value=euc_ave_c, label='pixels') - outputs.add_observation(variable='ang_ave_c', trait='average angle between landmark point and centroid', + outputs.add_observation(sample=label, variable='ang_ave_c', + trait='average angle between landmark point and centroid', method='plantcv.plantcv.landmark_reference_pt_dist', scale='degrees', datatype=float, value=ang_ave_c, label='degrees') - outputs.add_observation(variable='vert_ave_b', trait='average vertical distance from baseline', + outputs.add_observation(sample=label, variable='vert_ave_b', trait='average vertical distance from baseline', method='plantcv.plantcv.landmark_reference_pt_dist', scale='pixels', datatype=float, value=vert_ave_b, label='pixels') - outputs.add_observation(variable='hori_ave_b', trait='average horizontal distance from baseline', + outputs.add_observation(sample=label, variable='hori_ave_b', trait='average horizontal distance from baseline', method='plantcv.plantcv.landmark_reference_pt_dist', scale='pixels', datatype=float, value=hori_ave_b, label='pixels') - outputs.add_observation(variable='euc_ave_b', trait='average euclidean distance from baseline', + outputs.add_observation(sample=label, variable='euc_ave_b', trait='average euclidean distance from baseline', method='plantcv.plantcv.landmark_reference_pt_dist', scale='pixels', datatype=float, value=euc_ave_b, label='pixels') - outputs.add_observation(variable='ang_ave_b', trait='average angle between landmark point and baseline', + outputs.add_observation(sample=label, variable='ang_ave_b', + trait='average angle between landmark point and baseline', method='plantcv.plantcv.landmark_reference_pt_dist', scale='degrees', datatype=float, value=ang_ave_b, label='degrees') diff --git a/plantcv/plantcv/morphology/analyze_stem.py b/plantcv/plantcv/morphology/analyze_stem.py index 4d0c66b02..142809f14 100644 --- a/plantcv/plantcv/morphology/analyze_stem.py +++ b/plantcv/plantcv/morphology/analyze_stem.py @@ -9,12 +9,13 @@ from plantcv.plantcv import print_image -def analyze_stem(rgb_img, stem_objects): +def analyze_stem(rgb_img, stem_objects, label="default"): """ Calculate angle of segments (in degrees) by fitting a linear regression line to segments. Inputs: rgb_img = RGB image to plot debug image stem_objects = List of stem segments (output from segment_sort function) + label = optional label parameter, modifies the variable name of observations recorded Returns: labeled_img = Stem analysis debugging image @@ -22,6 +23,7 @@ def analyze_stem(rgb_img, stem_objects): :param rgb_img: numpy.ndarray :param stem_objects: list + :param label: str :return labeled_img: numpy.ndarray """ params.device += 1 @@ -39,13 +41,13 @@ def analyze_stem(rgb_img, stem_objects): # Calculate stem path length stem_length = cv2.arcLength(grouped_stem, False) / 2 - outputs.add_observation(variable='stem_height', trait='vertical length of stem segments', + outputs.add_observation(sample=label, variable='stem_height', trait='vertical length of stem segments', method='plantcv.plantcv.morphology.analyze_stem', scale='pixels', datatype=float, value=height, label=None) - outputs.add_observation(variable='stem_angle', trait='angle of combined stem object', + outputs.add_observation(sample=label, variable='stem_angle', trait='angle of combined stem object', method='plantcv.plantcv.morphology.analyze_stem', scale='degrees', datatype=float, value=float(slope), label=None) - outputs.add_observation(variable='stem_length', trait='path length of combined stem object', + outputs.add_observation(sample=label, variable='stem_length', trait='path length of combined stem object', method='plantcv.plantcv.morphology.analyze_stem', scale='None', datatype=float, value=stem_length, label=None) @@ -53,7 +55,7 @@ def analyze_stem(rgb_img, stem_objects): # Draw culm_height cv2.line(labeled_img, (int(stem_x), stem_y), (int(stem_x), stem_y + height), (0, 255, 0), params.line_thickness) # Draw combined stem angle - x_min = 0 # Set bounds for regression lines to get drawn + x_min = 0 # Set bounds for regression lines to get drawn x_max = img_x intercept1 = int(((x - x_min) * slope) + y) intercept2 = int(((x - x_max) * slope) + y) @@ -62,7 +64,7 @@ def analyze_stem(rgb_img, stem_objects): else: cv2.line(labeled_img, (x_max - 1, intercept2), (x_min, intercept1), (0, 0, 255), 1) if params.debug == 'print': - print_image(labeled_img, os.path.join(params.debug_outdir, str(params.device) + '_stem_analysis.png')) + print_image(labeled_img, os.path.join(params.debug_outdir, str(params.device) + 'stem_analze.png')) elif params.debug == 'plot': plot_image(labeled_img) diff --git a/plantcv/plantcv/morphology/check_cycles.py b/plantcv/plantcv/morphology/check_cycles.py index 099b7e888..0984209e5 100644 --- a/plantcv/plantcv/morphology/check_cycles.py +++ b/plantcv/plantcv/morphology/check_cycles.py @@ -13,15 +13,17 @@ from plantcv.plantcv import color_palette -def check_cycles(skel_img): +def check_cycles(skel_img, label="default"): """ Check for cycles in a skeleton image Inputs: skel_img = Skeletonized image + label = optional label parameter, modifies the variable name of observations recorded Returns: cycle_img = Image with cycles identified :param skel_img: numpy.ndarray + :param label: str :return cycle_img: numpy.ndarray """ @@ -61,7 +63,7 @@ def check_cycles(skel_img): hierarchy=cycle_hierarchies) # Store Cycle Data - outputs.add_observation(variable='num_cycles', trait='number of cycles', + outputs.add_observation(sample=label, variable='num_cycles', trait='number of cycles', method='plantcv.plantcv.morphology.check_cycles', scale='none', datatype=int, value=int(num_cycles), label='none') diff --git a/plantcv/plantcv/morphology/fill_segments.py b/plantcv/plantcv/morphology/fill_segments.py index d8abd411a..5cf99628c 100644 --- a/plantcv/plantcv/morphology/fill_segments.py +++ b/plantcv/plantcv/morphology/fill_segments.py @@ -11,7 +11,7 @@ from plantcv.plantcv import print_image -def fill_segments(mask, objects, stem_objects=None): +def fill_segments(mask, objects, stem_objects=None, label="default"): """Fills masked segments from contours. Inputs: @@ -22,40 +22,43 @@ def fill_segments(mask, objects, stem_objects=None): filled_img = Filled mask :param mask: numpy.ndarray - :param object: list + :param objects: list + :param stem_objects: numpy.ndarray + :param label: str :return filled_img: numpy.ndarray """ params.device += 1 - h,w = mask.shape - markers = np.zeros((h,w)) + h, w = mask.shape + markers = np.zeros((h, w)) objects_unique = objects.copy() - if not stem_objects==None: + if stem_objects is not None: objects_unique.append(np.vstack(stem_objects)) labels = np.arange(len(objects_unique)) + 1 - for i,l in enumerate(labels): - cv2.drawContours(markers, objects_unique, i ,int(l) , 5) + for i, l in enumerate(labels): + cv2.drawContours(markers, objects_unique, i, int(l), 5) # Fill as a watershed segmentation from contours as markers - filled_mask = watershed(mask==0, markers=markers, - mask=mask!=0,compactness=0) + filled_mask = watershed(mask == 0, markers=markers, + mask=mask != 0, compactness=0) # Count area in pixels of each segment ids, counts = np.unique(filled_mask, return_counts=True) - outputs.add_observation(variable='segment_area', trait='segment area', + + outputs.add_observation(sample=label, variable='segment_area', trait='segment area', method='plantcv.plantcv.morphology.fill_segments', scale='pixels', datatype=list, value=counts[1:].tolist(), label=(ids[1:]-1).tolist()) rgb_vals = color_palette(num=len(labels), saved=False) - filled_img = np.zeros((h,w,3), dtype=np.uint8) + filled_img = np.zeros((h, w, 3), dtype=np.uint8) for l in labels: for ch in range(3): - filled_img[:,:,ch][filled_mask==l] = rgb_vals[l-1][ch] + filled_img[:, :, ch][filled_mask == l] = rgb_vals[l - 1][ch] if params.debug == 'print': print_image(filled_img, os.path.join(params.debug_outdir, str(params.device) + '_filled_img.png')) diff --git a/plantcv/plantcv/morphology/find_branch_pts.py b/plantcv/plantcv/morphology/find_branch_pts.py index 8718fb5c3..55f7f6641 100644 --- a/plantcv/plantcv/morphology/find_branch_pts.py +++ b/plantcv/plantcv/morphology/find_branch_pts.py @@ -11,18 +11,20 @@ from plantcv.plantcv import find_objects -def find_branch_pts(skel_img, mask=None): +def find_branch_pts(skel_img, mask=None, label="default"): """ The branching algorithm was inspired by Jean-Patrick Pommier: https://gist.github.com/jeanpat/5712699 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 Returns: branch_pts_img = Image with just branch points, rest 0 :param skel_img: numpy.ndarray :param mask: np.ndarray + :param label: str :return branch_pts_img: numpy.ndarray """ @@ -94,7 +96,8 @@ def find_branch_pts(skel_img, mask=None): branch_labels.append(i) cv2.circle(branch_plot, (x, y), params.line_thickness, (255, 0, 255), -1) - outputs.add_observation(variable='branch_pts', trait='list of branch-point coordinates identified from a skeleton', + outputs.add_observation(sample=label, variable='branch_pts', + trait='list of branch-point coordinates identified from a skeleton', method='plantcv.plantcv.morphology.find_branch_pts', scale='pixels', datatype=list, value=branch_list, label=branch_labels) diff --git a/plantcv/plantcv/morphology/find_tips.py b/plantcv/plantcv/morphology/find_tips.py index 7ae8e6e31..6b5abd160 100644 --- a/plantcv/plantcv/morphology/find_tips.py +++ b/plantcv/plantcv/morphology/find_tips.py @@ -11,7 +11,7 @@ from plantcv.plantcv import find_objects -def find_tips(skel_img, mask=None): +def find_tips(skel_img, mask=None, label="default"): """ The endpoints algorithm was inspired by Jean-Patrick Pommier: https://gist.github.com/jeanpat/5712699 Find tips in skeletonized image. @@ -19,12 +19,14 @@ def find_tips(skel_img, mask=None): 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 Returns: tip_img = Image with just tips, rest 0 :param skel_img: numpy.ndarray :param mask: numpy.ndarray + :param label: str :return tip_img: numpy.ndarray """ @@ -77,7 +79,7 @@ def find_tips(skel_img, mask=None): tip_labels.append(i) cv2.circle(tip_plot, (x, y), params.line_thickness, (0, 255, 0), -1) - outputs.add_observation(variable='tips', trait='list of tip coordinates identified from a skeleton', + outputs.add_observation(sample=label, variable='tips', trait='list of tip coordinates identified from a skeleton', method='plantcv.plantcv.morphology.find_tips', scale='pixels', datatype=list, value=tip_list, label=tip_labels) diff --git a/plantcv/plantcv/morphology/segment_angle.py b/plantcv/plantcv/morphology/segment_angle.py index e8aac9cfa..fa30640a2 100644 --- a/plantcv/plantcv/morphology/segment_angle.py +++ b/plantcv/plantcv/morphology/segment_angle.py @@ -11,12 +11,13 @@ from plantcv.plantcv import color_palette -def segment_angle(segmented_img, objects): +def segment_angle(segmented_img, objects, label="default"): """ Calculate angle of segments (in degrees) by fitting a linear regression line to segments. Inputs: segmented_img = Segmented image to plot slope lines and angles on objects = List of contours + label = optional label parameter, modifies the variable name of observations recorded Returns: labeled_img = Segmented debugging image with angles labeled @@ -24,6 +25,7 @@ def segment_angle(segmented_img, objects): :param segmented_img: numpy.ndarray :param objects: list + :param label: str :return labeled_img: numpy.ndarray """ @@ -74,7 +76,7 @@ def segment_angle(segmented_img, objects): # segment_label = "ID" + str(i) segment_ids.append(i) - outputs.add_observation(variable='segment_angle', trait='segment angle', + outputs.add_observation(sample=label, variable='segment_angle', trait='segment angle', method='plantcv.plantcv.morphology.segment_angle', scale='degrees', datatype=list, value=segment_angles, label=segment_ids) diff --git a/plantcv/plantcv/morphology/segment_curvature.py b/plantcv/plantcv/morphology/segment_curvature.py index 8d038ba6e..fa90af810 100644 --- a/plantcv/plantcv/morphology/segment_curvature.py +++ b/plantcv/plantcv/morphology/segment_curvature.py @@ -14,13 +14,14 @@ from plantcv.plantcv.morphology import segment_euclidean_length -def segment_curvature(segmented_img, objects): +def segment_curvature(segmented_img, objects, label="default"): """ Calculate segment curvature as defined by the ratio between geodesic and euclidean distance. Measurement of two-dimensional tortuosity. Inputs: segmented_img = Segmented image to plot lengths on objects = List of contours + label = optional label parameter, modifies the variable name of observations recorded Returns: labeled_img = Segmented debugging image with curvature labeled @@ -28,6 +29,7 @@ def segment_curvature(segmented_img, objects): :param segmented_img: numpy.ndarray :param objects: list + :param label: str :return labeled_img: numpy.ndarray """ @@ -40,10 +42,10 @@ def segment_curvature(segmented_img, objects): debug = params.debug params.debug = None - _ = segment_euclidean_length(segmented_img, objects) - _ = segment_path_length(segmented_img, objects) - eu_lengths = outputs.observations['segment_eu_length']['value'] - path_lengths = outputs.observations['segment_path_length']['value'] + _ = segment_euclidean_length(segmented_img, objects, label="backend") + _ = segment_path_length(segmented_img, objects, label="backend") + eu_lengths = outputs.observations['backend']['segment_eu_length']['value'] + path_lengths = outputs.observations['backend']['segment_path_length']['value'] curvature_measure = [float(x / y) for x, y in zip(path_lengths, eu_lengths)] # Create a color scale, use a previously stored scale if available rand_color = color_palette(num=len(objects), saved=True) @@ -83,7 +85,7 @@ def segment_curvature(segmented_img, objects): # segment_label = "ID" + str(i) segment_ids.append(i) - outputs.add_observation(variable='segment_curvature', trait='segment curvature', + outputs.add_observation(sample=label, variable='segment_curvature', trait='segment curvature', method='plantcv.plantcv.morphology.segment_curvature', scale='none', datatype=list, value=curvature_measure, label=segment_ids) diff --git a/plantcv/plantcv/morphology/segment_euclidean_length.py b/plantcv/plantcv/morphology/segment_euclidean_length.py index 6ccbb8edd..6f217a768 100644 --- a/plantcv/plantcv/morphology/segment_euclidean_length.py +++ b/plantcv/plantcv/morphology/segment_euclidean_length.py @@ -14,18 +14,20 @@ from plantcv.plantcv.morphology import find_tips -def segment_euclidean_length(segmented_img, objects): +def segment_euclidean_length(segmented_img, objects, label="default"): """ Use segmented skeleton image to gather euclidean length measurements per segment Inputs: segmented_img = Segmented image to plot lengths on objects = List of contours + label = optional label parameter, modifies the variable name of observations recorded Returns: labeled_img = Segmented debugging image with lengths labeled :param segmented_img: numpy.ndarray :param objects: list + :param label: str :return labeled_img: numpy.ndarray """ @@ -79,7 +81,7 @@ def segment_euclidean_length(segmented_img, objects): # segment_label = "ID" + str(c) segment_ids.append(c) - outputs.add_observation(variable='segment_eu_length', trait='segment euclidean length', + outputs.add_observation(sample=label, variable='segment_eu_length', trait='segment euclidean length', method='plantcv.plantcv.morphology.segment_euclidean_length', scale='pixels', datatype=list, value=segment_lengths, label=segment_ids) diff --git a/plantcv/plantcv/morphology/segment_insertion_angle.py b/plantcv/plantcv/morphology/segment_insertion_angle.py index c2d7debe9..370412678 100644 --- a/plantcv/plantcv/morphology/segment_insertion_angle.py +++ b/plantcv/plantcv/morphology/segment_insertion_angle.py @@ -19,7 +19,7 @@ from plantcv.plantcv.morphology.segment_tangent_angle import _slope_to_intesect_angle -def segment_insertion_angle(skel_img, segmented_img, leaf_objects, stem_objects, size): +def segment_insertion_angle(skel_img, segmented_img, leaf_objects, stem_objects, size, label="default"): """ Find leaf insertion angles in degrees of skeleton segments. Fit a linear regression line to the stem. Use `size` pixels on the portion of leaf next to the stem find a linear regression line, and calculate angle between the two lines per leaf object. @@ -29,13 +29,17 @@ def segment_insertion_angle(skel_img, segmented_img, leaf_objects, stem_objects, leaf_objects = List of leaf segments stem_objects = List of stem segments size = Size of inner leaf used to calculate slope lines + label = optional label parameter, modifies the variable name of observations recorded + Returns: labeled_img = Debugging image with angles labeled + :param skel_img: numpy.ndarray :param segmented_img: numpy.ndarray :param leaf_objects: list :param stem_objects: list :param size: int + :param label: str :return labeled_img: numpy.ndarray """ @@ -92,7 +96,7 @@ def segment_insertion_angle(skel_img, segmented_img, leaf_objects, stem_objects, segment_plot = np.zeros(segmented_img.shape[:2], np.uint8) cv2.drawContours(segment_plot, obj, -1, 255, 1, lineType=8) segment_plot = dilate(segment_plot, 3, 1) - #tips = dilate(tips, 3, 1) + # tips = dilate(tips, 3, 1) overlap_img = logical_and(segment_plot, tips) # If none of the tips are within a segment_end then it's an insertion segment @@ -182,7 +186,7 @@ def segment_insertion_angle(skel_img, segmented_img, leaf_objects, stem_objects, # segment_label = "ID" + str(i) segment_ids.append(i) - outputs.add_observation(variable='segment_insertion_angle', trait='segment insertion angle', + outputs.add_observation(sample=label, variable='segment_insertion_angle', trait='segment insertion angle', method='plantcv.plantcv.morphology.segment_insertion_angle', scale='degrees', datatype=list, value=all_intersection_angles, label=segment_ids) diff --git a/plantcv/plantcv/morphology/segment_path_length.py b/plantcv/plantcv/morphology/segment_path_length.py index ee45e7bbb..7aaa6fa8a 100644 --- a/plantcv/plantcv/morphology/segment_path_length.py +++ b/plantcv/plantcv/morphology/segment_path_length.py @@ -8,18 +8,20 @@ from plantcv.plantcv import print_image -def segment_path_length(segmented_img, objects): +def segment_path_length(segmented_img, objects, label="default"): """ Use segments to calculate geodesic distance per segment Inputs: segmented_img = Segmented image to plot lengths on objects = List of contours + label = optional label parameter, modifies the variable name of observations recorded Returns: labeled_img = Segmented debugging image with lengths labeled :param segmented_img: numpy.ndarray :param objects: list + :param label: str :return labeled_img: numpy.ndarray """ @@ -44,10 +46,9 @@ def segment_path_length(segmented_img, objects): h = label_coord_y[c] cv2.putText(img=labeled_img, text=text, org=(w, h), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=params.text_size, color=(150, 150, 150), thickness=params.text_thickness) - # segment_label = "ID" + str(c) segment_ids.append(c) - outputs.add_observation(variable='segment_path_length', trait='segment path length', + outputs.add_observation(sample=label, variable='segment_path_length', trait='segment path length', method='plantcv.plantcv.morphology.segment_path_length', scale='pixels', datatype=list, value=segment_lengths, label=segment_ids) diff --git a/plantcv/plantcv/morphology/segment_tangent_angle.py b/plantcv/plantcv/morphology/segment_tangent_angle.py index 89294cb84..73b746440 100644 --- a/plantcv/plantcv/morphology/segment_tangent_angle.py +++ b/plantcv/plantcv/morphology/segment_tangent_angle.py @@ -31,15 +31,16 @@ def _slope_to_intesect_angle(m1, m2): return angle -def segment_tangent_angle(segmented_img, objects, size): +def segment_tangent_angle(segmented_img, objects, size, label="default"): """ Find 'tangent' angles in degrees of skeleton segments. Use `size` pixels on either end of each segment to find a linear regression line, and calculate angle between the two lines drawn per segment. Inputs: segmented_img = Segmented image to plot slope lines and intersection angles on - objects = List of contours - size = Size of ends used to calculate "tangent" lines + objects = List of contours + size = Size of ends used to calculate "tangent" lines + label = optional label parameter, modifies the variable name of observations recorded Returns: labeled_img = Segmented debugging image with angles labeled @@ -47,6 +48,7 @@ def segment_tangent_angle(segmented_img, objects, size): :param segmented_img: numpy.ndarray :param objects: list :param size: int + :param label: str :return labeled_img: numpy.ndarray """ # Store debug @@ -123,7 +125,7 @@ def segment_tangent_angle(segmented_img, objects, size): # segment_label = "ID" + str(i) segment_ids.append(i) - outputs.add_observation(variable='segment_tangent_angle', trait='segment tangent angle', + outputs.add_observation(sample=label, variable='segment_tangent_angle', trait='segment tangent angle', method='plantcv.plantcv.morphology.segment_tangent_angle', scale='degrees', datatype=list, value=intersection_angles, label=segment_ids) diff --git a/plantcv/plantcv/opening.py b/plantcv/plantcv/opening.py index ecf704f70..6235e9b77 100644 --- a/plantcv/plantcv/opening.py +++ b/plantcv/plantcv/opening.py @@ -1,3 +1,5 @@ +# Remove small bright spots + import os import numpy as np from skimage import morphology diff --git a/plantcv/plantcv/photosynthesis/analyze_fvfm.py b/plantcv/plantcv/photosynthesis/analyze_fvfm.py index 87b1f48fa..9a3077e3e 100755 --- a/plantcv/plantcv/photosynthesis/analyze_fvfm.py +++ b/plantcv/plantcv/photosynthesis/analyze_fvfm.py @@ -12,7 +12,7 @@ from plantcv.plantcv import outputs -def analyze_fvfm(fdark, fmin, fmax, mask, bins=256): +def analyze_fvfm(fdark, fmin, fmax, mask, bins=256, label="default"): """Analyze PSII camera images. Inputs: fdark = grayscale fdark image @@ -20,6 +20,8 @@ def analyze_fvfm(fdark, fmin, fmax, mask, bins=256): fmax = grayscale fmax image mask = mask of plant (binary, single channel) bins = number of bins (1 to 256 for 8-bit; 1 to 65,536 for 16-bit; default is 256) + label = optional label parameter, modifies the variable name of observations recorded + Returns: analysis_images = list of images (fv image and fvfm histogram image) :param fdark: numpy.ndarray @@ -27,6 +29,7 @@ def analyze_fvfm(fdark, fmin, fmax, mask, bins=256): :param fmax: numpy.ndarray :param mask: numpy.ndarray :param bins: int + :param label: str :return analysis_images: numpy.ndarray """ @@ -97,16 +100,16 @@ def analyze_fvfm(fdark, fmin, fmax, mask, bins=256): plot_image(fv, cmap='gray') print(fvfm_hist_fig) - outputs.add_observation(variable='fvfm_hist', trait='Fv/Fm frequencies', + outputs.add_observation(sample=label, variable='fvfm_hist', trait='Fv/Fm frequencies', method='plantcv.plantcv.fluor_fvfm', scale='none', datatype=list, value=fvfm_hist.tolist(), label=np.around(midpoints, decimals=len(str(bins))).tolist()) - outputs.add_observation(variable='fvfm_hist_peak', trait='peak Fv/Fm value', + outputs.add_observation(sample=label, variable='fvfm_hist_peak', trait='peak Fv/Fm value', method='plantcv.plantcv.fluor_fvfm', scale='none', datatype=float, value=float(max_bin), label='none') - outputs.add_observation(variable='fvfm_median', trait='Fv/Fm median', + outputs.add_observation(sample=label, variable='fvfm_median', trait='Fv/Fm median', method='plantcv.plantcv.fluor_fvfm', scale='none', datatype=float, value=float(np.around(fvfm_median, decimals=4)), label='none') - outputs.add_observation(variable='fdark_passed_qc', trait='Fdark passed QC', + outputs.add_observation(sample=label, variable='fdark_passed_qc', trait='Fdark passed QC', method='plantcv.plantcv.fluor_fvfm', scale='none', datatype=bool, value=qc_fdark, label='none') diff --git a/plantcv/plantcv/report_size_marker_area.py b/plantcv/plantcv/report_size_marker_area.py index 4c24e88f0..c6f31eacb 100755 --- a/plantcv/plantcv/report_size_marker_area.py +++ b/plantcv/plantcv/report_size_marker_area.py @@ -17,7 +17,7 @@ def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, - thresh=None): + thresh=None, label="default"): """Detects a size marker in a specified region and reports its size and eccentricity Inputs: @@ -29,6 +29,7 @@ def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', ob objcolor = Object color is 'dark' or 'light' (is the marker darker or lighter than the background) thresh_channel = 'h', 's', or 'v' for hue, saturation or value thresh = Binary threshold value (integer) + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_images = List of output images @@ -40,6 +41,7 @@ def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', ob :param objcolor: str :param thresh_channel: str :param thresh: int + :param label: str :return: analysis_images: list """ # Store debug @@ -124,16 +126,18 @@ def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', ob elif params.debug == 'plot': plot_image(ref_img) - outputs.add_observation(variable='marker_area', trait='marker area', + outputs.add_observation(sample=label, variable='marker_area', trait='marker area', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=marker_area, label='pixels') - outputs.add_observation(variable='marker_ellipse_major_axis', trait='marker ellipse major axis length', + outputs.add_observation(sample=label, variable='marker_ellipse_major_axis', + trait='marker ellipse major axis length', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=major_axis_length, label='pixels') - outputs.add_observation(variable='marker_ellipse_minor_axis', trait='marker ellipse minor axis length', + outputs.add_observation(sample=label, variable='marker_ellipse_minor_axis', + trait='marker ellipse minor axis length', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=minor_axis_length, label='pixels') - outputs.add_observation(variable='marker_ellipse_eccentricity', trait='marker ellipse eccentricity', + outputs.add_observation(sample=label, variable='marker_ellipse_eccentricity', trait='marker ellipse eccentricity', method='plantcv.plantcv.report_size_marker_area', scale='none', datatype=float, value=eccentricity, label='none') diff --git a/plantcv/plantcv/roi_objects.py b/plantcv/plantcv/roi_objects.py index cfc2b22cd..3ac9a30ff 100755 --- a/plantcv/plantcv/roi_objects.py +++ b/plantcv/plantcv/roi_objects.py @@ -1,3 +1,5 @@ +# Find objects partially inside a region of interest or cuts objects to the ROI + import cv2 import numpy as np import os diff --git a/plantcv/plantcv/transform/color_correction.py b/plantcv/plantcv/transform/color_correction.py index 7e8b25656..eced97fad 100644 --- a/plantcv/plantcv/transform/color_correction.py +++ b/plantcv/plantcv/transform/color_correction.py @@ -481,7 +481,7 @@ def quick_color_check(target_matrix, source_matrix, num_chips): def find_color_card(rgb_img, threshold_type='adaptgauss', threshvalue=125, blurry=False, background='dark', - record_chip_size="median"): + record_chip_size="median", label="default"): """Automatically detects a color card and output info to use in create_color_card_mask function Algorithm written by Brandon Hurr. Updated and implemented into PlantCV by Haley Schuhl. @@ -496,6 +496,7 @@ def find_color_card(rgb_img, threshold_type='adaptgauss', threshvalue=125, blurr is a dark background record_chip_size = Optional str for choosing chip size measurement to be recorded, either "median", "mean", or None + label = optional label parameter, modifies the variable name of observations recorded (default 'default') Returns: df = Dataframe containing information about the filtered contours @@ -508,6 +509,7 @@ def find_color_card(rgb_img, threshold_type='adaptgauss', threshvalue=125, blurr :param blurry: bool :param background: str :param record_chip_size: str + :param label: str :return df: pandas.core.frame.DataFrame :return start_coord: tuple :return spacing: tuple @@ -761,14 +763,16 @@ def find_color_card(rgb_img, threshold_type='adaptgauss', threshvalue=125, blurr chip_height = None chip_width = None # Store into global measurements - outputs.add_observation(variable='color_chip_size', trait='size of color card chips identified', + outputs.add_observation(sample=label, variable='color_chip_size', trait='size of color card chips identified', method='plantcv.plantcv.transform.find_color_card', scale='none', datatype=float, value=chip_size, label=str(record_chip_size)) method = record_chip_size.lower() - outputs.add_observation(variable=f'{method}_color_chip_height', trait=f'{method} height of color card chips identified', + outputs.add_observation(sample=label, variable=f'{method}_color_chip_height', + trait=f'{method} height of color card chips identified', method='plantcv.plantcv.transform.find_color_card', scale='none', datatype=float, value=chip_height, label=str(record_chip_size)) - outputs.add_observation(variable=f'{method}_color_chip_width', trait=f'{method} size of color card chips identified', + outputs.add_observation(sample=label, variable=f'{method}_color_chip_width', + trait=f'{method} size of color card chips identified', method='plantcv.plantcv.transform.find_color_card', scale='none', datatype=float, value=chip_width, label=str(record_chip_size)) diff --git a/plantcv/plantcv/visualize/pseudocolor.py b/plantcv/plantcv/visualize/pseudocolor.py index 87c42b494..25de99990 100644 --- a/plantcv/plantcv/visualize/pseudocolor.py +++ b/plantcv/plantcv/visualize/pseudocolor.py @@ -5,7 +5,6 @@ import numpy as np from matplotlib import pyplot as plt from plantcv.plantcv import params -from plantcv.plantcv import plot_image from plantcv.plantcv import fatal_error diff --git a/plantcv/plantcv/watershed.py b/plantcv/plantcv/watershed.py index 99b02c803..c18030693 100755 --- a/plantcv/plantcv/watershed.py +++ b/plantcv/plantcv/watershed.py @@ -16,7 +16,7 @@ from plantcv.plantcv import outputs -def watershed_segmentation(rgb_img, mask, distance=10): +def watershed_segmentation(rgb_img, mask, distance=10, label="default"): """Uses the watershed algorithm to detect boundary of objects. Needs a marker file which specifies area which is object (white), background (grey), unknown area (black). @@ -24,6 +24,7 @@ def watershed_segmentation(rgb_img, mask, distance=10): rgb_img = image to perform watershed on needs to be 3D (i.e. np.shape = x,y,z not np.shape = x,y) mask = binary image, single channel, object in white and background black distance = min_distance of local maximum + label = optional label parameter, modifies the variable name of observations recorded Returns: analysis_images = list of output images @@ -31,6 +32,7 @@ def watershed_segmentation(rgb_img, mask, distance=10): :param rgb_img: numpy.ndarray :param mask: numpy.ndarray :param distance: int + :param label: str :return analysis_images: list """ params.device += 1 @@ -41,9 +43,9 @@ def watershed_segmentation(rgb_img, mask, distance=10): dist_transform = cv2.distanceTransformWithLabels(mask, cv2.DIST_L2, maskSize=0)[0] - localMax = peak_local_max(dist_transform, indices=False, min_distance=distance, labels=mask) + local_max = peak_local_max(dist_transform, indices=False, min_distance=distance, labels=mask) - markers = ndi.label(localMax, structure=np.ones((3, 3)))[0] + markers = ndi.label(local_max, structure=np.ones((3, 3)))[0] dist_transform1 = -dist_transform labels = watershed(dist_transform1, markers, mask=mask) @@ -68,7 +70,7 @@ def watershed_segmentation(rgb_img, mask, distance=10): plot_image(dist_transform, cmap='gray') plot_image(joined) - outputs.add_observation(variable='estimated_object_count', trait='estimated object count', + outputs.add_observation(sample=label, variable='estimated_object_count', trait='estimated object count', method='plantcv.plantcv.watershed', scale='none', datatype=int, value=estimated_object_count, label='none') diff --git a/plantcv/plantcv/within_frame.py b/plantcv/plantcv/within_frame.py index 191d9fd1b..cd89b50a0 100644 --- a/plantcv/plantcv/within_frame.py +++ b/plantcv/plantcv/within_frame.py @@ -5,18 +5,20 @@ from plantcv.plantcv import outputs -def within_frame(mask, border_width=1): +def within_frame(mask, border_width=1, label="default"): """ This function tests whether the plant touches the edge of the image, i.e. it is completely in the field of view. Input: mask = a binary image of 0 and nonzero values border_width = distance from border of image considered out of frame (default = 1) + label = optional label parameter, modifies the variable name of observations recorded Returns: in_bounds = a boolean (True or False) confirming that the object does not touch the edge of the image :param mask: numpy.ndarray :param border_width: int + :param label: str :return in_bounds: bool """ @@ -41,7 +43,7 @@ def within_frame(mask, border_width=1): out_of_bounds = bool(np.count_nonzero(border_pxs)) in_bounds = not out_of_bounds - outputs.add_observation(variable='in_bounds', trait='whether the plant goes out of bounds ', + outputs.add_observation(sample=label, variable='in_bounds', trait='whether the plant goes out of bounds ', method='plantcv.plantcv.within_frame', scale='none', datatype=bool, value=in_bounds, label='none') diff --git a/plantcv/plantcv/x_axis_pseudolandmarks.py b/plantcv/plantcv/x_axis_pseudolandmarks.py index a007f6882..36f4c57fb 100755 --- a/plantcv/plantcv/x_axis_pseudolandmarks.py +++ b/plantcv/plantcv/x_axis_pseudolandmarks.py @@ -10,13 +10,14 @@ from plantcv.plantcv import fatal_error -def x_axis_pseudolandmarks(img, obj, mask): +def x_axis_pseudolandmarks(img, obj, mask, label="default"): """Divide up object contour into 20 equidistance segments and generate landmarks for each Inputs: img = This is a copy of the original plant image generated using np.copy if debug is true it will be drawn on obj = a contour of the plant object (this should be output from the object_composition.py fxn) mask = this is a binary image. The object should be white and the background should be black + label = optional label parameter, modifies the variable name of observations recorded Returns: top = List of landmark points within 'top' portion @@ -26,6 +27,7 @@ def x_axis_pseudolandmarks(img, obj, mask): :param img: numpy.ndarray :param obj: list :param mask: numpy.ndarray + :param label: str :return top: list :return bottom: list :return center_v: list @@ -216,13 +218,13 @@ def x_axis_pseudolandmarks(img, obj, mask): for pt in center_v: center_v_list.append(pt[0].tolist()) - outputs.add_observation(variable='top_lmk', trait='top landmark coordinates', + outputs.add_observation(sample=label, variable='top_lmk', trait='top landmark coordinates', method='plantcv.plantcv.x_axis_pseudolandmarks', scale='none', datatype=tuple, value=tuple(top_list), label='none') - outputs.add_observation(variable='bottom_lmk', trait='bottom landmark coordinates', + outputs.add_observation(sample=label, variable='bottom_lmk', trait='bottom landmark coordinates', method='plantcv.plantcv.x_axis_pseudolandmarks', scale='none', datatype=tuple, value=tuple(bottom_list), label='none') - outputs.add_observation(variable='center_v_lmk', trait='center vertical landmark coordinates', + outputs.add_observation(sample=label, variable='center_v_lmk', trait='center vertical landmark coordinates', method='plantcv.plantcv.x_axis_pseudolandmarks', scale='none', datatype=tuple, value=tuple(center_v_list), label='none') diff --git a/plantcv/plantcv/y_axis_pseudolandmarks.py b/plantcv/plantcv/y_axis_pseudolandmarks.py index c08f0e146..e8d763a40 100755 --- a/plantcv/plantcv/y_axis_pseudolandmarks.py +++ b/plantcv/plantcv/y_axis_pseudolandmarks.py @@ -10,13 +10,14 @@ from plantcv.plantcv import fatal_error -def y_axis_pseudolandmarks(img, obj, mask): +def y_axis_pseudolandmarks(img, obj, mask, label="default"): """Divide up object contour into 19 equidistant segments and generate landmarks for each Inputs: img = This is a copy of the original plant image generated using np.copy if debug is true it will be drawn on obj = a contour of the plant object (this should be output from the object_composition.py fxn) mask = this is a binary image. The object should be white and the background should be black + label = optional label parameter, modifies the variable name of observations recorded Returns: left = List of landmarks within the left side @@ -26,6 +27,7 @@ def y_axis_pseudolandmarks(img, obj, mask): :param img: numpy.ndarray :param obj: list :param mask: numpy.ndarray + :param label: str :return left: list :return right: list :return center_h: list @@ -213,13 +215,13 @@ def y_axis_pseudolandmarks(img, obj, mask): for pt in center_h: center_h_list.append(pt[0].tolist()) - outputs.add_observation(variable='left_lmk', trait='left landmark coordinates', + outputs.add_observation(sample=label, variable='left_lmk', trait='left landmark coordinates', method='plantcv.plantcv.x_axis_pseudolandmarks', scale='none', datatype=tuple, value=tuple(left_list), label='none') - outputs.add_observation(variable='right_lmk', trait='right landmark coordinates', + outputs.add_observation(sample=label, variable='right_lmk', trait='right landmark coordinates', method='plantcv.plantcv.x_axis_pseudolandmarks', scale='none', datatype=tuple, value=tuple(right_list), label='none') - outputs.add_observation(variable='center_h_lmk', trait='center horizontal landmark coordinates', + outputs.add_observation(sample=label, variable='center_h_lmk', trait='center horizontal landmark coordinates', method='plantcv.plantcv.x_axis_pseudolandmarks', scale='none', datatype=tuple, value=tuple(center_h_list), label='none') diff --git a/plantcv/utils/converters.py b/plantcv/utils/converters.py index 454b175f3..01fa726be 100644 --- a/plantcv/utils/converters.py +++ b/plantcv/utils/converters.py @@ -28,7 +28,7 @@ def json2csv(json_file, csv_file): csv = open(csv_file + "-single-value-traits.csv", "w") # Build the single-value variables output table - csv.write(",".join(map(str, meta_vars + scalar_vars)) + "\n") + csv.write(",".join(map(str, meta_vars + ["sample"] + scalar_vars)) + "\n") for entity in data["entities"]: row = [] # Add metadata variables @@ -39,19 +39,21 @@ def json2csv(json_file, csv_file): else: row.append("NA") # Add scalar variables - for var in scalar_vars: - obs = entity[data["variables"][var]["category"]] - if var in obs: - row.append(obs[var]["value"]) - else: - row.append("NA") - csv.write(",".join(map(str, row)) + "\n") + for sample in entity["observations"]: + measurements = [sample] + for var in scalar_vars: + obs = entity[data["variables"][var]["category"]][sample] + if var in obs: + measurements.append(obs[var]["value"]) + else: + measurements.append("NA") + csv.write(",".join(map(str, row + measurements)) + "\n") # Close the CSV file csv.close() # Create a CSV file of multi-value variables csv = open(csv_file + "-multi-value-traits.csv", "w") - csv.write(",".join(map(str, meta_vars + ["trait", "value", "label"])) + "\n") + csv.write(",".join(map(str, meta_vars + ["sample", "trait", "value", "label"])) + "\n") for entity in data["entities"]: meta_row = [] # Add metadata variables @@ -62,17 +64,16 @@ def json2csv(json_file, csv_file): else: meta_row.append("NA") # Add multi-value variables - for var in multi_vars: - obs = entity[data["variables"][var]["category"]] - if var in obs: - if obs[var]["label"] != "none": - for i in range(0, len(obs[var]["value"])): - row = [var] - row.append(obs[var]["value"][i]) - row.append(obs[var]["label"][i]) - csv.write(",".join(map(str, meta_row + row)) + "\n") - else: - csv.write(",".join(map(str, meta_row + [var, "NA", "NA"])) + "\n") + for sample in entity["observations"]: + for var in multi_vars: + obs = entity[data["variables"][var]["category"]][sample] + if var in obs: + if obs[var]["label"] != "none": + for i in range(0, len(obs[var]["value"])): + row = [sample, var, obs[var]["value"][i], obs[var]["label"][i]] + csv.write(",".join(map(str, meta_row + row)) + "\n") + else: + csv.write(",".join(map(str, meta_row + [var, "NA", "NA"])) + "\n") csv.close() else: # If the file does not exist raise an error diff --git a/tests/data/merged_output.json b/tests/data/merged_output.json index 1326fef5d..f70c2d9ab 100644 --- a/tests/data/merged_output.json +++ b/tests/data/merged_output.json @@ -1 +1,5759 @@ -{"variables": {"camera": {"category": "metadata", "datatype": ""}, "imgtype": {"category": "metadata", "datatype": ""}, "zoom": {"category": "metadata", "datatype": ""}, "exposure": {"category": "metadata", "datatype": ""}, "gain": {"category": "metadata", "datatype": ""}, "frame": {"category": "metadata", "datatype": ""}, "lifter": {"category": "metadata", "datatype": ""}, "timestamp": {"category": "metadata", "datatype": ""}, "id": {"category": "metadata", "datatype": ""}, "plantbarcode": {"category": "metadata", "datatype": ""}, "treatment": {"category": "metadata", "datatype": ""}, "cartag": {"category": "metadata", "datatype": ""}, "measurementlabel": {"category": "metadata", "datatype": ""}, "other": {"category": "metadata", "datatype": ""}, "image": {"category": "metadata", "datatype": ""}, "nir_frequencies": {"category": "observations", "datatype": ""}, "area": {"category": "observations", "datatype": ""}, "convex_hull_area": {"category": "observations", "datatype": ""}, "solidity": {"category": "observations", "datatype": ""}, "perimeter": {"category": "observations", "datatype": ""}, "width": {"category": "observations", "datatype": ""}, "height": {"category": "observations", "datatype": ""}, "longest_path": {"category": "observations", "datatype": ""}, "center_of_mass": {"category": "observations", "datatype": ""}, "convex_hull_vertices": {"category": "observations", "datatype": ""}, "object_in_frame": {"category": "observations", "datatype": ""}, "ellipse_center": {"category": "observations", "datatype": ""}, "ellipse_major_axis": {"category": "observations", "datatype": ""}, "ellipse_minor_axis": {"category": "observations", "datatype": ""}, "ellipse_angle": {"category": "observations", "datatype": ""}, "ellipse_eccentricity": {"category": "observations", "datatype": ""}, "horizontal_reference_position": {"category": "observations", "datatype": ""}, "height_above_reference": {"category": "observations", "datatype": ""}, "height_below_reference": {"category": "observations", "datatype": ""}, "area_above_reference": {"category": "observations", "datatype": ""}, "percent_area_above_reference": {"category": "observations", "datatype": ""}, "area_below_reference": {"category": "observations", "datatype": ""}, "percent_area_below_reference": {"category": "observations", "datatype": ""}, "blue_frequencies": {"category": "observations", "datatype": ""}, "green_frequencies": {"category": "observations", "datatype": ""}, "red_frequencies": {"category": "observations", "datatype": ""}, "lightness_frequencies": {"category": "observations", "datatype": ""}, "green-magenta_frequencies": {"category": "observations", "datatype": ""}, "blue-yellow_frequencies": {"category": "observations", "datatype": ""}, "hue_frequencies": {"category": "observations", "datatype": ""}, "saturation_frequencies": {"category": "observations", "datatype": ""}, "value_frequencies": {"category": "observations", "datatype": ""}, "hue_circular_mean": {"category": "observations", "datatype": ""}, "hue_circular_std": {"category": "observations", "datatype": ""}, "hue_median": {"category": "observations", "datatype": ""}}, "entities": [{"metadata": {"camera": {"label": "camera identifier", "datatype": "", "value": "SV"}, "imgtype": {"label": "image type", "datatype": "", "value": "NIR"}, "zoom": {"label": "camera zoom setting", "datatype": "", "value": "z1"}, "exposure": {"label": "camera exposure setting", "datatype": "", "value": "e65"}, "gain": {"label": "camera gain setting", "datatype": "", "value": "g0"}, "frame": {"label": "image series frame identifier", "datatype": "", "value": "90"}, "lifter": {"label": "imaging platform height setting", "datatype": "", "value": "h1"}, "timestamp": {"label": "datetime of image", "datatype": "", "value": "2014-10-22 17:59:23.046"}, "id": {"label": "image identifier", "datatype": "", "value": "117881"}, "plantbarcode": {"label": "plant barcode identifier", "datatype": "", "value": "Ca002AA010557"}, "treatment": {"label": "treatment identifier", "datatype": "", "value": "none"}, "cartag": {"label": "plant carrier identifier", "datatype": "", "value": "1663"}, "measurementlabel": {"label": "experiment identifier", "datatype": "", "value": "C002ch_092214_biomass"}, "other": {"label": "other identifier", "datatype": "", "value": "none"}, "image": {"label": "image file", "datatype": "", "value": "./images/snapshot57393/NIR_SV_90_z1_h1_g0_e65_117881.png"}}, "observations": {"nir_frequencies": {"trait": "near-infrared frequencies", "method": "plantcv.plantcv.analyze_nir_intensity", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 3.0, 3.0, 2.0, 8.0, 9.0, 3.0, 5.0, 2.0, 8.0, 2.0, 3.0, 3.0, 2.0, 4.0, 3.0, 2.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0, 4.0, 1.0, 1.0, 3.0, 3.0, 3.0, 6.0, 7.0, 6.0, 13.0, 13.0, 8.0, 7.0, 8.0, 10.0, 12.0, 10.0, 11.0, 6.0, 14.0, 14.0, 14.0, 8.0, 9.0, 9.0, 9.0, 9.0, 9.0, 5.0, 7.0, 6.0, 7.0, 4.0, 5.0, 8.0, 5.0, 6.0, 3.0, 5.0, 5.0, 5.0, 5.0, 7.0, 6.0, 7.0, 5.0, 5.0, 6.0, 12.0, 7.0, 10.0, 15.0, 14.0, 12.0, 15.0, 11.0, 4.0, 9.0, 10.0, 4.0, 15.0, 10.0, 11.0, 6.0, 18.0, 11.0, 5.0, 7.0, 4.0, 5.0, 5.0, 10.0, 5.0, 10.0, 6.0, 11.0, 12.0, 8.0, 6.0, 1.0, 8.0, 9.0, 9.0, 11.0, 9.0, 9.0, 7.0, 13.0, 6.0, 4.0, 7.0, 8.0, 7.0, 8.0, 4.0, 4.0, 9.0, 3.0, 1.0, 1.0, 2.0, 5.0, 9.0, 3.0, 7.0, 10.0, 10.0, 5.0, 2.0, 5.0, 4.0, 0.0, 1.0, 1.0, 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [1.0, 2.0, 2.99, 3.99, 4.98, 5.98, 6.98, 7.97, 8.97, 9.96, 10.96, 11.96, 12.95, 13.95, 14.95, 15.94, 16.94, 17.93, 18.93, 19.93, 20.92, 21.92, 22.91, 23.91, 24.91, 25.9, 26.9, 27.89, 28.89, 29.89, 30.88, 31.88, 32.88, 33.87, 34.87, 35.86, 36.86, 37.86, 38.85, 39.85, 40.84, 41.84, 42.84, 43.83, 44.83, 45.82, 46.82, 47.82, 48.81, 49.81, 50.8, 51.8, 52.8, 53.79, 54.79, 55.79, 56.78, 57.78, 58.77, 59.77, 60.77, 61.76, 62.76, 63.75, 64.75, 65.75, 66.74, 67.74, 68.73, 69.73, 70.73, 71.72, 72.72, 73.71, 74.71, 75.71, 76.7, 77.7, 78.7, 79.69, 80.69, 81.68, 82.68, 83.68, 84.67, 85.67, 86.66, 87.66, 88.66, 89.65, 90.65, 91.64, 92.64, 93.64, 94.63, 95.63, 96.62, 97.62, 98.62, 99.61, 100.61, 101.61, 102.6, 103.6, 104.59, 105.59, 106.59, 107.58, 108.58, 109.57, 110.57, 111.57, 112.56, 113.56, 114.55, 115.55, 116.55, 117.54, 118.54, 119.54, 120.53, 121.53, 122.52, 123.52, 124.52, 125.51, 126.51, 127.5, 128.5, 129.5, 130.49, 131.49, 132.48, 133.48, 134.48, 135.47, 136.47, 137.46, 138.46, 139.46, 140.45, 141.45, 142.45, 143.44, 144.44, 145.43, 146.43, 147.43, 148.42, 149.42, 150.41, 151.41, 152.41, 153.4, 154.4, 155.39, 156.39, 157.39, 158.38, 159.38, 160.38, 161.37, 162.37, 163.36, 164.36, 165.36, 166.35, 167.35, 168.34, 169.34, 170.34, 171.33, 172.33, 173.32, 174.32, 175.32, 176.31, 177.31, 178.3, 179.3, 180.3, 181.29, 182.29, 183.29, 184.28, 185.28, 186.27, 187.27, 188.27, 189.26, 190.26, 191.25, 192.25, 193.25, 194.24, 195.24, 196.23, 197.23, 198.23, 199.22, 200.22, 201.21, 202.21, 203.21, 204.2, 205.2, 206.2, 207.19, 208.19, 209.18, 210.18, 211.18, 212.17, 213.17, 214.16, 215.16, 216.16, 217.15, 218.15, 219.14, 220.14, 221.14, 222.13, 223.13, 224.12, 225.12, 226.12, 227.11, 228.11, 229.11, 230.1, 231.1, 232.09, 233.09, 234.09, 235.08, 236.08, 237.07, 238.07, 239.07, 240.06, 241.06, 242.05, 243.05, 244.05, 245.04, 246.04, 247.04, 248.03, 249.03, 250.02, 251.02, 252.02, 253.01, 254.01, 255.0]}, "area": {"trait": "area", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 923.0, "label": "pixels"}, "convex_hull_area": {"trait": "convex hull area", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 3027.0, "label": "pixels"}, "solidity": {"trait": "solidity", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": 0.3049223653782623, "label": "none"}, "perimeter": {"trait": "perimeter", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 627.1807925701141, "label": "pixels"}, "width": {"trait": "width", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 54, "label": "pixels"}, "height": {"trait": "height", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 78, "label": "pixels"}, "longest_path": {"trait": "longest path", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 513, "label": "pixels"}, "center_of_mass": {"trait": "center of mass", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": [151.19501625135428, 192.3694474539545], "label": "none"}, "convex_hull_vertices": {"trait": "convex hull vertices", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": 14, "label": "none"}, "object_in_frame": {"trait": "object in frame", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": true, "label": "none"}, "ellipse_center": {"trait": "ellipse center", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": [153.5628662109375, 192.63124084472656], "label": "none"}, "ellipse_major_axis": {"trait": "ellipse major axis length", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 65.83857727050781, "label": "pixels"}, "ellipse_minor_axis": {"trait": "ellipse minor axis length", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 42.24153518676758, "label": "pixels"}, "ellipse_angle": {"trait": "ellipse major axis angle", "method": "plantcv.plantcv.analyze_object", "scale": "degrees", "datatype": "", "value": 167.28292846679688, "label": "degrees"}, "ellipse_eccentricity": {"trait": "ellipse eccentricity", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": 0.7670457006165471, "label": "none"}}}, {"metadata": {"camera": {"label": "camera identifier", "datatype": "", "value": "SV"}, "imgtype": {"label": "image type", "datatype": "", "value": "VIS"}, "zoom": {"label": "camera zoom setting", "datatype": "", "value": "z1"}, "exposure": {"label": "camera exposure setting", "datatype": "", "value": "e82"}, "gain": {"label": "camera gain setting", "datatype": "", "value": "g0"}, "frame": {"label": "image series frame identifier", "datatype": "", "value": "90"}, "timestamp": {"label": "datetime of image", "datatype": "", "value": "2014-10-22 17:59:23.046"}, "id": {"label": "image identifier", "datatype": "", "value": "117872"}, "plantbarcode": {"label": "plant barcode identifier", "datatype": "", "value": "Ca002AA010557"}, "treatment": {"label": "treatment identifier", "datatype": "", "value": "none"}, "cartag": {"label": "plant carrier identifier", "datatype": "", "value": "1663"}, "measurementlabel": {"label": "experiment identifier", "datatype": "", "value": "C002ch_092214_biomass"}, "other": {"label": "other identifier", "datatype": "", "value": "none"}, "image": {"label": "image file", "datatype": "", "value": "./images/snapshot57393/VIS_SV_90_z1_h1_g0_e82_117872.png"}}, "observations": {"area": {"trait": "area", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 65851.0, "label": "pixels"}, "convex_hull_area": {"trait": "convex hull area", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 232885.5, "label": "pixels"}, "solidity": {"trait": "solidity", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": 0.28276127109674065, "label": "none"}, "perimeter": {"trait": "perimeter", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 7412.37744474411, "label": "pixels"}, "width": {"trait": "width", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 472, "label": "pixels"}, "height": {"trait": "height", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 677, "label": "pixels"}, "longest_path": {"trait": "longest path", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 4533, "label": "pixels"}, "center_of_mass": {"trait": "center of mass", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": [1286.1963068138677, 1418.1369151569452], "label": "none"}, "convex_hull_vertices": {"trait": "convex hull vertices", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": 20, "label": "none"}, "object_in_frame": {"trait": "object in frame", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": true, "label": "none"}, "ellipse_center": {"trait": "ellipse center", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": [1265.687255859375, 1426.276123046875], "label": "none"}, "ellipse_major_axis": {"trait": "ellipse major axis length", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 576.3345336914062, "label": "pixels"}, "ellipse_minor_axis": {"trait": "ellipse minor axis length", "method": "plantcv.plantcv.analyze_object", "scale": "pixels", "datatype": "", "value": 367.6441955566406, "label": "pixels"}, "ellipse_angle": {"trait": "ellipse major axis angle", "method": "plantcv.plantcv.analyze_object", "scale": "degrees", "datatype": "", "value": 12.61721134185791, "label": "degrees"}, "ellipse_eccentricity": {"trait": "ellipse eccentricity", "method": "plantcv.plantcv.analyze_object", "scale": "none", "datatype": "", "value": 0.77011863518315, "label": "none"}, "horizontal_reference_position": {"trait": "horizontal reference position", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "none", "datatype": "", "value": 384, "label": "none"}, "height_below_reference": {"trait": "height_below_reference", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 677, "label": "pixels"}, "area_above_reference": {"trait": "area above reference", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 0, "label": "pixels"}, "percent_area_above_reference": {"trait": "percent area above reference", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "none", "datatype": "", "value": 0.0, "label": "none"}, "area_below_reference": {"trait": "area below reference", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 65851, "label": "pixels"}, "percent_area_below_reference": {"trait": "percent area below reference", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "none", "datatype": "", "value": 100.0, "label": "none"}, "blue_frequencies": {"trait": "blue frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 10.0, 14.0, 56.0, 141.0, 290.0, 382.0, 481.0, 725.0, 1017.0, 1295.0, 1683.0, 1897.0, 2269.0, 2446.0, 2709.0, 2826.0, 3149.0, 3142.0, 2989.0, 2943.0, 2765.0, 2665.0, 2110.0, 1856.0, 1622.0, 1576.0, 1391.0, 1301.0, 1288.0, 1232.0, 1180.0, 1110.0, 1007.0, 886.0, 786.0, 679.0, 605.0, 566.0, 503.0, 465.0, 459.0, 419.0, 379.0, 309.0, 303.0, 288.0, 255.0, 202.0, 203.0, 188.0, 180.0, 158.0, 165.0, 144.0, 150.0, 144.0, 126.0, 141.0, 111.0, 123.0, 111.0, 95.0, 102.0, 80.0, 83.0, 89.0, 70.0, 99.0, 78.0, 72.0, 80.0, 86.0, 74.0, 70.0, 65.0, 58.0, 79.0, 62.0, 68.0, 71.0, 69.0, 69.0, 74.0, 68.0, 52.0, 53.0, 60.0, 67.0, 39.0, 64.0, 54.0, 44.0, 59.0, 59.0, 46.0, 48.0, 46.0, 58.0, 73.0, 58.0, 54.0, 43.0, 58.0, 53.0, 55.0, 58.0, 41.0, 50.0, 48.0, 73.0, 50.0, 35.0, 52.0, 49.0, 42.0, 51.0, 50.0, 49.0, 59.0, 57.0, 40.0, 43.0, 31.0, 51.0, 46.0, 60.0, 41.0, 62.0, 50.0, 57.0, 40.0, 43.0, 41.0, 42.0, 51.0, 37.0, 36.0, 31.0, 39.0, 31.0, 37.0, 38.0, 39.0, 42.0, 26.0, 30.0, 40.0, 23.0, 34.0, 16.0, 30.0, 30.0, 30.0, 29.0, 20.0, 15.0, 16.0, 24.0, 13.0, 17.0, 15.0, 8.0, 9.0, 8.0, 13.0, 9.0, 13.0, 6.0, 7.0, 9.0, 4.0, 5.0, 6.0, 2.0, 4.0, 2.0, 2.0, 5.0, 6.0, 0.0, 0.0, 2.0, 1.0, 0.0, 2.0, 1.0, 1.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]}, "green_frequencies": {"trait": "green frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 2.0, 12.0, 12.0, 13.0, 9.0, 14.0, 12.0, 19.0, 31.0, 46.0, 74.0, 103.0, 133.0, 159.0, 222.0, 197.0, 299.0, 319.0, 395.0, 484.0, 547.0, 604.0, 734.0, 850.0, 965.0, 991.0, 1085.0, 1243.0, 1427.0, 1541.0, 1619.0, 1711.0, 1834.0, 1861.0, 1948.0, 1807.0, 1833.0, 1750.0, 1834.0, 1712.0, 1812.0, 1930.0, 1946.0, 2154.0, 2005.0, 2014.0, 1697.0, 1383.0, 1201.0, 1075.0, 965.0, 969.0, 847.0, 917.0, 875.0, 803.0, 713.0, 652.0, 613.0, 574.0, 542.0, 510.0, 448.0, 393.0, 376.0, 341.0, 331.0, 297.0, 245.0, 212.0, 202.0, 179.0, 159.0, 144.0, 130.0, 120.0, 127.0, 110.0, 110.0, 106.0, 105.0, 90.0, 84.0, 93.0, 88.0, 100.0, 88.0, 73.0, 104.0, 75.0, 72.0, 79.0, 79.0, 88.0, 64.0, 75.0, 80.0, 68.0, 75.0, 67.0, 69.0, 63.0, 64.0, 57.0, 68.0, 56.0, 53.0, 67.0, 69.0, 61.0, 57.0, 52.0, 68.0, 52.0, 46.0, 68.0, 53.0, 60.0, 75.0, 46.0, 55.0, 51.0, 54.0, 41.0, 58.0, 61.0, 57.0, 46.0, 44.0, 50.0, 54.0, 61.0, 46.0, 40.0, 35.0, 67.0, 47.0, 45.0, 53.0, 35.0, 52.0, 35.0, 54.0, 51.0, 39.0, 54.0, 43.0, 55.0, 44.0, 37.0, 30.0, 45.0, 41.0, 30.0, 39.0, 37.0, 47.0, 30.0, 26.0, 41.0, 29.0, 36.0, 34.0, 24.0, 27.0, 25.0, 25.0, 25.0, 23.0, 23.0, 15.0, 12.0, 12.0, 12.0, 16.0, 13.0, 12.0, 10.0, 4.0, 14.0, 10.0, 8.0, 6.0, 6.0, 9.0, 13.0, 8.0, 0.0, 3.0, 5.0, 6.0, 0.0, 2.0, 4.0, 1.0, 0.0, 3.0, 1.0, 2.0, 3.0, 0.0, 1.0, 1.0, 0.0, 1.0, 4.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]}, "red_frequencies": {"trait": "red frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 8.0, 16.0, 39.0, 68.0, 95.0, 136.0, 180.0, 279.0, 452.0, 544.0, 769.0, 859.0, 1009.0, 1041.0, 1218.0, 1372.0, 1446.0, 1573.0, 1826.0, 2054.0, 2320.0, 2190.0, 2194.0, 2127.0, 2090.0, 1989.0, 2131.0, 2431.0, 2169.0, 2148.0, 1961.0, 1898.0, 1542.0, 1356.0, 1267.0, 1169.0, 1010.0, 953.0, 825.0, 818.0, 728.0, 674.0, 618.0, 621.0, 556.0, 584.0, 568.0, 560.0, 447.0, 419.0, 346.0, 332.0, 323.0, 312.0, 298.0, 279.0, 243.0, 240.0, 217.0, 198.0, 163.0, 166.0, 173.0, 160.0, 150.0, 128.0, 128.0, 121.0, 136.0, 103.0, 112.0, 110.0, 96.0, 103.0, 118.0, 119.0, 100.0, 99.0, 81.0, 98.0, 95.0, 96.0, 86.0, 95.0, 68.0, 97.0, 74.0, 97.0, 75.0, 90.0, 78.0, 92.0, 83.0, 72.0, 64.0, 64.0, 69.0, 75.0, 71.0, 79.0, 75.0, 66.0, 66.0, 76.0, 77.0, 71.0, 59.0, 50.0, 69.0, 67.0, 66.0, 55.0, 77.0, 58.0, 53.0, 70.0, 73.0, 57.0, 52.0, 74.0, 55.0, 66.0, 79.0, 52.0, 48.0, 55.0, 53.0, 46.0, 63.0, 71.0, 41.0, 52.0, 45.0, 54.0, 41.0, 53.0, 51.0, 59.0, 44.0, 39.0, 43.0, 41.0, 43.0, 43.0, 36.0, 50.0, 44.0, 41.0, 43.0, 43.0, 34.0, 40.0, 36.0, 41.0, 36.0, 35.0, 28.0, 36.0, 28.0, 29.0, 31.0, 23.0, 26.0, 27.0, 27.0, 25.0, 23.0, 19.0, 22.0, 23.0, 16.0, 20.0, 17.0, 14.0, 11.0, 11.0, 6.0, 13.0, 8.0, 5.0, 14.0, 6.0, 11.0, 4.0, 7.0, 5.0, 3.0, 10.0, 3.0, 5.0, 6.0, 2.0, 3.0, 3.0, 2.0, 5.0, 5.0, 3.0, 3.0, 3.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]}, "lightness_frequencies": {"trait": "lightness frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 3.0, 5.0, 8.0, 9.0, 17.0, 6.0, 7.0, 16.0, 20.0, 38.0, 41.0, 89.0, 61.0, 111.0, 188.0, 104.0, 243.0, 289.0, 315.0, 378.0, 187.0, 518.0, 478.0, 678.0, 690.0, 1142.0, 925.0, 801.0, 911.0, 1502.0, 1124.0, 1184.0, 1863.0, 1223.0, 1818.0, 1405.0, 2022.0, 1773.0, 1349.0, 1878.0, 1742.0, 1563.0, 1722.0, 1690.0, 1773.0, 1885.0, 2070.0, 1901.0, 2204.0, 1321.0, 1200.0, 1352.0, 828.0, 1067.0, 1010.0, 690.0, 918.0, 829.0, 828.0, 738.0, 655.0, 579.0, 593.0, 512.0, 478.0, 467.0, 506.0, 351.0, 374.0, 277.0, 336.0, 242.0, 301.0, 234.0, 245.0, 186.0, 216.0, 162.0, 135.0, 140.0, 161.0, 114.0, 110.0, 105.0, 81.0, 121.0, 109.0, 124.0, 90.0, 90.0, 100.0, 79.0, 70.0, 99.0, 83.0, 76.0, 84.0, 96.0, 64.0, 88.0, 77.0, 71.0, 82.0, 82.0, 78.0, 63.0, 77.0, 83.0, 61.0, 68.0, 67.0, 57.0, 62.0, 69.0, 63.0, 49.0, 63.0, 50.0, 82.0, 52.0, 63.0, 48.0, 55.0, 72.0, 47.0, 57.0, 56.0, 81.0, 55.0, 63.0, 62.0, 44.0, 60.0, 52.0, 53.0, 64.0, 59.0, 64.0, 38.0, 41.0, 45.0, 56.0, 47.0, 49.0, 56.0, 54.0, 40.0, 56.0, 56.0, 49.0, 39.0, 50.0, 52.0, 46.0, 53.0, 43.0, 48.0, 43.0, 45.0, 40.0, 41.0, 34.0, 45.0, 45.0, 30.0, 33.0, 31.0, 32.0, 45.0, 29.0, 27.0, 26.0, 22.0, 20.0, 15.0, 27.0, 20.0, 17.0, 14.0, 7.0, 11.0, 15.0, 7.0, 16.0, 11.0, 10.0, 11.0, 6.0, 7.0, 8.0, 9.0, 1.0, 3.0, 3.0, 6.0, 3.0, 2.0, 2.0, 3.0, 4.0, 1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 2.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [0.0, 0.39, 0.78, 1.18, 1.57, 1.96, 2.35, 2.75, 3.14, 3.53, 3.92, 4.31, 4.71, 5.1, 5.49, 5.88, 6.27, 6.67, 7.06, 7.45, 7.84, 8.24, 8.63, 9.02, 9.41, 9.8, 10.2, 10.59, 10.98, 11.37, 11.76, 12.16, 12.55, 12.94, 13.33, 13.73, 14.12, 14.51, 14.9, 15.29, 15.69, 16.08, 16.47, 16.86, 17.25, 17.65, 18.04, 18.43, 18.82, 19.22, 19.61, 20.0, 20.39, 20.78, 21.18, 21.57, 21.96, 22.35, 22.75, 23.14, 23.53, 23.92, 24.31, 24.71, 25.1, 25.49, 25.88, 26.27, 26.67, 27.06, 27.45, 27.84, 28.24, 28.63, 29.02, 29.41, 29.8, 30.2, 30.59, 30.98, 31.37, 31.76, 32.16, 32.55, 32.94, 33.33, 33.73, 34.12, 34.51, 34.9, 35.29, 35.69, 36.08, 36.47, 36.86, 37.25, 37.65, 38.04, 38.43, 38.82, 39.22, 39.61, 40.0, 40.39, 40.78, 41.18, 41.57, 41.96, 42.35, 42.75, 43.14, 43.53, 43.92, 44.31, 44.71, 45.1, 45.49, 45.88, 46.27, 46.67, 47.06, 47.45, 47.84, 48.24, 48.63, 49.02, 49.41, 49.8, 50.2, 50.59, 50.98, 51.37, 51.76, 52.16, 52.55, 52.94, 53.33, 53.73, 54.12, 54.51, 54.9, 55.29, 55.69, 56.08, 56.47, 56.86, 57.25, 57.65, 58.04, 58.43, 58.82, 59.22, 59.61, 60.0, 60.39, 60.78, 61.18, 61.57, 61.96, 62.35, 62.75, 63.14, 63.53, 63.92, 64.31, 64.71, 65.1, 65.49, 65.88, 66.27, 66.67, 67.06, 67.45, 67.84, 68.24, 68.63, 69.02, 69.41, 69.8, 70.2, 70.59, 70.98, 71.37, 71.76, 72.16, 72.55, 72.94, 73.33, 73.73, 74.12, 74.51, 74.9, 75.29, 75.69, 76.08, 76.47, 76.86, 77.25, 77.65, 78.04, 78.43, 78.82, 79.22, 79.61, 80.0, 80.39, 80.78, 81.18, 81.57, 81.96, 82.35, 82.75, 83.14, 83.53, 83.92, 84.31, 84.71, 85.1, 85.49, 85.88, 86.27, 86.67, 87.06, 87.45, 87.84, 88.24, 88.63, 89.02, 89.41, 89.8, 90.2, 90.59, 90.98, 91.37, 91.76, 92.16, 92.55, 92.94, 93.33, 93.73, 94.12, 94.51, 94.9, 95.29, 95.69, 96.08, 96.47, 96.86, 97.25, 97.65, 98.04, 98.43, 98.82, 99.22, 99.61, 100.0]}, "green-magenta_frequencies": {"trait": "green-magenta frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 17.0, 76.0, 429.0, 1525.0, 3788.0, 6803.0, 9196.0, 9962.0, 8484.0, 6621.0, 4859.0, 3467.0, 2368.0, 1852.0, 1510.0, 1199.0, 935.0, 743.0, 577.0, 415.0, 320.0, 247.0, 176.0, 125.0, 80.0, 42.0, 11.0, 12.0, 5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127]}, "blue-yellow_frequencies": {"trait": "blue-yellow frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 352.0, 377.0, 472.0, 711.0, 1009.0, 1659.0, 2405.0, 3140.0, 4487.0, 5885.0, 6881.0, 7038.0, 6708.0, 7055.0, 5978.0, 4737.0, 3087.0, 1820.0, 886.0, 450.0, 219.0, 148.0, 109.0, 89.0, 59.0, 38.0, 25.0, 15.0, 7.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127]}, "hue_frequencies": {"trait": "hue frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.0, 10.0, 15.0, 22.0, 47.0, 63.0, 114.0, 125.0, 164.0, 209.0, 270.0, 272.0, 406.0, 395.0, 420.0, 511.0, 399.0, 552.0, 590.0, 637.0, 752.0, 843.0, 1147.0, 1295.0, 1972.0, 2583.0, 3537.0, 5285.0, 7184.0, 8334.0, 8292.0, 7947.0, 4566.0, 3209.0, 1625.0, 848.0, 385.0, 187.0, 96.0, 119.0, 52.0, 48.0, 56.0, 36.0, 31.0, 24.0, 24.0, 26.0, 17.0, 27.0, 18.0, 16.0, 20.0, 12.0, 5.0, 3.0, 3.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359]}, "saturation_frequencies": {"trait": "saturation frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 2.0, 4.0, 5.0, 12.0, 10.0, 13.0, 17.0, 24.0, 27.0, 34.0, 50.0, 55.0, 60.0, 70.0, 78.0, 56.0, 92.0, 56.0, 85.0, 74.0, 104.0, 97.0, 94.0, 84.0, 112.0, 115.0, 102.0, 111.0, 140.0, 119.0, 109.0, 120.0, 135.0, 124.0, 125.0, 119.0, 130.0, 132.0, 111.0, 137.0, 155.0, 135.0, 122.0, 120.0, 129.0, 126.0, 99.0, 116.0, 97.0, 123.0, 119.0, 104.0, 108.0, 120.0, 120.0, 120.0, 113.0, 101.0, 130.0, 111.0, 107.0, 114.0, 134.0, 124.0, 123.0, 130.0, 132.0, 123.0, 160.0, 137.0, 153.0, 168.0, 193.0, 208.0, 137.0, 257.0, 241.0, 234.0, 315.0, 257.0, 319.0, 301.0, 355.0, 369.0, 353.0, 452.0, 425.0, 464.0, 531.0, 521.0, 514.0, 596.0, 625.0, 709.0, 648.0, 753.0, 855.0, 604.0, 1057.0, 790.0, 717.0, 1176.0, 687.0, 1023.0, 917.0, 955.0, 949.0, 971.0, 1002.0, 862.0, 1143.0, 1072.0, 1047.0, 1024.0, 1090.0, 1230.0, 988.0, 1179.0, 1255.0, 1153.0, 1223.0, 1080.0, 1265.0, 990.0, 1347.0, 922.0, 1308.0, 1071.0, 1087.0, 968.0, 1099.0, 1176.0, 877.0, 868.0, 933.0, 891.0, 745.0, 726.0, 754.0, 693.0, 478.0, 561.0, 657.0, 452.0, 276.0, 487.0, 333.0, 246.0, 213.0, 285.0, 186.0, 195.0, 152.0, 116.0, 97.0, 91.0, 80.0, 73.0, 49.0, 34.0, 34.0, 23.0, 24.0, 13.0, 8.0, 4.0, 5.0, 4.0, 2.0, 3.0, 1.0, 2.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [0.0, 0.39, 0.78, 1.18, 1.57, 1.96, 2.35, 2.75, 3.14, 3.53, 3.92, 4.31, 4.71, 5.1, 5.49, 5.88, 6.27, 6.67, 7.06, 7.45, 7.84, 8.24, 8.63, 9.02, 9.41, 9.8, 10.2, 10.59, 10.98, 11.37, 11.76, 12.16, 12.55, 12.94, 13.33, 13.73, 14.12, 14.51, 14.9, 15.29, 15.69, 16.08, 16.47, 16.86, 17.25, 17.65, 18.04, 18.43, 18.82, 19.22, 19.61, 20.0, 20.39, 20.78, 21.18, 21.57, 21.96, 22.35, 22.75, 23.14, 23.53, 23.92, 24.31, 24.71, 25.1, 25.49, 25.88, 26.27, 26.67, 27.06, 27.45, 27.84, 28.24, 28.63, 29.02, 29.41, 29.8, 30.2, 30.59, 30.98, 31.37, 31.76, 32.16, 32.55, 32.94, 33.33, 33.73, 34.12, 34.51, 34.9, 35.29, 35.69, 36.08, 36.47, 36.86, 37.25, 37.65, 38.04, 38.43, 38.82, 39.22, 39.61, 40.0, 40.39, 40.78, 41.18, 41.57, 41.96, 42.35, 42.75, 43.14, 43.53, 43.92, 44.31, 44.71, 45.1, 45.49, 45.88, 46.27, 46.67, 47.06, 47.45, 47.84, 48.24, 48.63, 49.02, 49.41, 49.8, 50.2, 50.59, 50.98, 51.37, 51.76, 52.16, 52.55, 52.94, 53.33, 53.73, 54.12, 54.51, 54.9, 55.29, 55.69, 56.08, 56.47, 56.86, 57.25, 57.65, 58.04, 58.43, 58.82, 59.22, 59.61, 60.0, 60.39, 60.78, 61.18, 61.57, 61.96, 62.35, 62.75, 63.14, 63.53, 63.92, 64.31, 64.71, 65.1, 65.49, 65.88, 66.27, 66.67, 67.06, 67.45, 67.84, 68.24, 68.63, 69.02, 69.41, 69.8, 70.2, 70.59, 70.98, 71.37, 71.76, 72.16, 72.55, 72.94, 73.33, 73.73, 74.12, 74.51, 74.9, 75.29, 75.69, 76.08, 76.47, 76.86, 77.25, 77.65, 78.04, 78.43, 78.82, 79.22, 79.61, 80.0, 80.39, 80.78, 81.18, 81.57, 81.96, 82.35, 82.75, 83.14, 83.53, 83.92, 84.31, 84.71, 85.1, 85.49, 85.88, 86.27, 86.67, 87.06, 87.45, 87.84, 88.24, 88.63, 89.02, 89.41, 89.8, 90.2, 90.59, 90.98, 91.37, 91.76, 92.16, 92.55, 92.94, 93.33, 93.73, 94.12, 94.51, 94.9, 95.29, 95.69, 96.08, 96.47, 96.86, 97.25, 97.65, 98.04, 98.43, 98.82, 99.22, 99.61, 100.0]}, "value_frequencies": {"trait": "value frequencies", "method": "plantcv.plantcv.analyze_color", "scale": "frequency", "datatype": "", "value": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 2.0, 12.0, 11.0, 14.0, 9.0, 13.0, 12.0, 20.0, 26.0, 47.0, 75.0, 104.0, 133.0, 157.0, 224.0, 193.0, 300.0, 322.0, 391.0, 478.0, 546.0, 600.0, 734.0, 852.0, 958.0, 996.0, 1088.0, 1241.0, 1429.0, 1545.0, 1616.0, 1711.0, 1832.0, 1860.0, 1937.0, 1806.0, 1832.0, 1749.0, 1826.0, 1712.0, 1807.0, 1926.0, 1944.0, 2150.0, 1997.0, 2012.0, 1683.0, 1380.0, 1189.0, 1082.0, 958.0, 958.0, 835.0, 914.0, 879.0, 792.0, 698.0, 642.0, 596.0, 561.0, 524.0, 502.0, 433.0, 390.0, 364.0, 329.0, 325.0, 298.0, 241.0, 229.0, 191.0, 171.0, 177.0, 132.0, 120.0, 121.0, 125.0, 100.0, 116.0, 96.0, 111.0, 104.0, 93.0, 86.0, 83.0, 97.0, 90.0, 82.0, 98.0, 71.0, 62.0, 81.0, 91.0, 91.0, 80.0, 87.0, 75.0, 75.0, 80.0, 72.0, 79.0, 65.0, 50.0, 62.0, 71.0, 71.0, 51.0, 76.0, 66.0, 65.0, 72.0, 64.0, 71.0, 54.0, 49.0, 65.0, 58.0, 63.0, 70.0, 51.0, 60.0, 57.0, 52.0, 55.0, 70.0, 50.0, 60.0, 45.0, 56.0, 58.0, 50.0, 60.0, 60.0, 43.0, 35.0, 58.0, 44.0, 48.0, 53.0, 34.0, 47.0, 39.0, 55.0, 52.0, 41.0, 53.0, 48.0, 53.0, 47.0, 41.0, 40.0, 41.0, 49.0, 34.0, 39.0, 36.0, 48.0, 33.0, 29.0, 48.0, 24.0, 39.0, 34.0, 29.0, 29.0, 28.0, 28.0, 28.0, 21.0, 24.0, 18.0, 12.0, 16.0, 18.0, 15.0, 14.0, 15.0, 12.0, 7.0, 15.0, 11.0, 6.0, 12.0, 6.0, 11.0, 14.0, 4.0, 1.0, 3.0, 5.0, 10.0, 2.0, 3.0, 6.0, 4.0, 0.0, 4.0, 1.0, 3.0, 2.0, 0.0, 2.0, 1.0, 1.0, 2.0, 3.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "label": [0.0, 0.39, 0.78, 1.18, 1.57, 1.96, 2.35, 2.75, 3.14, 3.53, 3.92, 4.31, 4.71, 5.1, 5.49, 5.88, 6.27, 6.67, 7.06, 7.45, 7.84, 8.24, 8.63, 9.02, 9.41, 9.8, 10.2, 10.59, 10.98, 11.37, 11.76, 12.16, 12.55, 12.94, 13.33, 13.73, 14.12, 14.51, 14.9, 15.29, 15.69, 16.08, 16.47, 16.86, 17.25, 17.65, 18.04, 18.43, 18.82, 19.22, 19.61, 20.0, 20.39, 20.78, 21.18, 21.57, 21.96, 22.35, 22.75, 23.14, 23.53, 23.92, 24.31, 24.71, 25.1, 25.49, 25.88, 26.27, 26.67, 27.06, 27.45, 27.84, 28.24, 28.63, 29.02, 29.41, 29.8, 30.2, 30.59, 30.98, 31.37, 31.76, 32.16, 32.55, 32.94, 33.33, 33.73, 34.12, 34.51, 34.9, 35.29, 35.69, 36.08, 36.47, 36.86, 37.25, 37.65, 38.04, 38.43, 38.82, 39.22, 39.61, 40.0, 40.39, 40.78, 41.18, 41.57, 41.96, 42.35, 42.75, 43.14, 43.53, 43.92, 44.31, 44.71, 45.1, 45.49, 45.88, 46.27, 46.67, 47.06, 47.45, 47.84, 48.24, 48.63, 49.02, 49.41, 49.8, 50.2, 50.59, 50.98, 51.37, 51.76, 52.16, 52.55, 52.94, 53.33, 53.73, 54.12, 54.51, 54.9, 55.29, 55.69, 56.08, 56.47, 56.86, 57.25, 57.65, 58.04, 58.43, 58.82, 59.22, 59.61, 60.0, 60.39, 60.78, 61.18, 61.57, 61.96, 62.35, 62.75, 63.14, 63.53, 63.92, 64.31, 64.71, 65.1, 65.49, 65.88, 66.27, 66.67, 67.06, 67.45, 67.84, 68.24, 68.63, 69.02, 69.41, 69.8, 70.2, 70.59, 70.98, 71.37, 71.76, 72.16, 72.55, 72.94, 73.33, 73.73, 74.12, 74.51, 74.9, 75.29, 75.69, 76.08, 76.47, 76.86, 77.25, 77.65, 78.04, 78.43, 78.82, 79.22, 79.61, 80.0, 80.39, 80.78, 81.18, 81.57, 81.96, 82.35, 82.75, 83.14, 83.53, 83.92, 84.31, 84.71, 85.1, 85.49, 85.88, 86.27, 86.67, 87.06, 87.45, 87.84, 88.24, 88.63, 89.02, 89.41, 89.8, 90.2, 90.59, 90.98, 91.37, 91.76, 92.16, 92.55, 92.94, 93.33, 93.73, 94.12, 94.51, 94.9, 95.29, 95.69, 96.08, 96.47, 96.86, 97.25, 97.65, 98.04, 98.43, 98.82, 99.22, 99.61, 100.0]}, "hue_circular_mean": {"trait": "hue circular mean", "method": "plantcv.plantcv.analyze_color", "scale": "degrees", "datatype": "", "value": 83.67294573455045, "label": "degrees"}, "hue_circular_std": {"trait": "hue circular standard deviation", "method": "plantcv.plantcv.analyze_color", "scale": "degrees", "datatype": "", "value": 86.0, "label": "degrees"}, "hue_median": {"trait": "hue median", "method": "plantcv.plantcv.analyze_color", "scale": "degrees", "datatype": "", "value": 86.0, "label": "degrees"}}}]} \ No newline at end of file +{ + "variables": { + "camera": { + "category": "metadata", + "datatype": "" + }, + "imgtype": { + "category": "metadata", + "datatype": "" + }, + "zoom": { + "category": "metadata", + "datatype": "" + }, + "exposure": { + "category": "metadata", + "datatype": "" + }, + "gain": { + "category": "metadata", + "datatype": "" + }, + "frame": { + "category": "metadata", + "datatype": "" + }, + "lifter": { + "category": "metadata", + "datatype": "" + }, + "timestamp": { + "category": "metadata", + "datatype": "" + }, + "id": { + "category": "metadata", + "datatype": "" + }, + "plantbarcode": { + "category": "metadata", + "datatype": "" + }, + "treatment": { + "category": "metadata", + "datatype": "" + }, + "cartag": { + "category": "metadata", + "datatype": "" + }, + "measurementlabel": { + "category": "metadata", + "datatype": "" + }, + "other": { + "category": "metadata", + "datatype": "" + }, + "image": { + "category": "metadata", + "datatype": "" + }, + "nir_frequencies": { + "category": "observations", + "datatype": "" + }, + "area": { + "category": "observations", + "datatype": "" + }, + "convex_hull_area": { + "category": "observations", + "datatype": "" + }, + "solidity": { + "category": "observations", + "datatype": "" + }, + "perimeter": { + "category": "observations", + "datatype": "" + }, + "width": { + "category": "observations", + "datatype": "" + }, + "height": { + "category": "observations", + "datatype": "" + }, + "longest_path": { + "category": "observations", + "datatype": "" + }, + "center_of_mass": { + "category": "observations", + "datatype": "" + }, + "convex_hull_vertices": { + "category": "observations", + "datatype": "" + }, + "object_in_frame": { + "category": "observations", + "datatype": "" + }, + "ellipse_center": { + "category": "observations", + "datatype": "" + }, + "ellipse_major_axis": { + "category": "observations", + "datatype": "" + }, + "ellipse_minor_axis": { + "category": "observations", + "datatype": "" + }, + "ellipse_angle": { + "category": "observations", + "datatype": "" + }, + "ellipse_eccentricity": { + "category": "observations", + "datatype": "" + }, + "horizontal_reference_position": { + "category": "observations", + "datatype": "" + }, + "height_above_reference": { + "category": "observations", + "datatype": "" + }, + "height_below_reference": { + "category": "observations", + "datatype": "" + }, + "area_above_reference": { + "category": "observations", + "datatype": "" + }, + "percent_area_above_reference": { + "category": "observations", + "datatype": "" + }, + "area_below_reference": { + "category": "observations", + "datatype": "" + }, + "percent_area_below_reference": { + "category": "observations", + "datatype": "" + }, + "blue_frequencies": { + "category": "observations", + "datatype": "" + }, + "green_frequencies": { + "category": "observations", + "datatype": "" + }, + "red_frequencies": { + "category": "observations", + "datatype": "" + }, + "lightness_frequencies": { + "category": "observations", + "datatype": "" + }, + "green-magenta_frequencies": { + "category": "observations", + "datatype": "" + }, + "blue-yellow_frequencies": { + "category": "observations", + "datatype": "" + }, + "hue_frequencies": { + "category": "observations", + "datatype": "" + }, + "saturation_frequencies": { + "category": "observations", + "datatype": "" + }, + "value_frequencies": { + "category": "observations", + "datatype": "" + }, + "hue_circular_mean": { + "category": "observations", + "datatype": "" + }, + "hue_circular_std": { + "category": "observations", + "datatype": "" + }, + "hue_median": { + "category": "observations", + "datatype": "" + } + }, + "entities": [ + { + "metadata": { + "camera": { + "label": "camera identifier", + "datatype": "", + "value": "SV" + }, + "imgtype": { + "label": "image type", + "datatype": "", + "value": "NIR" + }, + "zoom": { + "label": "camera zoom setting", + "datatype": "", + "value": "z1" + }, + "exposure": { + "label": "camera exposure setting", + "datatype": "", + "value": "e65" + }, + "gain": { + "label": "camera gain setting", + "datatype": "", + "value": "g0" + }, + "frame": { + "label": "image series frame identifier", + "datatype": "", + "value": "90" + }, + "lifter": { + "label": "imaging platform height setting", + "datatype": "", + "value": "h1" + }, + "timestamp": { + "label": "datetime of image", + "datatype": "", + "value": "2014-10-22 17:59:23.046" + }, + "id": { + "label": "image identifier", + "datatype": "", + "value": "117881" + }, + "plantbarcode": { + "label": "plant barcode identifier", + "datatype": "", + "value": "Ca002AA010557" + }, + "treatment": { + "label": "treatment identifier", + "datatype": "", + "value": "none" + }, + "cartag": { + "label": "plant carrier identifier", + "datatype": "", + "value": "1663" + }, + "measurementlabel": { + "label": "experiment identifier", + "datatype": "", + "value": "C002ch_092214_biomass" + }, + "other": { + "label": "other identifier", + "datatype": "", + "value": "none" + }, + "image": { + "label": "image file", + "datatype": "", + "value": "./images/snapshot57393/NIR_SV_90_z1_h1_g0_e65_117881.png" + } + }, + "observations": { + "sample1": { + "nir_frequencies": { + "trait": "near-infrared frequencies", + "method": "plantcv.plantcv.analyze_nir_intensity", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 3, + 3, + 2, + 8, + 9, + 3, + 5, + 2, + 8, + 2, + 3, + 3, + 2, + 4, + 3, + 2, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 6, + 7, + 6, + 13, + 13, + 8, + 7, + 8, + 10, + 12, + 10, + 11, + 6, + 14, + 14, + 14, + 8, + 9, + 9, + 9, + 9, + 9, + 5, + 7, + 6, + 7, + 4, + 5, + 8, + 5, + 6, + 3, + 5, + 5, + 5, + 5, + 7, + 6, + 7, + 5, + 5, + 6, + 12, + 7, + 10, + 15, + 14, + 12, + 15, + 11, + 4, + 9, + 10, + 4, + 15, + 10, + 11, + 6, + 18, + 11, + 5, + 7, + 4, + 5, + 5, + 10, + 5, + 10, + 6, + 11, + 12, + 8, + 6, + 1, + 8, + 9, + 9, + 11, + 9, + 9, + 7, + 13, + 6, + 4, + 7, + 8, + 7, + 8, + 4, + 4, + 9, + 3, + 1, + 1, + 2, + 5, + 9, + 3, + 7, + 10, + 10, + 5, + 2, + 5, + 4, + 0, + 1, + 1, + 1, + 2, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 2, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 1, + 2, + 2.99, + 3.99, + 4.98, + 5.98, + 6.98, + 7.97, + 8.97, + 9.96, + 10.96, + 11.96, + 12.95, + 13.95, + 14.95, + 15.94, + 16.94, + 17.93, + 18.93, + 19.93, + 20.92, + 21.92, + 22.91, + 23.91, + 24.91, + 25.9, + 26.9, + 27.89, + 28.89, + 29.89, + 30.88, + 31.88, + 32.88, + 33.87, + 34.87, + 35.86, + 36.86, + 37.86, + 38.85, + 39.85, + 40.84, + 41.84, + 42.84, + 43.83, + 44.83, + 45.82, + 46.82, + 47.82, + 48.81, + 49.81, + 50.8, + 51.8, + 52.8, + 53.79, + 54.79, + 55.79, + 56.78, + 57.78, + 58.77, + 59.77, + 60.77, + 61.76, + 62.76, + 63.75, + 64.75, + 65.75, + 66.74, + 67.74, + 68.73, + 69.73, + 70.73, + 71.72, + 72.72, + 73.71, + 74.71, + 75.71, + 76.7, + 77.7, + 78.7, + 79.69, + 80.69, + 81.68, + 82.68, + 83.68, + 84.67, + 85.67, + 86.66, + 87.66, + 88.66, + 89.65, + 90.65, + 91.64, + 92.64, + 93.64, + 94.63, + 95.63, + 96.62, + 97.62, + 98.62, + 99.61, + 100.61, + 101.61, + 102.6, + 103.6, + 104.59, + 105.59, + 106.59, + 107.58, + 108.58, + 109.57, + 110.57, + 111.57, + 112.56, + 113.56, + 114.55, + 115.55, + 116.55, + 117.54, + 118.54, + 119.54, + 120.53, + 121.53, + 122.52, + 123.52, + 124.52, + 125.51, + 126.51, + 127.5, + 128.5, + 129.5, + 130.49, + 131.49, + 132.48, + 133.48, + 134.48, + 135.47, + 136.47, + 137.46, + 138.46, + 139.46, + 140.45, + 141.45, + 142.45, + 143.44, + 144.44, + 145.43, + 146.43, + 147.43, + 148.42, + 149.42, + 150.41, + 151.41, + 152.41, + 153.4, + 154.4, + 155.39, + 156.39, + 157.39, + 158.38, + 159.38, + 160.38, + 161.37, + 162.37, + 163.36, + 164.36, + 165.36, + 166.35, + 167.35, + 168.34, + 169.34, + 170.34, + 171.33, + 172.33, + 173.32, + 174.32, + 175.32, + 176.31, + 177.31, + 178.3, + 179.3, + 180.3, + 181.29, + 182.29, + 183.29, + 184.28, + 185.28, + 186.27, + 187.27, + 188.27, + 189.26, + 190.26, + 191.25, + 192.25, + 193.25, + 194.24, + 195.24, + 196.23, + 197.23, + 198.23, + 199.22, + 200.22, + 201.21, + 202.21, + 203.21, + 204.2, + 205.2, + 206.2, + 207.19, + 208.19, + 209.18, + 210.18, + 211.18, + 212.17, + 213.17, + 214.16, + 215.16, + 216.16, + 217.15, + 218.15, + 219.14, + 220.14, + 221.14, + 222.13, + 223.13, + 224.12, + 225.12, + 226.12, + 227.11, + 228.11, + 229.11, + 230.1, + 231.1, + 232.09, + 233.09, + 234.09, + 235.08, + 236.08, + 237.07, + 238.07, + 239.07, + 240.06, + 241.06, + 242.05, + 243.05, + 244.05, + 245.04, + 246.04, + 247.04, + 248.03, + 249.03, + 250.02, + 251.02, + 252.02, + 253.01, + 254.01, + 255 + ] + }, + "area": { + "trait": "area", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 923, + "label": "pixels" + }, + "convex_hull_area": { + "trait": "convex hull area", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 3027, + "label": "pixels" + }, + "solidity": { + "trait": "solidity", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": 0.3049223653782623, + "label": "none" + }, + "perimeter": { + "trait": "perimeter", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 627.1807925701141, + "label": "pixels" + }, + "width": { + "trait": "width", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 54, + "label": "pixels" + }, + "height": { + "trait": "height", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 78, + "label": "pixels" + }, + "longest_path": { + "trait": "longest path", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 513, + "label": "pixels" + }, + "center_of_mass": { + "trait": "center of mass", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": [ + 151.19501625135428, + 192.3694474539545 + ], + "label": "none" + }, + "convex_hull_vertices": { + "trait": "convex hull vertices", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": 14, + "label": "none" + }, + "object_in_frame": { + "trait": "object in frame", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": true, + "label": "none" + }, + "ellipse_center": { + "trait": "ellipse center", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": [ + 153.5628662109375, + 192.63124084472656 + ], + "label": "none" + }, + "ellipse_major_axis": { + "trait": "ellipse major axis length", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 65.83857727050781, + "label": "pixels" + }, + "ellipse_minor_axis": { + "trait": "ellipse minor axis length", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 42.24153518676758, + "label": "pixels" + }, + "ellipse_angle": { + "trait": "ellipse major axis angle", + "method": "plantcv.plantcv.analyze_object", + "scale": "degrees", + "datatype": "", + "value": 167.28292846679688, + "label": "degrees" + }, + "ellipse_eccentricity": { + "trait": "ellipse eccentricity", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": 0.7670457006165471, + "label": "none" + } + } + } + }, + { + "metadata": { + "camera": { + "label": "camera identifier", + "datatype": "", + "value": "SV" + }, + "imgtype": { + "label": "image type", + "datatype": "", + "value": "VIS" + }, + "zoom": { + "label": "camera zoom setting", + "datatype": "", + "value": "z1" + }, + "exposure": { + "label": "camera exposure setting", + "datatype": "", + "value": "e82" + }, + "gain": { + "label": "camera gain setting", + "datatype": "", + "value": "g0" + }, + "frame": { + "label": "image series frame identifier", + "datatype": "", + "value": "90" + }, + "timestamp": { + "label": "datetime of image", + "datatype": "", + "value": "2014-10-22 17:59:23.046" + }, + "id": { + "label": "image identifier", + "datatype": "", + "value": "117872" + }, + "plantbarcode": { + "label": "plant barcode identifier", + "datatype": "", + "value": "Ca002AA010557" + }, + "treatment": { + "label": "treatment identifier", + "datatype": "", + "value": "none" + }, + "cartag": { + "label": "plant carrier identifier", + "datatype": "", + "value": "1663" + }, + "measurementlabel": { + "label": "experiment identifier", + "datatype": "", + "value": "C002ch_092214_biomass" + }, + "other": { + "label": "other identifier", + "datatype": "", + "value": "none" + }, + "image": { + "label": "image file", + "datatype": "", + "value": "./images/snapshot57393/VIS_SV_90_z1_h1_g0_e82_117872.png" + } + }, + "observations": { + "sample1": { + "area": { + "trait": "area", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 65851, + "label": "pixels" + }, + "convex_hull_area": { + "trait": "convex hull area", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 232885.5, + "label": "pixels" + }, + "solidity": { + "trait": "solidity", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": 0.28276127109674065, + "label": "none" + }, + "perimeter": { + "trait": "perimeter", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 7412.37744474411, + "label": "pixels" + }, + "width": { + "trait": "width", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 472, + "label": "pixels" + }, + "height": { + "trait": "height", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 677, + "label": "pixels" + }, + "longest_path": { + "trait": "longest path", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 4533, + "label": "pixels" + }, + "center_of_mass": { + "trait": "center of mass", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": [ + 1286.1963068138677, + 1418.1369151569452 + ], + "label": "none" + }, + "convex_hull_vertices": { + "trait": "convex hull vertices", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": 20, + "label": "none" + }, + "object_in_frame": { + "trait": "object in frame", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": true, + "label": "none" + }, + "ellipse_center": { + "trait": "ellipse center", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": [ + 1265.687255859375, + 1426.276123046875 + ], + "label": "none" + }, + "ellipse_major_axis": { + "trait": "ellipse major axis length", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 576.3345336914062, + "label": "pixels" + }, + "ellipse_minor_axis": { + "trait": "ellipse minor axis length", + "method": "plantcv.plantcv.analyze_object", + "scale": "pixels", + "datatype": "", + "value": 367.6441955566406, + "label": "pixels" + }, + "ellipse_angle": { + "trait": "ellipse major axis angle", + "method": "plantcv.plantcv.analyze_object", + "scale": "degrees", + "datatype": "", + "value": 12.61721134185791, + "label": "degrees" + }, + "ellipse_eccentricity": { + "trait": "ellipse eccentricity", + "method": "plantcv.plantcv.analyze_object", + "scale": "none", + "datatype": "", + "value": 0.77011863518315, + "label": "none" + }, + "horizontal_reference_position": { + "trait": "horizontal reference position", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "none", + "datatype": "", + "value": 384, + "label": "none" + }, + "height_below_reference": { + "trait": "height_below_reference", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 677, + "label": "pixels" + }, + "area_above_reference": { + "trait": "area above reference", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 0, + "label": "pixels" + }, + "percent_area_above_reference": { + "trait": "percent area above reference", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "none", + "datatype": "", + "value": 0, + "label": "none" + }, + "area_below_reference": { + "trait": "area below reference", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 65851, + "label": "pixels" + }, + "percent_area_below_reference": { + "trait": "percent area below reference", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "none", + "datatype": "", + "value": 100, + "label": "none" + }, + "blue_frequencies": { + "trait": "blue frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 10, + 14, + 56, + 141, + 290, + 382, + 481, + 725, + 1017, + 1295, + 1683, + 1897, + 2269, + 2446, + 2709, + 2826, + 3149, + 3142, + 2989, + 2943, + 2765, + 2665, + 2110, + 1856, + 1622, + 1576, + 1391, + 1301, + 1288, + 1232, + 1180, + 1110, + 1007, + 886, + 786, + 679, + 605, + 566, + 503, + 465, + 459, + 419, + 379, + 309, + 303, + 288, + 255, + 202, + 203, + 188, + 180, + 158, + 165, + 144, + 150, + 144, + 126, + 141, + 111, + 123, + 111, + 95, + 102, + 80, + 83, + 89, + 70, + 99, + 78, + 72, + 80, + 86, + 74, + 70, + 65, + 58, + 79, + 62, + 68, + 71, + 69, + 69, + 74, + 68, + 52, + 53, + 60, + 67, + 39, + 64, + 54, + 44, + 59, + 59, + 46, + 48, + 46, + 58, + 73, + 58, + 54, + 43, + 58, + 53, + 55, + 58, + 41, + 50, + 48, + 73, + 50, + 35, + 52, + 49, + 42, + 51, + 50, + 49, + 59, + 57, + 40, + 43, + 31, + 51, + 46, + 60, + 41, + 62, + 50, + 57, + 40, + 43, + 41, + 42, + 51, + 37, + 36, + 31, + 39, + 31, + 37, + 38, + 39, + 42, + 26, + 30, + 40, + 23, + 34, + 16, + 30, + 30, + 30, + 29, + 20, + 15, + 16, + 24, + 13, + 17, + 15, + 8, + 9, + 8, + 13, + 9, + 13, + 6, + 7, + 9, + 4, + 5, + 6, + 2, + 4, + 2, + 2, + 5, + 6, + 0, + 0, + 2, + 1, + 0, + 2, + 1, + 1, + 1, + 0, + 1, + 2, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255 + ] + }, + "green_frequencies": { + "trait": "green frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 12, + 12, + 13, + 9, + 14, + 12, + 19, + 31, + 46, + 74, + 103, + 133, + 159, + 222, + 197, + 299, + 319, + 395, + 484, + 547, + 604, + 734, + 850, + 965, + 991, + 1085, + 1243, + 1427, + 1541, + 1619, + 1711, + 1834, + 1861, + 1948, + 1807, + 1833, + 1750, + 1834, + 1712, + 1812, + 1930, + 1946, + 2154, + 2005, + 2014, + 1697, + 1383, + 1201, + 1075, + 965, + 969, + 847, + 917, + 875, + 803, + 713, + 652, + 613, + 574, + 542, + 510, + 448, + 393, + 376, + 341, + 331, + 297, + 245, + 212, + 202, + 179, + 159, + 144, + 130, + 120, + 127, + 110, + 110, + 106, + 105, + 90, + 84, + 93, + 88, + 100, + 88, + 73, + 104, + 75, + 72, + 79, + 79, + 88, + 64, + 75, + 80, + 68, + 75, + 67, + 69, + 63, + 64, + 57, + 68, + 56, + 53, + 67, + 69, + 61, + 57, + 52, + 68, + 52, + 46, + 68, + 53, + 60, + 75, + 46, + 55, + 51, + 54, + 41, + 58, + 61, + 57, + 46, + 44, + 50, + 54, + 61, + 46, + 40, + 35, + 67, + 47, + 45, + 53, + 35, + 52, + 35, + 54, + 51, + 39, + 54, + 43, + 55, + 44, + 37, + 30, + 45, + 41, + 30, + 39, + 37, + 47, + 30, + 26, + 41, + 29, + 36, + 34, + 24, + 27, + 25, + 25, + 25, + 23, + 23, + 15, + 12, + 12, + 12, + 16, + 13, + 12, + 10, + 4, + 14, + 10, + 8, + 6, + 6, + 9, + 13, + 8, + 0, + 3, + 5, + 6, + 0, + 2, + 4, + 1, + 0, + 3, + 1, + 2, + 3, + 0, + 1, + 1, + 0, + 1, + 4, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255 + ] + }, + "red_frequencies": { + "trait": "red frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4, + 8, + 16, + 39, + 68, + 95, + 136, + 180, + 279, + 452, + 544, + 769, + 859, + 1009, + 1041, + 1218, + 1372, + 1446, + 1573, + 1826, + 2054, + 2320, + 2190, + 2194, + 2127, + 2090, + 1989, + 2131, + 2431, + 2169, + 2148, + 1961, + 1898, + 1542, + 1356, + 1267, + 1169, + 1010, + 953, + 825, + 818, + 728, + 674, + 618, + 621, + 556, + 584, + 568, + 560, + 447, + 419, + 346, + 332, + 323, + 312, + 298, + 279, + 243, + 240, + 217, + 198, + 163, + 166, + 173, + 160, + 150, + 128, + 128, + 121, + 136, + 103, + 112, + 110, + 96, + 103, + 118, + 119, + 100, + 99, + 81, + 98, + 95, + 96, + 86, + 95, + 68, + 97, + 74, + 97, + 75, + 90, + 78, + 92, + 83, + 72, + 64, + 64, + 69, + 75, + 71, + 79, + 75, + 66, + 66, + 76, + 77, + 71, + 59, + 50, + 69, + 67, + 66, + 55, + 77, + 58, + 53, + 70, + 73, + 57, + 52, + 74, + 55, + 66, + 79, + 52, + 48, + 55, + 53, + 46, + 63, + 71, + 41, + 52, + 45, + 54, + 41, + 53, + 51, + 59, + 44, + 39, + 43, + 41, + 43, + 43, + 36, + 50, + 44, + 41, + 43, + 43, + 34, + 40, + 36, + 41, + 36, + 35, + 28, + 36, + 28, + 29, + 31, + 23, + 26, + 27, + 27, + 25, + 23, + 19, + 22, + 23, + 16, + 20, + 17, + 14, + 11, + 11, + 6, + 13, + 8, + 5, + 14, + 6, + 11, + 4, + 7, + 5, + 3, + 10, + 3, + 5, + 6, + 2, + 3, + 3, + 2, + 5, + 5, + 3, + 3, + 3, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255 + ] + }, + "lightness_frequencies": { + "trait": "lightness frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 3, + 5, + 8, + 9, + 17, + 6, + 7, + 16, + 20, + 38, + 41, + 89, + 61, + 111, + 188, + 104, + 243, + 289, + 315, + 378, + 187, + 518, + 478, + 678, + 690, + 1142, + 925, + 801, + 911, + 1502, + 1124, + 1184, + 1863, + 1223, + 1818, + 1405, + 2022, + 1773, + 1349, + 1878, + 1742, + 1563, + 1722, + 1690, + 1773, + 1885, + 2070, + 1901, + 2204, + 1321, + 1200, + 1352, + 828, + 1067, + 1010, + 690, + 918, + 829, + 828, + 738, + 655, + 579, + 593, + 512, + 478, + 467, + 506, + 351, + 374, + 277, + 336, + 242, + 301, + 234, + 245, + 186, + 216, + 162, + 135, + 140, + 161, + 114, + 110, + 105, + 81, + 121, + 109, + 124, + 90, + 90, + 100, + 79, + 70, + 99, + 83, + 76, + 84, + 96, + 64, + 88, + 77, + 71, + 82, + 82, + 78, + 63, + 77, + 83, + 61, + 68, + 67, + 57, + 62, + 69, + 63, + 49, + 63, + 50, + 82, + 52, + 63, + 48, + 55, + 72, + 47, + 57, + 56, + 81, + 55, + 63, + 62, + 44, + 60, + 52, + 53, + 64, + 59, + 64, + 38, + 41, + 45, + 56, + 47, + 49, + 56, + 54, + 40, + 56, + 56, + 49, + 39, + 50, + 52, + 46, + 53, + 43, + 48, + 43, + 45, + 40, + 41, + 34, + 45, + 45, + 30, + 33, + 31, + 32, + 45, + 29, + 27, + 26, + 22, + 20, + 15, + 27, + 20, + 17, + 14, + 7, + 11, + 15, + 7, + 16, + 11, + 10, + 11, + 6, + 7, + 8, + 9, + 1, + 3, + 3, + 6, + 3, + 2, + 2, + 3, + 4, + 1, + 0, + 2, + 0, + 2, + 0, + 2, + 0, + 1, + 2, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 0, + 0.39, + 0.78, + 1.18, + 1.57, + 1.96, + 2.35, + 2.75, + 3.14, + 3.53, + 3.92, + 4.31, + 4.71, + 5.1, + 5.49, + 5.88, + 6.27, + 6.67, + 7.06, + 7.45, + 7.84, + 8.24, + 8.63, + 9.02, + 9.41, + 9.8, + 10.2, + 10.59, + 10.98, + 11.37, + 11.76, + 12.16, + 12.55, + 12.94, + 13.33, + 13.73, + 14.12, + 14.51, + 14.9, + 15.29, + 15.69, + 16.08, + 16.47, + 16.86, + 17.25, + 17.65, + 18.04, + 18.43, + 18.82, + 19.22, + 19.61, + 20, + 20.39, + 20.78, + 21.18, + 21.57, + 21.96, + 22.35, + 22.75, + 23.14, + 23.53, + 23.92, + 24.31, + 24.71, + 25.1, + 25.49, + 25.88, + 26.27, + 26.67, + 27.06, + 27.45, + 27.84, + 28.24, + 28.63, + 29.02, + 29.41, + 29.8, + 30.2, + 30.59, + 30.98, + 31.37, + 31.76, + 32.16, + 32.55, + 32.94, + 33.33, + 33.73, + 34.12, + 34.51, + 34.9, + 35.29, + 35.69, + 36.08, + 36.47, + 36.86, + 37.25, + 37.65, + 38.04, + 38.43, + 38.82, + 39.22, + 39.61, + 40, + 40.39, + 40.78, + 41.18, + 41.57, + 41.96, + 42.35, + 42.75, + 43.14, + 43.53, + 43.92, + 44.31, + 44.71, + 45.1, + 45.49, + 45.88, + 46.27, + 46.67, + 47.06, + 47.45, + 47.84, + 48.24, + 48.63, + 49.02, + 49.41, + 49.8, + 50.2, + 50.59, + 50.98, + 51.37, + 51.76, + 52.16, + 52.55, + 52.94, + 53.33, + 53.73, + 54.12, + 54.51, + 54.9, + 55.29, + 55.69, + 56.08, + 56.47, + 56.86, + 57.25, + 57.65, + 58.04, + 58.43, + 58.82, + 59.22, + 59.61, + 60, + 60.39, + 60.78, + 61.18, + 61.57, + 61.96, + 62.35, + 62.75, + 63.14, + 63.53, + 63.92, + 64.31, + 64.71, + 65.1, + 65.49, + 65.88, + 66.27, + 66.67, + 67.06, + 67.45, + 67.84, + 68.24, + 68.63, + 69.02, + 69.41, + 69.8, + 70.2, + 70.59, + 70.98, + 71.37, + 71.76, + 72.16, + 72.55, + 72.94, + 73.33, + 73.73, + 74.12, + 74.51, + 74.9, + 75.29, + 75.69, + 76.08, + 76.47, + 76.86, + 77.25, + 77.65, + 78.04, + 78.43, + 78.82, + 79.22, + 79.61, + 80, + 80.39, + 80.78, + 81.18, + 81.57, + 81.96, + 82.35, + 82.75, + 83.14, + 83.53, + 83.92, + 84.31, + 84.71, + 85.1, + 85.49, + 85.88, + 86.27, + 86.67, + 87.06, + 87.45, + 87.84, + 88.24, + 88.63, + 89.02, + 89.41, + 89.8, + 90.2, + 90.59, + 90.98, + 91.37, + 91.76, + 92.16, + 92.55, + 92.94, + 93.33, + 93.73, + 94.12, + 94.51, + 94.9, + 95.29, + 95.69, + 96.08, + 96.47, + 96.86, + 97.25, + 97.65, + 98.04, + 98.43, + 98.82, + 99.22, + 99.61, + 100 + ] + }, + "green-magenta_frequencies": { + "trait": "green-magenta frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5, + 17, + 76, + 429, + 1525, + 3788, + 6803, + 9196, + 9962, + 8484, + 6621, + 4859, + 3467, + 2368, + 1852, + 1510, + 1199, + 935, + 743, + 577, + 415, + 320, + 247, + 176, + 125, + 80, + 42, + 11, + 12, + 5, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + -128, + -127, + -126, + -125, + -124, + -123, + -122, + -121, + -120, + -119, + -118, + -117, + -116, + -115, + -114, + -113, + -112, + -111, + -110, + -109, + -108, + -107, + -106, + -105, + -104, + -103, + -102, + -101, + -100, + -99, + -98, + -97, + -96, + -95, + -94, + -93, + -92, + -91, + -90, + -89, + -88, + -87, + -86, + -85, + -84, + -83, + -82, + -81, + -80, + -79, + -78, + -77, + -76, + -75, + -74, + -73, + -72, + -71, + -70, + -69, + -68, + -67, + -66, + -65, + -64, + -63, + -62, + -61, + -60, + -59, + -58, + -57, + -56, + -55, + -54, + -53, + -52, + -51, + -50, + -49, + -48, + -47, + -46, + -45, + -44, + -43, + -42, + -41, + -40, + -39, + -38, + -37, + -36, + -35, + -34, + -33, + -32, + -31, + -30, + -29, + -28, + -27, + -26, + -25, + -24, + -23, + -22, + -21, + -20, + -19, + -18, + -17, + -16, + -15, + -14, + -13, + -12, + -11, + -10, + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127 + ] + }, + "blue-yellow_frequencies": { + "trait": "blue-yellow frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 352, + 377, + 472, + 711, + 1009, + 1659, + 2405, + 3140, + 4487, + 5885, + 6881, + 7038, + 6708, + 7055, + 5978, + 4737, + 3087, + 1820, + 886, + 450, + 219, + 148, + 109, + 89, + 59, + 38, + 25, + 15, + 7, + 5, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + -128, + -127, + -126, + -125, + -124, + -123, + -122, + -121, + -120, + -119, + -118, + -117, + -116, + -115, + -114, + -113, + -112, + -111, + -110, + -109, + -108, + -107, + -106, + -105, + -104, + -103, + -102, + -101, + -100, + -99, + -98, + -97, + -96, + -95, + -94, + -93, + -92, + -91, + -90, + -89, + -88, + -87, + -86, + -85, + -84, + -83, + -82, + -81, + -80, + -79, + -78, + -77, + -76, + -75, + -74, + -73, + -72, + -71, + -70, + -69, + -68, + -67, + -66, + -65, + -64, + -63, + -62, + -61, + -60, + -59, + -58, + -57, + -56, + -55, + -54, + -53, + -52, + -51, + -50, + -49, + -48, + -47, + -46, + -45, + -44, + -43, + -42, + -41, + -40, + -39, + -38, + -37, + -36, + -35, + -34, + -33, + -32, + -31, + -30, + -29, + -28, + -27, + -26, + -25, + -24, + -23, + -22, + -21, + -20, + -19, + -18, + -17, + -16, + -15, + -14, + -13, + -12, + -11, + -10, + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127 + ] + }, + "hue_frequencies": { + "trait": "hue frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 4, + 10, + 15, + 22, + 47, + 63, + 114, + 125, + 164, + 209, + 270, + 272, + 406, + 395, + 420, + 511, + 399, + 552, + 590, + 637, + 752, + 843, + 1147, + 1295, + 1972, + 2583, + 3537, + 5285, + 7184, + 8334, + 8292, + 7947, + 4566, + 3209, + 1625, + 848, + 385, + 187, + 96, + 119, + 52, + 48, + 56, + 36, + 31, + 24, + 24, + 26, + 17, + 27, + 18, + 16, + 20, + 12, + 5, + 3, + 3, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 1, + 3, + 5, + 7, + 9, + 11, + 13, + 15, + 17, + 19, + 21, + 23, + 25, + 27, + 29, + 31, + 33, + 35, + 37, + 39, + 41, + 43, + 45, + 47, + 49, + 51, + 53, + 55, + 57, + 59, + 61, + 63, + 65, + 67, + 69, + 71, + 73, + 75, + 77, + 79, + 81, + 83, + 85, + 87, + 89, + 91, + 93, + 95, + 97, + 99, + 101, + 103, + 105, + 107, + 109, + 111, + 113, + 115, + 117, + 119, + 121, + 123, + 125, + 127, + 129, + 131, + 133, + 135, + 137, + 139, + 141, + 143, + 145, + 147, + 149, + 151, + 153, + 155, + 157, + 159, + 161, + 163, + 165, + 167, + 169, + 171, + 173, + 175, + 177, + 179, + 181, + 183, + 185, + 187, + 189, + 191, + 193, + 195, + 197, + 199, + 201, + 203, + 205, + 207, + 209, + 211, + 213, + 215, + 217, + 219, + 221, + 223, + 225, + 227, + 229, + 231, + 233, + 235, + 237, + 239, + 241, + 243, + 245, + 247, + 249, + 251, + 253, + 255, + 257, + 259, + 261, + 263, + 265, + 267, + 269, + 271, + 273, + 275, + 277, + 279, + 281, + 283, + 285, + 287, + 289, + 291, + 293, + 295, + 297, + 299, + 301, + 303, + 305, + 307, + 309, + 311, + 313, + 315, + 317, + 319, + 321, + 323, + 325, + 327, + 329, + 331, + 333, + 335, + 337, + 339, + 341, + 343, + 345, + 347, + 349, + 351, + 353, + 355, + 357, + 359 + ] + }, + "saturation_frequencies": { + "trait": "saturation frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 4, + 5, + 12, + 10, + 13, + 17, + 24, + 27, + 34, + 50, + 55, + 60, + 70, + 78, + 56, + 92, + 56, + 85, + 74, + 104, + 97, + 94, + 84, + 112, + 115, + 102, + 111, + 140, + 119, + 109, + 120, + 135, + 124, + 125, + 119, + 130, + 132, + 111, + 137, + 155, + 135, + 122, + 120, + 129, + 126, + 99, + 116, + 97, + 123, + 119, + 104, + 108, + 120, + 120, + 120, + 113, + 101, + 130, + 111, + 107, + 114, + 134, + 124, + 123, + 130, + 132, + 123, + 160, + 137, + 153, + 168, + 193, + 208, + 137, + 257, + 241, + 234, + 315, + 257, + 319, + 301, + 355, + 369, + 353, + 452, + 425, + 464, + 531, + 521, + 514, + 596, + 625, + 709, + 648, + 753, + 855, + 604, + 1057, + 790, + 717, + 1176, + 687, + 1023, + 917, + 955, + 949, + 971, + 1002, + 862, + 1143, + 1072, + 1047, + 1024, + 1090, + 1230, + 988, + 1179, + 1255, + 1153, + 1223, + 1080, + 1265, + 990, + 1347, + 922, + 1308, + 1071, + 1087, + 968, + 1099, + 1176, + 877, + 868, + 933, + 891, + 745, + 726, + 754, + 693, + 478, + 561, + 657, + 452, + 276, + 487, + 333, + 246, + 213, + 285, + 186, + 195, + 152, + 116, + 97, + 91, + 80, + 73, + 49, + 34, + 34, + 23, + 24, + 13, + 8, + 4, + 5, + 4, + 2, + 3, + 1, + 2, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 0, + 0.39, + 0.78, + 1.18, + 1.57, + 1.96, + 2.35, + 2.75, + 3.14, + 3.53, + 3.92, + 4.31, + 4.71, + 5.1, + 5.49, + 5.88, + 6.27, + 6.67, + 7.06, + 7.45, + 7.84, + 8.24, + 8.63, + 9.02, + 9.41, + 9.8, + 10.2, + 10.59, + 10.98, + 11.37, + 11.76, + 12.16, + 12.55, + 12.94, + 13.33, + 13.73, + 14.12, + 14.51, + 14.9, + 15.29, + 15.69, + 16.08, + 16.47, + 16.86, + 17.25, + 17.65, + 18.04, + 18.43, + 18.82, + 19.22, + 19.61, + 20, + 20.39, + 20.78, + 21.18, + 21.57, + 21.96, + 22.35, + 22.75, + 23.14, + 23.53, + 23.92, + 24.31, + 24.71, + 25.1, + 25.49, + 25.88, + 26.27, + 26.67, + 27.06, + 27.45, + 27.84, + 28.24, + 28.63, + 29.02, + 29.41, + 29.8, + 30.2, + 30.59, + 30.98, + 31.37, + 31.76, + 32.16, + 32.55, + 32.94, + 33.33, + 33.73, + 34.12, + 34.51, + 34.9, + 35.29, + 35.69, + 36.08, + 36.47, + 36.86, + 37.25, + 37.65, + 38.04, + 38.43, + 38.82, + 39.22, + 39.61, + 40, + 40.39, + 40.78, + 41.18, + 41.57, + 41.96, + 42.35, + 42.75, + 43.14, + 43.53, + 43.92, + 44.31, + 44.71, + 45.1, + 45.49, + 45.88, + 46.27, + 46.67, + 47.06, + 47.45, + 47.84, + 48.24, + 48.63, + 49.02, + 49.41, + 49.8, + 50.2, + 50.59, + 50.98, + 51.37, + 51.76, + 52.16, + 52.55, + 52.94, + 53.33, + 53.73, + 54.12, + 54.51, + 54.9, + 55.29, + 55.69, + 56.08, + 56.47, + 56.86, + 57.25, + 57.65, + 58.04, + 58.43, + 58.82, + 59.22, + 59.61, + 60, + 60.39, + 60.78, + 61.18, + 61.57, + 61.96, + 62.35, + 62.75, + 63.14, + 63.53, + 63.92, + 64.31, + 64.71, + 65.1, + 65.49, + 65.88, + 66.27, + 66.67, + 67.06, + 67.45, + 67.84, + 68.24, + 68.63, + 69.02, + 69.41, + 69.8, + 70.2, + 70.59, + 70.98, + 71.37, + 71.76, + 72.16, + 72.55, + 72.94, + 73.33, + 73.73, + 74.12, + 74.51, + 74.9, + 75.29, + 75.69, + 76.08, + 76.47, + 76.86, + 77.25, + 77.65, + 78.04, + 78.43, + 78.82, + 79.22, + 79.61, + 80, + 80.39, + 80.78, + 81.18, + 81.57, + 81.96, + 82.35, + 82.75, + 83.14, + 83.53, + 83.92, + 84.31, + 84.71, + 85.1, + 85.49, + 85.88, + 86.27, + 86.67, + 87.06, + 87.45, + 87.84, + 88.24, + 88.63, + 89.02, + 89.41, + 89.8, + 90.2, + 90.59, + 90.98, + 91.37, + 91.76, + 92.16, + 92.55, + 92.94, + 93.33, + 93.73, + 94.12, + 94.51, + 94.9, + 95.29, + 95.69, + 96.08, + 96.47, + 96.86, + 97.25, + 97.65, + 98.04, + 98.43, + 98.82, + 99.22, + 99.61, + 100 + ] + }, + "value_frequencies": { + "trait": "value frequencies", + "method": "plantcv.plantcv.analyze_color", + "scale": "frequency", + "datatype": "", + "value": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 12, + 11, + 14, + 9, + 13, + 12, + 20, + 26, + 47, + 75, + 104, + 133, + 157, + 224, + 193, + 300, + 322, + 391, + 478, + 546, + 600, + 734, + 852, + 958, + 996, + 1088, + 1241, + 1429, + 1545, + 1616, + 1711, + 1832, + 1860, + 1937, + 1806, + 1832, + 1749, + 1826, + 1712, + 1807, + 1926, + 1944, + 2150, + 1997, + 2012, + 1683, + 1380, + 1189, + 1082, + 958, + 958, + 835, + 914, + 879, + 792, + 698, + 642, + 596, + 561, + 524, + 502, + 433, + 390, + 364, + 329, + 325, + 298, + 241, + 229, + 191, + 171, + 177, + 132, + 120, + 121, + 125, + 100, + 116, + 96, + 111, + 104, + 93, + 86, + 83, + 97, + 90, + 82, + 98, + 71, + 62, + 81, + 91, + 91, + 80, + 87, + 75, + 75, + 80, + 72, + 79, + 65, + 50, + 62, + 71, + 71, + 51, + 76, + 66, + 65, + 72, + 64, + 71, + 54, + 49, + 65, + 58, + 63, + 70, + 51, + 60, + 57, + 52, + 55, + 70, + 50, + 60, + 45, + 56, + 58, + 50, + 60, + 60, + 43, + 35, + 58, + 44, + 48, + 53, + 34, + 47, + 39, + 55, + 52, + 41, + 53, + 48, + 53, + 47, + 41, + 40, + 41, + 49, + 34, + 39, + 36, + 48, + 33, + 29, + 48, + 24, + 39, + 34, + 29, + 29, + 28, + 28, + 28, + 21, + 24, + 18, + 12, + 16, + 18, + 15, + 14, + 15, + 12, + 7, + 15, + 11, + 6, + 12, + 6, + 11, + 14, + 4, + 1, + 3, + 5, + 10, + 2, + 3, + 6, + 4, + 0, + 4, + 1, + 3, + 2, + 0, + 2, + 1, + 1, + 2, + 3, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "label": [ + 0, + 0.39, + 0.78, + 1.18, + 1.57, + 1.96, + 2.35, + 2.75, + 3.14, + 3.53, + 3.92, + 4.31, + 4.71, + 5.1, + 5.49, + 5.88, + 6.27, + 6.67, + 7.06, + 7.45, + 7.84, + 8.24, + 8.63, + 9.02, + 9.41, + 9.8, + 10.2, + 10.59, + 10.98, + 11.37, + 11.76, + 12.16, + 12.55, + 12.94, + 13.33, + 13.73, + 14.12, + 14.51, + 14.9, + 15.29, + 15.69, + 16.08, + 16.47, + 16.86, + 17.25, + 17.65, + 18.04, + 18.43, + 18.82, + 19.22, + 19.61, + 20, + 20.39, + 20.78, + 21.18, + 21.57, + 21.96, + 22.35, + 22.75, + 23.14, + 23.53, + 23.92, + 24.31, + 24.71, + 25.1, + 25.49, + 25.88, + 26.27, + 26.67, + 27.06, + 27.45, + 27.84, + 28.24, + 28.63, + 29.02, + 29.41, + 29.8, + 30.2, + 30.59, + 30.98, + 31.37, + 31.76, + 32.16, + 32.55, + 32.94, + 33.33, + 33.73, + 34.12, + 34.51, + 34.9, + 35.29, + 35.69, + 36.08, + 36.47, + 36.86, + 37.25, + 37.65, + 38.04, + 38.43, + 38.82, + 39.22, + 39.61, + 40, + 40.39, + 40.78, + 41.18, + 41.57, + 41.96, + 42.35, + 42.75, + 43.14, + 43.53, + 43.92, + 44.31, + 44.71, + 45.1, + 45.49, + 45.88, + 46.27, + 46.67, + 47.06, + 47.45, + 47.84, + 48.24, + 48.63, + 49.02, + 49.41, + 49.8, + 50.2, + 50.59, + 50.98, + 51.37, + 51.76, + 52.16, + 52.55, + 52.94, + 53.33, + 53.73, + 54.12, + 54.51, + 54.9, + 55.29, + 55.69, + 56.08, + 56.47, + 56.86, + 57.25, + 57.65, + 58.04, + 58.43, + 58.82, + 59.22, + 59.61, + 60, + 60.39, + 60.78, + 61.18, + 61.57, + 61.96, + 62.35, + 62.75, + 63.14, + 63.53, + 63.92, + 64.31, + 64.71, + 65.1, + 65.49, + 65.88, + 66.27, + 66.67, + 67.06, + 67.45, + 67.84, + 68.24, + 68.63, + 69.02, + 69.41, + 69.8, + 70.2, + 70.59, + 70.98, + 71.37, + 71.76, + 72.16, + 72.55, + 72.94, + 73.33, + 73.73, + 74.12, + 74.51, + 74.9, + 75.29, + 75.69, + 76.08, + 76.47, + 76.86, + 77.25, + 77.65, + 78.04, + 78.43, + 78.82, + 79.22, + 79.61, + 80, + 80.39, + 80.78, + 81.18, + 81.57, + 81.96, + 82.35, + 82.75, + 83.14, + 83.53, + 83.92, + 84.31, + 84.71, + 85.1, + 85.49, + 85.88, + 86.27, + 86.67, + 87.06, + 87.45, + 87.84, + 88.24, + 88.63, + 89.02, + 89.41, + 89.8, + 90.2, + 90.59, + 90.98, + 91.37, + 91.76, + 92.16, + 92.55, + 92.94, + 93.33, + 93.73, + 94.12, + 94.51, + 94.9, + 95.29, + 95.69, + 96.08, + 96.47, + 96.86, + 97.25, + 97.65, + 98.04, + 98.43, + 98.82, + 99.22, + 99.61, + 100 + ] + }, + "hue_circular_mean": { + "trait": "hue circular mean", + "method": "plantcv.plantcv.analyze_color", + "scale": "degrees", + "datatype": "", + "value": 83.67294573455045, + "label": "degrees" + }, + "hue_circular_std": { + "trait": "hue circular standard deviation", + "method": "plantcv.plantcv.analyze_color", + "scale": "degrees", + "datatype": "", + "value": 86, + "label": "degrees" + }, + "hue_median": { + "trait": "hue median", + "method": "plantcv.plantcv.analyze_color", + "scale": "degrees", + "datatype": "", + "value": 86, + "label": "degrees" + } + } + } + } + ] +} diff --git a/tests/parallel_data/appended_results.json b/tests/parallel_data/appended_results.json index 100b415aa..0e4fe56df 100644 --- a/tests/parallel_data/appended_results.json +++ b/tests/parallel_data/appended_results.json @@ -1 +1,252 @@ -{"variables": {"camera": {"category": "metadata", "datatype": ""}, "imgtype": {"category": "metadata", "datatype": ""}, "zoom": {"category": "metadata", "datatype": ""}, "exposure": {"category": "metadata", "datatype": ""}, "gain": {"category": "metadata", "datatype": ""}, "frame": {"category": "metadata", "datatype": ""}, "lifter": {"category": "metadata", "datatype": ""}, "timestamp": {"category": "metadata", "datatype": ""}, "id": {"category": "metadata", "datatype": ""}, "plantbarcode": {"category": "metadata", "datatype": ""}, "treatment": {"category": "metadata", "datatype": ""}, "cartag": {"category": "metadata", "datatype": ""}, "measurementlabel": {"category": "metadata", "datatype": ""}, "other": {"category": "metadata", "datatype": ""}, "image": {"category": "metadata", "datatype": ""}, "test": {"category": "observations", "datatype": ""}}, "entities": [{"metadata": {"camera": {"label": "camera identifier", "datatype": "", "value": "SV"}, "imgtype": {"label": "image type", "datatype": "", "value": "VIS"}, "zoom": {"label": "camera zoom setting", "datatype": "", "value": "z1"}, "exposure": {"label": "camera exposure setting", "datatype": "", "value": "e82"}, "gain": {"label": "camera gain setting", "datatype": "", "value": "g0"}, "frame": {"label": "image series frame identifier", "datatype": "", "value": "0"}, "lifter": {"label": "imaging platform height setting", "datatype": "", "value": "h1"}, "timestamp": {"label": "datetime of image", "datatype": "", "value": "2014-10-22 17:49:35.187"}, "id": {"label": "image identifier", "datatype": "", "value": "117770"}, "plantbarcode": {"label": "plant barcode identifier", "datatype": "", "value": "Ca031AA010564"}, "treatment": {"label": "treatment identifier", "datatype": "", "value": "none"}, "cartag": {"label": "plant carrier identifier", "datatype": "", "value": "2143"}, "measurementlabel": {"label": "experiment identifier", "datatype": "", "value": "C002ch_092214_biomass"}, "other": {"label": "other identifier", "datatype": "", "value": "none"}, "image": {"label": "image file", "datatype": "", "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg"}}, "observations": {"test": {"trait": "test trait", "method": "test", "scale": "none", "datatype": "", "value": "test", "label": "none"}}}, {"metadata": {"camera": {"label": "camera identifier", "datatype": "", "value": "SV"}, "imgtype": {"label": "image type", "datatype": "", "value": "VIS"}, "zoom": {"label": "camera zoom setting", "datatype": "", "value": "z1"}, "exposure": {"label": "camera exposure setting", "datatype": "", "value": "e82"}, "gain": {"label": "camera gain setting", "datatype": "", "value": "g0"}, "frame": {"label": "image series frame identifier", "datatype": "", "value": "0"}, "lifter": {"label": "imaging platform height setting", "datatype": "", "value": "h1"}, "timestamp": {"label": "datetime of image", "datatype": "", "value": "2014-10-22 17:49:35.187"}, "id": {"label": "image identifier", "datatype": "", "value": "117770"}, "plantbarcode": {"label": "plant barcode identifier", "datatype": "", "value": "Ca031AA010564"}, "treatment": {"label": "treatment identifier", "datatype": "", "value": "none"}, "cartag": {"label": "plant carrier identifier", "datatype": "", "value": "2143"}, "measurementlabel": {"label": "experiment identifier", "datatype": "", "value": "C002ch_092214_biomass"}, "other": {"label": "other identifier", "datatype": "", "value": "none"}, "image": {"label": "image file", "datatype": "", "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg"}}, "observations": {"test": {"trait": "test trait", "method": "test", "scale": "none", "datatype": "", "value": "test", "label": "none"}}}]} \ No newline at end of file +{ + "variables": { + "camera": { + "category": "metadata", + "datatype": "" + }, + "imgtype": { + "category": "metadata", + "datatype": "" + }, + "zoom": { + "category": "metadata", + "datatype": "" + }, + "exposure": { + "category": "metadata", + "datatype": "" + }, + "gain": { + "category": "metadata", + "datatype": "" + }, + "frame": { + "category": "metadata", + "datatype": "" + }, + "lifter": { + "category": "metadata", + "datatype": "" + }, + "timestamp": { + "category": "metadata", + "datatype": "" + }, + "id": { + "category": "metadata", + "datatype": "" + }, + "plantbarcode": { + "category": "metadata", + "datatype": "" + }, + "treatment": { + "category": "metadata", + "datatype": "" + }, + "cartag": { + "category": "metadata", + "datatype": "" + }, + "measurementlabel": { + "category": "metadata", + "datatype": "" + }, + "other": { + "category": "metadata", + "datatype": "" + }, + "image": { + "category": "metadata", + "datatype": "" + }, + "test": { + "category": "observations", + "datatype": "" + } + }, + "entities": [ + { + "metadata": { + "camera": { + "label": "camera identifier", + "datatype": "", + "value": "SV" + }, + "imgtype": { + "label": "image type", + "datatype": "", + "value": "VIS" + }, + "zoom": { + "label": "camera zoom setting", + "datatype": "", + "value": "z1" + }, + "exposure": { + "label": "camera exposure setting", + "datatype": "", + "value": "e82" + }, + "gain": { + "label": "camera gain setting", + "datatype": "", + "value": "g0" + }, + "frame": { + "label": "image series frame identifier", + "datatype": "", + "value": "0" + }, + "lifter": { + "label": "imaging platform height setting", + "datatype": "", + "value": "h1" + }, + "timestamp": { + "label": "datetime of image", + "datatype": "", + "value": "2014-10-22 17:49:35.187" + }, + "id": { + "label": "image identifier", + "datatype": "", + "value": "117770" + }, + "plantbarcode": { + "label": "plant barcode identifier", + "datatype": "", + "value": "Ca031AA010564" + }, + "treatment": { + "label": "treatment identifier", + "datatype": "", + "value": "none" + }, + "cartag": { + "label": "plant carrier identifier", + "datatype": "", + "value": "2143" + }, + "measurementlabel": { + "label": "experiment identifier", + "datatype": "", + "value": "C002ch_092214_biomass" + }, + "other": { + "label": "other identifier", + "datatype": "", + "value": "none" + }, + "image": { + "label": "image file", + "datatype": "", + "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg" + } + }, + "observations": { + "prefix": { + "test": { + "trait": "test trait", + "method": "test", + "scale": "none", + "datatype": "", + "value": "test", + "label": "none" + } + } + } + }, + { + "metadata": { + "camera": { + "label": "camera identifier", + "datatype": "", + "value": "SV" + }, + "imgtype": { + "label": "image type", + "datatype": "", + "value": "VIS" + }, + "zoom": { + "label": "camera zoom setting", + "datatype": "", + "value": "z1" + }, + "exposure": { + "label": "camera exposure setting", + "datatype": "", + "value": "e82" + }, + "gain": { + "label": "camera gain setting", + "datatype": "", + "value": "g0" + }, + "frame": { + "label": "image series frame identifier", + "datatype": "", + "value": "0" + }, + "lifter": { + "label": "imaging platform height setting", + "datatype": "", + "value": "h1" + }, + "timestamp": { + "label": "datetime of image", + "datatype": "", + "value": "2014-10-22 17:49:35.187" + }, + "id": { + "label": "image identifier", + "datatype": "", + "value": "117770" + }, + "plantbarcode": { + "label": "plant barcode identifier", + "datatype": "", + "value": "Ca031AA010564" + }, + "treatment": { + "label": "treatment identifier", + "datatype": "", + "value": "none" + }, + "cartag": { + "label": "plant carrier identifier", + "datatype": "", + "value": "2143" + }, + "measurementlabel": { + "label": "experiment identifier", + "datatype": "", + "value": "C002ch_092214_biomass" + }, + "other": { + "label": "other identifier", + "datatype": "", + "value": "none" + }, + "image": { + "label": "image file", + "datatype": "", + "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg" + } + }, + "observations": { + "prefix": { + "test": { + "trait": "test trait", + "method": "test", + "scale": "none", + "datatype": "", + "value": "test", + "label": "none" + } + } + } + } + ] +} diff --git a/tests/parallel_data/new_result.json b/tests/parallel_data/new_result.json index f4e8ef81a..417d4a11b 100644 --- a/tests/parallel_data/new_result.json +++ b/tests/parallel_data/new_result.json @@ -1 +1,161 @@ -{"variables": {"camera": {"category": "metadata", "datatype": ""}, "imgtype": {"category": "metadata", "datatype": ""}, "zoom": {"category": "metadata", "datatype": ""}, "exposure": {"category": "metadata", "datatype": ""}, "gain": {"category": "metadata", "datatype": ""}, "frame": {"category": "metadata", "datatype": ""}, "lifter": {"category": "metadata", "datatype": ""}, "timestamp": {"category": "metadata", "datatype": ""}, "id": {"category": "metadata", "datatype": ""}, "plantbarcode": {"category": "metadata", "datatype": ""}, "treatment": {"category": "metadata", "datatype": ""}, "cartag": {"category": "metadata", "datatype": ""}, "measurementlabel": {"category": "metadata", "datatype": ""}, "other": {"category": "metadata", "datatype": ""}, "image": {"category": "metadata", "datatype": ""}, "test": {"category": "observations", "datatype": ""}}, "entities": [{"metadata": {"camera": {"label": "camera identifier", "datatype": "", "value": "SV"}, "imgtype": {"label": "image type", "datatype": "", "value": "VIS"}, "zoom": {"label": "camera zoom setting", "datatype": "", "value": "z1"}, "exposure": {"label": "camera exposure setting", "datatype": "", "value": "e82"}, "gain": {"label": "camera gain setting", "datatype": "", "value": "g0"}, "frame": {"label": "image series frame identifier", "datatype": "", "value": "0"}, "lifter": {"label": "imaging platform height setting", "datatype": "", "value": "h1"}, "timestamp": {"label": "datetime of image", "datatype": "", "value": "2014-10-22 17:49:35.187"}, "id": {"label": "image identifier", "datatype": "", "value": "117770"}, "plantbarcode": {"label": "plant barcode identifier", "datatype": "", "value": "Ca031AA010564"}, "treatment": {"label": "treatment identifier", "datatype": "", "value": "none"}, "cartag": {"label": "plant carrier identifier", "datatype": "", "value": "2143"}, "measurementlabel": {"label": "experiment identifier", "datatype": "", "value": "C002ch_092214_biomass"}, "other": {"label": "other identifier", "datatype": "", "value": "none"}, "image": {"label": "image file", "datatype": "", "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg"}}, "observations": {"test": {"trait": "test trait", "method": "test", "scale": "none", "datatype": "", "value": "test", "label": "none"}}}]} \ No newline at end of file +{ + "variables": { + "camera": { + "category": "metadata", + "datatype": "" + }, + "imgtype": { + "category": "metadata", + "datatype": "" + }, + "zoom": { + "category": "metadata", + "datatype": "" + }, + "exposure": { + "category": "metadata", + "datatype": "" + }, + "gain": { + "category": "metadata", + "datatype": "" + }, + "frame": { + "category": "metadata", + "datatype": "" + }, + "lifter": { + "category": "metadata", + "datatype": "" + }, + "timestamp": { + "category": "metadata", + "datatype": "" + }, + "id": { + "category": "metadata", + "datatype": "" + }, + "plantbarcode": { + "category": "metadata", + "datatype": "" + }, + "treatment": { + "category": "metadata", + "datatype": "" + }, + "cartag": { + "category": "metadata", + "datatype": "" + }, + "measurementlabel": { + "category": "metadata", + "datatype": "" + }, + "other": { + "category": "metadata", + "datatype": "" + }, + "image": { + "category": "metadata", + "datatype": "" + }, + "test": { + "category": "observations", + "datatype": "" + } + }, + "entities": [ + { + "metadata": { + "camera": { + "label": "camera identifier", + "datatype": "", + "value": "SV" + }, + "imgtype": { + "label": "image type", + "datatype": "", + "value": "VIS" + }, + "zoom": { + "label": "camera zoom setting", + "datatype": "", + "value": "z1" + }, + "exposure": { + "label": "camera exposure setting", + "datatype": "", + "value": "e82" + }, + "gain": { + "label": "camera gain setting", + "datatype": "", + "value": "g0" + }, + "frame": { + "label": "image series frame identifier", + "datatype": "", + "value": "0" + }, + "lifter": { + "label": "imaging platform height setting", + "datatype": "", + "value": "h1" + }, + "timestamp": { + "label": "datetime of image", + "datatype": "", + "value": "2014-10-22 17:49:35.187" + }, + "id": { + "label": "image identifier", + "datatype": "", + "value": "117770" + }, + "plantbarcode": { + "label": "plant barcode identifier", + "datatype": "", + "value": "Ca031AA010564" + }, + "treatment": { + "label": "treatment identifier", + "datatype": "", + "value": "none" + }, + "cartag": { + "label": "plant carrier identifier", + "datatype": "", + "value": "2143" + }, + "measurementlabel": { + "label": "experiment identifier", + "datatype": "", + "value": "C002ch_092214_biomass" + }, + "other": { + "label": "other identifier", + "datatype": "", + "value": "none" + }, + "image": { + "label": "image file", + "datatype": "", + "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg" + } + }, + "observations": { + "prefix": { + "test": { + "trait": "test trait", + "method": "test", + "scale": "none", + "datatype": "", + "value": "test", + "label": "none" + } + } + } + } + ] +} diff --git a/tests/parallel_data/results/VIS_SV_0_z1_h1_g0_e82_117770.jpg.txt b/tests/parallel_data/results/VIS_SV_0_z1_h1_g0_e82_117770.jpg.txt index 193bf0576..1b5cbca8f 100644 --- a/tests/parallel_data/results/VIS_SV_0_z1_h1_g0_e82_117770.jpg.txt +++ b/tests/parallel_data/results/VIS_SV_0_z1_h1_g0_e82_117770.jpg.txt @@ -1 +1,91 @@ -{"metadata": {"camera": {"label": "camera identifier", "datatype": "", "value": "SV"}, "imgtype": {"label": "image type", "datatype": "", "value": "VIS"}, "zoom": {"label": "camera zoom setting", "datatype": "", "value": "z1"}, "exposure": {"label": "camera exposure setting", "datatype": "", "value": "e82"}, "gain": {"label": "camera gain setting", "datatype": "", "value": "g0"}, "frame": {"label": "image series frame identifier", "datatype": "", "value": "0"}, "lifter": {"label": "imaging platform height setting", "datatype": "", "value": "h1"}, "timestamp": {"label": "datetime of image", "datatype": "", "value": "2014-10-22 17:49:35.187"}, "id": {"label": "image identifier", "datatype": "", "value": "117770"}, "plantbarcode": {"label": "plant barcode identifier", "datatype": "", "value": "Ca031AA010564"}, "treatment": {"label": "treatment identifier", "datatype": "", "value": "none"}, "cartag": {"label": "plant carrier identifier", "datatype": "", "value": "2143"}, "measurementlabel": {"label": "experiment identifier", "datatype": "", "value": "C002ch_092214_biomass"}, "other": {"label": "other identifier", "datatype": "", "value": "none"}, "image": {"label": "image file", "datatype": "", "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg"}}, "observations": {"test": {"trait": "test trait", "method": "test", "scale": "none", "datatype": "", "value": "test", "label": "none"}}} \ No newline at end of file +{ + "metadata": { + "camera": { + "label": "camera identifier", + "datatype": "", + "value": "SV" + }, + "imgtype": { + "label": "image type", + "datatype": "", + "value": "VIS" + }, + "zoom": { + "label": "camera zoom setting", + "datatype": "", + "value": "z1" + }, + "exposure": { + "label": "camera exposure setting", + "datatype": "", + "value": "e82" + }, + "gain": { + "label": "camera gain setting", + "datatype": "", + "value": "g0" + }, + "frame": { + "label": "image series frame identifier", + "datatype": "", + "value": "0" + }, + "lifter": { + "label": "imaging platform height setting", + "datatype": "", + "value": "h1" + }, + "timestamp": { + "label": "datetime of image", + "datatype": "", + "value": "2014-10-22 17:49:35.187" + }, + "id": { + "label": "image identifier", + "datatype": "", + "value": "117770" + }, + "plantbarcode": { + "label": "plant barcode identifier", + "datatype": "", + "value": "Ca031AA010564" + }, + "treatment": { + "label": "treatment identifier", + "datatype": "", + "value": "none" + }, + "cartag": { + "label": "plant carrier identifier", + "datatype": "", + "value": "2143" + }, + "measurementlabel": { + "label": "experiment identifier", + "datatype": "", + "value": "C002ch_092214_biomass" + }, + "other": { + "label": "other identifier", + "datatype": "", + "value": "none" + }, + "image": { + "label": "image file", + "datatype": "", + "value": "./snapshots/snapshot57383/VIS_SV_0_z1_h1_g0_e82_117770.jpg" + } + }, + "observations": { + "prefix": { + "test": { + "trait": "test trait", + "method": "test", + "scale": "none", + "datatype": "", + "value": "test", + "label": "none" + } + } + } +} diff --git a/tests/parallel_data/valid.json b/tests/parallel_data/valid.json index 788d8ca13..ec7538b75 100644 --- a/tests/parallel_data/valid.json +++ b/tests/parallel_data/valid.json @@ -1 +1,63 @@ -{"metadata": {}, "observations": {"horizontal_line_position": {"trait": "horizontal_line_position", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "none", "datatype": "", "value": 1756, "label": "none"}, "height_above_bound": {"trait": "height_above_bound", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 661, "label": "pixels"}, "height_below_bound": {"trait": "height_below_bound", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 52, "label": "pixels"}, "above_bound_area": {"trait": "above_bound_area", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 62555, "label": "pixels"}, "percent_above_bound_area": {"trait": "percent_above_bound_area", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "none", "datatype": "", "value": 98.30745536836811, "label": "none"}, "below_bound_area": {"trait": "below_bound_area", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "pixels", "datatype": "", "value": 1077, "label": "pixels"}, "percent_bound_area_below": {"trait": "percent_bound_area_below", "method": "plantcv.plantcv.analyze_bound_horizontal", "scale": "none", "datatype": "", "value": 1.6925446316318833, "label": "none"}}} \ No newline at end of file +{ + "metadata": {}, + "observations": { + "prefix": { + "horizontal_line_position": { + "trait": "horizontal_line_position", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "none", + "datatype": "", + "value": 1756, + "label": "none" + }, + "height_above_bound": { + "trait": "height_above_bound", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 661, + "label": "pixels" + }, + "height_below_bound": { + "trait": "height_below_bound", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 52, + "label": "pixels" + }, + "above_bound_area": { + "trait": "above_bound_area", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 62555, + "label": "pixels" + }, + "percent_above_bound_area": { + "trait": "percent_above_bound_area", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "none", + "datatype": "", + "value": 98.30745536836811, + "label": "none" + }, + "below_bound_area": { + "trait": "below_bound_area", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "pixels", + "datatype": "", + "value": 1077, + "label": "pixels" + }, + "percent_bound_area_below": { + "trait": "percent_bound_area_below", + "method": "plantcv.plantcv.analyze_bound_horizontal", + "scale": "none", + "datatype": "", + "value": 1.6925446316318833, + "label": "none" + } + } + } +} diff --git a/tests/tests.py b/tests/tests.py index 13f3f8953..3d25c6328 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -1046,7 +1046,7 @@ def test_plantcv_acute_vertex(): obj_contour = contours_npz['arr_0'] # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.acute_vertex(obj=obj_contour, win=5, thresh=15, sep=5, img=img) + _ = pcv.acute_vertex(obj=obj_contour, win=5, thresh=15, sep=5, img=img, label="prefix") _ = pcv.acute_vertex(obj=[], win=5, thresh=15, sep=5, img=img) _ = pcv.acute_vertex(obj=[], win=.01, thresh=.01, sep=1, img=img) # Test with debug = "plot" @@ -1069,6 +1069,8 @@ def test_plantcv_acute_vertex_bad_obj(): def test_plantcv_analyze_bound_horizontal(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_bound_horizontal") os.mkdir(cache_dir) @@ -1081,7 +1083,8 @@ def test_plantcv_analyze_bound_horizontal(): object_contours = contours_npz['arr_0'] # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.analyze_bound_horizontal(img=img, obj=object_contours, mask=mask, line_position=300) + _ = pcv.analyze_bound_horizontal(img=img, obj=object_contours, mask=mask, line_position=300, label="prefix") + pcv.outputs.clear() _ = pcv.analyze_bound_horizontal(img=img, obj=object_contours, mask=mask, line_position=100) _ = pcv.analyze_bound_horizontal(img=img_above_bound_only, obj=object_contours, mask=mask, line_position=1756) # Test with debug = "plot" @@ -1093,8 +1096,7 @@ def test_plantcv_analyze_bound_horizontal(): # Copy a test file to the cache directory shutil.copyfile(os.path.join(TEST_DATA, "data_results.txt"), os.path.join(cache_dir, "data_results.txt")) pcv.print_results(os.path.join(cache_dir, "data_results.txt")) - assert len(pcv.outputs.observations) == 7 - pcv.outputs.clear() + assert len(pcv.outputs.observations["default"]) == 7 def test_plantcv_analyze_bound_horizontal_grayscale_image(): @@ -1110,6 +1112,8 @@ def test_plantcv_analyze_bound_horizontal_grayscale_image(): def test_plantcv_analyze_bound_horizontal_neg_y(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_bound_horizontal") os.mkdir(cache_dir) @@ -1126,11 +1130,12 @@ def test_plantcv_analyze_bound_horizontal_neg_y(): _ = pcv.analyze_bound_horizontal(img=img, obj=object_contours, mask=mask, line_position=2056) shutil.copyfile(os.path.join(TEST_DATA, "data_results.txt"), os.path.join(cache_dir, "data_results.txt")) pcv.print_results(os.path.join(cache_dir, "data_results.txt")) - assert pcv.outputs.observations['height_above_reference']['value'] == 713 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['height_above_reference']['value'] == 713 def test_plantcv_analyze_bound_vertical(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_bound_vertical") os.mkdir(cache_dir) @@ -1142,7 +1147,7 @@ def test_plantcv_analyze_bound_vertical(): object_contours = contours_npz['arr_0'] # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=1000) + _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=1000, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=1000) @@ -1150,8 +1155,7 @@ def test_plantcv_analyze_bound_vertical(): pcv.params.debug = None _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=1000) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['width_left_reference']['value'] == 94 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['width_left_reference']['value'] == 94 def test_plantcv_analyze_bound_vertical_grayscale_image(): @@ -1168,11 +1172,13 @@ def test_plantcv_analyze_bound_vertical_grayscale_image(): pcv.params.debug = "plot" _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=1000) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['width_left_reference']['value'] == 94 + assert pcv.outputs.observations['default']['width_left_reference']['value'] == 94 pcv.outputs.clear() def test_plantcv_analyze_bound_vertical_neg_x(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_bound_vertical") os.mkdir(cache_dir) @@ -1186,11 +1192,12 @@ def test_plantcv_analyze_bound_vertical_neg_x(): pcv.params.debug = "plot" _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=2454) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['width_left_reference']['value'] == 441 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['width_left_reference']['value'] == 441 def test_plantcv_analyze_bound_vertical_small_x(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_bound_vertical") os.mkdir(cache_dir) @@ -1204,11 +1211,12 @@ def test_plantcv_analyze_bound_vertical_small_x(): pcv.params.debug = "plot" _ = pcv.analyze_bound_vertical(img=img, obj=object_contours, mask=mask, line_position=1) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['width_right_reference']['value'] == 441 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['width_right_reference']['value'] == 441 def test_plantcv_analyze_color(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_color") os.mkdir(cache_dir) @@ -1219,7 +1227,7 @@ def test_plantcv_analyze_color(): # Test with debug = "print" pcv.params.debug = "print" _ = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type="all") - _ = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type=None) + _ = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type=None, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" @@ -1232,8 +1240,7 @@ def test_plantcv_analyze_color(): pcv.params.debug = None _ = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type='rgb') pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['hue_median']['value'] == 84.0 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['hue_median']['value'] == 84.0 def test_plantcv_analyze_color_incorrect_image(): @@ -1260,6 +1267,8 @@ def test_plantcv_analyze_color_incorrect_hist_plot_type(): def test_plantcv_analyze_nir(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_nir") os.mkdir(cache_dir) @@ -1269,7 +1278,7 @@ def test_plantcv_analyze_nir(): mask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_BINARY), -1) # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.analyze_nir_intensity(gray_img=np.uint16(img), mask=mask, bins=256, histplot=True) + _ = pcv.analyze_nir_intensity(gray_img=np.uint16(img), mask=mask, bins=256, histplot=True, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" _ = pcv.analyze_nir_intensity(gray_img=img, mask=mask, bins=256, histplot=False) @@ -1279,8 +1288,7 @@ def test_plantcv_analyze_nir(): pcv.params.debug = None _ = pcv.analyze_nir_intensity(gray_img=img, mask=mask, bins=256, histplot=True) pcv.print_results(os.path.join(cache_dir, "results.txt")) - result = len(pcv.outputs.observations['nir_frequencies']['value']) - pcv.outputs.clear() + result = len(pcv.outputs.observations['default']['nir_frequencies']['value']) assert result == 256 @@ -1297,7 +1305,7 @@ def test_plantcv_analyze_object(): # max_obj = max(obj_contour, key=len) # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.analyze_object(img=img, obj=obj_contour, mask=mask) + _ = pcv.analyze_object(img=img, obj=obj_contour, mask=mask, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" _ = pcv.analyze_object(img=img, obj=obj_contour, mask=mask) @@ -1410,6 +1418,8 @@ def test_plantcv_analyze_object_small_contour(): def test_plantcv_analyze_thermal_values(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_thermal_values") os.mkdir(cache_dir) @@ -1421,11 +1431,11 @@ def test_plantcv_analyze_thermal_values(): img = contours_npz['arr_0'] # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.analyze_thermal_values(thermal_array=img, mask=mask, histplot=True) + _ = pcv.analyze_thermal_values(thermal_array=img, mask=mask, histplot=True, label="prefix") pcv.params.debug = "plot" thermal_hist = pcv.analyze_thermal_values(thermal_array=img, mask=mask, histplot=True) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert thermal_hist is not None and pcv.outputs.observations['median_temp']['value'] == 33.20922 + assert thermal_hist is not None and pcv.outputs.observations['default']['median_temp']['value'] == 33.20922 def test_plantcv_apply_mask_white(): @@ -2268,6 +2278,8 @@ def test_plantcv_invert(): def test_plantcv_landmark_reference_pt_dist(): + # Clear previous outputs + pcv.outputs.clear() cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_landmark_reference") os.mkdir(cache_dir) points_rescaled = [(0.0139, 0.2569), (0.2361, 0.2917), (0.3542, 0.3819), (0.3542, 0.4167), (0.375, 0.4236), @@ -2280,10 +2292,10 @@ def test_plantcv_landmark_reference_pt_dist(): _ = pcv.landmark_reference_pt_dist(points_r=[], centroid_r=('a', 'b'), bline_r=(0, 0)) _ = pcv.landmark_reference_pt_dist(points_r=[(10, 1000)], centroid_r=(10, 10), bline_r=(10, 10)) _ = pcv.landmark_reference_pt_dist(points_r=[], centroid_r=(0, 0), bline_r=(0, 0)) - pcv.landmark_reference_pt_dist(points_r=points_rescaled, centroid_r=centroid_rescaled, bline_r=bottomline_rescaled) + _ = pcv.landmark_reference_pt_dist(points_r=points_rescaled, centroid_r=centroid_rescaled, + bline_r=bottomline_rescaled, label="prefix") pcv.print_results(os.path.join(cache_dir, "results.txt")) - pcv.outputs.clear() - assert len(pcv.outputs.observations) == 0 + assert len(pcv.outputs.observations['prefix'].keys()) == 8 def test_plantcv_laplace_filter(): @@ -2493,7 +2505,7 @@ def test_plantcv_within_frame(): # Read in test data mask_ib = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_MASK), -1) mask_oob = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_MASK_OOB), -1) - in_bounds_ib = pcv.within_frame(mask=mask_ib, border_width=1) + in_bounds_ib = pcv.within_frame(mask=mask_ib, border_width=1, label="prefix") in_bounds_oob = pcv.within_frame(mask=mask_oob, border_width=1) assert (in_bounds_ib is True and in_bounds_oob is False) @@ -2861,7 +2873,7 @@ def test_plantcv_report_size_marker_detect(): # Test with debug = "print" pcv.params.debug = "print" _ = pcv.report_size_marker_area(img=img, roi_contour=roi_contour, roi_hierarchy=roi_hierarchy, marker='detect', - objcolor='light', thresh_channel='s', thresh=120) + objcolor='light', thresh_channel='s', thresh=120, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" _ = pcv.report_size_marker_area(img=img, roi_contour=roi_contour, roi_hierarchy=roi_hierarchy, marker='detect', @@ -3257,6 +3269,8 @@ def test_plantcv_stdev_filter(): def test_plantcv_watershed_segmentation(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_watershed_segmentation") os.mkdir(cache_dir) @@ -3266,7 +3280,7 @@ def test_plantcv_watershed_segmentation(): mask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_CROPPED_MASK), -1) # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.watershed_segmentation(rgb_img=img, mask=mask, distance=10) + _ = pcv.watershed_segmentation(rgb_img=img, mask=mask, distance=10, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" _ = pcv.watershed_segmentation(rgb_img=img, mask=mask, distance=10) @@ -3274,7 +3288,7 @@ def test_plantcv_watershed_segmentation(): pcv.params.debug = None _ = pcv.watershed_segmentation(rgb_img=img, mask=mask, distance=10) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['estimated_object_count']['value'] > 9 + assert pcv.outputs.observations['default']['estimated_object_count']['value'] > 9 def test_plantcv_white_balance_gray_16bit(): @@ -3387,7 +3401,7 @@ def test_plantcv_x_axis_pseudolandmarks(): _ = pcv.x_axis_pseudolandmarks(obj=obj_contour, mask=mask, img=img) # Test with debug = "plot" pcv.params.debug = "plot" - _ = pcv.x_axis_pseudolandmarks(obj=obj_contour, mask=mask, img=img) + _ = pcv.x_axis_pseudolandmarks(obj=obj_contour, mask=mask, img=img, label="prefix") _ = pcv.x_axis_pseudolandmarks(obj=np.array([[0, 0], [0, 0]]), mask=np.array([[0, 0], [0, 0]]), img=img) _ = pcv.x_axis_pseudolandmarks(obj=np.array(([[89, 222]], [[252, 39]], [[89, 207]])), mask=np.array(([[42, 161]], [[2, 47]], [[211, 222]])), img=img) @@ -3446,7 +3460,7 @@ def test_plantcv_y_axis_pseudolandmarks(): contours_npz = np.load(os.path.join(TEST_DATA, TEST_VIS_COMP_CONTOUR), encoding="latin1") obj_contour = contours_npz['arr_0'] pcv.params.debug = "print" - _ = pcv.y_axis_pseudolandmarks(obj=obj_contour, mask=mask, img=img) + _ = pcv.y_axis_pseudolandmarks(obj=obj_contour, mask=mask, img=img, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" _ = pcv.y_axis_pseudolandmarks(obj=obj_contour, mask=mask, img=img) @@ -3637,6 +3651,8 @@ def test_plantcv_learn_naive_bayes_multiclass(): # Tests for the morphology subpackage # #################################### def test_plantcv_morphology_segment_curvature(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_curvature") os.mkdir(cache_dir) @@ -3645,31 +3661,30 @@ def test_plantcv_morphology_segment_curvature(): pcv.params.debug = "print" segmented_img, seg_objects = pcv.morphology.segment_skeleton(skel_img=skeleton) pcv.outputs.clear() - _ = pcv.morphology.segment_curvature(segmented_img, seg_objects) + _ = pcv.morphology.segment_curvature(segmented_img, seg_objects, label="prefix") pcv.params.debug = "plot" pcv.outputs.clear() _ = pcv.morphology.segment_curvature(segmented_img, seg_objects) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert len(pcv.outputs.observations['segment_curvature']['value']) == 22 - pcv.outputs.clear() + assert len(pcv.outputs.observations['default']['segment_curvature']['value']) == 22 def test_plantcv_morphology_check_cycles(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_branches") os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir mask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_BINARY), -1) pcv.params.debug = "print" - _ = pcv.morphology.check_cycles(mask) + _ = pcv.morphology.check_cycles(mask, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.check_cycles(mask) pcv.params.debug = None _ = pcv.morphology.check_cycles(mask) - print(pcv.outputs.observations["num_cycles"]["value"]) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['num_cycles']['value'] == 1 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['num_cycles']['value'] == 1 def test_plantcv_morphology_find_branch_pts(): @@ -3680,7 +3695,7 @@ def test_plantcv_morphology_find_branch_pts(): mask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_BINARY), -1) skeleton = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_SKELETON), -1) pcv.params.debug = "print" - _ = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=mask) + _ = pcv.morphology.find_branch_pts(skel_img=skeleton, mask=mask, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.find_branch_pts(skel_img=skeleton) pcv.params.debug = None @@ -3696,7 +3711,7 @@ def test_plantcv_morphology_find_tips(): mask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_BINARY), -1) skeleton = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_SKELETON), -1) pcv.params.debug = "print" - _ = pcv.morphology.find_tips(skel_img=skeleton, mask=mask) + _ = pcv.morphology.find_tips(skel_img=skeleton, mask=mask, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.find_tips(skel_img=skeleton) pcv.params.debug = None @@ -3754,6 +3769,8 @@ def test_plantcv_morphology_segment_skeleton(): def test_plantcv_morphology_fill_segments(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_fill_segments") os.mkdir(cache_dir) @@ -3764,18 +3781,19 @@ def test_plantcv_morphology_fill_segments(): for key, val in obj_dic.items(): obj.append(val) pcv.params.debug = "print" - _ = pcv.morphology.fill_segments(mask, obj) + _ = pcv.morphology.fill_segments(mask, obj, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.fill_segments(mask, obj) pcv.print_results(os.path.join(cache_dir, "results.txt")) - tests = [pcv.outputs.observations['segment_area']['value'][42] == 5529, - pcv.outputs.observations['segment_area']['value'][20] == 5057, - pcv.outputs.observations['segment_area']['value'][49] == 3323] + tests = [pcv.outputs.observations['default']['segment_area']['value'][42] == 5529, + pcv.outputs.observations['default']['segment_area']['value'][20] == 5057, + pcv.outputs.observations['default']['segment_area']['value'][49] == 3323] assert all(tests) - pcv.outputs.clear() def test_plantcv_morphology_fill_segments_with_stem(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_fill_segments") os.mkdir(cache_dir) @@ -3790,12 +3808,13 @@ def test_plantcv_morphology_fill_segments_with_stem(): pcv.params.debug = "print" _ = pcv.morphology.fill_segments(mask, obj, stem_obj) pcv.print_results(os.path.join(cache_dir, "results.txt")) - num_objects = len(pcv.outputs.observations['segment_area']['value']) + num_objects = len(pcv.outputs.observations['default']['segment_area']['value']) assert num_objects == 70 - pcv.outputs.clear() def test_plantcv_morphology_segment_angle(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_angles") os.mkdir(cache_dir) @@ -3803,15 +3822,16 @@ def test_plantcv_morphology_segment_angle(): skeleton = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_SKELETON_PRUNED), -1) pcv.params.debug = "print" segmented_img, segment_objects = pcv.morphology.segment_skeleton(skel_img=skeleton) - _ = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=segment_objects) + _ = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=segment_objects, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.segment_angle(segmented_img, segment_objects) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert len(pcv.outputs.observations['segment_angle']['value']) == 22 - pcv.outputs.clear() + assert len(pcv.outputs.observations['default']['segment_angle']['value']) == 22 def test_plantcv_morphology_segment_angle_overflow(): + # Clear previous outputs + pcv.outputs.clear() # Don't prune, would usually give overflow error without extra if statement in segment_angle # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_angles") @@ -3820,11 +3840,12 @@ def test_plantcv_morphology_segment_angle_overflow(): skeleton = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_SKELETON), -1) segmented_img, segment_objects = pcv.morphology.segment_skeleton(skel_img=skeleton) _ = pcv.morphology.segment_angle(segmented_img, segment_objects) - assert len(pcv.outputs.observations['segment_angle']['value']) == 73 - pcv.outputs.clear() + assert len(pcv.outputs.observations['default']['segment_angle']['value']) == 73 def test_plantcv_morphology_segment_euclidean_length(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_eu_length") os.mkdir(cache_dir) @@ -3832,12 +3853,11 @@ def test_plantcv_morphology_segment_euclidean_length(): skeleton = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_SKELETON_PRUNED), -1) pcv.params.debug = "print" segmented_img, segment_objects = pcv.morphology.segment_skeleton(skel_img=skeleton) - _ = pcv.morphology.segment_euclidean_length(segmented_img, segment_objects) + _ = pcv.morphology.segment_euclidean_length(segmented_img, segment_objects, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.segment_euclidean_length(segmented_img, segment_objects) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert len(pcv.outputs.observations['segment_eu_length']['value']) == 22 - pcv.outputs.clear() + assert len(pcv.outputs.observations['default']['segment_eu_length']['value']) == 22 def test_plantcv_morphology_segment_euclidean_length_bad_input(): @@ -3850,6 +3870,8 @@ def test_plantcv_morphology_segment_euclidean_length_bad_input(): def test_plantcv_morphology_segment_path_length(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_path_length") os.mkdir(cache_dir) @@ -3857,12 +3879,11 @@ def test_plantcv_morphology_segment_path_length(): skeleton = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_SKELETON_PRUNED), -1) pcv.params.debug = "print" segmented_img, segment_objects = pcv.morphology.segment_skeleton(skel_img=skeleton) - _ = pcv.morphology.segment_path_length(segmented_img, segment_objects) + _ = pcv.morphology.segment_path_length(segmented_img, segment_objects, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.segment_path_length(segmented_img, segment_objects) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert len(pcv.outputs.observations['segment_path_length']['value']) == 22 - pcv.outputs.clear() + assert len(pcv.outputs.observations['default']['segment_path_length']['value']) == 22 def test_plantcv_morphology_skeletonize(): @@ -3897,6 +3918,8 @@ def test_plantcv_morphology_segment_sort(): def test_plantcv_morphology_segment_tangent_angle(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_tangent_angle") os.mkdir(cache_dir) @@ -3905,12 +3928,11 @@ def test_plantcv_morphology_segment_tangent_angle(): objects = np.load(os.path.join(TEST_DATA, TEST_SKELETON_OBJECTS), encoding="latin1") objs = [objects[arr_n] for arr_n in objects] pcv.params.debug = "print" - _ = pcv.morphology.segment_tangent_angle(skel, objs, 2) + _ = pcv.morphology.segment_tangent_angle(skel, objs, 2, label="prefix") pcv.params.debug = "plot" _ = pcv.morphology.segment_tangent_angle(skel, objs, 2) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert len(pcv.outputs.observations['segment_tangent_angle']['value']) == 73 - pcv.outputs.clear() + assert len(pcv.outputs.observations['default']['segment_tangent_angle']['value']) == 73 def test_plantcv_morphology_segment_id(): @@ -3929,6 +3951,8 @@ def test_plantcv_morphology_segment_id(): def test_plantcv_morphology_segment_insertion_angle(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_insertion_angle") os.mkdir(cache_dir) @@ -3938,14 +3962,14 @@ def test_plantcv_morphology_segment_insertion_angle(): segmented_img, seg_objects = pcv.morphology.segment_skeleton(skel_img=pruned) leaf_obj, stem_obj = pcv.morphology.segment_sort(pruned, seg_objects) pcv.params.debug = "plot" - _ = pcv.morphology.segment_insertion_angle(pruned, segmented_img, leaf_obj, stem_obj, 3) + _ = pcv.morphology.segment_insertion_angle(pruned, segmented_img, leaf_obj, stem_obj, 3, label="prefix") pcv.params.debug = "print" _ = pcv.morphology.segment_insertion_angle(pruned, segmented_img, leaf_obj, stem_obj, 10) pcv.print_results(os.path.join(cache_dir, "results.txt")) - assert pcv.outputs.observations['segment_insertion_angle']['value'][:6] == ['NA', 'NA', 'NA', 24.956918822001636, - 50.7313343343401, - 56.427712102130734] - pcv.outputs.clear() + assert pcv.outputs.observations['default']['segment_insertion_angle']['value'][:6] == ['NA', 'NA', 'NA', + 24.956918822001636, + 50.7313343343401, + 56.427712102130734] def test_plantcv_morphology_segment_insertion_angle_bad_stem(): @@ -3993,6 +4017,8 @@ def test_plantcv_morphology_segment_combine_bad_input(): def test_plantcv_morphology_analyze_stem(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_analyze_stem") os.mkdir(cache_dir) @@ -4002,14 +4028,15 @@ def test_plantcv_morphology_analyze_stem(): segmented_img, seg_objects = pcv.morphology.segment_skeleton(skel_img=pruned) leaf_obj, stem_obj = pcv.morphology.segment_sort(pruned, seg_objects) pcv.params.debug = "plot" - _ = pcv.morphology.analyze_stem(rgb_img=segmented_img, stem_objects=stem_obj) + _ = pcv.morphology.analyze_stem(rgb_img=segmented_img, stem_objects=stem_obj, label="prefix") pcv.params.debug = "print" _ = pcv.morphology.analyze_stem(rgb_img=segmented_img, stem_objects=stem_obj) - assert pcv.outputs.observations['stem_angle']['value'] == -12.531776428222656 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['stem_angle']['value'] == -12.531776428222656 def test_plantcv_morphology_analyze_stem_bad_angle(): + # Clear previous outputs + pcv.outputs.clear() # Test cache directory cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_morphology_segment_insertion_angle") os.mkdir(cache_dir) @@ -4022,8 +4049,7 @@ def test_plantcv_morphology_analyze_stem_bad_angle(): # stem_obj = [stem_obj[3]] stem_obj = [[[[1116, 1728]], [[1116, 1]]]] _ = pcv.morphology.analyze_stem(rgb_img=segmented_img, stem_objects=stem_obj) - assert pcv.outputs.observations['stem_angle']['value'] == 22877334.0 - pcv.outputs.clear() + assert pcv.outputs.observations['default']['stem_angle']['value'] == 22877334.0 # ######################################## @@ -4606,6 +4632,8 @@ def test_plantcv_spectral_index_wi_bad_input(): def test_plantcv_hyperspectral_analyze_spectral(): + # Clear previous outputs + pcv.outputs.clear() cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_hyperspectral_analyze_spectral") os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir @@ -4616,11 +4644,13 @@ def test_plantcv_hyperspectral_analyze_spectral(): pcv.params.debug = "plot" _ = pcv.hyperspectral.analyze_spectral(array=array_data, mask=mask, histplot=True) pcv.params.debug = "print" - _ = pcv.hyperspectral.analyze_spectral(array=array_data, mask=mask, histplot=True) - assert len(pcv.outputs.observations['spectral_frequencies']['value']) == 978 + _ = pcv.hyperspectral.analyze_spectral(array=array_data, mask=mask, histplot=True, label="prefix") + assert len(pcv.outputs.observations['prefix']['spectral_frequencies']['value']) == 978 def test_plantcv_hyperspectral_analyze_index(): + # Clear previous outputs + pcv.outputs.clear() cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_hyperspectral_analyze_index") os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir @@ -4632,10 +4662,12 @@ def test_plantcv_hyperspectral_analyze_index(): pcv.hyperspectral.analyze_index(index_array=index_array, mask=mask_img, histplot=True) pcv.params.debug = "plot" pcv.hyperspectral.analyze_index(index_array=index_array, mask=mask_img, histplot=True) - assert pcv.outputs.observations['mean_index_savi']['value'] > 0 + assert pcv.outputs.observations['default']['mean_index_savi']['value'] > 0 def test_plantcv_hyperspectral_analyze_index_set_range(): + # Clear previous outputs + pcv.outputs.clear() cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_hyperspectral_analyze_index_set_range") os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir @@ -4644,10 +4676,12 @@ def test_plantcv_hyperspectral_analyze_index_set_range(): index_array = pcv.spectral_index.savi(hsi=array_data, distance=801) mask_img = np.ones(np.shape(index_array.array_data), dtype=np.uint8) * 255 pcv.hyperspectral.analyze_index(index_array=index_array, mask=mask_img, histplot=True, min_bin=0, max_bin=1) - assert pcv.outputs.observations['mean_index_savi']['value'] > 0 + assert pcv.outputs.observations['default']['mean_index_savi']['value'] > 0 def test_plantcv_hyperspectral_analyze_index_auto_range(): + # Clear previous outputs + pcv.outputs.clear() cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_hyperspectral_analyze_index_auto_range") os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir @@ -4656,7 +4690,7 @@ def test_plantcv_hyperspectral_analyze_index_auto_range(): index_array = pcv.spectral_index.savi(hsi=array_data, distance=801) mask_img = np.ones(np.shape(index_array.array_data), dtype=np.uint8) * 255 pcv.hyperspectral.analyze_index(index_array=index_array, mask=mask_img, min_bin="auto", max_bin="auto") - assert pcv.outputs.observations['mean_index_savi']['value'] > 0 + assert pcv.outputs.observations['default']['mean_index_savi']['value'] > 0 def test_plantcv_hyperspectral_analyze_index_outside_range_warning(): @@ -4671,7 +4705,7 @@ def test_plantcv_hyperspectral_analyze_index_outside_range_warning(): mask_img = np.ones(np.shape(index_array.array_data), dtype=np.uint8) * 255 f = io.StringIO() with redirect_stdout(f): - pcv.hyperspectral.analyze_index(index_array=index_array, mask=mask_img, min_bin=.5, max_bin=.55) + pcv.hyperspectral.analyze_index(index_array=index_array, mask=mask_img, min_bin=.5, max_bin=.55, label="i") out = f.getvalue() # assert os.listdir(cache_dir) is 0 assert out[0:10] == 'WARNING!!!' @@ -4780,7 +4814,7 @@ def test_plantcv_photosynthesis_analyze_fvfm(): fmask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_FMASK), -1) # Test with debug = "print" pcv.params.debug = "print" - _ = pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=fmask, bins=1000) + _ = pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=fmask, bins=1000, label="prefix") # Test with debug = "plot" pcv.params.debug = "plot" fvfm_images = pcv.photosynthesis.analyze_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=fmask, bins=1000) @@ -4804,6 +4838,8 @@ def test_plantcv_photosynthesis_analyze_fvfm_print_analysis_results(): def test_plantcv_photosynthesis_analyze_fvfm_bad_fdark(): + # Clear previous outputs + pcv.outputs.clear() cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_analyze_fvfm") os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir @@ -4813,8 +4849,7 @@ def test_plantcv_photosynthesis_analyze_fvfm_bad_fdark(): fmax = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_FMAX), -1) fmask = cv2.imread(os.path.join(TEST_DATA, TEST_INPUT_FMASK), -1) _ = pcv.photosynthesis.analyze_fvfm(fdark=fdark + 3000, fmin=fmin, fmax=fmax, mask=fmask, bins=1000) - check = pcv.outputs.observations['fdark_passed_qc']['value'] is False - pcv.outputs.clear() + check = pcv.outputs.observations['default']['fdark_passed_qc']['value'] is False assert check @@ -5383,6 +5418,8 @@ def test_plantcv_transform_find_color_card(): def test_plantcv_transform_find_color_card_optional_parameters(): + # Clear previous outputs + pcv.outputs.clear() # Load rgb image rgb_img = cv2.imread(os.path.join(TEST_DATA, TEST_TARGET_IMG_COLOR_CARD)) # Test cache directory @@ -5391,23 +5428,28 @@ def test_plantcv_transform_find_color_card_optional_parameters(): pcv.params.debug_outdir = cache_dir # Test with threshold ='normal' df1, start1, space1 = pcv.transform.find_color_card(rgb_img=rgb_img, threshold_type='normal', blurry=True, - background='light', threshvalue=90) - _ = pcv.transform.create_color_card_mask(rgb_img=rgb_img, radius=6, start_coord=start1, - spacing=space1, nrows=6, ncols=4, exclude=[20, 0]) - # Test with threshold='otsu' - df2, start2, space2 = pcv.transform.find_color_card(rgb_img=rgb_img, threshold_type='otsu', blurry=True) - _ = pcv.transform.create_color_card_mask(rgb_img=rgb_img, radius=6, start_coord=start2, - spacing=space2, nrows=6, ncols=4, exclude=[20, 0]) - # Test with debug = None - pcv.params.debug = None - mask = pcv.transform.create_color_card_mask(rgb_img=rgb_img, radius=6, start_coord=start2, - spacing=space2, nrows=6, ncols=4, exclude=[20, 0]) - assert all([i == j] for i, j in zip(np.unique(mask), np.array([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, - 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, - 220], dtype=np.uint8))) + background='light', threshvalue=90, label="prefix") + assert pcv.outputs.observations["prefix"]["color_chip_size"]["value"] > 15000 + + +def test_plantcv_transform_find_color_card_otsu(): + # Clear previous outputs + pcv.outputs.clear() + # Load rgb image + rgb_img = cv2.imread(os.path.join(TEST_DATA, TEST_TARGET_IMG_COLOR_CARD)) + # Test cache directory + cache_dir = os.path.join(TEST_TMPDIR, "test_plantcv_transform_find_color_card_otsu") + os.mkdir(cache_dir) + pcv.params.debug_outdir = cache_dir + # Test with threshold ='normal' + df1, start1, space1 = pcv.transform.find_color_card(rgb_img=rgb_img, threshold_type='otsu', blurry=True, + background='light', threshvalue=90, label="prefix") + assert pcv.outputs.observations["prefix"]["color_chip_size"]["value"] > 15000 def test_plantcv_transform_find_color_card_optional_size_parameters(): + # Clear previous outputs + pcv.outputs.clear() # Load rgb image rgb_img = cv2.imread(os.path.join(TEST_DATA, TEST_TARGET_IMG_COLOR_CARD)) # Test cache directory @@ -5415,11 +5457,12 @@ def test_plantcv_transform_find_color_card_optional_size_parameters(): os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir _, _, _ = pcv.transform.find_color_card(rgb_img=rgb_img, record_chip_size="mean") - assert pcv.outputs.observations["color_chip_size"]["value"] > 15000 + assert pcv.outputs.observations["default"]["color_chip_size"]["value"] > 15000 def test_plantcv_transform_find_color_card_optional_size_parameters_none(): - pcv.outputs.observations.clear() + # Clear previous outputs + pcv.outputs.clear() # Load rgb image rgb_img = cv2.imread(os.path.join(TEST_DATA, TEST_TARGET_IMG_COLOR_CARD)) # Test cache directory @@ -5427,15 +5470,17 @@ def test_plantcv_transform_find_color_card_optional_size_parameters_none(): os.mkdir(cache_dir) pcv.params.debug_outdir = cache_dir _, _, _ = pcv.transform.find_color_card(rgb_img=rgb_img, record_chip_size=None) - assert pcv.outputs.observations.get("color_chip_size") is None + assert pcv.outputs.observations.get("default") is None def test_plantcv_transform_find_color_card_bad_record_chip_size(): + # Clear previous outputs + pcv.outputs.clear() # Load rgb image rgb_img = cv2.imread(os.path.join(TEST_DATA, TEST_TARGET_IMG)) pcv.params.debug = None _, _, _ = pcv.transform.find_color_card(rgb_img=rgb_img, record_chip_size='averageeeed') - assert pcv.outputs.observations["color_chip_size"]["value"] is None + assert pcv.outputs.observations["default"]["color_chip_size"]["value"] is None def test_plantcv_transform_find_color_card_bad_thresh_input():