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