-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into 18-napari-import-data
- Loading branch information
Showing
26 changed files
with
1,189 additions
and
44 deletions.
There are no files selected for viewing
This file contains 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,4 @@ | ||
[run] | ||
omit = | ||
config.py | ||
config-3.py |
This file contains 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 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 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 |
---|---|---|
@@ -1,3 +1,31 @@ | ||
## Changelog | ||
|
||
All notable changes to this project will be documented below. | ||
|
||
#### annotate.get_centroids | ||
|
||
* v0.1dev: coords_list = **annotate.get_centroids**(*bin_img*) | ||
|
||
#### annotate.napari_classes | ||
|
||
* v0.1dev: class_list = **annotate.napari_classes**(*viewer*) | ||
|
||
#### annotate.napari_join_labels | ||
|
||
* v0.1dev: relabeled_mask, mask_dict = **annotate.napari_join_labels**(*img, viewer*) | ||
|
||
#### annotate.napari_label_classes | ||
|
||
* v0.1dev: viewer = **annotate.napari_label_classes**(*img, classes, show=True*) | ||
|
||
#### annotate.napari_open | ||
|
||
* v0.1dev: viewer = **annotate.napari_open**(*img, mode = 'native', show=True*) | ||
|
||
#### annotate.napari_save_coor | ||
|
||
* v0.1dev: datadict = **annotate.napari_save_coor**(*viewer, filepath*) | ||
|
||
#### annotate.Points | ||
|
||
* v0.1dev: viewer = **annotate.Points**(*img, figsize=(12,6), label="dafault"*) |
This file contains 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,44 @@ | ||
## Get Centroids | ||
|
||
Extract the centroid coordinate (column,row) from regions in a binary image. | ||
|
||
**plantcv.annotate.get_centroids**(*bin_img*) | ||
|
||
**returns** list containing coordinates of centroids | ||
|
||
- **Parameters:** | ||
- bin_img - Binary image containing the connected regions to consider | ||
- **Context:** | ||
- Given an arbitrary mask of the objects of interest, `get_centroids` | ||
returns a list of coordinates that can the be imported into the annotation class [Points](Points.md). | ||
|
||
- **Example use:** | ||
- Below | ||
|
||
**Binary image** | ||
|
||
![count_img](img/documentation_images/get_centroids/discs_mask.png) | ||
|
||
```python | ||
|
||
from plantcv import plantcv as pcv | ||
|
||
# Set global debug behavior to None (default), "print" (to file), | ||
# or "plot" | ||
pcv.params.debug = "plot" | ||
|
||
# Apply get centroids to the binary image | ||
coords = pcv.annotate.get_centroids(bin_img=binary_img) | ||
print(coords) | ||
# [[1902, 600], [1839, 1363], [1837, 383], [1669, 1977], [1631, 1889], [1590, 1372], [1550, 1525], | ||
# [1538, 1633], [1522, 1131], [1494, 2396], [1482, 1917], [1446, 1808], [1425, 726], [1418, 2392], | ||
# [1389, 198], [1358, 1712], [1288, 522], [1289, 406], [1279, 368], [1262, 1376], [1244, 1795], | ||
# [1224, 1327], [1201, 624], [1181, 725], [1062, 85], [999, 840], [885, 399], [740, 324], [728, 224], | ||
# [697, 860], [660, 650], [638, 2390], [622, 1565], [577, 497], [572, 2179], [550, 2230], [547, 1826], | ||
# [537, 892], [538, 481], [524, 2144], [521, 2336], [497, 201], [385, 1141], [342, 683], [342, 102], | ||
# [332, 1700], [295, 646], [271, 60], [269, 1626], [210, 1694], [189, 878], [178, 1570], [171, 2307], | ||
# [61, 286], [28, 2342]] | ||
|
||
``` | ||
|
||
**Source Code:** [Here](https://github.com/danforthcenter/plantcv-annotate/blob/main/plantcv/annotate/get_centroids.py) |
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 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 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 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 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 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 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 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,160 @@ | ||
# Class helpers | ||
|
||
# Imports | ||
import cv2 | ||
import json | ||
from math import floor | ||
import matplotlib.pyplot as plt | ||
from plantcv.plantcv.annotate.points import _find_closest_pt | ||
from plantcv.plantcv import warn | ||
|
||
|
||
class Points: | ||
"""Point annotation/collection class to use in Jupyter notebooks. It allows the user to | ||
interactively click to collect coordinates from an image. Left click collects the point and | ||
right click removes the closest collected point. | ||
""" | ||
|
||
def __init__(self, img, figsize=(12, 6), label="default", color="r", view_all=False): | ||
"""Points initialization method. | ||
Parameters | ||
---------- | ||
img : numpy.ndarray | ||
image to annotate | ||
figsize : tuple, optional | ||
figure plotting size, by default (12, 6) | ||
label : str, optional | ||
class label, by default "default" | ||
""" | ||
self.img = img | ||
self.figsize = figsize | ||
self.label = label # current label | ||
self.color = color # current color | ||
self.view_all = view_all # a flag indicating whether or not view all labels | ||
self.coords = {} # dictionary of all coordinates per group label | ||
self.events = [] # includes right and left click events | ||
self.count = {} # a dictionary that saves the counts of different groups (labels) | ||
self.sample_labels = [] # list of all sample labels, one to one with points collected | ||
self.colors = {} # all used colors | ||
|
||
self.view(label=self.label, color=self.color, view_all=self.view_all) | ||
|
||
def onclick(self, event): | ||
"""Handle mouse click events | ||
Parameters | ||
---------- | ||
event : matplotlib.backend_bases.MouseEvent | ||
matplotlib MouseEvent object | ||
""" | ||
print(type(event)) | ||
self.events.append(event) | ||
if event.button == 1: | ||
# Add point to the plot | ||
self.ax.plot(event.xdata, event.ydata, marker='x', c=self.color) | ||
self.coords[self.label].append((floor(event.xdata), floor(event.ydata))) | ||
self.count[self.label] += 1 | ||
self.sample_labels.append(self.label) | ||
else: | ||
idx_remove, _ = _find_closest_pt((event.xdata, event.ydata), self.coords[self.label]) | ||
# remove the closest point to the user right clicked one | ||
self.coords[self.label].pop(idx_remove) | ||
self.count[self.label] -= 1 | ||
idx_remove = idx_remove + self.p_not_current | ||
self.ax.lines[idx_remove].remove() | ||
self.sample_labels.pop(idx_remove) | ||
self.fig.canvas.draw() | ||
|
||
def print_coords(self, filename): | ||
"""Save collected coordinates to a file. | ||
Parameters | ||
---------- | ||
filename : str | ||
output filename | ||
""" | ||
# Open the file for writing | ||
with open(filename, "w") as fp: | ||
# Save the data in JSON format with indentation | ||
json.dump(obj=self.coords, fp=fp, indent=4) | ||
|
||
def import_list(self, coords, label="default"): | ||
"""Import coordinates. | ||
Parameters | ||
---------- | ||
coords : list | ||
list of coordinates (tuples) | ||
label : str, optional | ||
class label, by default "default" | ||
""" | ||
if label not in self.coords: | ||
self.coords[label] = [] | ||
for (y, x) in coords: | ||
self.coords[label].append((x, y)) | ||
self.count[label] = len(self.coords[label]) | ||
self.view(label=label, color=self.color, view_all=False) | ||
else: | ||
warn(f"{label} already included and counted, nothing is imported!") | ||
|
||
def import_file(self, filename): | ||
"""Import coordinates from a file. | ||
Parameters | ||
---------- | ||
filename : str | ||
JSON file containing Points annotations | ||
""" | ||
with open(filename, "r") as fp: | ||
coords = json.load(fp) | ||
|
||
keys = list(coords.keys()) | ||
|
||
for key in keys: | ||
keycoor = coords[key] | ||
keycoor = list(map(lambda sub: (sub[1], sub[0]), keycoor)) | ||
self.import_list(keycoor, label=key) | ||
|
||
def view(self, label="default", color="r", view_all=False): | ||
"""View coordinates for a specific class label. | ||
Parameters | ||
---------- | ||
label : str, optional | ||
class label, by default "default" | ||
color : str, optional | ||
marker color, by default "r" | ||
view_all : bool, optional | ||
view all classes or a single class, by default False | ||
""" | ||
if label not in self.coords and color in self.colors.values(): | ||
warn("The color assigned to the new class label is already used, if proceeding, " | ||
"items from different classes will not be distinguishable in plots!") | ||
self.label = label | ||
self.color = color | ||
self.view_all = view_all | ||
|
||
if self.label not in self.coords: | ||
self.coords[self.label] = [] | ||
self.count[self.label] = 0 | ||
self.colors[self.label] = self.color | ||
|
||
self.fig, self.ax = plt.subplots(1, 1, figsize=self.figsize) | ||
|
||
self.events = [] | ||
self.fig.canvas.mpl_connect('button_press_event', self.onclick) | ||
|
||
self.ax.imshow(cv2.cvtColor(self.img, cv2.COLOR_BGR2RGB)) | ||
self.ax.set_title("Please left click on objects\n Right click to remove") | ||
self.p_not_current = 0 | ||
# if view_all is True, show all already marked markers | ||
if self.view_all: | ||
for k in self.coords: | ||
for (x, y) in self.coords[k]: | ||
self.ax.plot(x, y, marker='x', c=self.colors[k]) | ||
if self.label not in self.coords or len(self.coords[self.label]) == 0: | ||
self.p_not_current += 1 | ||
else: | ||
for (x, y) in self.coords[self.label]: | ||
self.ax.plot(x, y, marker='x', c=self.color) |
Oops, something went wrong.