From b5811dbbcb44d7c914e54342c371e48c9cf696d7 Mon Sep 17 00:00:00 2001 From: Tim Zeuner Date: Thu, 22 Dec 2022 23:13:53 +0100 Subject: [PATCH] Ressource guarding multithreading --- AutonomousMode/autonomous_mode_main.cpp | 49 +++++++-- AutonomousMode/lfr.cpp | 128 ++++++++++++++++-------- AutonomousMode/lfr.h | 45 ++++++--- 3 files changed, 158 insertions(+), 64 deletions(-) diff --git a/AutonomousMode/autonomous_mode_main.cpp b/AutonomousMode/autonomous_mode_main.cpp index 63abfd1..fcbc4f3 100644 --- a/AutonomousMode/autonomous_mode_main.cpp +++ b/AutonomousMode/autonomous_mode_main.cpp @@ -4,19 +4,52 @@ int main(void) { //Disable opencv logging messages - cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING); + //cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING); const int thresholdBinary = 140; const int videoHeight = 720; const int videoWidth = 1280; const int gaussKernelSize = 11; - LFR lfr(videoHeight, videoWidth, thresholdBinary, gaussKernelSize); - lfr.saveOutputFlag = false; - lfr.videoFlag = true; + std::mutex mutex; + + LFR lfr(videoHeight, videoWidth, thresholdBinary, gaussKernelSize, [&](std::exception const &ex) + { + std::unique_lock lock(mutex); + std::cerr<<"camera exception:"<(std::chrono::system_clock::now().time_since_epoch()); + std::chrono::milliseconds now = duration_cast(std::chrono::system_clock::now().time_since_epoch()); + + cv::Mat img; + lfr.addListener([&](LFR_Result result) + { + std::unique_lock lock(mutex); + if (!result.rawImage.empty()) + { + cv::resize(result.rawImage, img, cv::Size(128, 64+32)); + + //Calculate frame rate + now = duration_cast(std::chrono::system_clock::now().time_since_epoch()); + unsigned int deltaMs = (now-last).count(); + float delta = static_cast(deltaMs) / 1000.0; + float frameRate = 1.0 / static_cast(delta); + + //std::cout << "Frame rate: " << frameRate << std::endl; + last = now; + } + }, &mutex); + lfr.startLoop(); - //To end the video stream, write any char in the console. - char a; - std::cin >> a; - lfr.endLoop(); + + for(int finished = false; finished != 'q';){ + finished = std::tolower(cv::waitKey(66)); + std::unique_lock lock(mutex); + if(!img.empty()){ + cv::imshow("frame", img); + } + } } diff --git a/AutonomousMode/lfr.cpp b/AutonomousMode/lfr.cpp index 4343ae2..28feaa6 100644 --- a/AutonomousMode/lfr.cpp +++ b/AutonomousMode/lfr.cpp @@ -3,63 +3,114 @@ #define right false #define left true -LFR::LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize) - : iAmLooping(false), input(videoHeight, videoWidth), processing(), controlModule(), interpreter(), intersectionHandler() +LFR::LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, ExceptionCallback cb): + stop(false), + input(videoHeight, videoWidth), + processing(), + controlModule(), + interpreter(), + intersectionHandler(), + cb(cb) { - this->iAmLooping = false; this->thresholdBinary = thresholdBinary; this->gaussKernelSize = gaussKernelSize; - - this->videoFlag = false; - this->saveOutputFlag = false; - this->outputFileName = ""; } LFR::~LFR() { - if(iAmLooping) + endLoop(); + thread->join(); +} + +void LFR::removeListener(LFR::ListenerKey key) +{ + std::lock_guard lock(mutex); + auto it = std::find_if(std::begin(listeners), std::end(listeners), [&](auto const &val){ + return val.first == key; + }); + if(it != std::end(listeners)) { - this->endLoop(); + listeners.erase(it); } } -void LFR::loop() +void LFR::addListener(LFR::ListenerCallback cb, LFR::ListenerKey key) { - if(this->videoFlag) {namedWindow("Display window");} - while(iAmLooping) - { - Mat originalImage = input.readWebcam(); + std::lock_guard lock(mutex); + listeners.emplace_back(key, std::move(cb)); +} - Point roiOrigin(0, int(originalImage.rows*(3.25/6.0))); - Rect roi(roiOrigin.x, roiOrigin.y, originalImage.cols, originalImage.rows/6); - //Mat processedImage = originalImage(roi); - Mat processedImage = originalImage; +void LFR::setStop(bool val) +{ + std::lock_guard lock(mutex); + stop = val; +} - processing.processImage(processedImage, this->thresholdBinary, this->gaussKernelSize); - //processedImage = processedImage(roi); - FrameData data = processing.calculateLineSegments(processedImage, roi); - processing.filterReflections(data); - processing.calcAngles(data, originalImage.cols, originalImage.rows, left); - this->provideOutput(originalImage, processedImage, data, roi); - } - if(this->videoFlag) {destroyWindow("Display window");} - input.freeWebcam(); +void LFR::createThread() +{ + thread = std::make_unique([this](){ + while(true) + { + LFR_Result result; + if(!stop && !listeners.empty()) + { + try + { + std::lock_guard lock(mutex); + Mat originalImage = input.readWebcam(); + + Point roiOrigin(0, int(originalImage.rows*(3.25/6.0))); + Rect roi(roiOrigin.x, roiOrigin.y, originalImage.cols, originalImage.rows/6); + Mat processedImage = originalImage; + + processing.processImage(processedImage, this->thresholdBinary, this->gaussKernelSize); + FrameData data = processing.calculateLineSegments(processedImage, roi); + processing.filterReflections(data); + processing.calcAngles(data, originalImage.cols, originalImage.rows, left); + + processedImage = provideOutput(originalImage, processedImage, data, roi); + + result.rawImage = originalImage; + result.processedImage = processedImage; + result.data = data; + } + catch(std::exception const &ex) + { + cb(ex); + } + + //Invoke the callback method (ListenerPair second -> ListenerCallback) + for(auto &val : listeners) + { + val.second(result); + } + } + else + { + break; + } + } + }); } void LFR::startLoop() { - iAmLooping = true; - this->loopThread=thread(&LFR::loop, this); + if(thread) + { + //Restart thread if it is running + setStop(true); + thread->join(); + setStop(false); + } + createThread(); } void LFR::endLoop() { - iAmLooping = false; - this->loopThread.join(); - return; + setStop(true); } -void LFR::provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi) +cv::Mat LFR::provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi) { for(int i = 0; i < frameData.contours.size(); i++) { @@ -83,14 +134,5 @@ void LFR::provideOutput(Mat originalImage, Mat processedImage, const FrameData& P2.y = (int)round(P1.y + length * sin(frameData.angle * CV_PI / 180.0)); cv::arrowedLine(originalImage, P1, P2, Scalar(0,0,255), 2, 8); } - if(this->videoFlag) - { - imshow("Display window", originalImage); - imshow("processed:", processedImage); - char c = (char)waitKey(25); - } - if (this->saveOutputFlag && !(this->outputFileName.empty())) - { - imwrite(this->outputFileName, originalImage); - } + return originalImage; } diff --git a/AutonomousMode/lfr.h b/AutonomousMode/lfr.h index 22140fb..b421d16 100644 --- a/AutonomousMode/lfr.h +++ b/AutonomousMode/lfr.h @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -15,34 +16,52 @@ using namespace cv; +struct LFR_Result +{ + cv::Mat rawImage; + cv::Mat processedImage; + FrameData data; +}; + class LFR { +public: + using ListenerKey = void const*; + using ExceptionCallback = std::function; + using ListenerCallback = std::function; + +private: + using ListenerPair = std::pair; + using ListenerVector = std::vector; + Input input; Processing processing; ControlModule controlModule; Interpreter interpreter; IntersectionHandler intersectionHandler; - volatile bool iAmLooping; - void loop(); - thread loopThread; + int thresholdBinary; int gaussKernelSize; - void provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi); + ListenerVector listeners; + ExceptionCallback cb; + bool stop; + std::unique_ptr thread; + mutable std::mutex mutex; + + //void provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi); + void createThread(); + void setStop(bool val); public: - LFR() = delete; - LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize); + LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, ExceptionCallback cb); ~LFR(); void startLoop(); void endLoop(); - - - bool videoFlag; - bool saveOutputFlag; - - std::string outputFileName; - + void addListener(ListenerCallback cv, ListenerKey key); + void removeListener(ListenerKey key); + void isStopped() const noexcept; + Mat provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi); }; \ No newline at end of file