From 5333b8628c5010b325fc33be30c5ea47d20237d8 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 18 May 2014 16:35:38 -0500 Subject: [PATCH] Added better support for mjpeg. When an http:// or https:// URL is provided, assume MJPEG and pass the correct values into OpenCV Added a VideoBuffer class that pulls the latest images in a background thread. --- src/CMakeLists.txt | 2 +- src/main.cpp | 53 +++++++++++++++++++ src/videobuffer.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++ src/videobuffer.h | 86 ++++++++++++++++++++++++++++++ 4 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/videobuffer.cpp create mode 100644 src/videobuffer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38382c3..f019eae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,7 +60,7 @@ include_directories(./openalpr ) set(CMAKE_CSS_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall ") -ADD_EXECUTABLE( alpr main.cpp ) +ADD_EXECUTABLE( alpr main.cpp videobuffer.cpp ) diff --git a/src/main.cpp b/src/main.cpp index 03593a6..8a7c76a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" @@ -29,6 +30,7 @@ #include "tclap/CmdLine.h" #include "support/filesystem.h" #include "support/timing.h" +#include "videobuffer.h" #include "alpr.h" const std::string MAIN_WINDOW_NAME = "ALPR main window"; @@ -38,9 +40,14 @@ const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg"; /** Function Headers */ bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson); +void sighandler(int sig); bool measureProcessingTime = false; +// This boolean is set to false when the user hits terminates (e.g., CTRL+C ) +// so we can end infinite loops for things like video processing. +bool program_active = true; + int main( int argc, const char** argv ) { std::string filename; @@ -100,6 +107,18 @@ int main( int argc, const char** argv ) return 1; } + struct sigaction sigIntHandler; + + sigIntHandler.sa_handler = sighandler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + + sigaction(SIGHUP, &sigIntHandler, NULL); + sigaction(SIGINT, &sigIntHandler, NULL); + sigaction(SIGQUIT, &sigIntHandler, NULL); + sigaction(SIGKILL, &sigIntHandler, NULL); + sigaction(SIGTERM, &sigIntHandler, NULL); + //sigaction(SIGABRT, &sigIntHandler, NULL); cv::Mat frame; @@ -152,6 +171,32 @@ int main( int argc, const char** argv ) framenum++; } } + else if (startsWith(filename, "http://") || startsWith(filename, "https://")) + { + int framenum = 0; + + VideoBuffer videoBuffer; + + videoBuffer.connect(filename, 5); + + cv::Mat latestFrame; + + while (program_active) + { + int response = videoBuffer.getLatestFrame(&latestFrame); + + if (response != -1) + { + detectandshow( &alpr, latestFrame, "", outputJson); + } + + cv::waitKey(10); + } + + videoBuffer.disconnect(); + + std::cout << "Video processing ended" << std::endl; + } else if (hasEnding(filename, ".avi") || hasEnding(filename, ".mp4") || hasEnding(filename, ".webm") || hasEnding(filename, ".flv")) { if (fileExists(filename.c_str())) @@ -227,6 +272,14 @@ int main( int argc, const char** argv ) return 0; } + +void sighandler(int sig) +{ + program_active = false; + //std::cout << "Sig handler caught " << sig << std::endl; +} + + bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson) { std::vector buffer; diff --git a/src/videobuffer.cpp b/src/videobuffer.cpp new file mode 100644 index 0000000..6e694fd --- /dev/null +++ b/src/videobuffer.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013 New Designs Unlimited, LLC + * Opensource Automated License Plate Recognition [http://www.openalpr.com] + * + * This file is part of OpenAlpr. + * + * OpenAlpr is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License + * version 3 as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +*/ + +#include "videobuffer.h" + + +void imageCollectionThread(void* arg); + + +VideoBuffer::VideoBuffer() +{ + dispatcher = NULL; + +} + +VideoBuffer::~VideoBuffer() +{ + if (dispatcher != NULL) + { + dispatcher->active = false; + } +} + + +void VideoBuffer::connect(std::string mjpeg_url, int fps) +{ + + if (hasEnding(mjpeg_url, ".mjpg") == false) + { + // The filename doesn't end with ".mjpg" so the downstream processing may not treat it as such + // OpenCV doesn't have a way to force the rendering, other than via URL path. So, let's add it to the URL + + + std::size_t found = mjpeg_url.find("?"); + if (found!=std::string::npos) + { + // URL already contains a "?" + mjpeg_url = mjpeg_url + "&openalprfiletype=file.mjpg"; + } + else + { + // URL does not contain a "?" + mjpeg_url = mjpeg_url + "?openalprfiletype=file.mjpg"; + } + + } + + dispatcher = new VideoDispatcher(mjpeg_url, fps); + + tthread::thread* t = new tthread::thread(imageCollectionThread, (void*) dispatcher); + +} + +int VideoBuffer::getLatestFrame(cv::Mat* frame) +{ + if (dispatcher == NULL) + return -1; + + return dispatcher->getLatestFrame(frame); +} + + +void VideoBuffer::disconnect() +{ + if (dispatcher != NULL) + { + dispatcher->active = false; + } + + dispatcher = NULL; +} + + + + + +void imageCollectionThread(void* arg) +{ + + VideoDispatcher* dispatcher = (VideoDispatcher*) arg; + + cv::VideoCapture cap=cv::VideoCapture(); + cap.open(dispatcher->mjpeg_url); + + cv::Mat frame; + + while (dispatcher->active) + { + while (dispatcher->active) + { + + dispatcher->mMutex.lock(); + bool hasImage = cap.read(frame); + dispatcher->setLatestFrame(&frame); + dispatcher->mMutex.unlock(); + + if (hasImage == false) + break; + + + // Delay 15ms + cv::waitKey(15); + } + + // Delay 100ms + cv::waitKey(100); + } +} \ No newline at end of file diff --git a/src/videobuffer.h b/src/videobuffer.h new file mode 100644 index 0000000..813bb5a --- /dev/null +++ b/src/videobuffer.h @@ -0,0 +1,86 @@ +#ifndef OPENALPR_VIDEOBUFFER_H +#define OPENALPR_VIDEOBUFFER_H + +#include + +#include "opencv2/highgui/highgui.hpp" + +#include "support/filesystem.h" +#include "support/tinythread.h" + + + +class VideoDispatcher +{ + public: + VideoDispatcher(std::string mjpeg_url, int fps) + { + this->active = true; + this->latestFrameNumber = -1; + this->lastFrameRead = -1; + this->fps = fps; + this->mjpeg_url = mjpeg_url; + } + + + int getLatestFrame(cv::Mat* frame) + { + if (latestFrameNumber == lastFrameRead) + return -1; + + tthread::lock_guard guard(mMutex); + + frame->create(latestFrame->size(), latestFrame->type()); + latestFrame->copyTo(*frame); + + this->lastFrameRead = this->latestFrameNumber; + + return this->lastFrameRead; + } + + void setLatestFrame(cv::Mat* frame) + { + this->latestFrame = frame; + + this->latestFrameNumber++; + } + + bool active; + + int latestFrameNumber; + int lastFrameRead; + + std::string mjpeg_url; + int fps; + tthread::mutex mMutex; + + private: + cv::Mat* latestFrame; + +}; + +class VideoBuffer +{ + + public: + VideoBuffer(); + virtual ~VideoBuffer(); + + void connect(std::string mjpeg_url, int fps); + + // If a new frame is available, the function sets "frame" to it and returns the frame number + // If no frames are available, or the latest has already been grabbed, returns -1. + int getLatestFrame(cv::Mat* frame); + + void disconnect(); + + private: + + + VideoDispatcher* dispatcher; +}; + + + + +#endif // OPENALPR_VIDEOBUFFER_H \ No newline at end of file