Home | History | Annotate | Download | only in src
      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, &current));
    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, &current));
   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