2010-05-11 17:44:00 +00:00
\section { Object detection and descriptors}
\ifCpp
\cvclass { RandomizedTree}
The class contains base structure for \texttt { RTreeClassifier}
\begin { lstlisting}
class CV_ EXPORTS RandomizedTree
{
public:
friend class RTreeClassifier;
RandomizedTree();
~RandomizedTree();
void train(std::vector<BaseKeypoint> const& base_ set,
cv::RNG & rng, int depth, int views,
size_ t reduced_ num_ dim, int num_ quant_ bits);
void train(std::vector<BaseKeypoint> const& base_ set,
cv::RNG & rng, PatchGenerator & make_ patch, int depth,
int views, size_ t reduced_ num_ dim, int num_ quant_ bits);
// following two funcs are EXPERIMENTAL
//(do not use unless you know exactly what you do)
static void quantizeVector(float *vec, int dim, int N, float bnds[2],
int clamp_ mode=0);
static void quantizeVector(float *src, int dim, int N, float bnds[2],
uchar *dst);
// patch_ data must be a 32x32 array (no row padding)
float* getPosterior(uchar* patch_ data);
const float* getPosterior(uchar* patch_ data) const;
uchar* getPosterior2(uchar* patch_ data);
void read(const char* file_ name, int num_ quant_ bits);
void read(std::istream & is, int num_ quant_ bits);
void write(const char* file_ name) const;
void write(std::ostream & os) const;
int classes() { return classes_ ; }
int depth() { return depth_ ; }
void discardFloatPosteriors() { freePosteriors(1); }
inline void applyQuantization(int num_ quant_ bits)
{ makePosteriors2(num_ quant_ bits); }
private:
int classes_ ;
int depth_ ;
int num_ leaves_ ;
std::vector<RTreeNode> nodes_ ;
float **posteriors_ ; // 16-bytes aligned posteriors
uchar **posteriors2_ ; // 16-bytes aligned posteriors
std::vector<int> leaf_ counts_ ;
void createNodes(int num_ nodes, cv::RNG & rng);
void allocPosteriorsAligned(int num_ leaves, int num_ classes);
void freePosteriors(int which);
// which: 1=posteriors_ , 2=posteriors2_ , 3=both
void init(int classes, int depth, cv::RNG & rng);
void addExample(int class_ id, uchar* patch_ data);
void finalize(size_ t reduced_ num_ dim, int num_ quant_ bits);
int getIndex(uchar* patch_ data) const;
inline float* getPosteriorByIndex(int index);
inline uchar* getPosteriorByIndex2(int index);
inline const float* getPosteriorByIndex(int index) const;
void convertPosteriorsToChar();
void makePosteriors2(int num_ quant_ bits);
void compressLeaves(size_ t reduced_ num_ dim);
void estimateQuantPercForPosteriors(float perc[2]);
} ;
\end { lstlisting}
\cvCppFunc { RandomizedTree::train}
Trains a randomized tree using input set of keypoints
\cvdefCpp {
void train(std::vector<BaseKeypoint> const\& base\_ set, cv::RNG \& rng,
PatchGenerator \& make\_ patch, int depth, int views, size\_ t reduced\_ num\_ dim,
int num\_ quant\_ bits);
}
\cvdefCpp {
void train(std::vector<BaseKeypoint> const\& base\_ set, cv::RNG \& rng,
PatchGenerator \& make\_ patch, int depth, int views, size\_ t reduced\_ num\_ dim,
int num\_ quant\_ bits);
}
\begin { description}
\cvarg { base\_ set} { Vector of \texttt { BaseKeypoint} type. Contains keypoints from the image are used for training}
\cvarg { rng} { Random numbers generator is used for training}
\cvarg { make\_ patch} { Patch generator is used for training}
\cvarg { depth} { Maximum tree depth}
%\cvarg{views} {}
\cvarg { reduced\_ num\_ dim} { Number of dimensions are used in compressed signature}
\cvarg { num\_ quant\_ bits} { Number of bits are used for quantization}
\end { description}
2010-10-19 13:44:57 +00:00
\cvCppFunc { RandomizedTree::read}
2010-05-11 17:44:00 +00:00
Reads pre-saved randomized tree from file or stream
\cvdefCpp { read(const char* file\_ name, int num\_ quant\_ bits)}
\cvdefCpp { read(std::istream \& is, int num\_ quant\_ bits)}
\begin { description}
\cvarg { file\_ name} { Filename of file contains randomized tree data}
\cvarg { is} { Input stream associated with file contains randomized tree data}
\cvarg { num\_ quant\_ bits} { Number of bits are used for quantization}
\end { description}
2010-10-19 13:44:57 +00:00
\cvCppFunc { RandomizedTree::write}
2010-05-11 17:44:00 +00:00
Writes current randomized tree to a file or stream
\cvdefCpp { void write(const char* file\_ name) const;}
\cvdefCpp { void write(std::ostream \& os) const;}
\begin { description}
\cvarg { file\_ name} { Filename of file where randomized tree data will be stored}
\cvarg { is} { Output stream associated with file where randomized tree data will be stored}
\end { description}
2010-10-19 13:44:57 +00:00
\cvCppFunc { RandomizedTree::applyQuantization}
2010-05-11 17:44:00 +00:00
Applies quantization to the current randomized tree
\cvdefCpp { void applyQuantization(int num\_ quant\_ bits)}
\begin { description}
\cvarg { num\_ quant\_ bits} { Number of bits are used for quantization}
\end { description}
\cvstruct { RTreeNode}
The class contains base structure for \texttt { RandomizedTree}
\begin { lstlisting}
struct RTreeNode
{
short offset1, offset2;
RTreeNode() { }
RTreeNode(uchar x1, uchar y1, uchar x2, uchar y2)
: offset1(y1*PATCH_ SIZE + x1),
offset2(y2*PATCH_ SIZE + x2)
{ }
//! Left child on 0, right child on 1
inline bool operator() (uchar* patch_ data) const
{
return patch_ data[offset1] > patch_ data[offset2];
}
} ;
\end { lstlisting}
\cvclass { RTreeClassifier}
The class contains \texttt { RTreeClassifier} . It represents calonder descriptor which was originally introduced by Michael Calonder
\begin { lstlisting}
class CV_ EXPORTS RTreeClassifier
{
public:
static const int DEFAULT_ TREES = 48;
static const size_ t DEFAULT_ NUM_ QUANT_ BITS = 4;
RTreeClassifier();
void train(std::vector<BaseKeypoint> const& base_ set,
cv::RNG & rng,
int num_ trees = RTreeClassifier::DEFAULT_ TREES,
int depth = DEFAULT_ DEPTH,
int views = DEFAULT_ VIEWS,
size_ t reduced_ num_ dim = DEFAULT_ REDUCED_ NUM_ DIM,
int num_ quant_ bits = DEFAULT_ NUM_ QUANT_ BITS,
bool print_ status = true);
void train(std::vector<BaseKeypoint> const& base_ set,
cv::RNG & rng,
PatchGenerator & make_ patch,
int num_ trees = RTreeClassifier::DEFAULT_ TREES,
int depth = DEFAULT_ DEPTH,
int views = DEFAULT_ VIEWS,
size_ t reduced_ num_ dim = DEFAULT_ REDUCED_ NUM_ DIM,
int num_ quant_ bits = DEFAULT_ NUM_ QUANT_ BITS,
bool print_ status = true);
// sig must point to a memory block of at least
//classes()*sizeof(float|uchar) bytes
void getSignature(IplImage *patch, uchar *sig);
void getSignature(IplImage *patch, float *sig);
void getSparseSignature(IplImage *patch, float *sig,
float thresh);
static int countNonZeroElements(float *vec, int n, double tol=1e-10);
static inline void safeSignatureAlloc(uchar **sig, int num_ sig=1,
int sig_ len=176);
static inline uchar* safeSignatureAlloc(int num_ sig=1,
int sig_ len=176);
inline int classes() { return classes_ ; }
inline int original_ num_ classes()
{ return original_ num_ classes_ ; }
void setQuantization(int num_ quant_ bits);
void discardFloatPosteriors();
void read(const char* file_ name);
void read(std::istream & is);
void write(const char* file_ name) const;
void write(std::ostream & os) const;
std::vector<RandomizedTree> trees_ ;
private:
int classes_ ;
int num_ quant_ bits_ ;
uchar **posteriors_ ;
ushort *ptemp_ ;
int original_ num_ classes_ ;
bool keep_ floats_ ;
} ;
\end { lstlisting}
\cvCppFunc { RTreeClassifier::train}
Trains a randomized tree classificator using input set of keypoints
\cvdefCpp {
void train(std::vector<BaseKeypoint> const\& base\_ set,
cv::RNG \& rng,
int num\_ trees = RTreeClassifier::DEFAULT\_ TREES,
int depth = DEFAULT\_ DEPTH,
int views = DEFAULT\_ VIEWS,
size\_ t reduced\_ num\_ dim = DEFAULT\_ REDUCED\_ NUM\_ DIM,
int num\_ quant\_ bits = DEFAULT\_ NUM\_ QUANT\_ BITS, bool print\_ status = true);
}
\cvdefCpp {
void train(std::vector<BaseKeypoint> const\& base\_ set,
cv::RNG \& rng,
PatchGenerator \& make\_ patch,
int num\_ trees = RTreeClassifier::DEFAULT\_ TREES,
int depth = DEFAULT\_ DEPTH,
int views = DEFAULT\_ VIEWS,
size\_ t reduced\_ num\_ dim = DEFAULT\_ REDUCED\_ NUM\_ DIM,
int num\_ quant\_ bits = DEFAULT\_ NUM\_ QUANT\_ BITS, bool print\_ status = true);
}
\begin { description}
\cvarg { base\_ set} { Vector of \texttt { BaseKeypoint} type. Contains keypoints from the image are used for training}
\cvarg { rng} { Random numbers generator is used for training}
\cvarg { make\_ patch} { Patch generator is used for training}
\cvarg { num\_ trees} { Number of randomized trees used in RTreeClassificator}
\cvarg { depth} { Maximum tree depth}
%\cvarg{views} {}
\cvarg { reduced\_ num\_ dim} { Number of dimensions are used in compressed signature}
\cvarg { num\_ quant\_ bits} { Number of bits are used for quantization}
\cvarg { print\_ status} { Print current status of training on the console}
\end { description}
\cvCppFunc { RTreeClassifier::getSignature}
Returns signature for image patch
\cvdefCpp {
void getSignature(IplImage *patch, uchar *sig)
}
\cvdefCpp {
void getSignature(IplImage *patch, float *sig)
}
\begin { description}
\cvarg { patch} { Image patch to calculate signature for}
\cvarg { sig} { Output signature (array dimension is \texttt { reduced\_ num\_ dim)} }
\end { description}
\cvCppFunc { RTreeClassifier::getSparseSignature}
The function is simular to \texttt { getSignature} but uses the threshold for removing all signature elements less than the threshold. So that the signature is compressed
\cvdefCpp {
void getSparseSignature(IplImage *patch, float *sig,
float thresh);
}
\begin { description}
\cvarg { patch} { Image patch to calculate signature for}
\cvarg { sig} { Output signature (array dimension is \texttt { reduced\_ num\_ dim)} }
\cvarg { tresh} { The threshold that is used for compressing the signature}
\end { description}
\cvCppFunc { RTreeClassifier::countNonZeroElements}
The function returns the number of non-zero elements in the input array.
\cvdefCpp {
static int countNonZeroElements(float *vec, int n, double tol=1e-10);
}
\begin { description}
\cvarg { vec} { Input vector contains float elements}
\cvarg { n} { Input vector size}
\cvarg { tol} { The threshold used for elements counting. We take all elements are less than \texttt { tol} as zero elements}
\end { description}
2010-10-19 13:44:57 +00:00
\cvCppFunc { RTreeClassifier::read}
2010-05-11 17:44:00 +00:00
Reads pre-saved RTreeClassifier from file or stream
\cvdefCpp { read(const char* file\_ name)}
\cvdefCpp { read(std::istream \& is)}
\begin { description}
\cvarg { file\_ name} { Filename of file contains randomized tree data}
\cvarg { is} { Input stream associated with file contains randomized tree data}
\end { description}
2010-10-19 13:44:57 +00:00
\cvCppFunc { RTreeClassifier::write}
2010-05-11 17:44:00 +00:00
Writes current RTreeClassifier to a file or stream
\cvdefCpp { void write(const char* file\_ name) const;}
\cvdefCpp { void write(std::ostream \& os) const;}
\begin { description}
\cvarg { file\_ name} { Filename of file where randomized tree data will be stored}
\cvarg { is} { Output stream associated with file where randomized tree data will be stored}
\end { description}
2010-10-19 13:44:57 +00:00
\cvCppFunc { RTreeClassifier::setQuantization}
2010-05-11 17:44:00 +00:00
Applies quantization to the current randomized tree
\cvdefCpp { void setQuantization(int num\_ quant\_ bits)}
\begin { description}
\cvarg { num\_ quant\_ bits} { Number of bits are used for quantization}
\end { description}
Below there is an example of \texttt { RTreeClassifier} usage for feature matching. There are test and train images and we extract features from both with SURF. Output is $ best \_ corr $ and $ best \_ corr \_ idx $ arrays which keep the best probabilities and corresponding features indexes for every train feature.
% ===== Example. Using RTreeClassifier for features matching =====
\begin { lstlisting}
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
CvSeq *imageKeypoints = 0, *imageDescriptors = 0;
CvSURFParams params = cvSURFParams(500, 1);
cvExtractSURF( test_ image, 0, & imageKeypoints, & imageDescriptors,
storage, params );
cvExtractSURF( train_ image, 0, & objectKeypoints, & objectDescriptors,
storage, params );
cv::RTreeClassifier detector;
int patch_ width = cv::PATCH_ SIZE;
iint patch_ height = cv::PATCH_ SIZE;
vector<cv::BaseKeypoint> base_ set;
int i=0;
CvSURFPoint* point;
for (i=0;i<(n_ points > 0 ? n_ points : objectKeypoints->total);i++)
{
point=(CvSURFPoint*)cvGetSeqElem(objectKeypoints,i);
base_ set.push_ back(
cv::BaseKeypoint(point->pt.x,point->pt.y,train_ image));
}
//Detector training
cv::RNG rng( cvGetTickCount() );
cv::PatchGenerator gen(0,255,2,false,0.7,1.3,-CV_ PI/3,CV_ PI/3,
-CV_ PI/3,CV_ PI/3);
printf("RTree Classifier training...\n ");
detector.train(base_ set,rng,gen,24,cv::DEFAULT_ DEPTH,2000,
(int)base_ set.size(), detector.DEFAULT_ NUM_ QUANT_ BITS);
printf("Done\n ");
float* signature = new float[detector.original_ num_ classes()];
float* best_ corr;
int* best_ corr_ idx;
if (imageKeypoints->total > 0)
{
best_ corr = new float[imageKeypoints->total];
best_ corr_ idx = new int[imageKeypoints->total];
}
for(i=0; i < imageKeypoints->total; i++)
{
point=(CvSURFPoint*)cvGetSeqElem(imageKeypoints,i);
int part_ idx = -1;
float prob = 0.0f;
CvRect roi = cvRect((int)(point->pt.x) - patch_ width/2,
(int)(point->pt.y) - patch_ height/2,
patch_ width, patch_ height);
cvSetImageROI(test_ image, roi);
roi = cvGetImageROI(test_ image);
if(roi.width != patch_ width || roi.height != patch_ height)
{
best_ corr_ idx[i] = part_ idx;
best_ corr[i] = prob;
}
else
{
cvSetImageROI(test_ image, roi);
IplImage* roi_ image =
cvCreateImage(cvSize(roi.width, roi.height),
test_ image->depth, test_ image->nChannels);
cvCopy(test_ image,roi_ image);
detector.getSignature(roi_ image, signature);
for (int j = 0; j< detector.original_ num_ classes();j++)
{
if (prob < signature[j])
{
part_ idx = j;
prob = signature[j];
}
}
best_ corr_ idx[i] = part_ idx;
best_ corr[i] = prob;
if (roi_ image)
cvReleaseImage(& roi_ image);
}
cvResetImageROI(test_ image);
}
\end { lstlisting}
2010-10-19 11:51:56 +00:00
\fi