From 781b5a488b965bb19c29d0953e1d0b552ffb14a7 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Fri, 1 Nov 2013 17:14:08 -0400 Subject: [PATCH] Bug 930715 - Allow configuration of camera settings via json config files;r=davehunt --- .gitignore | 7 +- src/videocapture/videocapture/controller.py | 12 + .../videocapture/pointgrey/Capture.cpp | 104 ++- .../videocapture/pointgrey/FL3-U3-13E4C.json | 39 + .../videocapture/pointgrey/FL3-U3-13Y3M.json | 39 + .../videocapture/pointgrey/JSON.cpp | 280 ++++++ .../videocapture/pointgrey/JSON.h | 112 +++ .../videocapture/pointgrey/JSONValue.cpp | 803 ++++++++++++++++++ .../videocapture/pointgrey/JSONValue.h | 86 ++ .../videocapture/pointgrey/Makefile | 26 +- .../pointgrey/dump-current-settings.cpp | 59 ++ .../videocapture/pointgrey/get-camera-id.cpp | 40 + 12 files changed, 1589 insertions(+), 18 deletions(-) create mode 100644 src/videocapture/videocapture/pointgrey/FL3-U3-13E4C.json create mode 100644 src/videocapture/videocapture/pointgrey/FL3-U3-13Y3M.json create mode 100644 src/videocapture/videocapture/pointgrey/JSON.cpp create mode 100644 src/videocapture/videocapture/pointgrey/JSON.h create mode 100644 src/videocapture/videocapture/pointgrey/JSONValue.cpp create mode 100755 src/videocapture/videocapture/pointgrey/JSONValue.h create mode 100644 src/videocapture/videocapture/pointgrey/dump-current-settings.cpp create mode 100644 src/videocapture/videocapture/pointgrey/get-camera-id.cpp diff --git a/.gitignore b/.gitignore index 1ad9ffc..648c8f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*.d +*.o *.pyc *~ bin/activate @@ -29,4 +31,7 @@ lib/ src/videocapture/build/* src/videocapture/videocapture.egg-info/ src/videocapture/videocapture/decklink/decklink-capture -src/dashboard/httplogs/*.json \ No newline at end of file +src/videocapture/videocapture/pointgrey/pointgrey-capture +src/videocapture/videocapture/pointgrey/get-camera-id +src/videocapture/videocapture/pointgrey/dump-current-settings +rc/dashboard/httplogs/*.json diff --git a/src/videocapture/videocapture/controller.py b/src/videocapture/videocapture/controller.py index 6b412d6..3d313a8 100644 --- a/src/videocapture/videocapture/controller.py +++ b/src/videocapture/videocapture/controller.py @@ -36,6 +36,11 @@ supported_formats = { "720p@59.94": { "decklink_mode": 12 } } +camera_configs = { + "Flea3 FL3-U3-13Y3M": "FL3-U3-13Y3M.json", + "Flea3 FL3-U3-13E4C": "FL3-U3-13E4C.json" +} + class CaptureProcess(multiprocessing.Process): def __init__(self, capture_device, video_format, frame_counter, @@ -68,7 +73,14 @@ class CaptureProcess(multiprocessing.Process): '-f', self.output_raw_filename) elif self.capture_device == "pointgrey": + # get the device type + camera_id = subprocess.check_output([os.path.join(POINTGREY_DIR, "get-camera-id")]).strip() + camera_config = camera_configs.get(camera_id) + if not camera_config: + raise Exception("No camera configuration for model '%s'" % camera_id) args = (os.path.join(POINTGREY_DIR, 'pointgrey-capture'), + '-c', + os.path.join(POINTGREY_DIR, camera_config), '-o', '-n', '1200', diff --git a/src/videocapture/videocapture/pointgrey/Capture.cpp b/src/videocapture/videocapture/pointgrey/Capture.cpp index 8a5e767..025afca 100644 --- a/src/videocapture/videocapture/pointgrey/Capture.cpp +++ b/src/videocapture/videocapture/pointgrey/Capture.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include +#include "JSON.h" #include "FlyCapture2.h" using namespace FlyCapture2; @@ -31,7 +33,7 @@ int usage(char *progname, int status) " -d Output debugging information\n" " -o If specified, print frame numbers while capturing\n" " -f Directory video files will be written to\n" - " -r Framerate to capture at (default is 60)\n" + " -c Configuration file for camera (in JSON format)\n" " -n Max number of frames to capture (default is 20 * framerate)\n" "\n" "Capture video to a set of pngs.\n" @@ -47,18 +49,98 @@ void printError( Error error ) error.PrintErrorTrace(); } +PropertyType getPropertyType(const std::wstring propertyTypeStr) +{ + if (propertyTypeStr == L"BRIGHTNESS") return BRIGHTNESS; + else if (propertyTypeStr == L"AUTO_EXPOSURE") return AUTO_EXPOSURE; + else if (propertyTypeStr == L"SHARPNESS") return SHARPNESS; + else if (propertyTypeStr == L"SHUTTER") return SHUTTER; + else if (propertyTypeStr == L"GAIN") return GAIN; + else if (propertyTypeStr == L"FRAME_RATE") return FRAME_RATE; + else if (propertyTypeStr == L"WHITE_BALANCE") return WHITE_BALANCE; + + char propertyLabel[40]; + wcstombs(propertyLabel, propertyTypeStr.c_str(), 40); + fprintf(stderr, "Error: Unknown property type '%s'\n", propertyLabel); + exit(1); +} + +void processPropertiesFile(const char *filename, Camera &cam) +{ + struct stat st; + if (stat(filename, &st) != 0) { + fprintf(stderr, "Error processing prefs file '%s': can't stat", filename); + } + char *contents = new char[st.st_size + 1]; + FILE *fp = fopen(filename, "r"); + fread(contents, st.st_size, 1, fp); + contents[st.st_size] = '\0'; + JSONValue *prefdict = JSON::Parse(contents); + if (!prefdict) { + fprintf(stderr, "Error processing prefs file '%s': not valid JSON\n", filename); + exit(1); + } + if (!prefdict->IsObject()) { + fprintf(stderr, "Error processing prefs file '%s': root not a JSON object\n", filename); + exit(1); + } + + JSONObject root = prefdict->AsObject(); + JSONObject properties = root[L"properties"]->AsObject(); + for (JSONObject::iterator iter = properties.begin(); + iter != properties.end(); iter++) { + Property property; + + property.type = getPropertyType(iter->first); + cam.GetProperty(&property); + char pname[512]; + wcstombs(pname, iter->first.c_str(), 512); + + JSONObject propertySettings = iter->second->AsObject(); + for (JSONObject::iterator iter2 = propertySettings.begin(); + iter2 != propertySettings.end(); iter2++) { + if (iter2->first == L"onOff" || iter2->first == L"autoManualMode") { + if (!iter2->second->IsBool()) { + fprintf(stderr, "Error: Expected onOff/auto property to be boolean, was not\n"); + exit(1); + } + if (iter2->first == L"onOff") + property.onOff = iter2->second->AsBool(); + else + property.autoManualMode = iter2->second->AsBool(); + } + else if (iter2->first == L"absValue" || iter2->first == L"valueA" || + iter2->first == L"valueB") { + if (!iter2->second->IsNumber()) { + fprintf(stderr, "Error: Expected absValue/valueA/valueB property " + "to be a number, was not\n"); + exit(1); + } + if (iter2->first == L"absValue") + property.absValue = iter2->second->AsNumber(); + else if (iter2->first == L"valueA") + property.valueA = iter2->second->AsNumber(); + else + property.valueB = iter2->second->AsNumber(); + } + } + cam.SetProperty(&property); + } +} + int main(int argc, char *argv[]) { Error error; int ch; const char *videoOutputDir = NULL; + const char *configFilename = NULL; int maxFrames = 0; int fps = 60; bool printFrameNums = false; bool debug = false; // Parse command line options - while ((ch = getopt(argc, argv, "do?h3f:n:r:")) != -1) { + while ((ch = getopt(argc, argv, "do?h3f:n:r:c:")) != -1) { switch (ch) { case 'd': debug = true; @@ -75,6 +157,9 @@ int main(int argc, char *argv[]) case 'r': fps = atoi(optarg); break; + case 'c': + configFilename = optarg; + break; case '?': case 'h': usage(argv[0], 0); @@ -127,7 +212,6 @@ int main(int argc, char *argv[]) cam.GetProperty(&frameRateProp); frameRateProp.onOff = true; frameRateProp.autoManualMode = false; - frameRateProp.valueA = fps; frameRateProp.absValue = (float)fps; cam.SetProperty(&frameRateProp); } @@ -146,17 +230,9 @@ int main(int argc, char *argv[]) return -1; } } - // turn off all auto adjustments for eideticker - PropertyType propTypes[5] = { AUTO_EXPOSURE, SHARPNESS, SHUTTER, GAIN, WHITE_BALANCE }; - for (int i=0; i<5; i++) { - Property prop; - prop.type = propTypes[i]; - cam.GetProperty(&prop); - if (propTypes[i] == AUTO_EXPOSURE) - prop.onOff = true; - prop.autoManualMode = false; - cam.SetProperty(&prop); - } + + if (configFilename) + processPropertiesFile(configFilename, cam); // setup signal handler for termination signal(SIGTERM, term); diff --git a/src/videocapture/videocapture/pointgrey/FL3-U3-13E4C.json b/src/videocapture/videocapture/pointgrey/FL3-U3-13E4C.json new file mode 100644 index 0000000..b1979ec --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/FL3-U3-13E4C.json @@ -0,0 +1,39 @@ +{ + "properties": { + "AUTO_EXPOSURE": { + "absValue": 1.32196, + "autoManualMode": false, + "onOff": true, + "valueA": 480, + "valueB": 0 + }, + "GAIN": { + "absValue": 18.06179, + "autoManualMode": false, + "onOff": true, + "valueA": 162, + "valueB": 0 + }, + "SHARPNESS": { + "absValue": 0.0, + "autoManualMode": false, + "onOff": false, + "valueA": 512, + "valueB": 0 + }, + "SHUTTER": { + "absValue": 3.078401, + "autoManualMode": false, + "onOff": true, + "valueA": 196, + "valueB": 0 + }, + "WHITE_BALANCE": { + "absValue": 0.0, + "autoManualMode": false, + "onOff": false, + "valueA": 0, + "valueB": 0 + } + } +} diff --git a/src/videocapture/videocapture/pointgrey/FL3-U3-13Y3M.json b/src/videocapture/videocapture/pointgrey/FL3-U3-13Y3M.json new file mode 100644 index 0000000..8a2583d --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/FL3-U3-13Y3M.json @@ -0,0 +1,39 @@ +{ + "properties": { + "AUTO_EXPOSURE": { + "absValue": -0.941101, + "autoManualMode": false, + "onOff": false, + "valueA": 100, + "valueB": 0 + }, + "GAIN": { + "absValue": 18.056644, + "autoManualMode": false, + "onOff": true, + "valueA": 305, + "valueB": 0 + }, + "SHARPNESS": { + "absValue": 0.0, + "autoManualMode": false, + "onOff": false, + "valueA": 1024, + "valueB": 0 + }, + "SHUTTER": { + "absValue": 16.494692, + "autoManualMode": false, + "onOff": true, + "valueA": 2315, + "valueB": 0 + }, + "WHITE_BALANCE": { + "absValue": 0.0, + "autoManualMode": false, + "onOff": false, + "valueA": 0, + "valueB": 0 + } + } +} diff --git a/src/videocapture/videocapture/pointgrey/JSON.cpp b/src/videocapture/videocapture/pointgrey/JSON.cpp new file mode 100644 index 0000000..0b966c0 --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/JSON.cpp @@ -0,0 +1,280 @@ +/* + * File JSON.cpp part of the SimpleJSON Library - http://mjpa.in/json + * + * Copyright (C) 2010 Mike Anchor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "JSON.h" + +/** + * Blocks off the public constructor + * + * @access private + * + */ +JSON::JSON() +{ +} + +/** + * Parses a complete JSON encoded string + * This is just a wrapper around the UNICODE Parse(). + * + * @access public + * + * @param char* data The JSON text + * + * @return JSONValue* Returns a JSON Value representing the root, or NULL on error + */ +JSONValue *JSON::Parse(const char *data) +{ + size_t length = strlen(data) + 1; + wchar_t *w_data = (wchar_t*)malloc(length * sizeof(wchar_t)); + + #if defined(WIN32) && !defined(__GNUC__) + size_t ret_value = 0; + if (mbstowcs_s(&ret_value, w_data, length, data, length) != 0) + { + free(w_data); + return NULL; + } + #elif defined(ANDROID) + // mbstowcs seems to misbehave on android + for(size_t i = 0; iStringify(); + else + return L""; +} + +/** + * Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text + * + * @return bool Returns true if there is more data, or false if the end of the text was reached + */ +bool JSON::SkipWhitespace(const wchar_t **data) +{ + while (**data != 0 && (**data == L' ' || **data == L'\t' || **data == L'\r' || **data == L'\n')) + (*data)++; + + return **data != 0; +} + +/** + * Extracts a JSON String as defined by the spec - "" + * Any escaped characters are swapped out for their unescaped values + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text + * @param std::wstring& str Reference to a std::wstring to receive the extracted string + * + * @return bool Returns true on success, false on failure + */ +bool JSON::ExtractString(const wchar_t **data, std::wstring &str) +{ + str = L""; + + while (**data != 0) + { + // Save the char so we can change it if need be + wchar_t next_char = **data; + + // Escaping something? + if (next_char == L'\\') + { + // Move over the escape char + (*data)++; + + // Deal with the escaped char + switch (**data) + { + case L'"': next_char = L'"'; break; + case L'\\': next_char = L'\\'; break; + case L'/': next_char = L'/'; break; + case L'b': next_char = L'\b'; break; + case L'f': next_char = L'\f'; break; + case L'n': next_char = L'\n'; break; + case L'r': next_char = L'\r'; break; + case L't': next_char = L'\t'; break; + case L'u': + { + // We need 5 chars (4 hex + the 'u') or its not valid + if (!simplejson_wcsnlen(*data, 5)) + return false; + + // Deal with the chars + next_char = 0; + for (int i = 0; i < 4; i++) + { + // Do it first to move off the 'u' and leave us on the + // final hex digit as we move on by one later on + (*data)++; + + next_char <<= 4; + + // Parse the hex digit + if (**data >= '0' && **data <= '9') + next_char |= (**data - '0'); + else if (**data >= 'A' && **data <= 'F') + next_char |= (10 + (**data - 'A')); + else if (**data >= 'a' && **data <= 'f') + next_char |= (10 + (**data - 'a')); + else + { + // Invalid hex digit = invalid JSON + return false; + } + } + break; + } + + // By the spec, only the above cases are allowed + default: + return false; + } + } + + // End of the string? + else if (next_char == L'"') + { + (*data)++; + str.reserve(); // Remove unused capacity + return true; + } + + // Disallowed char? + else if (next_char < L' ' && next_char != L'\t') + { + // SPEC Violation: Allow tabs due to real world cases + return false; + } + + // Add the next char + str += next_char; + + // Move on + (*data)++; + } + + // If we're here, the string ended incorrectly + return false; +} + +/** + * Parses some text as though it is an integer + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text + * + * @return double Returns the double value of the number found + */ +double JSON::ParseInt(const wchar_t **data) +{ + double integer = 0; + while (**data != 0 && **data >= '0' && **data <= '9') + integer = integer * 10 + (*(*data)++ - '0'); + + return integer; +} + +/** + * Parses some text as though it is a decimal + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the JSON text + * + * @return double Returns the double value of the decimal found + */ +double JSON::ParseDecimal(const wchar_t **data) +{ + double decimal = 0.0; + double factor = 0.1; + while (**data != 0 && **data >= '0' && **data <= '9') + { + int digit = (*(*data)++ - '0'); + decimal = decimal + digit * factor; + factor *= 0.1; + } + return decimal; +} diff --git a/src/videocapture/videocapture/pointgrey/JSON.h b/src/videocapture/videocapture/pointgrey/JSON.h new file mode 100644 index 0000000..184a5de --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/JSON.h @@ -0,0 +1,112 @@ +/* + * File JSON.h part of the SimpleJSON Library - http://mjpa.in/json + * + * Copyright (C) 2010 Mike Anchor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _JSON_H_ +#define _JSON_H_ + +// Win32 incompatibilities +#if defined(WIN32) && !defined(__GNUC__) + #define wcsncasecmp _wcsnicmp + static inline bool isnan(double x) { return x != x; } + static inline bool isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif + +#include +#include +#include + +// Linux compile fix - from quaker66 +#ifdef __GNUC__ + #include + #include +#endif + +// Mac compile fixes - from quaker66, Lion fix by dabrahams +#if defined(__APPLE__) && __DARWIN_C_LEVEL < 200809L || (defined(WIN32) && defined(__GNUC__)) || defined(ANDROID) + #include + #include + + static inline int wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n) + { + int lc1 = 0; + int lc2 = 0; + + while (n--) + { + lc1 = towlower (*s1); + lc2 = towlower (*s2); + + if (lc1 != lc2) + return (lc1 - lc2); + + if (!lc1) + return 0; + + ++s1; + ++s2; + } + + return 0; + } +#endif + +// Simple function to check a string 's' has at least 'n' characters +static inline bool simplejson_wcsnlen(const wchar_t *s, size_t n) { + if (s == 0) + return false; + + const wchar_t *save = s; + while (n-- > 0) + { + if (*(save++) == 0) return false; + } + + return true; +} + +// Custom types +class JSONValue; +typedef std::vector JSONArray; +typedef std::map JSONObject; + +#include "JSONValue.h" + +class JSON +{ + friend class JSONValue; + + public: + static JSONValue* Parse(const char *data); + static JSONValue* Parse(const wchar_t *data); + static std::wstring Stringify(const JSONValue *value); + protected: + static bool SkipWhitespace(const wchar_t **data); + static bool ExtractString(const wchar_t **data, std::wstring &str); + static double ParseInt(const wchar_t **data); + static double ParseDecimal(const wchar_t **data); + private: + JSON(); +}; + +#endif diff --git a/src/videocapture/videocapture/pointgrey/JSONValue.cpp b/src/videocapture/videocapture/pointgrey/JSONValue.cpp new file mode 100644 index 0000000..3e6f8a0 --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/JSONValue.cpp @@ -0,0 +1,803 @@ +/* + * File JSONValue.cpp part of the SimpleJSON Library - http://mjpa.in/json + * + * Copyright (C) 2010 Mike Anchor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "JSONValue.h" + +// Macros to free an array/object +#define FREE_ARRAY(x) { JSONArray::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete *iter; } } +#define FREE_OBJECT(x) { JSONObject::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete (*iter).second; } } + +/** + * Parses a JSON encoded value to a JSONValue object + * + * @access protected + * + * @param wchar_t** data Pointer to a wchar_t* that contains the data + * + * @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error + */ +JSONValue *JSONValue::Parse(const wchar_t **data) +{ + // Is it a string? + if (**data == '"') + { + std::wstring str; + if (!JSON::ExtractString(&(++(*data)), str)) + return NULL; + else + return new JSONValue(str); + } + + // Is it a boolean? + else if ((simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, L"true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && wcsncasecmp(*data, L"false", 5) == 0)) + { + bool value = wcsncasecmp(*data, L"true", 4) == 0; + (*data) += value ? 4 : 5; + return new JSONValue(value); + } + + // Is it a null? + else if (simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, L"null", 4) == 0) + { + (*data) += 4; + return new JSONValue(); + } + + // Is it a number? + else if (**data == L'-' || (**data >= L'0' && **data <= L'9')) + { + // Negative? + bool neg = **data == L'-'; + if (neg) (*data)++; + + double number = 0.0; + + // Parse the whole part of the number - only if it wasn't 0 + if (**data == L'0') + (*data)++; + else if (**data >= L'1' && **data <= L'9') + number = JSON::ParseInt(data); + else + return NULL; + + // Could be a decimal now... + if (**data == '.') + { + (*data)++; + + // Not get any digits? + if (!(**data >= L'0' && **data <= L'9')) + return NULL; + + // Find the decimal and sort the decimal place out + // Use ParseDecimal as ParseInt won't work with decimals less than 0.1 + // thanks to Javier Abadia for the report & fix + double decimal = JSON::ParseDecimal(data); + + // Save the number + number += decimal; + } + + // Could be an exponent now... + if (**data == L'E' || **data == L'e') + { + (*data)++; + + // Check signage of expo + bool neg_expo = false; + if (**data == L'-' || **data == L'+') + { + neg_expo = **data == L'-'; + (*data)++; + } + + // Not get any digits? + if (!(**data >= L'0' && **data <= L'9')) + return NULL; + + // Sort the expo out + double expo = JSON::ParseInt(data); + for (double i = 0.0; i < expo; i++) + number = neg_expo ? (number / 10.0) : (number * 10.0); + } + + // Was it neg? + if (neg) number *= -1; + + return new JSONValue(number); + } + + // An object? + else if (**data == L'{') + { + JSONObject object; + + (*data)++; + + while (**data != 0) + { + // Whitespace at the start? + if (!JSON::SkipWhitespace(data)) + { + FREE_OBJECT(object); + return NULL; + } + + // Special case - empty object + if (object.size() == 0 && **data == L'}') + { + (*data)++; + return new JSONValue(object); + } + + // We want a string now... + std::wstring name; + if (!JSON::ExtractString(&(++(*data)), name)) + { + FREE_OBJECT(object); + return NULL; + } + + // More whitespace? + if (!JSON::SkipWhitespace(data)) + { + FREE_OBJECT(object); + return NULL; + } + + // Need a : now + if (*((*data)++) != L':') + { + FREE_OBJECT(object); + return NULL; + } + + // More whitespace? + if (!JSON::SkipWhitespace(data)) + { + FREE_OBJECT(object); + return NULL; + } + + // The value is here + JSONValue *value = Parse(data); + if (value == NULL) + { + FREE_OBJECT(object); + return NULL; + } + + // Add the name:value + if (object.find(name) != object.end()) + delete object[name]; + object[name] = value; + + // More whitespace? + if (!JSON::SkipWhitespace(data)) + { + FREE_OBJECT(object); + return NULL; + } + + // End of object? + if (**data == L'}') + { + (*data)++; + return new JSONValue(object); + } + + // Want a , now + if (**data != L',') + { + FREE_OBJECT(object); + return NULL; + } + + (*data)++; + } + + // Only here if we ran out of data + FREE_OBJECT(object); + return NULL; + } + + // An array? + else if (**data == L'[') + { + JSONArray array; + + (*data)++; + + while (**data != 0) + { + // Whitespace at the start? + if (!JSON::SkipWhitespace(data)) + { + FREE_ARRAY(array); + return NULL; + } + + // Special case - empty array + if (array.size() == 0 && **data == L']') + { + (*data)++; + return new JSONValue(array); + } + + // Get the value + JSONValue *value = Parse(data); + if (value == NULL) + { + FREE_ARRAY(array); + return NULL; + } + + // Add the value + array.push_back(value); + + // More whitespace? + if (!JSON::SkipWhitespace(data)) + { + FREE_ARRAY(array); + return NULL; + } + + // End of array? + if (**data == L']') + { + (*data)++; + return new JSONValue(array); + } + + // Want a , now + if (**data != L',') + { + FREE_ARRAY(array); + return NULL; + } + + (*data)++; + } + + // Only here if we ran out of data + FREE_ARRAY(array); + return NULL; + } + + // Ran out of possibilites, it's bad! + else + { + return NULL; + } +} + +/** + * Basic constructor for creating a JSON Value of type NULL + * + * @access public + */ +JSONValue::JSONValue(/*NULL*/) +{ + type = JSONType_Null; +} + +/** + * Basic constructor for creating a JSON Value of type String + * + * @access public + * + * @param wchar_t* m_char_value The string to use as the value + */ +JSONValue::JSONValue(const wchar_t *m_char_value) +{ + type = JSONType_String; + string_value = std::wstring(m_char_value); +} + +/** + * Basic constructor for creating a JSON Value of type String + * + * @access public + * + * @param std::wstring m_string_value The string to use as the value + */ +JSONValue::JSONValue(const std::wstring &m_string_value) +{ + type = JSONType_String; + string_value = m_string_value; +} + +/** + * Basic constructor for creating a JSON Value of type Bool + * + * @access public + * + * @param bool m_bool_value The bool to use as the value + */ +JSONValue::JSONValue(bool m_bool_value) +{ + type = JSONType_Bool; + bool_value = m_bool_value; +} + +/** + * Basic constructor for creating a JSON Value of type Number + * + * @access public + * + * @param double m_number_value The number to use as the value + */ +JSONValue::JSONValue(double m_number_value) +{ + type = JSONType_Number; + number_value = m_number_value; +} + +/** + * Basic constructor for creating a JSON Value of type Array + * + * @access public + * + * @param JSONArray m_array_value The JSONArray to use as the value + */ +JSONValue::JSONValue(const JSONArray &m_array_value) +{ + type = JSONType_Array; + array_value = m_array_value; +} + +/** + * Basic constructor for creating a JSON Value of type Object + * + * @access public + * + * @param JSONObject m_object_value The JSONObject to use as the value + */ +JSONValue::JSONValue(const JSONObject &m_object_value) +{ + type = JSONType_Object; + object_value = m_object_value; +} + +/** + * The destructor for the JSON Value object + * Handles deleting the objects in the array or the object value + * + * @access public + */ +JSONValue::~JSONValue() +{ + if (type == JSONType_Array) + { + JSONArray::iterator iter; + for (iter = array_value.begin(); iter != array_value.end(); iter++) + delete *iter; + } + else if (type == JSONType_Object) + { + JSONObject::iterator iter; + for (iter = object_value.begin(); iter != object_value.end(); iter++) + { + delete (*iter).second; + } + } +} + +/** + * Checks if the value is a NULL + * + * @access public + * + * @return bool Returns true if it is a NULL value, false otherwise + */ +bool JSONValue::IsNull() const +{ + return type == JSONType_Null; +} + +/** + * Checks if the value is a String + * + * @access public + * + * @return bool Returns true if it is a String value, false otherwise + */ +bool JSONValue::IsString() const +{ + return type == JSONType_String; +} + +/** + * Checks if the value is a Bool + * + * @access public + * + * @return bool Returns true if it is a Bool value, false otherwise + */ +bool JSONValue::IsBool() const +{ + return type == JSONType_Bool; +} + +/** + * Checks if the value is a Number + * + * @access public + * + * @return bool Returns true if it is a Number value, false otherwise + */ +bool JSONValue::IsNumber() const +{ + return type == JSONType_Number; +} + +/** + * Checks if the value is an Array + * + * @access public + * + * @return bool Returns true if it is an Array value, false otherwise + */ +bool JSONValue::IsArray() const +{ + return type == JSONType_Array; +} + +/** + * Checks if the value is an Object + * + * @access public + * + * @return bool Returns true if it is an Object value, false otherwise + */ +bool JSONValue::IsObject() const +{ + return type == JSONType_Object; +} + +/** + * Retrieves the String value of this JSONValue + * Use IsString() before using this method. + * + * @access public + * + * @return std::wstring Returns the string value + */ +const std::wstring &JSONValue::AsString() const +{ + return string_value; +} + +/** + * Retrieves the Bool value of this JSONValue + * Use IsBool() before using this method. + * + * @access public + * + * @return bool Returns the bool value + */ +bool JSONValue::AsBool() const +{ + return bool_value; +} + +/** + * Retrieves the Number value of this JSONValue + * Use IsNumber() before using this method. + * + * @access public + * + * @return double Returns the number value + */ +double JSONValue::AsNumber() const +{ + return number_value; +} + +/** + * Retrieves the Array value of this JSONValue + * Use IsArray() before using this method. + * + * @access public + * + * @return JSONArray Returns the array value + */ +const JSONArray &JSONValue::AsArray() const +{ + return array_value; +} + +/** + * Retrieves the Object value of this JSONValue + * Use IsObject() before using this method. + * + * @access public + * + * @return JSONObject Returns the object value + */ +const JSONObject &JSONValue::AsObject() const +{ + return object_value; +} + +/** + * Retrieves the number of children of this JSONValue. + * This number will be 0 or the actual number of children + * if IsArray() or IsObject(). + * + * @access public + * + * @return The number of children. + */ +std::size_t JSONValue::CountChildren() const +{ + switch (type) + { + case JSONType_Array: + return array_value.size(); + case JSONType_Object: + return object_value.size(); + default: + return 0; + } +} + +/** + * Checks if this JSONValue has a child at the given index. + * Use IsArray() before using this method. + * + * @access public + * + * @return bool Returns true if the array has a value at the given index. + */ +bool JSONValue::HasChild(std::size_t index) const +{ + if (type == JSONType_Array) + { + return index < array_value.size(); + } + else + { + return false; + } +} + +/** + * Retrieves the child of this JSONValue at the given index. + * Use IsArray() before using this method. + * + * @access public + * + * @return JSONValue* Returns JSONValue at the given index or NULL + * if it doesn't exist. + */ +JSONValue *JSONValue::Child(std::size_t index) +{ + if (index < array_value.size()) + { + return array_value[index]; + } + else + { + return NULL; + } +} + +/** + * Checks if this JSONValue has a child at the given key. + * Use IsObject() before using this method. + * + * @access public + * + * @return bool Returns true if the object has a value at the given key. + */ +bool JSONValue::HasChild(const wchar_t* name) const +{ + if (type == JSONType_Object) + { + return object_value.find(name) != object_value.end(); + } + else + { + return false; + } +} + +/** + * Retrieves the child of this JSONValue at the given key. + * Use IsObject() before using this method. + * + * @access public + * + * @return JSONValue* Returns JSONValue for the given key in the object + * or NULL if it doesn't exist. + */ +JSONValue* JSONValue::Child(const wchar_t* name) +{ + JSONObject::const_iterator it = object_value.find(name); + if (it != object_value.end()) + { + return it->second; + } + else + { + return NULL; + } +} + +/** + * Creates a JSON encoded string for the value with all necessary characters escaped + * + * @access public + * + * @return std::wstring Returns the JSON string + */ +std::wstring JSONValue::Stringify() const +{ + std::wstring ret_string; + + switch (type) + { + case JSONType_Null: + ret_string = L"null"; + break; + + case JSONType_String: + ret_string = StringifyString(string_value); + break; + + case JSONType_Bool: + ret_string = bool_value ? L"true" : L"false"; + break; + + case JSONType_Number: + { + if (isinf(number_value) || isnan(number_value)) + ret_string = L"null"; + else + { + std::wstringstream ss; + ss.precision(15); + ss << number_value; + ret_string = ss.str(); + } + break; + } + + case JSONType_Array: + { + ret_string = L"["; + JSONArray::const_iterator iter = array_value.begin(); + while (iter != array_value.end()) + { + ret_string += (*iter)->Stringify(); + + // Not at the end - add a separator + if (++iter != array_value.end()) + ret_string += L","; + } + ret_string += L"]"; + break; + } + + case JSONType_Object: + { + ret_string = L"{"; + JSONObject::const_iterator iter = object_value.begin(); + while (iter != object_value.end()) + { + ret_string += StringifyString((*iter).first); + ret_string += L":"; + ret_string += (*iter).second->Stringify(); + + // Not at the end - add a separator + if (++iter != object_value.end()) + ret_string += L","; + } + ret_string += L"}"; + break; + } + } + + return ret_string; +} + +/** + * Creates a JSON encoded string with all required fields escaped + * Works from http://www.ecma-internationl.org/publications/files/ECMA-ST/ECMA-262.pdf + * Section 15.12.3. + * + * @access private + * + * @param std::wstring str The string that needs to have the characters escaped + * + * @return std::wstring Returns the JSON string + */ +std::wstring JSONValue::StringifyString(const std::wstring &str) +{ + std::wstring str_out = L"\""; + + std::wstring::const_iterator iter = str.begin(); + while (iter != str.end()) + { + wchar_t chr = *iter; + + if (chr == L'"' || chr == L'\\' || chr == L'/') + { + str_out += L'\\'; + str_out += chr; + } + else if (chr == L'\b') + { + str_out += L"\\b"; + } + else if (chr == L'\f') + { + str_out += L"\\f"; + } + else if (chr == L'\n') + { + str_out += L"\\n"; + } + else if (chr == L'\r') + { + str_out += L"\\r"; + } + else if (chr == L'\t') + { + str_out += L"\\t"; + } + else if (chr < L' ') + { + str_out += L"\\u"; + for (int i = 0; i < 4; i++) + { + int value = (chr >> 12) & 0xf; + if (value >= 0 && value <= 9) + str_out += (wchar_t)('0' + value); + else if (value >= 10 && value <= 15) + str_out += (wchar_t)('A' + (value - 10)); + chr <<= 4; + } + } + else + { + str_out += chr; + } + + iter++; + } + + str_out += L"\""; + return str_out; +} diff --git a/src/videocapture/videocapture/pointgrey/JSONValue.h b/src/videocapture/videocapture/pointgrey/JSONValue.h new file mode 100755 index 0000000..f84389f --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/JSONValue.h @@ -0,0 +1,86 @@ +/* + * File JSONValue.h part of the SimpleJSON Library - http://mjpa.in/json + * + * Copyright (C) 2010 Mike Anchor + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _JSONVALUE_H_ +#define _JSONVALUE_H_ + +#include +#include + +#include "JSON.h" + +class JSON; + +enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object }; + +class JSONValue +{ + friend class JSON; + + public: + JSONValue(/*NULL*/); + JSONValue(const wchar_t *m_char_value); + JSONValue(const std::wstring &m_string_value); + JSONValue(bool m_bool_value); + JSONValue(double m_number_value); + JSONValue(const JSONArray &m_array_value); + JSONValue(const JSONObject &m_object_value); + ~JSONValue(); + + bool IsNull() const; + bool IsString() const; + bool IsBool() const; + bool IsNumber() const; + bool IsArray() const; + bool IsObject() const; + + const std::wstring &AsString() const; + bool AsBool() const; + double AsNumber() const; + const JSONArray &AsArray() const; + const JSONObject &AsObject() const; + + std::size_t CountChildren() const; + bool HasChild(std::size_t index) const; + JSONValue *Child(std::size_t index); + bool HasChild(const wchar_t* name) const; + JSONValue *Child(const wchar_t* name); + + std::wstring Stringify() const; + + protected: + static JSONValue *Parse(const wchar_t **data); + + private: + static std::wstring StringifyString(const std::wstring &str); + + JSONType type; + std::wstring string_value; + bool bool_value; + double number_value; + JSONArray array_value; + JSONObject object_value; +}; + +#endif diff --git a/src/videocapture/videocapture/pointgrey/Makefile b/src/videocapture/videocapture/pointgrey/Makefile index d93c33b..17459d2 100644 --- a/src/videocapture/videocapture/pointgrey/Makefile +++ b/src/videocapture/videocapture/pointgrey/Makefile @@ -1,9 +1,29 @@ CC = g++ CFLAGS = -I/usr/include/flycapture LDFLAGS = -lflycapture +OBJECTS = Capture.o JSON.o JSONValue.o -pointgrey-capture: Capture.cpp - $(CC) -o pointgrey-capture Capture.cpp $(CFLAGS) $(LDFLAGS) +%.o: %.cpp + g++ $< -c -o $@ $(CFLAGS) -g + @g++ $< -MM $(CFLAGS) > $*.d + @mv -f $*.d $*.d.tmp + @sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d + @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \ + sed -e 's/^ *//' -e 's/$$/:/' >> $*.d + @rm -f $*.d.tmp + +default: pointgrey-capture get-camera-id dump-current-settings + +pointgrey-capture: $(OBJECTS) + $(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LDFLAGS) + +dump-current-settings: dump-current-settings.o + $(CC) -o $@ dump-current-settings.o $(CFLAGS) $(LDFLAGS) + +get-camera-id: get-camera-id.o + $(CC) -o $@ get-camera-id.o $(CFLAGS) $(LDFLAGS) clean: - rm -f pointgrey-capture + rm -f pointgrey-capture get-camera-id dump-current-settings *.o *.d + +-include $(OBJECTS:.o=.d) diff --git a/src/videocapture/videocapture/pointgrey/dump-current-settings.cpp b/src/videocapture/videocapture/pointgrey/dump-current-settings.cpp new file mode 100644 index 0000000..1f97adc --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/dump-current-settings.cpp @@ -0,0 +1,59 @@ +#include + +#include "FlyCapture2.h" + +using namespace FlyCapture2; + +void printError(Error error) +{ + error.PrintErrorTrace(); +} + +int main(int argc, char *argv[]) +{ + BusManager busMgr; + PGRGuid guid; + Error error; + + error = busMgr.GetCameraFromIndex(0, &guid); + if (error != PGRERROR_OK) { + printError(error); + exit(1); + } + + Camera cam; + error = cam.Connect(&guid); + if (error != PGRERROR_OK) { + printError(error); + return -1; + } + + // do different things depending on camera model detected... + CameraInfo camInfo; + error = cam.GetCameraInfo(&camInfo); + if (error != PGRERROR_OK) { + printError(error); + return -1; + } + + printf("{\n \"properties\": {\n"); + + PropertyType propTypes[5] = { AUTO_EXPOSURE, SHARPNESS, SHUTTER, GAIN, + WHITE_BALANCE }; + const char *propTypeNames[5] = { "AUTO_EXPOSURE", "SHARPNESS", "SHUTTER", + "GAIN", "WHITE_BALANCE" }; + for (int i=0; i<5; i++) { + Property property; + property.type = propTypes[i]; + cam.GetProperty(&property); + + if (i>0) + printf(",\n"); + printf(" \"%s\": { \"onOff\": %s, \"autoManualMode\": %s, \"absValue\": %f, " + "\"valueA\": %d, \"valueB\": %d }", propTypeNames[i], + property.onOff ? "true" : "false", + property.autoManualMode ? "true" : "false", + (float)property.absValue, property.valueA, property.valueB); + } + printf("\n }\n}\n"); +} diff --git a/src/videocapture/videocapture/pointgrey/get-camera-id.cpp b/src/videocapture/videocapture/pointgrey/get-camera-id.cpp new file mode 100644 index 0000000..ca3179c --- /dev/null +++ b/src/videocapture/videocapture/pointgrey/get-camera-id.cpp @@ -0,0 +1,40 @@ +#include + +#include "FlyCapture2.h" + +using namespace FlyCapture2; + +void printError(Error error) +{ + error.PrintErrorTrace(); +} + +int main(int argc, char *argv[]) +{ + BusManager busMgr; + PGRGuid guid; + Error error; + + error = busMgr.GetCameraFromIndex(0, &guid); + if (error != PGRERROR_OK) { + printError(error); + exit(1); + } + + Camera cam; + error = cam.Connect(&guid); + if (error != PGRERROR_OK) { + printError(error); + return -1; + } + + // do different things depending on camera model detected... + CameraInfo camInfo; + error = cam.GetCameraInfo(&camInfo); + if (error != PGRERROR_OK) { + printError(error); + return -1; + } + + printf("%s\n", camInfo.modelName); +}