opencv/doc/features2d_object_detection.tex

405 lines
13 KiB
TeX

\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}
\cvCppFunc {RandomizedTree::read}
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}
\cvCppFunc {RandomizedTree::write}
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}
\cvCppFunc {RandomizedTree::applyQuantization}
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}
\cvCppFunc {RTreeClassifier::read}
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}
\cvCppFunc {RTreeClassifier::write}
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}
\cvCppFunc {RTreeClassifier::setQuantization}
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}
\fi