Hello World

For our first example, we look at how to create a Hello World example using the Holoscan SDK.

In this example we’ll cover:

  • how to define your application class

  • how to define a custom operator class

  • how to define a one-operator workflow

  • how to use a CountCondition to limit the number of times an operator is executed

Every Holoscan application requires defining an Application class. Creating a new Application starts with inheriting from the Application base class which in turn inherits from the Fragment class to provide the functionalities of a Fragment (i.e., creating a workflow and running it). When defining your application class, the primary task is to override the Application’s compose() method to define the operators used in your application and the interconnectivity between the operators to define the application workflow. For more details, see the section on Creating an Application.

The following code snippet shows an example Application code skeleton:

Copy
Copied!
            

#include class App : public holoscan::Application { public: void compose() override { using namespace holoscan; // Define Operators // ... // Define the workflow // ... } }; int main() { auto app = holoscan::make_application<App>(); app->run(); return 0; }

  • The holoscan::Application base class and other Holoscan classes and functions used in this example are defined in the headers included with the holoscan header file (line 1).

  • We define the App class that inherits from the holoscan::Application base class (line 3).

  • An instance of the application is created using the make_application() function (line 18).

  • The run() method (line 19) starts the application which executes it’s compose() method to define the application’s operators and workflow (line 5).

Copy
Copied!
            

from holoscan.conditions import CountCondition from holoscan.core import Application, Operator, OperatorSpec class App(Application): def compose(self): # Define Operators # ... # Define the workflow # ... if __name__ == "__main__": app = App() app.run()

  • The Holoscan classes needed for this example are imported on lines 1-2.

  • The App class is defined and inherits from the Application class (line 4).

  • An instance of the App class is created (line 14).

  • The app’s run() method (line 15) starts the application which executes it’s compose() method to define the application’s operators and workflow (line 6).

Operators are the unit of work in the Holoscan SDK Framework. An operator receives streaming data at an input port, processes it, and publishes it to one of its output ports. There are several categories of operators in the framework, but in this tutorial we will be referring to Native C++/Python operators unless mentioned otherwise. For more details on how operators work and the different categories of operators, see C++ Operators and Python Operators.

The Hello World example uses the simplest form of a workflow which consists of a single operator.

%%{init: {"theme": "base", "themeVariables": { "fontSize": "16px"}} }%% classDiagram direction LR class HelloWorldOp { }

Fig. 5 A one-operator workflow

The HelloWorldOp has neither input nor output ports. Whenever this operator’s compute() method is invoked by the Scheduler, it simply prints a Hello World message to standard output. We can add an operator to the workflow by calling add_operator() in the application’s compose() method.

The following code snippet shows how to define the custom HelloWordOp operator class.

Copy
Copied!
            

#include <iostream> #include <holoscan/holoscan.hpp> namespace holoscan::ops { class HelloWorldOp : public Operator { public: HOLOSCAN_OPERATOR_FORWARD_ARGS(HelloWorldOp) HelloWorldOp() = default; void setup(OperatorSpec& spec) override { } void compute(InputContext& op_input, OutputContext& op_output, ExecutionContext& context) override { std::cout << std::endl; std::cout << "Hello World!\n"; std::cout << std::endl; } }; } // namespace holoscan::ops

At this point, we will ignore some details of defining a custom operator and focus on the following:

  • We define the HelloWorldOp class (line 6) that inherits from the Operator base class.

  • Whenever this operator’s compute() method is invoked, it prints out hello world to standard output (line 15).

Copy
Copied!
            

from holoscan.conditions import CountCondition from holoscan.core import Application, Operator, OperatorSpec class HelloWorldOp(Operator): """Simple hello world operator. This operator has no ports. On each tick, this operator prints out hello world. """ def setup(self, spec: OperatorSpec): pass def compute(self, op_input, op_output, context): print("") print("Hello World!") print("")

  • We define the HelloWorldOp class that inherits from the Operator base class (line 4).

  • Whenever this operator’s compute() method is invoked, it prints out hello world to standard output (line 15).

Holoscan applications deal with streaming data, so an operator’s compute() method will be called continuously until some situation arises that causes the operator to stop. For our Hello World example, we want the operator’s compute() method to be called once, and we can impose such a condition by passing a CountConditionobject as an argument the the operator’s constructor.

Copy
Copied!
            

class HelloWorldApp : public holoscan::Application { public: void compose() override { using namespace holoscan; // Define the operators, allowing the hello operator to execute once auto hello = make_operator<ops::HelloWorldOp>("hello", make_condition<CountCondition>(1)); // Define the workflow by adding operator into the graph add_operator(hello); } }; int main(int argc, char** argv) { auto app = holoscan::make_application<HelloWorldApp>(); app->run(); return 0; }

  • We define the HelloWorldApp class that inherits from the holoscan::Application base class (line 26).

  • An instance of our operator is created with the name “hello” with the CountCondition argument which constrains the hello operator’s compute() method to execute only once (line 32).

  • This instance is added into the workflow by calling add_operator() (line 35).

Copy
Copied!
            

class HelloWorldApp(Application): def compose(self): # Define the operators hello = HelloWorldOp(self, CountCondition(self, 1), name="hello") # Define the one-operator workflow self.add_operator(hello) if __name__ == "__main__": app = HelloWorldApp() app.run()

  • We define the HelloWorldApp class that inherits from the Application base class (line 21).

  • An instance of our operator is created with the name “hello” with the CountCondition argument which constrains the hello operator’s compute() method to execute only once (line 24).

  • This instance is added into the workflow by calling add_operator() (line 27).

To run the application, please refer to the run instructions for your type of installation found on GitHub under the examples directory.

Running the application should give you the following output in your terminal:

Copy
Copied!
            

Hello World!

Congratulations! You have successfully run your first Holoscan SDK application!

© Copyright 2022-2023, NVIDIA. Last updated on Apr 27, 2023.