From 61c27ac81e1f900c2bcf598d973445edfc1950aa Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Mon, 23 Sep 2013 19:00:49 +0200 Subject: [PATCH 01/12] Shape module added --- modules/shape/CMakeLists.txt | 2 + modules/shape/doc/emdL1.rst | 11 + modules/shape/doc/histogram_cost_matrix.rst | 82 ++ modules/shape/doc/shape.rst | 15 + modules/shape/doc/shape.rst~ | 15 + modules/shape/doc/shape_distances.rst | 231 +++++ modules/shape/doc/shape_transformers.rst | 108 +++ modules/shape/include/opencv2/shape.hpp | 58 ++ modules/shape/include/opencv2/shape/emdL1.hpp | 58 ++ .../shape/include/opencv2/shape/hist_cost.hpp | 103 +++ modules/shape/include/opencv2/shape/shape.hpp | 48 + .../include/opencv2/shape/shape_distance.hpp | 143 +++ .../opencv2/shape/shape_transformer.hpp | 109 +++ modules/shape/src/aff_trans.cpp | 266 ++++++ modules/shape/src/emdL1.cpp | 794 ++++++++++++++++ modules/shape/src/emdL1_def.hpp | 142 +++ modules/shape/src/haus_dis.cpp | 151 ++++ modules/shape/src/hist_cost.cpp | 547 +++++++++++ modules/shape/src/precomp.cpp | 45 + modules/shape/src/precomp.hpp | 59 ++ modules/shape/src/sc_dis.cpp | 848 ++++++++++++++++++ modules/shape/src/tps_trans.cpp | 288 ++++++ modules/shape/test/test_emdl1.cpp | 266 ++++++ modules/shape/test/test_hausdorff.cpp | 280 ++++++ modules/shape/test/test_main.cpp | 3 + modules/shape/test/test_precomp.cpp | 1 + modules/shape/test/test_precomp.hpp | 21 + modules/shape/test/test_shape.cpp | 267 ++++++ samples/cpp/CMakeLists.txt | 2 +- samples/cpp/shape_example.cpp | 111 +++ samples/cpp/shape_sample/1.png | Bin 0 -> 705 bytes samples/cpp/shape_sample/10.png | Bin 0 -> 1024 bytes samples/cpp/shape_sample/11.png | Bin 0 -> 722 bytes samples/cpp/shape_sample/12.png | Bin 0 -> 437 bytes samples/cpp/shape_sample/13.png | Bin 0 -> 443 bytes samples/cpp/shape_sample/14.png | Bin 0 -> 1820 bytes samples/cpp/shape_sample/15.png | Bin 0 -> 803 bytes samples/cpp/shape_sample/16.png | Bin 0 -> 830 bytes samples/cpp/shape_sample/17.png | Bin 0 -> 3100 bytes samples/cpp/shape_sample/18.png | Bin 0 -> 3283 bytes samples/cpp/shape_sample/19.png | Bin 0 -> 1560 bytes samples/cpp/shape_sample/2.png | Bin 0 -> 813 bytes samples/cpp/shape_sample/20.png | Bin 0 -> 1571 bytes samples/cpp/shape_sample/3.png | Bin 0 -> 2301 bytes samples/cpp/shape_sample/4.png | Bin 0 -> 2431 bytes samples/cpp/shape_sample/5.png | Bin 0 -> 852 bytes samples/cpp/shape_sample/6.png | Bin 0 -> 969 bytes samples/cpp/shape_sample/7.png | Bin 0 -> 874 bytes samples/cpp/shape_sample/8.png | Bin 0 -> 851 bytes samples/cpp/shape_sample/9.png | Bin 0 -> 1204 bytes samples/cpp/shape_transformation.cpp | 74 ++ 51 files changed, 5147 insertions(+), 1 deletion(-) create mode 100644 modules/shape/CMakeLists.txt create mode 100644 modules/shape/doc/emdL1.rst create mode 100644 modules/shape/doc/histogram_cost_matrix.rst create mode 100644 modules/shape/doc/shape.rst create mode 100644 modules/shape/doc/shape.rst~ create mode 100644 modules/shape/doc/shape_distances.rst create mode 100644 modules/shape/doc/shape_transformers.rst create mode 100644 modules/shape/include/opencv2/shape.hpp create mode 100644 modules/shape/include/opencv2/shape/emdL1.hpp create mode 100644 modules/shape/include/opencv2/shape/hist_cost.hpp create mode 100644 modules/shape/include/opencv2/shape/shape.hpp create mode 100644 modules/shape/include/opencv2/shape/shape_distance.hpp create mode 100644 modules/shape/include/opencv2/shape/shape_transformer.hpp create mode 100644 modules/shape/src/aff_trans.cpp create mode 100644 modules/shape/src/emdL1.cpp create mode 100644 modules/shape/src/emdL1_def.hpp create mode 100644 modules/shape/src/haus_dis.cpp create mode 100644 modules/shape/src/hist_cost.cpp create mode 100644 modules/shape/src/precomp.cpp create mode 100644 modules/shape/src/precomp.hpp create mode 100644 modules/shape/src/sc_dis.cpp create mode 100644 modules/shape/src/tps_trans.cpp create mode 100644 modules/shape/test/test_emdl1.cpp create mode 100644 modules/shape/test/test_hausdorff.cpp create mode 100644 modules/shape/test/test_main.cpp create mode 100644 modules/shape/test/test_precomp.cpp create mode 100644 modules/shape/test/test_precomp.hpp create mode 100644 modules/shape/test/test_shape.cpp create mode 100644 samples/cpp/shape_example.cpp create mode 100644 samples/cpp/shape_sample/1.png create mode 100644 samples/cpp/shape_sample/10.png create mode 100644 samples/cpp/shape_sample/11.png create mode 100644 samples/cpp/shape_sample/12.png create mode 100644 samples/cpp/shape_sample/13.png create mode 100644 samples/cpp/shape_sample/14.png create mode 100644 samples/cpp/shape_sample/15.png create mode 100644 samples/cpp/shape_sample/16.png create mode 100644 samples/cpp/shape_sample/17.png create mode 100644 samples/cpp/shape_sample/18.png create mode 100644 samples/cpp/shape_sample/19.png create mode 100644 samples/cpp/shape_sample/2.png create mode 100644 samples/cpp/shape_sample/20.png create mode 100644 samples/cpp/shape_sample/3.png create mode 100644 samples/cpp/shape_sample/4.png create mode 100644 samples/cpp/shape_sample/5.png create mode 100644 samples/cpp/shape_sample/6.png create mode 100644 samples/cpp/shape_sample/7.png create mode 100644 samples/cpp/shape_sample/8.png create mode 100644 samples/cpp/shape_sample/9.png create mode 100644 samples/cpp/shape_transformation.cpp diff --git a/modules/shape/CMakeLists.txt b/modules/shape/CMakeLists.txt new file mode 100644 index 000000000..77150c4de --- /dev/null +++ b/modules/shape/CMakeLists.txt @@ -0,0 +1,2 @@ +set(the_description "Shape descriptors and matchers.") +ocv_define_module(shape opencv_core opencv_imgproc opencv_video) diff --git a/modules/shape/doc/emdL1.rst b/modules/shape/doc/emdL1.rst new file mode 100644 index 000000000..853756d0c --- /dev/null +++ b/modules/shape/doc/emdL1.rst @@ -0,0 +1,11 @@ +EMD-L1 +====== +Computes the "minimal work" distance between two weighted point configurations base on the papers "EMD-L1: An efficient and Robust Algorithm +for comparing histogram-based descriptors", by Haibin Ling and Kazunori Okuda; and "The Earth Mover's Distance is the Mallows Distance: +Some Insights from Statistics", by Elizaveta Levina and Peter Bickel. + +.. ocv:function:: float EMDL1( InputArray signature1, InputArray signature2 ) + + :param signature1: First signature, a single column floating-point matrix. Each row is the value of the histogram in each bin. + + :param signature2: Second signature of the same format and size as ``signature1``. diff --git a/modules/shape/doc/histogram_cost_matrix.rst b/modules/shape/doc/histogram_cost_matrix.rst new file mode 100644 index 000000000..9f6804dd0 --- /dev/null +++ b/modules/shape/doc/histogram_cost_matrix.rst @@ -0,0 +1,82 @@ +Cost Matrix for Histograms Common Interface +=========================================== + +.. highlight:: cpp + +A common interface is defined to ease the implementation of some algorithms pipelines, such +as the Shape Context Matching Algorithm. A common class is defined, so any object that implements +a Cost Matrix builder inherits the +:ocv:class:`HistogramCostExtractor` interface. + +HistogramCostExtractor +---------------------- +.. ocv:class:: HistogramCostExtractor : public Algorithm + +Abstract base class for histogram cost algorithms. :: + + class CV_EXPORTS_W HistogramCostExtractor : public Algorithm + { + public: + CV_WRAP virtual void buildCostMatrix(InputArray descriptors1, InputArray descriptors2, OutputArray costMatrix) = 0; + + CV_WRAP virtual void setNDummies(int nDummies) = 0; + CV_WRAP virtual int getNDummies() const = 0; + + CV_WRAP virtual void setDefaultCost(float defaultCost) = 0; + CV_WRAP virtual float getDefaultCost() const = 0; + }; + +NormHistogramCostExtractor +-------------------------- +.. ocv:class:: NormHistogramCostExtractor : public HistogramCostExtractor + +A norm based cost extraction. :: + + class CV_EXPORTS_W NormHistogramCostExtractor : public HistogramCostExtractor + { + public: + CV_WRAP virtual void setNormFlag(int flag) = 0; + CV_WRAP virtual int getNormFlag() const = 0; + }; + + CV_EXPORTS_W Ptr<HistogramCostExtractor> + createNormHistogramCostExtractor(int flag=cv::DIST_L2, int nDummies=25, float defaultCost=0.2); + +EMDHistogramCostExtractor +------------------------- +.. ocv:class:: EMDHistogramCostExtractor : public HistogramCostExtractor + +An EMD based cost extraction. :: + + class CV_EXPORTS_W EMDHistogramCostExtractor : public HistogramCostExtractor + { + public: + CV_WRAP virtual void setNormFlag(int flag) = 0; + CV_WRAP virtual int getNormFlag() const = 0; + }; + + CV_EXPORTS_W Ptr<HistogramCostExtractor> + createEMDHistogramCostExtractor(int flag=cv::DIST_L2, int nDummies=25, float defaultCost=0.2); + +ChiHistogramCostExtractor +------------------------- +.. ocv:class:: ChiHistogramCostExtractor : public HistogramCostExtractor + +An Chi based cost extraction. :: + + class CV_EXPORTS_W ChiHistogramCostExtractor : public HistogramCostExtractor + {}; + + CV_EXPORTS_W Ptr<HistogramCostExtractor> createChiHistogramCostExtractor(int nDummies=25, float defaultCost=0.2); + +EMDL1HistogramCostExtractor +------------------------- +.. ocv:class:: EMDL1HistogramCostExtractor : public HistogramCostExtractor + +An EMD-L1 based cost extraction. :: + + class CV_EXPORTS_W EMDL1HistogramCostExtractor : public HistogramCostExtractor + {}; + + CV_EXPORTS_W Ptr<HistogramCostExtractor> + createEMDL1HistogramCostExtractor(int nDummies=25, float defaultCost=0.2); diff --git a/modules/shape/doc/shape.rst b/modules/shape/doc/shape.rst new file mode 100644 index 000000000..663a8b2e1 --- /dev/null +++ b/modules/shape/doc/shape.rst @@ -0,0 +1,15 @@ +********************************** +shape. Shape Distance and Matching +********************************** + +The module contains algorithms that embed a notion of shape distance. +These algorithms may be used for shape matching and retrieval, or shape +comparison. + +.. toctree:: + :maxdepth: 2 + + shape_distances + shape_transformers + histogram_cost_matrix + emdL1 diff --git a/modules/shape/doc/shape.rst~ b/modules/shape/doc/shape.rst~ new file mode 100644 index 000000000..b18968556 --- /dev/null +++ b/modules/shape/doc/shape.rst~ @@ -0,0 +1,15 @@ +***** +shape +***** + +The module contains algorithms that embed a notion of shape distance. +These algorithms may be used for shape matching and retrieval, or shape +comparison. + +.. toctree:: + :maxdepth: 2 + + shape_distances + shape_transformers + histogram_cost_matrix + emdL1 diff --git a/modules/shape/doc/shape_distances.rst b/modules/shape/doc/shape_distances.rst new file mode 100644 index 000000000..c671e97ed --- /dev/null +++ b/modules/shape/doc/shape_distances.rst @@ -0,0 +1,231 @@ +Shape Distance and Common Interfaces +==================================== + +.. highlight:: cpp + +Shape Distance algorithms in OpenCV are derivated from a common interface that allows you to +switch between them in a practical way for solving the same problem with different methods. +Thus, all objects that implement shape distance measures inherit the +:ocv:class:`ShapeDistanceExtractor` interface. + + +ShapeDistanceExtractor +---------------------- +.. ocv:class:: ShapeDistanceExtractor : public Algorithm + +Abstract base class for shape distance algorithms. :: + + class CV_EXPORTS_W ShapeDistanceExtractor : public Algorithm + { + public: + CV_WRAP virtual float computeDistance(InputArray contour1, InputArray contour2) = 0; + }; + +ShapeDistanceExtractor::computeDistance +--------------------------------------- +Compute the shape distance between two shapes defined by its contours. + +.. ocv:function:: float ShapeDistanceExtractor::computeDistance( InputArray contour1, InputArray contour2 ) + + :param contour1: Contour defining first shape. + + :param contour2: Contour defining second shape. + +ShapeContextDistanceExtractor +----------------------------- +.. ocv:class:: ShapeContextDistanceExtractor : public ShapeDistanceExtractor + +Implementation of the Shape Context descriptor and matching algorithm proposed by Belongie et al. in +"Shape Matching and Object Recognition Using Shape Contexts" (PAMI 2002). +This implementation is packaged in a generic scheme, in order to allow you the implementation of the +common variations of the original pipeline. :: + + class CV_EXPORTS_W ShapeContextDistanceExtractor : public ShapeDistanceExtractor + { + public: + CV_WRAP virtual void setAngularBins(int nAngularBins) = 0; + CV_WRAP virtual int getAngularBins() const = 0; + + CV_WRAP virtual void setRadialBins(int nRadialBins) = 0; + CV_WRAP virtual int getRadialBins() const = 0; + + CV_WRAP virtual void setInnerRadius(float innerRadius) = 0; + CV_WRAP virtual float getInnerRadius() const = 0; + + CV_WRAP virtual void setOuterRadius(float outerRadius) = 0; + CV_WRAP virtual float getOuterRadius() const = 0; + + CV_WRAP virtual void setRotationInvariant(bool rotationInvariant) = 0; + CV_WRAP virtual bool getRotationInvariant() const = 0; + + CV_WRAP virtual void setShapeContextWeight(float shapeContextWeight) = 0; + CV_WRAP virtual float getShapeContextWeight() const = 0; + + CV_WRAP virtual void setImageAppearanceWeight(float imageAppearanceWeight) = 0; + CV_WRAP virtual float getImageAppearanceWeight() const = 0; + + CV_WRAP virtual void setBendingEnergyWeight(float bendingEnergyWeight) = 0; + CV_WRAP virtual float getBendingEnergyWeight() const = 0; + + CV_WRAP virtual void setImages(InputArray image1, InputArray image2) = 0; + CV_WRAP virtual void getImages(OutputArray image1, OutputArray image2) const = 0; + + CV_WRAP virtual void setIterations(int iterations) = 0; + CV_WRAP virtual int getIterations() const = 0; + + CV_WRAP virtual void setCostExtractor(Ptr<HistogramCostExtractor> comparer) = 0; + CV_WRAP virtual Ptr<HistogramCostExtractor> getCostExtractor() const = 0; + + CV_WRAP virtual void setTransformAlgorithm(Ptr<ShapeTransformer> transformer) = 0; + CV_WRAP virtual Ptr<ShapeTransformer> getTransformAlgorithm() const = 0; + }; + + /* Complete constructor */ + CV_EXPORTS_W Ptr<ShapeContextDistanceExtractor> + createShapeContextDistanceExtractor(int nAngularBins=12, int nRadialBins=4, + float innerRadius=0.2, float outerRadius=2, int iterations=3, + const Ptr<HistogramCostExtractor> &comparer = createChiHistogramCostExtractor(), + const Ptr<ShapeTransformer> &transformer = createThinPlateSplineShapeTransformer()); + +ShapeContextDistanceExtractor::setAngularBins +--------------------------------------------- +Establish the number of angular bins for the Shape Context Descriptor used in the shape matching pipeline. + +.. ocv:function:: void setAngularBins( int nAngularBins ) + + :param nAngularBins: The number of angular bins in the shape context descriptor. + +ShapeContextDistanceExtractor::setRadialBins +-------------------------------------------- +Establish the number of radial bins for the Shape Context Descriptor used in the shape matching pipeline. + +.. ocv:function:: void setRadialBins( int nRadialBins ) + + :param nRadialBins: The number of radial bins in the shape context descriptor. + +ShapeContextDistanceExtractor::setInnerRadius +--------------------------------------------- +Set the inner radius of the shape context descriptor. + +.. ocv:function:: void setInnerRadius(float innerRadius) + + :param innerRadius: The value of the inner radius. + +ShapeContextDistanceExtractor::setOuterRadius +--------------------------------------------- +Set the outer radius of the shape context descriptor. + +.. ocv:function:: void setOuterRadius(float outerRadius) + + :param outerRadius: The value of the outer radius. + +ShapeContextDistanceExtractor::setShapeContextWeight +---------------------------------------------------- +Set the weight of the shape context distance in the final value of the shape distance. +The shape context distance between two shapes is defined as the symmetric sum of shape +context matching costs over best matching points. +The final value of the shape distance is a user-defined linear combination of the shape +context distance, an image appearance distance, and a bending energy. + +.. ocv:function:: void setShapeContextWeight( float shapeContextWeight ) + + :param shapeContextWeight: The weight of the shape context distance in the final distance value. + +ShapeContextDistanceExtractor::setImageAppearanceWeight +------------------------------------------------------- +Set the weight of the Image Appearance cost in the final value of the shape distance. +The image appearance cost is defined as the sum of squared brightness differences in +Gaussian windows around corresponding image points. +The final value of the shape distance is a user-defined linear combination of the shape +context distance, an image appearance distance, and a bending energy. +If this value is set to a number different from 0, is mandatory to set the images that +correspond to each shape. + +.. ocv:function:: void setImageAppearanceWeight( float imageAppearanceWeight ) + + :param imageAppearanceWeight: The weight of the appearance cost in the final distance value. + +ShapeContextDistanceExtractor::setBendingEnergyWeight +----------------------------------------------------- +Set the weight of the Bending Energy in the final value of the shape distance. +The bending energy definition depends on what transformation is being used to align the +shapes. +The final value of the shape distance is a user-defined linear combination of the shape +context distance, an image appearance distance, and a bending energy. + +.. ocv:function:: void setBendingEnergyWeight( float bendingEnergyWeight ) + + :param bendingEnergyWeight: The weight of the Bending Energy in the final distance value. + +ShapeContextDistanceExtractor::setImages +---------------------------------------- +Set the images that correspond to each shape. This images are used in the calculation of the +Image Appearance cost. + +.. ocv:function:: void setImages( InputArray image1, InputArray image2 ) + + :param image1: Image corresponding to the shape defined by ``contours1``. + + :param image2: Image corresponding to the shape defined by ``contours2``. + +ShapeContextDistanceExtractor::setCostExtractor +----------------------------------------------- +Set the algorithm used for building the shape context descriptor cost matrix. + +.. ocv:function:: void setCostExtractor( Ptr<HistogramCostExtractor> comparer ) + + :param comparer: Smart pointer to a HistogramCostExtractor, an algorithm that defines the cost matrix between descriptors. + +ShapeContextDistanceExtractor::setStdDev +---------------------------------------- +Set the value of the standard deviation for the Gaussian window for the image appearance cost. + +.. ocv:function:: void setStdDev( float sigma ) + + :param sigma: Standard Deviation. + +ShapeContextDistanceExtractor::setTransformAlgorithm +---------------------------------------------------- +Set the algorithm used for aligning the shapes. + +.. ocv:function:: void setTransformAlgorithm( Ptr<ShapeTransformer> transformer ) + + :param comparer: Smart pointer to a ShapeTransformer, an algorithm that defines the aligning transformation. + +HausdorffDistanceExtractor +-------------------------- +.. ocv:class:: HausdorffDistanceExtractor : public ShapeDistanceExtractor + +A simple Hausdorff distance measure between shapes defined by contours, +according to the paper "Comparing Images using the Hausdorff distance." by +D.P. Huttenlocher, G.A. Klanderman, and W.J. Rucklidge. (PAMI 1993). :: + + class CV_EXPORTS_W HausdorffDistanceExtractor : public ShapeDistanceExtractor + { + public: + CV_WRAP virtual void setDistanceFlag(int distanceFlag) = 0; + CV_WRAP virtual int getDistanceFlag() const = 0; + + CV_WRAP virtual void setRankProportion(float rankProportion) = 0; + CV_WRAP virtual float getRankProportion() const = 0; + }; + + /* Constructor */ + CV_EXPORTS_W Ptr<HausdorffDistanceExtractor> createHausdorffDistanceExtractor(int distanceFlag=cv::NORM_L2, float rankProp=0.6); + +HausdorffDistanceExtractor::setDistanceFlag +------------------------------------------- +Set the norm used to compute the Hausdorff value between two shapes. It can be L1 or L2 norm. + +.. ocv:function:: void setDistanceFlag( int distanceFlag ) + + :param distanceFlag: Flag indicating which norm is used to compute the Hausdorff distance (NORM_L1, NORM_L2). + +HausdorffDistanceExtractor::setRankProportion +--------------------------------------------- +This method sets the rank proportion (or fractional value) that establish the Kth ranked value of the +partial Hausdorff distance. Experimentally had been shown that 0.6 is a good value to compare shapes. + +.. ocv:function:: void setRankProportion( float rankProportion ) + + :param rankProportion: fractional value (between 0 and 1). diff --git a/modules/shape/doc/shape_transformers.rst b/modules/shape/doc/shape_transformers.rst new file mode 100644 index 000000000..b1d202602 --- /dev/null +++ b/modules/shape/doc/shape_transformers.rst @@ -0,0 +1,108 @@ +Shape Transformers and Interfaces +================================= + +.. highlight:: cpp + +A virtual interface that ease the use of transforming algorithms in some pipelines, such as +the Shape Context Matching Algorithm. Thus, all objects that implement shape transformation +techniques inherit the +:ocv:class:`ShapeTransformer` interface. + +ShapeTransformer +---------------- +.. ocv:class:: ShapeTransformer : public Algorithm + +Abstract base class for shape transformation algorithms. :: + + class CV_EXPORTS_W ShapeTransformer : public Algorithm + { + public: + CV_WRAP virtual void estimateTransformation(InputArray transformingShape, InputArray targetShape, + std::vector<DMatch>& matches) = 0; + + CV_WRAP virtual float applyTransformation(InputArray input, OutputArray output=noArray()) = 0; + + CV_WRAP virtual void warpImage(InputArray transformingImage, OutputArray output, + int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, + const Scalar& borderValue=Scalar()) const = 0; + }; + +ShapeTransformer::estimateTransformation +---------------------------------------- +Estimate the transformation parameters of the current transformer algorithm, based on point matches. + +.. ocv:function:: void estimateTransformation( InputArray transformingShape, InputArray targetShape, std::vector<DMatch>& matches ) + + :param transformingShape: Contour defining first shape. + + :param targetShape: Contour defining second shape (Target). + + :param matches: Standard vector of Matches between points. + +ShapeTransformer::applyTransformation +------------------------------------- +Apply a transformation, given a pre-estimated transformation parameters. + +.. ocv:function:: float applyTransformation( InputArray input, OutputArray output=noArray() ) + + :param input: Contour (set of points) to apply the transformation. + + :param output: Output contour. + +ShapeTransformer::warpImage +--------------------------- +Apply a transformation, given a pre-estimated transformation parameters, to an Image. + +.. ocv:function:: void warpImage( InputArray transformingImage, OutputArray output, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar() ) + + :param transformingImage: Input image. + + :param output: Output image. + + :param flags: Image interpolation method. + + :param borderMode: border style. + + :param borderValue: border value. + +ThinPlateSplineShapeTransformer +------------------------------- +.. ocv:class:: ThinPlateSplineShapeTransformer : public Algorithm + +Definition of the transformation ocupied in the paper "Principal Warps: Thin-Plate Splines and Decomposition +of Deformations", by F.L. Bookstein (PAMI 1989). :: + + class CV_EXPORTS_W ThinPlateSplineShapeTransformer : public ShapeTransformer + { + public: + CV_WRAP virtual void setRegularizationParameter(double beta) = 0; + CV_WRAP virtual double getRegularizationParameter() const = 0; + }; + + /* Complete constructor */ + CV_EXPORTS_W Ptr<ThinPlateSplineShapeTransformer> + createThinPlateSplineShapeTransformer(double regularizationParameter=0); + +ThinPlateSplineShapeTransformer::setRegularizationParameter +----------------------------------------------------------- +Set the regularization parameter for relaxing the exact interpolation requirements of the TPS algorithm. + +.. ocv:function:: void setRegularizationParameter( double beta ) + + :param beta: value of the regularization parameter. + +AffineTransformer +----------------- +.. ocv:class:: AffineTransformer : public Algorithm + +Wrapper class for the OpenCV Affine Transformation algorithm. :: + + class CV_EXPORTS_W AffineTransformer : public ShapeTransformer + { + public: + CV_WRAP virtual void setFullAffine(bool fullAffine) = 0; + CV_WRAP virtual bool getFullAffine() const = 0; + }; + + /* Complete constructor */ + CV_EXPORTS_W Ptr<AffineTransformer> createAffineTransformer(bool fullAffine); diff --git a/modules/shape/include/opencv2/shape.hpp b/modules/shape/include/opencv2/shape.hpp new file mode 100644 index 000000000..d07bf5e45 --- /dev/null +++ b/modules/shape/include/opencv2/shape.hpp @@ -0,0 +1,58 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2012, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_SHAPE_HPP__ +#define __OPENCV_SHAPE_HPP__ + +#include "opencv2/shape/emdL1.hpp" +#include "opencv2/shape/shape_transformer.hpp" +#include "opencv2/shape/hist_cost.hpp" +#include "opencv2/shape/shape_distance.hpp" + +namespace cv +{ +CV_EXPORTS bool initModule_shape(); +} + +#endif + +/* End of file. */ diff --git a/modules/shape/include/opencv2/shape/emdL1.hpp b/modules/shape/include/opencv2/shape/emdL1.hpp new file mode 100644 index 000000000..400d26b26 --- /dev/null +++ b/modules/shape/include/opencv2/shape/emdL1.hpp @@ -0,0 +1,58 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009-2012, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_EMD_L1_HPP__ +#define __OPENCV_EMD_L1_HPP__ + +#include "opencv2/core.hpp" + +namespace cv +{ +/****************************************************************************************\ +* EMDL1 Function * +\****************************************************************************************/ + +CV_EXPORTS float EMDL1(InputArray signature1, InputArray signature2); + +}//namespace cv + +#endif diff --git a/modules/shape/include/opencv2/shape/hist_cost.hpp b/modules/shape/include/opencv2/shape/hist_cost.hpp new file mode 100644 index 000000000..9ca3825fd --- /dev/null +++ b/modules/shape/include/opencv2/shape/hist_cost.hpp @@ -0,0 +1,103 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// 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, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_HIST_COST_HPP__ +#define __OPENCV_HIST_COST_HPP__ + +#include "opencv2/imgproc.hpp" + +namespace cv +{ + +/*! + * The base class for HistogramCostExtractor. + */ +class CV_EXPORTS_W HistogramCostExtractor : public Algorithm +{ +public: + CV_WRAP virtual void buildCostMatrix(InputArray descriptors1, InputArray descriptors2, OutputArray costMatrix) = 0; + + CV_WRAP virtual void setNDummies(int nDummies) = 0; + CV_WRAP virtual int getNDummies() const = 0; + + CV_WRAP virtual void setDefaultCost(float defaultCost) = 0; + CV_WRAP virtual float getDefaultCost() const = 0; +}; + +/*! */ +class CV_EXPORTS_W NormHistogramCostExtractor : public HistogramCostExtractor +{ +public: + CV_WRAP virtual void setNormFlag(int flag) = 0; + CV_WRAP virtual int getNormFlag() const = 0; +}; + +CV_EXPORTS_W Ptr<HistogramCostExtractor> + createNormHistogramCostExtractor(int flag=DIST_L2, int nDummies=25, float defaultCost=0.2); + +/*! */ +class CV_EXPORTS_W EMDHistogramCostExtractor : public HistogramCostExtractor +{ +public: + CV_WRAP virtual void setNormFlag(int flag) = 0; + CV_WRAP virtual int getNormFlag() const = 0; +}; + +CV_EXPORTS_W Ptr<HistogramCostExtractor> + createEMDHistogramCostExtractor(int flag=DIST_L2, int nDummies=25, float defaultCost=0.2); + +/*! */ +class CV_EXPORTS_W ChiHistogramCostExtractor : public HistogramCostExtractor +{}; + +CV_EXPORTS_W Ptr<HistogramCostExtractor> createChiHistogramCostExtractor(int nDummies=25, float defaultCost=0.2); + +/*! */ +class CV_EXPORTS_W EMDL1HistogramCostExtractor : public HistogramCostExtractor +{}; + +CV_EXPORTS_W Ptr<HistogramCostExtractor> + createEMDL1HistogramCostExtractor(int nDummies=25, float defaultCost=0.2); + +} // cv +#endif diff --git a/modules/shape/include/opencv2/shape/shape.hpp b/modules/shape/include/opencv2/shape/shape.hpp new file mode 100644 index 000000000..5c4da3cef --- /dev/null +++ b/modules/shape/include/opencv2/shape/shape.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// 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, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/shape.hpp" diff --git a/modules/shape/include/opencv2/shape/shape_distance.hpp b/modules/shape/include/opencv2/shape/shape_distance.hpp new file mode 100644 index 000000000..55e21aaa4 --- /dev/null +++ b/modules/shape/include/opencv2/shape/shape_distance.hpp @@ -0,0 +1,143 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// 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, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_SHAPE_SHAPE_DISTANCE_HPP__ +#define __OPENCV_SHAPE_SHAPE_DISTANCE_HPP__ +#include "opencv2/core.hpp" +#include "opencv2/shape/hist_cost.hpp" +#include "opencv2/shape/shape_transformer.hpp" + +namespace cv +{ + +/*! + * The base class for ShapeDistanceExtractor. + * This is just to define the common interface for + * shape comparisson techniques. + */ +class CV_EXPORTS_W ShapeDistanceExtractor : public Algorithm +{ +public: + CV_WRAP virtual float computeDistance(InputArray contour1, InputArray contour2) = 0; +}; + +/***********************************************************************************/ +/***********************************************************************************/ +/***********************************************************************************/ +/*! + * Shape Context implementation. + * The SCD class implements SCD algorithm proposed by Belongie et al.in + * "Shape Matching and Object Recognition Using Shape Contexts". + * Implemented by Juan M. Perez for the GSOC 2013. + */ +class CV_EXPORTS_W ShapeContextDistanceExtractor : public ShapeDistanceExtractor +{ +public: + CV_WRAP virtual void setAngularBins(int nAngularBins) = 0; + CV_WRAP virtual int getAngularBins() const = 0; + + CV_WRAP virtual void setRadialBins(int nRadialBins) = 0; + CV_WRAP virtual int getRadialBins() const = 0; + + CV_WRAP virtual void setInnerRadius(float innerRadius) = 0; + CV_WRAP virtual float getInnerRadius() const = 0; + + CV_WRAP virtual void setOuterRadius(float outerRadius) = 0; + CV_WRAP virtual float getOuterRadius() const = 0; + + CV_WRAP virtual void setRotationInvariant(bool rotationInvariant) = 0; + CV_WRAP virtual bool getRotationInvariant() const = 0; + + CV_WRAP virtual void setShapeContextWeight(float shapeContextWeight) = 0; + CV_WRAP virtual float getShapeContextWeight() const = 0; + + CV_WRAP virtual void setImageAppearanceWeight(float imageAppearanceWeight) = 0; + CV_WRAP virtual float getImageAppearanceWeight() const = 0; + + CV_WRAP virtual void setBendingEnergyWeight(float bendingEnergyWeight) = 0; + CV_WRAP virtual float getBendingEnergyWeight() const = 0; + + CV_WRAP virtual void setImages(InputArray image1, InputArray image2) = 0; + CV_WRAP virtual void getImages(OutputArray image1, OutputArray image2) const = 0; + + CV_WRAP virtual void setIterations(int iterations) = 0; + CV_WRAP virtual int getIterations() const = 0; + + CV_WRAP virtual void setCostExtractor(Ptr<HistogramCostExtractor> comparer) = 0; + CV_WRAP virtual Ptr<HistogramCostExtractor> getCostExtractor() const = 0; + + CV_WRAP virtual void setStdDev(float sigma) = 0; + CV_WRAP virtual float getStdDev() const = 0; + + CV_WRAP virtual void setTransformAlgorithm(Ptr<ShapeTransformer> transformer) = 0; + CV_WRAP virtual Ptr<ShapeTransformer> getTransformAlgorithm() const = 0; +}; + +/* Complete constructor */ +CV_EXPORTS_W Ptr<ShapeContextDistanceExtractor> + createShapeContextDistanceExtractor(int nAngularBins=12, int nRadialBins=4, + float innerRadius=0.2, float outerRadius=2, int iterations=3, + const Ptr<HistogramCostExtractor> &comparer = createChiHistogramCostExtractor(), + const Ptr<ShapeTransformer> &transformer = createThinPlateSplineShapeTransformer()); + +/***********************************************************************************/ +/***********************************************************************************/ +/***********************************************************************************/ +/*! + * Hausdorff distace implementation based on + */ +class CV_EXPORTS_W HausdorffDistanceExtractor : public ShapeDistanceExtractor +{ +public: + CV_WRAP virtual void setDistanceFlag(int distanceFlag) = 0; + CV_WRAP virtual int getDistanceFlag() const = 0; + + CV_WRAP virtual void setRankProportion(float rankProportion) = 0; + CV_WRAP virtual float getRankProportion() const = 0; +}; + +/* Constructor */ +CV_EXPORTS_W Ptr<HausdorffDistanceExtractor> createHausdorffDistanceExtractor(int distanceFlag=cv::NORM_L2, float rankProp=0.6); + +} // cv +#endif diff --git a/modules/shape/include/opencv2/shape/shape_transformer.hpp b/modules/shape/include/opencv2/shape/shape_transformer.hpp new file mode 100644 index 000000000..cdabf971c --- /dev/null +++ b/modules/shape/include/opencv2/shape/shape_transformer.hpp @@ -0,0 +1,109 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// 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, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_SHAPE_SHAPE_TRANSFORM_HPP__ +#define __OPENCV_SHAPE_SHAPE_TRANSFORM_HPP__ +#include <vector> +#include "opencv2/core.hpp" +#include "opencv2/imgproc.hpp" + +namespace cv +{ + +/*! + * The base class for ShapeTransformer. + * This is just to define the common interface for + * shape transformation techniques. + */ +class CV_EXPORTS_W ShapeTransformer : public Algorithm +{ +public: + /* Estimate, Apply Transformation and return Transforming cost*/ + CV_WRAP virtual void estimateTransformation(InputArray transformingShape, InputArray targetShape, + std::vector<DMatch>& matches) = 0; + + CV_WRAP virtual float applyTransformation(InputArray input, OutputArray output=noArray()) = 0; + + CV_WRAP virtual void warpImage(InputArray transformingImage, OutputArray output, + int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, + const Scalar& borderValue=Scalar()) const = 0; +}; + +/***********************************************************************************/ +/***********************************************************************************/ +/*! + * Thin Plate Spline Transformation + * Implementation of the TPS transformation + * according to "Principal Warps: Thin-Plate Splines and the + * Decomposition of Deformations" by Juan Manuel Perez for the GSOC 2013 + */ + +class CV_EXPORTS_W ThinPlateSplineShapeTransformer : public ShapeTransformer +{ +public: + CV_WRAP virtual void setRegularizationParameter(double beta) = 0; + CV_WRAP virtual double getRegularizationParameter() const = 0; +}; + +/* Complete constructor */ +CV_EXPORTS_W Ptr<ThinPlateSplineShapeTransformer> + createThinPlateSplineShapeTransformer(double regularizationParameter=0); + +/***********************************************************************************/ +/***********************************************************************************/ +/*! + * Affine Transformation as a derivated from ShapeTransformer + */ + +class CV_EXPORTS_W AffineTransformer : public ShapeTransformer +{ +public: + CV_WRAP virtual void setFullAffine(bool fullAffine) = 0; + CV_WRAP virtual bool getFullAffine() const = 0; +}; + +/* Complete constructor */ +CV_EXPORTS_W Ptr<AffineTransformer> createAffineTransformer(bool fullAffine); + +} // cv +#endif diff --git a/modules/shape/src/aff_trans.cpp b/modules/shape/src/aff_trans.cpp new file mode 100644 index 000000000..c416b8205 --- /dev/null +++ b/modules/shape/src/aff_trans.cpp @@ -0,0 +1,266 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +namespace cv +{ + +class AffineTransformerImpl : public AffineTransformer +{ +public: + /* Constructors */ + AffineTransformerImpl() + { + fullAffine = true; + name_ = "ShapeTransformer.AFF"; + } + + AffineTransformerImpl(bool _fullAffine) + { + fullAffine = _fullAffine; + name_ = "ShapeTransformer.AFF"; + } + + /* Destructor */ + ~AffineTransformerImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual void estimateTransformation(InputArray transformingShape, InputArray targetShape, std::vector<DMatch> &matches); + virtual float applyTransformation(InputArray input, OutputArray output=noArray()); + virtual void warpImage(InputArray transformingImage, OutputArray output, + int flags, int borderMode, const Scalar& borderValue) const; + + //! Setters/Getters + virtual void setFullAffine(bool _fullAffine) {fullAffine=_fullAffine;} + virtual bool getFullAffine() const {return fullAffine;} + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "affine_type" << fullAffine; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + fullAffine = (int)fn["affine_type"]; + } + +private: + bool fullAffine; + Mat affineMat; + float transformCost; + +protected: + String name_; +}; + +void AffineTransformerImpl::warpImage(InputArray transformingImage, OutputArray output, + int flags, int borderMode, const Scalar& borderValue) const +{ + CV_Assert(!affineMat.empty()); + warpAffine(transformingImage, output, affineMat, transformingImage.getMat().size(), flags, borderMode, borderValue); +} + + +static Mat _localAffineEstimate(const std::vector<Point2f>& shape1, const std::vector<Point2f>& shape2, + bool fullAfine) +{ + Mat out(2,3,CV_32F); + int siz=2*shape1.size(); + + if (fullAfine) + { + Mat matM(siz, 6, CV_32F); + Mat matP(siz,1,CV_32F); + int contPt=0; + for (int ii=0; ii<siz; ii++) + { + Mat therow = Mat::zeros(1,6,CV_32F); + if (ii%2==0) + { + therow.at<float>(0,0)=shape1[contPt].x; + therow.at<float>(0,1)=shape1[contPt].y; + therow.at<float>(0,2)=1; + therow.row(0).copyTo(matM.row(ii)); + matP.at<float>(ii,0) = shape2[contPt].x; + } + else + { + therow.at<float>(0,3)=shape1[contPt].x; + therow.at<float>(0,4)=shape1[contPt].y; + therow.at<float>(0,5)=1; + therow.row(0).copyTo(matM.row(ii)); + matP.at<float>(ii,0) = shape2[contPt].y; + contPt++; + } + } + Mat sol; + solve(matM, matP, sol, DECOMP_SVD); + out = sol.reshape(0,2); + } + else + { + Mat matM(siz, 4, CV_32F); + Mat matP(siz,1,CV_32F); + int contPt=0; + for (int ii=0; ii<siz; ii++) + { + Mat therow = Mat::zeros(1,4,CV_32F); + if (ii%2==0) + { + therow.at<float>(0,0)=shape1[contPt].x; + therow.at<float>(0,1)=shape1[contPt].y; + therow.at<float>(0,2)=1; + therow.row(0).copyTo(matM.row(ii)); + matP.at<float>(ii,0) = shape2[contPt].x; + } + else + { + therow.at<float>(0,0)=-shape1[contPt].y; + therow.at<float>(0,1)=shape1[contPt].x; + therow.at<float>(0,3)=1; + therow.row(0).copyTo(matM.row(ii)); + matP.at<float>(ii,0) = shape2[contPt].y; + contPt++; + } + } + Mat sol; + solve(matM, matP, sol, DECOMP_SVD); + out.at<float>(0,0)=sol.at<float>(0,0); + out.at<float>(0,1)=sol.at<float>(1,0); + out.at<float>(0,2)=sol.at<float>(2,0); + out.at<float>(1,0)=-sol.at<float>(1,0); + out.at<float>(1,1)=sol.at<float>(0,0); + out.at<float>(1,2)=sol.at<float>(3,0); + } + return out; +} + +void AffineTransformerImpl::estimateTransformation(InputArray _pts1, InputArray _pts2, std::vector<DMatch>& _matches) +{ + Mat pts1 = _pts1.getMat(); + Mat pts2 = _pts2.getMat(); + CV_Assert((pts1.channels()==2) & (pts1.cols>0) & (pts2.channels()==2) & (pts2.cols>0)); + CV_Assert(_matches.size()>1); + + if (pts1.type() != CV_32F) + pts1.convertTo(pts1, CV_32F); + if (pts2.type() != CV_32F) + pts2.convertTo(pts2, CV_32F); + + // Use only valid matchings // + std::vector<DMatch> matches; + for (size_t i=0; i<_matches.size(); i++) + { + if (_matches[i].queryIdx<pts1.cols && + _matches[i].trainIdx<pts2.cols) + { + matches.push_back(_matches[i]); + } + } + + // Organizing the correspondent points in vector style // + std::vector<Point2f> shape1; // transforming shape + std::vector<Point2f> shape2; // target shape + for (size_t i=0; i<matches.size(); i++) + { + Point2f pt1=pts1.at<Point2f>(0,matches[i].queryIdx); + shape1.push_back(pt1); + + Point2f pt2=pts2.at<Point2f>(0,matches[i].trainIdx); + shape2.push_back(pt2); + } + + // estimateRigidTransform // + Mat affine; + estimateRigidTransform(shape1, shape2, fullAffine).convertTo(affine, CV_32F); + + if (affine.empty()) + affine=_localAffineEstimate(shape1, shape2, fullAffine); //In case there is not good solution, just give a LLS based one + + affineMat = affine; +} + +float AffineTransformerImpl::applyTransformation(InputArray inPts, OutputArray outPts) +{ + Mat pts1 = inPts.getMat(); + CV_Assert((pts1.channels()==2) & (pts1.cols>0)); + + //Apply transformation in the complete set of points + Mat fAffine; + transform(pts1, fAffine, affineMat); + + // Ensambling output // + if (outPts.needed()) + { + outPts.create(1,fAffine.cols, CV_32FC2); + Mat outMat = outPts.getMat(); + for (int i=0; i<fAffine.cols; i++) + outMat.at<Point2f>(0,i)=fAffine.at<Point2f>(0,i); + } + + // Updating Transform Cost // + Mat Af(2, 2, CV_32F); + Af.at<float>(0,0)=affineMat.at<float>(0,0); + Af.at<float>(0,1)=affineMat.at<float>(1,0); + Af.at<float>(1,0)=affineMat.at<float>(0,1); + Af.at<float>(1,1)=affineMat.at<float>(1,1); + SVD mysvd(Af, SVD::NO_UV); + Mat singVals=mysvd.w; + transformCost=std::log((singVals.at<float>(0,0)+FLT_MIN)/(singVals.at<float>(1,0)+FLT_MIN)); + + return transformCost; +} + +Ptr <AffineTransformer> createAffineTransformer(bool fullAffine) +{ + return Ptr<AffineTransformer>( new AffineTransformerImpl(fullAffine) ); +} + +} // cv diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp new file mode 100644 index 000000000..cdd903e1a --- /dev/null +++ b/modules/shape/src/emdL1.cpp @@ -0,0 +1,794 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +/* + * Implementation of an optimized EMD for histograms based in + * the papers "EMD-L1: An efficient and Robust Algorithm + * for comparing histogram-based descriptors", by Haibin Ling and + * Kazunori Okuda; and "The Earth Mover's Distance is the Mallows + * Distance: Some Insights from Statistics", by Elizaveta Levina and + * Peter Bickel, based on HAIBIN LING AND KAZUNORI OKADA implementation. + */ + +#include "precomp.hpp" +#include "emdL1_def.hpp" + + +/****************************************************************************************\ +* EMDL1 Class * +\****************************************************************************************/ + +float EmdL1::getEMDL1(cv::Mat &sig1, cv::Mat &sig2) +{ + // Initialization + CV_Assert((sig1.rows==sig2.rows) & (sig1.cols==sig2.cols) & (!sig1.empty()) & (!sig2.empty())); + if(!initBaseTrees(sig1.rows, 1)) + return -1; + + float *H1=new float[sig1.rows], *H2 = new float[sig2.rows]; + for (int ii=0; ii<sig1.rows; ii++) + { + H1[ii]=sig1.at<float>(ii,0); + H2[ii]=sig2.at<float>(ii,0); + } + + fillBaseTrees(H1,H2); // Initialize histograms + greedySolution(); // Construct an initial Basic Feasible solution + initBVTree(); // Initialize BVTree + + // Iteration + bool bOptimal = false; + m_nItr = 0; + while(!bOptimal && m_nItr<nMaxIt) + { + // Derive U=(u_ij) for row i and column j + if(m_nItr==0) updateSubtree(m_pRoot); + else updateSubtree(m_pEnter->pChild); + + // Optimality test + bOptimal = isOptimal(); + + // Find new solution + if(!bOptimal) + findNewSolution(); + ++m_nItr; + } + delete [] H1; + delete [] H2; + // Output the total flow + return compuTotalFlow(); +} + +void EmdL1::setMaxIteration(int _nMaxIt) +{ + nMaxIt=_nMaxIt; +} + +//-- SubFunctions called in the EMD algorithm +bool EmdL1::initBaseTrees(int n1, int n2, int n3) +{ + if(binsDim1==n1 && binsDim2==n2 && binsDim3==n3) + return true; + binsDim1 = n1; + binsDim2 = n2; + binsDim3 = n3; + if(binsDim1==0 || binsDim2==0) dimension = 0; + else dimension = (binsDim3==0)?2:3; + + if(dimension==2) + { + m_Nodes.resize(binsDim1); + m_EdgesUp.resize(binsDim1); + m_EdgesRight.resize(binsDim1); + for(int i1=0; i1<binsDim1; i1++) + { + m_Nodes[i1].resize(binsDim2); + m_EdgesUp[i1].resize(binsDim2); + m_EdgesRight[i1].resize(binsDim2); + } + m_NBVEdges.resize(binsDim1*binsDim2*4+2); + m_auxQueue.resize(binsDim1*binsDim2+2); + m_fromLoop.resize(binsDim1*binsDim2+2); + m_toLoop.resize(binsDim1*binsDim2+2); + } + else if(dimension==3) + { + m_3dNodes.resize(binsDim1); + m_3dEdgesUp.resize(binsDim1); + m_3dEdgesRight.resize(binsDim1); + m_3dEdgesDeep.resize(binsDim1); + for(int i1=0; i1<binsDim1; i1++) + { + m_3dNodes[i1].resize(binsDim2); + m_3dEdgesUp[i1].resize(binsDim2); + m_3dEdgesRight[i1].resize(binsDim2); + m_3dEdgesDeep[i1].resize(binsDim2); + for(int i2=0; i2<binsDim2; i2++) + { + m_3dNodes[i1][i2].resize(binsDim3); + m_3dEdgesUp[i1][i2].resize(binsDim3); + m_3dEdgesRight[i1][i2].resize(binsDim3); + m_3dEdgesDeep[i1][i2].resize(binsDim3); + } + } + m_NBVEdges.resize(binsDim1*binsDim2*binsDim3*6+4); + m_auxQueue.resize(binsDim1*binsDim2*binsDim3+4); + m_fromLoop.resize(binsDim1*binsDim2*binsDim3+4); + m_toLoop.resize(binsDim1*binsDim2*binsDim3+2); + } + else + return false; + + return true; +} + +bool EmdL1::fillBaseTrees(float *H1, float *H2) +{ + //- Set global counters + m_pRoot = NULL; + // Graph initialization + float *p1 = H1; + float *p2 = H2; + if(dimension==2) + { + for(int c=0; c<binsDim2; c++) + { + for(int r=0; r<binsDim1; r++) + { + //- initialize nodes and links + m_Nodes[r][c].pos[0] = r; + m_Nodes[r][c].pos[1] = c; + m_Nodes[r][c].d = *(p1++)-*(p2++); + m_Nodes[r][c].pParent = NULL; + m_Nodes[r][c].pChild = NULL; + m_Nodes[r][c].iLevel = -1; + + //- initialize edges + // to the right + m_EdgesRight[r][c].pParent = &(m_Nodes[r][c]); + m_EdgesRight[r][c].pChild = &(m_Nodes[r][(c+1)%binsDim2]); + m_EdgesRight[r][c].flow = 0; + m_EdgesRight[r][c].iDir = 1; + m_EdgesRight[r][c].pNxt = NULL; + + // to the upward + m_EdgesUp[r][c].pParent = &(m_Nodes[r][c]); + m_EdgesUp[r][c].pChild = &(m_Nodes[(r+1)%binsDim1][c]); + m_EdgesUp[r][c].flow = 0; + m_EdgesUp[r][c].iDir = 1; + m_EdgesUp[r][c].pNxt = NULL; + } + } + } + else if(dimension==3) + { + for(int z=0; z<binsDim3; z++) + { + for(int c=0; c<binsDim2; c++) + { + for(int r=0; r<binsDim1; r++) + { + //- initialize nodes and edges + m_3dNodes[r][c][z].pos[0] = r; + m_3dNodes[r][c][z].pos[1] = c; + m_3dNodes[r][c][z].pos[2] = z; + m_3dNodes[r][c][z].d = *(p1++)-*(p2++); + m_3dNodes[r][c][z].pParent = NULL; + m_3dNodes[r][c][z].pChild = NULL; + m_3dNodes[r][c][z].iLevel = -1; + + //- initialize edges + // to the upward + m_3dEdgesUp[r][c][z].pParent= &(m_3dNodes[r][c][z]); + m_3dEdgesUp[r][c][z].pChild = &(m_3dNodes[(r+1)%binsDim1][c][z]); + m_3dEdgesUp[r][c][z].flow = 0; + m_3dEdgesUp[r][c][z].iDir = 1; + m_3dEdgesUp[r][c][z].pNxt = NULL; + + // to the right + m_3dEdgesRight[r][c][z].pParent = &(m_3dNodes[r][c][z]); + m_3dEdgesRight[r][c][z].pChild = &(m_3dNodes[r][(c+1)%binsDim2][z]); + m_3dEdgesRight[r][c][z].flow = 0; + m_3dEdgesRight[r][c][z].iDir = 1; + m_3dEdgesRight[r][c][z].pNxt = NULL; + + // to the deep + m_3dEdgesDeep[r][c][z].pParent = &(m_3dNodes[r][c][z]); + m_3dEdgesDeep[r][c][z].pChild = &(m_3dNodes[r][c])[(z+1)%binsDim3]; + m_3dEdgesDeep[r][c][z].flow = 0; + m_3dEdgesDeep[r][c][z].iDir = 1; + m_3dEdgesDeep[r][c][z].pNxt = NULL; + } + } + } + } + return true; +} + +bool EmdL1::greedySolution() +{ + return dimension==2?greedySolution2():greedySolution3(); +} + +bool EmdL1::greedySolution2() +{ + //- Prepare auxiliary array, D=H1-H2 + int c,r; + floatArray2D D(binsDim1); + for(r=0; r<binsDim1; r++) + { + D[r].resize(binsDim2); + for(c=0; c<binsDim2; c++) D[r][c] = m_Nodes[r][c].d; + } + // compute integrated values along each dimension + std::vector<float> d2s(binsDim2); + d2s[0] = 0; + for(c=0; c<binsDim2-1; c++) + { + d2s[c+1] = d2s[c]; + for(r=0; r<binsDim1; r++) d2s[c+1]-= D[r][c]; + } + + std::vector<float> d1s(binsDim1); + d1s[0] = 0; + for(r=0; r<binsDim1-1; r++) + { + d1s[r+1] = d1s[r]; + for(c=0; c<binsDim2; c++) d1s[r+1]-= D[r][c]; + } + + //- Greedy algorithm for initial solution + cvPEmdEdge pBV; + float dFlow; + bool bUpward = false; + nNBV = 0; // number of NON-BV edges + + for(c=0; c<binsDim2-1; c++) + for(r=0; r<binsDim1; r++) + { + dFlow = D[r][c]; + bUpward = (r<binsDim1-1) && (fabs(dFlow+d2s[c+1]) > fabs(dFlow+d1s[r+1])); // Move upward or right + + // modify basic variables, record BV and related values + if(bUpward) + { + // move to up + pBV = &(m_EdgesUp[r][c]); + m_NBVEdges[nNBV++] = &(m_EdgesRight[r][c]); + D[r+1][c] += dFlow; // auxilary matrix maintanence + d1s[r+1] += dFlow; // auxilary matrix maintanence + } + else + { + // move to right, no other choice + pBV = &(m_EdgesRight[r][c]); + if(r<binsDim1-1) + m_NBVEdges[nNBV++] = &(m_EdgesUp[r][c]); + + D[r][c+1] += dFlow; // auxilary matrix maintanence + d2s[c+1] += dFlow; // auxilary matrix maintanence + } + pBV->pParent->pChild = pBV; + pBV->flow = fabs(dFlow); + pBV->iDir = dFlow>0; // 1:outward, 0:inward + } + + //- rightmost column, no choice but move upward + c = binsDim2-1; + for(r=0; r<binsDim1-1; r++) + { + dFlow = D[r][c]; + pBV = &(m_EdgesUp[r][c]); + D[r+1][c] += dFlow; // auxilary matrix maintanence + pBV->pParent->pChild= pBV; + pBV->flow = fabs(dFlow); + pBV->iDir = dFlow>0; // 1:outward, 0:inward + } + return true; +} + +bool EmdL1::greedySolution3() +{ + //- Prepare auxiliary array, D=H1-H2 + int i1,i2,i3; + std::vector<floatArray2D> D(binsDim1); + for(i1=0; i1<binsDim1; i1++) + { + D[i1].resize(binsDim2); + for(i2=0; i2<binsDim2; i2++) + { + D[i1][i2].resize(binsDim3); + for(i3=0; i3<binsDim3; i3++) + D[i1][i2][i3] = m_3dNodes[i1][i2][i3].d; + } + } + + // compute integrated values along each dimension + std::vector<float> d1s(binsDim1); + d1s[0] = 0; + for(i1=0; i1<binsDim1-1; i1++) + { + d1s[i1+1] = d1s[i1]; + for(i2=0; i2<binsDim2; i2++) + { + for(i3=0; i3<binsDim3; i3++) + d1s[i1+1] -= D[i1][i2][i3]; + } + } + + std::vector<float> d2s(binsDim2); + d2s[0] = 0; + for(i2=0; i2<binsDim2-1; i2++) + { + d2s[i2+1] = d2s[i2]; + for(i1=0; i1<binsDim1; i1++) + { + for(i3=0; i3<binsDim3; i3++) + d2s[i2+1] -= D[i1][i2][i3]; + } + } + + std::vector<float> d3s(binsDim3); + d3s[0] = 0; + for(i3=0; i3<binsDim3-1; i3++) + { + d3s[i3+1] = d3s[i3]; + for(i1=0; i1<binsDim1; i1++) + { + for(i2=0; i2<binsDim2; i2++) + d3s[i3+1] -= D[i1][i2][i3]; + } + } + + //- Greedy algorithm for initial solution + cvPEmdEdge pBV; + float dFlow, f1,f2,f3; + nNBV = 0; // number of NON-BV edges + for(i3=0; i3<binsDim3; i3++) + { + for(i2=0; i2<binsDim2; i2++) + { + for(i1=0; i1<binsDim1; i1++) + { + if(i3==binsDim3-1 && i2==binsDim2-1 && i1==binsDim1-1) break; + + //- determine which direction to move, either right or upward + dFlow = D[i1][i2][i3]; + f1 = i1<(binsDim1-1)?fabs(dFlow+d1s[i1+1]):VHIGH; + f2 = i2<(binsDim2-1)?fabs(dFlow+d2s[i2+1]):VHIGH; + f3 = i3<(binsDim3-1)?fabs(dFlow+d3s[i3+1]):VHIGH; + + if(f1<f2 && f1<f3) + { + pBV = &(m_3dEdgesUp[i1][i2][i3]); // up + if(i2<binsDim2-1) m_NBVEdges[nNBV++] = &(m_3dEdgesRight[i1][i2][i3]); // right + if(i3<binsDim3-1) m_NBVEdges[nNBV++] = &(m_3dEdgesDeep[i1][i2][i3]); // deep + D[i1+1][i2][i3] += dFlow; // maintain auxilary matrix + d1s[i1+1] += dFlow; + } + else if(f2<f3) + { + pBV = &(m_3dEdgesRight[i1][i2][i3]); // right + if(i1<binsDim1-1) m_NBVEdges[nNBV++] = &(m_3dEdgesUp[i1][i2][i3]); // up + if(i3<binsDim3-1) m_NBVEdges[nNBV++] = &(m_3dEdgesDeep[i1][i2][i3]); // deep + D[i1][i2+1][i3] += dFlow; // maintain auxilary matrix + d2s[i2+1] += dFlow; + } + else + { + pBV = &(m_3dEdgesDeep[i1][i2][i3]); // deep + if(i2<binsDim2-1) m_NBVEdges[nNBV++] = &(m_3dEdgesRight[i1][i2][i3]); // right + if(i1<binsDim1-1) m_NBVEdges[nNBV++] = &(m_3dEdgesUp[i1][i2][i3]); // up + D[i1][i2][i3+1] += dFlow; // maintain auxilary matrix + d3s[i3+1] += dFlow; + } + + pBV->flow = fabs(dFlow); + pBV->iDir = dFlow>0; // 1:outward, 0:inward + pBV->pParent->pChild= pBV; + } + } + } + return true; +} + +void EmdL1::initBVTree() +{ + // initialize BVTree from the initial BF solution + //- Using the center of the graph as the root + int r = (int)(0.5*binsDim1-.5); + int c = (int)(0.5*binsDim2-.5); + int z = (int)(0.5*binsDim3-.5); + m_pRoot = dimension==2 ? &(m_Nodes[r][c]) : &(m_3dNodes[r][c][z]); + m_pRoot->u = 0; + m_pRoot->iLevel = 0; + m_pRoot->pParent= NULL; + m_pRoot->pPEdge = NULL; + + //- Prepare a queue + m_auxQueue[0] = m_pRoot; + int nQueue = 1; // length of queue + int iQHead = 0; // head of queue + + //- Recursively build subtrees + cvPEmdEdge pCurE=NULL, pNxtE=NULL; + cvPEmdNode pCurN=NULL, pNxtN=NULL; + int nBin = binsDim1*binsDim2*std::max(binsDim3,1); + while(iQHead<nQueue && nQueue<nBin) + { + pCurN = m_auxQueue[iQHead++]; // pop out from queue + r = pCurN->pos[0]; + c = pCurN->pos[1]; + z = pCurN->pos[2]; + + // check connection from itself + pCurE = pCurN->pChild; // the initial child from initial solution + if(pCurE) + { + pNxtN = pCurE->pChild; + pNxtN->pParent = pCurN; + pNxtN->pPEdge = pCurE; + m_auxQueue[nQueue++] = pNxtN; + } + + // check four neighbor nodes + int nNB = dimension==2?4:6; + for(int k=0;k<nNB;k++) + { + if(dimension==2) + { + if(k==0 && c>0) pNxtN = &(m_Nodes[r][c-1]); // left + else if(k==1 && r>0) pNxtN = &(m_Nodes[r-1][c]); // down + else if(k==2 && c<binsDim2-1) pNxtN = &(m_Nodes[r][c+1]); // right + else if(k==3 && r<binsDim1-1) pNxtN = &(m_Nodes[r+1][c]); // up + else continue; + } + else if(dimension==3) + { + if(k==0 && c>0) pNxtN = &(m_3dNodes[r][c-1][z]); // left + else if(k==1 && c<binsDim2-1) pNxtN = &(m_3dNodes[r][c+1][z]); // right + else if(k==2 && r>0) pNxtN = &(m_3dNodes[r-1][c][z]); // down + else if(k==3 && r<binsDim1-1) pNxtN = &(m_3dNodes[r+1][c][z]); // up + else if(k==4 && z>0) pNxtN = &(m_3dNodes[r][c][z-1]); // shallow + else if(k==5 && z<binsDim3-1) pNxtN = &(m_3dNodes[r][c][z+1]); // deep + else continue; + } + if(pNxtN != pCurN->pParent) + { + pNxtE = pNxtN->pChild; + if(pNxtE && pNxtE->pChild==pCurN) // has connection + { + pNxtN->pParent = pCurN; + pNxtN->pPEdge = pNxtE; + pNxtN->pChild = NULL; + m_auxQueue[nQueue++] = pNxtN; + + pNxtE->pParent = pCurN; // reverse direction + pNxtE->pChild = pNxtN; + pNxtE->iDir = !pNxtE->iDir; + + if(pCurE) pCurE->pNxt = pNxtE; // add to edge list + else pCurN->pChild = pNxtE; + pCurE = pNxtE; + } + } + } + } +} + +void EmdL1::updateSubtree(cvPEmdNode pRoot) +{ + // Initialize auxiliary queue + m_auxQueue[0] = pRoot; + int nQueue = 1; // queue length + int iQHead = 0; // head of queue + + // BFS browing + cvPEmdNode pCurN=NULL,pNxtN=NULL; + cvPEmdEdge pCurE=NULL; + while(iQHead<nQueue) + { + pCurN = m_auxQueue[iQHead++]; // pop out from queue + pCurE = pCurN->pChild; + + // browsing all children + while(pCurE) + { + pNxtN = pCurE->pChild; + pNxtN->iLevel = pCurN->iLevel+1; + pNxtN->u = pCurE->iDir ? (pCurN->u - 1) : (pCurN->u + 1); + pCurE = pCurE->pNxt; + m_auxQueue[nQueue++] = pNxtN; + } + } +} + +bool EmdL1::isOptimal() +{ + int iC, iMinC = 0; + cvPEmdEdge pE; + m_pEnter = NULL; + m_iEnter = -1; + + // test each NON-BV edges + for(int k=0; k<nNBV; ++k) + { + pE = m_NBVEdges[k]; + iC = 1 - pE->pParent->u + pE->pChild->u; + if(iC<iMinC) + { + iMinC = iC; + m_iEnter= k; + } + else + { + // Try reversing the direction + iC = 1 + pE->pParent->u - pE->pChild->u; + if(iC<iMinC) + { + iMinC = iC; + m_iEnter= k; + } + } + } + + if(m_iEnter>=0) + { + m_pEnter = m_NBVEdges[m_iEnter]; + if(iMinC == (1 - m_pEnter->pChild->u + m_pEnter->pParent->u)) { + // reverse direction + cvPEmdNode pN = m_pEnter->pParent; + m_pEnter->pParent = m_pEnter->pChild; + m_pEnter->pChild = pN; + } + + m_pEnter->iDir = 1; + } + return m_iEnter==-1; +} + +void EmdL1::findNewSolution() +{ + // Find loop formed by adding the Enter BV edge. + findLoopFromEnterBV(); + // Modify flow values along the loop + cvPEmdEdge pE = NULL; + float minFlow = m_pLeave->flow; + int k; + for(k=0; k<m_iFrom; k++) + { + pE = m_fromLoop[k]; + if(pE->iDir) pE->flow += minFlow; // outward + else pE->flow -= minFlow; // inward + } + for(k=0; k<m_iTo; k++) + { + pE = m_toLoop[k]; + if(pE->iDir) pE->flow -= minFlow; // outward + else pE->flow += minFlow; // inward + } + + // Update BV Tree, removing the Leaving-BV edge + cvPEmdNode pLParentN = m_pLeave->pParent; + cvPEmdNode pLChildN = m_pLeave->pChild; + cvPEmdEdge pPreE = pLParentN->pChild; + if(pPreE==m_pLeave) + { + pLParentN->pChild = m_pLeave->pNxt; // Leaving-BV is the first child + } + else + { + while(pPreE->pNxt != m_pLeave) + pPreE = pPreE->pNxt; + pPreE->pNxt = m_pLeave->pNxt; // remove Leaving-BV from child list + } + pLChildN->pParent = NULL; + pLChildN->pPEdge = NULL; + + m_NBVEdges[m_iEnter]= m_pLeave; // put the leaving-BV into the NBV array + + // Add the Enter BV edge + cvPEmdNode pEParentN = m_pEnter->pParent; + cvPEmdNode pEChildN = m_pEnter->pChild; + m_pEnter->flow = minFlow; + m_pEnter->pNxt = pEParentN->pChild; // insert the Enter BV as the first child + pEParentN->pChild = m_pEnter; // of its parent + + // Recursively update the tree start from pEChildN + cvPEmdNode pPreN = pEParentN; + cvPEmdNode pCurN = pEChildN; + cvPEmdNode pNxtN; + cvPEmdEdge pNxtE, pPreE0; + pPreE = m_pEnter; + while(pCurN) + { + pNxtN = pCurN->pParent; + pNxtE = pCurN->pPEdge; + pCurN->pParent = pPreN; + pCurN->pPEdge = pPreE; + if(pNxtN) + { + // remove the edge from pNxtN's child list + if(pNxtN->pChild==pNxtE) + { + pNxtN->pChild = pNxtE->pNxt; // first child + } + else + { + pPreE0 = pNxtN->pChild; + while(pPreE0->pNxt != pNxtE) + pPreE0 = pPreE0->pNxt; + pPreE0->pNxt = pNxtE->pNxt; // remove Leaving-BV from child list + } + // reverse the parent-child direction + pNxtE->pParent = pCurN; + pNxtE->pChild = pNxtN; + pNxtE->iDir = !pNxtE->iDir; + pNxtE->pNxt = pCurN->pChild; + pCurN->pChild = pNxtE; + pPreE = pNxtE; + pPreN = pCurN; + } + pCurN = pNxtN; + } + + // Update U at the child of the Enter BV + pEChildN->u = m_pEnter->iDir?(pEParentN->u-1):(pEParentN->u + 1); + pEChildN->iLevel = pEParentN->iLevel+1; +} + +void EmdL1::findLoopFromEnterBV() +{ + // Initialize Leaving-BV edge + float minFlow = VHIGH; + cvPEmdEdge pE = NULL; + int iLFlag = 0; // 0: in the FROM list, 1: in the TO list + + // Using two loop list to store the loop nodes + cvPEmdNode pFrom = m_pEnter->pParent; + cvPEmdNode pTo = m_pEnter->pChild; + m_iFrom = 0; + m_iTo = 0; + m_pLeave = NULL; + + // Trace back to make pFrom and pTo at the same level + while(pFrom->iLevel > pTo->iLevel) + { + pE = pFrom->pPEdge; + m_fromLoop[m_iFrom++] = pE; + if(!pE->iDir && pE->flow<minFlow) + { + minFlow = pE->flow; + m_pLeave = pE; + iLFlag = 0; // 0: in the FROM list + } + pFrom = pFrom->pParent; + } + + while(pTo->iLevel > pFrom->iLevel) + { + pE = pTo->pPEdge; + m_toLoop[m_iTo++] = pE; + if(pE->iDir && pE->flow<minFlow) + { + minFlow = pE->flow; + m_pLeave = pE; + iLFlag = 1; // 1: in the TO list + } + pTo = pTo->pParent; + } + + // Trace pTo and pFrom simultaneously till find their common ancester + while(pTo!=pFrom) + { + pE = pFrom->pPEdge; + m_fromLoop[m_iFrom++] = pE; + if(!pE->iDir && pE->flow<minFlow) + { + minFlow = pE->flow; + m_pLeave = pE; + iLFlag = 0; // 0: in the FROM list, 1: in the TO list + } + pFrom = pFrom->pParent; + + pE = pTo->pPEdge; + m_toLoop[m_iTo++] = pE; + if(pE->iDir && pE->flow<minFlow) + { + minFlow = pE->flow; + m_pLeave = pE; + iLFlag = 1; // 0: in the FROM list, 1: in the TO list + } + pTo = pTo->pParent; + } + + // Reverse the direction of the Enter BV edge if necessary + if(iLFlag==0) + { + cvPEmdNode pN = m_pEnter->pParent; + m_pEnter->pParent = m_pEnter->pChild; + m_pEnter->pChild = pN; + m_pEnter->iDir = !m_pEnter->iDir; + } +} + +float EmdL1::compuTotalFlow() +{ + // Computing the total flow as the final distance + float f = 0; + + // Initialize auxiliary queue + m_auxQueue[0] = m_pRoot; + int nQueue = 1; // length of queue + int iQHead = 0; // head of queue + + // BFS browing the tree + cvPEmdNode pCurN=NULL,pNxtN=NULL; + cvPEmdEdge pCurE=NULL; + while(iQHead<nQueue) + { + pCurN = m_auxQueue[iQHead++]; // pop out from queue + pCurE = pCurN->pChild; + + // browsing all children + while(pCurE) + { + f += pCurE->flow; + pNxtN = pCurE->pChild; + pCurE = pCurE->pNxt; + m_auxQueue[nQueue++] = pNxtN; + } + } + return f; +} + +/****************************************************************************************\ +* EMDL1 Function * +\****************************************************************************************/ + +float cv::EMDL1(InputArray _signature1, InputArray _signature2) +{ + Mat signature1 = _signature1.getMat(), signature2 = _signature2.getMat(); + EmdL1 emdl1; + return emdl1.getEMDL1(signature1, signature2); +} + diff --git a/modules/shape/src/emdL1_def.hpp b/modules/shape/src/emdL1_def.hpp new file mode 100644 index 000000000..fae473344 --- /dev/null +++ b/modules/shape/src/emdL1_def.hpp @@ -0,0 +1,142 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include <stdlib.h> +#include <math.h> +#include <vector> + +#define VHIGH 1e10; +/****************************************************************************************\ +* For EMDL1 Framework * +\****************************************************************************************/ +typedef struct cvEMDEdge* cvPEmdEdge; +typedef struct cvEMDNode* cvPEmdNode; +struct cvEMDNode +{ + int pos[3]; // grid position + float d; // initial value + int u; + // tree maintainance + int iLevel; // level in the tree, 0 means root + cvPEmdNode pParent; // pointer to its parent + cvPEmdEdge pChild; + cvPEmdEdge pPEdge; // point to the edge coming out from its parent +}; +struct cvEMDEdge +{ + float flow; // initial value + int iDir; // 1:outward, 0:inward + // tree maintainance + cvPEmdNode pParent; // point to its parent + cvPEmdNode pChild; // the child node + cvPEmdEdge pNxt; // next child/edge +}; +typedef std::vector<cvEMDNode> cvEMDNodeArray; +typedef std::vector<cvEMDEdge> cvEMDEdgeArray; +typedef std::vector<cvEMDNodeArray> cvEMDNodeArray2D; +typedef std::vector<cvEMDEdgeArray> cvEMDEdgeArray2D; +typedef std::vector<float> floatArray; +typedef std::vector<floatArray> floatArray2D; + +/****************************************************************************************\ +* EMDL1 Class * +\****************************************************************************************/ +class EmdL1 +{ +public: + EmdL1() + { + m_pRoot = NULL; + binsDim1 = 0; + binsDim2 = 0; + binsDim3 = 0; + dimension = 0; + nMaxIt = 500; + } + + ~EmdL1() + { + } + + float getEMDL1(cv::Mat &sig1, cv::Mat &sig2); + void setMaxIteration(int _nMaxIt); + +private: + //-- SubFunctions called in the EMD algorithm + bool initBaseTrees(int n1=0, int n2=0, int n3=0); + bool fillBaseTrees(float *H1, float *H2); + bool greedySolution(); + bool greedySolution2(); + bool greedySolution3(); + void initBVTree(); + void updateSubtree(cvPEmdNode pRoot); + bool isOptimal(); + void findNewSolution(); + void findLoopFromEnterBV(); + float compuTotalFlow(); + +private: + int dimension; + int binsDim1, binsDim2, binsDim3; // the hitogram contains m_n1 rows and m_n2 columns + int nNBV; // number of Non-Basic Variables (NBV) + int nMaxIt; + cvEMDNodeArray2D m_Nodes; // all nodes + cvEMDEdgeArray2D m_EdgesRight; // all edges to right + cvEMDEdgeArray2D m_EdgesUp; // all edges to upward + std::vector<cvEMDNodeArray2D> m_3dNodes; // all nodes for 3D + std::vector<cvEMDEdgeArray2D> m_3dEdgesRight; // all edges to right, 3D + std::vector<cvEMDEdgeArray2D> m_3dEdgesUp; // all edges to upward, 3D + std::vector<cvEMDEdgeArray2D> m_3dEdgesDeep; // all edges to deep, 3D + std::vector<cvPEmdEdge> m_NBVEdges; // pointers to all NON-BV edges + std::vector<cvPEmdNode> m_auxQueue; // auxiliary node queue + cvPEmdNode m_pRoot; // root of the BV Tree + cvPEmdEdge m_pEnter; // Enter BV edge + int m_iEnter; // Enter BV edge, index in m_NBVEdges + cvPEmdEdge m_pLeave; // Leave BV edge + int m_nItr; // number of iteration + // auxiliary variables for searching a new loop + std::vector<cvPEmdEdge> m_fromLoop; + std::vector<cvPEmdEdge> m_toLoop; + int m_iFrom; + int m_iTo; +}; + diff --git a/modules/shape/src/haus_dis.cpp b/modules/shape/src/haus_dis.cpp new file mode 100644 index 000000000..5e16a699c --- /dev/null +++ b/modules/shape/src/haus_dis.cpp @@ -0,0 +1,151 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// 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 +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +namespace cv +{ + +class HausdorffDistanceExtractorImpl : public HausdorffDistanceExtractor +{ +public: + /* Constructor */ + HausdorffDistanceExtractorImpl(int _distanceFlag = NORM_L1, float _rankProportion=0.6) + { + distanceFlag = _distanceFlag; + rankProportion = _rankProportion; + name_ = "ShapeDistanceExtractor.HAU"; + } + + /* Destructor */ + ~HausdorffDistanceExtractorImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual float computeDistance(InputArray contour1, InputArray contour2); + + //! Setters/Getters + virtual void setDistanceFlag(int _distanceFlag) {distanceFlag=_distanceFlag;} + virtual int getDistanceFlag() const {return distanceFlag;} + + virtual void setRankProportion(float _rankProportion) + { + CV_Assert((_rankProportion>0) & (_rankProportion<=1)); + rankProportion=_rankProportion; + } + virtual float getRankProportion() const {return rankProportion;} + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "distance" << distanceFlag + << "rank" << rankProportion; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + distanceFlag = (int)fn["distance"]; + rankProportion = (int)fn["rank"]; + } + +private: + int distanceFlag; + float rankProportion; + +protected: + String name_; +}; + +//! Hausdorff distance for a pair of set of points +static float _apply(const Mat &set1, const Mat &set2, int distType, double propRank) +{ + // Building distance matrix // + Mat disMat(set1.cols, set2.cols, CV_32F); + int K = int(propRank*(disMat.rows-1)); + + for (int r=0; r<disMat.rows; r++) + { + for (int c=0; c<disMat.cols; c++) + { + Point2f diff = set1.at<Point2f>(0,r)-set2.at<Point2f>(0,c); + disMat.at<float>(r,c) = norm(Mat(diff), distType); + } + } + + Mat shortest(disMat.rows,1,CV_32F); + for (int ii=0; ii<disMat.rows; ii++) + { + Mat therow = disMat.row(ii); + double mindis; + minMaxIdx(therow, &mindis); + shortest.at<float>(ii,0) = float(mindis); + } + Mat sorted; + cv::sort(shortest, sorted, SORT_EVERY_ROW | SORT_DESCENDING); + return sorted.at<float>(K,0); +} + +float HausdorffDistanceExtractorImpl::computeDistance(InputArray contour1, InputArray contour2) +{ + Mat set1=contour1.getMat(), set2=contour2.getMat(); + if (set1.type() != CV_32F) + set1.convertTo(set1, CV_32F); + if (set2.type() != CV_32F) + set2.convertTo(set2, CV_32F); + CV_Assert((set1.channels()==2) & (set1.cols>0)); + CV_Assert((set2.channels()==2) & (set2.cols>0)); + return std::max( _apply(set1, set2, distanceFlag, rankProportion), + _apply(set2, set1, distanceFlag, rankProportion) ); +} + +Ptr <HausdorffDistanceExtractor> createHausdorffDistanceExtractor(int distanceFlag, float rankProp) +{ + return Ptr<HausdorffDistanceExtractor>(new HausdorffDistanceExtractorImpl(distanceFlag, rankProp)); +} + +} // cv + + diff --git a/modules/shape/src/hist_cost.cpp b/modules/shape/src/hist_cost.cpp new file mode 100644 index 000000000..67e4063e6 --- /dev/null +++ b/modules/shape/src/hist_cost.cpp @@ -0,0 +1,547 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// 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 +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +namespace cv +{ + +/*! */ +class NormHistogramCostExtractorImpl : public NormHistogramCostExtractor +{ +public: + /* Constructors */ + NormHistogramCostExtractorImpl(int _flag, int _nDummies, float _defaultCost) + { + flag=_flag; + nDummies=_nDummies; + defaultCost=_defaultCost; + name_ = "HistogramCostExtractor.NOR"; + } + + /* Destructor */ + ~NormHistogramCostExtractorImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual void buildCostMatrix(InputArray descriptors1, InputArray descriptors2, OutputArray costMatrix); + + //! Setters/Getters + void setNDummies(int _nDummies) + { + nDummies=_nDummies; + } + + int getNDummies() const + { + return nDummies; + } + + void setDefaultCost(float _defaultCost) + { + defaultCost=_defaultCost; + } + + float getDefaultCost() const + { + return defaultCost; + } + + virtual void setNormFlag(int _flag) + { + flag=_flag; + } + + virtual int getNormFlag() const + { + return flag; + } + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "flag" << flag + << "dummies" << nDummies + << "default" << defaultCost; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + flag = (int)fn["flag"]; + nDummies = (int)fn["dummies"]; + defaultCost = (float)fn["default"]; + } + +private: + int flag; + int nDummies; + float defaultCost; + +protected: + String name_; +}; + +void NormHistogramCostExtractorImpl::buildCostMatrix(InputArray _descriptors1, InputArray _descriptors2, OutputArray _costMatrix) +{ + // size of the costMatrix with dummies // + Mat descriptors1=_descriptors1.getMat(); + Mat descriptors2=_descriptors2.getMat(); + int costrows = std::max(descriptors1.rows, descriptors2.rows)+nDummies; + _costMatrix.create(costrows, costrows, CV_32F); + Mat costMatrix=_costMatrix.getMat(); + + + // Obtain copies of the descriptors // + cv::Mat scd1 = descriptors1.clone(); + cv::Mat scd2 = descriptors2.clone(); + + // row normalization // + for(int i=0; i<scd1.rows; i++) + { + scd1.row(i)/=(sum(scd1.row(i))[0]+FLT_EPSILON); + } + for(int i=0; i<scd2.rows; i++) + { + scd2.row(i)/=(sum(scd2.row(i))[0]+FLT_EPSILON); + } + + // Compute the Cost Matrix // + for(int i=0; i<costrows; i++) + { + for(int j=0; j<costrows; j++) + { + if (i<scd1.rows && j<scd2.rows) + { + Mat columnDiff = scd1.row(i)-scd2.row(j); + costMatrix.at<float>(i,j)=norm(columnDiff, flag); + } + else + { + costMatrix.at<float>(i,j)=defaultCost; + } + } + } +} + +Ptr <HistogramCostExtractor> createNormHistogramCostExtractor(int flag, int nDummies, float defaultCost) +{ + return Ptr <HistogramCostExtractor>( new NormHistogramCostExtractorImpl(flag, nDummies, defaultCost) ); +} + +/*! */ +class EMDHistogramCostExtractorImpl : public EMDHistogramCostExtractor +{ +public: + /* Constructors */ + EMDHistogramCostExtractorImpl(int _flag, int _nDummies, float _defaultCost) + { + flag=_flag; + nDummies=_nDummies; + defaultCost=_defaultCost; + name_ = "HistogramCostExtractor.EMD"; + } + + /* Destructor */ + ~EMDHistogramCostExtractorImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual void buildCostMatrix(InputArray descriptors1, InputArray descriptors2, OutputArray costMatrix); + + //! Setters/Getters + void setNDummies(int _nDummies) + { + nDummies=_nDummies; + } + + int getNDummies() const + { + return nDummies; + } + + void setDefaultCost(float _defaultCost) + { + defaultCost=_defaultCost; + } + + float getDefaultCost() const + { + return defaultCost; + } + + virtual void setNormFlag(int _flag) + { + flag=_flag; + } + + virtual int getNormFlag() const + { + return flag; + } + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "flag" << flag + << "dummies" << nDummies + << "default" << defaultCost; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + flag = (int)fn["flag"]; + nDummies = (int)fn["dummies"]; + defaultCost = (float)fn["default"]; + } + +private: + int flag; + int nDummies; + float defaultCost; + +protected: + String name_; +}; + +void EMDHistogramCostExtractorImpl::buildCostMatrix(InputArray _descriptors1, InputArray _descriptors2, OutputArray _costMatrix) +{ + // size of the costMatrix with dummies // + Mat descriptors1=_descriptors1.getMat(); + Mat descriptors2=_descriptors2.getMat(); + int costrows = std::max(descriptors1.rows, descriptors2.rows)+nDummies; + _costMatrix.create(costrows, costrows, CV_32F); + Mat costMatrix=_costMatrix.getMat(); + + // Obtain copies of the descriptors // + cv::Mat scd1=descriptors1.clone(); + cv::Mat scd2=descriptors2.clone(); + + // row normalization // + for(int i=0; i<scd1.rows; i++) + { + cv::Mat row = scd1.row(i); + scd1.row(i)/=(sum(row)[0]+FLT_EPSILON); + } + for(int i=0; i<scd2.rows; i++) + { + cv::Mat row = scd2.row(i); + scd2.row(i)/=(sum(row)[0]+FLT_EPSILON); + } + + // Compute the Cost Matrix // + for(int i=0; i<costrows; i++) + { + for(int j=0; j<costrows; j++) + { + if (i<scd1.rows && j<scd2.rows) + { + cv::Mat sig1(scd1.cols,2,CV_32F), sig2(scd2.cols,2,CV_32F); + sig1.col(0)=scd1.row(i).t(); + sig2.col(0)=scd2.row(j).t(); + for (int k=0; k<sig1.rows; k++) + { + sig1.at<float>(k,1)=k; + } + for (int k=0; k<sig2.rows; k++) + { + sig2.at<float>(k,1)=k; + } + + costMatrix.at<float>(i,j) = cv::EMD(sig1, sig2, flag); + } + else + { + costMatrix.at<float>(i,j) = defaultCost; + } + } + } +} + +Ptr <HistogramCostExtractor> createEMDHistogramCostExtractor(int flag, int nDummies, float defaultCost) +{ + return Ptr <HistogramCostExtractor>( new EMDHistogramCostExtractorImpl(flag, nDummies, defaultCost) ); +} + +/*! */ +class ChiHistogramCostExtractorImpl : public ChiHistogramCostExtractor +{ +public: + /* Constructors */ + ChiHistogramCostExtractorImpl(int _nDummies, float _defaultCost) + { + name_ = "HistogramCostExtractor.CHI"; + nDummies=_nDummies; + defaultCost=_defaultCost; + } + + /* Destructor */ + ~ChiHistogramCostExtractorImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual void buildCostMatrix(InputArray descriptors1, InputArray descriptors2, OutputArray costMatrix); + + //! setters / getters + void setNDummies(int _nDummies) + { + nDummies=_nDummies; + } + + int getNDummies() const + { + return nDummies; + } + + void setDefaultCost(float _defaultCost) + { + defaultCost=_defaultCost; + } + + float getDefaultCost() const + { + return defaultCost; + } + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "dummies" << nDummies + << "default" << defaultCost; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + nDummies = (int)fn["dummies"]; + defaultCost = (float)fn["default"]; + } + +protected: + String name_; + int nDummies; + float defaultCost; +}; + +void ChiHistogramCostExtractorImpl::buildCostMatrix(InputArray _descriptors1, InputArray _descriptors2, OutputArray _costMatrix) +{ + // size of the costMatrix with dummies // + Mat descriptors1=_descriptors1.getMat(); + Mat descriptors2=_descriptors2.getMat(); + int costrows = std::max(descriptors1.rows, descriptors2.rows)+nDummies; + _costMatrix.create(costrows, costrows, CV_32FC1); + Mat costMatrix=_costMatrix.getMat(); + + // Obtain copies of the descriptors // + cv::Mat scd1=descriptors1.clone(); + cv::Mat scd2=descriptors2.clone(); + + // row normalization // + for(int i=0; i<scd1.rows; i++) + { + cv::Mat row = scd1.row(i); + scd1.row(i)/=(sum(row)[0]+FLT_EPSILON); + } + for(int i=0; i<scd2.rows; i++) + { + cv::Mat row = scd2.row(i); + scd2.row(i)/=(sum(row)[0]+FLT_EPSILON); + } + + // Compute the Cost Matrix // + for(int i=0; i<costrows; i++) + { + for(int j=0; j<costrows; j++) + { + if (i<scd1.rows && j<scd2.rows) + { + float csum = 0; + for(int k=0; k<scd2.cols; k++) + { + float resta=scd1.at<float>(i,k)-scd2.at<float>(j,k); + float suma=scd1.at<float>(i,k)+scd2.at<float>(j,k); + csum += resta*resta/(FLT_EPSILON+suma); + } + costMatrix.at<float>(i,j)=csum/2; + } + else + { + costMatrix.at<float>(i,j)=defaultCost; + } + } + } +} + +Ptr <HistogramCostExtractor> createChiHistogramCostExtractor(int nDummies, float defaultCost) +{ + return Ptr <HistogramCostExtractor>( new ChiHistogramCostExtractorImpl(nDummies, defaultCost) ); +} + +/*! */ +class EMDL1HistogramCostExtractorImpl : public EMDL1HistogramCostExtractor +{ +public: + /* Constructors */ + EMDL1HistogramCostExtractorImpl(int _nDummies, float _defaultCost) + { + name_ = "HistogramCostExtractor.CHI"; + nDummies=_nDummies; + defaultCost=_defaultCost; + } + + /* Destructor */ + ~EMDL1HistogramCostExtractorImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual void buildCostMatrix(InputArray descriptors1, InputArray descriptors2, OutputArray costMatrix); + + //! setters / getters + void setNDummies(int _nDummies) + { + nDummies=_nDummies; + } + + int getNDummies() const + { + return nDummies; + } + + void setDefaultCost(float _defaultCost) + { + defaultCost=_defaultCost; + } + + float getDefaultCost() const + { + return defaultCost; + } + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "dummies" << nDummies + << "default" << defaultCost; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + nDummies = (int)fn["dummies"]; + defaultCost = (float)fn["default"]; + } + +protected: + String name_; + int nDummies; + float defaultCost; +}; + +void EMDL1HistogramCostExtractorImpl::buildCostMatrix(InputArray _descriptors1, InputArray _descriptors2, OutputArray _costMatrix) +{ + // size of the costMatrix with dummies // + Mat descriptors1=_descriptors1.getMat(); + Mat descriptors2=_descriptors2.getMat(); + int costrows = std::max(descriptors1.rows, descriptors2.rows)+nDummies; + _costMatrix.create(costrows, costrows, CV_32F); + Mat costMatrix=_costMatrix.getMat(); + + // Obtain copies of the descriptors // + cv::Mat scd1=descriptors1.clone(); + cv::Mat scd2=descriptors2.clone(); + + // row normalization // + for(int i=0; i<scd1.rows; i++) + { + cv::Mat row = scd1.row(i); + scd1.row(i)/=(sum(row)[0]+FLT_EPSILON); + } + for(int i=0; i<scd2.rows; i++) + { + cv::Mat row = scd2.row(i); + scd2.row(i)/=(sum(row)[0]+FLT_EPSILON); + } + + // Compute the Cost Matrix // + for(int i=0; i<costrows; i++) + { + for(int j=0; j<costrows; j++) + { + if (i<scd1.rows && j<scd2.rows) + { + cv::Mat sig1(scd1.cols,1,CV_32F), sig2(scd2.cols,1,CV_32F); + sig1.col(0)=scd1.row(i).t(); + sig2.col(0)=scd2.row(j).t(); + costMatrix.at<float>(i,j) = cv::EMDL1(sig1, sig2); + } + else + { + costMatrix.at<float>(i,j) = defaultCost; + } + } + } +} + +Ptr <HistogramCostExtractor> createEMDL1HistogramCostExtractor(int nDummies, float defaultCost) +{ + return Ptr <HistogramCostExtractor>( new EMDL1HistogramCostExtractorImpl(nDummies, defaultCost) ); +} + +} // cv + + diff --git a/modules/shape/src/precomp.cpp b/modules/shape/src/precomp.cpp new file mode 100644 index 000000000..730edbb63 --- /dev/null +++ b/modules/shape/src/precomp.cpp @@ -0,0 +1,45 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +/* End of file. */ diff --git a/modules/shape/src/precomp.hpp b/modules/shape/src/precomp.hpp new file mode 100644 index 000000000..bc00e5993 --- /dev/null +++ b/modules/shape/src/precomp.hpp @@ -0,0 +1,59 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_PRECOMP_H__ +#define __OPENCV_PRECOMP_H__ + +#include <vector> +#include <cmath> +#include <iostream> + +#include "opencv2/video/tracking.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/shape.hpp" + +#include "opencv2/core/utility.hpp" +#include "opencv2/core/private.hpp" + +#include "opencv2/opencv_modules.hpp" + +#endif diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp new file mode 100644 index 000000000..98edc56b1 --- /dev/null +++ b/modules/shape/src/sc_dis.cpp @@ -0,0 +1,848 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +/* + * Implementation of the paper Shape Matching and Object Recognition Using Shape Contexts + * Belongie et al., 2002 by Juan Manuel Perez for GSoC 2013. + */ +#include "precomp.hpp" +//#include "opencv2/highgui.hpp" +/* + * ShapeContextDescriptor class + */ +class SCD +{ +public: + //! the full constructor taking all the necessary parameters + explicit SCD(int _nAngularBins=12, int _nRadialBins=5, + double _innerRadius=0.1, double _outerRadius=1, bool _rotationInvariant=false) + { + setAngularBins(_nAngularBins); + setRadialBins(_nRadialBins); + setInnerRadius(_innerRadius); + setOuterRadius(_outerRadius); + setRotationInvariant(_rotationInvariant); + } + + void extractSCD(cv::Mat& contour, cv::Mat& descriptors, + const std::vector<int>& queryInliers=std::vector<int>(), + const float _meanDistance=-1) + { + cv::Mat contourMat = contour; + cv::Mat disMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F); + cv::Mat angleMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F); + + std::vector<double> logspaces, angspaces; + logarithmicSpaces(logspaces); + angularSpaces(angspaces); + buildNormalizedDistanceMatrix(contourMat, disMatrix, queryInliers, _meanDistance); + buildAngleMatrix(contourMat, angleMatrix); + + // Now, build the descriptor matrix (each row is a point) // + descriptors = cv::Mat::zeros(contourMat.cols, descriptorSize(), CV_32F); + + for (int ptidx=0; ptidx<contourMat.cols; ptidx++) + { + for (int cmp=0; cmp<contourMat.cols; cmp++) + { + if (ptidx==cmp) continue; + if ((int)queryInliers.size()>0) + { + if (queryInliers[ptidx]==0 || queryInliers[cmp]==0) continue; //avoid outliers + } + + int angidx=-1, radidx=-1; + for (int i=0; i<nRadialBins; i++) + { + if (disMatrix.at<float>(ptidx, cmp)<logspaces[i]) + { + radidx=i; + break; + } + } + for (int i=0; i<nAngularBins; i++) + { + if (angleMatrix.at<float>(ptidx, cmp)<angspaces[i]) + { + angidx=i; + break; + } + } + if (angidx!=-1 && radidx!=-1) + { + int idx = angidx+radidx*nAngularBins; + descriptors.at<float>(ptidx, idx)++; + } + } + } + } + + int descriptorSize() {return nAngularBins*nRadialBins;} + void setAngularBins(int angularBins) { nAngularBins=angularBins; } + void setRadialBins(int radialBins) { nRadialBins=radialBins; } + void setInnerRadius(double _innerRadius) { innerRadius=_innerRadius; } + void setOuterRadius(double _outerRadius) { outerRadius=_outerRadius; } + void setRotationInvariant(bool _rotationInvariant) { rotationInvariant=_rotationInvariant; } + int getAngularBins() const { return nAngularBins; } + int getRadialBins() const { return nRadialBins; } + double getInnerRadius() const { return innerRadius; } + double getOuterRadius() const { return outerRadius; } + bool getRotationInvariant() const { return rotationInvariant; } + float getMeanDistance() const { return meanDistance; } + +private: + int nAngularBins; + int nRadialBins; + double innerRadius; + double outerRadius; + bool rotationInvariant; + float meanDistance; + +protected: + void logarithmicSpaces(std::vector<double>& vecSpaces) const + { + double logmin=log10(innerRadius); + double logmax=log10(outerRadius); + double delta=(logmax-logmin)/(nRadialBins-1); + double accdelta=0; + + for (int i=0; i<nRadialBins; i++) + { + double val = std::pow(10,logmin+accdelta); + vecSpaces.push_back(val); + accdelta += delta; + } + } + + void angularSpaces(std::vector<double>& vecSpaces) const + { + double delta=2*CV_PI/nAngularBins; + double val=0; + + for (int i=0; i<nAngularBins; i++) + { + val += delta; + vecSpaces.push_back(val); + } + } + + void buildNormalizedDistanceMatrix(cv::Mat& contour, + cv::Mat& disMatrix, const std::vector<int> &queryInliers, + const float _meanDistance=-1) + { + cv::Mat contourMat = contour; + cv::Mat mask(disMatrix.rows, disMatrix.cols, CV_8U); + + for (int i=0; i<contourMat.cols; i++) + { + for (int j=0; j<contourMat.cols; j++) + { + disMatrix.at<float>(i,j) = norm( cv::Mat(contourMat.at<cv::Point2f>(0,i)-contourMat.at<cv::Point2f>(0,j)), cv::NORM_L2 ); + if (_meanDistance<0) + { + if (queryInliers.size()>0) + { + mask.at<char>(i,j)=char(queryInliers[j] & queryInliers[i]); + } + else + { + mask.at<char>(i,j)=1; + } + } + } + } + + if (_meanDistance<0) + { + meanDistance=mean(disMatrix, mask)[0]; + } + else + { + meanDistance=_meanDistance; + } + disMatrix/=meanDistance+FLT_EPSILON; + } + + void buildAngleMatrix(cv::Mat& contour, + cv::Mat& angleMatrix) const + { + cv::Mat contourMat = contour; + + // if descriptor is rotationInvariant compute massCenter // + cv::Point2f massCenter(0,0); + if (rotationInvariant) + { + for (int i=0; i<contourMat.cols; i++) + { + massCenter+=contourMat.at<cv::Point2f>(0,i); + } + massCenter.x=massCenter.x/(float)contourMat.cols; + massCenter.y=massCenter.y/(float)contourMat.cols; + } + + + for (int i=0; i<contourMat.cols; i++) + { + for (int j=0; j<contourMat.cols; j++) + { + if (i==j) + { + angleMatrix.at<float>(i,j)=0.0; + } + else + { + cv::Point2f dif = contourMat.at<cv::Point2f>(0,i) - contourMat.at<cv::Point2f>(0,j); + angleMatrix.at<float>(i,j) = std::atan2(dif.y, dif.x); + + if (rotationInvariant) + { + cv::Point2f refPt = contourMat.at<cv::Point2f>(0,i) - massCenter; + float refAngle = atan2(refPt.y, refPt.x); + angleMatrix.at<float>(i,j) -= refAngle; + } + angleMatrix.at<float>(i,j) = fmod(angleMatrix.at<float>(i,j)+FLT_EPSILON,2*CV_PI)+CV_PI; + //angleMatrix.at<float>(i,j) = 1+floor( angleMatrix.at<float>(i,j)*nAngularBins/(2*CV_PI) ); + } + } + } + } +}; + +/* + * Matcher + */ +class SCDMatcher +{ +public: + // the full constructor + SCDMatcher() + { + } + + // the matcher function using Hungarian method + void matchDescriptors(cv::Mat& descriptors1, cv::Mat& descriptors2, std::vector<cv::DMatch>& matches, cv::Ptr<cv::HistogramCostExtractor>& comparer, + std::vector<int>& inliers1, std::vector<int> &inliers2) + { + matches.clear(); + + // Build the cost Matrix between descriptors // + cv::Mat costMat; + buildCostMatrix(descriptors1, descriptors2, costMat, comparer); + + // Solve the matching problem using the hungarian method // + hungarian(costMat, matches, inliers1, inliers2, descriptors1.rows, descriptors2.rows); + } + + // matching cost + float getMatchingCost() const {return minMatchCost;} + +private: + float minMatchCost; + float betaAdditional; +protected: + void buildCostMatrix(const cv::Mat& descriptors1, const cv::Mat& descriptors2, + cv::Mat& costMatrix, cv::Ptr<cv::HistogramCostExtractor>& comparer) const + { + comparer->buildCostMatrix(descriptors1, descriptors2, costMatrix); + } + + void hungarian(cv::Mat& costMatrix, std::vector<cv::DMatch>& outMatches, std::vector<int> &inliers1, + std::vector<int> &inliers2, int sizeScd1=0, int sizeScd2=0) + { + std::vector<int> free(costMatrix.rows, 0), collist(costMatrix.rows, 0); + std::vector<int> matches(costMatrix.rows, 0), colsol(costMatrix.rows), rowsol(costMatrix.rows); + std::vector<float> d(costMatrix.rows), pred(costMatrix.rows), v(costMatrix.rows); + + const float LOWV=1e-10; + bool unassignedfound; + int i=0, imin=0, numfree=0, prvnumfree=0, f=0, i0=0, k=0, freerow=0; + int j=0, j1=0, j2=0, endofpath=0, last=0, low=0, up=0; + float min=0, h=0, umin=0, usubmin=0, v2=0; + + // COLUMN REDUCTION // + for (j = costMatrix.rows-1; j >= 0; j--) + { + // find minimum cost over rows. + min = costMatrix.at<float>(0,j); + imin = 0; + for (i = 1; i < costMatrix.rows; i++) + if (costMatrix.at<float>(i,j) < min) + { + min = costMatrix.at<float>(i,j); + imin = i; + } + v[j] = min; + + if (++matches[imin] == 1) + { + rowsol[imin] = j; + colsol[j] = imin; + } + else + { + colsol[j]=-1; + } + } + + // REDUCTION TRANSFER // + for (i=0; i<costMatrix.rows; i++) + { + if (matches[i] == 0) + { + free[numfree++] = i; + } + else + { + if (matches[i] == 1) + { + j1=rowsol[i]; + min=std::numeric_limits<float>::max(); + for (j=0; j<costMatrix.rows; j++) + { + if (j!=j1) + { + if (costMatrix.at<float>(i,j)-v[j] < min) + { + min=costMatrix.at<float>(i,j)-v[j]; + } + } + } + v[j1] = v[j1]-min; + } + } + } + // AUGMENTING ROW REDUCTION // + int loopcnt = 0; + do + { + loopcnt++; + k=0; + prvnumfree=numfree; + numfree=0; + while (k < prvnumfree) + { + i=free[k]; + k++; + umin = costMatrix.at<float>(i,0)-v[0]; + j1=0; + usubmin = std::numeric_limits<float>::max(); + for (j=1; j<costMatrix.rows; j++) + { + h = costMatrix.at<float>(i,j)-v[j]; + if (h < usubmin) + { + if (h >= umin) + { + usubmin = h; + j2 = j; + } + else + { + usubmin = umin; + umin = h; + j2 = j1; + j1 = j; + } + } + } + i0 = colsol[j1]; + + if (fabs(umin-usubmin) > LOWV) //if( umin < usubmin ) + { + v[j1] = v[j1] - (usubmin - umin); + } + else // minimum and subminimum equal. + { + if (i0 >= 0) // minimum column j1 is assigned. + { + j1 = j2; + i0 = colsol[j2]; + } + } + // (re-)assign i to j1, possibly de-assigning an i0. + rowsol[i]=j1; + colsol[j1]=i; + + if (i0 >= 0) + { + //if( umin < usubmin ) + if (fabs(umin-usubmin) > LOWV) + { + free[--k] = i0; + } + else + { + free[numfree++] = i0; + } + } + } + }while (loopcnt<2); // repeat once. + + // AUGMENT SOLUTION for each free row // + for (f = 0; f<numfree; f++) + { + freerow = free[f]; // start row of augmenting path. + // Dijkstra shortest path algorithm. + // runs until unassigned column added to shortest path tree. + for (j = 0; j < costMatrix.rows; j++) + { + d[j] = costMatrix.at<float>(freerow,j) - v[j]; + pred[j] = freerow; + collist[j] = j; // init column list. + } + + low=0; // columns in 0..low-1 are ready, now none. + up=0; // columns in low..up-1 are to be scanned for current minimum, now none. + unassignedfound = false; + do + { + if (up == low) + { + last=low-1; + min = d[collist[up++]]; + for (k = up; k < costMatrix.rows; k++) + { + j = collist[k]; + h = d[j]; + if (h <= min) + { + if (h < min) // new minimum. + { + up = low; // restart list at index low. + min = h; + } + collist[k] = collist[up]; + collist[up++] = j; + } + } + for (k=low; k<up; k++) + { + if (colsol[collist[k]] < 0) + { + endofpath = collist[k]; + unassignedfound = true; + break; + } + } + } + + if (!unassignedfound) + { + // update 'distances' between freerow and all unscanned columns, via next scanned column. + j1 = collist[low]; + low++; + i = colsol[j1]; + h = costMatrix.at<float>(i,j1)-v[j1]-min; + + for (k = up; k < costMatrix.rows; k++) + { + j = collist[k]; + v2 = costMatrix.at<float>(i,j) - v[j] - h; + if (v2 < d[j]) + { + pred[j] = i; + if (v2 == min) + { + if (colsol[j] < 0) + { + // if unassigned, shortest augmenting path is complete. + endofpath = j; + unassignedfound = true; + break; + } + else + { + collist[k] = collist[up]; + collist[up++] = j; + } + } + d[j] = v2; + } + } + } + }while (!unassignedfound); + + // update column prices. + for (k = 0; k <= last; k++) + { + j1 = collist[k]; + v[j1] = v[j1] + d[j1] - min; + } + + // reset row and column assignments along the alternating path. + do + { + i = pred[endofpath]; + colsol[endofpath] = i; + j1 = endofpath; + endofpath = rowsol[i]; + rowsol[i] = j1; + }while (i != freerow); + } + + // calculate symmetric shape context cost + cv::Mat trueCostMatrix(costMatrix, cv::Rect(0,0,sizeScd1, sizeScd2)); + float leftcost = 0; + for (int nrow=0; nrow<trueCostMatrix.rows; nrow++) + { + double minval; + minMaxIdx(trueCostMatrix.row(nrow), &minval); + leftcost+=minval; + } + leftcost /= trueCostMatrix.rows; + + float rightcost = 0; + for (int ncol=0; ncol<trueCostMatrix.cols; ncol++) + { + double minval; + minMaxIdx(trueCostMatrix.col(ncol), &minval); + rightcost+=minval; + } + rightcost /= trueCostMatrix.cols; + + minMatchCost = std::max(leftcost,rightcost); + + // Save in a DMatch vector + for (i=0;i<costMatrix.cols;i++) + { + cv::DMatch singleMatch(colsol[i],i,costMatrix.at<float>(colsol[i],i));//queryIdx,trainIdx,distance + outMatches.push_back(singleMatch); + } + + // Update inliers + inliers1.reserve(sizeScd1); + for (size_t kc = 0; kc<inliers1.size(); kc++) + { + if (rowsol[kc]<sizeScd1) // if a real match + inliers1[kc]=1; + else + inliers1[kc]=0; + } + inliers2.reserve(sizeScd2); + for (size_t kc = 0; kc<inliers2.size(); kc++) + { + if (colsol[kc]<sizeScd2) // if a real match + inliers2[kc]=1; + else + inliers2[kc]=0; + } + } + +}; + +/* + * + */ + +namespace cv +{ +class ShapeContextDistanceExtractorImpl : public ShapeContextDistanceExtractor +{ +public: + /* Constructors */ + ShapeContextDistanceExtractorImpl(int _nAngularBins, int _nRadialBins, float _innerRadius, float _outerRadius, int _iterations, + const Ptr<HistogramCostExtractor> &_comparer, const Ptr<ShapeTransformer> &_transformer) + { + nAngularBins=_nAngularBins; + nRadialBins=_nRadialBins; + innerRadius=_innerRadius; + outerRadius=_outerRadius; + rotationInvariant=false; + comparer=_comparer; + iterations=_iterations; + transformer=_transformer; + bendingEnergyWeight=0.3; + imageAppearanceWeight=0.0; + shapeContextWeight=1.0; + sigma=10; + name_ = "ShapeDistanceExtractor.SCD"; + } + + /* Destructor */ + ~ShapeContextDistanceExtractorImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operator + virtual float computeDistance(InputArray contour1, InputArray contour2); + + //! Setters/Getters + virtual void setAngularBins(int _nAngularBins){CV_Assert(_nAngularBins>0); nAngularBins=_nAngularBins;} + virtual int getAngularBins() const {return nAngularBins;} + + virtual void setRadialBins(int _nRadialBins){CV_Assert(_nRadialBins>0); nRadialBins=_nRadialBins;} + virtual int getRadialBins() const {return nRadialBins;} + + virtual void setInnerRadius(float _innerRadius) {CV_Assert(_innerRadius>0); innerRadius=_innerRadius;} + virtual float getInnerRadius() const {return innerRadius;} + + virtual void setOuterRadius(float _outerRadius) {CV_Assert(_outerRadius>0); outerRadius=_outerRadius;} + virtual float getOuterRadius() const {return outerRadius;} + + virtual void setRotationInvariant(bool _rotationInvariant) {rotationInvariant=_rotationInvariant;} + virtual bool getRotationInvariant() const {return rotationInvariant;} + + virtual void setCostExtractor(Ptr<HistogramCostExtractor> _comparer) { comparer = _comparer; } + virtual Ptr<HistogramCostExtractor> getCostExtractor() const { return comparer; } + + virtual void setShapeContextWeight(float _shapeContextWeight) {shapeContextWeight=_shapeContextWeight;} + virtual float getShapeContextWeight() const {return shapeContextWeight;} + + virtual void setImageAppearanceWeight(float _imageAppearanceWeight) {imageAppearanceWeight=_imageAppearanceWeight;} + virtual float getImageAppearanceWeight() const {return imageAppearanceWeight;} + + virtual void setBendingEnergyWeight(float _bendingEnergyWeight) {bendingEnergyWeight=_bendingEnergyWeight;} + virtual float getBendingEnergyWeight() const {return bendingEnergyWeight;} + + virtual void setStdDev(float _sigma) {sigma=_sigma;} + virtual float getStdDev() const {return sigma;} + + virtual void setImages(InputArray _image1, InputArray _image2) + { + Mat image1_=_image1.getMat(), image2_=_image2.getMat(); + CV_Assert((image1_.depth()==0) & (image2_.depth()==0)); + image1=image1_; + image2=image2_; + } + + virtual void getImages(OutputArray _image1, OutputArray _image2) const + { + CV_Assert((!image1.empty()) & (!image2.empty())); + _image1.create(image1.size(), image1.type()); + _image2.create(image2.size(), image2.type()); + _image1.getMat()=image1; + _image2.getMat()=image2; + } + + virtual void setIterations(int _iterations) {CV_Assert(_iterations>0); iterations=_iterations;} + virtual int getIterations() const {return iterations;} + + virtual void setTransformAlgorithm(Ptr<ShapeTransformer> _transformer) {transformer=_transformer;} + virtual Ptr<ShapeTransformer> getTransformAlgorithm() const {return transformer;} + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "nRads" << nRadialBins + << "nAngs" << nAngularBins + << "iters" << iterations + << "img_1" << image1 + << "img_2" << image2 + << "beWei" << bendingEnergyWeight + << "scWei" << shapeContextWeight + << "iaWei" << imageAppearanceWeight + << "costF" << costFlag + << "rotIn" << rotationInvariant + << "sigma" << sigma; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + nRadialBins = (int)fn["nRads"]; + nAngularBins = (int)fn["nAngs"]; + iterations = (int)fn["iters"]; + bendingEnergyWeight = (float)fn["beWei"]; + shapeContextWeight = (float)fn["scWei"]; + imageAppearanceWeight = (float)fn["iaWei"]; + costFlag = (int)fn["costF"]; + sigma = (float)fn["sigma"]; + } + +private: + int nAngularBins; + int nRadialBins; + float innerRadius; + float outerRadius; + bool rotationInvariant; + int costFlag; + int iterations; + Ptr<ShapeTransformer> transformer; + Ptr<HistogramCostExtractor> comparer; + Mat image1; + Mat image2; + float bendingEnergyWeight; + float imageAppearanceWeight; + float shapeContextWeight; + float sigma; + +protected: + String name_; +}; + +float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, InputArray contour2) +{ + // Checking // + Mat sset1=contour1.getMat(), sset2=contour2.getMat(), set1, set2; + if (set1.type() != CV_32F) + sset1.convertTo(set1, CV_32F); + else + sset1.copyTo(set1); + + if (set2.type() != CV_32F) + sset2.convertTo(set2, CV_32F); + else + sset1.copyTo(set2); + + CV_Assert((set1.channels()==2) & (set1.cols>0)); + CV_Assert((set2.channels()==2) & (set2.cols>0)); + if (imageAppearanceWeight!=0) + { + CV_Assert((!image1.empty()) & (!image2.empty())); + } + + // Initializing Extractor, Descriptor structures and Matcher // + SCD set1SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, false); + Mat set1SCD; + SCD set2SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, false); + Mat set2SCD; + SCDMatcher matcher; + std::vector<DMatch> matches; + + // Distance components (The output is a linear combination of these 3) // + float sDistance=0, bEnergy=0, iAppearance=0; + float beta; + + // Initializing some variables // + std::vector<int> inliers1, inliers2; + bool isTPS=false; + if ( dynamic_cast<ThinPlateSplineShapeTransformer*>(&*transformer) ) + isTPS=true; + Mat warpedImage; + for (int ii=0; ii<iterations; ii++) + { + // Extract SCD descriptor in the set1 // + set1SCE.extractSCD(set1, set1SCD, inliers1); + + // Extract SCD descriptor of the set2 (TARGET) // + set2SCE.extractSCD(set2, set2SCD, inliers2, set1SCE.getMeanDistance()); + + // regularization parameter with annealing rate annRate // + beta=std::pow(set1SCE.getMeanDistance(),2); + + // match // + matcher.matchDescriptors(set1SCD, set2SCD, matches, comparer, inliers1, inliers2); + + // apply TPS transform // + if ( isTPS ) + dynamic_cast<ThinPlateSplineShapeTransformer*>(&*transformer)->setRegularizationParameter(beta); + transformer->estimateTransformation(set1, set2, matches); + bEnergy += transformer->applyTransformation(set1, set1); + + // Image appearance // + if (imageAppearanceWeight!=0) + { + // Have to accumulate the transformation along all the iterations + if (ii==0) + { + if ( isTPS ) + { + image2.copyTo(warpedImage); + } + else + { + image1.copyTo(warpedImage); + } + } + transformer->warpImage(warpedImage, warpedImage); + } + } + + Mat gaussWindow, diffIm; + if (imageAppearanceWeight!=0) + { + // compute appearance cost + if ( isTPS ) + { + resize(warpedImage, warpedImage, image1.size()); + Mat temp=(warpedImage-image1); + multiply(temp, temp, diffIm); + } + else + { + resize(warpedImage, warpedImage, image2.size()); + Mat temp=(warpedImage-image2); + multiply(temp, temp, diffIm); + } + gaussWindow = Mat::zeros(warpedImage.rows, warpedImage.cols, CV_32F); + for (int pt=0; pt<sset1.cols; pt++) + { + for (int ii=0; ii<diffIm.rows; ii++) + { + for (int jj=0; jj<diffIm.cols; jj++) + { + float xx = sset1.at<Point2f>(0,pt).x; + float yy = sset1.at<Point2f>(0,pt).y; + float val = std::exp( -( (xx-jj)*(xx-jj) + (yy-ii)*(yy-ii) )/(2*sigma*sigma) ) / (sigma*sigma*2*CV_PI); + gaussWindow.at<float>(ii,jj) += val; + } + } + } + + Mat appIm(diffIm.rows, diffIm.cols, CV_32F); + for (int ii=0; ii<diffIm.rows; ii++) + { + for (int jj=0; jj<diffIm.cols; jj++) + { + float elema=float( diffIm.at<uchar>(ii,jj) )/255; + float elemb=gaussWindow.at<float>(ii,jj); + appIm.at<float>(ii,jj) = elema*elemb; + } + } + iAppearance = cv::sum(appIm)[0]/sset1.cols; + } + sDistance = matcher.getMatchingCost(); + + return (sDistance*shapeContextWeight+bEnergy*bendingEnergyWeight+iAppearance*imageAppearanceWeight); +} + +Ptr <ShapeContextDistanceExtractor> createShapeContextDistanceExtractor(int nAngularBins, int nRadialBins, float innerRadius, float outerRadius, int iterations, + const Ptr<HistogramCostExtractor> &comparer, const Ptr<ShapeTransformer> &transformer) +{ + return Ptr <ShapeContextDistanceExtractor> ( new ShapeContextDistanceExtractorImpl(nAngularBins, nRadialBins, innerRadius, + outerRadius, iterations, comparer, transformer) ); +} + +} // cv diff --git a/modules/shape/src/tps_trans.cpp b/modules/shape/src/tps_trans.cpp new file mode 100644 index 000000000..dd839d670 --- /dev/null +++ b/modules/shape/src/tps_trans.cpp @@ -0,0 +1,288 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +namespace cv +{ + +class ThinPlateSplineShapeTransformerImpl : public ThinPlateSplineShapeTransformer +{ +public: + /* Constructors */ + ThinPlateSplineShapeTransformerImpl() + { + regularizationParameter=0; + name_ = "ShapeTransformer.TPS"; + tpsComputed=false; + } + + ThinPlateSplineShapeTransformerImpl(double _regularizationParameter) + { + regularizationParameter=_regularizationParameter; + name_ = "ShapeTransformer.TPS"; + tpsComputed=false; + } + + /* Destructor */ + ~ThinPlateSplineShapeTransformerImpl() + { + } + + virtual AlgorithmInfo* info() const { return 0; } + + //! the main operators + virtual void estimateTransformation(InputArray transformingShape, InputArray targetShape, std::vector<DMatch> &matches); + virtual float applyTransformation(InputArray inPts, OutputArray output=noArray()); + virtual void warpImage(InputArray transformingImage, OutputArray output, + int flags, int borderMode, const Scalar& borderValue) const; + + //! Setters/Getters + virtual void setRegularizationParameter(double _regularizationParameter) {regularizationParameter=_regularizationParameter;} + virtual double getRegularizationParameter() const {return regularizationParameter;} + + //! write/read + virtual void write(FileStorage& fs) const + { + fs << "name" << name_ + << "regularization" << regularizationParameter; + } + + virtual void read(const FileNode& fn) + { + CV_Assert( (String)fn["name"] == name_ ); + regularizationParameter = (int)fn["regularization"]; + } + +private: + bool tpsComputed; + double regularizationParameter; + float transformCost; + Mat tpsParameters; + Mat shapeReference; + +protected: + String name_; +}; + +static double distance(Point2f p, Point2f q) +{ + Point2f diff = p - q; + float norma = diff.x*diff.x + diff.y*diff.y;// - 2*diff.x*diff.y; + if (norma<0) norma=0; + //else norma = std::sqrt(norma); + norma = norma*std::log(norma+FLT_EPSILON); + return norma; +} + +static Point2f _applyTransformation(const Mat &shapeRef, const Point2f point, const Mat &tpsParameters) +{ + Point2f out; + for (int i=0; i<2; i++) + { + float a1=tpsParameters.at<float>(tpsParameters.rows-3,i); + float ax=tpsParameters.at<float>(tpsParameters.rows-2,i); + float ay=tpsParameters.at<float>(tpsParameters.rows-1,i); + + float affine=a1+ax*point.x+ay*point.y; + float nonrigid=0; + for (int j=0; j<shapeRef.rows; j++) + { + nonrigid+=tpsParameters.at<float>(j,i)* + distance(Point2f(shapeRef.at<float>(j,0),shapeRef.at<float>(j,1)), + point); + } + if (i==0) + { + out.x=affine+nonrigid; + } + if (i==1) + { + out.y=affine+nonrigid; + } + } + return out; +} + +/* public methods */ +void ThinPlateSplineShapeTransformerImpl::warpImage(InputArray transformingImage, OutputArray output, + int flags, int borderMode, const Scalar& borderValue) const +{ + CV_Assert(tpsComputed==true); + + Mat theinput = transformingImage.getMat(); + Mat mapX(theinput.rows, theinput.cols, CV_32FC1); + Mat mapY(theinput.rows, theinput.cols, CV_32FC1); + + for (int row = 0; row < theinput.rows; row++) + { + for (int col = 0; col < theinput.cols; col++) + { + Point2f pt = _applyTransformation(shapeReference, Point2f(float(col), float(row)), tpsParameters); + mapX.at<float>(row, col) = pt.x; + mapY.at<float>(row, col) = pt.y; + } + } + remap(transformingImage, output, mapX, mapY, flags, borderMode, borderValue); +} + +float ThinPlateSplineShapeTransformerImpl::applyTransformation(InputArray inPts, OutputArray outPts) +{ + CV_Assert(tpsComputed); + Mat pts1 = inPts.getMat(); + CV_Assert((pts1.channels()==2) & (pts1.cols>0)); + + //Apply transformation in the complete set of points + // Ensambling output // + if (outPts.needed()) + { + outPts.create(1,pts1.cols, CV_32FC2); + Mat outMat = outPts.getMat(); + for (int i=0; i<pts1.cols; i++) + { + Point2f pt=pts1.at<Point2f>(0,i); + outMat.at<Point2f>(0,i)=_applyTransformation(shapeReference, pt, tpsParameters); + } + } + + return transformCost; +} + +void ThinPlateSplineShapeTransformerImpl::estimateTransformation(InputArray _pts1, InputArray _pts2, + std::vector<DMatch>& _matches ) +{ + Mat pts1 = _pts1.getMat(); + Mat pts2 = _pts2.getMat(); + CV_Assert((pts1.channels()==2) & (pts1.cols>0) & (pts2.channels()==2) & (pts2.cols>0)); + CV_Assert(_matches.size()>1); + + if (pts1.type() != CV_32F) + pts1.convertTo(pts1, CV_32F); + if (pts2.type() != CV_32F) + pts2.convertTo(pts2, CV_32F); + + // Use only valid matchings // + std::vector<DMatch> matches; + for (size_t i=0; i<_matches.size(); i++) + { + if (_matches[i].queryIdx<pts1.cols && + _matches[i].trainIdx<pts2.cols) + { + matches.push_back(_matches[i]); + } + } + + // Organizing the correspondent points in matrix style // + Mat shape1(matches.size(),2,CV_32F); // transforming shape + Mat shape2(matches.size(),2,CV_32F); // target shape + for (size_t i=0; i<matches.size(); i++) + { + Point2f pt1=pts1.at<Point2f>(0,matches[i].queryIdx); + shape1.at<float>(i,0) = pt1.x; + shape1.at<float>(i,1) = pt1.y; + + Point2f pt2=pts2.at<Point2f>(0,matches[i].trainIdx); + shape2.at<float>(i,0) = pt2.x; + shape2.at<float>(i,1) = pt2.y; + } + shape1.copyTo(shapeReference); + + // Building the matrices for solving the L*(w|a)=(v|0) problem with L={[K|P];[P'|0]} + + //Building K and P (Neede to buil L) + Mat matK(matches.size(),matches.size(),CV_32F); + Mat matP(matches.size(),3,CV_32F); + for (size_t i=0; i<matches.size(); i++) + { + for (size_t j=0; j<matches.size(); j++) + { + if (i==j) + { + matK.at<float>(i,j)=regularizationParameter; + } + else + { + matK.at<float>(i,j) = distance(Point2f(shape1.at<float>(i,0),shape1.at<float>(i,1)), + Point2f(shape1.at<float>(j,0),shape1.at<float>(j,1))); + } + } + matP.at<float>(i,0) = 1; + matP.at<float>(i,1) = shape1.at<float>(i,0); + matP.at<float>(i,2) = shape1.at<float>(i,1); + } + + //Building L + Mat matL=Mat::zeros(matches.size()+3,matches.size()+3,CV_32F); + Mat matLroi(matL, Rect(0,0,matches.size(),matches.size())); //roi for K + matK.copyTo(matLroi); + matLroi = Mat(matL,Rect(matches.size(),0,3,matches.size())); //roi for P + matP.copyTo(matLroi); + Mat matPt; + transpose(matP,matPt); + matLroi = Mat(matL,Rect(0,matches.size(),matches.size(),3)); //roi for P' + matPt.copyTo(matLroi); + + //Building B (v|0) + Mat matB = Mat::zeros(matches.size()+3,2,CV_32F); + for (size_t i=0; i<matches.size(); i++) + { + matB.at<float>(i,0) = shape2.at<float>(i,0); //x's + matB.at<float>(i,1) = shape2.at<float>(i,1); //y's + } + + //Obtaining transformation params (w|a) + solve(matL, matB, tpsParameters, DECOMP_LU); + //tpsParameters = matL.inv()*matB; + + //Setting transform Cost and Shape reference + Mat w(tpsParameters, Rect(0,0,2,tpsParameters.rows-3)); + Mat Q=w.t()*matK*w; + transformCost=fabs(Q.at<float>(0,0)*Q.at<float>(1,1));//fabs(mean(Q.diag(0))[0]);//std::max(Q.at<float>(0,0),Q.at<float>(1,1)); + tpsComputed=true; +} + +Ptr <ThinPlateSplineShapeTransformer> createThinPlateSplineShapeTransformer(double regularizationParameter) +{ + return Ptr<ThinPlateSplineShapeTransformer>( new ThinPlateSplineShapeTransformerImpl(regularizationParameter) ); +} + +} // cv diff --git a/modules/shape/test/test_emdl1.cpp b/modules/shape/test/test_emdl1.cpp new file mode 100644 index 000000000..1f7aba545 --- /dev/null +++ b/modules/shape/test/test_emdl1.cpp @@ -0,0 +1,266 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// 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 +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +const int angularBins=12; +const int radialBins=4; +const float minRad=0.2; +const float maxRad=2; +const int NSN=5;//10;//20; //number of shapes per class +const int NP=100; //number of points sympliying the contour +const float outlierWeight=0.1; +const int numOutliers=20; +const float CURRENT_MAX_ACCUR=95; //98% and 99% reached in several tests, 95 is fixed as minimum boundary + +class CV_ShapeEMDTest : public cvtest::BaseTest +{ +public: + CV_ShapeEMDTest(); + ~CV_ShapeEMDTest(); +protected: + void run(int); + +private: + void mpegTest(); + void listShapeNames(vector<string> &listHeaders); + vector<Point2f> convertContourType(const Mat &, int n=0 ); + float computeShapeDistance(vector <Point2f>& queryNormal, + vector <Point2f>& queryFlipped1, + vector <Point2f>& queryFlipped2, + vector<Point2f>& testq); + void displayMPEGResults(); +}; + +CV_ShapeEMDTest::CV_ShapeEMDTest() +{ +} +CV_ShapeEMDTest::~CV_ShapeEMDTest() +{ +} + +vector <Point2f> CV_ShapeEMDTest::convertContourType(const Mat& currentQuery, int n) +{ + vector<vector<Point> > _contoursQuery; + vector <Point2f> contoursQuery; + findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE); + for (size_t border=0; border<_contoursQuery.size(); border++) + { + for (size_t p=0; p<_contoursQuery[border].size(); p++) + { + contoursQuery.push_back(Point2f((float)_contoursQuery[border][p].x, + (float)_contoursQuery[border][p].y)); + } + } + + // In case actual number of points is less than n + int dum=0; + for (int add=contoursQuery.size()-1; add<n; add++) + { + contoursQuery.push_back(contoursQuery[dum++]); //adding dummy values + } + + // Uniformly sampling + random_shuffle(contoursQuery.begin(), contoursQuery.end()); + int nStart=n; + vector<Point2f> cont; + for (int i=0; i<nStart; i++) + { + cont.push_back(contoursQuery[i]); + } + return cont; +} + +void CV_ShapeEMDTest::listShapeNames( vector<string> &listHeaders) +{ + listHeaders.push_back("apple"); //ok + listHeaders.push_back("children"); // ok + listHeaders.push_back("device7"); // ok + listHeaders.push_back("Heart"); // ok + listHeaders.push_back("teddy"); // ok +} +float CV_ShapeEMDTest::computeShapeDistance(vector <Point2f>& query1, vector <Point2f>& query2, + vector <Point2f>& query3, vector <Point2f>& testq) +{ + //waitKey(0); + Ptr <ShapeContextDistanceExtractor> mysc = createShapeContextDistanceExtractor(angularBins, radialBins, minRad, maxRad); + //Ptr <HistogramCostExtractor> cost = createNormHistogramCostExtractor(cv::DIST_L1); + //Ptr <HistogramCostExtractor> cost = createChiHistogramCostExtractor(30,0.15); + //Ptr <HistogramCostExtractor> cost = createEMDHistogramCostExtractor(); + // Ptr <HistogramCostExtractor> cost = createEMDL1HistogramCostExtractor(); + mysc->setIterations(1); //(3) + mysc->setCostExtractor( createEMDL1HistogramCostExtractor() ); + //mysc->setTransformAlgorithm(createAffineTransformer(true)); + mysc->setTransformAlgorithm( createThinPlateSplineShapeTransformer() ); + //mysc->setImageAppearanceWeight(1.6); + //mysc->setImageAppearanceWeight(0.0); + //mysc->setImages(im1,imtest); + return ( std::min( mysc->computeDistance(query1, testq), + std::min(mysc->computeDistance(query2, testq), mysc->computeDistance(query3, testq) ))); +} + +void CV_ShapeEMDTest::mpegTest() +{ + string baseTestFolder="shape/mpeg_test/"; + string path = cvtest::TS::ptr()->get_data_path() + baseTestFolder; + vector<string> namesHeaders; + listShapeNames(namesHeaders); + + // distance matrix // + Mat distanceMat=Mat::zeros(NSN*namesHeaders.size(), NSN*namesHeaders.size(), CV_32F); + + // query contours (normal v flipped, h flipped) and testing contour // + vector<Point2f> contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting; + + // reading query and computing its properties // + int counter=0; + const int loops=NSN*namesHeaders.size()*NSN*namesHeaders.size(); + for (size_t n=0; n<namesHeaders.size(); n++) + { + for (int i=1; i<=NSN; i++) + { + // read current image // + stringstream thepathandname; + thepathandname<<path+namesHeaders[n]<<"-"<<i<<".png"; + Mat currentQuery, flippedHQuery, flippedVQuery; + currentQuery=imread(thepathandname.str(), IMREAD_GRAYSCALE); + Mat currentQueryBuf=currentQuery.clone(); + flip(currentQuery, flippedHQuery, 0); + flip(currentQuery, flippedVQuery, 1); + // compute border of the query and its flipped versions // + vector<Point2f> origContour; + contoursQuery1=convertContourType(currentQuery, NP); + origContour=contoursQuery1; + contoursQuery2=convertContourType(flippedHQuery, NP); + contoursQuery3=convertContourType(flippedVQuery, NP); + + // compare with all the rest of the images: testing // + for (size_t nt=0; nt<namesHeaders.size(); nt++) + { + for (int it=1; it<=NSN; it++) + { + // skip self-comparisson // + counter++; + if (nt==n && it==i) + { + distanceMat.at<float>(NSN*n+i-1, + NSN*nt+it-1)=0; + continue; + } + // read testing image // + stringstream thetestpathandname; + thetestpathandname<<path+namesHeaders[nt]<<"-"<<it<<".png"; + Mat currentTest; + currentTest=imread(thetestpathandname.str().c_str(), 0); + // compute border of the testing // + contoursTesting=convertContourType(currentTest, NP); + + // compute shape distance // + std::cout<<std::endl<<"Progress: "<<counter<<"/"<<loops<<": "<<100*double(counter)/loops<<"% *******"<<std::endl; + std::cout<<"Computing shape distance between "<<namesHeaders[n]<<i<< + " and "<<namesHeaders[nt]<<it<<": "; + distanceMat.at<float>(NSN*n+i-1, NSN*nt+it-1)= + computeShapeDistance(contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting); + std::cout<<distanceMat.at<float>(NSN*n+i-1, NSN*nt+it-1)<<std::endl; + } + } + } + } + // save distance matrix // + FileStorage fs(cvtest::TS::ptr()->get_data_path() + baseTestFolder + "distanceMatrixMPEGTest.yml", FileStorage::WRITE); + fs << "distanceMat" << distanceMat; +} + +const int FIRST_MANY=2*NSN; +void CV_ShapeEMDTest::displayMPEGResults() +{ + string baseTestFolder="shape/mpeg_test/"; + Mat distanceMat; + FileStorage fs(cvtest::TS::ptr()->get_data_path() + baseTestFolder + "distanceMatrixMPEGTest.yml", FileStorage::READ); + vector<string> namesHeaders; + listShapeNames(namesHeaders); + + // Read generated MAT // + fs["distanceMat"]>>distanceMat; + + int corrects=0; + int divi=0; + for (int row=0; row<distanceMat.rows; row++) + { + if (row%NSN==0) //another group + { + divi+=NSN; + } + for (int col=divi-NSN; col<divi; col++) + { + int nsmall=0; + for (int i=0; i<distanceMat.cols; i++) + { + if (distanceMat.at<float>(row,col)>distanceMat.at<float>(row,i)) + { + nsmall++; + } + } + if (nsmall<=FIRST_MANY) + { + corrects++; + } + } + } + float porc = 100*float(corrects)/(NSN*distanceMat.rows); + std::cout<<"%="<<porc<<std::endl; + if (porc >= CURRENT_MAX_ACCUR) + ts->set_failed_test_info(cvtest::TS::OK); + else + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + +} + +void CV_ShapeEMDTest::run( int /*start_from*/ ) +{ + mpegTest(); + displayMPEGResults(); +} + +TEST(ShapeEMD_SCD, regression) { CV_ShapeEMDTest test; test.safe_run(); } diff --git a/modules/shape/test/test_hausdorff.cpp b/modules/shape/test/test_hausdorff.cpp new file mode 100644 index 000000000..dfff6bcb3 --- /dev/null +++ b/modules/shape/test/test_hausdorff.cpp @@ -0,0 +1,280 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// 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 +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#include <stdlib.h> + +using namespace cv; +using namespace std; + +const int NSN=5;//10;//20; //number of shapes per class +const float CURRENT_MAX_ACCUR=85; //90% and 91% reached in several tests, 85 is fixed as minimum boundary + +class CV_HaussTest : public cvtest::BaseTest +{ +public: + CV_HaussTest(); + ~CV_HaussTest(); +protected: + void run(int); +private: + float computeShapeDistance(vector<Point> &query1, vector<Point> &query2, + vector<Point> &query3, vector<Point> &testq); + vector <Point> convertContourType(const Mat& currentQuery, int n=180); + vector<Point2f> normalizeContour(const vector <Point>& contour); + void listShapeNames( vector<string> &listHeaders); + void mpegTest(); + void displayMPEGResults(); +}; + +CV_HaussTest::CV_HaussTest() +{ +} +CV_HaussTest::~CV_HaussTest() +{ +} + +vector<Point2f> CV_HaussTest::normalizeContour(const vector<Point> &contour) +{ + vector<Point2f> output(contour.size()); + Mat disMat(contour.size(),contour.size(),CV_32F); + Point2f meanpt(0,0); + float meanVal=1; + + for (size_t ii=0; ii<contour.size(); ii++) + { + for (size_t jj=0; jj<contour.size(); jj++) + { + if (ii==jj) disMat.at<float>(ii,jj)=0; + else + { + disMat.at<float>(ii,jj)= + fabs(contour[ii].x*contour[jj].x)+fabs(contour[ii].y*contour[jj].y); + } + } + meanpt.x+=contour[ii].x; + meanpt.y+=contour[ii].y; + } + meanpt.x/=contour.size(); + meanpt.y/=contour.size(); + meanVal=cv::mean(disMat)[0]; + for (size_t ii=0; ii<contour.size(); ii++) + { + output[ii].x = (contour[ii].x-meanpt.x)/meanVal; + output[ii].y = (contour[ii].y-meanpt.y)/meanVal; + } + return output; +} + +void CV_HaussTest::listShapeNames( vector<string> &listHeaders) +{ + listHeaders.push_back("apple"); //ok + listHeaders.push_back("children"); // ok + listHeaders.push_back("device7"); // ok + listHeaders.push_back("Heart"); // ok + listHeaders.push_back("teddy"); // ok +} + + +vector <Point> CV_HaussTest::convertContourType(const Mat& currentQuery, int n) +{ + vector<vector<Point> > _contoursQuery; + vector <Point> contoursQuery; + findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE); + for (size_t border=0; border<_contoursQuery.size(); border++) + { + for (size_t p=0; p<_contoursQuery[border].size(); p++) + { + contoursQuery.push_back(_contoursQuery[border][p]); + } + } + + // In case actual number of points is less than n + for (int add=contoursQuery.size()-1; add<n; add++) + { + contoursQuery.push_back(contoursQuery[contoursQuery.size()-add+1]); //adding dummy values + } + + // Uniformly sampling + random_shuffle(contoursQuery.begin(), contoursQuery.end()); + int nStart=n; + vector<Point> cont; + for (int i=0; i<nStart; i++) + { + cont.push_back(contoursQuery[i]); + } + return cont; +} + +float CV_HaussTest::computeShapeDistance(vector <Point>& query1, vector <Point>& query2, + vector <Point>& query3, vector <Point>& testq) +{ + Ptr <HausdorffDistanceExtractor> haus = createHausdorffDistanceExtractor(); + return std::min(haus->computeDistance(query1,testq), std::min(haus->computeDistance(query2,testq), + haus->computeDistance(query3,testq))); +} + +void CV_HaussTest::mpegTest() +{ + string baseTestFolder="shape/mpeg_test/"; + string path = cvtest::TS::ptr()->get_data_path() + baseTestFolder; + vector<string> namesHeaders; + listShapeNames(namesHeaders); + + // distance matrix // + Mat distanceMat=Mat::zeros(NSN*namesHeaders.size(), NSN*namesHeaders.size(), CV_32F); + + // query contours (normal v flipped, h flipped) and testing contour // + vector<Point> contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting; + + // reading query and computing its properties // + int counter=0; + const int loops=NSN*namesHeaders.size()*NSN*namesHeaders.size(); + for (size_t n=0; n<namesHeaders.size(); n++) + { + for (int i=1; i<=NSN; i++) + { + // read current image // + stringstream thepathandname; + thepathandname<<path+namesHeaders[n]<<"-"<<i<<".png"; + Mat currentQuery, flippedHQuery, flippedVQuery; + currentQuery=imread(thepathandname.str(), IMREAD_GRAYSCALE); + flip(currentQuery, flippedHQuery, 0); + flip(currentQuery, flippedVQuery, 1); + // compute border of the query and its flipped versions // + vector<Point> origContour; + contoursQuery1=convertContourType(currentQuery); + origContour=contoursQuery1; + contoursQuery2=convertContourType(flippedHQuery); + contoursQuery3=convertContourType(flippedVQuery); + + // compare with all the rest of the images: testing // + for (size_t nt=0; nt<namesHeaders.size(); nt++) + { + for (int it=1; it<=NSN; it++) + { + /* skip self-comparisson */ + counter++; + if (nt==n && it==i) + { + distanceMat.at<float>(NSN*n+i-1, + NSN*nt+it-1)=0; + continue; + } + // read testing image // + stringstream thetestpathandname; + thetestpathandname<<path+namesHeaders[nt]<<"-"<<it<<".png"; + Mat currentTest; + currentTest=imread(thetestpathandname.str().c_str(), 0); + + // compute border of the testing // + contoursTesting=convertContourType(currentTest); + + // compute shape distance // + std::cout<<std::endl<<"Progress: "<<counter<<"/"<<loops<<": "<<100*double(counter)/loops<<"% *******"<<std::endl; + std::cout<<"Computing shape distance between "<<namesHeaders[n]<<i<< + " and "<<namesHeaders[nt]<<it<<": "; + distanceMat.at<float>(NSN*n+i-1, NSN*nt+it-1)= + computeShapeDistance(contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting); + std::cout<<distanceMat.at<float>(NSN*n+i-1, NSN*nt+it-1)<<std::endl; + } + } + } + } + // save distance matrix // + FileStorage fs(cvtest::TS::ptr()->get_data_path() + baseTestFolder + "distanceMatrixMPEGTest.yml", FileStorage::WRITE); + fs << "distanceMat" << distanceMat; +} + +const int FIRST_MANY=2*NSN; +void CV_HaussTest::displayMPEGResults() +{ + string baseTestFolder="shape/mpeg_test/"; + Mat distanceMat; + FileStorage fs(cvtest::TS::ptr()->get_data_path() + baseTestFolder + "distanceMatrixMPEGTest.yml", FileStorage::READ); + vector<string> namesHeaders; + listShapeNames(namesHeaders); + + // Read generated MAT // + fs["distanceMat"]>>distanceMat; + + int corrects=0; + int divi=0; + for (int row=0; row<distanceMat.rows; row++) + { + if (row%NSN==0) //another group + { + divi+=NSN; + } + for (int col=divi-NSN; col<divi; col++) + { + int nsmall=0; + for (int i=0; i<distanceMat.cols; i++) + { + if (distanceMat.at<float>(row,col)>distanceMat.at<float>(row,i)) + { + nsmall++; + } + } + if (nsmall<=FIRST_MANY) + { + corrects++; + } + } + } + float porc = 100*float(corrects)/(NSN*distanceMat.rows); + std::cout<<"%="<<porc<<std::endl; + if (porc >= CURRENT_MAX_ACCUR) + ts->set_failed_test_info(cvtest::TS::OK); + else + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + +} + + +void CV_HaussTest::run(int /* */) +{ + mpegTest(); + displayMPEGResults(); + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Hauss, regression) { CV_HaussTest test; test.safe_run(); } diff --git a/modules/shape/test/test_main.cpp b/modules/shape/test/test_main.cpp new file mode 100644 index 000000000..6b2499344 --- /dev/null +++ b/modules/shape/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/shape/test/test_precomp.cpp b/modules/shape/test/test_precomp.cpp new file mode 100644 index 000000000..5956e13e3 --- /dev/null +++ b/modules/shape/test/test_precomp.cpp @@ -0,0 +1 @@ +#include "test_precomp.hpp" diff --git a/modules/shape/test/test_precomp.hpp b/modules/shape/test/test_precomp.hpp new file mode 100644 index 000000000..e73248422 --- /dev/null +++ b/modules/shape/test/test_precomp.hpp @@ -0,0 +1,21 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include <iostream> +#include "opencv2/ts.hpp" +#include "opencv2/core.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/shape.hpp" + +#include "opencv2/opencv_modules.hpp" + +#endif diff --git a/modules/shape/test/test_shape.cpp b/modules/shape/test/test_shape.cpp new file mode 100644 index 000000000..81d67f6ac --- /dev/null +++ b/modules/shape/test/test_shape.cpp @@ -0,0 +1,267 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// 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 +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +const int angularBins=12; +const int radialBins=4; +const float minRad=0.2; +const float maxRad=2; +const int NSN=5;//10;//20; //number of shapes per class +const int NP=120; //number of points sympliying the contour +const float outlierWeight=0.1; +const int numOutliers=20; +const float CURRENT_MAX_ACCUR=95.0; //99% and 100% reached in several tests, 95 is fixed as minimum boundary + +class CV_ShapeTest : public cvtest::BaseTest +{ +public: + CV_ShapeTest(); + ~CV_ShapeTest(); +protected: + void run(int); + +private: + void mpegTest(); + void listShapeNames(vector<string> &listHeaders); + vector<Point2f> convertContourType(const Mat &, int n=0 ); + float computeShapeDistance(vector <Point2f>& queryNormal, + vector <Point2f>& queryFlipped1, + vector <Point2f>& queryFlipped2, + vector<Point2f>& testq); + void displayMPEGResults(); +}; + +CV_ShapeTest::CV_ShapeTest() +{ +} +CV_ShapeTest::~CV_ShapeTest() +{ +} + +vector <Point2f> CV_ShapeTest::convertContourType(const Mat& currentQuery, int n) +{ + vector<vector<Point> > _contoursQuery; + vector <Point2f> contoursQuery; + findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE); + for (size_t border=0; border<_contoursQuery.size(); border++) + { + for (size_t p=0; p<_contoursQuery[border].size(); p++) + { + contoursQuery.push_back(Point2f((float)_contoursQuery[border][p].x, + (float)_contoursQuery[border][p].y)); + } + } + + // In case actual number of points is less than n + for (int add=contoursQuery.size()-1; add<n; add++) + { + contoursQuery.push_back(contoursQuery[contoursQuery.size()-add+1]); //adding dummy values + } + + // Uniformly sampling + random_shuffle(contoursQuery.begin(), contoursQuery.end()); + int nStart=n; + vector<Point2f> cont; + for (int i=0; i<nStart; i++) + { + cont.push_back(contoursQuery[i]); + } + return cont; +} + +void CV_ShapeTest::listShapeNames( vector<string> &listHeaders) +{ + listHeaders.push_back("apple"); //ok + listHeaders.push_back("children"); // ok + listHeaders.push_back("device7"); // ok + listHeaders.push_back("Heart"); // ok + listHeaders.push_back("teddy"); // ok +} + +float CV_ShapeTest::computeShapeDistance(vector <Point2f>& query1, vector <Point2f>& query2, + vector <Point2f>& query3, vector <Point2f>& testq) +{ + //waitKey(0); + Ptr <ShapeContextDistanceExtractor> mysc = createShapeContextDistanceExtractor(angularBins, radialBins, minRad, maxRad); + //Ptr <HistogramCostExtractor> cost = createNormHistogramCostExtractor(cv::DIST_L1); + Ptr <HistogramCostExtractor> cost = createChiHistogramCostExtractor(30,0.15); + //Ptr <HistogramCostExtractor> cost = createEMDHistogramCostExtractor(); + //Ptr <HistogramCostExtractor> cost = createEMDL1HistogramCostExtractor(); + mysc->setIterations(1); + mysc->setCostExtractor( cost ); + //mysc->setTransformAlgorithm(createAffineTransformer(true)); + mysc->setTransformAlgorithm( createThinPlateSplineShapeTransformer() ); + //mysc->setImageAppearanceWeight(1.6); + //mysc->setImageAppearanceWeight(0.0); + //mysc->setImages(im1,imtest); + return ( std::min( mysc->computeDistance(query1, testq), + std::min(mysc->computeDistance(query2, testq), mysc->computeDistance(query3, testq) ))); +} + +void CV_ShapeTest::mpegTest() +{ + string baseTestFolder="shape/mpeg_test/"; + string path = cvtest::TS::ptr()->get_data_path() + baseTestFolder; + vector<string> namesHeaders; + listShapeNames(namesHeaders); + + // distance matrix // + Mat distanceMat=Mat::zeros(NSN*namesHeaders.size(), NSN*namesHeaders.size(), CV_32F); + + // query contours (normal v flipped, h flipped) and testing contour // + vector<Point2f> contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting; + + // reading query and computing its properties // + int counter=0; + const int loops=NSN*namesHeaders.size()*NSN*namesHeaders.size(); + for (size_t n=0; n<namesHeaders.size(); n++) + { + for (int i=1; i<=NSN; i++) + { + // read current image // + stringstream thepathandname; + thepathandname<<path+namesHeaders[n]<<"-"<<i<<".png"; + Mat currentQuery, flippedHQuery, flippedVQuery; + currentQuery=imread(thepathandname.str(), IMREAD_GRAYSCALE); + Mat currentQueryBuf=currentQuery.clone(); + flip(currentQuery, flippedHQuery, 0); + flip(currentQuery, flippedVQuery, 1); + // compute border of the query and its flipped versions // + vector<Point2f> origContour; + contoursQuery1=convertContourType(currentQuery, NP); + origContour=contoursQuery1; + contoursQuery2=convertContourType(flippedHQuery, NP); + contoursQuery3=convertContourType(flippedVQuery, NP); + + // compare with all the rest of the images: testing // + for (size_t nt=0; nt<namesHeaders.size(); nt++) + { + for (int it=1; it<=NSN; it++) + { + // skip self-comparisson // + counter++; + if (nt==n && it==i) + { + distanceMat.at<float>(NSN*n+i-1, + NSN*nt+it-1)=0; + continue; + } + // read testing image // + stringstream thetestpathandname; + thetestpathandname<<path+namesHeaders[nt]<<"-"<<it<<".png"; + Mat currentTest; + currentTest=imread(thetestpathandname.str().c_str(), 0); + // compute border of the testing // + contoursTesting=convertContourType(currentTest, NP); + + // compute shape distance // + std::cout<<std::endl<<"Progress: "<<counter<<"/"<<loops<<": "<<100*double(counter)/loops<<"% *******"<<std::endl; + std::cout<<"Computing shape distance between "<<namesHeaders[n]<<i<< + " and "<<namesHeaders[nt]<<it<<": "; + distanceMat.at<float>(NSN*n+i-1, NSN*nt+it-1)= + computeShapeDistance(contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting); + std::cout<<distanceMat.at<float>(NSN*n+i-1, NSN*nt+it-1)<<std::endl; + } + } + } + } + // save distance matrix // + FileStorage fs(cvtest::TS::ptr()->get_data_path() + baseTestFolder + "distanceMatrixMPEGTest.yml", FileStorage::WRITE); + fs << "distanceMat" << distanceMat; +} + +const int FIRST_MANY=2*NSN; +void CV_ShapeTest::displayMPEGResults() +{ + string baseTestFolder="shape/mpeg_test/"; + Mat distanceMat; + FileStorage fs(cvtest::TS::ptr()->get_data_path() + baseTestFolder + "distanceMatrixMPEGTest.yml", FileStorage::READ); + vector<string> namesHeaders; + listShapeNames(namesHeaders); + + // Read generated MAT // + fs["distanceMat"]>>distanceMat; + + int corrects=0; + int divi=0; + for (int row=0; row<distanceMat.rows; row++) + { + if (row%NSN==0) //another group + { + divi+=NSN; + } + for (int col=divi-NSN; col<divi; col++) + { + int nsmall=0; + for (int i=0; i<distanceMat.cols; i++) + { + if (distanceMat.at<float>(row,col)>distanceMat.at<float>(row,i)) + { + nsmall++; + } + } + if (nsmall<=FIRST_MANY) + { + corrects++; + } + } + } + float porc = 100*float(corrects)/(NSN*distanceMat.rows); + std::cout<<"%="<<porc<<std::endl; + if (porc >= CURRENT_MAX_ACCUR) + ts->set_failed_test_info(cvtest::TS::OK); + else + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + //done +} + +void CV_ShapeTest::run( int /*start_from*/ ) +{ + mpegTest(); + displayMPEGResults(); + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Shape_SCD, regression) { CV_ShapeTest test; test.safe_run(); } diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 5d38958fc..c3477e3fb 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -5,7 +5,7 @@ SET(OPENCV_CPP_SAMPLES_REQUIRED_DEPS opencv_core opencv_flann opencv_imgproc opencv_highgui opencv_ml opencv_video opencv_objdetect opencv_photo opencv_nonfree opencv_softcascade - opencv_features2d opencv_calib3d opencv_legacy opencv_contrib opencv_stitching opencv_videostab opencv_bioinspired) + opencv_features2d opencv_calib3d opencv_legacy opencv_contrib opencv_stitching opencv_videostab opencv_bioinspired opencv_shape) ocv_check_dependencies(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS}) diff --git a/samples/cpp/shape_example.cpp b/samples/cpp/shape_example.cpp new file mode 100644 index 000000000..921330e89 --- /dev/null +++ b/samples/cpp/shape_example.cpp @@ -0,0 +1,111 @@ +/* + * shape_context.cpp -- Shape context demo for shape matching + */ + +#include "opencv2/shape.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" +#include <opencv2/core/utility.hpp> +#include <iostream> +#include <string> + +using namespace std; +using namespace cv; + +static void help() +{ + printf("\n" + "This program demonstrates a method for shape comparisson based on Shape Context\n" + "You should run the program providing a number between 1 and 20 for selecting an image in the folder shape_sample.\n" + "Call\n" + "./shape_example [number between 1 and 20]\n\n"); +} + +static vector<Point> simpleContour( const Mat& currentQuery, int n=300 ) +{ + vector<vector<Point> > _contoursQuery; + vector <Point> contoursQuery; + findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE); + for (size_t border=0; border<_contoursQuery.size(); border++) + { + for (size_t p=0; p<_contoursQuery[border].size(); p++) + { + contoursQuery.push_back( _contoursQuery[border][p] ); + } + } + + // In case actual number of points is less than n + int dummy=0; + for (int add=contoursQuery.size()-1; add<n; add++) + { + contoursQuery.push_back(contoursQuery[dummy++]); //adding dummy values + } + + // Uniformly sampling + random_shuffle(contoursQuery.begin(), contoursQuery.end()); + vector<Point> cont; + for (int i=0; i<n; i++) + { + cont.push_back(contoursQuery[i]); + } + return cont; +} + +int main(int argc, char** argv) +{ + help(); + string path = "./shape_sample/"; + int indexQuery = 1; + if( argc < 2 ) + { + std::cout<<"Using first image as query."<<std::endl; + } + else + { + sscanf( argv[1], "%i", &indexQuery ); + } + cv::Ptr <cv::ShapeContextDistanceExtractor> mysc = cv::createShapeContextDistanceExtractor(); + + Size sz2Sh(300,300); + stringstream queryName; + queryName<<path<<indexQuery<<".png"; + Mat query=imread(queryName.str(), IMREAD_GRAYSCALE); + Mat queryToShow; + resize(query, queryToShow, sz2Sh); + imshow("QUERY", queryToShow); + moveWindow("TEST", 0,0); + vector<Point> contQuery = simpleContour(query); + int bestMatch; + float bestDis=FLT_MAX; + for ( int ii=1; ii<=20; ii++ ) + { + if (ii==indexQuery) continue; + waitKey(30); + stringstream iiname; + iiname<<path<<ii<<".png"; + cout<<"name: "<<iiname.str()<<endl; + Mat iiIm=imread(iiname.str(), 0); + Mat iiToShow; + resize(iiIm, iiToShow, sz2Sh); + imshow("TEST", iiToShow); + moveWindow("TEST", sz2Sh.width+50,0); + vector<Point> contii = simpleContour(iiIm); + float dis = mysc->computeDistance( contQuery, contii ); + if ( dis<bestDis ) + { + bestMatch = ii; + bestDis = dis; + } + std::cout<<" distance between "<<queryName.str()<<" and "<<iiname.str()<<" is: "<<dis<<std::endl; + } + destroyWindow("TEST"); + stringstream bestname; + bestname<<path<<bestMatch<<".png"; + Mat iiIm=imread(bestname.str(), 0); + Mat bestToShow; + resize(iiIm, bestToShow, sz2Sh); + imshow("BEST MATCH", bestToShow); + moveWindow("BEST MATCH", sz2Sh.width+50,0); + + return 0; +} diff --git a/samples/cpp/shape_sample/1.png b/samples/cpp/shape_sample/1.png new file mode 100644 index 0000000000000000000000000000000000000000..f473cb4bde69432aebd5c3e9ab70b1682429a18b GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5!6ZWo=hV|c9#fLHy9)!us$OLyAdj=aBeEDs zdw{Xq$!t5I`iq_}jv*QM-rlgzJ8ZzimLMr*^NQn@Z;1)#iwlPCF9Kh!z5YSari$Z; z7iUwH{-!m_ESX>RW&bd$s-D`QbYy?`Hx`B&7oQ6~=xGu>5X>OM7Q!IRnlx4atR8cO z(^_M;YaiHI7y4@(JZSyG5}?iH@S>F=O|4-rN5h(JafhP#F4X8v;TBf>$ZPtctG}gb zyC|!WA2%19#aRu34}J{4?H(Se5pZ~%{F`|}MuR<nd%FIF-wrd>MOrTJO=2#cc~WuC zoM+X?j%<GE`dc;O@ySo0s#>%qA6;qQ&sV?qFn7m)jeTmTG=BJ%ZaK>q!q1*_dCf<$ z>#IIaI~CQ-xgunxP{7I{=D-%lMIAg-vNMA>1TDx>u8qr7W<J2?%y%cxVBzea(gx2G zo-rL|;}*{_i`aR<hGEXm19l8g*qPt)KhSKbW-}=LP~KR}mcYl*$NA->gDk(Boq&%* zKl@Shp65)COs83oK40*#=}~uZutVX&NzH}kC(5(WIqs7T=3^1x!4$zI!IhF7YqNa4 z#uHWb*p;Vh4PsaBWXYUwI^oMpt%TcdU(GBY`7X)5qQ6yay`kozP{%tT&zJSycA91; z&|By=o%_EnN5e1PMa$vf`MsY^i6<2|E6xoB#kg9DYeY$Mer|4RUI{~DK|xNcu8Cej zdM;OTeqLE>QAuiwLV%yILRw}{YKgFyA5e!XNJmOyNvc(HQ7VvPFfuSS)-^EKH8cw` zG_f)=wlXo+HZZa>FwpVqTZ^J0H$Npat&+je($Z4b&`{UVD8$gx3WN-`4Ge)AS}y4C Q0%~CJboFyt=akR{05*IHP5=M^ literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/10.png b/samples/cpp/shape_sample/10.png new file mode 100644 index 0000000000000000000000000000000000000000..46283caacac38a87916a833b1f933f4c83daccfb GIT binary patch literal 1024 zcmV+b1poVqP)<h;3K|Lk000e1NJLTq009610096200000bP1Kq00002VoOIv0063u zBQgL0010qNS#tmY07w7;07w8v$!k6U00Rz5L_t(&-tE}EZQU>s0ALa-lq&QFS|J-~ zm5h)90#FYwQf7pV5*aC6dIcI;KqU)-3`yxpBy~T}u@NMQ*ctpR-W^Yro`g8ZInHs8 zbNpkY_udcqwDf6ADDdkr$h^J+KZ5gWoA-`f09!=mz%dtqF42}?CQ^VCX{vc|sSu$s z>lNxX<YB(r5p@LR8vPNJ+YZbZwQ3+>=@7~wpb&~6!1*i)34^^7EevKl#ym<m#w<#W zfRRpOPou$rrIXmxP#CzT(P6+dS#Ei16pVD1*E}T-Cb|sQLbN!TZvzSkj;YgHDHiku z7;XZZ0OL)7m|r%71Cj#Mp8*X8><Qiw{0-av`lfw@8>c70Z8vX6fZGlbh0nV;ItJO3 zmOZQ4)1H4`;H4aHd}g7Qx<2*T=O+8)WuM*r(;v%odw)RE*xx|dcNq392+7lMU|?d% z-sNCmIbb-jhrqv1D*sGXAp&#Uul$X*^G0!?BX%N!@d(^YKo0?5fT8zKM4=KGSWuM0 z6hANkFzE{D0(5@r4<Ot=TW@~#76NX&L7u$r@_yJrSSa+T_K$v6`F7?&M(`5{@gDiU z01JZ<>$lK94hMPx?E%OY^dZTK29FUmdB?0^PM}L5{doj_1y%zfUNg2)1oN_D4eSbj zL~wzCyaE?WN`tVMFqk8FUVx{8(HKy%AG_Fd6=-*$7cgKT%&Azbu?ABD?FxDle2Ku6 zK<%JMfLTG9yZ3Y6);b4G1P?V3N07I51;Rf+#Gd<IAk*?RPz$gaaB08twl083K-a(n zz!kvlz$b8@wG;?0**WJ6koGMWbH74Z?Fhy!$Qq;_H`-Dl6jK`l#6zy%T29s=?05*H z8&Gh+fNh1Q1hL_42;+SQX1M~F0a1dv1~vzF0b&Qui-71S2DA_;KD>Q809k^&8icf_ z9EcKJA{aR^&_GmQ_Kv`CpbR1GaSiIxaX@_Lz`J;&P(b7W{0u8IU?3p69Sk549~PjA z@69U^uLX_+BMwvo_`GN$_(%ZZ#5vA!j{jx+0zGo8Pm|b8SO5S3D0D?wbYpLAZDnqB z0B>V-Z*X;UEjcc5XKe~&Z*F#Fa&%>6AW%+CAZBT7Whv|brvLx|C3HntbYx+4WjbSW zWnpw>05UK!Gc7PTEipD!F*G_dGdeUlD=;!TFfcAj+E)Mo03~!qSaf7zbY(hiZ)9m^ uc>pmvIXNvcF)cANR53X^F*!OjGb=DLIxsL%YB}xz0000<MNUMnLSTZ=z?nJ# literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/11.png b/samples/cpp/shape_sample/11.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f114d809e0d7e386b8ac2454acd0a26a3261ee GIT binary patch literal 722 zcmeAS@N?(olHy`uVBq!ia0vp^TY<Qc5lArX4!pk%NHHaOySp$jtm;)Z0`fQuJR*yM zv<Dcwoy@jlU|@XY>EamTaeV7F@4mwdJPa3HB2!KZT5!25@7@`fXC%|8s_gnPA^AYS zjBU!ID>Z7BYI+vNPJ4b-CVBhw8xJ?{Yn1=Rw@^K6#fk8bm&y{?p1k~d@%uyHXCGn+ zuRN!`tCPK`;=}G8nKE6`4`&x-{7s2x><hmWYX5Y{&FwpPpZj{k_LpsDJ#Xy7^*4PB zZ{2_8{>?Ph#=m;1=W?y;81vn}bsKgs+pGJoc=;QPiIZ)@t`<(7+AmVzSsJ5XBkHHk zY1q}ceBYF;^KqT`Lax?+e60Wd;>0!29nDH)=TDk_&bD?^@Er4qC9e<oWzMz{;eXR5 z!@NlUTv_SH)sMIqN7+2B`y<sXD_{EH-}NWv%cAdSPxk9O+`HI(qou=!dEX7z&zZ%% zq+tq2R>P|~&T}?jEta~##(h6{-2)M>%eOAwG#1zM+`)EAp|(4(;Hz4!Y~{Nci`Ie} z`Y)d!`hJb?{-wL8zTaW<<+PsPv-NBAb77uZOSN;yFP6>8m-@Wu`Rt(5dFzw!?wr44 z_7BUCkI#NPAG7!6UA8OS*Y`b}xx4qH@cD-?kK5Y6d3UpBS83>V;qb?W9~t@%UEcL0 z_S*U#KY|m~-b(NP|NhtEb!v9q{+lO$IzO>wKM(tl!b95&&5eZIAE%~lFFDFrrI~zv z=YOV2J5Q;l{_>RvM!Q;xYeY$Mer|4RUI{~TQGR)zu9;pzdM;OTeqLE>QAuiwLV%yI zLRw}{YA5pwEuaonkdBnZl2ohYqEsNoU}RuutZQJdYiJf?Xkuk#Y-MVoZD3?&U~rLV z$~qJcx%nxXX_X9ymX?;fhK9O^Mj?ilR$!!UU<lN(-9GUyPy>UftDnm{r-UW|vaCMJ literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/12.png b/samples/cpp/shape_sample/12.png new file mode 100644 index 0000000000000000000000000000000000000000..ad876e9397aa8377d57d89cd842a94b106a49ea4 GIT binary patch literal 437 zcmeAS@N?(olHy`uVBq!ia0vp^@jz_K2qYNzBe+t46jPG7y9)!us$OLyAdj=aBeEDs zdw{Xq$!t3y<EW>LV~EE2wU?~<4lD4mCAjnS$TTi4Fu75+R*qFuJVCp;^|idZU{L#( zpqZZ<;};10t^c`8;S1};^)ogpgm^Wti1cBfvs9JMre29(frmMG&w|88@f~R&o-%M9 zS+1s`^y|ngR*&8-u1k5IY%(khVm#rSk)rM^BeFF>R8!m~<$0%_qU-?`wlizn9cGt{ zpJGUMd)t&Rxuku{H_pPQhjDG}?*yJiNZg)#;Bc<`v&Zunus#-7VP2nM-f_S6uGohA z$JcIf==sLxlOz0QPIJg?poi2-Tq8=7^K)}k^GX<!i}K6!bj|e&(sQ|z^YhA5i%L>c z6axHw71A<uQaRMl@&a|Jf^?)LmZVxG7o`Fz1|tJQV_gGtT|=`FLlY|_V=GewZ381K z1A~h^Q`Vtq$jwj5Osixtw6wI;H8j*UGzu}av;re-14E#O?e>Xxff^V*UHx3vIVCg! E0Q--T;Q#;t literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/13.png b/samples/cpp/shape_sample/13.png new file mode 100644 index 0000000000000000000000000000000000000000..2ec2621b2ef090b30e95d45fca00879a31737598 GIT binary patch literal 443 zcmeAS@N?(olHy`uVBq!ia0y~yVA2C&MxX%02E%<tK#D2J+uenMVO6iP5s=4O;1O92 zq&>jc?PRtcka5D(#WAGf*4v8*IU5WFTn~zJZ&s?_#5Mb45@*?h2fiN_8YfIRb9i-g zeeVPn&q+}9GH=Fp-KUXpXJ%e&JeXiGqhsT$^#@bZ|9p7UBb=T-=SJ?GW4U*h*<5~- z+hcS}V{wnxe^WQf)G2{r;;Mmf->;mieVhJQur>pAOYpFfO}*@$2zJt|P24?4FK*GC zvDOjjC(RPqh?3;|+}zZ>5{8u2vdrXE3tb}vy@K>yuH^i@vecrI)D(pPKVOBk%$!t* z|MS~`np8oWQW8s2t&)pUffR$0fuXUkfw``sS%{&Dm65TPiJ7*6k(Gf#UD-lVs3SDw w=BH$)RWcY_T3YHF8tNJvg&0~|0g;ihwt*o~!`uUw1wai9p00i_>zopr0E~T>(f|Me literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/14.png b/samples/cpp/shape_sample/14.png new file mode 100644 index 0000000000000000000000000000000000000000..956e5b8145e37cfcb7066c30e96e1b399ef7018d GIT binary patch literal 1820 zcmZXVc~H{_7RLh&v|vz_>j%YxBH@sP-!I`vf=12=m_P(nU_p+M0CI$qh*S^}mJlEc zX%qqy4Oir*6kL=;Wy2MS7DZ*DMk-aHV#*>Qa!J$uYj<|$edjap&3yiQ^Iks1_qeu} zu@(#l)AsiA@Q1-vQGZ26Ie`5LJxEbPHR{B14;ZZBsySNOX(oFGGnMrEPl(cZ0t}{M z?d{<jm^P)5lapRK0vZv|5=6Gy=O$4F4PRNinN}Vj-g^-y@&DDcgy7cMo2$73`=cuT z@Vq*}I*HV)f3`JWTF(32tJa>g7=~TqZoxe>YwFq}clEtV#+vNTll(uP5FT!1JbnCu z=WL1?5f{BmhjvK9sSWUM8QLP7cV02@7DI1err^plZdSctpXex>B#&&yUYUF;#kb7a z^~P7~&})}Yb5`Q=p>v6Ahse1-wvgxZBOBWd7<AR^THqLJPti!ii>9&D!YH4(X9?N? z19^7I=o=&B`rFLL1@{IiCNJ+;aN^GxN1Wt&F3MNy@jj+yVT;46YJj*Fdn(VS^oFgo zI5eW+j!#8-4e!S4ofhPH7jGMLx5*EFI<T&<vc)J<HZ+x?^Y9**-gm7rc32>_-g9fC z`b(%*%<<_?>dL(z;?liJ+{n&Oy7lj!C#1tm?W^v{w~CrGDLjJTZs(yM`|+dcmYJ{A z6e<XmN678S{tsDh@AAEFN@~1PmiXr`(lW;q-n?ix-`7aS<S?p9r86j-S#t1z{qoGB zm{VZo7mxXNcHpC~bgTH>+6HZ<cH1%)Gu{=1Z8~~iK6F{`SJGI<F9X{w1B8n5Gtn$c zvu0ub(K2tj2S8LG@PeD4EtMij?}s>}Al_<lxyB{z2yIJGj|%ct`rjHxW*BPeP+NK5 zP5bp4Fv~3DrNLdu&2Wr}_Y<JeK_QhOr=`h88Z3X&;he>HNofBE6aV&>e@^1M$~L;Y z`S-`<@<LPHmHph^4Gw0A%rM0D?ruxN_DicLKv{L+<O9wh(@(bwyEudo1Uf*kN)QG% zd`Zh*Z$6uWNx1IwvW!1@J1`*kMY)fyN7s&);c{q$e3y{H)mi+cxNv?cM}KB*Q?iCB zS?+l_>}@zspik{&SY{HKA?Stfaa)M-g#2StA}4<B+CSmPf=S^!ZSkF_V$EM-w{!h* zg4WI)Myj|GcykZgF7e}^C*fm=LjBtj8m)Kc)&k6Di7inx7}>{-<?qwJeQ#TB2=JpN zhBp<ApO)rM2#*kKZ*|Z=H7nBenT|GA^td`ln_b+=k(y#=h*d>%!OeO{Ov}b**Y@1L z(A6^urz>))ofy?|;|b0tsL9R;x&7d^CreRuA(e4S5IhH%(#~dOa$%4)`zb)j2i`Z; zr3&WJ+*K+fcbOA;(2tHDs<p&eQ<qdEpAiG#Z<EEnXnv?UlZX0riYWWE-(N(^j@e32 z>k)5ast2&kbUn6FbJgHZlZz~QeRSLCYUDnd_dptAIflJD*D4aY8tKwM7cZ?}S*E^@ z0Mv~Z5AgR2zD85j*l3EZg5!p%Q7Hw8Spq#tEL@ig<VGH1L%^U|N8AtUvo%@hVuONA zG^Hg~ZE!)}hSC(Y9RMLg<*Ig_!#wH<#O(u(*axhw5QE@eF=LE-pNNQ|8>`dd`Md~( zIi&i`VLuguC%Qj{IuzuG@)0i|C;yH?(ZZ}v8Mpsynb-cciRT~HAY;iP0KrB-=M{k5 zC^{rUX=r2X3S605qm<V2yj6B;(XdR5jrSL+>{%Eoc8S3jNMpH!pj~mI|JtA_uP<S0 zjKu!^oAQ)s&K#@h8yjZbOvSJX>Vd&{xr@Qcu`KF{-PmTm$+%O;%fI6!^-5J#XcwEV z9d%Hal>?l=N(u@yp$dKzKOPh4jT>K0Bm<V}A%#V7vP!tbmGQ62RK3U!B2~jAFjFMW znw-x1hqsw{^UL*tj@fcxEUQ{zZ>90dsia1<z)o$ie*{<D((h+oa_@e91=wrAtO)2I z74I(a24&xs`u1?bE%+`12Ka7b#{<W%d|dBRX4ZADkq{zZ;c6C0A#Sa5ZGQio`_!&O zfS92jX9vSuW2voqS4zW!;Bk-4N%0pELtrok?&*40BU5+~+O9ymLO6Dt-fUj4u5MXp zcV&a#Dly~2&r@G69x*omC0rilcp>KU55UP^=1*AIuUp1&bxv*O5o7Z`H@^y<y_`F0 zzNkXvGa#9^@Ynx7;W;Ch!`_;z5*F`TE5C;|%QJ{YWh5rjl31{4T524XhDAa+RB}wB z7L}2dN=sqUqRlA2WHWkP0u6yrCMhM0zoh6$77b5Lp(z1FgAfJ@Iw2v)K*%1C#^4>G z!yp<Df)~<n8~=}x$cT=ke-DFjI2;m!kPtc$!r_&L#vBGAr67ESOI8XnZ%<#37PpA( FKLLo)K%M{q literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/15.png b/samples/cpp/shape_sample/15.png new file mode 100644 index 0000000000000000000000000000000000000000..422395fb0aacf1406c46020f49b64bcc29855cab GIT binary patch literal 803 zcmeAS@N?(olHy`uVBq!ia0y~yVC(|2Z!iK0hPx&+zXB<yByV>Y28LC=%0@sQXMsm# zF_88EW4Dvpc0d)To-U3d8Ta1a^zB=0Ai$cSEEZS9()TF)Y}SW6XD-FGC|8_hddSzP zlIr~EyMu4P+dAE!OkbIe+*S&ydQH&?T;$T3->ud%QAMrgfklyU;sO_2;diHI&15;f zOvuT<<CWea7lFMK943^h_W8DYrHLNAyi#CprW}j@O=ZzVGyNH-`+QzfDYzx2P5o~7 zg6(I#7xb=VVU$+pv^c$0WtuvlQq-h*>T*hFJ)6F6n#%He=|q;-sV*Dh$^<vs6moEe zPk3eWk}+t$nw)}_+KCCOERvg;mvnL(O}BSyI(MH@lhN-g+pRZ~p1=JTT`KH+&)3}H z(&rgAp2wvXnf)i_C_GZWq?q#H<cZ0h>K_+4Db<A}ANjasf6EW=69*=L6f%l+I#lnn zZsm=}FgN3Cjz>N(dEWWB^J(Xzla0%tHl`Q-d1B7-Q9-bKg`9Hpj*NCe{l3T^i;Zdl z5*xXHZq7XY`|8o6HQ^RBrzMxIJJ>OCXZ2a0vuPjyw7sa-URAi)s==o2%j<1ge&;ss z4=8z+{6_Hi%onnY{;>w-yXLvho3P*ezn$ABy|n!Q6RQ<hUNTwrW_Hk4&&8i57nvRT zA~N%GMr>cnq%+r-6f3FS6uxs<fO)68f}mi5^eS8T1AjL@lVLP}_r>y&VbEt6zxsYb z%M?D9MSsOq7M)rkpxV5BLA>f=^IbQkv;)&jstcuJY^)B~Y&OXGcK)2f{K~AnLoO#} z9IyS+b&lG0tbJdErrGbWrz{M`8@%?pbb=!N$HgB^GY_bn-%7l71elUkN?apKlJj$O zQ}ap~vQsN{&GZV=bGef9^U6|-N>Wo40{nax(lT>W|9&WU0;*R9sZU8PNwrEYN(E93 zMh1q)x(4RDhGrp#CRRqqRwm}!21Zr}1~(X2?L*O!o1c=IR>@#!X=$lzXsByw6k=#; d1w_V%+6IO|4adG}90zJ(@O1TaS?83{1OPfVOK|`I literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/16.png b/samples/cpp/shape_sample/16.png new file mode 100644 index 0000000000000000000000000000000000000000..57ad4104a67291baaf605bf3a66cba2b7424c094 GIT binary patch literal 830 zcmeAS@N?(olHy`uVBq!ia0y~yU|a^oT#P^ghQcdntbr6$lDE4H1H-CbWg{Suv%n*= z7)X17vD?XPJD>`GPZ!6KjC*fy`W7uV5MWKv(A{!HutcO!O}2M&M&1HdZZ2Lq>BaW# z*JI~Rnfz+|=SRjbwl0uKl~57tbWxh<0ir&gHZGjtHL-Z*6dTE2&81V<oLLu?a7W>B z(#!Y`kGogTM1(DxmjAwM#|F(pv2PAr9Q)h3M3(Zou5y;|3Td~z;uiQVV2RM(;Ik}W zU8V>f57ylgv|MS*3V&CPi*vd*EYDf?L1_6A&5Nm8hqRnqgo;_GTRh{s*SRV{E@W4L z@rpCjAq-P>Ls+gJ30fEMLU@|RDjB0)OSQJ<Yw~(r)!@!q?X@&|<-COxco%6uWlw3o zk|h`Er6|25%&|Dc<OG}H)F@r|#MvJ|Pkp=MweS7VXIUw>rL}v*U%kHmA^YpqSihH6 z#@@O*TQ`P8^DJTVHqEti0rECoITqBp{M9SPD{nM?Sf_RAY~66>RM6D}$NsOpWG%8o zFL3^%JI7bPJ-+g#^wicXzj{~I$xC@0-LApAa^}*Sr6Nn%`l2FN{d~%>cN6oXX}{QB z8SPp>JK%1`bCpL|OEyg`sav@_=#S_5nX{&!&YDtw(<rAt%K3AIS^MQxzn8b@UMTx* z6LVZ7Yi}*vlsu85C&7LtH}|s!K7H2ld%J@=$JcEK{It6o&;Hh6TGZ88>2fEChsX8k zfe(jI)g=5V%(C0SvAQN9p!WNf^U7k!R~X1kJiYK|&AcLq^^!-QUv+PivJ_3bTHRID z^XU95&;M&H?6jwFoqxsbsd<i9PStJErY~&m$LFk^zN`HG$*lA(x8x0byglJb!PCB; zG3ES?HMd$XgVLC4iEBhja(-@ZYF-ILc50=rp_yJmdM;OTeqLE>QAuiwLV%yILRw}{ z>bF~m5@8xr5=&C8l8aJ-6oZk0p|P%kIZ$JWp^24|v6YF1wt<nAfq|aqt#A|#x%nxX tX_X9ymX?;fhK9O^Mj?ilRzPHIsBK^f)Nt&p#&Mtq22WQ%mvv4FO#nb2UUmQg literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/17.png b/samples/cpp/shape_sample/17.png new file mode 100644 index 0000000000000000000000000000000000000000..58417bc57b70d3a61f438ecbec7a6e9fcd4930cb GIT binary patch literal 3100 zcmYM0X*iU7AIJaqJu5S|X@ryp$)1^^!lMiZGeeoAEQ!h9LDr+P<Q|1lwxg^kON&-T zwo@cGofZ*t#3VE+M3FX2Nza_;&2wG9<@<f{y?*}}{}-PO{!T|Z*)_5N068v)?Fs-a zl`w#$6AWXf-2ZicPL6B<u4QR6rA{W2;~68}od2~$N9c9{NCsTCjoXo-nUP!FGv-P_ za>8gU4lWcux!aa>{M&X}+%}yNOA06Cdbs|AT!?-hoKdh9#hqt+K3mH_GxzG3OIVLs zqeo2GWIOmgIr{X+%txzqbtg9*R^8s<GL!sQ)(D!nDM1$=h<#xMH^vrIpq$uZB`Cop z@?vKd(cBd6lR3*2>`>_dgQTz{&y28nw)_^vEajr2TZub`^KchQ`!UEPUNSvutvv(# z{B(A*!)E#k-s3bnC<o*f=^2oj?o&U>;yZ9{gN_Cc8{&yI`8|D<->Jocv_G~FkWgN2 zG}znsf@H=9hFsOQ^lxn!x9E5V0;jL>v$zs1(T`zjTN=>CjqzMtkb=0j^E_8$mN$2Y z(%BL8GaKbV#|^H^i6!qH6$F1GEh^?T+7sa{Zu^---rUReqzH7V3_})a7nNfc>$9?0 z;)_ai`DW*vuKhKIeWOQss0%rn)I8iqf$pnI;)OR<j?`oDR9n-?9%77Pq)PjV2^EKb zK+!k?=l9Fs_7NmdkVy}RKW%iLMoUT*=O>6w!qDXH@j_F4!D_)l+|8d$C8uLCf;X<q zMd$#zi0WaFWE*yTg&QHhKCM%DGW10~COAT6bhHsy^u}v@C~LyDUXk<bmMrQtK6NGm z^HQ=9PS}gC@BWpEgziiz-5Z0XAY5CKP#;s_KjT!n?1fES4_OwQMhFy=A93B(z{I;U zYEB{+UwZpmz)-f(0@}E|<i9g77Bh(`JRejKLf+PPwwPX2QXPJyZ@gKZ$H)*^eF5S( z5$I9cVvMWUU-c_faddZO^FVRz7kndG(6=<@^>Y!finUJ_>YKLNU%#N!Tz*cI{#Ho8 zYYEKZQfT(MUC2V7Hm<9<tWICSYA&yyH1FJVHdtZ!jcIx=8SW<2aS~q{M5`#OgPkAy zaXX{tq_DNUXzgqY{(|xX>4c<14zrOYCPorj`%(LHSY0$F)D}H5*nRSzLL+b{3$ldk zAX7cDv;L@m>}i74xM8Vz4<qsz1XP8`_K99%k#Nz%Mxxk?4ogIq53nqqmWju~ILJN7 z5M3RJgLiXG8QJS=*Ty-5E@w2ge$y75w=5c!c$8E=pbVO@QaNP@(RBoD)D{kvtvGPR zbDM%Vq3Co_6lE4S``teaehSH^DrRQ~Q=uk~1GF%gAh-c`%S9fjuN$Py(W^8>rx*9) zmkcfOutMo61UAFQ<zu=wv+tyxT)Hhxeu#2`xePH8ck}l~BP8^biyH0ln2&RNG2~_v zd82khcuT&>N3sF(=IKWkCLH*|qj>A(Q|Ql;P{k*M<G&$GIhaeZcXEDpPq$fQPR!Ev zv<&=&0jr>I`tpvkPWeu_ky*i&thf)#7Fn|7?o*Nr!aBy*WEZrcuJ|hlh=fF0I+h*< z?7{21Ss`r34^1tTe^z;BTJyF9GQI;=gC8aPoi=B;blNGtm_{8iP1sE5iZdsY{NPIA zaMC)9HF|r#+)r=Zi`cH$NFzCK7F-3Xe#6USL-VN{5)ao36Dcc72G<7OU9iGjIXm6o z`uziL)Eyb>0bQO4)mvI_hWox$4pa8%n3<fOSFR`#uHnBb8!e<_-Z>xm5|u%qyK?q2 zbwO>Onmx;%sht|26Z0?^Yp%b6^hd)sXfB@R#;hi=b-py3sA$y@#%=qpD!fH^XF5Ts zwRr|F=#tcRQK|1mFOO@{qlBYWGapOh4G+;U;DP_(5`q;@riR>RT~)mEvJk|{^75<V zp-%kg{H{?Y-fn~Ux$^^-gj?9Tzk1x6$%;o+&Ri|14(KG*2GUfITUB6LjoH|j!S6YJ z$HUY(2>qnB#jj(I+N#H;id-Ual>MipdC>jU-IW_Us4Q>mN{4D)OYDz2-<9R-t==Z3 zW;k6h>+hv<)iX!BP6RG$rqd;_NHOo`eynRJdiTAyFTfOLaIqJ|XK=^zCnSqru+h}L z+XiaLLgfzgO<euXg-v3D?kfxQ%n{;b`nsLO`NiVMk3;PrqRW!kJ}l==dZ;|*CN~Ee za^<V-W`ftRtlWIfnwk_Zm?VGKPE%fxR3?4fXZ`9FUi0wTJhJV4fDMzlPo59c60QuC zZd<G_n>F%e<)(a?PhmdJ7G5NKo8*r8@EucKe6Q6JV8K&NNpsQ^=E{^av|jHb;@go* zjZMY}yMMl*qF0IR?+Df9-_1T(#QKy{GV5%!@LcbZW`T)}mqu?AxAz<TfJ!IbItvG_ z3%5rsDpx0<M(i;XUV}di)hQP4M82opkHe$zW0RwnyT!kZR}pBfvu~+meKNhiVkU5e zr7y^|txELwChT2P(=gMj77rd~)duS-%0BxFFNJLi6ZAOnTQ~Q(Oy^kD+!D?uDD+;l z_9j~A>t_bb@$c2)&?sRgU#4NhVw>R}w;Pm9-)hj0@54N}^XwDSqG}UJ`#5a<q|Q`s zF`!VGkE<GAr5w#uS3|qu)zc~|?B=cixe{Y+AoF@K{ckA9U;2?Ek0Ca&+v@zBJaFYF zH-VK8b?cXCLw>56zNxUcT8)w5I_B%H_-fd3obHQn-^briOVf<a%d9ARz^Gz5@PEoZ z^n&Ks)bw``@X)t}bJ4YXuGuR$aWL8VMuW&+bch^Met$@2k_X%2Sr^9iBqC(QXWud& zPUY1cQ=~`~V8JPnTsv<(?W!UAepwOeDP9Xz-65`hGVS2@nPOj)0Oam>V>NV;+s3n^ zQ_SJQw4BB}t|c-O1|3-(b;;7^%ocY7gC`#wUdJ6ydbHbFzV{jNGPoC<O|XKIo|h+4 zSuN3Q5sT}=Yi`fQbqAFFZiL!{X}e2B)!i`d-V7T<GoD~*lr@t+WtprZiDgmCikyZ@ z^_%;lNX)FAW{UCz0Q9U5^OS^V5n%Kigluj#M9#;*AcG33_i8bL+O|JAC-;)8=G`0z z25yY7%nlfK?kZ951-*OFHe!2th@)A5Dz7%`w<MLsQC7eUUhOAI0a6TD9`{I#8b;^y zSbkQ>NBD=$t-Ou&#tEO`zpwv;IB9W^T%6m<nEq#rVY)|KeHGC^ps?Ebb9q5Ej}N!y z+;ven^46uF%5f6WaFQ?8XrB(|9hH@%O#XAN9iFcyDa{Hs467YSEHDA(uY%C$yqWh~ z%WJLZbHeqPD?@B@hb|xNg8jX?ie#m~b3AP|X%Y{LWNu^!^3`UvhIyU!nIHf4ipjd~ z;a||s=-ky`%lEPoeXt6<(R`!9hls<*{({AIzZ)ONkbOUcJ=ipZxsr*Y($8U1Lb&83 z_yD}gRX12OLIQQJF)N88VY8NpRZkaFi_v}9rga|i!p%?8=Q8S}urVaMJ=#yobb$jD z&SVLj3tmD$HVFOz{>x=eD*P67xW;-C43eGYP{qP-F)6oa>;TlLLYHC^MjN=@Um-V2 zvFGtdET~BTt5bMRy(Stho6@Z~Bv=xm!}ze3J+BWQUnZiPFBK5e4xwA1n<^r$-WDaj zPuy(b{Q<eI7U}WFzgA^lmcm$*dW|tSHiEkn49;txvLWPMg~SO^@Axj+ce+5`Ly+n? z7!2ff?T1ni32>x|9AG49+kov~1#Szd^8PS+%o4r6Prt@mz1J1E2zVub-%yDR=@=$C z_t|<q%iFcuX5yDmLp#y6Vk@kT>1$|)j4^$gPnooBR2MA)Pq+&oHdT8_;-pk_sdKvu z<ka-Rky7!ab*j7J<2Sf`=<@upkrlZ@H6O_DE$Sx4XT4Z^k68bR@bJJxu^=EY?x26* zR%5eGk-_1z{t<`b0;6LC18Dr6J7_@%!vcL%KQu`*I{#(@4#Wmp`bP&!7huvErpENG z#*8g)3^PlnsioN#LpsxvPCpiR?q96`e;_;};9$^Ez_75eFlI1}8B8~Zg{8EZriOHe UG*F6KL`nmIYrm6y(^io3UykTOn*aa+ literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/18.png b/samples/cpp/shape_sample/18.png new file mode 100644 index 0000000000000000000000000000000000000000..25fb50b1003f0d223d4b303ab5df59c95e4f099e GIT binary patch literal 3283 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 literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/19.png b/samples/cpp/shape_sample/19.png new file mode 100644 index 0000000000000000000000000000000000000000..256b8889c7d48ff023aa9450b44706c4a842512e GIT binary patch literal 1560 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 KMNUMnLSTYsFV-;t literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/2.png b/samples/cpp/shape_sample/2.png new file mode 100644 index 0000000000000000000000000000000000000000..7c7c31b9426545d24a0c3ae83562edaf85f3ede5 GIT binary patch literal 813 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5!6ZWo=hV|c9#fLHy9)!us$OLyAdj=aBeEDs zdw{Xq$!t5I3R_PX$B>MBZ*K<XEmaUXR@lpLV`N>RsmHt9I9>f5-yC!GjZ4cewKU88 z`0nL<*)8tp(^EeW+&-Oo`Oagx?>mn7ORQy2_#4Bv_r6pxQ}cHYH{)psT@utblrUV& z)0+0uoJrp`Vej|S18*L19*kkQ%U37-BbZ6IF^#>x`+(pL^T+XmUs%2=%<EpDdO@MW zy2Les<BdGy0@e-e8~JCQ?|xT%Z~afD`^O{xHQaIk!uVpfz~`)ub6K=)9BbRU4=ibs z=~ZP^W0Tf#jBK%PSh=UAMDVVl%;XL3A12*Bd8Tg{i%i(eGxFb7dpqv&n%rdJJ-cZ~ z&z25nNk7Iw$x}5UZmSu0O3!6Wnd`>9$!|4dWcqr>$`x#)9SU=}Tm)Y2;aK$b1BWSF zWGkObK$f3Tmbb(C%sPe_j$LyMJY+l8E>JLhIXU_x$A#@qb1Y;#?rzh%!O`*f!hGgO z7CcuEtW5al^FW{dt>=asnUZ+6G^U4FO~M&#^6i9g<nt_heb6UiCEM$-3+g5^&X8v` z%Ksk4yDdxL->a?ajqU<*Yk6jHu3>SS+jr^&!~a5qkmh|-5vQaYExFY!f)cF-pZJ|| zO8Cvnu4HpKP?CeqYt{LNS8sWin{Af-QD^@_w%LBlkLz>fo{BWcrT%DoaC_0LDSPrC z^@_gVQ;~hF+Wh{VtqN|OQ>-4pWBew+p}Emk;N7AnsXc}sr!2~fHi);}Re9rH=+0f8 zbR+ceKAG}w8_YZIy1w&IY8S3vawqku-P`($)xXlu+P*9NSi!2${Vnj9{-SJPic%|a zjVMXZ&&^HED`7}1D9B0GwbUy}&*e(a&nrtUDoIUI2=Mb&NXyJg^|S5m0P0W$=}1W| zNwrEYN(E93Mh1q)x(4RDhGrp#CRRqqRwl;U21Zr}20DIyYf&`h=BH$)RWcY_T3YHF j8tNJvg&0~|fsmoLfgw;s%LV;iKn)C@u6{1-oD!M<(78rw literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/20.png b/samples/cpp/shape_sample/20.png new file mode 100644 index 0000000000000000000000000000000000000000..de8d5ed3e2cf10397a512d87ec5bfa9236e95747 GIT binary patch literal 1571 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 VFfd?(BXa-%002ovPDHLkV1h|F?B)Of literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/3.png b/samples/cpp/shape_sample/3.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8f7bc471e8e7bf7683466e6e173165808a4b08 GIT binary patch literal 2301 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 XF*-0XwKz2=00000NkvXXu0mjfvG|E? literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/4.png b/samples/cpp/shape_sample/4.png new file mode 100644 index 0000000000000000000000000000000000000000..5cced92c73694844dbab2f227ad5d880173cd4fe GIT binary patch literal 2431 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 literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/5.png b/samples/cpp/shape_sample/5.png new file mode 100644 index 0000000000000000000000000000000000000000..786d658ca52345ceb4180a75c65dc65ba6b7ecd6 GIT binary patch literal 852 zcmeAS@N?(olHy`uVBq!ia0y~yU=#wfKQaOd2L0EpVnB*1$=lt9fnimzvJsHSS>O>_ z45U54*zIJt9Z*Gzr;B4q#=W;U9rF$=2(Shyi^UbO^gYu4%2L{QEO0^<<1`(C%ND*; zJj|b0M+AzeUH>BbU(z7#aps|RC3Pw9NE4S5!H#`WFOxoBWijLn6gk5*<7<{s!jeas z(#eYwiYsrvm|f^>%zW%lkGvH33|0f}ggloUG7W({B=Z#h+WS^rU18Gw?A}82nVhC4 zdaI@^yv|#vq44aJ*kXRuck?CfO+GJFV^G_7RN`mAkLHsN3m5k=_sC0f+wkfXWZm;+ z<55ftx#3il$S=$|nZdcEu;F8aWJ{bU-`{KwLp}pJ13?2xgBA&P3H})ujQuaYFG(~? z*yZ1Hsp0_30hI$O2fjGH?_u^~@L}-*nskg2XcLnfbK83p#)X_5u`^jD%)W57tm_4; z;1RE4bh_TgeOE$l%UWLsA10uN&C7k0WfGVYZrR$Jt1&NoyCtue9Vl^G@#u_YRZMD+ zeD1!QYA9$>`jb0-Rb2GBknqQ6_n91?9lJUG-kVp~WrhD|EL(i$mSfW5%>B+*zIS|6 zs+L9Eh!Z}2WiQ**?)6p6c&?ZpowT-LV}oJCOvc3wmw97>0T{P{dqb@EW$r6``S)8b z;}Get-D!N@VP!^jseujC&QI;9be=AMW|@B7=9=0DwYR6fC@;RfbjGazde^s|-T&F< z-zA2&zt5`ney`9{OX7d|$l3KzV!XV?*34Upw~i-9vt_R`^7;Hi_2+L#*H7K7>q5?P zq(o<YZ0($;obOiEzVWVgP~`1v9~>f^HlA6QyiR2syWyMI0N!tX$-Y9qW{+8SuWSBT zHRsp!?em>K8_s<!^W5UN<++ciey|x#Jh1uC{P*F&gr`>G8c~v*pPQSSSHh5#S(Kt{ zXrWh-p39Y-pI4SzRFaya5a8#lkd~Q~Dld4%5~xEJq$4G<B-JXpC>2OC7#SEE>l&Er z8k&U|nphbbTbUYY8yHy`7+mC;vJOQ<ZhlH;S|x*_rKP2=p`os!QHY_X6$lw?8yEsL U2>aVj25MmNboFyt=akR{0E}i$-~a#s literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/6.png b/samples/cpp/shape_sample/6.png new file mode 100644 index 0000000000000000000000000000000000000000..e87cf4d2c45dfc9c6b98c939722ebe225a03ffa2 GIT binary patch literal 969 zcmV;)12+7LP)<h;3K|Lk000e1NJLTq00BS%008^}00000E|fsM00002VoOIv0063u zBQgL0010qNS#tmY07w7;07w8v$!k6U00P-bL_t(&-tE~zZlf?1fMHuiqKlEd>0)+V zV-}UTSS5Tkj^ZOQJESsrAce#>#{Q`?6IJ3#-Be$&-}AGL5+Tkw<BT)T_|IVn=9IM> z!N%H-IBBiz2`naP2`p#W0$9z^uv}O+vm@EaD+DDkj=>RDhZJ&wItvwrHHDupzlL&w z2Ii87io$Av9ZWh8o1-w>XZC4O9)`IVe=h862zABa@Y!j3Xc!dJt}4+9U<HKl%b}8m zZxsHb@F#^Y6kaG4>rwh0pnf=P9|%i!k{*WLGoYeZyP`MtK?pjxqE~xGFXE%HrO;$y z4R6C0=9i{s$NdfFE56>u+4`(7Tb~tX>x17G4&Uwht7Z)hb7>!kxwI|J=Jpbh%f^8% zg&l<>h3QUnp`oy+&|=t9m?}`=!jZyMfqd9eI8bOYG!%9ejuh?}D1?T>fkKO6OJPUh zK;d42q=jQ|;XvWe*b!(b>?s^6+?k}MkBq?hddJ{EVYEbX7-`EU4*yIu^&eZ?{b(ul zDw>4u;8hBC6uSMP=o6FBQ0Qh|rl4ELA_aR2-8Pab=(TVP+AqO|)(69y!iqvglS!>% zPocNHQfuh;V{#4sBO?L*<0@5gd<3|4Oct`G(5p|XkY0TrQb-K_I|svt!kR)wVWN;; z*4wm;_yfbV`}?fJE6h(@-zO8o%5|eNned+V(jJ_ThR|z^-YG-~y|j&!UlM9(+M9QQ zAeY-`H?Rq&xHiBBI&HBoS{bXOTOaLCXY;P))W=%|6G9Q@viwhPU>#2JI!xrX<~GUJ zZGr8`_er)Q6PsN2Ld6?!cWje|0#0!mn|w_x?O32+@8q<w1#r_OJH#eP#=Sz2@j<8` z4xy76lI5GDps=JvD+VvqKGf`as28ut0)sW#fO9lQAW8=AKp}p!JuQSO*ze-f2=zgJ z<)5IC2+oPTQ}{-bnc*vediwi8;f266{(1Zb*CQ(6Lf7L(0000cbVXQnV{dJ3Wo~o; zVrg<@EipMRaA$1_V{dMDWpZ?7WFSyZO(14zY-OG_p&kGL03~!qSaf7zbY(hYa%Ew3 zWdJfTF*7YNH!U$XR53I<GBY|fGb=DMIxsLSPL8br001R)MObuXVRU6WZEs|0W_bWH rIXO8kF)=MMGE^}+Ix#sqF*7SLF*-0X6Hh*q00000NkvXXu0mjfk_e>J literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/7.png b/samples/cpp/shape_sample/7.png new file mode 100644 index 0000000000000000000000000000000000000000..b12aa52d2bebfbbf6235bc66e586a54bf2f7df3c GIT binary patch literal 874 zcmeAS@N?(olHy`uVBq!ia0y~yV4MJC-(>_640}r@(}5IIlDE4H1H-CbWg{Suv%n*= z7)X17vD?XPJD`eUPZ!6KjC*fy+7_)b5MW8rU~=kPu=qvBqYRl=WdWgpYpb`%UErS; z_<dK|TL)pw8|Mvdn<IU$Z_T>8Ds*+&+NiC+JS_JYCwvH(onc_NN>b2n<yyX~`J$I9 z!nCiliySBr{?~2iv2|wCiX%4DPAn3NR;o%-UC3XoAyn$vz4nFXY^A{9veZROW5l9P z?RA(kg}qTjv!5w2XdcU=HTx%QG?UO4NHy?N`jnue$iGay(_)6B%A*9gi5<;`N|qUl zyl(GYWX>#^IMY@?(;!Uw=|PL$9Gjb!MIV;T*>tPUvPtV^#iq|s<QkJthA@9GUDrK_ zf&HZC)``-Rj1vW)C}<jXcyL^*_FCX3bg5ZM>4l>bQ<;#k&bpR}c?*;TEH*f>%{w5; zZuId;P0Nk_UU%e`uiLVTs~_C{Y1hxk(Uo`qs5j-jiSRqL<<p+e55rHp&Ip^Qo>p<F z<o};_AM!SB@UGN5vzxPa>zwHl#S_F&9GpBi{)u>5{nL|&z8igR`G0u(=aipE)+YUD zG&Hzb=di4N($xP}pO`GwJ?~6Rn^$5|`Oy0l<K(}4mMuy*S5TiW82<jizyF!sM^>fm zw%f#Jv@&Ji389WzpXB^f-fj3d-C@^`Cwco0nNGFaCAs(Kd;TT;SAw4^^yl6eQhHZE z=fW%j&CL@gp8sE}JRw8Ctlma1Kdnkl*5R3-uBy(;JC#a&Y8UzDF8OA5phb~KVdkNk zUlb%4pKnO{uw6Rw?a~1GwR4Ypi1Xf7%hsRHEuGp>TKVslrPulEaXB`2)9wFVbD0-c z8=v=*vGM6k$MuIUeQ7wbN#m7x#7apon>LnxHhZ#Cn5$I|l->s?n7Wn!82t?@_!j(_ zUj$5tx+Sg=CCT}@xv6<23<ar0#rb)OIq}JfMY?8s1?jn5$@zI@sYNBJDGC98z6xoX zIjJ?}rhkAMRY4k45=&C8l8aJ-6oZk0p|P%kxvrsEh@pv<k+GGDg|>l_m4Sht=dExQ y4Y~O#nQ4^_hL)C=x`u|jhDITVmR3MyY@%&o2-Gm;EPDb_1B0ilpUXO@geCx+-fK+& literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/8.png b/samples/cpp/shape_sample/8.png new file mode 100644 index 0000000000000000000000000000000000000000..52fff13d41f67f7859caf61b526f02909b09adf1 GIT binary patch literal 851 zcmeAS@N?(olHy`uVBq!ia0y~yV4MJCUt|Ol45zl=lmSvqN#5=*3=FG!m5qQr&H|6f zVj%4S#%?FG?SLv`JY5_^GVZ;-={sq$0T1hg0G)_m9Cls(X?@26EttH;R#s@u^-B_d z`7(3HtXDHPUHe<QM%}S)M@-e%S6}(B26#ov*3Un^c%2T%)7Z_?Q)Wj8{N-7jaI2F& zNbG(0lug#wJEyyTQs3zJP4iUSQ4N8`Gd&gD&IBqPV^V39n3&pm#)D_lfo!ubmSr+7 zOu>ChjhfweA5ZCyd3PY;)M6)xQ<H(%QBmRP3V{hlK^z{I+AK@faN0g~RP8R;QAnM; zVxq5B;BS7bi?cedE<T%IvS_Xj^P-fnfX=%-6FRERoRsu>9mDRcY-MPg@8Yv0Q_W|| z)iZfb$CdcfK21)VYOdcHF_%MSXXn-34x488v{(pOuj4<|@yI^><NVdCK5@Da&t0AH zK>g0nET^0&6AgZsD#RS&^{jI$i~n;syhSF(w`u-{M$!6&=N{jL!{#V`QMq4US2ZdB z(7HNTChv9W9w{gPg`aq)zIy(dbUpEDF@Kl*S}^6#-ODN8g#W+Y`p;zA-MgDptldw% zyFTA*+W#p}?ESwTe{@Yn^2zVIR|==U_wL(us3-gXzR!;*vZ(C4r_gr%r-zaK^9wbV zj`^OqwXe)oec$rR)$RS%-`!WPtJJvHol}z8C(r!J>O@hdx9Bf@&q(FfUP^LZAyFwW z%3YqOXe%&2R~4JHIEM4=#X=*EBWF$OR;^3kmumgUdvn_M=bCHNIllf-o0s!e)4cgf z)?&84C02^nvF}7*|DUBP^eRz3>BskJ`Mu`t`cK_$S7aS}Fn{6RLmT83wwnGt>5)>i zqVdF!h?~Ea_5#zz)z#ta<M!6Au47cU7cNQu*s2LkaJnU~5hcm_xw)x%B@6|rMaB7f zi8=AfiAB2RdIjmZT*>))WvN9asVNEpe!dE6nK`L{g92^?HL8L%rX-f6S|t~y0x1R~ z14Cn719M$Nvk*fQD<fko6ANtvBP#;~J<nU=C>nC}Q!>*k84N8gEp-hIbq$R|3@xpI b$k;^Nz!0cm%31aVpaup{S3j3^P6<r_Wvp%{ literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_sample/9.png b/samples/cpp/shape_sample/9.png new file mode 100644 index 0000000000000000000000000000000000000000..a54aa3ab78d0c1927ba79586ffb699adbb16f7b0 GIT binary patch literal 1204 zcmV;l1WWsgP)<h;3K|Lk000e1NJLTq009610096200000bP1Kq00002VoOIv0063u zBQgL0010qNS#tmY07w7;07w8v$!k6U00Y8FL_t(&-tE~<Zrvad0AP?8UUCF(QeP|* zj<V>oN9a-NsEaIf1)^TSOBNQC!8RU#2432zYOBX84POm24B))S*uCA`z1`cr{bOx% z&dqc;ccHB$a7WWbzdC_is{JDNJKG$98bn}VI}fNmVobr-2%#Rxko7y$nqqC5`3U(K zyy(wXM3f*MV=6(q?3Ml`S3?b$_7u`d)TWS3qS}up!Rz44#8?MgdyaUP^*N$hDgrF+ zAg(Y53Yc~fR~Ui<XBd(Kt`lX9Q;}e4M|sA{X|T4_a3*4;!FC%EG_ajK9hvHasR8Dj zfNFr{CeY~L7o!Jw3#@+z3@u>p-~++mu-w02v>&i>rUtle^U4Oe?En#Uzx|+NuzS*W z&+6`J-@h;DRL(cvS!AZpTd#X>c5mM9-QB<asW{{N0};mc2GV_p>AnRKaq1qJJ99wq za&=&OK=;5kh5qZLaBr<F0MO-n;Wt+4wCDlZSP1}23G92oR0A#n^W<I|i3(uuf+Q6> z`2(W@R+Ryj0O{7L0Dk+#+Wg{74cNFrUY(J+9wP7q`TA7u#ccvFCk7nA4;sK8`L+X7 z2Y#+!ef?M(s2z+2;0Ks|kZT*f02pw^9AFDT1`z&P0(StTDgd4dWdvZ`N6djaz;^(T zHNa0`eM;NFuVo!<0N!@s+CVD`2&hL1HJ5?$0%`{{1$>`?TrD|R0~il5HNj^9ItAha z69M!AeBYDXa9Jx11^~}FU?sq1odN%!pP}Y{74R^88;Bhk3fQn-xU3Vv0#G?{6<`x! zFW>@rLM2rNkLa9p2?*;3$wYsc+X`6FrIdrP;(nACkN{R1z#%uqRPr476&LW3jRH>x zI1@3X01D3dX^5BzpLKo!i@+EY5x|xMGaz7gz%CGecP3)21BSr*D*z6~1BeQMh~wsy zC4jKtzCD3X!DC2_fAVtf-8vYq&p=85Kk{=e{9#iC>>-w94WL8L!CjXFe8_p+1b$I` zGD{GP9_nQbHI#z&0$T-KB@2&-<rp!5YkX@1iGhI=Jc9kU5!dc70n5P(3z`53++I;= zSb7YUW5YWD_c@z#FyouoXI&M5A@C`LCJ=zzW=cK`b|sK&0W<=djY|p`2D12Q-oY;; zm@B}BZE^xpd|a)zFtKAbDKJMc7QhFsn!pE*!B7AnGzPr(4Mcd4>w^89^pDadm|!~t zfP|=p0OkO)>}PUg`{V`j4TvxuFz60o>;u4_pON?y<`%FgaA<s46cWfC*?%PPf&F*w zCz{Ftb&7G>uK)l5D0D?wbYpLAZDnqB0B>V-Z*X;UEjTW4XKe~&Z*F#Fa&%>6AW%+C zAZBT7WvI@YfB*mhC3HntbYx+4WjbSWWnpw>05UK!Gc7PTEipD!F*G_dGdeUlD=;!T zFfcAj+E)Mo03~!qSaf7zbY(hiZ)9m^c>pmvIXNvcF)cANR53X^F*!OjGb=DLIxsL% SYB}xz0000<MNUMnLSTa1wC^1N literal 0 HcmV?d00001 diff --git a/samples/cpp/shape_transformation.cpp b/samples/cpp/shape_transformation.cpp new file mode 100644 index 000000000..aa83cd7d1 --- /dev/null +++ b/samples/cpp/shape_transformation.cpp @@ -0,0 +1,74 @@ +/* + * shape_context.cpp -- Shape context demo for shape matching + */ + +#include "opencv2/shape.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/features2d/features2d.hpp" +#include "opencv2/nonfree/nonfree.hpp" +#include <opencv2/core/utility.hpp> +#include <iostream> +#include <string> + +using namespace std; +using namespace cv; + +static void help() +{ + printf("\nThis program demonstrates how to use common interface for shape transformers\n" + "Call\n" + "shape_transformation [image1] [image2]\n"); +} + +int main(int argc, char** argv) +{ + help(); + Mat img1 = imread(argv[1], IMREAD_GRAYSCALE); + Mat img2 = imread(argv[2], IMREAD_GRAYSCALE); + if(img1.empty() || img2.empty() || argc<2) + { + printf("Can't read one of the images\n"); + return -1; + } + + // detecting keypoints + SurfFeatureDetector detector(5000); + vector<KeyPoint> keypoints1, keypoints2; + detector.detect(img1, keypoints1); + detector.detect(img2, keypoints2); + + // computing descriptors + SurfDescriptorExtractor extractor; + Mat descriptors1, descriptors2; + extractor.compute(img1, keypoints1, descriptors1); + extractor.compute(img2, keypoints2, descriptors2); + + // matching descriptors + BFMatcher matcher(NORM_L2); + vector<DMatch> matches; + matcher.match(descriptors1, descriptors2, matches); + + // drawing the results + namedWindow("matches", 1); + Mat img_matches; + drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches); + imshow("matches", img_matches); + + // extract points + vector<Point2f> pts1, pts2; + for (size_t ii=0; ii<keypoints1.size(); ii++) + pts1.push_back( keypoints1[ii].pt ); + for (size_t ii=0; ii<keypoints2.size(); ii++) + pts2.push_back( keypoints2[ii].pt ); + + // Apply TPS + Ptr<ThinPlateSplineShapeTransformer> mytps = createThinPlateSplineShapeTransformer(25000); //TPS with a relaxed constraint + mytps->estimateTransformation(pts1, pts2, matches); + mytps->warpImage(img2, img2); + + imshow("Tranformed", img2); + waitKey(0); + + return 0; +} From fe7bab499fb43625cc59837ada11f19779131aa6 Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Mon, 23 Sep 2013 21:24:27 +0200 Subject: [PATCH 02/12] Corrections for compiling issues in Win, And and Doc --- modules/shape/doc/histogram_cost_matrix.rst | 2 +- modules/shape/doc/shape_distances.rst | 6 ++--- modules/shape/include/opencv2/shape/emdL1.hpp | 2 +- modules/shape/src/aff_trans.cpp | 2 +- modules/shape/src/emdL1.cpp | 15 ++++++------- modules/shape/src/emdL1_def.hpp | 1 - modules/shape/src/haus_dis.cpp | 6 ++--- modules/shape/src/hist_cost.cpp | 8 +++---- modules/shape/src/sc_dis.cpp | 22 +++++++++---------- modules/shape/src/tps_trans.cpp | 4 ++-- modules/shape/test/test_hausdorff.cpp | 6 ++--- modules/shape/test/test_shape.cpp | 2 +- samples/cpp/shape_example.cpp | 2 +- samples/cpp/shape_transformation.cpp | 2 +- 14 files changed, 37 insertions(+), 43 deletions(-) diff --git a/modules/shape/doc/histogram_cost_matrix.rst b/modules/shape/doc/histogram_cost_matrix.rst index 9f6804dd0..8c338dbee 100644 --- a/modules/shape/doc/histogram_cost_matrix.rst +++ b/modules/shape/doc/histogram_cost_matrix.rst @@ -70,7 +70,7 @@ An Chi based cost extraction. :: CV_EXPORTS_W Ptr<HistogramCostExtractor> createChiHistogramCostExtractor(int nDummies=25, float defaultCost=0.2); EMDL1HistogramCostExtractor -------------------------- +--------------------------- .. ocv:class:: EMDL1HistogramCostExtractor : public HistogramCostExtractor An EMD-L1 based cost extraction. :: diff --git a/modules/shape/doc/shape_distances.rst b/modules/shape/doc/shape_distances.rst index c671e97ed..6a6ce9532 100644 --- a/modules/shape/doc/shape_distances.rst +++ b/modules/shape/doc/shape_distances.rst @@ -1,11 +1,11 @@ -Shape Distance and Common Interfaces +Shape Distance and Common Interfaces ==================================== .. highlight:: cpp Shape Distance algorithms in OpenCV are derivated from a common interface that allows you to switch between them in a practical way for solving the same problem with different methods. -Thus, all objects that implement shape distance measures inherit the +Thus, all objects that implement shape distance measures inherit the :ocv:class:`ShapeDistanceExtractor` interface. @@ -123,7 +123,7 @@ ShapeContextDistanceExtractor::setShapeContextWeight ---------------------------------------------------- Set the weight of the shape context distance in the final value of the shape distance. The shape context distance between two shapes is defined as the symmetric sum of shape -context matching costs over best matching points. +context matching costs over best matching points. The final value of the shape distance is a user-defined linear combination of the shape context distance, an image appearance distance, and a bending energy. diff --git a/modules/shape/include/opencv2/shape/emdL1.hpp b/modules/shape/include/opencv2/shape/emdL1.hpp index 400d26b26..74c734a51 100644 --- a/modules/shape/include/opencv2/shape/emdL1.hpp +++ b/modules/shape/include/opencv2/shape/emdL1.hpp @@ -55,4 +55,4 @@ CV_EXPORTS float EMDL1(InputArray signature1, InputArray signature2); }//namespace cv -#endif +#endif diff --git a/modules/shape/src/aff_trans.cpp b/modules/shape/src/aff_trans.cpp index c416b8205..e3d41d0ea 100644 --- a/modules/shape/src/aff_trans.cpp +++ b/modules/shape/src/aff_trans.cpp @@ -88,7 +88,7 @@ public: virtual void read(const FileNode& fn) { CV_Assert( (String)fn["name"] == name_ ); - fullAffine = (int)fn["affine_type"]; + fullAffine = (bool)int(fn["affine_type"]); } private: diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp index cdd903e1a..423f8ef5d 100644 --- a/modules/shape/src/emdL1.cpp +++ b/modules/shape/src/emdL1.cpp @@ -41,11 +41,11 @@ //M*/ /* - * Implementation of an optimized EMD for histograms based in - * the papers "EMD-L1: An efficient and Robust Algorithm - * for comparing histogram-based descriptors", by Haibin Ling and + * Implementation of an optimized EMD for histograms based in + * the papers "EMD-L1: An efficient and Robust Algorithm + * for comparing histogram-based descriptors", by Haibin Ling and * Kazunori Okuda; and "The Earth Mover's Distance is the Mallows - * Distance: Some Insights from Statistics", by Elizaveta Levina and + * Distance: Some Insights from Statistics", by Elizaveta Levina and * Peter Bickel, based on HAIBIN LING AND KAZUNORI OKADA implementation. */ @@ -393,9 +393,9 @@ bool EmdL1::greedySolution3() //- determine which direction to move, either right or upward dFlow = D[i1][i2][i3]; - f1 = i1<(binsDim1-1)?fabs(dFlow+d1s[i1+1]):VHIGH; - f2 = i2<(binsDim2-1)?fabs(dFlow+d2s[i2+1]):VHIGH; - f3 = i3<(binsDim3-1)?fabs(dFlow+d3s[i3+1]):VHIGH; + f1 = i1<(binsDim1-1)?(float)fabs(dFlow+d1s[i1+1]):VHIGH; + f2 = i2<(binsDim2-1)?(float)fabs(dFlow+d2s[i2+1]):VHIGH; + f3 = i3<(binsDim3-1)?(float)fabs(dFlow+d3s[i3+1]):VHIGH; if(f1<f2 && f1<f3) { @@ -791,4 +791,3 @@ float cv::EMDL1(InputArray _signature1, InputArray _signature2) EmdL1 emdl1; return emdl1.getEMDL1(signature1, signature2); } - diff --git a/modules/shape/src/emdL1_def.hpp b/modules/shape/src/emdL1_def.hpp index fae473344..2a75ab4d4 100644 --- a/modules/shape/src/emdL1_def.hpp +++ b/modules/shape/src/emdL1_def.hpp @@ -139,4 +139,3 @@ private: int m_iFrom; int m_iTo; }; - diff --git a/modules/shape/src/haus_dis.cpp b/modules/shape/src/haus_dis.cpp index 5e16a699c..a2c555ed7 100644 --- a/modules/shape/src/haus_dis.cpp +++ b/modules/shape/src/haus_dis.cpp @@ -88,7 +88,7 @@ public: { CV_Assert( (String)fn["name"] == name_ ); distanceFlag = (int)fn["distance"]; - rankProportion = (int)fn["rank"]; + rankProportion = (float)fn["rank"]; } private: @@ -111,7 +111,7 @@ static float _apply(const Mat &set1, const Mat &set2, int distType, double propR for (int c=0; c<disMat.cols; c++) { Point2f diff = set1.at<Point2f>(0,r)-set2.at<Point2f>(0,c); - disMat.at<float>(r,c) = norm(Mat(diff), distType); + disMat.at<float>(r,c) = (float)norm(Mat(diff), distType); } } @@ -147,5 +147,3 @@ Ptr <HausdorffDistanceExtractor> createHausdorffDistanceExtractor(int distanceFl } } // cv - - diff --git a/modules/shape/src/hist_cost.cpp b/modules/shape/src/hist_cost.cpp index 67e4063e6..4e18687ad 100644 --- a/modules/shape/src/hist_cost.cpp +++ b/modules/shape/src/hist_cost.cpp @@ -156,7 +156,7 @@ void NormHistogramCostExtractorImpl::buildCostMatrix(InputArray _descriptors1, I if (i<scd1.rows && j<scd2.rows) { Mat columnDiff = scd1.row(i)-scd2.row(j); - costMatrix.at<float>(i,j)=norm(columnDiff, flag); + costMatrix.at<float>(i,j)=(float)norm(columnDiff, flag); } else { @@ -288,11 +288,11 @@ void EMDHistogramCostExtractorImpl::buildCostMatrix(InputArray _descriptors1, In sig2.col(0)=scd2.row(j).t(); for (int k=0; k<sig1.rows; k++) { - sig1.at<float>(k,1)=k; + sig1.at<float>(k,1)=float(k); } for (int k=0; k<sig2.rows; k++) { - sig2.at<float>(k,1)=k; + sig2.at<float>(k,1)=float(k); } costMatrix.at<float>(i,j) = cv::EMD(sig1, sig2, flag); @@ -543,5 +543,3 @@ Ptr <HistogramCostExtractor> createEMDL1HistogramCostExtractor(int nDummies, flo } } // cv - - diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp index 98edc56b1..e41efc229 100644 --- a/modules/shape/src/sc_dis.cpp +++ b/modules/shape/src/sc_dis.cpp @@ -42,7 +42,7 @@ /* * Implementation of the paper Shape Matching and Object Recognition Using Shape Contexts - * Belongie et al., 2002 by Juan Manuel Perez for GSoC 2013. + * Belongie et al., 2002 by Juan Manuel Perez for GSoC 2013. */ #include "precomp.hpp" //#include "opencv2/highgui.hpp" @@ -176,7 +176,7 @@ protected: { for (int j=0; j<contourMat.cols; j++) { - disMatrix.at<float>(i,j) = norm( cv::Mat(contourMat.at<cv::Point2f>(0,i)-contourMat.at<cv::Point2f>(0,j)), cv::NORM_L2 ); + disMatrix.at<float>(i,j) = (float)norm( cv::Mat(contourMat.at<cv::Point2f>(0,i)-contourMat.at<cv::Point2f>(0,j)), cv::NORM_L2 ); if (_meanDistance<0) { if (queryInliers.size()>0) @@ -193,7 +193,7 @@ protected: if (_meanDistance<0) { - meanDistance=mean(disMatrix, mask)[0]; + meanDistance=(float)mean(disMatrix, mask)[0]; } else { @@ -239,7 +239,7 @@ protected: float refAngle = atan2(refPt.y, refPt.x); angleMatrix.at<float>(i,j) -= refAngle; } - angleMatrix.at<float>(i,j) = fmod(angleMatrix.at<float>(i,j)+FLT_EPSILON,2*CV_PI)+CV_PI; + angleMatrix.at<float>(i,j) = float(fmod(double(angleMatrix.at<float>(i,j)+(double)FLT_EPSILON),2*CV_PI)+CV_PI); //angleMatrix.at<float>(i,j) = 1+floor( angleMatrix.at<float>(i,j)*nAngularBins/(2*CV_PI) ); } } @@ -426,7 +426,7 @@ protected: for (j = 0; j < costMatrix.rows; j++) { d[j] = costMatrix.at<float>(freerow,j) - v[j]; - pred[j] = freerow; + pred[j] = float(freerow); collist[j] = j; // init column list. } @@ -479,7 +479,7 @@ protected: v2 = costMatrix.at<float>(i,j) - v[j] - h; if (v2 < d[j]) { - pred[j] = i; + pred[j] = float(i); if (v2 == min) { if (colsol[j] < 0) @@ -511,7 +511,7 @@ protected: // reset row and column assignments along the alternating path. do { - i = pred[endofpath]; + i = int(pred[endofpath]); colsol[endofpath] = i; j1 = endofpath; endofpath = rowsol[i]; @@ -526,7 +526,7 @@ protected: { double minval; minMaxIdx(trueCostMatrix.row(nrow), &minval); - leftcost+=minval; + leftcost+=float(minval); } leftcost /= trueCostMatrix.rows; @@ -535,7 +535,7 @@ protected: { double minval; minMaxIdx(trueCostMatrix.col(ncol), &minval); - rightcost+=minval; + rightcost+=float(minval); } rightcost /= trueCostMatrix.cols; @@ -815,7 +815,7 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In { float xx = sset1.at<Point2f>(0,pt).x; float yy = sset1.at<Point2f>(0,pt).y; - float val = std::exp( -( (xx-jj)*(xx-jj) + (yy-ii)*(yy-ii) )/(2*sigma*sigma) ) / (sigma*sigma*2*CV_PI); + float val = float(std::exp( -float( (xx-jj)*(xx-jj) + (yy-ii)*(yy-ii) )/(2*sigma*sigma) ) / (sigma*sigma*2*CV_PI)); gaussWindow.at<float>(ii,jj) += val; } } @@ -831,7 +831,7 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In appIm.at<float>(ii,jj) = elema*elemb; } } - iAppearance = cv::sum(appIm)[0]/sset1.cols; + iAppearance = float(cv::sum(appIm)[0]/sset1.cols); } sDistance = matcher.getMatchingCost(); diff --git a/modules/shape/src/tps_trans.cpp b/modules/shape/src/tps_trans.cpp index dd839d670..b841567dc 100644 --- a/modules/shape/src/tps_trans.cpp +++ b/modules/shape/src/tps_trans.cpp @@ -104,7 +104,7 @@ protected: String name_; }; -static double distance(Point2f p, Point2f q) +static float distance(Point2f p, Point2f q) { Point2f diff = p - q; float norma = diff.x*diff.x + diff.y*diff.y;// - 2*diff.x*diff.y; @@ -237,7 +237,7 @@ void ThinPlateSplineShapeTransformerImpl::estimateTransformation(InputArray _pts { if (i==j) { - matK.at<float>(i,j)=regularizationParameter; + matK.at<float>(i,j)=float(regularizationParameter); } else { diff --git a/modules/shape/test/test_hausdorff.cpp b/modules/shape/test/test_hausdorff.cpp index dfff6bcb3..d1fdf3881 100644 --- a/modules/shape/test/test_hausdorff.cpp +++ b/modules/shape/test/test_hausdorff.cpp @@ -87,7 +87,7 @@ vector<Point2f> CV_HaussTest::normalizeContour(const vector<Point> &contour) else { disMat.at<float>(ii,jj)= - fabs(contour[ii].x*contour[jj].x)+fabs(contour[ii].y*contour[jj].y); + float(fabs(double(contour[ii].x*contour[jj].x)))+float(fabs(double(contour[ii].y*contour[jj].y))); } } meanpt.x+=contour[ii].x; @@ -95,7 +95,7 @@ vector<Point2f> CV_HaussTest::normalizeContour(const vector<Point> &contour) } meanpt.x/=contour.size(); meanpt.y/=contour.size(); - meanVal=cv::mean(disMat)[0]; + meanVal=float(cv::mean(disMat)[0]); for (size_t ii=0; ii<contour.size(); ii++) { output[ii].x = (contour[ii].x-meanpt.x)/meanVal; @@ -274,7 +274,7 @@ void CV_HaussTest::run(int /* */) { mpegTest(); displayMPEGResults(); - ts->set_failed_test_info(cvtest::TS::OK); + ts->set_failed_test_info(cvtest::TS::OK); } TEST(Hauss, regression) { CV_HaussTest test; test.safe_run(); } diff --git a/modules/shape/test/test_shape.cpp b/modules/shape/test/test_shape.cpp index 81d67f6ac..641542007 100644 --- a/modules/shape/test/test_shape.cpp +++ b/modules/shape/test/test_shape.cpp @@ -81,7 +81,7 @@ CV_ShapeTest::~CV_ShapeTest() } vector <Point2f> CV_ShapeTest::convertContourType(const Mat& currentQuery, int n) -{ +{ vector<vector<Point> > _contoursQuery; vector <Point2f> contoursQuery; findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE); diff --git a/samples/cpp/shape_example.cpp b/samples/cpp/shape_example.cpp index 921330e89..d723a73e5 100644 --- a/samples/cpp/shape_example.cpp +++ b/samples/cpp/shape_example.cpp @@ -75,7 +75,7 @@ int main(int argc, char** argv) imshow("QUERY", queryToShow); moveWindow("TEST", 0,0); vector<Point> contQuery = simpleContour(query); - int bestMatch; + int bestMatch = 0; float bestDis=FLT_MAX; for ( int ii=1; ii<=20; ii++ ) { diff --git a/samples/cpp/shape_transformation.cpp b/samples/cpp/shape_transformation.cpp index aa83cd7d1..abd7eab7b 100644 --- a/samples/cpp/shape_transformation.cpp +++ b/samples/cpp/shape_transformation.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) BFMatcher matcher(NORM_L2); vector<DMatch> matches; matcher.match(descriptors1, descriptors2, matches); - + // drawing the results namedWindow("matches", 1); Mat img_matches; From ead966709d78127848e452cf5ec242c92c98b082 Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Tue, 24 Sep 2013 18:26:19 +0200 Subject: [PATCH 03/12] Remove ~ file --- modules/shape/doc/shape.rst~ | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 modules/shape/doc/shape.rst~ diff --git a/modules/shape/doc/shape.rst~ b/modules/shape/doc/shape.rst~ deleted file mode 100644 index b18968556..000000000 --- a/modules/shape/doc/shape.rst~ +++ /dev/null @@ -1,15 +0,0 @@ -***** -shape -***** - -The module contains algorithms that embed a notion of shape distance. -These algorithms may be used for shape matching and retrieval, or shape -comparison. - -.. toctree:: - :maxdepth: 2 - - shape_distances - shape_transformers - histogram_cost_matrix - emdL1 From 5a1e9876fcec863ae454b0f540a01f0ffe127efe Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Tue, 24 Sep 2013 22:21:31 +0200 Subject: [PATCH 04/12] Corrected compilation errors Win --- modules/shape/src/aff_trans.cpp | 4 ++-- modules/shape/src/emdL1.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/shape/src/aff_trans.cpp b/modules/shape/src/aff_trans.cpp index e3d41d0ea..02f290fec 100644 --- a/modules/shape/src/aff_trans.cpp +++ b/modules/shape/src/aff_trans.cpp @@ -82,13 +82,13 @@ public: virtual void write(FileStorage& fs) const { fs << "name" << name_ - << "affine_type" << fullAffine; + << "affine_type" << int(fullAffine); } virtual void read(const FileNode& fn) { CV_Assert( (String)fn["name"] == name_ ); - fullAffine = (bool)int(fn["affine_type"]); + fullAffine = int(fn["affine_type"])?true:false; } private: diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp index 423f8ef5d..1c09ae40a 100644 --- a/modules/shape/src/emdL1.cpp +++ b/modules/shape/src/emdL1.cpp @@ -48,7 +48,7 @@ * Distance: Some Insights from Statistics", by Elizaveta Levina and * Peter Bickel, based on HAIBIN LING AND KAZUNORI OKADA implementation. */ - + #include "precomp.hpp" #include "emdL1_def.hpp" @@ -393,9 +393,9 @@ bool EmdL1::greedySolution3() //- determine which direction to move, either right or upward dFlow = D[i1][i2][i3]; - f1 = i1<(binsDim1-1)?(float)fabs(dFlow+d1s[i1+1]):VHIGH; - f2 = i2<(binsDim2-1)?(float)fabs(dFlow+d2s[i2+1]):VHIGH; - f3 = i3<(binsDim3-1)?(float)fabs(dFlow+d3s[i3+1]):VHIGH; + f1 = i1<(binsDim1-1)?(float)fabs(float(dFlow+d1s[i1+1])):VHIGH; + f2 = i2<(binsDim2-1)?(float)fabs(float(dFlow+d2s[i2+1])):VHIGH; + f3 = i3<(binsDim3-1)?(float)fabs(float(dFlow+d3s[i3+1])):VHIGH; if(f1<f2 && f1<f3) { From 752d88f5d2d656a074e3bd21be1481ff7c6f419d Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Tue, 24 Sep 2013 23:05:29 +0200 Subject: [PATCH 05/12] Compile error fix for Win --- modules/shape/src/emdL1.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp index 1c09ae40a..c07e92f34 100644 --- a/modules/shape/src/emdL1.cpp +++ b/modules/shape/src/emdL1.cpp @@ -393,9 +393,9 @@ bool EmdL1::greedySolution3() //- determine which direction to move, either right or upward dFlow = D[i1][i2][i3]; - f1 = i1<(binsDim1-1)?(float)fabs(float(dFlow+d1s[i1+1])):VHIGH; - f2 = i2<(binsDim2-1)?(float)fabs(float(dFlow+d2s[i2+1])):VHIGH; - f3 = i3<(binsDim3-1)?(float)fabs(float(dFlow+d3s[i3+1])):VHIGH; + f1 = i1<(binsDim1-1)?(float)fabs(double(dFlow+d1s[i1+1])):VHIGH; + f2 = i2<(binsDim2-1)?(float)fabs(double(dFlow+d2s[i2+1])):VHIGH; + f3 = i3<(binsDim3-1)?(float)fabs(double(dFlow+d3s[i3+1])):VHIGH; if(f1<f2 && f1<f3) { From aa73dafbbb91c729e1a22042a046ca21145eba50 Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Wed, 25 Sep 2013 00:09:59 +0200 Subject: [PATCH 06/12] 2 Compile error fix for Win --- modules/shape/src/emdL1.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp index c07e92f34..db947b528 100644 --- a/modules/shape/src/emdL1.cpp +++ b/modules/shape/src/emdL1.cpp @@ -286,7 +286,7 @@ bool EmdL1::greedySolution2() for(r=0; r<binsDim1; r++) { dFlow = D[r][c]; - bUpward = (r<binsDim1-1) && (fabs(dFlow+d2s[c+1]) > fabs(dFlow+d1s[r+1])); // Move upward or right + bUpward = (r<binsDim1-1) && (std::abs(dFlow+d2s[c+1]) > std::abs(dFlow+d1s[r+1])); // Move upward or right // modify basic variables, record BV and related values if(bUpward) @@ -308,7 +308,7 @@ bool EmdL1::greedySolution2() d2s[c+1] += dFlow; // auxilary matrix maintanence } pBV->pParent->pChild = pBV; - pBV->flow = fabs(dFlow); + pBV->flow = std::abs(dFlow); pBV->iDir = dFlow>0; // 1:outward, 0:inward } @@ -320,7 +320,7 @@ bool EmdL1::greedySolution2() pBV = &(m_EdgesUp[r][c]); D[r+1][c] += dFlow; // auxilary matrix maintanence pBV->pParent->pChild= pBV; - pBV->flow = fabs(dFlow); + pBV->flow = std::abs(dFlow); pBV->iDir = dFlow>0; // 1:outward, 0:inward } return true; @@ -393,9 +393,9 @@ bool EmdL1::greedySolution3() //- determine which direction to move, either right or upward dFlow = D[i1][i2][i3]; - f1 = i1<(binsDim1-1)?(float)fabs(double(dFlow+d1s[i1+1])):VHIGH; - f2 = i2<(binsDim2-1)?(float)fabs(double(dFlow+d2s[i2+1])):VHIGH; - f3 = i3<(binsDim3-1)?(float)fabs(double(dFlow+d3s[i3+1])):VHIGH; + f1 = (i1<(binsDim1-1))?std::abs(dFlow+d1s[i1+1]):VHIGH; + f2 = (i2<(binsDim2-1))?std::abs(dFlow+d2s[i2+1]):VHIGH; + f3 = (i3<(binsDim3-1))?std::abs(dFlow+d3s[i3+1]):VHIGH; if(f1<f2 && f1<f3) { @@ -422,7 +422,7 @@ bool EmdL1::greedySolution3() d3s[i3+1] += dFlow; } - pBV->flow = fabs(dFlow); + pBV->flow = std::abs(dFlow); pBV->iDir = dFlow>0; // 1:outward, 0:inward pBV->pParent->pChild= pBV; } From 1bf4298251231a38b99e7f342b3f7a928cf46366 Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Wed, 25 Sep 2013 14:13:42 +0200 Subject: [PATCH 07/12] Macro removal --- modules/shape/src/emdL1.cpp | 18 +++++++++--------- modules/shape/src/emdL1_def.hpp | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp index db947b528..b8381de5a 100644 --- a/modules/shape/src/emdL1.cpp +++ b/modules/shape/src/emdL1.cpp @@ -51,7 +51,7 @@ #include "precomp.hpp" #include "emdL1_def.hpp" - +#include <limits> /****************************************************************************************\ * EMDL1 Class * @@ -286,7 +286,7 @@ bool EmdL1::greedySolution2() for(r=0; r<binsDim1; r++) { dFlow = D[r][c]; - bUpward = (r<binsDim1-1) && (std::abs(dFlow+d2s[c+1]) > std::abs(dFlow+d1s[r+1])); // Move upward or right + bUpward = (r<binsDim1-1) && (fabs(dFlow+d2s[c+1]) > fabs(dFlow+d1s[r+1])); // Move upward or right // modify basic variables, record BV and related values if(bUpward) @@ -308,7 +308,7 @@ bool EmdL1::greedySolution2() d2s[c+1] += dFlow; // auxilary matrix maintanence } pBV->pParent->pChild = pBV; - pBV->flow = std::abs(dFlow); + pBV->flow = fabs(dFlow); pBV->iDir = dFlow>0; // 1:outward, 0:inward } @@ -320,7 +320,7 @@ bool EmdL1::greedySolution2() pBV = &(m_EdgesUp[r][c]); D[r+1][c] += dFlow; // auxilary matrix maintanence pBV->pParent->pChild= pBV; - pBV->flow = std::abs(dFlow); + pBV->flow = fabs(dFlow); pBV->iDir = dFlow>0; // 1:outward, 0:inward } return true; @@ -393,9 +393,9 @@ bool EmdL1::greedySolution3() //- determine which direction to move, either right or upward dFlow = D[i1][i2][i3]; - f1 = (i1<(binsDim1-1))?std::abs(dFlow+d1s[i1+1]):VHIGH; - f2 = (i2<(binsDim2-1))?std::abs(dFlow+d2s[i2+1]):VHIGH; - f3 = (i3<(binsDim3-1))?std::abs(dFlow+d3s[i3+1]):VHIGH; + f1 = (i1<(binsDim1-1))?fabs(dFlow+d1s[i1+1]):std::numeric_limits<float>::max(); + f2 = (i2<(binsDim2-1))?fabs(dFlow+d2s[i2+1]):std::numeric_limits<float>::max(); + f3 = (i3<(binsDim3-1))?fabs(dFlow+d3s[i3+1]):std::numeric_limits<float>::max(); if(f1<f2 && f1<f3) { @@ -422,7 +422,7 @@ bool EmdL1::greedySolution3() d3s[i3+1] += dFlow; } - pBV->flow = std::abs(dFlow); + pBV->flow = fabs(dFlow); pBV->iDir = dFlow>0; // 1:outward, 0:inward pBV->pParent->pChild= pBV; } @@ -679,7 +679,7 @@ void EmdL1::findNewSolution() void EmdL1::findLoopFromEnterBV() { // Initialize Leaving-BV edge - float minFlow = VHIGH; + float minFlow = std::numeric_limits<float>::max(); cvPEmdEdge pE = NULL; int iLFlag = 0; // 0: in the FROM list, 1: in the TO list diff --git a/modules/shape/src/emdL1_def.hpp b/modules/shape/src/emdL1_def.hpp index 2a75ab4d4..38f78cc3d 100644 --- a/modules/shape/src/emdL1_def.hpp +++ b/modules/shape/src/emdL1_def.hpp @@ -44,7 +44,6 @@ #include <math.h> #include <vector> -#define VHIGH 1e10; /****************************************************************************************\ * For EMDL1 Framework * \****************************************************************************************/ From 4672a70c1f5c969ae48926eea1c166e32b1939a3 Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Wed, 25 Sep 2013 18:30:33 +0200 Subject: [PATCH 08/12] Replaced dynamic_cas with Ptr::dynamicCast<>, and & with && --- modules/shape/src/aff_trans.cpp | 4 ++-- modules/shape/src/emdL1.cpp | 2 +- modules/shape/src/haus_dis.cpp | 6 +++--- modules/shape/src/sc_dis.cpp | 28 ++++++++++++++-------------- modules/shape/src/tps_trans.cpp | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/modules/shape/src/aff_trans.cpp b/modules/shape/src/aff_trans.cpp index 02f290fec..c290e3fa1 100644 --- a/modules/shape/src/aff_trans.cpp +++ b/modules/shape/src/aff_trans.cpp @@ -186,7 +186,7 @@ void AffineTransformerImpl::estimateTransformation(InputArray _pts1, InputArray { Mat pts1 = _pts1.getMat(); Mat pts2 = _pts2.getMat(); - CV_Assert((pts1.channels()==2) & (pts1.cols>0) & (pts2.channels()==2) & (pts2.cols>0)); + CV_Assert((pts1.channels()==2) && (pts1.cols>0) && (pts2.channels()==2) && (pts2.cols>0)); CV_Assert(_matches.size()>1); if (pts1.type() != CV_32F) @@ -230,7 +230,7 @@ void AffineTransformerImpl::estimateTransformation(InputArray _pts1, InputArray float AffineTransformerImpl::applyTransformation(InputArray inPts, OutputArray outPts) { Mat pts1 = inPts.getMat(); - CV_Assert((pts1.channels()==2) & (pts1.cols>0)); + CV_Assert((pts1.channels()==2) && (pts1.cols>0)); //Apply transformation in the complete set of points Mat fAffine; diff --git a/modules/shape/src/emdL1.cpp b/modules/shape/src/emdL1.cpp index b8381de5a..75f1b13ad 100644 --- a/modules/shape/src/emdL1.cpp +++ b/modules/shape/src/emdL1.cpp @@ -60,7 +60,7 @@ float EmdL1::getEMDL1(cv::Mat &sig1, cv::Mat &sig2) { // Initialization - CV_Assert((sig1.rows==sig2.rows) & (sig1.cols==sig2.cols) & (!sig1.empty()) & (!sig2.empty())); + CV_Assert((sig1.rows==sig2.rows) && (sig1.cols==sig2.cols) && (!sig1.empty()) && (!sig2.empty())); if(!initBaseTrees(sig1.rows, 1)) return -1; diff --git a/modules/shape/src/haus_dis.cpp b/modules/shape/src/haus_dis.cpp index a2c555ed7..ff5bd8c3d 100644 --- a/modules/shape/src/haus_dis.cpp +++ b/modules/shape/src/haus_dis.cpp @@ -71,7 +71,7 @@ public: virtual void setRankProportion(float _rankProportion) { - CV_Assert((_rankProportion>0) & (_rankProportion<=1)); + CV_Assert((_rankProportion>0) && (_rankProportion<=1)); rankProportion=_rankProportion; } virtual float getRankProportion() const {return rankProportion;} @@ -135,8 +135,8 @@ float HausdorffDistanceExtractorImpl::computeDistance(InputArray contour1, Input set1.convertTo(set1, CV_32F); if (set2.type() != CV_32F) set2.convertTo(set2, CV_32F); - CV_Assert((set1.channels()==2) & (set1.cols>0)); - CV_Assert((set2.channels()==2) & (set2.cols>0)); + CV_Assert((set1.channels()==2) && (set1.cols>0)); + CV_Assert((set2.channels()==2) && (set2.cols>0)); return std::max( _apply(set1, set2, distanceFlag, rankProportion), _apply(set2, set1, distanceFlag, rankProportion) ); } diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp index e41efc229..f19080b86 100644 --- a/modules/shape/src/sc_dis.cpp +++ b/modules/shape/src/sc_dis.cpp @@ -45,7 +45,7 @@ * Belongie et al., 2002 by Juan Manuel Perez for GSoC 2013. */ #include "precomp.hpp" -//#include "opencv2/highgui.hpp" +#include "opencv2/core.hpp" /* * ShapeContextDescriptor class */ @@ -181,7 +181,7 @@ protected: { if (queryInliers.size()>0) { - mask.at<char>(i,j)=char(queryInliers[j] & queryInliers[i]); + mask.at<char>(i,j)=char(queryInliers[j] && queryInliers[i]); } else { @@ -641,14 +641,14 @@ public: virtual void setImages(InputArray _image1, InputArray _image2) { Mat image1_=_image1.getMat(), image2_=_image2.getMat(); - CV_Assert((image1_.depth()==0) & (image2_.depth()==0)); + CV_Assert((image1_.depth()==0) && (image2_.depth()==0)); image1=image1_; image2=image2_; } virtual void getImages(OutputArray _image1, OutputArray _image2) const { - CV_Assert((!image1.empty()) & (!image2.empty())); + CV_Assert((!image1.empty()) && (!image2.empty())); _image1.create(image1.size(), image1.type()); _image2.create(image2.size(), image2.type()); _image1.getMat()=image1; @@ -726,11 +726,11 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In else sset1.copyTo(set2); - CV_Assert((set1.channels()==2) & (set1.cols>0)); - CV_Assert((set2.channels()==2) & (set2.cols>0)); + CV_Assert((set1.channels()==2) && (set1.cols>0)); + CV_Assert((set2.channels()==2) && (set2.cols>0)); if (imageAppearanceWeight!=0) { - CV_Assert((!image1.empty()) & (!image2.empty())); + CV_Assert((!image1.empty()) && (!image2.empty())); } // Initializing Extractor, Descriptor structures and Matcher // @@ -747,9 +747,9 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In // Initializing some variables // std::vector<int> inliers1, inliers2; - bool isTPS=false; - if ( dynamic_cast<ThinPlateSplineShapeTransformer*>(&*transformer) ) - isTPS=true; + + Ptr<ThinPlateSplineShapeTransformer> transDown = transformer.dynamicCast<ThinPlateSplineShapeTransformer>(); + Mat warpedImage; for (int ii=0; ii<iterations; ii++) { @@ -766,8 +766,8 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In matcher.matchDescriptors(set1SCD, set2SCD, matches, comparer, inliers1, inliers2); // apply TPS transform // - if ( isTPS ) - dynamic_cast<ThinPlateSplineShapeTransformer*>(&*transformer)->setRegularizationParameter(beta); + if ( !transDown.empty() ) + transDown->setRegularizationParameter(beta); transformer->estimateTransformation(set1, set2, matches); bEnergy += transformer->applyTransformation(set1, set1); @@ -777,7 +777,7 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In // Have to accumulate the transformation along all the iterations if (ii==0) { - if ( isTPS ) + if ( !transDown.empty() ) { image2.copyTo(warpedImage); } @@ -794,7 +794,7 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In if (imageAppearanceWeight!=0) { // compute appearance cost - if ( isTPS ) + if ( !transDown.empty() ) { resize(warpedImage, warpedImage, image1.size()); Mat temp=(warpedImage-image1); diff --git a/modules/shape/src/tps_trans.cpp b/modules/shape/src/tps_trans.cpp index b841567dc..cbf2d1b99 100644 --- a/modules/shape/src/tps_trans.cpp +++ b/modules/shape/src/tps_trans.cpp @@ -169,7 +169,7 @@ float ThinPlateSplineShapeTransformerImpl::applyTransformation(InputArray inPts, { CV_Assert(tpsComputed); Mat pts1 = inPts.getMat(); - CV_Assert((pts1.channels()==2) & (pts1.cols>0)); + CV_Assert((pts1.channels()==2) && (pts1.cols>0)); //Apply transformation in the complete set of points // Ensambling output // @@ -192,7 +192,7 @@ void ThinPlateSplineShapeTransformerImpl::estimateTransformation(InputArray _pts { Mat pts1 = _pts1.getMat(); Mat pts2 = _pts2.getMat(); - CV_Assert((pts1.channels()==2) & (pts1.cols>0) & (pts2.channels()==2) & (pts2.cols>0)); + CV_Assert((pts1.channels()==2) && (pts1.cols>0) && (pts2.channels()==2) && (pts2.cols>0)); CV_Assert(_matches.size()>1); if (pts1.type() != CV_32F) From f6fc39ce8ff602302b6f2be1890bf1fd96ea5868 Mon Sep 17 00:00:00 2001 From: Juan Manuel Perez <juanmanpr@gmail.com> Date: Wed, 25 Sep 2013 23:25:10 +0200 Subject: [PATCH 09/12] Putting definitions of SCD and SCDMatcher separated from sc_dis.cpp file --- modules/shape/src/sc_dis.cpp | 989 ++++++++++++++++------------------ modules/shape/src/scd_def.hpp | 128 +++++ 2 files changed, 589 insertions(+), 528 deletions(-) create mode 100644 modules/shape/src/scd_def.hpp diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp index f19080b86..fc5ed426f 100644 --- a/modules/shape/src/sc_dis.cpp +++ b/modules/shape/src/sc_dis.cpp @@ -46,532 +46,7 @@ */ #include "precomp.hpp" #include "opencv2/core.hpp" -/* - * ShapeContextDescriptor class - */ -class SCD -{ -public: - //! the full constructor taking all the necessary parameters - explicit SCD(int _nAngularBins=12, int _nRadialBins=5, - double _innerRadius=0.1, double _outerRadius=1, bool _rotationInvariant=false) - { - setAngularBins(_nAngularBins); - setRadialBins(_nRadialBins); - setInnerRadius(_innerRadius); - setOuterRadius(_outerRadius); - setRotationInvariant(_rotationInvariant); - } - - void extractSCD(cv::Mat& contour, cv::Mat& descriptors, - const std::vector<int>& queryInliers=std::vector<int>(), - const float _meanDistance=-1) - { - cv::Mat contourMat = contour; - cv::Mat disMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F); - cv::Mat angleMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F); - - std::vector<double> logspaces, angspaces; - logarithmicSpaces(logspaces); - angularSpaces(angspaces); - buildNormalizedDistanceMatrix(contourMat, disMatrix, queryInliers, _meanDistance); - buildAngleMatrix(contourMat, angleMatrix); - - // Now, build the descriptor matrix (each row is a point) // - descriptors = cv::Mat::zeros(contourMat.cols, descriptorSize(), CV_32F); - - for (int ptidx=0; ptidx<contourMat.cols; ptidx++) - { - for (int cmp=0; cmp<contourMat.cols; cmp++) - { - if (ptidx==cmp) continue; - if ((int)queryInliers.size()>0) - { - if (queryInliers[ptidx]==0 || queryInliers[cmp]==0) continue; //avoid outliers - } - - int angidx=-1, radidx=-1; - for (int i=0; i<nRadialBins; i++) - { - if (disMatrix.at<float>(ptidx, cmp)<logspaces[i]) - { - radidx=i; - break; - } - } - for (int i=0; i<nAngularBins; i++) - { - if (angleMatrix.at<float>(ptidx, cmp)<angspaces[i]) - { - angidx=i; - break; - } - } - if (angidx!=-1 && radidx!=-1) - { - int idx = angidx+radidx*nAngularBins; - descriptors.at<float>(ptidx, idx)++; - } - } - } - } - - int descriptorSize() {return nAngularBins*nRadialBins;} - void setAngularBins(int angularBins) { nAngularBins=angularBins; } - void setRadialBins(int radialBins) { nRadialBins=radialBins; } - void setInnerRadius(double _innerRadius) { innerRadius=_innerRadius; } - void setOuterRadius(double _outerRadius) { outerRadius=_outerRadius; } - void setRotationInvariant(bool _rotationInvariant) { rotationInvariant=_rotationInvariant; } - int getAngularBins() const { return nAngularBins; } - int getRadialBins() const { return nRadialBins; } - double getInnerRadius() const { return innerRadius; } - double getOuterRadius() const { return outerRadius; } - bool getRotationInvariant() const { return rotationInvariant; } - float getMeanDistance() const { return meanDistance; } - -private: - int nAngularBins; - int nRadialBins; - double innerRadius; - double outerRadius; - bool rotationInvariant; - float meanDistance; - -protected: - void logarithmicSpaces(std::vector<double>& vecSpaces) const - { - double logmin=log10(innerRadius); - double logmax=log10(outerRadius); - double delta=(logmax-logmin)/(nRadialBins-1); - double accdelta=0; - - for (int i=0; i<nRadialBins; i++) - { - double val = std::pow(10,logmin+accdelta); - vecSpaces.push_back(val); - accdelta += delta; - } - } - - void angularSpaces(std::vector<double>& vecSpaces) const - { - double delta=2*CV_PI/nAngularBins; - double val=0; - - for (int i=0; i<nAngularBins; i++) - { - val += delta; - vecSpaces.push_back(val); - } - } - - void buildNormalizedDistanceMatrix(cv::Mat& contour, - cv::Mat& disMatrix, const std::vector<int> &queryInliers, - const float _meanDistance=-1) - { - cv::Mat contourMat = contour; - cv::Mat mask(disMatrix.rows, disMatrix.cols, CV_8U); - - for (int i=0; i<contourMat.cols; i++) - { - for (int j=0; j<contourMat.cols; j++) - { - disMatrix.at<float>(i,j) = (float)norm( cv::Mat(contourMat.at<cv::Point2f>(0,i)-contourMat.at<cv::Point2f>(0,j)), cv::NORM_L2 ); - if (_meanDistance<0) - { - if (queryInliers.size()>0) - { - mask.at<char>(i,j)=char(queryInliers[j] && queryInliers[i]); - } - else - { - mask.at<char>(i,j)=1; - } - } - } - } - - if (_meanDistance<0) - { - meanDistance=(float)mean(disMatrix, mask)[0]; - } - else - { - meanDistance=_meanDistance; - } - disMatrix/=meanDistance+FLT_EPSILON; - } - - void buildAngleMatrix(cv::Mat& contour, - cv::Mat& angleMatrix) const - { - cv::Mat contourMat = contour; - - // if descriptor is rotationInvariant compute massCenter // - cv::Point2f massCenter(0,0); - if (rotationInvariant) - { - for (int i=0; i<contourMat.cols; i++) - { - massCenter+=contourMat.at<cv::Point2f>(0,i); - } - massCenter.x=massCenter.x/(float)contourMat.cols; - massCenter.y=massCenter.y/(float)contourMat.cols; - } - - - for (int i=0; i<contourMat.cols; i++) - { - for (int j=0; j<contourMat.cols; j++) - { - if (i==j) - { - angleMatrix.at<float>(i,j)=0.0; - } - else - { - cv::Point2f dif = contourMat.at<cv::Point2f>(0,i) - contourMat.at<cv::Point2f>(0,j); - angleMatrix.at<float>(i,j) = std::atan2(dif.y, dif.x); - - if (rotationInvariant) - { - cv::Point2f refPt = contourMat.at<cv::Point2f>(0,i) - massCenter; - float refAngle = atan2(refPt.y, refPt.x); - angleMatrix.at<float>(i,j) -= refAngle; - } - angleMatrix.at<float>(i,j) = float(fmod(double(angleMatrix.at<float>(i,j)+(double)FLT_EPSILON),2*CV_PI)+CV_PI); - //angleMatrix.at<float>(i,j) = 1+floor( angleMatrix.at<float>(i,j)*nAngularBins/(2*CV_PI) ); - } - } - } - } -}; - -/* - * Matcher - */ -class SCDMatcher -{ -public: - // the full constructor - SCDMatcher() - { - } - - // the matcher function using Hungarian method - void matchDescriptors(cv::Mat& descriptors1, cv::Mat& descriptors2, std::vector<cv::DMatch>& matches, cv::Ptr<cv::HistogramCostExtractor>& comparer, - std::vector<int>& inliers1, std::vector<int> &inliers2) - { - matches.clear(); - - // Build the cost Matrix between descriptors // - cv::Mat costMat; - buildCostMatrix(descriptors1, descriptors2, costMat, comparer); - - // Solve the matching problem using the hungarian method // - hungarian(costMat, matches, inliers1, inliers2, descriptors1.rows, descriptors2.rows); - } - - // matching cost - float getMatchingCost() const {return minMatchCost;} - -private: - float minMatchCost; - float betaAdditional; -protected: - void buildCostMatrix(const cv::Mat& descriptors1, const cv::Mat& descriptors2, - cv::Mat& costMatrix, cv::Ptr<cv::HistogramCostExtractor>& comparer) const - { - comparer->buildCostMatrix(descriptors1, descriptors2, costMatrix); - } - - void hungarian(cv::Mat& costMatrix, std::vector<cv::DMatch>& outMatches, std::vector<int> &inliers1, - std::vector<int> &inliers2, int sizeScd1=0, int sizeScd2=0) - { - std::vector<int> free(costMatrix.rows, 0), collist(costMatrix.rows, 0); - std::vector<int> matches(costMatrix.rows, 0), colsol(costMatrix.rows), rowsol(costMatrix.rows); - std::vector<float> d(costMatrix.rows), pred(costMatrix.rows), v(costMatrix.rows); - - const float LOWV=1e-10; - bool unassignedfound; - int i=0, imin=0, numfree=0, prvnumfree=0, f=0, i0=0, k=0, freerow=0; - int j=0, j1=0, j2=0, endofpath=0, last=0, low=0, up=0; - float min=0, h=0, umin=0, usubmin=0, v2=0; - - // COLUMN REDUCTION // - for (j = costMatrix.rows-1; j >= 0; j--) - { - // find minimum cost over rows. - min = costMatrix.at<float>(0,j); - imin = 0; - for (i = 1; i < costMatrix.rows; i++) - if (costMatrix.at<float>(i,j) < min) - { - min = costMatrix.at<float>(i,j); - imin = i; - } - v[j] = min; - - if (++matches[imin] == 1) - { - rowsol[imin] = j; - colsol[j] = imin; - } - else - { - colsol[j]=-1; - } - } - - // REDUCTION TRANSFER // - for (i=0; i<costMatrix.rows; i++) - { - if (matches[i] == 0) - { - free[numfree++] = i; - } - else - { - if (matches[i] == 1) - { - j1=rowsol[i]; - min=std::numeric_limits<float>::max(); - for (j=0; j<costMatrix.rows; j++) - { - if (j!=j1) - { - if (costMatrix.at<float>(i,j)-v[j] < min) - { - min=costMatrix.at<float>(i,j)-v[j]; - } - } - } - v[j1] = v[j1]-min; - } - } - } - // AUGMENTING ROW REDUCTION // - int loopcnt = 0; - do - { - loopcnt++; - k=0; - prvnumfree=numfree; - numfree=0; - while (k < prvnumfree) - { - i=free[k]; - k++; - umin = costMatrix.at<float>(i,0)-v[0]; - j1=0; - usubmin = std::numeric_limits<float>::max(); - for (j=1; j<costMatrix.rows; j++) - { - h = costMatrix.at<float>(i,j)-v[j]; - if (h < usubmin) - { - if (h >= umin) - { - usubmin = h; - j2 = j; - } - else - { - usubmin = umin; - umin = h; - j2 = j1; - j1 = j; - } - } - } - i0 = colsol[j1]; - - if (fabs(umin-usubmin) > LOWV) //if( umin < usubmin ) - { - v[j1] = v[j1] - (usubmin - umin); - } - else // minimum and subminimum equal. - { - if (i0 >= 0) // minimum column j1 is assigned. - { - j1 = j2; - i0 = colsol[j2]; - } - } - // (re-)assign i to j1, possibly de-assigning an i0. - rowsol[i]=j1; - colsol[j1]=i; - - if (i0 >= 0) - { - //if( umin < usubmin ) - if (fabs(umin-usubmin) > LOWV) - { - free[--k] = i0; - } - else - { - free[numfree++] = i0; - } - } - } - }while (loopcnt<2); // repeat once. - - // AUGMENT SOLUTION for each free row // - for (f = 0; f<numfree; f++) - { - freerow = free[f]; // start row of augmenting path. - // Dijkstra shortest path algorithm. - // runs until unassigned column added to shortest path tree. - for (j = 0; j < costMatrix.rows; j++) - { - d[j] = costMatrix.at<float>(freerow,j) - v[j]; - pred[j] = float(freerow); - collist[j] = j; // init column list. - } - - low=0; // columns in 0..low-1 are ready, now none. - up=0; // columns in low..up-1 are to be scanned for current minimum, now none. - unassignedfound = false; - do - { - if (up == low) - { - last=low-1; - min = d[collist[up++]]; - for (k = up; k < costMatrix.rows; k++) - { - j = collist[k]; - h = d[j]; - if (h <= min) - { - if (h < min) // new minimum. - { - up = low; // restart list at index low. - min = h; - } - collist[k] = collist[up]; - collist[up++] = j; - } - } - for (k=low; k<up; k++) - { - if (colsol[collist[k]] < 0) - { - endofpath = collist[k]; - unassignedfound = true; - break; - } - } - } - - if (!unassignedfound) - { - // update 'distances' between freerow and all unscanned columns, via next scanned column. - j1 = collist[low]; - low++; - i = colsol[j1]; - h = costMatrix.at<float>(i,j1)-v[j1]-min; - - for (k = up; k < costMatrix.rows; k++) - { - j = collist[k]; - v2 = costMatrix.at<float>(i,j) - v[j] - h; - if (v2 < d[j]) - { - pred[j] = float(i); - if (v2 == min) - { - if (colsol[j] < 0) - { - // if unassigned, shortest augmenting path is complete. - endofpath = j; - unassignedfound = true; - break; - } - else - { - collist[k] = collist[up]; - collist[up++] = j; - } - } - d[j] = v2; - } - } - } - }while (!unassignedfound); - - // update column prices. - for (k = 0; k <= last; k++) - { - j1 = collist[k]; - v[j1] = v[j1] + d[j1] - min; - } - - // reset row and column assignments along the alternating path. - do - { - i = int(pred[endofpath]); - colsol[endofpath] = i; - j1 = endofpath; - endofpath = rowsol[i]; - rowsol[i] = j1; - }while (i != freerow); - } - - // calculate symmetric shape context cost - cv::Mat trueCostMatrix(costMatrix, cv::Rect(0,0,sizeScd1, sizeScd2)); - float leftcost = 0; - for (int nrow=0; nrow<trueCostMatrix.rows; nrow++) - { - double minval; - minMaxIdx(trueCostMatrix.row(nrow), &minval); - leftcost+=float(minval); - } - leftcost /= trueCostMatrix.rows; - - float rightcost = 0; - for (int ncol=0; ncol<trueCostMatrix.cols; ncol++) - { - double minval; - minMaxIdx(trueCostMatrix.col(ncol), &minval); - rightcost+=float(minval); - } - rightcost /= trueCostMatrix.cols; - - minMatchCost = std::max(leftcost,rightcost); - - // Save in a DMatch vector - for (i=0;i<costMatrix.cols;i++) - { - cv::DMatch singleMatch(colsol[i],i,costMatrix.at<float>(colsol[i],i));//queryIdx,trainIdx,distance - outMatches.push_back(singleMatch); - } - - // Update inliers - inliers1.reserve(sizeScd1); - for (size_t kc = 0; kc<inliers1.size(); kc++) - { - if (rowsol[kc]<sizeScd1) // if a real match - inliers1[kc]=1; - else - inliers1[kc]=0; - } - inliers2.reserve(sizeScd2); - for (size_t kc = 0; kc<inliers2.size(); kc++) - { - if (colsol[kc]<sizeScd2) // if a real match - inliers2[kc]=1; - else - inliers2[kc]=0; - } - } - -}; - -/* - * - */ +#include "scd_def.hpp" namespace cv { @@ -734,9 +209,9 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In } // Initializing Extractor, Descriptor structures and Matcher // - SCD set1SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, false); + SCD set1SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, rotationInvariant); Mat set1SCD; - SCD set2SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, false); + SCD set2SCE(nAngularBins, nRadialBins, innerRadius, outerRadius, rotationInvariant); Mat set2SCD; SCDMatcher matcher; std::vector<DMatch> matches; @@ -846,3 +321,461 @@ Ptr <ShapeContextDistanceExtractor> createShapeContextDistanceExtractor(int nAng } } // cv + +//! SCD +void SCD::extractSCD(cv::Mat &contour, cv::Mat &descriptors, const std::vector<int> &queryInliers, const float _meanDistance) +{ + cv::Mat contourMat = contour; + cv::Mat disMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F); + cv::Mat angleMatrix = cv::Mat::zeros(contourMat.cols, contourMat.cols, CV_32F); + + std::vector<double> logspaces, angspaces; + logarithmicSpaces(logspaces); + angularSpaces(angspaces); + buildNormalizedDistanceMatrix(contourMat, disMatrix, queryInliers, _meanDistance); + buildAngleMatrix(contourMat, angleMatrix); + + // Now, build the descriptor matrix (each row is a point) // + descriptors = cv::Mat::zeros(contourMat.cols, descriptorSize(), CV_32F); + + for (int ptidx=0; ptidx<contourMat.cols; ptidx++) + { + for (int cmp=0; cmp<contourMat.cols; cmp++) + { + if (ptidx==cmp) continue; + if ((int)queryInliers.size()>0) + { + if (queryInliers[ptidx]==0 || queryInliers[cmp]==0) continue; //avoid outliers + } + + int angidx=-1, radidx=-1; + for (int i=0; i<nRadialBins; i++) + { + if (disMatrix.at<float>(ptidx, cmp)<logspaces[i]) + { + radidx=i; + break; + } + } + for (int i=0; i<nAngularBins; i++) + { + if (angleMatrix.at<float>(ptidx, cmp)<angspaces[i]) + { + angidx=i; + break; + } + } + if (angidx!=-1 && radidx!=-1) + { + int idx = angidx+radidx*nAngularBins; + descriptors.at<float>(ptidx, idx)++; + } + } + } +} + +void SCD::logarithmicSpaces(std::vector<double> &vecSpaces) const +{ + double logmin=log10(innerRadius); + double logmax=log10(outerRadius); + double delta=(logmax-logmin)/(nRadialBins-1); + double accdelta=0; + + for (int i=0; i<nRadialBins; i++) + { + double val = std::pow(10,logmin+accdelta); + vecSpaces.push_back(val); + accdelta += delta; + } +} + +void SCD::angularSpaces(std::vector<double> &vecSpaces) const +{ + double delta=2*CV_PI/nAngularBins; + double val=0; + + for (int i=0; i<nAngularBins; i++) + { + val += delta; + vecSpaces.push_back(val); + } +} + +void SCD::buildNormalizedDistanceMatrix(cv::Mat &contour, cv::Mat &disMatrix, const std::vector<int> &queryInliers, const float _meanDistance) +{ + cv::Mat contourMat = contour; + cv::Mat mask(disMatrix.rows, disMatrix.cols, CV_8U); + + for (int i=0; i<contourMat.cols; i++) + { + for (int j=0; j<contourMat.cols; j++) + { + disMatrix.at<float>(i,j) = (float)norm( cv::Mat(contourMat.at<cv::Point2f>(0,i)-contourMat.at<cv::Point2f>(0,j)), cv::NORM_L2 ); + if (_meanDistance<0) + { + if (queryInliers.size()>0) + { + mask.at<char>(i,j)=char(queryInliers[j] && queryInliers[i]); + } + else + { + mask.at<char>(i,j)=1; + } + } + } + } + + if (_meanDistance<0) + { + meanDistance=(float)mean(disMatrix, mask)[0]; + } + else + { + meanDistance=_meanDistance; + } + disMatrix/=meanDistance+FLT_EPSILON; +} + +void SCD::buildAngleMatrix(cv::Mat &contour, cv::Mat &angleMatrix) const +{ + cv::Mat contourMat = contour; + + // if descriptor is rotationInvariant compute massCenter // + cv::Point2f massCenter(0,0); + if (rotationInvariant) + { + for (int i=0; i<contourMat.cols; i++) + { + massCenter+=contourMat.at<cv::Point2f>(0,i); + } + massCenter.x=massCenter.x/(float)contourMat.cols; + massCenter.y=massCenter.y/(float)contourMat.cols; + } + + + for (int i=0; i<contourMat.cols; i++) + { + for (int j=0; j<contourMat.cols; j++) + { + if (i==j) + { + angleMatrix.at<float>(i,j)=0.0; + } + else + { + cv::Point2f dif = contourMat.at<cv::Point2f>(0,i) - contourMat.at<cv::Point2f>(0,j); + angleMatrix.at<float>(i,j) = std::atan2(dif.y, dif.x); + + if (rotationInvariant) + { + cv::Point2f refPt = contourMat.at<cv::Point2f>(0,i) - massCenter; + float refAngle = atan2(refPt.y, refPt.x); + angleMatrix.at<float>(i,j) -= refAngle; + } + angleMatrix.at<float>(i,j) = float(fmod(double(angleMatrix.at<float>(i,j)+(double)FLT_EPSILON),2*CV_PI)+CV_PI); + } + } + } +} + +//! SCDMatcher +void SCDMatcher::matchDescriptors(cv::Mat &descriptors1, cv::Mat &descriptors2, std::vector<cv::DMatch> &matches, + cv::Ptr<cv::HistogramCostExtractor> &comparer, std::vector<int> &inliers1, std::vector<int> &inliers2) +{ + matches.clear(); + + // Build the cost Matrix between descriptors // + cv::Mat costMat; + buildCostMatrix(descriptors1, descriptors2, costMat, comparer); + + // Solve the matching problem using the hungarian method // + hungarian(costMat, matches, inliers1, inliers2, descriptors1.rows, descriptors2.rows); +} + +void SCDMatcher::buildCostMatrix(const cv::Mat &descriptors1, const cv::Mat &descriptors2, + cv::Mat &costMatrix, cv::Ptr<cv::HistogramCostExtractor> &comparer) const +{ + comparer->buildCostMatrix(descriptors1, descriptors2, costMatrix); +} + +void SCDMatcher::hungarian(cv::Mat &costMatrix, std::vector<cv::DMatch> &outMatches, std::vector<int> &inliers1, + std::vector<int> &inliers2, int sizeScd1, int sizeScd2) +{ + std::vector<int> free(costMatrix.rows, 0), collist(costMatrix.rows, 0); + std::vector<int> matches(costMatrix.rows, 0), colsol(costMatrix.rows), rowsol(costMatrix.rows); + std::vector<float> d(costMatrix.rows), pred(costMatrix.rows), v(costMatrix.rows); + + const float LOWV=1e-10; + bool unassignedfound; + int i=0, imin=0, numfree=0, prvnumfree=0, f=0, i0=0, k=0, freerow=0; + int j=0, j1=0, j2=0, endofpath=0, last=0, low=0, up=0; + float min=0, h=0, umin=0, usubmin=0, v2=0; + + // COLUMN REDUCTION // + for (j = costMatrix.rows-1; j >= 0; j--) + { + // find minimum cost over rows. + min = costMatrix.at<float>(0,j); + imin = 0; + for (i = 1; i < costMatrix.rows; i++) + if (costMatrix.at<float>(i,j) < min) + { + min = costMatrix.at<float>(i,j); + imin = i; + } + v[j] = min; + + if (++matches[imin] == 1) + { + rowsol[imin] = j; + colsol[j] = imin; + } + else + { + colsol[j]=-1; + } + } + + // REDUCTION TRANSFER // + for (i=0; i<costMatrix.rows; i++) + { + if (matches[i] == 0) + { + free[numfree++] = i; + } + else + { + if (matches[i] == 1) + { + j1=rowsol[i]; + min=std::numeric_limits<float>::max(); + for (j=0; j<costMatrix.rows; j++) + { + if (j!=j1) + { + if (costMatrix.at<float>(i,j)-v[j] < min) + { + min=costMatrix.at<float>(i,j)-v[j]; + } + } + } + v[j1] = v[j1]-min; + } + } + } + // AUGMENTING ROW REDUCTION // + int loopcnt = 0; + do + { + loopcnt++; + k=0; + prvnumfree=numfree; + numfree=0; + while (k < prvnumfree) + { + i=free[k]; + k++; + umin = costMatrix.at<float>(i,0)-v[0]; + j1=0; + usubmin = std::numeric_limits<float>::max(); + for (j=1; j<costMatrix.rows; j++) + { + h = costMatrix.at<float>(i,j)-v[j]; + if (h < usubmin) + { + if (h >= umin) + { + usubmin = h; + j2 = j; + } + else + { + usubmin = umin; + umin = h; + j2 = j1; + j1 = j; + } + } + } + i0 = colsol[j1]; + + if (fabs(umin-usubmin) > LOWV) //if( umin < usubmin ) + { + v[j1] = v[j1] - (usubmin - umin); + } + else // minimum and subminimum equal. + { + if (i0 >= 0) // minimum column j1 is assigned. + { + j1 = j2; + i0 = colsol[j2]; + } + } + // (re-)assign i to j1, possibly de-assigning an i0. + rowsol[i]=j1; + colsol[j1]=i; + + if (i0 >= 0) + { + //if( umin < usubmin ) + if (fabs(umin-usubmin) > LOWV) + { + free[--k] = i0; + } + else + { + free[numfree++] = i0; + } + } + } + }while (loopcnt<2); // repeat once. + + // AUGMENT SOLUTION for each free row // + for (f = 0; f<numfree; f++) + { + freerow = free[f]; // start row of augmenting path. + // Dijkstra shortest path algorithm. + // runs until unassigned column added to shortest path tree. + for (j = 0; j < costMatrix.rows; j++) + { + d[j] = costMatrix.at<float>(freerow,j) - v[j]; + pred[j] = float(freerow); + collist[j] = j; // init column list. + } + + low=0; // columns in 0..low-1 are ready, now none. + up=0; // columns in low..up-1 are to be scanned for current minimum, now none. + unassignedfound = false; + do + { + if (up == low) + { + last=low-1; + min = d[collist[up++]]; + for (k = up; k < costMatrix.rows; k++) + { + j = collist[k]; + h = d[j]; + if (h <= min) + { + if (h < min) // new minimum. + { + up = low; // restart list at index low. + min = h; + } + collist[k] = collist[up]; + collist[up++] = j; + } + } + for (k=low; k<up; k++) + { + if (colsol[collist[k]] < 0) + { + endofpath = collist[k]; + unassignedfound = true; + break; + } + } + } + + if (!unassignedfound) + { + // update 'distances' between freerow and all unscanned columns, via next scanned column. + j1 = collist[low]; + low++; + i = colsol[j1]; + h = costMatrix.at<float>(i,j1)-v[j1]-min; + + for (k = up; k < costMatrix.rows; k++) + { + j = collist[k]; + v2 = costMatrix.at<float>(i,j) - v[j] - h; + if (v2 < d[j]) + { + pred[j] = float(i); + if (v2 == min) + { + if (colsol[j] < 0) + { + // if unassigned, shortest augmenting path is complete. + endofpath = j; + unassignedfound = true; + break; + } + else + { + collist[k] = collist[up]; + collist[up++] = j; + } + } + d[j] = v2; + } + } + } + }while (!unassignedfound); + + // update column prices. + for (k = 0; k <= last; k++) + { + j1 = collist[k]; + v[j1] = v[j1] + d[j1] - min; + } + + // reset row and column assignments along the alternating path. + do + { + i = int(pred[endofpath]); + colsol[endofpath] = i; + j1 = endofpath; + endofpath = rowsol[i]; + rowsol[i] = j1; + }while (i != freerow); + } + + // calculate symmetric shape context cost + cv::Mat trueCostMatrix(costMatrix, cv::Rect(0,0,sizeScd1, sizeScd2)); + float leftcost = 0; + for (int nrow=0; nrow<trueCostMatrix.rows; nrow++) + { + double minval; + minMaxIdx(trueCostMatrix.row(nrow), &minval); + leftcost+=float(minval); + } + leftcost /= trueCostMatrix.rows; + + float rightcost = 0; + for (int ncol=0; ncol<trueCostMatrix.cols; ncol++) + { + double minval; + minMaxIdx(trueCostMatrix.col(ncol), &minval); + rightcost+=float(minval); + } + rightcost /= trueCostMatrix.cols; + + minMatchCost = std::max(leftcost,rightcost); + + // Save in a DMatch vector + for (i=0;i<costMatrix.cols;i++) + { + cv::DMatch singleMatch(colsol[i],i,costMatrix.at<float>(colsol[i],i));//queryIdx,trainIdx,distance + outMatches.push_back(singleMatch); + } + + // Update inliers + inliers1.reserve(sizeScd1); + for (size_t kc = 0; kc<inliers1.size(); kc++) + { + if (rowsol[kc]<sizeScd1) // if a real match + inliers1[kc]=1; + else + inliers1[kc]=0; + } + inliers2.reserve(sizeScd2); + for (size_t kc = 0; kc<inliers2.size(); kc++) + { + if (colsol[kc]<sizeScd2) // if a real match + inliers2[kc]=1; + else + inliers2[kc]=0; + } +} diff --git a/modules/shape/src/scd_def.hpp b/modules/shape/src/scd_def.hpp new file mode 100644 index 000000000..c7476ea5a --- /dev/null +++ b/modules/shape/src/scd_def.hpp @@ -0,0 +1,128 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * 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 +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include <stdlib.h> +#include <math.h> +#include <vector> + +/* + * ShapeContextDescriptor class + */ +class SCD +{ +public: + //! the full constructor taking all the necessary parameters + explicit SCD(int _nAngularBins=12, int _nRadialBins=5, + double _innerRadius=0.1, double _outerRadius=1, bool _rotationInvariant=false) + { + setAngularBins(_nAngularBins); + setRadialBins(_nRadialBins); + setInnerRadius(_innerRadius); + setOuterRadius(_outerRadius); + setRotationInvariant(_rotationInvariant); + } + + void extractSCD(cv::Mat& contour, cv::Mat& descriptors, + const std::vector<int>& queryInliers=std::vector<int>(), + const float _meanDistance=-1); + + int descriptorSize() {return nAngularBins*nRadialBins;} + void setAngularBins(int angularBins) { nAngularBins=angularBins; } + void setRadialBins(int radialBins) { nRadialBins=radialBins; } + void setInnerRadius(double _innerRadius) { innerRadius=_innerRadius; } + void setOuterRadius(double _outerRadius) { outerRadius=_outerRadius; } + void setRotationInvariant(bool _rotationInvariant) { rotationInvariant=_rotationInvariant; } + int getAngularBins() const { return nAngularBins; } + int getRadialBins() const { return nRadialBins; } + double getInnerRadius() const { return innerRadius; } + double getOuterRadius() const { return outerRadius; } + bool getRotationInvariant() const { return rotationInvariant; } + float getMeanDistance() const { return meanDistance; } + +private: + int nAngularBins; + int nRadialBins; + double innerRadius; + double outerRadius; + bool rotationInvariant; + float meanDistance; + +protected: + void logarithmicSpaces(std::vector<double>& vecSpaces) const; + void angularSpaces(std::vector<double>& vecSpaces) const; + + void buildNormalizedDistanceMatrix(cv::Mat& contour, + cv::Mat& disMatrix, const std::vector<int> &queryInliers, + const float _meanDistance=-1); + + void buildAngleMatrix(cv::Mat& contour, + cv::Mat& angleMatrix) const; +}; + +/* + * Matcher + */ +class SCDMatcher +{ +public: + // the full constructor + SCDMatcher() + { + } + + // the matcher function using Hungarian method + void matchDescriptors(cv::Mat& descriptors1, cv::Mat& descriptors2, std::vector<cv::DMatch>& matches, cv::Ptr<cv::HistogramCostExtractor>& comparer, + std::vector<int>& inliers1, std::vector<int> &inliers2); + + // matching cost + float getMatchingCost() const {return minMatchCost;} + +private: + float minMatchCost; + float betaAdditional; +protected: + void buildCostMatrix(const cv::Mat& descriptors1, const cv::Mat& descriptors2, + cv::Mat& costMatrix, cv::Ptr<cv::HistogramCostExtractor>& comparer) const; + void hungarian(cv::Mat& costMatrix, std::vector<cv::DMatch>& outMatches, std::vector<int> &inliers1, + std::vector<int> &inliers2, int sizeScd1=0, int sizeScd2=0); + +}; From cd847425562caa2cc309b41c18eef7cb47485818 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky <vadim.pisarevsky@gmail.com> Date: Mon, 30 Sep 2013 14:53:48 +0400 Subject: [PATCH 10/12] attempts to fix build errors on Android --- modules/shape/src/sc_dis.cpp | 23 ++++++++++++----------- modules/shape/src/scd_def.hpp | 5 +++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp index fc5ed426f..e09f7738e 100644 --- a/modules/shape/src/sc_dis.cpp +++ b/modules/shape/src/sc_dis.cpp @@ -166,7 +166,7 @@ public: sigma = (float)fn["sigma"]; } -private: +protected: int nAngularBins; int nRadialBins; float innerRadius; @@ -182,8 +182,6 @@ private: float imageAppearanceWeight; float shapeContextWeight; float sigma; - -protected: String name_; }; @@ -226,7 +224,9 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In Ptr<ThinPlateSplineShapeTransformer> transDown = transformer.dynamicCast<ThinPlateSplineShapeTransformer>(); Mat warpedImage; - for (int ii=0; ii<iterations; ii++) + int ii, jj, pt; + + for (ii=0; ii<iterations; ii++) { // Extract SCD descriptor in the set1 // set1SCE.extractSCD(set1, set1SCD, inliers1); @@ -282,11 +282,11 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In multiply(temp, temp, diffIm); } gaussWindow = Mat::zeros(warpedImage.rows, warpedImage.cols, CV_32F); - for (int pt=0; pt<sset1.cols; pt++) + for (pt=0; pt<sset1.cols; pt++) { - for (int ii=0; ii<diffIm.rows; ii++) + for (ii=0; ii<diffIm.rows; ii++) { - for (int jj=0; jj<diffIm.cols; jj++) + for (jj=0; jj<diffIm.cols; jj++) { float xx = sset1.at<Point2f>(0,pt).x; float yy = sset1.at<Point2f>(0,pt).y; @@ -297,9 +297,9 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In } Mat appIm(diffIm.rows, diffIm.cols, CV_32F); - for (int ii=0; ii<diffIm.rows; ii++) + for (ii=0; ii<diffIm.rows; ii++) { - for (int jj=0; jj<diffIm.cols; jj++) + for (jj=0; jj<diffIm.cols; jj++) { float elema=float( diffIm.at<uchar>(ii,jj) )/255; float elemb=gaussWindow.at<float>(ii,jj); @@ -320,8 +320,6 @@ Ptr <ShapeContextDistanceExtractor> createShapeContextDistanceExtractor(int nAng outerRadius, iterations, comparer, transformer) ); } -} // cv - //! SCD void SCD::extractSCD(cv::Mat &contour, cv::Mat &descriptors, const std::vector<int> &queryInliers, const float _meanDistance) { @@ -779,3 +777,6 @@ void SCDMatcher::hungarian(cv::Mat &costMatrix, std::vector<cv::DMatch> &outMatc inliers2[kc]=0; } } + +} + diff --git a/modules/shape/src/scd_def.hpp b/modules/shape/src/scd_def.hpp index c7476ea5a..18b3e9a78 100644 --- a/modules/shape/src/scd_def.hpp +++ b/modules/shape/src/scd_def.hpp @@ -44,6 +44,8 @@ #include <math.h> #include <vector> +namespace cv +{ /* * ShapeContextDescriptor class */ @@ -126,3 +128,6 @@ protected: std::vector<int> &inliers2, int sizeScd1=0, int sizeScd2=0); }; + +} + From c3748a5f95f6c6e1a6c8024ebb30cd4c58e1f45d Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky <vadim.pisarevsky@gmail.com> Date: Mon, 30 Sep 2013 16:06:48 +0400 Subject: [PATCH 11/12] fixed the trailing whitespaces --- modules/shape/src/sc_dis.cpp | 1 - modules/shape/src/scd_def.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp index e09f7738e..7a0893d5a 100644 --- a/modules/shape/src/sc_dis.cpp +++ b/modules/shape/src/sc_dis.cpp @@ -779,4 +779,3 @@ void SCDMatcher::hungarian(cv::Mat &costMatrix, std::vector<cv::DMatch> &outMatc } } - diff --git a/modules/shape/src/scd_def.hpp b/modules/shape/src/scd_def.hpp index 18b3e9a78..1a180fd84 100644 --- a/modules/shape/src/scd_def.hpp +++ b/modules/shape/src/scd_def.hpp @@ -130,4 +130,3 @@ protected: }; } - From c810d03d5c1e2d4a28987919f9c3dcbfecdc676c Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky <vadim.pisarevsky@gmail.com> Date: Mon, 30 Sep 2013 19:28:11 +0400 Subject: [PATCH 12/12] some more attempts to fix compile bug on Android --- modules/shape/src/sc_dis.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/shape/src/sc_dis.cpp b/modules/shape/src/sc_dis.cpp index 7a0893d5a..24e86af49 100644 --- a/modules/shape/src/sc_dis.cpp +++ b/modules/shape/src/sc_dis.cpp @@ -235,7 +235,8 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In set2SCE.extractSCD(set2, set2SCD, inliers2, set1SCE.getMeanDistance()); // regularization parameter with annealing rate annRate // - beta=std::pow(set1SCE.getMeanDistance(),2); + beta=set1SCE.getMeanDistance(); + beta *= beta; // match // matcher.matchDescriptors(set1SCD, set2SCD, matches, comparer, inliers1, inliers2); @@ -284,13 +285,12 @@ float ShapeContextDistanceExtractorImpl::computeDistance(InputArray contour1, In gaussWindow = Mat::zeros(warpedImage.rows, warpedImage.cols, CV_32F); for (pt=0; pt<sset1.cols; pt++) { + Point2f p = sset1.at<Point2f>(0,pt); for (ii=0; ii<diffIm.rows; ii++) { for (jj=0; jj<diffIm.cols; jj++) { - float xx = sset1.at<Point2f>(0,pt).x; - float yy = sset1.at<Point2f>(0,pt).y; - float val = float(std::exp( -float( (xx-jj)*(xx-jj) + (yy-ii)*(yy-ii) )/(2*sigma*sigma) ) / (sigma*sigma*2*CV_PI)); + float val = float(std::exp( -float( (p.x-jj)*(p.x-jj) + (p.y-ii)*(p.y-ii) )/(2*sigma*sigma) ) / (sigma*sigma*2*CV_PI)); gaussWindow.at<float>(ii,jj) += val; } }