Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc(HelloPipeline): Initial addition #615

Merged
merged 2 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,62 @@ jobs:
browser: chrome
start: npm start

build-test-hello-pipeline-example:
name: Hello Pipeline Build Test
runs-on: ubuntu-20.04

defaults:
run:
working-directory: ./examples/HelloPipeline

steps:
- uses: actions/checkout@v3

- uses: actions/setup-node@v2
with:
node-version: '16'

- name: Install
run: |
npm install

- name: Build
run: |
npm run build

- name: Test
run: |
npm run test
npm run test:quiet
npm run test:help

build-test-inputs-outputs-example:
name: Inputs Outputs Build test
runs-on: ubuntu-20.04

defaults:
run:
working-directory: ./examples/InputsOutputs

steps:
- uses: actions/checkout@v3

- uses: actions/setup-node@v2
with:
node-version: '16'

- name: Install
run: |
npm install

- name: Build
run: |
npm run build

- name: Test
run: |
npm run test

test-umd-example:
name: UMD
runs-on: ubuntu-20.04
Expand Down
Binary file added doc/content/examples/cthead1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
233 changes: 233 additions & 0 deletions doc/content/examples/hello_pipeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
title: Hello Pipeline World!
---

## Introduction

This example introduces the `itk::wasm::Pipeline`. An `itk::wasm::Pipeline` transforms elegant standalone C++ command line programs into powerful [WebAssembly](https://webassembly.org/) (WASM) modules with a simple, efficient interface for execution in the browser, other programming languages, and on the command line.

Make sure to complete the [Hello World!](./hello_world.html) example before you start your Hello Pipeline adventure.

## Write the code

First, let's create a new directory to house our project.

```sh
mkdir HelloPipeline
cd HelloPipeline
```

Let's write some code! Populate *HelloPipeline.cxx* first with the headers we need:

```c++
#include "itkPipeline.h"
#include "itkInputImage.h"
#include "itkImage.h"
```

The *itkImage.h* header is [ITK](https://itk.org)'s standard n-dimensional image data structure.

The *itkPipeline.h* and *itkInputImage.h* headers come from the itk-wasm *WebAssemblyInterface* [ITK module](https://www.kitware.com/advance-itk-with-modules/).

Next, create a standard `main` C command line interface function and an `itk::wasm::Pipeline`:


```c++
int main(int argc, char * argv[]) {
// Create the pipeline for parsing arguments. Provide a description.
itk::wasm::Pipeline pipeline("A hello world itk::wasm::Pipeline", argc, argv);

return EXIT_SUCCESS;
}
```

The `itk::wasm::Pipeline` extends the most-excellent [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`'s, `Mesh`'s, `PolyData`, and `Transform`'s
- Support for multiple dimensions and pixel types
- Colored help output

Add a standard CLI11 flag to the pipeline:

```c++
itk::wasm::Pipeline 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:

```c++
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("InputImage", inputImage, "The input image")->required();
```

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:

```c++
pipeline.add_option("InputImage", inputImage, "The input image")->required();


ITK_WASM_PARSE(pipeline);
```

This parses the command line arguments. 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 our pipeline:
```c++
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 at *CMakeLists.txt*:

```cmake
cmake_minimum_required(VERSION 3.16)
project(HelloPipeline)

# 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(HelloPipeline HelloPipeline.cxx)
target_link_libraries(HelloPipeline PUBLIC ${ITK_LIBRARIES})
```

## Create WebAssembly binary

[Build the WASI binary](../hello_world.html):

```sh
npx itk-wasm -i itkwasm/wasi build
```

## Run WebAssembly binary

Check the generated help output:

```sh
npx itk-wasm run HelloPipeline.wasi.wasm -- -- --help
```

![Hello pipeline help](./hello_pipeline.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 run HelloPipeline.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 run HelloPipeline.wasi.wasm -- -- --quiet cthead1.png

Hello pipeline world!
```

Congratulations! You just executed a C++ pipeline capable of processsing a scientific image in WebAssembly. 🎉
Binary file added doc/content/examples/hello_pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 10 additions & 3 deletions doc/content/examples/hello_world.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
title: Hello WASM World!
---

This example, walks through how to compile a *hello world* executable written in C++ to [WebAssembly](https://webassembly.org/) and how execute it with standalone WebAssembly runtimes, the Node.js JavaScript runtime, and web browser runtimes!
## Introduction

This example walks through how to compile a *hello world* executable written in C++ to [WebAssembly](https://webassembly.org/) and how to execute it with standalone WebAssembly runtimes, the Node.js JavaScript runtime, and web browser runtimes!

Before getting started, make sure [Node.js](https://nodejs.org/en/download/) and [Docker](https://docs.docker.com/install/) are installed. On Linux, make sure you can run [`docker` without `sudo`](https://askubuntu.com/questions/477551/how-can-i-use-docker-without-sudo). On Windows, we recommend [WSL 2 with Docker enabled](https://docs.docker.com/desktop/windows/wsl/).

While we recommend following along step-by-step, the complete examples can also be found in the [`examples/` directory of the project repository](https://github.com/InsightSoftwareConsortium/itk-wasm/tree/master/examples).

## Write the code

First, let's create a new directory to house our project.

```sh
mkdir itk-wasm-hello-world
cd itk-wasm-hello-world
mkdir HelloWorld
cd HelloWorld
```

Let's write some code! Populate *hello.cxx* with our Hello World program:
Expand All @@ -33,6 +38,8 @@ project(HelloWorld)
add_executable(hello hello.cxx)
```

## Install itk-wasm

We use the `add_executable` command to build executables with itk-wasm. The [Emscripten](https://kripken.github.io/emscripten-site/) and [WASI](https://github.com/WebAssembly/wasi-sdk) toolchains along with itk-wasm build and execution configurations are contained in the itk-wasm [dockcross](https://github.com/dockcross/dockcross) Docker images used by the itk-wasm command line interface (CLI). Note that the same code can also be built and tested with native operating system toolchains. This is useful for development and debugging.

Build the program with the itk-wasm CLI, `itk-wasm`. This is shipped with the `itk-wasm` Node.js package. First install *itk-wasm* with the Node Package Manager, `npm`, the CLI that ships with Node.js.
Expand Down
Loading