diff --git a/docs/user/report.md b/docs/user/report.md index 0cb9e94..6e001d8 100644 --- a/docs/user/report.md +++ b/docs/user/report.md @@ -20,6 +20,6 @@ If you encounter a bug or abnormal behaviors please report it by email at benjam Other information: --- - You can attach an archive with the Tracking_Result folder if necessary. + Do not forget to attach the log file that can be generated Help -> Generate log. Kind regards. diff --git a/src/annotation.cpp b/src/annotation.cpp index 6c46cd2..8a1deb3 100644 --- a/src/annotation.cpp +++ b/src/annotation.cpp @@ -31,22 +31,24 @@ This file is part of Fast Track. */ /** - * @brief Clear the object. -*/ + * @brief Clear the object. + */ void Annotation::clear() { if (!annotationFile->fileName().isEmpty()) { writeToFile(); + annotationFile->close(); annotationFile->setFileName(""); } findIndexes.clear(); findIndex = -1; annotations->clear(); + isActive = false; } /** - * @brief Set the path for the annotation. - * @param[in] filePath Path to the tracking folder. -*/ + * @brief Set the path for the annotation. + * @param[in] filePath Path to the tracking folder. + */ bool Annotation::setPath(const QString &filePath) { clear(); annotationFile->setFileName(filePath + "annotation.txt"); @@ -65,30 +67,30 @@ bool Annotation::setPath(const QString &filePath) { } } annotationFile->close(); + isActive = true; return true; } /** - * @brief Constructs the annotation object from a file path. - * @param[in] filePath Path to the tracking folder. -*/ -Annotation::Annotation(const QString &filePath) { - annotationFile = new QFile(); - annotations = new QMap(); + * @brief Constructs the annotation object from a file path. + * @param[in] filePath Path to the tracking folder. + */ +Annotation::Annotation(const QString &filePath) : Annotation() { setPath(filePath); } /** - * @brief Constructs the annotation object from a file path. -*/ + * @brief Constructs the annotation object from a file path. + */ Annotation::Annotation() { annotationFile = new QFile(); annotations = new QMap(); + isActive = false; } /** - * @brief Writes all the annotation to a file. -*/ + * @brief Writes all the annotation to a file. + */ void Annotation::writeToFile() { if (!annotationFile->open(QIODevice::WriteOnly)) { qInfo() << "Can't open file"; @@ -104,28 +106,32 @@ void Annotation::writeToFile() { } /** - * @brief Adds an annotation to the annotation QMap. - * @param[in] index Image index. - * @param[in] text Annotation text. -*/ + * @brief Adds an annotation to the annotation QMap. + * @param[in] index Image index. + * @param[in] text Annotation text. + */ void Annotation::write(int index, const QString &text) { - annotations->insert(index, text); - writeToFile(); + if (isActive) { + annotations->insert(index, text); + writeToFile(); + } } /** - * @brief Reads an annotation from the annotation QMap. - * @param[in] index Image index. -*/ + * @brief Reads an annotation from the annotation QMap. + * @param[in] index Image index. + */ void Annotation::read(int index) { - QString text = annotations->value(index); - emit(annotationText(text)); + if (isActive) { + QString text = annotations->value(index); + emit(annotationText(text)); + } } /** - * @brief Finds the index of all the annotation with expression inside their text. - * @param[in] expression Expression to find, case sensitive. -*/ + * @brief Finds the index of all the annotation with expression inside their text. + * @param[in] expression Expression to find, case sensitive. + */ void Annotation::find(const QString &expression) { findIndexes.clear(); QMapIterator i(*annotations); @@ -139,8 +145,8 @@ void Annotation::find(const QString &expression) { } /** - * @brief Returns the next element of the findIndexes list of annotations that contains the expression to find. -*/ + * @brief Returns the next element of the findIndexes list of annotations that contains the expression to find. + */ int Annotation::next() { ++findIndex; if (findIndex >= findIndexes.length()) { @@ -155,8 +161,8 @@ int Annotation::next() { } /** - * @brief Returns the previous element of the findIndexes list of annotations that contains the expression to find. -*/ + * @brief Returns the previous element of the findIndexes list of annotations that contains the expression to find. + */ int Annotation::prev() { --findIndex; if (findIndex < 0) { @@ -171,7 +177,9 @@ int Annotation::prev() { } Annotation::~Annotation() { - writeToFile(); + if (isActive) { + writeToFile(); + } delete annotationFile; delete annotations; } diff --git a/src/annotation.h b/src/annotation.h index 29e9975..d1e7e2d 100644 --- a/src/annotation.h +++ b/src/annotation.h @@ -32,9 +32,9 @@ class Annotation : public QWidget { signals: /** - * @brief Emitted when a new annotation is read. - * @param text Text of the requested annotation. - */ + * @brief Emitted when a new annotation is read. + * @param text Text of the requested annotation. + */ void annotationText(const QString &text); public: @@ -44,6 +44,7 @@ class Annotation : public QWidget { Annotation &operator=(const Annotation &T) = delete; ~Annotation(); bool setPath(const QString &annotationFile); + bool isActive; }; #endif diff --git a/src/interactive.cpp b/src/interactive.cpp index 4b33f7b..cef39c7 100644 --- a/src/interactive.cpp +++ b/src/interactive.cpp @@ -359,6 +359,11 @@ Interactive::Interactive(QWidget *parent) : QMainWindow(parent), connect(ui->actionContact, &QAction::triggered, []() { QDesktopServices::openUrl(QUrl("mailto:benjamin.gallois@fasttrack.sh?subject=[fasttrack]", QUrl::TolerantMode)); }); + connect(ui->actionGenerateLog, &QAction::triggered, [this]() { + QString fileName = QFileDialog::getSaveFileName(this, tr("Save Log File"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("Logs (*.log)")); + QFile::remove(fileName); + QFile::copy(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/fasttrack.log", fileName); + }); connect(ui->actionAbout, &QAction::triggered, []() { QMessageBox aboutBox; aboutBox.setText("FastTrack is a desktop tracking software, easy to install, easy to use, and performant.
Created and maintained by Benjamin Gallois.
Distributed under the terms of the GPL3.0 license.
"); @@ -588,11 +593,12 @@ void Interactive::openFolder() { crop(); } // If an error occurs during the opening, resets the information table and warns the user - catch (...) { + catch (exception &e) { ui->informationTable->item(ui->informationTable->row(ui->informationTable->findItems("Path", Qt::MatchExactly)[0]), 1)->setText(""); ui->informationTable->item(ui->informationTable->row(ui->informationTable->findItems("Image number", Qt::MatchExactly)[0]), 1)->setText("0"); ui->informationTable->item(ui->informationTable->row(ui->informationTable->findItems("Image width", Qt::MatchExactly)[0]), 1)->setText("0"); ui->informationTable->item(ui->informationTable->row(ui->informationTable->findItems("Image height", Qt::MatchExactly)[0]), 1)->setText("0"); + qWarning() << QString::fromStdString(e.what()) << "occurs during opening of " << dir; emit(message("No image found.")); } } @@ -678,7 +684,12 @@ void Interactive::display(int index, int scale) { } display(frame); } + catch (const std::exception &e) { + qWarning() << QString::fromStdString(e.what()) << " occurs at image " << index << " display"; + emit(message(QString::fromStdString(e.what()) + QString(" occurs on image %1.").arg(index))); + } catch (...) { + qWarning() << "Unknown error occurs at image " << index << " display"; emit(message(QString("An error occurs on image %1.").arg(index))); } } @@ -795,12 +806,14 @@ void Interactive::computeBackground() { try { background = Tracking::backgroundExtraction(*video, nBack, method, registrationMethod); } - catch (const std::exception &ex) { - emit(message("An error occurs. Please change the registration method")); - } catch (const std::runtime_error &e) { + qWarning() << QString::fromStdString(e.what()) << "occurs during background computation"; emit(message(e.what())); } + catch (...) { + qWarning() << "Unknown error occurs during background computation"; + emit(message("An error occurs. Please change the registration method")); + } return background; }); watcher->setFuture(future); diff --git a/src/interactive.h b/src/interactive.h index 90f5891..9d5ff38 100644 --- a/src/interactive.h +++ b/src/interactive.h @@ -44,6 +44,7 @@ This file is part of Fast Track. #include #include #include +#include #include #include #include diff --git a/src/interactive.ui b/src/interactive.ui index db3d21c..b39edab 100644 --- a/src/interactive.ui +++ b/src/interactive.ui @@ -62,8 +62,8 @@ 0 0 - 568 - 444 + 569 + 435 @@ -193,7 +193,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">The program will compute a cost for each pair of objects in two successive images using the distance </span><span style=" font-size:8pt; font-weight:600;">d</span><span style=" font-size:8pt; font-weight:600; vertical-align:sub;">ij</span><span style=" font-size:8pt;">, the angular difference </span><span style=" font-size:8pt; font-weight:600;">a</span><span style=" font-size:8pt; font-weight:600; vertical-align:sub;">ij ,</span><span style=" font-size:8pt;"> the area difference </span><span style=" font-size:8pt; font-weight:600;">ar</span><span style=" font-size:8pt; font-weight:600; vertical-align:sub;">ij </span><span style=" font-size:8pt;"> and the perimeter difference </span><span style=" font-size:8pt; font-weight:600;">p</span><span style=" font-size:8pt; font-weight:600; vertical-align:sub;">ij</span><span style=" font-size:8pt;"> between two objects. These two parameters can be computed using the direction and barycenter of the head, tail or body by selecting the</span><span style=" font-size:8pt; font-weight:600;"> Spot to track </span><span style=" font-size:8pt;">accordingly.</span></p> @@ -1185,7 +1185,7 @@ corner: 0 0 1156 - 20 + 19 @@ -1217,6 +1217,7 @@ corner: + @@ -1717,6 +1718,11 @@ corner: Ask a question + + + Generate a log + + diff --git a/src/main.cpp b/src/main.cpp index 38a1d27..afcc341 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,23 +16,37 @@ This file is part of Fast Track. */ #include +#include #include #include #include +#include #include #include #include "mainwindow.h" +QScopedPointer logFile; + +void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + QTextStream out(logFile.data()); + out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz "); + out << context.category << ": " << msg << Qt::endl; +} + int main(int argc, char *argv[]) { QApplication a(argc, argv); QPixmap pixmap(":/assets/icon.png"); QSplashScreen splash(pixmap); splash.show(); - MainWindow w; a.setApplicationName("FastTrack"); a.setApplicationVersion(APP_VERSION); a.setOrganizationName("FastTrackOrg"); a.setOrganizationDomain("www.fasttrack.sh"); + logFile.reset(new QFile(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/fasttrack.log")); + QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + logFile.data()->open(QFile::Append | QFile::Text); + qInstallMessageHandler(messageHandler); + MainWindow w; w.setWindowIcon(QIcon(":/assets/icon.png")); QFontDatabase::addApplicationFont(":/assets/Font.ttf"); w.setStyleSheet("QWidget {font-family: 'Lato', sans-serif;}"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b2dee84..303b3b9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -71,14 +71,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), trayIcon->setContextMenu(trayMenu); trayIcon->show(); - // Setup style - QFile stylesheet(""); - - if (stylesheet.open(QIODevice::ReadOnly | QIODevice::Text)) { // Read the theme file - qApp->setStyleSheet(stylesheet.readAll()); - stylesheet.close(); - } - QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager, &QNetworkAccessManager::finished, [this](QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { diff --git a/src/replay.cpp b/src/replay.cpp index 3c24b43..34d4122 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -403,6 +403,7 @@ void Replay::loadReplay(const QString& dir) { // If the user explicitly select another Tracking_Result folder, these data are loaded. // Delete existing data + QApplication::setOverrideCursor(Qt::WaitCursor); clear(); if (!dir.length()) { memoryDir.clear(); @@ -440,13 +441,15 @@ void Replay::loadReplay(const QString& dir) { ui->annotation->blockSignals(false); emit(opened(isReplayable)); } - catch (...) { + catch (const std::exception& e) { + qWarning() << QString::fromStdString(e.what()) << " occurs opening " << dir; isReplayable = false; memoryDir.clear(); QMessageBox msgBox; msgBox.setText("No file found."); msgBox.exec(); } + QApplication::restoreOverrideCursor(); } /** @@ -489,91 +492,99 @@ void Replay::loadTrackingDir(const QString& dir) { * @brief Displays the image and the tracking data in the ui->displayReplay. Triggered when the ui->replaySlider value is changed. */ void Replay::loadFrame(int frameIndex) { - if (!isReplayable) { - return; - } + try { + if (!isReplayable) { + return; + } - currentIndex = frameIndex; - object1Replay->clear(); + currentIndex = frameIndex; + object1Replay->clear(); - UMat frame; - if (!video->getImage(frameIndex, frame)) { - return; - } - cvtColor(frame, frame, COLOR_GRAY2BGR); + UMat frame; + if (!video->getImage(frameIndex, frame)) { + return; + } + cvtColor(frame, frame, COLOR_GRAY2BGR); - if (!trackingData->isEmpty) { - // Takes the tracking data corresponding to the replayed frame and parse data to display - int scale = ui->replaySize->value(); - QList> dataImage = trackingData->getData(frameIndex); - for (const QHash& coordinate : dataImage) { - int id = coordinate.value("id"); - - object1Replay->addItem(QString::number(id)); - - if (ui->ellipseBox->currentIndex() != 4) { - switch (ui->ellipseBox->currentIndex()) { - case 0: // Head + Tail - cv::ellipse(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Size(static_cast(coordinate.value("headMajorAxisLength")), static_cast(coordinate.value("headMinorAxisLength"))), 180 - (coordinate.value("tHead") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); - cv::ellipse(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Size(static_cast(coordinate.value("tailMajorAxisLength")), static_cast(coordinate.value("tailMinorAxisLength"))), 180 - (coordinate.value("tTail") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); - break; - - case 1: // Head - cv::ellipse(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Size(static_cast(coordinate.value("headMajorAxisLength")), static_cast(coordinate.value("headMinorAxisLength"))), 180 - (coordinate.value("tHead") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); - break; - - case 2: // Tail - cv::ellipse(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Size(static_cast(coordinate.value("tailMajorAxisLength")), static_cast(coordinate.value("tailMinorAxisLength"))), 180 - (coordinate.value("tTail") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); - break; - - case 3: // Body - cv::ellipse(frame, Point(static_cast(coordinate.value("xBody")), static_cast(coordinate.value("yBody"))), Size(static_cast(coordinate.value("bodyMajorAxisLength")), static_cast(coordinate.value("bodyMinorAxisLength"))), 180 - (coordinate.value("tBody") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); - break; + if (!trackingData->isEmpty) { + // Takes the tracking data corresponding to the replayed frame and parse data to display + int scale = ui->replaySize->value(); + QList> dataImage = trackingData->getData(frameIndex); + for (const QHash& coordinate : dataImage) { + int id = coordinate.value("id"); + + object1Replay->addItem(QString::number(id)); + + if (ui->ellipseBox->currentIndex() != 4) { + switch (ui->ellipseBox->currentIndex()) { + case 0: // Head + Tail + cv::ellipse(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Size(static_cast(coordinate.value("headMajorAxisLength")), static_cast(coordinate.value("headMinorAxisLength"))), 180 - (coordinate.value("tHead") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); + cv::ellipse(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Size(static_cast(coordinate.value("tailMajorAxisLength")), static_cast(coordinate.value("tailMinorAxisLength"))), 180 - (coordinate.value("tTail") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); + break; + + case 1: // Head + cv::ellipse(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Size(static_cast(coordinate.value("headMajorAxisLength")), static_cast(coordinate.value("headMinorAxisLength"))), 180 - (coordinate.value("tHead") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); + break; + + case 2: // Tail + cv::ellipse(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Size(static_cast(coordinate.value("tailMajorAxisLength")), static_cast(coordinate.value("tailMinorAxisLength"))), 180 - (coordinate.value("tTail") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); + break; + + case 3: // Body + cv::ellipse(frame, Point(static_cast(coordinate.value("xBody")), static_cast(coordinate.value("yBody"))), Size(static_cast(coordinate.value("bodyMajorAxisLength")), static_cast(coordinate.value("bodyMinorAxisLength"))), 180 - (coordinate.value("tBody") * 180) / M_PI, 0, 360, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); + break; + } } - } - if (ui->arrowBox->currentIndex() != 4) { - switch (ui->arrowBox->currentIndex()) { - case 0: - cv::arrowedLine(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Point(static_cast(coordinate.value("xHead") + coordinate.value("headMajorAxisLength") * cos(coordinate.value("tHead"))), static_cast(coordinate.value("yHead") - coordinate.value("headMajorAxisLength") * sin(coordinate.value("tHead")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); - cv::arrowedLine(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Point(static_cast(coordinate.value("xTail") + coordinate.value("tailMajorAxisLength") * cos(coordinate.value("tTail"))), static_cast(coordinate.value("yTail") - coordinate.value("tailMajorAxisLength") * sin(coordinate.value("tTail")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); - break; + if (ui->arrowBox->currentIndex() != 4) { + switch (ui->arrowBox->currentIndex()) { + case 0: + cv::arrowedLine(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Point(static_cast(coordinate.value("xHead") + coordinate.value("headMajorAxisLength") * cos(coordinate.value("tHead"))), static_cast(coordinate.value("yHead") - coordinate.value("headMajorAxisLength") * sin(coordinate.value("tHead")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); + cv::arrowedLine(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Point(static_cast(coordinate.value("xTail") + coordinate.value("tailMajorAxisLength") * cos(coordinate.value("tTail"))), static_cast(coordinate.value("yTail") - coordinate.value("tailMajorAxisLength") * sin(coordinate.value("tTail")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); + break; - case 1: - cv::arrowedLine(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Point(static_cast(coordinate.value("xHead") + coordinate.value("headMajorAxisLength") * cos(coordinate.value("tHead"))), static_cast(coordinate.value("yHead") - coordinate.value("headMajorAxisLength") * sin(coordinate.value("tHead")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); - break; + case 1: + cv::arrowedLine(frame, Point(static_cast(coordinate.value("xHead")), static_cast(coordinate.value("yHead"))), Point(static_cast(coordinate.value("xHead") + coordinate.value("headMajorAxisLength") * cos(coordinate.value("tHead"))), static_cast(coordinate.value("yHead") - coordinate.value("headMajorAxisLength") * sin(coordinate.value("tHead")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); + break; - case 2: - cv::arrowedLine(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Point(static_cast(coordinate.value("xTail") + coordinate.value("tailMajorAxisLength") * cos(coordinate.value("tTail"))), static_cast(coordinate.value("yTail") - coordinate.value("tailMajorAxisLength") * sin(coordinate.value("tTail")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); - break; + case 2: + cv::arrowedLine(frame, Point(static_cast(coordinate.value("xTail")), static_cast(coordinate.value("yTail"))), Point(static_cast(coordinate.value("xTail") + coordinate.value("tailMajorAxisLength") * cos(coordinate.value("tTail"))), static_cast(coordinate.value("yTail") - coordinate.value("tailMajorAxisLength") * sin(coordinate.value("tTail")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); + break; - case 3: - cv::arrowedLine(frame, Point(static_cast(coordinate.value("xBody")), static_cast(coordinate.value("yBody"))), Point(static_cast(coordinate.value("xBody") + coordinate.value("bodyMajorAxisLength") * cos(coordinate.value("tBody"))), static_cast(coordinate.value("yBody") - coordinate.value("bodyMajorAxisLength") * sin(coordinate.value("tBody")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); - break; + case 3: + cv::arrowedLine(frame, Point(static_cast(coordinate.value("xBody")), static_cast(coordinate.value("yBody"))), Point(static_cast(coordinate.value("xBody") + coordinate.value("bodyMajorAxisLength") * cos(coordinate.value("tBody"))), static_cast(coordinate.value("yBody") - coordinate.value("bodyMajorAxisLength") * sin(coordinate.value("tBody")))), Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8, 0, double(scale) / 10); + break; + } } - } - if (ui->replayNumbers->isChecked()) { - cv::putText(frame, to_string(id), Point(static_cast(coordinate.value("xHead") + coordinate.value("headMajorAxisLength") * cos(coordinate.value("tHead"))), static_cast(coordinate.value("yHead") - coordinate.value("headMajorAxisLength") * sin(coordinate.value("tHead")))), cv::FONT_HERSHEY_SIMPLEX, double(scale) * 0.5, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); - } + if (ui->replayNumbers->isChecked()) { + cv::putText(frame, to_string(id), Point(static_cast(coordinate.value("xHead") + coordinate.value("headMajorAxisLength") * cos(coordinate.value("tHead"))), static_cast(coordinate.value("yHead") - coordinate.value("headMajorAxisLength") * sin(coordinate.value("tHead")))), cv::FONT_HERSHEY_SIMPLEX, double(scale) * 0.5, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); + } - if (ui->replayTrace->isChecked()) { - vector memory; - QList> coordinate = trackingData->getData(frameIndex - ui->replayTraceLength->value(), frameIndex + 1, id); - for (auto const& a : coordinate) { - memory.push_back(Point(static_cast(a.value("xBody")), static_cast(a.value("yBody")))); + if (ui->replayTrace->isChecked()) { + vector memory; + QList> coordinate = trackingData->getData(frameIndex - ui->replayTraceLength->value(), frameIndex + 1, id); + for (auto const& a : coordinate) { + memory.push_back(Point(static_cast(a.value("xBody")), static_cast(a.value("yBody")))); + } + cv::polylines(frame, memory, false, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); } - cv::polylines(frame, memory, false, Scalar(colorMap[id].x, colorMap[id].y, colorMap[id].z), scale, cv::LINE_8); } } - } - int w = ui->replayDisplay->width(); - int h = ui->replayDisplay->height(); - QPixmap resizedPix = (QPixmap::fromImage(QImage(frame.u->data, frame.cols, frame.rows, static_cast(frame.step), QImage::Format_RGB888)).scaled(w, h, Qt::KeepAspectRatio)); - ui->replayDisplay->setPixmap(resizedPix); - resizedFrame.setWidth(resizedPix.width()); - resizedFrame.setHeight(resizedPix.height()); + int w = ui->replayDisplay->width(); + int h = ui->replayDisplay->height(); + QPixmap resizedPix = (QPixmap::fromImage(QImage(frame.u->data, frame.cols, frame.rows, static_cast(frame.step), QImage::Format_RGB888)).scaled(w, h, Qt::KeepAspectRatio)); + ui->replayDisplay->setPixmap(resizedPix); + resizedFrame.setWidth(resizedPix.width()); + resizedFrame.setHeight(resizedPix.height()); + } + catch (const std::exception& e) { + qWarning() << QString::fromStdString(e.what()) << " occurs at image " << frameIndex << " display"; + } + catch (...) { + qWarning() << "Unknown error occurs at image " << frameIndex << " display"; + } } /** diff --git a/src/tracking.cpp b/src/tracking.cpp index 938b4eb..a10bbf2 100644 --- a/src/tracking.cpp +++ b/src/tracking.cpp @@ -770,12 +770,24 @@ void Tracking::imageProcessing() { m_im++; emit(progress(m_im)); } + catch (const std::exception &e) { + outputDb.commit(); + exportTrackingResult(m_savingPath, outputDb); + delete timer; + outputDb.close(); + m_logFile.close(); + qWarning() << QString::fromStdString(e.what()) << " at image " << QString::number(m_im); + emit(forceFinished(QString::fromStdString(e.what()) + " error during the processing of the image " + QString::number(m_im))); + return; + } catch (...) { outputDb.commit(); exportTrackingResult(m_savingPath, outputDb); + delete timer; outputDb.close(); m_logFile.close(); - emit(forceFinished("Fatal error during the processing of the image " + QString::number(m_im))); + qWarning() << "Unknown at image " << QString::number(m_im); + emit(forceFinished("Fatal and unknown error during the processing of the image " + QString::number(m_im))); return; } } @@ -790,9 +802,11 @@ void Tracking::imageProcessing() { emit(finished()); } else if (isSaved && !m_error.isEmpty()) { + qWarning() << "Images" << m_error << " where skipped because unreadable"; emit(forceFinished("Images " + m_error + " where skipped because unreadable.")); } else { + qWarning() << "Can't write tracking.txt to the disk"; emit(forceFinished("Can't write tracking.txt to the disk!")); } } @@ -960,9 +974,11 @@ void Tracking::startProcess() { emit(finishedProcessFrame()); } catch (const std::runtime_error &e) { + qWarning() << QString::fromStdString(e.what()); emit(forceFinished(QString::fromStdString(e.what()))); } catch (...) { + qWarning() << "Fatal error, tracking initialization failed"; emit(forceFinished("Fatal error, tracking initialization failed")); } }