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:
#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 theholoscan::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’scompose()
method to define the application’s operators and workflow (line 5).
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’scompose()
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.
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.
#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 theOperator
base class.Whenever this operator’s
compute()
method is invoked, it prints out hello world to standard output (line 15).
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 theOperator
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 CountCondition
object as an argument the the operator’s constructor.
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 theholoscan::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’scompute()
method to execute only once (line 32).This instance is added into the workflow by calling
add_operator()
(line 35).
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 theApplication
base class (line 21).An instance of our operator is created with the name “hello” with the
CountCondition
argument which constrains the hello operator’scompute()
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:
Hello World!
Congratulations! You have successfully run your first Holoscan SDK application!