A camera device is an uobject whose val field is a binary object. The urbi kernel itself doesn’t differenciate between all the possible binary formats and data type, but the API provides image-specfic structures for conveniance. You must be careful about memory managment. The UBinary structure handles its own memory: copies are deep copies, and the destructor frees the associated buffer. The UImage and USound structures do not.
Let’s suppose we have an underlying camera API with the following functions:
bool initialize (int id); // initialize the camera with given ID
int getWidth (int id); // read image width
int getHeight (int id); // read image height
char* getImage (int id); // get image buffer of format RGB24. The buffer returned is always the same and doesn’t have to be freed.
Our device code can be written as follows:
//inherit from UObject
class Camera : public urbi::UObject
{
public:
// the class must have a single constructor taking a string
Camera(const std::string&);
// URBI constructor
int init (int id);
// our image variable and dimensions
urbi::UVar val;
urbi::UVar width;
urbi::UVar height;
// called on access
int getVal (UVar &);
// called periodically
virtual int update ();
//frame counter for caching
int frame;
//frame number of last access
int accessFrame;
//camera id UBinary bin;
int id_;
};
The constructor only registers init:
//the constructor registers init only
Camera::Camera (const std::string& s)
: urbi::UObject (s),
frame (0)
{
// register init
UBindFunction (Camera, init);
}
The init function binds the variable, a function called on access, and sets a timer up on update. It also initialises the UBinary structure.
int
Camera::init (int id)
{
//urbi constructor
id_ = id;
frame = 0;
accessFrame = 0;
if (!initialize (id))
return 0;
UBindVar (Camera,val);
UBindVar (Camera,width);
UBindVar (Camera,height);
width = getWidth (id);
height = getHeight (id);
UNotifyAccess (val, &Camera::getVal);
bin.type=BINARY_IMAGE;
bin.image.width = width;
bin.image.height = height;
bin.image.imageFormat = IMAGE_RGB;
bin.image.size = width * height * 3;
//will call update () periodically
USetUpdate (1);
return 0;
}
The update function simply updates the frame counter:
int
Camera::update ()
{
frame++;
return 0;
}
The getVal updates the camera value, only if it hasn’t already been called this frame, which provides a simple caching mechanism to avoid performing the potentially lenghty operation of acquiring an image too often.
int
Camera::getVal(urbi::UVar &)
{
if (frame == accessFrame)
return 1;
bin.image.data = getImage (id);
//assign image to bin
val = bin;
}
UStart(Camera);
The image data is copied inside the kernel when proceeding this way.
Be careful, suppose that we had created the UBinary structure inside the getVal method, our buffer would have been freed at the end of the function. To avoid this, set it to 0 after assigning the UBinary to the UVar.