diff --git a/docs/img/documentation_images/correct_color_imgs/auto_correct_color.png b/docs/img/documentation_images/correct_color_imgs/auto_correct_color.png new file mode 100644 index 000000000..c9bba3bc2 Binary files /dev/null and b/docs/img/documentation_images/correct_color_imgs/auto_correct_color.png differ diff --git a/docs/transform_auto_correct_color.md b/docs/transform_auto_correct_color.md new file mode 100644 index 000000000..17cc69389 --- /dev/null +++ b/docs/transform_auto_correct_color.md @@ -0,0 +1,43 @@ +# Automatically detect a color card and color correct in one step + +Corrects the color of the input image based on the target color matrix using an affine transformation +in the RGB space after automatic detection of a color card within the image. A one-step wrapper of +[plantcv.transform.detect_color_card](transform_detect_color_card.md), [plantcv.transform.std_color_matrix](std_color_matrix.md), +[plantcv.transform.get_color_matrix](get_color_matrix.md), and [plantcv.transform.affine_color_correction](transform_affine_color_correction.md). + +**plantcv.transform.auto_correct_color**(*rgb_img, label=None, \*\*kwargs*) + +**returns** corrected_img + +- **Parameters** + - rgb_img - Input RGB image data containing a color card. + - label - Optional label parameter, modifies the variable name of observations recorded. (default = `pcv.params.sample_label`) + - **kwargs - Other keyword arguments passed to `cv2.adaptiveThreshold` and `cv2.circle`. + - adaptive_method - Adaptive threhold method. 0 (mean) or 1 (Gaussian) (default = 1). + - block_size - Size of a pixel neighborhood that is used to calculate a threshold value (default = 51). We suggest using 127 if using `adaptive_method=0`. + - radius - Radius of circle to make the color card labeled mask (default = 20). + - min_size - Minimum chip size for filtering objects after edge detection (default = 1000) +- **Returns** + - corrected_img - Color corrected image + +- **Example Use** + - Below + +```python + +from plantcv import plantcv as pcv + +rgb_img, imgpath, imgname = pcv.readimage(filename="top_view_plant.png") + +corrected_rgb = pcv.transform.auto_correct_color(rgb_img=old_card) +``` + +**Debug Image: automatically detected and masked the color card** + +![Screenshot](img/documentation_images/correct_color_imgs/auto_correct_color.png) + +**Corrected Image:** + +![Screenshot](img/documentation_images/transform_affine_color_corr/tobacco_leaves_corrected.jpg) + +**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/main/plantcv/plantcv/transform/auto_correct_color.py) diff --git a/docs/updating.md b/docs/updating.md index 37f4e53c4..939a29fe3 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -1210,6 +1210,11 @@ pages for more details on the input and output variable types. * pre v3.0dev1: NA * post v3.0dev2: determinant, transformation_matrix = **plantcv.transform.calc_transformation_matrix**(*matrix_m, matrix_b*) +#### plantcv.transform.auto_correct_color + +* pre v4.6: NA +* post v4.6: corrected_img = **plantcv.transform.r**(*rgb_img, label=None, **kwargs*) + #### plantcv.transform.correct_color * pre v3.0dev1: NA diff --git a/mkdocs.yml b/mkdocs.yml index c4e0cb9a2..529dd81a3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -175,6 +175,7 @@ nav: - 'Transformation Methods': - 'Auto-Detect Color Card': transform_detect_color_card.md - 'Auto-Detect Color Card (old)': find_color_card.md + - 'Auto Correct Color': transform_auto_correct_color.md - 'Calibrate Camera': transform_calibrate_camera.md - 'Checkerboard Calibration': transform_checkerboard_calibration.md - 'Create Color Card Mask': create_color_card_mask.md diff --git a/plantcv/plantcv/transform/__init__.py b/plantcv/plantcv/transform/__init__.py index ecaf7b019..92ce895ce 100644 --- a/plantcv/plantcv/transform/__init__.py +++ b/plantcv/plantcv/transform/__init__.py @@ -19,9 +19,10 @@ from plantcv.plantcv.transform.gamma_correct import gamma_correct from plantcv.plantcv.transform.checkerboard_calib import checkerboard_calib, calibrate_camera from plantcv.plantcv.transform.merge_images import merge_images +from plantcv.plantcv.transform.auto_correct_color import auto_correct_color __all__ = ["get_color_matrix", "get_matrix_m", "calc_transformation_matrix", "apply_transformation_matrix", "save_matrix", "load_matrix", "correct_color", "create_color_card_mask", "quick_color_check", "find_color_card", "std_color_matrix", "affine_color_correction", "rescale", "nonuniform_illumination", "resize", "resize_factor", "warp", "rotate", "warp", "warp_align", "gamma_correct", "detect_color_card", "checkerboard_calib", - "calibrate_camera", "merge_images"] + "calibrate_camera", "merge_images", "auto_correct_color"] diff --git a/plantcv/plantcv/transform/auto_correct_color.py b/plantcv/plantcv/transform/auto_correct_color.py new file mode 100644 index 000000000..109bc38ec --- /dev/null +++ b/plantcv/plantcv/transform/auto_correct_color.py @@ -0,0 +1,40 @@ +# Automatically detect a color card and color correct to standard chip values + +from plantcv.plantcv import params +from plantcv.plantcv.transform.detect_color_card import detect_color_card +from plantcv.plantcv.transform.color_correction import get_color_matrix, std_color_matrix, affine_color_correction + + +def auto_correct_color(rgb_img, label=None, **kwargs): + """Automatically detect a color card. + Parameters + ---------- + rgb_img : numpy.ndarray + Input RGB image data containing a color card. + label : str, optional + modifies the variable name of observations recorded (default = pcv.params.sample_label). + **kwargs + Other keyword arguments passed to cv2.adaptiveThreshold and cv2.circle. + Valid keyword arguments: + adaptive_method: 0 (mean) or 1 (Gaussian) (default = 1) + block_size: int (default = 51) + radius: int (default = 20) + min_size: int (default = 1000) + Returns + ------- + numpy.ndarray + Color corrected image + """ + # Set lable to params.sample_label if None + if label is None: + label = params.sample_label + + # Get keyword arguments and set defaults if not set + labeled_mask = detect_color_card(rgb_img=rgb_img, min_size=kwargs.get("min_size", 1000), + radius=kwargs.get("radius", 20), + adaptive_method=kwargs.get("adaptive_method", 1), + block_size=kwargs.get("block_size", 51)) + _, card_matrix = get_color_matrix(rgb_img=rgb_img, mask=labeled_mask) + std_matrix = std_color_matrix(pos=3) + return affine_color_correction(rgb_img=rgb_img, source_matrix=card_matrix, + target_matrix=std_matrix) diff --git a/tests/plantcv/transform/test_auto_correct_color.py b/tests/plantcv/transform/test_auto_correct_color.py new file mode 100644 index 000000000..156131a88 --- /dev/null +++ b/tests/plantcv/transform/test_auto_correct_color.py @@ -0,0 +1,11 @@ +"""Tests for auto_correct_color.""" +import cv2 +import numpy as np +from plantcv.plantcv.transform import auto_correct_color + +def test_auto_correct_color(transform_test_data): + """Test for PlantCV.""" + # Load rgb image + rgb_img = cv2.imread(transform_test_data.colorcard_img) + corrected_img = auto_correct_color(rgb_img=rgb_img) + assert np.shape(corrected_img) == np.shape(rgb_img) and np.sum(corrected_img) != np.sum(rgb_img)