diff --git a/CMakeLists.txt b/CMakeLists.txt index f34148e..0a6bc6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,8 +45,8 @@ set(OpenCV_STATIC OFF) find_package(OpenCV REQUIRED) find_package(OpenGL REQUIRED) -find_package(Qt5Widgets REQUIRED) -find_package(Qt5OpenGL REQUIRED) +find_package(Qt5Widgets 5.4 REQUIRED) +find_package(Qt5OpenGL 5.4 REQUIRED) find_package(Threads REQUIRED) set(Boost_USE_STATIC_LIBS OFF) @@ -57,6 +57,11 @@ include_directories( SYSTEM ${Qt5Widgets_INCLUDE_DIRS} SYSTEM ${OpenCV_INCLUDE_DIRS} SYSTEM ${Qt5OpenGL_INCLUDE_DIRS} + SYSTEM ${Boost_INCLUDE_DIRS} +) + +link_directories( + ${Boost_LIBRARY_DIRS} ) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -65,3 +70,17 @@ add_definitions(${Qt5Widgets_DEFINITIONS}) add_definitions(-DQT_NO_KEYWORDS) add_subdirectory(biotracker) + +# Make all targets (core and GUI) build to a common output directory on windows to work around copying DLLs. +# Note that this must come before including the other projects. +if(MSVC) + set(common_output_directory ${CMAKE_BINARY_DIR}/bin) + foreach(SUBTARGET BioTracker ${CPM_LIBRARIES}) + foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) + set_target_properties(${SUBTARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${common_output_directory}) + set_target_properties(${SUBTARGET} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${common_output_directory}) + set_target_properties(${SUBTARGET} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${common_output_directory}) + endforeach() + endforeach() +endif() diff --git a/README.md b/README.md index 89f8493..6baecea 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains the GUI frontend for the [BioTracker](https://github.co ## Dependencies ### Unix -You can find an up-to-date list of [dependencies](https://github.com/BioroboticsLab/DockerFiles/blob/master/biotracker/dependencies.sh) +You can find an up-to-date list of [dependencies](https://github.com/BioroboticsLab/DockerFiles/blob/master/ubuntu-15.10-biotracker/dependencies.sh) needed to build the BioTracker in our docker repository. ### OSX diff --git a/biotracker/AlgorithmSelectionWidget.h b/biotracker/AlgorithmSelectionWidget.h index da266ee..90e6bf3 100644 --- a/biotracker/AlgorithmSelectionWidget.h +++ b/biotracker/AlgorithmSelectionWidget.h @@ -6,8 +6,6 @@ #include "ui_AlgorithmSelectionWidget.h" #include "biotracker/util/QtRaiiWrapper.hpp" -class Facade; - namespace BioTracker { namespace Gui { @@ -34,7 +32,8 @@ class AlgorithmSelectionWidget : public QWidget { void initConnects(); private Q_SLOTS: - void trackingAlgorithmSelected(const QString& name); + void trackingAlgorithmSelected(const QString &name); + void enableDisableTracking(bool state); }; } diff --git a/biotracker/CMakeLists.txt b/biotracker/CMakeLists.txt index c5e0f6d..7ce3bda 100644 --- a/biotracker/CMakeLists.txt +++ b/biotracker/CMakeLists.txt @@ -11,6 +11,13 @@ qt5_wrap_ui(UI_HEADERS ${ui}) add_executable(BioTracker ${src} ${hdr} ${UI_RESOURCES} ${UI_HEADERS} ) +# Make this target the default target for the solution. +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT "BioTracker") +# Give the debug executable a different name on windows as they are build +# into the same directory. +if(MSVC) + set_target_properties(BioTracker PROPERTIES OUTPUT_NAME_DEBUG BioTracker-Debug) +endif() include_directories( SYSTEM ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(BioTracker Qt5::Widgets diff --git a/biotracker/Gui.h b/biotracker/Gui.h index cca6455..0db7c7e 100644 --- a/biotracker/Gui.h +++ b/biotracker/Gui.h @@ -3,7 +3,6 @@ #include #include "biotracker/BioTrackerApp.h" -#include "biotracker/ZmqTrackingAlgorithm.h" #include "MainWindow.h" @@ -21,6 +20,10 @@ class Gui : public QObject { void initConnects(); QStringList browse(const QString &title, const QString &filter); + boost::optional> getOpenFiles(); + boost::optional getFileHash(std::string const& filename, const size_t numFiles) const; + std::vector getFilenamesFromPaths(std::vector const& paths) const; + private Q_SLOTS: void browseVideo(); void browsePictures(); diff --git a/biotracker/MainWindow.h b/biotracker/MainWindow.h index e905573..f94577e 100644 --- a/biotracker/MainWindow.h +++ b/biotracker/MainWindow.h @@ -9,6 +9,7 @@ #include "VideoControlWidget.h" #include "OpenCameraDialog.h" #include "VideoView.h" + #include "biotracker/util/QtRaiiWrapper.hpp" #include "biotracker/util/stringTools.h" @@ -48,14 +49,13 @@ class MainWindow : public QMainWindow { * handles the showing of filenames in statusbar */ void frameCalculated(const size_t frameNumber, - const std::string filename, const double currentFps); + const std::string filename, const double currentFps); /** * @brief trackerSelected * gets called whenever a new tracker is selected - * @param tracker */ - void trackerSelected(std::shared_ptr tracker); + void trackerSelected(std::shared_ptr tracker); private: MainWindowUi m_ui; @@ -66,14 +66,9 @@ class MainWindow : public QMainWindow { OpenCameraDialog m_openCameraDialog; QVBoxLayout m_tools; - /** - * @brief lastToolsWidget - * We need to store a reference to the last used widget so we can - * remove it upon selecting a new algorithm - */ - std::shared_ptr m_lastToolsWidget; - void initalizeVideoView(Core::BioTrackerApp &biotracker); + + void closeEvent(QCloseEvent *event) override; }; diff --git a/biotracker/NotificationWidget.h b/biotracker/NotificationWidget.h index f2d8833..755e6c6 100644 --- a/biotracker/NotificationWidget.h +++ b/biotracker/NotificationWidget.h @@ -25,7 +25,7 @@ class NotificationWidget : public QWidget { * @brief notify * Status messages for the user interface */ - void notify(const std::string &message, const MSGS::MTYPE type); + void notify(const std::string &message, const Core::MessageType type); diff --git a/biotracker/VideoControlWidget.h b/biotracker/VideoControlWidget.h index 29603e1..871634b 100644 --- a/biotracker/VideoControlWidget.h +++ b/biotracker/VideoControlWidget.h @@ -62,7 +62,7 @@ class VideoControlWidget : public QWidget { void speedSliderValueChanged(int value); void viewChanged(QString); void onRequestRepaint(); - void registerViews(const std::vector view); + void registerViews(const std::vector view); }; } diff --git a/biotracker/VideoView.h b/biotracker/VideoView.h index 918c211..16d6dba 100644 --- a/biotracker/VideoView.h +++ b/biotracker/VideoView.h @@ -36,16 +36,16 @@ class VideoView : public QOpenGLWidget, protected QOpenGLFunctions { bool isZoomed(); - void setView(TrackingAlgorithm::View v) { + void setView(Core::TrackingAlgorithm::View v) { m_view = v; update(); } -public Q_SLOTS: + public Q_SLOTS: void setMode(const Mode mode); void fitToWindow(); -private Q_SLOTS: + private Q_SLOTS: void handleLoggedMessage(const QOpenGLDebugMessage &debugMessage); private: @@ -70,7 +70,7 @@ private Q_SLOTS: Core::BioTrackerApp &m_biotracker; - TrackingAlgorithm::View m_view; + Core::TrackingAlgorithm::View m_view; /** * @brief m_painter @@ -78,7 +78,7 @@ private Q_SLOTS: QPainter m_painter; bool m_firstAttempt; - void paintGL(); + void paintGL() override; /** * @brief unprojectScreenPos diff --git a/biotracker/src/AlgorithmSelectionWidget.cpp b/biotracker/src/AlgorithmSelectionWidget.cpp index ea38c70..b6461fa 100644 --- a/biotracker/src/AlgorithmSelectionWidget.cpp +++ b/biotracker/src/AlgorithmSelectionWidget.cpp @@ -20,10 +20,12 @@ AlgorithmSelectionWidget::AlgorithmSelectionWidget(QWidget *parent, QComboBox::InsertPolicy::InsertAlphabetically); } -void AlgorithmSelectionWidget::addTrackingAlgorithm(const Core::TrackerType - type) { - m_ui.cb_algorithms->addItem(QString::fromStdString( - m_biotracker.getRegistry().getStringByType().at(type))); +void AlgorithmSelectionWidget::addTrackingAlgorithm(const Core::TrackerType type) { + const QString trackerName = QString::fromStdString( + m_biotracker.getRegistry().getStringByType().at(type)); + m_ui.cb_algorithms->addItem(trackerName); + const int index = m_ui.cb_algorithms->findText(trackerName); + m_ui.cb_algorithms->setCurrentIndex(index); } void AlgorithmSelectionWidget::initAlgorithmList() { @@ -44,10 +46,36 @@ void AlgorithmSelectionWidget::initConnects() { this, &AlgorithmSelectionWidget::addTrackingAlgorithm); QObject::connect(m_ui.cb_algorithms, static_cast(&QComboBox::currentIndexChanged), this, &AlgorithmSelectionWidget::trackingAlgorithmSelected); + QObject::connect(m_ui.chk_enableTracking, &QCheckBox::toggled, + this, &AlgorithmSelectionWidget::enableDisableTracking); + + const QString shortcutTrackingKey = QString::fromStdString( + m_biotracker.getSettings().getValueOrDefault + (GuiParam::SHORTCUT_TRACKING, "T")); + auto *shortcutTracking = new QShortcut(QKeySequence(shortcutTrackingKey), this); + QObject::connect(shortcutTracking, &QShortcut::activated, m_ui.chk_enableTracking, + &QCheckBox::click); } void AlgorithmSelectionWidget::trackingAlgorithmSelected(const QString &name) { m_biotracker.setTrackingAlgorithm(name.toStdString()); + + // check if we have "any" tracking or not + auto noTrackingStr = QString::fromStdString(m_biotracker.getRegistry().getStringByType().at(Core::NoTracking)); + if (name.compare(noTrackingStr) == 0) { + m_ui.chk_enableTracking->setChecked(true); + m_ui.chk_enableTracking->setEnabled(false); + } else { + m_ui.chk_enableTracking->setEnabled(true); + } +} + +void AlgorithmSelectionWidget::enableDisableTracking(bool checked) { + if (checked) { + m_biotracker.enableTracking(); + } else { + m_biotracker.disableTracking(); + } } } diff --git a/biotracker/src/AlgorithmSelectionWidget.ui b/biotracker/src/AlgorithmSelectionWidget.ui index 8ef92d0..23a6319 100644 --- a/biotracker/src/AlgorithmSelectionWidget.ui +++ b/biotracker/src/AlgorithmSelectionWidget.ui @@ -20,6 +20,21 @@ Form + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -69,6 +84,19 @@ + + + + false + + + Tracking enabled + + + true + + + diff --git a/biotracker/src/Gui.cpp b/biotracker/src/Gui.cpp index 0035892..9665445 100644 --- a/biotracker/src/Gui.cpp +++ b/biotracker/src/Gui.cpp @@ -12,6 +12,12 @@ #include #include "opencv2/highgui/highgui.hpp" +#include "biotracker/serialization/SerializationData.h" + +#include +#include +#include + namespace BioTracker { namespace Gui { @@ -20,11 +26,12 @@ Gui::Gui() , m_biotracker() , m_mainWindow(m_biotracker) { initConnects(); + m_mainWindow.show(); } void Gui::initConnects() { - //File -> Open Video + // File -> Open Video QObject::connect(m_mainWindow.getUi().actionOpen_Video, &QAction::triggered, this, &Gui::browseVideo); QObject::connect(m_mainWindow.getUi().actionOpen_Picture, &QAction::triggered, @@ -49,7 +56,7 @@ void Gui::initConnects() { &m_mainWindow, &MainWindow::frameCalculated); QObject::connect(&m_biotracker, &Core::BioTrackerApp::trackerSelected, - &m_mainWindow, &MainWindow::trackerSelected); + &m_mainWindow, &MainWindow::trackerSelected); QObject::connect(&m_biotracker, &Core::BioTrackerApp::notify, &m_mainWindow.getNotification(), &NotificationWidget::notify); @@ -59,7 +66,7 @@ void Gui::initConnects() { } void Gui::browseVideo() { - static const QString videoFilter("Video files (*.avi *.wmv *.mp4 *.mkv)"); + static const QString videoFilter("Video files (*.avi *.wmv *.mp4 *.mkv *.mov)"); std::vector files; const QString filename = QFileDialog::getOpenFileName(&m_mainWindow, @@ -93,16 +100,28 @@ void Gui::browseCameras() { QListWidget *cameraListWidget = cameraDialog.getUi().cameraList; cameraListWidget->clear(); + BioTracker::Core::Settings settings = m_biotracker.getSettings(); + BioTracker::Core::TrackerStatus trackerStatus = m_biotracker.getTrackingThread().getStatus(); // Add Item for each camera cv::VideoCapture vcap; - for (uint8_t i = 0; i <= 254; i++) { - vcap = cv::VideoCapture(i); - if (!vcap.isOpened()) { - break; + boost::optional mediaTypeOpt = settings.maybeGetValueOfParam(GuiParam::MEDIA_TYPE); + GuiParam::MediaType mediaType = mediaTypeOpt ? static_cast(*mediaTypeOpt) : GuiParam::MediaType::NoMedia; + boost::optional camIdOpt = settings.maybeGetValueOfParam(CaptureParam::CAP_CAMERA_ID); + int camId = camIdOpt ? *camIdOpt : -1; + // OpenCV does not have an API to list camera devices https://github.com/Itseez/opencv/issues/4269 + for (int i = 0; i <= 254; i++) { + QListWidgetItem* item = new QListWidgetItem(QString("Camera ") + QString::number(static_cast(i))); + if (mediaType == GuiParam::MediaType::Camera && camId == i && trackerStatus != BioTracker::Core::TrackerStatus::NothingLoaded) { + item->setText(item->text() + QString(" (in use)")); + item->setFlags(Qt::NoItemFlags); + } else { + vcap = cv::VideoCapture(i); + if (!vcap.isOpened()) { + break; + } + vcap.release(); } - vcap.release(); - cameraListWidget->addItem(QString("Camera ") + QString::number(static_cast - (i))); + cameraListWidget->addItem(item); } if (cameraDialog.exec() == QDialog::Accepted) { @@ -128,11 +147,161 @@ void Gui::loadTracker() { } void Gui::loadTrackingData() { + static const QString trackingdataFilter("Trackingdata Files (*.tdat)"); + const QString path = QFileDialog::getOpenFileName(&m_mainWindow, "Load trackingdata", "", trackingdataFilter); + if (!path.isEmpty()) { + std::ifstream is(path.toStdString()); + cereal::JSONInputArchive ar(is); + + Core::Serialization::Data sdata; + ar(sdata); + + if (m_biotracker.getTrackingThread().getTrackerType()){ + assert(m_biotracker.getTrackingThread().getTrackerType()); + const std::string trackerType = + Core::Registry::getInstance().getStringByType().at(m_biotracker.getTrackingThread().getTrackerType().get()); + + if (sdata.getType() != trackerType) { + QMessageBox::warning(&m_mainWindow, "Unable to load tracking data", + "Tracker type does not match."); + return; + } + } else { + // try to automatically select the required tracking algorithm + const auto& typeByString = Core::Registry::getInstance().getTypeByString(); + const auto it = typeByString.find(sdata.getType()); + if (it != typeByString.end()) { + m_biotracker.getTrackingThread().setTrackingAlgorithm( + Core::Registry::getInstance().makeNewTracker( + Core::Registry::getInstance().getTypeByString().at(sdata.getType()), m_biotracker.getSettings() + ) + ); + } else { + QMessageBox::warning(&m_mainWindow, "Unable to load tracking data", + "Unknown tracker type."); + return; + } + } + + const boost::optional> currentFiles = getOpenFiles(); + + if (!currentFiles) { + QMessageBox::warning(&m_mainWindow, "Unable to load tracking data", + "No file opened."); + return; + } + + const boost::optional hash = getFileHash(currentFiles.get().front(), currentFiles.get().size()); + + if (!hash) { + QMessageBox::warning(&m_mainWindow, "Unable to load tracking data", + "Could not calculate file hash."); + return; + } + + if (sdata.getFileSha1Hash() != hash.get()) { + auto buttonClicked = QMessageBox::warning(&m_mainWindow, "Unable to load tracking data", + "File hash does not match", QMessageBox::Ok | QMessageBox::Ignore); + if (buttonClicked == QMessageBox::Ok) + return; + } + + m_biotracker.getTrackingThread().loadTrackedObjects(sdata.getTrackedObjects()); + } } void Gui::storeTrackingData() { + if (!m_biotracker.getTrackingThread().getTrackerType()) { + QMessageBox::warning(&m_mainWindow, "Unable to store tracking data", + "No tracker selected."); + return; + } + QFileDialog dialog(&m_mainWindow, "Save tracking data"); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setDefaultSuffix("tdat"); + dialog.setNameFilter("Data Files (*.tdat)"); + if (dialog.exec()) { + const QStringList filenames = dialog.selectedFiles(); + if (!filenames.empty()) { + assert(m_biotracker.getTrackingThread().getTrackerType()); + + const std::string trackerType = + Core::Registry::getInstance().getStringByType().at(m_biotracker.getTrackingThread().getTrackerType().get()); + const boost::optional> currentFiles = getOpenFiles(); + + if (!currentFiles) { + QMessageBox::warning(&m_mainWindow, "Unable to store tracking data", + "No file opened."); + return; + } + + const boost::optional hash = getFileHash(currentFiles.get().front(), currentFiles.get().size()); + + if (!hash) { + QMessageBox::warning(&m_mainWindow, "Unable to store tracking data", + "Could not calculate file hash."); + return; + } + + Core::Serialization::Data sdata(trackerType, hash.get(), getFilenamesFromPaths(currentFiles.get()), + m_biotracker.getTrackingThread().getTrackedObjects().get()); + + std::ofstream ostream(filenames.first().toStdString(), std::ios::binary); + cereal::JSONOutputArchive archive(ostream); + archive(std::move(sdata)); + } + } +} + +boost::optional> Gui::getOpenFiles() { + Core::Settings settings = m_biotracker.getSettings(); + boost::optional mediaTypeOpt = settings.maybeGetValueOfParam(GuiParam::MEDIA_TYPE); + GuiParam::MediaType mediaType = mediaTypeOpt ? static_cast(*mediaTypeOpt) : GuiParam::MediaType::NoMedia; + + boost::optional> filename; + switch (mediaType) { + case GuiParam::MediaType::Images: + filename = settings.getValueOfParam>(PictureParam::PICTURE_FILES); + break; + case GuiParam::MediaType::Video: + filename = std::vector(); + filename.get().push_back(settings.getValueOfParam(CaptureParam::CAP_VIDEO_FILE)); + break; + default: + return boost::optional>(); + } + + // cap_video_file and picture_file can be set, but empty. therefore, we + // need to check if the parameter actually contains a file name. + if (filename && !filename.get().empty()) return filename; + + return boost::optional>(); +} + +boost::optional Gui::getFileHash(const std::string &filename, const size_t numFiles) const { + QCryptographicHash sha1Generator(QCryptographicHash::Sha1); + QFile file(QString::fromStdString(filename)); + if (file.open(QIODevice::ReadOnly)) { + // calculate hash from first 4096 bytes of file + sha1Generator.addData(file.peek(4096)); + sha1Generator.addData(QByteArray::number(static_cast(numFiles))); + return QString(sha1Generator.result().toHex()).toStdString(); + } + + return boost::optional(); +} + +std::vector Gui::getFilenamesFromPaths(const std::vector &paths) const { + std::vector filenames; + filenames.reserve(paths.size()); + for (std::string const& path : paths) { + const QFileInfo fi(QString::fromStdString(path)); + filenames.push_back(fi.baseName().toStdString()); + } + return filenames; } void Gui::exit() { diff --git a/biotracker/src/MainWindow.cpp b/biotracker/src/MainWindow.cpp index e276a7a..5e88b75 100644 --- a/biotracker/src/MainWindow.cpp +++ b/biotracker/src/MainWindow.cpp @@ -8,8 +8,22 @@ MainWindow::MainWindow(Core::BioTrackerApp &biotracker) , m_algorithmSelection(m_ui.widget_alg, biotracker) , m_notification(m_ui.dockWidgetNotificationContents, biotracker) , m_openCameraDialog(m_ui.centralWidget, biotracker) - , m_tools(m_ui.groupBox_tools){ + , m_tools(m_ui.groupBoxContents) { initalizeVideoView(biotracker); + + { + QFile file(QString::fromStdString(ConfigParam::GEOMETRY_FILE.string())); + if (file.open(QIODevice::ReadOnly)) { + restoreGeometry(file.readAll()); + } + } + + { + QFile file(QString::fromStdString(ConfigParam::STATE_FILE.string())); + if (file.open(QIODevice::ReadOnly)) { + restoreState(file.readAll()); + } + } } void MainWindow::initalizeVideoView(Core::BioTrackerApp &biotracker) { @@ -19,17 +33,38 @@ void MainWindow::initalizeVideoView(Core::BioTrackerApp &biotracker) { biotracker, m_videoView.get()); } -void MainWindow::frameCalculated(const size_t frameNumber, - const std::string filename, const double currentFps) { - setWindowTitle("BioTracker [" + QString::fromStdString(stem_filename(filename)) + "]"); -} +void MainWindow::closeEvent(QCloseEvent *event) { + const auto dialog = QMessageBox::warning( + this, "Exit", "Do you really want to close the application?", + QMessageBox::Yes | QMessageBox::No); + + if (dialog == QMessageBox::Yes) { + { + QFile file(QString::fromStdString(ConfigParam::GEOMETRY_FILE.string())); + if (file.open(QIODevice::WriteOnly)) { + file.write(saveGeometry()); + } + } -void MainWindow::trackerSelected(std::shared_ptr tracker) { - if (m_lastToolsWidget) { - m_tools.removeWidget(m_lastToolsWidget.get()); + { + QFile file(QString::fromStdString(ConfigParam::STATE_FILE.string())); + if (file.open(QIODevice::WriteOnly)) { + file.write(saveState()); + } + } + + QMainWindow::closeEvent(event); + } else { + event->ignore(); } - m_lastToolsWidget = tracker.get()->getToolsWidget(); - m_tools.addWidget(m_lastToolsWidget.get()); +} + +void MainWindow::frameCalculated(const size_t, const std::string filename, const double) { + setWindowTitle("BioTracker [" + QString::fromStdString(Util::stem_filename(filename)) + "]"); +} + +void MainWindow::trackerSelected(std::shared_ptr tracker) { + m_tools.addWidget(tracker->getToolsWidget()); m_ui.groupBox_tools->repaint(); } diff --git a/biotracker/src/MainWindow.ui b/biotracker/src/MainWindow.ui index 8b760c2..f135193 100644 --- a/biotracker/src/MainWindow.ui +++ b/biotracker/src/MainWindow.ui @@ -85,7 +85,7 @@ 3 - + 0 @@ -98,16 +98,22 @@ 150 - - Tracking Area - - - false - - - false - + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -144,7 +150,7 @@ 0 0 982 - 20 + 27 @@ -174,7 +180,7 @@ 200 - 172 + 207 @@ -204,19 +210,19 @@ - 3 + 2 - 8 + 3 - 8 + 3 - 8 + 3 - 8 + 3 @@ -231,7 +237,7 @@ - + 0 0 @@ -239,6 +245,67 @@ Tools + + false + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 0 + + + + QFrame::Plain + + + 0 + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 176 + 525 + + + + + 0 + 0 + + + + + + @@ -290,7 +357,7 @@ - Load Tracker... + L&oad Tracker... diff --git a/biotracker/src/NotificationWidget.cpp b/biotracker/src/NotificationWidget.cpp index 6d9d0cf..f289557 100644 --- a/biotracker/src/NotificationWidget.cpp +++ b/biotracker/src/NotificationWidget.cpp @@ -16,17 +16,23 @@ void NotificationWidget::initConnects() { } void NotificationWidget::notify(const std::string &message, - const MSGS::MTYPE type) { + const Core::MessageType type) { + std::stringstream ss; switch (type) { - case MSGS::FILE_OPEN: - m_ui.edit_notification->append(QString(message.c_str())); + case Core::MessageType::FILE_OPEN: + ss << "" << message << ""; break; - case MSGS::NOTIFICATION: - m_ui.edit_notification->append(QString(message.c_str())); + case Core::MessageType::NOTIFICATION: + ss << "" << message << ""; + break; + case Core::MessageType::FAIL: + ss << "" << message << ""; break; default: - ; + assert(false); } + ss << "
"; + m_ui.edit_notification->insertHtml(QString(ss.str().c_str())); } diff --git a/biotracker/src/VideoControlWidget.cpp b/biotracker/src/VideoControlWidget.cpp index 24c10de..821ca15 100644 --- a/biotracker/src/VideoControlWidget.cpp +++ b/biotracker/src/VideoControlWidget.cpp @@ -17,8 +17,7 @@ VideoControlWidget::VideoControlWidget(QWidget *parent, , m_ui(parent) , m_bioTracker(facade) , m_videoView(videoView) - , m_isPanZoomMode(false) -{ + , m_isPanZoomMode(false) { m_iconPause.addFile(QStringLiteral(":/BioTracker/resources/pause-sign.png"), QSize(), QIcon::Normal, QIcon::Off); m_iconPlay.addFile(QStringLiteral(":/BioTracker/resources/arrow-forward1.png"), @@ -68,7 +67,7 @@ void VideoControlWidget::updateWidgets() { void VideoControlWidget::initShortcuts() { const QString shortcutPanKey = QString::fromStdString( m_bioTracker.getSettings().getValueOrDefault - (GUIPARAM::SHORTCUT_ZOOM,"Z")); + (GuiParam::SHORTCUT_ZOOM,"Z")); const QShortcut *shortcutPan = new QShortcut(QKeySequence(shortcutPanKey), this); QObject::connect(shortcutPan, &QShortcut::activated, m_ui.button_panZoom, @@ -76,7 +75,7 @@ void VideoControlWidget::initShortcuts() { const QString shortcutPlayKey = QString::fromStdString( m_bioTracker.getSettings().getValueOrDefault - (GUIPARAM::SHORTCUT_PLAY,"Space")); + (GuiParam::SHORTCUT_PLAY,"Space")); const QShortcut *shortcutPlay = new QShortcut(QKeySequence(shortcutPlayKey), this); QObject::connect(shortcutPlay, &QShortcut::activated, m_ui.button_playPause, @@ -84,7 +83,7 @@ void VideoControlWidget::initShortcuts() { const QString shortcutNextKey = QString::fromStdString( m_bioTracker.getSettings().getValueOrDefault - (GUIPARAM::SHORTCUT_NEXT,"Right")); + (GuiParam::SHORTCUT_NEXT,"Right")); const QShortcut *shortcutNext = new QShortcut(QKeySequence(shortcutNextKey), this); QObject::connect(shortcutNext, &QShortcut::activated, m_ui.button_nextFrame, @@ -92,7 +91,7 @@ void VideoControlWidget::initShortcuts() { const QString shortcutPrevKey = QString::fromStdString( m_bioTracker.getSettings().getValueOrDefault - (GUIPARAM::SHORTCUT_PREV,"Left")); + (GuiParam::SHORTCUT_PREV,"Left")); const QShortcut *shortcutPrev = new QShortcut(QKeySequence(shortcutPrevKey), this); QObject::connect(shortcutPrev, &QShortcut::activated, m_ui.button_previousFrame, @@ -125,8 +124,9 @@ void VideoControlWidget::initConnects() { QObject::connect(m_ui.sld_speed, &QSlider::valueChanged, this, &VideoControlWidget::speedSliderValueChanged); - QObject::connect(m_ui.comboBoxSelectedView, static_cast(&QComboBox::currentIndexChanged), - this, &VideoControlWidget::viewChanged); + QObject::connect(m_ui.comboBoxSelectedView, + static_cast(&QComboBox::currentIndexChanged), + this, &VideoControlWidget::viewChanged); QObject::connect(&m_bioTracker, &Core::BioTrackerApp::registerViews, this, &VideoControlWidget::registerViews); @@ -174,7 +174,8 @@ void VideoControlWidget::fileOpened(const std::string filename, const double targetFps) { (void)filename; // "un-use" filename. FileOpen is a generic event, but we dont // need the filename at this place - m_ui.sld_video->setMaximum(totalFrames - 1); + assert(totalFrames > 0); + m_ui.sld_video->setMaximum(static_cast(totalFrames - 1)); m_ui.fps_label->setText(QString::number(targetFps)); const int fpsAsInt = static_cast(targetFps + 0.5); m_ui.sld_speed->setValue(fpsAsInt); @@ -222,7 +223,7 @@ void VideoControlWidget::speedSliderValueChanged(int speed) { } void VideoControlWidget::viewChanged(QString n) { - auto view = TrackingAlgorithm::OriginalView; + auto view = Core::TrackingAlgorithm::OriginalView; if (n != "Original") { view.name = n.toUtf8().constData(); } @@ -233,12 +234,11 @@ void repaintVideoView(VideoView *videoView) { videoView->update(); } -void VideoControlWidget::onRequestRepaint() -{ +void VideoControlWidget::onRequestRepaint() { repaintVideoView(m_videoView); } -void VideoControlWidget::registerViews(const std::vector views) { +void VideoControlWidget::registerViews(const std::vector views) { m_ui.comboBoxSelectedView->clear(); m_ui.comboBoxSelectedView->addItem("Original"); for (auto view : views) { @@ -251,17 +251,19 @@ void VideoControlWidget::changeCurrentFrameByEdit() { } void VideoControlWidget::takeScreenshot() { - /* - QString filename = ""; - const std::chrono::system_clock::time_point p = std::chrono::system_clock::now(); - const std::time_t t = std::chrono::system_clock::to_time_t(p); - std::string dateTime = std::ctime(&t); - //ctime adds a newline to the string due to historical reasons - dateTime = dateTime.substr(0, dateTime.size() - 1); - filename.append("/screenshot_").append(QString::fromStdString(dateTime)).append(".png"); - QString filepath = QFileDialog::getSaveFileName(this, tr("Save File"), "/home/"+filename , tr("Images (*.png)")); - ui.videoView->takeScreenshot(filepath); - */ + QString filename; + char buffer[80]; + time_t rawtime; + std::time(&rawtime); + struct tm *timeinfo = localtime(&rawtime); + strftime(buffer, 80, "%d-%m-%Y_%I-%M-%S", timeinfo); + filename.append("/Screenshot_").append(buffer).append(".png"); + QString filepath = QFileDialog::getSaveFileName(this, tr("Save File"), + QDir::homePath().append(filename), + tr("Images (*.png)")); + if (filepath.count()) { + m_bioTracker.getTrackingThread().getTexture().get().save(filepath); + } } void VideoControlWidget::switchPanZoomMode() { @@ -279,13 +281,13 @@ void VideoControlWidget::switchPanZoomMode() { } void VideoControlWidget::frameCalculated(const size_t frameNumber, - const std::string filename, const double currentFps) { + const std::string, const double currentFps) { const bool hasNext = frameNumber < m_bioTracker.getNumFrames() - 1; repaintVideoView(m_videoView); - m_ui.sld_video->setValue(frameNumber); + m_ui.sld_video->setValue(static_cast(frameNumber)); m_ui.frame_num_edit->setText(QString::number(frameNumber)); if (currentFps >= 0) { diff --git a/biotracker/src/VideoControlWidget.ui b/biotracker/src/VideoControlWidget.ui index f3b0d80..5cc564c 100644 --- a/biotracker/src/VideoControlWidget.ui +++ b/biotracker/src/VideoControlWidget.ui @@ -6,7 +6,7 @@ 0 0 - 719 + 721 193 @@ -68,6 +68,9 @@ Video Controls + + false + 3 @@ -99,7 +102,7 @@ - + :/BioTracker/resources/arrows-skip-back.png:/BioTracker/resources/arrows-skip-back.png @@ -125,7 +128,7 @@ - + :/BioTracker/resources/arrow-forward1.png:/BioTracker/resources/arrow-forward1.png @@ -154,7 +157,7 @@ - + :/BioTracker/resources/stop.png:/BioTracker/resources/stop.png @@ -183,7 +186,7 @@ - + :/BioTracker/resources/arrows-skip-forward.png:/BioTracker/resources/arrows-skip-forward.png @@ -197,7 +200,7 @@ - false + true @@ -217,7 +220,7 @@ - + :/BioTracker/resources/screenshot.png:/BioTracker/resources/screenshot.png @@ -340,7 +343,7 @@ - + :/BioTracker/resources/panZoom.png:/BioTracker/resources/panZoom.png @@ -502,7 +505,7 @@
- + diff --git a/biotracker/src/VideoView.cpp b/biotracker/src/VideoView.cpp index 4642688..c1ba162 100644 --- a/biotracker/src/VideoView.cpp +++ b/biotracker/src/VideoView.cpp @@ -20,10 +20,10 @@ VideoView::VideoView(QWidget *parent, Core::BioTrackerApp &biotracker) , m_currentMode(Mode::INTERACTION) , m_screenPicRatio(0) , m_biotracker(biotracker) - , m_view(TrackingAlgorithm::OriginalView) + , m_view(Core::TrackingAlgorithm::OriginalView) , m_painter() , m_firstAttempt(true) { - + setFocusPolicy(Qt::FocusPolicy::ClickFocus); } void VideoView::setMode(const VideoView::Mode mode) { @@ -52,10 +52,9 @@ void VideoView::handleLoggedMessage(const QOpenGLDebugMessage &debugMessage) { std::cout << debugMessage.message().toStdString() << std::endl; } -void VideoView::paintGL() -{ - BioTracker::Core::TextureObject const& texture = - m_biotracker.getTrackingThread().getTexture(); +void VideoView::paintGL() { + BioTracker::Core::TextureObject const &texture = + m_biotracker.getTrackingThread().getTexture(); const size_t width = this->width(); const size_t height = this->height(); @@ -78,16 +77,16 @@ void VideoView::paintGL() } QPoint VideoView::unprojectScreenPos(QPoint mouseCoords) { - BioTracker::Core::TextureObject const& texture = - m_biotracker.getTrackingThread().getTexture(); + BioTracker::Core::TextureObject const &texture = + m_biotracker.getTrackingThread().getTexture(); // variables required to map window coordinates to picture coordinates return BioTracker::Core::ScreenHelper::screenToImageCoords( - m_panZoomState, - texture.width(), texture.height(), - width(), height(), - mouseCoords - ); + m_panZoomState, + texture.width(), texture.height(), + width(), height(), + mouseCoords + ); } void VideoView::keyPressEvent(QKeyEvent *e) { @@ -99,117 +98,118 @@ void VideoView::keyPressEvent(QKeyEvent *e) { void VideoView::mouseMoveEvent(QMouseEvent *e) { switch (m_currentMode) { - case Mode::PANZOOM: { - if (m_panZoomState.panState) { - const QPointF delta = e->localPos() - (*m_panZoomState.panState).lastPos; - (*m_panZoomState.panState).lastPos = e->localPos(); - m_panZoomState.isChanged = true; - m_panZoomState.panX -= static_cast(delta.x()); - m_panZoomState.panY -= static_cast(delta.y()); - update(); - } - break; - } - case Mode::INTERACTION: { - e->accept(); - QPoint p = unprojectScreenPos(e->pos()); - const QPointF localPos(p); - QMouseEvent modifiedEvent(e->type(),localPos,e->screenPos(),e->button(),e->buttons(),e->modifiers()); - m_biotracker.mouseEvent(&modifiedEvent); - break; - } - default: { - assert(false); - break; + case Mode::PANZOOM: { + if (m_panZoomState.panState) { + const QPointF delta = e->localPos() - (*m_panZoomState.panState).lastPos; + (*m_panZoomState.panState).lastPos = e->localPos(); + m_panZoomState.isChanged = true; + m_panZoomState.panX -= static_cast(delta.x()); + m_panZoomState.panY -= static_cast(delta.y()); + update(); } + break; + } + case Mode::INTERACTION: { + e->accept(); + QPoint p = unprojectScreenPos(e->pos()); + const QPointF localPos(p); + QMouseEvent modifiedEvent(e->type(),localPos,e->screenPos(),e->button(),e->buttons(),e->modifiers()); + m_biotracker.mouseEvent(&modifiedEvent); + break; + } + default: { + assert(false); + break; + } } } void VideoView::mousePressEvent(QMouseEvent *e) { switch (m_currentMode) { - case Mode::PANZOOM: { - if (QApplication::keyboardModifiers() == Qt::NoModifier) { - m_panZoomState.isChanged = true; - m_panZoomState.panState = BioTracker::Core::CurrentPanState(e->localPos()); - setCursor(Qt::ClosedHandCursor); - } - if (e->button() == Qt::LeftButton && e->type() == QEvent::MouseButtonDblClick) { - fitToWindow(); - } - break; + case Mode::PANZOOM: { + if (QApplication::keyboardModifiers() == Qt::NoModifier) { + m_panZoomState.isChanged = true; + m_panZoomState.panState = BioTracker::Core::CurrentPanState(e->localPos()); + setCursor(Qt::ClosedHandCursor); } - case Mode::INTERACTION: { - e->accept(); - QPoint p = unprojectScreenPos(e->pos()); - const QPointF localPos(p); - QMouseEvent modifiedEvent(e->type(),localPos,e->screenPos(),e->button(),e->buttons(),e->modifiers()); - m_biotracker.mouseEvent(&modifiedEvent); - break; - } - default: { - assert(false); - break; + if (e->button() == Qt::LeftButton && e->type() == QEvent::MouseButtonDblClick) { + fitToWindow(); } + break; + } + case Mode::INTERACTION: { + e->accept(); + QPoint p = unprojectScreenPos(e->pos()); + const QPointF localPos(p); + QMouseEvent modifiedEvent(e->type(),localPos,e->screenPos(),e->button(),e->buttons(),e->modifiers()); + m_biotracker.mouseEvent(&modifiedEvent); + break; + } + default: { + assert(false); + break; + } } } void VideoView::mouseReleaseEvent(QMouseEvent *e) { switch (m_currentMode) { - case Mode::PANZOOM: { - setCursor(Qt::OpenHandCursor); - m_panZoomState.panState.reset(); - break; - } - case Mode::INTERACTION: { - e->accept(); - QPoint p = unprojectScreenPos(e->pos()); - const QPointF localPos(p); - QMouseEvent modifiedEvent(e->type(),localPos,e->screenPos(),e->button(),e->buttons(),e->modifiers()); - m_biotracker.mouseEvent(&modifiedEvent); - break; - } - default: { - assert(false); - break; - } + case Mode::PANZOOM: { + setCursor(Qt::OpenHandCursor); + m_panZoomState.panState.reset(); + break; + } + case Mode::INTERACTION: { + e->accept(); + QPoint p = unprojectScreenPos(e->pos()); + const QPointF localPos(p); + QMouseEvent modifiedEvent(e->type(),localPos,e->screenPos(),e->button(),e->buttons(),e->modifiers()); + m_biotracker.mouseEvent(&modifiedEvent); + break; + } + default: { + assert(false); + break; + } } } void VideoView::wheelEvent(QWheelEvent *e) { - BioTracker::Core::TextureObject const& texture = - m_biotracker.getTrackingThread().getTexture(); + BioTracker::Core::TextureObject const &texture = + m_biotracker.getTrackingThread().getTexture(); switch (m_currentMode) { - case Mode::PANZOOM: { - if (e->orientation() == Qt::Vertical) { - const int numDegrees = e->delta(); - const float deltaZoom = numDegrees; - m_panZoomState = BioTracker::Core::ScreenHelper::zoomTo( - m_panZoomState, - texture.width(), texture.height(), - this->width(), this->height(), - deltaZoom, - e->pos() - ); - update(); - e->accept(); - } - break; - } - case Mode::INTERACTION: { + case Mode::PANZOOM: { + if (e->orientation() == Qt::Vertical) { + const int numDegrees = e->delta(); + const float deltaZoom = numDegrees; + m_panZoomState = BioTracker::Core::ScreenHelper::zoomTo( + m_panZoomState, + texture.width(), texture.height(), + this->width(), this->height(), + deltaZoom, + e->pos() + ); + update(); e->accept(); - const QPoint p = unprojectScreenPos(e->pos()); - const QPointF localPos(p); - QWheelEvent modifiedEvent(e->pos(),localPos,e->pixelDelta(),e->angleDelta(),e->delta(),e->orientation(),e->buttons(),e->modifiers()); - QCoreApplication::sendEvent(QApplication::activeWindow(), &modifiedEvent); - m_biotracker.mouseWheelEvent(&modifiedEvent); - break; - } - default: { - assert(false); - break; } + break; + } + case Mode::INTERACTION: { + e->accept(); + const QPoint p = unprojectScreenPos(e->pos()); + const QPointF localPos(p); + QWheelEvent modifiedEvent(e->pos(),localPos,e->pixelDelta(),e->angleDelta(),e->delta(),e->orientation(),e->buttons(), + e->modifiers()); + QCoreApplication::sendEvent(QApplication::activeWindow(), &modifiedEvent); + m_biotracker.mouseWheelEvent(&modifiedEvent); + break; + } + default: { + assert(false); + break; + } } } diff --git a/biotracker/src/main.cpp b/biotracker/src/main.cpp index e5d7008..b1ceab6 100644 --- a/biotracker/src/main.cpp +++ b/biotracker/src/main.cpp @@ -18,7 +18,8 @@ int main(int argc, char *argv[]) { // meta types qRegisterMetaType("cv::Mat"); - qRegisterMetaType("MSGS::MTYPE"); + qRegisterMetaType("BioTracker::Core::Messages::MessageType"); + qRegisterMetaType("MessageType"); qRegisterMetaType("std::string"); qRegisterMetaType("std::size_t"); qRegisterMetaType("size_t"); @@ -31,8 +32,8 @@ int main(int argc, char *argv[]) { BioTracker::Gui::Gui w; return app.exec(); } else { - static const std::string title = MSGS::SYSTEM::APPLICATION_CANNOT_START; - static const std::string msg = MSGS::SYSTEM::NO_OPENGL; + static const std::string title = BioTracker::Core::Messages::System::APPLICATION_CANNOT_START; + static const std::string msg = BioTracker::Core::Messages::System::NO_OPENGL; QMessageBox::critical(nullptr, QString::fromStdString(title), QString::fromStdString(msg));