• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
GPhoto2Exception(const char * methodStr,int gPhoto2Result)63     GPhoto2Exception(const char * methodStr, int gPhoto2Result)
64     {
65         result = gPhoto2Result;
66         method = methodStr;
67     }
what() const68     virtual const char * what() const throw ()
69     {
70         return gp_result_as_string(result);
71     }
operator <<(std::ostream & ostream,GPhoto2Exception & e)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);
getCaptureDomain()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  */
ctxErrorFunc(GPContext *,const char * str,void * thatGPhotoCap)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  */
ctxStatusFunc(GPContext *,const char * str,void * thatGPhotoCap)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  */
ctxMessageFunc(GPContext *,const char * str,void * thatGPhotoCap)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  */
initContext()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  */
DigitalCameraCapture()353 DigitalCameraCapture::DigitalCameraCapture()
354 {
355     initContext();
356 }
357 
358 /**
359  * @see open(int)
360  */
DigitalCameraCapture(int index)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  */
DigitalCameraCapture(const String & deviceName)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  */
~DigitalCameraCapture()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  */
open(int index)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  */
isOpened() const460 bool DigitalCameraCapture::isOpened() const
461 {
462     return opened;
463 }
464 
465 /**
466  * Close connection to the camera. Remove all unread frames/files.
467  */
close()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  */
getGenericProperty(int propertyId,double & output) const509 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  */
getProperty(int propertyId) const591 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  */
setGenericProperty(int propertyId,double,bool & output) const685 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  */
setProperty(int propertyId,double value)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  */
grabFrame()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  */
retrieveFrame(int,OutputArray outputFrame)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  */
deviceExist(int index) const897 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  */
findDevice(const char * deviceName) const905 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  */
reloadConfig()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  */
getWidget(int widgetId) const956 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  */
findWidgetByName(const char * subName) const969 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  */
readFrameFromFile(CameraFile * file,OutputArray outputFrame)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  */
widgetDescription(std::ostream & os,CameraWidget * widget) const1024 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  */
collectWidgets(std::ostream & os,CameraWidget * widget)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>
message(MsgType msgType,const char * msg,OsstreamPrintable & arg) const1167 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  */
createGPhoto2Capture(int index)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  */
createGPhoto2Capture(const String & deviceName)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