1 /* 2 * Copyright (c) 2015, Piotr Dobrowolski dobrypd[at]gmail[dot]com 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 24 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28 #include "precomp.hpp" 29 30 #ifdef HAVE_GPHOTO2 31 32 #include <gphoto2/gphoto2.h> 33 34 #include <algorithm> 35 #include <clocale> 36 #include <cstdio> 37 #include <cstring> 38 #include <ctime> 39 #include <deque> 40 #include <exception> 41 #include <map> 42 #include <ostream> 43 #include <string> 44 45 namespace cv 46 { 47 48 namespace gphoto2 { 49 50 /** 51 * \brief Map gPhoto2 return code into this exception. 52 */ 53 class GPhoto2Exception: public std::exception 54 { 55 private: 56 int result; 57 const char * method; 58 public: 59 /** 60 * @param methodStr libgphoto2 method name 61 * @param gPhoto2Result libgphoto2 method result, should be less than GP_OK 62 */ 63 GPhoto2Exception(const char * methodStr, int gPhoto2Result) 64 { 65 result = gPhoto2Result; 66 method = methodStr; 67 } 68 virtual const char * what() const throw () 69 { 70 return gp_result_as_string(result); 71 } 72 friend std::ostream & operator<<(std::ostream & ostream, 73 GPhoto2Exception & e) 74 { 75 return ostream << e.method << ": " << e.what(); 76 } 77 }; 78 79 /** 80 * \brief Capture using your camera device via digital camera library - gPhoto2. 81 * 82 * For library description and list of supported cameras, go to 83 * @url http://gphoto.sourceforge.net/ 84 * 85 * Because gPhoto2 configuration is based on a widgets 86 * and OpenCV CvCapture property settings are double typed 87 * some assumptions and tricks has to be made. 88 * 1. Device properties can be changed by IDs, use @method setProperty(int, double) 89 * and @method getProperty(int) with __additive inversed__ 90 * camera setting ID as propertyId. (If you want to get camera setting 91 * with ID == x, you want to call #getProperty(-x)). 92 * 2. Digital camera settings IDs are device dependent. 93 * 3. You can list them by getting property CAP_PROP_GPHOTO2_WIDGET_ENUMERATE. 94 * 3.1. As return you will get pointer to char array (with listed properties) 95 * instead of double. This list is in CSV type. 96 * 4. There are several types of widgets (camera settings). 97 * 4.1. For "menu" and "radio", you can get/set choice number. 98 * 4.2. For "toggle" you can get/set int type. 99 * 4.3. For "range" you can get/set float. 100 * 4.4. For any other pointer will be fetched/set. 101 * 5. You can fetch camera messages by using CAP_PROP_GPHOTO2_COLLECT_MSGS 102 * and CAP_PROP_GPHOTO2_FLUSH_MSGS (will return pointer to char array). 103 * 6. Camera settings are fetched from device as lazy as possible. 104 * It creates problem with situation when change of one setting 105 * affects another setting. You can use CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE 106 * or CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG to be sure that property you are 107 * planning to get will be actual. 108 * 109 * Capture can work in 2 main modes: preview and final. 110 * Where preview is an output from digital camera "liveview". 111 * Change modes with CAP_PROP_GPHOTO2_PREVIEW property. 112 * 113 * Moreover some generic properties are mapped to widgets, or implemented: 114 * * CV_CAP_PROP_SPEED, 115 * * CV_CAP_PROP_APERATURE, 116 * * CV_CAP_PROP_EXPOSUREPROGRAM, 117 * * CV_CAP_PROP_VIEWFINDER, 118 * * CV_CAP_PROP_POS_MSEC, 119 * * CV_CAP_PROP_POS_FRAMES, 120 * * CV_CAP_PROP_FRAME_WIDTH, 121 * * CV_CAP_PROP_FRAME_HEIGHT, 122 * * CV_CAP_PROP_FPS, 123 * * CV_CAP_PROP_FRAME_COUNT 124 * * CV_CAP_PROP_FORMAT, 125 * * CV_CAP_PROP_EXPOSURE, 126 * * CV_CAP_PROP_TRIGGER_DELAY, 127 * * CV_CAP_PROP_ZOOM, 128 * * CV_CAP_PROP_FOCUS, 129 * * CV_CAP_PROP_ISO_SPEED. 130 */ 131 class DigitalCameraCapture: public IVideoCapture 132 { 133 public: 134 static const char * separator; 135 static const char * lineDelimiter; 136 137 DigitalCameraCapture(); 138 DigitalCameraCapture(int index); 139 DigitalCameraCapture(const String &deviceName); 140 virtual ~DigitalCameraCapture(); 141 142 virtual bool isOpened() const; 143 virtual double getProperty(int) const; 144 virtual bool setProperty(int, double); 145 virtual bool grabFrame(); 146 virtual bool retrieveFrame(int, OutputArray); 147 virtual int getCaptureDomain() 148 { 149 return CV_CAP_GPHOTO2; 150 } // Return the type of the capture object: CV_CAP_VFW, etc... 151 152 bool open(int index); 153 void close(); 154 bool deviceExist(int index) const; 155 int findDevice(const char * deviceName) const; 156 157 protected: 158 // Known widget names 159 static const char * PROP_EXPOSURE_COMPENSACTION; 160 static const char * PROP_SELF_TIMER_DELAY; 161 static const char * PROP_MANUALFOCUS; 162 static const char * PROP_AUTOFOCUS; 163 static const char * PROP_ISO; 164 static const char * PROP_SPEED; 165 static const char * PROP_APERTURE_NIKON; 166 static const char * PROP_APERTURE_CANON; 167 static const char * PROP_EXPOSURE_PROGRAM; 168 static const char * PROP_VIEWFINDER; 169 170 // Instance 171 GPContext * context = NULL; 172 int numDevices; 173 void initContext(); 174 175 // Selected device 176 bool opened; 177 Camera * camera = NULL; 178 Mat frame; 179 180 // Properties 181 CameraWidget * rootWidget = NULL; 182 CameraWidget * getGenericProperty(int propertyId, double & output) const; 183 CameraWidget * setGenericProperty(int propertyId, double value, 184 bool & output) const; 185 186 // Widgets 187 void reloadConfig() throw (GPhoto2Exception); 188 CameraWidget * getWidget(int widgetId) const; 189 CameraWidget * findWidgetByName(const char * name) const; 190 191 // Loading 192 void readFrameFromFile(CameraFile * file, OutputArray outputFrame) throw (GPhoto2Exception); 193 194 // Context feedback 195 friend void ctxErrorFunc(GPContext *, const char *, void *); 196 friend void ctxStatusFunc(GPContext *, const char *, void *); 197 friend void ctxMessageFunc(GPContext *, const char *, void *); 198 199 // Messages / debug 200 enum MsgType 201 { 202 ERROR = (int) 'E', 203 WARNING = (int) 'W', 204 STATUS = (int) 'S', 205 OTHER = (int) 'O' 206 }; 207 template<typename OsstreamPrintable> 208 void message(MsgType msgType, const char * msg, 209 OsstreamPrintable & arg) const; 210 211 private: 212 // Instance 213 CameraAbilitiesList * abilitiesList = NULL; 214 GPPortInfoList * capablePorts = NULL; 215 CameraList * allDevices = NULL; 216 217 // Selected device 218 CameraAbilities cameraAbilities; 219 std::deque<CameraFile *> grabbedFrames; 220 221 // Properties 222 bool preview; // CV_CAP_PROP_GPHOTO2_PREVIEW 223 std::string widgetInfo; // CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE 224 std::map<int, CameraWidget *> widgets; 225 bool reloadOnChange; // CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE 226 time_t firstCapturedFrameTime; 227 unsigned long int capturedFrames; 228 229 DigitalCameraCapture(const DigitalCameraCapture&); // Disable copying 230 DigitalCameraCapture& operator=(DigitalCameraCapture const&); // Disable assigning 231 232 // Widgets 233 int noOfWidgets; 234 int widgetDescription(std::ostream &os, CameraWidget * widget) const 235 throw (GPhoto2Exception); 236 int collectWidgets(std::ostream &os, CameraWidget * widget) 237 throw (GPhoto2Exception); 238 239 // Messages / debug 240 mutable std::ostringstream msgsBuffer; // CV_CAP_PROP_GPHOTO2_FLUSH_MSGS 241 mutable std::string lastFlush; // CV_CAP_PROP_GPHOTO2_FLUSH_MSGS 242 bool collectMsgs; // CV_CAP_PROP_GPHOTO2_COLLECT_MSGS 243 }; 244 245 /** 246 * \brief Check if gPhoto2 function ends successfully. If not, throw an exception. 247 */ 248 #define CR(GPHOTO2_FUN) do {\ 249 int r_0629c47b758;\ 250 if ((r_0629c47b758 = (GPHOTO2_FUN)) < GP_OK) {\ 251 throw GPhoto2Exception(#GPHOTO2_FUN, r_0629c47b758);\ 252 };\ 253 } while(0) 254 255 /** 256 * \brief gPhoto2 context error feedback function. 257 * @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object. 258 */ 259 void ctxErrorFunc(GPContext *, const char * str, void * thatGPhotoCap) 260 { 261 const DigitalCameraCapture * self = 262 (const DigitalCameraCapture *) thatGPhotoCap; 263 self->message(self->ERROR, "context feedback", str); 264 } 265 266 /** 267 * \brief gPhoto2 context status feedback function. 268 * @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object. 269 */ 270 void ctxStatusFunc(GPContext *, const char * str, void * thatGPhotoCap) 271 { 272 const DigitalCameraCapture * self = 273 (const DigitalCameraCapture *) thatGPhotoCap; 274 self->message(self->STATUS, "context feedback", str); 275 } 276 277 /** 278 * \brief gPhoto2 context message feedback function. 279 * @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object. 280 */ 281 void ctxMessageFunc(GPContext *, const char * str, void * thatGPhotoCap) 282 { 283 const DigitalCameraCapture * self = 284 (const DigitalCameraCapture *) thatGPhotoCap; 285 self->message(self->OTHER, "context feedback", str); 286 } 287 288 /** 289 * \brief Separator used while creating CSV. 290 */ 291 const char * DigitalCameraCapture::separator = ","; 292 /** 293 * \brief Line delimiter used while creating any readable output. 294 */ 295 const char * DigitalCameraCapture::lineDelimiter = "\n"; 296 /** 297 * \bief Some known widget names. 298 * 299 * Those are actually substrings of widget name. 300 * ie. for VIEWFINDER, Nikon uses "viewfinder", while Canon can use "eosviewfinder". 301 */ 302 const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSACTION = 303 "exposurecompensation"; 304 const char * DigitalCameraCapture::PROP_SELF_TIMER_DELAY = "selftimerdelay"; 305 const char * DigitalCameraCapture::PROP_MANUALFOCUS = "manualfocusdrive"; 306 const char * DigitalCameraCapture::PROP_AUTOFOCUS = "autofocusdrive"; 307 const char * DigitalCameraCapture::PROP_ISO = "iso"; 308 const char * DigitalCameraCapture::PROP_SPEED = "shutterspeed"; 309 const char * DigitalCameraCapture::PROP_APERTURE_NIKON = "f-number"; 310 const char * DigitalCameraCapture::PROP_APERTURE_CANON = "aperture"; 311 const char * DigitalCameraCapture::PROP_EXPOSURE_PROGRAM = "expprogram"; 312 const char * DigitalCameraCapture::PROP_VIEWFINDER = "viewfinder"; 313 314 /** 315 * Initialize gPhoto2 context, search for all available devices. 316 */ 317 void DigitalCameraCapture::initContext() 318 { 319 capturedFrames = noOfWidgets = numDevices = 0; 320 opened = preview = reloadOnChange = false; 321 firstCapturedFrameTime = 0; 322 323 context = gp_context_new(); 324 325 gp_context_set_error_func(context, ctxErrorFunc, (void*) this); 326 gp_context_set_status_func(context, ctxStatusFunc, (void*) this); 327 gp_context_set_message_func(context, ctxMessageFunc, (void*) this); 328 329 try 330 { 331 // Load abilities 332 CR(gp_abilities_list_new(&abilitiesList)); 333 CR(gp_abilities_list_load(abilitiesList, context)); 334 335 // Load ports 336 CR(gp_port_info_list_new(&capablePorts)); 337 CR(gp_port_info_list_load(capablePorts)); 338 339 // Auto-detect devices 340 CR(gp_list_new(&allDevices)); 341 CR(gp_camera_autodetect(allDevices, context)); 342 CR(numDevices = gp_list_count(allDevices)); 343 } 344 catch (GPhoto2Exception & e) 345 { 346 numDevices = 0; 347 } 348 } 349 350 /** 351 * Search for all devices while constructing. 352 */ 353 DigitalCameraCapture::DigitalCameraCapture() 354 { 355 initContext(); 356 } 357 358 /** 359 * @see open(int) 360 */ 361 DigitalCameraCapture::DigitalCameraCapture(int index) 362 { 363 initContext(); 364 if (deviceExist(index)) 365 open(index); 366 } 367 368 /** 369 * @see findDevice(const char*) 370 * @see open(int) 371 */ 372 DigitalCameraCapture::DigitalCameraCapture(const String & deviceName) 373 { 374 initContext(); 375 int index = findDevice(deviceName.c_str()); 376 if (deviceExist(index)) 377 open(index); 378 } 379 380 /** 381 * Always close connection to the device. 382 */ 383 DigitalCameraCapture::~DigitalCameraCapture() 384 { 385 close(); 386 try 387 { 388 CR(gp_abilities_list_free(abilitiesList)); 389 abilitiesList = NULL; 390 CR(gp_port_info_list_free(capablePorts)); 391 capablePorts = NULL; 392 CR(gp_list_unref(allDevices)); 393 allDevices = NULL; 394 gp_context_unref(context); 395 context = NULL; 396 } 397 catch (GPhoto2Exception & e) 398 { 399 message(ERROR, "destruction error", e); 400 } 401 } 402 403 /** 404 * Connects to selected device. 405 */ 406 bool DigitalCameraCapture::open(int index) 407 { 408 const char * model = 0, *path = 0; 409 int m, p; 410 GPPortInfo portInfo; 411 412 if (isOpened()) { 413 close(); 414 } 415 416 try 417 { 418 CR(gp_camera_new(&camera)); 419 CR(gp_list_get_name(allDevices, index, &model)); 420 CR(gp_list_get_value(allDevices, index, &path)); 421 422 // Set model abilities. 423 CR(m = gp_abilities_list_lookup_model(abilitiesList, model)); 424 CR(gp_abilities_list_get_abilities(abilitiesList, m, &cameraAbilities)); 425 CR(gp_camera_set_abilities(camera, cameraAbilities)); 426 427 // Set port 428 CR(p = gp_port_info_list_lookup_path(capablePorts, path)); 429 CR(gp_port_info_list_get_info(capablePorts, p, &portInfo)); 430 CR(gp_camera_set_port_info(camera, portInfo)); 431 432 // Initialize connection to the camera. 433 CR(gp_camera_init(camera, context)); 434 435 message(STATUS, "connected camera", model); 436 message(STATUS, "connected using", path); 437 438 // State initialization 439 firstCapturedFrameTime = 0; 440 capturedFrames = 0; 441 preview = false; 442 reloadOnChange = false; 443 collectMsgs = false; 444 445 reloadConfig(); 446 447 opened = true; 448 return true; 449 } 450 catch (GPhoto2Exception & e) 451 { 452 message(WARNING, "opening device failed", e); 453 return false; 454 } 455 } 456 457 /** 458 * 459 */ 460 bool DigitalCameraCapture::isOpened() const 461 { 462 return opened; 463 } 464 465 /** 466 * Close connection to the camera. Remove all unread frames/files. 467 */ 468 void DigitalCameraCapture::close() 469 { 470 try 471 { 472 if (!frame.empty()) 473 { 474 frame.release(); 475 } 476 if (camera) 477 { 478 CR(gp_camera_exit(camera, context)); 479 CR(gp_camera_unref(camera)); 480 camera = NULL; 481 } 482 opened = false; 483 if (int frames = grabbedFrames.size() > 0) 484 { 485 while (frames--) 486 { 487 CameraFile * file = grabbedFrames.front(); 488 grabbedFrames.pop_front(); 489 CR(gp_file_unref(file)); 490 } 491 } 492 if (rootWidget) 493 { 494 widgetInfo.clear(); 495 CR(gp_widget_unref(rootWidget)); 496 rootWidget = NULL; 497 } 498 } 499 catch (GPhoto2Exception & e) 500 { 501 message(ERROR, "cannot close device properly", e); 502 } 503 } 504 505 /** 506 * @param output will be changed if possible, return 0 if changed, 507 * @return widget, or NULL if output value was found (saved in argument), 508 */ 509 CameraWidget * DigitalCameraCapture::getGenericProperty(int propertyId, 510 double & output) const 511 { 512 switch (propertyId) 513 { 514 case CV_CAP_PROP_POS_MSEC: 515 { 516 // Only seconds level precision, FUTURE: cross-platform milliseconds 517 output = (time(0) - firstCapturedFrameTime) * 1e2; 518 return NULL; 519 } 520 case CV_CAP_PROP_POS_FRAMES: 521 { 522 output = capturedFrames; 523 return NULL; 524 } 525 case CV_CAP_PROP_FRAME_WIDTH: 526 { 527 if (!frame.empty()) 528 { 529 output = frame.cols; 530 } 531 return NULL; 532 } 533 case CV_CAP_PROP_FRAME_HEIGHT: 534 { 535 if (!frame.empty()) 536 { 537 output = frame.rows; 538 } 539 return NULL; 540 } 541 case CV_CAP_PROP_FORMAT: 542 { 543 if (!frame.empty()) 544 { 545 output = frame.type(); 546 } 547 return NULL; 548 } 549 case CV_CAP_PROP_FPS: // returns average fps from the begin 550 { 551 double wholeProcessTime = 0; 552 getGenericProperty(CV_CAP_PROP_POS_MSEC, wholeProcessTime); 553 wholeProcessTime /= 1e2; 554 output = capturedFrames / wholeProcessTime; 555 return NULL; 556 } 557 case CV_CAP_PROP_FRAME_COUNT: 558 { 559 output = capturedFrames; 560 return NULL; 561 } 562 case CV_CAP_PROP_EXPOSURE: 563 return findWidgetByName(PROP_EXPOSURE_COMPENSACTION); 564 case CV_CAP_PROP_TRIGGER_DELAY: 565 return findWidgetByName(PROP_SELF_TIMER_DELAY); 566 case CV_CAP_PROP_ZOOM: 567 return findWidgetByName(PROP_MANUALFOCUS); 568 case CV_CAP_PROP_FOCUS: 569 return findWidgetByName(PROP_AUTOFOCUS); 570 case CV_CAP_PROP_ISO_SPEED: 571 return findWidgetByName(PROP_ISO); 572 case CV_CAP_PROP_SPEED: 573 return findWidgetByName(PROP_SPEED); 574 case CV_CAP_PROP_APERTURE: 575 { 576 CameraWidget * widget = findWidgetByName(PROP_APERTURE_NIKON); 577 return (widget == 0) ? findWidgetByName(PROP_APERTURE_CANON) : widget; 578 } 579 case CV_CAP_PROP_EXPOSUREPROGRAM: 580 return findWidgetByName(PROP_EXPOSURE_PROGRAM); 581 case CV_CAP_PROP_VIEWFINDER: 582 return findWidgetByName(PROP_VIEWFINDER); 583 } 584 return NULL; 585 } 586 587 /** 588 * Get property. 589 * @see DigitalCameraCapture for more information about returned double type. 590 */ 591 double DigitalCameraCapture::getProperty(int propertyId) const 592 { 593 CameraWidget * widget = NULL; 594 double output = 0; 595 if (propertyId < 0) 596 { 597 widget = getWidget(-propertyId); 598 } 599 else 600 { 601 switch (propertyId) 602 { 603 // gphoto2 cap featured 604 case CV_CAP_PROP_GPHOTO2_PREVIEW: 605 return preview; 606 case CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE: 607 if (rootWidget == NULL) 608 return 0; 609 return (intptr_t) widgetInfo.c_str(); 610 case CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG: 611 return 0; // Trigger, only by set 612 case CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE: 613 return reloadOnChange; 614 case CV_CAP_PROP_GPHOTO2_COLLECT_MSGS: 615 return collectMsgs; 616 case CV_CAP_PROP_GPHOTO2_FLUSH_MSGS: 617 lastFlush = msgsBuffer.str(); 618 msgsBuffer.str(""); 619 msgsBuffer.clear(); 620 return (intptr_t) lastFlush.c_str(); 621 default: 622 widget = getGenericProperty(propertyId, output); 623 /* no break */ 624 } 625 } 626 if (widget == NULL) 627 return output; 628 try 629 { 630 CameraWidgetType type; 631 CR(gp_widget_get_type(widget, &type)); 632 switch (type) 633 { 634 case GP_WIDGET_MENU: 635 case GP_WIDGET_RADIO: 636 { 637 int cnt = 0, i; 638 const char * current; 639 CR(gp_widget_get_value(widget, ¤t)); 640 CR(cnt = gp_widget_count_choices(widget)); 641 for (i = 0; i < cnt; i++) 642 { 643 const char *choice; 644 CR(gp_widget_get_choice(widget, i, &choice)); 645 if (std::strcmp(choice, current) == 0) 646 { 647 return i; 648 } 649 } 650 return -1; 651 } 652 case GP_WIDGET_TOGGLE: 653 { 654 int value; 655 CR(gp_widget_get_value(widget, &value)); 656 return value; 657 } 658 case GP_WIDGET_RANGE: 659 { 660 float value; 661 CR(gp_widget_get_value(widget, &value)); 662 return value; 663 } 664 default: 665 { 666 char* value; 667 CR(gp_widget_get_value(widget, &value)); 668 return (intptr_t) value; 669 } 670 } 671 } 672 catch (GPhoto2Exception & e) 673 { 674 char buf[128] = ""; 675 sprintf(buf, "cannot get property: %d", propertyId); 676 message(WARNING, (const char *) buf, e); 677 return 0; 678 } 679 } 680 681 /** 682 * @param output will be changed if possible, return 0 if changed, 683 * @return widget, or 0 if output value was found (saved in argument), 684 */ 685 CameraWidget * DigitalCameraCapture::setGenericProperty(int propertyId, 686 double /*FUTURE: value*/, bool & output) const 687 { 688 switch (propertyId) 689 { 690 case CV_CAP_PROP_POS_MSEC: 691 case CV_CAP_PROP_POS_FRAMES: 692 case CV_CAP_PROP_FRAME_WIDTH: 693 case CV_CAP_PROP_FRAME_HEIGHT: 694 case CV_CAP_PROP_FPS: 695 case CV_CAP_PROP_FRAME_COUNT: 696 case CV_CAP_PROP_FORMAT: 697 output = false; 698 return NULL; 699 case CV_CAP_PROP_EXPOSURE: 700 return findWidgetByName(PROP_EXPOSURE_COMPENSACTION); 701 case CV_CAP_PROP_TRIGGER_DELAY: 702 return findWidgetByName(PROP_SELF_TIMER_DELAY); 703 case CV_CAP_PROP_ZOOM: 704 return findWidgetByName(PROP_MANUALFOCUS); 705 case CV_CAP_PROP_FOCUS: 706 return findWidgetByName(PROP_AUTOFOCUS); 707 case CV_CAP_PROP_ISO_SPEED: 708 return findWidgetByName(PROP_ISO); 709 case CV_CAP_PROP_SPEED: 710 return findWidgetByName(PROP_SPEED); 711 case CV_CAP_PROP_APERTURE: 712 { 713 CameraWidget * widget = findWidgetByName(PROP_APERTURE_NIKON); 714 return (widget == NULL) ? findWidgetByName(PROP_APERTURE_CANON) : widget; 715 } 716 case CV_CAP_PROP_EXPOSUREPROGRAM: 717 return findWidgetByName(PROP_EXPOSURE_PROGRAM); 718 case CV_CAP_PROP_VIEWFINDER: 719 return findWidgetByName(PROP_VIEWFINDER); 720 } 721 return NULL; 722 } 723 724 /** 725 * Set property. 726 * @see DigitalCameraCapture for more information about value, double typed, argument. 727 */ 728 bool DigitalCameraCapture::setProperty(int propertyId, double value) 729 { 730 CameraWidget * widget = NULL; 731 bool output = false; 732 if (propertyId < 0) 733 { 734 widget = getWidget(-propertyId); 735 } 736 else 737 { 738 switch (propertyId) 739 { 740 // gphoto2 cap featured 741 case CV_CAP_PROP_GPHOTO2_PREVIEW: 742 preview = value != 0; 743 return true; 744 case CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE: 745 return false; 746 case CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG: 747 reloadConfig(); 748 return true; 749 case CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE: 750 reloadOnChange = value != 0; 751 return true; 752 case CV_CAP_PROP_GPHOTO2_COLLECT_MSGS: 753 collectMsgs = value != 0; 754 return true; 755 case CV_CAP_PROP_GPHOTO2_FLUSH_MSGS: 756 return false; 757 default: 758 widget = setGenericProperty(propertyId, value, output); 759 /* no break */ 760 } 761 } 762 if (widget == NULL) 763 return output; 764 try 765 { 766 CameraWidgetType type; 767 CR(gp_widget_get_type(widget, &type)); 768 switch (type) 769 { 770 case GP_WIDGET_RADIO: 771 case GP_WIDGET_MENU: 772 { 773 int i = static_cast<int>(value); 774 char *choice; 775 CR(gp_widget_get_choice(widget, i, (const char**)&choice)); 776 CR(gp_widget_set_value(widget, choice)); 777 break; 778 } 779 case GP_WIDGET_TOGGLE: 780 { 781 int i = static_cast<int>(value); 782 CR(gp_widget_set_value(widget, &i)); 783 break; 784 } 785 case GP_WIDGET_RANGE: 786 { 787 float v = static_cast<float>(value); 788 CR(gp_widget_set_value(widget, &v)); 789 break; 790 } 791 default: 792 { 793 CR(gp_widget_set_value(widget, (void* )(intptr_t )&value)); 794 break; 795 } 796 } 797 if (!reloadOnChange) 798 { 799 // force widget change 800 CR(gp_widget_set_changed(widget, 1)); 801 } 802 803 // Use the same locale setting as while getting rootWidget. 804 char * localeTmp = setlocale(LC_ALL, "C"); 805 CR(gp_camera_set_config(camera, rootWidget, context)); 806 setlocale(LC_ALL, localeTmp); 807 808 if (reloadOnChange) 809 { 810 reloadConfig(); 811 } else { 812 CR(gp_widget_set_changed(widget, 0)); 813 } 814 } 815 catch (GPhoto2Exception & e) 816 { 817 char buf[128] = ""; 818 sprintf(buf, "cannot set property: %d to %f", propertyId, value); 819 message(WARNING, (const char *) buf, e); 820 return false; 821 } 822 return true; 823 } 824 825 /** 826 * Capture image, and store file in @field grabbedFrames. 827 * Do not read a file. File will be deleted from camera automatically. 828 */ 829 bool DigitalCameraCapture::grabFrame() 830 { 831 CameraFilePath filePath; 832 CameraFile * file = NULL; 833 try 834 { 835 CR(gp_file_new(&file)); 836 837 if (preview) 838 { 839 CR(gp_camera_capture_preview(camera, file, context)); 840 } 841 else 842 { 843 // Capture an image 844 CR(gp_camera_capture(camera, GP_CAPTURE_IMAGE, &filePath, context)); 845 CR(gp_camera_file_get(camera, filePath.folder, filePath.name, GP_FILE_TYPE_NORMAL, 846 file, context)); 847 CR(gp_camera_file_delete(camera, filePath.folder, filePath.name, context)); 848 } 849 // State update 850 if (firstCapturedFrameTime == 0) 851 { 852 firstCapturedFrameTime = time(0); 853 } 854 capturedFrames++; 855 grabbedFrames.push_back(file); 856 } 857 catch (GPhoto2Exception & e) 858 { 859 if (file) 860 gp_file_unref(file); 861 message(WARNING, "cannot grab new frame", e); 862 return false; 863 } 864 return true; 865 } 866 867 /** 868 * Read stored file with image. 869 */ 870 bool DigitalCameraCapture::retrieveFrame(int, OutputArray outputFrame) 871 { 872 if (grabbedFrames.size() > 0) 873 { 874 CameraFile * file = grabbedFrames.front(); 875 grabbedFrames.pop_front(); 876 try 877 { 878 readFrameFromFile(file, outputFrame); 879 CR(gp_file_unref(file)); 880 } 881 catch (GPhoto2Exception & e) 882 { 883 message(WARNING, "cannot read file grabbed from device", e); 884 return false; 885 } 886 } 887 else 888 { 889 return false; 890 } 891 return true; 892 } 893 894 /** 895 * @return true if device exists 896 */ 897 bool DigitalCameraCapture::deviceExist(int index) const 898 { 899 return (numDevices > 0) && (index < numDevices); 900 } 901 902 /** 903 * @return device index if exists, otherwise -1 904 */ 905 int DigitalCameraCapture::findDevice(const char * deviceName) const 906 { 907 const char * model = 0; 908 try 909 { 910 if (deviceName != 0) 911 { 912 for (int i = 0; i < numDevices; ++i) 913 { 914 CR(gp_list_get_name(allDevices, i, &model)); 915 if (model != 0 && strstr(model, deviceName)) 916 { 917 return i; 918 } 919 } 920 } 921 } 922 catch (GPhoto2Exception & e) 923 { 924 ; // pass 925 } 926 return -1; 927 } 928 929 /** 930 * Load device settings. 931 */ 932 void DigitalCameraCapture::reloadConfig() throw (GPhoto2Exception) 933 { 934 std::ostringstream widgetInfoListStream; 935 936 if (rootWidget != NULL) 937 { 938 widgetInfo.clear(); 939 CR(gp_widget_unref(rootWidget)); 940 rootWidget = NULL; 941 widgets.clear(); 942 } 943 // Make sure, that all configs (getting setting) will use the same locale setting. 944 char * localeTmp = setlocale(LC_ALL, "C"); 945 CR(gp_camera_get_config(camera, &rootWidget, context)); 946 setlocale(LC_ALL, localeTmp); 947 widgetInfoListStream << "id,label,name,info,readonly,type,value," 948 << lineDelimiter; 949 noOfWidgets = collectWidgets(widgetInfoListStream, rootWidget) + 1; 950 widgetInfo = widgetInfoListStream.str(); 951 } 952 953 /** 954 * Get widget which was fetched in time of last call to @reloadConfig(). 955 */ 956 CameraWidget * DigitalCameraCapture::getWidget(int widgetId) const 957 { 958 CameraWidget * widget; 959 std::map<int, CameraWidget *>::const_iterator it = widgets.find(widgetId); 960 if (it == widgets.end()) 961 return 0; 962 widget = it->second; 963 return widget; 964 } 965 966 /** 967 * Search for widget with name which has @param subName substring. 968 */ 969 CameraWidget * DigitalCameraCapture::findWidgetByName( 970 const char * subName) const 971 { 972 if (subName != NULL) 973 { 974 try 975 { 976 const char * name; 977 typedef std::map<int, CameraWidget *>::const_iterator it_t; 978 it_t it = widgets.begin(), end = widgets.end(); 979 while (it != end) 980 { 981 CR(gp_widget_get_name(it->second, &name)); 982 if (strstr(name, subName)) 983 break; 984 it++; 985 } 986 return (it != end) ? it->second : NULL; 987 } 988 catch (GPhoto2Exception & e) 989 { 990 message(WARNING, "error while searching for widget", e); 991 } 992 } 993 return 0; 994 } 995 996 /** 997 * Image file reader. 998 * 999 * @FUTURE: RAW format reader. 1000 */ 1001 void DigitalCameraCapture::readFrameFromFile(CameraFile * file, OutputArray outputFrame) 1002 throw (GPhoto2Exception) 1003 { 1004 // FUTURE: OpenCV cannot read RAW files right now. 1005 const char * data; 1006 unsigned long int size; 1007 CR(gp_file_get_data_and_size(file, &data, &size)); 1008 if (size > 0) 1009 { 1010 Mat buf = Mat(1, size, CV_8UC1, (void *) data); 1011 if(!buf.empty()) 1012 { 1013 frame = imdecode(buf, CV_LOAD_IMAGE_UNCHANGED); 1014 } 1015 frame.copyTo(outputFrame); 1016 } 1017 } 1018 1019 /** 1020 * Print widget description in @param os. 1021 * @return real widget ID (if config was reloaded couple of times 1022 * then IDs won't be the same) 1023 */ 1024 int DigitalCameraCapture::widgetDescription(std::ostream &os, 1025 CameraWidget * widget) const throw (GPhoto2Exception) 1026 { 1027 const char * label, *name, *info; 1028 int id, readonly; 1029 CameraWidgetType type; 1030 1031 CR(gp_widget_get_id(widget, &id)); 1032 CR(gp_widget_get_label(widget, &label)); 1033 CR(gp_widget_get_name(widget, &name)); 1034 CR(gp_widget_get_info(widget, &info)); 1035 CR(gp_widget_get_type(widget, &type)); 1036 CR(gp_widget_get_readonly(widget, &readonly)); 1037 1038 if ((type == GP_WIDGET_WINDOW) || (type == GP_WIDGET_SECTION) 1039 || (type == GP_WIDGET_BUTTON)) 1040 { 1041 readonly = 1; 1042 } 1043 os << (id - noOfWidgets) << separator << label << separator << name 1044 << separator << info << separator << readonly << separator; 1045 1046 switch (type) 1047 { 1048 case GP_WIDGET_WINDOW: 1049 { 1050 os << "window" << separator /* no value */<< separator; 1051 break; 1052 } 1053 case GP_WIDGET_SECTION: 1054 { 1055 os << "section" << separator /* no value */<< separator; 1056 break; 1057 } 1058 case GP_WIDGET_TEXT: 1059 { 1060 os << "text" << separator; 1061 char *txt; 1062 CR(gp_widget_get_value(widget, &txt)); 1063 os << txt << separator; 1064 break; 1065 } 1066 case GP_WIDGET_RANGE: 1067 { 1068 os << "range" << separator; 1069 float f, t, b, s; 1070 CR(gp_widget_get_range(widget, &b, &t, &s)); 1071 CR(gp_widget_get_value(widget, &f)); 1072 os << "(" << b << ":" << t << ":" << s << "):" << f << separator; 1073 break; 1074 } 1075 case GP_WIDGET_TOGGLE: 1076 { 1077 os << "toggle" << separator; 1078 int t; 1079 CR(gp_widget_get_value(widget, &t)); 1080 os << t << separator; 1081 break; 1082 } 1083 case GP_WIDGET_RADIO: 1084 case GP_WIDGET_MENU: 1085 { 1086 if (type == GP_WIDGET_RADIO) 1087 { 1088 os << "radio" << separator; 1089 } 1090 else 1091 { 1092 os << "menu" << separator; 1093 } 1094 int cnt = 0, i; 1095 char *current; 1096 CR(gp_widget_get_value(widget, ¤t)); 1097 CR(cnt = gp_widget_count_choices(widget)); 1098 os << "("; 1099 for (i = 0; i < cnt; i++) 1100 { 1101 const char *choice; 1102 CR(gp_widget_get_choice(widget, i, &choice)); 1103 os << i << ":" << choice; 1104 if (i + 1 < cnt) 1105 { 1106 os << ";"; 1107 } 1108 } 1109 os << "):" << current << separator; 1110 break; 1111 } 1112 case GP_WIDGET_BUTTON: 1113 { 1114 os << "button" << separator /* no value */<< separator; 1115 break; 1116 } 1117 case GP_WIDGET_DATE: 1118 { 1119 os << "date" << separator; 1120 int t; 1121 time_t xtime; 1122 struct tm *xtm; 1123 char timebuf[200]; 1124 CR(gp_widget_get_value(widget, &t)); 1125 xtime = t; 1126 xtm = localtime(&xtime); 1127 strftime(timebuf, sizeof(timebuf), "%c", xtm); 1128 os << t << ":" << timebuf << separator; 1129 break; 1130 } 1131 } 1132 return id; 1133 } 1134 1135 /** 1136 * Write all widget descriptions to @param os. 1137 * @return maximum of widget ID 1138 */ 1139 int DigitalCameraCapture::collectWidgets(std::ostream & os, 1140 CameraWidget * widget) throw (GPhoto2Exception) 1141 { 1142 int id = widgetDescription(os, widget); 1143 os << lineDelimiter; 1144 1145 widgets[id - noOfWidgets] = widget; 1146 1147 CameraWidget * child; 1148 CameraWidgetType type; 1149 CR(gp_widget_get_type(widget, &type)); 1150 if ((type == GP_WIDGET_WINDOW) || (type == GP_WIDGET_SECTION)) 1151 { 1152 for (int x = 0; x < gp_widget_count_children(widget); x++) 1153 { 1154 CR(gp_widget_get_child(widget, x, &child)); 1155 id = std::max(id, collectWidgets(os, child)); 1156 } 1157 } 1158 return id; 1159 } 1160 1161 /** 1162 * Write message to @field msgsBuffer if user want to store them 1163 * (@field collectMsgs). 1164 * Print debug informations on screen. 1165 */ 1166 template<typename OsstreamPrintable> 1167 void DigitalCameraCapture::message(MsgType msgType, const char * msg, 1168 OsstreamPrintable & arg) const 1169 { 1170 #if defined(NDEBUG) 1171 if (collectMsgs) 1172 { 1173 #endif 1174 std::ostringstream msgCreator; 1175 std::string out; 1176 char type = (char) msgType; 1177 msgCreator << "[gPhoto2][" << type << "]: " << msg << ": " << arg 1178 << lineDelimiter; 1179 out = msgCreator.str(); 1180 #if !defined(NDEBUG) 1181 if (collectMsgs) 1182 { 1183 #endif 1184 msgsBuffer << out; 1185 } 1186 #if !defined(NDEBUG) 1187 #if defined(WIN32) || defined(_WIN32) 1188 ::OutputDebugString(out.c_str()); 1189 #else 1190 fputs(out.c_str(), stderr); 1191 #endif 1192 #endif 1193 } 1194 1195 } // namespace gphoto2 1196 1197 /** 1198 * \brief IVideoCapture creator form device index. 1199 */ 1200 Ptr<IVideoCapture> createGPhoto2Capture(int index) 1201 { 1202 Ptr<IVideoCapture> capture = makePtr<gphoto2::DigitalCameraCapture>(index); 1203 1204 if (capture->isOpened()) 1205 return capture; 1206 1207 return Ptr<gphoto2::DigitalCameraCapture>(); 1208 } 1209 1210 /** 1211 * IVideoCapture creator, from device name. 1212 * 1213 * @param deviceName is a substring in digital camera model name. 1214 */ 1215 Ptr<IVideoCapture> createGPhoto2Capture(const String & deviceName) 1216 { 1217 Ptr<IVideoCapture> capture = makePtr<gphoto2::DigitalCameraCapture>(deviceName); 1218 1219 if (capture->isOpened()) 1220 return capture; 1221 1222 return Ptr<gphoto2::DigitalCameraCapture>(); 1223 } 1224 1225 } // namespace cv 1226 1227 #endif 1228