From: vp153 Date: Thu, 1 Apr 2010 23:06:23 +0000 (+0000) Subject: repaired MoG background subtraction algorithm (tickets #32, #246) ; added camera... X-Git-Url: https://rtime.felk.cvut.cz/gitweb/opencv.git/commitdiff_plain/8f21b8409ddc5c7bc3b15f3611673bddc70c4650?ds=sidebyside repaired MoG background subtraction algorithm (tickets #32, #246) ; added camera support to bgfg_segm.cpp sample, added "update/not-update" mode switch by space key. git-svn-id: https://code.ros.org/svn/opencv/trunk@2968 73c94f0f-984f-4a5f-82bc-2d8db8d8ee08 --- diff --git a/opencv/include/opencv/cvaux.hpp b/opencv/include/opencv/cvaux.hpp index 77aae3b8..f5ba5991 100644 --- a/opencv/include/opencv/cvaux.hpp +++ b/opencv/include/opencv/cvaux.hpp @@ -1449,7 +1449,7 @@ class CV_EXPORTS BackgroundSubtractorMOG : public BackgroundSubtractor { public: BackgroundSubtractorMOG(); - BackgroundSubtractorMOG(int history, int nmixtures, double backgroundRatio); + BackgroundSubtractorMOG(int history, int nmixtures, double backgroundRatio, double noiseSigma=0); virtual ~BackgroundSubtractorMOG(); virtual void operator()(const Mat& image, Mat& fgmask, double learningRate=0); @@ -1463,363 +1463,364 @@ public: int nmixtures; double varThreshold; double backgroundRatio; + double noiseSigma; }; - // CvAffinePose: defines a parameterized affine transformation of an image patch. - // An image patch is rotated on angle phi (in degrees), then scaled lambda1 times - // along horizontal and lambda2 times along vertical direction, and then rotated again - // on angle (theta - phi). - class CvAffinePose - { - public: - float phi; - float theta; - float lambda1; - float lambda2; - }; +// CvAffinePose: defines a parameterized affine transformation of an image patch. +// An image patch is rotated on angle phi (in degrees), then scaled lambda1 times +// along horizontal and lambda2 times along vertical direction, and then rotated again +// on angle (theta - phi). +class CV_EXPORTS CvAffinePose +{ +public: + float phi; + float theta; + float lambda1; + float lambda2; +}; + + +class CV_EXPORTS OneWayDescriptor +{ +public: + OneWayDescriptor(); + ~OneWayDescriptor(); + // allocates memory for given descriptor parameters + void Allocate(int pose_count, CvSize size, int nChannels); - class CV_EXPORTS OneWayDescriptor - { - public: - OneWayDescriptor(); - ~OneWayDescriptor(); - - // allocates memory for given descriptor parameters - void Allocate(int pose_count, CvSize size, int nChannels); - - // GenerateSamples: generates affine transformed patches with averaging them over small transformation variations. - // If external poses and transforms were specified, uses them instead of generating random ones - // - pose_count: the number of poses to be generated - // - frontal: the input patch (can be a roi in a larger image) - // - norm: if nonzero, normalizes the output patch so that the sum of pixel intensities is 1 - void GenerateSamples(int pose_count, IplImage* frontal, int norm = 0); - - // GenerateSamplesFast: generates affine transformed patches with averaging them over small transformation variations. - // Uses precalculated transformed pca components. - // - frontal: the input patch (can be a roi in a larger image) - // - pca_hr_avg: pca average vector - // - pca_hr_eigenvectors: pca eigenvectors - // - pca_descriptors: an array of precomputed descriptors of pca components containing their affine transformations - // pca_descriptors[0] corresponds to the average, pca_descriptors[1]-pca_descriptors[pca_dim] correspond to eigenvectors - void GenerateSamplesFast(IplImage* frontal, CvMat* pca_hr_avg, - CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); - - // sets the poses and corresponding transforms - void SetTransforms(CvAffinePose* poses, CvMat** transforms); - - // Initialize: builds a descriptor. - // - pose_count: the number of poses to build. If poses were set externally, uses them rather than generating random ones - // - frontal: input patch. Can be a roi in a larger image - // - feature_name: the feature name to be associated with the descriptor - // - norm: if 1, the affine transformed patches are normalized so that their sum is 1 - void Initialize(int pose_count, IplImage* frontal, const char* feature_name = 0, int norm = 0); - - // InitializeFast: builds a descriptor using precomputed descriptors of pca components - // - pose_count: the number of poses to build - // - frontal: input patch. Can be a roi in a larger image - // - feature_name: the feature name to be associated with the descriptor - // - pca_hr_avg: average vector for PCA - // - pca_hr_eigenvectors: PCA eigenvectors (one vector per row) - // - pca_descriptors: precomputed descriptors of PCA components, the first descriptor for the average vector - // followed by the descriptors for eigenvectors - void InitializeFast(int pose_count, IplImage* frontal, const char* feature_name, - CvMat* pca_hr_avg, CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); - - // ProjectPCASample: unwarps an image patch into a vector and projects it into PCA space - // - patch: input image patch - // - avg: PCA average vector - // - eigenvectors: PCA eigenvectors, one per row - // - pca_coeffs: output PCA coefficients - void ProjectPCASample(IplImage* patch, CvMat* avg, CvMat* eigenvectors, CvMat* pca_coeffs) const; - - // InitializePCACoeffs: projects all warped patches into PCA space - // - avg: PCA average vector - // - eigenvectors: PCA eigenvectors, one per row - void InitializePCACoeffs(CvMat* avg, CvMat* eigenvectors); - - // EstimatePose: finds the closest match between an input patch and a set of patches with different poses - // - patch: input image patch - // - pose_idx: the output index of the closest pose - // - distance: the distance to the closest pose (L2 distance) - void EstimatePose(IplImage* patch, int& pose_idx, float& distance) const; - - // EstimatePosePCA: finds the closest match between an input patch and a set of patches with different poses. - // The distance between patches is computed in PCA space - // - patch: input image patch - // - pose_idx: the output index of the closest pose - // - distance: distance to the closest pose (L2 distance in PCA space) - // - avg: PCA average vector. If 0, matching without PCA is used - // - eigenvectors: PCA eigenvectors, one per row - void EstimatePosePCA(CvArr* patch, int& pose_idx, float& distance, CvMat* avg, CvMat* eigenvalues) const; - - // GetPatchSize: returns the size of each image patch after warping (2 times smaller than the input patch) - CvSize GetPatchSize() const - { - return m_patch_size; - } - - // GetInputPatchSize: returns the required size of the patch that the descriptor is built from - // (2 time larger than the patch after warping) - CvSize GetInputPatchSize() const - { - return cvSize(m_patch_size.width*2, m_patch_size.height*2); - } - - // GetPatch: returns a patch corresponding to specified pose index - // - index: pose index - // - return value: the patch corresponding to specified pose index - IplImage* GetPatch(int index); - - // GetPose: returns a pose corresponding to specified pose index - // - index: pose index - // - return value: the pose corresponding to specified pose index - CvAffinePose GetPose(int index) const; - - // Save: saves all patches with different poses to a specified path - void Save(const char* path); - - // ReadByName: reads a descriptor from a file storage - // - fs: file storage - // - parent: parent node - // - name: node name - // - return value: 1 if succeeded, 0 otherwise - int ReadByName(CvFileStorage* fs, CvFileNode* parent, const char* name); - - // Write: writes a descriptor into a file storage - // - fs: file storage - // - name: node name - void Write(CvFileStorage* fs, const char* name); - - // GetFeatureName: returns a name corresponding to a feature - const char* GetFeatureName() const; - - // GetCenter: returns the center of the feature - CvPoint GetCenter() const; - - void SetPCADimHigh(int pca_dim_high) {m_pca_dim_high = pca_dim_high;}; - void SetPCADimLow(int pca_dim_low) {m_pca_dim_low = pca_dim_low;}; - - int GetPCADimLow() const; - int GetPCADimHigh() const; - - CvMat** GetPCACoeffs() const {return m_pca_coeffs;} - - protected: - int m_pose_count; // the number of poses - CvSize m_patch_size; // size of each image - IplImage** m_samples; // an array of length m_pose_count containing the patch in different poses - IplImage* m_input_patch; - IplImage* m_train_patch; - CvMat** m_pca_coeffs; // an array of length m_pose_count containing pca decomposition of the patch in different poses - CvAffinePose* m_affine_poses; // an array of poses - CvMat** m_transforms; // an array of affine transforms corresponding to poses - - std::string m_feature_name; // the name of the feature associated with the descriptor - CvPoint m_center; // the coordinates of the feature (the center of the input image ROI) - - int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses - int m_pca_dim_low; // the number of pca components to use for comparison - }; + // GenerateSamples: generates affine transformed patches with averaging them over small transformation variations. + // If external poses and transforms were specified, uses them instead of generating random ones + // - pose_count: the number of poses to be generated + // - frontal: the input patch (can be a roi in a larger image) + // - norm: if nonzero, normalizes the output patch so that the sum of pixel intensities is 1 + void GenerateSamples(int pose_count, IplImage* frontal, int norm = 0); + // GenerateSamplesFast: generates affine transformed patches with averaging them over small transformation variations. + // Uses precalculated transformed pca components. + // - frontal: the input patch (can be a roi in a larger image) + // - pca_hr_avg: pca average vector + // - pca_hr_eigenvectors: pca eigenvectors + // - pca_descriptors: an array of precomputed descriptors of pca components containing their affine transformations + // pca_descriptors[0] corresponds to the average, pca_descriptors[1]-pca_descriptors[pca_dim] correspond to eigenvectors + void GenerateSamplesFast(IplImage* frontal, CvMat* pca_hr_avg, + CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); - // OneWayDescriptorBase: encapsulates functionality for training/loading a set of one way descriptors - // and finding the nearest closest descriptor to an input feature - class CV_EXPORTS OneWayDescriptorBase - { - public: - - // creates an instance of OneWayDescriptor from a set of training files - // - patch_size: size of the input (large) patch - // - pose_count: the number of poses to generate for each descriptor - // - train_path: path to training files - // - pca_config: the name of the file that contains PCA for small patches (2 times smaller - // than patch_size each dimension - // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) - // - pca_desc_config: the name of the file that contains descriptors of PCA components - OneWayDescriptorBase(CvSize patch_size, int pose_count, const char* train_path = 0, const char* pca_config = 0, - const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1, - int pca_dim_high = 100, int pca_dim_low = 100); - - ~OneWayDescriptorBase(); - - // Allocate: allocates memory for a given number of descriptors - void Allocate(int train_feature_count); - - // AllocatePCADescriptors: allocates memory for pca descriptors - void AllocatePCADescriptors(); - - // returns patch size - CvSize GetPatchSize() const {return m_patch_size;}; - // returns the number of poses for each descriptor - int GetPoseCount() const {return m_pose_count;}; - - // returns the number of pyramid levels - int GetPyrLevels() const {return m_pyr_levels;}; - - // returns the number of descriptors - int GetDescriptorCount() const {return m_train_feature_count;}; - - // CreateDescriptorsFromImage: creates descriptors for each of the input features - // - src: input image - // - features: input features - // - pyr_levels: the number of pyramid levels - void CreateDescriptorsFromImage(IplImage* src, const std::vector& features); - - // CreatePCADescriptors: generates descriptors for PCA components, needed for fast generation of feature descriptors - void CreatePCADescriptors(); - - // returns a feature descriptor by feature index - const OneWayDescriptor* GetDescriptor(int desc_idx) const {return &m_descriptors[desc_idx];}; - - // FindDescriptor: finds the closest descriptor - // - patch: input image patch - // - desc_idx: output index of the closest descriptor to the input patch - // - pose_idx: output index of the closest pose of the closest descriptor to the input patch - // - distance: distance from the input patch to the closest feature pose - // - _scales: scales of the input patch for each descriptor - // - scale_ranges: input scales variation (float[2]) - void FindDescriptor(IplImage* patch, int& desc_idx, int& pose_idx, float& distance, float* _scale = 0, float* scale_ranges = 0) const; - - // - patch: input image patch - // - n: number of the closest indexes - // - desc_idxs: output indexes of the closest descriptor to the input patch (n) - // - pose_idx: output indexes of the closest pose of the closest descriptor to the input patch (n) - // - distances: distance from the input patch to the closest feature pose (n) - // - _scales: scales of the input patch - // - scale_ranges: input scales variation (float[2]) - void FindDescriptor(IplImage* patch, int n, std::vector& desc_idxs, std::vector& pose_idxs, - std::vector& distances, std::vector& _scales, float* scale_ranges = 0) const; - - // FindDescriptor: finds the closest descriptor - // - src: input image - // - pt: center of the feature - // - desc_idx: output index of the closest descriptor to the input patch - // - pose_idx: output index of the closest pose of the closest descriptor to the input patch - // - distance: distance from the input patch to the closest feature pose - void FindDescriptor(IplImage* src, cv::Point2f pt, int& desc_idx, int& pose_idx, float& distance) const; - - // InitializePoses: generates random poses - void InitializePoses(); - - // InitializeTransformsFromPoses: generates 2x3 affine matrices from poses (initializes m_transforms) - void InitializeTransformsFromPoses(); - - // InitializePoseTransforms: subsequently calls InitializePoses and InitializeTransformsFromPoses - void InitializePoseTransforms(); - - // InitializeDescriptor: initializes a descriptor - // - desc_idx: descriptor index - // - train_image: image patch (ROI is supported) - // - feature_label: feature textual label - void InitializeDescriptor(int desc_idx, IplImage* train_image, const char* feature_label); - - void InitializeDescriptor(int desc_idx, IplImage* train_image, const cv::KeyPoint& keypoint, const char* feature_label); - - // InitializeDescriptors: load features from an image and create descriptors for each of them - void InitializeDescriptors(IplImage* train_image, const vector& features, - const char* feature_label = "", int desc_start_idx = 0); - - // LoadPCADescriptors: loads PCA descriptors from a file - // - filename: input filename - int LoadPCADescriptors(const char* filename); - - // SavePCADescriptors: saves PCA descriptors to a file - // - filename: output filename - void SavePCADescriptors(const char* filename); - - // SetPCAHigh: sets the high resolution pca matrices (copied to internal structures) - void SetPCAHigh(CvMat* avg, CvMat* eigenvectors); - - // SetPCALow: sets the low resolution pca matrices (copied to internal structures) - void SetPCALow(CvMat* avg, CvMat* eigenvectors); - - int GetLowPCA(CvMat** avg, CvMat** eigenvectors) - { - *avg = m_pca_avg; - *eigenvectors = m_pca_eigenvectors; - return m_pca_dim_low; - }; - - void ConvertDescriptorsArrayToTree(); // Converting pca_descriptors array to KD tree - - - protected: - CvSize m_patch_size; // patch size - int m_pose_count; // the number of poses for each descriptor - int m_train_feature_count; // the number of the training features - OneWayDescriptor* m_descriptors; // array of train feature descriptors - CvMat* m_pca_avg; // PCA average Vector for small patches - CvMat* m_pca_eigenvectors; // PCA eigenvectors for small patches - CvMat* m_pca_hr_avg; // PCA average Vector for large patches - CvMat* m_pca_hr_eigenvectors; // PCA eigenvectors for large patches - OneWayDescriptor* m_pca_descriptors; // an array of PCA descriptors - - cv::flann::Index* m_pca_descriptors_tree; - CvMat* m_pca_descriptors_matrix; - - CvAffinePose* m_poses; // array of poses - CvMat** m_transforms; // array of affine transformations corresponding to poses - - int m_pca_dim_high; - int m_pca_dim_low; - - int m_pyr_levels; - - }; + // sets the poses and corresponding transforms + void SetTransforms(CvAffinePose* poses, CvMat** transforms); - class OneWayDescriptorObject : public OneWayDescriptorBase - { - public: - // creates an instance of OneWayDescriptorObject from a set of training files - // - patch_size: size of the input (large) patch - // - pose_count: the number of poses to generate for each descriptor - // - train_path: path to training files - // - pca_config: the name of the file that contains PCA for small patches (2 times smaller - // than patch_size each dimension - // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) - // - pca_desc_config: the name of the file that contains descriptors of PCA components - OneWayDescriptorObject(CvSize patch_size, int pose_count, const char* train_path, const char* pca_config, - const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1); - - ~OneWayDescriptorObject(); - - // Allocate: allocates memory for a given number of features - // - train_feature_count: the total number of features - // - object_feature_count: the number of features extracted from the object - void Allocate(int train_feature_count, int object_feature_count); - - - void SetLabeledFeatures(const vector& features) {m_train_features = features;}; - vector& GetLabeledFeatures() {return m_train_features;}; - const vector& GetLabeledFeatures() const {return m_train_features;}; - vector _GetLabeledFeatures() const; - - // IsDescriptorObject: returns 1 if descriptor with specified index is positive, otherwise 0 - int IsDescriptorObject(int desc_idx) const; - - // MatchPointToPart: returns the part number of a feature if it matches one of the object parts, otherwise -1 - int MatchPointToPart(CvPoint pt) const; - - // GetDescriptorPart: returns the part number of the feature corresponding to a specified descriptor - // - desc_idx: descriptor index - int GetDescriptorPart(int desc_idx) const; - - - void InitializeObjectDescriptors(IplImage* train_image, const vector& features, - const char* feature_label, int desc_start_idx = 0, float scale = 1.0f, - int is_background = 0); - - // GetObjectFeatureCount: returns the number of object features - int GetObjectFeatureCount() const {return m_object_feature_count;}; - - protected: - int* m_part_id; // contains part id for each of object descriptors - vector m_train_features; // train features - int m_object_feature_count; // the number of the positive features - - }; + // Initialize: builds a descriptor. + // - pose_count: the number of poses to build. If poses were set externally, uses them rather than generating random ones + // - frontal: input patch. Can be a roi in a larger image + // - feature_name: the feature name to be associated with the descriptor + // - norm: if 1, the affine transformed patches are normalized so that their sum is 1 + void Initialize(int pose_count, IplImage* frontal, const char* feature_name = 0, int norm = 0); + + // InitializeFast: builds a descriptor using precomputed descriptors of pca components + // - pose_count: the number of poses to build + // - frontal: input patch. Can be a roi in a larger image + // - feature_name: the feature name to be associated with the descriptor + // - pca_hr_avg: average vector for PCA + // - pca_hr_eigenvectors: PCA eigenvectors (one vector per row) + // - pca_descriptors: precomputed descriptors of PCA components, the first descriptor for the average vector + // followed by the descriptors for eigenvectors + void InitializeFast(int pose_count, IplImage* frontal, const char* feature_name, + CvMat* pca_hr_avg, CvMat* pca_hr_eigenvectors, OneWayDescriptor* pca_descriptors); + + // ProjectPCASample: unwarps an image patch into a vector and projects it into PCA space + // - patch: input image patch + // - avg: PCA average vector + // - eigenvectors: PCA eigenvectors, one per row + // - pca_coeffs: output PCA coefficients + void ProjectPCASample(IplImage* patch, CvMat* avg, CvMat* eigenvectors, CvMat* pca_coeffs) const; + + // InitializePCACoeffs: projects all warped patches into PCA space + // - avg: PCA average vector + // - eigenvectors: PCA eigenvectors, one per row + void InitializePCACoeffs(CvMat* avg, CvMat* eigenvectors); + + // EstimatePose: finds the closest match between an input patch and a set of patches with different poses + // - patch: input image patch + // - pose_idx: the output index of the closest pose + // - distance: the distance to the closest pose (L2 distance) + void EstimatePose(IplImage* patch, int& pose_idx, float& distance) const; + + // EstimatePosePCA: finds the closest match between an input patch and a set of patches with different poses. + // The distance between patches is computed in PCA space + // - patch: input image patch + // - pose_idx: the output index of the closest pose + // - distance: distance to the closest pose (L2 distance in PCA space) + // - avg: PCA average vector. If 0, matching without PCA is used + // - eigenvectors: PCA eigenvectors, one per row + void EstimatePosePCA(CvArr* patch, int& pose_idx, float& distance, CvMat* avg, CvMat* eigenvalues) const; + + // GetPatchSize: returns the size of each image patch after warping (2 times smaller than the input patch) + CvSize GetPatchSize() const + { + return m_patch_size; + } + + // GetInputPatchSize: returns the required size of the patch that the descriptor is built from + // (2 time larger than the patch after warping) + CvSize GetInputPatchSize() const + { + return cvSize(m_patch_size.width*2, m_patch_size.height*2); + } + + // GetPatch: returns a patch corresponding to specified pose index + // - index: pose index + // - return value: the patch corresponding to specified pose index + IplImage* GetPatch(int index); + + // GetPose: returns a pose corresponding to specified pose index + // - index: pose index + // - return value: the pose corresponding to specified pose index + CvAffinePose GetPose(int index) const; + + // Save: saves all patches with different poses to a specified path + void Save(const char* path); + + // ReadByName: reads a descriptor from a file storage + // - fs: file storage + // - parent: parent node + // - name: node name + // - return value: 1 if succeeded, 0 otherwise + int ReadByName(CvFileStorage* fs, CvFileNode* parent, const char* name); + + // Write: writes a descriptor into a file storage + // - fs: file storage + // - name: node name + void Write(CvFileStorage* fs, const char* name); + + // GetFeatureName: returns a name corresponding to a feature + const char* GetFeatureName() const; + + // GetCenter: returns the center of the feature + CvPoint GetCenter() const; + + void SetPCADimHigh(int pca_dim_high) {m_pca_dim_high = pca_dim_high;}; + void SetPCADimLow(int pca_dim_low) {m_pca_dim_low = pca_dim_low;}; + + int GetPCADimLow() const; + int GetPCADimHigh() const; + + CvMat** GetPCACoeffs() const {return m_pca_coeffs;} + +protected: + int m_pose_count; // the number of poses + CvSize m_patch_size; // size of each image + IplImage** m_samples; // an array of length m_pose_count containing the patch in different poses + IplImage* m_input_patch; + IplImage* m_train_patch; + CvMat** m_pca_coeffs; // an array of length m_pose_count containing pca decomposition of the patch in different poses + CvAffinePose* m_affine_poses; // an array of poses + CvMat** m_transforms; // an array of affine transforms corresponding to poses + + std::string m_feature_name; // the name of the feature associated with the descriptor + CvPoint m_center; // the coordinates of the feature (the center of the input image ROI) + + int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses + int m_pca_dim_low; // the number of pca components to use for comparison +}; + + +// OneWayDescriptorBase: encapsulates functionality for training/loading a set of one way descriptors +// and finding the nearest closest descriptor to an input feature +class CV_EXPORTS OneWayDescriptorBase +{ +public: + + // creates an instance of OneWayDescriptor from a set of training files + // - patch_size: size of the input (large) patch + // - pose_count: the number of poses to generate for each descriptor + // - train_path: path to training files + // - pca_config: the name of the file that contains PCA for small patches (2 times smaller + // than patch_size each dimension + // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) + // - pca_desc_config: the name of the file that contains descriptors of PCA components + OneWayDescriptorBase(CvSize patch_size, int pose_count, const char* train_path = 0, const char* pca_config = 0, + const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1, + int pca_dim_high = 100, int pca_dim_low = 100); + + ~OneWayDescriptorBase(); + + // Allocate: allocates memory for a given number of descriptors + void Allocate(int train_feature_count); + + // AllocatePCADescriptors: allocates memory for pca descriptors + void AllocatePCADescriptors(); + + // returns patch size + CvSize GetPatchSize() const {return m_patch_size;}; + // returns the number of poses for each descriptor + int GetPoseCount() const {return m_pose_count;}; + + // returns the number of pyramid levels + int GetPyrLevels() const {return m_pyr_levels;}; + + // returns the number of descriptors + int GetDescriptorCount() const {return m_train_feature_count;}; + + // CreateDescriptorsFromImage: creates descriptors for each of the input features + // - src: input image + // - features: input features + // - pyr_levels: the number of pyramid levels + void CreateDescriptorsFromImage(IplImage* src, const std::vector& features); + + // CreatePCADescriptors: generates descriptors for PCA components, needed for fast generation of feature descriptors + void CreatePCADescriptors(); + + // returns a feature descriptor by feature index + const OneWayDescriptor* GetDescriptor(int desc_idx) const {return &m_descriptors[desc_idx];}; + + // FindDescriptor: finds the closest descriptor + // - patch: input image patch + // - desc_idx: output index of the closest descriptor to the input patch + // - pose_idx: output index of the closest pose of the closest descriptor to the input patch + // - distance: distance from the input patch to the closest feature pose + // - _scales: scales of the input patch for each descriptor + // - scale_ranges: input scales variation (float[2]) + void FindDescriptor(IplImage* patch, int& desc_idx, int& pose_idx, float& distance, float* _scale = 0, float* scale_ranges = 0) const; + + // - patch: input image patch + // - n: number of the closest indexes + // - desc_idxs: output indexes of the closest descriptor to the input patch (n) + // - pose_idx: output indexes of the closest pose of the closest descriptor to the input patch (n) + // - distances: distance from the input patch to the closest feature pose (n) + // - _scales: scales of the input patch + // - scale_ranges: input scales variation (float[2]) + void FindDescriptor(IplImage* patch, int n, std::vector& desc_idxs, std::vector& pose_idxs, + std::vector& distances, std::vector& _scales, float* scale_ranges = 0) const; + + // FindDescriptor: finds the closest descriptor + // - src: input image + // - pt: center of the feature + // - desc_idx: output index of the closest descriptor to the input patch + // - pose_idx: output index of the closest pose of the closest descriptor to the input patch + // - distance: distance from the input patch to the closest feature pose + void FindDescriptor(IplImage* src, cv::Point2f pt, int& desc_idx, int& pose_idx, float& distance) const; + + // InitializePoses: generates random poses + void InitializePoses(); + + // InitializeTransformsFromPoses: generates 2x3 affine matrices from poses (initializes m_transforms) + void InitializeTransformsFromPoses(); + + // InitializePoseTransforms: subsequently calls InitializePoses and InitializeTransformsFromPoses + void InitializePoseTransforms(); + + // InitializeDescriptor: initializes a descriptor + // - desc_idx: descriptor index + // - train_image: image patch (ROI is supported) + // - feature_label: feature textual label + void InitializeDescriptor(int desc_idx, IplImage* train_image, const char* feature_label); + + void InitializeDescriptor(int desc_idx, IplImage* train_image, const cv::KeyPoint& keypoint, const char* feature_label); + + // InitializeDescriptors: load features from an image and create descriptors for each of them + void InitializeDescriptors(IplImage* train_image, const vector& features, + const char* feature_label = "", int desc_start_idx = 0); + + // LoadPCADescriptors: loads PCA descriptors from a file + // - filename: input filename + int LoadPCADescriptors(const char* filename); + + // SavePCADescriptors: saves PCA descriptors to a file + // - filename: output filename + void SavePCADescriptors(const char* filename); + + // SetPCAHigh: sets the high resolution pca matrices (copied to internal structures) + void SetPCAHigh(CvMat* avg, CvMat* eigenvectors); + + // SetPCALow: sets the low resolution pca matrices (copied to internal structures) + void SetPCALow(CvMat* avg, CvMat* eigenvectors); + + int GetLowPCA(CvMat** avg, CvMat** eigenvectors) + { + *avg = m_pca_avg; + *eigenvectors = m_pca_eigenvectors; + return m_pca_dim_low; + }; + + void ConvertDescriptorsArrayToTree(); // Converting pca_descriptors array to KD tree + + +protected: + CvSize m_patch_size; // patch size + int m_pose_count; // the number of poses for each descriptor + int m_train_feature_count; // the number of the training features + OneWayDescriptor* m_descriptors; // array of train feature descriptors + CvMat* m_pca_avg; // PCA average Vector for small patches + CvMat* m_pca_eigenvectors; // PCA eigenvectors for small patches + CvMat* m_pca_hr_avg; // PCA average Vector for large patches + CvMat* m_pca_hr_eigenvectors; // PCA eigenvectors for large patches + OneWayDescriptor* m_pca_descriptors; // an array of PCA descriptors + + cv::flann::Index* m_pca_descriptors_tree; + CvMat* m_pca_descriptors_matrix; + + CvAffinePose* m_poses; // array of poses + CvMat** m_transforms; // array of affine transformations corresponding to poses + + int m_pca_dim_high; + int m_pca_dim_low; + + int m_pyr_levels; + +}; + +class OneWayDescriptorObject : public OneWayDescriptorBase +{ +public: + // creates an instance of OneWayDescriptorObject from a set of training files + // - patch_size: size of the input (large) patch + // - pose_count: the number of poses to generate for each descriptor + // - train_path: path to training files + // - pca_config: the name of the file that contains PCA for small patches (2 times smaller + // than patch_size each dimension + // - pca_hr_config: the name of the file that contains PCA for large patches (of patch_size size) + // - pca_desc_config: the name of the file that contains descriptors of PCA components + OneWayDescriptorObject(CvSize patch_size, int pose_count, const char* train_path, const char* pca_config, + const char* pca_hr_config = 0, const char* pca_desc_config = 0, int pyr_levels = 1); + + ~OneWayDescriptorObject(); + + // Allocate: allocates memory for a given number of features + // - train_feature_count: the total number of features + // - object_feature_count: the number of features extracted from the object + void Allocate(int train_feature_count, int object_feature_count); + + + void SetLabeledFeatures(const vector& features) {m_train_features = features;}; + vector& GetLabeledFeatures() {return m_train_features;}; + const vector& GetLabeledFeatures() const {return m_train_features;}; + vector _GetLabeledFeatures() const; + + // IsDescriptorObject: returns 1 if descriptor with specified index is positive, otherwise 0 + int IsDescriptorObject(int desc_idx) const; + + // MatchPointToPart: returns the part number of a feature if it matches one of the object parts, otherwise -1 + int MatchPointToPart(CvPoint pt) const; + + // GetDescriptorPart: returns the part number of the feature corresponding to a specified descriptor + // - desc_idx: descriptor index + int GetDescriptorPart(int desc_idx) const; + + + void InitializeObjectDescriptors(IplImage* train_image, const vector& features, + const char* feature_label, int desc_start_idx = 0, float scale = 1.0f, + int is_background = 0); + + // GetObjectFeatureCount: returns the number of object features + int GetObjectFeatureCount() const {return m_object_feature_count;}; + +protected: + int* m_part_id; // contains part id for each of object descriptors + vector m_train_features; // train features + int m_object_feature_count; // the number of the positive features + +}; } diff --git a/opencv/samples/c/bgfg_segm.cpp b/opencv/samples/c/bgfg_segm.cpp index 15778f8b..348f48e2 100644 --- a/opencv/samples/c/bgfg_segm.cpp +++ b/opencv/samples/c/bgfg_segm.cpp @@ -7,38 +7,51 @@ int main(int argc, char** argv) { IplImage* tmp_frame = NULL; CvCapture* cap = NULL; + bool update_bg_model = true; if( argc < 2 ) + cap = cvCaptureFromCAM(0); + else + cap = cvCaptureFromFile(argv[1]); + + if( !cap ) { - printf("please specify video file name \n"); - exit(0); + printf("can not open camera or video file\n"); + return -1; } - - cap = cvCaptureFromFile(argv[1]); + tmp_frame = cvQueryFrame(cap); if(!tmp_frame) { - printf("bad video \n"); - exit(0); + printf("can not read data from the video source\n"); + return -1; } cvNamedWindow("BG", 1); cvNamedWindow("FG", 1); - //create BG model - CvBGStatModel* bg_model = cvCreateFGDStatModel( tmp_frame ); + CvBGStatModel* bg_model = 0; for( int fr = 1;tmp_frame; tmp_frame = cvQueryFrame(cap), fr++ ) { + if(!bg_model) + { + //create BG model + bg_model = cvCreateGaussianBGModel( tmp_frame ); + //bg_model = cvCreateFGDStatModel( temp ); + continue; + } + double t = (double)cvGetTickCount(); - cvUpdateBGStatModel( tmp_frame, bg_model ); + cvUpdateBGStatModel( tmp_frame, bg_model, update_bg_model ? -1 : 0 ); t = (double)cvGetTickCount() - t; - printf( "%.1f\n", t/(cvGetTickFrequency()*1000.) ); + printf( "%d. %.1f\n", fr, t/(cvGetTickFrequency()*1000.) ); cvShowImage("BG", bg_model->background); cvShowImage("FG", bg_model->foreground); char k = cvWaitKey(5); if( k == 27 ) break; - //printf("frame# %d \r", fr); + if( k == ' ' ) + update_bg_model = !update_bg_model; } diff --git a/opencv/src/cvaux/cvbgfg_gaussmix.cpp b/opencv/src/cvaux/cvbgfg_gaussmix.cpp index 3be167be..fbd768e7 100644 --- a/opencv/src/cvaux/cvbgfg_gaussmix.cpp +++ b/opencv/src/cvaux/cvbgfg_gaussmix.cpp @@ -67,6 +67,7 @@ static const int defaultNMixtures = CV_BGFG_MOG_NGAUSSIANS; static const int defaultHistory = CV_BGFG_MOG_WINDOW_SIZE; static const double defaultBackgroundRatio = CV_BGFG_MOG_BACKGROUND_THRESHOLD; static const double defaultVarThreshold = CV_BGFG_MOG_STD_THRESHOLD*CV_BGFG_MOG_STD_THRESHOLD; +static const double defaultNoiseSigma = CV_BGFG_MOG_SIGMA_INIT*0.5; BackgroundSubtractorMOG::BackgroundSubtractorMOG() { @@ -78,9 +79,12 @@ BackgroundSubtractorMOG::BackgroundSubtractorMOG() history = defaultHistory; varThreshold = defaultVarThreshold; backgroundRatio = defaultBackgroundRatio; + noiseSigma = defaultNoiseSigma; } -BackgroundSubtractorMOG::BackgroundSubtractorMOG(int _history, int _nmixtures, double _backgroundRatio) +BackgroundSubtractorMOG::BackgroundSubtractorMOG(int _history, int _nmixtures, + double _backgroundRatio, + double _noiseSigma) { frameSize = Size(0,0); frameType = 0; @@ -90,6 +94,7 @@ BackgroundSubtractorMOG::BackgroundSubtractorMOG(int _history, int _nmixtures, d history = _history > 0 ? _history : defaultHistory; varThreshold = defaultVarThreshold; backgroundRatio = min(_backgroundRatio > 0 ? _backgroundRatio : 0.95, 1.); + noiseSigma = _noiseSigma <= 0 ? defaultNoiseSigma : _noiseSigma; } BackgroundSubtractorMOG::~BackgroundSubtractorMOG() @@ -110,30 +115,8 @@ void BackgroundSubtractorMOG::initialize(Size _frameSize, int _frameType) // 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( frameSize.height, frameSize.width*nmixtures*(2 + 2*nchannels), CV_32F ); - const float w0 = (float)CV_BGFG_MOG_WEIGHT_INIT; - const float var0 = (float)(CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT); - const float sk0 = (float)(CV_BGFG_MOG_WEIGHT_INIT/(CV_BGFG_MOG_SIGMA_INIT*sqrt((double)nchannels))); - - for( int y = 0; y < frameSize.height; y++ ) - { - float* mptr = bgmodel.ptr(y); - for( int x = 0; x < frameSize.width; x++ ) - { - for( int k = 0; k < nmixtures; k++ ) - { - mptr[0] = sk0; - mptr[1] = w0; - mptr += 2; - for( int c = 0; c < nchannels; c++ ) - { - mptr[c] = 0; - mptr[c + nchannels] = var0; - } - mptr += nchannels*2; - } - } - } + bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + 2*nchannels), CV_32F ); + bgmodel = Scalar::all(0); } @@ -151,66 +134,121 @@ static void process8uC1( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fg int x, y, k, k1, rows = image.rows, cols = image.cols; float alpha = (float)learningRate, T = (float)obj.backgroundRatio, vT = (float)obj.varThreshold; int K = obj.nmixtures; + MixData* mptr = (MixData*)obj.bgmodel.data; const float w0 = (float)CV_BGFG_MOG_WEIGHT_INIT; - const float sk0 = (float)(CV_BGFG_MOG_WEIGHT_INIT/CV_BGFG_MOG_SIGMA_INIT); + const float sk0 = (float)(w0/CV_BGFG_MOG_SIGMA_INIT); const float var0 = (float)(CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT); + const float minVar = (float)(obj.noiseSigma*obj.noiseSigma); for( y = 0; y < rows; y++ ) { const uchar* src = image.ptr(y); uchar* dst = fgmask.ptr(y); - MixData* mptr = (MixData*)obj.bgmodel.ptr(y); - for( x = 0; x < cols; x++, mptr += K ) + if( alpha > 0 ) { - float wsum = 0, dw = 0; - float pix = src[x]; - for( k = 0; k < K; k++ ) + for( x = 0; x < cols; x++, mptr += K ) { - float w = mptr[k].weight; - float mu = mptr[k].mean; - float var = mptr[k].var; - float diff = pix - mu, d2 = diff*diff; - if( d2 < vT*var ) + float wsum = 0; + float pix = src[x]; + int kHit = -1, kForeground = -1; + + for( k = 0; k < K; k++ ) { - dw = alpha*(1.f - w); - mptr[k].weight = w + dw; - mptr[k].mean = mu + alpha*diff; - mptr[k].var = var = max(var + alpha*(d2 - var), FLT_EPSILON); - mptr[k].sortKey = w/sqrt(var); - - for( k1 = k-1; k1 >= 0; k1-- ) + float w = mptr[k].weight; + wsum += w; + if( w < FLT_EPSILON ) + break; + float mu = mptr[k].mean; + float var = mptr[k].var; + float diff = pix - mu; + float d2 = diff*diff; + if( d2 < vT*var ) { - if( mptr[k1].sortKey > mptr[k1+1].sortKey ) - break; - std::swap( mptr[k1], mptr[k1+1] ); + wsum -= w; + float dw = alpha*(1.f - w); + mptr[k].weight = w + dw; + mptr[k].mean = mu + alpha*diff; + var = max(var + alpha*(d2 - var), minVar); + mptr[k].var = var; + mptr[k].sortKey = w/sqrt(var); + + for( k1 = k-1; k1 >= 0; k1-- ) + { + if( mptr[k1].sortKey >= mptr[k1+1].sortKey ) + break; + std::swap( mptr[k1], mptr[k1+1] ); + } + + kHit = k1+1; + break; } - break; } - wsum += w; - } - - dst[x] = (uchar)(-(wsum >= T)); - wsum += dw; - - if( k == K ) // no appropriate gaussian mixture found at all, remove the weakest mixture and create a new one - { - wsum += w0 - mptr[K-1].weight; - mptr[K-1].weight = w0; - mptr[K-1].mean = pix; - mptr[K-1].var = var0; - mptr[K-1].sortKey = sk0; + + if( kHit < 0 ) // no appropriate gaussian mixture found at all, remove the weakest mixture and create a new one + { + kHit = k = min(k, K-1); + wsum += w0 - mptr[k].weight; + mptr[k].weight = w0; + mptr[k].mean = pix; + mptr[k].var = var0; + mptr[k].sortKey = sk0; + } + else + for( ; k < K; k++ ) + wsum += mptr[k].weight; + + float wscale = 1.f/wsum; + wsum = 0; + for( k = 0; k < K; k++ ) + { + wsum += mptr[k].weight *= wscale; + mptr[k].sortKey *= wscale; + if( wsum > T && kForeground < 0 ) + kForeground = k+1; + } + + dst[x] = (uchar)(-(kHit >= kForeground)); } - else - for( ; k < K; k++ ) - wsum += mptr[k].weight; - - dw = 1.f/wsum; - for( k = 0; k < K; k++ ) + } + else + { + for( x = 0; x < cols; x++, mptr += K ) { - mptr[k].weight *= dw; - mptr[k].sortKey *= dw; + float pix = src[x]; + int kHit = -1, kForeground = -1; + + for( k = 0; k < K; k++ ) + { + if( mptr[k].weight < FLT_EPSILON ) + break; + float mu = mptr[k].mean; + float var = mptr[k].var; + float diff = pix - mu; + float d2 = diff*diff; + if( d2 < vT*var ) + { + kHit = k; + break; + } + } + + if( kHit >= 0 ) + { + float wsum = 0; + for( k = 0; k < K; k++ ) + { + wsum += mptr[k].weight; + if( wsum > T ) + { + kForeground = k+1; + break; + } + } + } + + dst[x] = (uchar)(kHit < 0 || kHit >= kForeground ? 255 : 0); } } } @@ -223,68 +261,121 @@ static void process8uC3( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fg int K = obj.nmixtures; const float w0 = (float)CV_BGFG_MOG_WEIGHT_INIT; - const float sk0 = (float)(CV_BGFG_MOG_WEIGHT_INIT/CV_BGFG_MOG_SIGMA_INIT); + const float sk0 = (float)(w0/CV_BGFG_MOG_SIGMA_INIT*sqrt(3.)); const float var0 = (float)(CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT); + const float minVar = (float)(obj.noiseSigma*obj.noiseSigma); + MixData* mptr = (MixData*)obj.bgmodel.data; for( y = 0; y < rows; y++ ) { const uchar* src = image.ptr(y); uchar* dst = fgmask.ptr(y); - MixData* mptr = (MixData*)obj.bgmodel.ptr(y); - for( x = 0; x < cols; x++, mptr += K ) + if( alpha > 0 ) { - float wsum = 0, dw = 0; - Vec3f pix(src[x*3], src[x*3+1], src[x*3+2]); - for( k = 0; k < K; k++ ) + for( x = 0; x < cols; x++, mptr += K ) { - float w = mptr[k].weight; - Vec3f mu = mptr[k].mean[0]; - Vec3f var = mptr[k].var[0]; - Vec3f diff = pix - mu; - float d2 = diff.dot(diff); - if( d2 < vT*(var[0] + var[1] + var[2]) ) + float wsum = 0; + Vec3f pix(src[x*3], src[x*3+1], src[x*3+2]); + int kHit = -1, kForeground = -1; + + for( k = 0; k < K; k++ ) { - dw = alpha*(1.f - w); - mptr[k].weight = w + dw; - mptr[k].mean = mu + alpha*diff; - var = Vec3f(max(var[0] + alpha*(diff[0]*diff[0] - var[0]), FLT_EPSILON), - max(var[1] + alpha*(diff[1]*diff[1] - var[1]), FLT_EPSILON), - max(var[2] + alpha*(diff[2]*diff[2] - var[2]), FLT_EPSILON)); - mptr[k].var = var; - mptr[k].sortKey = w/sqrt(var[0] + var[1] + var[2]); - - for( k1 = k-1; k1 >= 0; k1-- ) + float w = mptr[k].weight; + wsum += w; + if( w < FLT_EPSILON ) + break; + Vec3f mu = mptr[k].mean; + Vec3f var = mptr[k].var; + Vec3f diff = pix - mu; + float d2 = diff.dot(diff); + if( d2 < vT*(var[0] + var[1] + var[2]) ) { - if( mptr[k1].sortKey > mptr[k1+1].sortKey ) - break; - std::swap( mptr[k1], mptr[k1+1] ); + wsum -= w; + float dw = alpha*(1.f - w); + mptr[k].weight = w + dw; + mptr[k].mean = mu + alpha*diff; + var = Vec3f(max(var[0] + alpha*(diff[0]*diff[0] - var[0]), minVar), + max(var[1] + alpha*(diff[1]*diff[1] - var[1]), minVar), + max(var[2] + alpha*(diff[2]*diff[2] - var[2]), minVar)); + mptr[k].var = var; + mptr[k].sortKey = w/sqrt(var[0] + var[1] + var[2]); + + for( k1 = k-1; k1 >= 0; k1-- ) + { + if( mptr[k1].sortKey >= mptr[k1+1].sortKey ) + break; + std::swap( mptr[k1], mptr[k1+1] ); + } + + kHit = k1+1; + break; } - break; } - wsum += w; - } - - dst[x] = (uchar)(-(wsum >= T)); - wsum += dw; + + if( kHit < 0 ) // no appropriate gaussian mixture found at all, remove the weakest mixture and create a new one + { + kHit = k = min(k, K-1); + wsum += w0 - mptr[k].weight; + mptr[k].weight = w0; + mptr[k].mean = pix; + mptr[k].var = Vec3f(var0, var0, var0); + mptr[k].sortKey = sk0; + } + else + for( ; k < K; k++ ) + wsum += mptr[k].weight; - if( k == K ) // no appropriate gaussian mixture found at all, remove the weakest mixture and create a new one - { - wsum += w0 - mptr[K-1].weight; - mptr[K-1].weight = w0; - mptr[K-1].mean = pix; - mptr[K-1].var = Vec3f(var0, var0, var0); - mptr[K-1].sortKey = sk0; + float wscale = 1.f/wsum; + wsum = 0; + for( k = 0; k < K; k++ ) + { + wsum += mptr[k].weight *= wscale; + mptr[k].sortKey *= wscale; + if( wsum > T && kForeground < 0 ) + kForeground = k+1; + } + + dst[x] = (uchar)(-(kHit >= kForeground)); } - else - for( ; k < K; k++ ) - wsum += mptr[k].weight; - - dw = 1.f/wsum; - for( k = 0; k < K; k++ ) + } + else + { + for( x = 0; x < cols; x++, mptr += K ) { - mptr[k].weight *= dw; - mptr[k].sortKey *= dw; + Vec3f pix(src[x*3], src[x*3+1], src[x*3+2]); + int kHit = -1, kForeground = -1; + + for( k = 0; k < K; k++ ) + { + if( mptr[k].weight < FLT_EPSILON ) + break; + Vec3f mu = mptr[k].mean; + Vec3f var = mptr[k].var; + Vec3f diff = pix - mu; + float d2 = diff.dot(diff); + if( d2 < vT*(var[0] + var[1] + var[2]) ) + { + kHit = k; + break; + } + } + + if( kHit >= 0 ) + { + float wsum = 0; + for( k = 0; k < K; k++ ) + { + wsum += mptr[k].weight; + if( wsum > T ) + { + kForeground = k+1; + break; + } + } + } + + dst[x] = (uchar)(kHit < 0 || kHit >= kForeground ? 255 : 0); } } } @@ -295,13 +386,14 @@ void BackgroundSubtractorMOG::operator()(const Mat& image, Mat& fgmask, double l bool needToInitialize = nframes == 0 || learningRate >= 1 || image.size() != frameSize || image.type() != frameType; if( needToInitialize ) - initialize(frameSize, frameType); + initialize(image.size(), image.type()); CV_Assert( image.depth() == CV_8U ); fgmask.create( image.size(), CV_8U ); ++nframes; learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./min( nframes, history ); + CV_Assert(learningRate >= 0); if( image.type() == CV_8UC1 ) process8uC1( *this, image, fgmask, learningRate ); @@ -366,7 +458,7 @@ icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel* bg_model, doubl //cvMorphologyEx( bg_model->foreground, bg_model->foreground, 0, 0, CV_MOP_OPEN, 1 ); //cvMorphologyEx( bg_model->foreground, bg_model->foreground, 0, 0, CV_MOP_CLOSE, 1 ); - cvFindContours( bg_model->foreground, bg_model->storage, &first_seq, sizeof(CvContour), CV_RETR_LIST ); + /*cvFindContours( bg_model->foreground, bg_model->storage, &first_seq, sizeof(CvContour), CV_RETR_LIST ); for( seq = first_seq; seq; seq = seq->h_next ) { CvContour* cnt = (CvContour*)seq; @@ -392,7 +484,9 @@ icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel* bg_model, doubl } bg_model->foreground_regions = first_seq; cvZero(bg_model->foreground); - cvDrawContours(bg_model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1); + cvDrawContours(bg_model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);*/ + CvMat _mask = mask; + cvCopy(&_mask, bg_model->foreground); return region_count; } @@ -446,3 +540,4 @@ cvCreateGaussianBGModel( IplImage* first_frame, CvGaussBGStatModelParams* parame } /* End of file. */ +