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