Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fakeFrameGrabber enhancements #2594

Merged
merged 7 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/device_invocation/fakeFrameGrabber_basic.dox
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ So this is just an example
<tr><td>horizontalFov</td><td>desired horizontal fov of test image</td><td>1.0</td></tr>
<tr><td>verticalFov</td><td>desired vertical fov of test image</td><td>2.0</td></tr>
<tr><td>mirror</td><td>mirroring disabled by default</td><td>0</td></tr>
<tr><td>syncro</td><td>syncronize producer and consumer, so that all images are used once and only once</td><td>0</td></tr>
<tr><td>topIsLow</td><td>explicitly set the topIsLow field in the images</td><td>1</td></tr>
<tr><td>physFocalLength</td><td>Physical focal length of the fakeFrameGrabber</td><td>3.0</td></tr>
<tr><td>focalLengthX</td><td>Horizontal component of the focal length of the fakeFrameGrabber</td><td>4.0</td></tr>
<tr><td>focalLengthY</td><td>Vertical component of the focal length of the fakeFrameGrabber</td><td>5.0</td></tr>
Expand All @@ -52,9 +54,10 @@ So this is just an example
<tr><td>t2</td><td>Tangential distortion of the lens(fake)</td><td>12.0</td></tr>
<tr><td>freq</td><td>rate of test images in Hz</td><td></td></tr>
<tr><td>period</td><td>period of test images in seconds</td><td></td></tr>
<tr><td>mode</td><td>bouncy [ball], scrolly [line], grid [grid], grid multisize [size], random [rand], noise [nois], none [none], time test[time]</td><td>line</td></tr>
<tr><td>mode</td><td>bouncy [ball], scrolly [line], grid [grid], grid multisize [size], random [rand], none [none], time test[time]</td><td>line</td></tr>
<tr><td>src</td><td></td><td></td></tr>
<tr><td>timestamp</td><td>should write the timestamp in the first bytes of the image</td><td></td></tr>
<tr><td>noise</td><td>Should add noise to the image (uses snr)</td><td></td></tr>
<tr><td>snr</td><td>Signal noise ratio ([0.0-1.0] default 0.5)</td><td>0.5</td></tr>
<tr><td>bayer</td><td>should emit bayer test image?</td><td></td></tr>
<tr><td>mono</td><td>should emit a monochrome image?</td><td></td></tr>
Expand Down
5 changes: 4 additions & 1 deletion doc/device_invocation/group_basic.dox
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ So this is just an example
<tr><td>mycam.horizontalFov</td><td>desired horizontal fov of test image</td><td>1.0</td></tr>
<tr><td>mycam.verticalFov</td><td>desired vertical fov of test image</td><td>2.0</td></tr>
<tr><td>mycam.mirror</td><td>mirroring disabled by default</td><td>0</td></tr>
<tr><td>mycam.syncro</td><td>syncronize producer and consumer, so that all images are used once and only once</td><td>0</td></tr>
<tr><td>mycam.topIsLow</td><td>explicitly set the topIsLow field in the images</td><td>1</td></tr>
<tr><td>mycam.physFocalLength</td><td>Physical focal length of the fakeFrameGrabber</td><td>3.0</td></tr>
<tr><td>mycam.focalLengthX</td><td>Horizontal component of the focal length of the fakeFrameGrabber</td><td>4.0</td></tr>
<tr><td>mycam.focalLengthY</td><td>Vertical component of the focal length of the fakeFrameGrabber</td><td>5.0</td></tr>
Expand All @@ -78,9 +80,10 @@ So this is just an example
<tr><td>mycam.t2</td><td>Tangential distortion of the lens(fake)</td><td>12.0</td></tr>
<tr><td>mycam.freq</td><td>rate of test images in Hz</td><td></td></tr>
<tr><td>mycam.period</td><td>period of test images in seconds</td><td></td></tr>
<tr><td>mycam.mode</td><td>bouncy [ball], scrolly [line], grid [grid], grid multisize [size], random [rand], noise [nois], none [none], time test[time]</td><td>line</td></tr>
<tr><td>mycam.mode</td><td>bouncy [ball], scrolly [line], grid [grid], grid multisize [size], random [rand], none [none], time test[time]</td><td>line</td></tr>
<tr><td>mycam.src</td><td></td><td></td></tr>
<tr><td>mycam.timestamp</td><td>should write the timestamp in the first bytes of the image</td><td></td></tr>
<tr><td>mycam.noise</td><td>Should add noise to the image (uses snr)</td><td></td></tr>
<tr><td>mycam.snr</td><td>Signal noise ratio ([0.0-1.0] default 0.5)</td><td>0.5</td></tr>
<tr><td>mycam.bayer</td><td>should emit bayer test image?</td><td></td></tr>
<tr><td>mycam.mono</td><td>should emit a monochrome image?</td><td></td></tr>
Expand Down
16 changes: 16 additions & 0 deletions doc/release/master/fakeFrameGrabber_enh2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
fakeFrameGrabber_enh2 {#master}
---------------------

## Devices

### `fakeFrameGrabber`

* The `getImage()` method no longer blocks the caller.
The `--syncro` option was added to restore the old behavior.

* The `topIsLow` option and the `set_topIsLow` rpc command were added to produce
images with bottom to top scanlines.

* The `nois` mode (introduced in fakeFrameGrabber_enh) was removed in favour of
the `--noise` option and `set_noise` and `set_snr` commands that adds noise
to the generated images.
224 changes: 154 additions & 70 deletions src/devices/fakeFrameGrabber/FakeFrameGrabber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ constexpr yarp::conf::vocab32_t VOCAB_LINE = yarp::os::createVocab('l'
constexpr yarp::conf::vocab32_t VOCAB_BALL = yarp::os::createVocab('b','a','l','l');
constexpr yarp::conf::vocab32_t VOCAB_GRID = yarp::os::createVocab('g','r','i','d');
constexpr yarp::conf::vocab32_t VOCAB_RAND = yarp::os::createVocab('r','a','n','d');
constexpr yarp::conf::vocab32_t VOCAB_NOIS = yarp::os::createVocab('n','o','i','s');
constexpr yarp::conf::vocab32_t VOCAB_NONE = yarp::os::createVocab('n','o','n','e');
constexpr yarp::conf::vocab32_t VOCAB_GRID_MULTISIZE = yarp::os::createVocab('s','i','z','e');
constexpr yarp::conf::vocab32_t VOCAB_TIMETEXT = yarp::os::createVocab('t','i','m','e');
Expand Down Expand Up @@ -137,7 +136,10 @@ bool FakeFrameGrabber::read(yarp::os::ConnectionReader& connection)
reply.addVocab(Vocab::encode("many"));
reply.addString("set_mode <mode>");
reply.addString("set_image <file_name>/off");
reply.addString("available modes: ball, line, grid, size, rand, nois, none, time");
reply.addString("available modes: ball, line, grid, size, rand, none, time");
reply.addString("set_topIsLow on/off");
reply.addString("set_noise on/off");
reply.addString("set_snr <snr>");
reply.addString("");
}
else if (command.get(0).asString() == "set_mode")
Expand Down Expand Up @@ -168,6 +170,35 @@ bool FakeFrameGrabber::read(yarp::os::ConnectionReader& connection)
}
}
}
else if (command.get(0).asString() == "set_topIsLow")
{
if (command.get(1).asString() == "off") {
topIsLow = false;
reply.addString("ack");
} else if (command.get(1).asString() == "on") {
topIsLow = true;
reply.addString("ack");
} else {
reply.addString("err");
}
}
else if (command.get(0).asString() == "set_noise")
{
if (command.get(1).asString() == "off") {
add_noise = false;
reply.addString("ack");
} else if (command.get(1).asString() == "on") {
add_noise = true;
reply.addString("ack");
} else {
reply.addString("err");
}
}
else if (command.get(0).asString() == "set_snr")
{
snr = yarp::conf::clamp(command.get(1).asFloat64(), 0.0, 1.0);
reply.addString("ack");
}
else
{
reply.addString("Unknown command. Type 'help'.");
Expand Down Expand Up @@ -200,6 +231,10 @@ bool FakeFrameGrabber::open(yarp::os::Searchable& config) {
"desired vertical fov of test image").asFloat64();
mirror=config.check("mirror",Value(false),
"mirroring disabled by default").asBool();
syncro=config.check("syncro",Value(false),
"syncronize producer and consumer, so that all images are used once and only once").asBool();
topIsLow=config.check("topIsLow",Value(true),
"explicitly set the topIsLow field in the images").asBool();
intrinsic.put("physFocalLength",config.check("physFocalLength",Value(3.0),"Physical focal length of the fakeFrameGrabber").asFloat64());
intrinsic.put("focalLengthX",config.check("focalLengthX",Value(4.0),"Horizontal component of the focal length of the fakeFrameGrabber").asFloat64());
intrinsic.put("focalLengthY",config.check("focalLengthY",Value(5.0),"Vertical component of the focal length of the fakeFrameGrabber").asFloat64());
Expand Down Expand Up @@ -254,7 +289,7 @@ bool FakeFrameGrabber::open(yarp::os::Searchable& config) {

mode = config.check("mode",
yarp::os::Value(VOCAB_LINE, true),
"bouncy [ball], scrolly [line], grid [grid], grid multisize [size], random [rand], noise [nois], none [none], time test[time]").asVocab();
"bouncy [ball], scrolly [line], grid [grid], grid multisize [size], random [rand], none [none], time test[time]").asVocab();

if (config.check("src")) {
if (!yarp::sig::file::read(background,
Expand All @@ -275,6 +310,8 @@ bool FakeFrameGrabber::open(yarp::os::Searchable& config) {

add_timestamp = config.check("timestamp", "should write the timestamp in the first bytes of the image");

add_noise = config.check("noise", "Should add noise to the image (uses snr)");

snr = yarp::conf::clamp(config.check("snr",Value(default_snr), "Signal noise ratio ([0.0-1.0] default 0.5)").asFloat64(), 0.0, 1.0);

use_bayer = config.check("bayer","should emit bayer test image?");
Expand Down Expand Up @@ -396,77 +433,137 @@ bool FakeFrameGrabber::setRgbMirroring(bool mirror){
void FakeFrameGrabber::run()
{
while (!isStopping()) {
for (size_t i = 0; i < 2; ++i) {
std::unique_lock<std::mutex> lk(mutex[i]);
img_consumed_cv[i].wait(lk, [&]{ if (img_ready[i]) {img_ready_cv[i].notify_one();} return (isStopping() || img_consumed[i]);});
if (isStopping()) {
break;
for (size_t i = 0; i < 2 && !isStopping(); ++i) {
if (!syncro) {
std::unique_lock<std::mutex> lk(mutex[i]);
createTestImage(buffs[i], buff_ts[i]);
timing();
curr_buff_mutex.lock();
curr_buff = i;
curr_buff_mutex.unlock();
} else {
std::unique_lock<std::mutex> lk(mutex[i]);
img_consumed_cv[i].wait(lk, [&]{ if (img_ready[i]) {img_ready_cv[i].notify_one();} return (isStopping() || img_consumed[i]);});
if (isStopping()) {
break;
}
img_ready[i] = false;
img_consumed[i] = false;
createTestImage(buffs[i], buff_ts[i]);
img_ready[i] = true;
img_ready_cv[i].notify_all();
}
img_ready[i] = false;
img_consumed[i] = false;
createTestImage(buffs[i], buff_ts[i]);
img_ready[i] = true;
img_ready_cv[i].notify_all();
}
}
}

void FakeFrameGrabber::onStop()
{
// Unlock any blocked thread.
for (size_t i = 0; i < 2; ++i) {
std::unique_lock<std::mutex> lk(mutex[i]);
img_consumed[i] = true;
img_consumed_cv[i].notify_all();
img_ready[i] = true;
img_ready_cv[i].notify_all();
if (syncro) {
for (size_t i = 0; i < 2; ++i) {
std::unique_lock<std::mutex> lk(mutex[i]);
img_consumed[i] = true;
img_consumed_cv[i].notify_all();
img_ready[i] = true;
img_ready_cv[i].notify_all();
}
}
}


bool FakeFrameGrabber::getImage(yarp::sig::ImageOf<yarp::sig::PixelRgb>& image)
{
timing();

std::unique_lock<std::mutex> lk(mutex[curr_buff]);
img_ready_cv[curr_buff].wait(lk, [&]{return (!isRunning() || img_ready[curr_buff]);});
if (!isRunning()) {
return false;
}
image.copy(buffs[curr_buff]);
stamp.update(buff_ts[curr_buff]);
img_consumed[curr_buff] = true;
img_consumed_cv[curr_buff].notify_one();

curr_buff = (curr_buff + 1) % 2;
if (!syncro) {
curr_buff_mutex.lock();
size_t cb = curr_buff;
std::unique_lock<std::mutex> lk(mutex[cb]);
curr_buff_mutex.unlock();
image.copy(buffs[cb]);
stamp.update(buff_ts[cb]);
} else {
curr_buff_mutex.lock();
timing();
size_t cb = curr_buff;
std::unique_lock<std::mutex> lk(mutex[cb]);
img_ready_cv[cb].wait(lk, [&]{return (!isRunning() || img_ready[cb]);});
if (!isRunning()) {
return false;
}

image.copy(buffs[cb]);
stamp.update(buff_ts[cb]);
img_consumed[cb] = true;
img_consumed_cv[cb].notify_one();

curr_buff = (cb + 1) % 2;
curr_buff_mutex.unlock();
}

return true;
}


bool FakeFrameGrabber::getImage(yarp::sig::ImageOf<yarp::sig::PixelMono>& image)
{
timing();

std::unique_lock<std::mutex> lk(mutex[curr_buff]);
img_ready_cv[curr_buff].wait(lk, [&]{return !isRunning() || img_ready[curr_buff];});
if (!isRunning()) {
return false;
}
if (use_bayer) {
makeSimpleBayer(buffs[curr_buff],image);

if (!syncro) {
curr_buff_mutex.lock();
size_t cb = curr_buff;
std::unique_lock<std::mutex> lk(mutex[cb]);
curr_buff_mutex.unlock();
if (use_bayer) {
makeSimpleBayer(buffs[cb], image);
} else {
image.copy(buffs[cb]);
}
stamp.update(buff_ts[cb]);
} else {
image.copy(buffs[curr_buff]);
}
stamp.update(buff_ts[curr_buff]);
img_consumed[curr_buff] = true;
img_consumed_cv[curr_buff].notify_one();
curr_buff_mutex.lock();
timing();
size_t cb = curr_buff;
std::unique_lock<std::mutex> lk(mutex[cb]);
img_ready_cv[cb].wait(lk, [&]{return (!isRunning() || img_ready[cb]);});
if (!isRunning()) {
return false;
}
if (use_bayer) {
makeSimpleBayer(buffs[cb], image);
} else {
image.copy(buffs[cb]);
}
stamp.update(buff_ts[cb]);
img_consumed[cb] = true;
img_consumed_cv[cb].notify_one();

curr_buff = (curr_buff + 1) % 2;
curr_buff = (cb + 1) % 2;
curr_buff_mutex.unlock();
}

return true;
}

bool FakeFrameGrabber::getImageCrop(cropType_id_t cropType,
yarp::sig::VectorOf<std::pair<int, int>> vertices,
yarp::sig::ImageOf<yarp::sig::PixelRgb>& image)
{
yCDebugThrottle(FAKEFRAMEGRABBER, 5.0) << "Hardware crop requested!";
return yarp::dev::IFrameGrabberOf<yarp::sig::ImageOf<yarp::sig::PixelRgb>>::getImageCrop(cropType, vertices, image);
}

bool FakeFrameGrabber::getImageCrop(cropType_id_t cropType,
yarp::sig::VectorOf<std::pair<int, int>> vertices,
yarp::sig::ImageOf<yarp::sig::PixelMono>& image)
{
yCDebugThrottle(FAKEFRAMEGRABBER, 5.0) << "Hardware crop requested!";
return yarp::dev::IFrameGrabberOf<yarp::sig::ImageOf<yarp::sig::PixelMono>>::getImageCrop(cropType, vertices, image);
}

yarp::os::Stamp FakeFrameGrabber::getLastInputStamp() {
return stamp;
Expand Down Expand Up @@ -688,35 +785,6 @@ void FakeFrameGrabber::createTestImage(yarp::sig::ImageOf<yarp::sig::PixelRgb>&
}
}
break;
case VOCAB_NOIS:
{
if (have_bg) {
image.copy(background);
} else {
image.zero();
}
static const double nsr = 1.0 - snr;
for (size_t x = 0; x < image.width(); ++x) {
for (size_t y = 0; y < image.height(); ++y) {
auto rand = ucdist(randengine);
if (have_bg) {
image.pixel(x,y) = PixelRgb {
static_cast<unsigned char>(image.pixel(x,y).r * snr + rand * nsr * 255),
static_cast<unsigned char>(image.pixel(x,y).g * snr + rand * nsr * 255),
static_cast<unsigned char>(image.pixel(x,y).b * snr + rand * nsr * 255)
};
} else {
image.pixel(x,y) = PixelRgb{
static_cast<unsigned char>(rand * nsr),
static_cast<unsigned char>(rand * nsr),
static_cast<unsigned char>(rand * nsr)
};
}
}
}
}
break;

case VOCAB_NONE:
{
if (have_bg) {
Expand All @@ -738,6 +806,20 @@ void FakeFrameGrabber::createTestImage(yarp::sig::ImageOf<yarp::sig::PixelRgb>&
bx = image.width()-1;
}

if (add_noise) {
static const double nsr = 1.0 - snr;
for (size_t x = 0; x < image.width(); ++x) {
for (size_t y = 0; y < image.height(); ++y) {
auto rand = ucdist(randengine);
image.pixel(x,y) = PixelRgb {
static_cast<unsigned char>(image.pixel(x,y).r * snr + (rand * nsr * 255)),
static_cast<unsigned char>(image.pixel(x,y).g * snr + (rand * nsr * 255)),
static_cast<unsigned char>(image.pixel(x,y).b * snr + (rand * nsr * 255))
};
}
}
}

if (add_timestamp) {
char ttxt[50];
std::snprintf(ttxt, 50, "%021.10f", timestamp);
Expand Down Expand Up @@ -769,6 +851,8 @@ void FakeFrameGrabber::createTestImage(yarp::sig::ImageOf<yarp::sig::PixelRgb>&
image.pixel(6, 0).g = ttxt[19] - '0';
image.pixel(6, 0).b = ttxt[20] - '0';
}

image.setTopIsLowIndex(topIsLow);
}


Expand Down
Loading