Mercurial > repos > imgteam > libcarna_render
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/render.py Mon Jan 05 20:10:28 2026 +0000 @@ -0,0 +1,143 @@ +import giatools +import libcarna +import libcarna._imshow +import pandas as pd + +# Fail early if an optional backend is not available +giatools.require_backend('omezarr') + +# Patch `libcarna._imshow` to return plain HTML +libcarna._imshow.IPythonHTML = lambda html: html + + +GEOMETRY_TYPE_INTENSITIES = 0 +GEOMETRY_TYPE_MASK = 1 + + +def wrap_color(params: dict) -> dict: + """ + Return the `params` dictionary but wrap values for `color` with `libcarna.color`. + """ + result = dict() + for key, value in params.items(): + if key == 'color': + value = libcarna.color(value) + result[key] = value + return result + + +if __name__ == "__main__": + tool = giatools.ToolBaseplate() + tool.add_input_image('intensities') + tool.add_input_image('mask', required=False) + tool.parser.add_argument('--colormap', type=str) + tool.parser.add_argument('--html', type=str) + tool.parse_args() + + # Load custom colormap + if tool.args.raw_args.colormap: + df_colormap = pd.read_csv(tool.args.raw_args.colormap, delimiter='\t') + + # Validate the input image(s) + try: + for image in tool.args.input_images.values(): + if any(image.shape[image.axes.index(axis)] > 1 for axis in image.axes if axis not in 'ZYX'): + raise ValueError(f'This tool is not applicable to images with {image.original_axes} axes.') + + # Create and configure frame renderer + print('Sample rate:', tool.args.params['sample_rate']) + mode = getattr(libcarna, tool.args.params['mode'])( + GEOMETRY_TYPE_INTENSITIES, + sr=tool.args.params['sample_rate'], + **tool.args.params['mode_kwargs'] + ) + mask_renderer = libcarna.mask_renderer( + GEOMETRY_TYPE_MASK, + sr=tool.args.params['sample_rate'], + **wrap_color(tool.args.params['mask_renderer_kwargs']), + ) + r = libcarna.renderer( + tool.args.params['width'], + tool.args.params['height'], + [mode, mask_renderer], + ) + print('EGL Vendor:', r.gl_context.vendor) + + # Build the scene graph + root = libcarna.node() + intensities = tool.args.input_images['intensities'] + intensities_volume = libcarna.volume( + GEOMETRY_TYPE_INTENSITIES, + intensities.normalize_axes_like(tool.args.params['axes']).data, + parent=root, + spacing=[ + { + 'X': intensities.metadata.pixel_size[0] or 1, + 'Y': intensities.metadata.pixel_size[1] or 1, + 'Z': intensities.metadata.z_spacing or 1, + } + [axis] for axis in tool.args.params['axes'] + ], + normals=(tool.args.params['mode'] == 'dvr'), + ) + camera = libcarna.camera( + parent=root, + ).frustum( + **tool.args.params['camera']['kwargs'], + ).translate( + z=tool.args.params['camera']['distance'], + ) + if (mask := tool.args.input_images.get('mask')): + libcarna.volume( + GEOMETRY_TYPE_MASK, + mask.normalize_axes_like(tool.args.params['axes']).data, + parent=intensities_volume, + spacing=intensities_volume.spacing, + ) + + # Apply colormap + if tool.args.params['colormap'] == 'custom': + mode.cmap.clear() + i0, color0 = None, None + for row in df_colormap.to_dict(orient='records'): + match row['type']: + case 'relative': + i1 = row['intensity'] + case 'absolute': + i1 = intensities_volume.normalized(row['intensity']) + case _: + raise ValueError('Unknown intensity type: "{}"'.format(row['type'])) + color1 = libcarna.color(row['color']) + if i0 is not None: + mode.cmap.linear_segment(i0, i1, color0, color1) + i0, color0 = i1, color1 + else: + cmap_kwargs = dict() + if (ramp_params := tool.args.params['ramp']): + ramp_values = list() + for val_type, value in ( + (ramp_params['start_type'], ramp_params['start_value']), + (ramp_params['end_type'], ramp_params['end_value']), + ): + ramp_values.append( + value if val_type == 'relative' else intensities_volume.normalized(value), + ) + cmap_kwargs['ramp'] = tuple(ramp_values) + mode.cmap(tool.args.params['colormap'], **cmap_kwargs) + + # Render + html = libcarna.imshow( + libcarna.animate( + libcarna.animate.rotate_local(camera), + n_frames=tool.args.params['video']['frames'], + ).render(r, camera), + mode.cmap.bar(intensities_volume), + fps=tool.args.params['video']['fps'], + ) + + # Write the result + with open(tool.args.raw_args.html, 'w') as fhtml: + fhtml.write(html) + + except ValueError as err: + exit(err.args[0])
