Skip to content

Commit 940d910

Browse files
authored
Merge pull request #3277 from iago-suarez:4.x
Add BAD descriptor to xfeatures2d module * Adding new BAD descriptor to xfeatures2d module * Changing BAD name by TEBLID and using int threshold again for BEBLID * Changing link to AKAZE tutorial and moved parameters initialization to ::create() * Adding f suffixes to floating-point parameters and using arrays again
1 parent 3951e03 commit 940d910

File tree

11 files changed

+495
-29
lines changed

11 files changed

+495
-29
lines changed

modules/xfeatures2d/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ Extra 2D Features Framework
55
2. Non-free 2D feature algorithms
66

77
Extra 2D Features Framework containing experimental and non-free 2D feature detector/descriptor algorithms:
8-
SURF, BRIEF, Censure, Freak, LUCID, Daisy, BEBLID, Self-similar.
8+
SURF, BRIEF, Censure, Freak, LUCID, Daisy, BEBLID, TEBLID, Self-similar.

modules/xfeatures2d/doc/xfeatures2d.bib

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,18 @@ @article{Suarez2020BEBLID
154154
author = {Iago Su\'arez and Ghesn Sfeir and Jos\'e M. Buenaposada and Luis Baumela},
155155
}
156156

157+
@article{Suarez2021TEBLID,
158+
title = {Revisiting Binary Local Image Description for Resource Limited Devices},
159+
journal = {IEEE Robotics and Automation Letters},
160+
volume = {6},
161+
pages = {8317--8324},
162+
year = {2021},
163+
number = {4},
164+
doi = {https://doi.org/10.1109/LRA.2021.3107024},
165+
url = {https://arxiv.org/pdf/2108.08380.pdf},
166+
author = {Iago Su\'arez and Jos\'e M. Buenaposada and Luis Baumela},
167+
}
168+
157169
@inproceedings{winder2007learning,
158170
title= {Learning Local Image Descriptors},
159171
author= {Winder, Simon AJ and Brown, Matthew},

modules/xfeatures2d/include/opencv2/xfeatures2d.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,49 @@ class CV_EXPORTS_W BEBLID : public Feature2D
224224
CV_WRAP static Ptr<BEBLID> create(float scale_factor, int n_bits = BEBLID::SIZE_512_BITS);
225225
};
226226

227+
/** @brief Class implementing TEBLID (Triplet-based Efficient Binary Local Image Descriptor),
228+
* described in @cite Suarez2021TEBLID.
229+
230+
TEBLID stands for Triplet-based Efficient Binary Local Image Descriptor, although originally it was called BAD
231+
\cite Suarez2021TEBLID. It is an improvement over BEBLID \cite Suarez2020BEBLID, that uses triplet loss,
232+
hard negative mining, and anchor swap to improve the image matching results.
233+
It is able to describe keypoints from any detector just by changing the scale_factor parameter.
234+
TEBLID is as efficient as ORB, BEBLID or BRISK, but the triplet-based training objective selected more
235+
discriminative features that explain the accuracy gain. It is also more compact than BEBLID,
236+
when running the [AKAZE example](https://github.com/opencv/opencv/blob/4.x/samples/cpp/tutorial_code/features2D/AKAZE_match.cpp)
237+
with 10000 keypoints detected by ORB, BEBLID obtains 561 inliers (75%) with 512 bits, whereas
238+
TEBLID obtains 621 (75.2%) with 256 bits. ORB obtains only 493 inliers (63%).
239+
240+
If you find this code useful, please add a reference to the following paper:
241+
<BLOCKQUOTE> Iago Suárez, José M. Buenaposada, and Luis Baumela.
242+
Revisiting Binary Local Image Description for Resource Limited Devices.
243+
IEEE Robotics and Automation Letters, vol. 6, no. 4, pp. 8317-8324, Oct. 2021. </BLOCKQUOTE>
244+
245+
The descriptor was trained in Liberty split of the UBC datasets \cite winder2007learning .
246+
*/
247+
class CV_EXPORTS_W TEBLID : public Feature2D
248+
{
249+
public:
250+
/**
251+
* @brief Descriptor number of bits, each bit is a box average difference.
252+
* The user can choose between 256 or 512 bits.
253+
*/
254+
enum TeblidSize
255+
{
256+
SIZE_256_BITS = 102, SIZE_512_BITS = 103,
257+
};
258+
/** @brief Creates the TEBLID descriptor.
259+
@param scale_factor Adjust the sampling window around detected keypoints:
260+
- <b> 1.00f </b> should be the scale for ORB keypoints
261+
- <b> 6.75f </b> should be the scale for SIFT detected keypoints
262+
- <b> 6.25f </b> is default and fits for KAZE, SURF detected keypoints
263+
- <b> 5.00f </b> should be the scale for AKAZE, MSD, AGAST, FAST, BRISK keypoints
264+
@param n_bits Determine the number of bits in the descriptor. Should be either
265+
TEBLID::SIZE_256_BITS or TEBLID::SIZE_512_BITS.
266+
*/
267+
CV_WRAP static Ptr<TEBLID> create(float scale_factor, int n_bits = TEBLID::SIZE_256_BITS);
268+
};
269+
227270
/** @brief Class implementing DAISY descriptor, described in @cite Tola10
228271
229272
@param radius radius of the descriptor at the initial scale
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
#include "perf_precomp.hpp"
5+
6+
namespace opencv_test { namespace {
7+
8+
typedef perf::TestBaseWithParam<std::string> teblid;
9+
10+
#define TEBLID_IMAGES \
11+
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
12+
"stitching/a3.png"
13+
14+
#ifdef OPENCV_ENABLE_NONFREE
15+
PERF_TEST_P(teblid, extract, testing::Values(TEBLID_IMAGES))
16+
{
17+
string filename = getDataPath(GetParam());
18+
Mat frame = imread(filename, IMREAD_GRAYSCALE);
19+
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
20+
21+
Mat mask;
22+
declare.in(frame).time(90);
23+
24+
Ptr<SURF> detector = SURF::create();
25+
vector<KeyPoint> points;
26+
detector->detect(frame, points, mask);
27+
28+
Ptr<TEBLID> descriptor = TEBLID::create(6.25f);
29+
cv::Mat descriptors;
30+
TEST_CYCLE() descriptor->compute(frame, points, descriptors);
31+
32+
SANITY_CHECK_NOTHING();
33+
}
34+
#endif // NONFREE
35+
36+
}} // namespace

modules/xfeatures2d/src/beblid.cpp

Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,21 @@ struct ABWLParams
3030
{
3131
int x1, y1, x2, y2, boxRadius, th;
3232
};
33+
// Same as previous with floating point threshold
34+
struct ABWLParamsFloatTh
35+
{
36+
int x1, y1, x2, y2, boxRadius;
37+
float th;
38+
};
3339

3440
// BEBLID implementation
41+
template <class WeakLearnerT>
3542
class BEBLID_Impl CV_FINAL: public BEBLID
3643
{
3744
public:
3845

3946
// constructor
40-
explicit BEBLID_Impl(float scale_factor, int n_bits = SIZE_512_BITS);
47+
explicit BEBLID_Impl(float scale_factor, const std::vector<WeakLearnerT>& wl_params);
4148

4249
// destructor
4350
~BEBLID_Impl() CV_OVERRIDE = default;
@@ -55,15 +62,65 @@ class BEBLID_Impl CV_FINAL: public BEBLID
5562
void compute(InputArray image, vector<KeyPoint> &keypoints, OutputArray descriptors) CV_OVERRIDE;
5663

5764
private:
58-
std::vector<ABWLParams> wl_params_;
65+
std::vector<WeakLearnerT> wl_params_;
5966
float scale_factor_;
6067
cv::Size patch_size_;
6168

62-
void computeBEBLID(const cv::Mat &integralImg,
63-
const std::vector<cv::KeyPoint> &keypoints,
64-
cv::Mat &descriptors);
69+
void computeBoxDiffsDescriptor(const cv::Mat &integralImg,
70+
const std::vector<cv::KeyPoint> &keypoints,
71+
cv::Mat &descriptors);
6572
}; // END BEBLID_Impl CLASS
6673

74+
75+
// TEBLID implementation
76+
class TEBLID_Impl CV_FINAL: public TEBLID
77+
{
78+
public:
79+
80+
// constructor
81+
explicit TEBLID_Impl(float scale_factor, const std::vector<ABWLParamsFloatTh>& wl_params) :
82+
impl(scale_factor, wl_params){}
83+
84+
// destructor
85+
~TEBLID_Impl() CV_OVERRIDE = default;
86+
87+
// returns the descriptor length in bytes
88+
int descriptorSize() const CV_OVERRIDE { return impl.descriptorSize(); }
89+
90+
// returns the descriptor type
91+
int descriptorType() const CV_OVERRIDE { return impl.descriptorType(); }
92+
93+
// returns the default norm type
94+
int defaultNorm() const CV_OVERRIDE { return impl.defaultNorm(); }
95+
96+
// compute descriptors given keypoints
97+
void compute(InputArray image, vector<KeyPoint> &keypoints, OutputArray descriptors) CV_OVERRIDE
98+
{
99+
impl.compute(image, keypoints, descriptors);
100+
}
101+
102+
private:
103+
BEBLID_Impl<ABWLParamsFloatTh> impl;
104+
}; // END TEBLID_Impl CLASS
105+
106+
Ptr<TEBLID> TEBLID::create(float scale_factor, int n_bits)
107+
{
108+
if (n_bits == TEBLID::SIZE_512_BITS)
109+
{
110+
#include "teblid.p512.hpp"
111+
return makePtr<TEBLID_Impl>(scale_factor, teblid_wl_params_512);
112+
}
113+
else if(n_bits == TEBLID::SIZE_256_BITS)
114+
{
115+
#include "teblid.p256.hpp"
116+
return makePtr<TEBLID_Impl>(scale_factor, teblid_wl_params_256);
117+
}
118+
else
119+
{
120+
CV_Error(Error::StsBadArg, "n_bits should be either TEBLID::SIZE_512_BITS or TEBLID::SIZE_256_BITS");
121+
}
122+
}
123+
67124
/**
68125
* @brief Function that determines if a keypoint is close to the image border.
69126
* @param kp The detected keypoint
@@ -100,8 +157,9 @@ static inline bool isKeypointInTheBorder(const cv::KeyPoint &kp,
100157
* @param scaleFactor A scale factor that magnifies the measurement functions w.r.t. the keypoint.
101158
* @param patchSize The size of the normalized patch where the measurement functions were learnt.
102159
*/
103-
static inline void rectifyABWL(const std::vector<ABWLParams> &wlPatchParams,
104-
std::vector<ABWLParams> &wlImageParams,
160+
template< typename WeakLearnerT>
161+
static inline void rectifyABWL(const std::vector<WeakLearnerT> &wlPatchParams,
162+
std::vector<WeakLearnerT> &wlImageParams,
105163
const cv::KeyPoint &kp,
106164
float scaleFactor = 1,
107165
const cv::Size &patchSize = cv::Size(32, 32))
@@ -151,7 +209,8 @@ static inline void rectifyABWL(const std::vector<ABWLParams> &wlPatchParams,
151209
* @param integralImage The integral image used to compute the average gray value in the square regions.
152210
* @return The difference of gray level in the two squares defined by wlImageParams
153211
*/
154-
static inline float computeABWLResponse(const ABWLParams &wlImageParams,
212+
template <typename WeakLearnerT>
213+
static inline float computeABWLResponse(const WeakLearnerT &wlImageParams,
155214
const cv::Mat &integralImage)
156215
{
157216
CV_DbgAssert(!integralImage.empty());
@@ -239,7 +298,8 @@ static inline float computeABWLResponse(const ABWLParams &wlImageParams,
239298
}
240299

241300
// descriptor computation using keypoints
242-
void BEBLID_Impl::compute(InputArray _image, vector<KeyPoint> &keypoints, OutputArray _descriptors)
301+
template <class WeakLearnerT>
302+
void BEBLID_Impl<WeakLearnerT>::compute(InputArray _image, vector<KeyPoint> &keypoints, OutputArray _descriptors)
243303
{
244304
Mat image = _image.getMat();
245305

@@ -281,27 +341,21 @@ void BEBLID_Impl::compute(InputArray _image, vector<KeyPoint> &keypoints, Output
281341
CV_DbgAssert(descriptors.type() == CV_8UC1);
282342

283343
// Compute the BEBLID descriptors
284-
computeBEBLID(integralImg, keypoints, descriptors);
344+
computeBoxDiffsDescriptor(integralImg, keypoints, descriptors);
285345
}
286346

287347
// constructor
288-
BEBLID_Impl::BEBLID_Impl(float scale_factor, int n_bits)
289-
: scale_factor_(scale_factor), patch_size_(32, 32)
348+
template <class WeakLearnerT>
349+
BEBLID_Impl<WeakLearnerT>::BEBLID_Impl(float scale_factor, const std::vector<WeakLearnerT>& wl_params)
350+
: wl_params_(wl_params), scale_factor_(scale_factor),patch_size_(32, 32)
290351
{
291-
#include "beblid.p512.hpp"
292-
#include "beblid.p256.hpp"
293-
if (n_bits == SIZE_512_BITS)
294-
wl_params_.assign(wl_params_512, wl_params_512 + sizeof(wl_params_512) / sizeof(wl_params_512[0]));
295-
else if(n_bits == SIZE_256_BITS)
296-
wl_params_.assign(wl_params_256, wl_params_256 + sizeof(wl_params_256) / sizeof(wl_params_256[0]));
297-
else
298-
CV_Error(Error::StsBadArg, "n_wls should be either SIZE_512_BITS or SIZE_256_BITS");
299352
}
300353

301354
// Internal function that implements the core of BEBLID descriptor
302-
void BEBLID_Impl::computeBEBLID(const cv::Mat &integralImg,
303-
const std::vector<cv::KeyPoint> &keypoints,
304-
cv::Mat &descriptors)
355+
template<class WeakLearnerT>
356+
void BEBLID_Impl<WeakLearnerT>::computeBoxDiffsDescriptor(const cv::Mat &integralImg,
357+
const std::vector<cv::KeyPoint> &keypoints,
358+
cv::Mat &descriptors)
305359
{
306360
CV_DbgAssert(!integralImg.empty());
307361
CV_DbgAssert(size_t(descriptors.rows) == keypoints.size());
@@ -316,13 +370,13 @@ void BEBLID_Impl::computeBEBLID(const cv::Mat &integralImg,
316370
#endif
317371
{
318372
// Get a pointer to the first element in the range
319-
ABWLParams *wl;
373+
WeakLearnerT *wl;
320374
float responseFun;
321375
int areaResponseFun, kpIdx;
322376
size_t wlIdx;
323377
int box1x1, box1y1, box1x2, box1y2, box2x1, box2y1, box2x2, box2y2, bit_idx, side;
324378
uchar byte = 0;
325-
std::vector<ABWLParams> imgWLParams(wl_params_.size());
379+
std::vector<WeakLearnerT> imgWLParams(wl_params_.size());
326380
uchar *d = &descriptors.at<uchar>(range.start, 0);
327381

328382
for (kpIdx = range.start; kpIdx < range.end; kpIdx++)
@@ -397,7 +451,20 @@ void BEBLID_Impl::computeBEBLID(const cv::Mat &integralImg,
397451

398452
Ptr<BEBLID> BEBLID::create(float scale_factor, int n_bits)
399453
{
400-
return makePtr<BEBLID_Impl>(scale_factor, n_bits);
454+
if (n_bits == BEBLID::SIZE_512_BITS)
455+
{
456+
#include "beblid.p512.hpp"
457+
return makePtr<BEBLID_Impl<ABWLParams>>(scale_factor, beblid_wl_params_512);
458+
}
459+
else if(n_bits == BEBLID::SIZE_256_BITS)
460+
{
461+
#include "beblid.p256.hpp"
462+
return makePtr<BEBLID_Impl<ABWLParams>>(scale_factor, beblid_wl_params_256);
463+
}
464+
else
465+
{
466+
CV_Error(Error::StsBadArg, "n_bits should be either BEBLID::SIZE_512_BITS or BEBLID::SIZE_256_BITS");
467+
}
401468
}
402469
} // END NAMESPACE XFEATURES2D
403470
} // END NAMESPACE CV

modules/xfeatures2d/src/beblid.p256.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
// Pre-trained parameters of BEBLID-256 trained in Liberty data set with
1414
// a million of patch pairs, 20% positives and 80% negatives
15-
static const ABWLParams wl_params_256[] = {
15+
static const ABWLParams beblid_wl_params_256_[] = {
1616
{26, 20, 14, 16, 5, 16}, {17, 17, 15, 15, 2, 7}, {18, 16, 8, 13, 3, 18},
1717
{19, 15, 13, 14, 3, 17}, {16, 16, 5, 15, 4, 10}, {25, 10, 16, 16, 6, 11},
1818
{16, 15, 12, 15, 1, 12}, {18, 17, 14, 17, 1, 13}, {15, 14, 5, 21, 5, 6}, {14, 14, 11, 7, 4, 2},
@@ -79,3 +79,5 @@ static const ABWLParams wl_params_256[] = {
7979
{2, 14, 1, 9, 1, 1}, {6, 25, 6, 21, 1, 1}, {6, 2, 2, 1, 1, 1}, {30, 19, 29, 20, 1, 0},
8080
{25, 21, 23, 20, 1, 0}, {16, 10, 16, 9, 1, 0}
8181
};
82+
static const std::vector<ABWLParams> beblid_wl_params_256(std::begin(beblid_wl_params_256_),
83+
std::end(beblid_wl_params_256_));

modules/xfeatures2d/src/beblid.p512.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
// Pre-trained parameters of BEBLID-512 trained in Liberty data set with
1414
// a million of patch pairs, 20% positives and 80% negatives
15-
static const ABWLParams wl_params_512[] = {
15+
static const ABWLParams beblid_wl_params_512_[] = {
1616
{24, 18, 15, 17, 6, 13}, {19, 14, 13, 17, 2, 18}, {23, 19, 12, 15, 6, 19},
1717
{24, 14, 16, 16, 6, 11}, {16, 15, 12, 16, 1, 12}, {16, 15, 7, 10, 4, 10},
1818
{17, 12, 8, 17, 3, 16}, {24, 12, 11, 17, 7, 19}, {19, 17, 14, 11, 3, 13},
@@ -144,3 +144,5 @@ static const ABWLParams wl_params_512[] = {
144144
{26, 4, 26, 1, 1, 0}, {5, 21, 2, 20, 1, -1}, {14, 1, 13, 3, 1, 1}, {30, 9, 28, 8, 1, 0},
145145
{13, 15, 12, 12, 1, 1}, {7, 23, 6, 25, 1, -1}
146146
};
147+
static const std::vector<ABWLParams> beblid_wl_params_512(std::begin(beblid_wl_params_512_),
148+
std::end(beblid_wl_params_512_));

0 commit comments

Comments
 (0)