1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41 
42 #include "test_precomp.hpp"
43 
44 using namespace cv;
45 using namespace std;
46 
47 template <typename T, typename compute>
48 class ShapeBaseTest : public cvtest::BaseTest
49 {
50 public:
51     typedef Point_<T> PointType;
ShapeBaseTest(int _NSN,int _NP,float _CURRENT_MAX_ACCUR)52     ShapeBaseTest(int _NSN, int _NP, float _CURRENT_MAX_ACCUR)
53         : NSN(_NSN), NP(_NP), CURRENT_MAX_ACCUR(_CURRENT_MAX_ACCUR)
54     {
55         // generate file list
56         vector<string> shapeNames;
57         shapeNames.push_back("apple"); //ok
58         shapeNames.push_back("children"); // ok
59         shapeNames.push_back("device7"); // ok
60         shapeNames.push_back("Heart"); // ok
61         shapeNames.push_back("teddy"); // ok
62         for (vector<string>::const_iterator i = shapeNames.begin(); i != shapeNames.end(); ++i)
63         {
64             for (int j = 0; j < NSN; ++j)
65             {
66                 stringstream filename;
67                 filename << cvtest::TS::ptr()->get_data_path()
68                          << "shape/mpeg_test/" << *i << "-" << j + 1 << ".png";
69                 filenames.push_back(filename.str());
70             }
71         }
72         // distance matrix
73         const int totalCount = (int)filenames.size();
74         distanceMat = Mat::zeros(totalCount, totalCount, CV_32F);
75     }
76 
77 protected:
run(int)78     void run(int)
79     {
80         mpegTest();
81         displayMPEGResults();
82     }
83 
convertContourType(const Mat & currentQuery) const84     vector<PointType> convertContourType(const Mat& currentQuery) const
85     {
86         vector<vector<Point> > _contoursQuery;
87         findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE);
88 
89         vector <PointType> contoursQuery;
90         for (size_t border=0; border<_contoursQuery.size(); border++)
91         {
92             for (size_t p=0; p<_contoursQuery[border].size(); p++)
93             {
94                 contoursQuery.push_back(PointType((T)_contoursQuery[border][p].x,
95                                                   (T)_contoursQuery[border][p].y));
96             }
97         }
98 
99         // In case actual number of points is less than n
100         for (int add=(int)contoursQuery.size()-1; add<NP; add++)
101         {
102             contoursQuery.push_back(contoursQuery[contoursQuery.size()-add+1]); //adding dummy values
103         }
104 
105         // Uniformly sampling
106         random_shuffle(contoursQuery.begin(), contoursQuery.end());
107         int nStart=NP;
108         vector<PointType> cont;
109         for (int i=0; i<nStart; i++)
110         {
111             cont.push_back(contoursQuery[i]);
112         }
113         return cont;
114     }
115 
mpegTest()116     void mpegTest()
117     {
118         // query contours (normal v flipped, h flipped) and testing contour
119         vector<PointType> contoursQuery1, contoursQuery2, contoursQuery3, contoursTesting;
120         // reading query and computing its properties
121         for (vector<string>::const_iterator a = filenames.begin(); a != filenames.end(); ++a)
122         {
123             // read current image
124             int aIndex = (int)(a - filenames.begin());
125             Mat currentQuery = imread(*a, IMREAD_GRAYSCALE);
126             Mat flippedHQuery, flippedVQuery;
127             flip(currentQuery, flippedHQuery, 0);
128             flip(currentQuery, flippedVQuery, 1);
129             // compute border of the query and its flipped versions
130             contoursQuery1=convertContourType(currentQuery);
131             contoursQuery2=convertContourType(flippedHQuery);
132             contoursQuery3=convertContourType(flippedVQuery);
133             // compare with all the rest of the images: testing
134             for (vector<string>::const_iterator b = filenames.begin(); b != filenames.end(); ++b)
135             {
136                 int bIndex = (int)(b - filenames.begin());
137                 float distance = 0;
138                 // skip self-comparisson
139                 if (a != b)
140                 {
141                     // read testing image
142                     Mat currentTest = imread(*b, IMREAD_GRAYSCALE);
143                     // compute border of the testing
144                     contoursTesting=convertContourType(currentTest);
145                     // compute shape distance
146                     distance = cmp(contoursQuery1, contoursQuery2,
147                                    contoursQuery3, contoursTesting);
148                 }
149                 distanceMat.at<float>(aIndex, bIndex) = distance;
150             }
151         }
152     }
153 
displayMPEGResults()154     void displayMPEGResults()
155     {
156         const int FIRST_MANY=2*NSN;
157 
158         int corrects=0;
159         int divi=0;
160         for (int row=0; row<distanceMat.rows; row++)
161         {
162             if (row%NSN==0) //another group
163             {
164                 divi+=NSN;
165             }
166             for (int col=divi-NSN; col<divi; col++)
167             {
168                 int nsmall=0;
169                 for (int i=0; i<distanceMat.cols; i++)
170                 {
171                     if (distanceMat.at<float>(row,col) > distanceMat.at<float>(row,i))
172                     {
173                         nsmall++;
174                     }
175                 }
176                 if (nsmall<=FIRST_MANY)
177                 {
178                     corrects++;
179                 }
180             }
181         }
182         float porc = 100*float(corrects)/(NSN*distanceMat.rows);
183         std::cout << "Test result: " << porc << "%" << std::endl;
184         if (porc >= CURRENT_MAX_ACCUR)
185             ts->set_failed_test_info(cvtest::TS::OK);
186         else
187             ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
188     }
189 
190 protected:
191     int NSN;
192     int NP;
193     float CURRENT_MAX_ACCUR;
194     vector<string> filenames;
195     Mat distanceMat;
196     compute cmp;
197 };
198 
199 //------------------------------------------------------------------------
200 //                       Test Shape_SCD.regression
201 //------------------------------------------------------------------------
202 
203 class computeShapeDistance_Chi
204 {
205     Ptr <ShapeContextDistanceExtractor> mysc;
206 public:
computeShapeDistance_Chi()207     computeShapeDistance_Chi()
208     {
209         const int angularBins=12;
210         const int radialBins=4;
211         const float minRad=0.2f;
212         const float maxRad=2;
213         mysc = createShapeContextDistanceExtractor(angularBins, radialBins, minRad, maxRad);
214         mysc->setIterations(1);
215         mysc->setCostExtractor(createChiHistogramCostExtractor(30,0.15f));
216         mysc->setTransformAlgorithm( createThinPlateSplineShapeTransformer() );
217     }
operator ()(vector<Point2f> & query1,vector<Point2f> & query2,vector<Point2f> & query3,vector<Point2f> & testq)218     float operator()(vector <Point2f>& query1, vector <Point2f>& query2,
219                      vector <Point2f>& query3, vector <Point2f>& testq)
220     {
221         return std::min(mysc->computeDistance(query1, testq),
222                         std::min(mysc->computeDistance(query2, testq),
223                                  mysc->computeDistance(query3, testq)));
224     }
225 };
226 
TEST(Shape_SCD,regression)227 TEST(Shape_SCD, regression)
228 {
229     const int NSN_val=5;//10;//20; //number of shapes per class
230     const int NP_val=120; //number of points simplifying the contour
231     const float CURRENT_MAX_ACCUR_val=95; //99% and 100% reached in several tests, 95 is fixed as minimum boundary
232     ShapeBaseTest<float, computeShapeDistance_Chi> test(NSN_val, NP_val, CURRENT_MAX_ACCUR_val);
233     test.safe_run();
234 }
235 
236 //------------------------------------------------------------------------
237 //                       Test ShapeEMD_SCD.regression
238 //------------------------------------------------------------------------
239 
240 class computeShapeDistance_EMD
241 {
242     Ptr <ShapeContextDistanceExtractor> mysc;
243 public:
computeShapeDistance_EMD()244     computeShapeDistance_EMD()
245     {
246         const int angularBins=12;
247         const int radialBins=4;
248         const float minRad=0.2f;
249         const float maxRad=2;
250         mysc = createShapeContextDistanceExtractor(angularBins, radialBins, minRad, maxRad);
251         mysc->setIterations(1);
252         mysc->setCostExtractor( createEMDL1HistogramCostExtractor() );
253         mysc->setTransformAlgorithm( createThinPlateSplineShapeTransformer() );
254     }
operator ()(vector<Point2f> & query1,vector<Point2f> & query2,vector<Point2f> & query3,vector<Point2f> & testq)255     float operator()(vector <Point2f>& query1, vector <Point2f>& query2,
256                      vector <Point2f>& query3, vector <Point2f>& testq)
257     {
258         return std::min(mysc->computeDistance(query1, testq),
259                         std::min(mysc->computeDistance(query2, testq),
260                                  mysc->computeDistance(query3, testq)));
261     }
262 };
263 
TEST(ShapeEMD_SCD,regression)264 TEST(ShapeEMD_SCD, regression)
265 {
266     const int NSN_val=5;//10;//20; //number of shapes per class
267     const int NP_val=100; //number of points simplifying the contour
268     const float CURRENT_MAX_ACCUR_val=95; //98% and 99% reached in several tests, 95 is fixed as minimum boundary
269     ShapeBaseTest<float, computeShapeDistance_EMD> test(NSN_val, NP_val, CURRENT_MAX_ACCUR_val);
270     test.safe_run();
271 }
272 
273 //------------------------------------------------------------------------
274 //                       Test Hauss.regression
275 //------------------------------------------------------------------------
276 
277 class computeShapeDistance_Haussdorf
278 {
279     Ptr <HausdorffDistanceExtractor> haus;
280 public:
computeShapeDistance_Haussdorf()281     computeShapeDistance_Haussdorf()
282     {
283         haus = createHausdorffDistanceExtractor();
284     }
operator ()(vector<Point> & query1,vector<Point> & query2,vector<Point> & query3,vector<Point> & testq)285     float operator()(vector<Point> &query1, vector<Point> &query2,
286                      vector<Point> &query3, vector<Point> &testq)
287     {
288         return std::min(haus->computeDistance(query1,testq),
289                         std::min(haus->computeDistance(query2,testq),
290                                  haus->computeDistance(query3,testq)));
291     }
292 };
293 
TEST(Hauss,regression)294 TEST(Hauss, regression)
295 {
296     const int NSN_val=5;//10;//20; //number of shapes per class
297     const int NP_val = 180; //number of points simplifying the contour
298     const float CURRENT_MAX_ACCUR_val=85; //90% and 91% reached in several tests, 85 is fixed as minimum boundary
299     ShapeBaseTest<int, computeShapeDistance_Haussdorf> test(NSN_val, NP_val, CURRENT_MAX_ACCUR_val);
300     test.safe_run();
301 }
302