diff --git a/docs/img/documentation_images/sharpen/sharp1.png b/docs/img/documentation_images/sharpen/sharp1.png new file mode 100644 index 000000000..e13d96d37 Binary files /dev/null and b/docs/img/documentation_images/sharpen/sharp1.png differ diff --git a/docs/img/documentation_images/sharpen/sharp5.png b/docs/img/documentation_images/sharpen/sharp5.png new file mode 100644 index 000000000..490174145 Binary files /dev/null and b/docs/img/documentation_images/sharpen/sharp5.png differ diff --git a/docs/sharpen.md b/docs/sharpen.md new file mode 100644 index 000000000..d7779b9a3 --- /dev/null +++ b/docs/sharpen.md @@ -0,0 +1,44 @@ +## Sharpen + +Sharpens an image through the unsharp masking method. Applies a gaussian blur which is subtracted from an exaggerated version of the starting image. + +**plantcv.sharpen**(*img, ksize, amount=1, threshold=0, sigma_x=0, sigma_y=None, roi=None*) + +**returns** sharpened image + +- **Parameters:** + - img - RGB or grayscale image data + - ksize - Tuple of kernel dimensions, e.g. (5, 5). Must be odd integers. + - amount - Integer describing amount of sharpening, higher numbers will sharpen more. + - threshold - Integer cutoff on low contrast, contrasts lower than this will be removed. + - sigma_x - standard deviation in X direction; if 0 (default), calculated from kernel size + - sigma_y - standard deviation in Y direction; if sigma_Y is None (default), sigma_Y is taken to equal sigma_X + - roi - Optional rectangular ROI as returned by [`pcv.roi.rectangle`](roi_rectangle.md) within which to apply this function. (default = None, which uses the entire image) +- **Context:** + - Used to reduce blur in an image + +**Original image** + +![Screenshot](img/documentation_images/threshold_2channels/VIS_TV_z500_h2_g0_e100_163042_0_m.png) + +**Sharpening Image** + +```python + +# Apply sharpening within an ROI to show differences +roi = pcv.roi.rectangle(img, 200, 0, 335, 200) +sharp1 = pcv.sharpen(img, (5, 5), amount=1, roi=roi) + +# Higher amount of sharpening will look more dramatic +sharp5 = pcv.sharpen(img, (5, 5), amount = 5, roi=roi) +``` + +**Sharpen (ksize = (5,5), amount=1, roi=roi)** + +![sharp1](img/documentation_images/sharpen/sharp1.png) + +**Sharpen (ksize = (5,5), amount=5, roi=roi)** + +![sharp5](img/documentation_images/sharpen/sharp5.png) + +**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/main/plantcv/plantcv/sharpen.py) diff --git a/docs/updating.md b/docs/updating.md index dd9789568..e317ef299 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -1156,6 +1156,10 @@ pages for more details on the input and output variable types. * post v3.0dev2: sr_img = **plantcv.scharr_filter**(*gray_img, dx, dy, scale*) * post v4.9: sr_img = **plantcv.scharr_filter**(*gray_img, dx, dy, scale, roi=None*) +#### plantcv.sharpen +* pre v5.0: NA +* post v5.0: img = **plantcv.sharpen**(*img, ksize, amount=1, threshold=0, sigma_x=0, sigma_y=None, roi=None*) + #### plantcv.shift_img * pre v3.0dev2: device, adjusted_img = **plantcv.shift_img**(*img, device, number, side="right", debug=None*) diff --git a/mkdocs.yml b/mkdocs.yml index 92dbbc8d4..95913b369 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -160,6 +160,7 @@ nav: - 'Filter a mask by ROI (quickly)': roi_quick_filter.md - 'Convert ROI to Mask': roi2mask.md - 'Segment Image Series': segment_image_series.md + - 'Sharpen Image': sharpen.md - 'Shift Image': shift.md - 'Spatial Clustering': spatial_clustering.md - 'Spectral Index': spectral_index.md diff --git a/plantcv/plantcv/__init__.py b/plantcv/plantcv/__init__.py index ffce4be62..a28ca9a4d 100644 --- a/plantcv/plantcv/__init__.py +++ b/plantcv/plantcv/__init__.py @@ -4,17 +4,15 @@ __version__ = version("plantcv") from plantcv.plantcv.fatal_error import fatal_error -from plantcv.plantcv.classes import Params -from plantcv.plantcv.classes import Outputs from plantcv.plantcv.classes import Spectral_data from plantcv.plantcv.classes import PSII_data -from plantcv.plantcv.classes import Points +from plantcv.plantcv.classes import Point from plantcv.plantcv.classes import Objects # Initialize an instance of the Params and Outputs class with default values # params and outputs are available when plantcv is imported -params = Params() -outputs = Outputs() +from plantcv.plantcv._globals import Params, Outputs +from plantcv.plantcv._globals import params, outputs from plantcv.plantcv.deprecation_warning import deprecation_warning from plantcv.plantcv.warn import warn @@ -63,6 +61,7 @@ from plantcv.plantcv.canny_edge_detect import canny_edge_detect from plantcv.plantcv.opening import opening from plantcv.plantcv.closing import closing +from plantcv.plantcv.sharpen import sharpen from plantcv.plantcv import roi from plantcv.plantcv import threshold from plantcv.plantcv import visualize @@ -87,12 +86,14 @@ __all__ = [ "fatal_error", - "Params", - "Outputs", "Spectral_data", 'PSII_data', - 'Points', + 'Point', "Objects", + "Params", + "Outputs", + "params", + "outputs", "deprecation_warning", "warn", "print_image", @@ -139,6 +140,7 @@ "canny_edge_detect", "opening", "closing", + "sharpen", "roi", "threshold", "visualize", diff --git a/plantcv/plantcv/_debug.py b/plantcv/plantcv/_debug.py index ce6d539b2..1b228b2ff 100644 --- a/plantcv/plantcv/_debug.py +++ b/plantcv/plantcv/_debug.py @@ -1,5 +1,5 @@ # Debugging module -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import print_image from plantcv.plantcv import plot_image diff --git a/plantcv/plantcv/_globals.py b/plantcv/plantcv/_globals.py new file mode 100644 index 000000000..bb9ac0265 --- /dev/null +++ b/plantcv/plantcv/_globals.py @@ -0,0 +1,315 @@ +# define singletons +import os +import json +import datetime +import altair as alt +import pandas as pd +from importlib.metadata import version +from plantcv.plantcv.fatal_error import fatal_error + + +class Params: + """PlantCV parameters class.""" + + def __init__(self, device=0, debug=None, debug_outdir=".", line_thickness=5, + line_color=(255, 0, 255), dpi=100, text_size=0.55, + text_thickness=2, marker_size=60, color_scale="gist_rainbow", color_sequence="sequential", + sample_label="default", saved_color_scale=None, verbose=True, unit="pixels", px_height=1, px_width=1): + """Initialize parameters. + + Parameters + ---------- + device : int + Device number. Used to count steps in the pipeline. Default is 0. + debug : str, optional + None, print, or plot. Print = save to file, Plot = print to screen. Default is None. + debug_outdir : str + Debug images output directory. Default is ".". + line_thickness : int + Width of line drawings. Default is 5. + line_color : tuple + Color of line annotations. Default is (255, 0, 255). + dpi : int + Figure plotting resolution, dots per inch. Default is 100. + text_size : float + Size of plotting text. Default is 0.55. + text_thickness : int + Thickness of plotting text. Default is 2. + marker_size : int + Size of plotting markers. Default is 60. + color_scale : str + Name of plotting color scale (matplotlib colormap). Default is "gist_rainbow". + color_sequence : str + Build color scales in "sequential" or "random" order. Default is "sequential". + sample_label : str + Sample name prefix. Used in analyze functions. Default is "default". + saved_color_scale : list, optional + Saved color scale that will be applied next time color_palette is called. Default is None. + verbose : bool + Whether or not in verbose mode. Default is True. + unit : str + Units of size trait outputs. Default is "pixels". + px_height : float + Size scaling information about pixel height. Default is 1. + px_width : float + Size scaling information about pixel width. Default is 1. + + """ + self.device = device + self.debug = debug + self.debug_outdir = debug_outdir + self.line_thickness = line_thickness + self.line_color = line_color + self.dpi = dpi + self.text_size = text_size + self.text_thickness = text_thickness + self.marker_size = marker_size + self.color_scale = color_scale + self.color_sequence = color_sequence + self.sample_label = sample_label + self.saved_color_scale = saved_color_scale + self.verbose = verbose + self.unit = unit + self.px_height = px_height + self.px_width = px_width + + +class Outputs: + """PlantCV outputs class""" + + def __init__(self): + self.measurements = {} + self.images = [] + self.observations = {} + self.metadata = {} + + # Add a method to clear measurements + def clear(self): + """Clear all measurements""" + self.measurements = {} + self.images = [] + self.observations = {} + self.metadata = {} + + # Method to add observation to outputs + def add_observation(self, sample, variable, trait, method, scale, datatype, value, label): + """Add an observation to outputs. + + Parameters + ---------- + sample : str + Sample name. Used to distinguish between multiple samples. + variable : str + A local unique identifier of a variable, e.g. a short name, + that is a key linking the definitions of variables with observations. + trait : str + A name of the trait mapped to an external ontology; if there is no exact mapping, an informative + description of the trait. + method : str + A name of the measurement method mapped to an external ontology; if there is no exact mapping, an + informative description of the measurement procedure. + scale : str + Units of the measurement or scale in which the observations are expressed; if possible, standard + units and scales should be used and mapped to existing ontologies; in the case of non-standard + scale a full explanation should be given. + datatype : type + The type of data to be stored, e.g. 'int', 'float', 'str', 'list', 'bool', etc. + value : any + The data itself. + label : str or list + The label for each value (most useful when the data is a frequency table as in hue, + or other tables). + """ + # Create an empty dictionary for the sample if it does not exist + if sample not in self.observations: + self.observations[sample] = {} + + # Validate that the data type is supported by JSON + _ = _validate_data_type(value) + + # Save the observation for the sample and variable + self.observations[sample][variable] = { + "trait": trait, + "method": method, + "scale": scale, + "datatype": str(datatype), + "value": value, + "label": label + } + + # Method to add metadata instance to outputs + def add_metadata(self, term, datatype, value): + """Add a metadata term and value to outputs. + + Parameters + ---------- + term : str + Metadata term/name. + datatype : type + The type of data to be stored, e.g. 'int', 'float', 'str', 'list', 'bool', etc. + value : any + The data itself. + """ + # Create an empty dictionary for the sample if it does not exist + if term not in self.metadata: + self.metadata[term] = {} + + # Validate that the data type is supported by JSON + _ = _validate_data_type(value) + + # Save the observation for the sample and variable + self.metadata[term] = { + "datatype": str(datatype), + "value": [value] + } + + # Method to save observations to a file + def save_results(self, filename, outformat="json"): + """Save results to a file. + + Parameters + ---------- + filename : str + Output filename. + outformat : str + Output file format ("json" or "csv"). Default is "json". + """ + # Add current date & time to metadata in UTC format + run_datetime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") + self.add_metadata(term="run_date", datatype=str, value=run_datetime) + self.add_metadata(term="plantcv_version", datatype=str, value=version("plantcv")) + + if outformat.upper() == "JSON": + if os.path.isfile(filename): + with open(filename, 'r') as f: + hierarchical_data = json.load(f) + hierarchical_data["observations"] = self.observations + existing_metadata = hierarchical_data["metadata"] + for term in self.metadata: + save_term = term + if term in existing_metadata: + save_term = f"{term}_1" + hierarchical_data["metadata"][save_term] = self.metadata[term] + else: + hierarchical_data = {"metadata": self.metadata, "observations": self.observations} + with open(filename, mode='w') as f: + json.dump(hierarchical_data, f) + + elif outformat.upper() == "CSV": + # Open output CSV file + with open(filename, "w") as csv_table: + # Gather any additional metadata + metadata_key_list = list(self.metadata.keys()) + metadata_val_list = [val["value"] for val in self.metadata.values()] + # Write the header + header = metadata_key_list + ["sample", "trait", "value", "label"] + csv_table.write(",".join(map(str, header)) + "\n") + # Iterate over data samples + for sample in self.observations: + # Iterate over traits for each sample + for var in self.observations[sample]: + val = self.observations[sample][var]["value"] + # If the data type is a list or tuple we need to unpack the data + if isinstance(val, (list, tuple)): + # Combine each value with its label + for value, label in zip(self.observations[sample][var]["value"], + self.observations[sample][var]["label"]): + # Skip list of tuple data types + if not isinstance(value, tuple): + # Save one row per value-label + row = metadata_val_list + [sample, var, value, label] + csv_table.write(",".join(map(str, row)) + "\n") + # If the data type is Boolean, store as a numeric 1/0 instead of True/False + elif isinstance(val, bool): + row = metadata_val_list + [sample, var, int(self.observations[sample][var]["value"]), + self.observations[sample][var]["label"]] + csv_table.write(",".join(map(str, row)) + "\n") + # For all other supported data types, save one row per trait + # Assumes no unusual data types are present (possibly a bad assumption) + else: + row = metadata_val_list + [sample, var, self.observations[sample][var]["value"], + self.observations[sample][var]["label"]] + csv_table.write(",".join(map(str, row)) + "\n") + + def plot_dists(self, variable): + """Plot a distribution of data. + Parameters + ---------- + variable : str + A local unique identifier of a variable, e.g. a short name, + + Returns + ------- + chart : altair.vegalite.v4.api.Chart + Altair chart object displaying the distribution of the specified variable + across samples, with faceted rows for each sample and area plot visualization. + """ + alt.data_transformers.disable_max_rows() + data = {"sample": [], "value": [], "label": []} + # Iterate over measurement sample groups + for sample in self.observations: + # If the measurement variable is present in the sample + # And the data type is a list + if variable in self.observations[sample] and "list" in (self.observations[sample][variable]["datatype"]): + data["value"] = data["value"] + self.observations[sample][variable]["value"] + data["label"] = data["label"] + self.observations[sample][variable]["label"] + data["sample"] = data["sample"] + [sample] * len(self.observations[sample][variable]["value"]) + df = pd.DataFrame(data) + step = 10 + overlap = 10 + chart = alt.Chart(df, height=step, width=500).mark_area( + interpolate="monotone", fillOpacity=0.8, stroke='lightgray', strokeWidth=0.5 + ).encode( + alt.X('label:Q').title('Bin labels'), + alt.Y('value:Q').axis(None).scale(range=[step, -step * overlap]) + ).facet( + alt.Row('sample:N').title(None).header(labelAngle=0, labelAlign='left', labelOrient="left") + ).configure_facet( + spacing=0, + columns=1 + ).properties( + bounds='flush' + ).configure_title( + anchor="end" + ).configure_view( + stroke=None + ).configure_axis( + grid=False + ) + return chart + + +def _validate_data_type(data): + """Validate that the data type is supported by JSON. + + Parameters + ---------- + data : any + Data to be validated. + + Returns + ------- + bool + True if the data type is supported by JSON. + + Raises + ------ + ValueError + If the data type is not supported by JSON. + """ + # Supported data types + supported_dtype = ["int", "float", "str", "list", "bool", "tuple", "dict", "NoneType", "numpy.float64"] + # Supported class types + class_list = [f"" for cls in supported_dtype] + + # Send an error message if datatype is not supported by json + if str(type(data)) not in class_list: + # String list of supported types + type_list = ', '.join(map(str, supported_dtype)) + fatal_error(f"The Data type {type(data)} is not compatible with JSON! Please use only these: {type_list}!") + + return True + + +params = Params() +outputs = Outputs() diff --git a/plantcv/plantcv/_helpers.py b/plantcv/plantcv/_helpers.py index 30443d120..a9026d8e1 100644 --- a/plantcv/plantcv/_helpers.py +++ b/plantcv/plantcv/_helpers.py @@ -3,7 +3,7 @@ import math from skimage import morphology from plantcv.plantcv import fatal_error, warn -from plantcv.plantcv import params +from plantcv.plantcv._globals import params import pandas as pd diff --git a/plantcv/plantcv/analyze/bound_horizontal.py b/plantcv/plantcv/analyze/bound_horizontal.py index 20a4e67c0..727eb6eef 100644 --- a/plantcv/plantcv/analyze/bound_horizontal.py +++ b/plantcv/plantcv/analyze/bound_horizontal.py @@ -4,8 +4,7 @@ import numpy as np from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _iterate_analysis, _grayscale_to_rgb, _scale_size -from plantcv.plantcv import params -from plantcv.plantcv import outputs +from plantcv.plantcv._globals import params, outputs def _get_boundary_values(bound_mask, total_area, axis=0): diff --git a/plantcv/plantcv/analyze/bound_vertical.py b/plantcv/plantcv/analyze/bound_vertical.py index 08aea91ab..4dc01e660 100644 --- a/plantcv/plantcv/analyze/bound_vertical.py +++ b/plantcv/plantcv/analyze/bound_vertical.py @@ -4,7 +4,7 @@ from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _iterate_analysis, _grayscale_to_rgb, _scale_size from plantcv.plantcv.analyze.bound_horizontal import _get_boundary_values, _boundary_img_annotation -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs diff --git a/plantcv/plantcv/analyze/color.py b/plantcv/plantcv/analyze/color.py index ea0d1220f..7f3fb8e68 100644 --- a/plantcv/plantcv/analyze/color.py +++ b/plantcv/plantcv/analyze/color.py @@ -4,7 +4,7 @@ import numpy as np from scipy import stats from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import outputs from plantcv.plantcv.visualize import histogram diff --git a/plantcv/plantcv/analyze/size.py b/plantcv/plantcv/analyze/size.py index 1025a3c5c..bc9c54e1e 100644 --- a/plantcv/plantcv/analyze/size.py +++ b/plantcv/plantcv/analyze/size.py @@ -5,7 +5,7 @@ from scipy.spatial.distance import euclidean from plantcv.plantcv._helpers import _iterate_analysis, _cv2_findcontours, _object_composition, _grayscale_to_rgb, _scale_size from plantcv.plantcv import outputs, within_frame -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/apply_mask.py b/plantcv/plantcv/apply_mask.py index 4d6dbe8f9..98c8a467c 100755 --- a/plantcv/plantcv/apply_mask.py +++ b/plantcv/plantcv/apply_mask.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error from plantcv.plantcv.transform import rescale diff --git a/plantcv/plantcv/background_subtraction.py b/plantcv/plantcv/background_subtraction.py index 6d1002084..dce2ed563 100644 --- a/plantcv/plantcv/background_subtraction.py +++ b/plantcv/plantcv/background_subtraction.py @@ -6,7 +6,7 @@ import numpy as np from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error, warn -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def background_subtraction(background_image, foreground_image): diff --git a/plantcv/plantcv/canny_edge_detect.py b/plantcv/plantcv/canny_edge_detect.py index b18b515db..f49b28b0e 100644 --- a/plantcv/plantcv/canny_edge_detect.py +++ b/plantcv/plantcv/canny_edge_detect.py @@ -2,7 +2,7 @@ from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _dilate -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import fatal_error from skimage import feature import numpy as np diff --git a/plantcv/plantcv/classes.py b/plantcv/plantcv/classes.py index 3d5eddc40..67e757198 100644 --- a/plantcv/plantcv/classes.py +++ b/plantcv/plantcv/classes.py @@ -1,315 +1,9 @@ # PlantCV classes -import os import cv2 -import json import numpy as np -import datetime -from plantcv.plantcv import __version__ as ver -from plantcv.plantcv import fatal_error from plantcv.plantcv.annotate.points import _find_closest_pt import matplotlib.pyplot as plt from math import floor -import altair as alt -import pandas as pd - - -class Params: - """PlantCV parameters class.""" - - def __init__(self, device=0, debug=None, debug_outdir=".", line_thickness=5, - line_color=(255, 0, 255), dpi=100, text_size=0.55, - text_thickness=2, marker_size=60, color_scale="gist_rainbow", color_sequence="sequential", - sample_label="default", saved_color_scale=None, verbose=True, unit="pixels", px_height=1, px_width=1): - """Initialize parameters. - - Keyword arguments/parameters: - device = Device number. Used to count steps in the pipeline. (default: 0) - debug = None, print, or plot. Print = save to file, Plot = print to screen. (default: None) - debug_outdir = Debug images output directory. (default: .) - line_thickness = Width of line drawings. (default: 5) - line_color = Color of line annotations (default = (255, 0, 255)) - dpi = Figure plotting resolution, dots per inch. (default: 100) - text_size = Size of plotting text. (default: 0.55) - text_thickness = Thickness of plotting text. (default: 2) - marker_size = Size of plotting markers (default: 60) - color_scale = Name of plotting color scale (matplotlib colormap). (default: gist_rainbow) - color_sequence = Build color scales in "sequential" or "random" order. (default: sequential) - sample_label = Sample name prefix. Used in analyze functions. (default: "default") - saved_color_scale = Saved color scale that will be applied next time color_palette is called. (default: None) - verbose = Whether or not in verbose mode. (default: True) - unit = Units of size trait outputs. (default: "pixels") - px_height = Size scaling information about pixel height (default: 1) - px_width = Size scaling information about pixel width (default: 1) - - - :param device: int - :param debug: str - :param debug_outdir: str - :param line_thickness: numeric - :param dpi: int - :param text_size: float - :param text_thickness: int - :param marker_size: int - :param color_scale: str - :param color_sequence: str - :param sample_label: str - :param saved_color_scale: list - :param verbose: bool - :param unit: str - :param px_height: float - :param px_width: float - - """ - self.device = device - self.debug = debug - self.debug_outdir = debug_outdir - self.line_thickness = line_thickness - self.line_color = line_color - self.dpi = dpi - self.text_size = text_size - self.text_thickness = text_thickness - self.marker_size = marker_size - self.color_scale = color_scale - self.color_sequence = color_sequence - self.sample_label = sample_label - self.saved_color_scale = saved_color_scale - self.verbose = verbose - self.unit = unit - self.px_height = px_height - self.px_width = px_width - - -class Outputs: - """PlantCV outputs class""" - - def __init__(self): - self.measurements = {} - self.images = [] - self.observations = {} - self.metadata = {} - - # Add a method to clear measurements - def clear(self): - """Clear all measurements""" - self.measurements = {} - self.images = [] - self.observations = {} - self.metadata = {} - - # Method to add observation to outputs - 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 - description of the trait. - method = A name of the measurement method mapped to an external ontology; if there is no exact mapping, an - informative description of the measurement procedure - scale = Units of the measurement or scale in which the observations are expressed; if possible, standard - units and scales should be used and mapped to existing ontologies; in the case of non-standard - scale a full explanation should be given - datatype = The type of data to be stored, e.g. 'int', 'float', 'str', 'list', 'bool', etc. - value = The data itself - 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 - :param scale: str - :param datatype: type - :param value: - :param label: - """ - # Create an empty dictionary for the sample if it does not exist - if sample not in self.observations: - self.observations[sample] = {} - - # Validate that the data type is supported by JSON - _ = _validate_data_type(value) - - # Save the observation for the sample and variable - self.observations[sample][variable] = { - "trait": trait, - "method": method, - "scale": scale, - "datatype": str(datatype), - "value": value, - "label": label - } - - # Method to add metadata instance to outputs - def add_metadata(self, term, datatype, value): - """Add a metadata term and value to outputs. - - Parameters - ---------- - term : str - Metadata term/name. - datatype : type - The type of data to be stored, e.g. 'int', 'float', 'str', 'list', 'bool', etc. - value : any - The data itself. - """ - # Create an empty dictionary for the sample if it does not exist - if term not in self.metadata: - self.metadata[term] = {} - - # Validate that the data type is supported by JSON - _ = _validate_data_type(value) - - # Save the observation for the sample and variable - self.metadata[term] = { - "datatype": str(datatype), - "value": [value] - } - - # Method to save observations to a file - def save_results(self, filename, outformat="json"): - """Save results to a file. - - Keyword arguments/parameters: - filename = Output filename - outformat = Output file format ("json" or "csv"). Default = "json" - - :param filename: str - :param outformat: str - """ - # Add current date & time to metadata in UTC format - run_datetime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") - self.add_metadata(term="run_date", datatype=str, value=run_datetime) - self.add_metadata(term="plantcv_version", datatype=str, value=ver) - - if outformat.upper() == "JSON": - if os.path.isfile(filename): - with open(filename, 'r') as f: - hierarchical_data = json.load(f) - hierarchical_data["observations"] = self.observations - existing_metadata = hierarchical_data["metadata"] - for term in self.metadata: - save_term = term - if term in existing_metadata: - save_term = f"{term}_1" - hierarchical_data["metadata"][save_term] = self.metadata[term] - else: - hierarchical_data = {"metadata": self.metadata, "observations": self.observations} - with open(filename, mode='w') as f: - json.dump(hierarchical_data, f) - - elif outformat.upper() == "CSV": - # Open output CSV file - with open(filename, "w") as csv_table: - # Gather any additional metadata - metadata_key_list = list(self.metadata.keys()) - metadata_val_list = [val["value"] for val in self.metadata.values()] - # Write the header - header = metadata_key_list + ["sample", "trait", "value", "label"] - csv_table.write(",".join(map(str, header)) + "\n") - # Iterate over data samples - for sample in self.observations: - # Iterate over traits for each sample - for var in self.observations[sample]: - val = self.observations[sample][var]["value"] - # If the data type is a list or tuple we need to unpack the data - if isinstance(val, (list, tuple)): - # Combine each value with its label - for value, label in zip(self.observations[sample][var]["value"], - self.observations[sample][var]["label"]): - # Skip list of tuple data types - if not isinstance(value, tuple): - # Save one row per value-label - row = metadata_val_list + [sample, var, value, label] - csv_table.write(",".join(map(str, row)) + "\n") - # If the data type is Boolean, store as a numeric 1/0 instead of True/False - elif isinstance(val, bool): - row = metadata_val_list + [sample, var, int(self.observations[sample][var]["value"]), - self.observations[sample][var]["label"]] - csv_table.write(",".join(map(str, row)) + "\n") - # For all other supported data types, save one row per trait - # Assumes no unusual data types are present (possibly a bad assumption) - else: - row = metadata_val_list + [sample, var, self.observations[sample][var]["value"], - self.observations[sample][var]["label"]] - csv_table.write(",".join(map(str, row)) + "\n") - - def plot_dists(self, variable): - """Plot a distribution of data. - - Keyword arguments/parameters: - variable = A local unique identifier of a variable, e.g. a short name, - that is a key linking the definitions of variables with observations. - Returns: - chart = Altair chart object - :param variable: str - :return chart: altair.vegalite.v4.api.Chart - """ - alt.data_transformers.disable_max_rows() - data = {"sample": [], "value": [], "label": []} - # Iterate over measurement sample groups - for sample in self.observations: - # If the measurement variable is present in the sample - # And the data type is a list - if variable in self.observations[sample] and "list" in (self.observations[sample][variable]["datatype"]): - data["value"] = data["value"] + self.observations[sample][variable]["value"] - data["label"] = data["label"] + self.observations[sample][variable]["label"] - data["sample"] = data["sample"] + [sample] * len(self.observations[sample][variable]["value"]) - df = pd.DataFrame(data) - step = 10 - overlap = 10 - chart = alt.Chart(df, height=step, width=500).mark_area( - interpolate="monotone", fillOpacity=0.8, stroke='lightgray', strokeWidth=0.5 - ).encode( - alt.X('label:Q').title('Bin labels'), - alt.Y('value:Q').axis(None).scale(range=[step, -step * overlap]) - ).facet( - alt.Row('sample:N').title(None).header(labelAngle=0, labelAlign='left', labelOrient="left") - ).configure_facet( - spacing=0, - columns=1 - ).properties( - bounds='flush' - ).configure_title( - anchor="end" - ).configure_view( - stroke=None - ).configure_axis( - grid=False - ) - return chart - - -def _validate_data_type(data): - """Validate that the data type is supported by JSON. - - Parameters - ---------- - data : any - Data to be validated. - - Returns - ------- - bool - True if the data type is supported by JSON. - - Raises - ------ - ValueError - If the data type is not supported by JSON. - """ - # Supported data types - supported_dtype = ["int", "float", "str", "list", "bool", "tuple", "dict", "NoneType", "numpy.float64"] - # Supported class types - class_list = [f"" for cls in supported_dtype] - - # Send an error message if datatype is not supported by json - if str(type(data)) not in class_list: - # String list of supported types - type_list = ', '.join(map(str, supported_dtype)) - fatal_error(f"The Data type {type(data)} is not compatible with JSON! Please use only these: {type_list}!") - - return True class Spectral_data: @@ -380,7 +74,7 @@ def add_data(self, protocol): self.__dict__[protocol.name] = protocol -class Points: +class Point: """Point annotation/collection class to use in Jupyter notebooks. It allows the user to interactively click to collect coordinates from an image. Left click collects the point and right click removes the closest collected point diff --git a/plantcv/plantcv/closing.py b/plantcv/plantcv/closing.py index eeaf50fc7..e6bfb2586 100644 --- a/plantcv/plantcv/closing.py +++ b/plantcv/plantcv/closing.py @@ -1,5 +1,5 @@ import os -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _closing diff --git a/plantcv/plantcv/color_palette.py b/plantcv/plantcv/color_palette.py index 75ca8f024..ef091a112 100755 --- a/plantcv/plantcv/color_palette.py +++ b/plantcv/plantcv/color_palette.py @@ -2,7 +2,7 @@ from matplotlib import pyplot as plt import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def color_palette(num, saved=False): diff --git a/plantcv/plantcv/create_labels.py b/plantcv/plantcv/create_labels.py index 03c17edd0..ee3c7554e 100644 --- a/plantcv/plantcv/create_labels.py +++ b/plantcv/plantcv/create_labels.py @@ -4,7 +4,7 @@ import numpy as np from skimage.measure import label from skimage.color import label2rgb -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _roi_filter, _cv2_findcontours diff --git a/plantcv/plantcv/crop.py b/plantcv/plantcv/crop.py index e54f9d634..78ea66ab5 100644 --- a/plantcv/plantcv/crop.py +++ b/plantcv/plantcv/crop.py @@ -3,8 +3,8 @@ import cv2 import numpy as np from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params -from plantcv.plantcv import Spectral_data +from plantcv.plantcv._globals import params +from plantcv.plantcv.classes import Spectral_data def crop(img, x, y, h, w): diff --git a/plantcv/plantcv/crop_position_mask.py b/plantcv/plantcv/crop_position_mask.py index 0380636e8..a746e1db5 100755 --- a/plantcv/plantcv/crop_position_mask.py +++ b/plantcv/plantcv/crop_position_mask.py @@ -7,7 +7,7 @@ from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _cv2_findcontours, _grayscale_to_rgb from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def crop_position_mask(img, mask, x, y, v_pos="top", h_pos="right"): diff --git a/plantcv/plantcv/deprecation_warning.py b/plantcv/plantcv/deprecation_warning.py index 60db64814..2b0cf60af 100644 --- a/plantcv/plantcv/deprecation_warning.py +++ b/plantcv/plantcv/deprecation_warning.py @@ -1,8 +1,8 @@ # Deprecation handling import sys -from plantcv.plantcv import __version__ as version -from plantcv.plantcv import params +from importlib.metadata import version +from plantcv.plantcv._globals import params def deprecation_warning(warning): @@ -13,6 +13,7 @@ def deprecation_warning(warning): :param warning: str """ - warning_msg = f"DeprecationWarning: {warning} Current PlantCV version: {version}" + v = version("plantcv") + warning_msg = f"DeprecationWarning: {warning} Current PlantCV version: {v}" if params.verbose is True: print(warning_msg, file=sys.stderr) diff --git a/plantcv/plantcv/dilate.py b/plantcv/plantcv/dilate.py index c1087f87d..c454df9e5 100755 --- a/plantcv/plantcv/dilate.py +++ b/plantcv/plantcv/dilate.py @@ -3,7 +3,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _dilate -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def dilate(gray_img, ksize, i, roi=None): diff --git a/plantcv/plantcv/distance_transform.py b/plantcv/plantcv/distance_transform.py index 73e4ca78a..b4095b340 100644 --- a/plantcv/plantcv/distance_transform.py +++ b/plantcv/plantcv/distance_transform.py @@ -3,7 +3,7 @@ import cv2 import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def distance_transform(bin_img, distance_type, mask_size): diff --git a/plantcv/plantcv/erode.py b/plantcv/plantcv/erode.py index 4b87b7369..d728393ac 100755 --- a/plantcv/plantcv/erode.py +++ b/plantcv/plantcv/erode.py @@ -3,7 +3,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _erode -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def erode(gray_img, ksize, i, roi=None): diff --git a/plantcv/plantcv/fill.py b/plantcv/plantcv/fill.py index cd6f531a2..01b497f45 100755 --- a/plantcv/plantcv/fill.py +++ b/plantcv/plantcv/fill.py @@ -3,7 +3,7 @@ import numpy as np import os from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace from skimage.morphology import remove_small_objects diff --git a/plantcv/plantcv/fill_holes.py b/plantcv/plantcv/fill_holes.py index 317ff9aec..ccde95f3d 100644 --- a/plantcv/plantcv/fill_holes.py +++ b/plantcv/plantcv/fill_holes.py @@ -4,7 +4,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _rect_filter, _rect_replace from scipy.ndimage import binary_fill_holes diff --git a/plantcv/plantcv/filters/eccentricity.py b/plantcv/plantcv/filters/eccentricity.py index ac0fb4ab5..fd30ab218 100644 --- a/plantcv/plantcv/filters/eccentricity.py +++ b/plantcv/plantcv/filters/eccentricity.py @@ -2,7 +2,7 @@ import os import numpy as np from skimage.measure import label, regionprops -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/flip.py b/plantcv/plantcv/flip.py index 90c57fbfb..a7160a993 100755 --- a/plantcv/plantcv/flip.py +++ b/plantcv/plantcv/flip.py @@ -5,7 +5,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def flip(img, direction): diff --git a/plantcv/plantcv/floodfill.py b/plantcv/plantcv/floodfill.py index 3952eba35..f17074985 100644 --- a/plantcv/plantcv/floodfill.py +++ b/plantcv/plantcv/floodfill.py @@ -5,7 +5,7 @@ from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from skimage.segmentation import flood_fill diff --git a/plantcv/plantcv/gaussian_blur.py b/plantcv/plantcv/gaussian_blur.py index 2eefbb5fa..5f50873f6 100755 --- a/plantcv/plantcv/gaussian_blur.py +++ b/plantcv/plantcv/gaussian_blur.py @@ -5,7 +5,7 @@ import numpy as np from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def gaussian_blur(img, ksize, sigma_x=0, sigma_y=None, roi=None): diff --git a/plantcv/plantcv/hist_equalization.py b/plantcv/plantcv/hist_equalization.py index 1e841eac0..58e905b8e 100755 --- a/plantcv/plantcv/hist_equalization.py +++ b/plantcv/plantcv/hist_equalization.py @@ -5,7 +5,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def hist_equalization(gray_img): diff --git a/plantcv/plantcv/homology/constella.py b/plantcv/plantcv/homology/constella.py index fb74caaec..6631db76b 100644 --- a/plantcv/plantcv/homology/constella.py +++ b/plantcv/plantcv/homology/constella.py @@ -2,7 +2,7 @@ import matplotlib.pyplot as plt from scipy.cluster.hierarchy import dendrogram, linkage from scipy.cluster.hierarchy import cut_tree -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def _get_empty_count(cur_index_id): diff --git a/plantcv/plantcv/homology/constellaqc.py b/plantcv/plantcv/homology/constellaqc.py index 29859786e..9d773dadc 100644 --- a/plantcv/plantcv/homology/constellaqc.py +++ b/plantcv/plantcv/homology/constellaqc.py @@ -1,6 +1,6 @@ import pandas as pd import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def constellaqc(denovo_groups, annotated_groups): diff --git a/plantcv/plantcv/homology/landmark_reference_pt_dist.py b/plantcv/plantcv/homology/landmark_reference_pt_dist.py index 7e8dec303..70749a4ea 100755 --- a/plantcv/plantcv/homology/landmark_reference_pt_dist.py +++ b/plantcv/plantcv/homology/landmark_reference_pt_dist.py @@ -2,7 +2,7 @@ import numpy as np import math import numbers -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs diff --git a/plantcv/plantcv/homology/scale_features.py b/plantcv/plantcv/homology/scale_features.py index 9d049e358..6f07575d4 100755 --- a/plantcv/plantcv/homology/scale_features.py +++ b/plantcv/plantcv/homology/scale_features.py @@ -4,7 +4,7 @@ import cv2 import numpy as np from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _cv2_findcontours, _object_composition diff --git a/plantcv/plantcv/homology/space.py b/plantcv/plantcv/homology/space.py index b0454a8a8..6f6ddd4fe 100644 --- a/plantcv/plantcv/homology/space.py +++ b/plantcv/plantcv/homology/space.py @@ -1,7 +1,7 @@ # Generate a plm multivariate space for downstream use in homology group assignments import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def space(cur_plms, include_bound_dist=False, include_centroid_dist=False, include_orient_angles=False): diff --git a/plantcv/plantcv/homology/starscape.py b/plantcv/plantcv/homology/starscape.py index a57959a84..429936089 100644 --- a/plantcv/plantcv/homology/starscape.py +++ b/plantcv/plantcv/homology/starscape.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as plt import pandas as pd import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def starscape(cur_plms, group_a, group_b, outfile_prefix): diff --git a/plantcv/plantcv/homology/y_axis_pseudolandmarks.py b/plantcv/plantcv/homology/y_axis_pseudolandmarks.py index 5d3a72659..136f7b792 100644 --- a/plantcv/plantcv/homology/y_axis_pseudolandmarks.py +++ b/plantcv/plantcv/homology/y_axis_pseudolandmarks.py @@ -3,7 +3,7 @@ import numpy as np from plantcv.plantcv._helpers import _cv2_findcontours, _object_composition from plantcv.plantcv.homology.x_axis_pseudolandmark import _small_img_pseudolandmarks, _pseudolandmarks -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def y_axis_pseudolandmarks(img, mask, label=None): diff --git a/plantcv/plantcv/hyperspectral/calibrate.py b/plantcv/plantcv/hyperspectral/calibrate.py index 09412f26e..2c41f8abc 100644 --- a/plantcv/plantcv/hyperspectral/calibrate.py +++ b/plantcv/plantcv/hyperspectral/calibrate.py @@ -2,7 +2,7 @@ import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import Spectral_data from plantcv.plantcv.hyperspectral.read_data import _make_pseudo_rgb diff --git a/plantcv/plantcv/hyperspectral/extract_wavelength.py b/plantcv/plantcv/hyperspectral/extract_wavelength.py index f84324e4a..e758076e2 100644 --- a/plantcv/plantcv/hyperspectral/extract_wavelength.py +++ b/plantcv/plantcv/hyperspectral/extract_wavelength.py @@ -2,7 +2,7 @@ import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import Spectral_data from plantcv.plantcv.hyperspectral.read_data import _find_closest diff --git a/plantcv/plantcv/hyperspectral/read_data.py b/plantcv/plantcv/hyperspectral/read_data.py index 6995efbb2..3aca6aaf9 100644 --- a/plantcv/plantcv/hyperspectral/read_data.py +++ b/plantcv/plantcv/hyperspectral/read_data.py @@ -4,11 +4,11 @@ import re import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug -from plantcv.plantcv import Spectral_data +from plantcv.plantcv.classes import Spectral_data from plantcv.plantcv.transform import rescale -from plantcv.plantcv import fatal_error +from plantcv.plantcv.fatal_error import fatal_error def _find_closest(spectral_array, target): diff --git a/plantcv/plantcv/hyperspectral/rot90.py b/plantcv/plantcv/hyperspectral/rot90.py index 2b0293f44..cd4bd10a7 100644 --- a/plantcv/plantcv/hyperspectral/rot90.py +++ b/plantcv/plantcv/hyperspectral/rot90.py @@ -3,7 +3,7 @@ # imports import numpy as np import os -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import Spectral_data diff --git a/plantcv/plantcv/image_add.py b/plantcv/plantcv/image_add.py index 65f9a77ca..1753e2ebd 100755 --- a/plantcv/plantcv/image_add.py +++ b/plantcv/plantcv/image_add.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def image_add(gray_img1, gray_img2): diff --git a/plantcv/plantcv/image_fusion.py b/plantcv/plantcv/image_fusion.py index 006651bb2..3c044d83a 100644 --- a/plantcv/plantcv/image_fusion.py +++ b/plantcv/plantcv/image_fusion.py @@ -3,9 +3,9 @@ import os import numpy as np from skimage import img_as_ubyte -from plantcv.plantcv import fatal_error -from plantcv.plantcv import Spectral_data -from plantcv.plantcv import params +from plantcv.plantcv.fatal_error import fatal_error +from plantcv.plantcv.classes import Spectral_data +from plantcv.plantcv._globals import params from plantcv.plantcv.hyperspectral.read_data import _make_pseudo_rgb from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/image_subtract.py b/plantcv/plantcv/image_subtract.py index 4ac6a54ec..a55cc7241 100755 --- a/plantcv/plantcv/image_subtract.py +++ b/plantcv/plantcv/image_subtract.py @@ -2,7 +2,7 @@ from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _image_subtract -from plantcv.plantcv import params +from plantcv.plantcv._globals import params import os diff --git a/plantcv/plantcv/invert.py b/plantcv/plantcv/invert.py index 7bd38e7ec..ce48fae9a 100755 --- a/plantcv/plantcv/invert.py +++ b/plantcv/plantcv/invert.py @@ -3,7 +3,7 @@ import cv2 import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def invert(gray_img): diff --git a/plantcv/plantcv/kmeans_classifier.py b/plantcv/plantcv/kmeans_classifier.py index f412407bf..215498eb1 100644 --- a/plantcv/plantcv/kmeans_classifier.py +++ b/plantcv/plantcv/kmeans_classifier.py @@ -4,7 +4,7 @@ import numpy as np from joblib import load from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.learn.train_kmeans import patch_extract from plantcv.plantcv._helpers import _logical_operation diff --git a/plantcv/plantcv/laplace_filter.py b/plantcv/plantcv/laplace_filter.py index 9620259c4..cf3a8f2c2 100755 --- a/plantcv/plantcv/laplace_filter.py +++ b/plantcv/plantcv/laplace_filter.py @@ -4,7 +4,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def laplace_filter(gray_img, ksize, scale, roi=None): diff --git a/plantcv/plantcv/logical_and.py b/plantcv/plantcv/logical_and.py index 567f51fe1..999172bf8 100755 --- a/plantcv/plantcv/logical_and.py +++ b/plantcv/plantcv/logical_and.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _logical_operation diff --git a/plantcv/plantcv/logical_or.py b/plantcv/plantcv/logical_or.py index 5df7f4b40..008dbf780 100755 --- a/plantcv/plantcv/logical_or.py +++ b/plantcv/plantcv/logical_or.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _logical_operation diff --git a/plantcv/plantcv/logical_xor.py b/plantcv/plantcv/logical_xor.py index 54c11f46f..7a492565e 100755 --- a/plantcv/plantcv/logical_xor.py +++ b/plantcv/plantcv/logical_xor.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _logical_operation diff --git a/plantcv/plantcv/median_blur.py b/plantcv/plantcv/median_blur.py index 464970e29..faae506d6 100755 --- a/plantcv/plantcv/median_blur.py +++ b/plantcv/plantcv/median_blur.py @@ -3,7 +3,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import fatal_error from scipy.ndimage import median_filter diff --git a/plantcv/plantcv/morphology/analyze_stem.py b/plantcv/plantcv/morphology/analyze_stem.py index e215953b4..afb6ba02c 100644 --- a/plantcv/plantcv/morphology/analyze_stem.py +++ b/plantcv/plantcv/morphology/analyze_stem.py @@ -2,7 +2,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _scale_size diff --git a/plantcv/plantcv/morphology/check_cycles.py b/plantcv/plantcv/morphology/check_cycles.py index f62f884cf..e8b523db0 100644 --- a/plantcv/plantcv/morphology/check_cycles.py +++ b/plantcv/plantcv/morphology/check_cycles.py @@ -2,7 +2,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv import color_palette from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/find_branch_pts.py b/plantcv/plantcv/morphology/find_branch_pts.py index 16ff926d7..71220360d 100644 --- a/plantcv/plantcv/morphology/find_branch_pts.py +++ b/plantcv/plantcv/morphology/find_branch_pts.py @@ -2,7 +2,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _cv2_findcontours, _dilate diff --git a/plantcv/plantcv/morphology/find_tips.py b/plantcv/plantcv/morphology/find_tips.py index 4831c8772..7767266c9 100644 --- a/plantcv/plantcv/morphology/find_tips.py +++ b/plantcv/plantcv/morphology/find_tips.py @@ -1,6 +1,6 @@ """Find tips from skeleton image.""" import os -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _find_tips diff --git a/plantcv/plantcv/morphology/prune.py b/plantcv/plantcv/morphology/prune.py index d2ac2ee0b..97e7faf74 100644 --- a/plantcv/plantcv/morphology/prune.py +++ b/plantcv/plantcv/morphology/prune.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv.morphology import segment_sort, segment_skeleton from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _cv2_findcontours, _iterative_prune, _image_subtract diff --git a/plantcv/plantcv/morphology/segment_angle.py b/plantcv/plantcv/morphology/segment_angle.py index 39232a42f..ad8001a1e 100644 --- a/plantcv/plantcv/morphology/segment_angle.py +++ b/plantcv/plantcv/morphology/segment_angle.py @@ -3,7 +3,7 @@ import cv2 import numpy as np import pandas as pd -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv import color_palette from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/segment_combine.py b/plantcv/plantcv/morphology/segment_combine.py index d732a7ee9..0021f2f57 100644 --- a/plantcv/plantcv/morphology/segment_combine.py +++ b/plantcv/plantcv/morphology/segment_combine.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import fatal_error from plantcv.plantcv import color_palette from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/segment_curvature.py b/plantcv/plantcv/morphology/segment_curvature.py index 94ee8c85b..597085abb 100644 --- a/plantcv/plantcv/morphology/segment_curvature.py +++ b/plantcv/plantcv/morphology/segment_curvature.py @@ -2,7 +2,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv import color_palette from plantcv.plantcv.morphology import segment_path_length diff --git a/plantcv/plantcv/morphology/segment_ends.py b/plantcv/plantcv/morphology/segment_ends.py index bf583c210..56cb07cca 100644 --- a/plantcv/plantcv/morphology/segment_ends.py +++ b/plantcv/plantcv/morphology/segment_ends.py @@ -1,7 +1,7 @@ # Find both segment end coordinates import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _find_segment_ends diff --git a/plantcv/plantcv/morphology/segment_euclidean_length.py b/plantcv/plantcv/morphology/segment_euclidean_length.py index 5fd8599e1..0099b52b9 100644 --- a/plantcv/plantcv/morphology/segment_euclidean_length.py +++ b/plantcv/plantcv/morphology/segment_euclidean_length.py @@ -2,7 +2,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv import fatal_error from plantcv.plantcv import color_palette diff --git a/plantcv/plantcv/morphology/segment_id.py b/plantcv/plantcv/morphology/segment_id.py index 31f7d4a65..936e55ef1 100644 --- a/plantcv/plantcv/morphology/segment_id.py +++ b/plantcv/plantcv/morphology/segment_id.py @@ -3,7 +3,7 @@ import os import cv2 from plantcv.plantcv import color_palette -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/segment_insertion_angle.py b/plantcv/plantcv/morphology/segment_insertion_angle.py index 1439bded5..13bbb2048 100644 --- a/plantcv/plantcv/morphology/segment_insertion_angle.py +++ b/plantcv/plantcv/morphology/segment_insertion_angle.py @@ -2,7 +2,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv import fatal_error from plantcv.plantcv import color_palette diff --git a/plantcv/plantcv/morphology/segment_skeleton.py b/plantcv/plantcv/morphology/segment_skeleton.py index ddd498330..801e392dc 100644 --- a/plantcv/plantcv/morphology/segment_skeleton.py +++ b/plantcv/plantcv/morphology/segment_skeleton.py @@ -2,7 +2,7 @@ import os import cv2 -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import color_palette from plantcv.plantcv.morphology import find_branch_pts from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/segment_sort.py b/plantcv/plantcv/morphology/segment_sort.py index 6d0ddd7fd..ca701048c 100644 --- a/plantcv/plantcv/morphology/segment_sort.py +++ b/plantcv/plantcv/morphology/segment_sort.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _find_tips, _logical_operation, _dilate from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/segment_tangent_angle.py b/plantcv/plantcv/morphology/segment_tangent_angle.py index 7eb66ec85..2e66ad8b6 100644 --- a/plantcv/plantcv/morphology/segment_tangent_angle.py +++ b/plantcv/plantcv/morphology/segment_tangent_angle.py @@ -3,7 +3,7 @@ import cv2 import numpy as np import pandas as pd -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import outputs from plantcv.plantcv import color_palette from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/morphology/skeletonize.py b/plantcv/plantcv/morphology/skeletonize.py index 0b2160057..27032a6cc 100644 --- a/plantcv/plantcv/morphology/skeletonize.py +++ b/plantcv/plantcv/morphology/skeletonize.py @@ -2,7 +2,7 @@ import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from skimage import morphology as skmorph diff --git a/plantcv/plantcv/naive_bayes_classifier.py b/plantcv/plantcv/naive_bayes_classifier.py index c1e556908..306015985 100644 --- a/plantcv/plantcv/naive_bayes_classifier.py +++ b/plantcv/plantcv/naive_bayes_classifier.py @@ -6,7 +6,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def naive_bayes_classifier(rgb_img, pdf_file): diff --git a/plantcv/plantcv/opening.py b/plantcv/plantcv/opening.py index 2df1d2098..0ef3cdbea 100644 --- a/plantcv/plantcv/opening.py +++ b/plantcv/plantcv/opening.py @@ -3,7 +3,7 @@ import os import numpy as np from skimage import morphology -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace from plantcv.plantcv import fatal_error diff --git a/plantcv/plantcv/output_mask_ori_img.py b/plantcv/plantcv/output_mask_ori_img.py index 973ce1bb2..ede8b06dc 100755 --- a/plantcv/plantcv/output_mask_ori_img.py +++ b/plantcv/plantcv/output_mask_ori_img.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv import print_image -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/photosynthesis/read_cropreporter.py b/plantcv/plantcv/photosynthesis/read_cropreporter.py index 10a43c96f..58a3d80c9 100644 --- a/plantcv/plantcv/photosynthesis/read_cropreporter.py +++ b/plantcv/plantcv/photosynthesis/read_cropreporter.py @@ -2,7 +2,7 @@ import os import numpy as np import xarray as xr -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import PSII_data from plantcv.plantcv import Spectral_data diff --git a/plantcv/plantcv/photosynthesis/reassign_frame_labels.py b/plantcv/plantcv/photosynthesis/reassign_frame_labels.py index a37fdc6d1..c89c0ffd8 100644 --- a/plantcv/plantcv/photosynthesis/reassign_frame_labels.py +++ b/plantcv/plantcv/photosynthesis/reassign_frame_labels.py @@ -2,7 +2,7 @@ import numpy as np from plantcv.plantcv import fatal_error from plantcv.plantcv.classes import PSII_data -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def reassign_frame_labels(ps_da, mask): diff --git a/plantcv/plantcv/print_image.py b/plantcv/plantcv/print_image.py index 99cde43b4..8e5790ea4 100755 --- a/plantcv/plantcv/print_image.py +++ b/plantcv/plantcv/print_image.py @@ -4,7 +4,7 @@ import matplotlib from xarray.core.dataarray import DataArray from plantcv.plantcv.classes import PSII_data -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import fatal_error from plantcv.plantcv._show_dataarray import _show_dataarray from altair.vegalite.v5.api import FacetChart, LayerChart, Chart diff --git a/plantcv/plantcv/readbayer.py b/plantcv/plantcv/readbayer.py index 937e14b78..2e3599a16 100644 --- a/plantcv/plantcv/readbayer.py +++ b/plantcv/plantcv/readbayer.py @@ -4,7 +4,7 @@ import cv2 from plantcv.plantcv import fatal_error from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def readbayer(filename, bayerpattern='BG', alg='default'): diff --git a/plantcv/plantcv/readimage.py b/plantcv/plantcv/readimage.py index 2e3fc0fa0..4bf5b52ff 100755 --- a/plantcv/plantcv/readimage.py +++ b/plantcv/plantcv/readimage.py @@ -7,8 +7,8 @@ import nd2 import flyr from plantcv.plantcv import fatal_error -from plantcv.plantcv import params -from plantcv.plantcv.hyperspectral import read_data +from plantcv.plantcv._globals import params +from plantcv.plantcv.hyperspectral.read_data import read_data from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/rgb2gray.py b/plantcv/plantcv/rgb2gray.py index b0530d13b..1fe7f8576 100755 --- a/plantcv/plantcv/rgb2gray.py +++ b/plantcv/plantcv/rgb2gray.py @@ -1,7 +1,7 @@ # RGB -> Gray import os -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rgb2gray diff --git a/plantcv/plantcv/rgb2gray_cmyk.py b/plantcv/plantcv/rgb2gray_cmyk.py index 91c5a2f4a..fcb8b84de 100644 --- a/plantcv/plantcv/rgb2gray_cmyk.py +++ b/plantcv/plantcv/rgb2gray_cmyk.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _rgb2cmyk diff --git a/plantcv/plantcv/rgb2gray_hsv.py b/plantcv/plantcv/rgb2gray_hsv.py index 3f5d238ba..0b280d5a2 100755 --- a/plantcv/plantcv/rgb2gray_hsv.py +++ b/plantcv/plantcv/rgb2gray_hsv.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _rgb2hsv diff --git a/plantcv/plantcv/rgb2gray_lab.py b/plantcv/plantcv/rgb2gray_lab.py index 77f541713..6254227dd 100755 --- a/plantcv/plantcv/rgb2gray_lab.py +++ b/plantcv/plantcv/rgb2gray_lab.py @@ -2,7 +2,7 @@ import os from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._helpers import _rgb2lab diff --git a/plantcv/plantcv/roi/quick_filter.py b/plantcv/plantcv/roi/quick_filter.py index ce035e027..52e579db3 100644 --- a/plantcv/plantcv/roi/quick_filter.py +++ b/plantcv/plantcv/roi/quick_filter.py @@ -4,7 +4,7 @@ import numpy as np from skimage.measure import label from skimage.color import label2rgb -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv.roi import roi2mask from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _logical_operation diff --git a/plantcv/plantcv/roi/roi2mask.py b/plantcv/plantcv/roi/roi2mask.py index b63b0eb59..c74f49e77 100644 --- a/plantcv/plantcv/roi/roi2mask.py +++ b/plantcv/plantcv/roi/roi2mask.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/scharr_filter.py b/plantcv/plantcv/scharr_filter.py index 76a0d62ef..a49d564ca 100755 --- a/plantcv/plantcv/scharr_filter.py +++ b/plantcv/plantcv/scharr_filter.py @@ -4,7 +4,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def scharr_filter(img, dx, dy, scale, roi=None): diff --git a/plantcv/plantcv/segment_image_series.py b/plantcv/plantcv/segment_image_series.py index aa1542ab4..4fd2eb689 100644 --- a/plantcv/plantcv/segment_image_series.py +++ b/plantcv/plantcv/segment_image_series.py @@ -9,7 +9,7 @@ from skimage.color import label2rgb from plantcv.plantcv import readimage from plantcv.plantcv import fill_holes -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rgb2gray diff --git a/plantcv/plantcv/sharpen.py b/plantcv/plantcv/sharpen.py new file mode 100644 index 000000000..4027b01d6 --- /dev/null +++ b/plantcv/plantcv/sharpen.py @@ -0,0 +1,80 @@ +# Sharpen an image + +import cv2 +import os +import numpy as np +from plantcv.plantcv._debug import _debug +from plantcv.plantcv._helpers import _rect_filter, _rect_replace +from plantcv.plantcv._globals import params + + +def sharpen(img, ksize, amount=1, threshold=0, sigma_x=0, sigma_y=None, roi=None): + """Sharpen an image + + Parameters + ---------- + img = numpy.ndarray, RGB or grayscale image + ksize = tuple, kernel dimensions such as (5, 5) + amount = int, degree of sharpening, higher numbers will sharpen the image more + threshold = int, threshold on low contrast. Contrasts below this amount are removed. + sigma_x = int, standard deviation in X direction; if 0, calculated from kernel size + sigma_y = str or int, standard deviation in Y direction; if sigmaY is None, sigmaY is taken to equal sigmaX + roi = Optional rectangular ROI to apply sharpening in + + Returns + ------- + sharp_img = numpy.ndarray, + sharpened image + """ + sub_sharp_img = _rect_filter(img, roi, _unsharp_masking, + **{"ksize": ksize, + "sigma_x": sigma_x, + "sigma_y": sigma_y, + "amount": amount, + "threshold": threshold}) + sharp_img = _rect_replace(img, sub_sharp_img, roi) + + if len(np.shape(sharp_img)) == 3: + cmap = None + else: + cmap = 'gray' + + _debug(visual=sharp_img, + filename=os.path.join(params.debug_outdir, str(params.device) + '_sharpen.png'), + cmap=cmap) + + return sharp_img + + +def _unsharp_masking(img, ksize, amount=1, threshold=0, sigma_x=0, sigma_y=None): + """Helper function to sharpen image using unsharp masking + Parameters + ---------- + img = numpy.ndarray, RGB or grayscale image + ksize = tuple, kernel dimensions such as (5, 5) + amount = int, degree of sharpening, higher numbers will sharpen the image more + threshold = int, threshold on low contrast. Contrasts below this amount are removed. + sigma_x = int, + passed to gaussian_blur. Standard deviation in X direction; if 0, calculated from kernel size + sigma_y = None or int, + passed to gaussian blur. standard deviation in Y direction; if sigmaY is None, sigmaY is taken to equal sigmaX + + Returns + ------- + sharpened = numpy.ndarray, + sharpened image + """ + blurred = cv2.GaussianBlur(img, ksize=ksize, sigmaX=sigma_x, sigmaY=sigma_y) + # subtract blurry image from "saturated" version of image + sharpened_unscaled = float(amount + 1) * img - float(amount) * blurred + # ensure image is [0, 255] + sharpened_unscaled = np.maximum(sharpened_unscaled, np.zeros(sharpened_unscaled.shape)) + sharpened_float = np.minimum(sharpened_unscaled, 255 * np.ones(sharpened_unscaled.shape)) + # ensure image is uint8 + sharpened = sharpened_float.round().astype(np.uint8) + # if threshold is greater than 0 then remove contrasts below that amount + if threshold > 0: + low_contrast_mask = np.absolute(img - blurred) < threshold + np.copyto(sharpened, img, where=low_contrast_mask) + + return sharpened diff --git a/plantcv/plantcv/shift_img.py b/plantcv/plantcv/shift_img.py index fb687d144..70b6b8ca7 100755 --- a/plantcv/plantcv/shift_img.py +++ b/plantcv/plantcv/shift_img.py @@ -4,7 +4,7 @@ import numpy as np from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def shift_img(img, number, side="right"): diff --git a/plantcv/plantcv/sobel_filter.py b/plantcv/plantcv/sobel_filter.py index ab1c06be9..05532a140 100755 --- a/plantcv/plantcv/sobel_filter.py +++ b/plantcv/plantcv/sobel_filter.py @@ -4,7 +4,7 @@ import os from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def sobel_filter(gray_img, dx, dy, ksize, roi=None): diff --git a/plantcv/plantcv/spatial_clustering.py b/plantcv/plantcv/spatial_clustering.py index f3b4ea875..7a6afdd81 100644 --- a/plantcv/plantcv/spatial_clustering.py +++ b/plantcv/plantcv/spatial_clustering.py @@ -5,7 +5,7 @@ from sklearn.cluster import DBSCAN from sklearn.cluster import OPTICS from sklearn.preprocessing import StandardScaler -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import color_palette diff --git a/plantcv/plantcv/spectral_index/spectral_index.py b/plantcv/plantcv/spectral_index/spectral_index.py index 122cbd487..42db4f1a5 100644 --- a/plantcv/plantcv/spectral_index/spectral_index.py +++ b/plantcv/plantcv/spectral_index/spectral_index.py @@ -3,7 +3,7 @@ import os import numpy as np import cv2 -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import warn from plantcv.plantcv import Spectral_data diff --git a/plantcv/plantcv/stdev_filter.py b/plantcv/plantcv/stdev_filter.py index 1b9f87b48..147e36c46 100644 --- a/plantcv/plantcv/stdev_filter.py +++ b/plantcv/plantcv/stdev_filter.py @@ -5,7 +5,7 @@ import numpy as np from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _rect_filter, _rect_replace -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from scipy.ndimage import generic_filter diff --git a/plantcv/plantcv/transform/checkerboard_calib.py b/plantcv/plantcv/transform/checkerboard_calib.py index 6c5a631e4..0c2d8bb68 100644 --- a/plantcv/plantcv/transform/checkerboard_calib.py +++ b/plantcv/plantcv/transform/checkerboard_calib.py @@ -4,7 +4,7 @@ import os import numpy as np from plantcv.plantcv.readimage import readimage -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv.transform.color_correction import save_matrix from plantcv.plantcv.transform.color_correction import load_matrix diff --git a/plantcv/plantcv/transform/gamma_correct.py b/plantcv/plantcv/transform/gamma_correct.py index 62423afda..9ff0d56c4 100644 --- a/plantcv/plantcv/transform/gamma_correct.py +++ b/plantcv/plantcv/transform/gamma_correct.py @@ -2,7 +2,7 @@ import os from skimage import exposure -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/transform/merge_images.py b/plantcv/plantcv/transform/merge_images.py index b87a2a1f0..11933d718 100644 --- a/plantcv/plantcv/transform/merge_images.py +++ b/plantcv/plantcv/transform/merge_images.py @@ -4,7 +4,7 @@ import os import numpy as np import random -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/transform/nonuniform_illumination.py b/plantcv/plantcv/transform/nonuniform_illumination.py index 717226e35..cab02f116 100644 --- a/plantcv/plantcv/transform/nonuniform_illumination.py +++ b/plantcv/plantcv/transform/nonuniform_illumination.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import gaussian_blur from plantcv.plantcv.transform import rescale from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/transform/rescale.py b/plantcv/plantcv/transform/rescale.py index 64fa82dc5..5938adb3c 100644 --- a/plantcv/plantcv/transform/rescale.py +++ b/plantcv/plantcv/transform/rescale.py @@ -3,7 +3,7 @@ import os import numpy as np from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/transform/resize.py b/plantcv/plantcv/transform/resize.py index 86de60ec9..4edfb06f7 100755 --- a/plantcv/plantcv/transform/resize.py +++ b/plantcv/plantcv/transform/resize.py @@ -4,7 +4,7 @@ import os import numpy as np from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/transform/rotate.py b/plantcv/plantcv/transform/rotate.py index 173c9a5e2..fffa5cc04 100644 --- a/plantcv/plantcv/transform/rotate.py +++ b/plantcv/plantcv/transform/rotate.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/transform/warp.py b/plantcv/plantcv/transform/warp.py index 2441632c7..d0bf97d41 100644 --- a/plantcv/plantcv/transform/warp.py +++ b/plantcv/plantcv/transform/warp.py @@ -3,7 +3,7 @@ import cv2 import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error from plantcv.plantcv import color_palette diff --git a/plantcv/plantcv/visualize/auto_threshold_methods.py b/plantcv/plantcv/visualize/auto_threshold_methods.py index 6f1982f2c..48c7d8eab 100644 --- a/plantcv/plantcv/visualize/auto_threshold_methods.py +++ b/plantcv/plantcv/visualize/auto_threshold_methods.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv.transform import resize_factor from plantcv.plantcv import fatal_error from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/visualize/colorize_label_img.py b/plantcv/plantcv/visualize/colorize_label_img.py index c9111dc19..7603cb4bd 100644 --- a/plantcv/plantcv/visualize/colorize_label_img.py +++ b/plantcv/plantcv/visualize/colorize_label_img.py @@ -2,7 +2,7 @@ import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import color_palette from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/visualize/colorize_masks.py b/plantcv/plantcv/visualize/colorize_masks.py index bd8c9af4b..25d2a76fe 100644 --- a/plantcv/plantcv/visualize/colorize_masks.py +++ b/plantcv/plantcv/visualize/colorize_masks.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv import fatal_error diff --git a/plantcv/plantcv/visualize/colorspaces.py b/plantcv/plantcv/visualize/colorspaces.py index 028ad1e89..3034b04e2 100644 --- a/plantcv/plantcv/visualize/colorspaces.py +++ b/plantcv/plantcv/visualize/colorspaces.py @@ -3,7 +3,7 @@ import os import cv2 import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv.transform import resize_factor from plantcv.plantcv import fatal_error from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/visualize/histogram.py b/plantcv/plantcv/visualize/histogram.py index b92e44382..ae68689cb 100755 --- a/plantcv/plantcv/visualize/histogram.py +++ b/plantcv/plantcv/visualize/histogram.py @@ -1,7 +1,7 @@ """Visualize histograms from image data.""" import os import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import fatal_error from plantcv.plantcv._debug import _debug import pandas as pd diff --git a/plantcv/plantcv/visualize/obj_size_ecdf.py b/plantcv/plantcv/visualize/obj_size_ecdf.py index 52c4f0c05..6963ede5a 100644 --- a/plantcv/plantcv/visualize/obj_size_ecdf.py +++ b/plantcv/plantcv/visualize/obj_size_ecdf.py @@ -2,7 +2,7 @@ import os import cv2 import pandas as pd -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug from plantcv.plantcv._helpers import _cv2_findcontours from statsmodels.distributions.empirical_distribution import ECDF diff --git a/plantcv/plantcv/visualize/obj_sizes.py b/plantcv/plantcv/visualize/obj_sizes.py index 0a380fb3d..a3c9b906b 100644 --- a/plantcv/plantcv/visualize/obj_sizes.py +++ b/plantcv/plantcv/visualize/obj_sizes.py @@ -4,7 +4,7 @@ import cv2 import random import numpy as np -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import color_palette from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/visualize/overlay_two_imgs.py b/plantcv/plantcv/visualize/overlay_two_imgs.py index fe9825de6..93b7d8283 100644 --- a/plantcv/plantcv/visualize/overlay_two_imgs.py +++ b/plantcv/plantcv/visualize/overlay_two_imgs.py @@ -6,7 +6,7 @@ from skimage import img_as_ubyte from plantcv.plantcv import fatal_error from plantcv.plantcv._debug import _debug -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv.transform import rescale diff --git a/plantcv/plantcv/visualize/pseudocolor.py b/plantcv/plantcv/visualize/pseudocolor.py index d3cc3c615..353c33acb 100644 --- a/plantcv/plantcv/visualize/pseudocolor.py +++ b/plantcv/plantcv/visualize/pseudocolor.py @@ -4,7 +4,7 @@ import numpy as np from cv2 import cvtColor, COLOR_BGR2RGB from matplotlib import pyplot as plt -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv import fatal_error from plantcv.plantcv.apply_mask import apply_mask diff --git a/plantcv/plantcv/visualize/tile.py b/plantcv/plantcv/visualize/tile.py index 1574703c1..abf580ee0 100644 --- a/plantcv/plantcv/visualize/tile.py +++ b/plantcv/plantcv/visualize/tile.py @@ -3,7 +3,7 @@ import cv2 import numpy as np import os -from plantcv.plantcv import params +from plantcv.plantcv._globals import params from plantcv.plantcv._debug import _debug diff --git a/plantcv/plantcv/warn.py b/plantcv/plantcv/warn.py index b28275f85..ab20ddc8c 100644 --- a/plantcv/plantcv/warn.py +++ b/plantcv/plantcv/warn.py @@ -1,7 +1,7 @@ # Warnings handling import sys -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def warn(warning): diff --git a/plantcv/plantcv/watershed.py b/plantcv/plantcv/watershed.py index af2c55771..1173ef324 100755 --- a/plantcv/plantcv/watershed.py +++ b/plantcv/plantcv/watershed.py @@ -9,9 +9,8 @@ from skimage.feature import peak_local_max from skimage.segmentation import watershed from plantcv.plantcv._debug import _debug -from plantcv.plantcv import color_palette -from plantcv.plantcv import params -from plantcv.plantcv import outputs +from plantcv.plantcv.color_palette import color_palette +from plantcv.plantcv._globals import params, outputs from plantcv.plantcv._helpers import _cv2_findcontours diff --git a/plantcv/plantcv/white_balance.py b/plantcv/plantcv/white_balance.py index dd1954d11..a15305f3c 100755 --- a/plantcv/plantcv/white_balance.py +++ b/plantcv/plantcv/white_balance.py @@ -6,7 +6,7 @@ from plantcv.plantcv._debug import _debug from plantcv.plantcv import apply_mask from plantcv.plantcv import fatal_error -from plantcv.plantcv import params +from plantcv.plantcv._globals import params def _hist(img, hmax, x, y, h, w, data_type): diff --git a/tests/plantcv/annotate/test_points.py b/tests/plantcv/annotate/test_points.py index 8378addc8..df1f17868 100644 --- a/tests/plantcv/annotate/test_points.py +++ b/tests/plantcv/annotate/test_points.py @@ -1,6 +1,6 @@ import cv2 import matplotlib -from plantcv.plantcv import Points +from plantcv.plantcv import Point def test_points_interactive(annotate_test_data): @@ -9,7 +9,7 @@ def test_points_interactive(annotate_test_data): img = cv2.imread(annotate_test_data.small_rgb_img) # initialize interactive tool - drawer_rgb = Points(img, figsize=(12, 6)) + drawer_rgb = Point(img, figsize=(12, 6)) # simulate mouse clicks # event 1, left click to add point diff --git a/tests/plantcv/test_sharpen.py b/tests/plantcv/test_sharpen.py new file mode 100644 index 000000000..abef25657 --- /dev/null +++ b/tests/plantcv/test_sharpen.py @@ -0,0 +1,60 @@ +import cv2 +import numpy as np +from plantcv.plantcv.classes import Objects +from plantcv.plantcv.sharpen import sharpen + + +def test_sharpen_zero_amount(test_data): + """Test for PlantCV.""" + # Read in test data + img = cv2.imread(test_data.small_rgb_img) + sharp_img = sharpen(img, (5, 5), amount=0, threshold=0) + assert np.average(img) == np.average(sharp_img) + + +def test_sharpen_any_amount(test_data): + """Test for PlantCV.""" + # Read in test data + img = cv2.imread(test_data.small_rgb_img) + sharp_img = sharpen(img, (5, 5), amount=1, threshold=0) + assert np.average(img) != np.average(sharp_img) + + +def test_sharpen_threshold(test_data): + """Test for PlantCV.""" + # Read in test data + img = cv2.imread(test_data.small_rgb_img) + zero_thresh = sharpen(img, (5, 5), amount=1, threshold=0) + thresh = sharpen(img, (5, 5), amount=1, threshold=100) + assert np.average(thresh) != np.average(zero_thresh) + + +def test_sharpen_grayscale(test_data): + """Test for PlantCV.""" + # Read in test data + gray_img = cv2.imread(test_data.small_gray_img, -1) + zero_thresh = sharpen(gray_img, (5, 5), amount=1, threshold=0) + thresh = sharpen(gray_img, (5, 5), amount=1, threshold=100) + assert np.average(thresh) != np.average(zero_thresh) + + +def test_sharpen_roi(test_data): + """Test for PlantCV.""" + # Read in test data + img = cv2.imread(test_data.small_rgb_img) + roi_con = [np.array([[[10, 20]], [[10, 200]], [[200, 200]], [[200, 20]]], dtype=np.int32)] + roi_str = np.array([[[-1, -1, -1, -1]]], dtype=np.int32) + roi = Objects(contours=[roi_con], hierarchy=[roi_str]) + sharp_img = sharpen(img, (5, 5), amount=1, threshold=0, roi=roi) + assert np.average(img) != np.average(sharp_img) + + +def test_sharpen_grayscale_roi(test_data): + """Test for PlantCV.""" + # Read in test data + gray_img = cv2.imread(test_data.small_gray_img, -1) + roi_con = [np.array([[[10, 20]], [[10, 200]], [[200, 200]], [[200, 20]]], dtype=np.int32)] + roi_str = np.array([[[-1, -1, -1, -1]]], dtype=np.int32) + roi = Objects(contours=[roi_con], hierarchy=[roi_str]) + sharp_img = sharpen(gray_img, (5, 5), amount=1, threshold=0, roi=roi) + assert np.average(gray_img) != np.average(sharp_img)