From 60cbdaa339eab24f8fe72eb9cf893832e224abe0 Mon Sep 17 00:00:00 2001 From: Alexander Duda Date: Tue, 16 Feb 2016 15:20:24 +0100 Subject: [PATCH] support for cv::VideoCapture VideoCapture is a generic camera interface from opencv supporting cameras, video streams and video files. This is a basic implementaion for using VideoCapture as camera_base --- camera_base.orogen | 10 ++ tasks/VideoCapture.cpp | 226 +++++++++++++++++++++++++++++++++++++++++ tasks/VideoCapture.hpp | 109 ++++++++++++++++++++ 3 files changed, 345 insertions(+) create mode 100644 tasks/VideoCapture.cpp create mode 100644 tasks/VideoCapture.hpp diff --git a/camera_base.orogen b/camera_base.orogen index 4cdbd83..aad70e9 100644 --- a/camera_base.orogen +++ b/camera_base.orogen @@ -173,6 +173,14 @@ task_context "Task" do argument('type', '/camera/double_attrib/CamAttrib', 'type') end +# Task using OpenCV video caputre +task_context "VideoCapture" do + subclasses "camera_base::Task" + needs_configuration + + periodic 0.02 +end + # Task to pre-process images before used by other components task_context "Preprocess" do reports :PROCESSING_ERROR @@ -208,3 +216,5 @@ task_context "Preprocess" do port_driven end + + diff --git a/tasks/VideoCapture.cpp b/tasks/VideoCapture.cpp new file mode 100644 index 0000000..158c1e3 --- /dev/null +++ b/tasks/VideoCapture.cpp @@ -0,0 +1,226 @@ +/* Generated from orogen/lib/orogen/templates/tasks/Task.cpp */ + +#include "VideoCapture.hpp" +#include +#include + +using namespace camera; +using namespace camera_base; + +// links the opencv interface to the CamInterface +class CamVideoCapture :public camera::CamInterface +{ + public: + virtual ~CamVideoCapture(){}; + virtual int listCameras(std::vector &cam_infos)const + { + return 0; + } + virtual void openCamera(std::string camera_name) + { + frame_available = false; + if(!capture.open(camera_name)) + throw std::runtime_error("cannot open camera"); + capture.set(cv::CAP_PROP_CONVERT_RGB,1); + } + virtual bool isOpen()const + { + return capture.isOpened(); + } + virtual bool close() + { + capture = cv::VideoCapture(); + return true; + } + virtual bool grab(const GrabMode mode = SingleFrame, const int buffer_len=1) + { + // changing grab mode is not supported + return true; + } + virtual bool retrieveFrame(base::samples::frame::Frame &frame,const int timeout=1000) + { + base::Time time = base::Time::now(); + do + { + if(isFrameAvailable()) + { + // create copy because we are not allowed to modify internal + // buffer + frame_available = false; + if(capture.retrieve(image)) + { + frame_helper::FrameHelper::copyMatToFrame(image,frame); + frame.time = time; + frame.frame_status = base::samples::frame::STATUS_VALID; + return true; + } + } + usleep(100); + } + while((base::Time::now()-time).toMilliseconds() <= timeout); + return false; + } + virtual bool isFrameAvailable() + { + if(!frame_available) + frame_available = capture.grab(); + return frame_available; + } + + virtual bool setFrameSettings(const base::samples::frame::frame_size_t size, + const base::samples::frame::frame_mode_t mode, + const uint8_t color_depth, + const bool resize_frames) + { + base::samples::frame::Frame frame(0,0,color_depth,mode); + // this does not work for video streams and some cameras! + // capture.set(cv::CAP_PROP_FRAME_WIDTH,size.width); + // capture.set(cv::CAP_PROP_FRAME_HEIGHT,size.height); + // capture.set(cv::CAP_PROP_FORMAT,frame_helper::FrameHelper::getOpenCvType(frame)); + return true; + } + bool isAttribAvail(const int_attrib::CamAttrib attrib) + { + switch(attrib) + { + case int_attrib::GainValue: + return true; + default: + return false; + } + return true; + } + bool isAttribAvail(const double_attrib::CamAttrib attrib) + { + switch(attrib) + { + case double_attrib::FrameRate: + return true; + default: + return false; + } + return true; + } + bool isAttribAvail(const enum_attrib::CamAttrib attrib) + { + return false; + } + bool setAttrib(const int_attrib::CamAttrib attrib,const int value) + { + switch(attrib) + { + case int_attrib::GainValue: + capture.set(cv::CAP_PROP_GAIN,value); + break; + default: + return false; + } + return true; + } + bool setAttrib(const double_attrib::CamAttrib attrib,const double value) + { + switch(attrib) + { + case double_attrib::FrameRate: + capture.set(cv::CAP_PROP_FPS,value); + break; + default: + return false; + } + return true; + } + int getAttrib(const int_attrib::CamAttrib attrib) + { + switch(attrib) + { + case int_attrib::GainValue: + return capture.get(cv::CAP_PROP_GAIN); + default: + return false; + } + return true; + } + double getAttrib(const double_attrib::CamAttrib attrib) + { + switch(attrib) + { + case double_attrib::FrameRate: + return capture.get(cv::CAP_PROP_FPS); + default: + return false; + } + return true; + } + + private: + cv::VideoCapture capture; + cv::Mat image; + bool frame_available; +}; + +//CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. +//CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. +//CAP_PROP_FORMAT Format of the Mat objects returned by retrieve() . +//CAP_PROP_CONTRAST Contrast of the image (only for cameras). +//CAP_PROP_SATURATION Saturation of the image (only for cameras). +//CAP_PROP_HUE Hue of the image (only for cameras). +//CAP_PROP_EXPOSURE Exposure (only for cameras). +// + +VideoCapture::VideoCapture(std::string const& name) + : VideoCaptureBase(name) +{ +} + +VideoCapture::VideoCapture(std::string const& name, RTT::ExecutionEngine* engine) + : VideoCaptureBase(name, engine) +{ +} + +VideoCapture::~VideoCapture() +{ +} + + +void VideoCapture::processImage() +{ + // resize output otherwise processing goes crazy + // this is needed because the size is not always known at the beginning + base::samples::frame::Frame *frame_ptr = output_frame.write_access(); + frame_ptr->init(camera_frame->size.width,camera_frame->size.height,camera_frame->data_depth,_output_format.get()); + output_frame.reset(frame_ptr); + Task::processImage(); +} + +bool VideoCapture::configureHook() +{ + if (! VideoCaptureBase::configureHook()) + return false; + + CamVideoCapture* camera = new CamVideoCapture(); + camera->openCamera(_camera_id.value()); + cam_interface = camera; + return true; +} +bool VideoCapture::startHook() +{ + if (! VideoCaptureBase::startHook()) + return false; + return true; +} +void VideoCapture::updateHook() +{ + VideoCaptureBase::updateHook(); +} +void VideoCapture::errorHook() +{ + VideoCaptureBase::errorHook(); +} +void VideoCapture::stopHook() +{ + VideoCaptureBase::stopHook(); +} +void VideoCapture::cleanupHook() +{ + VideoCaptureBase::cleanupHook(); +} diff --git a/tasks/VideoCapture.hpp b/tasks/VideoCapture.hpp new file mode 100644 index 0000000..ff21ecf --- /dev/null +++ b/tasks/VideoCapture.hpp @@ -0,0 +1,109 @@ +/* Generated from orogen/lib/orogen/templates/tasks/Task.hpp */ + +#ifndef CAMERA_BASE_VIDEOCAPTURE_TASK_HPP +#define CAMERA_BASE_VIDEOCAPTURE_TASK_HPP + +#include "camera_base/VideoCaptureBase.hpp" + +namespace camera_base{ + + /*! \class VideoCapture + * \brief The task context provides and requires services. It uses an ExecutionEngine to perform its functions. + * Essential interfaces are operations, data flow ports and properties. These interfaces have been defined using the oroGen specification. + * In order to modify the interfaces you should (re)use oroGen and rely on the associated workflow. + * Task using OpenCV video caputre + * \details + * The name of a TaskContext is primarily defined via: + \verbatim + deployment 'deployment_name' + task('custom_task_name','camera_base::VideoCapture') + end + \endverbatim + * It can be dynamically adapted when the deployment is called with a prefix argument. + */ + class VideoCapture : public VideoCaptureBase + { + friend class VideoCaptureBase; + protected: + virtual void processImage(); + + public: + /** TaskContext constructor for VideoCapture + * \param name Name of the task. This name needs to be unique to make it identifiable via nameservices. + * \param initial_state The initial TaskState of the TaskContext. Default is Stopped state. + */ + VideoCapture(std::string const& name = "camera_base::VideoCapture"); + + /** TaskContext constructor for VideoCapture + * \param name Name of the task. This name needs to be unique to make it identifiable for nameservices. + * \param engine The RTT Execution engine to be used for this task, which serialises the execution of all commands, programs, state machines and incoming events for a task. + * + */ + VideoCapture(std::string const& name, RTT::ExecutionEngine* engine); + + /** Default deconstructor of VideoCapture + */ + ~VideoCapture(); + + /** This hook is called by Orocos when the state machine transitions + * from PreOperational to Stopped. If it returns false, then the + * component will stay in PreOperational. Otherwise, it goes into + * Stopped. + * + * It is meaningful only if the #needs_configuration has been specified + * in the task context definition with (for example): + \verbatim + task_context "TaskName" do + needs_configuration + ... + end + \endverbatim + */ + bool configureHook(); + + /** This hook is called by Orocos when the state machine transitions + * from Stopped to Running. If it returns false, then the component will + * stay in Stopped. Otherwise, it goes into Running and updateHook() + * will be called. + */ + bool startHook(); + + /** This hook is called by Orocos when the component is in the Running + * state, at each activity step. Here, the activity gives the "ticks" + * when the hook should be called. + * + * The error(), exception() and fatal() calls, when called in this hook, + * allow to get into the associated RunTimeError, Exception and + * FatalError states. + * + * In the first case, updateHook() is still called, and recover() allows + * you to go back into the Running state. In the second case, the + * errorHook() will be called instead of updateHook(). In Exception, the + * component is stopped and recover() needs to be called before starting + * it again. Finally, FatalError cannot be recovered. + */ + void updateHook(); + + /** This hook is called by Orocos when the component is in the + * RunTimeError state, at each activity step. See the discussion in + * updateHook() about triggering options. + * + * Call recover() to go back in the Runtime state. + */ + void errorHook(); + + /** This hook is called by Orocos when the state machine transitions + * from Running to Stopped after stop() has been called. + */ + void stopHook(); + + /** This hook is called by Orocos when the state machine transitions + * from Stopped to PreOperational, requiring the call to configureHook() + * before calling start() again. + */ + void cleanupHook(); + }; +} + +#endif +