/* * Copyright (c) 2015, Piotr Dobrowolski dobrypd[at]gmail[dot]com * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "precomp.hpp" #ifdef HAVE_GPHOTO2 #include #include #include #include #include #include #include #include #include #include #include namespace cv { namespace gphoto2 { /** * \brief Map gPhoto2 return code into this exception. */ class GPhoto2Exception: public std::exception { private: int result; const char * method; public: /** * @param methodStr libgphoto2 method name * @param gPhoto2Result libgphoto2 method result, should be less than GP_OK */ GPhoto2Exception(const char * methodStr, int gPhoto2Result) { result = gPhoto2Result; method = methodStr; } virtual const char * what() const throw () { return gp_result_as_string(result); } friend std::ostream & operator<<(std::ostream & ostream, GPhoto2Exception & e) { return ostream << e.method << ": " << e.what(); } }; /** * \brief Capture using your camera device via digital camera library - gPhoto2. * * For library description and list of supported cameras, go to * @url http://gphoto.sourceforge.net/ * * Because gPhoto2 configuration is based on a widgets * and OpenCV CvCapture property settings are double typed * some assumptions and tricks has to be made. * 1. Device properties can be changed by IDs, use @method setProperty(int, double) * and @method getProperty(int) with __additive inversed__ * camera setting ID as propertyId. (If you want to get camera setting * with ID == x, you want to call #getProperty(-x)). * 2. Digital camera settings IDs are device dependent. * 3. You can list them by getting property CAP_PROP_GPHOTO2_WIDGET_ENUMERATE. * 3.1. As return you will get pointer to char array (with listed properties) * instead of double. This list is in CSV type. * 4. There are several types of widgets (camera settings). * 4.1. For "menu" and "radio", you can get/set choice number. * 4.2. For "toggle" you can get/set int type. * 4.3. For "range" you can get/set float. * 4.4. For any other pointer will be fetched/set. * 5. You can fetch camera messages by using CAP_PROP_GPHOTO2_COLLECT_MSGS * and CAP_PROP_GPHOTO2_FLUSH_MSGS (will return pointer to char array). * 6. Camera settings are fetched from device as lazy as possible. * It creates problem with situation when change of one setting * affects another setting. You can use CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE * or CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG to be sure that property you are * planning to get will be actual. * * Capture can work in 2 main modes: preview and final. * Where preview is an output from digital camera "liveview". * Change modes with CAP_PROP_GPHOTO2_PREVIEW property. * * Moreover some generic properties are mapped to widgets, or implemented: * * CV_CAP_PROP_SPEED, * * CV_CAP_PROP_APERATURE, * * CV_CAP_PROP_EXPOSUREPROGRAM, * * CV_CAP_PROP_VIEWFINDER, * * CV_CAP_PROP_POS_MSEC, * * CV_CAP_PROP_POS_FRAMES, * * CV_CAP_PROP_FRAME_WIDTH, * * CV_CAP_PROP_FRAME_HEIGHT, * * CV_CAP_PROP_FPS, * * CV_CAP_PROP_FRAME_COUNT * * CV_CAP_PROP_FORMAT, * * CV_CAP_PROP_EXPOSURE, * * CV_CAP_PROP_TRIGGER_DELAY, * * CV_CAP_PROP_ZOOM, * * CV_CAP_PROP_FOCUS, * * CV_CAP_PROP_ISO_SPEED. */ class DigitalCameraCapture: public IVideoCapture { public: static const char * separator; static const char * lineDelimiter; DigitalCameraCapture(); DigitalCameraCapture(int index); DigitalCameraCapture(const String &deviceName); virtual ~DigitalCameraCapture(); virtual bool isOpened() const; virtual double getProperty(int) const; virtual bool setProperty(int, double); virtual bool grabFrame(); virtual bool retrieveFrame(int, OutputArray); virtual int getCaptureDomain() { return CV_CAP_GPHOTO2; } // Return the type of the capture object: CV_CAP_VFW, etc... bool open(int index); void close(); bool deviceExist(int index) const; int findDevice(const char * deviceName) const; protected: // Known widget names static const char * PROP_EXPOSURE_COMPENSACTION; static const char * PROP_SELF_TIMER_DELAY; static const char * PROP_MANUALFOCUS; static const char * PROP_AUTOFOCUS; static const char * PROP_ISO; static const char * PROP_SPEED; static const char * PROP_APERTURE_NIKON; static const char * PROP_APERTURE_CANON; static const char * PROP_EXPOSURE_PROGRAM; static const char * PROP_VIEWFINDER; // Instance GPContext * context = NULL; int numDevices; void initContext(); // Selected device bool opened; Camera * camera = NULL; Mat frame; // Properties CameraWidget * rootWidget = NULL; CameraWidget * getGenericProperty(int propertyId, double & output) const; CameraWidget * setGenericProperty(int propertyId, double value, bool & output) const; // Widgets void reloadConfig() throw (GPhoto2Exception); CameraWidget * getWidget(int widgetId) const; CameraWidget * findWidgetByName(const char * name) const; // Loading void readFrameFromFile(CameraFile * file, OutputArray outputFrame) throw (GPhoto2Exception); // Context feedback friend void ctxErrorFunc(GPContext *, const char *, void *); friend void ctxStatusFunc(GPContext *, const char *, void *); friend void ctxMessageFunc(GPContext *, const char *, void *); // Messages / debug enum MsgType { ERROR = (int) 'E', WARNING = (int) 'W', STATUS = (int) 'S', OTHER = (int) 'O' }; template void message(MsgType msgType, const char * msg, OsstreamPrintable & arg) const; private: // Instance CameraAbilitiesList * abilitiesList = NULL; GPPortInfoList * capablePorts = NULL; CameraList * allDevices = NULL; // Selected device CameraAbilities cameraAbilities; std::deque grabbedFrames; // Properties bool preview; // CV_CAP_PROP_GPHOTO2_PREVIEW std::string widgetInfo; // CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE std::map widgets; bool reloadOnChange; // CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE time_t firstCapturedFrameTime; unsigned long int capturedFrames; DigitalCameraCapture(const DigitalCameraCapture&); // Disable copying DigitalCameraCapture& operator=(DigitalCameraCapture const&); // Disable assigning // Widgets int noOfWidgets; int widgetDescription(std::ostream &os, CameraWidget * widget) const throw (GPhoto2Exception); int collectWidgets(std::ostream &os, CameraWidget * widget) throw (GPhoto2Exception); // Messages / debug mutable std::ostringstream msgsBuffer; // CV_CAP_PROP_GPHOTO2_FLUSH_MSGS mutable std::string lastFlush; // CV_CAP_PROP_GPHOTO2_FLUSH_MSGS bool collectMsgs; // CV_CAP_PROP_GPHOTO2_COLLECT_MSGS }; /** * \brief Check if gPhoto2 function ends successfully. If not, throw an exception. */ #define CR(GPHOTO2_FUN) do {\ int r_0629c47b758;\ if ((r_0629c47b758 = (GPHOTO2_FUN)) < GP_OK) {\ throw GPhoto2Exception(#GPHOTO2_FUN, r_0629c47b758);\ };\ } while(0) /** * \brief gPhoto2 context error feedback function. * @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object. */ void ctxErrorFunc(GPContext *, const char * str, void * thatGPhotoCap) { const DigitalCameraCapture * self = (const DigitalCameraCapture *) thatGPhotoCap; self->message(self->ERROR, "context feedback", str); } /** * \brief gPhoto2 context status feedback function. * @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object. */ void ctxStatusFunc(GPContext *, const char * str, void * thatGPhotoCap) { const DigitalCameraCapture * self = (const DigitalCameraCapture *) thatGPhotoCap; self->message(self->STATUS, "context feedback", str); } /** * \brief gPhoto2 context message feedback function. * @param thatGPhotoCap is required to be pointer to DigitalCameraCapture object. */ void ctxMessageFunc(GPContext *, const char * str, void * thatGPhotoCap) { const DigitalCameraCapture * self = (const DigitalCameraCapture *) thatGPhotoCap; self->message(self->OTHER, "context feedback", str); } /** * \brief Separator used while creating CSV. */ const char * DigitalCameraCapture::separator = ","; /** * \brief Line delimiter used while creating any readable output. */ const char * DigitalCameraCapture::lineDelimiter = "\n"; /** * \bief Some known widget names. * * Those are actually substrings of widget name. * ie. for VIEWFINDER, Nikon uses "viewfinder", while Canon can use "eosviewfinder". */ const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSACTION = "exposurecompensation"; const char * DigitalCameraCapture::PROP_SELF_TIMER_DELAY = "selftimerdelay"; const char * DigitalCameraCapture::PROP_MANUALFOCUS = "manualfocusdrive"; const char * DigitalCameraCapture::PROP_AUTOFOCUS = "autofocusdrive"; const char * DigitalCameraCapture::PROP_ISO = "iso"; const char * DigitalCameraCapture::PROP_SPEED = "shutterspeed"; const char * DigitalCameraCapture::PROP_APERTURE_NIKON = "f-number"; const char * DigitalCameraCapture::PROP_APERTURE_CANON = "aperture"; const char * DigitalCameraCapture::PROP_EXPOSURE_PROGRAM = "expprogram"; const char * DigitalCameraCapture::PROP_VIEWFINDER = "viewfinder"; /** * Initialize gPhoto2 context, search for all available devices. */ void DigitalCameraCapture::initContext() { capturedFrames = noOfWidgets = numDevices = 0; opened = preview = reloadOnChange = false; firstCapturedFrameTime = 0; context = gp_context_new(); gp_context_set_error_func(context, ctxErrorFunc, (void*) this); gp_context_set_status_func(context, ctxStatusFunc, (void*) this); gp_context_set_message_func(context, ctxMessageFunc, (void*) this); try { // Load abilities CR(gp_abilities_list_new(&abilitiesList)); CR(gp_abilities_list_load(abilitiesList, context)); // Load ports CR(gp_port_info_list_new(&capablePorts)); CR(gp_port_info_list_load(capablePorts)); // Auto-detect devices CR(gp_list_new(&allDevices)); CR(gp_camera_autodetect(allDevices, context)); CR(numDevices = gp_list_count(allDevices)); } catch (GPhoto2Exception & e) { numDevices = 0; } } /** * Search for all devices while constructing. */ DigitalCameraCapture::DigitalCameraCapture() { initContext(); } /** * @see open(int) */ DigitalCameraCapture::DigitalCameraCapture(int index) { initContext(); if (deviceExist(index)) open(index); } /** * @see findDevice(const char*) * @see open(int) */ DigitalCameraCapture::DigitalCameraCapture(const String & deviceName) { initContext(); int index = findDevice(deviceName.c_str()); if (deviceExist(index)) open(index); } /** * Always close connection to the device. */ DigitalCameraCapture::~DigitalCameraCapture() { close(); try { CR(gp_abilities_list_free(abilitiesList)); abilitiesList = NULL; CR(gp_port_info_list_free(capablePorts)); capablePorts = NULL; CR(gp_list_unref(allDevices)); allDevices = NULL; gp_context_unref(context); context = NULL; } catch (GPhoto2Exception & e) { message(ERROR, "destruction error", e); } } /** * Connects to selected device. */ bool DigitalCameraCapture::open(int index) { const char * model = 0, *path = 0; int m, p; GPPortInfo portInfo; if (isOpened()) { close(); } try { CR(gp_camera_new(&camera)); CR(gp_list_get_name(allDevices, index, &model)); CR(gp_list_get_value(allDevices, index, &path)); // Set model abilities. CR(m = gp_abilities_list_lookup_model(abilitiesList, model)); CR(gp_abilities_list_get_abilities(abilitiesList, m, &cameraAbilities)); CR(gp_camera_set_abilities(camera, cameraAbilities)); // Set port CR(p = gp_port_info_list_lookup_path(capablePorts, path)); CR(gp_port_info_list_get_info(capablePorts, p, &portInfo)); CR(gp_camera_set_port_info(camera, portInfo)); // Initialize connection to the camera. CR(gp_camera_init(camera, context)); message(STATUS, "connected camera", model); message(STATUS, "connected using", path); // State initialization firstCapturedFrameTime = 0; capturedFrames = 0; preview = false; reloadOnChange = false; collectMsgs = false; reloadConfig(); opened = true; return true; } catch (GPhoto2Exception & e) { message(WARNING, "opening device failed", e); return false; } } /** * */ bool DigitalCameraCapture::isOpened() const { return opened; } /** * Close connection to the camera. Remove all unread frames/files. */ void DigitalCameraCapture::close() { try { if (!frame.empty()) { frame.release(); } if (camera) { CR(gp_camera_exit(camera, context)); CR(gp_camera_unref(camera)); camera = NULL; } opened = false; if (int frames = grabbedFrames.size() > 0) { while (frames--) { CameraFile * file = grabbedFrames.front(); grabbedFrames.pop_front(); CR(gp_file_unref(file)); } } if (rootWidget) { widgetInfo.clear(); CR(gp_widget_unref(rootWidget)); rootWidget = NULL; } } catch (GPhoto2Exception & e) { message(ERROR, "cannot close device properly", e); } } /** * @param output will be changed if possible, return 0 if changed, * @return widget, or NULL if output value was found (saved in argument), */ CameraWidget * DigitalCameraCapture::getGenericProperty(int propertyId, double & output) const { switch (propertyId) { case CV_CAP_PROP_POS_MSEC: { // Only seconds level precision, FUTURE: cross-platform milliseconds output = (time(0) - firstCapturedFrameTime) * 1e2; return NULL; } case CV_CAP_PROP_POS_FRAMES: { output = capturedFrames; return NULL; } case CV_CAP_PROP_FRAME_WIDTH: { if (!frame.empty()) { output = frame.cols; } return NULL; } case CV_CAP_PROP_FRAME_HEIGHT: { if (!frame.empty()) { output = frame.rows; } return NULL; } case CV_CAP_PROP_FORMAT: { if (!frame.empty()) { output = frame.type(); } return NULL; } case CV_CAP_PROP_FPS: // returns average fps from the begin { double wholeProcessTime = 0; getGenericProperty(CV_CAP_PROP_POS_MSEC, wholeProcessTime); wholeProcessTime /= 1e2; output = capturedFrames / wholeProcessTime; return NULL; } case CV_CAP_PROP_FRAME_COUNT: { output = capturedFrames; return NULL; } case CV_CAP_PROP_EXPOSURE: return findWidgetByName(PROP_EXPOSURE_COMPENSACTION); case CV_CAP_PROP_TRIGGER_DELAY: return findWidgetByName(PROP_SELF_TIMER_DELAY); case CV_CAP_PROP_ZOOM: return findWidgetByName(PROP_MANUALFOCUS); case CV_CAP_PROP_FOCUS: return findWidgetByName(PROP_AUTOFOCUS); case CV_CAP_PROP_ISO_SPEED: return findWidgetByName(PROP_ISO); case CV_CAP_PROP_SPEED: return findWidgetByName(PROP_SPEED); case CV_CAP_PROP_APERTURE: { CameraWidget * widget = findWidgetByName(PROP_APERTURE_NIKON); return (widget == 0) ? findWidgetByName(PROP_APERTURE_CANON) : widget; } case CV_CAP_PROP_EXPOSUREPROGRAM: return findWidgetByName(PROP_EXPOSURE_PROGRAM); case CV_CAP_PROP_VIEWFINDER: return findWidgetByName(PROP_VIEWFINDER); } return NULL; } /** * Get property. * @see DigitalCameraCapture for more information about returned double type. */ double DigitalCameraCapture::getProperty(int propertyId) const { CameraWidget * widget = NULL; double output = 0; if (propertyId < 0) { widget = getWidget(-propertyId); } else { switch (propertyId) { // gphoto2 cap featured case CV_CAP_PROP_GPHOTO2_PREVIEW: return preview; case CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE: if (rootWidget == NULL) return 0; return (intptr_t) widgetInfo.c_str(); case CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG: return 0; // Trigger, only by set case CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE: return reloadOnChange; case CV_CAP_PROP_GPHOTO2_COLLECT_MSGS: return collectMsgs; case CV_CAP_PROP_GPHOTO2_FLUSH_MSGS: lastFlush = msgsBuffer.str(); msgsBuffer.str(""); msgsBuffer.clear(); return (intptr_t) lastFlush.c_str(); default: widget = getGenericProperty(propertyId, output); /* no break */ } } if (widget == NULL) return output; try { CameraWidgetType type; CR(gp_widget_get_type(widget, &type)); switch (type) { case GP_WIDGET_MENU: case GP_WIDGET_RADIO: { int cnt = 0, i; const char * current; CR(gp_widget_get_value(widget, ¤t)); CR(cnt = gp_widget_count_choices(widget)); for (i = 0; i < cnt; i++) { const char *choice; CR(gp_widget_get_choice(widget, i, &choice)); if (std::strcmp(choice, current) == 0) { return i; } } return -1; } case GP_WIDGET_TOGGLE: { int value; CR(gp_widget_get_value(widget, &value)); return value; } case GP_WIDGET_RANGE: { float value; CR(gp_widget_get_value(widget, &value)); return value; } default: { char* value; CR(gp_widget_get_value(widget, &value)); return (intptr_t) value; } } } catch (GPhoto2Exception & e) { char buf[128] = ""; sprintf(buf, "cannot get property: %d", propertyId); message(WARNING, (const char *) buf, e); return 0; } } /** * @param output will be changed if possible, return 0 if changed, * @return widget, or 0 if output value was found (saved in argument), */ CameraWidget * DigitalCameraCapture::setGenericProperty(int propertyId, double /*FUTURE: value*/, bool & output) const { switch (propertyId) { case CV_CAP_PROP_POS_MSEC: case CV_CAP_PROP_POS_FRAMES: case CV_CAP_PROP_FRAME_WIDTH: case CV_CAP_PROP_FRAME_HEIGHT: case CV_CAP_PROP_FPS: case CV_CAP_PROP_FRAME_COUNT: case CV_CAP_PROP_FORMAT: output = false; return NULL; case CV_CAP_PROP_EXPOSURE: return findWidgetByName(PROP_EXPOSURE_COMPENSACTION); case CV_CAP_PROP_TRIGGER_DELAY: return findWidgetByName(PROP_SELF_TIMER_DELAY); case CV_CAP_PROP_ZOOM: return findWidgetByName(PROP_MANUALFOCUS); case CV_CAP_PROP_FOCUS: return findWidgetByName(PROP_AUTOFOCUS); case CV_CAP_PROP_ISO_SPEED: return findWidgetByName(PROP_ISO); case CV_CAP_PROP_SPEED: return findWidgetByName(PROP_SPEED); case CV_CAP_PROP_APERTURE: { CameraWidget * widget = findWidgetByName(PROP_APERTURE_NIKON); return (widget == NULL) ? findWidgetByName(PROP_APERTURE_CANON) : widget; } case CV_CAP_PROP_EXPOSUREPROGRAM: return findWidgetByName(PROP_EXPOSURE_PROGRAM); case CV_CAP_PROP_VIEWFINDER: return findWidgetByName(PROP_VIEWFINDER); } return NULL; } /** * Set property. * @see DigitalCameraCapture for more information about value, double typed, argument. */ bool DigitalCameraCapture::setProperty(int propertyId, double value) { CameraWidget * widget = NULL; bool output = false; if (propertyId < 0) { widget = getWidget(-propertyId); } else { switch (propertyId) { // gphoto2 cap featured case CV_CAP_PROP_GPHOTO2_PREVIEW: preview = value != 0; return true; case CV_CAP_PROP_GPHOTO2_WIDGET_ENUMERATE: return false; case CV_CAP_PROP_GPHOTO2_RELOAD_CONFIG: reloadConfig(); return true; case CV_CAP_PROP_GPHOTO2_RELOAD_ON_CHANGE: reloadOnChange = value != 0; return true; case CV_CAP_PROP_GPHOTO2_COLLECT_MSGS: collectMsgs = value != 0; return true; case CV_CAP_PROP_GPHOTO2_FLUSH_MSGS: return false; default: widget = setGenericProperty(propertyId, value, output); /* no break */ } } if (widget == NULL) return output; try { CameraWidgetType type; CR(gp_widget_get_type(widget, &type)); switch (type) { case GP_WIDGET_RADIO: case GP_WIDGET_MENU: { int i = static_cast(value); char *choice; CR(gp_widget_get_choice(widget, i, (const char**)&choice)); CR(gp_widget_set_value(widget, choice)); break; } case GP_WIDGET_TOGGLE: { int i = static_cast(value); CR(gp_widget_set_value(widget, &i)); break; } case GP_WIDGET_RANGE: { float v = static_cast(value); CR(gp_widget_set_value(widget, &v)); break; } default: { CR(gp_widget_set_value(widget, (void* )(intptr_t )&value)); break; } } if (!reloadOnChange) { // force widget change CR(gp_widget_set_changed(widget, 1)); } // Use the same locale setting as while getting rootWidget. char * localeTmp = setlocale(LC_ALL, "C"); CR(gp_camera_set_config(camera, rootWidget, context)); setlocale(LC_ALL, localeTmp); if (reloadOnChange) { reloadConfig(); } else { CR(gp_widget_set_changed(widget, 0)); } } catch (GPhoto2Exception & e) { char buf[128] = ""; sprintf(buf, "cannot set property: %d to %f", propertyId, value); message(WARNING, (const char *) buf, e); return false; } return true; } /** * Capture image, and store file in @field grabbedFrames. * Do not read a file. File will be deleted from camera automatically. */ bool DigitalCameraCapture::grabFrame() { CameraFilePath filePath; CameraFile * file = NULL; try { CR(gp_file_new(&file)); if (preview) { CR(gp_camera_capture_preview(camera, file, context)); } else { // Capture an image CR(gp_camera_capture(camera, GP_CAPTURE_IMAGE, &filePath, context)); CR(gp_camera_file_get(camera, filePath.folder, filePath.name, GP_FILE_TYPE_NORMAL, file, context)); CR(gp_camera_file_delete(camera, filePath.folder, filePath.name, context)); } // State update if (firstCapturedFrameTime == 0) { firstCapturedFrameTime = time(0); } capturedFrames++; grabbedFrames.push_back(file); } catch (GPhoto2Exception & e) { if (file) gp_file_unref(file); message(WARNING, "cannot grab new frame", e); return false; } return true; } /** * Read stored file with image. */ bool DigitalCameraCapture::retrieveFrame(int, OutputArray outputFrame) { if (grabbedFrames.size() > 0) { CameraFile * file = grabbedFrames.front(); grabbedFrames.pop_front(); try { readFrameFromFile(file, outputFrame); CR(gp_file_unref(file)); } catch (GPhoto2Exception & e) { message(WARNING, "cannot read file grabbed from device", e); return false; } } else { return false; } return true; } /** * @return true if device exists */ bool DigitalCameraCapture::deviceExist(int index) const { return (numDevices > 0) && (index < numDevices); } /** * @return device index if exists, otherwise -1 */ int DigitalCameraCapture::findDevice(const char * deviceName) const { const char * model = 0; try { if (deviceName != 0) { for (int i = 0; i < numDevices; ++i) { CR(gp_list_get_name(allDevices, i, &model)); if (model != 0 && strstr(model, deviceName)) { return i; } } } } catch (GPhoto2Exception & e) { ; // pass } return -1; } /** * Load device settings. */ void DigitalCameraCapture::reloadConfig() throw (GPhoto2Exception) { std::ostringstream widgetInfoListStream; if (rootWidget != NULL) { widgetInfo.clear(); CR(gp_widget_unref(rootWidget)); rootWidget = NULL; widgets.clear(); } // Make sure, that all configs (getting setting) will use the same locale setting. char * localeTmp = setlocale(LC_ALL, "C"); CR(gp_camera_get_config(camera, &rootWidget, context)); setlocale(LC_ALL, localeTmp); widgetInfoListStream << "id,label,name,info,readonly,type,value," << lineDelimiter; noOfWidgets = collectWidgets(widgetInfoListStream, rootWidget) + 1; widgetInfo = widgetInfoListStream.str(); } /** * Get widget which was fetched in time of last call to @reloadConfig(). */ CameraWidget * DigitalCameraCapture::getWidget(int widgetId) const { CameraWidget * widget; std::map::const_iterator it = widgets.find(widgetId); if (it == widgets.end()) return 0; widget = it->second; return widget; } /** * Search for widget with name which has @param subName substring. */ CameraWidget * DigitalCameraCapture::findWidgetByName( const char * subName) const { if (subName != NULL) { try { const char * name; typedef std::map::const_iterator it_t; it_t it = widgets.begin(), end = widgets.end(); while (it != end) { CR(gp_widget_get_name(it->second, &name)); if (strstr(name, subName)) break; ++it; } return (it != end) ? it->second : NULL; } catch (GPhoto2Exception & e) { message(WARNING, "error while searching for widget", e); } } return 0; } /** * Image file reader. * * @FUTURE: RAW format reader. */ void DigitalCameraCapture::readFrameFromFile(CameraFile * file, OutputArray outputFrame) throw (GPhoto2Exception) { // FUTURE: OpenCV cannot read RAW files right now. const char * data; unsigned long int size; CR(gp_file_get_data_and_size(file, &data, &size)); if (size > 0) { Mat buf = Mat(1, size, CV_8UC1, (void *) data); if(!buf.empty()) { frame = imdecode(buf, CV_LOAD_IMAGE_UNCHANGED); } frame.copyTo(outputFrame); } } /** * Print widget description in @param os. * @return real widget ID (if config was reloaded couple of times * then IDs won't be the same) */ int DigitalCameraCapture::widgetDescription(std::ostream &os, CameraWidget * widget) const throw (GPhoto2Exception) { const char * label, *name, *info; int id, readonly; CameraWidgetType type; CR(gp_widget_get_id(widget, &id)); CR(gp_widget_get_label(widget, &label)); CR(gp_widget_get_name(widget, &name)); CR(gp_widget_get_info(widget, &info)); CR(gp_widget_get_type(widget, &type)); CR(gp_widget_get_readonly(widget, &readonly)); if ((type == GP_WIDGET_WINDOW) || (type == GP_WIDGET_SECTION) || (type == GP_WIDGET_BUTTON)) { readonly = 1; } os << (id - noOfWidgets) << separator << label << separator << name << separator << info << separator << readonly << separator; switch (type) { case GP_WIDGET_WINDOW: { os << "window" << separator /* no value */<< separator; break; } case GP_WIDGET_SECTION: { os << "section" << separator /* no value */<< separator; break; } case GP_WIDGET_TEXT: { os << "text" << separator; char *txt; CR(gp_widget_get_value(widget, &txt)); os << txt << separator; break; } case GP_WIDGET_RANGE: { os << "range" << separator; float f, t, b, s; CR(gp_widget_get_range(widget, &b, &t, &s)); CR(gp_widget_get_value(widget, &f)); os << "(" << b << ":" << t << ":" << s << "):" << f << separator; break; } case GP_WIDGET_TOGGLE: { os << "toggle" << separator; int t; CR(gp_widget_get_value(widget, &t)); os << t << separator; break; } case GP_WIDGET_RADIO: case GP_WIDGET_MENU: { if (type == GP_WIDGET_RADIO) { os << "radio" << separator; } else { os << "menu" << separator; } int cnt = 0, i; char *current; CR(gp_widget_get_value(widget, ¤t)); CR(cnt = gp_widget_count_choices(widget)); os << "("; for (i = 0; i < cnt; i++) { const char *choice; CR(gp_widget_get_choice(widget, i, &choice)); os << i << ":" << choice; if (i + 1 < cnt) { os << ";"; } } os << "):" << current << separator; break; } case GP_WIDGET_BUTTON: { os << "button" << separator /* no value */<< separator; break; } case GP_WIDGET_DATE: { os << "date" << separator; int t; time_t xtime; struct tm *xtm; char timebuf[200]; CR(gp_widget_get_value(widget, &t)); xtime = t; xtm = localtime(&xtime); strftime(timebuf, sizeof(timebuf), "%c", xtm); os << t << ":" << timebuf << separator; break; } } return id; } /** * Write all widget descriptions to @param os. * @return maximum of widget ID */ int DigitalCameraCapture::collectWidgets(std::ostream & os, CameraWidget * widget) throw (GPhoto2Exception) { int id = widgetDescription(os, widget); os << lineDelimiter; widgets[id - noOfWidgets] = widget; CameraWidget * child; CameraWidgetType type; CR(gp_widget_get_type(widget, &type)); if ((type == GP_WIDGET_WINDOW) || (type == GP_WIDGET_SECTION)) { for (int x = 0; x < gp_widget_count_children(widget); x++) { CR(gp_widget_get_child(widget, x, &child)); id = std::max(id, collectWidgets(os, child)); } } return id; } /** * Write message to @field msgsBuffer if user want to store them * (@field collectMsgs). * Print debug informations on screen. */ template void DigitalCameraCapture::message(MsgType msgType, const char * msg, OsstreamPrintable & arg) const { #if defined(NDEBUG) if (collectMsgs) { #endif std::ostringstream msgCreator; std::string out; char type = (char) msgType; msgCreator << "[gPhoto2][" << type << "]: " << msg << ": " << arg << lineDelimiter; out = msgCreator.str(); #if !defined(NDEBUG) if (collectMsgs) { #endif msgsBuffer << out; } #if !defined(NDEBUG) #if defined(WIN32) || defined(_WIN32) ::OutputDebugString(out.c_str()); #else fputs(out.c_str(), stderr); #endif #endif } } // namespace gphoto2 /** * \brief IVideoCapture creator form device index. */ Ptr createGPhoto2Capture(int index) { Ptr capture = makePtr(index); if (capture->isOpened()) return capture; return Ptr(); } /** * IVideoCapture creator, from device name. * * @param deviceName is a substring in digital camera model name. */ Ptr createGPhoto2Capture(const String & deviceName) { Ptr capture = makePtr(deviceName); if (capture->isOpened()) return capture; return Ptr(); } } // namespace cv #endif