Skip to content
Merged
4 changes: 2 additions & 2 deletions docs/threshold_dual_channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ Pixels in the plane above and below the straight line are assigned two different
- **Parameters:**
- rgb_img - RGB image
- x_channel - Channel to use for the horizontal coordinate.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'
- y_channel - Channel to use for the vertical coordinate.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'
- points - List containing two points as tuples defining the segmenting straight line
- above - Whether the pixels above the line are given the value of 0 or 255

Expand Down
4 changes: 2 additions & 2 deletions docs/visualize_pixel_scatter_vis.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ This function plots a 2D pixel scatter plot visualization for a dataset of image
- **Parameters:**
- paths_to_imgs - List of paths to the images.
- x_channel - Channel to use for the horizontal coordinate of the scatter plot.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'.
- y_channel - Channel to use for the vertical coordinate of the scatter plot.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'.


- **Context:**
Expand Down
28 changes: 17 additions & 11 deletions plantcv/plantcv/threshold/threshold_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
import math
import numpy as np
from matplotlib import pyplot as plt
from plantcv.plantcv import fatal_error, warn
from plantcv.plantcv import params
from plantcv.plantcv import fatal_error, warn, params
from plantcv.plantcv._debug import _debug
from plantcv.plantcv._helpers import _rgb2lab, _rgb2hsv, _rgb2gray
from plantcv.plantcv._helpers import _rgb2lab, _rgb2hsv, _rgb2gray, _rgb2cmyk
from skimage.feature import graycomatrix, graycoprops
from scipy.ndimage import generic_filter

Expand Down Expand Up @@ -795,7 +794,7 @@ def mask_bad(float_img, bad_type='native'):


# functions to get a given channel with parameters compatible
# with _rgb2lab and _rgb2hsv to use in the dict
# with rgb2gray_lab, rgb2gray_hsv, and rgb2gray_cmyk to use in the dict
def _get_R(rgb_img, _):
"""Get the red channel from a RGB image"""
return rgb_img[:, :, 2]
Expand Down Expand Up @@ -824,24 +823,27 @@ def _get_index(rgb_img, _):

def _not_valid(*args):
"""Error for a non valid channel"""
return fatal_error("channel not valid, use R, G, B, l, a, b, h, s, v, gray, or index")
return fatal_error("channel not valid, use R, G, B, l, a, b, h, s, v, c, m, y, k, gray, or index")


def dual_channels(rgb_img, x_channel, y_channel, points, above=True):
"""Create a binary image from an RGB image based on the pixels values in two channels.
"""
Create a binary image from an RGB image based on the pixels values in two channels.
The x and y channels define a 2D plane and the two input points define a straight line.
Pixels in the plane above and below the straight line are assigned two different values.

Inputs:
rgb_img = RGB image
ch_x = Channel to use for the horizontal coordinate.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'
ch_y = Channel to use for the vertical coordinate.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'
x_channel = Channel to use for the horizontal coordinate.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'
y_channel = Channel to use for the vertical coordinate.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'
points = List containing two points as tuples defining the segmenting straight line
above = Whether the pixels above the line are given the value of 0 or max_value

Returns:
bin_img = Thresholded, binary image
bin_img = Thresholded, binary image

:param rgb_img: numpy.ndarray
:param x_channel: str
:param y_channel: str
Expand All @@ -862,6 +864,10 @@ def dual_channels(rgb_img, x_channel, y_channel, points, above=True):
's': _rgb2hsv,
'v': _rgb2hsv,
'index': _get_index,
'c': _rgb2cmyk,
'm': _rgb2cmyk,
'y': _rgb2cmyk,
'k': _rgb2cmyk
}

debug = params.debug
Expand Down
26 changes: 15 additions & 11 deletions plantcv/plantcv/visualize/pixel_scatter_vis.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Visualize a scatter plot of pixels

import numpy as np
import cv2 as cv
import cv2
from matplotlib import pyplot as plt
from plantcv import plantcv as pcv
from plantcv.plantcv import fatal_error, params
from plantcv.plantcv._helpers import _rgb2lab, _rgb2hsv, _rgb2gray
from plantcv.plantcv.readimage import readimage
from plantcv.plantcv._helpers import _rgb2lab, _rgb2hsv, _rgb2cmyk, _rgb2gray


MAX_MARKER_SIZE = 20
IMG_WIDTH = 128


# functions to get a given channel with parameters compatible
# with _rgb2lab and _rgb2hsv to use in the dict
# with rgb2gray_lab, rgb2gray_hsv, and rgb2gray_cmyk to use in the dict
def _get_R(rgb_img, _):
"""Get the red channel from a RGB image."""
return rgb_img[:, :, 2]
Expand Down Expand Up @@ -42,7 +42,7 @@ def _get_index(rgb_img, _):

def _not_valid(*args):
"""Error for a non valid channel."""
return fatal_error("channel not valid, use R, G, B, l, a, b, h, s, v, gray, or index")
return fatal_error("channel not valid, use R, G, B, l, a, b, h, s, v, c, m, y, k, gray, or index")


def pixel_scatter_plot(paths_to_imgs, x_channel, y_channel):
Expand All @@ -55,15 +55,15 @@ def pixel_scatter_plot(paths_to_imgs, x_channel, y_channel):
Inputs:
paths_to_imgs = List of paths to the images
x_channel = Channel to use for the horizontal coordinate of the scatter plot.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'
y_channel = Channel to use for the vertical coordinate of the scatter plot.
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray', and 'index'
Options: 'R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray', and 'index'

Returns:
fig = matplotlib pyplot Figure object of the visualization
ax = matplotlib pyplot Axes object of the visualization

:param paths_to_imgs: str
:param paths_to_imgs: list of str
:param x_channel: str
:param y_channel: str
:return fig: matplotlib.pyplot Figure object
Expand All @@ -82,6 +82,10 @@ def pixel_scatter_plot(paths_to_imgs, x_channel, y_channel):
's': _rgb2hsv,
'v': _rgb2hsv,
'index': _get_index,
'c': _rgb2cmyk,
'm': _rgb2cmyk,
'y': _rgb2cmyk,
'k': _rgb2cmyk
}

# store debug mode
Expand All @@ -93,17 +97,17 @@ def pixel_scatter_plot(paths_to_imgs, x_channel, y_channel):
fig, ax = plt.subplots()
# load and plot the set of images sequentially
for p in paths_to_imgs:
img, _, _ = pcv.readimage(filename=p, mode="native")
img, _, _ = readimage(filename=p)
h, _, c = img.shape

# resizing to predetermined width to reduce the number of pixels
ratio = h/IMG_WIDTH
img_height = int(IMG_WIDTH*ratio)
# nearest interpolation avoids mixing pixel values
sub_img = cv.resize(img, (IMG_WIDTH, img_height), interpolation=cv.INTER_NEAREST)
sub_img = cv2.resize(img, (IMG_WIDTH, img_height), interpolation=cv2.INTER_NEAREST)

# organize the channels as RGB to use as facecolor for the markers
sub_img_rgb = cv.cvtColor(sub_img, cv.COLOR_BGR2RGB)
sub_img_rgb = cv2.cvtColor(sub_img, cv2.COLOR_BGR2RGB)
fcolors = sub_img_rgb.reshape(img_height*IMG_WIDTH, c)/255

# get channels
Expand Down
9 changes: 4 additions & 5 deletions tests/plantcv/visualize/test_pixel_scatter_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
import cv2
import os
import numpy as np
from plantcv.plantcv.visualize.pixel_scatter_vis import pixel_scatter_plot
from plantcv.plantcv.visualize import pixel_scatter_plot


@pytest.mark.parametrize("ch", ['R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray'])
@pytest.mark.parametrize("ch", ['R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'c', 'm', 'y', 'k', 'gray'])
def test_pixel_scatter_plot(ch, tmpdir):
"""Test for PlantCV."""
# Create a tmp directory
cache_dir = tmpdir.mkdir("cache")
rng = np.random.default_rng()
img_size = (10,10,3)
img_size = (10, 10, 3)
# create a random image and write it to the temp directory
img = rng.integers(low=0, high=255, size=img_size, dtype=np.uint8, endpoint=True)
path_to_img = os.path.join(cache_dir, 'tmp_img.png')
Expand All @@ -26,12 +26,11 @@ def test_pixel_scatter_plot_wrong_ch(tmpdir):
# Create a tmp directory
cache_dir = tmpdir.mkdir("cache")
rng = np.random.default_rng()
img_size = (10,10,3)
img_size = (10, 10, 3)
# create a random image and write it to the temp directory
img = rng.integers(low=0, high=255, size=img_size, dtype=np.uint8, endpoint=True)
path_to_img = os.path.join(cache_dir, 'tmp_img.png')
cv2.imwrite(path_to_img, img)
# test the function with channel parameter that is not an option
with pytest.raises(RuntimeError):
_, _ = pixel_scatter_plot(paths_to_imgs=[path_to_img], x_channel='wrong_ch', y_channel='index')