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, ¤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 */
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, ¤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 */
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