This source file includes following definitions.
- copyVectorToUMat
- ocl_HarrisResponses
- ocl_ICAngles
- ocl_computeOrbDescriptors
- HarrisResponses
- ICAngles
- computeOrbDescriptors
- initializeOrbPattern
- makeRandomPattern
- getScale
- fastThreshold
- setMaxFeatures
- getMaxFeatures
- setScaleFactor
- getScaleFactor
- setNLevels
- getNLevels
- setEdgeThreshold
- getEdgeThreshold
- setFirstLevel
- getFirstLevel
- setWTA_K
- getWTA_K
- setScoreType
- getScoreType
- setPatchSize
- getPatchSize
- setFastThreshold
- getFastThreshold
- descriptorSize
- descriptorType
- defaultNorm
- uploadORBKeypoints
- uploadORBKeypoints
- computeKeyPoints
- detectAndCompute
- create
#include "precomp.hpp"
#include "opencl_kernels_features2d.hpp"
#include <iterator>
#ifndef CV_IMPL_ADD
#define CV_IMPL_ADD(x)
#endif
namespace cv
{
const float HARRIS_K = 0.04f;
template<typename _Tp> inline void copyVectorToUMat(const std::vector<_Tp>& v, OutputArray um)
{
if(v.empty())
um.release();
else
Mat(1, (int)(v.size()*sizeof(v[0])), CV_8U, (void*)&v[0]).copyTo(um);
}
static bool
ocl_HarrisResponses(const UMat& imgbuf,
const UMat& layerinfo,
const UMat& keypoints,
UMat& responses,
int nkeypoints, int blockSize, float harris_k)
{
size_t globalSize[] = {nkeypoints};
float scale = 1.f/((1 << 2) * blockSize * 255.f);
float scale_sq_sq = scale * scale * scale * scale;
ocl::Kernel hr_ker("ORB_HarrisResponses", ocl::features2d::orb_oclsrc,
format("-D ORB_RESPONSES -D blockSize=%d -D scale_sq_sq=%.12ef -D HARRIS_K=%.12ff", blockSize, scale_sq_sq, harris_k));
if( hr_ker.empty() )
return false;
return hr_ker.args(ocl::KernelArg::ReadOnlyNoSize(imgbuf),
ocl::KernelArg::PtrReadOnly(layerinfo),
ocl::KernelArg::PtrReadOnly(keypoints),
ocl::KernelArg::PtrWriteOnly(responses),
nkeypoints).run(1, globalSize, 0, true);
}
static bool
ocl_ICAngles(const UMat& imgbuf, const UMat& layerinfo,
const UMat& keypoints, UMat& responses,
const UMat& umax, int nkeypoints, int half_k)
{
size_t globalSize[] = {nkeypoints};
ocl::Kernel icangle_ker("ORB_ICAngle", ocl::features2d::orb_oclsrc, "-D ORB_ANGLES");
if( icangle_ker.empty() )
return false;
return icangle_ker.args(ocl::KernelArg::ReadOnlyNoSize(imgbuf),
ocl::KernelArg::PtrReadOnly(layerinfo),
ocl::KernelArg::PtrReadOnly(keypoints),
ocl::KernelArg::PtrWriteOnly(responses),
ocl::KernelArg::PtrReadOnly(umax),
nkeypoints, half_k).run(1, globalSize, 0, true);
}
static bool
ocl_computeOrbDescriptors(const UMat& imgbuf, const UMat& layerInfo,
const UMat& keypoints, UMat& desc, const UMat& pattern,
int nkeypoints, int dsize, int wta_k)
{
size_t globalSize[] = {nkeypoints};
ocl::Kernel desc_ker("ORB_computeDescriptor", ocl::features2d::orb_oclsrc,
format("-D ORB_DESCRIPTORS -D WTA_K=%d", wta_k));
if( desc_ker.empty() )
return false;
return desc_ker.args(ocl::KernelArg::ReadOnlyNoSize(imgbuf),
ocl::KernelArg::PtrReadOnly(layerInfo),
ocl::KernelArg::PtrReadOnly(keypoints),
ocl::KernelArg::PtrWriteOnly(desc),
ocl::KernelArg::PtrReadOnly(pattern),
nkeypoints, dsize).run(1, globalSize, 0, true);
}
static void
HarrisResponses(const Mat& img, const std::vector<Rect>& layerinfo,
std::vector<KeyPoint>& pts, int blockSize, float harris_k)
{
CV_Assert( img.type() == CV_8UC1 && blockSize*blockSize <= 2048 );
size_t ptidx, ptsize = pts.size();
const uchar* ptr00 = img.ptr<uchar>();
int step = (int)(img.step/img.elemSize1());
int r = blockSize/2;
float scale = 1.f/((1 << 2) * blockSize * 255.f);
float scale_sq_sq = scale * scale * scale * scale;
AutoBuffer<int> ofsbuf(blockSize*blockSize);
int* ofs = ofsbuf;
for( int i = 0; i < blockSize; i++ )
for( int j = 0; j < blockSize; j++ )
ofs[i*blockSize + j] = (int)(i*step + j);
for( ptidx = 0; ptidx < ptsize; ptidx++ )
{
int x0 = cvRound(pts[ptidx].pt.x);
int y0 = cvRound(pts[ptidx].pt.y);
int z = pts[ptidx].octave;
const uchar* ptr0 = ptr00 + (y0 - r + layerinfo[z].y)*step + x0 - r + layerinfo[z].x;
int a = 0, b = 0, c = 0;
for( int k = 0; k < blockSize*blockSize; k++ )
{
const uchar* ptr = ptr0 + ofs[k];
int Ix = (ptr[1] - ptr[-1])*2 + (ptr[-step+1] - ptr[-step-1]) + (ptr[step+1] - ptr[step-1]);
int Iy = (ptr[step] - ptr[-step])*2 + (ptr[step-1] - ptr[-step-1]) + (ptr[step+1] - ptr[-step+1]);
a += Ix*Ix;
b += Iy*Iy;
c += Ix*Iy;
}
pts[ptidx].response = ((float)a * b - (float)c * c -
harris_k * ((float)a + b) * ((float)a + b))*scale_sq_sq;
}
}
static void ICAngles(const Mat& img, const std::vector<Rect>& layerinfo,
std::vector<KeyPoint>& pts, const std::vector<int> & u_max, int half_k)
{
int step = (int)img.step1();
size_t ptidx, ptsize = pts.size();
for( ptidx = 0; ptidx < ptsize; ptidx++ )
{
const Rect& layer = layerinfo[pts[ptidx].octave];
const uchar* center = &img.at<uchar>(cvRound(pts[ptidx].pt.y) + layer.y, cvRound(pts[ptidx].pt.x) + layer.x);
int m_01 = 0, m_10 = 0;
for (int u = -half_k; u <= half_k; ++u)
m_10 += u * center[u];
for (int v = 1; v <= half_k; ++v)
{
int v_sum = 0;
int d = u_max[v];
for (int u = -d; u <= d; ++u)
{
int val_plus = center[u + v*step], val_minus = center[u - v*step];
v_sum += (val_plus - val_minus);
m_10 += u * (val_plus + val_minus);
}
m_01 += v * v_sum;
}
pts[ptidx].angle = fastAtan2((float)m_01, (float)m_10);
}
}
static void
computeOrbDescriptors( const Mat& imagePyramid, const std::vector<Rect>& layerInfo,
const std::vector<float>& layerScale, std::vector<KeyPoint>& keypoints,
Mat& descriptors, const std::vector<Point>& _pattern, int dsize, int wta_k )
{
int step = (int)imagePyramid.step;
int j, i, nkeypoints = (int)keypoints.size();
for( j = 0; j < nkeypoints; j++ )
{
const KeyPoint& kpt = keypoints[j];
const Rect& layer = layerInfo[kpt.octave];
float scale = 1.f/layerScale[kpt.octave];
float angle = kpt.angle;
angle *= (float)(CV_PI/180.f);
float a = (float)cos(angle), b = (float)sin(angle);
const uchar* center = &imagePyramid.at<uchar>(cvRound(kpt.pt.y*scale) + layer.y,
cvRound(kpt.pt.x*scale) + layer.x);
float x, y;
int ix, iy;
const Point* pattern = &_pattern[0];
uchar* desc = descriptors.ptr<uchar>(j);
#if 1
#define GET_VALUE(idx) \
(x = pattern[idx].x*a - pattern[idx].y*b, \
y = pattern[idx].x*b + pattern[idx].y*a, \
ix = cvRound(x), \
iy = cvRound(y), \
*(center + iy*step + ix) )
#else
#define GET_VALUE(idx) \
(x = pattern[idx].x*a - pattern[idx].y*b, \
y = pattern[idx].x*b + pattern[idx].y*a, \
ix = cvFloor(x), iy = cvFloor(y), \
x -= ix, y -= iy, \
cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \
center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y))
#endif
if( wta_k == 2 )
{
for (i = 0; i < dsize; ++i, pattern += 16)
{
int t0, t1, val;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
val = t0 < t1;
t0 = GET_VALUE(2); t1 = GET_VALUE(3);
val |= (t0 < t1) << 1;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
val |= (t0 < t1) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7);
val |= (t0 < t1) << 3;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
val |= (t0 < t1) << 4;
t0 = GET_VALUE(10); t1 = GET_VALUE(11);
val |= (t0 < t1) << 5;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
val |= (t0 < t1) << 6;
t0 = GET_VALUE(14); t1 = GET_VALUE(15);
val |= (t0 < t1) << 7;
desc[i] = (uchar)val;
}
}
else if( wta_k == 3 )
{
for (i = 0; i < dsize; ++i, pattern += 12)
{
int t0, t1, t2, val;
t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);
val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);
t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;
t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;
desc[i] = (uchar)val;
}
}
else if( wta_k == 4 )
{
for (i = 0; i < dsize; ++i, pattern += 16)
{
int t0, t1, t2, t3, u, v, k, val;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
t2 = GET_VALUE(2); t3 = GET_VALUE(3);
u = 0, v = 2;
if( t1 > t0 ) t0 = t1, u = 1;
if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v;
val = k;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
t2 = GET_VALUE(6); t3 = GET_VALUE(7);
u = 0, v = 2;
if( t1 > t0 ) t0 = t1, u = 1;
if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v;
val |= k << 2;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
t2 = GET_VALUE(10); t3 = GET_VALUE(11);
u = 0, v = 2;
if( t1 > t0 ) t0 = t1, u = 1;
if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v;
val |= k << 4;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
t2 = GET_VALUE(14); t3 = GET_VALUE(15);
u = 0, v = 2;
if( t1 > t0 ) t0 = t1, u = 1;
if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v;
val |= k << 6;
desc[i] = (uchar)val;
}
}
else
CV_Error( Error::StsBadSize, "Wrong wta_k. It can be only 2, 3 or 4." );
#undef GET_VALUE
}
}
static void initializeOrbPattern( const Point* pattern0, std::vector<Point>& pattern, int ntuples, int tupleSize, int poolSize )
{
RNG rng(0x12345678);
int i, k, k1;
pattern.resize(ntuples*tupleSize);
for( i = 0; i < ntuples; i++ )
{
for( k = 0; k < tupleSize; k++ )
{
for(;;)
{
int idx = rng.uniform(0, poolSize);
Point pt = pattern0[idx];
for( k1 = 0; k1 < k; k1++ )
if( pattern[tupleSize*i + k1] == pt )
break;
if( k1 == k )
{
pattern[tupleSize*i + k] = pt;
break;
}
}
}
}
}
static int bit_pattern_31_[256*4] =
{
8,-3, 9,5,
4,2, 7,-12,
-11,9, -8,2,
7,-12, 12,-13,
2,-13, 2,12,
1,-7, 1,6,
-2,-10, -2,-4,
-13,-13, -11,-8,
-13,-3, -12,-9,
10,4, 11,9,
-13,-8, -8,-9,
-11,7, -9,12,
7,7, 12,6,
-4,-5, -3,0,
-13,2, -12,-3,
-9,0, -7,5,
12,-6, 12,-1,
-3,6, -2,12,
-6,-13, -4,-8,
11,-13, 12,-8,
4,7, 5,1,
5,-3, 10,-3,
3,-7, 6,12,
-8,-7, -6,-2,
-2,11, -1,-10,
-13,12, -8,10,
-7,3, -5,-3,
-4,2, -3,7,
-10,-12, -6,11,
5,-12, 6,-7,
5,-6, 7,-1,
1,0, 4,-5,
9,11, 11,-13,
4,7, 4,12,
2,-1, 4,4,
-4,-12, -2,7,
-8,-5, -7,-10,
4,11, 9,12,
0,-8, 1,-13,
-13,-2, -8,2,
-3,-2, -2,3,
-6,9, -4,-9,
8,12, 10,7,
0,9, 1,3,
7,-5, 11,-10,
-13,-6, -11,0,
10,7, 12,1,
-6,-3, -6,12,
10,-9, 12,-4,
-13,8, -8,-12,
-13,0, -8,-4,
3,3, 7,8,
5,7, 10,-7,
-1,7, 1,-12,
3,-10, 5,6,
2,-4, 3,-10,
-13,0, -13,5,
-13,-7, -12,12,
-13,3, -11,8,
-7,12, -4,7,
6,-10, 12,8,
-9,-1, -7,-6,
-2,-5, 0,12,
-12,5, -7,5,
3,-10, 8,-13,
-7,-7, -4,5,
-3,-2, -1,-7,
2,9, 5,-11,
-11,-13, -5,-13,
-1,6, 0,-1,
5,-3, 5,2,
-4,-13, -4,12,
-9,-6, -9,6,
-12,-10, -8,-4,
10,2, 12,-3,
7,12, 12,12,
-7,-13, -6,5,
-4,9, -3,4,
7,-1, 12,2,
-7,6, -5,1,
-13,11, -12,5,
-3,7, -2,-6,
7,-8, 12,-7,
-13,-7, -11,-12,
1,-3, 12,12,
2,-6, 3,0,
-4,3, -2,-13,
-1,-13, 1,9,
7,1, 8,-6,
1,-1, 3,12,
9,1, 12,6,
-1,-9, -1,3,
-13,-13, -10,5,
7,7, 10,12,
12,-5, 12,9,
6,3, 7,11,
5,-13, 6,10,
2,-12, 2,3,
3,8, 4,-6,
2,6, 12,-13,
9,-12, 10,3,
-8,4, -7,9,
-11,12, -4,-6,
1,12, 2,-8,
6,-9, 7,-4,
2,3, 3,-2,
6,3, 11,0,
3,-3, 8,-8,
7,8, 9,3,
-11,-5, -6,-4,
-10,11, -5,10,
-5,-8, -3,12,
-10,5, -9,0,
8,-1, 12,-6,
4,-6, 6,-11,
-10,12, -8,7,
4,-2, 6,7,
-2,0, -2,12,
-5,-8, -5,2,
7,-6, 10,12,
-9,-13, -8,-8,
-5,-13, -5,-2,
8,-8, 9,-13,
-9,-11, -9,0,
1,-8, 1,-2,
7,-4, 9,1,
-2,1, -1,-4,
11,-6, 12,-11,
-12,-9, -6,4,
3,7, 7,12,
5,5, 10,8,
0,-4, 2,8,
-9,12, -5,-13,
0,7, 2,12,
-1,2, 1,7,
5,11, 7,-9,
3,5, 6,-8,
-13,-4, -8,9,
-5,9, -3,-3,
-4,-7, -3,-12,
6,5, 8,0,
-7,6, -6,12,
-13,6, -5,-2,
1,-10, 3,10,
4,1, 8,-4,
-2,-2, 2,-13,
2,-12, 12,12,
-2,-13, 0,-6,
4,1, 9,3,
-6,-10, -3,-5,
-3,-13, -1,1,
7,5, 12,-11,
4,-2, 5,-7,
-13,9, -9,-5,
7,1, 8,6,
7,-8, 7,6,
-7,-4, -7,1,
-8,11, -7,-8,
-13,6, -12,-8,
2,4, 3,9,
10,-5, 12,3,
-6,-5, -6,7,
8,-3, 9,-8,
2,-12, 2,8,
-11,-2, -10,3,
-12,-13, -7,-9,
-11,0, -10,-5,
5,-3, 11,8,
-2,-13, -1,12,
-1,-8, 0,9,
-13,-11, -12,-5,
-10,-2, -10,11,
-3,9, -2,-13,
2,-3, 3,2,
-9,-13, -4,0,
-4,6, -3,-10,
-4,12, -2,-7,
-6,-11, -4,9,
6,-3, 6,11,
-13,11, -5,5,
11,11, 12,6,
7,-5, 12,-2,
-1,12, 0,7,
-4,-8, -3,-2,
-7,1, -6,7,
-13,-12, -8,-13,
-7,-2, -6,-8,
-8,5, -6,-9,
-5,-1, -4,5,
-13,7, -8,10,
1,5, 5,-13,
1,0, 10,-13,
9,12, 10,-1,
5,-8, 10,-9,
-1,11, 1,-13,
-9,-3, -6,2,
-1,-10, 1,12,
-13,1, -8,-10,
8,-11, 10,-6,
2,-13, 3,-6,
7,-13, 12,-9,
-10,-10, -5,-7,
-10,-8, -8,-13,
4,-6, 8,5,
3,12, 8,-13,
-4,2, -3,-3,
5,-13, 10,-12,
4,-13, 5,-1,
-9,9, -4,3,
0,3, 3,-9,
-12,1, -6,1,
3,2, 4,-8,
-10,-10, -10,9,
8,-13, 12,12,
-8,-12, -6,-5,
2,2, 3,7,
10,6, 11,-8,
6,8, 8,-12,
-7,10, -6,5,
-3,-9, -3,9,
-1,-13, -1,5,
-3,-7, -3,4,
-8,-2, -8,3,
4,2, 12,12,
2,-5, 3,11,
6,-9, 11,-13,
3,-1, 7,12,
11,-1, 12,4,
-3,0, -3,6,
4,-11, 4,12,
2,-4, 2,1,
-10,-6, -8,1,
-13,7, -11,1,
-13,12, -11,-13,
6,0, 11,-13,
0,-1, 1,4,
-13,3, -9,-2,
-9,8, -6,-3,
-13,-6, -8,-2,
5,-9, 8,10,
2,7, 3,-9,
-1,-6, -1,-1,
9,5, 11,-2,
11,-3, 12,-8,
3,0, 3,5,
-1,4, 0,10,
3,-6, 4,5,
-13,0, -10,5,
5,8, 12,11,
8,9, 9,-6,
7,-4, 8,-12,
-10,4, -10,9,
7,3, 12,4,
9,-7, 10,-2,
7,0, 12,-2,
-1,-6, 0,-11
};
static void makeRandomPattern(int patchSize, Point* pattern, int npoints)
{
RNG rng(0x34985739);
for( int i = 0; i < npoints; i++ )
{
pattern[i].x = rng.uniform(-patchSize/2, patchSize/2+1);
pattern[i].y = rng.uniform(-patchSize/2, patchSize/2+1);
}
}
static inline float getScale(int level, int firstLevel, double scaleFactor)
{
return (float)std::pow(scaleFactor, (double)(level - firstLevel));
}
class ORB_Impl : public ORB
{
public:
explicit ORB_Impl(int _nfeatures, float _scaleFactor, int _nlevels, int _edgeThreshold,
int _firstLevel, int _WTA_K, int _scoreType, int _patchSize, int _fastThreshold) :
nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
edgeThreshold(_edgeThreshold), firstLevel(_firstLevel), wta_k(_WTA_K),
scoreType(_scoreType), patchSize(_patchSize), fastThreshold(_fastThreshold)
{}
void setMaxFeatures(int maxFeatures) { nfeatures = maxFeatures; }
int getMaxFeatures() const { return nfeatures; }
void setScaleFactor(double scaleFactor_) { scaleFactor = scaleFactor_; }
double getScaleFactor() const { return scaleFactor; }
void setNLevels(int nlevels_) { nlevels = nlevels_; }
int getNLevels() const { return nlevels; }
void setEdgeThreshold(int edgeThreshold_) { edgeThreshold = edgeThreshold_; }
int getEdgeThreshold() const { return edgeThreshold; }
void setFirstLevel(int firstLevel_) { firstLevel = firstLevel_; }
int getFirstLevel() const { return firstLevel; }
void setWTA_K(int wta_k_) { wta_k = wta_k_; }
int getWTA_K() const { return wta_k; }
void setScoreType(int scoreType_) { scoreType = scoreType_; }
int getScoreType() const { return scoreType; }
void setPatchSize(int patchSize_) { patchSize = patchSize_; }
int getPatchSize() const { return patchSize; }
void setFastThreshold(int fastThreshold_) { fastThreshold = fastThreshold_; }
int getFastThreshold() const { return fastThreshold; }
int descriptorSize() const;
int descriptorType() const;
int defaultNorm() const;
void detectAndCompute( InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints,
OutputArray descriptors, bool useProvidedKeypoints=false );
protected:
int nfeatures;
double scaleFactor;
int nlevels;
int edgeThreshold;
int firstLevel;
int wta_k;
int scoreType;
int patchSize;
int fastThreshold;
};
int ORB_Impl::descriptorSize() const
{
return kBytes;
}
int ORB_Impl::descriptorType() const
{
return CV_8U;
}
int ORB_Impl::defaultNorm() const
{
return NORM_HAMMING;
}
static void uploadORBKeypoints(const std::vector<KeyPoint>& src, std::vector<Vec3i>& buf, OutputArray dst)
{
size_t i, n = src.size();
buf.resize(std::max(buf.size(), n));
for( i = 0; i < n; i++ )
buf[i] = Vec3i(cvRound(src[i].pt.x), cvRound(src[i].pt.y), src[i].octave);
copyVectorToUMat(buf, dst);
}
typedef union if32_t
{
int i;
float f;
}
if32_t;
static void uploadORBKeypoints(const std::vector<KeyPoint>& src,
const std::vector<float>& layerScale,
std::vector<Vec4i>& buf, OutputArray dst)
{
size_t i, n = src.size();
buf.resize(std::max(buf.size(), n));
for( i = 0; i < n; i++ )
{
int z = src[i].octave;
float scale = 1.f/layerScale[z];
if32_t angle;
angle.f = src[i].angle;
buf[i] = Vec4i(cvRound(src[i].pt.x*scale), cvRound(src[i].pt.y*scale), z, angle.i);
}
copyVectorToUMat(buf, dst);
}
static void computeKeyPoints(const Mat& imagePyramid,
const UMat& uimagePyramid,
const Mat& maskPyramid,
const std::vector<Rect>& layerInfo,
const UMat& ulayerInfo,
const std::vector<float>& layerScale,
std::vector<KeyPoint>& allKeypoints,
int nfeatures, double scaleFactor,
int edgeThreshold, int patchSize, int scoreType,
bool useOCL, int fastThreshold )
{
int i, nkeypoints, level, nlevels = (int)layerInfo.size();
std::vector<int> nfeaturesPerLevel(nlevels);
float factor = (float)(1.0 / scaleFactor);
float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)std::pow((double)factor, (double)nlevels));
int sumFeatures = 0;
for( level = 0; level < nlevels-1; level++ )
{
nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale);
sumFeatures += nfeaturesPerLevel[level];
ndesiredFeaturesPerScale *= factor;
}
nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);
int halfPatchSize = patchSize / 2;
std::vector<int> umax(halfPatchSize + 2);
int v, v0, vmax = cvFloor(halfPatchSize * std::sqrt(2.f) / 2 + 1);
int vmin = cvCeil(halfPatchSize * std::sqrt(2.f) / 2);
for (v = 0; v <= vmax; ++v)
umax[v] = cvRound(std::sqrt((double)halfPatchSize * halfPatchSize - v * v));
for (v = halfPatchSize, v0 = 0; v >= vmin; --v)
{
while (umax[v0] == umax[v0 + 1])
++v0;
umax[v] = v0;
++v0;
}
allKeypoints.clear();
std::vector<KeyPoint> keypoints;
std::vector<int> counters(nlevels);
keypoints.reserve(nfeaturesPerLevel[0]*2);
for( level = 0; level < nlevels; level++ )
{
int featuresNum = nfeaturesPerLevel[level];
Mat img = imagePyramid(layerInfo[level]);
Mat mask = maskPyramid.empty() ? Mat() : maskPyramid(layerInfo[level]);
{
Ptr<FastFeatureDetector> fd = FastFeatureDetector::create(fastThreshold, true);
fd->detect(img, keypoints, mask);
}
KeyPointsFilter::runByImageBorder(keypoints, img.size(), edgeThreshold);
KeyPointsFilter::retainBest(keypoints, scoreType == ORB_Impl::HARRIS_SCORE ? 2 * featuresNum : featuresNum);
nkeypoints = (int)keypoints.size();
counters[level] = nkeypoints;
float sf = layerScale[level];
for( i = 0; i < nkeypoints; i++ )
{
keypoints[i].octave = level;
keypoints[i].size = patchSize*sf;
}
std::copy(keypoints.begin(), keypoints.end(), std::back_inserter(allKeypoints));
}
std::vector<Vec3i> ukeypoints_buf;
nkeypoints = (int)allKeypoints.size();
if(nkeypoints == 0)
{
return;
}
Mat responses;
UMat ukeypoints, uresponses(1, nkeypoints, CV_32F);
if( scoreType == ORB_Impl::HARRIS_SCORE )
{
if( useOCL )
{
uploadORBKeypoints(allKeypoints, ukeypoints_buf, ukeypoints);
useOCL = ocl_HarrisResponses( uimagePyramid, ulayerInfo, ukeypoints,
uresponses, nkeypoints, 7, HARRIS_K );
if( useOCL )
{
CV_IMPL_ADD(CV_IMPL_OCL);
uresponses.copyTo(responses);
for( i = 0; i < nkeypoints; i++ )
allKeypoints[i].response = responses.at<float>(i);
}
}
if( !useOCL )
HarrisResponses(imagePyramid, layerInfo, allKeypoints, 7, HARRIS_K);
std::vector<KeyPoint> newAllKeypoints;
newAllKeypoints.reserve(nfeaturesPerLevel[0]*nlevels);
int offset = 0;
for( level = 0; level < nlevels; level++ )
{
int featuresNum = nfeaturesPerLevel[level];
nkeypoints = counters[level];
keypoints.resize(nkeypoints);
std::copy(allKeypoints.begin() + offset,
allKeypoints.begin() + offset + nkeypoints,
keypoints.begin());
offset += nkeypoints;
KeyPointsFilter::retainBest(keypoints, featuresNum);
std::copy(keypoints.begin(), keypoints.end(), std::back_inserter(newAllKeypoints));
}
std::swap(allKeypoints, newAllKeypoints);
}
nkeypoints = (int)allKeypoints.size();
if( useOCL )
{
UMat uumax;
if( useOCL )
copyVectorToUMat(umax, uumax);
uploadORBKeypoints(allKeypoints, ukeypoints_buf, ukeypoints);
useOCL = ocl_ICAngles(uimagePyramid, ulayerInfo, ukeypoints, uresponses, uumax,
nkeypoints, halfPatchSize);
if( useOCL )
{
CV_IMPL_ADD(CV_IMPL_OCL);
uresponses.copyTo(responses);
for( i = 0; i < nkeypoints; i++ )
allKeypoints[i].angle = responses.at<float>(i);
}
}
if( !useOCL )
{
ICAngles(imagePyramid, layerInfo, allKeypoints, umax, halfPatchSize);
}
for( i = 0; i < nkeypoints; i++ )
{
float scale = layerScale[allKeypoints[i].octave];
allKeypoints[i].pt *= scale;
}
}
void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask,
std::vector<KeyPoint>& keypoints,
OutputArray _descriptors, bool useProvidedKeypoints )
{
CV_Assert(patchSize >= 2);
bool do_keypoints = !useProvidedKeypoints;
bool do_descriptors = _descriptors.needed();
if( (!do_keypoints && !do_descriptors) || _image.empty() )
return;
const int HARRIS_BLOCK_SIZE = 9;
int halfPatchSize = patchSize / 2;
int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;
bool useOCL = ocl::useOpenCL();
Mat image = _image.getMat(), mask = _mask.getMat();
if( image.type() != CV_8UC1 )
cvtColor(_image, image, COLOR_BGR2GRAY);
int i, level, nLevels = this->nlevels, nkeypoints = (int)keypoints.size();
bool sortedByLevel = true;
if( !do_keypoints )
{
nLevels = 0;
for( i = 0; i < nkeypoints; i++ )
{
level = keypoints[i].octave;
CV_Assert(level >= 0);
if( i > 0 && level < keypoints[i-1].octave )
sortedByLevel = false;
nLevels = std::max(nLevels, level);
}
nLevels++;
}
std::vector<Rect> layerInfo(nLevels);
std::vector<int> layerOfs(nLevels);
std::vector<float> layerScale(nLevels);
Mat imagePyramid, maskPyramid;
UMat uimagePyramid, ulayerInfo;
int level_dy = image.rows + border*2;
Point level_ofs(0,0);
Size bufSize((image.cols + border*2 + 15) & -16, 0);
for( level = 0; level < nLevels; level++ )
{
float scale = getScale(level, firstLevel, scaleFactor);
layerScale[level] = scale;
Size sz(cvRound(image.cols/scale), cvRound(image.rows/scale));
Size wholeSize(sz.width + border*2, sz.height + border*2);
if( level_ofs.x + wholeSize.width > bufSize.width )
{
level_ofs = Point(0, level_ofs.y + level_dy);
level_dy = wholeSize.height;
}
Rect linfo(level_ofs.x + border, level_ofs.y + border, sz.width, sz.height);
layerInfo[level] = linfo;
layerOfs[level] = linfo.y*bufSize.width + linfo.x;
level_ofs.x += wholeSize.width;
}
bufSize.height = level_ofs.y + level_dy;
imagePyramid.create(bufSize, CV_8U);
if( !mask.empty() )
maskPyramid.create(bufSize, CV_8U);
Mat prevImg = image, prevMask = mask;
for (level = 0; level < nLevels; ++level)
{
Rect linfo = layerInfo[level];
Size sz(linfo.width, linfo.height);
Size wholeSize(sz.width + border*2, sz.height + border*2);
Rect wholeLinfo = Rect(linfo.x - border, linfo.y - border, wholeSize.width, wholeSize.height);
Mat extImg = imagePyramid(wholeLinfo), extMask;
Mat currImg = extImg(Rect(border, border, sz.width, sz.height)), currMask;
if( !mask.empty() )
{
extMask = maskPyramid(wholeLinfo);
currMask = extMask(Rect(border, border, sz.width, sz.height));
}
if( level != firstLevel )
{
resize(prevImg, currImg, sz, 0, 0, INTER_LINEAR);
if( !mask.empty() )
{
resize(prevMask, currMask, sz, 0, 0, INTER_LINEAR);
if( level > firstLevel )
threshold(currMask, currMask, 254, 0, THRESH_TOZERO);
}
copyMakeBorder(currImg, extImg, border, border, border, border,
BORDER_REFLECT_101+BORDER_ISOLATED);
if (!mask.empty())
copyMakeBorder(currMask, extMask, border, border, border, border,
BORDER_CONSTANT+BORDER_ISOLATED);
}
else
{
copyMakeBorder(image, extImg, border, border, border, border,
BORDER_REFLECT_101);
if( !mask.empty() )
copyMakeBorder(mask, extMask, border, border, border, border,
BORDER_CONSTANT+BORDER_ISOLATED);
}
prevImg = currImg;
prevMask = currMask;
}
if( useOCL )
copyVectorToUMat(layerOfs, ulayerInfo);
if( do_keypoints )
{
if( useOCL )
imagePyramid.copyTo(uimagePyramid);
computeKeyPoints(imagePyramid, uimagePyramid, maskPyramid,
layerInfo, ulayerInfo, layerScale, keypoints,
nfeatures, scaleFactor, edgeThreshold, patchSize, scoreType, useOCL, fastThreshold);
}
else
{
KeyPointsFilter::runByImageBorder(keypoints, image.size(), edgeThreshold);
if( !sortedByLevel )
{
std::vector<std::vector<KeyPoint> > allKeypoints(nLevels);
nkeypoints = (int)keypoints.size();
for( i = 0; i < nkeypoints; i++ )
{
level = keypoints[i].octave;
CV_Assert(0 <= level);
allKeypoints[level].push_back(keypoints[i]);
}
keypoints.clear();
for( level = 0; level < nLevels; level++ )
std::copy(allKeypoints[level].begin(), allKeypoints[level].end(), std::back_inserter(keypoints));
}
}
if( do_descriptors )
{
int dsize = descriptorSize();
nkeypoints = (int)keypoints.size();
if( nkeypoints == 0 )
{
_descriptors.release();
return;
}
_descriptors.create(nkeypoints, dsize, CV_8U);
std::vector<Point> pattern;
const int npoints = 512;
Point patternbuf[npoints];
const Point* pattern0 = (const Point*)bit_pattern_31_;
if( patchSize != 31 )
{
pattern0 = patternbuf;
makeRandomPattern(patchSize, patternbuf, npoints);
}
CV_Assert( wta_k == 2 || wta_k == 3 || wta_k == 4 );
if( wta_k == 2 )
std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));
else
{
int ntuples = descriptorSize()*4;
initializeOrbPattern(pattern0, pattern, ntuples, wta_k, npoints);
}
for( level = 0; level < nLevels; level++ )
{
Mat workingMat = imagePyramid(layerInfo[level]);
GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);
}
if( useOCL )
{
imagePyramid.copyTo(uimagePyramid);
std::vector<Vec4i> kptbuf;
UMat ukeypoints, upattern;
copyVectorToUMat(pattern, upattern);
uploadORBKeypoints(keypoints, layerScale, kptbuf, ukeypoints);
UMat udescriptors = _descriptors.getUMat();
useOCL = ocl_computeOrbDescriptors(uimagePyramid, ulayerInfo,
ukeypoints, udescriptors, upattern,
nkeypoints, dsize, wta_k);
if(useOCL)
{
CV_IMPL_ADD(CV_IMPL_OCL);
}
}
if( !useOCL )
{
Mat descriptors = _descriptors.getMat();
computeOrbDescriptors(imagePyramid, layerInfo, layerScale,
keypoints, descriptors, pattern, dsize, wta_k);
}
}
}
Ptr<ORB> ORB::create(int nfeatures, float scaleFactor, int nlevels, int edgeThreshold,
int firstLevel, int wta_k, int scoreType, int patchSize, int fastThreshold)
{
return makePtr<ORB_Impl>(nfeatures, scaleFactor, nlevels, edgeThreshold,
firstLevel, wta_k, scoreType, patchSize, fastThreshold);
}
}