Compute dive computer/camera time offset from sample picture

Signed-off-by: Robert C. Helling <helling@atdotde.de>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Robert C. Helling 2014-02-13 16:43:55 +01:00 committed by Dirk Hohndel
parent fe2a264db3
commit bbb071f1e8
5 changed files with 216 additions and 35 deletions

View file

@ -804,6 +804,7 @@ void DiveListView::loadImages()
{ {
struct memblock mem; struct memblock mem;
EXIFInfo exif; EXIFInfo exif;
int retval;
time_t imagetime; time_t imagetime;
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open Image Files"), lastUsedImageDir(), tr("Image Files (*.jpg *.jpeg *.pnm *.tif *.tiff)")); QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open Image Files"), lastUsedImageDir(), tr("Image Files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
@ -818,23 +819,16 @@ void DiveListView::loadImages()
updateLastImageTimeOffset(shiftDialog.amount()); updateLastImageTimeOffset(shiftDialog.amount());
for (int i = 0; i < fileNames.size(); ++i) { for (int i = 0; i < fileNames.size(); ++i) {
struct tm tm;
int year, month, day, hour, min, sec;
int retval;
if (readfile(fileNames.at(i).toUtf8().data(), &mem) <= 0) if (readfile(fileNames.at(i).toUtf8().data(), &mem) <= 0)
continue; continue;
retval = exif.parseFrom((const unsigned char *) mem.buffer, (unsigned) mem.size); retval = exif.parseFrom((const unsigned char *) mem.buffer, (unsigned) mem.size);
free(mem.buffer); free(mem.buffer);
if (retval != PARSE_EXIF_SUCCESS) if (retval != PARSE_EXIF_SUCCESS)
continue; continue;
sscanf(exif.DateTime.c_str(), "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec); imagetime = shiftDialog.epochFromExiv(&exif);
tm.tm_year = year; if (!imagetime)
tm.tm_mon = month - 1; continue;
tm.tm_mday = day; imagetime += shiftDialog.amount();
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
imagetime = utc_mktime(&tm) + shiftDialog.amount();
int j = 0; int j = 0;
struct dive *dive; struct dive *dive;
for_each_dive(j, dive){ for_each_dive(j, dive){

View file

@ -52,6 +52,7 @@ public slots:
void shiftTimes(); void shiftTimes();
void loadImages(); void loadImages();
void uploadToDivelogsDE(); void uploadToDivelogsDE();
QString lastUsedImageDir();
signals: signals:
void currentDiveChanged(int divenr); void currentDiveChanged(int divenr);
@ -73,7 +74,6 @@ private:
void restoreExpandedRows(); void restoreExpandedRows();
int lastVisibleColumn(); int lastVisibleColumn();
void selectTrip ( dive_trip_t* trip ); void selectTrip ( dive_trip_t* trip );
QString lastUsedImageDir();
void updateLastUsedImageDir(const QString& s); void updateLastUsedImageDir(const QString& s);
void updateLastImageTimeOffset(int offset); void updateLastImageTimeOffset(int offset);
int lastImageTimeOffset(); int lastImageTimeOffset();

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>343</width> <width>693</width>
<height>177</height> <height>606</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -25,7 +25,7 @@
</iconset> </iconset>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item alignment="Qt::AlignTop"> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Shift times of image(s) by</string> <string>Shift times of image(s) by</string>
@ -40,6 +40,16 @@
<day>1</day> <day>1</day>
</date> </date>
</property> </property>
<property name="maximumDateTime">
<datetime>
<hour>23</hour>
<minute>59</minute>
<second>59</second>
<year>2010</year>
<month>12</month>
<day>31</day>
</datetime>
</property>
<property name="minimumDateTime"> <property name="minimumDateTime">
<datetime> <datetime>
<hour>0</hour> <hour>0</hour>
@ -51,14 +61,32 @@
</datetime> </datetime>
</property> </property>
<property name="maximumDate"> <property name="maximumDate">
<date>
<year>2010</year>
<month>12</month>
<day>31</day>
</date>
</property>
<property name="minimumDate">
<date> <date>
<year>2000</year> <year>2000</year>
<month>1</month> <month>1</month>
<day>1</day> <day>1</day>
</date> </date>
</property> </property>
<property name="displayFormat"> <property name="maximumTime">
<string>h:mm</string> <time>
<hour>23</hour>
<minute>59</minute>
<second>59</second>
</time>
</property>
<property name="minimumTime">
<time>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
</time>
</property> </property>
<property name="timeSpec"> <property name="timeSpec">
<enum>Qt::LocalTime</enum> <enum>Qt::LocalTime</enum>
@ -82,19 +110,113 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>60</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>To compute the offset between the clocks of your dive computer and your camera use your camera to take a picture of your dive compuer displaying the current time. Download that image to your computer and press this button.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="syncCamera">
<property name="toolTip">
<string>Determine camera time offset</string>
</property>
<property name="text">
<string>Select image of divecomputer showing time</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="displayDC">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string/>
</property>
<widget class="QDateTimeEdit" name="dcTime">
<property name="geometry">
<rect>
<x>0</x>
<y>70</y>
<width>161</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>234</width>
<height>60</height>
</rect>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>60</height>
</size>
</property>
<property name="text">
<string>Which date and time are displayed on the image?</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QGraphicsView" name="DCImage">
<property name="geometry">
<rect>
<x>260</x>
<y>10</y>
<width>361</width>
<height>281</height>
</rect>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources> <resources>

View file

@ -12,8 +12,11 @@
#include <QStringList> #include <QStringList>
#include <QDebug> #include <QDebug>
#include <QTime> #include <QTime>
#include <QFileDialog>
#include <QDateTime>
#include "exif.h"
#include "../dive.h" #include "../dive.h"
#include "../file.h"
#include "mainwindow.h" #include "mainwindow.h"
class MinMaxAvgWidgetPrivate { class MinMaxAvgWidgetPrivate {
@ -168,23 +171,80 @@ void ShiftImageTimesDialog::buttonClicked(QAbstractButton* button)
} }
} }
void ShiftImageTimesDialog::syncCameraClicked()
{
struct memblock mem;
EXIFInfo exiv;
int retval;
QPixmap picture;
QDateTime dcDateTime = QDateTime::QDateTime();
QStringList fileNames = QFileDialog::getOpenFileNames(this,
tr("Open Image File"),
MainWindow().dive_list()->lastUsedImageDir(),
tr("Image Files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
if (fileNames.isEmpty())
return;
picture.load(fileNames.at(0));
ui.displayDC->setEnabled(TRUE);
QGraphicsScene *scene = new QGraphicsScene (this);
scene->addPixmap(picture.scaled(ui.DCImage->size()));
ui.DCImage->setScene(scene);
if (readfile(fileNames.at(0).toUtf8().data(), &mem) <= 0)
return;
retval = exiv.parseFrom((const unsigned char *) mem.buffer, (unsigned) mem.size);
free(mem.buffer);
if (retval != PARSE_EXIF_SUCCESS)
return;
dcImageEpoch = epochFromExiv(&exiv);
dcDateTime.setTime_t(dcImageEpoch);
ui.dcTime->setDateTime(dcDateTime);
connect(ui.dcTime, SIGNAL(dateTimeChanged(const QDateTime &)), this, SLOT(dcDateTimeChanged(const QDateTime &)));
}
time_t ShiftImageTimesDialog::epochFromExiv(EXIFInfo *exif)
{
struct tm tm;
int year, month, day, hour, min, sec;
sscanf(exif->DateTime.c_str(), "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec);
tm.tm_year = year;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
return (utc_mktime(&tm));
}
void ShiftImageTimesDialog::dcDateTimeChanged(const QDateTime &newDateTime)
{
if (!dcImageEpoch)
return;
setOffset(newDateTime.toTime_t() - dcImageEpoch);
}
ShiftImageTimesDialog::ShiftImageTimesDialog(QWidget *parent): QDialog(parent), m_amount(0) ShiftImageTimesDialog::ShiftImageTimesDialog(QWidget *parent): QDialog(parent), m_amount(0)
{ {
ui.setupUi(this); ui.setupUi(this);
connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
connect(ui.syncCamera, SIGNAL(clicked()), this, SLOT(syncCameraClicked()));
dcImageEpoch = (time_t) 0;
} }
int ShiftImageTimesDialog::amount() const time_t ShiftImageTimesDialog::amount() const
{ {
return m_amount; return m_amount;
} }
void ShiftImageTimesDialog::setOffset(int offset) void ShiftImageTimesDialog::setOffset(time_t offset)
{ {
if (offset >= 0) { if (offset >= 0) {
ui.forward->setDown(TRUE); ui.forward->setChecked(TRUE);
} else { } else {
ui.backwards->setDown(TRUE); ui.backwards->setChecked(TRUE);
offset *= -1; offset *= -1;
} }
ui.timeEdit->setTime(QTime::QTime(offset / 3600, (offset % 3600) / 60, offset % 60)); ui.timeEdit->setTime(QTime::QTime(offset / 3600, (offset % 3600) / 60, offset % 60));

View file

@ -10,6 +10,7 @@ class QAbstractButton;
#include "ui_renumber.h" #include "ui_renumber.h"
#include "ui_shifttimes.h" #include "ui_shifttimes.h"
#include "ui_shiftimagetimes.h" #include "ui_shiftimagetimes.h"
#include "exif.h"
class MinMaxAvgWidget : public QWidget{ class MinMaxAvgWidget : public QWidget{
Q_OBJECT Q_OBJECT
@ -59,13 +60,17 @@ class ShiftImageTimesDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ShiftImageTimesDialog(QWidget *parent); explicit ShiftImageTimesDialog(QWidget *parent);
int amount() const; time_t amount() const;
void setOffset(int offset); void setOffset(time_t offset);
time_t epochFromExiv(EXIFInfo *exif);
private slots: private slots:
void buttonClicked(QAbstractButton *button); void buttonClicked(QAbstractButton *button);
void syncCameraClicked();
void dcDateTimeChanged(const QDateTime &);
private: private:
Ui::ShiftImageTimesDialog ui; Ui::ShiftImageTimesDialog ui;
int m_amount; time_t m_amount;
time_t dcImageEpoch;
}; };
bool isGnome3Session(); bool isGnome3Session();