diff --git a/docs/img/documentation_images/qc_color_chip_comparison/input.png b/docs/img/documentation_images/qc_color_chip_comparison/input.png new file mode 100644 index 0000000000..5394273799 Binary files /dev/null and b/docs/img/documentation_images/qc_color_chip_comparison/input.png differ diff --git a/docs/img/documentation_images/qc_color_chip_comparison/output.png b/docs/img/documentation_images/qc_color_chip_comparison/output.png new file mode 100644 index 0000000000..127321143a Binary files /dev/null and b/docs/img/documentation_images/qc_color_chip_comparison/output.png differ diff --git a/docs/img/documentation_images/visualize_color_correction_scatter/am003_sv_ex.png b/docs/img/documentation_images/qc_color_correction_scatter/am003_sv_ex.png similarity index 100% rename from docs/img/documentation_images/visualize_color_correction_scatter/am003_sv_ex.png rename to docs/img/documentation_images/qc_color_correction_scatter/am003_sv_ex.png diff --git a/docs/img/documentation_images/visualize_color_correction_scatter/am003_sv_input.png b/docs/img/documentation_images/qc_color_correction_scatter/am003_sv_input.png similarity index 100% rename from docs/img/documentation_images/visualize_color_correction_scatter/am003_sv_input.png rename to docs/img/documentation_images/qc_color_correction_scatter/am003_sv_input.png diff --git a/docs/img/documentation_images/qc_plot_delta_e/input.png b/docs/img/documentation_images/qc_plot_delta_e/input.png new file mode 100644 index 0000000000..00eccc211e Binary files /dev/null and b/docs/img/documentation_images/qc_plot_delta_e/input.png differ diff --git a/docs/img/documentation_images/qc_plot_delta_e/uncalibrated_output.png b/docs/img/documentation_images/qc_plot_delta_e/uncalibrated_output.png new file mode 100644 index 0000000000..6f8e9adc9f Binary files /dev/null and b/docs/img/documentation_images/qc_plot_delta_e/uncalibrated_output.png differ diff --git a/docs/qc_color_chip_comparison.md b/docs/qc_color_chip_comparison.md new file mode 100644 index 0000000000..b15d88ce1e --- /dev/null +++ b/docs/qc_color_chip_comparison.md @@ -0,0 +1,45 @@ +## Color Chip Comparison + +This function makes a plot comparing observed versus expected values from 1 or more color cards against a standard color card matrix via a "greenness rank". The greenness rank is useful in checking color card quality. The ninth (red) color chip is known to fade most quickly and the proportion of green light that it reflects can vary dramatically as the color card ages. The color of each bar is determined by the standard color matrix on the left side of the bar and by the observed color matrix on the right side of the bar. The order along the x axis is conserved from the order of `*args`. + +**plantcv.qc.color_chip_comparison**(*std_matrix, \*args*) + +**returns** plot, a altair.vegalite.v5.api.VConcatChart object + +- **Parameters:** + - std_matrix - A numpy.ndarray as returned from [`pcv.transform.std_color_matrix`](std_color_matrix.md). + - \*args - Any number of numpy.ndarrays as returned from [`pcv.transform.get_color_matrix`](get_color_matrix.md) + +- **Context:** + - The aim of this visualization is to help evaluate the condition of a color card or set of color cards. + + +- **Example use:** + - Below + +**Dataset images:** + +![Screenshot](img/documentation_images/qc_color_chip_comparison/input.png) + +```python + +from plantcv import plantcv as pcv + +tgt_matrix = pcv.transform.std_color_matrix(pos=3) +_, cc1_matrix = pcv.transform.get_color_matrix(rgb_img=img, mask=cc_mask) +# ... masking more color cards for example +_, cc6_matrix = pcv.transform.get_color_matrix(rgb_img=img, mask=cc_mask6) + +plot = pcv.qc.color_chip_comparison(tgt_matrix, cc1_matrix, + cc2_matrix, cc3_matrix, + cc4_matrix, cc5_matrix, + cc6_matrix) + +``` + +**Color chip comparison visualizations:** + +![Screenshot](img/documentation_images/qc_color_chip_comparison/output.png) + + +**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/qc/color_chip_comparison.py) diff --git a/docs/visualize_color_correction_scatter.md b/docs/qc_color_correction_scatter.md similarity index 76% rename from docs/visualize_color_correction_scatter.md rename to docs/qc_color_correction_scatter.md index 58b494ebea..751d8f8390 100644 --- a/docs/visualize_color_correction_scatter.md +++ b/docs/qc_color_correction_scatter.md @@ -2,7 +2,7 @@ This function plots 4 panels of 2D scatter plot visualizations showing RGB and grayscale values of an input image, the expected color card, and optionally a color corrected image. The horizontal and vertical coordinates are defined by the intensity of the pixels in the specified channels. The color of each dot is given by the original RGB color of the image, ideal color card, or corrected image. -**plantcv.visualize.color_correction_plot**(*color_matrix, std_matrix, corrected_matrix=None*) +**plantcv.qc.color_correction_plot**(*color_matrix, std_matrix, corrected_matrix=None*) **returns** fig, axs @@ -20,19 +20,19 @@ This function plots 4 panels of 2D scatter plot visualizations showing RGB and g **Dataset images:** -![Screenshot](img/documentation_images/visualize_color_correction_scatter/am003_sv_input.png) +![Screenshot](img/documentation_images/qc_color_correction_scatter/am003_sv_input.png) ```python from plantcv import plantcv as pcv -fig, axs = pcv.visualize.color_correction_plot(colmat, stdmat, ccmat) +fig, axs = pcv.qc.color_correction_plot(colmat, stdmat, ccmat) ``` **Color correction scatter visualizations:** -![Screenshot](img/documentation_images/visualize_color_correction_scatter/am003_sv_ex.png) +![Screenshot](img/documentation_images/qc_color_correction_scatter/am003_sv_ex.png) -**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/visualize/color_correction_scatter.py) +**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/qc/color_correction_scatter.py) diff --git a/docs/qc_plot_delta_e.md b/docs/qc_plot_delta_e.md new file mode 100644 index 0000000000..ccbdafe628 --- /dev/null +++ b/docs/qc_plot_delta_e.md @@ -0,0 +1,39 @@ +## Plot Delta E + +This function creates an interactive bar chart visualizing per-chip Delta E (color difference) values from a color checker card. Each bar is colored by a standard interpretation category: green (<1) indicates imperceptible differences, progressing through yellow (<2, <10) to orange and red (<49, >49) for increasingly noticeable differences. Reference lines at the category boundaries are drawn for quick visual assessment. Standard chip color swatches from the target color matrix are displayed below the x-axis to aid chip identification. + +**plantcv.qc.plot_deltaE**(*deltaE_matrix*) + +**returns** chart, an altair.vegalite.v5.api.LayerChart object + +- **Parameters:** + - deltaE_matrix - numpy.ndarray of per-chip Delta E values, shaped to match the color card layout (e.g., (6, 4) for a 24-chip Macbeth card or (3, 5) for a 15-chip AstroBotany card), as returned from [`pcv.transform.deltaE`](https://github.com/danforthcenter/plantcv/blob/main/plantcv/plantcv/transform/detect_color_card.py). + +- **Context:** + - Used to evaluate the quality of color calibration by visualizing how closely observed chip colors match their expected values. Lower Delta E values indicate better color fidelity. This function is best used during workflow development to interactively inspect calibration results before and after color correction or between different color correction methods. + +- **Example use:** + - Below + +**Dataset image:** + +![Screenshot](img/documentation_images/qc_plot_delta_e/input.png) + +```python + +from plantcv import plantcv as pcv + +# Calculate Delta E values for each chip relative to the standard color matrix +de_matrix = pcv.transform.deltaE(rgb_img=img, color_chip_size="classic") + +# Plot the Delta E values +chart = pcv.qc.plot_deltaE(deltaE_matrix=de_matrix) + +``` + +**Delta E bar chart:** + +![Screenshot](img/documentation_images/qc_plot_delta_e/uncalibrated_output.png) + + +**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/main/plantcv/plantcv/qc/plot_delta_e.py) diff --git a/docs/updating.md b/docs/updating.md index 9998a7272b..9983e0542f 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -1002,6 +1002,21 @@ pages for more details on the input and output variable types. * pre v5.0: NA, see `plantcv.transform.quick_color_check` * post v5.0: chart = **plantcv.transform.quick_color_check**(*target_matrix, source_matrix, num_chips*) +#### plantcv.qc.color_correction_plot + +* pre v5.0: NA, see `plantcv.visualize.color_correction_scatterplot` +* post v5.0: fig = **plantcv.qc.color_correction_plot**(*color_matrix, std_matrix, corrected_matrix=None*) + +#### plantcv.qc.color_chip_comparison + +* pre v5.0: NA +* post v5.0: fig = **plantcv.qc.color_chip_comparison**(*std_matrix, \*args*) + +#### plantcv.qc.plot_deltaE + +* pre v5.0: NA +* post v5.0: fig = **plantcv.qc.plot_deltaE**(*deltaE_matrix*) + #### plantcv.readbayer * pre v3.0: NA @@ -1537,6 +1552,12 @@ pages for more details on the input and output variable types. * pre v4.0: NA * post v4.0: chart = **plantcv.visualize.chlorophyll_fluorescence**(*ps_da, labeled_mask, n_labels=1, label="object"*) +#### plantcv.visualize.color_chip_comparison + +* pre v4.10: NA +* post v4.10: plot = **plantcv.visualize.color_chip_comparison**(*std_matrix, \*args*) +* post v5.0: moved to **plantcv.qc.color_chip_comparison** + #### plantcv.visualize.color_correction_scatter * pre v4.11: NA diff --git a/mkdocs.yml b/mkdocs.yml index 13078ea2cb..0bd65f4ccf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -145,6 +145,9 @@ nav: - 'Print Image': print_image.md - 'Plot Image': plot_image.md - "Quality Control Tools": + - 'Color Correction Scatter Plot': qc_color_correction_scatter.md + - 'Color Chip Comparison Plot': qc_color_chip_comparison.md + - 'Plot Delta E': qc_plot_delta_e.md - "Exposure": qc_exposure.md - "Quick Color Check": quick_color_check.md - 'Read Image': read_image.md @@ -211,7 +214,6 @@ nav: - 'Visualization Methods': - 'Auto Threshold Methods': visualize_auto_threshold_methods.md - 'Chlorophyll Fluorescence': visualize_chlorophyll_fluorescence.md - - 'Color Correction Scatter Plot': visualize_color_correction_scatter.md - 'Colorize Label Image': visualize_colorize_label_img.md - 'Colorize Masks': visualize_colorize_masks.md - 'Colorspaces': visualize_colorspace.md diff --git a/plantcv/plantcv/qc/__init__.py b/plantcv/plantcv/qc/__init__.py index 9607a4cbd4..2be38c5193 100644 --- a/plantcv/plantcv/qc/__init__.py +++ b/plantcv/plantcv/qc/__init__.py @@ -1,5 +1,9 @@ from plantcv.plantcv.qc.exposure import exposure from plantcv.plantcv.qc.quick_color_check import quick_color_check +from plantcv.plantcv.qc.color_correction_scatter import color_correction_plot +from plantcv.plantcv.qc.color_chip_comparison import color_chip_comparison +from plantcv.plantcv.qc.plot_delta_e import plot_deltaE -__all__ = ["exposure", "quick_color_check"] +__all__ = ["exposure", "quick_color_check", "color_correction_plot", "color_chip_comparison", + "plot_deltaE"] diff --git a/plantcv/plantcv/qc/color_chip_comparison.py b/plantcv/plantcv/qc/color_chip_comparison.py new file mode 100644 index 0000000000..d4b80fc85d --- /dev/null +++ b/plantcv/plantcv/qc/color_chip_comparison.py @@ -0,0 +1,149 @@ +# Compare observed vs standard color card chips using a "greenness rank" visualization. + +import pandas as pd +import altair as alt + + +def color_chip_comparison(std_matrix, *args): + """ + Compare one or more observed color matrices to a standard color matrix using a "greenness rank" plot. + Parameters + ---------- + std_matrix : numpy.ndarray + Output from pcv.transform.std_color_matrix + *args: list of numpy.ndarrays + Output from pcv.transform.get_color_matrix + + Returns + ------- + altair.vegalite.v5.api.VConcatChart of color chip greenness ranks between observed and expected values. + """ + # make standard color matrix into a rescaled dataframe + stddf = pd.DataFrame(std_matrix) + stddf.columns = ["chip", "R", "G", "B"] + stddf["card"] = "std" + stddf["std_R"] = stddf["R"] * 255 + stddf["std_G"] = stddf["G"] * 255 + stddf["std_B"] = stddf["B"] * 255 + # initialize a list of like dataframes + df_list = [stddf] + # format and append all kwargs into list of dataframes + for i, mat in enumerate(args): + df = pd.DataFrame(mat) + df.columns = ["chip", "R", "G", "B"] + df["card"] = f"card {i + 1}" + df["std_R"] = stddf["std_R"] + df["std_G"] = stddf["std_G"] + df["std_B"] = stddf["std_B"] + df_list.append(df) + # rbind all dataframes from list + fulldf = pd.concat(df_list, ignore_index=True) + # rescale rgb values to 0-255 + fulldf["R"] = fulldf["R"] * 255 + fulldf["G"] = fulldf["G"] * 255 + fulldf["B"] = fulldf["B"] * 255 + # calculate greenness, in the future maybe a named metric. + fulldf["greenness"] = fulldf["G"] / (fulldf["R"] + fulldf["G"] + fulldf["B"]) + # rank greenness, chips should have same order in any "healthy" card + fulldf["greenness_rank"] = fulldf.groupby("card")["greenness"].rank( + method="first", ascending=False + ) + # make standard greenness and rank it + fulldf["std_greenness"] = fulldf["std_G"] / ( + fulldf["std_R"] + fulldf["std_G"] + fulldf["std_B"] + ) + fulldf["std_greenness_rank"] = fulldf.groupby("card")["std_greenness"].rank( + method="first", ascending=False + ) + # label chips 1 to 24 + fulldf["chip"] = (fulldf["chip"] / 10).astype(int) + # initiate base of upper color chip chart + base = ( + alt.Chart(fulldf) + .encode( + alt.X( + "card:O", + axis=alt.Axis( + grid=False, ticks=False, domain=False, labels=False, title=None + ), + ).scale(paddingInner=0), + alt.Y("greenness_rank:O", title="Greenness Rank").scale(paddingInner=0), + ) + .properties(height=300, width=500) + ) + # make rect layer of observed colors + tiles1 = base.mark_rect(width=alt.RelativeBandSize(0.6), align="left").encode( + color=alt.value( + alt.ExprRef(alt.expr.rgb(alt.datum.R, alt.datum.G, alt.datum.B)) + ), + ) + # make rect layer of standard colors + tiles2 = base.mark_rect(width=alt.RelativeBandSize(0.3), align="right").encode( + color=alt.value( + alt.ExprRef(alt.expr.rgb(alt.datum.std_R, alt.datum.std_G, alt.datum.std_B)) + ), + ) + # make text layer to label chip numbers + text = base.mark_text(baseline="middle", align="center").encode( + text="chip:Q", color=alt.value("white") + ) + # combine rect and text layers + upper = tiles1 + tiles2 + text + # initialize list of margin plots + margin_plots = [] + # for each kwarg matrix and std matrix make a margin plot of residual ranks + for i in range(0, len(args) + 1): + # select card + whichcard = f"card {i + 1}" + if i + 1 > len(args): + whichcard = "std" + sub1 = fulldf[fulldf["card"] == whichcard] + # initialize plot + subbase = ( + alt.Chart(sub1) + .encode(alt.X("std_greenness_rank:Q"), alt.Y("std_greenness_rank:Q")) + .properties( + height=500 / (10 / 9 * len(args) + 1), + width=500 / (10 / 9 * len(args) + 1), + title=whichcard, + ) + ) + # make line+points layer of observed vs expected ranks + subpoints = subbase.mark_line( + point=True, strokeWidth=1.25, strokeDash=[5, 5] + ).encode( + x=alt.X( + "greenness_rank:Q", + axis=alt.Axis( + grid=False, ticks=False, domain=False, labels=False, title=None + ), + ), + y=alt.Y( + "std_greenness_rank:Q", + axis=alt.Axis( + grid=False, ticks=False, domain=False, labels=False, title=None + ), + ), + ) + # draw expected line with slope 1 + sublinear = subbase.mark_line(color="black", strokeWidth=0.5).encode( + x=alt.X( + "std_greenness_rank:Q", + axis=alt.Axis( + grid=False, ticks=False, domain=False, labels=False, title=None + ), + ), + y=alt.Y( + "std_greenness_rank:Q", + axis=alt.Axis( + grid=False, ticks=False, domain=False, labels=False, title=None + ), + ), + ) + # combine layers + iterchart = subpoints + sublinear + # add to list of margin plots for combination + margin_plots.append(iterchart) + # combine tile plot and margin plots + out = alt.vconcat(upper, alt.hconcat(*margin_plots, spacing=5)) + return out diff --git a/plantcv/plantcv/visualize/color_correction_scatter.py b/plantcv/plantcv/qc/color_correction_scatter.py similarity index 100% rename from plantcv/plantcv/visualize/color_correction_scatter.py rename to plantcv/plantcv/qc/color_correction_scatter.py diff --git a/plantcv/plantcv/qc/plot_delta_e.py b/plantcv/plantcv/qc/plot_delta_e.py new file mode 100644 index 0000000000..5e0a127bb2 --- /dev/null +++ b/plantcv/plantcv/qc/plot_delta_e.py @@ -0,0 +1,126 @@ +"""Visualize delta E values for color checker chips in an image""" + +import os +import math +import numpy as np +import pandas as pd +import altair as alt +from plantcv.plantcv._globals import params +from plantcv.plantcv._debug import _debug +from plantcv.plantcv.transform.color_correction import std_color_matrix, astro_color_matrix + + +def _bin_deltaE(v): + """Helper for discretizing delta E + Parameters + ---------- + v = float, delta E value + + Returns + ------- + label = str, discretized bin for delta E value per standard interpretation ranges + """ + if v < 1: + return "<1" + if v < 2: + return "<2" + if v < 10: + return "<10" + if v < 49: + return "<49" + return ">49" + + +def plot_deltaE(deltaE_matrix): + """ + Plot a bar chart of deltaE values for color checker chips. + + Parameters + ---------- + deltaE_matrix = numpy.ndarray, + 4x6 (macbeth) or 3x5 (astrobotany) array of per-chip deltaE values + + Returns + ------- + chart = altair.vegalite.v5.api.LayerChart + Altair layered chart with bars colored by deltaE category, reference lines, + and standard chip color swatches below the x-axis + """ + n_chips = math.prod(np.shape(deltaE_matrix)) + # chip color swatches below x-axis + std_mat = std_color_matrix(pos=3) + if n_chips == 15: + # astrobotany stuff + std_mat = astro_color_matrix() + + params.device += 1 + + chip_nos = np.arange(1, n_chips + 1) + delta_vals = deltaE_matrix.flatten(order="C") + df = pd.DataFrame({ + "chip_no": chip_nos, + "deltaE": delta_vals, + "xmin": chip_nos - 0.4, + "xmax": chip_nos + 0.4, + "discrete_deltaE": pd.Categorical( + [_bin_deltaE(v) for v in delta_vals], + categories=["<1", "<2", "<10", "<49", ">49"], + ordered=True, + ), + }) + + y_max = float(df["deltaE"].max()) * 1.1 + y_min = -1 * (y_max / 10) + + color_domain = ["<1", "<2", "<10", "<49", ">49"] + color_range = ["#5AB45A", "#85BF40", "#C8C040", "#CC8800", "#CC5555"] + + bars = alt.Chart(df).mark_rect().encode( + x=alt.X("xmin:Q", title="Color Chip", + scale=alt.Scale(domain=[0.5, n_chips + 0.5]), + axis=alt.Axis(values=list(range(1, n_chips + 1)))), + x2="xmax:Q", + y=alt.Y("deltaE:Q", scale=alt.Scale(domain=[y_min, y_max], nice=False), title="Delta E"), + y2=alt.datum(0), + color=alt.Color( + "discrete_deltaE:O", + scale=alt.Scale(domain=color_domain, range=color_range), + title="Delta E", + ), + tooltip=["chip_no:Q", "deltaE:Q", "discrete_deltaE:O"], + ) + hline_df = pd.DataFrame({ + "y": [1.0, 2.0, 10.0, 49.0], + "stroke": ["#3D8C3D", "#5A9E28", "#A09000", "#CC7700"], + }) + hlines = alt.Chart(hline_df).mark_rule(strokeDash=[8, 4]).encode( + y="y:Q", + color=alt.Color("stroke:N", scale=None), + ) + swatch_chip_nos = (std_mat[:, 0] / 10).astype(int) + swatch_hex = [ + f"#{int(r * 255):02X}{int(g * 255):02X}{int(b * 255):02X}" + for r, g, b in std_mat[:, 1:4] + ] + swatch_df = pd.DataFrame({ + "xmin": swatch_chip_nos - 0.5, + "xmax": swatch_chip_nos + 0.5, + "color": swatch_hex, + }) + swatches = alt.Chart(swatch_df).mark_rect().encode( + x=alt.X("xmin:Q", scale=alt.Scale(domain=[0.5, n_chips + 0.5])), + x2="xmax:Q", + y=alt.datum(y_min), + y2=alt.datum(0), + color=alt.Color("color:N", scale=None), + ) + + chart = (swatches + bars + hlines).properties( + title="Delta E by Color Chip" + ).resolve_scale(color="independent") + + _debug( + visual=chart, + filename=os.path.join(params.debug_outdir, str(params.device) + "_deltaE.png"), + ) + return chart.interactive() diff --git a/plantcv/plantcv/transform/delta_e.py b/plantcv/plantcv/transform/delta_e.py index 7102f1387f..724dc73fbd 100644 --- a/plantcv/plantcv/transform/delta_e.py +++ b/plantcv/plantcv/transform/delta_e.py @@ -61,8 +61,8 @@ def _delta_e(obs_rgb, card_type=None, obs="uncalibrated", method="deltaE_ciede20 ax2.set_title('Reference Colors') if params.debug == "print": fig.savefig(fname=os.path.join(params.debug_outdir, f"{params.device}_{obs}_{method}.png")) - if params.debug == "plot": - plt.show() - plt.close(fig) + plt.close(fig) + elif params.debug == "plot": + plt.show(block=False) return delta_e_mat diff --git a/plantcv/plantcv/visualize/__init__.py b/plantcv/plantcv/visualize/__init__.py index 6ff80b5253..bb5b3fa31f 100644 --- a/plantcv/plantcv/visualize/__init__.py +++ b/plantcv/plantcv/visualize/__init__.py @@ -9,10 +9,9 @@ from plantcv.plantcv.visualize.obj_size_ecdf import obj_size_ecdf from plantcv.plantcv.visualize.hyper_histogram import hyper_histogram from plantcv.plantcv.visualize.pixel_scatter_vis import pixel_scatter_plot -from plantcv.plantcv.visualize.color_correction_scatter import color_correction_plot from plantcv.plantcv.visualize.chlorophyll_fluorescence import chlorophyll_fluorescence from plantcv.plantcv.visualize.tile import tile __all__ = ["pseudocolor", "colorize_masks", "histogram", "colorspaces", "auto_threshold_methods", "overlay_two_imgs", "colorize_label_img", "obj_size_ecdf", "obj_sizes", "hyper_histogram", - "pixel_scatter_plot", "color_correction_plot", "chlorophyll_fluorescence", "tile"] + "pixel_scatter_plot", "chlorophyll_fluorescence", "tile"] diff --git a/tests/plantcv/qc/test_color_chip_comparison.py b/tests/plantcv/qc/test_color_chip_comparison.py new file mode 100644 index 0000000000..520c353bd2 --- /dev/null +++ b/tests/plantcv/qc/test_color_chip_comparison.py @@ -0,0 +1,58 @@ +import numpy as np +import altair.vegalite.v5.api as alt +from plantcv.plantcv.qc import color_chip_comparison + + +def test_color_chip_comparison(): + """Test for PlantCV.""" + colmat = np.array([[1.00000000e+01, 9.25084624e-01, 9.31330432e-01, 9.24875599e-01], + [2.00000000e+01, 5.84148377e-02, 9.82357621e-02, 2.78593601e-01], + [3.00000000e+01, 5.66449842e-01, 2.14622428e-01, 6.97146957e-02], + [4.00000000e+01, 1.52245465e-01, 9.10103421e-02, 6.87600418e-02], + [5.00000000e+01, 6.33256899e-01, 6.47542390e-01, 6.31672048e-01], + [6.00000000e+01, 1.35726208e-01, 2.72441387e-01, 1.16561374e-01], + [7.00000000e+01, 1.05152323e-01, 1.51069306e-01, 3.47459716e-01], + [8.00000000e+01, 4.72060773e-01, 2.63902538e-01, 2.12054846e-01], + [9.00000000e+01, 3.95404558e-01, 4.12794235e-01, 4.04445692e-01], + [1.00000000e+02, 3.58740855e-01, 9.12973622e-02, 6.38027049e-02], + [1.10000000e+02, 4.71290187e-01, 1.55299733e-01, 1.47334924e-01], + [1.20000000e+02, 1.42767560e-01, 1.90565773e-01, 2.73146458e-01], + [1.30000000e+02, 2.13418191e-01, 2.17058980e-01, 2.12466657e-01], + [1.40000000e+02, 7.25025348e-01, 5.66406165e-01, 9.67476251e-02], + [1.50000000e+02, 1.10989440e-01, 7.56610043e-02, 1.35953952e-01], + [1.60000000e+02, 1.05782520e-01, 1.33214782e-01, 6.59709548e-02], + [1.70000000e+02, 1.09123809e-01, 1.10440358e-01, 1.12564931e-01], + [1.80000000e+02, 4.48749747e-01, 1.81608873e-01, 2.92411125e-01], + [1.90000000e+02, 3.32768652e-01, 4.34991499e-01, 1.17930959e-01], + [2.00000000e+02, 2.26908138e-01, 2.28374436e-01, 3.47381721e-01], + [2.10000000e+02, 4.91677976e-02, 4.75455099e-02, 4.95328123e-02], + [2.20000000e+02, 1.11005038e-01, 2.74721949e-01, 3.99319887e-01], + [2.30000000e+02, 6.28196609e-01, 3.67697755e-01, 7.98633535e-02], + [2.40000000e+02, 2.18640710e-01, 4.18827897e-01, 3.76467468e-01]]) + + stdmat = np.array([[1.00000000e+01, 9.52941176e-01, 9.52941176e-01, 9.49019608e-01], + [2.00000000e+01, 2.19607843e-01, 2.39215686e-01, 5.88235294e-01], + [3.00000000e+01, 8.39215686e-01, 4.94117647e-01, 1.72549020e-01], + [4.00000000e+01, 4.50980392e-01, 3.21568627e-01, 2.66666667e-01], + [5.00000000e+01, 7.84313725e-01, 7.84313725e-01, 7.84313725e-01], + [6.00000000e+01, 2.74509804e-01, 5.80392157e-01, 2.86274510e-01], + [7.00000000e+01, 3.13725490e-01, 3.56862745e-01, 6.50980392e-01], + [8.00000000e+01, 7.60784314e-01, 5.88235294e-01, 5.09803922e-01], + [9.00000000e+01, 6.27450980e-01, 6.27450980e-01, 6.27450980e-01], + [1.00000000e+02, 6.86274510e-01, 2.11764706e-01, 2.35294118e-01], + [1.10000000e+02, 7.56862745e-01, 3.52941176e-01, 3.88235294e-01], + [1.20000000e+02, 3.84313725e-01, 4.78431373e-01, 6.15686275e-01], + [1.30000000e+02, 4.78431373e-01, 4.78431373e-01, 4.74509804e-01], + [1.40000000e+02, 9.05882353e-01, 7.80392157e-01, 1.21568627e-01], + [1.50000000e+02, 3.68627451e-01, 2.35294118e-01, 4.23529412e-01], + [1.60000000e+02, 3.41176471e-01, 4.23529412e-01, 2.62745098e-01], + [1.70000000e+02, 3.33333333e-01, 3.33333333e-01, 3.33333333e-01], + [1.80000000e+02, 7.33333333e-01, 3.37254902e-01, 5.84313725e-01], + [1.90000000e+02, 6.15686275e-01, 7.37254902e-01, 2.50980392e-01], + [2.00000000e+02, 5.21568627e-01, 5.01960784e-01, 6.94117647e-01], + [2.10000000e+02, 2.03921569e-01, 2.03921569e-01, 2.03921569e-01], + [2.20000000e+02, 3.13725490e-02, 5.21568627e-01, 6.31372549e-01], + [2.30000000e+02, 8.78431373e-01, 6.39215686e-01, 1.80392157e-01], + [2.40000000e+02, 4.03921569e-01, 7.41176471e-01, 6.66666667e-01]]) + fig = color_chip_comparison(stdmat, colmat) + assert isinstance(fig, alt.VConcatChart) diff --git a/tests/plantcv/visualize/test_color_correction_scatter.py b/tests/plantcv/qc/test_color_correction_scatter.py similarity index 99% rename from tests/plantcv/visualize/test_color_correction_scatter.py rename to tests/plantcv/qc/test_color_correction_scatter.py index ceb819a6d1..dcc27e2337 100644 --- a/tests/plantcv/visualize/test_color_correction_scatter.py +++ b/tests/plantcv/qc/test_color_correction_scatter.py @@ -1,6 +1,6 @@ import numpy as np from matplotlib import figure as mpfig -from plantcv.plantcv.visualize import color_correction_plot +from plantcv.plantcv.qc import color_correction_plot def test_color_correction_scatter(): diff --git a/tests/plantcv/qc/test_plot_deltaE.py b/tests/plantcv/qc/test_plot_deltaE.py new file mode 100644 index 0000000000..ef13e4510a --- /dev/null +++ b/tests/plantcv/qc/test_plot_deltaE.py @@ -0,0 +1,30 @@ +import numpy as np +from plantcv.plantcv.qc import plot_deltaE +import altair.vegalite.v5.api as alt + + +def test_plot_deltaE_macbeth(): + """Test for PlantCV""" + de_mat = np.array( + [[11.64761218, 78.27646683, 25.84812195, 0], + [9.13727445, 53.453794, 46.75456717, 1.69993386], + [10.24720418, 25.34728848, 58.35893906, 40.86886883], + [35.7394108, 13.90053859, 57.72055646, 69.18880135], + [72.58465134, 20.24932197, 20.4335013, 55.23924463], + [73.52994424, 39.50763664, 29.62213005, 47.82905849]] + ) + p = plot_deltaE(de_mat) + assert isinstance(p, alt.LayerChart) + + +def test_plot_deltaE_astro(): + """Test for PlantCV""" + de_mat = np.array( + [[11.64761218, 25.84812195, 0], + [9.13727445, 46.75456717, 1.69993386], + [10.24720418, 58.35893906, 40.86886883], + [72.58465134, 20.4335013, 55.23924463], + [73.52994424, 29.62213005, 47.82905849]] + ) + p = plot_deltaE(de_mat) + assert isinstance(p, alt.LayerChart)