Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/features2d/test/test_descriptors_regression.impl.hpp
16354 views
1
// This file is part of OpenCV project.
2
// It is subject to the license terms in the LICENSE file found in the top-level directory
3
// of this distribution and at http://opencv.org/license.html
4
5
namespace opencv_test { namespace {
6
7
/****************************************************************************************\
8
* Regression tests for descriptor extractors. *
9
\****************************************************************************************/
10
static void writeMatInBin( const Mat& mat, const string& filename )
11
{
12
FILE* f = fopen( filename.c_str(), "wb");
13
if( f )
14
{
15
CV_Assert(4 == sizeof(int));
16
int type = mat.type();
17
fwrite( (void*)&mat.rows, sizeof(int), 1, f );
18
fwrite( (void*)&mat.cols, sizeof(int), 1, f );
19
fwrite( (void*)&type, sizeof(int), 1, f );
20
int dataSize = (int)(mat.step * mat.rows);
21
fwrite( (void*)&dataSize, sizeof(int), 1, f );
22
fwrite( (void*)mat.ptr(), 1, dataSize, f );
23
fclose(f);
24
}
25
}
26
27
static Mat readMatFromBin( const string& filename )
28
{
29
FILE* f = fopen( filename.c_str(), "rb" );
30
if( f )
31
{
32
CV_Assert(4 == sizeof(int));
33
int rows, cols, type, dataSize;
34
size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f );
35
size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f );
36
size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f );
37
size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f );
38
CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1);
39
40
int step = dataSize / rows / CV_ELEM_SIZE(type);
41
CV_Assert(step >= cols);
42
43
Mat returnMat = Mat(rows, step, type).colRange(0, cols);
44
45
size_t elements_read = fread( returnMat.ptr(), 1, dataSize, f );
46
CV_Assert(elements_read == (size_t)(dataSize));
47
48
fclose(f);
49
50
return returnMat;
51
}
52
return Mat();
53
}
54
55
template<class Distance>
56
class CV_DescriptorExtractorTest : public cvtest::BaseTest
57
{
58
public:
59
typedef typename Distance::ValueType ValueType;
60
typedef typename Distance::ResultType DistanceType;
61
62
CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr<DescriptorExtractor>& _dextractor,
63
Distance d = Distance(), Ptr<FeatureDetector> _detector = Ptr<FeatureDetector>()):
64
name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) , detector(_detector) {}
65
66
~CV_DescriptorExtractorTest()
67
{
68
}
69
protected:
70
virtual void createDescriptorExtractor() {}
71
72
void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors )
73
{
74
if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() )
75
{
76
ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n");
77
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
78
return;
79
}
80
81
CV_Assert( DataType<ValueType>::type == validDescriptors.type() );
82
83
int dimension = validDescriptors.cols;
84
DistanceType curMaxDist = 0;
85
size_t exact_count = 0, failed_count = 0;
86
for( int y = 0; y < validDescriptors.rows; y++ )
87
{
88
DistanceType dist = distance( validDescriptors.ptr<ValueType>(y), calcDescriptors.ptr<ValueType>(y), dimension );
89
if (dist == 0)
90
exact_count++;
91
if( dist > curMaxDist )
92
{
93
if (dist > maxDist)
94
failed_count++;
95
curMaxDist = dist;
96
}
97
#if 0
98
if (dist > 0)
99
{
100
std::cout << "i=" << y << " fail_count=" << failed_count << " dist=" << dist << std::endl;
101
std::cout << "valid: " << validDescriptors.row(y) << std::endl;
102
std::cout << " calc: " << calcDescriptors.row(y) << std::endl;
103
}
104
#endif
105
}
106
107
float exact_percents = (100 * (float)exact_count / validDescriptors.rows);
108
float failed_percents = (100 * (float)failed_count / validDescriptors.rows);
109
std::stringstream ss;
110
ss << "Exact count (dist == 0): " << exact_count << " (" << (int)exact_percents << "%)" << std::endl
111
<< "Failed count (dist > " << maxDist << "): " << failed_count << " (" << (int)failed_percents << "%)" << std::endl
112
<< "Max distance between valid and computed descriptors (" << validDescriptors.size() << "): " << curMaxDist;
113
EXPECT_LE(failed_percents, 20.0f);
114
std::cout << ss.str() << std::endl;
115
}
116
117
void emptyDataTest()
118
{
119
assert( dextractor );
120
121
// One image.
122
Mat image;
123
vector<KeyPoint> keypoints;
124
Mat descriptors;
125
126
try
127
{
128
dextractor->compute( image, keypoints, descriptors );
129
}
130
catch(...)
131
{
132
ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n");
133
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
134
}
135
136
RNG rng;
137
image = cvtest::randomMat(rng, Size(50, 50), CV_8UC3, 0, 255, false);
138
try
139
{
140
dextractor->compute( image, keypoints, descriptors );
141
}
142
catch(...)
143
{
144
ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n");
145
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
146
}
147
148
// Several images.
149
vector<Mat> images;
150
vector<vector<KeyPoint> > keypointsCollection;
151
vector<Mat> descriptorsCollection;
152
try
153
{
154
dextractor->compute( images, keypointsCollection, descriptorsCollection );
155
}
156
catch(...)
157
{
158
ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n");
159
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
160
}
161
}
162
163
void regressionTest()
164
{
165
assert( dextractor );
166
167
// Read the test image.
168
string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME;
169
Mat img = imread( imgFilename );
170
if( img.empty() )
171
{
172
ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() );
173
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
174
return;
175
}
176
const std::string keypoints_filename = string(ts->get_data_path()) +
177
(detector.empty()
178
? (FEATURES2D_DIR + "/" + std::string("keypoints.xml.gz"))
179
: (DESCRIPTOR_DIR + "/" + name + "_keypoints.xml.gz"));
180
FileStorage fs(keypoints_filename, FileStorage::READ);
181
182
vector<KeyPoint> keypoints;
183
EXPECT_TRUE(fs.isOpened()) << "Keypoint testdata is missing. Re-computing and re-writing keypoints testdata...";
184
if (!fs.isOpened())
185
{
186
fs.open(keypoints_filename, FileStorage::WRITE);
187
ASSERT_TRUE(fs.isOpened()) << "File for writing keypoints can not be opened.";
188
if (detector.empty())
189
{
190
Ptr<ORB> fd = ORB::create();
191
fd->detect(img, keypoints);
192
}
193
else
194
{
195
detector->detect(img, keypoints);
196
}
197
write(fs, "keypoints", keypoints);
198
fs.release();
199
}
200
else
201
{
202
read(fs.getFirstTopLevelNode(), keypoints);
203
fs.release();
204
}
205
206
if(!detector.empty())
207
{
208
vector<KeyPoint> calcKeypoints;
209
detector->detect(img, calcKeypoints);
210
// TODO validate received keypoints
211
int diff = abs((int)calcKeypoints.size() - (int)keypoints.size());
212
if (diff > 0)
213
{
214
std::cout << "Keypoints difference: " << diff << std::endl;
215
EXPECT_LE(diff, (int)(keypoints.size() * 0.03f));
216
}
217
}
218
ASSERT_FALSE(keypoints.empty());
219
{
220
Mat calcDescriptors;
221
double t = (double)getTickCount();
222
dextractor->compute(img, keypoints, calcDescriptors);
223
t = getTickCount() - t;
224
ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)getTickFrequency()*1000.)/calcDescriptors.rows);
225
226
if (calcDescriptors.rows != (int)keypoints.size())
227
{
228
ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" );
229
ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() );
230
ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows );
231
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
232
return;
233
}
234
235
if (calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType())
236
{
237
ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" );
238
ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() );
239
ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols );
240
ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() );
241
ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() );
242
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
243
return;
244
}
245
246
// TODO read and write descriptor extractor parameters and check them
247
Mat validDescriptors = readDescriptors();
248
EXPECT_FALSE(validDescriptors.empty()) << "Descriptors testdata is missing. Re-writing descriptors testdata...";
249
if (!validDescriptors.empty())
250
{
251
compareDescriptors(validDescriptors, calcDescriptors);
252
}
253
else
254
{
255
ASSERT_TRUE(writeDescriptors(calcDescriptors)) << "Descriptors can not be written.";
256
}
257
}
258
}
259
260
void run(int)
261
{
262
createDescriptorExtractor();
263
if( !dextractor )
264
{
265
ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n");
266
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
267
return;
268
}
269
270
emptyDataTest();
271
regressionTest();
272
273
ts->set_failed_test_info( cvtest::TS::OK );
274
}
275
276
virtual Mat readDescriptors()
277
{
278
Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );
279
return res;
280
}
281
282
virtual bool writeDescriptors( Mat& descs )
283
{
284
writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );
285
return true;
286
}
287
288
string name;
289
const DistanceType maxDist;
290
Ptr<DescriptorExtractor> dextractor;
291
Distance distance;
292
Ptr<FeatureDetector> detector;
293
294
private:
295
CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; }
296
};
297
298
}} // namespace
299
300