Writing custom code for CEDAR
This page describes how to develop a new processing step for CEDAR.
There are two ways to accomplishing this.
You can either integrate your step directly into your local CEDAR version or write a CEDAR plugin, containing that step, which you can later include inside the CEDAR GUI.
In either case, you will have to write custom code for the processing step. We have created a tutorial that creates an exemplary processing step, which sums two inputs.
Tutorial: Create a step integrated in CEDAR
This tutorial briefly explains how to create a new processing step that is directly integrated in CEDAR and adheres to the principles of its processing framework.
Download CEDAR
Open a terminal and navigate to a directory where you want CEDAR to be located, for example
cd /home/username/src
Download CEDAR from the github repository; it will be stored in a folder called cedar
.
git clone https://github.com/cedar/cedar
For the rest of the tutorial, we will assume that CEDAR is located in the directory $CEDARPATH
. Either replace that variable in the remaining commands or set the variable like this:
CEDARPATH=/home/username/src/cedar
Create source files
$CEDARPATH/tools
.cd $CEDARPATH/tools
./create_cedar_class.py cedar::proc::steps::TestStep
TestStep.cpp
, TestStep.h
, and TestStep.fwd.h
.They will automatically be put in the right place within the CEDAR source file tree under $CEDARPATH/cedar/processing/steps/
.
Write code for your step
The script only generated empty templates that you will have to fill with code.
(See tutorial at the bottom: Writing the source code)
Prepare building CEDAR
Once you would like to compile your code, navigate to the folder $CEDARPATH/cedar/processing
.
cd $CEDARPATH/cedar/processing
CMakeLists.txt
in a text editor. Its contents are structured somewhat like this:cedar_add_library(cedarproc
LINK_LIBRARIES
...
MOC_HEADERS
TestStep.h
to the list of header files by adding the following line:steps/TestStep.h
Building CEDAR
See building CEDAR tutorial for instructions on how to build CEDAR.
Run CEDAR
You can now run CEDAR to test your step. You should find your processing step in the "Elements" tab under the category that you chose for it.
Tutorial: Create a plugin for a step in CEDAR
This tutorial requires a compiled version of CEDAR.
Download the plugins repository
cd /home/username/src
Download the CEDAR plugins from the github repository; they will be stored in a folder called plugins
.
git clone https://github.com/cedar/plugins
For the rest of the tutorial, we will assume that CEDAR is located in the directory $CEDARPATH
. Either replace that variable in the remaining commands or set the variable like this:
CEDARPATH=/home/username/src/cedar
We will also assume that the CEDAR plugins are located in the directory $PLUGINSPATH
. Either replace that variable in the remaining commands or set the variable like this:
PLUGINSPATH=/home/username/src/plugins
Create source files
cd $CEDARPATH/tools
./create_cedar_class.py cedar::proc::steps::TestStep
TestStep.cpp
, TestStep.h
, and TestStep.fwd.h
.The files are created inside the CEDAR source file tree by default, under $CEDARPATH/cedar/processing/steps
. Since the files have to be part of the plugin, move them to $PLUGINSPATH/steps/CATEGORY
, where CATEGORY is a folder with the name of a category that best describes your step (here we will use the category "programming").
mv $CEDARPATH/cedar/processing/steps/TestStep.* $PLUGINSPATH/steps/programming
Write code for your step
Prepare building CEDAR
Create a file called TestStep.cmake
in the same folder where the source code of our step is. ($PLUGINSPATH/steps/programming
)
The content of the file should look like the following:
DECLARE_STEP(cedar::proc::steps::TestStep
MOC
CATEGORY "Programming"
DESCRIPTION "DESCRIPTION THAT DESCRIBES YOUR STEP SHORTLY"
MAINTAINER "YOUR NAME"
)
$PLUGINSPATH
directory.cd $PLUGINSPATH
project.conf.example
to create a new file called project.conf
.cp project.conf.example project.conf
Open the file in a text editor. Change the following line
set(CEDAR_HOME "~/workspace/src/cedar.release")
to set the path in quotation marks to the path of your CEDAR installation ($CEDARPATH
).
Creating a buildset
To create a plugin, you have to write a buildset file that determines which classes are included in the plugin. For now, we will only include the step that we have written.
In the $PLUGINSPATH
directory, create a file called buildset.TestPlugin.cmake
, where "TestPlugin" is the name you choose to give your plugin. The content of the file should look like this:
DECLARE_PLUGIN(TestPlugin)
ADD_TO_PLUGIN(STEPS cedar::proc::steps::TestStep)
Building the plugin
Create a directory called build
in the $PLUGINSPATH
directory and navigate into that directory.
cd $PLUGINSPATH
mkdir build
cd build
Use cmake and the buildset file as a parameter to create a build environment.
cmake -D BUILDSET=buildset.TestPlugin.cmake ..
You can now compile your plugin.
make all
The compilation will create a *.so file in the directory $PLUGINSPATH/build
, which we can include as a plugin in CEDAR.
Integrating your plugin in CEDAR
Open the graphical user interface of CEDAR. Click on "Tools" in the main menu, then click on "Manage plugins ...". In the dialog that opens, click "add", navigate to the $PLUGINSPATH/build
directory and select the file libTestPlugin.so
. Click the "ok" button to load the plugin into the framework.
You should find your processing step in the "Elements" tab under the category that you chose for it (for example "Programming").
Tutorial: Writing source code for a processing step
To demonstrate a basic example for the source code of a step, we will write a step which will add up two matrices, received as an input.
The header file
TestStep.h
). For the step to work correctly, you have to include the following files:configuration.h
, Step.h
, and the forward header of your step, TestStep.fwd.h
.
We will also include MatData.h
because we want to use matrix calculation functions in this example.
The forward header of the first tutorial (Tutorial: Create a step integrated in CEDAR) is not located at the same place as the one of the second tutorial (Tutorial: Create a plugin for a step in CEDAR). If you come from the first tutorial, and your step is located in the cedar source code, your includes have to look like this:
// CEDAR CONFIGURATION
#include "cedar/configuration.h"
// CEDAR INCLUDES
#include <cedar/processing/Step.h>
#include <cedar/auxiliaries/MatData.h>
// FORWARD DECLARATIONS
#include "cedar/processing/steps/TestStep.fwd.h"
If you come from the second tutorial, and your files are located somewhere in the plugins root folder, you have to change the path in the last include to the path of your *.fwd.h file. If you followed the folder structure of the above tutorial, your includes will loke like this:
// CEDAR CONFIGURATION
#include "cedar/configuration.h"
// CEDAR INCLUDES
#include <cedar/processing/Step.h>
#include <cedar/auxiliaries/MatData.h>
// FORWARD DECLARATIONS
#include "steps/programming/TestStep.fwd.h"
In the header file a class called TestStep is defined. As it will turn out as a step, the class has to extend cedar::proc::Step
.
class cedar::proc::steps::TestStep : public cedar::proc::Step
{
...
}
In the class you have to add a macro called Q_OBJECT
, which you will need, as this step is represented graphically as a Qt-object.
//--------------------------------------------------------------------------------------------------------------------
// nested types
//--------------------------------------------------------------------------------------------------------------------
Q_OBJECT
You now just have to add a private function called compute and a private member called mOutput
//--------------------------------------------------------------------------------------------------------------------
// private methods
//--------------------------------------------------------------------------------------------------------------------
private:
void compute(const cedar::proc::Arguments&);
//--------------------------------------------------------------------------------------------------------------------
// members
//--------------------------------------------------------------------------------------------------------------------
protected:
// none yet
private:
cedar::aux::MatDataPtr mOutput;
The source file
Finally let's have a look at the TestStep.cpp
file
In this file we will include the configuration.h
file and the TestStep.h
file.
// CEDAR CONFIGURATION
#include "cedar/configuration.h"
// CLASS HEADER
#include "cedar/processing/ElementDeclaration.h"
#include "cedar/processing/steps/TestStep.h"
Again if you create this step in a plugin you have to change the path of the TestStep.h
file here.
#include "steps/programming/TestStep.h"
For now this are all the includes that we need.
Register the class
If you create the step directly in the cedar source code (first tutorial) you also have to register the class.
If you create the step in a plugin for CEDAR (second tutorial) you can skip this part.
To register the class we have to add the following code below the includes. You can add your iconpath and description in there. The line "Programming" defines in which category of the Elements tab in CEDAR's GUI your step will be found.
//----------------------------------------------------------------------------------------------------------------------
// register the class
//----------------------------------------------------------------------------------------------------------------------
namespace
{
bool declare()
{
using cedar::proc::ElementDeclarationPtr;
using cedar::proc::ElementDeclarationTemplate;
ElementDeclarationPtr declaration
(
new ElementDeclarationTemplate<cedar::proc::steps::TestStep>
(
"Programming",
"cedar.processing.TestStep"
)
);
declaration->setIconPath(":/steps/YOURICON.svg");
declaration->setDescription
(
"ADD A DESCRIPTION FOR THE TOOLTIP OF YOUR STEP IN HERE"
);
declaration->declare();
return true;
}
bool declared = declare();
}
Constructor, destructor and the compute function
For the constructor and destructor the code looks like this:
//----------------------------------------------------------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------------------------------------------------------
cedar::proc::steps::TestStep::TestStep()
: // <- the colon starts the member initialization list
mOutput(new cedar::aux::MatData(cv::Mat::zeros(1, 1, CV_32F)))
{
/* Declare both inputs; the "true" means that the inputs are mandatory, i.e.,
the step will not run unless both of the inputs are connected to data.
*/
this->declareInput("inputMatrix1", true);
this->declareInput("inputMatrix2", true);
// Declare the output and set it to the output matrix defined above.
this->declareOutput("sum", mOutput);
}
cedar::proc::steps::TestStep::~TestStep()
{
}
Note that we declared two inputs, which will be the two matrices our step will add up. We also declared one output. We defined inputMatrix1
and inputmatrix2
as the names of the inputs and sum
as the name of the output, so we can access those later in the code. The names are just for source code purposes and will not appear in the CEDAR GUI.
In order to be able to use the step, we also have to tell it what to compute; for this, we implement the compute function.
Note, that the compute function does not check that the inputs are valid, non-null pointers. This is done automatically by the framework, because we specified the inputs to be mandatory in the constructor. We also assume that the data connected to the input slots can be cast to cedar::aux::MatData
.
// The arguments are unused here
void cedar::proc::steps::TestStep::compute(const cedar::proc::Arguments&){
// Using data like this is more convenient.
cv::Mat& sum = mOutput->getData();
// Get a pointer to the first input.
cedar::aux::ConstDataPtr op1 = this->getInputSlot("inputMatrix1")->getData();
/* Retreive its stored data and add it to the sum.
Note, that we assume that op1 can be cast to MatData!
The call to the clone function is necessary to avoid writing into the input matrix.
*/
sum = op1->getData<cv::Mat>().clone();
// Same as above for the second input.
cedar::aux::ConstDataPtr op2 = this->getInputSlot("inputMatrix2")->getData();
sum += op2->getData<cv::Mat>().clone();
// In a console application, this lets us see that the computation is actually happening.
std::cout << "A sum was computed!" << std::endl;
}
Now you have determined how many inputs and outputs there have to be and what the step should compute.
The souce code is now ready to go!