Path: blob/master/modules/videoio/src/cap_gphoto2.cpp
16339 views
/*1* Copyright (c) 2015, Piotr Dobrowolski dobrypd[at]gmail[dot]com2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without modification,5* are permitted provided that the following conditions are met:6*7* 1. Redistributions of source code must retain the above copyright notice,8* this list of conditions and the following disclaimer.9*10* 2. Redistributions in binary form must reproduce the above copyright notice,11* this list of conditions and the following disclaimer in the documentation12* and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED16* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,18* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES19* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;20* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED21* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,22* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF23* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*25*/2627#include "precomp.hpp"2829#ifdef HAVE_GPHOTO23031#include <gphoto2/gphoto2.h>3233#include <algorithm>34#include <clocale>35#include <cstdio>36#include <cstring>37#include <ctime>38#include <deque>39#include <exception>40#include <map>41#include <ostream>42#include <string>4344namespace cv45{4647namespace gphoto2 {4849/**50* \brief Map gPhoto2 return code into this exception.51*/52class GPhoto2Exception: public std::exception53{54private:55int result;56const char * method;57public:58/**59* @param methodStr libgphoto2 method name60* @param gPhoto2Result libgphoto2 method result, should be less than GP_OK61*/62GPhoto2Exception(const char * methodStr, int gPhoto2Result)63{64result = gPhoto2Result;65method = methodStr;66}67virtual const char * what() const throw() CV_OVERRIDE68{69return gp_result_as_string(result);70}71friend std::ostream & operator<<(std::ostream & ostream,72GPhoto2Exception & e)73{74return ostream << e.method << ": " << e.what();75}76};7778/**79* \brief Capture using your camera device via digital camera library - gPhoto2.80*81* For library description and list of supported cameras, go to82* @url http://gphoto.sourceforge.net/83*84* Because gPhoto2 configuration is based on a widgets85* and OpenCV CvCapture property settings are double typed86* some assumptions and tricks has to be made.87* 1. Device properties can be changed by IDs, use @method setProperty(int, double)88* and @method getProperty(int) with __additive inversed__89* camera setting ID as propertyId. (If you want to get camera setting90* with ID == x, you want to call #getProperty(-x)).91* 2. Digital camera settings IDs are device dependent.92* 3. You can list them by getting property CAP_PROP_GPHOTO2_WIDGET_ENUMERATE.93* 3.1. As return you will get pointer to char array (with listed properties)94* instead of double. This list is in CSV type.95* 4. There are several types of widgets (camera settings).96* 4.1. For "menu" and "radio", you can get/set choice number.97* 4.2. For "toggle" you can get/set int type.98* 4.3. For "range" you can get/set float.99* 4.4. For any other pointer will be fetched/set.100* 5. You can fetch camera messages by using CAP_PROP_GPHOTO2_COLLECT_MSGS101* and CAP_PROP_GPHOTO2_FLUSH_MSGS (will return pointer to char array).102* 6. Camera settings are fetched from device as lazy as possible.103* It creates problem with situation when change of one setting104* affects another setting. You can use CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE105* or CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG to be sure that property you are106* planning to get will be actual.107*108* Capture can work in 2 main modes: preview and final.109* Where preview is an output from digital camera "liveview".110* Change modes with CAP_PROP_GPHOTO2_PREVIEW property.111*112* Moreover some generic properties are mapped to widgets, or implemented:113* * CV_CAP_PROP_SPEED,114* * CV_CAP_PROP_APERATURE,115* * CV_CAP_PROP_EXPOSUREPROGRAM,116* * CV_CAP_PROP_VIEWFINDER,117* * CV_CAP_PROP_POS_MSEC,118* * CV_CAP_PROP_POS_FRAMES,119* * CV_CAP_PROP_FRAME_WIDTH,120* * CV_CAP_PROP_FRAME_HEIGHT,121* * CV_CAP_PROP_FPS,122* * CV_CAP_PROP_FRAME_COUNT123* * CV_CAP_PROP_FORMAT,124* * CV_CAP_PROP_EXPOSURE,125* * CV_CAP_PROP_TRIGGER_DELAY,126* * CV_CAP_PROP_ZOOM,127* * CV_CAP_PROP_FOCUS,128* * CV_CAP_PROP_ISO_SPEED.129*/130class DigitalCameraCapture: public IVideoCapture131{132public:133static const char * separator;134static const char * lineDelimiter;135136DigitalCameraCapture();137DigitalCameraCapture(int index);138DigitalCameraCapture(const String &deviceName);139virtual ~DigitalCameraCapture() CV_OVERRIDE;140141virtual bool isOpened() const CV_OVERRIDE;142virtual double getProperty(int) const CV_OVERRIDE;143virtual bool setProperty(int, double) CV_OVERRIDE;144virtual bool grabFrame() CV_OVERRIDE;145virtual bool retrieveFrame(int, OutputArray) CV_OVERRIDE;146virtual int getCaptureDomain() CV_OVERRIDE { return CV_CAP_GPHOTO2; }147148bool open(int index);149void close();150bool deviceExist(int index) const;151int findDevice(const char * deviceName) const;152153protected:154// Known widget names155static const char * PROP_EXPOSURE_COMPENSACTION;156static const char * PROP_SELF_TIMER_DELAY;157static const char * PROP_MANUALFOCUS;158static const char * PROP_AUTOFOCUS;159static const char * PROP_ISO;160static const char * PROP_SPEED;161static const char * PROP_APERTURE_NIKON;162static const char * PROP_APERTURE_CANON;163static const char * PROP_EXPOSURE_PROGRAM;164static const char * PROP_VIEWFINDER;165166// Instance167GPContext * context = NULL;168int numDevices;169void initContext();170171// Selected device172bool opened;173Camera * camera = NULL;174Mat frame;175176// Properties177CameraWidget * rootWidget = NULL;178CameraWidget * getGenericProperty(int propertyId, double & output) const;179CameraWidget * setGenericProperty(int propertyId, double value,180bool & output) const;181182// Widgets183void reloadConfig();184CameraWidget * getWidget(int widgetId) const;185CameraWidget * findWidgetByName(const char * name) const;186187// Loading188void readFrameFromFile(CameraFile * file, OutputArray outputFrame);189190// Context feedback191friend void ctxErrorFunc(GPContext *, const char *, void *);192friend void ctxStatusFunc(GPContext *, const char *, void *);193friend void ctxMessageFunc(GPContext *, const char *, void *);194195// Messages / debug196enum MsgType197{198ERROR = (int) 'E',199WARNING = (int) 'W',200STATUS = (int) 'S',201OTHER = (int) 'O'202};203template<typename OsstreamPrintable>204void message(MsgType msgType, const char * msg,205OsstreamPrintable & arg) const;206207private:208// Instance209CameraAbilitiesList * abilitiesList = NULL;210GPPortInfoList * capablePorts = NULL;211CameraList * allDevices = NULL;212213// Selected device214CameraAbilities cameraAbilities;215std::deque<CameraFile *> grabbedFrames;216217// Properties218bool preview; // CV_CAP_PROP_GPHOTO2_PREVIEW219std::string widgetInfo; // CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE220std::map<int, CameraWidget *> widgets;221bool reloadOnChange; // CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE222time_t firstCapturedFrameTime;223unsigned long int capturedFrames;224225DigitalCameraCapture(const DigitalCameraCapture&); // Disable copying226DigitalCameraCapture& operator=(DigitalCameraCapture const&); // Disable assigning227228// Widgets229int noOfWidgets;230int widgetDescription(std::ostream &os, CameraWidget * widget) const;231int collectWidgets(std::ostream &os, CameraWidget * widget);232233// Messages / debug234mutable std::ostringstream msgsBuffer; // CV_CAP_PROP_GPHOTO2_FLUSH_MSGS235mutable std::string lastFlush; // CV_CAP_PROP_GPHOTO2_FLUSH_MSGS236bool collectMsgs; // CV_CAP_PROP_GPHOTO2_COLLECT_MSGS237};238239/**240* \brief Check if gPhoto2 function ends successfully. If not, throw an exception.241*/242#define CR(GPHOTO2_FUN) do {\243int r_0629c47b758;\244if ((r_0629c47b758 = (GPHOTO2_FUN)) < GP_OK) {\245throw GPhoto2Exception(#GPHOTO2_FUN, r_0629c47b758);\246};\247} while(0)248249/**250* \brief gPhoto2 context error feedback function.251* @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object.252*/253void ctxErrorFunc(GPContext *, const char * str, void * thatGPhotoCap)254{255const DigitalCameraCapture * self =256(const DigitalCameraCapture *) thatGPhotoCap;257self->message(self->ERROR, "context feedback", str);258}259260/**261* \brief gPhoto2 context status feedback function.262* @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object.263*/264void ctxStatusFunc(GPContext *, const char * str, void * thatGPhotoCap)265{266const DigitalCameraCapture * self =267(const DigitalCameraCapture *) thatGPhotoCap;268self->message(self->STATUS, "context feedback", str);269}270271/**272* \brief gPhoto2 context message feedback function.273* @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object.274*/275void ctxMessageFunc(GPContext *, const char * str, void * thatGPhotoCap)276{277const DigitalCameraCapture * self =278(const DigitalCameraCapture *) thatGPhotoCap;279self->message(self->OTHER, "context feedback", str);280}281282/**283* \brief Separator used while creating CSV.284*/285const char * DigitalCameraCapture::separator = ",";286/**287* \brief Line delimiter used while creating any readable output.288*/289const char * DigitalCameraCapture::lineDelimiter = "\n";290/**291* \bief Some known widget names.292*293* Those are actually substrings of widget name.294* ie. for VIEWFINDER, Nikon uses "viewfinder", while Canon can use "eosviewfinder".295*/296const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSACTION =297"exposurecompensation";298const char * DigitalCameraCapture::PROP_SELF_TIMER_DELAY = "selftimerdelay";299const char * DigitalCameraCapture::PROP_MANUALFOCUS = "manualfocusdrive";300const char * DigitalCameraCapture::PROP_AUTOFOCUS = "autofocusdrive";301const char * DigitalCameraCapture::PROP_ISO = "iso";302const char * DigitalCameraCapture::PROP_SPEED = "shutterspeed";303const char * DigitalCameraCapture::PROP_APERTURE_NIKON = "f-number";304const char * DigitalCameraCapture::PROP_APERTURE_CANON = "aperture";305const char * DigitalCameraCapture::PROP_EXPOSURE_PROGRAM = "expprogram";306const char * DigitalCameraCapture::PROP_VIEWFINDER = "viewfinder";307308/**309* Initialize gPhoto2 context, search for all available devices.310*/311void DigitalCameraCapture::initContext()312{313capturedFrames = noOfWidgets = numDevices = 0;314opened = preview = reloadOnChange = false;315firstCapturedFrameTime = 0;316317context = gp_context_new();318319gp_context_set_error_func(context, ctxErrorFunc, (void*) this);320gp_context_set_status_func(context, ctxStatusFunc, (void*) this);321gp_context_set_message_func(context, ctxMessageFunc, (void*) this);322323try324{325// Load abilities326CR(gp_abilities_list_new(&abilitiesList));327CR(gp_abilities_list_load(abilitiesList, context));328329// Load ports330CR(gp_port_info_list_new(&capablePorts));331CR(gp_port_info_list_load(capablePorts));332333// Auto-detect devices334CR(gp_list_new(&allDevices));335CR(gp_camera_autodetect(allDevices, context));336CR(numDevices = gp_list_count(allDevices));337}338catch (GPhoto2Exception & e)339{340numDevices = 0;341}342}343344/**345* Search for all devices while constructing.346*/347DigitalCameraCapture::DigitalCameraCapture()348{349initContext();350}351352/**353* @see open(int)354*/355DigitalCameraCapture::DigitalCameraCapture(int index)356{357initContext();358if (deviceExist(index))359open(index);360}361362/**363* @see findDevice(const char*)364* @see open(int)365*/366DigitalCameraCapture::DigitalCameraCapture(const String & deviceName)367{368initContext();369int index = findDevice(deviceName.c_str());370if (deviceExist(index))371open(index);372}373374/**375* Always close connection to the device.376*/377DigitalCameraCapture::~DigitalCameraCapture()378{379close();380try381{382CR(gp_abilities_list_free(abilitiesList));383abilitiesList = NULL;384CR(gp_port_info_list_free(capablePorts));385capablePorts = NULL;386CR(gp_list_unref(allDevices));387allDevices = NULL;388gp_context_unref(context);389context = NULL;390}391catch (GPhoto2Exception & e)392{393message(ERROR, "destruction error", e);394}395}396397/**398* Connects to selected device.399*/400bool DigitalCameraCapture::open(int index)401{402const char * model = 0, *path = 0;403int m, p;404GPPortInfo portInfo;405406if (isOpened()) {407close();408}409410try411{412CR(gp_camera_new(&camera));413CR(gp_list_get_name(allDevices, index, &model));414CR(gp_list_get_value(allDevices, index, &path));415416// Set model abilities.417CR(m = gp_abilities_list_lookup_model(abilitiesList, model));418CR(gp_abilities_list_get_abilities(abilitiesList, m, &cameraAbilities));419CR(gp_camera_set_abilities(camera, cameraAbilities));420421// Set port422CR(p = gp_port_info_list_lookup_path(capablePorts, path));423CR(gp_port_info_list_get_info(capablePorts, p, &portInfo));424CR(gp_camera_set_port_info(camera, portInfo));425426// Initialize connection to the camera.427CR(gp_camera_init(camera, context));428429message(STATUS, "connected camera", model);430message(STATUS, "connected using", path);431432// State initialization433firstCapturedFrameTime = 0;434capturedFrames = 0;435preview = false;436reloadOnChange = false;437collectMsgs = false;438439reloadConfig();440441opened = true;442return true;443}444catch (GPhoto2Exception & e)445{446message(WARNING, "opening device failed", e);447return false;448}449}450451/**452*453*/454bool DigitalCameraCapture::isOpened() const455{456return opened;457}458459/**460* Close connection to the camera. Remove all unread frames/files.461*/462void DigitalCameraCapture::close()463{464try465{466if (!frame.empty())467{468frame.release();469}470if (camera)471{472CR(gp_camera_exit(camera, context));473CR(gp_camera_unref(camera));474camera = NULL;475}476opened = false;477if (int frames = grabbedFrames.size() > 0)478{479while (frames--)480{481CameraFile * file = grabbedFrames.front();482grabbedFrames.pop_front();483CR(gp_file_unref(file));484}485}486if (rootWidget)487{488widgetInfo.clear();489CR(gp_widget_unref(rootWidget));490rootWidget = NULL;491}492}493catch (GPhoto2Exception & e)494{495message(ERROR, "cannot close device properly", e);496}497}498499/**500* @param output will be changed if possible, return 0 if changed,501* @return widget, or NULL if output value was found (saved in argument),502*/503CameraWidget * DigitalCameraCapture::getGenericProperty(int propertyId,504double & output) const505{506switch (propertyId)507{508case CV_CAP_PROP_POS_MSEC:509{510// Only seconds level precision, FUTURE: cross-platform milliseconds511output = (time(0) - firstCapturedFrameTime) * 1e2;512return NULL;513}514case CV_CAP_PROP_POS_FRAMES:515{516output = capturedFrames;517return NULL;518}519case CV_CAP_PROP_FRAME_WIDTH:520{521if (!frame.empty())522{523output = frame.cols;524}525return NULL;526}527case CV_CAP_PROP_FRAME_HEIGHT:528{529if (!frame.empty())530{531output = frame.rows;532}533return NULL;534}535case CV_CAP_PROP_FORMAT:536{537if (!frame.empty())538{539output = frame.type();540}541return NULL;542}543case CV_CAP_PROP_FPS: // returns average fps from the begin544{545double wholeProcessTime = 0;546getGenericProperty(CV_CAP_PROP_POS_MSEC, wholeProcessTime);547wholeProcessTime /= 1e2;548output = capturedFrames / wholeProcessTime;549return NULL;550}551case CV_CAP_PROP_FRAME_COUNT:552{553output = capturedFrames;554return NULL;555}556case CV_CAP_PROP_EXPOSURE:557return findWidgetByName(PROP_EXPOSURE_COMPENSACTION);558case CV_CAP_PROP_TRIGGER_DELAY:559return findWidgetByName(PROP_SELF_TIMER_DELAY);560case CV_CAP_PROP_ZOOM:561return findWidgetByName(PROP_MANUALFOCUS);562case CV_CAP_PROP_FOCUS:563return findWidgetByName(PROP_AUTOFOCUS);564case CV_CAP_PROP_ISO_SPEED:565return findWidgetByName(PROP_ISO);566case CV_CAP_PROP_SPEED:567return findWidgetByName(PROP_SPEED);568case CV_CAP_PROP_APERTURE:569{570CameraWidget * widget = findWidgetByName(PROP_APERTURE_NIKON);571return (widget == 0) ? findWidgetByName(PROP_APERTURE_CANON) : widget;572}573case CV_CAP_PROP_EXPOSUREPROGRAM:574return findWidgetByName(PROP_EXPOSURE_PROGRAM);575case CV_CAP_PROP_VIEWFINDER:576return findWidgetByName(PROP_VIEWFINDER);577}578return NULL;579}580581/**582* Get property.583* @see DigitalCameraCapture for more information about returned double type.584*/585double DigitalCameraCapture::getProperty(int propertyId) const586{587CameraWidget * widget = NULL;588double output = 0;589if (propertyId < 0)590{591widget = getWidget(-propertyId);592}593else594{595switch (propertyId)596{597// gphoto2 cap featured598case CV_CAP_PROP_GPHOTO2_PREVIEW:599return preview;600case CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE:601if (rootWidget == NULL)602return 0;603return (intptr_t) widgetInfo.c_str();604case CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG:605return 0; // Trigger, only by set606case CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE:607return reloadOnChange;608case CV_CAP_PROP_GPHOTO2_COLLECT_MSGS:609return collectMsgs;610case CV_CAP_PROP_GPHOTO2_FLUSH_MSGS:611lastFlush = msgsBuffer.str();612msgsBuffer.str("");613msgsBuffer.clear();614return (intptr_t) lastFlush.c_str();615default:616widget = getGenericProperty(propertyId, output);617/* no break */618}619}620if (widget == NULL)621return output;622try623{624CameraWidgetType type;625CR(gp_widget_get_type(widget, &type));626switch (type)627{628case GP_WIDGET_MENU:629case GP_WIDGET_RADIO:630{631int cnt = 0, i;632const char * current;633CR(gp_widget_get_value(widget, ¤t));634CR(cnt = gp_widget_count_choices(widget));635for (i = 0; i < cnt; i++)636{637const char *choice;638CR(gp_widget_get_choice(widget, i, &choice));639if (std::strcmp(choice, current) == 0)640{641return i;642}643}644return -1;645}646case GP_WIDGET_TOGGLE:647{648int value;649CR(gp_widget_get_value(widget, &value));650return value;651}652case GP_WIDGET_RANGE:653{654float value;655CR(gp_widget_get_value(widget, &value));656return value;657}658default:659{660char* value;661CR(gp_widget_get_value(widget, &value));662return (intptr_t) value;663}664}665}666catch (GPhoto2Exception & e)667{668char buf[128] = "";669sprintf(buf, "cannot get property: %d", propertyId);670message(WARNING, (const char *) buf, e);671return 0;672}673}674675/**676* @param output will be changed if possible, return 0 if changed,677* @return widget, or 0 if output value was found (saved in argument),678*/679CameraWidget * DigitalCameraCapture::setGenericProperty(int propertyId,680double /*FUTURE: value*/, bool & output) const681{682switch (propertyId)683{684case CV_CAP_PROP_POS_MSEC:685case CV_CAP_PROP_POS_FRAMES:686case CV_CAP_PROP_FRAME_WIDTH:687case CV_CAP_PROP_FRAME_HEIGHT:688case CV_CAP_PROP_FPS:689case CV_CAP_PROP_FRAME_COUNT:690case CV_CAP_PROP_FORMAT:691output = false;692return NULL;693case CV_CAP_PROP_EXPOSURE:694return findWidgetByName(PROP_EXPOSURE_COMPENSACTION);695case CV_CAP_PROP_TRIGGER_DELAY:696return findWidgetByName(PROP_SELF_TIMER_DELAY);697case CV_CAP_PROP_ZOOM:698return findWidgetByName(PROP_MANUALFOCUS);699case CV_CAP_PROP_FOCUS:700return findWidgetByName(PROP_AUTOFOCUS);701case CV_CAP_PROP_ISO_SPEED:702return findWidgetByName(PROP_ISO);703case CV_CAP_PROP_SPEED:704return findWidgetByName(PROP_SPEED);705case CV_CAP_PROP_APERTURE:706{707CameraWidget * widget = findWidgetByName(PROP_APERTURE_NIKON);708return (widget == NULL) ? findWidgetByName(PROP_APERTURE_CANON) : widget;709}710case CV_CAP_PROP_EXPOSUREPROGRAM:711return findWidgetByName(PROP_EXPOSURE_PROGRAM);712case CV_CAP_PROP_VIEWFINDER:713return findWidgetByName(PROP_VIEWFINDER);714}715return NULL;716}717718/**719* Set property.720* @see DigitalCameraCapture for more information about value, double typed, argument.721*/722bool DigitalCameraCapture::setProperty(int propertyId, double value)723{724CameraWidget * widget = NULL;725bool output = false;726if (propertyId < 0)727{728widget = getWidget(-propertyId);729}730else731{732switch (propertyId)733{734// gphoto2 cap featured735case CV_CAP_PROP_GPHOTO2_PREVIEW:736preview = value != 0;737return true;738case CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE:739return false;740case CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG:741reloadConfig();742return true;743case CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE:744reloadOnChange = value != 0;745return true;746case CV_CAP_PROP_GPHOTO2_COLLECT_MSGS:747collectMsgs = value != 0;748return true;749case CV_CAP_PROP_GPHOTO2_FLUSH_MSGS:750return false;751default:752widget = setGenericProperty(propertyId, value, output);753/* no break */754}755}756if (widget == NULL)757return output;758try759{760CameraWidgetType type;761CR(gp_widget_get_type(widget, &type));762switch (type)763{764case GP_WIDGET_RADIO:765case GP_WIDGET_MENU:766{767int i = static_cast<int>(value);768char *choice;769CR(gp_widget_get_choice(widget, i, (const char**)&choice));770CR(gp_widget_set_value(widget, choice));771break;772}773case GP_WIDGET_TOGGLE:774{775int i = static_cast<int>(value);776CR(gp_widget_set_value(widget, &i));777break;778}779case GP_WIDGET_RANGE:780{781float v = static_cast<float>(value);782CR(gp_widget_set_value(widget, &v));783break;784}785default:786{787CR(gp_widget_set_value(widget, (void* )(intptr_t )&value));788break;789}790}791if (!reloadOnChange)792{793// force widget change794CR(gp_widget_set_changed(widget, 1));795}796797// Use the same locale setting as while getting rootWidget.798char * localeTmp = setlocale(LC_ALL, "C");799CR(gp_camera_set_config(camera, rootWidget, context));800setlocale(LC_ALL, localeTmp);801802if (reloadOnChange)803{804reloadConfig();805} else {806CR(gp_widget_set_changed(widget, 0));807}808}809catch (GPhoto2Exception & e)810{811char buf[128] = "";812sprintf(buf, "cannot set property: %d to %f", propertyId, value);813message(WARNING, (const char *) buf, e);814return false;815}816return true;817}818819/**820* Capture image, and store file in @field grabbedFrames.821* Do not read a file. File will be deleted from camera automatically.822*/823bool DigitalCameraCapture::grabFrame()824{825CameraFilePath filePath;826CameraFile * file = NULL;827try828{829CR(gp_file_new(&file));830831if (preview)832{833CR(gp_camera_capture_preview(camera, file, context));834}835else836{837// Capture an image838CR(gp_camera_capture(camera, GP_CAPTURE_IMAGE, &filePath, context));839CR(gp_camera_file_get(camera, filePath.folder, filePath.name, GP_FILE_TYPE_NORMAL,840file, context));841CR(gp_camera_file_delete(camera, filePath.folder, filePath.name, context));842}843// State update844if (firstCapturedFrameTime == 0)845{846firstCapturedFrameTime = time(0);847}848capturedFrames++;849grabbedFrames.push_back(file);850}851catch (GPhoto2Exception & e)852{853if (file)854gp_file_unref(file);855message(WARNING, "cannot grab new frame", e);856return false;857}858return true;859}860861/**862* Read stored file with image.863*/864bool DigitalCameraCapture::retrieveFrame(int, OutputArray outputFrame)865{866if (grabbedFrames.size() > 0)867{868CameraFile * file = grabbedFrames.front();869grabbedFrames.pop_front();870try871{872readFrameFromFile(file, outputFrame);873CR(gp_file_unref(file));874}875catch (GPhoto2Exception & e)876{877message(WARNING, "cannot read file grabbed from device", e);878return false;879}880}881else882{883return false;884}885return true;886}887888/**889* @return true if device exists890*/891bool DigitalCameraCapture::deviceExist(int index) const892{893return (numDevices > 0) && (index < numDevices);894}895896/**897* @return device index if exists, otherwise -1898*/899int DigitalCameraCapture::findDevice(const char * deviceName) const900{901const char * model = 0;902try903{904if (deviceName != 0)905{906for (int i = 0; i < numDevices; ++i)907{908CR(gp_list_get_name(allDevices, i, &model));909if (model != 0 && strstr(model, deviceName))910{911return i;912}913}914}915}916catch (GPhoto2Exception & e)917{918; // pass919}920return -1;921}922923/**924* Load device settings.925*/926void DigitalCameraCapture::reloadConfig()927{928std::ostringstream widgetInfoListStream;929930if (rootWidget != NULL)931{932widgetInfo.clear();933CR(gp_widget_unref(rootWidget));934rootWidget = NULL;935widgets.clear();936}937// Make sure, that all configs (getting setting) will use the same locale setting.938char * localeTmp = setlocale(LC_ALL, "C");939CR(gp_camera_get_config(camera, &rootWidget, context));940setlocale(LC_ALL, localeTmp);941widgetInfoListStream << "id,label,name,info,readonly,type,value,"942<< lineDelimiter;943noOfWidgets = collectWidgets(widgetInfoListStream, rootWidget) + 1;944widgetInfo = widgetInfoListStream.str();945}946947/**948* Get widget which was fetched in time of last call to @reloadConfig().949*/950CameraWidget * DigitalCameraCapture::getWidget(int widgetId) const951{952CameraWidget * widget;953std::map<int, CameraWidget *>::const_iterator it = widgets.find(widgetId);954if (it == widgets.end())955return 0;956widget = it->second;957return widget;958}959960/**961* Search for widget with name which has @param subName substring.962*/963CameraWidget * DigitalCameraCapture::findWidgetByName(964const char * subName) const965{966if (subName != NULL)967{968try969{970const char * name;971typedef std::map<int, CameraWidget *>::const_iterator it_t;972it_t it = widgets.begin(), end = widgets.end();973while (it != end)974{975CR(gp_widget_get_name(it->second, &name));976if (strstr(name, subName))977break;978++it;979}980return (it != end) ? it->second : NULL;981}982catch (GPhoto2Exception & e)983{984message(WARNING, "error while searching for widget", e);985}986}987return 0;988}989990/**991* Image file reader.992*993* @FUTURE: RAW format reader.994*/995void DigitalCameraCapture::readFrameFromFile(CameraFile * file, OutputArray outputFrame)996997{998// FUTURE: OpenCV cannot read RAW files right now.999const char * data;1000unsigned long int size;1001CR(gp_file_get_data_and_size(file, &data, &size));1002if (size > 0)1003{1004Mat buf = Mat(1, size, CV_8UC1, (void *) data);1005if(!buf.empty())1006{1007frame = imdecode(buf, CV_LOAD_IMAGE_UNCHANGED);1008}1009frame.copyTo(outputFrame);1010}1011}10121013/**1014* Print widget description in @param os.1015* @return real widget ID (if config was reloaded couple of times1016* then IDs won't be the same)1017*/1018int DigitalCameraCapture::widgetDescription(std::ostream &os,1019CameraWidget * widget) const1020{1021const char * label, *name, *info;1022int id, readonly;1023CameraWidgetType type;10241025CR(gp_widget_get_id(widget, &id));1026CR(gp_widget_get_label(widget, &label));1027CR(gp_widget_get_name(widget, &name));1028CR(gp_widget_get_info(widget, &info));1029CR(gp_widget_get_type(widget, &type));1030CR(gp_widget_get_readonly(widget, &readonly));10311032if ((type == GP_WIDGET_WINDOW) || (type == GP_WIDGET_SECTION)1033|| (type == GP_WIDGET_BUTTON))1034{1035readonly = 1;1036}1037os << (id - noOfWidgets) << separator << label << separator << name1038<< separator << info << separator << readonly << separator;10391040switch (type)1041{1042case GP_WIDGET_WINDOW:1043{1044os << "window" << separator /* no value */<< separator;1045break;1046}1047case GP_WIDGET_SECTION:1048{1049os << "section" << separator /* no value */<< separator;1050break;1051}1052case GP_WIDGET_TEXT:1053{1054os << "text" << separator;1055char *txt;1056CR(gp_widget_get_value(widget, &txt));1057os << txt << separator;1058break;1059}1060case GP_WIDGET_RANGE:1061{1062os << "range" << separator;1063float f, t, b, s;1064CR(gp_widget_get_range(widget, &b, &t, &s));1065CR(gp_widget_get_value(widget, &f));1066os << "(" << b << ":" << t << ":" << s << "):" << f << separator;1067break;1068}1069case GP_WIDGET_TOGGLE:1070{1071os << "toggle" << separator;1072int t;1073CR(gp_widget_get_value(widget, &t));1074os << t << separator;1075break;1076}1077case GP_WIDGET_RADIO:1078case GP_WIDGET_MENU:1079{1080if (type == GP_WIDGET_RADIO)1081{1082os << "radio" << separator;1083}1084else1085{1086os << "menu" << separator;1087}1088int cnt = 0, i;1089char *current;1090CR(gp_widget_get_value(widget, ¤t));1091CR(cnt = gp_widget_count_choices(widget));1092os << "(";1093for (i = 0; i < cnt; i++)1094{1095const char *choice;1096CR(gp_widget_get_choice(widget, i, &choice));1097os << i << ":" << choice;1098if (i + 1 < cnt)1099{1100os << ";";1101}1102}1103os << "):" << current << separator;1104break;1105}1106case GP_WIDGET_BUTTON:1107{1108os << "button" << separator /* no value */<< separator;1109break;1110}1111case GP_WIDGET_DATE:1112{1113os << "date" << separator;1114int t;1115time_t xtime;1116struct tm *xtm;1117char timebuf[200];1118CR(gp_widget_get_value(widget, &t));1119xtime = t;1120xtm = localtime(&xtime);1121strftime(timebuf, sizeof(timebuf), "%c", xtm);1122os << t << ":" << timebuf << separator;1123break;1124}1125}1126return id;1127}11281129/**1130* Write all widget descriptions to @param os.1131* @return maximum of widget ID1132*/1133int DigitalCameraCapture::collectWidgets(std::ostream & os,1134CameraWidget * widget)1135{1136int id = widgetDescription(os, widget);1137os << lineDelimiter;11381139widgets[id - noOfWidgets] = widget;11401141CameraWidget * child;1142CameraWidgetType type;1143CR(gp_widget_get_type(widget, &type));1144if ((type == GP_WIDGET_WINDOW) || (type == GP_WIDGET_SECTION))1145{1146for (int x = 0; x < gp_widget_count_children(widget); x++)1147{1148CR(gp_widget_get_child(widget, x, &child));1149id = std::max(id, collectWidgets(os, child));1150}1151}1152return id;1153}11541155/**1156* Write message to @field msgsBuffer if user want to store them1157* (@field collectMsgs).1158* Print debug information on screen.1159*/1160template<typename OsstreamPrintable>1161void DigitalCameraCapture::message(MsgType msgType, const char * msg,1162OsstreamPrintable & arg) const1163{1164#if defined(NDEBUG)1165if (collectMsgs)1166{1167#endif1168std::ostringstream msgCreator;1169std::string out;1170char type = (char) msgType;1171msgCreator << "[gPhoto2][" << type << "]: " << msg << ": " << arg1172<< lineDelimiter;1173out = msgCreator.str();1174#if !defined(NDEBUG)1175if (collectMsgs)1176{1177#endif1178msgsBuffer << out;1179}1180#if !defined(NDEBUG)1181#if defined(_WIN32)1182::OutputDebugString(out.c_str());1183#else1184fputs(out.c_str(), stderr);1185#endif1186#endif1187}11881189} // namespace gphoto211901191/**1192* \brief IVideoCapture creator form device index.1193*/1194Ptr<IVideoCapture> createGPhoto2Capture(int index)1195{1196Ptr<IVideoCapture> capture = makePtr<gphoto2::DigitalCameraCapture>(index);11971198if (capture->isOpened())1199return capture;12001201return Ptr<gphoto2::DigitalCameraCapture>();1202}12031204/**1205* IVideoCapture creator, from device name.1206*1207* @param deviceName is a substring in digital camera model name.1208*/1209Ptr<IVideoCapture> createGPhoto2Capture(const String & deviceName)1210{1211Ptr<IVideoCapture> capture = makePtr<gphoto2::DigitalCameraCapture>(deviceName);12121213if (capture->isOpened())1214return capture;12151216return Ptr<gphoto2::DigitalCameraCapture>();1217}12181219} // namespace cv12201221#endif122212231224