Commit e76ca3ee authored by Vadim Pisarevsky's avatar Vadim Pisarevsky

Merge pull request #207 from cbalint13/brief

Add rotation invariance option for BRIEF descriptor.
parents 860eda95 ee4a9c8d
...@@ -118,15 +118,14 @@ public: ...@@ -118,15 +118,14 @@ public:
/** @brief Class for computing BRIEF descriptors described in @cite calon2010 . /** @brief Class for computing BRIEF descriptors described in @cite calon2010 .
@note @param bytes legth of the descriptor in bytes, valid values are: 16, 32 (default) or 64 .
- A complete BRIEF extractor sample can be found at @param use_orientation sample patterns using keypoints orientation, disabled by default.
opencv_source_code/samples/cpp/brief_match_test.cpp
*/ */
class CV_EXPORTS_W BriefDescriptorExtractor : public Feature2D class CV_EXPORTS_W BriefDescriptorExtractor : public Feature2D
{ {
public: public:
CV_WRAP static Ptr<BriefDescriptorExtractor> create( int bytes = 32 ); static Ptr<BriefDescriptorExtractor> create( int bytes = 32, bool use_orientation = false );
}; };
/** @brief Class implementing the locally uniform comparison image descriptor, described in @cite LUCID /** @brief Class implementing the locally uniform comparison image descriptor, described in @cite LUCID
......
...@@ -61,7 +61,7 @@ public: ...@@ -61,7 +61,7 @@ public:
enum { PATCH_SIZE = 48, KERNEL_SIZE = 9 }; enum { PATCH_SIZE = 48, KERNEL_SIZE = 9 };
// bytes is a length of descriptor in bytes. It can be equal 16, 32 or 64 bytes. // bytes is a length of descriptor in bytes. It can be equal 16, 32 or 64 bytes.
BriefDescriptorExtractorImpl( int bytes = 32 ); BriefDescriptorExtractorImpl( int bytes = 32, bool use_orientation = false );
virtual void read( const FileNode& ); virtual void read( const FileNode& );
virtual void write( FileStorage& ) const; virtual void write( FileStorage& ) const;
...@@ -73,67 +73,103 @@ public: ...@@ -73,67 +73,103 @@ public:
virtual void compute(InputArray image, std::vector<KeyPoint>& keypoints, OutputArray descriptors); virtual void compute(InputArray image, std::vector<KeyPoint>& keypoints, OutputArray descriptors);
protected: protected:
typedef void(*PixelTestFn)(InputArray, const std::vector<KeyPoint>&, OutputArray); typedef void(*PixelTestFn)(InputArray, const std::vector<KeyPoint>&, OutputArray, bool use_orientation );
int bytes_; int bytes_;
bool use_orientation_;
PixelTestFn test_fn_; PixelTestFn test_fn_;
}; };
Ptr<BriefDescriptorExtractor> BriefDescriptorExtractor::create( int bytes ) Ptr<BriefDescriptorExtractor> BriefDescriptorExtractor::create( int bytes, bool use_orientation )
{ {
return makePtr<BriefDescriptorExtractorImpl>(bytes); return makePtr<BriefDescriptorExtractorImpl>(bytes, use_orientation );
} }
inline int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x) inline int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x, bool use_orientation, Matx21f R)
{ {
static const int HALF_KERNEL = BriefDescriptorExtractorImpl::KERNEL_SIZE / 2; static const int HALF_KERNEL = BriefDescriptorExtractorImpl::KERNEL_SIZE / 2;
int img_y = (int)(pt.pt.y + 0.5) + y; if ( use_orientation )
int img_x = (int)(pt.pt.x + 0.5) + x; {
int rx = (int)(((float)x)*R(1,0) - ((float)y)*R(0,0));
int ry = (int)(((float)x)*R(0,0) + ((float)y)*R(1,0));
if (rx > 24) rx = 24; if (rx < -24) rx = -24;
if (ry > 24) ry = 24; if (ry < -24) ry = -24;
x = rx; y = ry;
}
const int img_y = (int)(pt.pt.y + 0.5) + y;
const int img_x = (int)(pt.pt.x + 0.5) + x;
return sum.at<int>(img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1) return sum.at<int>(img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1)
- sum.at<int>(img_y + HALF_KERNEL + 1, img_x - HALF_KERNEL) - sum.at<int>(img_y + HALF_KERNEL + 1, img_x - HALF_KERNEL)
- sum.at<int>(img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1) - sum.at<int>(img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1)
+ sum.at<int>(img_y - HALF_KERNEL, img_x - HALF_KERNEL); + sum.at<int>(img_y - HALF_KERNEL, img_x - HALF_KERNEL);
} }
static void pixelTests16(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors) static void pixelTests16(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation )
{ {
Matx21f R;
Mat sum = _sum.getMat(), descriptors = _descriptors.getMat(); Mat sum = _sum.getMat(), descriptors = _descriptors.getMat();
for (int i = 0; i < (int)keypoints.size(); ++i) for (size_t i = 0; i < keypoints.size(); ++i)
{ {
uchar* desc = descriptors.ptr(i); uchar* desc = descriptors.ptr(static_cast<int>(i));
const KeyPoint& pt = keypoints[i]; const KeyPoint& pt = keypoints[i];
if ( use_orientation )
{
float angle = pt.angle;
angle *= (float)(CV_PI/180.f);
R(0,0) = sin(angle);
R(1,0) = cos(angle);
}
#include "generated_16.i" #include "generated_16.i"
} }
} }
static void pixelTests32(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors) static void pixelTests32(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation)
{ {
Matx21f R;
Mat sum = _sum.getMat(), descriptors = _descriptors.getMat(); Mat sum = _sum.getMat(), descriptors = _descriptors.getMat();
for (int i = 0; i < (int)keypoints.size(); ++i) for (size_t i = 0; i < keypoints.size(); ++i)
{ {
uchar* desc = descriptors.ptr(i); uchar* desc = descriptors.ptr(static_cast<int>(i));
const KeyPoint& pt = keypoints[i]; const KeyPoint& pt = keypoints[i];
if ( use_orientation )
{
float angle = pt.angle;
angle *= (float)(CV_PI / 180.f);
R(0,0) = sin(angle);
R(1,0) = cos(angle);
}
#include "generated_32.i" #include "generated_32.i"
} }
} }
static void pixelTests64(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors) static void pixelTests64(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation)
{ {
Matx21f R;
Mat sum = _sum.getMat(), descriptors = _descriptors.getMat(); Mat sum = _sum.getMat(), descriptors = _descriptors.getMat();
for (int i = 0; i < (int)keypoints.size(); ++i) for (size_t i = 0; i < keypoints.size(); ++i)
{ {
uchar* desc = descriptors.ptr(i); uchar* desc = descriptors.ptr(static_cast<int>(i));
const KeyPoint& pt = keypoints[i]; const KeyPoint& pt = keypoints[i];
if ( use_orientation )
{
float angle = pt.angle;
angle *= (float)(CV_PI/180.f);
R(0,0) = sin(angle);
R(1,0) = cos(angle);
}
#include "generated_64.i" #include "generated_64.i"
} }
} }
BriefDescriptorExtractorImpl::BriefDescriptorExtractorImpl(int bytes) : BriefDescriptorExtractorImpl::BriefDescriptorExtractorImpl(int bytes, bool use_orientation) :
bytes_(bytes), test_fn_(NULL) bytes_(bytes), test_fn_(NULL)
{ {
use_orientation_ = use_orientation;
switch (bytes) switch (bytes)
{ {
case 16: case 16:
...@@ -212,7 +248,7 @@ void BriefDescriptorExtractorImpl::compute(InputArray image, ...@@ -212,7 +248,7 @@ void BriefDescriptorExtractorImpl::compute(InputArray image,
descriptors.create((int)keypoints.size(), bytes_, CV_8U); descriptors.create((int)keypoints.size(), bytes_, CV_8U);
descriptors.setTo(Scalar::all(0)); descriptors.setTo(Scalar::all(0));
test_fn_(sum, keypoints, descriptors); test_fn_(sum, keypoints, descriptors, use_orientation_);
} }
} }
......
// Code generated with '$ scripts/generate_code.py src/test_pairs.txt 16' // Code generated with '$ scripts/generate_code.py src/test_pairs.txt 16'
#define SMOOTHED(y,x) smoothedSum(sum, pt, y, x) #define SMOOTHED(y,x) smoothedSum(sum, pt, y, x, use_orientation, R)
desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0)); desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0));
desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0)); desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0));
desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0)); desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0));
......
// Code generated with '$ scripts/generate_code.py src/test_pairs.txt 32' // Code generated with '$ scripts/generate_code.py src/test_pairs.txt 32'
#define SMOOTHED(y,x) smoothedSum(sum, pt, y, x) #define SMOOTHED(y,x) smoothedSum(sum, pt, y, x, use_orientation, R)
desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0)); desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0));
desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0)); desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0));
desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0)); desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0));
......
// Code generated with '$ scripts/generate_code.py src/test_pairs.txt 64' // Code generated with '$ scripts/generate_code.py src/test_pairs.txt 64'
#define SMOOTHED(y,x) smoothedSum(sum, pt, y, x) #define SMOOTHED(y,x) smoothedSum(sum, pt, y, x, use_orientation, R)
desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0)); desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0));
desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0)); desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0));
desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0)); desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0));
......
...@@ -669,6 +669,32 @@ TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression) ...@@ -669,6 +669,32 @@ TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression)
test.safe_run(); test.safe_run();
} }
TEST(Features2d_RotationInvariance_Descriptor_BRIEF_64, regression)
{
DescriptorRotationInvarianceTest test(SURF::create(),
BriefDescriptorExtractor::create(64,true),
NORM_L1,
0.98f);
test.safe_run();
}
TEST(Features2d_RotationInvariance_Descriptor_BRIEF_32, regression)
{
DescriptorRotationInvarianceTest test(SURF::create(),
BriefDescriptorExtractor::create(32,true),
NORM_L1,
0.97f);
test.safe_run();
}
TEST(Features2d_RotationInvariance_Descriptor_BRIEF_16, regression)
{
DescriptorRotationInvarianceTest test(SURF::create(),
BriefDescriptorExtractor::create(16,true),
NORM_L1,
0.85f);
test.safe_run();
}
/* /*
* Detector's scale invariance check * Detector's scale invariance check
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment