-
Notifications
You must be signed in to change notification settings - Fork 279
Visualize show instances #655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 13 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
087f85f
Create display_instances.py
DannieSheng 1ea34c4
Update __init__.py
DannieSheng 25e413e
upload test data
DannieSheng 4c7ecee
Update tests.py
DannieSheng fe3f21e
Update display_instances.py
DannieSheng d96adac
Update display_instances.py
DannieSheng c3fcc18
Update tests.py
DannieSheng 6a599dd
Create visualize_display_instances.md
DannieSheng 6eae9af
upload documentation images
DannieSheng 90d1350
Merge branch 'master' into visualize_show_instances
HaleySchuhl 2416362
Merge branch 'master' into visualize_show_instances
DannieSheng 8783ed1
Update display_instances.py
DannieSheng 0b4cfd3
Merge branch '4.x' into visualize_show_instances
DannieSheng 9ce5f6d
Update display_instances.py
DannieSheng 38b9888
Update tests.py
DannieSheng 26d647f
Update tests.py
DannieSheng 0047468
Merge branch '4.x' into visualize_show_instances
DannieSheng cbe1dd4
Update display_instances.py
DannieSheng 0a61171
Update tests.py
DannieSheng 53477c4
Update __init__.py
DannieSheng aef9932
Merge branch 'master' into visualize_show_instances
DannieSheng 688e6aa
Update __init__.py
DannieSheng accf955
fix RGB -> BGR colors
DannieSheng 347a121
upload new doc image
DannieSheng 2483442
Update visualize_display_instances.md
DannieSheng c14785b
Update display_instances.py
DannieSheng eafaa6a
Update tests.py
DannieSheng e0bdf77
Merge remote-tracking branch 'origin/master' into visualize_show_inst…
DannieSheng c562169
Merge branch '4.x' into visualize_show_instances
DannieSheng 7979d0e
Merge remote-tracking branch 'origin/master' into visualize_show_inst…
DannieSheng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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.
Binary file added
BIN
+4.04 KB
docs/img/documentation_images/visualize_display_instances/mask_10.png
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.
Binary file added
BIN
+73.1 KB
docs/img/documentation_images/visualize_display_instances/result1.png
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.
Binary file added
BIN
+125 KB
...img/documentation_images/visualize_display_instances/visualize_inst_seg_img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| ## Display Instances | ||
|
|
||
| This function displays different object intances in different colors on top of the original image. | ||
|
|
||
| **plantcv.visualize.display_instances**(*img, masks, figsize=(16, 16), title="", ax=None, colors=None, captions=None, show_bbox=True*) | ||
| **returns** masked_img, colors | ||
|
|
||
| - **Parameters:** | ||
| - img - (required, ndarray)input image | ||
| - masks - (required, ndarray) instance masks represented by a 3-d array, the 3rd dimension represents the number of insatnces to show. | ||
| - figsize - (optional, tuple) the size of the generated figure | ||
| - title - (optional, str) the title of the figure | ||
| - ax - (optional, matplotlib.axes._subplots.AxesSubplot) the axis to plot on. If no axis is passed, create one and automatically call show()) | ||
| - colors - (optional, list of tuples, every value should be in the range of [0.0,1.0]) a list of colors to use with each object. If no value is passed, a set of random colors would be used | ||
| - captions - (optional, str) a list of strings to use as captions for each object. If no list of captions is provided, show the local index of the instance | ||
| - show_bbox - (optional, bool) indicator of whether showing the bounding-box | ||
|
|
||
| - **Context:** | ||
| - Used to display different segmented instances on top of the original image. | ||
| - **Example use:** | ||
| - Below | ||
|
|
||
| **Original image: RGB image** | ||
|
|
||
|  | ||
|
|
||
| **masks: 10 different segmentation masks represent for different leaves** | ||
|
|
||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|
|
||
|
|
||
| ```python | ||
|
|
||
| from plantcv import plantcv as pcv | ||
|
|
||
| masked_img,colors = pcv.visualize.display_instances(img, masks, figsize=(10, 10), title="", ax=None, colors=None, captions=None, show_bbox=True) | ||
|
|
||
| # option to add customized captions and/or customized figure title and/or not showing the bounding box | ||
| captions = ["leaf{}".format(i) for i in range(masks.shape[2])] | ||
| _,_ = pcv.visualize.display_instances(img, masks, figsize=(16, 16), title="Visualization of segmentation", ax=None, colors=None, captions=captions, show_bbox=False) | ||
|
|
||
| # the number of instances to display depends on the number of input masks | ||
| # the colors can also be customized | ||
| masks_reduced = masks[:,:,2:7], | ||
| colors = [(0.0,1.0,0.2),(0.4,0.0,1.0),(0.5,1.0,0.0),(1.0,0.6,0.0),(0.9,0.0,1.0)] | ||
| _,_= pcv.visualize.display_instances(img, masks_reduced, figsize=(16, 16), title="", ax=None, colors=colors, captions=None, show_bbox=True) | ||
|
|
||
| ``` | ||
|
|
||
| **Blended Image** | ||
|
|
||
|  | ||
|  | ||
|  | ||
| **Source Code:** [Here](https://github.com/danforthcenter/plantcv/blob/master/plantcv/plantcv/visualize/display_instances.py) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| # display instances in an image | ||
| import cv2 | ||
| from matplotlib.patches import Polygon | ||
| import colorsys | ||
| import random | ||
| from skimage.measure import find_contours | ||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
| from matplotlib import patches, lines | ||
| from matplotlib.patches import Polygon | ||
| # from plantcv.plantcv.visualize import overlay_two_imgs | ||
| # from plantcv.plantcv.visualize import colorize_masks | ||
| from plantcv import plantcv as pcv | ||
| from plantcv.plantcv import fatal_error | ||
|
|
||
| def _overlay_mask_on_image(image, mask, color, alpha=0.5): | ||
| """ apply a mask to an input image with a user defined color | ||
| :param image: input RGB or grayscale image | ||
| :param mask: desired mask | ||
| :param color: (a tuple) desired color to show the mask on top of the image | ||
| :param alpha: (a value between 0 and 1) transparency value when blending mask and image, by default 0.5 | ||
| :return: image with an mask with desired color on top of it | ||
| """ | ||
| if len(image.shape) == 2: | ||
| image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) | ||
| for c in range(image.shape[-1]): | ||
| image[:, :, c] = np.where(mask == 1, | ||
| image[:, :, c] * | ||
| (1 - alpha) + alpha * color[c] * 255, | ||
| image[:, :, c]) | ||
| return image | ||
|
|
||
| def _random_colors(num, bright=True): | ||
DannieSheng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| Generate desired number of random colors. To get visually distinct colors, generate them in HSV space then convert to RGB. | ||
| :param num: number of colors to be generated | ||
| :param bright: True or False, if true, the brightness would be 1.0; if False, the brightness would be 0.7. By default it would be True (brightness 0.7) | ||
| :return: generated colors (a list of tuples) | ||
| """ | ||
|
|
||
| brightness = 1.0 if bright else 0.7 | ||
| hsv = [(i / num, 1, brightness) for i in range(num)] | ||
| colors = list(map(lambda c: colorsys.hsv_to_rgb(*c), hsv)) | ||
| random.shuffle(colors) | ||
| return colors | ||
|
|
||
| # def display_instances(image, masks, figsize=(16, 16), title="", ax=None, colors=None, captions=None, show_bbox=True): | ||
DannieSheng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # """ | ||
| # This function is inspired by the same function used in mrcnn, showing different instances with different colors on top of the original image | ||
| # Users also have the option to specify the color for every instance, as well as the caption for every instance. Showing bounding boxes is another option. | ||
| # :param image: (required, ndarray) | ||
| # :param masks: (required, ndarray) | ||
| # :param figsize: (optional, tuple) the size of the generated figure | ||
| # :param title: (optional, str) the title of the figure | ||
| # :param ax: (optional, matplotlib.axes._subplots.AxesSubplot) the axis to plot on. If no axis is passed, create one and automatically call show() | ||
| # :param colors: (optional, list of tuples, every value should be in the range of [0.0,1.0]) a list of colors to use with each object. If no value is passed, a set of random colors would be used | ||
| # :param captions: (optional, str) a list of strings to use as captions for each object. If no list of captions is provided, show the local index of the instance | ||
| # :param show_bbox: (optional, bool) indicator of whether showing the bounding-box | ||
| # :return:masked_image | ||
| # :return:colors: colors used to show the instances (same as number of instances) | ||
| # """ | ||
| # | ||
| # if image.shape[0:2] != masks.shape[0:2]: | ||
| # fatal_error("Sizes of image and mask mismatch!") | ||
| # # | ||
| # # auto_show = False | ||
| # if not ax: | ||
| # _, ax = plt.subplots(1, figsize=figsize) | ||
| # # auto_show = True | ||
| # | ||
| # num_insts = masks.shape[2] | ||
| # # Generate random colors | ||
| # colors = colors or _random_colors(num_insts) | ||
| # if len(colors) < num_insts: | ||
| # fatal_error("Not enough colors provided to show all instances!") | ||
| # if len(colors) > num_insts: | ||
| # colors = colors[0:num_insts] | ||
| # | ||
| # # Show area outside image boundaries. | ||
| # height, width = image.shape[:2] | ||
| # ax.set_ylim(height + 10, -10) | ||
| # ax.set_xlim(-10, width + 10) | ||
| # ax.axis('off') | ||
| # ax.set_title(title) | ||
| # | ||
| # masks_ = [masks[:, :, i] for i in range(num_insts)] | ||
| # colors_ = [tuple([x * 255 for x in color]) for color in colors] | ||
| # colorized_mask = pcv.visualize.colorize_masks(masks_, colors_) | ||
| # masked_image = pcv.visualize.overlay_two_imgs(image, colorized_mask, alpha=0.5) | ||
| # | ||
| # # masked_image = image.astype(np.uint32).copy() | ||
| # for i in range(num_insts): | ||
| # color = colors[i] | ||
| # | ||
| # # Mask | ||
| # mask = masks[:, :, i] | ||
| # # masked_image = _apply_mask(masked_image, mask, color) | ||
| # | ||
| # ys, xs = np.where(mask > 0) | ||
| # x1, x2 = min(xs), max(xs) | ||
| # y1, y2 = min(ys), max(ys) | ||
| # if show_bbox: | ||
| # p = patches.Rectangle((x1, y1), x2 - x1, y2 - y1, linewidth=2, alpha=0.7, linestyle="dashed", | ||
| # edgecolor=color, facecolor='none') | ||
| # ax.add_patch(p) | ||
| # | ||
| # # Mask Polygon | ||
| # # Pad to ensure proper polygons for masks that touch image edges. | ||
| # padded_mask = np.zeros( | ||
| # (mask.shape[0] + 2, mask.shape[1] + 2), dtype=np.uint8) | ||
| # padded_mask[1:-1, 1:-1] = mask | ||
| # contours = find_contours(padded_mask, 0.5) | ||
| # for verts in contours: | ||
| # # Subtract the padding and flip (y, x) to (x, y) | ||
| # verts = np.fliplr(verts) - 1 | ||
| # p = Polygon(verts, facecolor="none", edgecolor=color) | ||
| # ax.add_patch(p) | ||
| # if not captions: | ||
| # caption = str(i) | ||
| # else: | ||
| # caption = captions[i] | ||
| # ax.text(x1, y1 + 8, caption, | ||
| # color='w', size=13, backgroundcolor="none") | ||
| # ax.imshow(masked_image.astype(np.uint8)) | ||
| # return masked_image, colors | ||
|
|
||
| # former definition of this function, relies on the definition of the local funciton "_apply_mask" | ||
| def display_instances(image, masks, figsize=(16, 16), title="", ax=None, colors=None, captions=None, show_bbox=True): | ||
| """ | ||
| This function is inspired by the same function used in mrcnn, showing different instances with different colors on top of the original image | ||
| Users also have the option to specify the color for every instance, as well as the caption for every instance. Showing bounding boxes is another option. | ||
| :param image: (required, ndarray) | ||
| :param masks: (required, ndarray) | ||
| :param figsize: (optional, tuple) the size of the generated figure | ||
| :param title: (optional, str) the title of the figure | ||
| :param ax: (optional, matplotlib.axes._subplots.AxesSubplot) the axis to plot on. If no axis is passed, create one and automatically call show() | ||
| :param colors: (optional, list of tuples, every value should be in the range of [0.0,1.0]) a list of colors to use with each object. If no value is passed, a set of random colors would be used | ||
| :param captions: (optional, str) a list of strings to use as captions for each object. If no list of captions is provided, show the local index of the instance | ||
| :param show_bbox: (optional, bool) indicator of whether showing the bounding-box | ||
| :return:masked_image | ||
| :return:colors: colors used to show the instances (same as number of instances) | ||
| """ | ||
| if image.shape[0:2] != masks.shape[0:2]: | ||
| fatal_error("Sizes of image and mask mismatch!") | ||
| # | ||
| # auto_show = False | ||
| if not ax: | ||
| _, ax = plt.subplots(1, figsize=figsize) | ||
| # auto_show = True | ||
|
|
||
| num_insts = masks.shape[2] | ||
| # Generate random colors | ||
| colors = colors or _random_colors(num_insts) | ||
| if len(colors) < num_insts: | ||
| fatal_error("Not enough colors provided to show all instances!") | ||
| if len(colors) > num_insts: | ||
| colors = colors[0:num_insts] | ||
|
|
||
| # Show area outside image boundaries. | ||
| height, width = image.shape[:2] | ||
| ax.set_ylim(height + 10, -10) | ||
| ax.set_xlim(-10, width + 10) | ||
| ax.axis('off') | ||
| ax.set_title(title) | ||
|
|
||
| masked_image = image.astype(np.uint32).copy() | ||
| for i in range(num_insts): | ||
| color = colors[i] | ||
|
|
||
| # Mask | ||
| mask = masks[:, :, i] | ||
| masked_image = _overlay_mask_on_image(masked_image, mask, color) | ||
|
|
||
| ys, xs = np.where(mask > 0) | ||
| x1, x2 = min(xs), max(xs) | ||
| y1, y2 = min(ys), max(ys) | ||
| if show_bbox: | ||
| p = patches.Rectangle((x1, y1), x2 - x1, y2 - y1, linewidth=2, alpha=0.7, linestyle="dashed", | ||
| edgecolor=color, facecolor='none') | ||
| ax.add_patch(p) | ||
|
|
||
| # Mask Polygon | ||
| # Pad to ensure proper polygons for masks that touch image edges. | ||
| padded_mask = np.zeros( | ||
| (mask.shape[0] + 2, mask.shape[1] + 2), dtype=np.uint8) | ||
| padded_mask[1:-1, 1:-1] = mask | ||
| contours = find_contours(padded_mask, 0.5) | ||
| for verts in contours: | ||
| # Subtract the padding and flip (y, x) to (x, y) | ||
| verts = np.fliplr(verts) - 1 | ||
| p = Polygon(verts, facecolor="none", edgecolor=color) | ||
| ax.add_patch(p) | ||
| if not captions: | ||
| caption = str(i) | ||
| else: | ||
| caption = captions[i] | ||
| ax.text(x1, y1 + 8, caption, | ||
| color='w', size=13, backgroundcolor="none") | ||
| ax.imshow(masked_image.astype(np.uint8)) | ||
| return masked_image, colors | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.