From 50cf25540245967c45172d45df33bb8f9f828da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Gon=C3=A7alves?= Date: Tue, 26 Feb 2019 20:09:25 +0000 Subject: [PATCH 1/9] vendor/libraries/exiftool: add exiftool library. --- vendor/libraries/exiftool/ConsoleColor.hpp | 24 + vendor/libraries/exiftool/ExifTool.cpp | 773 +++++++++++++++++++++ vendor/libraries/exiftool/ExifTool.h | 89 +++ vendor/libraries/exiftool/ExifToolPipe.cpp | 179 +++++ vendor/libraries/exiftool/ExifToolPipe.h | 43 ++ vendor/libraries/exiftool/Library.cmake | 7 + vendor/libraries/exiftool/TagInfo.cpp | 52 ++ vendor/libraries/exiftool/TagInfo.h | 35 + 8 files changed, 1202 insertions(+) create mode 100644 vendor/libraries/exiftool/ConsoleColor.hpp create mode 100644 vendor/libraries/exiftool/ExifTool.cpp create mode 100644 vendor/libraries/exiftool/ExifTool.h create mode 100644 vendor/libraries/exiftool/ExifToolPipe.cpp create mode 100644 vendor/libraries/exiftool/ExifToolPipe.h create mode 100644 vendor/libraries/exiftool/Library.cmake create mode 100644 vendor/libraries/exiftool/TagInfo.cpp create mode 100644 vendor/libraries/exiftool/TagInfo.h diff --git a/vendor/libraries/exiftool/ConsoleColor.hpp b/vendor/libraries/exiftool/ConsoleColor.hpp new file mode 100644 index 0000000000..aa671deb77 --- /dev/null +++ b/vendor/libraries/exiftool/ConsoleColor.hpp @@ -0,0 +1,24 @@ +#include +namespace Color { + enum Code { + FG_RED = 31, + FG_GREEN = 32, + FG_YELLOW = 33, + FG_BLUE = 34, + FG_DEFAULT = 39, + BG_RED = 41, + BG_GREEN = 42, + BG_YELLOW = 43, + BG_BLUE = 44, + BG_DEFAULT = 49 + }; + class Modifier { + Code code; + public: + Modifier(Code pCode) : code(pCode) {} + friend std::ostream& + operator<<(std::ostream& os, const Modifier& mod) { + return os << "\033[" << mod.code << "m"; + } + }; +} diff --git a/vendor/libraries/exiftool/ExifTool.cpp b/vendor/libraries/exiftool/ExifTool.cpp new file mode 100644 index 0000000000..0de662afa9 --- /dev/null +++ b/vendor/libraries/exiftool/ExifTool.cpp @@ -0,0 +1,773 @@ +//------------------------------------------------------------------------------ +// File: ExifTool.cpp +// +// Description: C++ library interface to Perl exiftool application script +// +// License: Copyright 2013-2016, Phil Harvey (phil at owl.phy.queensu.ca) +// +// This is software, in whole or part, is free for use in +// non-commercial applications, provided that this copyright notice +// is retained. A licensing fee may be required for use in a +// commercial application. +// +// Created: 2013-11-23 - Phil Harvey +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ExifTool.h" + +const int kOutBlockSize = 65536; // size increment for exiftool stdout buffer +const int kErrBlockSize = 4096; // size increment for exiftool stderr buffer +const int kCmdBlockSize = 8192; // size increment for exiftool command buffer + +const char *kDefaultExec = "exiftool"; + +static int sBrokenPipe = -1; + +int ExifTool::sNoSigPipe = 0; +int ExifTool::sNoWatchdog = 0; + +//------------------------------------------------------------------------------ +// SIGPIPE handler +static void sigPipeAction(int sig) +{ + sBrokenPipe = 1; +} + +//------------------------------------------------------------------------------ +// Unescape C-style escape sequences in a null-terminated string +// (as found in values of exiftool -php output) +// Returns: number of bytes in unescaped data (including null terminator) +// - on return string contains binary data which may have embedded zero bytes +static int unescape(char *str) +{ + char *src = strchr(str, '\\'); + // return string length without converting if string doesn't contain escapes + if (!src) return((int)(strlen(str) + 1)); + char *dst = src; + for (;;) { + char ch = *(src++); + if (ch == '\\') { + // must un-escape this character + ch = *(src++); + switch (ch) { + case 'x': + // decode 2-digit hex character + ch = 0; + for (int i=0; i<2; ++i) { + char nibble = *(src++); + if (nibble >= '0' && nibble <= '9') { + nibble -= '0'; + } else if (nibble >= 'A' && nibble <= 'F') { + nibble -= 'A' - 10; + } else if (nibble >= 'a' && nibble <= 'f') { + nibble -= 'a' - 10; + } else { + ch = 0; // (shouldn't happen) + break; + } + ch = (ch << 4) + nibble; + } + break; + case 't': + ch = '\t'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case '\0': // (shouldn't happen, but just to be safe) + *(dst++) = ch; + return((int)(dst - str)); + default: + // pass any other character straight through + break; + } + *(dst++) = ch; + } else { + *(dst++) = ch; + if (!ch) break; + } + } + return((int)(dst - str)); +} + +//------------------------------------------------------------------------------ +// get current hi-resolution time +static double getTime() +{ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv,&tz); + return(tv.tv_sec + 1e-6 * tv.tv_usec); +} + +//============================================================================== +// ExifTool object constructor +//------------------------------------------------------------------------------ +// Inputs: exec - path name to executable file (ie. "perl" or "exiftool") +// arg1 - optional first argument (ie. "exiftool" if exec="perl") +ExifTool::ExifTool(const char *exec, const char *arg1) + : mWriteInfo(NULL), mCmdQueue(NULL), mCmdQueueLen(0), mCmdQueueSize(0), + mWatchdog(-1), mLastComplete(0), mCmdNum(0) +{ + int to[2], from[2], err[2]; + const char *args[7]; + args[0] = NULL; + args[1] = kDefaultExec; + args[2] = "-stay_open"; + args[3] = "true"; + args[4] = "-@"; + args[5] = "-"; + args[6] = NULL; + + int firstArg = 1; + if (arg1) { + args[1] = arg1; + --firstArg; + } + args[firstArg] = exec ? exec : kDefaultExec; + + // set up handler for SIGPIPE if not done already + if (sBrokenPipe == -1 && !sNoSigPipe) { + struct sigaction act1; + sigemptyset(&act1.sa_mask); + act1.sa_flags = SA_SIGINFO; + act1.sa_handler = sigPipeAction; + if (sigaction(SIGPIPE, &act1, (struct sigaction *)NULL) < 0) { + sBrokenPipe = -2; // error! + } else { + sBrokenPipe = 0; + } + } + + // create our pipes + int ret = 0; + ret = pipe(to); + ret = pipe(from); + ret = pipe(err); + + // fork and exec exiftool + mPid = fork(); + + if (mPid == 0) { + // this is our exiftool thread + close(to[1]); + close(from[0]); + close(err[0]); + dup2(to[0], STDIN_FILENO); + dup2(from[1], STDOUT_FILENO); + dup2(err[1], STDERR_FILENO); + close(to[0]); + close(from[1]); + close(err[1]); + execvp(args[firstArg], (char * const *)args + firstArg); + // (if execvp succeeds, it will never return) + exit(0); + } else { + // this is our working thread + close(to[0]); + close(from[1]); + close(err[1]); + // allocate memory for exiftool response and error messages + mStdout.Init(from[0], mPid, kOutBlockSize); + mStderr.Init(err[0], mPid, kErrBlockSize); + mTo = to[1]; + // set output exiftool argument pipe to non-blocking + int flags = fcntl(mTo, F_GETFL, 0); + fcntl(mTo, F_SETFL, flags | O_NONBLOCK); + // create a watchdog process to monitor the main program thread and + // terminate the exiftool process if necessary when the program exits + // (otherwise exiftool processes may be left running if an ExifTool + // object wasn't properly deleted) + if (mPid != -1 && !sNoWatchdog) { + int pid = getpid(); // pid of this process + mWatchdog = fork(); + if (!mWatchdog) { + // monitor the main program and clean up if it dies + // (under normal conditions this watchdog process + // is killed in the destructor) + for (;;) { + sleep(1); + // if the main thread dies, our parent PID will change + if (getppid() == pid) continue; + // our parent has died, so send a SIGINT to exiftool + kill(mPid, SIGINT); + exit(0); // exit the watchdog process + } + } + } + } +} + +//------------------------------------------------------------------------------ +// Terminate exiftool application process and close files +ExifTool::~ExifTool() +{ + delete mWriteInfo; + delete [] mCmdQueue; + Command("-stay_open\nfalse\n"); + close(mTo); + // wait for the exiftool process to terminate + while (IsRunning()) { + // keep reading exiftool output so the process won't be blocked, + // and wait until all commands complete unless we get an error + if (Complete() < 0) { + kill(mPid, SIGINT); // pull the plug + break; + } + usleep(100); // relax + } + if (mWatchdog > 0) kill(mWatchdog, SIGINT); // kill our watchdog process +} + +//------------------------------------------------------------------------------ +// Extract metadata from specified image +// Inputs: file - source file name +// opts - string of exiftool options, separated by newlines +// timeout - maximum wait time (floating point seconds) +// Returns: linked list of tag information structures for extracted information +// - valid options: "-b\n" - extract binary data (other options may mess up TagInfo parsing) +// - waits for exiftool command to complete, then parses returned messages +// to generate the TagInfo list +// - caller is responsible for deleting returned TagInfo ("delete info;") +TagInfo * ExifTool::ImageInfo(const char *file, const char *opts, double timeout) +{ + int cmdNum = ExtractInfo(file, opts); + + // error unless command number is > 0 + if (cmdNum <= 0) return (TagInfo *)NULL; + + return GetInfo(cmdNum, timeout); +} + +//------------------------------------------------------------------------------ +// Send command to extract information from one or more files +// Inputs: file - source file name(s) +// opts - exiftool options, separated by newlines +// Returns: command number (>0), or error (<0) +int ExifTool::ExtractInfo(const char *file, const char *opts) +{ + if (!file) return -5; + + // prepare command arguments for exiftool + int flen = (int)strlen(file); + int olen = opts ? (int)strlen(opts) : 0; + + char *buff = new char[flen + olen + 64]; + if (!buff) return -3; // out of memory! + memcpy(buff, file, flen); + + // extract information using exiftool -php option + // (also add "-l -G:0:1:2:4 -D" to get more details for TagInfo structure) + strcpy(buff + flen, "\n-php\n-l\n-G:0:1:2:4\n-D\n-sep\n, \n"); + + // add extra options specified by the caller + if (opts) { + strcat(buff, opts); + strcat(buff, "\n"); // (to be safe; blank lines are ignored) + } + + // send the command to exiftool + int cmdNum = Command(buff); + delete [] buff; + + return cmdNum; +} + +//------------------------------------------------------------------------------ +// Read exiftool output and convert to list of TagInfo structures +// Inputs: cmdNum - command number (0 to process next output in series, +// or -1 to process previously completed command) +// timeout - maximum wait time (floating point seconds) +// Returns: linked list of tag information structures for extracted information +// - waits up to timeout time for exiftool command to complete +// - caller is responsible for deleting returned TagInfo ("delete info;") +// - after return, GetError() may be called to get exiftool errors +TagInfo *ExifTool::GetInfo(int cmdNum, double timeout) +{ + TagInfo *info = (TagInfo *)NULL; + TagInfo *next = (TagInfo *)NULL; + TagInfo *infoList = (TagInfo *)NULL; + + // wait for specified command to complete + if (cmdNum >= 0) { + for (;;) { + int n = Complete(timeout); + if (n <= 0) return info; + if (n == cmdNum || !cmdNum) break; + } + } else if (mLastComplete <= 0) { + return info; + } + + // parse the response string, line by line + char *pt = mStdout.GetString(); + if (!pt) return info; + + int mode = 0; // 0=looking for tag name, 1=tag properties + + for (;;) { + // find the end of this line + char *p0 = pt; + pt = strchr(pt, '\n'); + if (!pt) break; + *pt = '\0'; // null terminate this line + ++pt; // continue at next line + // scan for opening quote + p0 = strchr(p0, '"'); + if (!p0) { + // finish with most recent TagInfo structure + if (info) { + // (name and value are guaranteed to exist) + if (!info->value) { + info->value = new char[1]; + if (!info->value) break; + info->value[0] = '\0'; + } + if (!info->num) { + info->num = info->value; + info->numLen = info->valueLen; + } + } + mode = 0; // look for next tag name + continue; + } + char *p1 = ++p0; + if (!mode) { // looking for new tag + if (next) delete next; // delete old unused structure if necessary + // create new TagInfo structure for this tag + next = new TagInfo; + if (!next) break; + // extract tag/group names + int g = 0; + for (;;) { + char ch = *p1; + if (ch == '"' || ch == ':') { + int n = (int)(p1 - p0); + char *str = new char[n + 1]; + if (!str) break; + memcpy(str, p0, n); + str[n] = '\0'; + if (ch == '"') { + next->name = str; // save tag name + break; + } + if (g > 2) { + // get copy number + if (!memcmp(str, "Copy", 4)) { + next->copyNum = atoi(str+4); + delete [] str; // done with this string + } + } else { + next->group[g] = str; // save group name + } + ++g; + p0 = p1 + 1; + } + ++p1; + } + if (!next->name) continue; + // file name given by line like: "SourceFile" => "images/a.jpg", + if (!strcmp(next->name,"SourceFile")) { + char *p2 = pt - 2; + if (*p2 == '\r') --p2; // skip Windows CR + if (*p2 == ',') --p2; + if (*p2 != '"') continue; + int n = (int)(p2 - p1 - 6); + if (n < 0) continue; + char *str = new char[n+1]; + if (!str) break; + memcpy(str, p1+6, n); + str[n] = '\0'; + next->value = str; + next->valueLen = n; + } else { + mode = 1; // read tag properties next + } + // add to linked list of information + if (info) { + info->next = next; + } else { + infoList = next; + } + info = next; + next = NULL; + } else { + // isolate the property name + p1 = strchr(p0, '"'); + if (!p1) break; // (shouldn't happen) + *p1 = '\0'; // null terminate property name + p1 += 5; // step to start of value + if (p1 >= pt) break; // (shouldn't happen); + if (*p1 == '"') ++p1; // skip quote if it exists + char *p2 = pt - 1; + if (p2[-1] == '\r') --p2;// skip Windows CR + if (p2[-1] == ',') --p2;// skip trailing comma + if (p2[-1] == '"') --p2;// skip trailing quote + if (p2 < p1) break; // (shouldn't happen) + *p2 = '\0'; // null terminate property value + int n = unescape(p1); // unescape characters in property value + char **dst; + if (!strcmp(p0, "desc")) { + dst = &info->desc; + } else if (!strcmp(p0, "id")) { + dst = &info->id; + } else if (!strcmp(p0, "num")) { + dst = &info->num; + info->numLen = n - 1; // save length too (could be binary data) + } else if (!strcmp(p0, "val")) { + dst = &info->value; + info->valueLen = n - 1; // save length too (could be binary data) + } else { + continue; // (shouldn't happen) + } + *dst = new char[n]; + if (!*dst) break; + memcpy(*dst, p1, n); // copy property value + } + } + if (next) delete next; + return infoList; +} + +//------------------------------------------------------------------------------ +// Set the new value for a tag +// Inputs: tag = tag name (may contain leading group names and trailing '#') +// value = tag value data +// len = length of value in bytes (defaults to strlen(value)) +// Returns: number of tags set, or <0 on memory error +// - must call WriteInfo() at some point after this to actually write the new values +// - call with tag=NULL to reset previous new values +// - call with value=NULL to delete tag +int ExifTool::SetNewValue(const char *tag, const char *value, int len) +{ + int numSet = 0; + if (tag) { + TagInfo *info = new TagInfo; + if (!info) return -3; + info->name = new char[strlen(tag) + 1]; + if (!info->name) { delete info; return -3; } + strcpy(info->name, tag); + if (value) { + if (len < 0) { + if (value) len = (int)strlen(value); + else len = 0; + } + if (len) { + info->value = new char[len+1]; + if (!info->value) { delete info; return -3; } + memcpy(info->value, value, len); + // add null terminator (but note that we don't use it) + info->value[len] = '\0'; + info->valueLen = len; + } + } + // place at the end of the linked list + TagInfo **pt = &mWriteInfo; + while (*pt) { + ++numSet; + pt = &((*pt)->next); + } + *pt = info; + ++numSet; + } else { + delete mWriteInfo; + mWriteInfo = NULL; + } + return numSet; +} + +//------------------------------------------------------------------------------ +// Write metadata to specified image +// Inputs: file - one or more directory and/or file names, separated by newlines +// opts - extra exiftool options, separated by newlines +// info - pointer to linked list of tags to write (overrides SetNewValue() calls) +// Returns: >0 command number, <0=error +// - each option argument must be terminated with a newline +// - file may be one or more file names separated by newlines +// - must call Complete() after this to check the return messages +// - ignores "SourceFile" entries in input TagInfo list +int ExifTool::WriteInfo(const char *file, const char *opts, TagInfo *info) +{ + if (!file) return -5; + + const int kBlockSize = 65536; + char *buff = new char[kBlockSize]; + if (!buff) return -3; + // get length of all options (plus 12 extra characters for "-ex\n-sep\n, \n") + int olen = (int)((opts ? strlen(opts)+1 : 0) + 12); + int size = kBlockSize; + strcpy(buff, file); + int pos = (int)strlen(file); + buff[pos++] = '\n'; + int escaped = 0; + + // use internal new value list if not specified + if (!info) info = mWriteInfo; + + // prepare assignment arguments for exiftool, looping through all tags to write + while (info) { + if (!info->name || strlen(info->name) > 100 || !strcmp(info->name, "SourceFile")) { + info = info->next; + continue; + } + // construct the tag name + char tag[1024]; + tag[0] = '\0'; + for (int i=0; i<3; ++i) { + if (info->group[i] && strlen(info->group[i]) < 100) { + char *pt = strchr(tag, '\0'); + *(pt++) = '0' + i; // leading family number + strcpy(pt, info->group[i]); // group name + strcat(tag,":"); // colon separator + } + } + strcat(tag, info->name); + // which value are we writing (converted or numerical?) + char *val = info->value; + int valLen = info->valueLen; + if (!val) { + val = info->num; + valLen = info->numLen; + if (val) strcat(tag, "#"); // write numerical value + } + int tagLen = (int)strlen(tag); + int origLen = valLen; + // count the number of characters in the value that need escaping + if (val) { + char *pt = val; + char *end = pt + origLen; + int count = 0; + while (pt < end) { + char ch = *(pt++); + if (ch==10 || ch=='&' || ch=='\0') ++count; + } + valLen += count * 4; // 4 extra bytes for each escaped character + } + // prepare exiftool argument (format is: "-TAG=VALUE\n") + int n = tagLen + valLen + 3; + // expand buffer if necessary + if (pos + n + olen > size) { + size += n + kBlockSize; + char *buf2 = new char[size]; + if (!buf2) { delete [] buff; return -3; } + memcpy(buf2, buff, pos); + delete [] buff; + buff = buf2; + } + buff[pos++] = '-'; + memcpy(buff+pos, tag, tagLen); + pos += tagLen; + buff[pos++] = '='; + if (valLen == origLen) { + if (val) { + memcpy(buff+pos, val, valLen); + pos += valLen; + } + } else { + // escape newlines and '&' characters in the value + char *pt = val; + char *end = pt + origLen; + char *dst = buff + pos; + while (pt < end) { + char ch = *(pt++); + if (ch == 10) { + memcpy(dst, " ", 5); + dst += 5; + } else if (ch == 0) { + memcpy(dst, "�", 5); + dst += 5; + } else if (ch == '&') { + memcpy(dst, "&", 5); + dst += 5; + } else { + *(dst++) = ch; + } + } + pos = (int)(dst - buff); + escaped = 1; + } + buff[pos++] = '\n'; + info = info->next; + } + // get exiftool to unescape our newlines if necessary + if (escaped) { strcpy(buff+pos, "-ex\n"); pos += 4; } + // split concatenated lists back into individual items + strcpy(buff+pos, "-sep\n, \n"); pos += 8; + // add user-defined options last + if (opts) { + strcpy(buff+pos, opts); + pos += strlen(opts); + buff[pos++] = '\n'; // (just in case) + buff[pos] = '\0'; + } + int cmdNum = Command(buff); + delete [] buff; + return cmdNum; +} + +//------------------------------------------------------------------------------ +// Send command to exiftool +// Inputs: cmd - exiftool command arguments (separated by newlines) +// Returns: command number (1-99999), error (<0), +// or 0 if cmd is NULL and the queue is empty +// - set cmd to NULL to continue writing previously queued commands +// - this routine always returns immediately, and queues command if it couldn't send +int ExifTool::Command(const char *cmd) +{ + int n; + // check to make sure our exiftool process is still running + if (!IsRunning()) return -1; + // must first try to send previously queued command + if (mCmdQueue) { + n = (int)write(mTo, mCmdQueue, mCmdQueueLen); + if (n < 0) { + if (errno != EAGAIN) return -2; // write error! + n = 0; + } + if (n == mCmdQueueLen) { + delete [] mCmdQueue; + mCmdQueue = NULL; + mCmdQueueSize = 0; + } else if (n != 0) { + memmove(mCmdQueue, mCmdQueue+n, mCmdQueueLen-n); + } + mCmdQueueLen -= n; + } + if (cmd) { + // increment to next command number + int cmdNum = mCmdNum + 1; + if (cmdNum > 99999) cmdNum = 1; + + // compose full command string (cmd2) + char buf2[64]; + int len = (int)strlen(cmd); + int len2 = sprintf(buf2, "\n-echo4\n{ready%.5d}\n-execute%.5d\n", cmdNum, cmdNum); + char *cmd2 = new char[len + len2]; + if (!cmd2) return -3; // out of memory! + memcpy(cmd2, cmd, len); + memcpy(cmd2+len, buf2, len2); + len2 += len; // len2 is now the length of the complete command + + // add command to the queue if not empty + if (mCmdQueue) { + if (mCmdQueueLen + len2 > mCmdQueueSize) { + // enlarge queue and add new command + int newSize = mCmdQueueLen + len2 + kCmdBlockSize; + char *queue = new char[newSize]; + if (!queue) return -3; // out of memory! + memcpy(queue, mCmdQueue, mCmdQueueLen); + delete [] mCmdQueue; + mCmdQueue = queue; + mCmdQueueSize = newSize; + } + // copy this command into the queue + memcpy(mCmdQueue+mCmdQueueLen, cmd2, len2); + mCmdQueueLen += len2; + delete [] cmd2; // free memory for this command + } else { + // write the command + n = (int)write(mTo, cmd2, len2); + if (n < 0) { + if (errno != EAGAIN) return -2; // write error! + n = 0; + } + if (n == len2) { + delete [] cmd2; // success! delete the buffered command + } else { + // don't bother allocating any new memory, + // just use our cmd2 string as the new queue + if (n) memmove(cmd2, cmd2+n, len2-n); + mCmdQueue = cmd2; + mCmdQueueLen = len2 - n; + mCmdQueueSize = len2; + } + } + mCmdNum = cmdNum; + } else if (!mCmdQueue) { + return 0; // cmd is NULL, and queue is empty + } + return mCmdNum; +} + +//------------------------------------------------------------------------------ +// Wait for a command to complete (up to specified timeout) +// Inputs: timeout - maximum wait time (floating point seconds) +// Returns: command number on success, 0 on timeout, or <0 on error +int ExifTool::Complete(double timeout) +{ + if (mCmdQueue) Command(); // try to send queued commands (if any) + double doneTime = getTime() + timeout; + int cmdNum; + for (;;) { + cmdNum = mStdout.Read(); + if (cmdNum) break; + if (getTime() >= doneTime) break; + if (mCmdQueue) Command(); // keep sending queued commands + usleep(1000); // chill and have a beer + } + if (cmdNum > 0) { + // get errors from the same command (we know they must be coming, + // so loop as quickly as possible to read them, but impose a + // 1-second timeout just in case) + doneTime = getTime() + 1; + for (;;) { + int n = mStderr.Read(); + if (n == cmdNum) break; + if (n < 0) { cmdNum = n; break; } + if (getTime() >= doneTime) { cmdNum = -4; break; } + } + } + return mLastComplete = cmdNum; +} + +//------------------------------------------------------------------------------ +// Get specified summary message +// Inputs: msg - message string in summary output +// Returns: corresponding number from summary statistics, or -1 if the +// specified message wasn't found +int ExifTool::GetSummary(const char *msg) +{ + for (int out=0; out<2; ++out) { + // check stderr first because it isn't likely to be too long + char *str = out ? GetOutput() : GetError(); + if (!str) continue; + char *pt = strstr(str, msg); + if (!pt || pt - str < 2 || pt[-1] != ' ' || !isdigit(pt[-2])) continue; + char ch = pt[strlen(msg)]; + if (ch != '\n' && ch != '\r') continue; // message must end with a newline + pt -= 2; + while (pt > str && isdigit(pt[-1])) --pt; + return atoi(pt); + } + return -1; // message not found +} + +//------------------------------------------------------------------------------ +// Check to see if exiftool process is still running +int ExifTool::IsRunning() +{ + int status; + if (mPid == -1) return 0; + if (waitpid(mPid, &status, WNOHANG)) { + // no more child process + mPid = -1; + return 0; + } + return 1; // yes! +} + +// end + diff --git a/vendor/libraries/exiftool/ExifTool.h b/vendor/libraries/exiftool/ExifTool.h new file mode 100644 index 0000000000..72c83a15d2 --- /dev/null +++ b/vendor/libraries/exiftool/ExifTool.h @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// File: ExifTool.h +// +// Description: C++ library interface to Perl exiftool application script +// +// License: Copyright 2013-2016, Phil Harvey (phil at owl.phy.queensu.ca) +// +// This is software, in whole or part, is free for use in +// non-commercial applications, provided that this copyright notice +// is retained. A licensing fee may be required for use in a +// commercial application. +// +// Created: 2013-11-23 - Phil Harvey +//------------------------------------------------------------------------------ +#ifndef __EXIFTOOL_H__ +#define __EXIFTOOL_H__ + +#include "ExifToolPipe.h" +#include "TagInfo.h" + +#define NOW 0 +#define NEVER 1e9 + +#define SUMMARY_DIRECTORIES_SCANNED "directories scanned" +#define SUMMARY_DIRECTORIES_CREATED "directories created" +#define SUMMARY_FILES_FAILED_CONDITION "files failed condition" +#define SUMMARY_IMAGE_FILES_CREATED "image files created" +#define SUMMARY_IMAGE_FILES_UPDATED "image files updated" +#define SUMMARY_IMAGE_FILES_UNCHANGED "image files unchanged" +#define SUMMARY_IMAGE_FILES_MOVED "image files moved" +#define SUMMARY_IMAGE_FILES_COPIED "image files copied" +#define SUMMARY_FILE_UPDATE_ERRORS "files weren't updated due to errors" +#define SUMMARY_FILE_CREATE_ERRORS "files weren't created due to errors" +#define SUMMARY_IMAGE_FILES_READ "image files read" +#define SUMMARY_IMAGE_FILE_ERRORS "files could not be read" +#define SUMMARY_OUTPUT_FILES_CREATED "output files created" +#define SUMMARY_OUTPUT_FILES_APPENDED "output files appended" +#define SUMMARY_HARD_LINKS_CREATED "hard links created" +#define SUMMARY_HARD_LINK_ERRORS "hard links could not be created" + +class ExifTool +{ +public: + ExifTool(const char *exec=NULL, const char *arg1=NULL); + virtual ~ExifTool(); + + TagInfo *ImageInfo(const char *file, const char *opts=NULL, double timeout=NEVER); + + int ExtractInfo(const char *file, const char *opts=NULL); + TagInfo *GetInfo(int cmdNum=0, double timeout=NEVER); + + int SetNewValue(const char *tag=NULL, const char *value=NULL, int len=-1); + int WriteInfo(const char *file, const char *opts=NULL, TagInfo *info=NULL); + + int Command(const char *cmd=NULL); + int Complete(double timeout=NEVER); + + int IsRunning(); + int LastComplete() { return mLastComplete; } + int LastCommand() { return mCmdNum; } // (undocumented) + void SetLastComplete(int lastComplete) { mLastComplete = lastComplete; } + + char * GetOutput() { return mLastComplete > 0 ? mStdout.GetString() : NULL; } + int GetOutputLen() { return mLastComplete > 0 ? mStdout.GetStringLen() : 0; } + char * GetError() { return mLastComplete > 0 ? mStderr.GetString() : NULL; } + int GetErrorLen() { return mLastComplete > 0 ? mStderr.GetStringLen() : 0; } // (undocumented) + + int GetSummary(const char *msg); + + // flags to allow some ExifTool features to be disabled + // (must be set before creating ExifTool object) + static int sNoSigPipe; // set to disable SIGPIPE handler + static int sNoWatchdog; // set to disable watchdog process + +private: + ExifToolPipe mStdout; // buffer for exiftool stdout read pipe + ExifToolPipe mStderr; // buffer for exiftool stderr read pipe + int mTo; // write pipe for exiftool stdin + int mPid; // exiftool application process ID + int mWatchdog; // watchdog process ID + TagInfo * mWriteInfo; // tag information to write + char * mCmdQueue; // queued command arguments (NULL if nothing queued) + int mCmdQueueLen; // length of data in command queue + int mCmdQueueSize;// size of command queue + int mLastComplete;// result of last Complete() call + int mCmdNum; // last command number +}; + +#endif // __EXIFTOOL_H__ diff --git a/vendor/libraries/exiftool/ExifToolPipe.cpp b/vendor/libraries/exiftool/ExifToolPipe.cpp new file mode 100644 index 0000000000..bf7f5e54a8 --- /dev/null +++ b/vendor/libraries/exiftool/ExifToolPipe.cpp @@ -0,0 +1,179 @@ +//------------------------------------------------------------------------------ +// File: ExifToolPipe.cpp +// +// Description: Piped output from exiftool application +// +// License: Copyright 2013-2016, Phil Harvey (phil at owl.phy.queensu.ca) +// +// This is software, in whole or part, is free for use in +// non-commercial applications, provided that this copyright notice +// is retained. A licensing fee may be required for use in a +// commercial application. +// +// Created: 2013-11-23 - Phil Harvey +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#include "ExifToolPipe.h" + +//------------------------------------------------------------------------------ +// ExifToolPipe constructor +ExifToolPipe::ExifToolPipe() + : mFile(-1), mBuff(NULL), mSize(0), mLen(0), mPos(0), mSearchPos(0), + mBlockSize(0), mString(NULL), mStringLen(0), mPid(-1) +{ +} + +//------------------------------------------------------------------------------ +// Destructor -- close the input file and delete the buffer +ExifToolPipe::~ExifToolPipe() +{ + if (mFile >= 0) close(mFile); + Free(); +} + +//------------------------------------------------------------------------------ +// Initialize the ExifTool pipe object +// - sets input file to non-blocking +void ExifToolPipe::Init(int fd, int pid, int initialSize) +{ + mFile = fd; + mPid = pid; + mBlockSize = initialSize; + // set read pipe to non-blocking + int flags = fcntl(fd, F_GETFL, 0) | O_NONBLOCK; +#ifdef O_BINARY + // set to binary mode (Windows only) + flags |= O_BINARY; +#endif + fcntl(fd, F_SETFL, flags); +} + +//------------------------------------------------------------------------------ +// Read exiftool response for specified command number +// returns: command number on success, 0 if no response available, <0 on error +// - this routine returns immediately +int ExifToolPipe::Read() +{ + const int kMinRemaining = 1024; // enlarge buffer if less than this free + + Flush(); // remove previous response from buffer + + // keep reading until we get a complete response or there is no more to read + for (;;) { + + // read to fill remaining response buffer, but leave room for null terminator + int remaining = mSize - mLen - 1; + + // enlarge buffer if necessary + // (could test for "remaining < 1", but what is the point in reading just 1 byte?) + if (remaining < kMinRemaining) { + int newSize = mSize + mLen + mBlockSize; + char *pt = new char[newSize]; + if (!pt) return -3; // out of memory! + if (mSize) memcpy(pt, mBuff, mSize); + delete [] mBuff; + mBuff = pt; + mSize = newSize; + remaining = newSize - mLen - 1; + } + + // read output from exiftool process + int bytesRead = (int)read(mFile, mBuff + mLen, remaining); + if (bytesRead < 0) { + if (errno != EAGAIN) return -2; // read error! + bytesRead = 0; + } + mLen += bytesRead; + if (mLen < 13) { + if (!bytesRead) { + // no response, so check to be sure our exiftool process is still running + int status; + if (mPid == -1) return -1; + if (waitpid(mPid, &status, WNOHANG)) { + mPid = -1; + return -1; + } + } + return 0; + } + // must null terminate response string + mBuff[mLen] = '\0'; + // 0123456789012 + // response should end with "{ready#####}\n" or "{ready#####}\r\n" + // - continue searching from where we left off + char *pt = mBuff + mSearchPos; + char *end = mBuff + mLen; + + for (;;) { + + pt = (char *)memmem(pt, end-pt, "{ready", 6); + if (!pt) { + mSearchPos = mLen - 5; // continue next search where we left off + break; + } + if (end-pt>=13 && pt[11]=='}' && + // must end with newline, or CR+LF in Windows + (pt[12]=='\n' || (pt[12]=='\r' && end-pt>=14 && pt[13]=='\n'))) + { + // validate and extract command number + int cmdNum = 0; + for (int i=0; i<5; ++i) { + unsigned d = pt[i+6] -'0'; + if (d > 9) { + cmdNum = 0; + break; + } + cmdNum = cmdNum * 10 + d; + } + if (cmdNum) { + *pt = '\0'; // NULL terminate original response + mStringLen = (int)(pt - mBuff); + // skip LF if this was a Windows CR+LF combo + if (pt[12] == '\r') ++pt; + pt += 13; // step to start of next response + // update current position in response buffer + mPos = (int)(pt - mBuff); + mString = mBuff; // set return string + return cmdNum; // success! + } + } + pt += 6; // step to next possible search position + } + if (bytesRead != remaining) break; // stop if we read everything + } + return 0; // no complete response available +} + +//------------------------------------------------------------------------------ +// Free buffer memory +void ExifToolPipe::Free() +{ + delete [] mBuff; + mBuff = mString = NULL; + mLen = mSize = mPos = mSearchPos = mStringLen = 0; +} + +//------------------------------------------------------------------------------ +// Remove previous response from buffer +void ExifToolPipe::Flush() +{ + if (mPos) { + if (mLen > mPos) { + memmove(mBuff, mBuff+mPos, mLen-mPos); + mLen -= mPos; + } else { + mLen = 0; + } + mPos = mSearchPos = 0; + } + mString = NULL; + mStringLen = 0; +} + +// end diff --git a/vendor/libraries/exiftool/ExifToolPipe.h b/vendor/libraries/exiftool/ExifToolPipe.h new file mode 100644 index 0000000000..5ff024a92e --- /dev/null +++ b/vendor/libraries/exiftool/ExifToolPipe.h @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// File: ExifToolPipe.h +// +// Description: Piped output from exiftool application +// +// License: Copyright 2013-2016, Phil Harvey (phil at owl.phy.queensu.ca) +// +// This is software, in whole or part, is free for use in +// non-commercial applications, provided that this copyright notice +// is retained. A licensing fee may be required for use in a +// commercial application. +// +// Created: 2013-11-23 - Phil Harvey +//------------------------------------------------------------------------------ +#ifndef __EXIFTOOLPIPE_H__ +#define __EXIFTOOLPIPE_H__ + +class ExifToolPipe +{ +public: + ExifToolPipe(); + ~ExifToolPipe(); + void Init(int fd, int pid, int initialSize); + int Read(); // read exiftool response + void Free(); // free buffer memory + void Flush(); // remove previous response from buffer + char * GetString() { return (mString && mString[0]) ? mString : (char *)0; } + int GetStringLen() { return mStringLen; } + +private: + int mFile; // read file descriptor + char * mBuff; // buffer pointer + int mSize; // buffer size + int mLen; // length of data in buffer + int mPos; // current read position in buffer + int mSearchPos; // current search position in buffer + int mBlockSize; // initial buffer size + char * mString; // returned string from last Read() + int mStringLen; // length of returned string + int mPid; // process id for other side of the pipe +}; + +#endif // __EXIFTOOLPIPE_H__ diff --git a/vendor/libraries/exiftool/Library.cmake b/vendor/libraries/exiftool/Library.cmake new file mode 100644 index 0000000000..d4f3d0891d --- /dev/null +++ b/vendor/libraries/exiftool/Library.cmake @@ -0,0 +1,7 @@ +file(GLOB DUNE_EXIFTOOL_FILES + vendor/libraries/exiftool/*.cpp) + +set_source_files_properties(${DUNE_EXIFTOOL_FILES} + PROPERTIES COMPILE_FLAGS "${DUNE_CXX_FLAGS}") + +list(APPEND DUNE_VENDOR_FILES ${DUNE_EXIFTOOL_FILES}) diff --git a/vendor/libraries/exiftool/TagInfo.cpp b/vendor/libraries/exiftool/TagInfo.cpp new file mode 100644 index 0000000000..80620e7616 --- /dev/null +++ b/vendor/libraries/exiftool/TagInfo.cpp @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// File: TagInfo.cpp +// +// Description: Tag information object +// +// License: Copyright 2013-2016, Phil Harvey (phil at owl.phy.queensu.ca) +// +// This is software, in whole or part, is free for use in +// non-commercial applications, provided that this copyright notice +// is retained. A licensing fee may be required for use in a +// commercial application. +// +// Created: 2013-11-23 - Phil Harvey +//------------------------------------------------------------------------------ + +#include +#include "TagInfo.h" + +//------------------------------------------------------------------------------ +TagInfo::TagInfo() + : name(NULL), desc(NULL), id(NULL), value(NULL), valueLen(0), + num(NULL), numLen(0), copyNum(0), next(NULL) +{ + group[0] = group[1] = group[2] = NULL; +} + +//------------------------------------------------------------------------------ +// delete entire linked list of TagInfo objects +TagInfo::~TagInfo() +{ + // delete our members + delete [] group[0]; + delete [] group[1]; + delete [] group[2]; + delete [] name; + delete [] desc; + delete [] id; + if (num != value) delete [] num; // delete numerical value if unique + delete [] value; + + // delete remaining elements of linked list + while (next) { + TagInfo *info = next; + // remove next entry from the list, then delete it + next = info->next; + info->next = (TagInfo *)NULL; + delete info; + } +} + +// end + diff --git a/vendor/libraries/exiftool/TagInfo.h b/vendor/libraries/exiftool/TagInfo.h new file mode 100644 index 0000000000..aeb6466b3c --- /dev/null +++ b/vendor/libraries/exiftool/TagInfo.h @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// File: TagInfo.h +// +// Description: Tag information object +// +// License: Copyright 2013-2016, Phil Harvey (phil at owl.phy.queensu.ca) +// +// This is software, in whole or part, is free for use in +// non-commercial applications, provided that this copyright notice +// is retained. A licensing fee may be required for use in a +// commercial application. +// +// Created: 2013-11-23 - Phil Harvey +//------------------------------------------------------------------------------ +#ifndef __TAGINFO_H__ +#define __TAGINFO_H__ + +struct TagInfo +{ + TagInfo(); + virtual ~TagInfo(); + + char *group[3]; // family 0-2 group names + char *name; // tag name + char *desc; // tag description + char *id; // tag ID + char *value; // converted value + int valueLen; // length of value in bytes (not including null terminator) + char *num; // "numerical" value + int numLen; // length of numerical value + int copyNum; // copy number for this tag name + TagInfo *next; // next TagInfo in linked list +}; + +#endif // __TAGINFO_H__ From 75d54f338b80645e3f555aa7dab9ecc202d1d15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Gon=C3=A7alves?= Date: Tue, 26 Feb 2019 20:10:04 +0000 Subject: [PATCH 2/9] programs/utils/dune-flir-duo-temp: Calculate image temperature (FlirDuoRTemp). --- programs/utils/dune-flir-duo-temp.cpp | 280 ++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 programs/utils/dune-flir-duo-temp.cpp diff --git a/programs/utils/dune-flir-duo-temp.cpp b/programs/utils/dune-flir-duo-temp.cpp new file mode 100644 index 0000000000..93f600ef8a --- /dev/null +++ b/programs/utils/dune-flir-duo-temp.cpp @@ -0,0 +1,280 @@ +//*************************************************************************** +// Copyright 2007-2019 Universidade do Porto - Faculdade de Engenharia * +// Laboratório de Sistemas e Tecnologia Subaquática (LSTS) * +//*************************************************************************** +// This file is part of DUNE: Unified Navigation Environment. * +// * +// Commercial Licence Usage * +// Licencees holding valid commercial DUNE licences may use this file in * +// accordance with the commercial licence agreement provided with the * +// Software or, alternatively, in accordance with the terms contained in a * +// written agreement between you and Faculdade de Engenharia da * +// Universidade do Porto. For licensing terms, conditions, and further * +// information contact lsts@fe.up.pt. * +// * +// Modified European Union Public Licence - EUPL v.1.1 Usage * +// Alternatively, this file may be used under the terms of the Modified * +// EUPL, Version 1.1 only (the "Licence"), appearing in the file LICENCE.md * +// included in the packaging of this file. You may not use this work * +// except in compliance with the Licence. Unless required by applicable * +// law or agreed to in writing, software distributed under the Licence is * +// distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF * +// ANY KIND, either express or implied. See the Licence for the specific * +// language governing permissions and limitations at * +// https://github.com/LSTS/dune/blob/master/LICENCE.md and * +// http://ec.europa.eu/idabc/eupl.html. * +//*************************************************************************** +//# Author: Pedro Gonçalves * +//#************************************************************************** +// Calculate image temperature (FlirDuoRTemp). * +//*************************************************************************** + +#include +#include +#include +#include +#include + +#include "../../vendor/libraries/exiftool/ExifTool.h" +#include "../../vendor/libraries/exiftool/ConsoleColor.hpp" + +using namespace std; + +string readInfo[] = + { "ReflectedApparentTemperature", "AtmosphericTemperature", "ObjectDistance", + "RelativeHumidity", "Emissivity", "PlanckR1", "PlanckR2", "PlanckB", + "PlanckO", "PlanckF", "AtmosphericTransAlpha1", "AtmosphericTransAlpha2", + "AtmosphericTransBeta1", "AtmosphericTransBeta2", "AtmosphericTransX", + "RawValueMedian", "RawValueRange", "RawThermalImage" }; + +float readInfoValues[32]; +string lineInfo[256][2]; +int cntMetaData = 0; +int numberInfoToRead = 0; +char * fileName; + +void +printData(void); +void +calcTemp(void); +void +getRawThermalImage(char * path); + +ExifTool *et; + +float tau; +float raw_atm; +float emmissivity; +float raw_refl; +float b; +float o; +float r1; +float r2; +float f; + +Color::Modifier green(Color::FG_GREEN); +Color::Modifier blue(Color::FG_BLUE); +Color::Modifier red(Color::FG_RED); +Color::Modifier def(Color::FG_DEFAULT); + +int +main(int argc, char **argv) +{ + if (argc < 2) + { + cout << "Please specify input file name" << endl; + return 1; + } + // create our ExifTool object + et = new ExifTool(); + // read metadata from the image + TagInfo *info = et->ImageInfo(argv[1]); + fileName = argv[1]; + + cout << green; + if (info) + { + for (TagInfo *i = info; i; i = i->next) + { + lineInfo[cntMetaData][0] = i->name; + lineInfo[cntMetaData++][1] = i->value; + cout << "# " << i->name << " = " << i->value << endl; + } + delete info; + } + else if (et->LastComplete() <= 0) + { + cerr << "Error executing exiftool!" << endl; + } + + cout << def << endl << endl; + + printData(); + calcTemp(); + getRawThermalImage(argv[1]); + + // print exiftool stderr messages + char *err = et->GetError(); + if (err) + cout << err; + + delete et; // delete our ExifTool object + return 0; +} + +void +printData(void) +{ + numberInfoToRead = sizeof(readInfo) / sizeof(readInfo[0]); + bool haveInfo = false; + + for (int i = 0; i < numberInfoToRead; i++) + { + haveInfo = false; + for (int t = 0; t < cntMetaData; t++) + { + if (lineInfo[t][0].compare(readInfo[i]) == 0) + { + haveInfo = true; + try + { + readInfoValues[i] = stof(lineInfo[t][1]); + cout << "# " << readInfo[i] << " = " << lineInfo[t][1] << endl; + } + catch (...) + { + + cout << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; + cout << "# " << lineInfo[t][1] << endl; + } + } + if ((t == cntMetaData - 1) && !haveInfo) + { + cout << readInfo[i] << " = Not found " << endl; + } + } + } +} + +void +calcTemp(void) +{ + //# get parameters from meta data + /* + ReflectedApparentTemperature = 0 | AtmosphericTemperature = 1 + ObjectDistance = 2 | RelativeHumidity = 3 + Emissivity = 4 | PlanckR1 = 5 + PlanckR2 = 6 | PlanckB = 7 + PlanckO = 8 | PlanckF = 9 + AtmosphericTransAlpha1 = 10 | AtmosphericTransAlpha2 = 11 + AtmosphericTransBeta1 = 12 | AtmosphericTransBeta2 = 13 + AtmosphericTransX = 14 | RawValueMedian = 15 + RawValueRange = 16 | RawThermalImage = 17 + */ + + float temp_ref = readInfoValues[0]; + float temp_atm = readInfoValues[1]; + float distance = readInfoValues[2]; + float humidity = readInfoValues[3] / 100.0; + emmissivity = readInfoValues[4]; + r1 = readInfoValues[5]; + r2 = readInfoValues[6]; + b = readInfoValues[7]; + o = readInfoValues[8]; + f = readInfoValues[9]; + float a1 = readInfoValues[10]; + float a2 = readInfoValues[11]; + float b1 = readInfoValues[12]; + float b2 = readInfoValues[13]; + float x = readInfoValues[14]; + + // Raw temperature range from FLIR + float raw_max = readInfoValues[15] + (readInfoValues[16] / 2); + float raw_min = raw_max - readInfoValues[16]; + + // Calculate atmospheric transmission + float h2o = (humidity / 100) + * exp( + 1.5587 + 6.939e-2 * temp_atm - 2.7816e-4 * pow(temp_atm, 2) + + 6.8455e-7 * pow(temp_atm, 3)); + tau = x * exp(-sqrt(distance) * (a1 + b1 * sqrt(h2o))) + + (1 - x) * exp(-sqrt(distance) * (a2 + b2 * sqrt(h2o))); + + // Radiance from atmosphere + // The camera is reporting the ambient temp as -273.15 deg celsius + try + { + raw_atm = (r1 / (r2 * (exp(b / (temp_atm + 273.15)) - f))) - o; + } + catch (...) + { + cout << "ZeroDivisionError" << endl; + raw_atm = -o; + } + + // Radiance from reflected objects + raw_refl = r1 / (r2 * (exp(b / (temp_ref + 273.15)) - f)) - o; + + // get displayed object temp max/min + float raw_max_obj = (raw_max - (1 - tau) * raw_atm + - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + float raw_min_obj = (raw_min - (1 - tau) * raw_atm + - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + + float temp_min = b / log(r1 / (r2 * (raw_min_obj + o)) + f) - 273.15; + float temp_max = b / log(r1 / (r2 * (raw_max_obj + o)) + f) - 273.15; +} + +void +getRawThermalImage(char * path) +{ + //Raw thermal image + TagInfo *infoEP = et->ImageInfo(path, "\n-RawThermalImage\n-b\n"); + int cnt = 0; + for (TagInfo *i = infoEP; i; i = i->next) + { + if (cnt == 1) + { + int bufferMat[120][160]; + float bufferMatFloat[120][160]; + char *buff; + buff = i->value; + double sizeBuf = i->valueLen; + int start = sizeBuf - (120 * (2 * 160)); + int r = 0; + int c = 0; + for (int t = start; t < sizeBuf; t = t + 2) + { + bufferMat[r][c] = (buff[t + 1] << 8) | (buff[t] & 0xff); + c++; + if (c > 159) + { + c = 0; + r++; + } + } + + for (int i = 0; i < 120; i++) + for (int t = 0; t < 160; t++) + bufferMatFloat[i][t] = (bufferMat[i][t] - (1 - tau) * raw_atm + - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + + for (int i = 0; i < 120; i++) + for (int t = 0; t < 160; t++) + bufferMatFloat[i][t] = (b + / log(r1 / (r2 * (bufferMatFloat[i][t] + o)) + f) - 273.15); + + double med = 0; + + for (int i = 0; i < 120; i++) + for (int t = 0; t < 160; t++) + med = med + bufferMatFloat[i][t]; + + std::cout.precision(8); + cout << endl << blue << "# " << green << fileName << blue << " # Pos calc > Temperature med of picture: " << red << med / (120 * 160) << " ºC" << def << endl << endl; + + } + cnt++; + } +} + From ff3f7cf663a41f8045311429bba34a4c597aa3f8 Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Wed, 27 Feb 2019 18:04:02 +0000 Subject: [PATCH 3/9] programs/utils: Added utility to process data from Flir DuoR --- .gitignore | 2 + programs/scripts/dune-flir-temp-processing.sh | 239 ++++++++++++ programs/utils/dune-flir-duor-temp-logs.cpp | 350 ++++++++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 programs/scripts/dune-flir-temp-processing.sh create mode 100644 programs/utils/dune-flir-duor-temp-logs.cpp diff --git a/.gitignore b/.gitignore index 139fa75dd1..d395525597 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ private/ CMakeLists.txt.user user/ build/ +lauv-build/ .cproject .settings/ /.vscode +/.vs diff --git a/programs/scripts/dune-flir-temp-processing.sh b/programs/scripts/dune-flir-temp-processing.sh new file mode 100644 index 0000000000..c3bb73b895 --- /dev/null +++ b/programs/scripts/dune-flir-temp-processing.sh @@ -0,0 +1,239 @@ +#!/bin/sh + +CSVName="FlirThermalData.csv" + +usage() { + echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c]" + echo " -s start start date fro which to start processing (yyyymmdd_hhmmss or partial)" + echo " -e end date at which to stop processing (yyyymmdd_hhmmss or partial)" + echo " -d device path to the device to be mounted in order to access the photos" + echo " -l logs vehicle logs path" + echo " -o output output path, overrides the logs parameter and outputs to a single file" + echo " -c copy enable file copying to logs" + echo " -jc just-copy disable image analisys and enable file copying to logs" + echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" + echo " -nr no-replace prevents the program from overwriting existing data files" +} + +inRange() { + [ "$START" \< "$1" ] && [ "$END" \> "$1" ] +} + +findLog() { + echo "In function with filename $1" + + currDayPath="" + nextDayPath="" + currTimePath="" + nextTimePath="" + + for fullDayFile in $LOGS/*; do + local dayFile=$(basename $fullDayFile) + for fullTimeFile in $LOGS/$dayFile/*; do + local timeFile=$(basename $fullTimeFile) + logfile="$(printf "%s_%s" $dayFile $timeFile)" + if [ $logfile \< $1 ] || [ $logfile = $1 ] + then + currDayPath=$dayFile + currTimePath=$timeFile + else + nextDayPath=$dayFile + nextTimePath=$timeFile + return + fi + done + done +} + +getEpochFilename() { + local name=$1 + local year=`echo $name | cut -b 1-4` + local month=`echo $name | cut -b 5-6` + local day=`echo $name | cut -b 7-8` + local hour=`echo $name | cut -b 10-11` + local minute=`echo $name | cut -b 12-13` + local second=`echo $name | cut -b 14-15` + # date "+%s" -d "2013-02-20 08:41:15" + local date="$year-$month-$day $hour:$minute:$second" + epochFilename="$(date +%s -d "$date")".`echo $name | cut -b 17-19`.jpg +} + +getNextPhotoDir() { + for photoDir in $photosPath/*; do + currPhotoIndex="$(ls $photoDir | wc -l)" + if [ $currPhotoIndex -lt 1000 ]; + then + currPhotosDir=$photoDir + break + fi + done + # if all existing directories are full + if [ $currPhotoIndex -eq 1000 ]; + then + currPhotoIndex=0 + local newPhotoDirIndex=$(($(basename $photoDir)+1)) + local newFolder="$(printf "%.6d" $newPhotoDirIndex)" + currPhotosDir=$photosPath/$newFolder + mkdir $currPhotosDir + fi + + if [ "$(basename $photoDir)" = "*" ]; + then + currPhotoIndex=0 + currPhotosDir=$photosPath/000000 + mkdir $currPhotosDir + fi +} + +for i in "$@" +do +case $i in + -e=*|--extension=*) + END="${i#*=}" + shift # past argument=value + ;; + -s=*|--searchpath=*) + START="${i#*=}" + shift # past argument=value + ;; + -d=*|--device=*) + DEVICE="${i#*=}" + shift # past argument=value + ;; + -l=*|--logs=*) + LOGS="${i#*=}" + shift # past argument=value + ;; + -o=*|--output=*) + OUTPUT="${i#*=}" + shift # past argument=value + ;; + -c|--copy) + COPY="copy" + shift # past argument=value + ;; + -jc|--just-copy) + JUSTCOPY="justcopy" + shift # past argument=value + ;; + -fn=*|--file_name=*) + CSVName="${i#*=}" + shift # past argument=value + ;; + -nr|--no-replace) + NOREPLACE="noreplace" + shift # past argument=value + ;; + *) + printf "Unkown option %s\n\n" $i # unknown option + usage + exit 1 + ;; +esac +done + +if [ -z $DEVICE ] || [ -z $START ] || [ -z $END ] || [ -z $LOGS -a -z $OUTPUT ]; +then + printf 'Some required variables are not defined\n\n' + usage + exit 1 +fi + +noSave=0 +nextLog="0" +savePath="$(printf %s$CSVNAme $OUTPUT)" +epochFilename="" +currPhotosDir="" +currPhotoIndex=0 + +if [ ! -z $OUTPUT ]; then +printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath +fi + +for top_filename in $DEVICE/*; do + # printf "OUTSIDE FOR: $top_filename\n" + +# find ../../test_photos -maxdepth 1 -regextype posix-extended -regex [.\/a-z\_]*/[0-9]{8}_[0-9]{6}$ + + if [ -n "$(echo $top_filename | sed -E '/[.\/a-z\_]*\/[0-9]{8}_[0-9]{6}$/g')" ]; + then + printf "Skipped file: $top_filename; For not keeping the naming standard\n" + continue + fi + for filename in $top_filename/*; do + # printf "INNER FOR: $filename\n" + truncFilename="$(basename $filename _R.jpg)" + truncFilename=${truncFilename%_*} + # check if the curr file is in the specified range + if inRange $truncFilename; + then + # printf "IS IN RANGE: $truncFilename\n" + day=${truncFilename%_*} + inputTime=${truncFilename#*_} + # check if the next log file should be used to store info about the current file + # printf "NEXTLOG: $nextLog\n" + # printf "OUTPUT: $OUTPUT\n" + if [ $truncFilename \> $nextLog -o $truncFilename = $nextLog ] && [ -z $OUTPUT ]; + then + findLog $truncFilename + nextLog="$(printf "%s_%s" $nextDayPath $nextTimePath)" + generalPath=$LOGS/$currDayPath/$currTimePath + savePath=$generalPath/$CSVName + photosPath=$generalPath/Photos + # verify if there is already a photos directory in curr log + if [ ! -d $photosPath ] && [ -n $COPY ]; + then + currPhotosDir=$photosPath/000000 + mkdir -p $currPhotosDir + currPhotoIndex=0 + else + getNextPhotoDir + fi + # verify if there exists a data file already present and don't overwrite if the options is present + # check also if there is a valid log file to save the items + if [ -e $savePath -a $NOREPLACE ] || [ -z $currDayPath ] || [ -z $currTimePath ]; + then + noSave=1 + printf "Skipped: $filename\n" + continue + else + noSave=0 + fi + + if [ -z $JUSTCOPY ]; + then + # add header to the new data.csv file + printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath + fi + fi + # check if the file should be processed + if [ $noSave = 1 ]; + then + printf "Skipped: $filename\n" + continue + fi + + # process file + if [ -z $JUSTCOPY ]; + then + ./dune-flir-duor-temp-logs $filename >> $savePath + printf "Processed $filename\n" + printf "Saving to: %s\n" $savePath + fi + + # copy files to log if option is present + if [ $COPY ] || [ $JUSTCOPY ]; + then + getEpochFilename $(basename $filename _R.jpg) + cp -p $filename $currPhotosDir/$epochFilename + printf "Coppied $filename to $currPhotosDir/$epochFilename\n" + if [ $currPhotoIndex -eq 999 ]; + then + getNextPhotoDir + else + currPhotoIndex=$(($currPhotoIndex+1)) + fi + fi + fi + done +done \ No newline at end of file diff --git a/programs/utils/dune-flir-duor-temp-logs.cpp b/programs/utils/dune-flir-duor-temp-logs.cpp new file mode 100644 index 0000000000..135d3fe7ff --- /dev/null +++ b/programs/utils/dune-flir-duor-temp-logs.cpp @@ -0,0 +1,350 @@ +//#*************************************************************************** +//# Copyright (C) 2018 Laboratório de Sistemas e Tecnologia Subaquática * +//# Departamento de Engenharia Electrotécnica e de Computadores * +//# Rua Dr. Roberto Frias, 4200-465 Porto, Portugal * +//#*************************************************************************** +//# Author: Pedro Gonçalves * +//#*************************************************************************** + +#include +#include +#include +#include +#include +#include +#include + +#include "../../vendor/libraries/exiftool/ExifTool.h" + +using namespace std; + +string readInfo[] = +{ "ReflectedApparentTemperature", "AtmosphericTemperature", "ObjectDistance", + "RelativeHumidity", "Emissivity", "PlanckR1", "PlanckR2", "PlanckB", + "PlanckO", "PlanckF", "AtmosphericTransAlpha1", "AtmosphericTransAlpha2", + "AtmosphericTransBeta1", "AtmosphericTransBeta2", "AtmosphericTransX", + "RawValueMedian", "RawValueRange", "RawThermalImage" }; + + +string csvInfo[] = { + "DateTimeOriginal", "MAVRoll", "MAVPitch", "MAVYaw", "GPSAltitude", "GPSLatitude", "GPSLongitude" +}; + +map readInfoValues; +string lineInfo[256][2]; +int cntMetaData = 0; +int numberInfoToRead = 0; +char * fileName; +bool hasErrors = false; + +void +saveData(void); +void +calcTemp(void); +void +getRawThermalImage(char * path); +void +printCSVLine(char separator); +int +toUnixTime(string date_time); +float +toDecimalDegrees(string dms); + +ExifTool *et; + +map csvInfoValues; + +float averageTemp = 0; + +float tau; +float raw_atm; +float emmissivity; +float raw_refl; +float b; +float o; +float r1; +float r2; +float f; + +int +main(int argc, char **argv) +{ + if (argc < 2) + { + cout << "Please specify input fil e name" << endl; + return 1; + } + // create our ExifTool object + et = new ExifTool(); + // read metadata from the image + TagInfo *info = et->ImageInfo(argv[1]); + fileName = argv[1]; + + if (info) + { + for (TagInfo *i = info; i; i = i->next) + { + lineInfo[cntMetaData][0] = i->name; + lineInfo[cntMetaData++][1] = i->value; + //cout << "# " << i->name << " = " << i->value << endl; + } + delete info; + } + else if (et->LastComplete() <= 0) + { + cerr << "Error executing exiftool!" << endl; + } + + saveData(); + calcTemp(); + getRawThermalImage(argv[1]); + + printCSVLine(','); + + // print exiftool stderr messages + char *err = et->GetError(); + if (err) { + cerr << err; + hasErrors = true; + } + + delete et; // delete our ExifTool object + return 0; +} + +void +saveData(void) +{ + numberInfoToRead = sizeof(readInfo) / sizeof(readInfo[0]); + + bool haveInfo = false; + + string * currLine = nullptr; + + for (int t = 0; t < cntMetaData; t++) + { + if ((currLine = find(readInfo, readInfo + numberInfoToRead, lineInfo[t][0])) != readInfo + numberInfoToRead) + { + haveInfo = true; + try + { + readInfoValues.insert(pair(*currLine,stof(lineInfo[t][1]))); + //cout << "# " << *currLine << " = " << lineInfo[t][1] << endl; + } + catch (...) + { + //cout << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; + //cout << lineInfo[t][1] << endl; + } + } + //cout << "Line Info: " << lineInfo[t][0] << " Find Result: " << find(csvInfo, csvInfo + sizeof(csvInfo), lineInfo[t][0]) << " CSVINFO: " << csvInfo << " CSVINFO + size off: " << csvInfo + sizeof(csvInfo) << endl; + if(find(csvInfo, csvInfo+7, lineInfo[t][0]) != csvInfo+7) + { + csvInfoValues[lineInfo[t][0]] = lineInfo[t][1]; + //cout << "Inserted: " << lineInfo[t][0] << " with value: " << lineInfo[t][1] << endl; + } + if ((t == cntMetaData - 1) && !haveInfo) + { + //cout << *currLine << " = Not found " << endl; + } + } +} + +void +calcTemp(void) +{ + //# get parameters from meta data + /* + ReflectedApparentTemperature = 0 | AtmosphericTemperature = 1 + ObjectDistance = 2 | RelativeHumidity = 3 + Emissivity = 4 | PlanckR1 = 5 + PlanckR2 = 6 | PlanckB = 7 + PlanckO = 8 | PlanckF = 9 + AtmosphericTransAlpha1 = 10 | AtmosphericTransAlpha2 = 11 + AtmosphericTransBeta1 = 12 | AtmosphericTransBeta2 = 13 + AtmosphericTransX = 14 | RawValueMedian = 15 + RawValueRange = 16 | RawThermalImage = 17 + */ + + float temp_ref = readInfoValues["ReflectedApparentTemperature"]; + float temp_atm = readInfoValues["AtmosphericTemperature"]; + float distance = readInfoValues["ObjectDistance"]; + float humidity = readInfoValues["RelativeHumidity"] / 100.0; + emmissivity = readInfoValues["Emissivity"]; + r1 = readInfoValues["PlanckR1"]; + r2 = readInfoValues["PlanckR2"]; + b = readInfoValues["PlanckB"]; + o = readInfoValues["PlanckO"]; + f = readInfoValues["PlanckF"]; + float a1 = readInfoValues["AtmosphericTransAlpha1"]; + float a2 = readInfoValues["AtmosphericTransAlpha2"]; + float b1 = readInfoValues["AtmosphericTransBeta1"]; + float b2 = readInfoValues["AtmosphericTransBeta2"]; + float x = readInfoValues["AtmosphericTransX"]; + + // Raw temperature range from FLIR + float raw_max = readInfoValues["RawValueMedian"] + (readInfoValues["RawValueRange"] / 2); + float raw_min = raw_max - readInfoValues["RawValueRange"]; + + // Calculate atmospheric transmission + float h2o = (humidity / 100) + * exp( + 1.5587 + 6.939e-2 * temp_atm - 2.7816e-4 * pow(temp_atm, 2) + + 6.8455e-7 * pow(temp_atm, 3)); + tau = x * exp(-sqrt(distance) * (a1 + b1 * sqrt(h2o))) + + (1 - x) * exp(-sqrt(distance) * (a2 + b2 * sqrt(h2o))); + + // Radiance from atmosphere + // The camera is reporting the ambient temp as -273.15 deg celsius + try + { + raw_atm = (r1 / (r2 * (exp(b / (temp_atm + 273.15)) - f))) - o; + } + catch (...) + { + cerr << "ZeroDivisionError" << endl; + raw_atm = -o; + } + + // Radiance from reflected objects + raw_refl = r1 / (r2 * (exp(b / (temp_ref + 273.15)) - f)) - o; + + // get displayed object temp max/min + float raw_max_obj = (raw_max - (1 - tau) * raw_atm + - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + float raw_min_obj = (raw_min - (1 - tau) * raw_atm + - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + + float temp_min = b / log(r1 / (r2 * (raw_min_obj + o)) + f) - 273.15; + float temp_max = b / log(r1 / (r2 * (raw_max_obj + o)) + f) - 273.15; +} + +void +getRawThermalImage(char * path) +{ + //Raw thermal image + TagInfo *infoEP = et->ImageInfo(path, "\n-RawThermalImage\n-b\n"); + int cnt = 0; + for (TagInfo *i = infoEP; i; i = i->next) + { + if (cnt == 1) + { + int bufferMat[120][160]; + float bufferMatFloat[120][160]; + char *buff; + buff = i->value; + double sizeBuf = i->valueLen; + int start = sizeBuf - (120 * (2 * 160)); + int r = 0; + int c = 0; + for (int t = start; t < sizeBuf; t = t + 2) + { + bufferMat[r][c] = (buff[t + 1] << 8) | (buff[t] & 0xff); + c++; + if (c > 159) + { + c = 0; + r++; + } + } + + for (int i = 0; i < 120; i++) + for (int t = 0; t < 160; t++) + bufferMatFloat[i][t] = (bufferMat[i][t] - (1 - tau) * raw_atm + - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + + for (int i = 0; i < 120; i++) + for (int t = 0; t < 160; t++) + bufferMatFloat[i][t] = (b + / log(r1 / (r2 * (bufferMatFloat[i][t] + o)) + f) - 273.15); + + double med = 0; + + for (int i = 0; i < 120; i++) + for (int t = 0; t < 160; t++) + med = med + bufferMatFloat[i][t]; + + averageTemp = med / (120 * 160); + } + cnt++; + } +} + +void +printCSVLine(char separtor) { + string altitudeStr = csvInfoValues["GPSAltitude"]; + float altitude = stof(altitudeStr.substr(0,altitudeStr.find(' '))); + cout.precision(8); + cout << endl << fixed + //<< csvInfoValues["DateTimeOriginal"] << separtor + << toUnixTime(csvInfoValues["DateTimeOriginal"]) << separtor + << toDecimalDegrees(csvInfoValues["GPSLatitude"]) << separtor + << toDecimalDegrees(csvInfoValues["GPSLongitude"]) << separtor; + cout.unsetf(std::ios::fixed); + cout.precision(6); + cout << altitude << separtor + << csvInfoValues["MAVRoll"] << separtor + << csvInfoValues["MAVPitch"] << separtor + << csvInfoValues["MAVYaw"] << separtor + << averageTemp; +} + +int +toUnixTime(string date_time) { + time_t rawtime; + struct tm * timeinfo; + + int year = stoi(date_time.substr(0, 4)), + month = stoi(date_time.substr(5, 7)), + day = stoi(date_time.substr(8, 10)), + hour = stoi(date_time.substr(11, 13)), + min = stoi(date_time.substr(14, 16)), + sec = stoi(date_time.substr(17, 19)), + gmtOffset = stoi(date_time.substr(23,26)); + + /* get current timeinfo: */ + time(&rawtime); //or: rawtime = time(0); + /* convert to struct: */ + timeinfo = localtime(&rawtime); + + /* now modify the timeinfo to the given date: */ + timeinfo->tm_year = year - 1900; + timeinfo->tm_mon = month - 1; //months since January - [0,11] + timeinfo->tm_mday = day; //day of the month - [1,31] + timeinfo->tm_hour = hour; //hours since midnight - [0,23] + timeinfo->tm_min = min; //minutes after the hour - [0,59] + timeinfo->tm_sec = sec; //seconds after the minute - [0,59] + + rawtime = timegm(timeinfo); + + //account GTM offset hours to be true GMT 1 hour = 3600 sec + rawtime -= gmtOffset * 3600; + + /* call gmtime: create unix time stamp from timeinfo struct in gmt time*/ + //int date = gmtime(rawtime); + return rawtime; +} + +float +toDecimalDegrees(string dms) { + //0 deg 0' 0.00" N + + int degreeDelimiter = dms.find(' '); + int minutesPos = degreeDelimiter + 5; + int minuteDelimiter = dms.find('\''); + int secondsPos = minuteDelimiter + 2; + int secondsDelimiter = dms.find('"'); + + float result = stoi(dms.substr(0, minutesPos-1)); + //cout << dms.substr(minutesPos, minuteDelimiter - minutesPos) << endl; + result += stoi(dms.substr(minutesPos, minuteDelimiter - minutesPos)) / 60.0; + //cout << dms.substr(secondsPos, secondsDelimiter - secondsPos) << endl; + result += stof(dms.substr(secondsPos, secondsDelimiter - secondsPos)) / 3600.0; + + if (dms.find('W') != string::npos || dms.find('S') != string::npos) + { + return -result; + } else { + return result; + } +} \ No newline at end of file From da698039f0a1e9c73b3ba4f6c282126de62938f3 Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Wed, 27 Feb 2019 18:50:09 +0000 Subject: [PATCH 4/9] programs/utils: Add coments, add date compatibility check and copyright in script, removed old code for thermal image analisys --- programs/scripts/dune-flir-temp-processing.sh | 44 ++- programs/utils/dune-flir-duo-temp.cpp | 280 ------------------ programs/utils/dune-flir-duor-temp-logs.cpp | 3 +- 3 files changed, 42 insertions(+), 285 deletions(-) delete mode 100644 programs/utils/dune-flir-duo-temp.cpp diff --git a/programs/scripts/dune-flir-temp-processing.sh b/programs/scripts/dune-flir-temp-processing.sh index c3bb73b895..3adf37a7a1 100644 --- a/programs/scripts/dune-flir-temp-processing.sh +++ b/programs/scripts/dune-flir-temp-processing.sh @@ -1,4 +1,32 @@ #!/bin/sh +############################################################################ +# Copyright 2007-2019 Universidade do Porto - Faculdade de Engenharia # +# Laboratório de Sistemas e Tecnologia Subaquática (LSTS) # +############################################################################ +# This file is part of DUNE: Unified Navigation Environment. # +# # +# Commercial Licence Usage # +# Licencees holding valid commercial DUNE licences may use this file in # +# accordance with the commercial licence agreement provided with the # +# Software or, alternatively, in accordance with the terms contained in a # +# written agreement between you and Faculdade de Engenharia da # +# Universidade do Porto. For licensing terms, conditions, and further # +# information contact lsts@fe.up.pt. # +# # +# Modified European Union Public Licence - EUPL v.1.1 Usage # +# Alternatively, this file may be used under the terms of the Modified # +# EUPL, Version 1.1 only (the "Licence"), appearing in the file LICENCE.md # +# included in the packaging of this file. You may not use this work # +# except in compliance with the Licence. Unless required by applicable # +# law or agreed to in writing, software distributed under the Licence is # +# distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF # +# ANY KIND, either express or implied. See the Licence for the specific # +# language governing permissions and limitations at # +# https://github.com/LSTS/dune/blob/master/LICENCE.md and # +# http://ec.europa.eu/idabc/eupl.html. # +############################################################################ +# Author: Eduardo Ramos # +############################################################################ CSVName="FlirThermalData.csv" @@ -45,7 +73,9 @@ findLog() { done } + getEpochFilename() { + # $1 is in the format "YYYYMMDD_hhmmss_(3 digits for miliseconds)" local name=$1 local year=`echo $name | cut -b 1-4` local month=`echo $name | cut -b 5-6` @@ -53,8 +83,8 @@ getEpochFilename() { local hour=`echo $name | cut -b 10-11` local minute=`echo $name | cut -b 12-13` local second=`echo $name | cut -b 14-15` - # date "+%s" -d "2013-02-20 08:41:15" local date="$year-$month-$day $hour:$minute:$second" + # date "+%s" -d "2013-02-20 08:41:15" epochFilename="$(date +%s -d "$date")".`echo $name | cut -b 17-19`.jpg } @@ -139,9 +169,15 @@ then exit 1 fi +if [ $START \> $END ] || [ $START = $END ] +then + printf 'The start(-s) and end(-e) dates are not compatible\n\n' + exit 1 +fi + noSave=0 nextLog="0" -savePath="$(printf %s$CSVNAme $OUTPUT)" +savePath="$(printf %s$CSVName $OUTPUT)" epochFilename="" currPhotosDir="" currPhotoIndex=0 @@ -194,7 +230,7 @@ for top_filename in $DEVICE/*; do if [ -e $savePath -a $NOREPLACE ] || [ -z $currDayPath ] || [ -z $currTimePath ]; then noSave=1 - printf "Skipped: $filename\n" + printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" continue else noSave=0 @@ -209,7 +245,7 @@ for top_filename in $DEVICE/*; do # check if the file should be processed if [ $noSave = 1 ]; then - printf "Skipped: $filename\n" + printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" continue fi diff --git a/programs/utils/dune-flir-duo-temp.cpp b/programs/utils/dune-flir-duo-temp.cpp deleted file mode 100644 index 93f600ef8a..0000000000 --- a/programs/utils/dune-flir-duo-temp.cpp +++ /dev/null @@ -1,280 +0,0 @@ -//*************************************************************************** -// Copyright 2007-2019 Universidade do Porto - Faculdade de Engenharia * -// Laboratório de Sistemas e Tecnologia Subaquática (LSTS) * -//*************************************************************************** -// This file is part of DUNE: Unified Navigation Environment. * -// * -// Commercial Licence Usage * -// Licencees holding valid commercial DUNE licences may use this file in * -// accordance with the commercial licence agreement provided with the * -// Software or, alternatively, in accordance with the terms contained in a * -// written agreement between you and Faculdade de Engenharia da * -// Universidade do Porto. For licensing terms, conditions, and further * -// information contact lsts@fe.up.pt. * -// * -// Modified European Union Public Licence - EUPL v.1.1 Usage * -// Alternatively, this file may be used under the terms of the Modified * -// EUPL, Version 1.1 only (the "Licence"), appearing in the file LICENCE.md * -// included in the packaging of this file. You may not use this work * -// except in compliance with the Licence. Unless required by applicable * -// law or agreed to in writing, software distributed under the Licence is * -// distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF * -// ANY KIND, either express or implied. See the Licence for the specific * -// language governing permissions and limitations at * -// https://github.com/LSTS/dune/blob/master/LICENCE.md and * -// http://ec.europa.eu/idabc/eupl.html. * -//*************************************************************************** -//# Author: Pedro Gonçalves * -//#************************************************************************** -// Calculate image temperature (FlirDuoRTemp). * -//*************************************************************************** - -#include -#include -#include -#include -#include - -#include "../../vendor/libraries/exiftool/ExifTool.h" -#include "../../vendor/libraries/exiftool/ConsoleColor.hpp" - -using namespace std; - -string readInfo[] = - { "ReflectedApparentTemperature", "AtmosphericTemperature", "ObjectDistance", - "RelativeHumidity", "Emissivity", "PlanckR1", "PlanckR2", "PlanckB", - "PlanckO", "PlanckF", "AtmosphericTransAlpha1", "AtmosphericTransAlpha2", - "AtmosphericTransBeta1", "AtmosphericTransBeta2", "AtmosphericTransX", - "RawValueMedian", "RawValueRange", "RawThermalImage" }; - -float readInfoValues[32]; -string lineInfo[256][2]; -int cntMetaData = 0; -int numberInfoToRead = 0; -char * fileName; - -void -printData(void); -void -calcTemp(void); -void -getRawThermalImage(char * path); - -ExifTool *et; - -float tau; -float raw_atm; -float emmissivity; -float raw_refl; -float b; -float o; -float r1; -float r2; -float f; - -Color::Modifier green(Color::FG_GREEN); -Color::Modifier blue(Color::FG_BLUE); -Color::Modifier red(Color::FG_RED); -Color::Modifier def(Color::FG_DEFAULT); - -int -main(int argc, char **argv) -{ - if (argc < 2) - { - cout << "Please specify input file name" << endl; - return 1; - } - // create our ExifTool object - et = new ExifTool(); - // read metadata from the image - TagInfo *info = et->ImageInfo(argv[1]); - fileName = argv[1]; - - cout << green; - if (info) - { - for (TagInfo *i = info; i; i = i->next) - { - lineInfo[cntMetaData][0] = i->name; - lineInfo[cntMetaData++][1] = i->value; - cout << "# " << i->name << " = " << i->value << endl; - } - delete info; - } - else if (et->LastComplete() <= 0) - { - cerr << "Error executing exiftool!" << endl; - } - - cout << def << endl << endl; - - printData(); - calcTemp(); - getRawThermalImage(argv[1]); - - // print exiftool stderr messages - char *err = et->GetError(); - if (err) - cout << err; - - delete et; // delete our ExifTool object - return 0; -} - -void -printData(void) -{ - numberInfoToRead = sizeof(readInfo) / sizeof(readInfo[0]); - bool haveInfo = false; - - for (int i = 0; i < numberInfoToRead; i++) - { - haveInfo = false; - for (int t = 0; t < cntMetaData; t++) - { - if (lineInfo[t][0].compare(readInfo[i]) == 0) - { - haveInfo = true; - try - { - readInfoValues[i] = stof(lineInfo[t][1]); - cout << "# " << readInfo[i] << " = " << lineInfo[t][1] << endl; - } - catch (...) - { - - cout << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; - cout << "# " << lineInfo[t][1] << endl; - } - } - if ((t == cntMetaData - 1) && !haveInfo) - { - cout << readInfo[i] << " = Not found " << endl; - } - } - } -} - -void -calcTemp(void) -{ - //# get parameters from meta data - /* - ReflectedApparentTemperature = 0 | AtmosphericTemperature = 1 - ObjectDistance = 2 | RelativeHumidity = 3 - Emissivity = 4 | PlanckR1 = 5 - PlanckR2 = 6 | PlanckB = 7 - PlanckO = 8 | PlanckF = 9 - AtmosphericTransAlpha1 = 10 | AtmosphericTransAlpha2 = 11 - AtmosphericTransBeta1 = 12 | AtmosphericTransBeta2 = 13 - AtmosphericTransX = 14 | RawValueMedian = 15 - RawValueRange = 16 | RawThermalImage = 17 - */ - - float temp_ref = readInfoValues[0]; - float temp_atm = readInfoValues[1]; - float distance = readInfoValues[2]; - float humidity = readInfoValues[3] / 100.0; - emmissivity = readInfoValues[4]; - r1 = readInfoValues[5]; - r2 = readInfoValues[6]; - b = readInfoValues[7]; - o = readInfoValues[8]; - f = readInfoValues[9]; - float a1 = readInfoValues[10]; - float a2 = readInfoValues[11]; - float b1 = readInfoValues[12]; - float b2 = readInfoValues[13]; - float x = readInfoValues[14]; - - // Raw temperature range from FLIR - float raw_max = readInfoValues[15] + (readInfoValues[16] / 2); - float raw_min = raw_max - readInfoValues[16]; - - // Calculate atmospheric transmission - float h2o = (humidity / 100) - * exp( - 1.5587 + 6.939e-2 * temp_atm - 2.7816e-4 * pow(temp_atm, 2) - + 6.8455e-7 * pow(temp_atm, 3)); - tau = x * exp(-sqrt(distance) * (a1 + b1 * sqrt(h2o))) - + (1 - x) * exp(-sqrt(distance) * (a2 + b2 * sqrt(h2o))); - - // Radiance from atmosphere - // The camera is reporting the ambient temp as -273.15 deg celsius - try - { - raw_atm = (r1 / (r2 * (exp(b / (temp_atm + 273.15)) - f))) - o; - } - catch (...) - { - cout << "ZeroDivisionError" << endl; - raw_atm = -o; - } - - // Radiance from reflected objects - raw_refl = r1 / (r2 * (exp(b / (temp_ref + 273.15)) - f)) - o; - - // get displayed object temp max/min - float raw_max_obj = (raw_max - (1 - tau) * raw_atm - - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; - float raw_min_obj = (raw_min - (1 - tau) * raw_atm - - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; - - float temp_min = b / log(r1 / (r2 * (raw_min_obj + o)) + f) - 273.15; - float temp_max = b / log(r1 / (r2 * (raw_max_obj + o)) + f) - 273.15; -} - -void -getRawThermalImage(char * path) -{ - //Raw thermal image - TagInfo *infoEP = et->ImageInfo(path, "\n-RawThermalImage\n-b\n"); - int cnt = 0; - for (TagInfo *i = infoEP; i; i = i->next) - { - if (cnt == 1) - { - int bufferMat[120][160]; - float bufferMatFloat[120][160]; - char *buff; - buff = i->value; - double sizeBuf = i->valueLen; - int start = sizeBuf - (120 * (2 * 160)); - int r = 0; - int c = 0; - for (int t = start; t < sizeBuf; t = t + 2) - { - bufferMat[r][c] = (buff[t + 1] << 8) | (buff[t] & 0xff); - c++; - if (c > 159) - { - c = 0; - r++; - } - } - - for (int i = 0; i < 120; i++) - for (int t = 0; t < 160; t++) - bufferMatFloat[i][t] = (bufferMat[i][t] - (1 - tau) * raw_atm - - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; - - for (int i = 0; i < 120; i++) - for (int t = 0; t < 160; t++) - bufferMatFloat[i][t] = (b - / log(r1 / (r2 * (bufferMatFloat[i][t] + o)) + f) - 273.15); - - double med = 0; - - for (int i = 0; i < 120; i++) - for (int t = 0; t < 160; t++) - med = med + bufferMatFloat[i][t]; - - std::cout.precision(8); - cout << endl << blue << "# " << green << fileName << blue << " # Pos calc > Temperature med of picture: " << red << med / (120 * 160) << " ºC" << def << endl << endl; - - } - cnt++; - } -} - diff --git a/programs/utils/dune-flir-duor-temp-logs.cpp b/programs/utils/dune-flir-duor-temp-logs.cpp index 135d3fe7ff..aaf9cd0361 100644 --- a/programs/utils/dune-flir-duor-temp-logs.cpp +++ b/programs/utils/dune-flir-duor-temp-logs.cpp @@ -18,6 +18,7 @@ using namespace std; +// information used to calculate image temperature string readInfo[] = { "ReflectedApparentTemperature", "AtmosphericTemperature", "ObjectDistance", "RelativeHumidity", "Emissivity", "PlanckR1", "PlanckR2", "PlanckB", @@ -25,7 +26,7 @@ string readInfo[] = "AtmosphericTransBeta1", "AtmosphericTransBeta2", "AtmosphericTransX", "RawValueMedian", "RawValueRange", "RawThermalImage" }; - +// output information to save on CSV string csvInfo[] = { "DateTimeOriginal", "MAVRoll", "MAVPitch", "MAVYaw", "GPSAltitude", "GPSLatitude", "GPSLongitude" }; From 995b6ed4b4b827f616acdc039587457b88f9208c Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Wed, 6 Mar 2019 11:30:03 +0000 Subject: [PATCH 5/9] programs/utils: Add dynamic image size, Fix epoch time in file copy, Rename script and cpp files. --- programs/scripts/dune-flir-temp-processing.sh | 275 -------------- programs/scripts/dune-flir.sh | 298 +++++++++++++++ programs/utils/dune-flir-duor-temp-logs.cpp | 351 ------------------ programs/utils/dune-flir-metadata.cpp | 351 ++++++++++++++++++ 4 files changed, 649 insertions(+), 626 deletions(-) delete mode 100644 programs/scripts/dune-flir-temp-processing.sh create mode 100644 programs/scripts/dune-flir.sh delete mode 100644 programs/utils/dune-flir-duor-temp-logs.cpp create mode 100644 programs/utils/dune-flir-metadata.cpp diff --git a/programs/scripts/dune-flir-temp-processing.sh b/programs/scripts/dune-flir-temp-processing.sh deleted file mode 100644 index 3adf37a7a1..0000000000 --- a/programs/scripts/dune-flir-temp-processing.sh +++ /dev/null @@ -1,275 +0,0 @@ -#!/bin/sh -############################################################################ -# Copyright 2007-2019 Universidade do Porto - Faculdade de Engenharia # -# Laboratório de Sistemas e Tecnologia Subaquática (LSTS) # -############################################################################ -# This file is part of DUNE: Unified Navigation Environment. # -# # -# Commercial Licence Usage # -# Licencees holding valid commercial DUNE licences may use this file in # -# accordance with the commercial licence agreement provided with the # -# Software or, alternatively, in accordance with the terms contained in a # -# written agreement between you and Faculdade de Engenharia da # -# Universidade do Porto. For licensing terms, conditions, and further # -# information contact lsts@fe.up.pt. # -# # -# Modified European Union Public Licence - EUPL v.1.1 Usage # -# Alternatively, this file may be used under the terms of the Modified # -# EUPL, Version 1.1 only (the "Licence"), appearing in the file LICENCE.md # -# included in the packaging of this file. You may not use this work # -# except in compliance with the Licence. Unless required by applicable # -# law or agreed to in writing, software distributed under the Licence is # -# distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF # -# ANY KIND, either express or implied. See the Licence for the specific # -# language governing permissions and limitations at # -# https://github.com/LSTS/dune/blob/master/LICENCE.md and # -# http://ec.europa.eu/idabc/eupl.html. # -############################################################################ -# Author: Eduardo Ramos # -############################################################################ - -CSVName="FlirThermalData.csv" - -usage() { - echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c]" - echo " -s start start date fro which to start processing (yyyymmdd_hhmmss or partial)" - echo " -e end date at which to stop processing (yyyymmdd_hhmmss or partial)" - echo " -d device path to the device to be mounted in order to access the photos" - echo " -l logs vehicle logs path" - echo " -o output output path, overrides the logs parameter and outputs to a single file" - echo " -c copy enable file copying to logs" - echo " -jc just-copy disable image analisys and enable file copying to logs" - echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" - echo " -nr no-replace prevents the program from overwriting existing data files" -} - -inRange() { - [ "$START" \< "$1" ] && [ "$END" \> "$1" ] -} - -findLog() { - echo "In function with filename $1" - - currDayPath="" - nextDayPath="" - currTimePath="" - nextTimePath="" - - for fullDayFile in $LOGS/*; do - local dayFile=$(basename $fullDayFile) - for fullTimeFile in $LOGS/$dayFile/*; do - local timeFile=$(basename $fullTimeFile) - logfile="$(printf "%s_%s" $dayFile $timeFile)" - if [ $logfile \< $1 ] || [ $logfile = $1 ] - then - currDayPath=$dayFile - currTimePath=$timeFile - else - nextDayPath=$dayFile - nextTimePath=$timeFile - return - fi - done - done -} - - -getEpochFilename() { - # $1 is in the format "YYYYMMDD_hhmmss_(3 digits for miliseconds)" - local name=$1 - local year=`echo $name | cut -b 1-4` - local month=`echo $name | cut -b 5-6` - local day=`echo $name | cut -b 7-8` - local hour=`echo $name | cut -b 10-11` - local minute=`echo $name | cut -b 12-13` - local second=`echo $name | cut -b 14-15` - local date="$year-$month-$day $hour:$minute:$second" - # date "+%s" -d "2013-02-20 08:41:15" - epochFilename="$(date +%s -d "$date")".`echo $name | cut -b 17-19`.jpg -} - -getNextPhotoDir() { - for photoDir in $photosPath/*; do - currPhotoIndex="$(ls $photoDir | wc -l)" - if [ $currPhotoIndex -lt 1000 ]; - then - currPhotosDir=$photoDir - break - fi - done - # if all existing directories are full - if [ $currPhotoIndex -eq 1000 ]; - then - currPhotoIndex=0 - local newPhotoDirIndex=$(($(basename $photoDir)+1)) - local newFolder="$(printf "%.6d" $newPhotoDirIndex)" - currPhotosDir=$photosPath/$newFolder - mkdir $currPhotosDir - fi - - if [ "$(basename $photoDir)" = "*" ]; - then - currPhotoIndex=0 - currPhotosDir=$photosPath/000000 - mkdir $currPhotosDir - fi -} - -for i in "$@" -do -case $i in - -e=*|--extension=*) - END="${i#*=}" - shift # past argument=value - ;; - -s=*|--searchpath=*) - START="${i#*=}" - shift # past argument=value - ;; - -d=*|--device=*) - DEVICE="${i#*=}" - shift # past argument=value - ;; - -l=*|--logs=*) - LOGS="${i#*=}" - shift # past argument=value - ;; - -o=*|--output=*) - OUTPUT="${i#*=}" - shift # past argument=value - ;; - -c|--copy) - COPY="copy" - shift # past argument=value - ;; - -jc|--just-copy) - JUSTCOPY="justcopy" - shift # past argument=value - ;; - -fn=*|--file_name=*) - CSVName="${i#*=}" - shift # past argument=value - ;; - -nr|--no-replace) - NOREPLACE="noreplace" - shift # past argument=value - ;; - *) - printf "Unkown option %s\n\n" $i # unknown option - usage - exit 1 - ;; -esac -done - -if [ -z $DEVICE ] || [ -z $START ] || [ -z $END ] || [ -z $LOGS -a -z $OUTPUT ]; -then - printf 'Some required variables are not defined\n\n' - usage - exit 1 -fi - -if [ $START \> $END ] || [ $START = $END ] -then - printf 'The start(-s) and end(-e) dates are not compatible\n\n' - exit 1 -fi - -noSave=0 -nextLog="0" -savePath="$(printf %s$CSVName $OUTPUT)" -epochFilename="" -currPhotosDir="" -currPhotoIndex=0 - -if [ ! -z $OUTPUT ]; then -printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath -fi - -for top_filename in $DEVICE/*; do - # printf "OUTSIDE FOR: $top_filename\n" - -# find ../../test_photos -maxdepth 1 -regextype posix-extended -regex [.\/a-z\_]*/[0-9]{8}_[0-9]{6}$ - - if [ -n "$(echo $top_filename | sed -E '/[.\/a-z\_]*\/[0-9]{8}_[0-9]{6}$/g')" ]; - then - printf "Skipped file: $top_filename; For not keeping the naming standard\n" - continue - fi - for filename in $top_filename/*; do - # printf "INNER FOR: $filename\n" - truncFilename="$(basename $filename _R.jpg)" - truncFilename=${truncFilename%_*} - # check if the curr file is in the specified range - if inRange $truncFilename; - then - # printf "IS IN RANGE: $truncFilename\n" - day=${truncFilename%_*} - inputTime=${truncFilename#*_} - # check if the next log file should be used to store info about the current file - # printf "NEXTLOG: $nextLog\n" - # printf "OUTPUT: $OUTPUT\n" - if [ $truncFilename \> $nextLog -o $truncFilename = $nextLog ] && [ -z $OUTPUT ]; - then - findLog $truncFilename - nextLog="$(printf "%s_%s" $nextDayPath $nextTimePath)" - generalPath=$LOGS/$currDayPath/$currTimePath - savePath=$generalPath/$CSVName - photosPath=$generalPath/Photos - # verify if there is already a photos directory in curr log - if [ ! -d $photosPath ] && [ -n $COPY ]; - then - currPhotosDir=$photosPath/000000 - mkdir -p $currPhotosDir - currPhotoIndex=0 - else - getNextPhotoDir - fi - # verify if there exists a data file already present and don't overwrite if the options is present - # check also if there is a valid log file to save the items - if [ -e $savePath -a $NOREPLACE ] || [ -z $currDayPath ] || [ -z $currTimePath ]; - then - noSave=1 - printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" - continue - else - noSave=0 - fi - - if [ -z $JUSTCOPY ]; - then - # add header to the new data.csv file - printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath - fi - fi - # check if the file should be processed - if [ $noSave = 1 ]; - then - printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" - continue - fi - - # process file - if [ -z $JUSTCOPY ]; - then - ./dune-flir-duor-temp-logs $filename >> $savePath - printf "Processed $filename\n" - printf "Saving to: %s\n" $savePath - fi - - # copy files to log if option is present - if [ $COPY ] || [ $JUSTCOPY ]; - then - getEpochFilename $(basename $filename _R.jpg) - cp -p $filename $currPhotosDir/$epochFilename - printf "Coppied $filename to $currPhotosDir/$epochFilename\n" - if [ $currPhotoIndex -eq 999 ]; - then - getNextPhotoDir - else - currPhotoIndex=$(($currPhotoIndex+1)) - fi - fi - fi - done -done \ No newline at end of file diff --git a/programs/scripts/dune-flir.sh b/programs/scripts/dune-flir.sh new file mode 100644 index 0000000000..f113455366 --- /dev/null +++ b/programs/scripts/dune-flir.sh @@ -0,0 +1,298 @@ +#!/bin/sh +############################################################################ +# Copyright 2007-2019 Universidade do Porto - Faculdade de Engenharia # +# Laboratório de Sistemas e Tecnologia Subaquática (LSTS) # +############################################################################ +# This file is part of DUNE: Unified Navigation Environment. # +# # +# Commercial Licence Usage # +# Licencees holding valid commercial DUNE licences may use this file in # +# accordance with the commercial licence agreement provided with the # +# Software or, alternatively, in accordance with the terms contained in a # +# written agreement between you and Faculdade de Engenharia da # +# Universidade do Porto. For licensing terms, conditions, and further # +# information contact lsts@fe.up.pt. # +# # +# Modified European Union Public Licence - EUPL v.1.1 Usage # +# Alternatively, this file may be used under the terms of the Modified # +# EUPL, Version 1.1 only (the "Licence"), appearing in the file LICENCE.md # +# included in the packaging of this file. You may not use this work # +# except in compliance with the Licence. Unless required by applicable # +# law or agreed to in writing, software distributed under the Licence is # +# distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF # +# ANY KIND, either express or implied. See the Licence for the specific # +# language governing permissions and limitations at # +# https://github.com/LSTS/dune/blob/master/LICENCE.md and # +# http://ec.europa.eu/idabc/eupl.html. # +############################################################################ +# Author: Eduardo Ramos # +############################################################################ + +CSVName="FlirThermalData.csv" +execPath="./dune-flir-metadata" + +usage() { + echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c]" + echo " -s start start date fro which to start processing (yyyymmdd_hhmmss or partial)" + echo " -e end date at which to stop processing (yyyymmdd_hhmmss or partial)" + echo " -d device path to the device to be mounted in order to access the photos" + echo " -l logs vehicle logs path" + echo " -o output output path, overrides the logs parameter and outputs to a single file" + echo " -c copy enable file copying to logs" + echo " -jc just-copy disable image analisys and enable file copying to logs" + echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" + echo " -nr no-replace prevents the program from overwriting existing data files" +} + +inRange() { + [ "$START" \< "$1" ] && [ "$END" \> "$1" ] +} + +findLog() { + echo "In function with filename $1" + + currDayPath="" + nextDayPath="" + currTimePath="" + nextTimePath="" + + for fullDayFile in $LOGS/*; do + local dayFile=$(basename $fullDayFile) + for fullTimeFile in $LOGS/$dayFile/*; do + local timeFile=$(basename $fullTimeFile) + logfile="$(printf "%s_%s" $dayFile $timeFile)" + if [ $logfile \< $1 ] || [ $logfile = $1 ] + then + currDayPath=$dayFile + currTimePath=$timeFile + else + nextDayPath=$dayFile + nextTimePath=$timeFile + return + fi + done + done +} + + +getEpochFilename() { + # $1 is in the format "YYYYMMDD_hhmmss_(3 digits for miliseconds)" + local name=$1 + local year=`echo $name | cut -b 1-4` + local month=`echo $name | cut -b 5-6` + local day=`echo $name | cut -b 7-8` + local hour=`echo $name | cut -b 10-11` + local minute=`echo $name | cut -b 12-13` + local second=`echo $name | cut -b 14-15` + local date="$year-$month-$day $hour:$minute:$second" + # date "+%s" -d "2013-02-20 08:41:15" + local epochTime=$(date +%s -d "$date") + if [ -z $UTCOffset ]; + then + UTCOffset=0 + fi + epochFilename="$(($epochTime+$UTCOffset))".`echo $name | cut -b 17-19`.jpg +} + +fillUTCOffset() { + getEpochFilename $(basename $filename _R.jpg) + local tempTime=${epochFilename%%\.*} + # printf "\n\ntempTime: $tempTime\n" + local output="$($execPath $filename)" + # printf "\n\nOutput: $output\n" + local tempUTCTime="${output%%,*}" + # printf "\n\nOutputTime: $tempUTCTime\n" + # printf "\n\noffset: %d\n" $(($tempUTCTime - $tempTime)) + UTCOffset=$(($tempUTCTime - $tempTime)) +} + +getNextPhotoDir() { + for photoDir in $photosPath/*; do + currPhotoIndex="$(ls $photoDir | wc -l)" + if [ $currPhotoIndex -lt 1000 ]; + then + currPhotosDir=$photoDir + break + fi + done + # if all existing directories are full + if [ $currPhotoIndex -eq 1000 ]; + then + currPhotoIndex=0 + local newPhotoDirIndex=$(($(basename $photoDir)+1)) + local newFolder="$(printf "%.6d" $newPhotoDirIndex)" + currPhotosDir=$photosPath/$newFolder + mkdir $currPhotosDir + fi + + if [ "$(basename $photoDir)" = "*" ]; + then + currPhotoIndex=0 + currPhotosDir=$photosPath/000000 + mkdir $currPhotosDir + fi +} + +for i in "$@" +do +case $i in + -e=*|--extension=*) + END="${i#*=}" + shift # past argument=value + ;; + -s=*|--searchpath=*) + START="${i#*=}" + shift # past argument=value + ;; + -d=*|--device=*) + DEVICE="${i#*=}" + shift # past argument=value + ;; + -l=*|--logs=*) + LOGS="${i#*=}" + shift # past argument=value + ;; + -o=*|--output=*) + OUTPUT="${i#*=}" + shift # past argument=value + ;; + -c|--copy) + COPY="copy" + shift # past argument=value + ;; + -jc|--just-copy) + JUSTCOPY="justcopy" + shift # past argument=value + ;; + -fn=*|--file_name=*) + CSVName="${i#*=}" + shift # past argument=value + ;; + -nr|--no-replace) + NOREPLACE="noreplace" + shift # past argument=value + ;; + *) + printf "Unkown option %s\n\n" $i # unknown option + usage + exit 1 + ;; +esac +done + +if [ -z $DEVICE ] || [ -z $START ] || [ -z $END ] || [ -z $LOGS -a -z $OUTPUT ]; +then + printf 'Some required variables are not defined\n\n' + usage + exit 1 +fi + +if [ $START \> $END ] || [ $START = $END ] +then + printf 'The start(-s) and end(-e) dates are not compatible\n\n' + exit 1 +fi + +noSave=0 +nextLog="0" +savePath="$(printf %s$CSVName $OUTPUT)" +epochFilename="" +currPhotosDir="" +currPhotoIndex=0 + +if [ ! -z $OUTPUT ]; then +printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath +fi + +for top_filename in $DEVICE/*; do + # printf "OUTSIDE FOR: $top_filename\n" + +# find ../../test_photos -maxdepth 1 -regextype posix-extended -regex [.\/a-z\_]*/[0-9]{8}_[0-9]{6}$ + + if [ -n "$(echo $top_filename | sed -E '/[.\/a-z\_]*\/[0-9]{8}_[0-9]{6}$/g')" ]; + then + printf "Skipped file: $top_filename; For not keeping the naming standard\n" + continue + fi + for filename in $top_filename/*; do + # printf "INNER FOR: $filename\n" + truncFilename="$(basename $filename _R.jpg)" + truncFilename=${truncFilename%_*} + # check if the curr file is in the specified range + if inRange $truncFilename; + then + # printf "IS IN RANGE: $truncFilename\n" + day=${truncFilename%_*} + inputTime=${truncFilename#*_} + # check if the next log file should be used to store info about the current file + # printf "NEXTLOG: $nextLog\n" + # printf "OUTPUT: $OUTPUT\n" + if [ $truncFilename \> $nextLog -o $truncFilename = $nextLog ] && [ -z $OUTPUT ]; + then + findLog $truncFilename + nextLog="$(printf "%s_%s" $nextDayPath $nextTimePath)" + generalPath=$LOGS/$currDayPath/$currTimePath + savePath=$generalPath/$CSVName + photosPath=$generalPath/Photos + # verify if there is already a photos directory in curr log + if [ ! -d $photosPath ] && [ -n $COPY ]; + then + currPhotosDir=$photosPath/000000 + mkdir -p $currPhotosDir + currPhotoIndex=0 + else + getNextPhotoDir + fi + # verify if there exists a data file already present and don't overwrite if the options is present + # check also if there is a valid log file to save the items + if [ -e $savePath -a $NOREPLACE ] || [ -z $currDayPath ] || [ -z $currTimePath ]; + then + noSave=1 + printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" + continue + else + noSave=0 + fi + + if [ -z $JUSTCOPY ]; + then + # add header to the new data.csv file + printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath + fi + fi + # check if the file should be processed + if [ $noSave = 1 ]; + then + printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" + continue + fi + + # process file + if [ -z $JUSTCOPY ]; + then + $execPath $filename >> $savePath + printf "Processed $filename\n" + printf "Saving to: %s\n" $savePath + fi + + if [ -z $UTCOffset ]; + then + fillUTCOffset + fi + + # copy files to log if option is present + if [ $COPY ] || [ $JUSTCOPY ]; + then + getEpochFilename $(basename $filename _R.jpg) + cp -p $filename $currPhotosDir/$epochFilename + printf "Coppied $filename to $currPhotosDir/$epochFilename\n" + if [ $currPhotoIndex -eq 999 ]; + then + getNextPhotoDir + else + currPhotoIndex=$(($currPhotoIndex+1)) + fi + fi + fi + done +done \ No newline at end of file diff --git a/programs/utils/dune-flir-duor-temp-logs.cpp b/programs/utils/dune-flir-duor-temp-logs.cpp deleted file mode 100644 index aaf9cd0361..0000000000 --- a/programs/utils/dune-flir-duor-temp-logs.cpp +++ /dev/null @@ -1,351 +0,0 @@ -//#*************************************************************************** -//# Copyright (C) 2018 Laboratório de Sistemas e Tecnologia Subaquática * -//# Departamento de Engenharia Electrotécnica e de Computadores * -//# Rua Dr. Roberto Frias, 4200-465 Porto, Portugal * -//#*************************************************************************** -//# Author: Pedro Gonçalves * -//#*************************************************************************** - -#include -#include -#include -#include -#include -#include -#include - -#include "../../vendor/libraries/exiftool/ExifTool.h" - -using namespace std; - -// information used to calculate image temperature -string readInfo[] = -{ "ReflectedApparentTemperature", "AtmosphericTemperature", "ObjectDistance", - "RelativeHumidity", "Emissivity", "PlanckR1", "PlanckR2", "PlanckB", - "PlanckO", "PlanckF", "AtmosphericTransAlpha1", "AtmosphericTransAlpha2", - "AtmosphericTransBeta1", "AtmosphericTransBeta2", "AtmosphericTransX", - "RawValueMedian", "RawValueRange", "RawThermalImage" }; - -// output information to save on CSV -string csvInfo[] = { - "DateTimeOriginal", "MAVRoll", "MAVPitch", "MAVYaw", "GPSAltitude", "GPSLatitude", "GPSLongitude" -}; - -map readInfoValues; -string lineInfo[256][2]; -int cntMetaData = 0; -int numberInfoToRead = 0; -char * fileName; -bool hasErrors = false; - -void -saveData(void); -void -calcTemp(void); -void -getRawThermalImage(char * path); -void -printCSVLine(char separator); -int -toUnixTime(string date_time); -float -toDecimalDegrees(string dms); - -ExifTool *et; - -map csvInfoValues; - -float averageTemp = 0; - -float tau; -float raw_atm; -float emmissivity; -float raw_refl; -float b; -float o; -float r1; -float r2; -float f; - -int -main(int argc, char **argv) -{ - if (argc < 2) - { - cout << "Please specify input fil e name" << endl; - return 1; - } - // create our ExifTool object - et = new ExifTool(); - // read metadata from the image - TagInfo *info = et->ImageInfo(argv[1]); - fileName = argv[1]; - - if (info) - { - for (TagInfo *i = info; i; i = i->next) - { - lineInfo[cntMetaData][0] = i->name; - lineInfo[cntMetaData++][1] = i->value; - //cout << "# " << i->name << " = " << i->value << endl; - } - delete info; - } - else if (et->LastComplete() <= 0) - { - cerr << "Error executing exiftool!" << endl; - } - - saveData(); - calcTemp(); - getRawThermalImage(argv[1]); - - printCSVLine(','); - - // print exiftool stderr messages - char *err = et->GetError(); - if (err) { - cerr << err; - hasErrors = true; - } - - delete et; // delete our ExifTool object - return 0; -} - -void -saveData(void) -{ - numberInfoToRead = sizeof(readInfo) / sizeof(readInfo[0]); - - bool haveInfo = false; - - string * currLine = nullptr; - - for (int t = 0; t < cntMetaData; t++) - { - if ((currLine = find(readInfo, readInfo + numberInfoToRead, lineInfo[t][0])) != readInfo + numberInfoToRead) - { - haveInfo = true; - try - { - readInfoValues.insert(pair(*currLine,stof(lineInfo[t][1]))); - //cout << "# " << *currLine << " = " << lineInfo[t][1] << endl; - } - catch (...) - { - //cout << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; - //cout << lineInfo[t][1] << endl; - } - } - //cout << "Line Info: " << lineInfo[t][0] << " Find Result: " << find(csvInfo, csvInfo + sizeof(csvInfo), lineInfo[t][0]) << " CSVINFO: " << csvInfo << " CSVINFO + size off: " << csvInfo + sizeof(csvInfo) << endl; - if(find(csvInfo, csvInfo+7, lineInfo[t][0]) != csvInfo+7) - { - csvInfoValues[lineInfo[t][0]] = lineInfo[t][1]; - //cout << "Inserted: " << lineInfo[t][0] << " with value: " << lineInfo[t][1] << endl; - } - if ((t == cntMetaData - 1) && !haveInfo) - { - //cout << *currLine << " = Not found " << endl; - } - } -} - -void -calcTemp(void) -{ - //# get parameters from meta data - /* - ReflectedApparentTemperature = 0 | AtmosphericTemperature = 1 - ObjectDistance = 2 | RelativeHumidity = 3 - Emissivity = 4 | PlanckR1 = 5 - PlanckR2 = 6 | PlanckB = 7 - PlanckO = 8 | PlanckF = 9 - AtmosphericTransAlpha1 = 10 | AtmosphericTransAlpha2 = 11 - AtmosphericTransBeta1 = 12 | AtmosphericTransBeta2 = 13 - AtmosphericTransX = 14 | RawValueMedian = 15 - RawValueRange = 16 | RawThermalImage = 17 - */ - - float temp_ref = readInfoValues["ReflectedApparentTemperature"]; - float temp_atm = readInfoValues["AtmosphericTemperature"]; - float distance = readInfoValues["ObjectDistance"]; - float humidity = readInfoValues["RelativeHumidity"] / 100.0; - emmissivity = readInfoValues["Emissivity"]; - r1 = readInfoValues["PlanckR1"]; - r2 = readInfoValues["PlanckR2"]; - b = readInfoValues["PlanckB"]; - o = readInfoValues["PlanckO"]; - f = readInfoValues["PlanckF"]; - float a1 = readInfoValues["AtmosphericTransAlpha1"]; - float a2 = readInfoValues["AtmosphericTransAlpha2"]; - float b1 = readInfoValues["AtmosphericTransBeta1"]; - float b2 = readInfoValues["AtmosphericTransBeta2"]; - float x = readInfoValues["AtmosphericTransX"]; - - // Raw temperature range from FLIR - float raw_max = readInfoValues["RawValueMedian"] + (readInfoValues["RawValueRange"] / 2); - float raw_min = raw_max - readInfoValues["RawValueRange"]; - - // Calculate atmospheric transmission - float h2o = (humidity / 100) - * exp( - 1.5587 + 6.939e-2 * temp_atm - 2.7816e-4 * pow(temp_atm, 2) - + 6.8455e-7 * pow(temp_atm, 3)); - tau = x * exp(-sqrt(distance) * (a1 + b1 * sqrt(h2o))) - + (1 - x) * exp(-sqrt(distance) * (a2 + b2 * sqrt(h2o))); - - // Radiance from atmosphere - // The camera is reporting the ambient temp as -273.15 deg celsius - try - { - raw_atm = (r1 / (r2 * (exp(b / (temp_atm + 273.15)) - f))) - o; - } - catch (...) - { - cerr << "ZeroDivisionError" << endl; - raw_atm = -o; - } - - // Radiance from reflected objects - raw_refl = r1 / (r2 * (exp(b / (temp_ref + 273.15)) - f)) - o; - - // get displayed object temp max/min - float raw_max_obj = (raw_max - (1 - tau) * raw_atm - - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; - float raw_min_obj = (raw_min - (1 - tau) * raw_atm - - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; - - float temp_min = b / log(r1 / (r2 * (raw_min_obj + o)) + f) - 273.15; - float temp_max = b / log(r1 / (r2 * (raw_max_obj + o)) + f) - 273.15; -} - -void -getRawThermalImage(char * path) -{ - //Raw thermal image - TagInfo *infoEP = et->ImageInfo(path, "\n-RawThermalImage\n-b\n"); - int cnt = 0; - for (TagInfo *i = infoEP; i; i = i->next) - { - if (cnt == 1) - { - int bufferMat[120][160]; - float bufferMatFloat[120][160]; - char *buff; - buff = i->value; - double sizeBuf = i->valueLen; - int start = sizeBuf - (120 * (2 * 160)); - int r = 0; - int c = 0; - for (int t = start; t < sizeBuf; t = t + 2) - { - bufferMat[r][c] = (buff[t + 1] << 8) | (buff[t] & 0xff); - c++; - if (c > 159) - { - c = 0; - r++; - } - } - - for (int i = 0; i < 120; i++) - for (int t = 0; t < 160; t++) - bufferMatFloat[i][t] = (bufferMat[i][t] - (1 - tau) * raw_atm - - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; - - for (int i = 0; i < 120; i++) - for (int t = 0; t < 160; t++) - bufferMatFloat[i][t] = (b - / log(r1 / (r2 * (bufferMatFloat[i][t] + o)) + f) - 273.15); - - double med = 0; - - for (int i = 0; i < 120; i++) - for (int t = 0; t < 160; t++) - med = med + bufferMatFloat[i][t]; - - averageTemp = med / (120 * 160); - } - cnt++; - } -} - -void -printCSVLine(char separtor) { - string altitudeStr = csvInfoValues["GPSAltitude"]; - float altitude = stof(altitudeStr.substr(0,altitudeStr.find(' '))); - cout.precision(8); - cout << endl << fixed - //<< csvInfoValues["DateTimeOriginal"] << separtor - << toUnixTime(csvInfoValues["DateTimeOriginal"]) << separtor - << toDecimalDegrees(csvInfoValues["GPSLatitude"]) << separtor - << toDecimalDegrees(csvInfoValues["GPSLongitude"]) << separtor; - cout.unsetf(std::ios::fixed); - cout.precision(6); - cout << altitude << separtor - << csvInfoValues["MAVRoll"] << separtor - << csvInfoValues["MAVPitch"] << separtor - << csvInfoValues["MAVYaw"] << separtor - << averageTemp; -} - -int -toUnixTime(string date_time) { - time_t rawtime; - struct tm * timeinfo; - - int year = stoi(date_time.substr(0, 4)), - month = stoi(date_time.substr(5, 7)), - day = stoi(date_time.substr(8, 10)), - hour = stoi(date_time.substr(11, 13)), - min = stoi(date_time.substr(14, 16)), - sec = stoi(date_time.substr(17, 19)), - gmtOffset = stoi(date_time.substr(23,26)); - - /* get current timeinfo: */ - time(&rawtime); //or: rawtime = time(0); - /* convert to struct: */ - timeinfo = localtime(&rawtime); - - /* now modify the timeinfo to the given date: */ - timeinfo->tm_year = year - 1900; - timeinfo->tm_mon = month - 1; //months since January - [0,11] - timeinfo->tm_mday = day; //day of the month - [1,31] - timeinfo->tm_hour = hour; //hours since midnight - [0,23] - timeinfo->tm_min = min; //minutes after the hour - [0,59] - timeinfo->tm_sec = sec; //seconds after the minute - [0,59] - - rawtime = timegm(timeinfo); - - //account GTM offset hours to be true GMT 1 hour = 3600 sec - rawtime -= gmtOffset * 3600; - - /* call gmtime: create unix time stamp from timeinfo struct in gmt time*/ - //int date = gmtime(rawtime); - return rawtime; -} - -float -toDecimalDegrees(string dms) { - //0 deg 0' 0.00" N - - int degreeDelimiter = dms.find(' '); - int minutesPos = degreeDelimiter + 5; - int minuteDelimiter = dms.find('\''); - int secondsPos = minuteDelimiter + 2; - int secondsDelimiter = dms.find('"'); - - float result = stoi(dms.substr(0, minutesPos-1)); - //cout << dms.substr(minutesPos, minuteDelimiter - minutesPos) << endl; - result += stoi(dms.substr(minutesPos, minuteDelimiter - minutesPos)) / 60.0; - //cout << dms.substr(secondsPos, secondsDelimiter - secondsPos) << endl; - result += stof(dms.substr(secondsPos, secondsDelimiter - secondsPos)) / 3600.0; - - if (dms.find('W') != string::npos || dms.find('S') != string::npos) - { - return -result; - } else { - return result; - } -} \ No newline at end of file diff --git a/programs/utils/dune-flir-metadata.cpp b/programs/utils/dune-flir-metadata.cpp new file mode 100644 index 0000000000..172e2438d6 --- /dev/null +++ b/programs/utils/dune-flir-metadata.cpp @@ -0,0 +1,351 @@ +//#*************************************************************************** +//# Copyright (C) 2018 Laboratório de Sistemas e Tecnologia Subaquática * +//# Departamento de Engenharia Electrotécnica e de Computadores * +//# Rua Dr. Roberto Frias, 4200-465 Porto, Portugal * +//#*************************************************************************** +//# Author: Pedro Gonçalves * +//#*************************************************************************** + +#include +#include +#include +#include +#include +#include +#include + +#include "../../vendor/libraries/exiftool/ExifTool.h" + +using namespace std; + +// information used to calculate image temperature +string readInfo[] = + {"ReflectedApparentTemperature", "AtmosphericTemperature", "ObjectDistance", + "RelativeHumidity", "Emissivity", "PlanckR1", "PlanckR2", "PlanckB", + "PlanckO", "PlanckF", "AtmosphericTransAlpha1", "AtmosphericTransAlpha2", + "AtmosphericTransBeta1", "AtmosphericTransBeta2", "AtmosphericTransX", + "RawValueMedian", "RawValueRange", + "RawThermalImageWidth", "RawThermalImageHeight"}; + +// output information to save on CSV +string csvInfo[] = { + "DateTimeOriginal", "MAVRoll", "MAVPitch", "MAVYaw", + "GPSAltitude", "GPSLatitude", "GPSLongitude"}; + +map readInfoValues; +string lineInfo[256][2]; +int cntMetaData = 0; +int numberInfoToRead = 0; +char *fileName; +bool hasErrors = false; + +void saveData(void); +void calcTemp(void); +void getRawThermalImage(char *path); +void printCSVLine(char separator); +int toUnixTime(string date_time); +float toDecimalDegrees(string dms); + +ExifTool *et; + +map csvInfoValues; + +float averageTemp = 0; + +float tau; +float raw_atm; +float emmissivity; +float raw_refl; +float b; +float o; +float r1; +float r2; +float f; + +int main(int argc, char **argv) +{ + if (argc < 2) + { + cout << "Please specify input fil e name" << endl; + return 1; + } + // create our ExifTool object + et = new ExifTool(); + // read metadata from the image + TagInfo *info = et->ImageInfo(argv[1]); + fileName = argv[1]; + + if (info) + { + for (TagInfo *i = info; i; i = i->next) + { + lineInfo[cntMetaData][0] = i->name; + lineInfo[cntMetaData++][1] = i->value; + // cout << "# " << i->name << " = " << i->value << endl; + } + delete info; + } + else if (et->LastComplete() <= 0) + { + cerr << "Error executing exiftool!" << endl; + } + + saveData(); + calcTemp(); + getRawThermalImage(argv[1]); + + printCSVLine(','); + + // print exiftool stderr messages + char *err = et->GetError(); + if (err) + { + cerr << err; + hasErrors = true; + } + + delete et; // delete our ExifTool object + return 0; +} + +void saveData(void) +{ + numberInfoToRead = sizeof(readInfo) / sizeof(readInfo[0]); + + bool haveInfo = false; + + string *currLine = nullptr; + + for (int t = 0; t < cntMetaData; t++) + { + if ((currLine = find(readInfo, readInfo + numberInfoToRead, lineInfo[t][0])) != readInfo + numberInfoToRead) + { + haveInfo = true; + try + { + readInfoValues.insert(pair(*currLine, stof(lineInfo[t][1]))); + // cout << "# " << *currLine << " = " << lineInfo[t][1] << endl; + } + catch (...) + { + //cout << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; + //cout << lineInfo[t][1] << endl; + } + } + //cout << "Line Info: " << lineInfo[t][0] << " Find Result: " << find(csvInfo, csvInfo + sizeof(csvInfo), lineInfo[t][0]) << " CSVINFO: " << csvInfo << " CSVINFO + size off: " << csvInfo + sizeof(csvInfo) << endl; + if (find(csvInfo, csvInfo + 7, lineInfo[t][0]) != csvInfo + 7) + { + csvInfoValues[lineInfo[t][0]] = lineInfo[t][1]; + //cout << "Inserted: " << lineInfo[t][0] << " with value: " << lineInfo[t][1] << endl; + } + if ((t == cntMetaData - 1) && !haveInfo) + { + //cout << *currLine << " = Not found " << endl; + } + } +} + +void calcTemp(void) +{ + //# get parameters from meta data + /* + ReflectedApparentTemperature = 0 | AtmosphericTemperature = 1 + ObjectDistance = 2 | RelativeHumidity = 3 + Emissivity = 4 | PlanckR1 = 5 + PlanckR2 = 6 | PlanckB = 7 + PlanckO = 8 | PlanckF = 9 + AtmosphericTransAlpha1 = 10 | AtmosphericTransAlpha2 = 11 + AtmosphericTransBeta1 = 12 | AtmosphericTransBeta2 = 13 + AtmosphericTransX = 14 | RawValueMedian = 15 + RawValueRange = 16 | RawThermalImage = 17 + */ + + float temp_ref = readInfoValues["ReflectedApparentTemperature"]; + float temp_atm = readInfoValues["AtmosphericTemperature"]; + float distance = readInfoValues["ObjectDistance"]; + float humidity = readInfoValues["RelativeHumidity"] / 100.0; + emmissivity = readInfoValues["Emissivity"]; + r1 = readInfoValues["PlanckR1"]; + r2 = readInfoValues["PlanckR2"]; + b = readInfoValues["PlanckB"]; + o = readInfoValues["PlanckO"]; + f = readInfoValues["PlanckF"]; + float a1 = readInfoValues["AtmosphericTransAlpha1"]; + float a2 = readInfoValues["AtmosphericTransAlpha2"]; + float b1 = readInfoValues["AtmosphericTransBeta1"]; + float b2 = readInfoValues["AtmosphericTransBeta2"]; + float x = readInfoValues["AtmosphericTransX"]; + + // Raw temperature range from FLIR + float raw_max = readInfoValues["RawValueMedian"] + (readInfoValues["RawValueRange"] / 2); + float raw_min = raw_max - readInfoValues["RawValueRange"]; + + // Calculate atmospheric transmission + float h2o = (humidity / 100) * exp( + 1.5587 + 6.939e-2 * temp_atm - 2.7816e-4 * pow(temp_atm, 2) + 6.8455e-7 * pow(temp_atm, 3)); + tau = x * exp(-sqrt(distance) * (a1 + b1 * sqrt(h2o))) + (1 - x) * exp(-sqrt(distance) * (a2 + b2 * sqrt(h2o))); + + // Radiance from atmosphere + // The camera is reporting the ambient temp as -273.15 deg celsius + try + { + raw_atm = (r1 / (r2 * (exp(b / (temp_atm + 273.15)) - f))) - o; + } + catch (...) + { + cerr << "ZeroDivisionError" << endl; + raw_atm = -o; + } + + // Radiance from reflected objects + raw_refl = r1 / (r2 * (exp(b / (temp_ref + 273.15)) - f)) - o; + + // get displayed object temp max/min + float raw_max_obj = (raw_max - (1 - tau) * raw_atm - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + float raw_min_obj = (raw_min - (1 - tau) * raw_atm - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + + float temp_min = b / log(r1 / (r2 * (raw_min_obj + o)) + f) - 273.15; + float temp_max = b / log(r1 / (r2 * (raw_max_obj + o)) + f) - 273.15; +} + +void getRawThermalImage(char *path) +{ + //Raw thermal image + TagInfo *infoEP = et->ImageInfo(path, "\n-RawThermalImage\n-b\n"); + const int imageHeight = readInfoValues["RawThermalImageHeight"]; + const int imageWidth = readInfoValues["RawThermalImageWidth"]; + // cout << "Image Height = " << imageHeight << endl; + // cout << "Image Width = " << imageWidth << endl; + int cnt = 0; + for (TagInfo *i = infoEP; i; i = i->next) + { + if (cnt == 1) + { + int **bufferMat = new int *[imageHeight]; + float **bufferMatFloat = new float *[imageHeight]; + for (int i = 0; i < imageHeight; ++i) + { + bufferMat[i] = new int[imageWidth]; + bufferMatFloat[i] = new float[imageWidth]; + } + char *buff; + buff = i->value; + double sizeBuf = i->valueLen; + int start = sizeBuf - (imageHeight * (2 * imageWidth)); + int r = 0; + int c = 0; + for (int t = start; t < sizeBuf; t = t + 2) + { + bufferMat[r][c] = (buff[t + 1] << 8) | (buff[t] & 0xff); + c++; + if (c > imageWidth-1) + { + c = 0; + r++; + } + } + + for (int i = 0; i < imageHeight; i++) + for (int t = 0; t < imageWidth; t++) + bufferMatFloat[i][t] = (bufferMat[i][t] - (1 - tau) * raw_atm - (1 - emmissivity) * tau * raw_refl) / emmissivity / tau; + + for (int i = 0; i < imageHeight; i++) + for (int t = 0; t < imageWidth; t++) + bufferMatFloat[i][t] = (b / log(r1 / (r2 * (bufferMatFloat[i][t] + o)) + f) - 273.15); + + double med = 0; + + for (int i = 0; i < imageHeight; i++) + for (int t = 0; t < imageWidth; t++) + med = med + bufferMatFloat[i][t]; + + averageTemp = med / (imageHeight * imageWidth); + + delete bufferMat; + delete bufferMatFloat; + } + cnt++; + } +} + +void printCSVLine(char separtor) +{ + string altitudeStr = csvInfoValues["GPSAltitude"]; + float altitude = stof(altitudeStr.substr(0, altitudeStr.find(' '))); + cout.precision(8); + cout << endl + << fixed + //<< csvInfoValues["DateTimeOriginal"] << separtor + << toUnixTime(csvInfoValues["DateTimeOriginal"]) << separtor + << toDecimalDegrees(csvInfoValues["GPSLatitude"]) << separtor + << toDecimalDegrees(csvInfoValues["GPSLongitude"]) << separtor; + cout.unsetf(std::ios::fixed); + cout.precision(6); + cout << altitude << separtor + << csvInfoValues["MAVRoll"] << separtor + << csvInfoValues["MAVPitch"] << separtor + << csvInfoValues["MAVYaw"] << separtor + << averageTemp; +} + +int toUnixTime(string date_time) +{ + time_t rawtime; + struct tm *timeinfo; + + int year = stoi(date_time.substr(0, 4)), + month = stoi(date_time.substr(5, 7)), + day = stoi(date_time.substr(8, 10)), + hour = stoi(date_time.substr(11, 13)), + min = stoi(date_time.substr(14, 16)), + sec = stoi(date_time.substr(17, 19)), + UTCOffset = stoi(date_time.substr(23, 26)); + + /* get current timeinfo: */ + time(&rawtime); //or: rawtime = time(0); + /* convert to struct: */ + timeinfo = localtime(&rawtime); + + /* now modify the timeinfo to the given date: */ + timeinfo->tm_year = year - 1900; + timeinfo->tm_mon = month - 1; //months since January - [0,11] + timeinfo->tm_mday = day; //day of the month - [1,31] + timeinfo->tm_hour = hour; //hours since midnight - [0,23] + timeinfo->tm_min = min; //minutes after the hour - [0,59] + timeinfo->tm_sec = sec; //seconds after the minute - [0,59] + + rawtime = timegm(timeinfo); + + //account GTM offset hours to be true GMT 1 hour = 3600 sec + rawtime -= UTCOffset * 3600; + + /* call gmtime: create unix time stamp from timeinfo struct in gmt time*/ + //int date = gmtime(rawtime); + return rawtime; +} + +float toDecimalDegrees(string dms) +{ + //0 deg 0' 0.00" N + + int degreeDelimiter = dms.find(' '); + int minutesPos = degreeDelimiter + 5; + int minuteDelimiter = dms.find('\''); + int secondsPos = minuteDelimiter + 2; + int secondsDelimiter = dms.find('"'); + + float result = stoi(dms.substr(0, minutesPos - 1)); + //cout << dms.substr(minutesPos, minuteDelimiter - minutesPos) << endl; + result += stoi(dms.substr(minutesPos, minuteDelimiter - minutesPos)) / 60.0; + //cout << dms.substr(secondsPos, secondsDelimiter - secondsPos) << endl; + result += stof(dms.substr(secondsPos, secondsDelimiter - secondsPos)) / 3600.0; + + if (dms.find('W') != string::npos || dms.find('S') != string::npos) + { + return -result; + } + else + { + return result; + } +} \ No newline at end of file From c45ead7d9b1908ef357333248637d31559720457 Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Thu, 7 Mar 2019 10:26:07 +0000 Subject: [PATCH 6/9] programs/scripts/dune-flir.sh: Fixed UTC date comparison with log files, improved coding style --- programs/scripts/dune-flir.sh | 458 ++++++++++++++++++---------------- 1 file changed, 246 insertions(+), 212 deletions(-) diff --git a/programs/scripts/dune-flir.sh b/programs/scripts/dune-flir.sh index f113455366..30a3386817 100644 --- a/programs/scripts/dune-flir.sh +++ b/programs/scripts/dune-flir.sh @@ -32,165 +32,179 @@ CSVName="FlirThermalData.csv" execPath="./dune-flir-metadata" usage() { - echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c]" - echo " -s start start date fro which to start processing (yyyymmdd_hhmmss or partial)" - echo " -e end date at which to stop processing (yyyymmdd_hhmmss or partial)" - echo " -d device path to the device to be mounted in order to access the photos" - echo " -l logs vehicle logs path" - echo " -o output output path, overrides the logs parameter and outputs to a single file" - echo " -c copy enable file copying to logs" - echo " -jc just-copy disable image analisys and enable file copying to logs" - echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" - echo " -nr no-replace prevents the program from overwriting existing data files" + echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c]" + echo " -s start start date from which to start processing at the photos directory (yyyymmdd_hhmmss or partial)" + echo " -e end date at which to stop processing at the photos directory(yyyymmdd_hhmmss or partial)" + echo " -d device path to the device to be mounted in order to access the photos" + echo " -l logs vehicle logs path" + echo " -o output output path, overrides the logs parameter and outputs to a single file" + echo " -c copy enable file copying to logs" + echo " -jc just-copy disable image analisys and enable file copying to logs" + echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" + echo " -nr no-replace prevents the program from overwriting existing data files" } inRange() { - [ "$START" \< "$1" ] && [ "$END" \> "$1" ] + [ "$START" \< "$1" ] && [ "$END" \> "$1" ] } findLog() { - echo "In function with filename $1" - - currDayPath="" - nextDayPath="" - currTimePath="" - nextTimePath="" - - for fullDayFile in $LOGS/*; do - local dayFile=$(basename $fullDayFile) - for fullTimeFile in $LOGS/$dayFile/*; do - local timeFile=$(basename $fullTimeFile) - logfile="$(printf "%s_%s" $dayFile $timeFile)" - if [ $logfile \< $1 ] || [ $logfile = $1 ] - then - currDayPath=$dayFile - currTimePath=$timeFile - else - nextDayPath=$dayFile - nextTimePath=$timeFile - return - fi - done + currDayPath="" + nextDayPath="" + currTimePath="" + nextTimePath="" + + for fullDayFile in $LOGS/*; do + # check for directory naming constraints + if [ -n "$(echo $fullDayFile | sed -E '/[.\/a-z\_]*\/[0-9]{8}$/g')" ]; + then + continue + fi + local dayFile=$(basename $fullDayFile) + + for fullTimeFile in $LOGS/$dayFile/*; do + local timeFile=$(basename $fullTimeFile) + local logFile="$(printf "%s_%s" $dayFile $timeFile)" + + #parse filenames to epoch for UTC comparission + fileToEpoch $logFile + local epochLogFile=$fileToEpochReturn + fileToEpoch $1 + local epochParam=$(($fileToEpochReturn+$UTCOffset)) + + if [ $epochLogFile \< $epochParam ] || [ $epochLogFile = $epochParam ] + then + currDayPath=$dayFile + currTimePath=$timeFile + else + nextDayPath=$dayFile + nextTimePath=$timeFile + return + fi done + done } +# get epoch time from string in format yyyyMMdd_hhmmss +fileToEpoch() { + local name=$1 + local year=`echo $name | cut -b 1-4` + local month=`echo $name | cut -b 5-6` + local day=`echo $name | cut -b 7-8` + local hour=`echo $name | cut -b 10-11` + local minute=`echo $name | cut -b 12-13` + local second=`echo $name | cut -b 14-15` + local date="$year-$month-$day $hour:$minute:$second UTC" + # date "+%s" -d "2013-02-20 08:41:15 UTC" + fileToEpochReturn=$(date +%s -d "$date") +} getEpochFilename() { - # $1 is in the format "YYYYMMDD_hhmmss_(3 digits for miliseconds)" - local name=$1 - local year=`echo $name | cut -b 1-4` - local month=`echo $name | cut -b 5-6` - local day=`echo $name | cut -b 7-8` - local hour=`echo $name | cut -b 10-11` - local minute=`echo $name | cut -b 12-13` - local second=`echo $name | cut -b 14-15` - local date="$year-$month-$day $hour:$minute:$second" - # date "+%s" -d "2013-02-20 08:41:15" - local epochTime=$(date +%s -d "$date") - if [ -z $UTCOffset ]; - then - UTCOffset=0 - fi - epochFilename="$(($epochTime+$UTCOffset))".`echo $name | cut -b 17-19`.jpg + # $1 is in the format "YYYYMMDD_hhmmss_(3 digits for miliseconds)" + fileToEpoch $1 + if [ -z $UTCOffset ]; + then + UTCOffset=0 + fi + epochFilename="$(($fileToEpochReturn+$UTCOffset))".`echo $1 | cut -b 17-19`.jpg } fillUTCOffset() { - getEpochFilename $(basename $filename _R.jpg) - local tempTime=${epochFilename%%\.*} - # printf "\n\ntempTime: $tempTime\n" - local output="$($execPath $filename)" - # printf "\n\nOutput: $output\n" - local tempUTCTime="${output%%,*}" - # printf "\n\nOutputTime: $tempUTCTime\n" - # printf "\n\noffset: %d\n" $(($tempUTCTime - $tempTime)) - UTCOffset=$(($tempUTCTime - $tempTime)) + getEpochFilename $(basename $filename _R.jpg) + local tempTime=${epochFilename%%\.*} + local output="$($execPath $filename)" + local tempUTCTime="${output%%,*}" + UTCOffset=$(($tempUTCTime - $tempTime)) + # printf "\nUTC Offset: %d\n\n" $UTCOffset } getNextPhotoDir() { - for photoDir in $photosPath/*; do - currPhotoIndex="$(ls $photoDir | wc -l)" - if [ $currPhotoIndex -lt 1000 ]; - then - currPhotosDir=$photoDir - break - fi - done - # if all existing directories are full - if [ $currPhotoIndex -eq 1000 ]; + for photoDir in $photosPath/*; do + currPhotoIndex="$(ls $photoDir | wc -l)" + if [ $currPhotoIndex -lt 1000 ]; then - currPhotoIndex=0 - local newPhotoDirIndex=$(($(basename $photoDir)+1)) - local newFolder="$(printf "%.6d" $newPhotoDirIndex)" - currPhotosDir=$photosPath/$newFolder - mkdir $currPhotosDir + currPhotosDir=$photoDir + break fi + done - if [ "$(basename $photoDir)" = "*" ]; - then - currPhotoIndex=0 - currPhotosDir=$photosPath/000000 - mkdir $currPhotosDir - fi + # if all existing directories are full + if [ $currPhotoIndex -eq 1000 ]; + then + currPhotoIndex=0 + local newPhotoDirIndex=$(($(basename $photoDir)+1)) + local newFolder="$(printf "%.6d" $newPhotoDirIndex)" + currPhotosDir=$photosPath/$newFolder + mkdir $currPhotosDir + fi + + # no directory exists in $photoDir + if [ "$(basename $photoDir)" = "*" ]; + then + currPhotoIndex=0 + currPhotosDir=$photosPath/000000 + mkdir $currPhotosDir + fi } for i in "$@" do case $i in - -e=*|--extension=*) - END="${i#*=}" - shift # past argument=value - ;; - -s=*|--searchpath=*) - START="${i#*=}" - shift # past argument=value - ;; - -d=*|--device=*) - DEVICE="${i#*=}" - shift # past argument=value - ;; - -l=*|--logs=*) - LOGS="${i#*=}" - shift # past argument=value - ;; - -o=*|--output=*) - OUTPUT="${i#*=}" - shift # past argument=value - ;; - -c|--copy) - COPY="copy" - shift # past argument=value - ;; - -jc|--just-copy) - JUSTCOPY="justcopy" - shift # past argument=value - ;; - -fn=*|--file_name=*) - CSVName="${i#*=}" - shift # past argument=value - ;; - -nr|--no-replace) - NOREPLACE="noreplace" - shift # past argument=value - ;; - *) - printf "Unkown option %s\n\n" $i # unknown option - usage - exit 1 - ;; + -e=*|--extension=*) + END="${i#*=}" + shift # past argument=value + ;; + -s=*|--searchpath=*) + START="${i#*=}" + shift # past argument=value + ;; + -d=*|--device=*) + DEVICE="${i#*=}" + shift # past argument=value + ;; + -l=*|--logs=*) + LOGS="${i#*=}" + shift # past argument=value + ;; + -o=*|--output=*) + OUTPUT="${i#*=}" + shift # past argument=value + ;; + -c|--copy) + COPY="copy" + shift # past argument=value + ;; + -jc|--just-copy) + JUSTCOPY="justcopy" + shift # past argument=value + ;; + -fn=*|--file_name=*) + CSVName="${i#*=}" + shift # past argument=value + ;; + -nr|--no-replace) + NOREPLACE="noreplace" + shift # past argument=value + ;; + *) + printf "Unkown option %s\n\n" $i # unknown option + usage + exit 1 + ;; esac done if [ -z $DEVICE ] || [ -z $START ] || [ -z $END ] || [ -z $LOGS -a -z $OUTPUT ]; then - printf 'Some required variables are not defined\n\n' - usage - exit 1 + printf 'Some required variables are not defined\n\n' + usage + exit 1 fi if [ $START \> $END ] || [ $START = $END ] then - printf 'The start(-s) and end(-e) dates are not compatible\n\n' - exit 1 + printf 'The start(-s) and end(-e) dates are not compatible\n\n' + exit 1 fi noSave=0 @@ -200,99 +214,119 @@ epochFilename="" currPhotosDir="" currPhotoIndex=0 -if [ ! -z $OUTPUT ]; then -printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath +if [ ! -z $OUTPUT ]; +then + printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath fi for top_filename in $DEVICE/*; do - # printf "OUTSIDE FOR: $top_filename\n" + # check for directory naming contraints + if [ -n "$(echo $top_filename | sed -E '/[.\/a-z\_]*\/[0-9]{8}_[0-9]{6}$/g')" ]; + then + printf "Skipped file: $top_filename; For not keeping the naming standard\n" + continue + fi -# find ../../test_photos -maxdepth 1 -regextype posix-extended -regex [.\/a-z\_]*/[0-9]{8}_[0-9]{6}$ + for filename in $top_filename/*; do + truncFilename="$(basename $filename _R.jpg)" + truncFilename=${truncFilename%_*} - if [ -n "$(echo $top_filename | sed -E '/[.\/a-z\_]*\/[0-9]{8}_[0-9]{6}$/g')" ]; + # check if the current file is in the specified range + if inRange $truncFilename; then - printf "Skipped file: $top_filename; For not keeping the naming standard\n" + day=${truncFilename%_*} + inputTime=${truncFilename#*_} + + # calculate UTCOffset if undefined + if [ -z $UTCOffset ]; + then + fillUTCOffset + fi + + # parse filename to epoch for UTC comparisson + fileToEpoch $truncFilename + epochTruncFilename=$(($fileToEpochReturn+$UTCOffset)) + + # check if the next log file should be used to store info about the current file + if [ $epochTruncFilename \> $nextLog -o $epochTruncFilename = $nextLog ] && [ -z $OUTPUT ]; + then + findLog $truncFilename + + if [ -z $nextDayPath ] || [ -z $nextTimePath ]; + then + # no future log file found so prevent the above if from being true + nextLog=9999999999 + else + fileToEpoch "$(printf "%s_%s" $nextDayPath $nextTimePath)" + nextLog=$fileToEpochReturn + fi + + # fill the new required paths + generalPath=$LOGS/$currDayPath/$currTimePath + savePath=$generalPath/$CSVName + photosPath=$generalPath/Photos + + # verify if there is already a photos directory in curr log + if [ ! -d $photosPath ] && [ -n $COPY ]; + then + currPhotosDir=$photosPath/000000 + mkdir -p $currPhotosDir + currPhotoIndex=0 + else + getNextPhotoDir + fi + + # verify if there exists a data file already present and don't overwrite if the options is present + # check also if there is a valid log file to save the items + if [ -e $savePath -a $NOREPLACE ] || [ -z $currDayPath ] || [ -z $currTimePath ]; + then + noSave=1 + printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" + continue + else + noSave=0 + fi + + if [ -z $JUSTCOPY ]; + then + # add header to the new data.csv file + printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath + fi + fi + + # check if the file should be processed + if [ $noSave = 1 ]; + then + printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" continue - fi - for filename in $top_filename/*; do - # printf "INNER FOR: $filename\n" - truncFilename="$(basename $filename _R.jpg)" - truncFilename=${truncFilename%_*} - # check if the curr file is in the specified range - if inRange $truncFilename; + fi + + # process file + if [ -z $JUSTCOPY ]; + then + $execPath $filename >> $savePath + printf "Processed $filename\n" + printf "Saving to: %s\n" $savePath + fi + + # copy files to log if option is present + if [ $COPY ] || [ $JUSTCOPY ]; + then + getEpochFilename $(basename $filename _R.jpg) + cp -p $filename $currPhotosDir/$epochFilename + printf "Coppied $filename to $currPhotosDir/$epochFilename\n" + if [ $currPhotoIndex -eq 999 ]; then - # printf "IS IN RANGE: $truncFilename\n" - day=${truncFilename%_*} - inputTime=${truncFilename#*_} - # check if the next log file should be used to store info about the current file - # printf "NEXTLOG: $nextLog\n" - # printf "OUTPUT: $OUTPUT\n" - if [ $truncFilename \> $nextLog -o $truncFilename = $nextLog ] && [ -z $OUTPUT ]; - then - findLog $truncFilename - nextLog="$(printf "%s_%s" $nextDayPath $nextTimePath)" - generalPath=$LOGS/$currDayPath/$currTimePath - savePath=$generalPath/$CSVName - photosPath=$generalPath/Photos - # verify if there is already a photos directory in curr log - if [ ! -d $photosPath ] && [ -n $COPY ]; - then - currPhotosDir=$photosPath/000000 - mkdir -p $currPhotosDir - currPhotoIndex=0 - else - getNextPhotoDir - fi - # verify if there exists a data file already present and don't overwrite if the options is present - # check also if there is a valid log file to save the items - if [ -e $savePath -a $NOREPLACE ] || [ -z $currDayPath ] || [ -z $currTimePath ]; - then - noSave=1 - printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" - continue - else - noSave=0 - fi - - if [ -z $JUSTCOPY ]; - then - # add header to the new data.csv file - printf DateTimeOriginal,GPSLatitude,GPSLongitude,GPSAltitude,MAVRoll,MAVPitch,MAVYaw,Temp\(°C\) > $savePath - fi - fi - # check if the file should be processed - if [ $noSave = 1 ]; - then - printf "Skipped: $filename; No suitable log folder was found OR no overwrite option is enabled\n" - continue - fi - - # process file - if [ -z $JUSTCOPY ]; - then - $execPath $filename >> $savePath - printf "Processed $filename\n" - printf "Saving to: %s\n" $savePath - fi - - if [ -z $UTCOffset ]; - then - fillUTCOffset - fi - - # copy files to log if option is present - if [ $COPY ] || [ $JUSTCOPY ]; - then - getEpochFilename $(basename $filename _R.jpg) - cp -p $filename $currPhotosDir/$epochFilename - printf "Coppied $filename to $currPhotosDir/$epochFilename\n" - if [ $currPhotoIndex -eq 999 ]; - then - getNextPhotoDir - else - currPhotoIndex=$(($currPhotoIndex+1)) - fi - fi + getNextPhotoDir + else + currPhotoIndex=$(($currPhotoIndex+1)) fi - done -done \ No newline at end of file + fi + else + printf "Skipped file: $filename; Not in Range\n" + continue + fi + done +done + +exit 0 \ No newline at end of file From 747a3b2a5c5abe3b9589c0c0ce4861dc10f90866 Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Fri, 8 Mar 2019 09:24:08 +0000 Subject: [PATCH 7/9] programs/scripts: Improved coding guidelines, Add copyright statement. --- programs/scripts/dune-flir.sh | 3 ++ programs/utils/dune-flir-metadata.cpp | 69 ++++++++++++++++----------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/programs/scripts/dune-flir.sh b/programs/scripts/dune-flir.sh index 30a3386817..b227e1129c 100644 --- a/programs/scripts/dune-flir.sh +++ b/programs/scripts/dune-flir.sh @@ -27,6 +27,9 @@ ############################################################################ # Author: Eduardo Ramos # ############################################################################ +# This script will extract the data capture from the FLIR cameras # +# (model DuoR or VueProR). # +############################################################################ CSVName="FlirThermalData.csv" execPath="./dune-flir-metadata" diff --git a/programs/utils/dune-flir-metadata.cpp b/programs/utils/dune-flir-metadata.cpp index 172e2438d6..44d63827f3 100644 --- a/programs/utils/dune-flir-metadata.cpp +++ b/programs/utils/dune-flir-metadata.cpp @@ -1,11 +1,36 @@ -//#*************************************************************************** -//# Copyright (C) 2018 Laboratório de Sistemas e Tecnologia Subaquática * -//# Departamento de Engenharia Electrotécnica e de Computadores * -//# Rua Dr. Roberto Frias, 4200-465 Porto, Portugal * -//#*************************************************************************** -//# Author: Pedro Gonçalves * -//#*************************************************************************** - +//*************************************************************************** +// Copyright 2007-2019 Universidade do Porto - Faculdade de Engenharia * +// Laboratório de Sistemas e Tecnologia Subaquática (LSTS) * +//*************************************************************************** +// This file is part of DUNE: Unified Navigation Environment. * +// * +// Commercial Licence Usage * +// Licencees holding valid commercial DUNE licences may use this file in * +// accordance with the commercial licence agreement provided with the * +// Software or, alternatively, in accordance with the terms contained in a * +// written agreement between you and Faculdade de Engenharia da * +// Universidade do Porto. For licensing terms, conditions, and further * +// information contact lsts@fe.up.pt. * +// * +// Modified European Union Public Licence - EUPL v.1.1 Usage * +// Alternatively, this file may be used under the terms of the Modified * +// EUPL, Version 1.1 only (the "Licence"), appearing in the file LICENCE.md * +// included in the packaging of this file. You may not use this work * +// except in compliance with the Licence. Unless required by applicable * +// law or agreed to in writing, software distributed under the Licence is * +// distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF * +// ANY KIND, either express or implied. See the Licence for the specific * +// language governing permissions and limitations at * +// https://github.com/LSTS/dune/blob/master/LICENCE.md and * +// http://ec.europa.eu/idabc/eupl.html. * +//*************************************************************************** +// Author: Pedro Gonçalves e Eduardo Ramos * +//*************************************************************************** +// Utility to process the metadata contained in the EXIF of the images * +// captured by the FLIR cameras(model DuoR or VueProR). * +//*************************************************************************** + +// ISO C++ 98 headers. #include #include #include @@ -14,9 +39,10 @@ #include #include +// ExifTool headers. #include "../../vendor/libraries/exiftool/ExifTool.h" -using namespace std; + using namespace std; // information used to calculate image temperature string readInfo[] = @@ -81,7 +107,6 @@ int main(int argc, char **argv) { lineInfo[cntMetaData][0] = i->name; lineInfo[cntMetaData++][1] = i->value; - // cout << "# " << i->name << " = " << i->value << endl; } delete info; } @@ -124,23 +149,20 @@ void saveData(void) try { readInfoValues.insert(pair(*currLine, stof(lineInfo[t][1]))); - // cout << "# " << *currLine << " = " << lineInfo[t][1] << endl; } catch (...) { - //cout << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; - //cout << lineInfo[t][1] << endl; + cerr << "# " << lineInfo[t][0] << " = error getting value, is binary data ?!?" << endl; + cerr << lineInfo[t][1] << endl; } } - //cout << "Line Info: " << lineInfo[t][0] << " Find Result: " << find(csvInfo, csvInfo + sizeof(csvInfo), lineInfo[t][0]) << " CSVINFO: " << csvInfo << " CSVINFO + size off: " << csvInfo + sizeof(csvInfo) << endl; if (find(csvInfo, csvInfo + 7, lineInfo[t][0]) != csvInfo + 7) { csvInfoValues[lineInfo[t][0]] = lineInfo[t][1]; - //cout << "Inserted: " << lineInfo[t][0] << " with value: " << lineInfo[t][1] << endl; } if ((t == cntMetaData - 1) && !haveInfo) { - //cout << *currLine << " = Not found " << endl; + cerr << *currLine << " = Not found " << endl; } } } @@ -214,8 +236,6 @@ void getRawThermalImage(char *path) TagInfo *infoEP = et->ImageInfo(path, "\n-RawThermalImage\n-b\n"); const int imageHeight = readInfoValues["RawThermalImageHeight"]; const int imageWidth = readInfoValues["RawThermalImageWidth"]; - // cout << "Image Height = " << imageHeight << endl; - // cout << "Image Width = " << imageWidth << endl; int cnt = 0; for (TagInfo *i = infoEP; i; i = i->next) { @@ -275,7 +295,6 @@ void printCSVLine(char separtor) cout.precision(8); cout << endl << fixed - //<< csvInfoValues["DateTimeOriginal"] << separtor << toUnixTime(csvInfoValues["DateTimeOriginal"]) << separtor << toDecimalDegrees(csvInfoValues["GPSLatitude"]) << separtor << toDecimalDegrees(csvInfoValues["GPSLongitude"]) << separtor; @@ -301,12 +320,12 @@ int toUnixTime(string date_time) sec = stoi(date_time.substr(17, 19)), UTCOffset = stoi(date_time.substr(23, 26)); - /* get current timeinfo: */ + // get current timeinfo: time(&rawtime); //or: rawtime = time(0); - /* convert to struct: */ + // convert to struct: timeinfo = localtime(&rawtime); - /* now modify the timeinfo to the given date: */ + // now modify the timeinfo to the given date: timeinfo->tm_year = year - 1900; timeinfo->tm_mon = month - 1; //months since January - [0,11] timeinfo->tm_mday = day; //day of the month - [1,31] @@ -319,15 +338,13 @@ int toUnixTime(string date_time) //account GTM offset hours to be true GMT 1 hour = 3600 sec rawtime -= UTCOffset * 3600; - /* call gmtime: create unix time stamp from timeinfo struct in gmt time*/ - //int date = gmtime(rawtime); + // call gmtime: create unix time stamp from timeinfo struct in gmt time return rawtime; } float toDecimalDegrees(string dms) { //0 deg 0' 0.00" N - int degreeDelimiter = dms.find(' '); int minutesPos = degreeDelimiter + 5; int minuteDelimiter = dms.find('\''); @@ -335,9 +352,7 @@ float toDecimalDegrees(string dms) int secondsDelimiter = dms.find('"'); float result = stoi(dms.substr(0, minutesPos - 1)); - //cout << dms.substr(minutesPos, minuteDelimiter - minutesPos) << endl; result += stoi(dms.substr(minutesPos, minuteDelimiter - minutesPos)) / 60.0; - //cout << dms.substr(secondsPos, secondsDelimiter - secondsPos) << endl; result += stof(dms.substr(secondsPos, secondsDelimiter - secondsPos)) / 3600.0; if (dms.find('W') != string::npos || dms.find('S') != string::npos) From fade0973d96321aebf3c2dbd8993dcc597a406bc Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Fri, 8 Mar 2019 09:35:54 +0000 Subject: [PATCH 8/9] programs/utils/dune-flir-metadata.cpp: Improve coding guidelines. --- programs/utils/dune-flir-metadata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/utils/dune-flir-metadata.cpp b/programs/utils/dune-flir-metadata.cpp index 44d63827f3..0b70beb2ed 100644 --- a/programs/utils/dune-flir-metadata.cpp +++ b/programs/utils/dune-flir-metadata.cpp @@ -42,7 +42,7 @@ // ExifTool headers. #include "../../vendor/libraries/exiftool/ExifTool.h" - using namespace std; +using namespace std; // information used to calculate image temperature string readInfo[] = @@ -258,7 +258,7 @@ void getRawThermalImage(char *path) { bufferMat[r][c] = (buff[t + 1] << 8) | (buff[t] & 0xff); c++; - if (c > imageWidth-1) + if (c > imageWidth - 1) { c = 0; r++; From 4588c4b5f70b116b8448270ecfd56519bfc6e990 Mon Sep 17 00:00:00 2001 From: edramos-97 Date: Fri, 15 Mar 2019 11:53:37 +0000 Subject: [PATCH 9/9] programs/scripts: Add support for manual time offset --- programs/scripts/dune-flir.sh | 30 ++++++++++++++++----------- programs/utils/dune-flir-metadata.cpp | 13 +++++++++--- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/programs/scripts/dune-flir.sh b/programs/scripts/dune-flir.sh index b227e1129c..4bc9ab3b2a 100644 --- a/programs/scripts/dune-flir.sh +++ b/programs/scripts/dune-flir.sh @@ -33,18 +33,20 @@ CSVName="FlirThermalData.csv" execPath="./dune-flir-metadata" +CLOCK_OFFSET=0 usage() { - echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c]" - echo " -s start start date from which to start processing at the photos directory (yyyymmdd_hhmmss or partial)" - echo " -e end date at which to stop processing at the photos directory(yyyymmdd_hhmmss or partial)" - echo " -d device path to the device to be mounted in order to access the photos" - echo " -l logs vehicle logs path" - echo " -o output output path, overrides the logs parameter and outputs to a single file" - echo " -c copy enable file copying to logs" - echo " -jc just-copy disable image analisys and enable file copying to logs" - echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" - echo " -nr no-replace prevents the program from overwriting existing data files" + echo "Usage: $0 -s=start_date -e=end_date -d=device_name (-l=logs_folder || -o=output_file_name) [-jc] [-fn=data_filename][-nr] [-c] [-co=seconds_of_delay]" + echo " -s start start date from which to start processing at the photos directory (yyyymmdd_hhmmss or partial)" + echo " -e end date at which to stop processing at the photos directory(yyyymmdd_hhmmss or partial)" + echo " -d device path to the device to be mounted in order to access the photos" + echo " -l logs vehicle logs path" + echo " -o output output path, overrides the logs parameter and outputs to a single file" + echo " -c copy enable file copying to logs" + echo " -co clock-offset number of seconds the camera timestamp should be advanced to match IMAGE_START_CAPTURE command" + echo " -jc just-copy disable image analisys and enable file copying to logs" + echo " -fn file-name name of the output file (defaults to \"FlirThermalDatav.csv\")" + echo " -nr no-replace prevents the program from overwriting existing data files" } inRange() { @@ -115,7 +117,7 @@ getEpochFilename() { fillUTCOffset() { getEpochFilename $(basename $filename _R.jpg) local tempTime=${epochFilename%%\.*} - local output="$($execPath $filename)" + local output="$($execPath $filename $CLOCK_OFFSET)" local tempUTCTime="${output%%,*}" UTCOffset=$(($tempUTCTime - $tempTime)) # printf "\nUTC Offset: %d\n\n" $UTCOffset @@ -173,6 +175,10 @@ case $i in OUTPUT="${i#*=}" shift # past argument=value ;; + -co=*|--clock-offset=*) + CLOCK_OFFSET="${i#*=}" + shift # past argument=value + ;; -c|--copy) COPY="copy" shift # past argument=value @@ -307,7 +313,7 @@ for top_filename in $DEVICE/*; do # process file if [ -z $JUSTCOPY ]; then - $execPath $filename >> $savePath + $execPath $filename $CLOCK_OFFSET >> $savePath printf "Processed $filename\n" printf "Saving to: %s\n" $savePath fi diff --git a/programs/utils/dune-flir-metadata.cpp b/programs/utils/dune-flir-metadata.cpp index 0b70beb2ed..6eafdb5cf9 100644 --- a/programs/utils/dune-flir-metadata.cpp +++ b/programs/utils/dune-flir-metadata.cpp @@ -78,6 +78,8 @@ map csvInfoValues; float averageTemp = 0; +int timeStampOffset = 0; + float tau; float raw_atm; float emmissivity; @@ -92,8 +94,11 @@ int main(int argc, char **argv) { if (argc < 2) { - cout << "Please specify input fil e name" << endl; + cout << "Please specify input file name and optionally an ineteger representing the seconds of advance to add to the timestamp" << endl; return 1; + } else if(argc == 3) + { + timeStampOffset = stoi(argv[2]); } // create our ExifTool object et = new ExifTool(); @@ -325,7 +330,7 @@ int toUnixTime(string date_time) // convert to struct: timeinfo = localtime(&rawtime); - // now modify the timeinfo to the given date: + // modify the timeinfo to the given date: timeinfo->tm_year = year - 1900; timeinfo->tm_mon = month - 1; //months since January - [0,11] timeinfo->tm_mday = day; //day of the month - [1,31] @@ -338,7 +343,9 @@ int toUnixTime(string date_time) //account GTM offset hours to be true GMT 1 hour = 3600 sec rawtime -= UTCOffset * 3600; - // call gmtime: create unix time stamp from timeinfo struct in gmt time + //fix clock synchronization + rawtime += timeStampOffset; + return rawtime; }