Skip to content

Commit 13da0e6

Browse files
Added plugin to create numbered label images
1 parent f32584f commit 13da0e6

8 files changed

+184
-12
lines changed

docs/img/label-image-numbered.png

261 KB
Loading
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Numbered label image
2+
3+
Segmentation results in a filled mask for each segmented object (cell or nucleus). Each mask has a unique integer value (0 is reserved for background). This value can be used to uniquely identify each object. An inspection of the resulting label image is possible in any image processing tool, such as FIJI. However, for convience, it might also be useful to have an overview image showing all labels, such as the one shown below
4+
5+
After segmentation, each object (cell or nucleus) has a unique id.
6+
![label-image-numbered.png](img/label-image-numbered.png)
7+
8+
This workflow permits to create such an image from any label image created by the segmentation workflow.
9+
10+
You can install the plugin from <a href="https://imjoy.io/#/app?w=fq-segmentation&plugin=fish-quant/fq-segmentation:CreateNumberedLabels@stable&upgrade=1" target="_blank">**here.**</a>
11+
12+
## Recommended workflow
13+
14+
The default settings of the plugins allow to quickly perform the recommended workflow. You only have
15+
to paste the path to folder containing the segmentation results (usually a folder called `segmentation-results`). The plugin will then loop over all files in this folder, and select the onces fitting the other two parameters:
16+
17+
- `File string`: specify a string that the file-name has to contain. The default `mask__` fits for both segmentation of cells and nuclei.
18+
- `Img extension: file-extension of the label images. The default is `.png`, which is the extension used by our segmentation pipeline.
19+
20+
The plugin will then create a new subfolder called `labels_numbered` where the numbered label images
21+
will be stored under their original file-name with an added suffix `__numbered.png`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<docs lang="markdown">
2+
ImJoy plugin to create numbered label images.
3+
</docs>
4+
5+
<config lang="json">
6+
{
7+
"name": "CreateNumberedLabels",
8+
"type": "native-python",
9+
"version": "0.0.1",
10+
"description": "Created numbered label images.",
11+
"tags": ["stable","dev"],
12+
"ui": [
13+
"<span style='font-weight: 550; color:#666362'>Path LABELS</span>: {id: 'path_labels', type: 'string', placeholder: 'paste-path-to-labels'}",
14+
"<span style='font-weight: 550; color:#666362'>File string</span>: {id: 'file_ident', type: 'string', placeholder: 'mask__'}",
15+
"<span style='font-weight: 550; color:#666362'>Img extension</span>: {id: 'img_ext', type: 'string', placeholder: '.png'}"
16+
],
17+
"cover": "",
18+
"inputs": null,
19+
"outputs": null,
20+
"flags": [],
21+
"icon": "extension",
22+
"api_version": "0.1.7",
23+
"env": "",
24+
"permissions": [],
25+
"requirements": {
26+
"dev": [""],
27+
"stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"]
28+
},
29+
"dependencies": []
30+
}
31+
</config>
32+
33+
<script lang="python">
34+
from imjoy import api
35+
import numpy as np
36+
import matplotlib.pyplot as plt
37+
from skimage.io import imread
38+
from skimage.measure import regionprops
39+
import segwrap
40+
41+
from pathlib import Path
42+
43+
class ImJoyPlugin():
44+
45+
async def setup(self):
46+
api.log('>>> Plugin CreateNumberedLabels initialized')
47+
api.log(f" * plugin version: {await api.getConfig('_version')}")
48+
api.log(f' * segwrap version: {segwrap.__version__}')
49+
50+
def run(self, ctx):
51+
52+
api.log('>>> Plugin CreateNumberedLabels running. Called with parameters:')
53+
api.log(ctx.config)
54+
55+
# >> Prepare INPUT PARAMETERS
56+
file_ident = ctx.config.file_ident
57+
img_ext = ctx.config.img_ext
58+
59+
# Path containing data, and to save results
60+
path_labels = ctx.config.path_labels
61+
62+
# Get path for testing
63+
if api.TAG == 'dev':
64+
if path_labels == 'paste-path-to-labels':
65+
path_labels = r'D:\Documents\data-test\fish-quant\fq-imjoy-dev\analysis\segmentation-results'
66+
67+
# Path containing the data
68+
if path_labels == 'paste-path-to-labels':
69+
api.alert('Path containing labels has to be specified.')
70+
return
71+
72+
path_labels = Path(path_labels)
73+
if not path_labels.is_dir():
74+
api.alert('Path containing labels does not exist.')
75+
return
76+
77+
# Path to save data
78+
path_save = path_labels / 'labels_numbered'
79+
if not path_save.is_dir():
80+
path_save.mkdir(parents=True)
81+
82+
# >> Create random lookup table for Matplotlib
83+
# From: https://gist.github.com/jgomezdans/402500
84+
vals = np.linspace(0, 1, 256)
85+
np.random.shuffle(vals)
86+
test = plt.cm.jet(vals)
87+
test[0 ,:] = [0, 0, 0, 0] # Set to zero
88+
cmap_random = plt.cm.colors.ListedColormap(test)
89+
90+
# >> Loop over files and create label images
91+
api.showStatus('Create numbered label images')
92+
93+
for f_labels in path_labels.glob(f'*{file_ident}*{img_ext}'):
94+
api.log(f'Processing label image {f_labels}')
95+
96+
# >> Read label image and analyse objects
97+
img_labels = imread(str(f_labels))
98+
props = regionprops(img_labels)
99+
100+
# >> Create plots
101+
fig, ax = plt.subplots(1, 1, facecolor='white')
102+
fig.set_size_inches((6, 6))
103+
ax.imshow(img_labels, cmap=cmap_random)
104+
ax.get_xaxis().set_visible(False)
105+
ax.get_yaxis().set_visible(False)
106+
107+
for prop in props:
108+
ax.text(prop.centroid[1],
109+
prop.centroid[0],
110+
f'{prop.label}',
111+
fontsize=5, weight='bold',
112+
verticalalignment='center', horizontalalignment='center')
113+
114+
name_save = path_save / f'{f_labels.stem}__numbered.png'
115+
plt.savefig(name_save, dpi=300)
116+
117+
plt.close()
118+
119+
api.showStatus('Preprocessing finished.')
120+
121+
api.export(ImJoyPlugin())
122+
</script>

manifest.imjoy.json

+33
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,39 @@
44
"uri_root": "",
55
"version": "0.2.0",
66
"plugins": [
7+
{
8+
"name": "CreateNumberedLabels",
9+
"type": "native-python",
10+
"version": "0.0.1",
11+
"description": "Created numbered label images.",
12+
"tags": [
13+
"stable",
14+
"dev"
15+
],
16+
"ui": [
17+
"<span style='font-weight: 550; color:#666362'>Path LABELS</span>: {id: 'path_labels', type: 'string', placeholder: 'paste-path-to-labels'}",
18+
"<span style='font-weight: 550; color:#666362'>File string</span>: {id: 'file_ident', type: 'string', placeholder: 'mask__'}",
19+
"<span style='font-weight: 550; color:#666362'>Img extension</span>: {id: 'img_ext', type: 'string', placeholder: '.png'}"
20+
],
21+
"cover": "",
22+
"inputs": null,
23+
"outputs": null,
24+
"flags": [],
25+
"icon": "extension",
26+
"api_version": "0.1.7",
27+
"env": "",
28+
"permissions": [],
29+
"requirements": {
30+
"dev": [
31+
""
32+
],
33+
"stable": [
34+
"pip: -U git+https://github.com/fish-quant/fq-segmentation@master"
35+
]
36+
},
37+
"dependencies": [],
38+
"uri": "imjoy-plugins\\CreateNumberedLabels.imjoy.html"
39+
},
740
{
841
"name": "ObjectDist",
942
"type": "native-python",

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
cellpose == 0.6.5
22
matplotlib == 3.4.2
3+
scikit-image == 0.19.2

segwrap/utils_cellpose.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -382,12 +382,12 @@ def segment_cells_nuclei_indiv(path_scan, str_channels, img_ext, new_size, model
382382
if img_cyto.ndim != 2:
383383
log_message(f'\nERROR\n Input image of cell has to be 2D. Current image is {img_cyto.ndim}D', callback_fun=callback_log)
384384
continue
385-
385+
386386
img_nuclei = io.imread(str(path_nuclei))
387387
if img_nuclei.ndim != 2:
388388
log_message(f'\nERROR\n Input image of cell has to be 2D. Current image is {img_nuclei.ndim}D', callback_fun=callback_log)
389389
continue
390-
390+
391391
# Resize image before CellPose if specified
392392
sizes_orginal.append(img_cyto.shape)
393393

@@ -400,11 +400,11 @@ def segment_cells_nuclei_indiv(path_scan, str_channels, img_ext, new_size, model
400400
img_size = img_cyto.shape
401401
new_size = tuple(int(ti/scale_factor) for ti in img_size)
402402

403-
# IMPORTANT: CV2 resize is defined as (width, height)
403+
# IMPORTANT: CV2 resize is defined as (width, height)
404404
dsize = (new_size[1], new_size[0])
405405
img_cyto = cv2.resize(img_cyto, dsize)
406406
img_nuclei = cv2.resize(img_nuclei, dsize)
407-
407+
408408
img_zeros = np.zeros(img_cyto.shape)
409409

410410
# For cell segmentation

segwrap/utils_general.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ def create_output_path(path_orig, str_replace, subfolder=None, create_path=True,
5252
str_rep = re.search(r'^(.*)>>(.*)$', str_replace).group(2)
5353

5454
path_replace = Path(str(path_orig).replace(str_orig, str_rep))
55-
55+
5656
if subfolder:
5757
path_replace = path_replace / subfolder
58-
58+
5959
log_message(f'Replacement parameters found: original string: {str_orig}, replacement string: {str_rep}', callback_fun=callback_log)
6060
log_message(f'Output path: {path_replace}', callback_fun=callback_log)
6161

@@ -68,4 +68,4 @@ def create_output_path(path_orig, str_replace, subfolder=None, create_path=True,
6868
if not path_replace.is_dir():
6969
path_replace.mkdir(parents=True)
7070

71-
return path_replace
71+
return path_replace

segwrap/utils_masks.py

-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# Imports
22
from cellpose.io import imread, imsave
3-
from skimage.measure import regionprops
43
import numpy as np
54
from tqdm import tqdm
65
from scipy import ndimage
7-
from pathlib import Path
86
import pathlib
97

108
from segwrap.utils_general import log_message, create_output_path
@@ -47,7 +45,6 @@ def create_img_closest_obj(path_scan, str_label, strs_save, path_save=None, sear
4745
path_save_results = path_save
4846
if not path_save_results.is_dir():
4947
path_save_results.mkdir(parents=True)
50-
path_save_settings = path_save_results
5148

5249
else:
5350
path_save_str_replace = path_save
@@ -62,7 +59,6 @@ def create_img_closest_obj(path_scan, str_label, strs_save, path_save=None, sear
6259
files_proc.append(path_mask)
6360

6461
# Process files
65-
n_files = len(files_proc)
6662
for idx, file_label in enumerate(files_proc):
6763

6864
log_message(f'\n>>> Processing file:\n{file_label}', callback_fun=callback_status)
@@ -106,7 +102,6 @@ def create_img_closest_obj(path_scan, str_label, strs_save, path_save=None, sear
106102
for indx, obj_int in enumerate(np.nditer(labels)):
107103
ind_obj_closest[dist_obj_ind_2D == indx] = obj_int
108104

109-
110105
# Save index of closest object
111106
name_save_ind = path_save_results / f'{file_label.stem.replace(str_label, strs_save[0])}.png'
112107
if str(name_save_ind) != str(file_label):

0 commit comments

Comments
 (0)