YARP
Yet Another Robot Platform
ServerFrameGrabberDual.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3 * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
8
9#include <yarp/os/Log.h>
10#include <yarp/dev/PolyDriver.h>
11#include <yarp/os/LogStream.h>
13#include <yarp/sig/Vector.h>
14#include <yarp/sig/ImageUtils.h>
16
18
19#include <cstring>
20#include <algorithm> // std::for_each
21
22using namespace yarp::os;
23using namespace yarp::dev;
24using namespace yarp::sig;
25
26namespace {
27YARP_LOG_COMPONENT(SERVERGRABBER, "yarp.device.grabberDual")
28}
29
30
31// **********ServerGrabberResponder**********
32
34 left(_left)
35{
36}
37
39{
40 if(_server)
41 {
42 server=_server;
43 return true;
44 }
45 yCError(SERVERGRABBER) << "ServerGrabberResponder: invalid server pointer";
46 return false;
47}
48
50 if(server)
51 {
52 if(server->respond(command,reply,left,false))
53 {
54 return true;
55 }
56 else
57 {
58 return DeviceResponder::respond(command, reply);
59 }
60 } else {
61 return false;
62 }
63}
64
65// **********ServerGrabber**********
66
69{
70}
71
73{
74 if (param.active) {
75 close();
76 }
77}
78
80 if (!param.active) {
81 return false;
82 }
83 stopThread();
84
85 param.active = false;
86 pImg.interrupt();
87 pImg.close();
88 rpcPort.interrupt();
89 rpcPort.close();
90
91 if(responder){
92 delete responder;
93 responder=nullptr;
94 }
95
96 if(param.split)
97 {
98 pImg2.interrupt();
99 pImg2.close();
100 }
101
102
103 if(param.twoCameras)
104 {
105 rpcPort2.interrupt();
106 rpcPort2.close();
107 }
108
109 cleanUp();
110 if(poly)
111 {
112 poly->close();
113 delete poly;
114 poly=nullptr;
115 }
116
117 if(responder2)
118 {
119 delete responder2;
120 responder2=nullptr;
121 }
122
123 if(isSubdeviceOwned && poly2)
124 {
125 poly2->close();
126 delete poly2;
127 poly2=nullptr;
128 }
129 isSubdeviceOwned=false;
130 if (p2!=nullptr) {
131 delete p2;
132 p2 =nullptr;
133 }
134 return true;
135}
136
138 yCIWarning(SERVERGRABBER, id()) << config.toString();
139 yCIWarning(SERVERGRABBER, id()) << "The 'grabberDual' device is deprecated in favour of 'frameGrabber_nws_yarp' (and eventually 'frameGrabberCropper').";
140 yCIWarning(SERVERGRABBER, id()) << "The old device is no longer supported, and it will be deprecated in YARP 3.7 and removed in YARP 4.";
141 yCIWarning(SERVERGRABBER, id()) << "Please update your scripts.";
142
143 if (param.active) {
144 yCIError(SERVERGRABBER, id(), "Did you just try to open the same ServerGrabber twice?");
145 return false;
146 }
147
148 if(!fromConfig(config))
149 {
150 yCIError(SERVERGRABBER, id()) << "Device ServerGrabber failed to open, check previous log for error messages.";
151 return false;
152 }
153
154 if(!initialize_YARP(config))
155 {
156 yCIError(SERVERGRABBER, id()) <<"Error initializing YARP ports";
157 return false;
158 }
159
160 if(isSubdeviceOwned){
161 if(! openAndAttachSubDevice(config))
162 {
163 yCIError(SERVERGRABBER, id(), "Error while opening subdevice");
164 return false;
165 }
166 }
167 else
168 {
169 yCIInfo(SERVERGRABBER, id()) << "Running, waiting for attach...";
170 if (!openDeferredAttach(config)) {
171 return false;
172 }
173 }
174
175
176 param.active = true;
177// //ASK/TODO update usage and see if we need to add DeviceResponder as dependency
178// DeviceResponder::makeUsage();
179// addUsage("[set] [bri] $fBrightness", "set brightness");
180// addUsage("[set] [expo] $fExposure", "set exposure");
181// addUsage("[set] [shar] $fSharpness", "set sharpness");
182// addUsage("[set] [whit] $fBlue $fRed", "set white balance");
183// addUsage("[set] [hue] $fHue", "set hue");
184// addUsage("[set] [satu] $fSaturation", "set saturation");
185// addUsage("[set] [gamm] $fGamma", "set gamma");
186// addUsage("[set] [shut] $fShutter", "set shutter");
187// addUsage("[set] [gain] $fGain", "set gain");
188// addUsage("[set] [iris] $fIris", "set iris");
189
190// addUsage("[get] [bri]", "get brightness");
191// addUsage("[get] [expo]", "get exposure");
192// addUsage("[get] [shar]", "get sharpness");
193// addUsage("[get] [whit]", "get white balance");
194// addUsage("[get] [hue]", "get hue");
195// addUsage("[get] [satu]", "get saturation");
196// addUsage("[get] [gamm]", "get gamma");
197// addUsage("[get] [shut]", "get shutter");
198// addUsage("[get] [gain]", "get gain");
199// addUsage("[get] [iris]", "get iris");
200
201// addUsage("[get] [w]", "get width of image");
202// addUsage("[get] [h]", "get height of image");
203
204
205 return true;
206}
207
209{
210 if (config.check("period", "refresh period(in ms) of the broadcasted values through yarp ports")
211 && config.find("period").isInt32()) {
212 period = config.find("period").asInt32() / 1000.0;
213 } else {
214 yCIInfo(SERVERGRABBER, id()) << "Period parameter not found, using default of" << DEFAULT_THREAD_PERIOD << "s";
215 }
216 if((config.check("subdevice")) && (config.check("left_config") || config.check("right_config")))
217 {
218 yCIError(SERVERGRABBER, id()) << "Found both 'subdevice' and 'left_config/right_config' parameters...";
219 return false;
220 }
221 if (!config.check("subdevice", "name of the subdevice to use as a data source")
222 && config.check("left_config", "name of the ini file containing the configuration of one of two subdevices to use as a data source")
223 && config.check("right_config", "name of the ini file containing the configuration of one of two subdevices to use as a data source")) {
224 param.twoCameras = true;
225 }
226 if (config.check("twoCameras", "if true ServerGrabber will open and handle two devices, if false only one")) { //extra conf parameter for the yarprobotinterface
227 param.twoCameras = config.find("twoCameras").asBool();
228 }
229 if (config.check("split", "set 'true' to split the streaming on two different ports")) {
230 param.split = config.find("split").asBool();
231 }
232 if(config.check("capabilities","two capabilities supported, COLOR and RAW respectively for rgb and raw streaming"))
233 {
234 if (config.find("capabilities").asString() == "COLOR") {
235 param.cap=COLOR;
236 } else if (config.find("capabilities").asString() == "RAW") {
237 param.cap = RAW;
238 }
239 } else {
240 yCIWarning(SERVERGRABBER, id()) << "'capabilities' parameter not found or misspelled, the option available are COLOR(default) and RAW, using default";
241 }
242 param.canDrop = !config.check("no_drop","if present, use strict policy for sending data");
243 param.addStamp = config.check("stamp","if present, add timestamps to data");
244
245 param.singleThreaded =
246 config.check("single_threaded",
247 "if present, operate in single threaded mode");
248 //TODO audio part
249 std::string rootName;
250 rootName = config.check("name",Value("/grabber"),
251 "name of port to send data on").asString();
252 if (!param.twoCameras && param.split) {
253 param.splitterMode = true;
254 }
255
256 responder = new ServerGrabberResponder(true);
257 if (!responder->configure(this)) {
258 return false;
259 }
260 if(param.twoCameras)
261 {
262 responder2 = new ServerGrabberResponder(false);
263 if (!responder2->configure(this)) {
264 return false;
265 }
266
267 rpcPort_Name = rootName + "/left/rpc";
268 rpcPort2_Name = rootName + "/right/rpc";
269 if(param.split)
270 {
271 pImg_Name = rootName + "/left";
272 pImg2_Name = rootName + "/right";
273 } else {
274 pImg_Name = rootName;
275 }
276
277 // check if we need to create subdevice or if they are
278 // passed later on thorugh attachAll()
279 if(config.check("left_config") && config.check("right_config"))
280 {
281 isSubdeviceOwned=true;
282 }
283 else
284 {
285 isSubdeviceOwned=false;
286 }
287 }
288 else
289 {
290 if(param.split)
291 {
292 responder2 = new ServerGrabberResponder(false);
293 if (!responder2->configure(this)) {
294 return false;
295 }
296 pImg_Name = rootName + "/left";
297 pImg2_Name = rootName + "/right";
298 }
299 else
300 {
301 pImg_Name = rootName;
302 }
303 rpcPort_Name = rootName + "/rpc";
304 if(config.check("subdevice"))
305 {
306 isSubdeviceOwned=true;
307 }
308 else
309 {
310 isSubdeviceOwned=false;
311 }
312 }
313
314
315 return true;
316}
317
319{
320 // Open ports
321 bool bRet;
322 bRet = true;
323 if(!rpcPort.open(rpcPort_Name))
324 {
325 yCIError(SERVERGRABBER, id()) << "Unable to open rpc Port" << rpcPort_Name.c_str();
326 bRet = false;
327 }
328 rpcPort.setReader(*responder);
329
330 pImg.promiseType(Type::byName("yarp/image"));
331 pImg.setWriteOnly();
332 if(!pImg.open(pImg_Name))
333 {
334 yCIError(SERVERGRABBER, id()) << "Unable to open image streaming Port" << pImg_Name.c_str();
335 bRet = false;
336 }
337 pImg.setReader(*responder);
338
339 if(param.twoCameras)
340 {
341 if(!rpcPort2.open(rpcPort2_Name))
342 {
343 yCIError(SERVERGRABBER, id()) << "Unable to open rpc Port" << rpcPort2_Name.c_str();
344 bRet = false;
345 }
346 rpcPort2.setReader(*responder2);
347 }
348 if(param.split)
349 {
350 pImg2.promiseType(Type::byName("yarp/image"));
351 pImg2.setWriteOnly();
352
353 if(!pImg2.open(pImg2_Name))
354 {
355 yCIError(SERVERGRABBER, id()) << "Unable to open image streaming Port" << pImg2_Name.c_str();
356 bRet = false;
357 }
358 pImg2.setReader(*responder2);
359 }
360
361 return bRet;
362}
363
365 yarp::os::Bottle& response, bool left, bool both=false) {
366 int code = cmd.get(0).asVocab32();
367 Bottle response2;
368 switch (code)
369 {
371 {
372 switch (cmd.get(1).asVocab32())
373 {
374 case VOCAB_GET:
375 {
376 switch (cmd.get(2).asVocab32())
377 {
378 case VOCAB_CROP:
379 {
380 response.clear();
381 // If the device driver support it, use the device implementation, because it may be more efficient.
382 // If not, acquire the whole image and crop it here before sending it.
383
384 Bottle *list = cmd.get(4).asList();
385 int nPoints = list->size() /2; // divided by 2 because each pixel is identified by 2 numbers (u,v)
386
388 vertices.resize(nPoints);
389
390 for(int i=0; i<nPoints; i++)
391 {
392 vertices[i].first = list->get(i*2).asInt32();
393 vertices[i].second = list->get(i*2 +1).asInt32();
394 }
395
396 ImageOf< PixelRgb > cropped;
397
398 // Choose the interface and eventual offset depending on case.
399
400 /* HW/SW configurations here: (1a, 1b, 2a, 2b), for each one the user can request a crop on left or right image
401 * 1) single HW camera as a source
402 * 1a) split false: a single image to handle
403 * 1b) split true : 2 images, I have to handle left or right image. If user request a crop in the right side,
404 * of the image, then add an offset
405 *
406 * 2) two HW sources
407 * 2a) split true: choose appropriate image source based on left/right request
408 * 2b) split false: choose appropriate image source based on crop position. Crop request have to belong to a
409 * single frame, either left or right. Example: 2 cameras with 320x240 pixels each placed
410 * one after the other, generates a single stitched image of 640x240.
411 * Anyway a crop request like (200,100)(400,200) shall be rejected, even if it could be
412 * considered as a part of the image resulting from the stitch.
413 * Right now the decision is took based on the first point of vector 'vertices', since all
414 * points are expected to belong to the same frame (left/right)
415 *
416 */
417
418 // Default values here are valid for cases 1a and `left` side of 2a
419 IFrameGrabberImage *imageInterface = fgImage;
420
421 if(param.twoCameras == false) // a single HW source of images
422 {
423 imageInterface = fgImage;
424 if(left == false) // if left is false, implicitly split is true
425 {
426 std::for_each(vertices.begin(), vertices.end(), [=](auto &pt) { pt.first += imageInterface->width() / 2; }); // 1b
427 }
428
429 }
430 else
431 {
432 if(param.split) // 2a, right image
433 {
434 if(left == false)
435 {
436 imageInterface = fgImage2;
437 // no offset
438 }
439 }
440 else
441 {
442 if(vertices[0].first >= fgImage->width()) // 2b, right image
443 {
444 imageInterface = fgImage2;
445 std::for_each(vertices.begin(), vertices.end(), [=](auto &pt) { pt.first -= fgImage->width(); });
446 }
447 }
448
449 }
450
451 if(imageInterface != nullptr)
452 {
453 if(imageInterface->getImageCrop((cropType_id_t) cmd.get(3).asVocab32(), vertices, cropped) )
454 {
455 // use the device output
456 }
457 else
458 {
459 // In case the device has not yet implemented this feature, do it here (less efficient)
460 if(cmd.get(3).asVocab32() == YARP_CROP_RECT)
461 {
462 if(nPoints != 2)
463 {
464 response.addString("GetImageCrop failed: RECT mode requires 2 vertices.");
465 yCIError(SERVERGRABBER, id()) << "GetImageCrop failed: RECT mode requires 2 vertices, got " << nPoints;
466 return false;
467 }
469 imageInterface->getImage(full);
470
471 if(!utils::cropRect(full, vertices[0], vertices[1], cropped))
472 {
473 response.addString("GetImageCrop failed: utils::cropRect error.");
474 yCIError(SERVERGRABBER, id(), "GetImageCrop failed: utils::cropRect error: (%d, %d) (%d, %d)",
475 vertices[0].first,
476 vertices[0].second,
477 vertices[1].first,
478 vertices[1].second);
479 return false;
480 }
481 }
482 else if(cmd.get(3).asVocab32() == YARP_CROP_LIST)
483 {
484 response.addString("List type not yet implemented");
485 }
486 else
487 {
488 response.addString("Crop type unknown");
489 }
490 }
491 }
492
493 response.addVocab32(VOCAB_CROP);
494 response.addVocab32(VOCAB_IS);
495 response.addInt32(cropped.width()); // Actual width of image in pixels, to check everything is ok
496 response.addInt32(cropped.height()); // Actual height of image in pixels, to check everything is ok
497
498 response.add(Value(cropped.getRawImage(), cropped.getRawImageSize()));
499 return true;
500 } break;
501 } break;
502
503 } break;
504
505 case VOCAB_SET: // Nothing to do here yet
506 default:
507 {
508 yCIError(SERVERGRABBER, id()) << "FrameGrabberImage interface received an unknown command " << cmd.toString();
509 } break;
510
511 }
512
513 } break;
514
515 // first check if requests are coming from new iFrameGrabberControl2 interface and process them
517 {
518 if(param.twoCameras)
519 {
520 bool ret;
521 if(both){
522 ret=ifgCtrl_Responder.respond(cmd, response);
523 ret&=ifgCtrl2_Responder.respond(cmd, response2);
524 if(!ret || (response!=response2))
525 {
526 response.clear();
527 response.addVocab32(VOCAB_FAILED);
528 ret=false;
529 yCIWarning(SERVERGRABBER, id()) << "Response different among cameras or failed";
530 }
531 }
532 else
533 {
534 if(left)
535 {
536 ret=ifgCtrl_Responder.respond(cmd, response);
537 }
538 else
539 {
540 ret=ifgCtrl2_Responder.respond(cmd, response);
541 }
542 }
543 return ret;
544 } else {
545 return ifgCtrl_Responder.respond(cmd, response);
546 }
547 } break;
548
550 {
551 if(param.twoCameras)
552 {
553 bool ret;
554 ret=rgbParser.respond(cmd,response);
555 ret&=rgbParser2.respond(cmd,response2);
556 if(ret)
557 {
558 switch (cmd.get(2).asVocab32())
559 {
560 //Only the intrinsic parameters are allowed to be different among the two cameras
561 // so we give both responses appending one to the other.
563 {
564 Bottle& newResponse = response.addList();
565 newResponse.append(*response2.get(3).asList());
566 break;
567 }
568 //In general if the two response are different we send a FAIL vocab
569 default:
570 {
571 if(response!=response2)
572 {
573 response.clear();
574 response.addVocab32(VOCAB_FAILED);
575 ret=false;
576 yCIWarning(SERVERGRABBER, id()) << "Response different among cameras or failed";
577 }
578 break;
579 }
580 }
581 }
582
583 return ret;
584 } else {
585 return rgbParser.respond(cmd, response);
586 }
587 } break;
589 // DC1394 COMMANDS
592 {
593 if(param.twoCameras)
594 {
595 bool ret;
596 if(both)
597 {
598 ret=ifgCtrl_DC1394_Responder.respond(cmd, response);
599 ret&=ifgCtrl2_DC1394_Responder.respond(cmd, response2);
600 if(!ret || (response!=response2))
601 {
602 response.clear();
603 response.addString("command not recognized");
604 ret=false;
605 yCIWarning(SERVERGRABBER, id()) << "Responses different among cameras or failed";
606
607 }
608 }
609 else
610 {
611 if(left)
612 {
613 ret=ifgCtrl_DC1394_Responder.respond(cmd, response);
614 }
615 else
616 {
617 ret=ifgCtrl2_DC1394_Responder.respond(cmd, response);
618 }
619 }
620 return ret;
621 } else {
622 return ifgCtrl_DC1394_Responder.respond(cmd, response);
623 }
624 } break;
625 }
626 yCIError(SERVERGRABBER, id()) << "Command not recognized" << cmd.toString();
627 return false;
628}
629
630bool ServerGrabber::attachAll(const PolyDriverList &device2attach)
631{
632 bool ret=false;
633 if(param.twoCameras)
634 {
635 bool leftDone=false;//for avoiding double left
636 bool rightDone=false;//for avoiding double right
637 if (device2attach.size() != 2)
638 {
639 yCIError(SERVERGRABBER, id(), "Expected two devices to be attached");
640 return false;
641 }
642 for(int i=0;i<device2attach.size();i++)
643 {
644 yarp::dev::PolyDriver * Idevice2attach = device2attach[i]->poly;
645 if (!Idevice2attach->isValid())
646 {
647 yCIError(SERVERGRABBER, id()) << "Device " << device2attach[i]->key << " to attach to is not valid ... cannot proceed";
648 return false;
649 }
650 if(device2attach[i]->key == "LEFT" && !leftDone)
651 {
652 leftDone |= Idevice2attach->view(rgbVis_p);
653 leftDone |= Idevice2attach->view(fgImage);
654 leftDone |= Idevice2attach->view(fgImageRaw);
655 leftDone |= Idevice2attach->view(fgCtrl);
656 leftDone |= Idevice2attach->view(fgCtrl_DC1394);
657 }
658 else if(device2attach[i]->key == "RIGHT" && !rightDone)
659 {
660 rightDone |= Idevice2attach->view(rgbVis_p2);
661 rightDone |= Idevice2attach->view(fgImage2);
662 rightDone |= Idevice2attach->view(fgImageRaw2);
663 rightDone |= Idevice2attach->view(fgCtrl2);
664 rightDone |= Idevice2attach->view(fgCtrl2_DC1394);
665 }
666 else
667 {
668 yCIError(SERVERGRABBER, id()) << "Failed to attach. The two targets must be LEFT RIGHT and devices must implement"
669 " either IFrameGrabberImage or IFrameGrabberImageRaw";
670 return false;
671
672 }
673 }
674 switch(param.cap)
675 {
676 case COLOR :
677 {
678 if((fgImage==nullptr) || (fgImage2==nullptr))
679 {
680 yCIError(SERVERGRABBER, id()) << "Capability \"COLOR\" required not supported";
681 return false;
682 }
683 }
684 break;
685 case RAW :
686 {
687 if((fgImageRaw==nullptr) || (fgImageRaw2==nullptr))
688 {
689 yCIError(SERVERGRABBER, id()) << "Capability \"RAW\" required not supported";
690 return false;
691 }
692 }
693 }
694 if((rgbVis_p == nullptr) || (rgbVis_p2 == nullptr))
695 {
696 yCIWarning(SERVERGRABBER, id()) << "Targets has not IVisualParamInterface, some features cannot be available";
697 }
698 //Configuring parsers
699 if(rgbVis_p != nullptr && rgbVis_p2 != nullptr)
700 {
701 if(!(rgbParser.configure(rgbVis_p)) || !(rgbParser2.configure(rgbVis_p2)))
702 {
703 yCIError(SERVERGRABBER, id()) << "Error configuring interfaces for parsers";
704 return false;
705 }
706 }
707 if(fgCtrl != nullptr && fgCtrl2 != nullptr)
708 {
709 if(!(ifgCtrl_Responder.configure(fgCtrl)) || !(ifgCtrl2_Responder.configure(fgCtrl2)))
710 {
711 yCIError(SERVERGRABBER, id()) << "Error configuring interfaces for parsers";
712 return false;
713 }
714 }
715 if(fgCtrl_DC1394 != nullptr && fgCtrl2_DC1394 != nullptr)
716 {
717 if(!(ifgCtrl_DC1394_Responder.configure(fgCtrl_DC1394)) || !(ifgCtrl2_DC1394_Responder.configure(fgCtrl2_DC1394)))
718 {
719 yCIError(SERVERGRABBER, id()) << "Error configuring interfaces for parsers";
720 return false;
721 }
722 }
723 }
724 else{
725 if (device2attach.size() != 1)
726 {
727 yCIError(SERVERGRABBER, id(), "Expected one device to be attached");
728 return false;
729 }
730 yarp::dev::PolyDriver * Idevice2attach = device2attach[0]->poly;
731 Idevice2attach->view(rgbVis_p);
732 Idevice2attach->view(fgImage);
733 Idevice2attach->view(fgImageRaw);
734 Idevice2attach->view(fgCtrl);
735 Idevice2attach->view(fgCtrl_DC1394);
736 switch(param.cap){
737 case COLOR :
738 {
739 if(fgImage==nullptr)
740 {
741 yCIError(SERVERGRABBER, id()) << "Capability \"COLOR\" required not supported";
742 return false;
743 }
744 }
745 break;
746 case RAW :
747 {
748 if(fgImageRaw==nullptr)
749 {
750 yCIError(SERVERGRABBER, id()) << "Capability \"RAW\" required not supported";
751 return false;
752 }
753 }
754 }
755
756 if (!Idevice2attach->isValid())
757 {
758 yCIError(SERVERGRABBER, id()) << "Device " << device2attach[0]->key << " to attach to is not valid ... cannot proceed";
759 return false;
760 }
761
762 if(rgbVis_p == nullptr)
763 {
764 yCIWarning(SERVERGRABBER, id()) << "Targets has not IVisualParamInterface, some features cannot be available";
765 }
766
767 //Configuring parsers
768 if(rgbVis_p != nullptr)
769 {
770 if(!(rgbParser.configure(rgbVis_p)))
771 {
772 yCIError(SERVERGRABBER, id()) << "Error configuring interfaces for parsers";
773 return false;
774 }
775 }
776 if(fgCtrl != nullptr)
777 {
778 if(!(ifgCtrl_Responder.configure(fgCtrl)))
779 {
780 yCIError(SERVERGRABBER, id()) << "Error configuring interfaces for parsers";
781 return false;
782 }
783 }
784
785 if(fgCtrl_DC1394 != nullptr)
786 {
787 if(!(ifgCtrl_DC1394_Responder.configure(fgCtrl_DC1394)))
788 {
789 yCIError(SERVERGRABBER, id()) << "Error configuring interfaces for parsers";
790 return false;
791 }
792 }
793 }
794
795 PeriodicThread::setPeriod(period);
796 ret = PeriodicThread::start();
797
798 return ret;
799}
801{
802 //check if we already instantiated a subdevice previously
803 if (isSubdeviceOwned) {
804 return false;
805 }
806 stopThread();
807 return true;
808
809}
811{
814 }
815
816 rgbVis_p = nullptr;
817 rgbVis_p2 = nullptr;
818 fgImage = nullptr;
819 fgImage2 = nullptr;
820 fgImageRaw = nullptr;
821 fgImageRaw2 = nullptr;
822 fgCtrl = nullptr;
823 fgCtrl2 = nullptr;
824 fgCtrl_DC1394 = nullptr;
825 fgCtrl2_DC1394 = nullptr;
826}
827
829{
830 flex_i.setPixelCode(_img.getPixelCode());
831 flex_i.setQuantum(_img.getQuantum());
832 flex_i.setTopIsLowIndex(_img.topIsLowIndex());
833 flex_i.setExternal(_img.getRawImage(), _img.width(),_img.height());
834
835}
836
838{
839 if(param.twoCameras)
840 {
841 yCIError(SERVERGRABBER, id()) << "Server grabber configured for two cameras, but only one provided";
842 return false;
843 }
844 PolyDriverList plist;
845 if(poly)
846 {
847 PolyDriverDescriptor p(poly,"poly");
848 plist.push(p);
849 return attachAll(plist);
850 }
851 else
852 {
853 yCIError(SERVERGRABBER, id()) << "Invalid device to be attached";
854 return false;
855 }
856 return true;
857}
859{
860 return detachAll();
861}
862
863bool ServerGrabber::openDeferredAttach(Searchable &prop){
864 // I dunno what to do here now...
865 isSubdeviceOwned = false;
866 return true;
867}
868
869bool ServerGrabber::openAndAttachSubDevice(Searchable &prop){
870 PolyDriverList plist;
871 if(param.twoCameras){
872 Property p,p2;
873 poly = new PolyDriver;
874 poly2 = new PolyDriver;
875 std::string file, file2;
876 if(!prop.check("left_config") || !prop.check("right_config"))
877 {
878 yCIError(SERVERGRABBER, id()) << "Missing 'left_config' or 'right_config' filename... ";
879 return false;
880 }
881 ResourceFinder rf, rf2;
882 if(prop.check("context"))
883 {
884 rf.setDefaultContext(prop.find("context").asString().c_str());
885 rf2.setDefaultContext(prop.find("context").asString().c_str());
886 }
887 file=prop.find("left_config").toString();
888 file2=prop.find("right_config").toString();
889 p.fromConfigFile(rf.findFileByName(file));
890 p2.fromConfigFile(rf2.findFileByName(file2));
891 if(p.isNull() || p2.isNull())
892 {
893 yCIError(SERVERGRABBER, id()) << "Unable to find files specified in 'left_config' and/or 'right_config'";
894 return false;
895 }
896// p.fromString(prop.findGroup("LEFT").toString().c_str());
897// p2.fromString(prop.findGroup("RIGHT").toString().c_str());
898 if(param.cap==COLOR){
899 p.put("pixelType", VOCAB_PIXEL_RGB);
900 p2.put("pixelType", VOCAB_PIXEL_RGB);
901 }
902 else
903 {
904 p.put("pixelType", VOCAB_PIXEL_MONO);
905 p2.put("pixelType", VOCAB_PIXEL_MONO);
906 }
907 if(p.find("height").asInt32() != p2.find("height").asInt32() ||
908 p.find("width").asInt32() != p2.find("width").asInt32())
909 {
910 yCIError(SERVERGRABBER, id()) << "Error in the configuration file, the two images have to have the same dimensions";
911 return false;
912 }
913 //COSA FA? Serve? Guardarci
914 //p.setMonitor(prop.getMonitor(), "subdevice"); // pass on any monitoring
915 // if errors occurred during open, quit here.
916 poly->open(p);
917 poly2->open(p2);
918
919 if (!(poly->isValid()) || !(poly2->isValid()))
920 {
921 yCIError(SERVERGRABBER, id(), "Opening devices... FAILED");
922 return false;
923 }
924 PolyDriverDescriptor pd(poly,"LEFT");
925 PolyDriverDescriptor pd2(poly2,"RIGHT");
926 plist.push(pd);
927 plist.push(pd2);
928 //The thread is started by attachAll()
929 if (!attachAll(plist)) {
930 return false;
931 }
932 }
933 else
934 {
935 Property p;
936 poly = new PolyDriver;
937 p.fromString(prop.toString());
938 if(param.cap==COLOR){
939 p.put("pixelType", VOCAB_PIXEL_RGB);
940 }
941 else
942 {
943 p.put("pixelType", VOCAB_PIXEL_MONO);
944 }
945 p.setMonitor(prop.getMonitor(), "subdevice"); // pass on any monitoring
946 p.unput("device");
947 p.put("device",prop.find("subdevice").asString()); // subdevice was already checked before
948
949 // if errors occurred during open, quit here.
950 poly->open(p);
951
952 if (!(poly->isValid()))
953 {
954 yCIError(SERVERGRABBER, id(), "opening subdevice... FAILED");
955 return false;
956 }
957 PolyDriverDescriptor pd(poly,"poly");
958 plist.push(pd);
959 //The thread is started by attachAll()
960 if (!attachAll(plist)) {
961 return false;
962 }
963 }
964 isSubdeviceOwned = true;
965 return true;
966}
967
969{
970 if(param.twoCameras)
971 {
972 if(param.cap==COLOR)
973 {
974 img= new ImageOf<PixelRgb>;
975 img->resize(fgImage->width(),fgImage->height());
976 img2= new ImageOf<PixelRgb>;
977 img2->resize(fgImage2->width(),fgImage2->height());
978 }
979 else
980 {
981 img_Raw= new ImageOf<PixelMono>;
982 img_Raw->resize(fgImageRaw->width(),fgImageRaw->height());
983 img2_Raw= new ImageOf<PixelMono>;
984 img2_Raw->resize(fgImageRaw2->width(),fgImageRaw2->height());
985 }
986 }
987 else
988 {
989 if(param.cap==COLOR)
990 {
991 img= new ImageOf<PixelRgb>;
992 if(param.splitterMode)
993 {
994 img->resize(fgImage->width()/2,fgImage->height());
995
996 img2= new ImageOf<PixelRgb>;
997 img2->resize(fgImage->width()/2,fgImage->height());
998 }
999 else
1000 {
1001 img->resize(fgImage->width(),fgImage->height());
1002 }
1003 }
1004 else
1005 {
1006 img_Raw= new ImageOf<PixelMono>;
1007 if(param.splitterMode)
1008 {
1009 img_Raw->resize(fgImageRaw->width()/2,fgImageRaw->height());
1010
1011 img2_Raw= new ImageOf<PixelMono>;
1012 img2_Raw->resize(fgImageRaw->width()/2,fgImageRaw->height());
1013 }
1014 else
1015 {
1016 img_Raw->resize(fgImageRaw->width(), fgImageRaw->height());
1017 }
1018 }
1019 }
1020 return true;
1021}
1022
1024
1025
1026
1027}
1028
1030{
1031 if(param.twoCameras)
1032 {
1033 if(param.split)
1034 {
1035 FlexImage& flex_i=pImg.prepare();
1036 FlexImage& flex_i2=pImg2.prepare();
1037 if(param.cap==COLOR)
1038 {
1039 if(fgImage!=nullptr && fgImage2 !=nullptr)
1040 {
1041 fgImage->getImage(*img);
1042 setupFlexImage(*img,flex_i);
1043 fgImage2->getImage(*img2);
1044 setupFlexImage(*img2,flex_i2);
1045 } else {
1046 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1047 }
1048 }
1049 if(param.cap==RAW)
1050 {
1051 if(fgImageRaw!=nullptr && fgImageRaw2 !=nullptr)
1052 {
1053 fgImageRaw->getImage(*img_Raw);
1054 setupFlexImage(*img_Raw,flex_i);
1055 fgImageRaw2->getImage(*img2_Raw);
1056 setupFlexImage(*img2_Raw,flex_i2);
1057 } else {
1058 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1059 }
1060 }
1061 Stamp s = Stamp(count,Time::now());
1062 pImg.setStrict(!param.canDrop);
1063 pImg.setEnvelope(s);
1064 pImg.write();
1065 pImg2.setStrict(!param.canDrop);
1066 Stamp s2 = Stamp(count2,Time::now());
1067 pImg2.setEnvelope(s2);
1068 pImg2.write();
1069 count++;
1070 count2++;
1071
1072 }
1073 else
1074 {
1075 FlexImage& flex_i=pImg.prepare();
1076 if(param.cap==COLOR)
1077 {
1078 if(fgImage!=nullptr && fgImage2 !=nullptr)
1079 {
1081 flex_i.resize(fgImage->width()*2,fgImage->height());
1082 fgImage->getImage(*img);
1083 fgImage2->getImage(*img2);
1084
1085 bool ok = utils::horzConcat(*img, *img2, flex_i);
1086 if (!ok)
1087 {
1088 yCIError(SERVERGRABBER, id()) << "Failed to concatenate images";
1089 return;
1090 }
1091 } else {
1092 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1093 }
1094 }
1095 if(param.cap==RAW)
1096 {
1097 if(fgImageRaw!=nullptr && fgImageRaw2 !=nullptr)
1098 {
1100 flex_i.resize(fgImageRaw->width()*2,fgImageRaw->height());
1101 fgImageRaw->getImage(*img_Raw);
1102 fgImageRaw2->getImage(*img2_Raw);
1103 bool ok = utils::horzConcat(*img_Raw, *img2_Raw, flex_i);
1104 if (!ok)
1105 {
1106 yCIError(SERVERGRABBER, id()) << "Failed to concatenate images";
1107 return;
1108 }
1109 } else {
1110 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1111 }
1112 }
1113
1114 Stamp s = Stamp(count,Time::now());
1115 pImg.setStrict(!param.canDrop);
1116 pImg.setEnvelope(s);
1117 pImg.write();
1118 count++;
1119 }
1120 }
1121 else
1122 {
1123 if(param.splitterMode)
1124 {
1125 FlexImage& flex_i=pImg.prepare();
1126 FlexImage& flex_i2=pImg2.prepare();
1127
1128 if(param.cap==COLOR)
1129 {
1130 if(fgImage!=nullptr)
1131 {
1133 fgImage->getImage(inputImage);
1134
1135 bool ok = utils::vertSplit(inputImage,*img,*img2);
1136 if (!ok)
1137 {
1138 yCIError(SERVERGRABBER, id()) << "Failed to split the image";
1139 return;
1140 }
1141
1142 setupFlexImage(*img,flex_i);
1143 setupFlexImage(*img2,flex_i2);
1144 } else {
1145 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1146 }
1147 }
1148 if(param.cap==RAW)
1149 {
1150 if(fgImageRaw!=nullptr)
1151 {
1153 fgImageRaw->getImage(inputImage);
1154
1155 bool ok = utils::vertSplit(inputImage,*img_Raw,*img2_Raw);
1156 if (!ok)
1157 {
1158 yCIError(SERVERGRABBER, id()) << "Failed to split the image";
1159 return;
1160 }
1161
1162 setupFlexImage(*img_Raw,flex_i);
1163 setupFlexImage(*img2_Raw,flex_i2);
1164
1165 } else {
1166 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1167 }
1168 }
1169 Stamp s = Stamp(count,Time::now());
1170 pImg.setStrict(!param.canDrop);
1171 pImg.setEnvelope(s);
1172 pImg.write();
1173 pImg2.setStrict(!param.canDrop);
1174 Stamp s2 = Stamp(count2,Time::now());
1175 pImg2.setEnvelope(s2);
1176 pImg2.write();
1177 count++;
1178 count2++;
1179 }
1180 else
1181 {
1182 FlexImage& flex_i=pImg.prepare();
1183
1184 if(param.cap==COLOR)
1185 {
1186 if(fgImage!=nullptr)
1187 {
1188 fgImage->getImage(*img);
1189 setupFlexImage(*img,flex_i);
1190 } else {
1191 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1192 }
1193 }
1194 if(param.cap==RAW)
1195 {
1196 if(fgImageRaw!=nullptr)
1197 {
1198 fgImageRaw->getImage(*img_Raw);
1199 setupFlexImage(*img_Raw,flex_i);
1200 } else {
1201 yCIError(SERVERGRABBER, id()) << "Image not captured.. check hardware configuration";
1202 }
1203 }
1204 Stamp s = Stamp(count,Time::now());
1205 pImg.setStrict(!param.canDrop);
1206 pImg.setEnvelope(s);
1207 pImg.write();
1208 count++;
1209 }
1210 }
1211}
1212
1214{
1215 dest.setPixelCode(src.getPixelCode());
1216 dest.setQuantum(src.getQuantum());
1217 dest.setTopIsLowIndex(src.topIsLowIndex());
1218 dest.setExternal(src.getRawImage(), src.width(), src.height());
1219}
1221{
1222 if(param.cap==COLOR)
1223 {
1224 if(img!=nullptr)
1225 {
1226 delete img;
1227 img=nullptr;
1228 }
1229 if(img2!=nullptr)
1230 {
1231 delete img2;
1232 img2=nullptr;
1233 }
1234 }
1235 else
1236 {
1237 if(img_Raw!=nullptr)
1238 {
1239 delete img_Raw;
1240 img_Raw=nullptr;
1241 }
1242 if(img2_Raw!=nullptr)
1243 {
1244 delete img2_Raw;
1245 img2_Raw=nullptr;
1246 }
1247 }
1248}
constexpr yarp::conf::vocab32_t VOCAB_RGB_VISUAL_PARAMS
Definition: CameraVocabs.h:18
constexpr yarp::conf::vocab32_t VOCAB_FRAMEGRABBER_CONTROL_DC1394
Definition: CameraVocabs.h:40
constexpr yarp::conf::vocab32_t VOCAB_FRAMEGRABBER_IMAGE
Definition: CameraVocabs.h:16
constexpr yarp::conf::vocab32_t VOCAB_FRAMEGRABBER_CONTROL
Definition: CameraVocabs.h:39
constexpr yarp::conf::vocab32_t VOCAB_CROP
Definition: CameraVocabs.h:38
constexpr yarp::conf::vocab32_t VOCAB_INTRINSIC_PARAM
Definition: CameraVocabs.h:106
constexpr yarp::conf::vocab32_t VOCAB_IS
Definition: GenericVocabs.h:14
constexpr yarp::conf::vocab32_t VOCAB_GET
Definition: GenericVocabs.h:13
constexpr yarp::conf::vocab32_t VOCAB_FAILED
Definition: GenericVocabs.h:16
constexpr yarp::conf::vocab32_t VOCAB_SET
Definition: GenericVocabs.h:12
cropType_id_t
@ YARP_CROP_LIST
@ YARP_CROP_RECT
bool ret
constexpr double DEFAULT_THREAD_PERIOD
contains the definition of a Vector type
ServerGrabberResponder(bool _left=false)
bool configure(ServerGrabber *_server)
bool respond(const yarp::os::Bottle &command, yarp::os::Bottle &reply) override
Respond to a message.
grabberDual deprecated: A Network grabber for camera devices.
void threadRelease() override
Release method.
bool attach(yarp::dev::PolyDriver *poly) override
Attach to another object.
bool detach() override
Detach the object (you must have first called attach).
bool open(yarp::os::Searchable &config) override
Configure with a set of options.
bool close() override
Close the DeviceDriver.
void setupFlexImage(const yarp::sig::Image &img, yarp::sig::FlexImage &flex_i)
void shallowCopyImages(const yarp::sig::FlexImage &src, yarp::sig::FlexImage &dest)
void run() override
Loop function.
bool detachAll() override
Detach the object (you must have first called attach).
bool initialize_YARP(yarp::os::Searchable &params)
bool attachAll(const yarp::dev::PolyDriverList &device2attach) override
Attach to a list of objects.
bool respond(const yarp::os::Bottle &command, yarp::os::Bottle &reply, bool left, bool both)
bool fromConfig(yarp::os::Searchable &config)
bool threadInit() override
Initialization method.
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:88
virtual int width() const =0
Return the width of each frame.
virtual int height() const =0
Return the height of each frame.
virtual bool getImage(ImageType &image)=0
Get an image from the frame grabber.
virtual bool getImageCrop(cropType_id_t cropType, yarp::sig::VectorOf< std::pair< int, int > > vertices, ImageType &image)
Get a crop of the image from the frame grabber.
void push(PolyDriver *p, const char *k)
A container for a device driver.
Definition: PolyDriver.h:23
bool close() override
Close the DeviceDriver.
Definition: PolyDriver.cpp:173
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:196
bool open(const std::string &txt)
Construct and configure a device by its common name.
Definition: PolyDriver.cpp:140
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:64
void add(const Value &value)
Add a Value to the bottle, at the end of the list.
Definition: Bottle.cpp:336
void addVocab32(yarp::conf::vocab32_t x)
Places a vocabulary item in the bottle, at the end of the list.
Definition: Bottle.cpp:164
void append(const Bottle &alt)
Append the content of the given bottle to the current list.
Definition: Bottle.cpp:380
Bottle & addList()
Places an empty nested list in the bottle, at the end of the list.
Definition: Bottle.cpp:182
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
void addInt32(std::int32_t x)
Places a 32-bit integer in the bottle, at the end of the list.
Definition: Bottle.cpp:140
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:211
void promiseType(const Type &typ) override
Commit the port to a particular type of data.
void close() override
Stop port activity.
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
void setReader(PortReader &reader) override
Set an external reader for port data.
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
void interrupt() override
Interrupt any current reads or writes attached to the port.
void setStrict(bool strict=true) override
Call this to strictly keep all messages, or allow old ones to be quietly dropped.
void write(bool forceStrict=false)
Write the current object being returned by BufferedPort::prepare.
T & prepare()
Access the object which will be transmitted by the next call to yarp::os::BufferedPort::write.
void setWriteOnly()
Shorthand for setInputMode(false), setOutputMode(true), setRpcMode(false)
Definition: Contactable.cpp:26
An abstraction for a periodic thread.
bool isRunning() const
Returns true when the thread is started, false otherwise.
void stop()
Call this to stop the thread, this call blocks until the thread is terminated (and releaseThread() ca...
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:511
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:383
void close() override
Stop port activity.
Definition: Port.cpp:363
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:79
A class for storing options and configuration information.
Definition: Property.h:33
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Property.cpp:1051
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1063
bool fromConfigFile(const std::string &fname, bool wipe=true)
Interprets a file as a list of properties.
Definition: Property.cpp:1098
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:1015
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Definition: Property.cpp:1046
Helper class for finding config files and other external resources.
bool setDefaultContext(const std::string &contextName)
Sets the context for the current ResourceFinder object.
std::string findFileByName(const std::string &name)
Find the full path to a file.
A base class for nested structures that can be searched.
Definition: Searchable.h:63
virtual bool isNull() const
Checks if the object is invalid.
Definition: Searchable.cpp:107
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
An abstraction for a time stamp and/or sequence number.
Definition: Stamp.h:21
A single value (typically within a Bottle).
Definition: Value.h:43
virtual yarp::conf::vocab32_t asVocab32() const
Get vocabulary identifier as an integer.
Definition: Value.cpp:228
virtual bool asBool() const
Get boolean value.
Definition: Value.cpp:186
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:240
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Value.cpp:356
virtual bool isInt32() const
Checks if value is a 32-bit integer.
Definition: Value.cpp:132
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
bool respond(const yarp::os::Bottle &cmd, yarp::os::Bottle &response) override
Respond to a message.
bool respond(const yarp::os::Bottle &cmd, yarp::os::Bottle &response) override
Respond to a message.
bool configure(yarp::dev::IFrameGrabberControls *interface)
bool configure(yarp::dev::IRgbVisualParams *interface)
bool respond(const yarp::os::Bottle &cmd, yarp::os::Bottle &response) override
Respond to a message.
Image class with user control of representation details.
Definition: Image.h:411
void setQuantum(size_t imgQuantum)
Definition: Image.h:426
void setPixelCode(int imgPixelCode)
Definition: Image.h:414
Base class for storing images.
Definition: Image.h:79
bool topIsLowIndex() const
Definition: Image.h:350
size_t width() const
Gets width of image in pixels.
Definition: Image.h:163
void setExternal(const void *data, size_t imgWidth, size_t imgHeight)
Use this to wrap an external image.
Definition: Image.cpp:904
unsigned char * getRawImage() const
Access to the internal image buffer.
Definition: Image.cpp:542
size_t getRawImageSize() const
Access to the internal buffer size information (this is how much memory has been allocated for the im...
Definition: Image.cpp:551
void resize(size_t imgWidth, size_t imgHeight)
Reallocate an image to be of a desired size, throwing away its current contents.
Definition: Image.cpp:453
void setTopIsLowIndex(bool flag)
control whether image has origin at top left (default) or bottom left.
Definition: Image.cpp:512
size_t getQuantum() const
The size of a row is constrained to be a multiple of the "quantum".
Definition: Image.h:196
size_t height() const
Gets height of image in pixels.
Definition: Image.h:169
virtual int getPixelCode() const
Gets pixel type identifier.
Definition: Image.cpp:441
Provides:
Definition: Vector.h:117
void resize(size_t size) override
Resize the vector.
Definition: Vector.h:220
iterator begin() noexcept
Returns an iterator to the beginning of the VectorOf.
Definition: Vector.h:453
iterator end() noexcept
Returns an iterator to the end of the VectorOf.
Definition: Vector.h:460
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCIError(component, id,...)
Definition: LogComponent.h:223
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:76
#define yCIInfo(component, id,...)
Definition: LogComponent.h:181
#define yCIWarning(component, id,...)
Definition: LogComponent.h:202
@ VOCAB_PIXEL_MONO
Definition: Image.h:42
@ VOCAB_PIXEL_RGB
Definition: Image.h:44
For streams capable of holding different kinds of content, check what they actually have.
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:121
An interface to the operating system, including Port based communication.
bool vertSplit(const yarp::sig::Image &inImg, yarp::sig::Image &outImgL, yarp::sig::Image &outImgR)
Split vertically an image in two images of the same size.
Definition: ImageUtils.cpp:23
bool cropRect(const yarp::sig::Image &inImg, const std::pair< unsigned int, unsigned int > &vertex1, const std::pair< unsigned int, unsigned int > &vertex2, yarp::sig::Image &outImg)
Crop a rectangle area out of an image given two opposite vertices.
Definition: ImageUtils.cpp:113
bool horzConcat(const yarp::sig::Image &inImgL, const yarp::sig::Image &inImgR, yarp::sig::Image &outImg)
Concatenate horizontally two images of the same size in one with double width.
Definition: ImageUtils.cpp:67