diff --git a/CMakeLists.txt b/CMakeLists.txt index 536e64b6..13b9c751 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,8 @@ CPM_ExportAdditionalDefinition(-DQT_NO_KEYWORDS) # Disable warnings add_definitions(-D_SCL_SECURE_NO_WARNINGS) +# Specify that we want to export symbols to a DLL (as opposed to importing them from a DLL). +add_definitions(-DBUILD_BIOTRACKER_DLL) # include external dependecies as SYSTEM headers to prevent warnings from # external files. unfortunately, this has no effect when using MSVC. diff --git a/biotracker/BioTrackerApp.h b/biotracker/BioTrackerApp.h index e76cec80..e3a58f01 100644 --- a/biotracker/BioTrackerApp.h +++ b/biotracker/BioTrackerApp.h @@ -9,6 +9,7 @@ #include #include +#include "util/platform.h" #include "TrackingThread.h" #include "ImageStream.h" #include "Registry.h" @@ -16,11 +17,12 @@ #include "TrackerStatus.h" #include "PanZoomState.h" + namespace BioTracker { namespace Core { /* Used to be Facade */ -class BioTrackerApp : public QObject { +class BIOTRACKER_DLLEXPORT BioTrackerApp : public QObject { public: Q_OBJECT public: @@ -46,6 +48,12 @@ class BioTrackerApp : public QObject { return m_trackingThread.getStatus(); } + /** + * @throw boost::filesystem_error + * @brief openMediaBySetting opens a medium (camera, images or video) which has been storen in the settings + */ + void openMediumBySetting(); + /** * @throw boost::filesystem_error * @brief openVideo opens a single video which type is supported by opencv and stores path in settings diff --git a/biotracker/Registry.h b/biotracker/Registry.h index 55b41996..19543e91 100644 --- a/biotracker/Registry.h +++ b/biotracker/Registry.h @@ -6,6 +6,7 @@ #include +#include "util/platform.h" #include "zmq/ZmqInfoFile.h" #include "TrackingAlgorithm.h" #include "util/stdext.h" @@ -22,12 +23,12 @@ static const TrackerType NoTracking = 0; // construct on first use idiom TrackerType getNextId(); -struct NewTrackerFactory { +struct BIOTRACKER_DLLEXPORT NewTrackerFactory { virtual std::shared_ptr operator()(Settings &settings) const = 0; virtual ~NewTrackerFactory() {} }; -class Registry : public QObject, public Util::Singleton { +class BIOTRACKER_DLLEXPORT Registry : public QObject, public Util::Singleton { Q_OBJECT public: typedef std::map> diff --git a/biotracker/TrackingAlgorithm.h b/biotracker/TrackingAlgorithm.h index b5f9a7d9..8e7a575c 100644 --- a/biotracker/TrackingAlgorithm.h +++ b/biotracker/TrackingAlgorithm.h @@ -12,6 +12,7 @@ #include #include +#include "util/platform.h" #include "settings/Messages.h" #include "settings/ParamNames.h" #include "serialization/TrackedObject.h" @@ -55,14 +56,14 @@ class ProxyMat { boost::optional m_modifiedMat; }; -class TrackingAlgorithm : public QObject { +class BIOTRACKER_DLLEXPORT TrackingAlgorithm : public QObject { Q_OBJECT public: TrackingAlgorithm(Settings &settings); virtual ~TrackingAlgorithm() override; - struct View { + struct BIOTRACKER_DLLEXPORT View { std::string name; }; diff --git a/biotracker/settings/SystemCompatibilityCheck.h b/biotracker/settings/SystemCompatibilityCheck.h index 06dc13f1..829f1af1 100644 --- a/biotracker/settings/SystemCompatibilityCheck.h +++ b/biotracker/settings/SystemCompatibilityCheck.h @@ -1,10 +1,12 @@ #pragma once +#include "util/platform.h" + namespace SystemCompatibilityCheck { /** * Check the system supports openGL. * @return true, system supports openGL, false otherwise. */ -bool checkOpenGLSupport(); +bool BIOTRACKER_DLLEXPORT checkOpenGLSupport(); } diff --git a/biotracker/src/BioTrackerApp.cpp b/biotracker/src/BioTrackerApp.cpp index f72b7e9d..7c698431 100644 --- a/biotracker/src/BioTrackerApp.cpp +++ b/biotracker/src/BioTrackerApp.cpp @@ -42,6 +42,10 @@ void BioTrackerApp::initConnects() { this, &BioTrackerApp::trackerIsAlreadyLoadedFromRegistry); } +void BioTrackerApp::openMediumBySetting() { + m_trackingThread.loadFromSettings(); +} + void BioTrackerApp::openVideo(const boost::filesystem::path &path) { m_trackingThread.loadVideo(path); } diff --git a/biotracker/src/ImageStream.cpp b/biotracker/src/ImageStream.cpp index 6fa1cb31..a6a08d1c 100644 --- a/biotracker/src/ImageStream.cpp +++ b/biotracker/src/ImageStream.cpp @@ -134,6 +134,10 @@ class ImageStreamPictures : public ImageStream { public: explicit ImageStreamPictures(std::vector picture_files) : m_picture_files(std::move(picture_files)) { + if (m_picture_files.empty()) { + throw file_not_found("Could not find any picture files"); + } + // load first image if (this->numFrames() > 0) { this->setFrameNumber_impl(0); @@ -244,7 +248,7 @@ class ImageStreamCamera : public ImageStream { : m_capture(device_id) , m_fps(m_capture.get(CV_CAP_PROP_FPS)) { if (! m_capture.isOpened()) { - throw device_open_error(":("); + throw device_open_error("Could not open camera " + std::to_string(device_id)); } // load first image if (this->numFrames() > 0) { diff --git a/biotracker/src/TrackingThread.cpp b/biotracker/src/TrackingThread.cpp index 70d288b9..7e984238 100644 --- a/biotracker/src/TrackingThread.cpp +++ b/biotracker/src/TrackingThread.cpp @@ -1,6 +1,7 @@ #include "TrackingThread.h" #include +#include #include #include @@ -10,6 +11,7 @@ #include "settings/Messages.h" #include "settings/Settings.h" #include "settings/ParamNames.h" +#include "Exceptions.h" #include #include @@ -40,33 +42,66 @@ TrackingThread::~TrackingThread(void) { } void TrackingThread::loadFromSettings() { - std::string filenameStr = m_settings.getValueOfParam - (CaptureParam::CAP_VIDEO_FILE); - boost::filesystem::path filename {filenameStr}; - m_imageStream = make_ImageStreamVideo(filename); - if (m_imageStream->type() == GuiParam::MediaType::NoMedia) { - // could not open video - std::string errorMsg = "unable to open file " + filename.string(); - Q_EMIT notifyGUI(errorMsg, MessageType::FAIL); - m_status = TrackerStatus::Invalid; - return; - } else { - playOnce(); - } + // Determine which media type was used the last time + boost::optional mediaTypeOpt = m_settings.maybeGetValueOfParam(GuiParam::MEDIA_TYPE); + GuiParam::MediaType mediaType = mediaTypeOpt ? static_cast(*mediaTypeOpt) : + GuiParam::MediaType::NoMedia; + + if (mediaType == GuiParam::MediaType::Video) { + boost::optional filenameStr = m_settings.maybeGetValueOfParam(CaptureParam::CAP_VIDEO_FILE); + // Abort, because filename string was not set (just to prevent manipulated config files) + if (!filenameStr) { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(GuiParam::MediaType::NoMedia)); + return; + } + boost::filesystem::path filename {*filenameStr}; + try { + loadVideo(filename); + } catch (file_not_found &e) { + // Preventing segfault when video file vanished + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(GuiParam::MediaType::NoMedia)); + Q_EMIT notifyGUI(e.what(), MessageType::FAIL); + } + } else if (mediaType == GuiParam::MediaType::Camera) { + boost::optional camIdOpt = m_settings.maybeGetValueOfParam(CaptureParam::CAP_CAMERA_ID); + int camId = camIdOpt ? *camIdOpt : -1; + try { + loadCamera(camId); + } catch (device_open_error e) { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(GuiParam::MediaType::NoMedia)); + Q_EMIT notifyGUI(e.what(), MessageType::FAIL); + } + } else if (mediaType == GuiParam::MediaType::Images) { + boost::optional filenamesStrOpt = m_settings.maybeGetValueOfParam + (PictureParam::PICTURE_FILES); - m_fps = m_imageStream->fps(); - m_ignoreFilenameChanged = false; - Q_EMIT fileOpened(filenameStr, m_imageStream->numFrames(), m_fps); - if (m_tracker && m_somethingIsLoaded && - m_lastFilename.compare(filenameStr) != 0) { - m_tracker->inputChanged(); - m_tracker->onFileChanged(filenameStr); - m_lastFilename = filenameStr; + if (!filenamesStrOpt) { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(GuiParam::MediaType::NoMedia)); + return; + } + std::string filenamesStr = *filenamesStrOpt; + + // Split string of paths into a vector a paths + // source: http://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c + std::vector filenames; + std::string delimiter = ";"; + size_t pos = 0; + std::string token; + while ((pos = filenamesStr.find(delimiter)) != std::string::npos) { + token = filenamesStr.substr(0, pos); + filenames.push_back(boost::filesystem::path(token)); + filenamesStr.erase(0, pos + delimiter.length()); + } + try { + loadPictures(std::move(filenames)); + } catch (file_not_found e) { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(GuiParam::MediaType::NoMedia)); + Q_EMIT notifyGUI(e.what(), MessageType::FAIL); + } + } else { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(GuiParam::MediaType::NoMedia)); } - std::string note = "opened file: " + filenameStr + " (#frames: " - + QString::number(m_imageStream->numFrames()).toStdString() + ")"; - Q_EMIT notifyGUI(note, MessageType::FILE_OPEN); } void TrackingThread::loadVideo(const boost::filesystem::path &filename) { @@ -78,6 +113,7 @@ void TrackingThread::loadVideo(const boost::filesystem::path &filename) { m_status = TrackerStatus::Invalid; return; } else { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(m_imageStream->type())); playOnce(); } @@ -97,13 +133,23 @@ void TrackingThread::loadVideo(const boost::filesystem::path &filename) { } } Q_EMIT notifyGUI(note, MessageType::FILE_OPEN); - m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(m_imageStream->type())); } void TrackingThread::loadPictures(std::vector &&filenames) { m_fps = 1; m_ignoreFilenameChanged = false; + + // Convert filenames into one string for settings. This is done here, because move may clear the vector with filenames. + std::stringstream filenamesStr; + if (!filenames.empty()) { + filenamesStr << filenames[0].string(); + for (uint32_t i = 1; i < filenames.size(); i++) { + filenamesStr << ';'; + filenamesStr << filenames[i].string(); + } + } + m_imageStream = make_ImageStreamPictures(std::move(filenames)); if (m_imageStream->type() == GuiParam::MediaType::NoMedia) { // could not open video @@ -116,9 +162,12 @@ void TrackingThread::loadPictures(std::vector m_status = TrackerStatus::Invalid; return; } else { + m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(m_imageStream->type())); playOnce(); Q_EMIT fileOpened(m_imageStream->currentFilename(), m_imageStream->numFrames(), m_fps); + + m_settings.setParam(PictureParam::PICTURE_FILES, filenamesStr.str()); if (m_tracker) { m_tracker->inputChanged(); if (m_somethingIsLoaded @@ -128,7 +177,6 @@ void TrackingThread::loadPictures(std::vector } } } - m_settings.setParam(GuiParam::MEDIA_TYPE, static_cast(m_imageStream->type())); } void TrackingThread::loadCamera(int device) { diff --git a/biotracker/src/util/stringTools.cpp b/biotracker/src/util/stringTools.cpp index 5055469b..711a8e80 100644 --- a/biotracker/src/util/stringTools.cpp +++ b/biotracker/src/util/stringTools.cpp @@ -4,10 +4,12 @@ #include // std::string #include // std::invalid_argument +#include "util/platform.h" + namespace BioTracker { namespace Util { -std::string escape_non_ascii(const std::string &s) { +std::string BIOTRACKER_DLLEXPORT escape_non_ascii(const std::string &s) { std::string result; for (const auto c : s) { // MSB is set --> not a valid ASCII character --> escape @@ -29,7 +31,7 @@ std::string escape_non_ascii(const std::string &s) { } -std::string unescape_non_ascii(const std::string &s) { +std::string BIOTRACKER_DLLEXPORT unescape_non_ascii(const std::string &s) { const auto advance = [&s](std::string::const_iterator &it) { if (++it == s.cend()) { @@ -78,7 +80,7 @@ std::string unescape_non_ascii(const std::string &s) { return result; } -std::string stem_filename(const std::string &s) { +std::string BIOTRACKER_DLLEXPORT stem_filename(const std::string &s) { boost::filesystem::path p(s); return p.stem().string(); } diff --git a/biotracker/util/platform.h b/biotracker/util/platform.h new file mode 100644 index 00000000..f7ff04c2 --- /dev/null +++ b/biotracker/util/platform.h @@ -0,0 +1,14 @@ +#pragma once + +/* +This macro must be used to mark all symbols that should be exported to a DLL. +*/ +#ifdef _MSC_VER +#ifdef BUILD_BIOTRACKER_DLL +#define BIOTRACKER_DLLEXPORT __declspec(dllexport) +#else +#define BIOTRACKER_DLLEXPORT __declspec(dllimport) +#endif +#else +#define BIOTRACKER_DLLEXPORT +#endif