Path: blob/master/modules/videoio/src/cap_openni.cpp
16344 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/40#include "precomp.hpp"41#include "opencv2/core.hpp"42#include "opencv2/imgproc.hpp"4344#ifdef HAVE_OPENNI4546#include <queue>4748#ifndef i38649# define i386 050#endif51#ifndef __arm__52# define __arm__ 053#endif54#ifndef _ARC55# define _ARC 056#endif57#ifndef __APPLE__58# define __APPLE__ 059#endif6061#include "XnCppWrapper.h"6263const cv::String XMLConfig =64"<OpenNI>"65"<Licenses>"66"<License vendor=\"PrimeSense\" key=\"0KOIk2JeIBYClPWVnMoRKn5cdY4=\"/>"67"</Licenses>"68"<Log writeToConsole=\"false\" writeToFile=\"false\">"69"<LogLevel value=\"3\"/>"70"<Masks>"71"<Mask name=\"ALL\" on=\"true\"/>"72"</Masks>"73"<Dumps>"74"</Dumps>"75"</Log>"76"<ProductionNodes>"77"<Node type=\"Image\" name=\"Image1\" stopOnError=\"false\">"78"<Configuration>"79"<MapOutputMode xRes=\"640\" yRes=\"480\" FPS=\"30\"/>"80"<Mirror on=\"false\"/>"81"</Configuration>"82"</Node> "83"<Node type=\"Depth\" name=\"Depth1\">"84"<Configuration>"85"<MapOutputMode xRes=\"640\" yRes=\"480\" FPS=\"30\"/>"86"<Mirror on=\"false\"/>"87"</Configuration>"88"</Node>"89"</ProductionNodes>"90"</OpenNI>\n";9192///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////93class ApproximateSyncGrabber94{95public:96ApproximateSyncGrabber( xn::Context &_context,97xn::DepthGenerator &_depthGenerator,98xn::ImageGenerator &_imageGenerator,99int _maxBufferSize, bool _isCircleBuffer, int _maxTimeDuration ) :100context(_context), depthGenerator(_depthGenerator), imageGenerator(_imageGenerator),101maxBufferSize(_maxBufferSize), isCircleBuffer(_isCircleBuffer), maxTimeDuration(_maxTimeDuration)102{103104CV_Assert( depthGenerator.IsValid() );105CV_Assert( imageGenerator.IsValid() );106}107108void setMaxBufferSize( int _maxBufferSize )109{110maxBufferSize = _maxBufferSize;111}112inline int getMaxBufferSize() const { return maxBufferSize; }113114void setIsCircleBuffer( bool _isCircleBuffer ) { isCircleBuffer = _isCircleBuffer; }115bool getIsCircleBuffer() const { return isCircleBuffer; }116117void setMaxTimeDuration( int _maxTimeDuration ) { maxTimeDuration = _maxTimeDuration; }118int getMaxTimeDuration() const { return maxTimeDuration; }119120bool grab( xn::DepthMetaData& depthMetaData,121xn::ImageMetaData& imageMetaData )122{123CV_Assert( task );124125126while( task->grab(depthMetaData, imageMetaData) == false )127{128task->spin();129}130return true;131132}133134void start()135{136CV_Assert( depthGenerator.IsValid() );137CV_Assert( imageGenerator.IsValid() );138task.reset( new ApproximateSynchronizer( *this ) );139}140141void finish()142{143task.release();144}145146bool isRun() const { return task != 0; }147148xn::Context &context;149xn::DepthGenerator &depthGenerator;150xn::ImageGenerator &imageGenerator;151152private:153ApproximateSyncGrabber(const ApproximateSyncGrabber&);154ApproximateSyncGrabber& operator=(const ApproximateSyncGrabber&);155156int maxBufferSize;157bool isCircleBuffer;158int maxTimeDuration;159160class ApproximateSynchronizerBase161{162public:163ApproximateSynchronizerBase( ApproximateSyncGrabber& _approxSyncGrabber ) :164approxSyncGrabber(_approxSyncGrabber), isDepthFilled(false), isImageFilled(false)165{}166167virtual ~ApproximateSynchronizerBase() {}168169virtual bool isSpinContinue() const = 0;170virtual void pushDepthMetaData( xn::DepthMetaData& depthMetaData ) = 0;171virtual void pushImageMetaData( xn::ImageMetaData& imageMetaData ) = 0;172virtual bool popDepthMetaData( xn::DepthMetaData& depthMetaData ) = 0;173virtual bool popImageMetaData( xn::ImageMetaData& imageMetaData ) = 0;174175void spin()176{177while(isSpinContinue() == true)178{179XnStatus status = approxSyncGrabber.context.WaitAnyUpdateAll();180if( status != XN_STATUS_OK )181continue;182183//xn::DepthMetaData depth;184//xn::ImageMetaData image;185approxSyncGrabber.depthGenerator.GetMetaData(depth);186approxSyncGrabber.imageGenerator.GetMetaData(image);187188if( depth.Data() && depth.IsDataNew() )189pushDepthMetaData( depth );190191if( image.Data() && image.IsDataNew() )192pushImageMetaData( image );193}194}195196virtual bool grab( xn::DepthMetaData& depthMetaData,197xn::ImageMetaData& imageMetaData )198{199for(;;)200{201if( !isDepthFilled )202isDepthFilled = popDepthMetaData(depth);203if( !isImageFilled )204isImageFilled = popImageMetaData(image);205206if( !isDepthFilled || !isImageFilled )207break;208209double timeDiff = 1e-3 * std::abs(static_cast<double>(depth.Timestamp()) - static_cast<double>(image.Timestamp()));210211if( timeDiff <= approxSyncGrabber.maxTimeDuration )212{213depthMetaData.InitFrom(depth);214imageMetaData.InitFrom(image);215isDepthFilled = isImageFilled = false;216return true;217}218else219{220if( depth.Timestamp() < image.Timestamp() )221isDepthFilled = false;222else223isImageFilled = false;224}225}226227return false;228}229230protected:231ApproximateSyncGrabber& approxSyncGrabber;232xn::DepthMetaData depth;233xn::ImageMetaData image;234bool isDepthFilled;235bool isImageFilled;236};237238// If there isn't TBB the synchronization will be executed in the main thread.239class ApproximateSynchronizer: public ApproximateSynchronizerBase240{241public:242ApproximateSynchronizer( ApproximateSyncGrabber& _approxSyncGrabber ) :243ApproximateSynchronizerBase(_approxSyncGrabber)244{}245246virtual bool isSpinContinue() const CV_OVERRIDE247{248int maxBufferSize = approxSyncGrabber.getMaxBufferSize();249return (maxBufferSize <= 0) || (static_cast<int>(depthQueue.size()) < maxBufferSize &&250static_cast<int>(imageQueue.size()) < maxBufferSize); // "<" to may push251}252253virtual inline void pushDepthMetaData( xn::DepthMetaData& depthMetaData ) CV_OVERRIDE254{255cv::Ptr<xn::DepthMetaData> depthPtr = cv::makePtr<xn::DepthMetaData>();256depthPtr->CopyFrom(depthMetaData);257depthQueue.push(depthPtr);258}259virtual inline void pushImageMetaData( xn::ImageMetaData& imageMetaData ) CV_OVERRIDE260{261cv::Ptr<xn::ImageMetaData> imagePtr = cv::makePtr<xn::ImageMetaData>();262imagePtr->CopyFrom(imageMetaData);263imageQueue.push(imagePtr);264}265virtual inline bool popDepthMetaData( xn::DepthMetaData& depthMetaData ) CV_OVERRIDE266{267if( depthQueue.empty() )268return false;269270depthMetaData.CopyFrom(*depthQueue.front());271depthQueue.pop();272return true;273}274virtual inline bool popImageMetaData( xn::ImageMetaData& imageMetaData ) CV_OVERRIDE275{276if( imageQueue.empty() )277return false;278279imageMetaData.CopyFrom(*imageQueue.front());280imageQueue.pop();281return true;282}283284private:285std::queue<cv::Ptr<xn::DepthMetaData> > depthQueue;286std::queue<cv::Ptr<xn::ImageMetaData> > imageQueue;287};288289cv::Ptr<ApproximateSynchronizer> task;290};291292///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////293class CvCapture_OpenNI : public CvCapture294{295public:296enum { DEVICE_DEFAULT=0, DEVICE_MS_KINECT=0, DEVICE_ASUS_XTION=1, DEVICE_MAX=1 };297298static const int INVALID_PIXEL_VAL = 0;299static const int INVALID_COORDINATE_VAL = 0;300301static const int DEFAULT_MAX_BUFFER_SIZE = 2;302static const int DEFAULT_IS_CIRCLE_BUFFER = 0;303static const int DEFAULT_MAX_TIME_DURATION = 20;304305CvCapture_OpenNI(int index=0);306CvCapture_OpenNI(const char * filename);307virtual ~CvCapture_OpenNI();308309virtual double getProperty(int propIdx) const CV_OVERRIDE;310virtual bool setProperty(int probIdx, double propVal) CV_OVERRIDE;311virtual bool grabFrame() CV_OVERRIDE;312virtual IplImage* retrieveFrame(int outputType) CV_OVERRIDE;313314bool isOpened() const;315316protected:317struct OutputMap318{319public:320cv::Mat mat;321IplImage* getIplImagePtr();322private:323IplImage iplHeader;324};325326static const int outputMapsTypesCount = 7;327328static XnMapOutputMode defaultMapOutputMode();329330IplImage* retrieveDepthMap();331IplImage* retrievePointCloudMap();332IplImage* retrieveDisparityMap();333IplImage* retrieveDisparityMap_32F();334IplImage* retrieveValidDepthMask();335IplImage* retrieveBGRImage();336IplImage* retrieveGrayImage();337338bool readCamerasParams();339340double getDepthGeneratorProperty(int propIdx) const;341bool setDepthGeneratorProperty(int propIdx, double propVal);342double getImageGeneratorProperty(int propIdx) const;343bool setImageGeneratorProperty(int propIdx, double propVal);344double getCommonProperty(int propIdx) const;345bool setCommonProperty(int propIdx, double propVal);346347// OpenNI context348xn::Context context;349bool isContextOpened;350351xn::ProductionNode productionNode;352353// Data generators with its metadata354xn::DepthGenerator depthGenerator;355xn::DepthMetaData depthMetaData;356357xn::ImageGenerator imageGenerator;358xn::ImageMetaData imageMetaData;359360int maxBufferSize, maxTimeDuration; // for approx sync361bool isCircleBuffer;362cv::Ptr<ApproximateSyncGrabber> approxSyncGrabber;363364// Cameras settings:365// TODO find in OpenNI function to convert z->disparity and remove fields "baseline" and depthFocalLength_VGA366// Distance between IR projector and IR camera (in meters)367XnDouble baseline;368// Focal length for the IR camera in VGA resolution (in pixels)369XnUInt64 depthFocalLength_VGA;370371// The value for shadow (occluded pixels)372XnUInt64 shadowValue;373// The value for pixels without a valid disparity measurement374XnUInt64 noSampleValue;375376std::vector<OutputMap> outputMaps;377};378379IplImage* CvCapture_OpenNI::OutputMap::getIplImagePtr()380{381if( mat.empty() )382return 0;383384iplHeader = cvIplImage(mat);385return &iplHeader;386}387388bool CvCapture_OpenNI::isOpened() const389{390return isContextOpened;391}392393XnMapOutputMode CvCapture_OpenNI::defaultMapOutputMode()394{395XnMapOutputMode mode;396mode.nXRes = XN_VGA_X_RES;397mode.nYRes = XN_VGA_Y_RES;398mode.nFPS = 30;399return mode;400}401402CvCapture_OpenNI::CvCapture_OpenNI( int index )403{404int deviceType = DEVICE_DEFAULT;405XnStatus status;406407isContextOpened = false;408maxBufferSize = DEFAULT_MAX_BUFFER_SIZE;409isCircleBuffer = DEFAULT_IS_CIRCLE_BUFFER;410maxTimeDuration = DEFAULT_MAX_TIME_DURATION;411412if( index >= 10 )413{414deviceType = index / 10;415index %= 10;416}417418if( deviceType > DEVICE_MAX )419return;420421// Initialize and configure the context.422status = context.Init();423if( status != XN_STATUS_OK )424{425fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to initialize the context: %s\n", xnGetStatusString(status));426return;427}428429// Find devices430xn::NodeInfoList devicesList;431status = context.EnumerateProductionTrees( XN_NODE_TYPE_DEVICE, NULL, devicesList, 0 );432if( status != XN_STATUS_OK )433{434fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to enumerate production trees: %s\n", xnGetStatusString(status));435return;436}437438// Chose device according to index439xn::NodeInfoList::Iterator it = devicesList.Begin();440for( int i = 0; i < index && it!=devicesList.End(); ++i ) it++;441if ( it == devicesList.End() )442{443fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed device with index %d\n", index);444return;445}446447xn::NodeInfo deviceNode = *it;448status = context.CreateProductionTree( deviceNode, productionNode );449if( status != XN_STATUS_OK )450{451fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to create production tree: %s\n", xnGetStatusString(status));452return;453}454455xn::ScriptNode scriptNode;456status = context.RunXmlScript( XMLConfig.c_str(), scriptNode );457if( status != XN_STATUS_OK )458{459fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to run xml script: %s\n", xnGetStatusString(status));460return;461}462463// Associate generators with context.464// enumerate the nodes to find if depth generator is present465xn::NodeInfoList depthList;466status = context.EnumerateExistingNodes( depthList, XN_NODE_TYPE_DEPTH );467if( status != XN_STATUS_OK )468{469fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to enumerate depth generators: %s\n", xnGetStatusString(status));470return;471}472if( depthList.IsEmpty() )473{474fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : The device doesn't have depth generator. Such devices aren't supported now.\n");475return;476}477status = depthGenerator.Create( context );478if( status != XN_STATUS_OK )479{480fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to create depth generator: %s\n", xnGetStatusString(status));481return;482}483484// enumerate the nodes to find if image generator is present485xn::NodeInfoList imageList;486status = context.EnumerateExistingNodes( imageList, XN_NODE_TYPE_IMAGE );487if( status != XN_STATUS_OK )488{489fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to enumerate image generators: %s\n", xnGetStatusString(status));490return;491}492493if( !imageList.IsEmpty() )494{495status = imageGenerator.Create( context );496if( status != XN_STATUS_OK )497{498fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to create image generator: %s\n", xnGetStatusString(status));499return;500}501}502503// Set map output mode.504if( depthGenerator.IsValid() )505{506CV_DbgAssert( depthGenerator.SetMapOutputMode(defaultMapOutputMode()) == XN_STATUS_OK ); // xn::DepthGenerator supports VGA only! (Jan 2011)507}508if( imageGenerator.IsValid() )509{510CV_DbgAssert( imageGenerator.SetMapOutputMode(defaultMapOutputMode()) == XN_STATUS_OK );511}512513if( deviceType == DEVICE_ASUS_XTION )514{515//ps/asus specific516imageGenerator.SetIntProperty("InputFormat", 1 /*XN_IO_IMAGE_FORMAT_YUV422*/);517imageGenerator.SetPixelFormat(XN_PIXEL_FORMAT_RGB24);518depthGenerator.SetIntProperty("RegistrationType", 1 /*XN_PROCESSING_HARDWARE*/);519}520521// Start generating data.522status = context.StartGeneratingAll();523if( status != XN_STATUS_OK )524{525fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to start generating OpenNI data: %s\n", xnGetStatusString(status));526return;527}528529if( !readCamerasParams() )530{531fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Could not read cameras parameters\n");532return;533}534535outputMaps.resize( outputMapsTypesCount );536537isContextOpened = true;538539setProperty(CV_CAP_PROP_OPENNI_REGISTRATION, 1.0);540}541542CvCapture_OpenNI::CvCapture_OpenNI(const char * filename)543{544XnStatus status;545546isContextOpened = false;547maxBufferSize = DEFAULT_MAX_BUFFER_SIZE;548isCircleBuffer = DEFAULT_IS_CIRCLE_BUFFER;549maxTimeDuration = DEFAULT_MAX_TIME_DURATION;550551// Initialize and configure the context.552status = context.Init();553if( status != XN_STATUS_OK )554{555fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to initialize the context: %s\n", xnGetStatusString(status));556return;557}558559// Open file560status = context.OpenFileRecording( filename, productionNode );561if( status != XN_STATUS_OK )562{563fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Failed to open input file (%s): %s\n", filename, xnGetStatusString(status));564return;565}566567context.FindExistingNode( XN_NODE_TYPE_DEPTH, depthGenerator );568context.FindExistingNode( XN_NODE_TYPE_IMAGE, imageGenerator );569570if( !readCamerasParams() )571{572fprintf(stderr, "CvCapture_OpenNI::CvCapture_OpenNI : Could not read cameras parameters\n");573return;574}575576outputMaps.resize( outputMapsTypesCount );577578isContextOpened = true;579}580581CvCapture_OpenNI::~CvCapture_OpenNI()582{583context.StopGeneratingAll();584context.Release();585}586587bool CvCapture_OpenNI::readCamerasParams()588{589XnDouble pixelSize = 0;590if( depthGenerator.GetRealProperty( "ZPPS", pixelSize ) != XN_STATUS_OK )591{592fprintf(stderr, "CvCapture_OpenNI::readCamerasParams : Could not read pixel size!\n");593return false;594}595596// pixel size @ VGA = pixel size @ SXGA x 2597pixelSize *= 2.0; // in mm598599// focal length of IR camera in pixels for VGA resolution600XnUInt64 zeroPlanDistance; // in mm601if( depthGenerator.GetIntProperty( "ZPD", zeroPlanDistance ) != XN_STATUS_OK )602{603fprintf(stderr, "CvCapture_OpenNI::readCamerasParams : Could not read virtual plane distance!\n");604return false;605}606607if( depthGenerator.GetRealProperty( "LDDIS", baseline ) != XN_STATUS_OK )608{609fprintf(stderr, "CvCapture_OpenNI::readCamerasParams : Could not read base line!\n");610return false;611}612613// baseline from cm -> mm614baseline *= 10;615616// focal length from mm -> pixels (valid for 640x480)617depthFocalLength_VGA = (XnUInt64)((double)zeroPlanDistance / (double)pixelSize);618619if( depthGenerator.GetIntProperty( "ShadowValue", shadowValue ) != XN_STATUS_OK )620{621fprintf(stderr, "CvCapture_OpenNI::readCamerasParams : Could not read property \"ShadowValue\"!\n");622return false;623}624625if( depthGenerator.GetIntProperty("NoSampleValue", noSampleValue ) != XN_STATUS_OK )626{627fprintf(stderr, "CvCapture_OpenNI::readCamerasParams : Could not read property \"NoSampleValue\"!\n");628return false;629}630631return true;632}633634double CvCapture_OpenNI::getProperty( int propIdx ) const635{636double propValue = 0;637638if( isOpened() )639{640int purePropIdx = propIdx & ~CV_CAP_OPENNI_GENERATORS_MASK;641642if( (propIdx & CV_CAP_OPENNI_GENERATORS_MASK) == CV_CAP_OPENNI_IMAGE_GENERATOR )643{644propValue = getImageGeneratorProperty( purePropIdx );645}646else if( (propIdx & CV_CAP_OPENNI_GENERATORS_MASK) == CV_CAP_OPENNI_DEPTH_GENERATOR )647{648propValue = getDepthGeneratorProperty( purePropIdx );649}650else651{652propValue = getCommonProperty( purePropIdx );653}654}655656return propValue;657}658659bool CvCapture_OpenNI::setProperty( int propIdx, double propValue )660{661bool isSet = false;662if( isOpened() )663{664int purePropIdx = propIdx & ~CV_CAP_OPENNI_GENERATORS_MASK;665666if( (propIdx & CV_CAP_OPENNI_GENERATORS_MASK) == CV_CAP_OPENNI_IMAGE_GENERATOR )667{668isSet = setImageGeneratorProperty( purePropIdx, propValue );669}670else if( (propIdx & CV_CAP_OPENNI_GENERATORS_MASK) == CV_CAP_OPENNI_DEPTH_GENERATOR )671{672isSet = setDepthGeneratorProperty( purePropIdx, propValue );673}674else675{676isSet = setCommonProperty( purePropIdx, propValue );677}678}679680return isSet;681}682683double CvCapture_OpenNI::getCommonProperty( int propIdx ) const684{685double propValue = 0;686687switch( propIdx )688{689// There is a set of properties that correspond to depth generator by default690// (is they are pass without particular generator flag). Two reasons of this:691// 1) We can assume that depth generator is the main one for depth sensor.692// 2) In the initial vertions of OpenNI integration to OpenCV the value of693// flag CV_CAP_OPENNI_DEPTH_GENERATOR was 0 (it isn't zero now).694case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT :695case CV_CAP_PROP_FRAME_WIDTH :696case CV_CAP_PROP_FRAME_HEIGHT :697case CV_CAP_PROP_FPS :698case CV_CAP_PROP_OPENNI_FRAME_MAX_DEPTH :699case CV_CAP_PROP_OPENNI_BASELINE :700case CV_CAP_PROP_OPENNI_FOCAL_LENGTH :701case CV_CAP_PROP_OPENNI_REGISTRATION :702propValue = getDepthGeneratorProperty( propIdx );703break;704case CV_CAP_PROP_OPENNI_APPROX_FRAME_SYNC :705propValue = !approxSyncGrabber.empty() && approxSyncGrabber->isRun() ? 1. : 0.;706break;707case CV_CAP_PROP_OPENNI_MAX_BUFFER_SIZE :708propValue = maxBufferSize;709break;710case CV_CAP_PROP_OPENNI_CIRCLE_BUFFER :711propValue = isCircleBuffer ? 1. : 0.;712break;713case CV_CAP_PROP_OPENNI_MAX_TIME_DURATION :714propValue = maxTimeDuration;715break;716default :717CV_Error( CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for getting.\n", propIdx) );718}719720return propValue;721}722723bool CvCapture_OpenNI::setCommonProperty( int propIdx, double propValue )724{725bool isSet = false;726727switch( propIdx )728{729// There is a set of properties that correspond to depth generator by default730// (is they are pass without particular generator flag).731case CV_CAP_PROP_OPENNI_REGISTRATION:732isSet = setDepthGeneratorProperty( propIdx, propValue );733break;734case CV_CAP_PROP_OPENNI_APPROX_FRAME_SYNC :735if( propValue && depthGenerator.IsValid() && imageGenerator.IsValid() )736{737// start synchronization738if( approxSyncGrabber.empty() )739{740approxSyncGrabber.reset(new ApproximateSyncGrabber( context, depthGenerator, imageGenerator, maxBufferSize, isCircleBuffer, maxTimeDuration ));741}742else743{744approxSyncGrabber->finish();745746// update params747approxSyncGrabber->setMaxBufferSize(maxBufferSize);748approxSyncGrabber->setIsCircleBuffer(isCircleBuffer);749approxSyncGrabber->setMaxTimeDuration(maxTimeDuration);750}751approxSyncGrabber->start();752}753else if( !propValue && !approxSyncGrabber.empty() )754{755// finish synchronization756approxSyncGrabber->finish();757}758break;759case CV_CAP_PROP_OPENNI_MAX_BUFFER_SIZE :760maxBufferSize = cvRound(propValue);761if( !approxSyncGrabber.empty() )762approxSyncGrabber->setMaxBufferSize(maxBufferSize);763break;764case CV_CAP_PROP_OPENNI_CIRCLE_BUFFER :765if( !approxSyncGrabber.empty() )766approxSyncGrabber->setIsCircleBuffer(isCircleBuffer);767break;768case CV_CAP_PROP_OPENNI_MAX_TIME_DURATION :769maxTimeDuration = cvRound(propValue);770if( !approxSyncGrabber.empty() )771approxSyncGrabber->setMaxTimeDuration(maxTimeDuration);772break;773default:774CV_Error( CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for setting.\n", propIdx) );775}776777return isSet;778}779780double CvCapture_OpenNI::getDepthGeneratorProperty( int propIdx ) const781{782double propValue = 0;783if( !depthGenerator.IsValid() )784return propValue;785786XnMapOutputMode mode;787788switch( propIdx )789{790case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT :791CV_DbgAssert( depthGenerator.IsValid() );792propValue = 1.;793break;794case CV_CAP_PROP_FRAME_WIDTH :795if( depthGenerator.GetMapOutputMode(mode) == XN_STATUS_OK )796propValue = mode.nXRes;797break;798case CV_CAP_PROP_FRAME_HEIGHT :799if( depthGenerator.GetMapOutputMode(mode) == XN_STATUS_OK )800propValue = mode.nYRes;801break;802case CV_CAP_PROP_FPS :803if( depthGenerator.GetMapOutputMode(mode) == XN_STATUS_OK )804propValue = mode.nFPS;805break;806case CV_CAP_PROP_OPENNI_FRAME_MAX_DEPTH :807propValue = depthGenerator.GetDeviceMaxDepth();808break;809case CV_CAP_PROP_OPENNI_BASELINE :810propValue = baseline;811break;812case CV_CAP_PROP_OPENNI_FOCAL_LENGTH :813propValue = (double)depthFocalLength_VGA;814break;815case CV_CAP_PROP_OPENNI_REGISTRATION :816propValue = depthGenerator.GetAlternativeViewPointCap().IsViewPointAs(const_cast<CvCapture_OpenNI *>(this)->imageGenerator) ? 1.0 : 0.0;817break;818case CV_CAP_PROP_POS_MSEC :819propValue = (double)depthGenerator.GetTimestamp();820break;821case CV_CAP_PROP_POS_FRAMES :822propValue = depthGenerator.GetFrameID();823break;824default :825CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for getting.\n", propIdx) );826}827828return propValue;829}830831bool CvCapture_OpenNI::setDepthGeneratorProperty( int propIdx, double propValue )832{833bool isSet = false;834835CV_Assert( depthGenerator.IsValid() );836837switch( propIdx )838{839case CV_CAP_PROP_OPENNI_REGISTRATION:840{841if( propValue != 0.0 ) // "on"842{843// if there isn't image generator (i.e. ASUS XtionPro doesn't have it)844// then the property isn't available845if( imageGenerator.IsValid() )846{847if( !depthGenerator.GetAlternativeViewPointCap().IsViewPointAs(imageGenerator) )848{849if( depthGenerator.GetAlternativeViewPointCap().IsViewPointSupported(imageGenerator) )850{851XnStatus status = depthGenerator.GetAlternativeViewPointCap().SetViewPoint(imageGenerator);852if( status != XN_STATUS_OK )853fprintf(stderr, "CvCapture_OpenNI::setDepthGeneratorProperty : %s\n", xnGetStatusString(status));854else855isSet = true;856}857else858fprintf(stderr, "CvCapture_OpenNI::setDepthGeneratorProperty : Unsupported viewpoint.\n");859}860else861isSet = true;862}863}864else // "off"865{866XnStatus status = depthGenerator.GetAlternativeViewPointCap().ResetViewPoint();867if( status != XN_STATUS_OK )868fprintf(stderr, "CvCapture_OpenNI::setDepthGeneratorProperty : %s\n", xnGetStatusString(status));869else870isSet = true;871}872}873break;874default:875CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for setting.\n", propIdx) );876}877878return isSet;879}880881double CvCapture_OpenNI::getImageGeneratorProperty( int propIdx ) const882{883double propValue = 0.;884if( !imageGenerator.IsValid() )885return propValue;886887XnMapOutputMode mode;888switch( propIdx )889{890case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT :891CV_DbgAssert( imageGenerator.IsValid() );892propValue = 1.;893break;894case CV_CAP_PROP_FRAME_WIDTH :895if( imageGenerator.GetMapOutputMode(mode) == XN_STATUS_OK )896propValue = mode.nXRes;897break;898case CV_CAP_PROP_FRAME_HEIGHT :899if( imageGenerator.GetMapOutputMode(mode) == XN_STATUS_OK )900propValue = mode.nYRes;901break;902case CV_CAP_PROP_FPS :903if( imageGenerator.GetMapOutputMode(mode) == XN_STATUS_OK )904propValue = mode.nFPS;905break;906case CV_CAP_PROP_POS_MSEC :907propValue = (double)imageGenerator.GetTimestamp();908break;909case CV_CAP_PROP_POS_FRAMES :910propValue = (double)imageGenerator.GetFrameID();911break;912default :913CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.\n", propIdx) );914}915916return propValue;917}918919bool CvCapture_OpenNI::setImageGeneratorProperty( int propIdx, double propValue )920{921bool isSet = false;922if( !imageGenerator.IsValid() )923return isSet;924925switch( propIdx )926{927case CV_CAP_PROP_OPENNI_OUTPUT_MODE :928{929XnMapOutputMode mode;930931switch( cvRound(propValue) )932{933case CV_CAP_OPENNI_VGA_30HZ :934mode.nXRes = XN_VGA_X_RES;935mode.nYRes = XN_VGA_Y_RES;936mode.nFPS = 30;937break;938case CV_CAP_OPENNI_SXGA_15HZ :939mode.nXRes = XN_SXGA_X_RES;940mode.nYRes = XN_SXGA_Y_RES;941mode.nFPS = 15;942break;943case CV_CAP_OPENNI_SXGA_30HZ :944mode.nXRes = XN_SXGA_X_RES;945mode.nYRes = XN_SXGA_Y_RES;946mode.nFPS = 30;947break;948case CV_CAP_OPENNI_QVGA_30HZ :949mode.nXRes = XN_QVGA_X_RES;950mode.nYRes = XN_QVGA_Y_RES;951mode.nFPS = 30;952break;953case CV_CAP_OPENNI_QVGA_60HZ :954mode.nXRes = XN_QVGA_X_RES;955mode.nYRes = XN_QVGA_Y_RES;956mode.nFPS = 60;957break;958default :959CV_Error( CV_StsBadArg, "Unsupported image generator output mode.\n");960}961962XnStatus status = imageGenerator.SetMapOutputMode( mode );963if( status != XN_STATUS_OK )964fprintf(stderr, "CvCapture_OpenNI::setImageGeneratorProperty : %s\n", xnGetStatusString(status));965else966isSet = true;967break;968}969default:970CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.\n", propIdx) );971}972973return isSet;974}975976bool CvCapture_OpenNI::grabFrame()977{978if( !isOpened() )979return false;980981bool isGrabbed = false;982if( !approxSyncGrabber.empty() && approxSyncGrabber->isRun() )983{984isGrabbed = approxSyncGrabber->grab( depthMetaData, imageMetaData );985}986else987{988XnStatus status = context.WaitAndUpdateAll();989if( status != XN_STATUS_OK )990return false;991992if( depthGenerator.IsValid() )993depthGenerator.GetMetaData( depthMetaData );994if( imageGenerator.IsValid() )995imageGenerator.GetMetaData( imageMetaData );996isGrabbed = true;997}998999return isGrabbed;1000}10011002inline void getDepthMapFromMetaData( const xn::DepthMetaData& depthMetaData, cv::Mat& depthMap, XnUInt64 noSampleValue, XnUInt64 shadowValue )1003{1004int cols = depthMetaData.XRes();1005int rows = depthMetaData.YRes();10061007depthMap.create( rows, cols, CV_16UC1 );10081009const XnDepthPixel* pDepthMap = depthMetaData.Data();10101011// CV_Assert( sizeof(unsigned short) == sizeof(XnDepthPixel) );1012memcpy( depthMap.data, pDepthMap, cols*rows*sizeof(XnDepthPixel) );10131014cv::Mat badMask = (depthMap == (double)noSampleValue) | (depthMap == (double)shadowValue) | (depthMap == 0);10151016// mask the pixels with invalid depth1017depthMap.setTo( cv::Scalar::all( CvCapture_OpenNI::INVALID_PIXEL_VAL ), badMask );1018}10191020IplImage* CvCapture_OpenNI::retrieveDepthMap()1021{1022if( !depthMetaData.Data() )1023return 0;10241025getDepthMapFromMetaData( depthMetaData, outputMaps[CV_CAP_OPENNI_DEPTH_MAP].mat, noSampleValue, shadowValue );10261027return outputMaps[CV_CAP_OPENNI_DEPTH_MAP].getIplImagePtr();1028}10291030IplImage* CvCapture_OpenNI::retrievePointCloudMap()1031{1032if( !depthMetaData.Data() )1033return 0;10341035cv::Mat depth;1036getDepthMapFromMetaData( depthMetaData, depth, noSampleValue, shadowValue );10371038const int badPoint = INVALID_PIXEL_VAL;1039const float badCoord = INVALID_COORDINATE_VAL;1040int cols = depthMetaData.XRes(), rows = depthMetaData.YRes();1041cv::Mat pointCloud_XYZ( rows, cols, CV_32FC3, cv::Scalar::all(badPoint) );10421043std::vector<XnPoint3D> proj(cols*rows);1044std::vector<XnPoint3D> real(cols*rows);1045for( int y = 0; y < rows; y++ )1046{1047for( int x = 0; x < cols; x++ )1048{1049int ind = y*cols+x;1050proj[ind].X = (float)x;1051proj[ind].Y = (float)y;1052proj[ind].Z = depth.at<unsigned short>(y, x);1053}1054}1055depthGenerator.ConvertProjectiveToRealWorld(cols*rows, &proj.front(), &real.front());10561057for( int y = 0; y < rows; y++ )1058{1059for( int x = 0; x < cols; x++ )1060{1061// Check for invalid measurements1062if( depth.at<unsigned short>(y, x) == badPoint ) // not valid1063pointCloud_XYZ.at<cv::Point3f>(y,x) = cv::Point3f( badCoord, badCoord, badCoord );1064else1065{1066int ind = y*cols+x;1067pointCloud_XYZ.at<cv::Point3f>(y,x) = cv::Point3f( real[ind].X*0.001f, real[ind].Y*0.001f, real[ind].Z*0.001f); // from mm to meters1068}1069}1070}10711072outputMaps[CV_CAP_OPENNI_POINT_CLOUD_MAP].mat = pointCloud_XYZ;10731074return outputMaps[CV_CAP_OPENNI_POINT_CLOUD_MAP].getIplImagePtr();1075}10761077static void computeDisparity_32F( const xn::DepthMetaData& depthMetaData, cv::Mat& disp, XnDouble baseline, XnUInt64 F,1078XnUInt64 noSampleValue, XnUInt64 shadowValue )1079{1080cv::Mat depth;1081getDepthMapFromMetaData( depthMetaData, depth, noSampleValue, shadowValue );1082CV_Assert( depth.type() == CV_16UC1 );108310841085// disparity = baseline * F / z;10861087float mult = (float)(baseline /*mm*/ * F /*pixels*/);10881089disp.create( depth.size(), CV_32FC1);1090disp = cv::Scalar::all( CvCapture_OpenNI::INVALID_PIXEL_VAL );1091for( int y = 0; y < disp.rows; y++ )1092{1093for( int x = 0; x < disp.cols; x++ )1094{1095unsigned short curDepth = depth.at<unsigned short>(y,x);1096if( curDepth != CvCapture_OpenNI::INVALID_PIXEL_VAL )1097disp.at<float>(y,x) = mult / curDepth;1098}1099}1100}11011102IplImage* CvCapture_OpenNI::retrieveDisparityMap()1103{1104if( !depthMetaData.Data() )1105return 0;11061107cv::Mat disp32;1108computeDisparity_32F( depthMetaData, disp32, baseline, depthFocalLength_VGA, noSampleValue, shadowValue );11091110disp32.convertTo( outputMaps[CV_CAP_OPENNI_DISPARITY_MAP].mat, CV_8UC1 );11111112return outputMaps[CV_CAP_OPENNI_DISPARITY_MAP].getIplImagePtr();1113}11141115IplImage* CvCapture_OpenNI::retrieveDisparityMap_32F()1116{1117if( !depthMetaData.Data() )1118return 0;11191120computeDisparity_32F( depthMetaData, outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].mat, baseline, depthFocalLength_VGA, noSampleValue, shadowValue );11211122return outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].getIplImagePtr();1123}11241125IplImage* CvCapture_OpenNI::retrieveValidDepthMask()1126{1127if( !depthMetaData.Data() )1128return 0;11291130cv::Mat depth;1131getDepthMapFromMetaData( depthMetaData, depth, noSampleValue, shadowValue );11321133outputMaps[CV_CAP_OPENNI_VALID_DEPTH_MASK].mat = depth != CvCapture_OpenNI::INVALID_PIXEL_VAL;11341135return outputMaps[CV_CAP_OPENNI_VALID_DEPTH_MASK].getIplImagePtr();1136}11371138inline void getBGRImageFromMetaData( const xn::ImageMetaData& imageMetaData, cv::Mat& bgrImage )1139{1140if( imageMetaData.PixelFormat() != XN_PIXEL_FORMAT_RGB24 )1141CV_Error( CV_StsUnsupportedFormat, "Unsupported format of grabbed image\n" );11421143cv::Mat rgbImage( imageMetaData.YRes(), imageMetaData.XRes(), CV_8UC3 );1144const XnRGB24Pixel* pRgbImage = imageMetaData.RGB24Data();11451146// CV_Assert( 3*sizeof(uchar) == sizeof(XnRGB24Pixel) );1147memcpy( rgbImage.data, pRgbImage, rgbImage.total()*sizeof(XnRGB24Pixel) );1148cv::cvtColor( rgbImage, bgrImage, CV_RGB2BGR );1149}11501151IplImage* CvCapture_OpenNI::retrieveBGRImage()1152{1153if( !imageMetaData.Data() )1154return 0;11551156getBGRImageFromMetaData( imageMetaData, outputMaps[CV_CAP_OPENNI_BGR_IMAGE].mat );11571158return outputMaps[CV_CAP_OPENNI_BGR_IMAGE].getIplImagePtr();1159}11601161IplImage* CvCapture_OpenNI::retrieveGrayImage()1162{1163if( !imageMetaData.Data() )1164return 0;11651166CV_Assert( imageMetaData.BytesPerPixel() == 3 ); // RGB11671168cv::Mat rgbImage;1169getBGRImageFromMetaData( imageMetaData, rgbImage );1170cv::cvtColor( rgbImage, outputMaps[CV_CAP_OPENNI_GRAY_IMAGE].mat, CV_BGR2GRAY );11711172return outputMaps[CV_CAP_OPENNI_GRAY_IMAGE].getIplImagePtr();1173}11741175IplImage* CvCapture_OpenNI::retrieveFrame( int outputType )1176{1177IplImage* image = 0;1178CV_Assert( outputType < outputMapsTypesCount && outputType >= 0);11791180if( outputType == CV_CAP_OPENNI_DEPTH_MAP )1181{1182image = retrieveDepthMap();1183}1184else if( outputType == CV_CAP_OPENNI_POINT_CLOUD_MAP )1185{1186image = retrievePointCloudMap();1187}1188else if( outputType == CV_CAP_OPENNI_DISPARITY_MAP )1189{1190image = retrieveDisparityMap();1191}1192else if( outputType == CV_CAP_OPENNI_DISPARITY_MAP_32F )1193{1194image = retrieveDisparityMap_32F();1195}1196else if( outputType == CV_CAP_OPENNI_VALID_DEPTH_MASK )1197{1198image = retrieveValidDepthMask();1199}1200else if( outputType == CV_CAP_OPENNI_BGR_IMAGE )1201{1202image = retrieveBGRImage();1203}1204else if( outputType == CV_CAP_OPENNI_GRAY_IMAGE )1205{1206image = retrieveGrayImage();1207}12081209return image;1210}121112121213CvCapture* cvCreateCameraCapture_OpenNI( int index )1214{1215CvCapture_OpenNI* capture = new CvCapture_OpenNI( index );12161217if( capture->isOpened() )1218return capture;12191220delete capture;1221return 0;1222}12231224CvCapture* cvCreateFileCapture_OpenNI( const char* filename )1225{1226CvCapture_OpenNI* capture = new CvCapture_OpenNI( filename );12271228if( capture->isOpened() )1229return capture;12301231delete capture;1232return 0;1233}12341235#endif123612371238