diff --git a/src/AnalyzeView/ExifParser.cc b/src/AnalyzeView/ExifParser.cc index 7b3a5fa4ddf..c53da90f69b 100644 --- a/src/AnalyzeView/ExifParser.cc +++ b/src/AnalyzeView/ExifParser.cc @@ -107,7 +107,7 @@ double readTime2(const QByteArray &buf) } } -bool write(QByteArray &buf, const GeoTagWorker::cameraFeedbackPacket &geotag) +bool write(QByteArray &buf, const GeoTagWorker::CameraFeedbackPacket &geotag) { try { // Convert QByteArray to std::string for Exiv2 diff --git a/src/AnalyzeView/ExifParser.h b/src/AnalyzeView/ExifParser.h index 71c0f96e96d..12bd3706195 100644 --- a/src/AnalyzeView/ExifParser.h +++ b/src/AnalyzeView/ExifParser.h @@ -20,5 +20,5 @@ Q_DECLARE_LOGGING_CATEGORY(ExifParserLog) namespace ExifParser { double readTime(const QByteArray &buf); double readTime2(const QByteArray &buf); - bool write(QByteArray &buf, const GeoTagWorker::cameraFeedbackPacket &geotag); + bool write(QByteArray &buf, const GeoTagWorker::CameraFeedbackPacket &geotag); } // namespace ExifParser diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index da32c74ac37..3e9cc0e8cc7 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -8,120 +8,166 @@ ****************************************************************************/ #include "GeoTagController.h" +#include "GeoTagWorker.h" #include "QGCLoggingCategory.h" #include +#include +#include #include QGC_LOGGING_CATEGORY(GeoTagControllerLog, "qgc.analyzeview.geotagcontroller") -GeoTagController::GeoTagController() - : _progress(0) - , _inProgress(false) +GeoTagController::GeoTagController(QObject *parent) + : QObject(parent) + , _worker(new GeoTagWorker()) + , _workerThread(new QThread(this)) { - connect(&_worker, &GeoTagWorker::progressChanged, this, &GeoTagController::_workerProgressChanged); - connect(&_worker, &GeoTagWorker::error, this, &GeoTagController::_workerError); - connect(&_worker, &GeoTagWorker::started, this, &GeoTagController::inProgressChanged); - connect(&_worker, &GeoTagWorker::finished, this, &GeoTagController::inProgressChanged); + // qCDebug(GeoTagControllerLog) << Q_FUNC_INFO << this; + + _worker->moveToThread(_workerThread); + + (void) connect(_worker, &GeoTagWorker::progressChanged, this, &GeoTagController::_workerProgressChanged); + (void) connect(_worker, &GeoTagWorker::error, this, &GeoTagController::_workerError); + (void) connect(_workerThread, &QThread::started, _worker, &GeoTagWorker::process); + (void) connect(_workerThread, &QThread::started, this, &GeoTagController::inProgressChanged); + (void) connect(_workerThread, &QThread::finished, this, &GeoTagController::inProgressChanged); } GeoTagController::~GeoTagController() { + // qCDebug(GeoTagControllerLog) << Q_FUNC_INFO << this; +} +void GeoTagController::cancelTagging() +{ + (void) QMetaObject::invokeMethod(_worker, "cancelTagging", Qt::AutoConnection); + (void) QMetaObject::invokeMethod(_workerThread, "quit", Qt::AutoConnection); + _workerThread->wait(); } -void GeoTagController::setLogFile(QString filename) +QString GeoTagController::logFile() const { - filename = QUrl(filename).toLocalFile(); - if (!filename.isEmpty()) { - _worker.setLogFile(filename); - emit logFileChanged(filename); - } + return _worker->logFile(); } -void GeoTagController::setImageDirectory(QString dir) +QString GeoTagController::imageDirectory() const { - dir = QUrl(dir).toLocalFile(); - if (!dir.isEmpty()) { - _worker.setImageDirectory(dir); - emit imageDirectoryChanged(dir); - if(_worker.saveDirectory() == "") { - QDir saveDirectory = QDir(_worker.imageDirectory() + kTagged); - if(saveDirectory.exists()) { - _setErrorMessage(tr("Images have alreay been tagged. Existing images will be removed.")); - return; - } - } + return _worker->imageDirectory(); +} + +QString GeoTagController::saveDirectory() const +{ + return _worker->saveDirectory(); +} + +bool GeoTagController::inProgress() const +{ + return _workerThread->isRunning(); +} + +void GeoTagController::setLogFile(const QString &filename) +{ + if (filename.isEmpty()) { + _setErrorMessage(tr("Empty Filename.")); + return; + } + + const QFileInfo logFileInfo = QFileInfo(filename); + if (!logFileInfo.exists() || !logFileInfo.isFile()) { + _setErrorMessage(tr("Invalid Filename.")); + return; } - _errorMessage.clear(); - emit errorMessageChanged(_errorMessage); + + _worker->setLogFile(filename); + emit logFileChanged(filename); + + _setErrorMessage(QString()); } -void GeoTagController::setSaveDirectory(QString dir) +void GeoTagController::setImageDirectory(const QString &dir) { - dir = QUrl(dir).toLocalFile(); - if (!dir.isEmpty()) { - _worker.setSaveDirectory(dir); - emit saveDirectoryChanged(dir); - //-- Check and see if there are images already there - QDir saveDirectory = QDir(_worker.saveDirectory()); - saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); - QStringList nameFilters; - nameFilters << "*.jpg" << "*.JPG"; - saveDirectory.setNameFilters(nameFilters); - QStringList imageList = saveDirectory.entryList(); - if(!imageList.isEmpty()) { - _setErrorMessage(tr("The save folder already contains images.")); + if (dir.isEmpty()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + const QFileInfo imageDirectoryInfo = QFileInfo(dir); + if (!imageDirectoryInfo.exists() || !imageDirectoryInfo.isDir()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + _worker->setImageDirectory(dir); + emit imageDirectoryChanged(dir); + + if (_worker->saveDirectory().isEmpty()) { + const QDir saveDirectory = QDir(_worker->imageDirectory() + kTagged); + if (saveDirectory.exists()) { + _setErrorMessage(tr("Images have already been tagged. Existing images will be removed.")); return; } } - _errorMessage.clear(); - emit errorMessageChanged(_errorMessage); + + _setErrorMessage(QString()); +} + +void GeoTagController::setSaveDirectory(const QString &dir) +{ + if (dir.isEmpty()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + const QFileInfo saveDirectoryInfo = QFileInfo(dir); + if (!saveDirectoryInfo.exists() || !saveDirectoryInfo.isDir()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + _worker->setSaveDirectory(dir); + emit saveDirectoryChanged(dir); + + QDir saveDirectory = QDir(_worker->saveDirectory()); + saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); + QStringList nameFilters; + nameFilters << "*.jpg" << "*.JPG"; + saveDirectory.setNameFilters(nameFilters); + const QStringList imageList = saveDirectory.entryList(); + if (!imageList.isEmpty()) { + _setErrorMessage(tr("The save folder already contains images.")); + return; + } + + _setErrorMessage(QString()); } void GeoTagController::startTagging() { - _errorMessage.clear(); - emit errorMessageChanged(_errorMessage); - QDir imageDirectory = QDir(_worker.imageDirectory()); - if(!imageDirectory.exists()) { + _setErrorMessage(QString()); + + const QDir imageDirectory = QDir(_worker->imageDirectory()); + if (!imageDirectory.exists()) { _setErrorMessage(tr("Cannot find the image directory.")); return; } - if(_worker.saveDirectory() == "") { - QDir oldTaggedFolder = QDir(_worker.imageDirectory() + kTagged); - if(oldTaggedFolder.exists()) { + + if (_worker->saveDirectory().isEmpty()) { + QDir oldTaggedFolder = QDir(_worker->imageDirectory() + kTagged); + if (oldTaggedFolder.exists()) { oldTaggedFolder.removeRecursively(); - if(!imageDirectory.mkdir(_worker.imageDirectory() + kTagged)) { + if (!imageDirectory.mkdir(_worker->imageDirectory() + kTagged)) { _setErrorMessage(tr("Couldn't replace the previously tagged images")); return; } } } else { - QDir saveDirectory = QDir(_worker.saveDirectory()); - if(!saveDirectory.exists()) { + const QDir saveDirectory = QDir(_worker->saveDirectory()); + if (!saveDirectory.exists()) { _setErrorMessage(tr("Cannot find the save directory.")); return; } } - _worker.start(); -} -void GeoTagController::_workerProgressChanged(double progress) -{ - _progress = progress; - emit progressChanged(progress); -} - -void GeoTagController::_workerError(QString errorMessage) -{ - _errorMessage = errorMessage; - emit errorMessageChanged(errorMessage); -} - - -void GeoTagController::_setErrorMessage(const QString& error) -{ - _errorMessage = error; - emit errorMessageChanged(error); + (void) QMetaObject::invokeMethod(_workerThread, "start", Qt::AutoConnection); } diff --git a/src/AnalyzeView/GeoTagController.h b/src/AnalyzeView/GeoTagController.h index 2311c282678..8ff327af8dd 100644 --- a/src/AnalyzeView/GeoTagController.h +++ b/src/AnalyzeView/GeoTagController.h @@ -9,12 +9,13 @@ #pragma once +#include #include #include -#include #include -#include "GeoTagWorker.h" +class GeoTagWorker; +class QThread; Q_DECLARE_LOGGING_CATEGORY(GeoTagControllerLog) @@ -24,56 +25,53 @@ class GeoTagController : public QObject Q_OBJECT QML_ELEMENT -public: - GeoTagController(); - ~GeoTagController(); - Q_PROPERTY(QString logFile READ logFile WRITE setLogFile NOTIFY logFileChanged) Q_PROPERTY(QString imageDirectory READ imageDirectory WRITE setImageDirectory NOTIFY imageDirectoryChanged) Q_PROPERTY(QString saveDirectory READ saveDirectory WRITE setSaveDirectory NOTIFY saveDirectoryChanged) + Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) + Q_PROPERTY(double progress READ progress NOTIFY progressChanged) + Q_PROPERTY(bool inProgress READ inProgress NOTIFY inProgressChanged) - /// Set to an error message is geotagging fails - Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) - - /// Progress indicator: 0-100 - Q_PROPERTY(double progress READ progress NOTIFY progressChanged) - - /// true: Currently in the process of tagging - Q_PROPERTY(bool inProgress READ inProgress NOTIFY inProgressChanged) +public: + GeoTagController(QObject *parent = nullptr); + ~GeoTagController(); Q_INVOKABLE void startTagging(); - Q_INVOKABLE void cancelTagging() { _worker.cancelTagging(); } + Q_INVOKABLE void cancelTagging(); - QString logFile () const { return _worker.logFile(); } - QString imageDirectory () const { return _worker.imageDirectory(); } - QString saveDirectory () const { return _worker.saveDirectory(); } - double progress () const { return _progress; } - bool inProgress () const { return _worker.isRunning(); } - QString errorMessage () const { return _errorMessage; } + QString logFile() const; + QString imageDirectory() const; + QString saveDirectory() const; + /// Progress indicator: 0-100 + double progress() const { return _progress; } + /// true: Currently in the process of tagging + bool inProgress() const; + /// Set to an error message if geotagging fails + QString errorMessage() const { return _errorMessage; } - void setLogFile (QString file); - void setImageDirectory (QString dir); - void setSaveDirectory (QString dir); + void setLogFile(const QString &file); + void setImageDirectory(const QString &dir); + void setSaveDirectory(const QString &dir); signals: - void logFileChanged (QString logFile); - void imageDirectoryChanged (QString imageDirectory); - void saveDirectoryChanged (QString saveDirectory); - void progressChanged (double progress); - void inProgressChanged (); - void errorMessageChanged (QString errorMessage); + void logFileChanged(const QString &logFile); + void imageDirectoryChanged(const QString &imageDirectory); + void saveDirectoryChanged(const QString &saveDirectory); + void progressChanged(double progress); + void inProgressChanged(); + void errorMessageChanged(const QString &errorMessage); private slots: - void _workerProgressChanged (double progress); - void _workerError (QString errorMsg); - void _setErrorMessage (const QString& error); + void _workerProgressChanged(double progress) { if (progress != _progress) { _progress = progress; emit progressChanged(_progress); } } + void _setErrorMessage(const QString &errorMsg) { if (errorMsg != _errorMessage) { _errorMessage = errorMsg; emit errorMessageChanged(_errorMessage); } } + void _workerError(const QString &errorMsg) { _setErrorMessage(errorMsg); } private: - QString _errorMessage; - double _progress; - bool _inProgress; - - GeoTagWorker _worker; + QString _errorMessage; + double _progress = 0.; + bool _inProgress = false; + GeoTagWorker *_worker = nullptr; + QThread *_workerThread = nullptr; - static constexpr const char* kTagged = "/TAGGED"; + static constexpr const char *kTagged = "/TAGGED"; }; diff --git a/src/AnalyzeView/GeoTagPage.qml b/src/AnalyzeView/GeoTagPage.qml index 7c8ec8b3918..f744c233438 100644 --- a/src/AnalyzeView/GeoTagPage.qml +++ b/src/AnalyzeView/GeoTagPage.qml @@ -76,7 +76,7 @@ AnalyzePage { nameFilters: [qsTr("ULog file (*.ulg)"), qsTr("PX4 log file (*.px4log)"), qsTr("All Files (*)")] defaultSuffix: "ulg" onAcceptedForLoad: (file) => { - geoController.logFile = openLogFile.file + geoController.logFile = file close() } } @@ -101,7 +101,7 @@ AnalyzePage { title: qsTr("Select image directory") selectFolder: true onAcceptedForLoad: (file) => { - geoController.imageDirectory = selectImageDir.file + geoController.imageDirectory = file close() } } @@ -126,7 +126,7 @@ AnalyzePage { title: qsTr("Select save directory") selectFolder: true onAcceptedForLoad: (file) => { - geoController.saveDirectory = selectDestDir.file + geoController.saveDirectory = file close() } } diff --git a/src/AnalyzeView/GeoTagWorker.cc b/src/AnalyzeView/GeoTagWorker.cc index 91aecb614ed..f1fa0ef6313 100644 --- a/src/AnalyzeView/GeoTagWorker.cc +++ b/src/AnalyzeView/GeoTagWorker.cc @@ -15,22 +15,84 @@ #include -QGC_LOGGING_CATEGORY(GeoTagWorkerLog, "qgc.analyzeview.geotagworker") +QGC_LOGGING_CATEGORY(GeoTagWorkerLog, "TEST.analyzeview.geotagworker") -GeoTagWorker::GeoTagWorker() - : _cancel(false) +GeoTagWorker::GeoTagWorker(QObject *parent) + : QObject(parent) { + // qCDebug(GeoTagWorkerLog) << Q_FUNC_INFO << this; +#ifdef QT_DEBUG + (void) connect(this, &GeoTagWorker::error, this, [this](const QString &errorMsg) { + qCDebug(GeotaggingLog) << errorMsg; + }, Qt::AutoConnection); +#endif } -void GeoTagWorker::run() +GeoTagWorker::~GeoTagWorker() +{ + // qCDebug(GeoTagWorkerLog) << Q_FUNC_INFO << this; +} + +bool GeoTagWorker::process() { _cancel = false; emit progressChanged(1); - double nSteps = 5; - // Load Images + if (!_loadImages()) { + return false; + } + + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + if (!_parseExif()) { + return false; + } + + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + if (!_initParser()) { + return false; + } + + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + if (!_triggerFiltering()) { + return false; + } + + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + if (!_tagImages()) { + return false; + } + + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + emit progressChanged(100); + + return true; +} + +bool GeoTagWorker::_loadImages() +{ _imageList.clear(); + QDir imageDirectory = QDir(_imageDirectory); imageDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); imageDirectory.setSorting(QDir::Name); @@ -38,46 +100,56 @@ void GeoTagWorker::run() nameFilters << "*.jpg" << "*.JPG"; imageDirectory.setNameFilters(nameFilters); _imageList = imageDirectory.entryInfoList(); - if(_imageList.isEmpty()) { + if (_imageList.isEmpty()) { emit error(tr("The image directory doesn't contain images, make sure your images are of the JPG format")); - return; + return false; } - emit progressChanged((100/nSteps)); - // Parse EXIF + emit progressChanged(100. / kSteps); + + return true; +} + +bool GeoTagWorker::_parseExif() +{ _imageTime.clear(); + for (int i = 0; i < _imageList.size(); ++i) { QFile file(_imageList.at(i).absoluteFilePath()); if (!file.open(QIODevice::ReadOnly)) { emit error(tr("Geotagging failed. Couldn't open an image.")); - return; + return false; } - QByteArray imageBuffer = file.readAll(); + const QByteArray imageBuffer = file.readAll(); file.close(); - _imageTime.append(ExifParser::readTime(imageBuffer)); + (void) _imageTime.append(ExifParser::readTime(imageBuffer)); - emit progressChanged((100/nSteps) + ((100/nSteps) / _imageList.size())*i); + emit progressChanged((100. / kSteps) + ((100. / kSteps) / _imageList.size()) * i); if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; emit error(tr("Tagging cancelled")); - return; + return false; } } - // Load log - bool isULog = _logFile.endsWith(".ulg", Qt::CaseSensitive); + return true; +} + +bool GeoTagWorker::_initParser() +{ + const bool isULog = _logFile.endsWith(".ulg", Qt::CaseSensitive); + QFile file(_logFile); if (!file.open(QIODevice::ReadOnly)) { emit error(tr("Geotagging failed. Couldn't open log file.")); - return; + return false; } - QByteArray log = file.readAll(); + const QByteArray log = file.readAll(); file.close(); - // Instantiate appropriate parser _triggerList.clear(); + bool parseComplete = false; QString errorString; if (isULog) { @@ -88,104 +160,93 @@ void GeoTagWorker::run() if (!parseComplete) { if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; emit error(tr("Tagging cancelled")); - return; + return false; } else { - qCDebug(GeotaggingLog) << "Log parsing failed"; errorString = tr("%1 - tagging cancelled").arg(errorString.isEmpty() ? tr("Log parsing failed") : errorString); emit error(errorString); - return; + return false; } } - emit progressChanged(3*(100/nSteps)); - qCDebug(GeotaggingLog) << "Found " << _triggerList.count() << " trigger logs."; + qCDebug(GeotaggingLog) << "Found" << _triggerList.count() << "trigger logs."; - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; + emit progressChanged(3. * (100. / kSteps)); + + return true; +} + +bool GeoTagWorker::_triggerFiltering() +{ + _imageIndices.clear(); + _triggerIndices.clear(); + + if (_imageList.count() > _triggerList.count()) { + qCDebug(GeotaggingLog) << "Detected missing feedback packets."; + } else if (_imageList.count() < _triggerList.count()) { + qCDebug(GeotaggingLog) << "Detected missing image frames."; } - // Filter Trigger - if (!triggerFiltering()) { - qCDebug(GeotaggingLog) << "Geotagging failed in trigger filtering"; - emit error(tr("Geotagging failed in trigger filtering")); - return; + // TODO: handle _triggerList does not start at 0, causes issue in _tagImages loop counter + if (_triggerList.first().imageSequence != 0) { + qCDebug(GeotaggingLog) << "Image sequence does not start at beginning."; } - emit progressChanged(4*(100/nSteps)); - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; + for (int i = 0; i < std::min(_imageList.count(), _triggerList.count()); i++) { + (void) _imageIndices.append(static_cast(_triggerList[i].imageSequence)); + (void) _triggerIndices.append(i); } - // Tag images - auto maxIndex = std::min(_imageIndices.count(), _triggerIndices.count()); + emit progressChanged(4. * (100. / kSteps)); + + return true; +} + +bool GeoTagWorker::_tagImages() +{ + qsizetype maxIndex = std::min(_imageIndices.count(), _triggerIndices.count()); maxIndex = std::min(maxIndex, _imageList.count()); - for(int i = 0; i < maxIndex; i++) { - int imageIndex = _imageIndices[i]; + for (int i = 0; i < maxIndex; i++) { + const int imageIndex = _imageIndices[i]; if (imageIndex >= _imageList.count()) { emit error(tr("Geotagging failed. Requesting image #%1, but only %2 images present.").arg(imageIndex).arg(_imageList.count())); - return; + return false; } - QFile fileRead(_imageList.at(_imageIndices[i]).absoluteFilePath()); + + const QFileInfo imageInfo = _imageList.at(imageIndex); + QFile fileRead(imageInfo.absoluteFilePath()); if (!fileRead.open(QIODevice::ReadOnly)) { emit error(tr("Geotagging failed. Couldn't open an image.")); - return; + return false; } QByteArray imageBuffer = fileRead.readAll(); fileRead.close(); - if (!ExifParser::write(imageBuffer, _triggerList[_triggerIndices[i]])) { + if (!ExifParser::write(imageBuffer, _triggerList[imageIndex])) { emit error(tr("Geotagging failed. Couldn't write to image.")); - return; + return false; + } + + QFile fileWrite; + if (_saveDirectory.isEmpty()) { + fileWrite.setFileName(_imageDirectory + "/TAGGED/" + imageInfo.fileName()); } else { - QFile fileWrite; - if(_saveDirectory == "") { - fileWrite.setFileName(_imageDirectory + "/TAGGED/" + _imageList.at(_imageIndices[i]).fileName()); - } else { - fileWrite.setFileName(_saveDirectory + "/" + _imageList.at(_imageIndices[i]).fileName()); - } - if (!fileWrite.open(QFile::WriteOnly)) { - emit error(tr("Geotagging failed. Couldn't write to an image.")); - return; - } - fileWrite.write(imageBuffer); - fileWrite.close(); + fileWrite.setFileName(_saveDirectory + "/" + imageInfo.fileName()); } - emit progressChanged(4*(100/nSteps) + ((100/nSteps) / maxIndex)*i); + if (!fileWrite.open(QFile::WriteOnly)) { + emit error(tr("Geotagging failed. Couldn't write to an image.")); + return false; + } + fileWrite.write(imageBuffer); + fileWrite.close(); + + emit progressChanged(4. * (100. / kSteps) + ((100. / kSteps) / maxIndex) * i); if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; emit error(tr("Tagging cancelled")); - return; + return false; } } - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; - } - - emit progressChanged(100); -} - -bool GeoTagWorker::triggerFiltering() -{ - _imageIndices.clear(); - _triggerIndices.clear(); - if(_imageList.count() > _triggerList.count()) { // Logging dropouts - qCDebug(GeotaggingLog) << "Detected missing feedback packets."; - } else if (_imageList.count() < _triggerList.count()) { // Camera skipped frames - qCDebug(GeotaggingLog) << "Detected missing image frames."; - } - for(int i = 0; i < _imageList.count() && i < _triggerList.count(); i++) { - _imageIndices.append(static_cast(_triggerList[i].imageSequence)); - _triggerIndices.append(i); - } return true; } diff --git a/src/AnalyzeView/GeoTagWorker.h b/src/AnalyzeView/GeoTagWorker.h index 0889828edc4..65c89ae2d49 100644 --- a/src/AnalyzeView/GeoTagWorker.h +++ b/src/AnalyzeView/GeoTagWorker.h @@ -9,32 +9,29 @@ #pragma once -#include -#include -#include #include #include +#include +#include Q_DECLARE_LOGGING_CATEGORY(GeoTagWorkerLog) -class GeoTagWorker : public QThread +class GeoTagWorker : public QObject { Q_OBJECT public: - GeoTagWorker(); - - void setLogFile (const QString& logFile) { _logFile = logFile; } - void setImageDirectory (const QString& imageDirectory) { _imageDirectory = imageDirectory; } - void setSaveDirectory (const QString& saveDirectory) { _saveDirectory = saveDirectory; } + GeoTagWorker(QObject *parent = nullptr); + ~GeoTagWorker(); - QString logFile () const { return _logFile; } - QString imageDirectory () const { return _imageDirectory; } - QString saveDirectory () const { return _saveDirectory; } + QString logFile() const { return _logFile; } + void setLogFile(const QString &logFile) { _logFile = logFile; } + QString imageDirectory() const { return _imageDirectory; } + void setImageDirectory(const QString &imageDirectory) { _imageDirectory = imageDirectory; } + QString saveDirectory() const { return _saveDirectory; } + void setSaveDirectory(const QString &saveDirectory) { _saveDirectory = saveDirectory; } - void cancelTagging () { _cancel = true; } - - struct cameraFeedbackPacket { + struct CameraFeedbackPacket { double timestamp; double timestampUTC; uint32_t imageSequence; @@ -46,24 +43,30 @@ class GeoTagWorker : public QThread uint8_t captureResult; }; -protected: - void run() final; - signals: - void error (QString errorMsg); - void taggingComplete (); - void progressChanged (double progress); + void error(const QString &errorMsg); + void progressChanged(double progress); + +public slots: + bool process(); + void cancelTagging() { _cancel = true; } private: - bool triggerFiltering(); + bool _loadImages(); + bool _parseExif(); + bool _initParser(); + bool _triggerFiltering(); + bool _tagImages(); + + bool _cancel = false; + QString _logFile; + QString _imageDirectory; + QString _saveDirectory; + QFileInfoList _imageList; + QList _imageTime; + QList _triggerList; + QList _imageIndices; + QList _triggerIndices; - bool _cancel; - QString _logFile; - QString _imageDirectory; - QString _saveDirectory; - QFileInfoList _imageList; - QList _imageTime; - QList _triggerList; - QList _imageIndices; - QList _triggerIndices; + static constexpr double kSteps = 5.; }; diff --git a/src/AnalyzeView/PX4LogParser.cc b/src/AnalyzeView/PX4LogParser.cc index 12ef498e267..21f9e240e89 100644 --- a/src/AnalyzeView/PX4LogParser.cc +++ b/src/AnalyzeView/PX4LogParser.cc @@ -35,7 +35,7 @@ static constexpr const int triggerLengths[2] = {8, 4}; namespace PX4LogParser { -bool getTagsFromLog(const QByteArray& log, QList& cameraFeedback) +bool getTagsFromLog(const QByteArray& log, QList& cameraFeedback) { // extract header information: message lengths const uint8_t* iptr = reinterpret_cast(log.mid(log.indexOf(gposHeaderHeader) + 4, 1).constData()); @@ -60,7 +60,7 @@ bool getTagsFromLog(const QByteArray& log, QList(log.mid(index + triggerOffsets[0], triggerLengths[0]).data()); diff --git a/src/AnalyzeView/PX4LogParser.h b/src/AnalyzeView/PX4LogParser.h index ede7dc1d402..17994f1a5d6 100644 --- a/src/AnalyzeView/PX4LogParser.h +++ b/src/AnalyzeView/PX4LogParser.h @@ -18,5 +18,5 @@ Q_DECLARE_LOGGING_CATEGORY(PX4LogParserLog) namespace PX4LogParser { - bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback); + bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback); } diff --git a/src/AnalyzeView/ULogParser.cc b/src/AnalyzeView/ULogParser.cc index fde59160397..4897f2c0731 100644 --- a/src/AnalyzeView/ULogParser.cc +++ b/src/AnalyzeView/ULogParser.cc @@ -22,7 +22,7 @@ QGC_LOGGING_CATEGORY(ULogParserLog, "qgc.analyzeview.ulogparser") namespace ULogParser { -bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage) +bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage) { errorMessage.clear(); @@ -51,7 +51,7 @@ bool getTagsFromLog(const QByteArray &log, QList subscription = data->subscription("camera_capture"); for (const TypedDataView &sample : *subscription) { - GeoTagWorker::cameraFeedbackPacket feedback = {0}; + GeoTagWorker::CameraFeedbackPacket feedback = {0}; try { feedback.timestamp = sample.at("timestamp").as() / 1.0e6; // to seconds diff --git a/src/AnalyzeView/ULogParser.h b/src/AnalyzeView/ULogParser.h index 5006e88f164..c813a49a45a 100644 --- a/src/AnalyzeView/ULogParser.h +++ b/src/AnalyzeView/ULogParser.h @@ -22,5 +22,5 @@ Q_DECLARE_LOGGING_CATEGORY(ULogParserLog) namespace ULogParser { /// Get GeoTags from a ULog /// @return true if failed, errorMessage set - bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage); + bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage); } // namespace ULogParser diff --git a/test/AnalyzeView/CMakeLists.txt b/test/AnalyzeView/CMakeLists.txt index a25b4012452..f7e443e3dcf 100644 --- a/test/AnalyzeView/CMakeLists.txt +++ b/test/AnalyzeView/CMakeLists.txt @@ -4,6 +4,8 @@ qt_add_library(AnalyzeViewTest STATIC ExifParserTest.cc ExifParserTest.h + GeoTagControllerTest.cc + GeoTagControllerTest.h LogDownloadTest.cc LogDownloadTest.h MavlinkLogTest.cc diff --git a/test/AnalyzeView/ExifParserTest.cc b/test/AnalyzeView/ExifParserTest.cc index f5fc35e70f3..fc9845038a8 100644 --- a/test/AnalyzeView/ExifParserTest.cc +++ b/test/AnalyzeView/ExifParserTest.cc @@ -31,7 +31,7 @@ void ExifParserTest::_writeTest() QByteArray imageBuffer = file.readAll(); file.close(); - struct GeoTagWorker::cameraFeedbackPacket data; + struct GeoTagWorker::CameraFeedbackPacket data; data.latitude = 37.225; data.longitude = -80.425; diff --git a/test/AnalyzeView/GeoTagControllerTest.cc b/test/AnalyzeView/GeoTagControllerTest.cc new file mode 100644 index 00000000000..9d712f53602 --- /dev/null +++ b/test/AnalyzeView/GeoTagControllerTest.cc @@ -0,0 +1,91 @@ +#include "GeoTagControllerTest.h" +#include "GeoTagController.h" +#include "GeoTagWorker.h" + +#include + +void GeoTagControllerTest::_geoTagControllerTest() +{ + GeoTagController* const controller = new GeoTagController(this); + + /*controller->logFile() + controller->imageDirectory() + controller->saveDirectory() + controller->progress() + controller->inProgress() + controller->errorMessage() + + controller->setLogFile(const QString &file); + controller->setImageDirectory(const QString &dir); + controller->setSaveDirectory(const QString &dir); + + QSignalSpy spyGeoTagControllerLogFile(worker, &GeoTagWorker::progressChanged); + QSignalSpy spyGeoTagControllerImageDirectory(worker, &GeoTagWorker::error); + QSignalSpy spyGeoTagControllerSaveDirectory(worker, &GeoTagWorker::progressChanged); + QSignalSpy spyGeoTagControllerProgress(worker, &GeoTagWorker::progressChanged); + QSignalSpy spyGeoTagControllerInProgress(worker, &GeoTagWorker::progressChanged); + QSignalSpy spyGeoTagControllerError(worker, &GeoTagWorker::progressChanged); + + QCOMPARE(spyGeoTagControllerLogFile.wait(5000), true); + QCOMPARE(spyGeoTagControllerImageDirectory.wait(5000), true); + QCOMPARE(spyGeoTagControllerSaveDirectory.wait(5000), true); + QCOMPARE(spyGeoTagControllerProgress.wait(5000), true); + QCOMPARE(spyGeoTagControllerInProgress.wait(5000), true); + QCOMPARE(spyGeoTagControllerError.wait(5000), true);*/ +} + +void GeoTagControllerTest::_geoTagWorkerTest() +{ + GeoTagWorker* const worker = new GeoTagWorker(this); + + const QDir tempDir = QDir(QDir::tempPath()); + const QString imageDirPath = QDir::tempPath() + "/QGC_GEOTAG_TEST"; + const QString subDirName = QString("QGC_GEOTAG_TEST"); + if (!tempDir.exists(subDirName)) { + QVERIFY(tempDir.mkdir(subDirName)); + } else { + QDir subDir(QDir::tempPath() + "/" + subDirName); + const QStringList files = subDir.entryList(QDir::Files); + for (const QString &fileName : files) { + QVERIFY(subDir.remove(fileName)); + } + } + + for (int i = 0; i < 58; ++i) { + QFile tempFile(imageDirPath + QStringLiteral("/geotag_temp_image_%1.jpg").arg(i)); + QVERIFY(tempFile.open(QIODevice::ReadWrite)); + QImage blankImage(100, 100, QImage::Format_RGB32); + blankImage.fill(Qt::white); + const QString tempFileName = tempFile.fileName(); + blankImage.save(tempFileName, "JPG"); + tempFile.close(); + } + + QDir imageDir = QDir(imageDirPath); + if (!imageDir.exists("TAGGED")) { + QVERIFY(imageDir.mkdir("TAGGED")); + } else { + QDir subDir(imageDirPath + "/TAGGED"); + const QStringList files = subDir.entryList(QDir::Files); + for (const QString &fileName : files) { + QVERIFY(subDir.remove(fileName)); + } + } + + const QFileInfo log = QFileInfo(":/SampleULog.ulg"); + worker->setLogFile(log.filePath()); + worker->setImageDirectory(imageDirPath); + worker->setSaveDirectory(worker->imageDirectory() + "/TAGGED"); + + QVERIFY(worker->process()); + + // worker->cancelTagging(); + + // QSignalSpy spyGeoTagWorkerProgress(worker, &GeoTagWorker::progressChanged); + // QSignalSpy spyGeoTagWorkerError(worker, &GeoTagWorker::error); + // QSignalSpy spyGeoTagWorkerComplete(worker, &GeoTagWorker::taggingComplete); + + // QCOMPARE(spyGeoTagWorkerProgress.wait(5000), true); + // QCOMPARE(spyGeoTagWorkerError.wait(5000), true); + // QCOMPARE(spyGeoTagWorkerComplete.wait(5000), true); +} diff --git a/test/AnalyzeView/GeoTagControllerTest.h b/test/AnalyzeView/GeoTagControllerTest.h new file mode 100644 index 00000000000..e51b646bb18 --- /dev/null +++ b/test/AnalyzeView/GeoTagControllerTest.h @@ -0,0 +1,12 @@ +#pragma once + +#include "UnitTest.h" + +class GeoTagControllerTest : public UnitTest +{ + Q_OBJECT + +private slots: + void _geoTagControllerTest(); + void _geoTagWorkerTest(); +}; diff --git a/test/AnalyzeView/PX4LogParserTest.cc b/test/AnalyzeView/PX4LogParserTest.cc index 5a9416fffff..70eb2709df7 100644 --- a/test/AnalyzeView/PX4LogParserTest.cc +++ b/test/AnalyzeView/PX4LogParserTest.cc @@ -12,11 +12,11 @@ void PX4LogParserTest::_getTagsFromLogTest() const QByteArray logBuffer = file.readAll(); file.close(); - QList cameraFeedback; + QList cameraFeedback; QVERIFY(PX4LogParser::getTagsFromLog(logBuffer, cameraFeedback)); QVERIFY(!cameraFeedback.isEmpty()); - GeoTagWorker::cameraFeedbackPacket firstCameraFeedback = cameraFeedback.first(); + GeoTagWorker::CameraFeedbackPacket firstCameraFeedback = cameraFeedback.first(); QVERIFY(!qFuzzyIsNull(firstCameraFeedback.timestamp)); QVERIFY(firstCameraFeedback.imageSequence != 0);*/ } diff --git a/test/AnalyzeView/ULogParserTest.cc b/test/AnalyzeView/ULogParserTest.cc index 06531333922..b83bcdc9e3d 100644 --- a/test/AnalyzeView/ULogParserTest.cc +++ b/test/AnalyzeView/ULogParserTest.cc @@ -12,13 +12,13 @@ void ULogParserTest::_getTagsFromLogTest() const QByteArray logBuffer = file.readAll(); file.close(); - QList cameraFeedback; + QList cameraFeedback; QString errorMessage; QVERIFY(ULogParser::getTagsFromLog(logBuffer, cameraFeedback, errorMessage)); QVERIFY(errorMessage.isEmpty()); QVERIFY(!cameraFeedback.isEmpty()); - const GeoTagWorker::cameraFeedbackPacket firstCameraFeedback = cameraFeedback.constFirst(); + const GeoTagWorker::CameraFeedbackPacket firstCameraFeedback = cameraFeedback.constFirst(); // QVERIFY(!qFuzzyIsNull(firstCameraFeedback.timestamp)); QVERIFY(firstCameraFeedback.imageSequence != 0); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 24f743bcd45..ef75ce1f526 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,7 @@ add_qgc_test(ADSBTest) add_subdirectory(AnalyzeView) add_qgc_test(ExifParserTest) +add_qgc_test(GeoTagControllerTest) # add_qgc_test(LogDownloadTest) # add_qgc_test(MavlinkLogTest) add_qgc_test(PX4LogParserTest) diff --git a/test/UnitTestList.cc b/test/UnitTestList.cc index 62476b9e8f9..7a408f67211 100644 --- a/test/UnitTestList.cc +++ b/test/UnitTestList.cc @@ -17,6 +17,7 @@ // AnalyzeView #include "ExifParserTest.h" +#include "GeoTagControllerTest.h" // #include "MavlinkLogTest.h" // #include "LogDownloadTest.h" #include "PX4LogParserTest.h" @@ -113,6 +114,7 @@ int runTests(bool stress, QStringView unitTestOptions) // AnalyzeView UT_REGISTER_TEST(ExifParserTest) + UT_REGISTER_TEST(GeoTagControllerTest) // UT_REGISTER_TEST(MavlinkLogTest) // UT_REGISTER_TEST(LogDownloadTest) UT_REGISTER_TEST(PX4LogParserTest)