# Create Elegant C++ Spatial Processing Pipelines in WebAssembly ###### tags: `post`, `itk-wasm`, `webassembly`, `cli11` ###### By: Matt McCormick [![ORCID](https://i0.wp.com/info.orcid.org/wp-content/uploads/2020/12/orcid_16x16.gif?resize=16%2C16&ssl=1)](https://orcid.org/0000-0001-9475-3756), Mary Elise Dedicke [![ORCID](https://i0.wp.com/info.orcid.org/wp-content/uploads/2020/12/orcid_16x16.gif?resize=16%2C16&ssl=1)](https://orcid.org/0000-0001-8848-3235), Henry Schreiner [![ORCID](https://i0.wp.com/info.orcid.org/wp-content/uploads/2020/12/orcid_16x16.gif?resize=16%2C16&ssl=1)](https://orcid.org/0000-0002-7833-783X) [WebAssembly](https://webassembly.org/)'s origins date back to [Alon Zakai](https://emscripten.org)'s incredible effort to build C++ to JavaScript. In 2015, we demonstrated the power of this technology to make scientific computational sustainable and accessible. [Try it](https://insightsoftwareconsortium.github.io/ITKAnisotropicDiffusionLBR/) -- reproducibility is still possible all these years later, with no installation (or maintenance!) required. [![](https://i.imgur.com/MwZSvfy.png)](https://insightsoftwareconsortium.github.io/ITKAnisotropicDiffusionLBR/) *An interactive, accessible and sustainable open science publication on anisotropic diffusion where C++ is built into JavaScript for browser execution.* Since that time, Emscripten's capabilities have advanced and been standardized with WebAssembly (Wasm) in the [Web Platform](https://en.wikipedia.org/wiki/Web_platform). Moreover, Wasm's scope has expanded dramatically with the advent of the [WebAssembly System Interface, WASI](https://github.com/WebAssembly/WASI), and [The Component Model](https://github.com/WebAssembly/component-model). However, there was a **significant gap** in capabilites for research software developers who aimed to create **data and computationally intense scientific processing pipelines** for applications like spatial analysis and visualization. Namely, - **An elegant, simple way to write processing pipelines in C++.** - Handling of non-trivial spatial data structures (Wasm natively only supports integers and floats 😮). - Easy-to-use, reproducible tools to build Wasm modules. - Safe and efficient memory handling. - Parallelism, whether multi-module, multi-threading, or SIMD. - Provide bindings for the command line and functional interfaces for languages like JavaScript, Typescript, Python, Rust, C#, R, and Java. - Debugging support. In this post, adapted from [itk-wasm](https://wasm.itk.org)'s documentation, we provide a C++ Wasm processing pipeline tutorial that demonstrates how we can **write elegant processing pipelines in C++ via itk-wasm's [CLI11](https://github.com/CLIUtils/CLI11) command line parser, which provides a rich feature set with a simple and intuitive interface**. At the end of this tutorial, you will have built and executed C++ code to Wasm for standalone execution on the command line and in the browser. [*itk-wasm*](https://wasm.itk.org) combines [the Insight Toolkit (ITK)](https://www.itk.org/) and WebAssembly to enable *high-performance spatial analysis* in a web browser, Node.js, and reproducible execution across programming languages and hardware architectures. [CLI11](https://cliutils.github.io/CLI11/book/) provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. itk-wasm enhances CLI11 with a `itk::wasm::Pipeline` wrapper to support efficient execution in multiple Wasm contexts, scientific data structures, and lovely colorized help output 🥰. Let's get started! 🚀 ## 0. Preliminaries Before starting this tutorial, check out our [Hello Wasm World](https://www.kitware.com/hello-wasm-world/) tutorial. ## 1. Write the code First, let's create a new directory to house our project. ```sh mkdir hello-pipeline cd hello-pipeline ``` Let's write some code! Populate *hello-pipeline.cxx* first with the headers we need: ```cpp #include "itkPipeline.h" #include "itkInputImage.h" #include "itkImage.h" ``` The *itkPipeline.h* and *itkInputImage.h* headers come from the itk-wasm *WebAssemblyInterface* [ITK module](https://www.kitware.com/advance-itk-with-modules/). The *itkImage.h* header is [ITK](https://itk.org)'s standard n-dimensional image data structure. Next, create a standard `main` C command line interface function and an `itk::wasm::Pipeline`: ```cpp int main(int argc, char * argv[]) { // Create the pipeline for parsing arguments. Provide a description. itk::wasm::Pipeline pipeline("hello-pipeline", "A hello world itk::wasm::Pipeline", argc, argv); return EXIT_SUCCESS; } ``` The `itk::wasm::Pipeline` extends the [CLI11 modern C++ command line parser](https://github.com/CLIUtils/CLI11). In addition to all of CLI11's functionality, `itk::wasm::Pipeline`'s adds: - Support for execution in Wasm modules along with command line execution - Support for spatial data structures such as `Image`, `Mesh`, `PolyData`, and `Transform` - Support for multiple dimensions and pixel types - Colored help output Add a standard CLI11 flag to the pipeline: ```cpp itk::wasm::Pipeline pipeline("hello-pipeline", "A hello world itk::wasm::Pipeline", argc, argv); bool quiet = false; pipeline.add_flag("-q,--quiet", quiet, "Do not print image information"); ``` Add an input image argument to the pipeline: ```cpp pipeline.add_flag("-q,--quiet", quiet, "Do not print image information"); constexpr unsigned int Dimension = 2; using PixelType = unsigned char; using ImageType = itk::Image<PixelType, Dimension>; // Add a input image argument. using InputImageType = itk::wasm::InputImage<ImageType>; InputImageType inputImage; pipeline.add_option("input-image", inputImage, "The input image")->required()->type_name("INPUT_IMAGE"); ``` The `inputImage` variable is populated from the filesystem if built as a native executable or a WASI binary run from the command line. When running in the browser or in a wrapped language, `inputImage` is read from WebAssembly memory without file IO. Parse the command line arguments with the `ITK_WASM_PARSE` macro: ```cpp pipeline.add_option("InputImage", inputImage, "The input image")->required()->type_name("INPUT_IMAGE"); ITK_WASM_PARSE(pipeline); ``` If `-q` or `--quiet` is set, the `quiet` variable will be set to `true`. Missing or invalid arguments will print an error and exit. The `-h` and `--help` flags are automatically generated from pipeline arguments to print usage information. Finally, run the pipeline: ```cpp std::cout << "Hello pipeline world!\n" << std::endl; if (!quiet) { // Obtain the itk::Image * from the itk::wasm::InputImage with `.Get()`. std::cout << "Input image: " << *inputImage.Get() << std::endl; } return EXIT_SUCCESS; ``` Next, provide a [CMake](https://cmake.org/) build configuration in *CMakeLists.txt*: ```cmake cmake_minimum_required(VERSION 3.16) project(hello-pipeline) # Use C++17 or newer with itk-wasm set(CMAKE_CXX_STANDARD 17) # We always want to build against the WebAssemblyInterface module. set(itk_components WebAssemblyInterface ) # WASI or native binaries if (NOT EMSCRIPTEN) # WebAssemblyInterface supports the .iwi, .iwi.cbor itk-wasm format. # We can list other ITK IO modules to build against to support other # formats when building native executable or WASI WebAssembly. # However, this will bloat the size of the WASI WebAssembly binary, so # add them judiciously. set(itk_components WebAssemblyInterface ITKIOPNG # ITKImageIO # Adds support for all available image IO modules ) endif() find_package(ITK REQUIRED COMPONENTS ${itk_components} ) include(${ITK_USE_FILE}) add_executable(hello-pipeline hello-pipeline.cxx) target_link_libraries(hello-pipeline PUBLIC ${ITK_LIBRARIES}) ``` ## 2. Create and Run WebAssembly binary [Build the WASI binary](https://www.kitware.com/hello-wasm-world/): ```sh npx itk-wasm@1.0.0-b.70 -i itkwasm/wasi build ``` And check the generated help output: ```sh npx itk-wasm@1.0.0-b.70 run hello-pipeline.wasi.wasm -- -- --help ``` ![Hello pipeline help](https://i.imgur.com/0MSHMgf.png) The two `--`'s are to separate arguments for the Wasm module from arguments to the `itk-wasm` CLI and the WebAssembly interpreter. Try running on an [example image](https://data.kitware.com/api/v1/file/63041ac8f64de9b9501e5a22/download). ``` > npx itk-wasm@1.0.0-b.65 run hello-pipeline.wasi.wasm -- -- cthead1.png Hello pipeline world! Input image: Image (0x2b910) RTTI typeinfo: itk::Image<unsigned char, 2u> Reference Count: 1 Modified Time: 54 Debug: Off Object Name: Observers: none Source: (none) Source output name: (none) Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 22 UpdateMTime: 53 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 2 Index: [0, 0] Size: [256, 256] BufferedRegion: Dimension: 2 Index: [0, 0] Size: [256, 256] RequestedRegion: Dimension: 2 Index: [0, 0] Size: [256, 256] Spacing: [1, 1] Origin: [0, 0] Direction: 1 0 0 1 IndexToPointMatrix: 1 0 0 1 PointToIndexMatrix: 1 0 0 1 Inverse Direction: 1 0 0 1 PixelContainer: ImportImageContainer (0x2ba60) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 50 Debug: Off Object Name: Observers: none Pointer: 0x2c070 Container manages memory: true Size: 65536 Capacity: 65536 ``` And with the `--quiet` flag: ``` > npx itk-wasm@1.0.0-b.65 run hello-pipeline.wasi.wasm -- -- --quiet cthead1.png Hello pipeline world! ``` Congratulations! You just executed a C++ pipeline capable of processsing a scientific image in WebAssembly. 🎉 ## What's next We created a processing pipeline that works with real data -- exciting! When creating these pipelines, however, we will sometimes need a more detailed understanding of their execution. In our next post, we will address this need by describing how to debug itk-wasm WASI modules.