From 550fd8e57c570939134aebf2a14a606f7a03323a Mon Sep 17 00:00:00 2001 From: Maria Dimashova Date: Tue, 27 Mar 2012 07:41:48 +0000 Subject: [PATCH] added image and depth synchronization --- doc/user_guide/ug_highgui.rst | 2 +- .../include/opencv2/highgui/highgui_c.h | 24 +- modules/highgui/src/cap_openni.cpp | 524 +++++++++++++++++- samples/cpp/openni_capture.cpp | 2 +- 4 files changed, 512 insertions(+), 40 deletions(-) diff --git a/doc/user_guide/ug_highgui.rst b/doc/user_guide/ug_highgui.rst index cc2b88b737..d425067b2d 100644 --- a/doc/user_guide/ug_highgui.rst +++ b/doc/user_guide/ug_highgui.rst @@ -126,7 +126,7 @@ Flags specifing the needed generator type must be used in combination with parti - ``CV_CAP_OPENNI_IMAGE_GENERATOR_OUTPUT_MODE = CV_CAP_OPENNI_IMAGE_GENERATOR + CV_CAP_PROP_OPENNI_OUTPUT_MODE`` - ``CV_CAP_OPENNI_DEPTH_GENERATOR_BASELINE = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_BASELINE`` - ``CV_CAP_OPENNI_DEPTH_GENERATOR_FOCAL_LENGTH = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_FOCAL_LENGTH`` - - ``CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION_ON = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_REGISTRATION_ON`` + - ``CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_REGISTRATION`` For more information please refer to the example of usage openni_capture.cpp_ in ``opencv/samples/cpp`` folder. diff --git a/modules/highgui/include/opencv2/highgui/highgui_c.h b/modules/highgui/include/opencv2/highgui/highgui_c.h index 8e72e4cfd8..7f27d3f333 100644 --- a/modules/highgui/include/opencv2/highgui/highgui_c.h +++ b/modules/highgui/include/opencv2/highgui/highgui_c.h @@ -375,26 +375,32 @@ enum CV_CAP_PROP_PREVIEW_FORMAT=1026, // readonly, tricky property, returns cpnst char* indeed // OpenNI map generators - CV_CAP_OPENNI_DEPTH_GENERATOR = 0, - CV_CAP_OPENNI_IMAGE_GENERATOR = 1 << 31, - CV_CAP_OPENNI_GENERATORS_MASK = 1 << 31, + CV_CAP_OPENNI_DEPTH_GENERATOR = 1 << 31, + CV_CAP_OPENNI_IMAGE_GENERATOR = 1 << 30, + CV_CAP_OPENNI_GENERATORS_MASK = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_OPENNI_IMAGE_GENERATOR, // Properties of cameras available through OpenNI interfaces CV_CAP_PROP_OPENNI_OUTPUT_MODE = 100, CV_CAP_PROP_OPENNI_FRAME_MAX_DEPTH = 101, // in mm CV_CAP_PROP_OPENNI_BASELINE = 102, // in mm CV_CAP_PROP_OPENNI_FOCAL_LENGTH = 103, // in pixels - CV_CAP_PROP_OPENNI_REGISTRATION_ON = 104, // flag - CV_CAP_PROP_OPENNI_REGISTRATION = CV_CAP_PROP_OPENNI_REGISTRATION_ON, // flag that synchronizes the remapping depth map to image map - // by changing depth generator's view point (if the flag is "on") or - // sets this view point to its normal one (if the flag is "off"). - CV_CAP_PROP_OPENNI_GENERATOR_PRESENT = 105, + CV_CAP_PROP_OPENNI_REGISTRATION = 104, // flag + CV_CAP_PROP_OPENNI_REGISTRATION_ON = CV_CAP_PROP_OPENNI_REGISTRATION, // flag that synchronizes the remapping depth map to image map + // by changing depth generator's view point (if the flag is "on") or + // sets this view point to its normal one (if the flag is "off"). + CV_CAP_PROP_OPENNI_APPROX_FRAME_SYNC = 105, + CV_CAP_PROP_OPENNI_MAX_BUFFER_SIZE = 106, + CV_CAP_PROP_OPENNI_CIRCLE_BUFFER = 107, + CV_CAP_PROP_OPENNI_MAX_TIME_DURATION = 108, + + CV_CAP_PROP_OPENNI_GENERATOR_PRESENT = 109, CV_CAP_OPENNI_IMAGE_GENERATOR_PRESENT = CV_CAP_OPENNI_IMAGE_GENERATOR + CV_CAP_PROP_OPENNI_GENERATOR_PRESENT, CV_CAP_OPENNI_IMAGE_GENERATOR_OUTPUT_MODE = CV_CAP_OPENNI_IMAGE_GENERATOR + CV_CAP_PROP_OPENNI_OUTPUT_MODE, CV_CAP_OPENNI_DEPTH_GENERATOR_BASELINE = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_BASELINE, CV_CAP_OPENNI_DEPTH_GENERATOR_FOCAL_LENGTH = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_FOCAL_LENGTH, - CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION_ON = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_REGISTRATION_ON, + CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_REGISTRATION, + CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION_ON = CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION, // Properties of cameras available through GStreamer interface CV_CAP_GSTREAMER_QUEUE_LENGTH = 200, // default is 1 diff --git a/modules/highgui/src/cap_openni.cpp b/modules/highgui/src/cap_openni.cpp index bdcb5c09a0..161aee9a28 100644 --- a/modules/highgui/src/cap_openni.cpp +++ b/modules/highgui/src/cap_openni.cpp @@ -45,6 +45,7 @@ #ifdef HAVE_OPENNI #include +#include #include "XnCppWrapper.h" const std::string XMLConfig = @@ -76,12 +77,346 @@ const std::string XMLConfig = "" "\n"; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class ApproximateSyncGrabber +{ +public: + ApproximateSyncGrabber( xn::Context &context, + xn::DepthGenerator &depthGenerator, + xn::ImageGenerator &imageGenerator, + int maxBufferSize, bool isCircleBuffer, int maxTimeDuration ) : + context(context), depthGenerator(depthGenerator), imageGenerator(imageGenerator), + maxBufferSize(maxBufferSize), isCircleBuffer(isCircleBuffer), maxTimeDuration(maxTimeDuration) + { + task = 0; + + CV_Assert( depthGenerator.IsValid() ); + CV_Assert( imageGenerator.IsValid() ); + } + + void setMaxBufferSize( int _maxBufferSize ) + { + maxBufferSize = _maxBufferSize; +#ifdef HAVE_TBB + task->setMaxBufferSize(); +#endif + } + inline int getMaxBufferSize() const { return maxBufferSize; } + + void setIsCircleBuffer( bool _isCircleBuffer ) { isCircleBuffer = _isCircleBuffer; } + bool getIsCircleBuffer() const { return isCircleBuffer; } + + void setMaxTimeDuration( int _maxTimeDuration ) { maxTimeDuration = _maxTimeDuration; } + int getMaxTimeDuration() const { return maxTimeDuration; } + + bool grab( xn::DepthMetaData& depthMetaData, + xn::ImageMetaData& imageMetaData ) + { + CV_Assert( task ); + + + while( task->grab(depthMetaData, imageMetaData) == false ) + { +#ifndef HAVE_TBB + task->spin(); +#endif + } + return true; + + } + + void start() + { + CV_Assert( depthGenerator.IsValid() ); + CV_Assert( imageGenerator.IsValid() ); +#ifdef HAVE_TBB + task = new( tbb::task::allocate_root() ) TBBApproximateSynchronizerTask( *this ); + tbb::task::enqueue(*task); +#else + task = new ApproximateSynchronizer( *this ); +#endif + } + + void finish() + { +#ifdef HAVE_TBB + if( task ) + tbb::task::destroy( *task ); +#else + task.release(); +#endif + } + + bool isRun() const { return task != 0; } + + xn::Context &context; + xn::DepthGenerator &depthGenerator; + xn::ImageGenerator &imageGenerator; + +private: + int maxBufferSize; + bool isCircleBuffer; + int maxTimeDuration; + + class ApproximateSynchronizerBase + { + public: + ApproximateSynchronizerBase( ApproximateSyncGrabber& approxSyncGrabber ) : + approxSyncGrabber(approxSyncGrabber), isDepthFilled(false), isImageFilled(false) + {} + + virtual bool isSpinContinue() const = 0; + virtual void pushDepthMetaData( xn::DepthMetaData& depthMetaData ) = 0; + virtual void pushImageMetaData( xn::ImageMetaData& imageMetaData ) = 0; + virtual bool popDepthMetaData( xn::DepthMetaData& depthMetaData ) = 0; + virtual bool popImageMetaData( xn::ImageMetaData& imageMetaData ) = 0; + + void spin() + { + while(isSpinContinue() == true) + { + XnStatus status = approxSyncGrabber.context.WaitAnyUpdateAll(); + if( status != XN_STATUS_OK ) + continue; + + xn::DepthMetaData depth; + xn::ImageMetaData image; + approxSyncGrabber.depthGenerator.GetMetaData(depth); + approxSyncGrabber.imageGenerator.GetMetaData(image); + + if( depth.Data() && depth.IsDataNew() ) + pushDepthMetaData( depth ); + + if( image.Data() && image.IsDataNew() ) + pushImageMetaData( image ); + } + } + + virtual bool grab( xn::DepthMetaData& depthMetaData, + xn::ImageMetaData& imageMetaData ) + { + while(1) + { + if( !isDepthFilled ) + isDepthFilled = popDepthMetaData(depth); + if( !isImageFilled ) + isImageFilled = popImageMetaData(image); + + if( !isDepthFilled || !isImageFilled ) + break; + + double timeDiff = 1e-3 * std::abs(static_cast(depth.Timestamp()) - static_cast(image.Timestamp())); + + if( timeDiff <= approxSyncGrabber.maxTimeDuration ) + { + depthMetaData.InitFrom(depth); + imageMetaData.InitFrom(image); + isDepthFilled = isImageFilled = false; + return true; + } + else + { + if( depth.Timestamp() < image.Timestamp() ) + isDepthFilled = false; + else + isImageFilled = false; + } + } + + return false; + } + + protected: + ApproximateSyncGrabber& approxSyncGrabber; + xn::DepthMetaData depth; + xn::ImageMetaData image; + bool isDepthFilled; + bool isImageFilled; + }; + + // If there isn't TBB the synchronization will be executed in the main thread. + class ApproximateSynchronizer: public ApproximateSynchronizerBase + { + public: + ApproximateSynchronizer( ApproximateSyncGrabber& approxSyncGrabber ) : + ApproximateSynchronizerBase(approxSyncGrabber) + {} + + virtual bool isSpinContinue() const + { + int maxBufferSize = approxSyncGrabber.getMaxBufferSize(); + return (maxBufferSize <= 0) || (static_cast(depthQueue.size()) < maxBufferSize && + static_cast(imageQueue.size()) < maxBufferSize); // "<" to may push + } + + virtual inline void pushDepthMetaData( xn::DepthMetaData& depthMetaData ) + { + cv::Ptr depthPtr = new xn::DepthMetaData; + depthPtr->CopyFrom(depthMetaData); + depthQueue.push(depthPtr); + } + virtual inline void pushImageMetaData( xn::ImageMetaData& imageMetaData ) + { + cv::Ptr imagePtr = new xn::ImageMetaData; + imagePtr->CopyFrom(imageMetaData); + imageQueue.push(imagePtr); + } + virtual inline bool popDepthMetaData( xn::DepthMetaData& depthMetaData ) + { + if( depthQueue.empty() ) + return false; + + depthMetaData.CopyFrom(*depthQueue.front()); + depthQueue.pop(); + return true; + } + virtual inline bool popImageMetaData( xn::ImageMetaData& imageMetaData ) + { + if( imageQueue.empty() ) + return false; + + imageMetaData.CopyFrom(*imageQueue.front()); + imageQueue.pop(); + return true; + } + + private: + std::queue > depthQueue; + std::queue > imageQueue; + }; + +#ifdef HAVE_TBB + // If there is TBB the synchronization will be executed in own thread. + class TBBApproximateSynchronizer: public ApproximateSynchronizerBase + { + public: + TBBApproximateSynchronizer( ApproximateSyncGrabber& approxSyncGrabber ) : + ApproximateSynchronizerBase(approxSyncGrabber) + { + setMaxBufferSize(); + } + + void setMaxBufferSize() + { + int maxBufferSize = ApproximateSynchronizerBase::approxSyncGrabber.getMaxBufferSize(); + if( maxBufferSize >= 0 ) + { + depthQueue.set_capacity( maxBufferSize ); + imageQueue.set_capacity( maxBufferSize ); + } + } + + virtual inline bool isSpinContinue() const { return true; } + + virtual inline void pushDepthMetaData( xn::DepthMetaData& depthMetaData ) + { + cv::Ptr depthPtr = new xn::DepthMetaData, tmp; + depthPtr->CopyFrom(depthMetaData); + + tbb::mutex mtx; + mtx.lock(); + if( depthQueue.try_push(depthPtr) == false ) + { + if( approxSyncGrabber.getIsCircleBuffer() ) + { + CV_Assert( depthQueue.try_pop(tmp) ); + CV_Assert( depthQueue.try_push(depthPtr) ); + } + } + mtx.unlock(); + } + + virtual inline void pushImageMetaData( xn::ImageMetaData& imageMetaData ) + { + cv::Ptr imagePtr = new xn::ImageMetaData, tmp; + imagePtr->CopyFrom(imageMetaData); + + tbb::mutex mtx; + mtx.lock(); + if( imageQueue.try_push(imagePtr) == false ) + { + if( approxSyncGrabber.getIsCircleBuffer() ) + { + CV_Assert( imageQueue.try_pop(tmp) ); + CV_Assert( imageQueue.try_push(imagePtr) ); + } + } + mtx.unlock(); + } + + virtual inline bool popDepthMetaData( xn::DepthMetaData& depthMetaData ) + { + cv::Ptr depthPtr; + bool isPop = depthQueue.try_pop(depthPtr); + if( isPop ) + depthMetaData.CopyFrom(*depthPtr); + return isPop; + } + virtual inline bool popImageMetaData( xn::ImageMetaData& imageMetaData ) + { + cv::Ptr imagePtr; + bool isPop = imageQueue.try_pop(imagePtr); + if( isPop ) + imageMetaData.CopyFrom(*imagePtr); + return isPop; + } + + private: + tbb::concurrent_bounded_queue > depthQueue; + tbb::concurrent_bounded_queue > imageQueue; + }; + + class TBBApproximateSynchronizerTask: public tbb::task + { + public: + TBBApproximateSynchronizerTask( ApproximateSyncGrabber& approxSyncGrabber ) : + synchronizer(approxSyncGrabber) + {} + + void setMaxBufferSize() + { + synchronizer.setMaxBufferSize(); + } + + bool grab( xn::DepthMetaData& depthMetaData, + xn::ImageMetaData& imageMetaData ) + { + return synchronizer.grab( depthMetaData, imageMetaData ); + } + + private: + tbb::task* execute() + { + synchronizer.spin(); + return 0; + } + TBBApproximateSynchronizer synchronizer; + }; +#endif // HAVE_TBB + +#ifdef HAVE_TBB + TBBApproximateSynchronizerTask* task; +#else + cv::Ptr task; +#endif +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CvCapture_OpenNI : public CvCapture { public: static const int INVALID_PIXEL_VAL = 0; static const int INVALID_COORDINATE_VAL = 0; + +#ifdef HAVE_TBB + static const int DEFAULT_MAX_BUFFER_SIZE = 8; +#else + static const int DEFAULT_MAX_BUFFER_SIZE = 2; +#endif + static const int DEFAULT_IS_CIRCLE_BUFFER = 1; + static const int DEFAULT_MAX_TIME_DURATION = 20; + CvCapture_OpenNI(int index=0); CvCapture_OpenNI(const char * filename); virtual ~CvCapture_OpenNI(); @@ -119,11 +454,13 @@ protected: bool setDepthGeneratorProperty(int propIdx, double propVal); double getImageGeneratorProperty(int propIdx); bool setImageGeneratorProperty(int propIdx, double propVal); + double getCommonProperty(int propIdx); + bool setCommonProperty(int propIdx, double propVal); // OpenNI context xn::Context context; bool isContextOpened; - + xn::ProductionNode productionNode; // Data generators with its metadata @@ -133,6 +470,10 @@ protected: xn::ImageGenerator imageGenerator; xn::ImageMetaData imageMetaData; + int maxBufferSize, maxTimeDuration; // for approx sync + bool isCircleBuffer; + cv::Ptr approxSyncGrabber; + // Cameras settings: // TODO find in OpenNI function to convert z->disparity and remove fields "baseline" and depthFocalLength_VGA // Distance between IR projector and IR camera (in meters) @@ -176,6 +517,9 @@ CvCapture_OpenNI::CvCapture_OpenNI( int index ) XnStatus status; isContextOpened = false; + maxBufferSize = DEFAULT_MAX_BUFFER_SIZE; + isCircleBuffer = DEFAULT_IS_CIRCLE_BUFFER; + maxTimeDuration = DEFAULT_MAX_TIME_DURATION; // Initialize and configure the context. status = context.Init(); @@ -209,7 +553,8 @@ CvCapture_OpenNI::CvCapture_OpenNI( int index ) return; } - status = context.RunXmlScript( XMLConfig.c_str() ); + xn::ScriptNode scriptNode; + status = context.RunXmlScript( XMLConfig.c_str(), scriptNode ); if( status != XN_STATUS_OK ) { std::cerr << "CvCapture_OpenNI::CvCapture_OpenNI : Failed to run xml script: " @@ -263,9 +608,13 @@ CvCapture_OpenNI::CvCapture_OpenNI( int index ) // Set map output mode. if( depthGenerator.IsValid() ) + { CV_DbgAssert( depthGenerator.SetMapOutputMode(defaultMapOutputMode()) == XN_STATUS_OK ); // xn::DepthGenerator supports VGA only! (Jan 2011) + } if( imageGenerator.IsValid() ) + { CV_DbgAssert( imageGenerator.SetMapOutputMode(defaultMapOutputMode()) == XN_STATUS_OK ); + } // Start generating data. status = context.StartGeneratingAll(); @@ -294,6 +643,9 @@ CvCapture_OpenNI::CvCapture_OpenNI(const char * filename) XnStatus status; isContextOpened = false; + maxBufferSize = DEFAULT_MAX_BUFFER_SIZE; + isCircleBuffer = DEFAULT_IS_CIRCLE_BUFFER; + maxTimeDuration = DEFAULT_MAX_TIME_DURATION; // Initialize and configure the context. status = context.Init(); @@ -330,7 +682,7 @@ CvCapture_OpenNI::CvCapture_OpenNI(const char * filename) CvCapture_OpenNI::~CvCapture_OpenNI() { context.StopGeneratingAll(); - context.Shutdown(); + context.Release(); } bool CvCapture_OpenNI::readCamerasParams() @@ -398,7 +750,7 @@ double CvCapture_OpenNI::getProperty( int propIdx ) } else { - CV_Error( CV_StsError, "Unsupported generator prefix!" ); + propValue = getCommonProperty( purePropIdx ); } } @@ -407,26 +759,131 @@ double CvCapture_OpenNI::getProperty( int propIdx ) bool CvCapture_OpenNI::setProperty( int propIdx, double propValue ) { - bool res = false; + bool isSet = false; if( isOpened() ) { int purePropIdx = propIdx & ~CV_CAP_OPENNI_GENERATORS_MASK; if( (propIdx & CV_CAP_OPENNI_GENERATORS_MASK) == CV_CAP_OPENNI_IMAGE_GENERATOR ) { - res = setImageGeneratorProperty( purePropIdx, propValue ); + isSet = setImageGeneratorProperty( purePropIdx, propValue ); } else if( (propIdx & CV_CAP_OPENNI_GENERATORS_MASK) == CV_CAP_OPENNI_DEPTH_GENERATOR ) { - res = setDepthGeneratorProperty( purePropIdx, propValue ); + isSet = setDepthGeneratorProperty( purePropIdx, propValue ); } else { - CV_Error( CV_StsError, "Unsupported generator prefix!" ); + isSet = setCommonProperty( purePropIdx, propValue ); } } - return res; + return isSet; +} + +double CvCapture_OpenNI::getCommonProperty( int propIdx ) +{ + double propValue = 0; + + switch( propIdx ) + { + // There is a set of properties that correspond to depth generator by default + // (is they are pass without particular generator flag). Two reasons of this: + // 1) We can assume that depth generator is the main one for depth sensor. + // 2) In the initial vertions of OpenNI integration to OpenCV the value of + // flag CV_CAP_OPENNI_DEPTH_GENERATOR was 0 (it isn't zero now). + case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT : + case CV_CAP_PROP_FRAME_WIDTH : + case CV_CAP_PROP_FRAME_HEIGHT : + case CV_CAP_PROP_FPS : + case CV_CAP_PROP_OPENNI_FRAME_MAX_DEPTH : + case CV_CAP_PROP_OPENNI_BASELINE : + case CV_CAP_PROP_OPENNI_FOCAL_LENGTH : + case CV_CAP_PROP_OPENNI_REGISTRATION : + propValue = getDepthGeneratorProperty( propIdx ); + break; + case CV_CAP_PROP_OPENNI_APPROX_FRAME_SYNC : + propValue = !approxSyncGrabber.empty() && approxSyncGrabber->isRun() ? 1. : 0.; + break; + case CV_CAP_PROP_OPENNI_MAX_BUFFER_SIZE : + propValue = maxBufferSize; + break; + case CV_CAP_PROP_OPENNI_CIRCLE_BUFFER : + propValue = isCircleBuffer ? 1. : 0.; + break; + case CV_CAP_PROP_OPENNI_MAX_TIME_DURATION : + propValue = maxTimeDuration; + break; + default : + { + std::stringstream ss; + ss << "Such parameter (propIdx=" << propIdx << ") isn't supported for getting.\n"; + CV_Error( CV_StsBadArg, ss.str().c_str() ); + } + } + + return propValue; +} + +bool CvCapture_OpenNI::setCommonProperty( int propIdx, double propValue ) +{ + bool isSet = false; + + switch( propIdx ) + { + // There is a set of properties that correspond to depth generator by default + // (is they are pass without particular generator flag). + case CV_CAP_PROP_OPENNI_REGISTRATION: + isSet = setDepthGeneratorProperty( propIdx, propValue ); + break; + case CV_CAP_PROP_OPENNI_APPROX_FRAME_SYNC : + if( propValue && depthGenerator.IsValid() && imageGenerator.IsValid() ) + { + // start synchronization + if( approxSyncGrabber.empty() ) + { + approxSyncGrabber = new ApproximateSyncGrabber( context, depthGenerator, imageGenerator, maxBufferSize, isCircleBuffer, maxTimeDuration ); + } + else + { + approxSyncGrabber->finish(); + + // update params + approxSyncGrabber->setMaxBufferSize(maxBufferSize); + approxSyncGrabber->setIsCircleBuffer(isCircleBuffer); + approxSyncGrabber->setMaxTimeDuration(maxTimeDuration); + } + approxSyncGrabber->start(); + } + else if( !propValue && !approxSyncGrabber.empty() ) + { + // finish synchronization + approxSyncGrabber->finish(); + } + break; + case CV_CAP_PROP_OPENNI_MAX_BUFFER_SIZE : + maxBufferSize = cvRound(propValue); + if( !approxSyncGrabber.empty() ) + approxSyncGrabber->setMaxBufferSize(maxBufferSize); + break; + case CV_CAP_PROP_OPENNI_CIRCLE_BUFFER : + if( !approxSyncGrabber.empty() ) + approxSyncGrabber->setIsCircleBuffer(isCircleBuffer); + break; + case CV_CAP_PROP_OPENNI_MAX_TIME_DURATION : + maxTimeDuration = cvRound(propValue); + if( !approxSyncGrabber.empty() ) + approxSyncGrabber->setMaxTimeDuration(maxTimeDuration); + break; + default: + { + std::stringstream ss; + ss << "Such parameter (propIdx=" << propIdx << ") isn't supported for setting.\n"; + CV_Error( CV_StsBadArg, ss.str().c_str() ); + } + } + + return isSet; } double CvCapture_OpenNI::getDepthGeneratorProperty( int propIdx ) @@ -536,7 +993,7 @@ double CvCapture_OpenNI::getImageGeneratorProperty( int propIdx ) { double propValue = 0.; if( !imageGenerator.IsValid() ) - return propValue; + return propValue; XnMapOutputMode mode; switch( propIdx ) @@ -570,10 +1027,10 @@ double CvCapture_OpenNI::getImageGeneratorProperty( int propIdx ) bool CvCapture_OpenNI::setImageGeneratorProperty( int propIdx, double propValue ) { - bool isSet = false; + bool isSet = false; if( !imageGenerator.IsValid() ) - return isSet; - + return isSet; + switch( propIdx ) { case CV_CAP_PROP_OPENNI_OUTPUT_MODE : @@ -624,16 +1081,25 @@ bool CvCapture_OpenNI::grabFrame() if( !isOpened() ) return false; - XnStatus status = context.WaitAndUpdateAll(); - if( status != XN_STATUS_OK ) - return false; + bool isGrabbed = false; + if( !approxSyncGrabber.empty() && approxSyncGrabber->isRun() ) + { + isGrabbed = approxSyncGrabber->grab( depthMetaData, imageMetaData ); + } + else + { + XnStatus status = context.WaitAndUpdateAll(); + if( status != XN_STATUS_OK ) + return false; - if( depthGenerator.IsValid() ) - depthGenerator.GetMetaData( depthMetaData ); - if( imageGenerator.IsValid() ) - imageGenerator.GetMetaData( imageMetaData ); + if( depthGenerator.IsValid() ) + depthGenerator.GetMetaData( depthMetaData ); + if( imageGenerator.IsValid() ) + imageGenerator.GetMetaData( imageMetaData ); + isGrabbed = true; + } - return true; + return isGrabbed; } inline void getDepthMapFromMetaData( const xn::DepthMetaData& depthMetaData, cv::Mat& depthMap, XnUInt64 noSampleValue, XnUInt64 shadowValue ) @@ -656,7 +1122,7 @@ inline void getDepthMapFromMetaData( const xn::DepthMetaData& depthMetaData, cv: IplImage* CvCapture_OpenNI::retrieveDepthMap() { - if( depthMetaData.XRes() <= 0 || depthMetaData.YRes() <= 0 ) + if( !depthMetaData.Data() ) return 0; getDepthMapFromMetaData( depthMetaData, outputMaps[CV_CAP_OPENNI_DEPTH_MAP].mat, noSampleValue, shadowValue ); @@ -666,8 +1132,7 @@ IplImage* CvCapture_OpenNI::retrieveDepthMap() IplImage* CvCapture_OpenNI::retrievePointCloudMap() { - int cols = depthMetaData.XRes(), rows = depthMetaData.YRes(); - if( cols <= 0 || rows <= 0 ) + if( !depthMetaData.Data() ) return 0; cv::Mat depth; @@ -675,6 +1140,7 @@ IplImage* CvCapture_OpenNI::retrievePointCloudMap() const int badPoint = INVALID_PIXEL_VAL; const float badCoord = INVALID_COORDINATE_VAL; + int cols = depthMetaData.XRes(), rows = depthMetaData.YRes(); cv::Mat pointCloud_XYZ( rows, cols, CV_32FC3, cv::Scalar::all(badPoint) ); cv::Ptr proj = new XnPoint3D[cols*rows]; @@ -738,7 +1204,7 @@ void computeDisparity_32F( const xn::DepthMetaData& depthMetaData, cv::Mat& disp IplImage* CvCapture_OpenNI::retrieveDisparityMap() { - if( depthMetaData.XRes() <= 0 || depthMetaData.YRes() <= 0 ) + if( !depthMetaData.Data() ) return 0; cv::Mat disp32; @@ -751,7 +1217,7 @@ IplImage* CvCapture_OpenNI::retrieveDisparityMap() IplImage* CvCapture_OpenNI::retrieveDisparityMap_32F() { - if( depthMetaData.XRes() <= 0 || depthMetaData.YRes() <= 0 ) + if( !depthMetaData.Data() ) return 0; computeDisparity_32F( depthMetaData, outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].mat, baseline, depthFocalLength_VGA, noSampleValue, shadowValue ); @@ -761,7 +1227,7 @@ IplImage* CvCapture_OpenNI::retrieveDisparityMap_32F() IplImage* CvCapture_OpenNI::retrieveValidDepthMask() { - if( depthMetaData.XRes() <= 0 || depthMetaData.YRes() <= 0 ) + if( !depthMetaData.Data() ) return 0; cv::Mat depth; @@ -787,7 +1253,7 @@ inline void getBGRImageFromMetaData( const xn::ImageMetaData& imageMetaData, cv: IplImage* CvCapture_OpenNI::retrieveBGRImage() { - if( imageMetaData.XRes() <= 0 || imageMetaData.YRes() <= 0 ) + if( !imageMetaData.Data() ) return 0; getBGRImageFromMetaData( imageMetaData, outputMaps[CV_CAP_OPENNI_BGR_IMAGE].mat ); @@ -797,7 +1263,7 @@ IplImage* CvCapture_OpenNI::retrieveBGRImage() IplImage* CvCapture_OpenNI::retrieveGrayImage() { - if( imageMetaData.XRes() <= 0 || imageMetaData.YRes() <= 0 ) + if( !imageMetaData.Data() ) return 0; CV_Assert( imageMetaData.BytesPerPixel() == 3 ); // RGB diff --git a/samples/cpp/openni_capture.cpp b/samples/cpp/openni_capture.cpp index 0428b8c328..366ca13ab6 100644 --- a/samples/cpp/openni_capture.cpp +++ b/samples/cpp/openni_capture.cpp @@ -8,7 +8,7 @@ using namespace std; void help() { - cout << "\nThis program demonstrates usage of depth sensors(Kinect,XtionPRO,...).\n" + cout << "\nThis program demonstrates usage of depth sensors (Kinect, XtionPRO,...).\n" "The user gets some of the supported output images.\n" "\nAll supported output map types:\n" "1.) Data given from depth generator\n"