Difference between revisions of "The protoComponent Example"
From David Vernon's Wiki
(Created page with "==<code>protoComponent.h</code>== / ** @file protoComponent.h Interface file for the example DREAM component * * Version 1.0 * * 20/8/2014 * * * \defgroup DR...") |
(→protoComponentConfiguration.cpp) |
||
(14 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
==<code>protoComponent.h</code>== | ==<code>protoComponent.h</code>== | ||
− | / ** @file protoComponent.h Interface file for the example DREAM component | + | /** @file protoComponent.h Interface file for the example DREAM component |
* | * | ||
* Version 1.0 | * Version 1.0 | ||
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) | ||
+ | * | ||
*/ | */ | ||
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
− | + | ||
#include <yarp/sig/all.h> | #include <yarp/sig/all.h> | ||
#include <yarp/os/all.h> | #include <yarp/os/all.h> | ||
Line 179: | Line 209: | ||
#include <yarp/os/Network.h> | #include <yarp/os/Network.h> | ||
#include <yarp/os/Thread.h> | #include <yarp/os/Thread.h> | ||
− | + | ||
using namespace std; | using namespace std; | ||
using namespace yarp::os; | using namespace yarp::os; | ||
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; | |
− | + | ||
− | int *thresholdValue; | + | /* 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: | 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>== | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | /* | |
− | + | * 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() | ||
{ | { | ||
− | /* module | + | 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]] | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ==<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