From 4a1e5fcbbeace020b80c545abb0b7c5b772943be Mon Sep 17 00:00:00 2001 From: Malia Gehan Date: Thu, 9 May 2024 13:10:13 -0500 Subject: [PATCH 1/7] Update napari_label_classes.py updating napari label classes to allow import of data, also updating to allow size parameter and to change color instead of shape. --- plantcv/annotate/napari_label_classes.py | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/plantcv/annotate/napari_label_classes.py b/plantcv/annotate/napari_label_classes.py index 918032d..16b8501 100755 --- a/plantcv/annotate/napari_label_classes.py +++ b/plantcv/annotate/napari_label_classes.py @@ -3,9 +3,11 @@ import numpy as np import random from plantcv.annotate import napari_open +from plantcv.annotate import napari_classes -def napari_label_classes(img, classes, show=True): +def napari_label_classes(img, classes=False, size=10, shape='square', + importdata=False, show=True): """ open img in napari and label classes @@ -19,6 +21,10 @@ def napari_label_classes(img, classes, show=True): is run. If all classes have points labeled, any clusters not labeled will default to the last class in the list when napari_join_labels is run. + size = size of marker in pixels + shape = either 'square' or 'circle' + importdata = dictionary of values in Napari format (y,x). + Output of napari_read_coor show = if show is True the viewer is launched. This opetion is useful for running tests without triggering the viewer. @@ -26,18 +32,40 @@ def napari_label_classes(img, classes, show=True): viewer = Napari viewer object :param img: numpy.ndarray + :param classes: list + :param size: int + :param shape: str + :param importdata: dict + :param show: str :return viewer: napari viewer object """ showcall = show viewer = napari_open(img, show=showcall) - symbols = ['arrow', 'clobber', 'cross', 'diamond', 'disc', 'hbar', 'ring', - 'square', 'star', 'tailed_arrow', 'triangle_down', - 'triangle_up', 'vbar'] + color = ['red', 'blue', 'purple', 'green', 'orange', 'yellow', 'cyan', + 'magenta'] - for x in classes: - viewer.add_points(np.array([]), name=x, symbol=random.choice(symbols), - face_color='white', size=10) + keys = napari_classes(viewer) + + if classes is not False: + for x in classes: + if x in keys: + pass + else: + viewer.add_points(np.array([]), name=x, symbol=shape, + face_color=random.choice(color), size=size) + keys = napari_classes(viewer) + + if importdata is not False: + importkeys = list(importdata.keys()) + for key in importkeys: + if len(importdata[key]) != 0: + if key in keys: + viewer.layers[key].add(importdata[key]) + else: + viewer.add_points(importdata[key], name=key, symbol=shape, + face_color=random.choice(color), + size=size) return viewer From 937150d1a03f043e4e0daeb02e149dff7d0a3724 Mon Sep 17 00:00:00 2001 From: Malia Gehan Date: Thu, 9 May 2024 13:10:13 -0500 Subject: [PATCH 2/7] Update napari_label_classes.py updating napari label classes to allow import of data, also updating to allow size parameter and to change color instead of shape. --- plantcv/annotate/napari_label_classes.py | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/plantcv/annotate/napari_label_classes.py b/plantcv/annotate/napari_label_classes.py index 918032d..16b8501 100755 --- a/plantcv/annotate/napari_label_classes.py +++ b/plantcv/annotate/napari_label_classes.py @@ -3,9 +3,11 @@ import numpy as np import random from plantcv.annotate import napari_open +from plantcv.annotate import napari_classes -def napari_label_classes(img, classes, show=True): +def napari_label_classes(img, classes=False, size=10, shape='square', + importdata=False, show=True): """ open img in napari and label classes @@ -19,6 +21,10 @@ def napari_label_classes(img, classes, show=True): is run. If all classes have points labeled, any clusters not labeled will default to the last class in the list when napari_join_labels is run. + size = size of marker in pixels + shape = either 'square' or 'circle' + importdata = dictionary of values in Napari format (y,x). + Output of napari_read_coor show = if show is True the viewer is launched. This opetion is useful for running tests without triggering the viewer. @@ -26,18 +32,40 @@ def napari_label_classes(img, classes, show=True): viewer = Napari viewer object :param img: numpy.ndarray + :param classes: list + :param size: int + :param shape: str + :param importdata: dict + :param show: str :return viewer: napari viewer object """ showcall = show viewer = napari_open(img, show=showcall) - symbols = ['arrow', 'clobber', 'cross', 'diamond', 'disc', 'hbar', 'ring', - 'square', 'star', 'tailed_arrow', 'triangle_down', - 'triangle_up', 'vbar'] + color = ['red', 'blue', 'purple', 'green', 'orange', 'yellow', 'cyan', + 'magenta'] - for x in classes: - viewer.add_points(np.array([]), name=x, symbol=random.choice(symbols), - face_color='white', size=10) + keys = napari_classes(viewer) + + if classes is not False: + for x in classes: + if x in keys: + pass + else: + viewer.add_points(np.array([]), name=x, symbol=shape, + face_color=random.choice(color), size=size) + keys = napari_classes(viewer) + + if importdata is not False: + importkeys = list(importdata.keys()) + for key in importkeys: + if len(importdata[key]) != 0: + if key in keys: + viewer.layers[key].add(importdata[key]) + else: + viewer.add_points(importdata[key], name=key, symbol=shape, + face_color=random.choice(color), + size=size) return viewer From 2dae81d5fa683a514e7a019f396cb16e4c9b9af6 Mon Sep 17 00:00:00 2001 From: Malia Gehan Date: Wed, 15 May 2024 12:34:09 -0500 Subject: [PATCH 3/7] viewer.close added viewer close --- tests/test_napari_classes.py | 1 + tests/test_napari_join_labels.py | 5 +++++ tests/test_napari_label_classes.py | 1 + tests/test_napari_open.py | 1 + tests/test_napari_save_coor.py | 1 + 5 files changed, 9 insertions(+) diff --git a/tests/test_napari_classes.py b/tests/test_napari_classes.py index f4050a9..e71826d 100644 --- a/tests/test_napari_classes.py +++ b/tests/test_napari_classes.py @@ -16,3 +16,4 @@ def test_napari_classes(make_napari_viewer): keys = napari_classes(viewer) assert keys == ['total', 'test'] + viewer.close() diff --git a/tests/test_napari_join_labels.py b/tests/test_napari_join_labels.py index 408f82e..f3ae04b 100644 --- a/tests/test_napari_join_labels.py +++ b/tests/test_napari_join_labels.py @@ -5,6 +5,7 @@ from plantcv.annotate.napari_join_labels import napari_join_labels from plantcv.plantcv import params + def test_napari_join_labels(test_data): """Test for PlantCV.Annotate""" # Read in test data @@ -13,6 +14,7 @@ def test_napari_join_labels(test_data): labeled, _ = napari_join_labels(img, viewer) assert np.shape(labeled) == (576, 537) + viewer.close() def test_napari_join_allclass(test_data): @@ -33,6 +35,7 @@ def test_napari_join_allclass(test_data): labeled, _ = napari_join_labels(img, viewer) assert np.shape(labeled) == (576, 537) + viewer.close() def test_napari_join_warn(test_data): @@ -53,6 +56,7 @@ def test_napari_join_warn(test_data): labeled, _ = napari_join_labels(img, viewer) assert np.shape(labeled) == (576, 537) + viewer.close() def test_napari_join_print(test_data, tmpdir): @@ -76,3 +80,4 @@ def test_napari_join_print(test_data, tmpdir): labeled, _ = napari_join_labels(img, viewer) assert np.shape(labeled) == (576, 537) + viewer.close() diff --git a/tests/test_napari_label_classes.py b/tests/test_napari_label_classes.py index ca49b8f..a2ba4f9 100644 --- a/tests/test_napari_label_classes.py +++ b/tests/test_napari_label_classes.py @@ -13,3 +13,4 @@ def test_napari_label_classes_gray(test_data): face_color="red", size=1) assert len(viewer.layers['background'].data) == 1 + viewer.close() diff --git a/tests/test_napari_open.py b/tests/test_napari_open.py index cfb9d25..0a445ee 100644 --- a/tests/test_napari_open.py +++ b/tests/test_napari_open.py @@ -38,3 +38,4 @@ def test_napari_open_envi(test_data): face_color="red", size=1) assert len(viewer.layers['total'].data) == 2 + viewer.close() diff --git a/tests/test_napari_save_coor.py b/tests/test_napari_save_coor.py index 3f3f9c0..fafc359 100644 --- a/tests/test_napari_save_coor.py +++ b/tests/test_napari_save_coor.py @@ -22,3 +22,4 @@ def test_napari_save_coor(test_data, tmpdir): _ = napari_save_coor(viewer, filename) assert os.path.exists(filename) + viewer.close() From be7d2f93c25cceca03926f5bed64a7cbf781419a Mon Sep 17 00:00:00 2001 From: Malia Gehan Date: Thu, 16 May 2024 12:11:40 -0500 Subject: [PATCH 4/7] more colors adding all of the colors --- plantcv/annotate/napari_label_classes.py | 40 +++++++++++++++++++++--- tests/test_napari_label_classes.py | 12 ++++--- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/plantcv/annotate/napari_label_classes.py b/plantcv/annotate/napari_label_classes.py index 16b8501..2303427 100755 --- a/plantcv/annotate/napari_label_classes.py +++ b/plantcv/annotate/napari_label_classes.py @@ -43,17 +43,46 @@ def napari_label_classes(img, classes=False, size=10, shape='square', showcall = show viewer = napari_open(img, show=showcall) - color = ['red', 'blue', 'purple', 'green', 'orange', 'yellow', 'cyan', - 'magenta'] + color = ["aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", + "beige", "bisque", "black", "blanchedalmond", "blue", + "blueviolet", "brown", "burlywood", "cadetblue", + "chartreuse", "chocolate", "coral", "cornflowerblue", + "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", + "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", + "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", + "darkred", "darksalmon", "darkseagreen", "darkslateblue", + "darkslategray", "darkturquoise", "darkviolet", "deeppink", + "deepskyblue", "dimgray", "dodgerblue", "firebrick", + "floralwhite", "forestgreen", "fuchsia", "gainsboro", + "ghostwhite", "gold", "goldenrod", "gray", "green", + "greenyellow", "honeydew", "hotpink", "indianred", + "indigo", "ivory", "khaki", "lavender", "lavenderblush", + "lawngreen", "lemonchiffon", "lightblue", "lightcoral", + "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", + "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", + "lightslategray", "lightsteelblue", "lightyellow", "lime", + "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", + "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", + "mediumslateblue", "mediumspringgreen", "mediumturquoise", + "mediumvioletred", "midnightblue", "mintcream", "mistyrose", + "moccasin", "navajowhite", "navy", "oldlace", "olive", + "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", + "palegreen", "paleturquoise", "palevioletred", "papayawhip", + "peachpuff", "peru", "pink", "plum", "powderblue", "purple", + "red", "rosybrown", "royalblue", "saddlebrown", + "salmon", "sandybrown", "seagreen", "seashell", "sienna", + "silver", "skyblue", "slateblue", "slategray", "snow", + "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", + "turquoise", "violet", "wheat", "white", "whitesmoke", + "yellow", "yellowgreen"] keys = napari_classes(viewer) if classes is not False: for x in classes: - if x in keys: - pass - else: + if x not in keys: viewer.add_points(np.array([]), name=x, symbol=shape, + edge_color=random.choice(color), face_color=random.choice(color), size=size) keys = napari_classes(viewer) @@ -65,6 +94,7 @@ def napari_label_classes(img, classes=False, size=10, shape='square', viewer.layers[key].add(importdata[key]) else: viewer.add_points(importdata[key], name=key, symbol=shape, + edge_color=random.choice(color), face_color=random.choice(color), size=size) diff --git a/tests/test_napari_label_classes.py b/tests/test_napari_label_classes.py index a2ba4f9..8732656 100644 --- a/tests/test_napari_label_classes.py +++ b/tests/test_napari_label_classes.py @@ -7,10 +7,12 @@ def test_napari_label_classes_gray(test_data): """Test for PlantCV.Annotate""" # Read in test data img, _, _ = readimage(test_data.kmeans_seed_gray_img) - viewer = napari_label_classes(img, ['seed'], show=False) - coor = [(25, 25)] - viewer.add_points(np.array(coor), symbol="o", name='background', - face_color="red", size=1) + data = {'total': [(25, 25)], 'background': [(50, 50)]} + viewer = napari_label_classes(img, ['total'], size=5, importdata=data, + show=False) + coor = [(50, 25)] + viewer.add_points(np.array(coor), symbol="o", name='coor', + face_color="red", size=5) - assert len(viewer.layers['background'].data) == 1 + assert len(viewer.layers['total'].data) == 1 viewer.close() From 225510158040a83a1ddf5f5ea810a3e11ae91215 Mon Sep 17 00:00:00 2001 From: Malia Gehan Date: Thu, 16 May 2024 13:40:35 -0500 Subject: [PATCH 5/7] Update napari_label_classes.md updating documentation for napari_label --- docs/napari_label_classes.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/napari_label_classes.md b/docs/napari_label_classes.md index b97735f..b612e8d 100644 --- a/docs/napari_label_classes.md +++ b/docs/napari_label_classes.md @@ -3,17 +3,17 @@ This function opens an image in Napari and then defines a set of classes to label. A random shape label is assigned to each class. Image can be annotate as long as viewer is open. -**plantcv.annotate.napari_label_classes*(*img, classes, show=True*) +**plantcv.annotate.napari_label_classes*(*img, classes, size, shape =10, 'square', importdata=False, show=True*) **returns** napari viewer object - **Parameters:** - img - image data (compatible with gray, RGB, and hyperspectral data. If data is hyperspecral it should be the array e.g. hyperspectral.array_data) - - classes - list of classes to label. If no points are selected for a class, - data without labels will default to this class when napari_join_labels - is run. If all classes have points labeled, any clusters not labeled - will default to the last class in the list if napari_join_labels is - run. + - classes - list of classes to label. This option is not necessary if data is data is imported. + - size - integer pixel size of label + - shape - can be 'o', 'arrow', 'clobber', 'cross', 'diamond', 'disc', 'hbar', 'ring', 'square', 'star', 'tailed_arrow', + 'triangle_down', 'triangle_up', 'vbar', 'x'. + - importdata - dictionary of data, data saved from napari_save_coor or data imported from napari_read_coor - show - if show = True, viewer is launched. False setting is useful for test purposes. - **Context:** @@ -31,7 +31,7 @@ import napari # Create an instance of the Points class img, path, name = pcv.readimage("./grayimg.png") -viewer = pcvan.napari_label_classes(img=img, classes=['background', 'wing','seed']) +viewer = pcvan.napari_label_classes(img=img, classes=['background', 'wing','seed'], size = 30) # Should open interactive napari viewer From 8dd136b5e05ae3355dc0e1aaefb08df2eccd6905 Mon Sep 17 00:00:00 2001 From: HaleySchuhl Date: Thu, 12 Sep 2024 15:14:41 -0500 Subject: [PATCH 6/7] Update napari_label_classes.md --- docs/napari_label_classes.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/napari_label_classes.md b/docs/napari_label_classes.md index db8ee17..6fab410 100644 --- a/docs/napari_label_classes.md +++ b/docs/napari_label_classes.md @@ -1,26 +1,26 @@ ## Label Image with Napari -This function opens an image in Napari and then defines a set of classes to label. A random shape label is assigned to each class. +This function opens an image in Napari and then defines a set of Points layers with the user-defined labels called `classes`. A random `shape` of the annotation symbol is assigned to each of the `classes`. Image can be annotated as long as viewer is open. -**plantcv.annotate.napari_label_classes*(*img, classes, size, shape =10, 'square', importdata=False, show=True*) +**plantcv.annotate.napari_label_classes*(*img, classes, size=10, shape='square', importdata=False, show=True*) **returns** napari viewer object - **Parameters:** - img - image data (compatible with gray, RGB, and hyperspectral data. If data is hyperspecral it should be the array e.g. hyperspectral.array_data) - classes - list of classes to label. This option is not necessary if data is data is imported. - - size - integer pixel size of label - - shape - can be 'o', 'arrow', 'clobber', 'cross', 'diamond', 'disc', 'hbar', 'ring', 'square', 'star', 'tailed_arrow', - 'triangle_down', 'triangle_up', 'vbar', 'x'. + - size - integer pixel size of label (also adjustable from the interactive Napari viewer) + - shape - shape of the annotation symbol. Can be 'o', 'arrow', 'clobber', 'cross', 'diamond', 'disc', 'hbar', 'ring', 'square' (default), 'star', 'tailed_arrow', + 'triangle_down', 'triangle_up', 'vbar', or 'x' (also adjustable from the interactive Napari viewer) - importdata - dictionary of data, data saved from napari_save_coor or data imported from napari_read_coor - - show - if show = True, viewer is launched. False setting is useful for test purposes. + - show - if `show=True`, viewer is launched. `False` setting is useful for test purposes. - **Context:** - - Adding class labels to images. Works best on an image that has objects segmented/classified with contours/clusters labeled with values (e.g. labeled mask, output of kmeans clustering). + - Adding one or more classes of points layer for annotation of the image. - **Example use:** - - Labeling output of kmeans clustering into classes. Labeling points. + - Ground truth counting, labeling classes of objects of interest. ```python @@ -31,9 +31,8 @@ import napari # Create an instance of the Points class img, path, name = pcv.readimage("./grayimg.png") -viewer = pcvan.napari_label_classes(img=img, classes=['background', 'wing','seed'], size = 30) - -# Should open interactive napari viewer +# Opens interactive napari viewer +viewer = pcvan.napari_label_classes(img=img, classes=['background', 'wing','seed'], size=30) ``` From 526fb46c9f320d62a499a5a6b95f6ed11eea71f7 Mon Sep 17 00:00:00 2001 From: HaleySchuhl Date: Thu, 12 Sep 2024 15:18:55 -0500 Subject: [PATCH 7/7] Update changelog.md --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7f8ac31..4d0ef44 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,7 +16,7 @@ All notable changes to this project will be documented below. #### annotate.napari_label_classes -* v0.1dev: viewer = **annotate.napari_label_classes**(*img, classes, show=True*) +* v0.1dev: viewer = **annotate.napari_label_classes**(*img, classes, size=10, shape='square', importdata=False, show=True*) #### annotate.napari_open