Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a4227a0
add basic read dataset and random subset functions
JorgeGtz Mar 24, 2021
c29dd10
add pattern matching to read dataset function
JorgeGtz Mar 24, 2021
5fc831a
update init file
JorgeGtz Mar 25, 2021
087a18d
update init file
JorgeGtz Mar 25, 2021
29ae557
update init file
JorgeGtz Mar 25, 2021
06163eb
move read_dataset and random_subset functions to new subpackage io
JorgeGtz Mar 30, 2021
6107fa8
add pixel_scatter_vis function
JorgeGtz Mar 30, 2021
a1fcd96
using channel instead of ch
JorgeGtz Mar 30, 2021
1ab922e
fix N and error string
JorgeGtz Mar 30, 2021
25e1cf3
use random.sample instead of loop
JorgeGtz Aug 4, 2021
588fc45
solve conflict by updating init
JorgeGtz Aug 5, 2021
f518064
did not solve conflict
JorgeGtz Aug 5, 2021
c8e00c2
add import
JorgeGtz Aug 5, 2021
9fe0b03
lint corrections
JorgeGtz Aug 9, 2021
e4c52de
add docstring to functions in io package and pixel scatter vis
JorgeGtz Aug 9, 2021
e5e0401
add doc for pixel scatter visualization
JorgeGtz Aug 9, 2021
5c18716
doc for io read dataset
JorgeGtz Aug 10, 2021
684773c
add io methods and pixel scatter vis to table of contents
JorgeGtz Aug 10, 2021
57b652d
remove io package. It is now in a different branch
JorgeGtz Sep 15, 2021
f17cdaf
remove io related docs in this branch
JorgeGtz Sep 15, 2021
5938a8f
get channels for both x and y axes in pixel scatter visualization
JorgeGtz Nov 12, 2021
2567073
update pixel scatter vis documentation for using two channels
JorgeGtz Nov 12, 2021
004c338
disable debug mode for inside calls of plantcv functions
JorgeGtz Jan 10, 2022
30a24aa
change name to pixel scatter plot in documentation
JorgeGtz Jan 12, 2022
b2c36f8
change name to pixel scatter plot in code
JorgeGtz Jan 12, 2022
50ee48f
add pixel scatter plot to updating file
JorgeGtz Jan 12, 2022
97c8335
fix calls to pixel scatter plot on documentation
JorgeGtz Jan 12, 2022
52abbe6
add header to new py file
HaleySchuhl Jan 25, 2022
ad604e6
Create test_pixel_scatter_plot.py
HaleySchuhl Jan 26, 2022
8a5ba1e
Revert "remove io package. It is now in a different branch"
HaleySchuhl Jan 26, 2022
06893af
update io random_subset from 4.x branch
HaleySchuhl Jan 26, 2022
8f5fa53
update read_dataset from 4.x
HaleySchuhl Jan 26, 2022
9b283fc
Update test_pixel_scatter_plot.py
HaleySchuhl Jan 26, 2022
9503d5b
add ch var
HaleySchuhl Jan 26, 2022
fdf0e34
Update test_pixel_scatter_plot.py
HaleySchuhl Jan 26, 2022
4b8a43a
Update test name
HaleySchuhl Jan 26, 2022
0d6c946
Update tests/plantcv/visualize/test_pixel_scatter_plot.py
nfahlgren Jan 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/updating.md
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,11 @@ pages for more details on the input and output variable types.
* pre v3.13: NA
* post v3.13: plotting_img = **pcv.visualize.obj_sizes**(*img, mask, num_objects=100*)

#### plantcv.visualize.pixel_scatter_plot

* pre v4.0: NA
* post v4.0: fig, ax = **pcv.visualize.pixel_scatter_plot**(*paths_to_imgs, x_channel, y_channel*)

#### plantcv.watershed_segmentation

* pre v3.0dev2: device, watershed_header, watershed_data, analysis_images = **plantcv.watershed_segmentation**(*device, img, mask, distance=10, filename=False, debug=None*)
Expand Down
78 changes: 78 additions & 0 deletions docs/visualize_pixel_scatter_vis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
## Pixel scatter plot

This function plots a 2D pixel scatter plot visualization for a dataset of images. 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 pixel.

**plantcv.visualize.pixel_scatter_plot**(*paths_to_imgs, x_channel, y_channel*)

**returns** fig, ax

- **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'.
- 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'.


- **Context:**
- The aim of this visualization is to help selecting the threshold parameters to segment an image or dataset of
images. This visualization can show the pixels in several images at once, making the selected value more
likely to be valid for the whole dataset.


- **Example use:**
- Below

**Dataset images:**

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/10.9.1.31_pos-153-001-004_2019-10-31-13-05_1.png)
![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/10.9.1.31_pos-153-001-004_2019-10-31-13-05_2.png)

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/10.9.1.31_pos-153-001-004_2019-10-31-13-05_3.png)
![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/10.9.1.31_pos-153-001-004_2019-10-31-13-05_4.png)



```python

from plantcv import plantcv as pcv

fig1, ax1 = pcv.visualize.pixel_scatter_plot(paths_to_imgs=file_paths, x_channel='index', y_channel='G')

fig2, ax2 = pcv.visualize.pixel_scatter_plot(paths_to_imgs=file_paths, x_channel='index', y_channel='s')

```

**Pixel scatter visualizations:**

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/pixel_scatter_G.png)

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/pixel_scatter_s.png)

**Dataset images:**

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/VIS_TV_z500_h2_g0_e100_167692_0_m.png)
![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/VIS_TV_z500_h2_g0_e100_163228_0_m.png)

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/VIS_TV_z500_h2_g0_e100_163174_0_m.png)
![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/VIS_TV_z500_h2_g0_e100_163042_0_m.png)

```python

from plantcv import plantcv as pcv

fig1, ax1 = pcv.visualize.pixel_scatter_plot(paths_to_imgs=file_paths, x_channel='b', y_channel='a')

fig2, ax2 = pcv.visualize.pixel_scatter_plot(paths_to_imgs=file_paths, x_channel='G', y_channel='b')

```

**Pixel scatter visualizations:**

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/brassica_pixel_scatter_ba.png)

![Screenshot](img/documentation_images/visualize_pixel_scatter_vis/brassica_pixel_scatter_Gb.png)



**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/visualize/pixel_scatter_vis.py)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ nav:
- 'Overlay Images': visualize_ovelay_two_imgs.md
- 'Object Size ECDF': visualize_obj_size_ecdf.md
- 'Object Sizes': visualize_obj_sizes.md
- 'Pixel Scatter Plot': 'visualize_pixel_scatter_vis.md'
- 'Pseudocolor': visualize_pseudocolor.md
- 'Watershed Segmentation': watershed.md
- 'White balance': white_balance.md
Expand Down
2 changes: 0 additions & 2 deletions plantcv/plantcv/io/read_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
def read_dataset(source_path, pattern='', sort=True):
"""
Read a dataset of images as a list of paths.

Inputs:
source_path = Path to the directory containing the images
pattern = Optional, return only filenames containing the pattern
sort = True by default, sorts the paths alphabetically

Returns:
dataset = List of paths to the images in the source path
:param source_path: str
Expand Down
4 changes: 3 additions & 1 deletion plantcv/plantcv/visualize/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from plantcv.plantcv.visualize.obj_sizes import obj_sizes
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

__all__ = ["pseudocolor", "colorize_masks", "histogram", "clustered_contours", "colorspaces", "auto_threshold_methods",
"overlay_two_imgs", "colorize_label_img", "obj_size_ecdf", "obj_sizes", "hyper_histogram"]
"overlay_two_imgs", "colorize_label_img", "obj_size_ecdf", "obj_sizes", "hyper_histogram",
"pixel_scatter_plot"]
123 changes: 123 additions & 0 deletions plantcv/plantcv/visualize/pixel_scatter_vis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Visualize a scatter plot of pixels

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
from plantcv import plantcv as pcv
from plantcv.plantcv import fatal_error
from plantcv.plantcv import params


MAX_MARKER_SIZE = 20
IMG_WIDTH = 128


# functions to get a given channel with parameters compatible
# with rgb2gray_lab and rgb2gray_hsv to use in the dict
def _get_R(rgb_img, _):
""" Get the red channel from a RGB image """
return rgb_img[:,:,2]


def _get_G(rgb_img, _):
""" Get the green channel from a RGB image """
return rgb_img[:,:,1]


def _get_B(rgb_img, _):
""" Get the blue channel from a RGB image """
return rgb_img[:,:,0]


def _get_gray(rgb_img, _):
""" Get the gray scale transformation of a RGB image """
return pcv.rgb2gray(rgb_img=rgb_img)

def _get_index(rgb_img, _):
""" Get a vector with linear indices of the pixels in an image """
h,w,_ = rgb_img.shape
return np.arange(h*w)


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")


def pixel_scatter_plot(paths_to_imgs, x_channel, y_channel):
"""
Plot a 2D pixel scatter plot visualization for a dataset of images.
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 pixel.

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'
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'

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

:param paths_to_imgs: str
:param x_channel: str
:param y_channel: str
:return fig: matplotlib.pyplot Figure object
:return ax: matplotlib.pyplot Axes object
"""
# dictionary returns the function that gets the required image channel
channel_dict = {
'R': _get_R,
'G': _get_G,
'B': _get_B,
'l': pcv.rgb2gray_lab,
'a': pcv.rgb2gray_lab,
'b': pcv.rgb2gray_lab,
'gray': _get_gray,
'h': pcv.rgb2gray_hsv,
's': pcv.rgb2gray_hsv,
'v': pcv.rgb2gray_hsv,
'index': _get_index,
}

# store debug mode
debug = params.debug
params.debug = None

N = len(paths_to_imgs)
# _ = plt.figure()
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")
h, w, 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)

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

# get channels
sub_img_x_ch = channel_dict.get(x_channel, _not_valid)(sub_img, x_channel)
sub_img_y_ch = channel_dict.get(y_channel, _not_valid)(sub_img, y_channel)

ax.scatter(sub_img_x_ch.reshape(-1),
sub_img_y_ch.reshape(-1),
alpha=0.05, s=MAX_MARKER_SIZE/N,
edgecolors=None, facecolors=fcolors)

plt.xlabel(x_channel)
plt.ylabel(y_channel)

# reset debug
params.debug = debug

return fig, ax
37 changes: 37 additions & 0 deletions tests/plantcv/visualize/test_pixel_scatter_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest
import cv2
import os
import numpy as np
from plantcv.plantcv.visualize.pixel_scatter_vis import pixel_scatter_plot


@pytest.mark.parametrize("ch", ['R', 'G', 'B', 'l', 'a', 'b', 'h', 's', 'v', 'gray'])
def test_plantcv_visualize_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)
# 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 a list of one path to the random image
_, _ = pixel_scatter_plot(paths_to_imgs=[path_to_img], x_channel=ch, y_channel='index')
assert 1


def test_plantcv_visualize_pixel_scatter_plot_wrong_ch(tmpdir):
"""Test for PlantCV."""
# Create a tmp directory
cache_dir = tmpdir.mkdir("cache")
rng = np.random.default_rng()
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')