From 07e0f7bf59d3e1b44f069c5005b6b3d81ce4ccf3 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 20 Mar 2013 19:51:49 +0400 Subject: [PATCH] refactored video module; use the new-style algorithms now --- modules/contrib/src/facerec.cpp | 2 +- .../core/include/opencv2/core/internal.hpp | 4 +- modules/gpu/perf/perf_video.cpp | 30 +- modules/gpu/test/test_bgfg.cpp | 14 +- modules/legacy/src/bgfg_gaussmix.cpp | 18 +- modules/ml/src/ml_init.cpp | 2 +- modules/nonfree/src/nonfree_init.cpp | 2 +- modules/python/src2/cv2.cpp | 1 + modules/softcascade/src/softcascade_init.cpp | 4 +- .../include/opencv2/video/background_segm.hpp | 220 ++-- .../video/include/opencv2/video/tracking.hpp | 25 +- modules/video/src/bgfg_gaussmix.cpp | 136 +- modules/video/src/bgfg_gaussmix2.cpp | 269 ++-- modules/video/src/bgfg_gmg.cpp | 193 ++- modules/video/src/camshift.cpp | 362 ++---- modules/video/src/ecc.cpp | 15 - modules/video/src/kalman.cpp | 170 --- modules/video/src/lkpyramid.cpp | 1135 ++--------------- modules/video/src/motempl.cpp | 542 ++++---- modules/video/src/optflowgf.cpp | 15 - modules/video/src/simpleflow.cpp | 34 +- modules/video/src/video_init.cpp | 43 +- .../test/test_backgroundsubtractor_gbh.cpp | 108 +- modules/video/test/test_estimaterigid.cpp | 2 +- samples/cpp/bgfg_gmg.cpp | 9 +- samples/cpp/bgfg_segm.cpp | 6 +- samples/cpp/segment_objects.cpp | 6 +- samples/gpu/performance/tests.cpp | 16 +- 28 files changed, 1113 insertions(+), 2270 deletions(-) diff --git a/modules/contrib/src/facerec.cpp b/modules/contrib/src/facerec.cpp index 9ce082819..fedaeab88 100644 --- a/modules/contrib/src/facerec.cpp +++ b/modules/contrib/src/facerec.cpp @@ -893,7 +893,7 @@ CV_INIT_ALGORITHM(LBPH, "FaceRecognizer.LBPH", bool initModule_contrib() { - Ptr efaces = createEigenfaces(), ffaces = createFisherfaces(), lbph = createLBPH(); + Ptr efaces = createEigenfaces_hidden(), ffaces = createFisherfaces_hidden(), lbph = createLBPH_hidden(); return efaces->info() != 0 && ffaces->info() != 0 && lbph->info() != 0; } diff --git a/modules/core/include/opencv2/core/internal.hpp b/modules/core/include/opencv2/core/internal.hpp index 1faa0944a..d8a623fbb 100644 --- a/modules/core/include/opencv2/core/internal.hpp +++ b/modules/core/include/opencv2/core/internal.hpp @@ -254,14 +254,14 @@ namespace cv } //namespace cv #define CV_INIT_ALGORITHM(classname, algname, memberinit) \ - static ::cv::Algorithm* create##classname() \ + static ::cv::Algorithm* create##classname##_hidden() \ { \ return new classname; \ } \ \ static ::cv::AlgorithmInfo& classname##_info() \ { \ - static ::cv::AlgorithmInfo classname##_info_var(algname, create##classname); \ + static ::cv::AlgorithmInfo classname##_info_var(algname, create##classname##_hidden); \ return classname##_info_var; \ } \ \ diff --git a/modules/gpu/perf/perf_video.cpp b/modules/gpu/perf/perf_video.cpp index 61c2687ca..69dadc983 100644 --- a/modules/gpu/perf/perf_video.cpp +++ b/modules/gpu/perf/perf_video.cpp @@ -614,10 +614,10 @@ PERF_TEST_P(Video_Cn_LearningRate, Video_MOG, } else { - cv::BackgroundSubtractorMOG mog; + cv::Ptr mog = cv::createBackgroundSubtractorMOG(); cv::Mat foreground; - mog(frame, foreground, learningRate); + mog->apply(frame, foreground, learningRate); for (int i = 0; i < 10; ++i) { @@ -635,7 +635,7 @@ PERF_TEST_P(Video_Cn_LearningRate, Video_MOG, } startTimer(); next(); - mog(frame, foreground, learningRate); + mog->apply(frame, foreground, learningRate); stopTimer(); } @@ -709,12 +709,12 @@ PERF_TEST_P(Video_Cn, Video_MOG2, } else { - cv::BackgroundSubtractorMOG2 mog2; - mog2.set("detectShadows", false); + cv::Ptr mog2 = cv::createBackgroundSubtractorMOG2(); + mog2->set("detectShadows", false); cv::Mat foreground; - mog2(frame, foreground); + mog2->apply(frame, foreground); for (int i = 0; i < 10; ++i) { @@ -732,7 +732,7 @@ PERF_TEST_P(Video_Cn, Video_MOG2, } startTimer(); next(); - mog2(frame, foreground); + mog2->apply(frame, foreground); stopTimer(); } @@ -789,7 +789,7 @@ PERF_TEST_P(Video_Cn, Video_MOG2GetBackgroundImage, } else { - cv::BackgroundSubtractorMOG2 mog2; + cv::Ptr mog2 = cv::createBackgroundSubtractorMOG2(); cv::Mat foreground; for (int i = 0; i < 10; ++i) @@ -807,12 +807,12 @@ PERF_TEST_P(Video_Cn, Video_MOG2GetBackgroundImage, cv::swap(temp, frame); } - mog2(frame, foreground); + mog2->apply(frame, foreground); } cv::Mat background; - TEST_CYCLE() mog2.getBackgroundImage(background); + TEST_CYCLE() mog2->getBackgroundImage(background); CPU_SANITY_CHECK(background); } @@ -958,11 +958,11 @@ PERF_TEST_P(Video_Cn_MaxFeatures, Video_GMG, cv::Mat foreground; cv::Mat zeros(frame.size(), CV_8UC1, cv::Scalar::all(0)); - cv::BackgroundSubtractorGMG gmg; - gmg.set("maxFeatures", maxFeatures); - gmg.initialize(frame.size(), 0.0, 255.0); + cv::Ptr gmg = cv::createBackgroundSubtractorGMG(); + gmg->set("maxFeatures", maxFeatures); + //gmg.initialize(frame.size(), 0.0, 255.0); - gmg(frame, foreground); + gmg->apply(frame, foreground); for (int i = 0; i < 150; ++i) { @@ -985,7 +985,7 @@ PERF_TEST_P(Video_Cn_MaxFeatures, Video_GMG, } startTimer(); next(); - gmg(frame, foreground); + gmg->apply(frame, foreground); stopTimer(); } diff --git a/modules/gpu/test/test_bgfg.cpp b/modules/gpu/test/test_bgfg.cpp index ebf0a88af..b56d7ad86 100644 --- a/modules/gpu/test/test_bgfg.cpp +++ b/modules/gpu/test/test_bgfg.cpp @@ -245,8 +245,8 @@ GPU_TEST_P(MOG2, Update) mog2.bShadowDetection = detectShadow; cv::gpu::GpuMat foreground = createMat(frame.size(), CV_8UC1, useRoi); - cv::BackgroundSubtractorMOG2 mog2_gold; - mog2_gold.set("detectShadows", detectShadow); + cv::Ptr mog2_gold = cv::createBackgroundSubtractorMOG2(); + mog2_gold.setDetectShadows(detectShadow); cv::Mat foreground_gold; for (int i = 0; i < 10; ++i) @@ -263,7 +263,7 @@ GPU_TEST_P(MOG2, Update) mog2(loadMat(frame, useRoi), foreground); - mog2_gold(frame, foreground_gold); + mog2_gold->apply(frame, foreground_gold); if (detectShadow) { @@ -290,8 +290,8 @@ GPU_TEST_P(MOG2, getBackgroundImage) mog2.bShadowDetection = detectShadow; cv::gpu::GpuMat foreground; - cv::BackgroundSubtractorMOG2 mog2_gold; - mog2_gold.set("detectShadows", detectShadow); + cv::Ptr mog2_gold = cv::createBackgroundSubtractorMOG2(); + mog2_gold.setDetectShadows(detectShadow); cv::Mat foreground_gold; for (int i = 0; i < 10; ++i) @@ -301,14 +301,14 @@ GPU_TEST_P(MOG2, getBackgroundImage) mog2(loadMat(frame, useRoi), foreground); - mog2_gold(frame, foreground_gold); + mog2_gold->apply(frame, foreground_gold); } cv::gpu::GpuMat background = createMat(frame.size(), frame.type(), useRoi); mog2.getBackgroundImage(background); cv::Mat background_gold; - mog2_gold.getBackgroundImage(background_gold); + mog2_gold->getBackgroundImage(background_gold); ASSERT_MAT_NEAR(background_gold, background, 0); } diff --git a/modules/legacy/src/bgfg_gaussmix.cpp b/modules/legacy/src/bgfg_gaussmix.cpp index f4c65f55c..e678bdfd6 100644 --- a/modules/legacy/src/bgfg_gaussmix.cpp +++ b/modules/legacy/src/bgfg_gaussmix.cpp @@ -50,7 +50,7 @@ icvReleaseGaussianBGModel( CvGaussBGModel** bg_model ) if( *bg_model ) { - delete (cv::BackgroundSubtractorMOG*)((*bg_model)->mog); + delete (cv::Ptr*)((*bg_model)->mog); cvReleaseImage( &(*bg_model)->background ); cvReleaseImage( &(*bg_model)->foreground ); memset( *bg_model, 0, sizeof(**bg_model) ); @@ -65,10 +65,10 @@ icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel* bg_model, doubl { cv::Mat image = cv::cvarrToMat(curr_frame), mask = cv::cvarrToMat(bg_model->foreground); - cv::BackgroundSubtractorMOG* mog = (cv::BackgroundSubtractorMOG*)(bg_model->mog); + cv::Ptr* mog = (cv::Ptr*)(bg_model->mog); CV_Assert(mog != 0); - (*mog)(image, mask, learningRate); + (*mog)->apply(image, mask, learningRate); bg_model->countFrames++; return 0; @@ -105,13 +105,11 @@ cvCreateGaussianBGModel( IplImage* first_frame, CvGaussBGStatModelParams* parame bg_model->params = params; - cv::BackgroundSubtractorMOG* mog = - new cv::BackgroundSubtractorMOG(params.win_size, - params.n_gauss, - params.bg_threshold, - params.variance_init); - - bg_model->mog = mog; + cv::Ptr mog = cv::createBackgroundSubtractorMOG(params.win_size, params.n_gauss, + params.bg_threshold); + cv::Ptr* pmog = new cv::Ptr; + *pmog = mog; + bg_model->mog = pmog; CvSize sz = cvGetSize(first_frame); bg_model->background = cvCreateImage(sz, IPL_DEPTH_8U, first_frame->nChannels); diff --git a/modules/ml/src/ml_init.cpp b/modules/ml/src/ml_init.cpp index 276f6f5d4..3d6669c26 100644 --- a/modules/ml/src/ml_init.cpp +++ b/modules/ml/src/ml_init.cpp @@ -56,7 +56,7 @@ CV_INIT_ALGORITHM(EM, "StatModel.EM", bool initModule_ml(void) { - Ptr em = createEM(); + Ptr em = createEM_hidden(); return em->info() != 0; } diff --git a/modules/nonfree/src/nonfree_init.cpp b/modules/nonfree/src/nonfree_init.cpp index 3c530e325..827853cfd 100644 --- a/modules/nonfree/src/nonfree_init.cpp +++ b/modules/nonfree/src/nonfree_init.cpp @@ -67,7 +67,7 @@ CV_INIT_ALGORITHM(SIFT, "Feature2D.SIFT", bool initModule_nonfree(void) { - Ptr sift = createSIFT(), surf = createSURF(); + Ptr sift = createSIFT_hidden(), surf = createSURF_hidden(); return sift->info() != 0 && surf->info() != 0; } diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index bbdd5abbe..8f4e3ee4f 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -125,6 +125,7 @@ typedef Ptr Ptr_FeatureDetector; typedef Ptr Ptr_DescriptorExtractor; typedef Ptr Ptr_Feature2D; typedef Ptr Ptr_DescriptorMatcher; +typedef Ptr Ptr_BackgroundSubtractor; typedef Ptr Ptr_ChannelFeatureBuilder; diff --git a/modules/softcascade/src/softcascade_init.cpp b/modules/softcascade/src/softcascade_init.cpp index 9563ac629..a30f936e6 100644 --- a/modules/softcascade/src/softcascade_init.cpp +++ b/modules/softcascade/src/softcascade_init.cpp @@ -58,8 +58,8 @@ CV_INIT_ALGORITHM(SCascade, "CascadeDetector.SCascade", bool initModule_softcascade(void) { - Ptr sc = createSCascade(); - Ptr sc1 = createDetector(); + Ptr sc = createSCascade_hidden(); + Ptr sc1 = createDetector_hidden(); return (sc1->info() != 0) && (sc->info() != 0); } diff --git a/modules/video/include/opencv2/video/background_segm.hpp b/modules/video/include/opencv2/video/background_segm.hpp index 91e9900aa..fe90ed8e4 100644 --- a/modules/video/include/opencv2/video/background_segm.hpp +++ b/modules/video/include/opencv2/video/background_segm.hpp @@ -12,6 +12,7 @@ // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -54,17 +55,14 @@ namespace cv The class is only used to define the common interface for the whole family of background/foreground segmentation algorithms. */ -class CV_EXPORTS_W BackgroundSubtractor : public Algorithm +class BackgroundSubtractor : public Algorithm { public: - //! the virtual destructor - virtual ~BackgroundSubtractor(); //! the update operator that takes the next video frame and returns the current foreground mask as 8-bit binary image. - CV_WRAP_AS(apply) virtual void operator()(InputArray image, OutputArray fgmask, - double learningRate=0); + virtual void apply(InputArray image, OutputArray fgmask, double learningRate=0) = 0; //! computes a background image - virtual void getBackgroundImage(OutputArray backgroundImage) const; + virtual void getBackgroundImage(OutputArray backgroundImage) const = 0; }; @@ -78,35 +76,26 @@ public: http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf */ -class CV_EXPORTS_W BackgroundSubtractorMOG : public BackgroundSubtractor +class BackgroundSubtractorMOG : public BackgroundSubtractor { public: - //! the default constructor - CV_WRAP BackgroundSubtractorMOG(); - //! the full constructor that takes the length of the history, the number of gaussian mixtures, the background ratio parameter and the noise strength - CV_WRAP BackgroundSubtractorMOG(int history, int nmixtures, double backgroundRatio, double noiseSigma=0); - //! the destructor - virtual ~BackgroundSubtractorMOG(); - //! the update operator - virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=0); + virtual int getHistory() const = 0; + virtual void setHistory(int nframes) = 0; - //! re-initiaization method - virtual void initialize(Size frameSize, int frameType); + virtual int getNMixtures() const = 0; + virtual void setNMixtures(int nmix) = 0; - virtual AlgorithmInfo* info() const; + virtual double getBackgroundRatio() const = 0; + virtual void setBackgroundRatio(double backgroundRatio) = 0; -protected: - Size frameSize; - int frameType; - Mat bgmodel; - int nframes; - int history; - int nmixtures; - double varThreshold; - double backgroundRatio; - double noiseSigma; + virtual double getNoiseSigma() const = 0; + virtual void setNoiseSigma(double noiseSigma) = 0; }; +CV_EXPORTS Ptr + createBackgroundSubtractorMOG(int history=200, int nmixtures=5, + double backgroundRatio=0.7, double noiseSigma=0); + /*! The class implements the following algorithm: @@ -114,82 +103,51 @@ protected: Z.Zivkovic International Conference Pattern Recognition, UK, August, 2004. http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf -*/ -class CV_EXPORTS BackgroundSubtractorMOG2 : public BackgroundSubtractor + */ +class BackgroundSubtractorMOG2 : public BackgroundSubtractor { public: - //! the default constructor - BackgroundSubtractorMOG2(); - //! the full constructor that takes the length of the history, the number of gaussian mixtures, the background ratio parameter and the noise strength - BackgroundSubtractorMOG2(int history, float varThreshold, bool bShadowDetection=true); - //! the destructor - virtual ~BackgroundSubtractorMOG2(); - //! the update operator - virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1); + virtual int getHistory() const = 0; + virtual void setHistory(int nframes) = 0; - //! computes a background image which are the mean of all background gaussians - virtual void getBackgroundImage(OutputArray backgroundImage) const; + virtual int getNMixtures() const = 0; + virtual void setNMixtures(int nmix) = 0; - //! re-initiaization method - virtual void initialize(Size frameSize, int frameType); + virtual double getBackgroundRatio() const = 0; + virtual void setBackgroundRatio(double backgroundRatio) = 0; - virtual AlgorithmInfo* info() const; + virtual double getVarThreshold() const = 0; + virtual void setVarThreshold(double varThreshold) = 0; -protected: - Size frameSize; - int frameType; - Mat bgmodel; - Mat bgmodelUsedModes;//keep track of number of modes per pixel - int nframes; - int history; - int nmixtures; - //! here it is the maximum allowed number of mixture components. - //! Actual number is determined dynamically per pixel - double varThreshold; - // threshold on the squared Mahalanobis distance to decide if it is well described - // by the background model or not. Related to Cthr from the paper. - // This does not influence the update of the background. A typical value could be 4 sigma - // and that is varThreshold=4*4=16; Corresponds to Tb in the paper. + virtual double getVarThresholdGen() const = 0; + virtual void setVarThresholdGen(double varThresholdGen) = 0; - ///////////////////////// - // less important parameters - things you might change but be carefull - //////////////////////// - float backgroundRatio; - // corresponds to fTB=1-cf from the paper - // TB - threshold when the component becomes significant enough to be included into - // the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0. - // For alpha=0.001 it means that the mode should exist for approximately 105 frames before - // it is considered foreground - // float noiseSigma; - float varThresholdGen; - //correspondts to Tg - threshold on the squared Mahalan. dist. to decide - //when a sample is close to the existing components. If it is not close - //to any a new component will be generated. I use 3 sigma => Tg=3*3=9. - //Smaller Tg leads to more generated components and higher Tg might make - //lead to small number of components but they can grow too large - float fVarInit; - float fVarMin; - float fVarMax; - //initial variance for the newly generated components. - //It will will influence the speed of adaptation. A good guess should be made. - //A simple way is to estimate the typical standard deviation from the images. - //I used here 10 as a reasonable value - // min and max can be used to further control the variance - float fCT;//CT - complexity reduction prior - //this is related to the number of samples needed to accept that a component - //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get - //the standard Stauffer&Grimson algorithm (maybe not exact but very similar) + virtual double getVarInit() const = 0; + virtual void setVarInit(double varInit) = 0; - //shadow detection parameters - bool bShadowDetection;//default 1 - do shadow detection - unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value - float fTau; - // Tau - shadow threshold. The shadow is detected if the pixel is darker - //version of the background. Tau is a threshold on how much darker the shadow can be. - //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow - //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. + virtual double getVarMin() const = 0; + virtual void setVarMin(double varMin) = 0; + + virtual double getVarMax() const = 0; + virtual void setVarMax(double varMax) = 0; + + virtual double getComplexityReductionThreshold() const = 0; + virtual void setComplexityReductionThreshold(double ct) = 0; + + virtual bool getDetectShadows() const = 0; + virtual void setDetectShadows(bool detectshadows) = 0; + + virtual int getShadowValue() const = 0; + virtual void setShadowValue(int value) = 0; + + virtual double getShadowThreshold() const = 0; + virtual void setShadowThreshold(double value) = 0; }; +CV_EXPORTS Ptr + createBackgroundSubtractorMOG2(int history=500, double varThreshold=16, + bool detectShadows=true); + /** * Background Subtractor module. Takes a series of images and returns a sequence of mask (8UC1) * images of the same size, where 255 indicates Foreground and 0 represents Background. @@ -197,66 +155,44 @@ protected: * Variable-Lighting Conditions for a Responsive Audio Art Installation," A. Godbehere, * A. Matsukawa, K. Goldberg, American Control Conference, Montreal, June 2012. */ -class CV_EXPORTS BackgroundSubtractorGMG: public cv::BackgroundSubtractor +class BackgroundSubtractorGMG : public BackgroundSubtractor { public: - BackgroundSubtractorGMG(); - virtual ~BackgroundSubtractorGMG(); - virtual AlgorithmInfo* info() const; + virtual int getMaxFeatures() const = 0; + virtual void setMaxFeatures(int maxFeatures) = 0; - /** - * Validate parameters and set up data structures for appropriate image size. - * Must call before running on data. - * @param frameSize input frame size - * @param min minimum value taken on by pixels in image sequence. Usually 0 - * @param max maximum value taken on by pixels in image sequence. e.g. 1.0 or 255 - */ - void initialize(cv::Size frameSize, double min, double max); + virtual double getDefaultLearningRate() const = 0; + virtual void setDefaultLearningRate(double lr) = 0; - /** - * Performs single-frame background subtraction and builds up a statistical background image - * model. - * @param image Input image - * @param fgmask Output mask image representing foreground and background pixels - */ - virtual void operator()(InputArray image, OutputArray fgmask, double learningRate=-1.0); + virtual int getNumFrames() const = 0; + virtual void setNumFrames(int nframes) = 0; - /** - * Releases all inner buffers. - */ - void release(); + virtual int getQuantizationLevels() const = 0; + virtual void setQuantizationLevels(int nlevels) = 0; - //! Total number of distinct colors to maintain in histogram. - int maxFeatures; - //! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms. - double learningRate; - //! Number of frames of video to use to initialize histograms. - int numInitializationFrames; - //! Number of discrete levels in each channel to be used in histograms. - int quantizationLevels; - //! Prior probability that any given pixel is a background pixel. A sensitivity parameter. - double backgroundPrior; - //! Value above which pixel is determined to be FG. - double decisionThreshold; - //! Smoothing radius, in pixels, for cleaning up FG image. - int smoothingRadius; - //! Perform background model update - bool updateBackgroundModel; + virtual double getBackgroundPrior() const = 0; + virtual void setBackgroundPrior(double bgprior) = 0; -private: - double maxVal_; - double minVal_; + virtual int getSmoothingRadius() const = 0; + virtual void setSmoothingRadius(int radius) = 0; - cv::Size frameSize_; - int frameNum_; + virtual double getDecisionThreshold() const = 0; + virtual void setDecisionThreshold(double thresh) = 0; - cv::Mat_ nfeatures_; - cv::Mat_ colors_; - cv::Mat_ weights_; + virtual bool getUpdateBackgroundModel() const = 0; + virtual void setUpdateBackgroundModel(bool update) = 0; - cv::Mat buf_; + virtual double getMinVal() const = 0; + virtual void setMinVal(double val) = 0; + + virtual double getMaxVal() const = 0; + virtual void setMaxVal(double val) = 0; }; + +CV_EXPORTS Ptr createBackgroundSubtractorGMG(int initializationFrames=120, + double decisionThreshold=0.8); + } #endif diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index ed322ef77..01f80c2de 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -219,23 +219,6 @@ CVAPI(const CvMat*) cvKalmanCorrect( CvKalman* kalman, const CvMat* measurement #define cvKalmanUpdateByMeasurement cvKalmanCorrect -/****************************************************************************************\ -* Image Alignment (ECC algorithm) * -\****************************************************************************************/ -enum -{ - MOTION_TRANSLATION, - MOTION_EUCLIDEAN, - MOTION_AFFINE, - MOTION_HOMOGRAPHY -}; - -/* Estimate the geometric transformation between 2 images (area-based alignment) */ -CVAPI(double) cvFindTransformECC (const CvArr* templateImage, const CvArr* inputImage, - CvMat* warpMatrix, - const int motionType, - const CvTermCriteria criteria); - #ifdef __cplusplus } @@ -341,6 +324,14 @@ CV_EXPORTS_W void calcOpticalFlowFarneback( InputArray prev, InputArray next, CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, bool fullAffine); +enum +{ + MOTION_TRANSLATION=0, + MOTION_EUCLIDEAN=1, + MOTION_AFFINE=2, + MOTION_HOMOGRAPHY=3 +}; + //! estimates the best-fit Translation, Euclidean, Affine or Perspective Transformation // with respect to Enhanced Correlation Coefficient criterion that maps one image to // another (area-based alignment) diff --git a/modules/video/src/bgfg_gaussmix.cpp b/modules/video/src/bgfg_gaussmix.cpp index be3732be5..43e1d1ce7 100644 --- a/modules/video/src/bgfg_gaussmix.cpp +++ b/modules/video/src/bgfg_gaussmix.cpp @@ -7,9 +7,11 @@ // copy or use the software. // // -// Intel License Agreement +// License Agreement +// For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -22,7 +24,7 @@ // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // -// * The name of Intel Corporation may not be used to endorse or promote products +// * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and @@ -58,15 +60,6 @@ namespace cv { -BackgroundSubtractor::~BackgroundSubtractor() {} -void BackgroundSubtractor::operator()(InputArray, OutputArray, double) -{ -} - -void BackgroundSubtractor::getBackgroundImage(OutputArray) const -{ -} - static const int defaultNMixtures = 5; static const int defaultHistory = 200; static const double defaultBackgroundRatio = 0.7; @@ -74,55 +67,88 @@ static const double defaultVarThreshold = 2.5*2.5; static const double defaultNoiseSigma = 30*0.5; static const double defaultInitialWeight = 0.05; -BackgroundSubtractorMOG::BackgroundSubtractorMOG() +class BackgroundSubtractorMOGImpl : public BackgroundSubtractorMOG { - frameSize = Size(0,0); - frameType = 0; +public: + //! the default constructor + BackgroundSubtractorMOGImpl() + { + frameSize = Size(0,0); + frameType = 0; - nframes = 0; - nmixtures = defaultNMixtures; - history = defaultHistory; - varThreshold = defaultVarThreshold; - backgroundRatio = defaultBackgroundRatio; - noiseSigma = defaultNoiseSigma; -} + nframes = 0; + nmixtures = defaultNMixtures; + history = defaultHistory; + varThreshold = defaultVarThreshold; + backgroundRatio = defaultBackgroundRatio; + noiseSigma = defaultNoiseSigma; + } + // the full constructor that takes the length of the history, + // the number of gaussian mixtures, the background ratio parameter and the noise strength + BackgroundSubtractorMOGImpl(int _history, int _nmixtures, double _backgroundRatio, double _noiseSigma=0) + { + frameSize = Size(0,0); + frameType = 0; -BackgroundSubtractorMOG::BackgroundSubtractorMOG(int _history, int _nmixtures, - double _backgroundRatio, - double _noiseSigma) -{ - frameSize = Size(0,0); - frameType = 0; + nframes = 0; + nmixtures = std::min(_nmixtures > 0 ? _nmixtures : defaultNMixtures, 8); + history = _history > 0 ? _history : defaultHistory; + varThreshold = defaultVarThreshold; + backgroundRatio = std::min(_backgroundRatio > 0 ? _backgroundRatio : 0.95, 1.); + noiseSigma = _noiseSigma <= 0 ? defaultNoiseSigma : _noiseSigma; + } - nframes = 0; - nmixtures = std::min(_nmixtures > 0 ? _nmixtures : defaultNMixtures, 8); - history = _history > 0 ? _history : defaultHistory; - varThreshold = defaultVarThreshold; - backgroundRatio = std::min(_backgroundRatio > 0 ? _backgroundRatio : 0.95, 1.); - noiseSigma = _noiseSigma <= 0 ? defaultNoiseSigma : _noiseSigma; -} + //! the update operator + virtual void apply(InputArray image, OutputArray fgmask, double learningRate=0); -BackgroundSubtractorMOG::~BackgroundSubtractorMOG() -{ -} + //! re-initiaization method + virtual void initialize(Size _frameSize, int _frameType) + { + frameSize = _frameSize; + frameType = _frameType; + nframes = 0; + int nchannels = CV_MAT_CN(frameType); + CV_Assert( CV_MAT_DEPTH(frameType) == CV_8U ); -void BackgroundSubtractorMOG::initialize(Size _frameSize, int _frameType) -{ - frameSize = _frameSize; - frameType = _frameType; - nframes = 0; + // for each gaussian mixture of each pixel bg model we store ... + // the mixture sort key (w/sum_of_variances), the mixture weight (w), + // the mean (nchannels values) and + // the diagonal covariance matrix (another nchannels values) + bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + 2*nchannels), CV_32F ); + bgmodel = Scalar::all(0); + } - int nchannels = CV_MAT_CN(frameType); - CV_Assert( CV_MAT_DEPTH(frameType) == CV_8U ); + virtual AlgorithmInfo* info() const { return 0; } - // for each gaussian mixture of each pixel bg model we store ... - // the mixture sort key (w/sum_of_variances), the mixture weight (w), - // the mean (nchannels values) and - // the diagonal covariance matrix (another nchannels values) - bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + 2*nchannels), CV_32F ); - bgmodel = Scalar::all(0); -} + virtual void getBackgroundImage(OutputArray) const + { + CV_Error( CV_StsNotImplemented, "" ); + } + + virtual int getHistory() const { return history; } + virtual void setHistory(int _nframes) { history = _nframes; } + + virtual int getNMixtures() const { return nmixtures; } + virtual void setNMixtures(int nmix) { nmixtures = nmix; } + + virtual double getBackgroundRatio() const { return backgroundRatio; } + virtual void setBackgroundRatio(double _backgroundRatio) { backgroundRatio = _backgroundRatio; } + + virtual double getNoiseSigma() const { return noiseSigma; } + virtual void setNoiseSigma(double _noiseSigma) { noiseSigma = _noiseSigma; } + +protected: + Size frameSize; + int frameType; + Mat bgmodel; + int nframes; + int history; + int nmixtures; + double varThreshold; + double backgroundRatio; + double noiseSigma; +}; template struct MixData @@ -391,7 +417,7 @@ static void process8uC3( const Mat& image, Mat& fgmask, double learningRate, } } -void BackgroundSubtractorMOG::operator()(InputArray _image, OutputArray _fgmask, double learningRate) +void BackgroundSubtractorMOGImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate) { Mat image = _image.getMat(); bool needToInitialize = nframes == 0 || learningRate >= 1 || image.size() != frameSize || image.type() != frameType; @@ -415,6 +441,12 @@ void BackgroundSubtractorMOG::operator()(InputArray _image, OutputArray _fgmask, CV_Error( CV_StsUnsupportedFormat, "Only 1- and 3-channel 8-bit images are supported in BackgroundSubtractorMOG" ); } +Ptr createBackgroundSubtractorMOG(int history, int nmixtures, + double backgroundRatio, double noiseSigma) +{ + return new BackgroundSubtractorMOGImpl(history, nmixtures, backgroundRatio, noiseSigma); +} + } /* End of file. */ diff --git a/modules/video/src/bgfg_gaussmix2.cpp b/modules/video/src/bgfg_gaussmix2.cpp index e764348b4..00857a6f0 100644 --- a/modules/video/src/bgfg_gaussmix2.cpp +++ b/modules/video/src/bgfg_gaussmix2.cpp @@ -7,9 +7,11 @@ // copy or use the software. // // -// Intel License Agreement +// License Agreement +// For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -22,7 +24,7 @@ // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // -// * The name of Intel Corporation may not be used to endorse or promote products +// * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and @@ -114,6 +116,176 @@ static const float defaultfCT2 = 0.05f; // complexity reduction prior constant 0 static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation + +class CV_EXPORTS BackgroundSubtractorMOG2Impl : public BackgroundSubtractorMOG2 +{ +public: + //! the default constructor + BackgroundSubtractorMOG2Impl() + { + frameSize = Size(0,0); + frameType = 0; + + nframes = 0; + history = defaultHistory2; + varThreshold = defaultVarThreshold2; + bShadowDetection = 1; + + nmixtures = defaultNMixtures2; + backgroundRatio = defaultBackgroundRatio2; + fVarInit = defaultVarInit2; + fVarMax = defaultVarMax2; + fVarMin = defaultVarMin2; + + varThresholdGen = defaultVarThresholdGen2; + fCT = defaultfCT2; + nShadowDetection = defaultnShadowDetection2; + fTau = defaultfTau; + } + //! the full constructor that takes the length of the history, + // the number of gaussian mixtures, the background ratio parameter and the noise strength + BackgroundSubtractorMOG2Impl(int _history, float _varThreshold, bool _bShadowDetection=true) + { + frameSize = Size(0,0); + frameType = 0; + + nframes = 0; + history = _history > 0 ? _history : defaultHistory2; + varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2; + bShadowDetection = _bShadowDetection; + + nmixtures = defaultNMixtures2; + backgroundRatio = defaultBackgroundRatio2; + fVarInit = defaultVarInit2; + fVarMax = defaultVarMax2; + fVarMin = defaultVarMin2; + + varThresholdGen = defaultVarThresholdGen2; + fCT = defaultfCT2; + nShadowDetection = defaultnShadowDetection2; + fTau = defaultfTau; + } + //! the destructor + ~BackgroundSubtractorMOG2Impl() {} + //! the update operator + void apply(InputArray image, OutputArray fgmask, double learningRate=-1); + + //! computes a background image which are the mean of all background gaussians + virtual void getBackgroundImage(OutputArray backgroundImage) const; + + //! re-initiaization method + void initialize(Size _frameSize, int _frameType) + { + frameSize = _frameSize; + frameType = _frameType; + nframes = 0; + + int nchannels = CV_MAT_CN(frameType); + CV_Assert( nchannels <= CV_CN_MAX ); + + // for each gaussian mixture of each pixel bg model we store ... + // the mixture weight (w), + // the mean (nchannels values) and + // the covariance + bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F ); + //make the array for keeping track of the used modes per pixel - all zeros at start + bgmodelUsedModes.create(frameSize,CV_8U); + bgmodelUsedModes = Scalar::all(0); + } + + virtual AlgorithmInfo* info() const { return 0; } + + virtual int getHistory() const { return history; } + virtual void setHistory(int _nframes) { history = _nframes; } + + virtual int getNMixtures() const { return nmixtures; } + virtual void setNMixtures(int nmix) { nmixtures = nmix; } + + virtual double getBackgroundRatio() const { return backgroundRatio; } + virtual void setBackgroundRatio(double _backgroundRatio) { backgroundRatio = (float)_backgroundRatio; } + + virtual double getVarThreshold() const { return varThreshold; } + virtual void setVarThreshold(double _varThreshold) { varThreshold = _varThreshold; } + + virtual double getVarThresholdGen() const { return varThresholdGen; } + virtual void setVarThresholdGen(double _varThresholdGen) { varThresholdGen = (float)_varThresholdGen; } + + virtual double getVarInit() const { return fVarInit; } + virtual void setVarInit(double varInit) { fVarInit = (float)varInit; } + + virtual double getVarMin() const { return fVarMin; } + virtual void setVarMin(double varMin) { fVarMin = (float)varMin; } + + virtual double getVarMax() const { return fVarMax; } + virtual void setVarMax(double varMax) { fVarMax = (float)varMax; } + + virtual double getComplexityReductionThreshold() const { return fCT; } + virtual void setComplexityReductionThreshold(double ct) { fCT = (float)ct; } + + virtual bool getDetectShadows() const { return bShadowDetection; } + virtual void setDetectShadows(bool detectshadows) { bShadowDetection = detectshadows; } + + virtual int getShadowValue() const { return nShadowDetection; } + virtual void setShadowValue(int value) { nShadowDetection = (uchar)value; } + + virtual double getShadowThreshold() const { return fTau; } + virtual void setShadowThreshold(double value) { fTau = (float)value; } + +protected: + Size frameSize; + int frameType; + Mat bgmodel; + Mat bgmodelUsedModes;//keep track of number of modes per pixel + int nframes; + int history; + int nmixtures; + //! here it is the maximum allowed number of mixture components. + //! Actual number is determined dynamically per pixel + double varThreshold; + // threshold on the squared Mahalanobis distance to decide if it is well described + // by the background model or not. Related to Cthr from the paper. + // This does not influence the update of the background. A typical value could be 4 sigma + // and that is varThreshold=4*4=16; Corresponds to Tb in the paper. + + ///////////////////////// + // less important parameters - things you might change but be carefull + //////////////////////// + float backgroundRatio; + // corresponds to fTB=1-cf from the paper + // TB - threshold when the component becomes significant enough to be included into + // the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0. + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + // float noiseSigma; + float varThresholdGen; + //correspondts to Tg - threshold on the squared Mahalan. dist. to decide + //when a sample is close to the existing components. If it is not close + //to any a new component will be generated. I use 3 sigma => Tg=3*3=9. + //Smaller Tg leads to more generated components and higher Tg might make + //lead to small number of components but they can grow too large + float fVarInit; + float fVarMin; + float fVarMax; + //initial variance for the newly generated components. + //It will will influence the speed of adaptation. A good guess should be made. + //A simple way is to estimate the typical standard deviation from the images. + //I used here 10 as a reasonable value + // min and max can be used to further control the variance + float fCT;//CT - complexity reduction prior + //this is related to the number of samples needed to accept that a component + //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get + //the standard Stauffer&Grimson algorithm (maybe not exact but very similar) + + //shadow detection parameters + bool bShadowDetection;//default 1 - do shadow detection + unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value + float fTau; + // Tau - shadow threshold. The shadow is detected if the pixel is darker + //version of the background. Tau is a threshold on how much darker the shadow can be. + //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow + //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. +}; + struct GaussBGStatModel2Params { //image info @@ -248,8 +420,9 @@ detectShadowGMM(const float* data, int nchannels, int nmodes, //IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004 //http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf -struct MOG2Invoker +class MOG2Invoker : public ParallelLoopBody { +public: MOG2Invoker(const Mat& _src, Mat& _dst, GMM* _gmm, float* _mean, uchar* _modesUsed, @@ -280,9 +453,9 @@ struct MOG2Invoker cvtfunc = src->depth() != CV_32F ? getConvertFunc(src->depth(), CV_32F) : 0; } - void operator()(const BlockedRange& range) const + void operator()(const Range& range) const { - int y0 = range.begin(), y1 = range.end(); + int y0 = range.start, y1 = range.end; int ncols = src->cols, nchannels = src->channels(); AutoBuffer buf(src->cols*nchannels); float alpha1 = 1.f - alphaT; @@ -479,75 +652,7 @@ struct MOG2Invoker BinaryFunc cvtfunc; }; -BackgroundSubtractorMOG2::BackgroundSubtractorMOG2() -{ - frameSize = Size(0,0); - frameType = 0; - - nframes = 0; - history = defaultHistory2; - varThreshold = defaultVarThreshold2; - bShadowDetection = 1; - - nmixtures = defaultNMixtures2; - backgroundRatio = defaultBackgroundRatio2; - fVarInit = defaultVarInit2; - fVarMax = defaultVarMax2; - fVarMin = defaultVarMin2; - - varThresholdGen = defaultVarThresholdGen2; - fCT = defaultfCT2; - nShadowDetection = defaultnShadowDetection2; - fTau = defaultfTau; -} - -BackgroundSubtractorMOG2::BackgroundSubtractorMOG2(int _history, float _varThreshold, bool _bShadowDetection) -{ - frameSize = Size(0,0); - frameType = 0; - - nframes = 0; - history = _history > 0 ? _history : defaultHistory2; - varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2; - bShadowDetection = _bShadowDetection; - - nmixtures = defaultNMixtures2; - backgroundRatio = defaultBackgroundRatio2; - fVarInit = defaultVarInit2; - fVarMax = defaultVarMax2; - fVarMin = defaultVarMin2; - - varThresholdGen = defaultVarThresholdGen2; - fCT = defaultfCT2; - nShadowDetection = defaultnShadowDetection2; - fTau = defaultfTau; -} - -BackgroundSubtractorMOG2::~BackgroundSubtractorMOG2() -{ -} - - -void BackgroundSubtractorMOG2::initialize(Size _frameSize, int _frameType) -{ - frameSize = _frameSize; - frameType = _frameType; - nframes = 0; - - int nchannels = CV_MAT_CN(frameType); - CV_Assert( nchannels <= CV_CN_MAX ); - - // for each gaussian mixture of each pixel bg model we store ... - // the mixture weight (w), - // the mean (nchannels values) and - // the covariance - bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F ); - //make the array for keeping track of the used modes per pixel - all zeros at start - bgmodelUsedModes.create(frameSize,CV_8U); - bgmodelUsedModes = Scalar::all(0); -} - -void BackgroundSubtractorMOG2::operator()(InputArray _image, OutputArray _fgmask, double learningRate) +void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate) { Mat image = _image.getMat(); bool needToInitialize = nframes == 0 || learningRate >= 1 || image.size() != frameSize || image.type() != frameType; @@ -562,18 +667,19 @@ void BackgroundSubtractorMOG2::operator()(InputArray _image, OutputArray _fgmask learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history ); CV_Assert(learningRate >= 0); - parallel_for(BlockedRange(0, image.rows), - MOG2Invoker(image, fgmask, + parallel_for_(Range(0, image.rows), + MOG2Invoker(image, fgmask, (GMM*)bgmodel.data, (float*)(bgmodel.data + sizeof(GMM)*nmixtures*image.rows*image.cols), bgmodelUsedModes.data, nmixtures, (float)learningRate, (float)varThreshold, backgroundRatio, varThresholdGen, fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau, - bShadowDetection, nShadowDetection)); + bShadowDetection, nShadowDetection), + image.total()/(double)(1 << 16)); } -void BackgroundSubtractorMOG2::getBackgroundImage(OutputArray backgroundImage) const +void BackgroundSubtractorMOG2Impl::getBackgroundImage(OutputArray backgroundImage) const { int nchannels = CV_MAT_CN(frameType); CV_Assert( nchannels == 3 ); @@ -626,6 +732,13 @@ void BackgroundSubtractorMOG2::getBackgroundImage(OutputArray backgroundImage) c } } + +Ptr createBackgroundSubtractorMOG2(int _history, double _varThreshold, + bool _bShadowDetection) +{ + return new BackgroundSubtractorMOG2Impl(_history, (float)_varThreshold, _bShadowDetection); +} + } /* End of file. */ diff --git a/modules/video/src/bgfg_gmg.cpp b/modules/video/src/bgfg_gmg.cpp index 42d6fb169..8a54fa11b 100644 --- a/modules/video/src/bgfg_gmg.cpp +++ b/modules/video/src/bgfg_gmg.cpp @@ -8,8 +8,10 @@ // // // License Agreement +// For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -22,7 +24,7 @@ // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // -// * The name of Intel Corporation may not be used to endorse or promote products +// * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and @@ -48,7 +50,105 @@ #include "precomp.hpp" -cv::BackgroundSubtractorGMG::BackgroundSubtractorGMG() +namespace cv +{ + +class CV_EXPORTS BackgroundSubtractorGMGImpl : public BackgroundSubtractorGMG +{ +public: + BackgroundSubtractorGMGImpl(); + ~BackgroundSubtractorGMGImpl(); + virtual AlgorithmInfo* info() const { return 0; } + + /** + * Validate parameters and set up data structures for appropriate image size. + * Must call before running on data. + * @param frameSize input frame size + * @param min minimum value taken on by pixels in image sequence. Usually 0 + * @param max maximum value taken on by pixels in image sequence. e.g. 1.0 or 255 + */ + void initialize(Size frameSize, double minVal, double maxVal); + + /** + * Performs single-frame background subtraction and builds up a statistical background image + * model. + * @param image Input image + * @param fgmask Output mask image representing foreground and background pixels + */ + virtual void apply(InputArray image, OutputArray fgmask, double learningRate=-1.0); + + /** + * Releases all inner buffers. + */ + void release(); + + virtual int getMaxFeatures() const { return maxFeatures; } + virtual void setMaxFeatures(int _maxFeatures) { maxFeatures = _maxFeatures; } + + virtual double getDefaultLearningRate() const { return learningRate; } + virtual void setDefaultLearningRate(double lr) { learningRate = lr; } + + virtual int getNumFrames() const { return numInitializationFrames; } + virtual void setNumFrames(int nframes) { numInitializationFrames = nframes; } + + virtual int getQuantizationLevels() const { return quantizationLevels; } + virtual void setQuantizationLevels(int nlevels) { quantizationLevels = nlevels; } + + virtual double getBackgroundPrior() const { return backgroundPrior; } + virtual void setBackgroundPrior(double bgprior) { backgroundPrior = bgprior; } + + virtual int getSmoothingRadius() const { return smoothingRadius; } + virtual void setSmoothingRadius(int radius) { smoothingRadius = radius; } + + virtual double getDecisionThreshold() const { return decisionThreshold; } + virtual void setDecisionThreshold(double thresh) { decisionThreshold = thresh; } + + virtual bool getUpdateBackgroundModel() const { return updateBackgroundModel; } + virtual void setUpdateBackgroundModel(bool update) { updateBackgroundModel = update; } + + virtual double getMinVal() const { return minVal_; } + virtual void setMinVal(double val) { minVal_ = val; } + + virtual double getMaxVal() const { return maxVal_; } + virtual void setMaxVal(double val) { maxVal_ = val; } + + virtual void getBackgroundImage(OutputArray) const + { + CV_Error( CV_StsNotImplemented, "" ); + } + + //! Total number of distinct colors to maintain in histogram. + int maxFeatures; + //! Set between 0.0 and 1.0, determines how quickly features are "forgotten" from histograms. + double learningRate; + //! Number of frames of video to use to initialize histograms. + int numInitializationFrames; + //! Number of discrete levels in each channel to be used in histograms. + int quantizationLevels; + //! Prior probability that any given pixel is a background pixel. A sensitivity parameter. + double backgroundPrior; + //! Value above which pixel is determined to be FG. + double decisionThreshold; + //! Smoothing radius, in pixels, for cleaning up FG image. + int smoothingRadius; + //! Perform background model update + bool updateBackgroundModel; + +private: + double maxVal_; + double minVal_; + + Size frameSize_; + int frameNum_; + + Mat_ nfeatures_; + Mat_ colors_; + Mat_ weights_; + + Mat buf_; +}; + +BackgroundSubtractorGMGImpl::BackgroundSubtractorGMGImpl() { /* * Default Parameter Values. Override with algorithm "set" method. @@ -61,23 +161,24 @@ cv::BackgroundSubtractorGMG::BackgroundSubtractorGMG() decisionThreshold = 0.8; smoothingRadius = 7; updateBackgroundModel = true; + minVal_ = maxVal_ = 0; } -cv::BackgroundSubtractorGMG::~BackgroundSubtractorGMG() +BackgroundSubtractorGMGImpl::~BackgroundSubtractorGMGImpl() { } -void cv::BackgroundSubtractorGMG::initialize(cv::Size frameSize, double min, double max) +void BackgroundSubtractorGMGImpl::initialize(Size frameSize, double minVal, double maxVal) { - CV_Assert(min < max); + CV_Assert(minVal < maxVal); CV_Assert(maxFeatures > 0); CV_Assert(learningRate >= 0.0 && learningRate <= 1.0); CV_Assert(numInitializationFrames >= 1); CV_Assert(quantizationLevels >= 1 && quantizationLevels <= 255); CV_Assert(backgroundPrior >= 0.0 && backgroundPrior <= 1.0); - minVal_ = min; - maxVal_ = max; + minVal_ = minVal; + maxVal_ = maxVal; frameSize_ = frameSize; frameNum_ = 0; @@ -86,7 +187,7 @@ void cv::BackgroundSubtractorGMG::initialize(cv::Size frameSize, double min, dou colors_.create(frameSize_.area(), maxFeatures); weights_.create(frameSize_.area(), maxFeatures); - nfeatures_.setTo(cv::Scalar::all(0)); + nfeatures_.setTo(Scalar::all(0)); } namespace @@ -181,10 +282,10 @@ namespace } }; - class GMG_LoopBody : public cv::ParallelLoopBody + class GMG_LoopBody : public ParallelLoopBody { public: - GMG_LoopBody(const cv::Mat& frame, const cv::Mat& fgmask, const cv::Mat_& nfeatures, const cv::Mat_& colors, const cv::Mat_& weights, + GMG_LoopBody(const Mat& frame, const Mat& fgmask, const Mat_& nfeatures, const Mat_& colors, const Mat_& weights, int maxFeatures, double learningRate, int numInitializationFrames, int quantizationLevels, double backgroundPrior, double decisionThreshold, double maxVal, double minVal, int frameNum, bool updateBackgroundModel) : frame_(frame), fgmask_(fgmask), nfeatures_(nfeatures), colors_(colors), weights_(weights), @@ -194,16 +295,16 @@ namespace { } - void operator() (const cv::Range& range) const; + void operator() (const Range& range) const; private: - cv::Mat frame_; + Mat frame_; - mutable cv::Mat_ fgmask_; + mutable Mat_ fgmask_; - mutable cv::Mat_ nfeatures_; - mutable cv::Mat_ colors_; - mutable cv::Mat_ weights_; + mutable Mat_ nfeatures_; + mutable Mat_ colors_; + mutable Mat_ weights_; int maxFeatures_; double learningRate_; @@ -218,7 +319,7 @@ namespace int frameNum_; }; - void GMG_LoopBody::operator() (const cv::Range& range) const + void GMG_LoopBody::operator() (const Range& range) const { typedef unsigned int (*func_t)(const void* src_, int x, int cn, double minVal, double maxVal, int quantizationLevels); static const func_t funcs[] = @@ -296,7 +397,7 @@ namespace } } -void cv::BackgroundSubtractorGMG::operator ()(InputArray _frame, OutputArray _fgmask, double newLearningRate) +void BackgroundSubtractorGMGImpl::apply(InputArray _frame, OutputArray _fgmask, double newLearningRate) { Mat frame = _frame.getMat(); @@ -310,7 +411,16 @@ void cv::BackgroundSubtractorGMG::operator ()(InputArray _frame, OutputArray _fg } if (frame.size() != frameSize_) - initialize(frame.size(), 0.0, frame.depth() == CV_8U ? 255.0 : frame.depth() == CV_16U ? std::numeric_limits::max() : 1.0); + { + double minval = minVal_; + double maxval = maxVal_; + if( minVal_ == 0 && maxVal_ == 0 ) + { + minval = 0; + maxval = frame.depth() == CV_8U ? 255.0 : frame.depth() == CV_16U ? std::numeric_limits::max() : 1.0; + } + initialize(frame.size(), minval, maxval); + } _fgmask.create(frameSize_, CV_8UC1); Mat fgmask = _fgmask.getMat(); @@ -323,19 +433,58 @@ void cv::BackgroundSubtractorGMG::operator ()(InputArray _frame, OutputArray _fg if (smoothingRadius > 0) { medianBlur(fgmask, buf_, smoothingRadius); - cv::swap(fgmask, buf_); + swap(fgmask, buf_); } // keep track of how many frames we have processed ++frameNum_; } -void cv::BackgroundSubtractorGMG::release() +void BackgroundSubtractorGMGImpl::release() { - frameSize_ = cv::Size(); + frameSize_ = Size(); nfeatures_.release(); colors_.release(); weights_.release(); buf_.release(); } + + +Ptr createBackgroundSubtractorGMG(int initializationFrames, double decisionThreshold) +{ + Ptr bgfg = new BackgroundSubtractorGMGImpl; + bgfg->setNumFrames(initializationFrames); + bgfg->setDecisionThreshold(decisionThreshold); + + return bgfg; +} + +/* + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + CV_INIT_ALGORITHM(BackgroundSubtractorGMG, "BackgroundSubtractor.GMG", + obj.info()->addParam(obj, "maxFeatures", obj.maxFeatures,false,0,0, + "Maximum number of features to store in histogram. Harsh enforcement of sparsity constraint."); + obj.info()->addParam(obj, "learningRate", obj.learningRate,false,0,0, + "Adaptation rate of histogram. Close to 1, slow adaptation. Close to 0, fast adaptation, features forgotten quickly."); + obj.info()->addParam(obj, "initializationFrames", obj.numInitializationFrames,false,0,0, + "Number of frames to use to initialize histograms of pixels."); + obj.info()->addParam(obj, "quantizationLevels", obj.quantizationLevels,false,0,0, + "Number of discrete colors to be used in histograms. Up-front quantization."); + obj.info()->addParam(obj, "backgroundPrior", obj.backgroundPrior,false,0,0, + "Prior probability that each individual pixel is a background pixel."); + obj.info()->addParam(obj, "smoothingRadius", obj.smoothingRadius,false,0,0, + "Radius of smoothing kernel to filter noise from FG mask image."); + obj.info()->addParam(obj, "decisionThreshold", obj.decisionThreshold,false,0,0, + "Threshold for FG decision rule. Pixel is FG if posterior probability exceeds threshold."); + obj.info()->addParam(obj, "updateBackgroundModel", obj.updateBackgroundModel,false,0,0, + "Perform background model update."); + obj.info()->addParam(obj, "minVal", obj.minVal_,false,0,0, + "Minimum of the value range (mostly for regression testing)"); + obj.info()->addParam(obj, "maxVal", obj.maxVal_,false,0,0, + "Maximum of the value range (mostly for regression testing)"); + ); +*/ + +} diff --git a/modules/video/src/camshift.cpp b/modules/video/src/camshift.cpp index eadf166c4..5cad1b82a 100644 --- a/modules/video/src/camshift.cpp +++ b/modules/video/src/camshift.cpp @@ -7,10 +7,11 @@ // copy or use the software. // // -// Intel License Agreement +// License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -40,285 +41,156 @@ //M*/ #include "precomp.hpp" - -/*F/////////////////////////////////////////////////////////////////////////////////////// -// Name: cvMeanShift -// Purpose: MeanShift algorithm -// Context: -// Parameters: -// imgProb - 2D object probability distribution -// windowIn - CvRect of CAMSHIFT Window intial size -// numIters - If CAMSHIFT iterates this many times, stop -// windowOut - Location, height and width of converged CAMSHIFT window -// len - If != NULL, return equivalent len -// width - If != NULL, return equivalent width -// Returns: -// Number of iterations CAMSHIFT took to converge -// Notes: -//F*/ -CV_IMPL int -cvMeanShift( const void* imgProb, CvRect windowIn, - CvTermCriteria criteria, CvConnectedComp* comp ) +int cv::meanShift( InputArray _probImage, Rect& window, TermCriteria criteria ) { - CvMoments moments; - int i = 0, eps; - CvMat stub, *mat = (CvMat*)imgProb; - CvMat cur_win; - CvRect cur_rect = windowIn; + Mat mat = _probImage.getMat(); + Rect cur_rect = window; - if( comp ) - comp->rect = windowIn; + CV_Assert( mat.channels() == 1 ); - moments.m00 = moments.m10 = moments.m01 = 0; - - mat = cvGetMat( mat, &stub ); - - if( CV_MAT_CN( mat->type ) > 1 ) - CV_Error( CV_BadNumChannels, cvUnsupportedFormat ); - - if( windowIn.height <= 0 || windowIn.width <= 0 ) + if( window.height <= 0 || window.width <= 0 ) CV_Error( CV_StsBadArg, "Input window has non-positive sizes" ); - windowIn = cv::Rect(windowIn) & cv::Rect(0, 0, mat->cols, mat->rows); + window = window & Rect(0, 0, mat.cols, mat.rows); - criteria = cvCheckTermCriteria( criteria, 1., 100 ); - eps = cvRound( criteria.epsilon * criteria.epsilon ); + double eps = (criteria.type & TermCriteria::EPS) ? std::max(criteria.epsilon, 0.) : 1.; + eps = cvRound(eps*eps); + int i, niters = (criteria.type & TermCriteria::MAX_ITER) ? std::max(criteria.maxCount, 1) : 100; - for( i = 0; i < criteria.max_iter; i++ ) + for( i = 0; i < niters; i++ ) { - int dx, dy, nx, ny; - double inv_m00; - cur_rect = cv::Rect(cur_rect) & cv::Rect(0, 0, mat->cols, mat->rows); - if( cv::Rect(cur_rect) == cv::Rect() ) + cur_rect = cur_rect & Rect(0, 0, mat.cols, mat.rows); + if( cur_rect == Rect() ) { - cur_rect.x = mat->cols/2; - cur_rect.y = mat->rows/2; + cur_rect.x = mat.cols/2; + cur_rect.y = mat.rows/2; } - cur_rect.width = MAX(cur_rect.width, 1); - cur_rect.height = MAX(cur_rect.height, 1); + cur_rect.width = std::max(cur_rect.width, 1); + cur_rect.height = std::max(cur_rect.height, 1); - cvGetSubRect( mat, &cur_win, cur_rect ); - cvMoments( &cur_win, &moments ); + Moments m = moments(mat(cur_rect)); - /* Calculating center of mass */ - if( fabs(moments.m00) < DBL_EPSILON ) + // Calculating center of mass + if( fabs(m.m00) < DBL_EPSILON ) break; - inv_m00 = moments.inv_sqrt_m00*moments.inv_sqrt_m00; - dx = cvRound( moments.m10 * inv_m00 - windowIn.width*0.5 ); - dy = cvRound( moments.m01 * inv_m00 - windowIn.height*0.5 ); + int dx = cvRound( m.m10/m.m00 - window.width*0.5 ); + int dy = cvRound( m.m01/m.m00 - window.height*0.5 ); - nx = cur_rect.x + dx; - ny = cur_rect.y + dy; - - if( nx < 0 ) - nx = 0; - else if( nx + cur_rect.width > mat->cols ) - nx = mat->cols - cur_rect.width; - - if( ny < 0 ) - ny = 0; - else if( ny + cur_rect.height > mat->rows ) - ny = mat->rows - cur_rect.height; + int nx = std::min(std::max(cur_rect.x + dx, 0), mat.cols - cur_rect.width); + int ny = std::min(std::max(cur_rect.y + dy, 0), mat.rows - cur_rect.height); dx = nx - cur_rect.x; dy = ny - cur_rect.y; cur_rect.x = nx; cur_rect.y = ny; - /* Check for coverage centers mass & window */ + // Check for coverage centers mass & window if( dx*dx + dy*dy < eps ) break; } - if( comp ) - { - comp->rect = cur_rect; - comp->area = (float)moments.m00; - } - + window = cur_rect; return i; } -/*F/////////////////////////////////////////////////////////////////////////////////////// -// Name: cvCamShift -// Purpose: CAMSHIFT algorithm -// Context: -// Parameters: -// imgProb - 2D object probability distribution -// windowIn - CvRect of CAMSHIFT Window intial size -// criteria - criteria of stop finding window -// windowOut - Location, height and width of converged CAMSHIFT window -// orientation - If != NULL, return distribution orientation -// len - If != NULL, return equivalent len -// width - If != NULL, return equivalent width -// area - sum of all elements in result window -// Returns: -// Number of iterations CAMSHIFT took to converge -// Notes: -//F*/ -CV_IMPL int -cvCamShift( const void* imgProb, CvRect windowIn, - CvTermCriteria criteria, - CvConnectedComp* _comp, - CvBox2D* box ) -{ - const int TOLERANCE = 10; - CvMoments moments; - double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00; - double a, b, c, xc, yc; - double rotate_a, rotate_c; - double theta = 0, square; - double cs, sn; - double length = 0, width = 0; - int itersUsed = 0; - CvConnectedComp comp; - CvMat cur_win, stub, *mat = (CvMat*)imgProb; - - comp.rect = windowIn; - - mat = cvGetMat( mat, &stub ); - - itersUsed = cvMeanShift( mat, windowIn, criteria, &comp ); - windowIn = comp.rect; - - windowIn.x -= TOLERANCE; - if( windowIn.x < 0 ) - windowIn.x = 0; - - windowIn.y -= TOLERANCE; - if( windowIn.y < 0 ) - windowIn.y = 0; - - windowIn.width += 2 * TOLERANCE; - if( windowIn.x + windowIn.width > mat->width ) - windowIn.width = mat->width - windowIn.x; - - windowIn.height += 2 * TOLERANCE; - if( windowIn.y + windowIn.height > mat->height ) - windowIn.height = mat->height - windowIn.y; - - cvGetSubRect( mat, &cur_win, windowIn ); - - /* Calculating moments in new center mass */ - cvMoments( &cur_win, &moments ); - - m00 = moments.m00; - m10 = moments.m10; - m01 = moments.m01; - mu11 = moments.mu11; - mu20 = moments.mu20; - mu02 = moments.mu02; - - if( fabs(m00) < DBL_EPSILON ) - return -1; - - inv_m00 = 1. / m00; - xc = cvRound( m10 * inv_m00 + windowIn.x ); - yc = cvRound( m01 * inv_m00 + windowIn.y ); - a = mu20 * inv_m00; - b = mu11 * inv_m00; - c = mu02 * inv_m00; - - /* Calculating width & height */ - square = sqrt( 4 * b * b + (a - c) * (a - c) ); - - /* Calculating orientation */ - theta = atan2( 2 * b, a - c + square ); - - /* Calculating width & length of figure */ - cs = cos( theta ); - sn = sin( theta ); - - rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02; - rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02; - length = sqrt( rotate_a * inv_m00 ) * 4; - width = sqrt( rotate_c * inv_m00 ) * 4; - - /* In case, when tetta is 0 or 1.57... the Length & Width may be exchanged */ - if( length < width ) - { - double t; - - CV_SWAP( length, width, t ); - CV_SWAP( cs, sn, t ); - theta = CV_PI*0.5 - theta; - } - - /* Saving results */ - if( _comp || box ) - { - int t0, t1; - int _xc = cvRound( xc ); - int _yc = cvRound( yc ); - - t0 = cvRound( fabs( length * cs )); - t1 = cvRound( fabs( width * sn )); - - t0 = MAX( t0, t1 ) + 2; - comp.rect.width = MIN( t0, (mat->width - _xc) * 2 ); - - t0 = cvRound( fabs( length * sn )); - t1 = cvRound( fabs( width * cs )); - - t0 = MAX( t0, t1 ) + 2; - comp.rect.height = MIN( t0, (mat->height - _yc) * 2 ); - - comp.rect.x = MAX( 0, _xc - comp.rect.width / 2 ); - comp.rect.y = MAX( 0, _yc - comp.rect.height / 2 ); - - comp.rect.width = MIN( mat->width - comp.rect.x, comp.rect.width ); - comp.rect.height = MIN( mat->height - comp.rect.y, comp.rect.height ); - comp.area = (float) m00; - } - - if( _comp ) - *_comp = comp; - - if( box ) - { - box->size.height = (float)length; - box->size.width = (float)width; - box->angle = (float)((CV_PI*0.5+theta)*180./CV_PI); - while(box->angle < 0) - box->angle += 360; - while(box->angle >= 360) - box->angle -= 360; - if(box->angle >= 180) - box->angle -= 180; - box->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f, - comp.rect.y + comp.rect.height*0.5f); - } - - return itersUsed; -} - - cv::RotatedRect cv::CamShift( InputArray _probImage, Rect& window, TermCriteria criteria ) { - CvConnectedComp comp; - CvBox2D box; + const int TOLERANCE = 10; + Mat mat = _probImage.getMat(); - box.center.x = box.center.y = 0; box.angle = 0; box.size.width = box.size.height = 0; - comp.rect.x = comp.rect.y = comp.rect.width = comp.rect.height = 0; + meanShift( mat, window, criteria ); - Mat probImage = _probImage.getMat(); - CvMat c_probImage = probImage; - cvCamShift(&c_probImage, window, (CvTermCriteria)criteria, &comp, &box); - window = comp.rect; - return RotatedRect(Point2f(box.center), Size2f(box.size), box.angle); -} + window.x -= TOLERANCE; + if( window.x < 0 ) + window.x = 0; -int cv::meanShift( InputArray _probImage, Rect& window, TermCriteria criteria ) -{ - CvConnectedComp comp; - Mat probImage = _probImage.getMat(); - CvMat c_probImage = probImage; - int iters = cvMeanShift(&c_probImage, window, (CvTermCriteria)criteria, &comp ); - window = comp.rect; - return iters; + window.y -= TOLERANCE; + if( window.y < 0 ) + window.y = 0; + + window.width += 2 * TOLERANCE; + if( window.x + window.width > mat.cols ) + window.width = mat.cols - window.x; + + window.height += 2 * TOLERANCE; + if( window.y + window.height > mat.rows ) + window.height = mat.rows - window.y; + + // Calculating moments in new center mass + Moments m = moments( mat(window) ); + + double m00 = m.m00, m10 = m.m10, m01 = m.m01; + double mu11 = m.mu11, mu20 = m.mu20, mu02 = m.mu02; + + if( fabs(m00) < DBL_EPSILON ) + return RotatedRect(); + + double inv_m00 = 1. / m00; + int xc = cvRound( m10 * inv_m00 + window.x ); + int yc = cvRound( m01 * inv_m00 + window.y ); + double a = mu20 * inv_m00, b = mu11 * inv_m00, c = mu02 * inv_m00; + + // Calculating width & height + double square = std::sqrt( 4 * b * b + (a - c) * (a - c) ); + + // Calculating orientation + double theta = atan2( 2 * b, a - c + square ); + + // Calculating width & length of figure + double cs = cos( theta ); + double sn = sin( theta ); + + double rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02; + double rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02; + double length = std::sqrt( rotate_a * inv_m00 ) * 4; + double width = std::sqrt( rotate_c * inv_m00 ) * 4; + + // In case, when tetta is 0 or 1.57... the Length & Width may be exchanged + if( length < width ) + { + std::swap( length, width ); + std::swap( cs, sn ); + theta = CV_PI*0.5 - theta; + } + + // Saving results + int _xc = cvRound( xc ); + int _yc = cvRound( yc ); + + int t0 = cvRound( fabs( length * cs )); + int t1 = cvRound( fabs( width * sn )); + + t0 = MAX( t0, t1 ) + 2; + window.width = MIN( t0, (mat.cols - _xc) * 2 ); + + t0 = cvRound( fabs( length * sn )); + t1 = cvRound( fabs( width * cs )); + + t0 = MAX( t0, t1 ) + 2; + window.height = MIN( t0, (mat.rows - _yc) * 2 ); + + window.x = MAX( 0, _xc - window.width / 2 ); + window.y = MAX( 0, _yc - window.height / 2 ); + + window.width = MIN( mat.cols - window.x, window.width ); + window.height = MIN( mat.rows - window.y, window.height ); + + RotatedRect box; + box.size.height = (float)length; + box.size.width = (float)width; + box.angle = (float)((CV_PI*0.5+theta)*180./CV_PI); + while(box.angle < 0) + box.angle += 360; + while(box.angle >= 360) + box.angle -= 360; + if(box.angle >= 180) + box.angle -= 180; + box.center = Point2f( window.x + window.width*0.5f, window.y + window.height*0.5f); + + return box; } /* End of file. */ diff --git a/modules/video/src/ecc.cpp b/modules/video/src/ecc.cpp index 6555c44ce..ac710d445 100644 --- a/modules/video/src/ecc.cpp +++ b/modules/video/src/ecc.cpp @@ -305,23 +305,8 @@ static void update_warping_matrix_ECC (Mat& map_matrix, const Mat& update, const mapPtr[3] = (float) sin(new_theta); mapPtr[1] = -mapPtr[3]; } - } -CV_IMPL double cvFindTransformECC (const CvArr* _image1, const CvArr* _image2, - CvMat* _map_matrix, - const int motionType, - const CvTermCriteria _criteria) -{ - - Mat image1 = cvarrToMat(_image1); - Mat image2 = cvarrToMat(_image2); - Mat map_matrix = cvarrToMat(_map_matrix); - double cc = cv::findTransformECC(image1, image2, map_matrix, motionType, - TermCriteria(TermCriteria::EPS+TermCriteria::COUNT, _criteria.max_iter, _criteria.epsilon)); - - return cc; -} double cv::findTransformECC(InputArray templateImage, InputArray inputImage, diff --git a/modules/video/src/kalman.cpp b/modules/video/src/kalman.cpp index a5485751e..3cb34c0d2 100644 --- a/modules/video/src/kalman.cpp +++ b/modules/video/src/kalman.cpp @@ -40,176 +40,6 @@ //M*/ #include "precomp.hpp" - -CV_IMPL CvKalman* -cvCreateKalman( int DP, int MP, int CP ) -{ - CvKalman *kalman = 0; - - if( DP <= 0 || MP <= 0 ) - CV_Error( CV_StsOutOfRange, - "state and measurement vectors must have positive number of dimensions" ); - - if( CP < 0 ) - CP = DP; - - /* allocating memory for the structure */ - kalman = (CvKalman *)cvAlloc( sizeof( CvKalman )); - memset( kalman, 0, sizeof(*kalman)); - - kalman->DP = DP; - kalman->MP = MP; - kalman->CP = CP; - - kalman->state_pre = cvCreateMat( DP, 1, CV_32FC1 ); - cvZero( kalman->state_pre ); - - kalman->state_post = cvCreateMat( DP, 1, CV_32FC1 ); - cvZero( kalman->state_post ); - - kalman->transition_matrix = cvCreateMat( DP, DP, CV_32FC1 ); - cvSetIdentity( kalman->transition_matrix ); - - kalman->process_noise_cov = cvCreateMat( DP, DP, CV_32FC1 ); - cvSetIdentity( kalman->process_noise_cov ); - - kalman->measurement_matrix = cvCreateMat( MP, DP, CV_32FC1 ); - cvZero( kalman->measurement_matrix ); - - kalman->measurement_noise_cov = cvCreateMat( MP, MP, CV_32FC1 ); - cvSetIdentity( kalman->measurement_noise_cov ); - - kalman->error_cov_pre = cvCreateMat( DP, DP, CV_32FC1 ); - - kalman->error_cov_post = cvCreateMat( DP, DP, CV_32FC1 ); - cvZero( kalman->error_cov_post ); - - kalman->gain = cvCreateMat( DP, MP, CV_32FC1 ); - - if( CP > 0 ) - { - kalman->control_matrix = cvCreateMat( DP, CP, CV_32FC1 ); - cvZero( kalman->control_matrix ); - } - - kalman->temp1 = cvCreateMat( DP, DP, CV_32FC1 ); - kalman->temp2 = cvCreateMat( MP, DP, CV_32FC1 ); - kalman->temp3 = cvCreateMat( MP, MP, CV_32FC1 ); - kalman->temp4 = cvCreateMat( MP, DP, CV_32FC1 ); - kalman->temp5 = cvCreateMat( MP, 1, CV_32FC1 ); - -#if 1 - kalman->PosterState = kalman->state_pre->data.fl; - kalman->PriorState = kalman->state_post->data.fl; - kalman->DynamMatr = kalman->transition_matrix->data.fl; - kalman->MeasurementMatr = kalman->measurement_matrix->data.fl; - kalman->MNCovariance = kalman->measurement_noise_cov->data.fl; - kalman->PNCovariance = kalman->process_noise_cov->data.fl; - kalman->KalmGainMatr = kalman->gain->data.fl; - kalman->PriorErrorCovariance = kalman->error_cov_pre->data.fl; - kalman->PosterErrorCovariance = kalman->error_cov_post->data.fl; -#endif - - return kalman; -} - - -CV_IMPL void -cvReleaseKalman( CvKalman** _kalman ) -{ - CvKalman *kalman; - - if( !_kalman ) - CV_Error( CV_StsNullPtr, "" ); - - kalman = *_kalman; - if( !kalman ) - return; - - /* freeing the memory */ - cvReleaseMat( &kalman->state_pre ); - cvReleaseMat( &kalman->state_post ); - cvReleaseMat( &kalman->transition_matrix ); - cvReleaseMat( &kalman->control_matrix ); - cvReleaseMat( &kalman->measurement_matrix ); - cvReleaseMat( &kalman->process_noise_cov ); - cvReleaseMat( &kalman->measurement_noise_cov ); - cvReleaseMat( &kalman->error_cov_pre ); - cvReleaseMat( &kalman->gain ); - cvReleaseMat( &kalman->error_cov_post ); - cvReleaseMat( &kalman->temp1 ); - cvReleaseMat( &kalman->temp2 ); - cvReleaseMat( &kalman->temp3 ); - cvReleaseMat( &kalman->temp4 ); - cvReleaseMat( &kalman->temp5 ); - - memset( kalman, 0, sizeof(*kalman)); - - /* deallocating the structure */ - cvFree( _kalman ); -} - - -CV_IMPL const CvMat* -cvKalmanPredict( CvKalman* kalman, const CvMat* control ) -{ - if( !kalman ) - CV_Error( CV_StsNullPtr, "" ); - - /* update the state */ - /* x'(k) = A*x(k) */ - cvMatMulAdd( kalman->transition_matrix, kalman->state_post, 0, kalman->state_pre ); - - if( control && kalman->CP > 0 ) - /* x'(k) = x'(k) + B*u(k) */ - cvMatMulAdd( kalman->control_matrix, control, kalman->state_pre, kalman->state_pre ); - - /* update error covariance matrices */ - /* temp1 = A*P(k) */ - cvMatMulAdd( kalman->transition_matrix, kalman->error_cov_post, 0, kalman->temp1 ); - - /* P'(k) = temp1*At + Q */ - cvGEMM( kalman->temp1, kalman->transition_matrix, 1, kalman->process_noise_cov, 1, - kalman->error_cov_pre, CV_GEMM_B_T ); - - /* handle the case when there will be measurement before the next predict */ - cvCopy(kalman->state_pre, kalman->state_post); - - return kalman->state_pre; -} - - -CV_IMPL const CvMat* -cvKalmanCorrect( CvKalman* kalman, const CvMat* measurement ) -{ - if( !kalman || !measurement ) - CV_Error( CV_StsNullPtr, "" ); - - /* temp2 = H*P'(k) */ - cvMatMulAdd( kalman->measurement_matrix, kalman->error_cov_pre, 0, kalman->temp2 ); - /* temp3 = temp2*Ht + R */ - cvGEMM( kalman->temp2, kalman->measurement_matrix, 1, - kalman->measurement_noise_cov, 1, kalman->temp3, CV_GEMM_B_T ); - - /* temp4 = inv(temp3)*temp2 = Kt(k) */ - cvSolve( kalman->temp3, kalman->temp2, kalman->temp4, CV_SVD ); - - /* K(k) */ - cvTranspose( kalman->temp4, kalman->gain ); - - /* temp5 = z(k) - H*x'(k) */ - cvGEMM( kalman->measurement_matrix, kalman->state_pre, -1, measurement, 1, kalman->temp5 ); - - /* x(k) = x'(k) + K(k)*temp5 */ - cvMatMulAdd( kalman->gain, kalman->temp5, kalman->state_pre, kalman->state_post ); - - /* P(k) = P'(k) - K(k)*temp2 */ - cvGEMM( kalman->gain, kalman->temp2, -1, kalman->error_cov_pre, 1, - kalman->error_cov_post, 0 ); - - return kalman->state_post; -} - namespace cv { diff --git a/modules/video/src/lkpyramid.cpp b/modules/video/src/lkpyramid.cpp index 28477100f..2fef04b48 100644 --- a/modules/video/src/lkpyramid.cpp +++ b/modules/video/src/lkpyramid.cpp @@ -7,10 +7,11 @@ // copy or use the software. // // -// Intel License Agreement +// License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -23,7 +24,7 @@ // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // -// * The name of Intel Corporation may not be used to endorse or promote products +// * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and @@ -156,6 +157,14 @@ cv::detail::LKTrackerInvoker::LKTrackerInvoker( minEigThreshold = _minEigThreshold; } +#if defined __arm__ && !CV_NEON +typedef int64 acctype; +typedef int itemtype; +#else +typedef float acctype; +typedef float itemtype; +#endif + void cv::detail::LKTrackerInvoker::operator()(const BlockedRange& range) const { Point2f halfWin((winSize.width-1)*0.5f, (winSize.height-1)*0.5f); @@ -215,7 +224,8 @@ void cv::detail::LKTrackerInvoker::operator()(const BlockedRange& range) const int dstep = (int)(derivI.step/derivI.elemSize1()); int stepI = (int)(I.step/I.elemSize1()); int stepJ = (int)(J.step/J.elemSize1()); - float A11 = 0, A12 = 0, A22 = 0; + acctype iA11 = 0, iA12 = 0, iA22 = 0; + float A11, A12, A22; #if CV_SSE2 __m128i qw0 = _mm_set1_epi32(iw00 + (iw01 << 16)); @@ -292,9 +302,9 @@ void cv::detail::LKTrackerInvoker::operator()(const BlockedRange& range) const dIptr[0] = (short)ixval; dIptr[1] = (short)iyval; - A11 += (float)(ixval*ixval); - A12 += (float)(ixval*iyval); - A22 += (float)(iyval*iyval); + iA11 += (itemtype)(ixval*ixval); + iA12 += (itemtype)(ixval*iyval); + iA22 += (itemtype)(iyval*iyval); } } @@ -303,14 +313,14 @@ void cv::detail::LKTrackerInvoker::operator()(const BlockedRange& range) const _mm_store_ps(A11buf, qA11); _mm_store_ps(A12buf, qA12); _mm_store_ps(A22buf, qA22); - A11 += A11buf[0] + A11buf[1] + A11buf[2] + A11buf[3]; - A12 += A12buf[0] + A12buf[1] + A12buf[2] + A12buf[3]; - A22 += A22buf[0] + A22buf[1] + A22buf[2] + A22buf[3]; + iA11 += A11buf[0] + A11buf[1] + A11buf[2] + A11buf[3]; + iA12 += A12buf[0] + A12buf[1] + A12buf[2] + A12buf[3]; + iA22 += A22buf[0] + A22buf[1] + A22buf[2] + A22buf[3]; #endif - A11 *= FLT_SCALE; - A12 *= FLT_SCALE; - A22 *= FLT_SCALE; + A11 = iA11*FLT_SCALE; + A12 = iA12*FLT_SCALE; + A22 = iA22*FLT_SCALE; float D = A11*A22 - A12*A12; float minEig = (A22 + A11 - std::sqrt((A11-A22)*(A11-A22) + @@ -350,7 +360,8 @@ void cv::detail::LKTrackerInvoker::operator()(const BlockedRange& range) const iw01 = cvRound(a*(1.f - b)*(1 << W_BITS)); iw10 = cvRound((1.f - a)*b*(1 << W_BITS)); iw11 = (1 << W_BITS) - iw00 - iw01 - iw10; - float b1 = 0, b2 = 0; + acctype ib1 = 0, ib2 = 0; + float b1, b2; #if CV_SSE2 qw0 = _mm_set1_epi32(iw00 + (iw01 << 16)); qw1 = _mm_set1_epi32(iw10 + (iw11 << 16)); @@ -405,20 +416,20 @@ void cv::detail::LKTrackerInvoker::operator()(const BlockedRange& range) const int diff = CV_DESCALE(Jptr[x]*iw00 + Jptr[x+cn]*iw01 + Jptr[x+stepJ]*iw10 + Jptr[x+stepJ+cn]*iw11, W_BITS1-5) - Iptr[x]; - b1 += (float)(diff*dIptr[0]); - b2 += (float)(diff*dIptr[1]); + ib1 += (itemtype)(diff*dIptr[0]); + ib2 += (itemtype)(diff*dIptr[1]); } } #if CV_SSE2 float CV_DECL_ALIGNED(16) bbuf[4]; _mm_store_ps(bbuf, _mm_add_ps(qb0, qb1)); - b1 += bbuf[0] + bbuf[2]; - b2 += bbuf[1] + bbuf[3]; + ib1 += bbuf[0] + bbuf[2]; + ib2 += bbuf[1] + bbuf[3]; #endif - b1 *= FLT_SCALE; - b2 *= FLT_SCALE; + b1 = ib1*FLT_SCALE; + b2 = ib2*FLT_SCALE; Point2f delta( (float)((A12*b2 - A22*b1) * D), (float)((A12*b1 - A11*b2) * D)); @@ -741,870 +752,31 @@ void cv::calcOpticalFlowPyrLK( InputArray _prevImg, InputArray _nextImg, } } - -static int icvMinimalPyramidSize( CvSize imgSize ) +namespace cv { - return cvAlign(imgSize.width,8) * imgSize.height / 3; -} - static void -icvInitPyramidalAlgorithm( const CvMat* imgA, const CvMat* imgB, - CvMat* pyrA, CvMat* pyrB, - int level, CvTermCriteria * criteria, - int max_iters, int flags, - uchar *** imgI, uchar *** imgJ, - int **step, CvSize** size, - double **scale, cv::AutoBuffer* buffer ) +getRTMatrix( const Point2f* a, const Point2f* b, + int count, Mat& M, bool fullAffine ) { - const int ALIGN = 8; - int pyrBytes, bufferBytes = 0, elem_size; - int level1 = level + 1; + CV_Assert( M.isContinuous() ); - int i; - CvSize imgSize, levelSize; - - *imgI = *imgJ = 0; - *step = 0; - *scale = 0; - *size = 0; - - /* check input arguments */ - if( ((flags & CV_LKFLOW_PYR_A_READY) != 0 && !pyrA) || - ((flags & CV_LKFLOW_PYR_B_READY) != 0 && !pyrB) ) - CV_Error( CV_StsNullPtr, "Some of the precomputed pyramids are missing" ); - - if( level < 0 ) - CV_Error( CV_StsOutOfRange, "The number of pyramid levels is negative" ); - - switch( criteria->type ) + if( fullAffine ) { - case CV_TERMCRIT_ITER: - criteria->epsilon = 0.f; - break; - case CV_TERMCRIT_EPS: - criteria->max_iter = max_iters; - break; - case CV_TERMCRIT_ITER | CV_TERMCRIT_EPS: - break; - default: - assert( 0 ); - CV_Error( CV_StsBadArg, "Invalid termination criteria" ); - } + double sa[6][6]={{0.}}, sb[6]={0.}; + Mat A( 6, 6, CV_64F, &sa[0][0] ), B( 6, 1, CV_64F, sb ); + Mat MM = M.reshape(1, 6); - /* compare squared values */ - criteria->epsilon *= criteria->epsilon; - - /* set pointers and step for every level */ - pyrBytes = 0; - - imgSize = cvGetSize(imgA); - elem_size = CV_ELEM_SIZE(imgA->type); - levelSize = imgSize; - - for( i = 1; i < level1; i++ ) - { - levelSize.width = (levelSize.width + 1) >> 1; - levelSize.height = (levelSize.height + 1) >> 1; - - int tstep = cvAlign(levelSize.width,ALIGN) * elem_size; - pyrBytes += tstep * levelSize.height; - } - - assert( pyrBytes <= imgSize.width * imgSize.height * elem_size * 4 / 3 ); - - /* buffer_size = + */ - bufferBytes = (int)((level1 >= 0) * ((pyrA->data.ptr == 0) + - (pyrB->data.ptr == 0)) * pyrBytes + - (sizeof(imgI[0][0]) * 2 + sizeof(step[0][0]) + - sizeof(size[0][0]) + sizeof(scale[0][0])) * level1); - - buffer->allocate( bufferBytes ); - - *imgI = (uchar **) (uchar*)(*buffer); - *imgJ = *imgI + level1; - *step = (int *) (*imgJ + level1); - *scale = (double *) (*step + level1); - *size = (CvSize *)(*scale + level1); - - imgI[0][0] = imgA->data.ptr; - imgJ[0][0] = imgB->data.ptr; - step[0][0] = imgA->step; - scale[0][0] = 1; - size[0][0] = imgSize; - - if( level > 0 ) - { - uchar *bufPtr = (uchar *) (*size + level1); - uchar *ptrA = pyrA->data.ptr; - uchar *ptrB = pyrB->data.ptr; - - if( !ptrA ) + for( int i = 0; i < count; i++ ) { - ptrA = bufPtr; - bufPtr += pyrBytes; - } + sa[0][0] += a[i].x*a[i].x; + sa[0][1] += a[i].y*a[i].x; + sa[0][2] += a[i].x; - if( !ptrB ) - ptrB = bufPtr; + sa[1][1] += a[i].y*a[i].y; + sa[1][2] += a[i].y; - levelSize = imgSize; - - /* build pyramids for both frames */ - for( i = 1; i <= level; i++ ) - { - int levelBytes; - CvMat prev_level, next_level; - - levelSize.width = (levelSize.width + 1) >> 1; - levelSize.height = (levelSize.height + 1) >> 1; - - size[0][i] = levelSize; - step[0][i] = cvAlign( levelSize.width, ALIGN ) * elem_size; - scale[0][i] = scale[0][i - 1] * 0.5; - - levelBytes = step[0][i] * levelSize.height; - imgI[0][i] = (uchar *) ptrA; - ptrA += levelBytes; - - if( !(flags & CV_LKFLOW_PYR_A_READY) ) - { - prev_level = cvMat( size[0][i-1].height, size[0][i-1].width, CV_8UC1 ); - next_level = cvMat( size[0][i].height, size[0][i].width, CV_8UC1 ); - cvSetData( &prev_level, imgI[0][i-1], step[0][i-1] ); - cvSetData( &next_level, imgI[0][i], step[0][i] ); - cvPyrDown( &prev_level, &next_level ); - } - - imgJ[0][i] = (uchar *) ptrB; - ptrB += levelBytes; - - if( !(flags & CV_LKFLOW_PYR_B_READY) ) - { - prev_level = cvMat( size[0][i-1].height, size[0][i-1].width, CV_8UC1 ); - next_level = cvMat( size[0][i].height, size[0][i].width, CV_8UC1 ); - cvSetData( &prev_level, imgJ[0][i-1], step[0][i-1] ); - cvSetData( &next_level, imgJ[0][i], step[0][i] ); - cvPyrDown( &prev_level, &next_level ); - } - } - } -} - - -/* compute dI/dx and dI/dy */ -static void -icvCalcIxIy_32f( const float* src, int src_step, float* dstX, float* dstY, int dst_step, - CvSize src_size, const float* smooth_k, float* buffer0 ) -{ - int src_width = src_size.width, dst_width = src_size.width-2; - int x, height = src_size.height - 2; - float* buffer1 = buffer0 + src_width; - - src_step /= sizeof(src[0]); - dst_step /= sizeof(dstX[0]); - - for( ; height--; src += src_step, dstX += dst_step, dstY += dst_step ) - { - const float* src2 = src + src_step; - const float* src3 = src + src_step*2; - - for( x = 0; x < src_width; x++ ) - { - float t0 = (src3[x] + src[x])*smooth_k[0] + src2[x]*smooth_k[1]; - float t1 = src3[x] - src[x]; - buffer0[x] = t0; buffer1[x] = t1; - } - - for( x = 0; x < dst_width; x++ ) - { - float t0 = buffer0[x+2] - buffer0[x]; - float t1 = (buffer1[x] + buffer1[x+2])*smooth_k[0] + buffer1[x+1]*smooth_k[1]; - dstX[x] = t0; dstY[x] = t1; - } - } -} - - -#undef CV_8TO32F -#define CV_8TO32F(a) (a) - -static const void* -icvAdjustRect( const void* srcptr, int src_step, int pix_size, - CvSize src_size, CvSize win_size, - CvPoint ip, CvRect* pRect ) -{ - CvRect rect; - const char* src = (const char*)srcptr; - - if( ip.x >= 0 ) - { - src += ip.x*pix_size; - rect.x = 0; - } - else - { - rect.x = -ip.x; - if( rect.x > win_size.width ) - rect.x = win_size.width; - } - - if( ip.x + win_size.width < src_size.width ) - rect.width = win_size.width; - else - { - rect.width = src_size.width - ip.x - 1; - if( rect.width < 0 ) - { - src += rect.width*pix_size; - rect.width = 0; - } - assert( rect.width <= win_size.width ); - } - - if( ip.y >= 0 ) - { - src += ip.y * src_step; - rect.y = 0; - } - else - rect.y = -ip.y; - - if( ip.y + win_size.height < src_size.height ) - rect.height = win_size.height; - else - { - rect.height = src_size.height - ip.y - 1; - if( rect.height < 0 ) - { - src += rect.height*src_step; - rect.height = 0; - } - } - - *pRect = rect; - return src - rect.x*pix_size; -} - - -static CvStatus CV_STDCALL icvGetRectSubPix_8u32f_C1R -( const uchar* src, int src_step, CvSize src_size, - float* dst, int dst_step, CvSize win_size, CvPoint2D32f center ) -{ - CvPoint ip; - float a12, a22, b1, b2; - float a, b; - double s = 0; - int i, j; - - center.x -= (win_size.width-1)*0.5f; - center.y -= (win_size.height-1)*0.5f; - - ip.x = cvFloor( center.x ); - ip.y = cvFloor( center.y ); - - if( win_size.width <= 0 || win_size.height <= 0 ) - return CV_BADRANGE_ERR; - - a = center.x - ip.x; - b = center.y - ip.y; - a = MAX(a,0.0001f); - a12 = a*(1.f-b); - a22 = a*b; - b1 = 1.f - b; - b2 = b; - s = (1. - a)/a; - - src_step /= sizeof(src[0]); - dst_step /= sizeof(dst[0]); - - if( 0 <= ip.x && ip.x + win_size.width < src_size.width && - 0 <= ip.y && ip.y + win_size.height < src_size.height ) - { - // extracted rectangle is totally inside the image - src += ip.y * src_step + ip.x; - -#if 0 - if( icvCopySubpix_8u32f_C1R_p && - icvCopySubpix_8u32f_C1R_p( src, src_step, dst, - dst_step*sizeof(dst[0]), win_size, a, b ) >= 0 ) - return CV_OK; -#endif - - for( ; win_size.height--; src += src_step, dst += dst_step ) - { - float prev = (1 - a)*(b1*CV_8TO32F(src[0]) + b2*CV_8TO32F(src[src_step])); - for( j = 0; j < win_size.width; j++ ) - { - float t = a12*CV_8TO32F(src[j+1]) + a22*CV_8TO32F(src[j+1+src_step]); - dst[j] = prev + t; - prev = (float)(t*s); - } - } - } - else - { - CvRect r; - - src = (const uchar*)icvAdjustRect( src, src_step*sizeof(*src), - sizeof(*src), src_size, win_size,ip, &r); - - for( i = 0; i < win_size.height; i++, dst += dst_step ) - { - const uchar *src2 = src + src_step; - - if( i < r.y || i >= r.height ) - src2 -= src_step; - - for( j = 0; j < r.x; j++ ) - { - float s0 = CV_8TO32F(src[r.x])*b1 + - CV_8TO32F(src2[r.x])*b2; - - dst[j] = (float)(s0); - } - - if( j < r.width ) - { - float prev = (1 - a)*(b1*CV_8TO32F(src[j]) + b2*CV_8TO32F(src2[j])); - - for( ; j < r.width; j++ ) - { - float t = a12*CV_8TO32F(src[j+1]) + a22*CV_8TO32F(src2[j+1]); - dst[j] = prev + t; - prev = (float)(t*s); - } - } - - for( ; j < win_size.width; j++ ) - { - float s0 = CV_8TO32F(src[r.width])*b1 + - CV_8TO32F(src2[r.width])*b2; - - dst[j] = (float)(s0); - } - - if( i < r.height ) - src = src2; - } - } - - return CV_OK; -} - - -#define ICV_32F8U(x) ((uchar)cvRound(x)) - -#define ICV_DEF_GET_QUADRANGLE_SUB_PIX_FUNC( flavor, srctype, dsttype, \ -worktype, cast_macro, cvt ) \ -static CvStatus CV_STDCALL \ -icvGetQuadrangleSubPix_##flavor##_C1R \ -( const srctype * src, int src_step, CvSize src_size, \ -dsttype *dst, int dst_step, CvSize win_size, const float *matrix ) \ -{ \ -int x, y; \ -double dx = (win_size.width - 1)*0.5; \ -double dy = (win_size.height - 1)*0.5; \ -double A11 = matrix[0], A12 = matrix[1], A13 = matrix[2]-A11*dx-A12*dy; \ -double A21 = matrix[3], A22 = matrix[4], A23 = matrix[5]-A21*dx-A22*dy; \ -\ -src_step /= sizeof(srctype); \ -dst_step /= sizeof(dsttype); \ -\ -for( y = 0; y < win_size.height; y++, dst += dst_step ) \ -{ \ -double xs = A12*y + A13; \ -double ys = A22*y + A23; \ -double xe = A11*(win_size.width-1) + A12*y + A13; \ -double ye = A21*(win_size.width-1) + A22*y + A23; \ -\ -if( (unsigned)(cvFloor(xs)-1) < (unsigned)(src_size.width - 3) && \ -(unsigned)(cvFloor(ys)-1) < (unsigned)(src_size.height - 3) && \ -(unsigned)(cvFloor(xe)-1) < (unsigned)(src_size.width - 3) && \ -(unsigned)(cvFloor(ye)-1) < (unsigned)(src_size.height - 3)) \ -{ \ -for( x = 0; x < win_size.width; x++ ) \ -{ \ -int ixs = cvFloor( xs ); \ -int iys = cvFloor( ys ); \ -const srctype *ptr = src + src_step*iys + ixs; \ -double a = xs - ixs, b = ys - iys, a1 = 1.f - a; \ -worktype p0 = cvt(ptr[0])*a1 + cvt(ptr[1])*a; \ -worktype p1 = cvt(ptr[src_step])*a1 + cvt(ptr[src_step+1])*a;\ -xs += A11; \ -ys += A21; \ -\ -dst[x] = cast_macro(p0 + b * (p1 - p0)); \ -} \ -} \ -else \ -{ \ -for( x = 0; x < win_size.width; x++ ) \ -{ \ -int ixs = cvFloor( xs ), iys = cvFloor( ys ); \ -double a = xs - ixs, b = ys - iys, a1 = 1.f - a; \ -const srctype *ptr0, *ptr1; \ -worktype p0, p1; \ -xs += A11; ys += A21; \ -\ -if( (unsigned)iys < (unsigned)(src_size.height-1) ) \ -ptr0 = src + src_step*iys, ptr1 = ptr0 + src_step; \ -else \ -ptr0 = ptr1 = src + (iys < 0 ? 0 : src_size.height-1)*src_step; \ -\ -if( (unsigned)ixs < (unsigned)(src_size.width-1) ) \ -{ \ -p0 = cvt(ptr0[ixs])*a1 + cvt(ptr0[ixs+1])*a; \ -p1 = cvt(ptr1[ixs])*a1 + cvt(ptr1[ixs+1])*a; \ -} \ -else \ -{ \ -ixs = ixs < 0 ? 0 : src_size.width - 1; \ -p0 = cvt(ptr0[ixs]); p1 = cvt(ptr1[ixs]); \ -} \ -dst[x] = cast_macro(p0 + b * (p1 - p0)); \ -} \ -} \ -} \ -\ -return CV_OK; \ -} - -ICV_DEF_GET_QUADRANGLE_SUB_PIX_FUNC( 8u32f, uchar, float, double, CV_CAST_32F, CV_8TO32F ) - - -CV_IMPL void -cvCalcOpticalFlowPyrLK( const void* arrA, const void* arrB, - void* /*pyrarrA*/, void* /*pyrarrB*/, - const CvPoint2D32f * featuresA, - CvPoint2D32f * featuresB, - int count, CvSize winSize, int level, - char *status, float *error, - CvTermCriteria criteria, int flags ) -{ - if( count <= 0 ) - return; - CV_Assert( featuresA && featuresB ); - cv::Mat A = cv::cvarrToMat(arrA), B = cv::cvarrToMat(arrB); - cv::Mat ptA(count, 1, CV_32FC2, (void*)featuresA); - cv::Mat ptB(count, 1, CV_32FC2, (void*)featuresB); - cv::Mat st, err; - - if( status ) - st = cv::Mat(count, 1, CV_8U, (void*)status); - if( error ) - err = cv::Mat(count, 1, CV_32F, (void*)error); - cv::calcOpticalFlowPyrLK( A, B, ptA, ptB, st, - error ? cv::_OutputArray(err) : cv::noArray(), - winSize, level, criteria, flags); -} - - -/* Affine tracking algorithm */ - -CV_IMPL void -cvCalcAffineFlowPyrLK( const void* arrA, const void* arrB, - void* pyrarrA, void* pyrarrB, - const CvPoint2D32f * featuresA, - CvPoint2D32f * featuresB, - float *matrices, int count, - CvSize winSize, int level, - char *status, float *error, - CvTermCriteria criteria, int flags ) -{ - const int MAX_ITERS = 100; - - cv::AutoBuffer _status; - cv::AutoBuffer buffer; - cv::AutoBuffer pyr_buffer; - - CvMat stubA, *imgA = (CvMat*)arrA; - CvMat stubB, *imgB = (CvMat*)arrB; - CvMat pstubA, *pyrA = (CvMat*)pyrarrA; - CvMat pstubB, *pyrB = (CvMat*)pyrarrB; - - static const float smoothKernel[] = { 0.09375, 0.3125, 0.09375 }; /* 3/32, 10/32, 3/32 */ - - int bufferBytes = 0; - - uchar **imgI = 0; - uchar **imgJ = 0; - int *step = 0; - double *scale = 0; - CvSize* size = 0; - - float *patchI; - float *patchJ; - float *Ix; - float *Iy; - - int i, j, k, l; - - CvSize patchSize = cvSize( winSize.width * 2 + 1, winSize.height * 2 + 1 ); - int patchLen = patchSize.width * patchSize.height; - int patchStep = patchSize.width * sizeof( patchI[0] ); - - CvSize srcPatchSize = cvSize( patchSize.width + 2, patchSize.height + 2 ); - int srcPatchLen = srcPatchSize.width * srcPatchSize.height; - int srcPatchStep = srcPatchSize.width * sizeof( patchI[0] ); - CvSize imgSize; - float eps = (float)MIN(winSize.width, winSize.height); - - imgA = cvGetMat( imgA, &stubA ); - imgB = cvGetMat( imgB, &stubB ); - - if( CV_MAT_TYPE( imgA->type ) != CV_8UC1 ) - CV_Error( CV_StsUnsupportedFormat, "" ); - - if( !CV_ARE_TYPES_EQ( imgA, imgB )) - CV_Error( CV_StsUnmatchedFormats, "" ); - - if( !CV_ARE_SIZES_EQ( imgA, imgB )) - CV_Error( CV_StsUnmatchedSizes, "" ); - - if( imgA->step != imgB->step ) - CV_Error( CV_StsUnmatchedSizes, "imgA and imgB must have equal steps" ); - - if( !matrices ) - CV_Error( CV_StsNullPtr, "" ); - - imgSize = cvGetMatSize( imgA ); - - if( pyrA ) - { - pyrA = cvGetMat( pyrA, &pstubA ); - - if( pyrA->step*pyrA->height < icvMinimalPyramidSize( imgSize ) ) - CV_Error( CV_StsBadArg, "pyramid A has insufficient size" ); - } - else - { - pyrA = &pstubA; - pyrA->data.ptr = 0; - } - - if( pyrB ) - { - pyrB = cvGetMat( pyrB, &pstubB ); - - if( pyrB->step*pyrB->height < icvMinimalPyramidSize( imgSize ) ) - CV_Error( CV_StsBadArg, "pyramid B has insufficient size" ); - } - else - { - pyrB = &pstubB; - pyrB->data.ptr = 0; - } - - if( count == 0 ) - return; - - /* check input arguments */ - if( !featuresA || !featuresB || !matrices ) - CV_Error( CV_StsNullPtr, "" ); - - if( winSize.width <= 1 || winSize.height <= 1 ) - CV_Error( CV_StsOutOfRange, "the search window is too small" ); - - if( count < 0 ) - CV_Error( CV_StsOutOfRange, "" ); - - icvInitPyramidalAlgorithm( imgA, imgB, - pyrA, pyrB, level, &criteria, MAX_ITERS, flags, - &imgI, &imgJ, &step, &size, &scale, &pyr_buffer ); - - /* buffer_size = + */ - bufferBytes = (srcPatchLen + patchLen*3)*sizeof(patchI[0]) + (36*2 + 6)*sizeof(double); - - buffer.allocate(bufferBytes); - - if( !status ) - { - _status.allocate(count); - status = _status; - } - - patchI = (float *)(uchar*)buffer; - patchJ = patchI + srcPatchLen; - Ix = patchJ + patchLen; - Iy = Ix + patchLen; - - if( status ) - memset( status, 1, count ); - - if( !(flags & CV_LKFLOW_INITIAL_GUESSES) ) - { - memcpy( featuresB, featuresA, count * sizeof( featuresA[0] )); - for( i = 0; i < count * 4; i += 4 ) - { - matrices[i] = matrices[i + 3] = 1.f; - matrices[i + 1] = matrices[i + 2] = 0.f; - } - } - - for( i = 0; i < count; i++ ) - { - featuresB[i].x = (float)(featuresB[i].x * scale[level] * 0.5); - featuresB[i].y = (float)(featuresB[i].y * scale[level] * 0.5); - } - - /* do processing from top pyramid level (smallest image) - to the bottom (original image) */ - for( l = level; l >= 0; l-- ) - { - CvSize levelSize = size[l]; - int levelStep = step[l]; - - /* find flow for each given point at the particular level */ - for( i = 0; i < count; i++ ) - { - CvPoint2D32f u; - float Av[6]; - double G[36]; - double meanI = 0, meanJ = 0; - int x, y; - int pt_status = status[i]; - CvMat mat; - - if( !pt_status ) - continue; - - Av[0] = matrices[i*4]; - Av[1] = matrices[i*4+1]; - Av[3] = matrices[i*4+2]; - Av[4] = matrices[i*4+3]; - - Av[2] = featuresB[i].x += featuresB[i].x; - Av[5] = featuresB[i].y += featuresB[i].y; - - u.x = (float) (featuresA[i].x * scale[l]); - u.y = (float) (featuresA[i].y * scale[l]); - - if( u.x < -eps || u.x >= levelSize.width+eps || - u.y < -eps || u.y >= levelSize.height+eps || - icvGetRectSubPix_8u32f_C1R( imgI[l], levelStep, - levelSize, patchI, srcPatchStep, srcPatchSize, u ) < 0 ) - { - /* point is outside the image. take the next */ - if( l == 0 ) - status[i] = 0; - continue; - } - - icvCalcIxIy_32f( patchI, srcPatchStep, Ix, Iy, - (srcPatchSize.width-2)*sizeof(patchI[0]), srcPatchSize, - smoothKernel, patchJ ); - - /* repack patchI (remove borders) */ - for( k = 0; k < patchSize.height; k++ ) - memcpy( patchI + k * patchSize.width, - patchI + (k + 1) * srcPatchSize.width + 1, patchStep ); - - memset( G, 0, sizeof( G )); - - /* calculate G matrix */ - for( y = -winSize.height, k = 0; y <= winSize.height; y++ ) - { - for( x = -winSize.width; x <= winSize.width; x++, k++ ) - { - double ixix = ((double) Ix[k]) * Ix[k]; - double ixiy = ((double) Ix[k]) * Iy[k]; - double iyiy = ((double) Iy[k]) * Iy[k]; - - double xx, xy, yy; - - G[0] += ixix; - G[1] += ixiy; - G[2] += x * ixix; - G[3] += y * ixix; - G[4] += x * ixiy; - G[5] += y * ixiy; - - // G[6] == G[1] - G[7] += iyiy; - // G[8] == G[4] - // G[9] == G[5] - G[10] += x * iyiy; - G[11] += y * iyiy; - - xx = x * x; - xy = x * y; - yy = y * y; - - // G[12] == G[2] - // G[13] == G[8] == G[4] - G[14] += xx * ixix; - G[15] += xy * ixix; - G[16] += xx * ixiy; - G[17] += xy * ixiy; - - // G[18] == G[3] - // G[19] == G[9] - // G[20] == G[15] - G[21] += yy * ixix; - // G[22] == G[17] - G[23] += yy * ixiy; - - // G[24] == G[4] - // G[25] == G[10] - // G[26] == G[16] - // G[27] == G[22] - G[28] += xx * iyiy; - G[29] += xy * iyiy; - - // G[30] == G[5] - // G[31] == G[11] - // G[32] == G[17] - // G[33] == G[23] - // G[34] == G[29] - G[35] += yy * iyiy; - - meanI += patchI[k]; - } - } - - meanI /= patchSize.width*patchSize.height; - - G[8] = G[4]; - G[9] = G[5]; - G[22] = G[17]; - - // fill part of G below its diagonal - for( y = 1; y < 6; y++ ) - for( x = 0; x < y; x++ ) - G[y * 6 + x] = G[x * 6 + y]; - - cvInitMatHeader( &mat, 6, 6, CV_64FC1, G ); - - if( cvInvert( &mat, &mat, CV_SVD ) < 1e-4 ) - { - /* bad matrix. take the next point */ - if( l == 0 ) - status[i] = 0; - continue; - } - - for( j = 0; j < criteria.max_iter; j++ ) - { - double b[6] = {0,0,0,0,0,0}, eta[6]; - double t0, t1, s = 0; - - if( Av[2] < -eps || Av[2] >= levelSize.width+eps || - Av[5] < -eps || Av[5] >= levelSize.height+eps || - icvGetQuadrangleSubPix_8u32f_C1R( imgJ[l], levelStep, - levelSize, patchJ, patchStep, patchSize, Av ) < 0 ) - { - pt_status = 0; - break; - } - - for( y = -winSize.height, k = 0, meanJ = 0; y <= winSize.height; y++ ) - for( x = -winSize.width; x <= winSize.width; x++, k++ ) - meanJ += patchJ[k]; - - meanJ = meanJ / (patchSize.width * patchSize.height) - meanI; - - for( y = -winSize.height, k = 0; y <= winSize.height; y++ ) - { - for( x = -winSize.width; x <= winSize.width; x++, k++ ) - { - double t = patchI[k] - patchJ[k] + meanJ; - double ixt = Ix[k] * t; - double iyt = Iy[k] * t; - - s += t; - - b[0] += ixt; - b[1] += iyt; - b[2] += x * ixt; - b[3] += y * ixt; - b[4] += x * iyt; - b[5] += y * iyt; - } - } - - for( k = 0; k < 6; k++ ) - eta[k] = G[k*6]*b[0] + G[k*6+1]*b[1] + G[k*6+2]*b[2] + - G[k*6+3]*b[3] + G[k*6+4]*b[4] + G[k*6+5]*b[5]; - - Av[2] = (float)(Av[2] + Av[0] * eta[0] + Av[1] * eta[1]); - Av[5] = (float)(Av[5] + Av[3] * eta[0] + Av[4] * eta[1]); - - t0 = Av[0] * (1 + eta[2]) + Av[1] * eta[4]; - t1 = Av[0] * eta[3] + Av[1] * (1 + eta[5]); - Av[0] = (float)t0; - Av[1] = (float)t1; - - t0 = Av[3] * (1 + eta[2]) + Av[4] * eta[4]; - t1 = Av[3] * eta[3] + Av[4] * (1 + eta[5]); - Av[3] = (float)t0; - Av[4] = (float)t1; - - if( eta[0] * eta[0] + eta[1] * eta[1] < criteria.epsilon ) - break; - } - - if( pt_status != 0 || l == 0 ) - { - status[i] = (char)pt_status; - featuresB[i].x = Av[2]; - featuresB[i].y = Av[5]; - - matrices[i*4] = Av[0]; - matrices[i*4+1] = Av[1]; - matrices[i*4+2] = Av[3]; - matrices[i*4+3] = Av[4]; - } - - if( pt_status && l == 0 && error ) - { - /* calc error */ - double err = 0; - - for( y = 0, k = 0; y < patchSize.height; y++ ) - { - for( x = 0; x < patchSize.width; x++, k++ ) - { - double t = patchI[k] - patchJ[k] + meanJ; - err += t * t; - } - } - error[i] = (float)std::sqrt(err); - } - } - } -} - - - -static void -icvGetRTMatrix( const CvPoint2D32f* a, const CvPoint2D32f* b, - int count, CvMat* M, int full_affine ) -{ - if( full_affine ) - { - double sa[36], sb[6]; - CvMat A = cvMat( 6, 6, CV_64F, sa ), B = cvMat( 6, 1, CV_64F, sb ); - CvMat MM = cvMat( 6, 1, CV_64F, M->data.db ); - - int i; - - memset( sa, 0, sizeof(sa) ); - memset( sb, 0, sizeof(sb) ); - - for( i = 0; i < count; i++ ) - { - sa[0] += a[i].x*a[i].x; - sa[1] += a[i].y*a[i].x; - sa[2] += a[i].x; - - sa[6] += a[i].x*a[i].y; - sa[7] += a[i].y*a[i].y; - sa[8] += a[i].y; - - sa[12] += a[i].x; - sa[13] += a[i].y; - sa[14] += 1; + sa[2][2] += 1; sb[0] += a[i].x*b[i].x; sb[1] += a[i].y*b[i].x; @@ -1614,50 +786,35 @@ icvGetRTMatrix( const CvPoint2D32f* a, const CvPoint2D32f* b, sb[5] += b[i].y; } - sa[21] = sa[0]; - sa[22] = sa[1]; - sa[23] = sa[2]; - sa[27] = sa[6]; - sa[28] = sa[7]; - sa[29] = sa[8]; - sa[33] = sa[12]; - sa[34] = sa[13]; - sa[35] = sa[14]; + sa[3][4] = sa[4][3] = sa[1][0] = sa[0][1]; + sa[3][5] = sa[5][3] = sa[2][0] = sa[0][2]; + sa[4][5] = sa[5][4] = sa[2][1] = sa[1][2]; - cvSolve( &A, &B, &MM, CV_SVD ); + sa[3][3] = sa[0][0]; + sa[4][4] = sa[1][1]; + sa[5][5] = sa[2][2]; + + solve( A, B, MM, DECOMP_EIG ); } else { - double sa[16], sb[4], m[4], *om = M->data.db; - CvMat A = cvMat( 4, 4, CV_64F, sa ), B = cvMat( 4, 1, CV_64F, sb ); - CvMat MM = cvMat( 4, 1, CV_64F, m ); + double sa[4][4]={{0.}}, sb[4]={0.}, m[4]; + Mat A( 4, 4, CV_64F, sa ), B( 4, 1, CV_64F, sb ); + Mat MM( 4, 1, CV_64F, m ); - int i; - - memset( sa, 0, sizeof(sa) ); - memset( sb, 0, sizeof(sb) ); - - for( i = 0; i < count; i++ ) + for( int i = 0; i < count; i++ ) { - sa[0] += a[i].x*a[i].x + a[i].y*a[i].y; - sa[1] += 0; - sa[2] += a[i].x; - sa[3] += a[i].y; + sa[0][0] += a[i].x*a[i].x + a[i].y*a[i].y; + sa[0][2] += a[i].x; + sa[0][3] += a[i].y; - sa[4] += 0; - sa[5] += a[i].x*a[i].x + a[i].y*a[i].y; - sa[6] += -a[i].y; - sa[7] += a[i].x; - sa[8] += a[i].x; - sa[9] += -a[i].y; - sa[10] += 1; - sa[11] += 0; + sa[2][1] += -a[i].y; + sa[2][2] += 1; - sa[12] += a[i].y; - sa[13] += a[i].x; - sa[14] += 0; - sa[15] += 1; + sa[3][0] += a[i].y; + sa[3][1] += a[i].x; + sa[3][3] += 1; sb[0] += a[i].x*b[i].x + a[i].y*b[i].y; sb[1] += a[i].x*b[i].y - a[i].y*b[i].x; @@ -1665,8 +822,15 @@ icvGetRTMatrix( const CvPoint2D32f* a, const CvPoint2D32f* b, sb[3] += b[i].y; } - cvSolve( &A, &B, &MM, CV_SVD ); + sa[1][1] = sa[0][0]; + sa[2][1] = sa[1][2] = -sa[0][3]; + sa[3][1] = sa[1][3] = sa[2][0] = sa[0][2]; + sa[2][2] = sa[3][3] = count; + sa[3][0] = sa[0][3]; + solve( A, B, MM, DECOMP_EIG ); + + double* om = M.ptr(); om[0] = om[4] = m[0]; om[1] = -m[1]; om[3] = m[1]; @@ -1675,88 +839,84 @@ icvGetRTMatrix( const CvPoint2D32f* a, const CvPoint2D32f* b, } } +} -CV_IMPL int -cvEstimateRigidTransform( const CvArr* matA, const CvArr* matB, CvMat* matM, int full_affine ) +cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullAffine ) { + Mat M(2, 3, CV_64F), A = src1.getMat(), B = src2.getMat(); + const int COUNT = 15; const int WIDTH = 160, HEIGHT = 120; const int RANSAC_MAX_ITERS = 500; const int RANSAC_SIZE0 = 3; const double RANSAC_GOOD_RATIO = 0.5; - cv::Ptr sA, sB; - cv::AutoBuffer pA, pB; - cv::AutoBuffer good_idx; - cv::AutoBuffer status; - cv::Ptr gray; + std::vector pA, pB; + std::vector good_idx; + std::vector status; - CvMat stubA, *A = cvGetMat( matA, &stubA ); - CvMat stubB, *B = cvGetMat( matB, &stubB ); - CvSize sz0, sz1; - int cn, equal_sizes; + double scale = 1.; int i, j, k, k1; - int count_x, count_y, count = 0; - double scale = 1; - CvRNG rng = cvRNG(-1); - double m[6]={0}; - CvMat M = cvMat( 2, 3, CV_64F, m ); + + RNG rng((uint64)-1); int good_count = 0; - CvRect brect; - if( !CV_IS_MAT(matM) ) - CV_Error( matM ? CV_StsBadArg : CV_StsNullPtr, "Output parameter M is not a valid matrix" ); - - if( !CV_ARE_SIZES_EQ( A, B ) ) + if( A.size() != B.size() ) CV_Error( CV_StsUnmatchedSizes, "Both input images must have the same size" ); - if( !CV_ARE_TYPES_EQ( A, B ) ) + if( A.type() != B.type() ) CV_Error( CV_StsUnmatchedFormats, "Both input images must have the same data type" ); - if( CV_MAT_TYPE(A->type) == CV_8UC1 || CV_MAT_TYPE(A->type) == CV_8UC3 ) - { - cn = CV_MAT_CN(A->type); - sz0 = cvGetSize(A); - sz1 = cvSize(WIDTH, HEIGHT); + int count = A.checkVector(2); + + if( count > 0 ) + { + A.reshape(2, count).convertTo(pA, CV_32F); + B.reshape(2, count).convertTo(pB, CV_32F); + } + else if( A.depth() == CV_8U ) + { + int cn = A.channels(); + CV_Assert( cn == 1 || cn == 3 || cn == 4 ); + Size sz0 = A.size(); + Size sz1(WIDTH, HEIGHT); + + scale = std::max(1., std::max( (double)sz1.width/sz0.width, (double)sz1.height/sz0.height )); - scale = MAX( (double)sz1.width/sz0.width, (double)sz1.height/sz0.height ); - scale = MIN( scale, 1. ); sz1.width = cvRound( sz0.width * scale ); sz1.height = cvRound( sz0.height * scale ); - equal_sizes = sz1.width == sz0.width && sz1.height == sz0.height; + bool equalSizes = sz1.width == sz0.width && sz1.height == sz0.height; - if( !equal_sizes || cn != 1 ) + if( !equalSizes || cn != 1 ) { - sA = cvCreateMat( sz1.height, sz1.width, CV_8UC1 ); - sB = cvCreateMat( sz1.height, sz1.width, CV_8UC1 ); + Mat sA, sB; if( cn != 1 ) { - gray = cvCreateMat( sz0.height, sz0.width, CV_8UC1 ); - cvCvtColor( A, gray, CV_BGR2GRAY ); - cvResize( gray, sA, CV_INTER_AREA ); - cvCvtColor( B, gray, CV_BGR2GRAY ); - cvResize( gray, sB, CV_INTER_AREA ); - gray.release(); + Mat gray; + cvtColor(A, gray, COLOR_BGR2GRAY); + resize(gray, sA, sz1, 0., 0., INTER_AREA); + cvtColor(B, gray, COLOR_BGR2GRAY); + resize(gray, sB, sz1, 0., 0., INTER_AREA); } else { - cvResize( A, sA, CV_INTER_AREA ); - cvResize( B, sB, CV_INTER_AREA ); + resize(A, sA, sz1, 0., 0., INTER_AREA); + resize(B, sB, sz1, 0., 0., INTER_AREA); } A = sA; B = sB; } - count_y = COUNT; - count_x = cvRound((double)COUNT*sz1.width/sz1.height); + int count_y = COUNT; + int count_x = cvRound((double)COUNT*sz1.width/sz1.height); count = count_x * count_y; - pA.allocate(count); - pB.allocate(count); - status.allocate(count); + pA.resize(count); + pB.resize(count); + status.resize(count); for( i = 0, k = 0; i < count_y; i++ ) for( j = 0; j < count_x; j++, k++ ) @@ -1766,8 +926,8 @@ cvEstimateRigidTransform( const CvArr* matA, const CvArr* matB, CvMat* matM, int } // find the corresponding points in B - cvCalcOpticalFlowPyrLK( A, B, 0, 0, pA, pB, count, cvSize(10,10), 3, - status, 0, cvTermCriteria(CV_TERMCRIT_ITER,40,0.1), 0 ); + calcOpticalFlowPyrLK(A, B, pA, pB, status, noArray(), Size(21, 21), 3, + TermCriteria(TermCriteria::MAX_ITER,40,0.1)); // repack the remained points for( i = 0, k = 0; i < count; i++ ) @@ -1780,48 +940,34 @@ cvEstimateRigidTransform( const CvArr* matA, const CvArr* matB, CvMat* matM, int } k++; } - count = k; - } - else if( CV_MAT_TYPE(A->type) == CV_32FC2 || CV_MAT_TYPE(A->type) == CV_32SC2 ) - { - count = A->cols*A->rows; - CvMat _pA, _pB; - pA.allocate(count); - pB.allocate(count); - _pA = cvMat( A->rows, A->cols, CV_32FC2, pA ); - _pB = cvMat( B->rows, B->cols, CV_32FC2, pB ); - cvConvert( A, &_pA ); - cvConvert( B, &_pB ); + pA.resize(count); + pB.resize(count); } else CV_Error( CV_StsUnsupportedFormat, "Both input images must have either 8uC1 or 8uC3 type" ); - good_idx.allocate(count); + good_idx.resize(count); if( count < RANSAC_SIZE0 ) - return 0; + return Mat(); - CvMat _pB = cvMat(1, count, CV_32FC2, pB); - brect = cvBoundingRect(&_pB, 1); + Rect brect = boundingRect(pB); // RANSAC stuff: // 1. find the consensus for( k = 0; k < RANSAC_MAX_ITERS; k++ ) { int idx[RANSAC_SIZE0]; - CvPoint2D32f a[3]; - CvPoint2D32f b[3]; - - memset( a, 0, sizeof(a) ); - memset( b, 0, sizeof(b) ); + Point2f a[RANSAC_SIZE0]; + Point2f b[RANSAC_SIZE0]; // choose random 3 non-complanar points from A & B for( i = 0; i < RANSAC_SIZE0; i++ ) { for( k1 = 0; k1 < RANSAC_MAX_ITERS; k1++ ) { - idx[i] = cvRandInt(&rng) % count; + idx[i] = rng.uniform(0, count); for( j = 0; j < i; j++ ) { @@ -1871,12 +1017,13 @@ cvEstimateRigidTransform( const CvArr* matA, const CvArr* matB, CvMat* matM, int continue; // estimate the transformation using 3 points - icvGetRTMatrix( a, b, 3, &M, full_affine ); + getRTMatrix( a, b, 3, M, fullAffine ); + const double* m = M.ptr(); for( i = 0, good_count = 0; i < count; i++ ) { - if( fabs( m[0]*pA[i].x + m[1]*pA[i].y + m[2] - pB[i].x ) + - fabs( m[3]*pA[i].x + m[4]*pA[i].y + m[5] - pB[i].y ) < MAX(brect.width,brect.height)*0.05 ) + if( std::abs( m[0]*pA[i].x + m[1]*pA[i].y + m[2] - pB[i].x ) + + std::abs( m[3]*pA[i].x + m[4]*pA[i].y + m[5] - pB[i].y ) < std::max(brect.width,brect.height)*0.05 ) good_idx[good_count++] = i; } @@ -1885,7 +1032,7 @@ cvEstimateRigidTransform( const CvArr* matA, const CvArr* matB, CvMat* matM, int } if( k >= RANSAC_MAX_ITERS ) - return 0; + return Mat(); if( good_count < count ) { @@ -1897,25 +1044,11 @@ cvEstimateRigidTransform( const CvArr* matA, const CvArr* matB, CvMat* matM, int } } - icvGetRTMatrix( pA, pB, good_count, &M, full_affine ); - m[2] /= scale; - m[5] /= scale; - cvConvert( &M, matM ); + getRTMatrix( &pA[0], &pB[0], good_count, M, fullAffine ); + M.at(0, 2) /= scale; + M.at(1, 2) /= scale; - return 1; -} - -cv::Mat cv::estimateRigidTransform( InputArray src1, - InputArray src2, - bool fullAffine ) -{ - Mat M(2, 3, CV_64F), A = src1.getMat(), B = src2.getMat(); - CvMat matA = A, matB = B, matM = M; - int err = cvEstimateRigidTransform(&matA, &matB, &matM, fullAffine); - if (err == 1) - return M; - else - return Mat(); + return M; } /* End of file. */ diff --git a/modules/video/src/motempl.cpp b/modules/video/src/motempl.cpp index f6e52c49e..7d992684b 100644 --- a/modules/video/src/motempl.cpp +++ b/modules/video/src/motempl.cpp @@ -41,34 +41,23 @@ #include "precomp.hpp" - -/* motion templates */ -CV_IMPL void -cvUpdateMotionHistory( const void* silhouette, void* mhimg, - double timestamp, double mhi_duration ) +void cv::updateMotionHistory( InputArray _silhouette, InputOutputArray _mhi, + double timestamp, double duration ) { - CvMat silhstub, *silh = cvGetMat(silhouette, &silhstub); - CvMat mhistub, *mhi = cvGetMat(mhimg, &mhistub); + Mat silh = _silhouette.getMat(), mhi = _mhi.getMat(); - if( !CV_IS_MASK_ARR( silh )) - CV_Error( CV_StsBadMask, "" ); + CV_Assert( silh.type() == CV_8U && mhi.type() == CV_32F ); + CV_Assert( silh.size() == mhi.size() ); - if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 ) - CV_Error( CV_StsUnsupportedFormat, "" ); - - if( !CV_ARE_SIZES_EQ( mhi, silh )) - CV_Error( CV_StsUnmatchedSizes, "" ); - - CvSize size = cvGetMatSize( mhi ); - - if( CV_IS_MAT_CONT( mhi->type & silh->type )) + Size size = silh.size(); + if( silh.isContinuous() && mhi.isContinuous() ) { size.width *= size.height; size.height = 1; } float ts = (float)timestamp; - float delbound = (float)(timestamp - mhi_duration); + float delbound = (float)(timestamp - duration); int x, y; #if CV_SSE2 volatile bool useSIMD = cv::checkHardwareSupport(CV_CPU_SSE2); @@ -76,8 +65,8 @@ cvUpdateMotionHistory( const void* silhouette, void* mhimg, for( y = 0; y < size.height; y++ ) { - const uchar* silhData = silh->data.ptr + silh->step*y; - float* mhiData = (float*)(mhi->data.ptr + mhi->step*y); + const uchar* silhData = silh.ptr(y); + float* mhiData = mhi.ptr(y); x = 0; #if CV_SSE2 @@ -117,26 +106,21 @@ cvUpdateMotionHistory( const void* silhouette, void* mhimg, } -CV_IMPL void -cvCalcMotionGradient( const CvArr* mhiimg, CvArr* maskimg, - CvArr* orientation, - double delta1, double delta2, - int aperture_size ) +void cv::calcMotionGradient( InputArray _mhi, OutputArray _mask, + OutputArray _orientation, + double delta1, double delta2, + int aperture_size ) { - cv::Ptr dX_min, dY_max; + static int runcase = 0; runcase++; - CvMat mhistub, *mhi = cvGetMat(mhiimg, &mhistub); - CvMat maskstub, *mask = cvGetMat(maskimg, &maskstub); - CvMat orientstub, *orient = cvGetMat(orientation, &orientstub); - CvMat dX_min_row, dY_max_row, orient_row, mask_row; - CvSize size; - int x, y; + Mat mhi = _mhi.getMat(); + Size size = mhi.size(); - float gradient_epsilon = 1e-4f * aperture_size * aperture_size; - float min_delta, max_delta; + _mask.create(size, CV_8U); + _orientation.create(size, CV_32F); - if( !CV_IS_MASK_ARR( mask )) - CV_Error( CV_StsBadMask, "" ); + Mat mask = _mask.getMat(); + Mat orient = _orientation.getMat(); if( aperture_size < 3 || aperture_size > 7 || (aperture_size & 1) == 0 ) CV_Error( CV_StsOutOfRange, "aperture_size must be 3, 5 or 7" ); @@ -144,343 +128,247 @@ cvCalcMotionGradient( const CvArr* mhiimg, CvArr* maskimg, if( delta1 <= 0 || delta2 <= 0 ) CV_Error( CV_StsOutOfRange, "both delta's must be positive" ); - if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 || CV_MAT_TYPE( orient->type ) != CV_32FC1 ) + if( mhi.type() != CV_32FC1 ) CV_Error( CV_StsUnsupportedFormat, - "MHI and orientation must be single-channel floating-point images" ); + "MHI must be single-channel floating-point images" ); - if( !CV_ARE_SIZES_EQ( mhi, mask ) || !CV_ARE_SIZES_EQ( orient, mhi )) - CV_Error( CV_StsUnmatchedSizes, "" ); - - if( orient->data.ptr == mhi->data.ptr ) - CV_Error( CV_StsInplaceNotSupported, "orientation image must be different from MHI" ); - - if( delta1 > delta2 ) + if( orient.data == mhi.data ) { - double t; - CV_SWAP( delta1, delta2, t ); + _orientation.release(); + _orientation.create(size, CV_32F); + orient = _orientation.getMat(); } - size = cvGetMatSize( mhi ); - min_delta = (float)delta1; - max_delta = (float)delta2; - dX_min = cvCreateMat( mhi->rows, mhi->cols, CV_32F ); - dY_max = cvCreateMat( mhi->rows, mhi->cols, CV_32F ); + if( delta1 > delta2 ) + std::swap(delta1, delta2); + + float gradient_epsilon = 1e-4f * aperture_size * aperture_size; + float min_delta = (float)delta1; + float max_delta = (float)delta2; + + Mat dX_min, dY_max; // calc Dx and Dy - cvSobel( mhi, dX_min, 1, 0, aperture_size ); - cvSobel( mhi, dY_max, 0, 1, aperture_size ); - cvGetRow( dX_min, &dX_min_row, 0 ); - cvGetRow( dY_max, &dY_max_row, 0 ); - cvGetRow( orient, &orient_row, 0 ); - cvGetRow( mask, &mask_row, 0 ); + Sobel( mhi, dX_min, CV_32F, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE ); + Sobel( mhi, dY_max, CV_32F, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE ); + + int x, y; + + if( mhi.isContinuous() && orient.isContinuous() && mask.isContinuous() ) + { + size.width *= size.height; + size.height = 1; + } // calc gradient for( y = 0; y < size.height; y++ ) { - dX_min_row.data.ptr = dX_min->data.ptr + y*dX_min->step; - dY_max_row.data.ptr = dY_max->data.ptr + y*dY_max->step; - orient_row.data.ptr = orient->data.ptr + y*orient->step; - mask_row.data.ptr = mask->data.ptr + y*mask->step; - cvCartToPolar( &dX_min_row, &dY_max_row, 0, &orient_row, 1 ); + const float* dX_min_row = dX_min.ptr(y); + const float* dY_max_row = dY_max.ptr(y); + float* orient_row = orient.ptr(y); + uchar* mask_row = mask.ptr(y); + + fastAtan2(dY_max_row, dX_min_row, orient_row, size.width, true); // make orientation zero where the gradient is very small for( x = 0; x < size.width; x++ ) { - float dY = dY_max_row.data.fl[x]; - float dX = dX_min_row.data.fl[x]; + float dY = dY_max_row[x]; + float dX = dX_min_row[x]; - if( fabs(dX) < gradient_epsilon && fabs(dY) < gradient_epsilon ) + if( std::abs(dX) < gradient_epsilon && std::abs(dY) < gradient_epsilon ) { - mask_row.data.ptr[x] = 0; - orient_row.data.i[x] = 0; + mask_row[x] = (uchar)0; + orient_row[x] = 0.f; } else - mask_row.data.ptr[x] = 1; + mask_row[x] = (uchar)1; } } - cvErode( mhi, dX_min, 0, (aperture_size-1)/2); - cvDilate( mhi, dY_max, 0, (aperture_size-1)/2); + erode( mhi, dX_min, noArray(), Point(-1,-1), (aperture_size-1)/2, BORDER_REPLICATE ); + dilate( mhi, dY_max, noArray(), Point(-1,-1), (aperture_size-1)/2, BORDER_REPLICATE ); // mask off pixels which have little motion difference in their neighborhood for( y = 0; y < size.height; y++ ) { - dX_min_row.data.ptr = dX_min->data.ptr + y*dX_min->step; - dY_max_row.data.ptr = dY_max->data.ptr + y*dY_max->step; - mask_row.data.ptr = mask->data.ptr + y*mask->step; - orient_row.data.ptr = orient->data.ptr + y*orient->step; + const float* dX_min_row = dX_min.ptr(y); + const float* dY_max_row = dY_max.ptr(y); + float* orient_row = orient.ptr(y); + uchar* mask_row = mask.ptr(y); for( x = 0; x < size.width; x++ ) { - float d0 = dY_max_row.data.fl[x] - dX_min_row.data.fl[x]; + float d0 = dY_max_row[x] - dX_min_row[x]; - if( mask_row.data.ptr[x] == 0 || d0 < min_delta || max_delta < d0 ) + if( mask_row[x] == 0 || d0 < min_delta || max_delta < d0 ) { - mask_row.data.ptr[x] = 0; - orient_row.data.i[x] = 0; + mask_row[x] = (uchar)0; + orient_row[x] = 0.f; } } } } - -CV_IMPL double -cvCalcGlobalOrientation( const void* orientation, const void* maskimg, const void* mhiimg, - double curr_mhi_timestamp, double mhi_duration ) -{ - int hist_size = 12; - cv::Ptr hist; - - CvMat mhistub, *mhi = cvGetMat(mhiimg, &mhistub); - CvMat maskstub, *mask = cvGetMat(maskimg, &maskstub); - CvMat orientstub, *orient = cvGetMat(orientation, &orientstub); - void* _orient; - float _ranges[] = { 0, 360 }; - float* ranges = _ranges; - int base_orient; - float shift_orient = 0, shift_weight = 0; - float a, b, fbase_orient; - float delbound; - CvMat mhi_row, mask_row, orient_row; - int x, y, mhi_rows, mhi_cols; - - if( !CV_IS_MASK_ARR( mask )) - CV_Error( CV_StsBadMask, "" ); - - if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 || CV_MAT_TYPE( orient->type ) != CV_32FC1 ) - CV_Error( CV_StsUnsupportedFormat, - "MHI and orientation must be single-channel floating-point images" ); - - if( !CV_ARE_SIZES_EQ( mhi, mask ) || !CV_ARE_SIZES_EQ( orient, mhi )) - CV_Error( CV_StsUnmatchedSizes, "" ); - - if( mhi_duration <= 0 ) - CV_Error( CV_StsOutOfRange, "MHI duration must be positive" ); - - if( orient->data.ptr == mhi->data.ptr ) - CV_Error( CV_StsInplaceNotSupported, "orientation image must be different from MHI" ); - - // calculate histogram of different orientation values - hist = cvCreateHist( 1, &hist_size, CV_HIST_ARRAY, &ranges ); - _orient = orient; - cvCalcArrHist( &_orient, hist, 0, mask ); - - // find the maximum index (the dominant orientation) - cvGetMinMaxHistValue( hist, 0, 0, 0, &base_orient ); - fbase_orient = base_orient*360.f/hist_size; - - // override timestamp with the maximum value in MHI - cvMinMaxLoc( mhi, 0, &curr_mhi_timestamp, 0, 0, mask ); - - // find the shift relative to the dominant orientation as weighted sum of relative angles - a = (float)(254. / 255. / mhi_duration); - b = (float)(1. - curr_mhi_timestamp * a); - delbound = (float)(curr_mhi_timestamp - mhi_duration); - mhi_rows = mhi->rows; - mhi_cols = mhi->cols; - - if( CV_IS_MAT_CONT( mhi->type & mask->type & orient->type )) - { - mhi_cols *= mhi_rows; - mhi_rows = 1; - } - - cvGetRow( mhi, &mhi_row, 0 ); - cvGetRow( mask, &mask_row, 0 ); - cvGetRow( orient, &orient_row, 0 ); - - /* - a = 254/(255*dt) - b = 1 - t*a = 1 - 254*t/(255*dur) = - (255*dt - 254*t)/(255*dt) = - (dt - (t - dt)*254)/(255*dt); - -------------------------------------------------------- - ax + b = 254*x/(255*dt) + (dt - (t - dt)*254)/(255*dt) = - (254*x + dt - (t - dt)*254)/(255*dt) = - ((x - (t - dt))*254 + dt)/(255*dt) = - (((x - (t - dt))/dt)*254 + 1)/255 = (((x - low_time)/dt)*254 + 1)/255 - */ - for( y = 0; y < mhi_rows; y++ ) - { - mhi_row.data.ptr = mhi->data.ptr + mhi->step*y; - mask_row.data.ptr = mask->data.ptr + mask->step*y; - orient_row.data.ptr = orient->data.ptr + orient->step*y; - - for( x = 0; x < mhi_cols; x++ ) - if( mask_row.data.ptr[x] != 0 && mhi_row.data.fl[x] > delbound ) - { - /* - orient in 0..360, base_orient in 0..360 - -> (rel_angle = orient - base_orient) in -360..360. - rel_angle is translated to -180..180 - */ - float weight = mhi_row.data.fl[x] * a + b; - float rel_angle = orient_row.data.fl[x] - fbase_orient; - - rel_angle += (rel_angle < -180 ? 360 : 0); - rel_angle += (rel_angle > 180 ? -360 : 0); - - if( fabs(rel_angle) < 45 ) - { - shift_orient += weight * rel_angle; - shift_weight += weight; - } - } - } - - // add the dominant orientation and the relative shift - if( shift_weight == 0 ) - shift_weight = 0.01f; - - fbase_orient += shift_orient / shift_weight; - fbase_orient -= (fbase_orient < 360 ? 0 : 360); - fbase_orient += (fbase_orient >= 0 ? 0 : 360); - - return fbase_orient; -} - - -CV_IMPL CvSeq* -cvSegmentMotion( const CvArr* mhiimg, CvArr* segmask, CvMemStorage* storage, - double timestamp, double seg_thresh ) -{ - CvSeq* components = 0; - cv::Ptr mask8u; - - CvMat mhistub, *mhi = cvGetMat(mhiimg, &mhistub); - CvMat maskstub, *mask = cvGetMat(segmask, &maskstub); - Cv32suf v, comp_idx; - int stub_val, ts; - int x, y; - - if( !storage ) - CV_Error( CV_StsNullPtr, "NULL memory storage" ); - - mhi = cvGetMat( mhi, &mhistub ); - mask = cvGetMat( mask, &maskstub ); - - if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 || CV_MAT_TYPE( mask->type ) != CV_32FC1 ) - CV_Error( CV_BadDepth, "Both MHI and the destination mask" ); - - if( !CV_ARE_SIZES_EQ( mhi, mask )) - CV_Error( CV_StsUnmatchedSizes, "" ); - - mask8u = cvCreateMat( mhi->rows + 2, mhi->cols + 2, CV_8UC1 ); - cvZero( mask8u ); - cvZero( mask ); - components = cvCreateSeq( CV_SEQ_KIND_GENERIC, sizeof(CvSeq), - sizeof(CvConnectedComp), storage ); - - v.f = (float)timestamp; ts = v.i; - v.f = FLT_MAX*0.1f; stub_val = v.i; - comp_idx.f = 1; - - for( y = 0; y < mhi->rows; y++ ) - { - int* mhi_row = (int*)(mhi->data.ptr + y*mhi->step); - for( x = 0; x < mhi->cols; x++ ) - { - if( mhi_row[x] == 0 ) - mhi_row[x] = stub_val; - } - } - - for( y = 0; y < mhi->rows; y++ ) - { - int* mhi_row = (int*)(mhi->data.ptr + y*mhi->step); - uchar* mask8u_row = mask8u->data.ptr + (y+1)*mask8u->step + 1; - - for( x = 0; x < mhi->cols; x++ ) - { - if( mhi_row[x] == ts && mask8u_row[x] == 0 ) - { - CvConnectedComp comp; - int x1, y1; - CvScalar _seg_thresh = cvRealScalar(seg_thresh); - CvPoint seed = cvPoint(x,y); - - cvFloodFill( mhi, seed, cvRealScalar(0), _seg_thresh, _seg_thresh, - &comp, CV_FLOODFILL_MASK_ONLY + 2*256 + 4, mask8u ); - - for( y1 = 0; y1 < comp.rect.height; y1++ ) - { - int* mask_row1 = (int*)(mask->data.ptr + - (comp.rect.y + y1)*mask->step) + comp.rect.x; - uchar* mask8u_row1 = mask8u->data.ptr + - (comp.rect.y + y1+1)*mask8u->step + comp.rect.x+1; - - for( x1 = 0; x1 < comp.rect.width; x1++ ) - { - if( mask8u_row1[x1] > 1 ) - { - mask8u_row1[x1] = 1; - mask_row1[x1] = comp_idx.i; - } - } - } - comp_idx.f++; - cvSeqPush( components, &comp ); - } - } - } - - for( y = 0; y < mhi->rows; y++ ) - { - int* mhi_row = (int*)(mhi->data.ptr + y*mhi->step); - for( x = 0; x < mhi->cols; x++ ) - { - if( mhi_row[x] == stub_val ) - mhi_row[x] = 0; - } - } - - return components; -} - - -void cv::updateMotionHistory( InputArray _silhouette, InputOutputArray _mhi, - double timestamp, double duration ) -{ - Mat silhouette = _silhouette.getMat(); - CvMat c_silhouette = silhouette, c_mhi = _mhi.getMat(); - cvUpdateMotionHistory( &c_silhouette, &c_mhi, timestamp, duration ); -} - -void cv::calcMotionGradient( InputArray _mhi, OutputArray _mask, - OutputArray _orientation, - double delta1, double delta2, - int aperture_size ) -{ - Mat mhi = _mhi.getMat(); - _mask.create(mhi.size(), CV_8U); - _orientation.create(mhi.size(), CV_32F); - CvMat c_mhi = mhi, c_mask = _mask.getMat(), c_orientation = _orientation.getMat(); - cvCalcMotionGradient(&c_mhi, &c_mask, &c_orientation, delta1, delta2, aperture_size); -} - double cv::calcGlobalOrientation( InputArray _orientation, InputArray _mask, - InputArray _mhi, double timestamp, + InputArray _mhi, double /*timestamp*/, double duration ) { - Mat orientation = _orientation.getMat(), mask = _mask.getMat(), mhi = _mhi.getMat(); - CvMat c_orientation = orientation, c_mask = mask, c_mhi = mhi; - return cvCalcGlobalOrientation(&c_orientation, &c_mask, &c_mhi, timestamp, duration); + Mat orient = _orientation.getMat(), mask = _mask.getMat(), mhi = _mhi.getMat(); + Size size = mhi.size(); + + CV_Assert( mask.type() == CV_8U && orient.type() == CV_32F && mhi.type() == CV_32F ); + CV_Assert( mask.size() == size && orient.size() == size ); + CV_Assert( duration > 0 ); + + int histSize = 12; + float _ranges[] = { 0.f, 360.f }; + const float* ranges = _ranges; + Mat hist; + + calcHist(&orient, 1, 0, mask, hist, 1, &histSize, &ranges); + + // find the maximum index (the dominant orientation) + Point baseOrientPt; + minMaxLoc(hist, 0, 0, 0, &baseOrientPt); + float fbaseOrient = (baseOrientPt.x + baseOrientPt.y)*360.f/histSize; + + // override timestamp with the maximum value in MHI + double timestamp = 0; + minMaxLoc( mhi, 0, ×tamp, 0, 0, mask ); + + // find the shift relative to the dominant orientation as weighted sum of relative angles + float a = (float)(254. / 255. / duration); + float b = (float)(1. - timestamp * a); + float delbound = (float)(timestamp - duration); + + if( mhi.isContinuous() && mask.isContinuous() && orient.isContinuous() ) + { + size.width *= size.height; + size.height = 1; + } + + /* + a = 254/(255*dt) + b = 1 - t*a = 1 - 254*t/(255*dur) = + (255*dt - 254*t)/(255*dt) = + (dt - (t - dt)*254)/(255*dt); + -------------------------------------------------------- + ax + b = 254*x/(255*dt) + (dt - (t - dt)*254)/(255*dt) = + (254*x + dt - (t - dt)*254)/(255*dt) = + ((x - (t - dt))*254 + dt)/(255*dt) = + (((x - (t - dt))/dt)*254 + 1)/255 = (((x - low_time)/dt)*254 + 1)/255 + */ + float shiftOrient = 0, shiftWeight = 0; + for( int y = 0; y < size.height; y++ ) + { + const float* mhiptr = mhi.ptr(y); + const float* oriptr = orient.ptr(y); + const uchar* maskptr = mask.ptr(y); + + for( int x = 0; x < size.width; x++ ) + { + if( maskptr[x] != 0 && mhiptr[x] > delbound ) + { + /* + orient in 0..360, base_orient in 0..360 + -> (rel_angle = orient - base_orient) in -360..360. + rel_angle is translated to -180..180 + */ + float weight = mhiptr[x] * a + b; + float relAngle = oriptr[x] - fbaseOrient; + + relAngle += (relAngle < -180 ? 360 : 0); + relAngle += (relAngle > 180 ? -360 : 0); + + if( fabs(relAngle) < 45 ) + { + shiftOrient += weight * relAngle; + shiftWeight += weight; + } + } + } + } + + // add the dominant orientation and the relative shift + if( shiftWeight == 0 ) + shiftWeight = 0.01f; + + fbaseOrient += shiftOrient / shiftWeight; + fbaseOrient -= (fbaseOrient < 360 ? 0 : 360); + fbaseOrient += (fbaseOrient >= 0 ? 0 : 360); + + return fbaseOrient; } + void cv::segmentMotion(InputArray _mhi, OutputArray _segmask, std::vector& boundingRects, double timestamp, double segThresh) { Mat mhi = _mhi.getMat(); + _segmask.create(mhi.size(), CV_32F); - CvMat c_mhi = mhi, c_segmask = _segmask.getMat(); - Ptr storage = cvCreateMemStorage(); - Seq comps = cvSegmentMotion(&c_mhi, &c_segmask, storage, timestamp, segThresh); - Seq::const_iterator it(comps); - size_t i, ncomps = comps.size(); - boundingRects.resize(ncomps); - for( i = 0; i < ncomps; i++, ++it) - boundingRects[i] = (*it).rect; + Mat segmask = _segmask.getMat(); + segmask = Scalar::all(0); + + CV_Assert( mhi.type() == CV_32F ); + CV_Assert( segThresh >= 0 ); + + Mat mask = Mat::zeros( mhi.rows + 2, mhi.cols + 2, CV_8UC1 ); + + int x, y; + + // protect zero mhi pixels from floodfill. + for( y = 0; y < mhi.rows; y++ ) + { + const float* mhiptr = mhi.ptr(y); + uchar* maskptr = mask.ptr(y+1) + 1; + + for( x = 0; x < mhi.cols; x++ ) + { + if( mhiptr[x] == 0 ) + maskptr[x] = 1; + } + } + + float ts = (float)timestamp; + float comp_idx = 1.f; + + for( y = 0; y < mhi.rows; y++ ) + { + float* mhiptr = mhi.ptr(y); + uchar* maskptr = mask.ptr(y+1) + 1; + + for( x = 0; x < mhi.cols; x++ ) + { + if( mhiptr[x] == ts && maskptr[x] == 0 ) + { + Rect cc; + floodFill( mhi, mask, Point(x,y), Scalar::all(0), + &cc, Scalar::all(segThresh), Scalar::all(segThresh), + FLOODFILL_MASK_ONLY + 2*256 + 4 ); + + for( int y1 = 0; y1 < cc.height; y1++ ) + { + float* segmaskptr = segmask.ptr(cc.y + y1) + cc.x; + uchar* maskptr1 = mask.ptr(cc.y + y1 + 1) + cc.x + 1; + + for( int x1 = 0; x1 < cc.width; x1++ ) + { + if( maskptr1[x1] > 1 ) + { + maskptr1[x1] = 1; + segmaskptr[x1] = comp_idx; + } + } + } + comp_idx += 1.f; + boundingRects.push_back(cc); + } + } + } } + /* End of file. */ diff --git a/modules/video/src/optflowgf.cpp b/modules/video/src/optflowgf.cpp index 650e24262..6784a55cd 100644 --- a/modules/video/src/optflowgf.cpp +++ b/modules/video/src/optflowgf.cpp @@ -644,18 +644,3 @@ void cv::calcOpticalFlowFarneback( InputArray _prev0, InputArray _next0, prevFlow = flow; } } - - -CV_IMPL void cvCalcOpticalFlowFarneback( - const CvArr* _prev, const CvArr* _next, - CvArr* _flow, double pyr_scale, int levels, - int winsize, int iterations, int poly_n, - double poly_sigma, int flags ) -{ - cv::Mat prev = cv::cvarrToMat(_prev), next = cv::cvarrToMat(_next); - cv::Mat flow = cv::cvarrToMat(_flow); - CV_Assert( flow.size() == prev.size() && flow.type() == CV_32FC2 ); - cv::calcOpticalFlowFarneback( prev, next, flow, pyr_scale, levels, - winsize, iterations, poly_n, poly_sigma, flags ); -} - diff --git a/modules/video/src/simpleflow.cpp b/modules/video/src/simpleflow.cpp index 8c5c74624..f987b42ee 100644 --- a/modules/video/src/simpleflow.cpp +++ b/modules/video/src/simpleflow.cpp @@ -41,7 +41,6 @@ //M*/ #include "precomp.hpp" -#include "simpleflow.hpp" // // 2D dense optical flow algorithm from the following paper: @@ -54,6 +53,39 @@ namespace cv { +static const uchar MASK_TRUE_VALUE = (uchar)255; + +inline static float dist(const Vec3b& p1, const Vec3b& p2) { + return (float)((p1[0] - p2[0]) * (p1[0] - p2[0]) + + (p1[1] - p2[1]) * (p1[1] - p2[1]) + + (p1[2] - p2[2]) * (p1[2] - p2[2])); +} + +inline static float dist(const Vec2f& p1, const Vec2f& p2) { + return (p1[0] - p2[0]) * (p1[0] - p2[0]) + + (p1[1] - p2[1]) * (p1[1] - p2[1]); +} + +inline static float dist(const Point2f& p1, const Point2f& p2) { + return (p1.x - p2.x) * (p1.x - p2.x) + + (p1.y - p2.y) * (p1.y - p2.y); +} + +inline static float dist(float x1, float y1, float x2, float y2) { + return (x1 - x2) * (x1 - x2) + + (y1 - y2) * (y1 - y2); +} + +inline static int dist(int x1, int y1, int x2, int y2) { + return (x1 - x2) * (x1 - x2) + + (y1 - y2) * (y1 - y2); +} + +template +inline static T min(T t1, T t2, T t3) { + return (t1 <= t2 && t1 <= t3) ? t1 : min(t2, t3); +} + static void removeOcclusions(const Mat& flow, const Mat& flow_inv, float occ_thr, diff --git a/modules/video/src/video_init.cpp b/modules/video/src/video_init.cpp index 189f4a36b..118d28a42 100644 --- a/modules/video/src/video_init.cpp +++ b/modules/video/src/video_init.cpp @@ -46,50 +46,9 @@ namespace cv { -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(BackgroundSubtractorMOG, "BackgroundSubtractor.MOG", - obj.info()->addParam(obj, "history", obj.history); - obj.info()->addParam(obj, "nmixtures", obj.nmixtures); - obj.info()->addParam(obj, "backgroundRatio", obj.backgroundRatio); - obj.info()->addParam(obj, "noiseSigma", obj.noiseSigma)); - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(BackgroundSubtractorMOG2, "BackgroundSubtractor.MOG2", - obj.info()->addParam(obj, "history", obj.history); - obj.info()->addParam(obj, "nmixtures", obj.nmixtures); - obj.info()->addParam(obj, "varThreshold", obj.varThreshold); - obj.info()->addParam(obj, "detectShadows", obj.bShadowDetection)); - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(BackgroundSubtractorGMG, "BackgroundSubtractor.GMG", - obj.info()->addParam(obj, "maxFeatures", obj.maxFeatures,false,0,0, - "Maximum number of features to store in histogram. Harsh enforcement of sparsity constraint."); - obj.info()->addParam(obj, "learningRate", obj.learningRate,false,0,0, - "Adaptation rate of histogram. Close to 1, slow adaptation. Close to 0, fast adaptation, features forgotten quickly."); - obj.info()->addParam(obj, "initializationFrames", obj.numInitializationFrames,false,0,0, - "Number of frames to use to initialize histograms of pixels."); - obj.info()->addParam(obj, "quantizationLevels", obj.quantizationLevels,false,0,0, - "Number of discrete colors to be used in histograms. Up-front quantization."); - obj.info()->addParam(obj, "backgroundPrior", obj.backgroundPrior,false,0,0, - "Prior probability that each individual pixel is a background pixel."); - obj.info()->addParam(obj, "smoothingRadius", obj.smoothingRadius,false,0,0, - "Radius of smoothing kernel to filter noise from FG mask image."); - obj.info()->addParam(obj, "decisionThreshold", obj.decisionThreshold,false,0,0, - "Threshold for FG decision rule. Pixel is FG if posterior probability exceeds threshold."); - obj.info()->addParam(obj, "updateBackgroundModel", obj.updateBackgroundModel,false,0,0, - "Perform background model update.")); - bool initModule_video(void) { - bool all = true; - all &= !BackgroundSubtractorMOG_info_auto.name().empty(); - all &= !BackgroundSubtractorMOG2_info_auto.name().empty(); - all &= !BackgroundSubtractorGMG_info_auto.name().empty(); - - return all; + return true; } } diff --git a/modules/video/test/test_backgroundsubtractor_gbh.cpp b/modules/video/test/test_backgroundsubtractor_gbh.cpp index 00d6fb05e..649a18b85 100644 --- a/modules/video/test/test_backgroundsubtractor_gbh.cpp +++ b/modules/video/test/test_backgroundsubtractor_gbh.cpp @@ -37,8 +37,7 @@ void CV_BackgroundSubtractorTest::run(int) int width = 2 + ((unsigned int)rng)%98; //!< Mat will be 2 to 100 in width and height int height = 2 + ((unsigned int)rng)%98; - Ptr fgbg = - Algorithm::create("BackgroundSubtractor.GMG"); + Ptr fgbg = createBackgroundSubtractorGMG(); Mat fgmask; if (fgbg.empty()) @@ -47,19 +46,13 @@ void CV_BackgroundSubtractorTest::run(int) /** * Set a few parameters */ - fgbg->set("smoothingRadius",7); - fgbg->set("decisionThreshold",0.7); - fgbg->set("initializationFrames",120); + fgbg->setSmoothingRadius(7); + fgbg->setDecisionThreshold(0.7); + fgbg->setNumFrames(120); /** * Generate bounds for the values in the matrix for each type */ - uchar maxuc = 0, minuc = 0; - char maxc = 0, minc = 0; - unsigned int maxui = 0, minui = 0; - int maxi=0, mini = 0; - long int maxli = 0, minli = 0; - float maxf = 0, minf = 0; double maxd = 0, mind = 0; /** @@ -69,34 +62,34 @@ void CV_BackgroundSubtractorTest::run(int) if (type == CV_8U) { uchar half = UCHAR_MAX/2; - maxuc = (unsigned char)rng.uniform(half+32, UCHAR_MAX); - minuc = (unsigned char)rng.uniform(0, half-32); + maxd = (unsigned char)rng.uniform(half+32, UCHAR_MAX); + mind = (unsigned char)rng.uniform(0, half-32); } else if (type == CV_8S) { - maxc = (char)rng.uniform(32, CHAR_MAX); - minc = (char)rng.uniform(CHAR_MIN, -32); + maxd = (char)rng.uniform(32, CHAR_MAX); + mind = (char)rng.uniform(CHAR_MIN, -32); } else if (type == CV_16U) { ushort half = USHRT_MAX/2; - maxui = (unsigned int)rng.uniform(half+32, USHRT_MAX); - minui = (unsigned int)rng.uniform(0, half-32); + maxd = (unsigned int)rng.uniform(half+32, USHRT_MAX); + mind = (unsigned int)rng.uniform(0, half-32); } else if (type == CV_16S) { - maxi = rng.uniform(32, SHRT_MAX); - mini = rng.uniform(SHRT_MIN, -32); + maxd = rng.uniform(32, SHRT_MAX); + mind = rng.uniform(SHRT_MIN, -32); } else if (type == CV_32S) { - maxli = rng.uniform(32, INT_MAX); - minli = rng.uniform(INT_MIN, -32); + maxd = rng.uniform(32, INT_MAX); + mind = rng.uniform(INT_MIN, -32); } else if (type == CV_32F) { - maxf = rng.uniform(32.0f, FLT_MAX); - minf = rng.uniform(-FLT_MAX, -32.0f); + maxd = rng.uniform(32.0f, FLT_MAX); + mind = rng.uniform(-FLT_MAX, -32.0f); } else if (type == CV_64F) { @@ -104,60 +97,22 @@ void CV_BackgroundSubtractorTest::run(int) mind = rng.uniform(-DBL_MAX, -32.0); } + fgbg->setMinVal(mind); + fgbg->setMaxVal(maxd); + Mat simImage = Mat::zeros(height, width, channelsAndType); - const unsigned int numLearningFrames = 120; - for (unsigned int i = 0; i < numLearningFrames; ++i) + int numLearningFrames = 120; + for (int i = 0; i < numLearningFrames; ++i) { /** * Genrate simulated "image" for any type. Values always confined to upper half of range. */ - if (type == CV_8U) - { - rng.fill(simImage,RNG::UNIFORM,(unsigned char)(minuc/2+maxuc/2),maxuc); - if (i == 0) - fgbg->initialize(simImage.size(),minuc,maxuc); - } - else if (type == CV_8S) - { - rng.fill(simImage,RNG::UNIFORM,(char)(minc/2+maxc/2),maxc); - if (i==0) - fgbg->initialize(simImage.size(),minc,maxc); - } - else if (type == CV_16U) - { - rng.fill(simImage,RNG::UNIFORM,(unsigned int)(minui/2+maxui/2),maxui); - if (i==0) - fgbg->initialize(simImage.size(),minui,maxui); - } - else if (type == CV_16S) - { - rng.fill(simImage,RNG::UNIFORM,(int)(mini/2+maxi/2),maxi); - if (i==0) - fgbg->initialize(simImage.size(),mini,maxi); - } - else if (type == CV_32F) - { - rng.fill(simImage,RNG::UNIFORM,(float)(minf/2.0+maxf/2.0),maxf); - if (i==0) - fgbg->initialize(simImage.size(),minf,maxf); - } - else if (type == CV_32S) - { - rng.fill(simImage,RNG::UNIFORM,(long int)(minli/2+maxli/2),maxli); - if (i==0) - fgbg->initialize(simImage.size(),minli,maxli); - } - else if (type == CV_64F) - { - rng.fill(simImage,RNG::UNIFORM,(double)(mind/2.0+maxd/2.0),maxd); - if (i==0) - fgbg->initialize(simImage.size(),mind,maxd); - } + rng.fill(simImage, RNG::UNIFORM, (mind + maxd)*0.5, maxd); /** * Feed simulated images into background subtractor */ - (*fgbg)(simImage,fgmask); + fgbg->apply(simImage,fgmask); Mat fullbg = Mat::zeros(simImage.rows, simImage.cols, CV_8U); //! fgmask should be entirely background during training @@ -166,22 +121,9 @@ void CV_BackgroundSubtractorTest::run(int) ts->set_failed_test_info( code ); } //! generate last image, distinct from training images - if (type == CV_8U) - rng.fill(simImage,RNG::UNIFORM,minuc,minuc); - else if (type == CV_8S) - rng.fill(simImage,RNG::UNIFORM,minc,minc); - else if (type == CV_16U) - rng.fill(simImage,RNG::UNIFORM,minui,minui); - else if (type == CV_16S) - rng.fill(simImage,RNG::UNIFORM,mini,mini); - else if (type == CV_32F) - rng.fill(simImage,RNG::UNIFORM,minf,minf); - else if (type == CV_32S) - rng.fill(simImage,RNG::UNIFORM,minli,minli); - else if (type == CV_64F) - rng.fill(simImage,RNG::UNIFORM,mind,mind); + rng.fill(simImage, RNG::UNIFORM, mind, maxd); - (*fgbg)(simImage,fgmask); + fgbg->apply(simImage,fgmask); //! now fgmask should be entirely foreground Mat fullfg = 255*Mat::ones(simImage.rows, simImage.cols, CV_8U); code = cvtest::cmpEps2( ts, fgmask, fullfg, 255, false, "The final foreground mask" ); diff --git a/modules/video/test/test_estimaterigid.cpp b/modules/video/test/test_estimaterigid.cpp index ddfd007a1..47122fe59 100644 --- a/modules/video/test/test_estimaterigid.cpp +++ b/modules/video/test/test_estimaterigid.cpp @@ -153,7 +153,7 @@ bool CV_RigidTransform_Test::testImage() Mat aff_est = estimateRigidTransform(img, rotated, true); - const double thres = 0.03; + const double thres = 0.033; if (norm(aff_est, aff, NORM_INF) > thres) { ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); diff --git a/samples/cpp/bgfg_gmg.cpp b/samples/cpp/bgfg_gmg.cpp index 3d2da44ab..a9197d194 100644 --- a/samples/cpp/bgfg_gmg.cpp +++ b/samples/cpp/bgfg_gmg.cpp @@ -32,16 +32,13 @@ int main(int argc, char** argv) setUseOptimized(true); setNumThreads(8); - Ptr fgbg = Algorithm::create("BackgroundSubtractor.GMG"); + Ptr fgbg = createBackgroundSubtractorGMG(20, 0.7); if (fgbg.empty()) { std::cerr << "Failed to create BackgroundSubtractor.GMG Algorithm." << std::endl; return -1; } - fgbg->set("initializationFrames", 20); - fgbg->set("decisionThreshold", 0.7); - VideoCapture cap; if (argc > 1) cap.open(argv[1]); @@ -65,9 +62,9 @@ int main(int argc, char** argv) if (frame.empty()) break; - (*fgbg)(frame, fgmask); + fgbg->apply(frame, fgmask); - frame.copyTo(segm); + frame.convertTo(segm, CV_8U, 0.5); add(frame, Scalar(100, 100, 0), segm, fgmask); imshow("FG Segmentation", segm); diff --git a/samples/cpp/bgfg_segm.cpp b/samples/cpp/bgfg_segm.cpp index 399c7ad28..00d0cbe8f 100644 --- a/samples/cpp/bgfg_segm.cpp +++ b/samples/cpp/bgfg_segm.cpp @@ -51,7 +51,7 @@ int main(int argc, const char** argv) namedWindow("foreground image", CV_WINDOW_NORMAL); namedWindow("mean background image", CV_WINDOW_NORMAL); - BackgroundSubtractorMOG2 bg_model;//(100, 3, 0.3, 5); + Ptr bg_model = createBackgroundSubtractorMOG2(); Mat img, fgmask, fgimg; @@ -68,13 +68,13 @@ int main(int argc, const char** argv) fgimg.create(img.size(), img.type()); //update the model - bg_model(img, fgmask, update_bg_model ? -1 : 0); + bg_model->apply(img, fgmask, update_bg_model ? -1 : 0); fgimg = Scalar::all(0); img.copyTo(fgimg, fgmask); Mat bgimg; - bg_model.getBackgroundImage(bgimg); + bg_model->getBackgroundImage(bgimg); imshow("image", img); imshow("foreground mask", fgmask); diff --git a/samples/cpp/segment_objects.cpp b/samples/cpp/segment_objects.cpp index 891b63cff..d70d0350b 100644 --- a/samples/cpp/segment_objects.cpp +++ b/samples/cpp/segment_objects.cpp @@ -87,15 +87,15 @@ int main(int argc, char** argv) namedWindow("video", 1); namedWindow("segmented", 1); - BackgroundSubtractorMOG bgsubtractor; - bgsubtractor.set("noiseSigma", 10); + Ptr bgsubtractor=createBackgroundSubtractorMOG(); + bgsubtractor->setNoiseSigma(10); for(;;) { cap >> tmp_frame; if( !tmp_frame.data ) break; - bgsubtractor(tmp_frame, bgmask, update_bg_model ? -1 : 0); + bgsubtractor->apply(tmp_frame, bgmask, update_bg_model ? -1 : 0); //CvMat _bgmask = bgmask; //cvSegmentFGMask(&_bgmask); refineSegments(tmp_frame, bgmask, out_frame); diff --git a/samples/gpu/performance/tests.cpp b/samples/gpu/performance/tests.cpp index b35102d86..59aa1e457 100644 --- a/samples/gpu/performance/tests.cpp +++ b/samples/gpu/performance/tests.cpp @@ -1316,10 +1316,10 @@ TEST(MOG) cv::Mat frame; cap >> frame; - cv::BackgroundSubtractorMOG mog; + cv::Ptr mog = cv::createBackgroundSubtractorMOG(); cv::Mat foreground; - mog(frame, foreground, 0.01); + mog->apply(frame, foreground, 0.01); while (!TestSystem::instance().stop()) { @@ -1327,7 +1327,7 @@ TEST(MOG) TestSystem::instance().cpuOn(); - mog(frame, foreground, 0.01); + mog->apply(frame, foreground, 0.01); TestSystem::instance().cpuOff(); } @@ -1367,12 +1367,12 @@ TEST(MOG2) cv::Mat frame; cap >> frame; - cv::BackgroundSubtractorMOG2 mog2; + cv::Ptr mog2 = cv::createBackgroundSubtractorMOG2(); cv::Mat foreground; cv::Mat background; - mog2(frame, foreground); - mog2.getBackgroundImage(background); + mog2->apply(frame, foreground); + mog2->getBackgroundImage(background); while (!TestSystem::instance().stop()) { @@ -1380,8 +1380,8 @@ TEST(MOG2) TestSystem::instance().cpuOn(); - mog2(frame, foreground); - mog2.getBackgroundImage(background); + mog2->apply(frame, foreground); + mog2->getBackgroundImage(background); TestSystem::instance().cpuOff(); }