Skip to content

Commit d8c89b5

Browse files
committed
docs(HelloPipeline): Initial addition
1 parent c324065 commit d8c89b5

12 files changed

Lines changed: 365 additions & 4 deletions

File tree

.github/workflows/examples.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,35 @@ jobs:
8484
browser: chrome
8585
start: npm start
8686

87+
build-hello-pipeline-example:
88+
name: Hello Pipeline Build
89+
runs-on: ubuntu-20.04
90+
91+
defaults:
92+
run:
93+
working-directory: ./examples/HelloPipeline
94+
95+
steps:
96+
- uses: actions/checkout@v3
97+
98+
- uses: actions/setup-node@v2
99+
with:
100+
node-version: '16'
101+
102+
- name: Install
103+
run: |
104+
npm install
105+
106+
- name: Build
107+
run: |
108+
npm run build
109+
110+
- name: Test
111+
run: |
112+
npm run test
113+
npm run test:quiet
114+
npm run test:help
115+
87116
test-umd-example:
88117
name: UMD
89118
runs-on: ubuntu-20.04
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
title: Hello Pipeline World!
2+
---
3+
4+
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.
5+
6+
First, let's create a new directory to house our project.
7+
8+
```sh
9+
mkdir HelloPipeline
10+
cd HelloPipeline
11+
```
12+
13+
Let's write some code! Populate *HelloPipeline.cxx* first with the headers we need:
14+
15+
```c++
16+
#include "itkPipeline.h"
17+
#include "itkInputImage.h"
18+
#include "itkImage.h"
19+
```
20+
21+
The *itkImage.h* header is [ITK](https://itk.org)'s standard n-dimensional image data structure.
22+
23+
The *itkPipeline.h* and *itkInputImage.h* headers come from the itk-wasm *WebAssemblyInterface* [ITK module](https://www.kitware.com/advance-itk-with-modules/).
24+
25+
Next, create a standard `main` C command line interface function and an `itk::wasm::Pipeline`:
26+
27+
28+
```c++
29+
int main(int argc, char * argv[]) {
30+
// Create the pipeline for parsing arguments. Provide a description.
31+
itk::wasm::Pipeline pipeline("A hello world itk::wasm::Pipeline", argc, argv);
32+
33+
return EXIT_SUCCESS;
34+
}
35+
```
36+
37+
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:
38+
39+
- Support for execution in WASM modules along with command line execution
40+
- Support for spatial data structures such as `Image`'s, `Mesh`'s, `PolyData`, and `Transform`'s
41+
- Support for multiple dimensions and pixel types
42+
- Colored help output
43+
44+
Add a standard CLI11 flag to the pipeline:
45+
46+
```c++
47+
itk::wasm::Pipeline pipeline("A hello world itk::wasm::Pipeline", argc, argv);
48+
49+
50+
bool quiet = false;
51+
pipeline.add_flag("-q,--quiet", quiet, "Do not print image information");
52+
}
53+
```
54+
55+
Add an input image argument to the pipeline:
56+
57+
```c++
58+
pipeline.add_flag("-q,--quiet", quiet, "Do not print image information");
59+
60+
61+
constexpr unsigned int Dimension = 2;
62+
using PixelType = unsigned char;
63+
using ImageType = itk::Image<PixelType, Dimension>;
64+
65+
// Add a input image argument.
66+
using InputImageType = itk::wasm::InputImage<ImageType>;
67+
InputImageType inputImage;
68+
pipeline.add_option("InputImage", inputImage, "The input image")->required();
69+
```
70+
71+
The `inputImage` variable is populated from the filesystem if built as a native executable. When running in the browser or in a wrapped language, `inputImage` is read from WebAssembly memory without file IO.
72+
73+
Parse the command line arguments with the `ITK_WASM_PARSE` macro:
74+
75+
```c++
76+
pipeline.add_option("InputImage", inputImage, "The input image")->required();
77+
78+
79+
ITK_WASM_PARSE(pipeline);
80+
```
81+
82+
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.
83+
84+
Finally, run our pipeline:
85+
```c++
86+
std::cout << "Hello pipeline world!\n" << std::endl;
87+
88+
if (!quiet)
89+
{
90+
// Obtain the itk::Image * from the itk::wasm::InputImage with `.Get()`.
91+
std::cout << "Input image: " << *inputImage.Get() << std::endl;
92+
}
93+
94+
return EXIT_SUCCESS;
95+
```
96+
97+
Next, provide a [CMake](https://cmake.org/) build configuration at *CMakeLists.txt*:
98+
99+
```cmake
100+
cmake_minimum_required(VERSION 3.16)
101+
project(HelloPipeline)
102+
103+
# Use C++17 or newer with itk-wasm
104+
set(CMAKE_CXX_STANDARD 17)
105+
106+
# We always want to build against the WebAssemblyInterface module.
107+
set(itk_components
108+
WebAssemblyInterface
109+
)
110+
# WASI or native binaries
111+
if (NOT EMSCRIPTEN)
112+
# WebAssemblyInterface supports the .iwi, .iwi.cbor itk-wasm format.
113+
# We can list other ITK IO modules to build against to support other
114+
# formats when building native executable or WASI WebAssembly.
115+
# However, this will bloat the size of the WASI WebAssembly binary, so
116+
# add them judiciously.
117+
set(itk_components
118+
WebAssemblyInterface
119+
ITKIOPNG
120+
# ITKImageIO # Adds support for all available image IO modules
121+
)
122+
endif()
123+
find_package(ITK REQUIRED
124+
COMPONENTS ${itk_components}
125+
)
126+
include(${ITK_USE_FILE})
127+
128+
add_executable(HelloPipeline HelloPipeline.cxx)
129+
target_link_libraries(HelloPipeline PUBLIC ${ITK_LIBRARIES})
130+
```
131+
132+
[Build the WASI binary](../hello_world.html):
133+
134+
```sh
135+
npx itk-wasm -i itkwasm/wasi build
136+
```
137+
138+
Check the generated help output:
139+
140+
```sh
141+
npx itk-wasm run HelloPipeline.wasi.wasm -- -- --help
142+
```
143+
144+
![Hello pipeline help](./hello_pipeline.png)
145+
146+
The two `--`'s are to separate arguments for the WASM module from arguments to the `itk-wasm` CLI and the WebAssembly interpreter.
147+
148+
Try running on an [example image](https://bafybeihibtxtdmwuekb64wnv3ras54lz4ojuqv4gabmigpfdha4dsmcr5y.ipfs.w3s.link/ipfs/bafybeihibtxtdmwuekb64wnv3ras54lz4ojuqv4gabmigpfdha4dsmcr5y/cthead1.png).
149+
150+
```
151+
> npx itk-wasm run HelloPipeline.wasi.wasm -- -- cthead1.png
152+
153+
Hello pipeline world!
154+
155+
Input image: Image (0x2b910)
156+
RTTI typeinfo: itk::Image<unsigned char, 2u>
157+
Reference Count: 1
158+
Modified Time: 54
159+
Debug: Off
160+
Object Name:
161+
Observers:
162+
none
163+
Source: (none)
164+
Source output name: (none)
165+
Release Data: Off
166+
Data Released: False
167+
Global Release Data: Off
168+
PipelineMTime: 22
169+
UpdateMTime: 53
170+
RealTimeStamp: 0 seconds
171+
LargestPossibleRegion:
172+
Dimension: 2
173+
Index: [0, 0]
174+
Size: [256, 256]
175+
BufferedRegion:
176+
Dimension: 2
177+
Index: [0, 0]
178+
Size: [256, 256]
179+
RequestedRegion:
180+
Dimension: 2
181+
Index: [0, 0]
182+
Size: [256, 256]
183+
Spacing: [1, 1]
184+
Origin: [0, 0]
185+
Direction:
186+
1 0
187+
0 1
188+
189+
IndexToPointMatrix:
190+
1 0
191+
0 1
192+
193+
PointToIndexMatrix:
194+
1 0
195+
0 1
196+
197+
Inverse Direction:
198+
1 0
199+
0 1
200+
201+
PixelContainer:
202+
ImportImageContainer (0x2ba60)
203+
RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char>
204+
Reference Count: 1
205+
Modified Time: 50
206+
Debug: Off
207+
Object Name:
208+
Observers:
209+
none
210+
Pointer: 0x2c070
211+
Container manages memory: true
212+
Size: 65536
213+
Capacity: 65536
214+
```
215+
216+
And with the `--quiet` flag:
217+
218+
```
219+
> npx itk-wasm run HelloPipeline.wasi.wasm -- -- --quiet cthead1.png
220+
221+
Hello pipeline world!
222+
```
223+
224+
Congratulations! You just executed a C++ pipeline capable of processsing a scientific image in WebAssembly. 🎉
27.6 KB
Loading

doc/content/examples/hello_world.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
title: Hello WASM World!
22
---
33

4-
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!
4+
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!
55

66
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/).
77

88

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

1111
```sh
12-
mkdir itk-wasm-hello-world
13-
cd itk-wasm-hello-world
12+
mkdir HelloWorld
13+
cd HelloWorld
1414
```
1515

1616
Let's write some code! Populate *hello.cxx* with our Hello World program:

doc/tpl/__en__

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ sidebar:
4444
recipes: Recipes
4545
examples: Examples
4646
hello_world: Hello World!
47+
hello_pipeline: Hello Pipeline!
4748
vue: Vue.js
4849

4950
api:

doc/tpl/__sidebar__

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ docs:
1111
examples:
1212
getting_started:
1313
hello_world: hello_world.html
14+
hello_pipeline: hello_pipeline.html
1415
debugging: debugging.html
1516
recipes:
1617
vue: paraview_glance.html

examples/HelloPipeline/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
itk-wasm-hello-world
2+
wasi-build
3+
cypress/support
4+
cypress/videos/
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
project(HelloPipeline)
3+
4+
# Use C++17 or newer with itk-wasm
5+
set(CMAKE_CXX_STANDARD 17)
6+
7+
# We always want to build against the WebAssemblyInterface module.
8+
set(itk_components
9+
WebAssemblyInterface
10+
)
11+
# WASI or native binaries
12+
if (NOT EMSCRIPTEN)
13+
# WebAssemblyInterface supports the .iwi, .iwi.cbor itk-wasm format.
14+
# We can list other ITK IO modules to build against to support other
15+
# formats when building native executable or WASI WebAssembly.
16+
# However, this will bloat the size of the WASI WebAssembly binary, so
17+
# add them judiciously.
18+
set(itk_components
19+
WebAssemblyInterface
20+
ITKIOPNG
21+
# ITKImageIO # Adds support for all available image IO modules
22+
)
23+
endif()
24+
find_package(ITK REQUIRED
25+
COMPONENTS ${itk_components}
26+
)
27+
include(${ITK_USE_FILE})
28+
29+
add_executable(HelloPipeline HelloPipeline.cxx)
30+
target_link_libraries(HelloPipeline PUBLIC ${ITK_LIBRARIES})
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
#include "itkPipeline.h"
19+
#include "itkInputImage.h"
20+
#include "itkImage.h"
21+
22+
int main(int argc, char * argv[]) {
23+
// Create the pipeline for parsing arguments. Provide a description.
24+
itk::wasm::Pipeline pipeline("A hello world itk::wasm::Pipeline", argc, argv);
25+
26+
// Add a flag to the pipeline.
27+
bool quiet = false;
28+
pipeline.add_flag("-q,--quiet", quiet, "Do not print image information");
29+
30+
constexpr unsigned int Dimension = 2;
31+
using PixelType = unsigned char;
32+
using ImageType = itk::Image<PixelType, Dimension>;
33+
34+
// Add a input image argument.
35+
using InputImageType = itk::wasm::InputImage<ImageType>;
36+
InputImageType inputImage;
37+
pipeline.add_option("InputImage", inputImage, "The input image")->required();
38+
39+
// Parse the arguments. If `-q` or `--quiet` is set, the `quiet` variable will be set to `true`.
40+
// The `inputImage` variable is populated from the filesystem if built as a native executable.
41+
// When running in the browser or in a wrapped language, `inputImage` is read from WebAssembly memory without file IO.
42+
ITK_WASM_PARSE(pipeline);
43+
44+
std::cout << "Hello pipeline world!\n" << std::endl;
45+
46+
if (!quiet)
47+
{
48+
// Obtain the itk::Image * from the itk::wasm::InputImage with `.Get()`.
49+
std::cout << "Input image: " << *inputImage.Get() << std::endl;
50+
}
51+
52+
return EXIT_SUCCESS;
53+
}

examples/HelloPipeline/cthead1.png

193 KB
Loading

0 commit comments

Comments
 (0)