Difference between revisions of "The protoComponent Example"
From David Vernon's Wiki
(→protoComponentConfiguration.cpp) |
(→protoComponentConfiguration.cpp) |
||
| (8 intermediate revisions by the same user not shown) | |||
| Line 32: | Line 32: | ||
* - computation | * - computation | ||
* | * | ||
| − | * Functionally, the module | + | * Functionally, the module |
| − | * converts it to a binary image based on | + | * - reads a colour image from an input port, |
| − | * and writes | + | * - reads a threshold value from an imput port, |
| + | * - converts it to a binary image based on a supplied threshold, | ||
| + | * - counts the number of foreground pixels and background pixels, | ||
| + | * - writes the binary image to an output port, | ||
| + | * - and writes the pixel statistics and the time to another output port. | ||
| + | * | ||
| + | * The purpose of these operations is simply to illustrate how input/ouput is performed. | ||
| + | * The image processing itself is trivial and of no importance. | ||
* | * | ||
* The module also reads a configuration file containing the intrinsic | * The module also reads a configuration file containing the intrinsic | ||
| Line 40: | Line 47: | ||
* techniques. | * techniques. | ||
* | * | ||
| − | * A complete tutorial | + | * A complete tutorial on this example is available on the DREAM wiki |
* | * | ||
* | * | ||
| Line 73: | Line 80: | ||
* The value part can be changed to suit your needs; the default values are shown below. | * The value part can be changed to suit your needs; the default values are shown below. | ||
* | * | ||
| − | * - \c | + | * - \c imageInputPort \c /image:i \n |
| − | * specifies the input port name (this string will be prefixed by \c /protoComponent | + | * specifies the image input port name (this string will be prefixed by \c /protoComponent |
| + | * or whatever else is specifed by the name parameter | ||
| + | * | ||
| + | * - \c thresholdInputPort \c /threshold:i \n | ||
| + | * specifies the threshold input port name (this string will be prefixed by \c /protoComponent | ||
* or whatever else is specifed by the name parameter | * or whatever else is specifed by the name parameter | ||
* | * | ||
| − | * - \c | + | * - \c imageOutputPort \c /image:o \n |
| − | * specifies the output port name (this string will be prefixed by \c /protoComponent | + | * specifies the image output port name (this string will be prefixed by \c /protoComponent |
| + | * or whatever else is specifed by the name parameter | ||
| + | * | ||
| + | * - \c statisticsOutputPort \c /statistics:o \n | ||
| + | * specifies the statistics output port name (this string will be prefixed by \c /protoComponent | ||
* or whatever else is specifed by the name parameter | * or whatever else is specifed by the name parameter | ||
* | * | ||
| Line 112: | Line 127: | ||
* | * | ||
* - \c /protoComponent/image:i \n | * - \c /protoComponent/image:i \n | ||
| + | * | ||
| + | * - \c /protoComponent/threshold:i \n | ||
* | * | ||
* <b>Output ports</b> | * <b>Output ports</b> | ||
| Line 119: | Line 136: | ||
* | * | ||
* - \c /protoComponent/image:o \n | * - \c /protoComponent/image:o \n | ||
| + | * | ||
| + | * - \c /protoComponent/statistics:o \n | ||
* | * | ||
* <b>Port types </b> | * <b>Port types </b> | ||
| Line 125: | Line 144: | ||
* but doesn't say anything about the data transmitted on the ports. This is defined by the following code. | * but doesn't say anything about the data transmitted on the ports. This is defined by the following code. | ||
* | * | ||
| − | * \c BufferedPort<ImageOf<PixelRgb> > \c | + | * \c BufferedPort<ImageOf<PixelRgb> > \c imageIn; \n |
| − | * \c BufferedPort<ImageOf<PixelRgb> > \c | + | * \c BufferedPort<VectorOf<int> > \c thresholdIn; <tt>//int threshold </tt> \n |
| + | * \c BufferedPort<ImageOf<PixelRgb> > \c imageOut; \n | ||
| + | * \c BufferedPort \c statisticsOut; \n | ||
| + | * | ||
| + | * Thus, there are three types of port in use here, all buffered: | ||
| + | * | ||
| + | * - image, | ||
| + | * - vector of int (there will be just one element in the vector in this case), and | ||
| + | * - a bottle comprising alphanumerics messages. | ||
| + | * | ||
* | * | ||
* \section in_files_sec Input Data Files | * \section in_files_sec Input Data Files | ||
| Line 157: | Line 185: | ||
* FP7 Project 611391 co-funded by the European Commission | * FP7 Project 611391 co-funded by the European Commission | ||
* | * | ||
| − | * Author: | + | * Author: David Vernon, University of Skövde |
| − | * Email: | + | * Email: david.vernon@his.se |
* Website: www.dream20202.eu | * Website: www.dream20202.eu | ||
* | * | ||
* This program comes with ABSOLUTELY NO WARRANTY | * This program comes with ABSOLUTELY NO WARRANTY | ||
*/ | */ | ||
| − | |||
| − | /* | + | |
| + | /* | ||
* Audit Trail | * Audit Trail | ||
* ----------- | * ----------- | ||
* 20/08/14 First version validated (David Vernon) | * 20/08/14 First version validated (David Vernon) | ||
| + | * 06/10/14 Additional ports for threshold input and statistic output (David Vernon) | ||
| + | * | ||
*/ | */ | ||
| Line 184: | Line 214: | ||
using namespace yarp::sig; | using namespace yarp::sig; | ||
| − | class ProtoComponentThread : public Thread | + | class ProtoComponentThread : public Thread { |
| − | + | ||
private: | private: | ||
| − | + | /* class variables */ | |
| − | + | bool debug; | |
| − | + | int x, y; | |
| − | + | PixelRgb rgbPixel; | |
| − | + | ImageOf<PixelRgb> *image; | |
| − | + | int *thresholdValue; | |
| + | VectorOf<int> *thresholdVector; | ||
| + | int numberOfForegroundPixels; | ||
| − | + | /* thread parameters: they are pointers so that they refer to the original variables in protoComponent */ | |
| − | + | ||
| − | + | ||
| − | + | BufferedPort<ImageOf<PixelRgb> > *imagePortIn; | |
| + | BufferedPort<VectorOf<int> > *thresholdPortIn; | ||
| + | BufferedPort<ImageOf<PixelRgb> > *imagePortOut; | ||
| + | BufferedPort<Bottle> *statisticsPortOut; | ||
| + | |||
| − | /* class methods */ | + | public: |
| − | + | ||
| − | + | /* class methods */ | |
| − | + | ||
| − | + | ProtoComponentThread(BufferedPort<ImageOf<PixelRgb> > *imageIn, | |
| − | + | BufferedPort<VectorOf<int> > *thresholdIn, | |
| + | BufferedPort<ImageOf<PixelRgb> > *imageOut, | ||
| + | BufferedPort<Bottle> *statisticsOut, | ||
| + | int *threshold ); | ||
| + | bool threadInit(); | ||
| + | void threadRelease(); | ||
| + | void run(); | ||
}; | }; | ||
| − | class ProtoComponent:public RFModule | + | class ProtoComponent:public RFModule { |
| − | + | ||
| − | + | ||
| − | + | /* module parameters */ | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | string moduleName; | |
| − | + | string imageInputPortName; | |
| − | + | string thresholdInputPortName; | |
| − | + | string imageOutputPortName; | |
| − | + | string statisticsOutputPortName; | |
| + | string handlerPortName; | ||
| + | string cameraConfigFilename; | ||
| + | float fxLeft, fyLeft; // focal length | ||
| + | float fxRight, fyRight; // focal length | ||
| + | float cxLeft, cyLeft; // coordinates of the principal point | ||
| + | float cxRight, cyRight; // coordinates of the principal point | ||
| + | int thresholdValue; | ||
| − | + | /* class variables */ | |
| − | + | BufferedPort<ImageOf<PixelRgb> > imageIn; // example image input port | |
| − | + | BufferedPort<VectorOf<int> > thresholdIn; // example vector input port | |
| + | BufferedPort<ImageOf<PixelRgb> > imageOut; // example image output port | ||
| + | BufferedPort<Bottle> statisticsOut; // example bottle output port | ||
| + | Port handlerPort; // a port to handle interactive messages (also uses bottles) | ||
| − | + | /* pointer to a new thread to be created and started in configure() and stopped in close() */ | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ProtoComponentThread *protoComponentThread; | |
| − | + | public: | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | bool configure(yarp::os::ResourceFinder &rf); // configure all the module parameters and return true if successful | |
| − | + | bool interruptModule(); // interrupt, e.g., the ports | |
| − | + | bool close(); // close and shut down the module | |
| − | + | bool respond(const Bottle& command, Bottle& reply); | |
| − | + | double getPeriod(); | |
| − | + | bool updateModule(); | |
| − | + | }; | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | + | ||
| − | } | + | |
| + | Back to [[Software Development Guide]] | ||
==<code>protoComponentConfiguration.cpp</code>== | ==<code>protoComponentConfiguration.cpp</code>== | ||
| − | + | ||
/* | /* | ||
* Copyright (C) 2014 DREAM Consortium | * Copyright (C) 2014 DREAM Consortium | ||
* FP7 Project 611391 co-funded by the European Commission | * FP7 Project 611391 co-funded by the European Commission | ||
* | * | ||
| − | * Author: | + | * Author: David Vernon, University of Skövde |
| − | * Email: | + | * Email: david.vernon@his.se |
| − | * Website: www.dream20202.eu | + | * Website: www.dream20202.eu |
* | * | ||
* This program comes with ABSOLUTELY NO WARRANTY | * This program comes with ABSOLUTELY NO WARRANTY | ||
*/ | */ | ||
| + | |||
| − | |||
/* | /* | ||
* Audit Trail | * Audit Trail | ||
* ----------- | * ----------- | ||
* 20/08/14 First version validated (David Vernon) | * 20/08/14 First version validated (David Vernon) | ||
| + | * 06/10/14 Additional ports for threshold input and statistic output (David Vernon) | ||
| + | * | ||
*/ | */ | ||
#include "protoComponent.h" | #include "protoComponent.h" | ||
| − | + | ||
/* | /* | ||
* Configure method ... use it to do component coordination, | * Configure method ... use it to do component coordination, | ||
| Line 337: | Line 336: | ||
* specifically the port names which are dependent on the module name | * specifically the port names which are dependent on the module name | ||
*/ | */ | ||
| − | + | ||
setName(moduleName.c_str()); | setName(moduleName.c_str()); | ||
| − | + | ||
/* now, get the rest of the parameters */ | /* now, get the rest of the parameters */ | ||
/* get the name of the input and output ports, automatically prefixing the module name by using getName() */ | /* get the name of the input and output ports, automatically prefixing the module name by using getName() */ | ||
| − | + | imageInputPortName = "/"; | |
| − | + | imageInputPortName += getName( | |
| − | + | rf.check("imageInputPort", | |
| − | + | Value("/image:i"), | |
| − | + | "Input image port (string)").asString() | |
| − | + | ); | |
| − | + | thresholdInputPortName = "/"; | |
| − | + | thresholdInputPortName += getName( | |
| − | + | rf.check("thresholdInputPort", | |
| − | + | Value("/threshold:i"), | |
| − | + | "Threshold input port (string)").asString() | |
| − | + | ); | |
| + | |||
| + | imageOutputPortName = "/"; | ||
| + | imageOutputPortName += getName( | ||
| + | rf.check("imageOutputPort", | ||
| + | Value("/image:o"), | ||
| + | "Output image port (string)").asString() | ||
| + | ); | ||
| + | |||
| + | statisticsOutputPortName = "/"; | ||
| + | statisticsOutputPortName += getName( | ||
| + | rf.check("statisticsOutputPort", | ||
| + | Value("/statistics:o"), | ||
| + | "Output image port (string)").asString() | ||
| + | ); | ||
| Line 395: | Line 408: | ||
/* open ports */ | /* open ports */ | ||
| − | if (!imageIn.open( | + | if (!imageIn.open(imageInputPortName.c_str())) { |
| − | cout << getName() << ": unable to open port " << | + | cout << getName() << ": unable to open port " << imageInputPortName << endl; |
return false; // unable to open; let RFModule know so that it won't run | return false; // unable to open; let RFModule know so that it won't run | ||
} | } | ||
| − | if (! | + | if (!thresholdIn.open(thresholdInputPortName.c_str())) { |
| − | cout << getName() << ": unable to open port " << | + | cout << getName() << ": unable to open port " << thresholdInputPortName << endl; |
return false; // unable to open; let RFModule know so that it won't run | return false; // unable to open; let RFModule know so that it won't run | ||
} | } | ||
| + | |||
| + | |||
| + | if (!imageOut.open(imageOutputPortName.c_str())) { | ||
| + | cout << getName() << ": unable to open port " << imageOutputPortName << endl; | ||
| + | return false; // unable to open; let RFModule know so that it won't run | ||
| + | } | ||
| + | |||
| + | if (!statisticsOut.open(statisticsOutputPortName.c_str())) { | ||
| + | cout << getName() << ": unable to open port " << statisticsOutputPortName << endl; | ||
| + | return false; // unable to open; let RFModule know so that it won't run | ||
| + | } | ||
| + | |||
/* | /* | ||
| Line 409: | Line 434: | ||
* so that messages received from the port are redirected to the respond method | * so that messages received from the port are redirected to the respond method | ||
*/ | */ | ||
| − | + | ||
handlerPortName = "/"; | handlerPortName = "/"; | ||
handlerPortName += getName(); // use getName() rather than a literal | handlerPortName += getName(); // use getName() rather than a literal | ||
| Line 422: | Line 447: | ||
/* create the thread and pass pointers to the module parameters */ | /* create the thread and pass pointers to the module parameters */ | ||
| − | protoComponentThread = new ProtoComponentThread(&imageIn, &imageOut, &thresholdValue); | + | protoComponentThread = new ProtoComponentThread(&imageIn, &thresholdIn, &imageOut, &statisticsOut, &thresholdValue); |
| − | + | ||
/* now start the thread to do the work */ | /* now start the thread to do the work */ | ||
protoComponentThread->start(); // this calls threadInit() and it if returns true, it then calls run() | protoComponentThread->start(); // this calls threadInit() and it if returns true, it then calls run() | ||
| − | + | ||
return true ; // let the RFModule know everything went well | return true ; // let the RFModule know everything went well | ||
// so that it will then run the module | // so that it will then run the module | ||
| Line 435: | Line 460: | ||
bool ProtoComponent::interruptModule() | bool ProtoComponent::interruptModule() | ||
{ | { | ||
| + | protoComponentThread->stop(); | ||
| + | |||
imageIn.interrupt(); | imageIn.interrupt(); | ||
| + | thresholdIn.interrupt(); | ||
imageOut.interrupt(); | imageOut.interrupt(); | ||
handlerPort.interrupt(); | handlerPort.interrupt(); | ||
| − | + | ||
return true; | return true; | ||
} | } | ||
| Line 448: | Line 476: | ||
imageIn.close(); | imageIn.close(); | ||
| + | thresholdIn.close(); | ||
imageOut.close(); | imageOut.close(); | ||
handlerPort.close(); | handlerPort.close(); | ||
| − | |||
| − | |||
return true; | return true; | ||
| − | } | + | } |
| Line 480: | Line 507: | ||
thresholdValue = command.get(2).asInt(); // set parameter value | thresholdValue = command.get(2).asInt(); // set parameter value | ||
reply.addString("ok"); | reply.addString("ok"); | ||
| − | + | } | |
} | } | ||
return true; | return true; | ||
| Line 487: | Line 514: | ||
/* Called periodically every getPeriod() seconds */ | /* Called periodically every getPeriod() seconds */ | ||
| − | + | ||
bool ProtoComponent::updateModule() | bool ProtoComponent::updateModule() | ||
{ | { | ||
return true; | return true; | ||
} | } | ||
| − | |||
| Line 498: | Line 524: | ||
{ | { | ||
/* module periodicity (seconds), called implicitly by protoComponent */ | /* module periodicity (seconds), called implicitly by protoComponent */ | ||
| − | + | ||
return 0.1; | return 0.1; | ||
} | } | ||
| + | |||
| + | Back to [[Software Development Guide]] | ||
==<code>protoComponentComputation.cpp</code>== | ==<code>protoComponentComputation.cpp</code>== | ||
| + | |||
| + | /* | ||
| + | * Copyright (C) 2014 DREAM Consortium | ||
| + | * FP7 Project 611391 co-funded by the European Commission | ||
| + | * | ||
| + | * Author: David Vernon, University of Skövde | ||
| + | * Email: david.vernon@his.se | ||
| + | * Website: www.dream20202.eu | ||
| + | * | ||
| + | * This program comes with ABSOLUTELY NO WARRANTY | ||
| + | */ | ||
| + | |||
| + | |||
| + | /* | ||
| + | * Audit Trail | ||
| + | * ----------- | ||
| + | * 20/08/14 First version validated (David Vernon) | ||
| + | * 06/10/14 Additional ports for threshold input and statistic output (David Vernon) | ||
| + | * | ||
| + | */ | ||
| + | |||
| + | |||
| + | #include "protoComponent.h" | ||
| + | |||
| + | ProtoComponentThread::ProtoComponentThread(BufferedPort<ImageOf<PixelRgb> > *imageIn, | ||
| + | BufferedPort<VectorOf<int> > *thresholdIn, | ||
| + | BufferedPort<ImageOf<PixelRgb> > *imageOut, | ||
| + | BufferedPort<Bottle> *statisticsOut, | ||
| + | int *threshold) | ||
| + | { | ||
| + | imagePortIn = imageIn; | ||
| + | thresholdPortIn = thresholdIn; | ||
| + | imagePortOut = imageOut; | ||
| + | statisticsPortOut = statisticsOut; | ||
| + | thresholdValue = threshold; | ||
| + | } | ||
| + | |||
| + | bool ProtoComponentThread::threadInit() | ||
| + | { | ||
| + | /* initialize variables and create data-structures if needed */ | ||
| + | |||
| + | debug = false; | ||
| + | |||
| + | return true; | ||
| + | } | ||
| + | |||
| + | void ProtoComponentThread::run(){ | ||
| + | |||
| + | /* | ||
| + | * do some work .... | ||
| + | * for example, convert the input image to a binary image using the threshold provided | ||
| + | */ | ||
| + | |||
| + | unsigned char value; | ||
| + | double start; | ||
| + | |||
| + | start = yarp::os::Time::now(); // start time | ||
| + | |||
| + | while (isStopping() != true) { // the thread continues to run until isStopping() returns true | ||
| + | |||
| + | if (debug) | ||
| + | cout << "protoComponentThread: threshold value is " << *thresholdValue << endl; | ||
| + | |||
| + | /* read image ... block until image is received */ | ||
| + | |||
| + | do { | ||
| + | image = imagePortIn->read(true); | ||
| + | } while ((image == NULL) && (isStopping() != true)); // exit loop if shutting down; | ||
| + | |||
| + | if (isStopping()) break; // abort this loop to avoid make sure we don't continue and possibly use NULL images | ||
| + | |||
| + | |||
| + | /* read threshold ... block if threshold is not received */ | ||
| + | /* | ||
| + | do { | ||
| + | thresholdVector = thresholdPortIn->read(false); | ||
| + | } while ((thresholdVector == NULL) && (isStopping() != true)); // exit loop if shutting down; | ||
| + | */ | ||
| + | |||
| + | /* read threshold ... do not block if threshold is not received */ | ||
| + | |||
| + | thresholdVector = thresholdPortIn->read(false); | ||
| + | |||
| + | if (thresholdVector != NULL) { | ||
| + | *thresholdValue = (int) (*thresholdVector)[0]; | ||
| + | } | ||
| + | |||
| + | |||
| + | if (debug) | ||
| + | cout << "protoComponentThread: threshold value is " << *thresholdValue << endl; | ||
| + | |||
| + | |||
| + | /* write out the binary image */ | ||
| + | |||
| + | ImageOf<PixelRgb> &binary_image = imagePortOut->prepare(); | ||
| + | binary_image.resize(image->width(),image->height()); | ||
| + | |||
| + | numberOfForegroundPixels = 0; | ||
| + | |||
| + | for (x=0; x<image->width(); x++) { | ||
| + | for (y=0; y<image->height(); y++) { | ||
| + | |||
| + | rgbPixel = image->safePixel(x,y); | ||
| + | |||
| + | if (((rgbPixel.r + rgbPixel.g + rgbPixel.b)/3) > *thresholdValue) { | ||
| + | value = (unsigned char) 255; | ||
| + | numberOfForegroundPixels++; | ||
| + | } | ||
| + | else { | ||
| + | value = (unsigned char) 0; | ||
| + | } | ||
| + | |||
| + | rgbPixel.r = value; | ||
| + | rgbPixel.g = value; | ||
| + | rgbPixel.b = value; | ||
| + | |||
| + | binary_image(x,y) = rgbPixel; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | imagePortOut->write(); | ||
| + | |||
| + | /* write out the image statistics */ | ||
| + | |||
| + | Bottle &statisticsMessage = statisticsPortOut->prepare(); | ||
| + | |||
| + | statisticsMessage.clear(); | ||
| + | |||
| + | statisticsMessage.addInt((int)(yarp::os::Time::now()-start)); | ||
| + | statisticsMessage.addString("seconds elapsed"); | ||
| + | statisticsMessage.addString(" - foreground pixel count is"); | ||
| + | statisticsMessage.addInt(numberOfForegroundPixels); | ||
| + | statisticsPortOut->write(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void ProtoComponentThread::threadRelease() | ||
| + | { | ||
| + | /* for example, delete dynamically created data-structures */ | ||
| + | } | ||
| + | |||
| + | Back to [[Software Development Guide]] | ||
| + | |||
| + | ==<code>protoComponentMain.cpp</code>== | ||
| + | |||
| + | /* | ||
| + | * Copyright (C) 2014 DREAM Consortium | ||
| + | * FP7 Project 611391 co-funded by the European Commission | ||
| + | * | ||
| + | * Author: <name of author>, <author institute> | ||
| + | * Email: <preferred email address> | ||
| + | * Website: www.dream20202.eu | ||
| + | * | ||
| + | * This program comes with ABSOLUTELY NO WARRANTY | ||
| + | */ | ||
| + | |||
| + | |||
| + | /* | ||
| + | * Audit Trail | ||
| + | * ----------- | ||
| + | * 20/08/14 First version validated (David Vernon) | ||
| + | */ | ||
| + | |||
| + | #include "protoComponent.h" | ||
| + | |||
| + | int main(int argc, char * argv[]) | ||
| + | { | ||
| + | /* initialize yarp network */ | ||
| + | |||
| + | Network yarp; | ||
| + | |||
| + | /* create your module */ | ||
| + | |||
| + | ProtoComponent protoComponent; | ||
| + | |||
| + | /* prepare and configure the resource finder */ | ||
| + | |||
| + | ResourceFinder rf; | ||
| + | rf.setVerbose(true); | ||
| + | rf.setDefaultConfigFile("protoComponent.ini"); // can be overridden by --from parameter | ||
| + | rf.setDefaultContext("protoComponent/configuration"); // can be overridden by --context parameter | ||
| + | rf.configure("DREAM_ROOT", argc, argv); // environment variable with root of configuration path | ||
| + | |||
| + | /* run the module: runModule() calls configure first and, if successful, it then runs */ | ||
| + | |||
| + | protoComponent.runModule(rf); | ||
| + | |||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | Back to [[Software Development Guide]] | ||
Latest revision as of 04:35, 4 November 2014
Contents
protoComponent.h
/** @file protoComponent.h Interface file for the example DREAM component
*
* Version 1.0
*
* 20/8/2014
*
*
* \defgroup DREAM_protoComponent protoComponent
*
* This application demonstates the use of the protoComponent module.
* This is a simple example to illustrate a component that is compliant with
* mandatory DREAM software standards for
*
* 1. File organization
* 2. Internal source code documentation
* 3. Component functionality
*
* as well as recommended DREAM software standards for
*
* 4. Programming style
* 5. Programming practice
*
* These standards are documented in Appendices A-E of Deliverable 3.2
*
* Overall, the standards address the 4 Cs of component-based software engineering:
*
* - configuration
* - coordination
* - computation
* - computation
*
* Functionally, the module
* - reads a colour image from an input port,
* - reads a threshold value from an imput port,
* - converts it to a binary image based on a supplied threshold,
* - counts the number of foreground pixels and background pixels,
* - writes the binary image to an output port,
* - and writes the pixel statistics and the time to another output port.
*
* The purpose of these operations is simply to illustrate how input/ouput is performed.
* The image processing itself is trivial and of no importance.
*
* The module also reads a configuration file containing the intrinsic
* parameters of the robot's two cameras as an example of more advanced parsing
* techniques.
*
* A complete tutorial on this example is available on the DREAM wiki
*
*
* \section lib_sec Libraries
*
* YARP.
*
* \section parameters_sec Parameters
*
* Command-line Parameters
*
* The following key-value pairs can be specified as command-line parameters
* by prefixing \c -- to the key e.g. \c --from file.ini.
* The value part can be changed to suit your needs; the default values are
* shown below.
*
* - \c from \c protoComponent.ini \n
* specifies the configuration file
*
* - \c context \c protoComponent/configuration \n
* specifies the sub-path from \c $DREAM_ROOT/protoComponent/configuration
* to the configuration file
*
* - \c name \c protoComponent \n
* specifies the name of the module (used to form the stem of module port names)
*
*
* Configuration File Parameters
*
* The following key-value pairs can be specified as parameters in the configuration file
* (they can also be specified as command-line parameters if you so wish).
* The value part can be changed to suit your needs; the default values are shown below.
*
* - \c imageInputPort \c /image:i \n
* specifies the image input port name (this string will be prefixed by \c /protoComponent
* or whatever else is specifed by the name parameter
*
* - \c thresholdInputPort \c /threshold:i \n
* specifies the threshold input port name (this string will be prefixed by \c /protoComponent
* or whatever else is specifed by the name parameter
*
* - \c imageOutputPort \c /image:o \n
* specifies the image output port name (this string will be prefixed by \c /protoComponent
* or whatever else is specifed by the name parameter
*
* - \c statisticsOutputPort \c /statistics:o \n
* specifies the statistics output port name (this string will be prefixed by \c /protoComponent
* or whatever else is specifed by the name parameter
*
* - \c cameraConfig \c cameras.ini \n
* specifies the camera configuration file containing the intrinsic parameters of
* the left and right cameras
*
* - \c threshold \c 7 \n
* specifies the threshold value
*
*
* \section portsa_sec Ports Accessed
*
* - None
*
* \section portsc_sec Ports Created
*
* Input ports
*
* - \c /protoComponent \n
* This port is used to change the parameters of the module at run time or stop the module. \n
* The following commands are available
*
* \c help \n
* \c quit \n
* \c set \c thr \c <n> ... set the threshold for binary segmentation of the input RGB image
* (where \c <n> is an integer number)
*
* Note that the name of this port mirrors whatever is provided by the \c --name parameter value
* The port is attached to the terminal so that you can type in commands and receive replies.
* The port can be used by other modules but also interactively by a user through the yarp rpc directive, viz.: \c yarp \c rpc \c /protoComponent
* This opens a connection from a terminal to the port and allows the user to then type in commands and receive replies.
*
* - \c /protoComponent/image:i \n
*
* - \c /protoComponent/threshold:i \n
*
* Output ports
*
* - \c /protoComponent \n
* see above
*
* - \c /protoComponent/image:o \n
*
* - \c /protoComponent/statistics:o \n
*
* Port types
*
* The functional specification only names the ports to be used to communicate with the module
* but doesn't say anything about the data transmitted on the ports. This is defined by the following code.
*
* \c BufferedPort<ImageOf<PixelRgb> > \c imageIn; \n
* \c BufferedPort<VectorOf<int> > \c thresholdIn; //int threshold \n
* \c BufferedPort<ImageOf<PixelRgb> > \c imageOut; \n
* \c BufferedPort \c statisticsOut; \n
*
* Thus, there are three types of port in use here, all buffered:
*
* - image,
* - vector of int (there will be just one element in the vector in this case), and
* - a bottle comprising alphanumerics messages.
*
*
* \section in_files_sec Input Data Files
*
* None
*
* \section out_data_sec Output Data Files
*
* None
*
* \section conf_file_sec Configuration Files
*
* \c protoComponent.ini
* \c cameras.ini
*
* \section example_sec Example Instantiation of the Module
*
* protoComponent --name protoComponent --context components/protoComponent/config --from protoComponent.ini
*
* \author
*
* <name of author>, <author institute>
*
* Copyright (C) 2014 DREAM Consortium
*
*/
/*
* Copyright (C) 2014 DREAM Consortium
* FP7 Project 611391 co-funded by the European Commission
*
* Author: David Vernon, University of Skövde
* Email: david.vernon@his.se
* Website: www.dream20202.eu
*
* This program comes with ABSOLUTELY NO WARRANTY
*/
/*
* Audit Trail
* -----------
* 20/08/14 First version validated (David Vernon)
* 06/10/14 Additional ports for threshold input and statistic output (David Vernon)
*
*/
#include <iostream>
#include <string>
#include <yarp/sig/all.h>
#include <yarp/os/all.h>
#include <yarp/os/RFModule.h>
#include <yarp/os/Network.h>
#include <yarp/os/Thread.h>
using namespace std;
using namespace yarp::os;
using namespace yarp::sig;
class ProtoComponentThread : public Thread {
private:
/* class variables */
bool debug;
int x, y;
PixelRgb rgbPixel;
ImageOf<PixelRgb> *image;
int *thresholdValue;
VectorOf<int> *thresholdVector;
int numberOfForegroundPixels;
/* thread parameters: they are pointers so that they refer to the original variables in protoComponent */
BufferedPort<ImageOf<PixelRgb> > *imagePortIn;
BufferedPort<VectorOf<int> > *thresholdPortIn;
BufferedPort<ImageOf<PixelRgb> > *imagePortOut;
BufferedPort<Bottle> *statisticsPortOut;
public:
/* class methods */
ProtoComponentThread(BufferedPort<ImageOf<PixelRgb> > *imageIn,
BufferedPort<VectorOf<int> > *thresholdIn,
BufferedPort<ImageOf<PixelRgb> > *imageOut,
BufferedPort<Bottle> *statisticsOut,
int *threshold );
bool threadInit();
void threadRelease();
void run();
};
class ProtoComponent:public RFModule {
/* module parameters */
string moduleName;
string imageInputPortName;
string thresholdInputPortName;
string imageOutputPortName;
string statisticsOutputPortName;
string handlerPortName;
string cameraConfigFilename;
float fxLeft, fyLeft; // focal length
float fxRight, fyRight; // focal length
float cxLeft, cyLeft; // coordinates of the principal point
float cxRight, cyRight; // coordinates of the principal point
int thresholdValue;
/* class variables */
BufferedPort<ImageOf<PixelRgb> > imageIn; // example image input port
BufferedPort<VectorOf<int> > thresholdIn; // example vector input port
BufferedPort<ImageOf<PixelRgb> > imageOut; // example image output port
BufferedPort<Bottle> statisticsOut; // example bottle output port
Port handlerPort; // a port to handle interactive messages (also uses bottles)
/* pointer to a new thread to be created and started in configure() and stopped in close() */
ProtoComponentThread *protoComponentThread;
public:
bool configure(yarp::os::ResourceFinder &rf); // configure all the module parameters and return true if successful
bool interruptModule(); // interrupt, e.g., the ports
bool close(); // close and shut down the module
bool respond(const Bottle& command, Bottle& reply);
double getPeriod();
bool updateModule();
};
Back to Software Development Guide
protoComponentConfiguration.cpp
/*
* Copyright (C) 2014 DREAM Consortium
* FP7 Project 611391 co-funded by the European Commission
*
* Author: David Vernon, University of Skövde
* Email: david.vernon@his.se
* Website: www.dream20202.eu
*
* This program comes with ABSOLUTELY NO WARRANTY
*/
/*
* Audit Trail
* -----------
* 20/08/14 First version validated (David Vernon)
* 06/10/14 Additional ports for threshold input and statistic output (David Vernon)
*
*/
#include "protoComponent.h"
/*
* Configure method ... use it to do component coordination,
* i.e. to configure your component at runtime
*/
bool ProtoComponent::configure(yarp::os::ResourceFinder &rf)
{
/* Process all parameters from both command-line and .ini file */
/* get the module name which will form the stem of all module port names */
moduleName = rf.check("name",
Value("protoComponent"),
"module name (string)").asString();
/*
* before continuing, set the module name before getting any other parameters,
* specifically the port names which are dependent on the module name
*/
setName(moduleName.c_str());
/* now, get the rest of the parameters */
/* get the name of the input and output ports, automatically prefixing the module name by using getName() */
imageInputPortName = "/";
imageInputPortName += getName(
rf.check("imageInputPort",
Value("/image:i"),
"Input image port (string)").asString()
);
thresholdInputPortName = "/";
thresholdInputPortName += getName(
rf.check("thresholdInputPort",
Value("/threshold:i"),
"Threshold input port (string)").asString()
);
imageOutputPortName = "/";
imageOutputPortName += getName(
rf.check("imageOutputPort",
Value("/image:o"),
"Output image port (string)").asString()
);
statisticsOutputPortName = "/";
statisticsOutputPortName += getName(
rf.check("statisticsOutputPort",
Value("/statistics:o"),
"Output image port (string)").asString()
);
/* get the threshold value */
thresholdValue = rf.check("threshold",
Value(8),
"Key value (int)").asInt();
/*
* get the cameraConfig file and read the required parameter values cx, cy
* in both the groups [CAMERA_CALIBRATION_LEFT] and [CAMERA_CALIBRATION_RIGHT]
*/
cameraConfigFilename = rf.check("cameraConfig",
Value("cameras.ini"),
"camera configuration filename (string)").asString();
cameraConfigFilename = (rf.findFile(cameraConfigFilename.c_str())).c_str();
Property cameraProperties;
if (cameraProperties.fromConfigFile(cameraConfigFilename.c_str()) == false) {
cout << "protoComponent: unable to read camera configuration file" << cameraConfigFilename << endl;
return 0;
}
else {
cxLeft = (float) cameraProperties.findGroup("CAMERA_CALIBRATION_LEFT").check("cx", Value(160.0), "cx left").asDouble();
cyLeft = (float) cameraProperties.findGroup("CAMERA_CALIBRATION_LEFT").check("cy", Value(120.0), "cy left").asDouble();
cxRight = (float) cameraProperties.findGroup("CAMERA_CALIBRATION_RIGHT").check("cx", Value(160.0), "cx right").asDouble();
cyRight = (float) cameraProperties.findGroup("CAMERA_CALIBRATION_RIGHT").check("cy", Value(120.0), "cy right").asDouble();
}
/* do all initialization here */
/* open ports */
if (!imageIn.open(imageInputPortName.c_str())) {
cout << getName() << ": unable to open port " << imageInputPortName << endl;
return false; // unable to open; let RFModule know so that it won't run
}
if (!thresholdIn.open(thresholdInputPortName.c_str())) {
cout << getName() << ": unable to open port " << thresholdInputPortName << endl;
return false; // unable to open; let RFModule know so that it won't run
}
if (!imageOut.open(imageOutputPortName.c_str())) {
cout << getName() << ": unable to open port " << imageOutputPortName << endl;
return false; // unable to open; let RFModule know so that it won't run
}
if (!statisticsOut.open(statisticsOutputPortName.c_str())) {
cout << getName() << ": unable to open port " << statisticsOutputPortName << endl;
return false; // unable to open; let RFModule know so that it won't run
}
/*
* attach a port of the same name as the module (prefixed with a /) to the module
* so that messages received from the port are redirected to the respond method
*/
handlerPortName = "/";
handlerPortName += getName(); // use getName() rather than a literal
if (!handlerPort.open(handlerPortName.c_str())) {
cout << getName() << ": Unable to open port " << handlerPortName << endl;
return false;
}
attach(handlerPort); // attach to port
/* create the thread and pass pointers to the module parameters */
protoComponentThread = new ProtoComponentThread(&imageIn, &thresholdIn, &imageOut, &statisticsOut, &thresholdValue);
/* now start the thread to do the work */
protoComponentThread->start(); // this calls threadInit() and it if returns true, it then calls run()
return true ; // let the RFModule know everything went well
// so that it will then run the module
}
bool ProtoComponent::interruptModule()
{
protoComponentThread->stop();
imageIn.interrupt();
thresholdIn.interrupt();
imageOut.interrupt();
handlerPort.interrupt();
return true;
}
bool ProtoComponent::close()
{
/* stop the thread */
imageIn.close();
thresholdIn.close();
imageOut.close();
handlerPort.close();
return true;
}
bool ProtoComponent::respond(const Bottle& command, Bottle& reply)
{
string helpMessage = string(getName().c_str()) +
" commands are: \n" +
"help \n" +
"quit \n" +
"set thr <n> ... set the threshold \n" +
"(where <n> is an integer number) \n";
reply.clear();
if (command.get(0).asString()=="quit") {
reply.addString("quitting");
return false;
}
else if (command.get(0).asString()=="help") {
cout << helpMessage;
reply.addString("command is: set thr <n>");
}
else if (command.get(0).asString()=="set") {
if (command.get(1).asString()=="thr") {
thresholdValue = command.get(2).asInt(); // set parameter value
reply.addString("ok");
}
}
return true;
}
/* Called periodically every getPeriod() seconds */
bool ProtoComponent::updateModule()
{
return true;
}
double ProtoComponent::getPeriod()
{
/* module periodicity (seconds), called implicitly by protoComponent */
return 0.1;
}
Back to Software Development Guide
protoComponentComputation.cpp
/*
* Copyright (C) 2014 DREAM Consortium
* FP7 Project 611391 co-funded by the European Commission
*
* Author: David Vernon, University of Skövde
* Email: david.vernon@his.se
* Website: www.dream20202.eu
*
* This program comes with ABSOLUTELY NO WARRANTY
*/
/*
* Audit Trail
* -----------
* 20/08/14 First version validated (David Vernon)
* 06/10/14 Additional ports for threshold input and statistic output (David Vernon)
*
*/
#include "protoComponent.h"
ProtoComponentThread::ProtoComponentThread(BufferedPort<ImageOf<PixelRgb> > *imageIn,
BufferedPort<VectorOf<int> > *thresholdIn,
BufferedPort<ImageOf<PixelRgb> > *imageOut,
BufferedPort<Bottle> *statisticsOut,
int *threshold)
{
imagePortIn = imageIn;
thresholdPortIn = thresholdIn;
imagePortOut = imageOut;
statisticsPortOut = statisticsOut;
thresholdValue = threshold;
}
bool ProtoComponentThread::threadInit()
{
/* initialize variables and create data-structures if needed */
debug = false;
return true;
}
void ProtoComponentThread::run(){
/*
* do some work ....
* for example, convert the input image to a binary image using the threshold provided
*/
unsigned char value;
double start;
start = yarp::os::Time::now(); // start time
while (isStopping() != true) { // the thread continues to run until isStopping() returns true
if (debug)
cout << "protoComponentThread: threshold value is " << *thresholdValue << endl;
/* read image ... block until image is received */
do {
image = imagePortIn->read(true);
} while ((image == NULL) && (isStopping() != true)); // exit loop if shutting down;
if (isStopping()) break; // abort this loop to avoid make sure we don't continue and possibly use NULL images
/* read threshold ... block if threshold is not received */
/*
do {
thresholdVector = thresholdPortIn->read(false);
} while ((thresholdVector == NULL) && (isStopping() != true)); // exit loop if shutting down;
*/
/* read threshold ... do not block if threshold is not received */
thresholdVector = thresholdPortIn->read(false);
if (thresholdVector != NULL) {
*thresholdValue = (int) (*thresholdVector)[0];
}
if (debug)
cout << "protoComponentThread: threshold value is " << *thresholdValue << endl;
/* write out the binary image */
ImageOf<PixelRgb> &binary_image = imagePortOut->prepare();
binary_image.resize(image->width(),image->height());
numberOfForegroundPixels = 0;
for (x=0; x<image->width(); x++) {
for (y=0; y<image->height(); y++) {
rgbPixel = image->safePixel(x,y);
if (((rgbPixel.r + rgbPixel.g + rgbPixel.b)/3) > *thresholdValue) {
value = (unsigned char) 255;
numberOfForegroundPixels++;
}
else {
value = (unsigned char) 0;
}
rgbPixel.r = value;
rgbPixel.g = value;
rgbPixel.b = value;
binary_image(x,y) = rgbPixel;
}
}
imagePortOut->write();
/* write out the image statistics */
Bottle &statisticsMessage = statisticsPortOut->prepare();
statisticsMessage.clear();
statisticsMessage.addInt((int)(yarp::os::Time::now()-start));
statisticsMessage.addString("seconds elapsed");
statisticsMessage.addString(" - foreground pixel count is");
statisticsMessage.addInt(numberOfForegroundPixels);
statisticsPortOut->write();
}
}
void ProtoComponentThread::threadRelease()
{
/* for example, delete dynamically created data-structures */
}
Back to Software Development Guide
protoComponentMain.cpp
/*
* Copyright (C) 2014 DREAM Consortium
* FP7 Project 611391 co-funded by the European Commission
*
* Author: <name of author>, <author institute>
* Email: <preferred email address>
* Website: www.dream20202.eu
*
* This program comes with ABSOLUTELY NO WARRANTY
*/
/*
* Audit Trail
* -----------
* 20/08/14 First version validated (David Vernon)
*/
#include "protoComponent.h"
int main(int argc, char * argv[])
{
/* initialize yarp network */
Network yarp;
/* create your module */
ProtoComponent protoComponent;
/* prepare and configure the resource finder */
ResourceFinder rf;
rf.setVerbose(true);
rf.setDefaultConfigFile("protoComponent.ini"); // can be overridden by --from parameter
rf.setDefaultContext("protoComponent/configuration"); // can be overridden by --context parameter
rf.configure("DREAM_ROOT", argc, argv); // environment variable with root of configuration path
/* run the module: runModule() calls configure first and, if successful, it then runs */
protoComponent.runModule(rf);
return 0;
}
Back to Software Development Guide