Skip to content

Commit

Permalink
Merge pull request #595 from cluebbert/master
Browse files Browse the repository at this point in the history
Adding option to segement_skeleton to generate random colors instead …
  • Loading branch information
nfahlgren authored Jul 21, 2020
2 parents 1725868 + a5e31c5 commit b810da9
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 109 deletions.
35 changes: 27 additions & 8 deletions docs/color_palette.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
## Color Palette

Returns a random list of RGB color values, equally spaced across a rainbow color spectrum.
If one color is requested, a random color is returned. Otherwise, evenly spaced colors are returned.
Returns a list of RGB color values, equally spaced across a color map. The color map used is configurable and colors
can either be returned in sequential or randomized order.

**plantcv.color_palette(*num*)**
**plantcv.color_palette(*num, saved=False*)**

**returns** colors

- **Parameters:**
- num - an integer number greater than or equal to 1
- saved - True/False whether a previously saved color scale should be used. Default = False
- **Context:**
- Used when a random set of colors is needed.
- Used when a set of colors is needed.
- See: [Multi-plant tutorial](multi-plant_tutorial.md), [Morphology tutorial](morphology_tutorial.md),
[Spatial clustering](spatial_clustering.md), and [Watershed segmentation](watershed.md).
- Visit the [Matplotlib](https://matplotlib.org/tutorials/colors/colormaps.html#sphx-glr-tutorials-colors-colormaps-py) website for a list of available colormaps.

```python

from plantcv import plantcv as pcv

# Get one random color
# Get one color
colors = pcv.color_palette(1)
print(colors)
[(255, 16, 0)]
# [[255, 0, 40]]

# Get five random colors
# The color scale is saved for use by other functions
print(pcv.params.saved_color_scale)
# [[255, 0, 40]]

# The color scale can be changed and the order can be changed from "sequential" to "random"
pcv.params.color_scale = "viridis"
pcv.params.color_sequence = "random"

# Get five colors (note this will be a new color scale because saved = False by default)
colors = pcv.color_palette(5)
print(colors)
[(0, 0, 255), (0, 255, 205), (100, 255, 0), (255, 106, 0), (255, 0, 199)]
# [[68, 1, 84], [94, 201, 97], [58, 82, 139], [253, 231, 36], [32, 144, 140]]

# To use a saved color scale (if it exists)
colors = pcv.color_palette(num=5, saved=True)
print(colors)
# [[68, 1, 84], [94, 201, 97], [58, 82, 139], [253, 231, 36], [32, 144, 140]]

# To explicitly remove the saved scale, set it to None
pcv.params.saved_color_scale = None
```

**Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/color_palette.py)
13 changes: 11 additions & 2 deletions docs/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ Attributes are accessed as plantcv.*attribute*.

**dpi**: Dots per inch for plotting debugging images.

**text_size**: Size of the text for labels in debugging plots created by [segment_angle](segment_angle.md), [segment_curvature](segment_curvature.md), [segment_euclidean_length.md),
**text_size**: Size of the text for labels in debugging plots created by [segment_angle](segment_angle.md), [segment_curvature](segment_curvature.md), [segment_euclidean_length](segment_euclidean_length.md),
[segment_id](segment_id.md), [segment_insertion_angle](segment_insertion_angle.md), [segment_path_length](segment_pathlength.md), and [segment_tangent_angle](segment_tangent_angle.md) from
the morphology sub-package.

**text_thickness**: Thickness of the text for labels in debugging plots created by [segment_angle](segment_angle.md), [segment_curvature](segment_curvature.md), [segment_euclidean_length.md),
**text_thickness**: Thickness of the text for labels in debugging plots created by [segment_angle](segment_angle.md), [segment_curvature](segment_curvature.md), [segment_euclidean_length](segment_euclidean_length.md),
[segment_id](segment_id.md), [segment_insertion_angle](segment_insertion_angle.md), [segment_path_length](segment_pathlength.md), and [segment_tangent_angle](segment_tangent_angle.md) from
the morphology sub-package.

**color_scale**: The name of a color scale (a Matplot lib colormap) used by [segment_angle](segment_angle.md), [segment_curvature](segment_curvature.md),
[segment_euclidean_length](segment_euclidean_length.md), [segment_insertion_angle](segment_insertion_angle.md), [segment_path_length](segment_pathlength.md), and [segment_skeleton](segment_skeleton.md),
[segment_tangent_angle](segment_tangent_angle.md) from the morphology sub-package, and [cluster_contours](cluster_contours.md), [spatial_clustering](spatial_clustering.md), and
[watershed_segmentation](watershed.md) from the base package. Default = "gist_rainbow". See the [Matplotlib](https://matplotlib.org/tutorials/colors/colormaps.html#sphx-glr-tutorials-colors-colormaps-py) website for available options.

**color_sequence**: Set the sequence of colors from the `color_scale` created by the `color_palette` function to "sequential" or "random" order. Default = "sequential".

**saved_color_scale**: Using the `color_palette` function will save the color scale here for reuse in downstream functions. Set to `None` to remove. Default = `None`.

### Example

Updated PlantCV functions use `params` implicitly, so overriding the `params` defaults will alter the behavior of
Expand Down
1 change: 1 addition & 0 deletions docs/updating.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ pages for more details on the input and output variable types.

* pre v3.0: NA
* post v3.0: colors = **plantcv.color_palette**(*num*)
* post v3.9: colors = **plantcv.color_palette**(*num, saved=False*)

#### plantcv.crop_position_mask

Expand Down
43 changes: 30 additions & 13 deletions plantcv/plantcv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,45 @@


class Params:
"""PlantCV parameters class
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: .)
:param device: int
:param debug: str
:param debug_outdir: str
:param line_thickness: numeric
:param dpi: int
:param text_size: float
"""
"""PlantCV parameters class."""

def __init__(self, device=0, debug=None, debug_outdir=".", line_thickness=5, dpi=100, text_size=0.55,
text_thickness=2):
text_thickness=2, color_scale="gist_rainbow", color_sequence="sequential", saved_color_scale=None):
"""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)
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)
color_scale = Name of plotting color scale (matplotlib colormap). (default: gist_rainbow)
color_sequence = Build color scales in "sequential" or "random" order. (default: sequential)
saved_color_scale = Saved color scale that will be applied next time color_palette is called. (default: None)
: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 color_scale: str
:param color_sequence: str
:param saved_color_scale: list
"""
self.device = device
self.debug = debug
self.debug_outdir = debug_outdir
self.line_thickness = line_thickness
self.dpi = dpi
self.text_size = text_size
self.text_thickness = text_thickness
self.color_scale = color_scale
self.color_sequence = color_sequence
self.saved_color_scale = saved_color_scale


class Outputs:
Expand Down
76 changes: 21 additions & 55 deletions plantcv/plantcv/color_palette.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,36 @@
# Color palette returns an array of colors (rainbow)

from random import randrange
from matplotlib import pyplot as plt
import numpy as np
from plantcv.plantcv import params


def color_palette(num):
def color_palette(num, saved=False):
"""color_palette: Returns a list of colors length num
Inputs:
num = number of colors to return. If num = 1 a random color is returned,
otherwise, evenly spaced colors are returned.
num = number of colors to return.
saved = use the previously stored color scale, if any (default = False).
Returns:
colors = a list of color tuples (RGB values)
colors = a list of color lists (RGB values)
:param num: int
:return colors: list
"""
# Rainbow color scheme (red->red)
rainbow = (
(0, 0, 255), (0, 6, 255), (0, 12, 255), (0, 18, 255), (0, 24, 255), (0, 30, 255), (0, 36, 255), (0, 42, 255),
(0, 48, 255), (0, 54, 255), (0, 60, 255), (0, 66, 255), (0, 72, 255), (0, 78, 255), (0, 84, 255), (0, 90, 255),
(0, 96, 255), (0, 102, 255), (0, 108, 255), (0, 114, 255), (0, 120, 255), (0, 126, 255), (0, 131, 255),
(0, 137, 255), (0, 143, 255), (0, 149, 255), (0, 155, 255), (0, 161, 255), (0, 167, 255), (0, 173, 255),
(0, 179, 255), (0, 185, 255), (0, 191, 255), (0, 197, 255), (0, 203, 255), (0, 209, 255), (0, 215, 255),
(0, 221, 255), (0, 227, 255), (0, 233, 255), (0, 239, 255), (0, 245, 255), (0, 251, 255), (0, 255, 253),
(0, 255, 247), (0, 255, 241), (0, 255, 235), (0, 255, 229), (0, 255, 223), (0, 255, 217), (0, 255, 211),
(0, 255, 205), (0, 255, 199), (0, 255, 193), (0, 255, 187), (0, 255, 181), (0, 255, 175), (0, 255, 169),
(0, 255, 163), (0, 255, 157), (0, 255, 151), (0, 255, 145), (0, 255, 139), (0, 255, 133), (0, 255, 128),
(0, 255, 122), (0, 255, 116), (0, 255, 110), (0, 255, 104), (0, 255, 98), (0, 255, 92), (0, 255, 86),
(0, 255, 80),
(0, 255, 74), (0, 255, 68), (0, 255, 62), (0, 255, 56), (0, 255, 50), (0, 255, 44), (0, 255, 38), (0, 255, 32),
(0, 255, 26), (0, 255, 20), (0, 255, 14), (0, 255, 8), (0, 255, 2), (4, 255, 0), (10, 255, 0), (16, 255, 0),
(22, 255, 0), (28, 255, 0), (34, 255, 0), (40, 255, 0), (46, 255, 0), (52, 255, 0), (58, 255, 0), (64, 255, 0),
(70, 255, 0), (76, 255, 0), (82, 255, 0), (88, 255, 0), (94, 255, 0), (100, 255, 0), (106, 255, 0),
(112, 255, 0),
(118, 255, 0), (124, 255, 0), (129, 255, 0), (135, 255, 0), (141, 255, 0), (147, 255, 0), (153, 255, 0),
(159, 255, 0), (165, 255, 0), (171, 255, 0), (177, 255, 0), (183, 255, 0), (189, 255, 0), (195, 255, 0),
(201, 255, 0), (207, 255, 0), (213, 255, 0), (219, 255, 0), (225, 255, 0), (231, 255, 0), (237, 255, 0),
(243, 255, 0), (249, 255, 0), (255, 255, 0), (255, 249, 0), (255, 243, 0), (255, 237, 0), (255, 231, 0),
(255, 225, 0), (255, 219, 0), (255, 213, 0), (255, 207, 0), (255, 201, 0), (255, 195, 0), (255, 189, 0),
(255, 183, 0), (255, 177, 0), (255, 171, 0), (255, 165, 0), (255, 159, 0), (255, 153, 0), (255, 147, 0),
(255, 141, 0), (255, 135, 0), (255, 129, 0), (255, 124, 0), (255, 118, 0), (255, 112, 0), (255, 106, 0),
(255, 100, 0), (255, 94, 0), (255, 88, 0), (255, 82, 0), (255, 76, 0), (255, 70, 0), (255, 64, 0), (255, 58, 0),
(255, 52, 0), (255, 46, 0), (255, 40, 0), (255, 34, 0), (255, 28, 0), (255, 22, 0), (255, 16, 0), (255, 10, 0),
(255, 4, 0), (255, 0, 2), (255, 0, 8), (255, 0, 14), (255, 0, 20), (255, 0, 26), (255, 0, 32), (255, 0, 38),
(255, 0, 44), (255, 0, 50), (255, 0, 56), (255, 0, 62), (255, 0, 68), (255, 0, 74), (255, 0, 80), (255, 0, 86),
(255, 0, 92), (255, 0, 98), (255, 0, 104), (255, 0, 110), (255, 0, 116), (255, 0, 122), (255, 0, 128),
(255, 0, 133), (255, 0, 139), (255, 0, 145), (255, 0, 151), (255, 0, 157), (255, 0, 163), (255, 0, 169),
(255, 0, 175), (255, 0, 181), (255, 0, 187), (255, 0, 193), (255, 0, 199), (255, 0, 205), (255, 0, 211),
(255, 0, 217), (255, 0, 223), (255, 0, 229), (255, 0, 235), (255, 0, 241), (255, 0, 247), (255, 0, 253),
(251, 0, 255), (245, 0, 255), (239, 0, 255), (233, 0, 255), (227, 0, 255), (221, 0, 255), (215, 0, 255),
(209, 0, 255), (203, 0, 255), (197, 0, 255), (191, 0, 255), (185, 0, 255), (179, 0, 255), (173, 0, 255),
(167, 0, 255), (161, 0, 255), (155, 0, 255), (149, 0, 255), (143, 0, 255), (137, 0, 255), (131, 0, 255),
(126, 0, 255), (120, 0, 255), (114, 0, 255), (108, 0, 255), (102, 0, 255), (96, 0, 255), (90, 0, 255),
(84, 0, 255),
(78, 0, 255), (72, 0, 255), (66, 0, 255), (60, 0, 255), (54, 0, 255), (48, 0, 255), (42, 0, 255), (36, 0, 255),
(30, 0, 255), (24, 0, 255), (18, 0, 255), (12, 0, 255), (6, 0, 255))

if num == 1:
color = rainbow[randrange(0, 255)]
return [color]
# If a previous palette is saved and saved = True, return it
if params.saved_color_scale is not None and saved is True:
return params.saved_color_scale
else:
dist = int(len(rainbow) / num)
colors = []
index = 0
for i in range(1, num + 1):
colors.append(rainbow[index])
index += dist
# Retrieve the matplotlib colormap
cmap = plt.get_cmap(params.color_scale)
# Get num evenly spaced colors
colors = cmap(np.linspace(0, 1, num), bytes=True)
colors = colors[:, 0:3].tolist()
# colors are sequential, if params.color_sequence is random then shuffle the colors
if params.color_sequence == "random":
np.random.shuffle(colors)
# Save the color scale for further use
params.saved_color_scale = colors

return colors
3 changes: 2 additions & 1 deletion plantcv/plantcv/morphology/check_cycles.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def check_cycles(skel_img):
cycle_img = dilate(cycle_img, params.line_thickness, 1)
cycle_img = cv2.cvtColor(cycle_img, cv2.COLOR_GRAY2RGB)
if num_cycles > 0:
rand_color = color_palette(num_cycles)
# Get a new color scale
rand_color = color_palette(num=num_cycles, saved=False)
for i, cnt in enumerate(cycle_objects):
cv2.drawContours(cycle_img, cycle_objects, i, rand_color[i], params.line_thickness, lineType=8,
hierarchy=cycle_hierarchies)
Expand Down
3 changes: 2 additions & 1 deletion plantcv/plantcv/morphology/segment_angle.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def segment_angle(segmented_img, objects):

labeled_img = segmented_img.copy()

rand_color = color_palette(len(objects))
# Use a previously saved color scale if available
rand_color = color_palette(num=len(objects), saved=True)

for i, cnt in enumerate(objects):
# Find bounds for regression lines to get drawn
Expand Down
4 changes: 2 additions & 2 deletions plantcv/plantcv/morphology/segment_combine.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def segment_combine(segment_list, objects, mask):
labeled_img = mask.copy()
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_GRAY2RGB)

# Color each segment a different color
rand_color = color_palette(len(all_objects))
# Color each segment a different color, use a previously saved scale if available
rand_color = color_palette(num=len(all_objects), saved=True)

# Plot all segment contours
for i, cnt in enumerate(all_objects):
Expand Down
3 changes: 2 additions & 1 deletion plantcv/plantcv/morphology/segment_curvature.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def segment_curvature(segmented_img, objects):
eu_lengths = outputs.observations['segment_eu_length']['value']
path_lengths = outputs.observations['segment_path_length']['value']
curvature_measure = [float(x / y) for x, y in zip(path_lengths, eu_lengths)]
rand_color = color_palette(len(objects))
# Create a color scale, use a previously stored scale if available
rand_color = color_palette(num=len(objects), saved=True)

for i, cnt in enumerate(objects):
# Store coordinates for labels
Expand Down
3 changes: 2 additions & 1 deletion plantcv/plantcv/morphology/segment_euclidean_length.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def segment_euclidean_length(segmented_img, objects):
x_list = []
y_list = []
segment_lengths = []
rand_color = color_palette(len(objects))
# Create a color scale, use a previously stored scale if available
rand_color = color_palette(num=len(objects), saved=True)

labeled_img = segmented_img.copy()
# Store debug
Expand Down
4 changes: 2 additions & 2 deletions plantcv/plantcv/morphology/segment_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ def segment_id(skel_img, objects, mask=None):

segmented_img = cv2.cvtColor(segmented_img, cv2.COLOR_GRAY2RGB)

# Color each segment a different color
rand_color = color_palette(len(objects))
# Create a color scale, use a previously stored scale if available
rand_color = color_palette(num=len(objects), saved=True)

# Plot all segment contours
for i, cnt in enumerate(objects):
Expand Down
3 changes: 2 additions & 1 deletion plantcv/plantcv/morphology/segment_insertion_angle.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ def segment_insertion_angle(skel_img, segmented_img, leaf_objects, stem_objects,
label_coord_x.append(leaf_objects[i][0][0][0])
label_coord_y.append(leaf_objects[i][0][0][1])

rand_color = color_palette(len(valid_segment))
# Create a color scale, use a previously stored scale if available
rand_color = color_palette(num=len(valid_segment), saved=True)

for i, cnt in enumerate(valid_segment):
cv2.drawContours(labeled_img, valid_segment, i, rand_color[i], params.line_thickness, lineType=8)
Expand Down
10 changes: 4 additions & 6 deletions plantcv/plantcv/morphology/segment_skeleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,17 @@ def segment_skeleton(skel_img, mask=None):
""" Segment a skeleton image into pieces
Inputs:
skel_img = Skeletonized image
mask = (Optional) binary mask for debugging. If provided, debug image will be overlaid on the mask.
skel_img = Skeletonized image
mask = (Optional) binary mask for debugging. If provided, debug image will be overlaid on the mask.
Returns:
segmented_img = Segmented debugging image
segment_objects = list of contours
segment_hierarchies = contour hierarchy list
:param skel_img: numpy.ndarray
:param mask: numpy.ndarray
:return segmented_img: numpy.ndarray
:return segment_objects: list
"return segment_hierarchies: numpy.ndarray
"""

# Store debug
Expand All @@ -48,8 +46,8 @@ def segment_skeleton(skel_img, mask=None):
# Reset debug mode
params.debug = debug

# Color each segment a different color
rand_color = color_palette(len(segment_objects))
# Color each segment a different color, do not used a previously saved color scale
rand_color = color_palette(num=len(segment_objects), saved=False)

if mask is None:
segmented_img = skel_img.copy()
Expand Down
3 changes: 2 additions & 1 deletion plantcv/plantcv/morphology/segment_tangent_angle.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def segment_tangent_angle(segmented_img, objects, size):
label_coord_x = []
label_coord_y = []

rand_color = color_palette(len(objects))
# Create a color scale, use a previously stored scale if available
rand_color = color_palette(num=len(objects), saved=True)

for i, cnt in enumerate(objects):
find_tangents = np.zeros(segmented_img.shape[:2], np.uint8)
Expand Down
Loading

0 comments on commit b810da9

Please sign in to comment.