comparison render.py @ 0:7d6be2b7e1e2 draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/libcarna/ commit 20db59ffe2a97f25d82ba02e451bf73f93ef84ee
author imgteam
date Mon, 05 Jan 2026 20:10:28 +0000
parents
children 31a2e1909ae5
comparison
equal deleted inserted replaced
-1:000000000000 0:7d6be2b7e1e2
1 import giatools
2 import libcarna
3 import libcarna._imshow
4 import pandas as pd
5
6 # Fail early if an optional backend is not available
7 giatools.require_backend('omezarr')
8
9 # Patch `libcarna._imshow` to return plain HTML
10 libcarna._imshow.IPythonHTML = lambda html: html
11
12
13 GEOMETRY_TYPE_INTENSITIES = 0
14 GEOMETRY_TYPE_MASK = 1
15
16
17 def wrap_color(params: dict) -> dict:
18 """
19 Return the `params` dictionary but wrap values for `color` with `libcarna.color`.
20 """
21 result = dict()
22 for key, value in params.items():
23 if key == 'color':
24 value = libcarna.color(value)
25 result[key] = value
26 return result
27
28
29 if __name__ == "__main__":
30 tool = giatools.ToolBaseplate()
31 tool.add_input_image('intensities')
32 tool.add_input_image('mask', required=False)
33 tool.parser.add_argument('--colormap', type=str)
34 tool.parser.add_argument('--html', type=str)
35 tool.parse_args()
36
37 # Load custom colormap
38 if tool.args.raw_args.colormap:
39 df_colormap = pd.read_csv(tool.args.raw_args.colormap, delimiter='\t')
40
41 # Validate the input image(s)
42 try:
43 for image in tool.args.input_images.values():
44 if any(image.shape[image.axes.index(axis)] > 1 for axis in image.axes if axis not in 'ZYX'):
45 raise ValueError(f'This tool is not applicable to images with {image.original_axes} axes.')
46
47 # Create and configure frame renderer
48 print('Sample rate:', tool.args.params['sample_rate'])
49 mode = getattr(libcarna, tool.args.params['mode'])(
50 GEOMETRY_TYPE_INTENSITIES,
51 sr=tool.args.params['sample_rate'],
52 **tool.args.params['mode_kwargs']
53 )
54 mask_renderer = libcarna.mask_renderer(
55 GEOMETRY_TYPE_MASK,
56 sr=tool.args.params['sample_rate'],
57 **wrap_color(tool.args.params['mask_renderer_kwargs']),
58 )
59 r = libcarna.renderer(
60 tool.args.params['width'],
61 tool.args.params['height'],
62 [mode, mask_renderer],
63 )
64 print('EGL Vendor:', r.gl_context.vendor)
65
66 # Build the scene graph
67 root = libcarna.node()
68 intensities = tool.args.input_images['intensities']
69 intensities_volume = libcarna.volume(
70 GEOMETRY_TYPE_INTENSITIES,
71 intensities.normalize_axes_like(tool.args.params['axes']).data,
72 parent=root,
73 spacing=[
74 {
75 'X': intensities.metadata.pixel_size[0] or 1,
76 'Y': intensities.metadata.pixel_size[1] or 1,
77 'Z': intensities.metadata.z_spacing or 1,
78 }
79 [axis] for axis in tool.args.params['axes']
80 ],
81 normals=(tool.args.params['mode'] == 'dvr'),
82 )
83 camera = libcarna.camera(
84 parent=root,
85 ).frustum(
86 **tool.args.params['camera']['kwargs'],
87 ).translate(
88 z=tool.args.params['camera']['distance'],
89 )
90 if (mask := tool.args.input_images.get('mask')):
91 libcarna.volume(
92 GEOMETRY_TYPE_MASK,
93 mask.normalize_axes_like(tool.args.params['axes']).data,
94 parent=intensities_volume,
95 spacing=intensities_volume.spacing,
96 )
97
98 # Apply colormap
99 if tool.args.params['colormap'] == 'custom':
100 mode.cmap.clear()
101 i0, color0 = None, None
102 for row in df_colormap.to_dict(orient='records'):
103 match row['type']:
104 case 'relative':
105 i1 = row['intensity']
106 case 'absolute':
107 i1 = intensities_volume.normalized(row['intensity'])
108 case _:
109 raise ValueError('Unknown intensity type: "{}"'.format(row['type']))
110 color1 = libcarna.color(row['color'])
111 if i0 is not None:
112 mode.cmap.linear_segment(i0, i1, color0, color1)
113 i0, color0 = i1, color1
114 else:
115 cmap_kwargs = dict()
116 if (ramp_params := tool.args.params['ramp']):
117 ramp_values = list()
118 for val_type, value in (
119 (ramp_params['start_type'], ramp_params['start_value']),
120 (ramp_params['end_type'], ramp_params['end_value']),
121 ):
122 ramp_values.append(
123 value if val_type == 'relative' else intensities_volume.normalized(value),
124 )
125 cmap_kwargs['ramp'] = tuple(ramp_values)
126 mode.cmap(tool.args.params['colormap'], **cmap_kwargs)
127
128 # Render
129 html = libcarna.imshow(
130 libcarna.animate(
131 libcarna.animate.rotate_local(camera),
132 n_frames=tool.args.params['video']['frames'],
133 ).render(r, camera),
134 mode.cmap.bar(intensities_volume),
135 fps=tool.args.params['video']['fps'],
136 )
137
138 # Write the result
139 with open(tool.args.raw_args.html, 'w') as fhtml:
140 fhtml.write(html)
141
142 except ValueError as err:
143 exit(err.args[0])