Skip to content

Commit 3409f65

Browse files
authored
Update colorize_labels: Add phase parameter + Zarr support (#218)
* Add phase offset * Add `phase` parameter * Update requirement versions * Update parameter names * Employ validators * Employ `<required_files>` * Fix linting * Add Zarr support
2 parents 15c1352 + bc9c094 commit 3409f65

11 files changed

Lines changed: 209 additions & 56 deletions

File tree

tools/colorize_labels/colorize_labels.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ def build_label_adjacency_graph(im, radius, bg_label):
4343
return G
4444

4545

46-
def get_n_unique_mpl_colors(n, colormap='jet', cyclic=False):
46+
def get_n_unique_mpl_colors(n: int, phase_offset: float, colormap: str = 'jet'):
4747
"""
4848
Yields `n` unique colors from the given `colormap`.
49-
50-
Set `cyclic` to `True` if the `colormap` is cyclic.
5149
"""
5250
cmap = plt.get_cmap(colormap)
53-
m = n if cyclic else n - 1
54-
for i in range(n):
55-
yield np.multiply(255, cmap(i / m))
51+
for t in np.linspace(0, 1, num=n, endpoint=False):
52+
f = (t + phase_offset) % 1
53+
yield np.multiply(255, cmap(f))
5654

5755

5856
if __name__ == '__main__':
@@ -62,33 +60,44 @@ def get_n_unique_mpl_colors(n, colormap='jet', cyclic=False):
6260
parser.add_argument('--bg_label', type=int)
6361
parser.add_argument('--bg_color', type=str)
6462
parser.add_argument('--radius', type=int)
63+
parser.add_argument('--phase', type=float)
6564
parser.add_argument('--output', type=str)
6665
args = parser.parse_args()
6766

67+
# Set random seed for reproducibility
68+
np.random.seed(0)
69+
6870
# Load image and normalize
69-
im = giatools.io.imread(args.input)
71+
im = np.asarray(giatools.Image.read(args.input).data)
7072
im = np.squeeze(im)
71-
assert im.ndim == 2
73+
if im.ndim == 2:
7274

73-
# Build adjacency graph of the labels
74-
G = build_label_adjacency_graph(im, args.radius, args.bg_label)
75+
# Build adjacency graph of the labels
76+
G = build_label_adjacency_graph(im, args.radius, args.bg_label)
7577

76-
# Apply greedy coloring
77-
graph_coloring = nx.greedy_color(G)
78-
unique_colors = frozenset(graph_coloring.values())
78+
# Apply greedy coloring
79+
graph_coloring = nx.greedy_color(G)
80+
unique_colors = frozenset(graph_coloring.values())
7981

80-
# Assign colors to nodes based on the greedy coloring
81-
graph_color_to_mpl_color = dict(zip(unique_colors, get_n_unique_mpl_colors(len(unique_colors))))
82-
node_colors = [graph_color_to_mpl_color[graph_coloring[n]] for n in G.nodes()]
82+
# Assign colors to nodes based on the greedy coloring
83+
colors = get_n_unique_mpl_colors(
84+
n=len(unique_colors),
85+
phase_offset=args.phase,
86+
)
87+
graph_color_to_mpl_color = dict(zip(unique_colors, colors))
88+
node_colors = [graph_color_to_mpl_color[graph_coloring[n]] for n in G.nodes()]
8389

84-
# Render result
85-
bg_color_rgb = color_hex_to_rgb_tuple(args.bg_color)
86-
result = np.dstack([np.full(im.shape, bg_color_rgb[ch], np.uint8) for ch in range(3)])
87-
for label, label_color in zip(G.nodes(), node_colors):
90+
# Render result
91+
bg_color_rgb = color_hex_to_rgb_tuple(args.bg_color)
92+
result = np.dstack([np.full(im.shape, bg_color_rgb[ch], np.uint8) for ch in range(3)])
93+
for label, label_color in zip(G.nodes(), node_colors):
8894

89-
cc = (im == label)
90-
for ch in range(3):
91-
result[:, :, ch][cc] = label_color[ch]
95+
cc = (im == label)
96+
for ch in range(3):
97+
result[:, :, ch][cc] = label_color[ch]
98+
99+
# Write result image
100+
skimage.io.imsave(args.output, result)
92101

93-
# Write result image
94-
skimage.io.imsave(args.output, result)
102+
else:
103+
exit('Input image has unsupported dimensions.')

tools/colorize_labels/colorize_labels.xml

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,72 +3,105 @@
33
<macros>
44
<import>creators.xml</import>
55
<import>tests.xml</import>
6-
<token name="@TOOL_VERSION@">3.2.1</token>
7-
<token name="@VERSION_SUFFIX@">3</token>
6+
<import>validators.xml</import>
7+
<token name="@TOOL_VERSION@">3.6.1</token>
8+
<token name="@VERSION_SUFFIX@">0</token>
89
</macros>
910
<creator>
10-
<expand macro="creators/bmcv" />
11+
<expand macro="creators/bmcv"/>
12+
<expand macro="creators/kostrykin"/>
1113
</creator>
1214
<edam_operations>
1315
<edam_operation>operation_3443</edam_operation>
1416
</edam_operations>
17+
<xrefs>
18+
<xref type="bio.tools">galaxy_image_analysis</xref>
19+
</xrefs>
1520
<requirements>
1621
<requirement type="package" version="@TOOL_VERSION@">networkx</requirement>
17-
<requirement type="package" version="1.22">numpy</requirement>
18-
<requirement type="package" version="0.18.1">scikit-image</requirement>
19-
<requirement type="package" version="0.1">giatools</requirement>
22+
<requirement type="package" version="2.3.5">numpy</requirement>
23+
<requirement type="package" version="0.25.2">scikit-image</requirement>
24+
<requirement type="package" version="0.7.3">giatools</requirement>
25+
<requirement type="package" version="3.10.8">matplotlib-base</requirement>
26+
<requirement type="package" version="0.12.2">ome-zarr</requirement>
2027
</requirements>
28+
<required_files>
29+
<include type="glob" path="*.py"/>
30+
</required_files>
2131
<command><![CDATA[
2232
23-
## Inputs
33+
python '$__tool_directory__/colorize_labels.py'
2434
25-
python '$__tool_directory__/colorize_labels.py' '$input'
26-
--radius $radius
27-
--bg_label $bg_label
28-
--bg_color '$bg_color'
35+
#if $input.extension == "zarr"
36+
'$input.extra_files_path/$input.metadata.store_root'
37+
#else
38+
'$input'
39+
#end if
2940
30-
## Outputs
31-
32-
--output output.png
41+
--radius $radius
42+
--bg_label $bg_label
43+
--bg_color '$bg_color'
44+
--phase $phase
45+
--output output.png
3346
3447
]]>
3548
</command>
3649
<inputs>
37-
<param name="input" type="data" format="tiff,png" label="Input image (label map)" />
38-
<param argument="--radius" type="integer" min="1" value="10" label="Radius of the neighborhood" help="Defines the neighborhood (in pixels) where labels are considered to be adjacent." />
39-
<param argument="--bg_label" type="integer" value="0" label="Background label" />
40-
<param argument="--bg_color" type="color" value="#000000" label="Background color"/>
50+
<param name="input" type="data" format="tiff,png,zarr" label="Input image (label map)">
51+
<expand macro="validators/is_single_channel"/>
52+
<expand macro="validators/is_single_frame"/>
53+
<expand macro="validators/is_2d"/>
54+
</param>
55+
<param name="radius" type="integer" min="1" value="10" label="Radius of the neighborhood" help="Defines the neighborhood (in pixels) where labels are considered to be adjacent." />
56+
<param name="bg_label" type="integer" value="0" label="Background label" />
57+
<param name="bg_color" type="color" value="#000000" label="Background color"/>
58+
<param name="phase" type="float" min="0" max="1" value="0.55" label="Label phase"
59+
help="Phase for sampling a color beam. Governs the overall teint of the colorization. Lower values tend to produce bluish colors, while higher values tend to favor reddish colors."/>
4160
</inputs>
4261
<outputs>
4362
<data format="png" name="output" from_work_dir="output.png" />
4463
</outputs>
4564
<tests>
4665
<!-- int64 -->
4766
<test>
48-
<param name="input" value="input1.tiff" />
49-
<param name="radius" value="1" />
50-
<param name="bg_label" value="0" />
51-
<param name="bg_color" value="#5a5a5a" />
67+
<param name="input" value="input1.tiff"/>
68+
<param name="radius" value="1"/>
69+
<param name="bg_label" value="0"/>
70+
<param name="bg_color" value="#5a5a5a"/>
71+
<param name="phase" value="0" />
5272
<expand macro="tests/intensity_image_diff" name="output" value="output1.png" ftype="png">
5373
<has_image_channels channels="3"/>
5474
</expand>
5575
</test>
56-
<!-- uint8 -->
76+
<!-- uint8, background label does not exist in image -->
5777
<test>
58-
<param name="input" value="input2.tiff" />
59-
<param name="radius" value="10" />
60-
<param name="bg_label" value="0" />
61-
<param name="bg_color" value="#ffffff" />
78+
<param name="input" value="input2.tiff"/>
79+
<param name="radius" value="10"/>
80+
<param name="bg_label" value="0"/>
81+
<param name="bg_color" value="#ffffff"/>
82+
<param name="phase" value="0.55"/>
6283
<expand macro="tests/intensity_image_diff" name="output" value="output2.png" ftype="png">
6384
<has_image_channels channels="3"/>
6485
</expand>
6586
</test>
87+
<!-- uint8, Zarr -->
88+
<test>
89+
<param name="input" value="input13.zarr"/>
90+
<param name="radius" value="100"/>
91+
<param name="bg_label" value="0"/>
92+
<param name="bg_color" value="#ffffff"/>
93+
<param name="phase" value="1.0"/>
94+
<expand macro="tests/intensity_image_diff" name="output" value="output13.png" ftype="png">
95+
<has_image_channels channels="3"/>
96+
</expand>
97+
</test>
6698
<!-- uint16 -->
6799
<test>
68-
<param name="input" value="input3.tiff" />
69-
<param name="radius" value="100" />
70-
<param name="bg_label" value="0" />
71-
<param name="bg_color" value="#ffffff" />
100+
<param name="input" value="input3.tiff"/>
101+
<param name="radius" value="100"/>
102+
<param name="bg_label" value="0"/>
103+
<param name="bg_color" value="#ffffff"/>
104+
<param name="phase" value="0.55"/>
72105
<expand macro="tests/intensity_image_diff" name="output" value="output3.png" ftype="png">
73106
<has_image_channels channels="3"/>
74107
</expand>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Overview of the test images
2+
3+
## `input1.tiff`:
4+
5+
- axes: `YX`
6+
- resolution: `(5, 5)`
7+
- dtype: `int64`
8+
- labels: `0...3`
9+
10+
## `input2.tiff`:
11+
12+
- axes: `YX`
13+
- resolution: `(50, 50)`
14+
- dtype: `uint8`
15+
- labels: `1...25`
16+
17+
## `input3.tiff`:
18+
19+
- axes: `YX`
20+
- resolution: `(462, 580)`
21+
- dtype: `uint16` little endian
22+
- labels: `0...218`
23+
24+
## `input13.zarr`
25+
26+
- axes: `YX`
27+
- resolution: `(265, 329)`
28+
- dtype: `uint8`
29+
- labels: `0...2`
230 Bytes
Binary file not shown.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"shape": [
3+
265,
4+
329
5+
],
6+
"data_type": "uint8",
7+
"chunk_grid": {
8+
"name": "regular",
9+
"configuration": {
10+
"chunk_shape": [
11+
265,
12+
329
13+
]
14+
}
15+
},
16+
"chunk_key_encoding": {
17+
"name": "default",
18+
"configuration": {
19+
"separator": "/"
20+
}
21+
},
22+
"fill_value": 0,
23+
"codecs": [
24+
{
25+
"name": "bytes"
26+
},
27+
{
28+
"name": "zstd",
29+
"configuration": {
30+
"level": 0,
31+
"checksum": false
32+
}
33+
}
34+
],
35+
"attributes": {},
36+
"dimension_names": [
37+
"Y",
38+
"X"
39+
],
40+
"zarr_format": 3,
41+
"node_type": "array",
42+
"storage_transformers": []
43+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"attributes": {
3+
"ome": {
4+
"version": "0.5",
5+
"multiscales": [
6+
{
7+
"datasets": [
8+
{
9+
"path": "0",
10+
"coordinateTransformations": [
11+
{
12+
"type": "scale",
13+
"scale": [
14+
1.0,
15+
1.0
16+
]
17+
}
18+
]
19+
}
20+
],
21+
"name": "/",
22+
"axes": [
23+
{
24+
"name": "Y",
25+
"type": "space"
26+
},
27+
{
28+
"name": "X",
29+
"type": "space"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
},
36+
"zarr_format": 3,
37+
"node_type": "group"
38+
}
-1 Bytes
Loading
891 Bytes
Loading
-7 Bytes
Loading
44 Bytes
Loading

0 commit comments

Comments
 (0)