2010-05-12 01:44:00 +08:00
|
|
|
//*M///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
|
|
//
|
|
|
|
// By downloading, copying, installing or using the software you agree to this license.
|
|
|
|
// If you do not agree to this license, do not download, install,
|
|
|
|
// copy or use the software.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// License Agreement
|
|
|
|
// For Open Source Computer Vision Library
|
|
|
|
//
|
|
|
|
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
|
|
|
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
|
|
|
// Third party copyrights are property of their respective owners.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
// are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// * Redistribution's of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
//
|
|
|
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
//
|
|
|
|
// * The name of the copyright holders may not be used to endorse or promote products
|
|
|
|
// derived from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// This software is provided by the copyright holders and contributors "as is" and
|
|
|
|
// any express or implied warranties, including, but not limited to, the implied
|
|
|
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
|
|
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
|
|
|
// indirect, incidental, special, exemplary, or consequential damages
|
|
|
|
// (including, but not limited to, procurement of substitute goods or services;
|
|
|
|
// loss of use, data, or profits; or business interruption) however caused
|
|
|
|
// and on any theory of liability, whether in contract, strict liability,
|
|
|
|
// or tort (including negligence or otherwise) arising in any way out of
|
|
|
|
// the use of this software, even if advised of the possibility of such damage.
|
|
|
|
//
|
|
|
|
//M*/
|
|
|
|
#include "precomp.hpp"
|
2010-07-26 16:58:46 +08:00
|
|
|
#include <cstdio>
|
2010-05-12 01:44:00 +08:00
|
|
|
#include <iostream>
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
using namespace std;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
const int progressBarSize = 50;
|
|
|
|
namespace cv
|
|
|
|
{
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
CalonderClassifier::CalonderClassifier()
|
|
|
|
{
|
|
|
|
verbose = false;
|
|
|
|
clear();
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
CalonderClassifier::~CalonderClassifier()
|
|
|
|
{}
|
|
|
|
|
|
|
|
CalonderClassifier::CalonderClassifier( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
|
|
|
|
const vector<vector<int> >& labels, int _numClasses,
|
|
|
|
int _pathSize, int _numTrees, int _treeDepth,
|
|
|
|
int _numViews, int _compressedDim, int _compressType, int _numQuantBits,
|
|
|
|
const PatchGenerator &patchGenerator )
|
|
|
|
{
|
|
|
|
verbose = false;
|
|
|
|
train( points, refimgs, labels, _numClasses, _pathSize, _numTrees, _treeDepth, _numViews,
|
|
|
|
_compressedDim, _compressType, _numQuantBits, patchGenerator );
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getPatchSize() const
|
|
|
|
{ return patchSize; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getNumTrees() const
|
|
|
|
{ return numTrees; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getTreeDepth() const
|
|
|
|
{ return treeDepth; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getNumViews() const
|
|
|
|
{ return numViews; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getSignatureSize() const
|
|
|
|
{ return signatureSize; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getCompressType() const
|
|
|
|
{ return compressType; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getNumQuantBits() const
|
|
|
|
{ return numQuantBits; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getOrigNumClasses() const
|
|
|
|
{ return origNumClasses; }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::setVerbose( bool _verbose )
|
|
|
|
{
|
|
|
|
verbose = _verbose;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::clear()
|
|
|
|
{
|
|
|
|
patchSize = numTrees = origNumClasses = signatureSize = treeDepth = numViews = numQuantBits = 0;
|
|
|
|
compressType = COMPRESS_NONE;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
nodes.clear();
|
|
|
|
posteriors.clear();
|
|
|
|
#if QUANTIZATION_AVAILABLE
|
|
|
|
quantizedPosteriors.clear();
|
|
|
|
#endif
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
bool CalonderClassifier::empty() const
|
|
|
|
{
|
|
|
|
return posteriors.empty() && quantizedPosteriors.empty();
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews )
|
|
|
|
{
|
|
|
|
clear();
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
patchSize = _patchSize;
|
|
|
|
signatureSize = _signatureSize;
|
|
|
|
numTrees = _numTrees;
|
|
|
|
treeDepth = _treeDepth;
|
|
|
|
numViews = _numViews;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
numLeavesPerTree = 1 << treeDepth; // 2^d
|
|
|
|
numNodesPerTree = numLeavesPerTree - 1; // 2^d - 1
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
nodes = vector<Node>( numTrees*numNodesPerTree );
|
|
|
|
posteriors = vector<float>( numTrees*numLeavesPerTree*signatureSize, 0.f );
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
static int calcNumPoints( const vector<vector<Point2f> >& points )
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
for( size_t i = 0; i < points.size(); i++ )
|
|
|
|
count += points[i].size();
|
|
|
|
return count;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::train( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
|
|
|
|
const vector<vector<int> >& labels, int _numClasses,
|
|
|
|
int _patchSize, int _numTrees, int _treeDepth, int _numViews,
|
|
|
|
int _compressedDim, int _compressType, int _numQuantBits,
|
|
|
|
const PatchGenerator &patchGenerator )
|
|
|
|
{
|
|
|
|
if( points.empty() || refimgs.size() != points.size() )
|
|
|
|
CV_Error( CV_StsBadSize, "points vector must be no empty and refimgs must have the same size as points" );
|
|
|
|
if( _patchSize < 5 || _patchSize >= 256 )
|
|
|
|
CV_Error( CV_StsBadArg, "patchSize must be in [5, 255]");
|
|
|
|
if( _numTrees <= 0 || _treeDepth <= 0 )
|
|
|
|
CV_Error( CV_StsBadArg, "numTrees, treeDepth, numViews must be positive");
|
|
|
|
int numPoints = calcNumPoints( points );
|
|
|
|
if( !labels.empty() && ( labels.size() != points.size() || _numClasses <=0 || _numClasses > numPoints ) )
|
|
|
|
CV_Error( CV_StsBadArg, "labels has incorrect size or _numClasses is not in [1, numPoints]");
|
|
|
|
_numViews = std::max( 1, _numViews );
|
|
|
|
|
|
|
|
int _origNumClasses = labels.empty() ? numPoints : _numClasses;
|
|
|
|
|
|
|
|
if( verbose )
|
|
|
|
{
|
|
|
|
cout << "Using train parameters:" << endl;
|
|
|
|
cout << " patchSize=" << _patchSize << endl;
|
|
|
|
cout << " numTrees=" << _numTrees << endl;
|
|
|
|
cout << " treeDepth=" << _treeDepth << endl;
|
|
|
|
cout << " numViews=" << _numViews << endl;
|
|
|
|
cout << " compressedDim=" << _compressedDim << endl;
|
|
|
|
cout << " compressType=" << _compressType << endl;
|
|
|
|
cout << " numQuantBits=" << _numQuantBits << endl;
|
|
|
|
cout << endl
|
|
|
|
<< " numPoints=" << numPoints << endl;
|
|
|
|
cout << " origNumClasses=" << _origNumClasses << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
prepare( _patchSize, _origNumClasses, _numTrees, _treeDepth, _numViews );
|
|
|
|
|
|
|
|
origNumClasses = _origNumClasses;
|
|
|
|
vector<int> leafSampleCounters = vector<int>( numTrees*numLeavesPerTree, 0 );
|
|
|
|
// generate nodes
|
|
|
|
RNG rng = theRNG();
|
|
|
|
for( int i = 0; i < numTrees*numNodesPerTree; i++ )
|
|
|
|
{
|
|
|
|
uchar x1 = rng(_patchSize);
|
|
|
|
uchar y1 = rng(_patchSize);
|
|
|
|
uchar x2 = rng(_patchSize);
|
|
|
|
uchar y2 = rng(_patchSize);
|
|
|
|
nodes[i] = Node(x1, y1, x2, y2);
|
|
|
|
}
|
|
|
|
|
|
|
|
Size size( patchSize, patchSize );
|
|
|
|
Mat patch;
|
|
|
|
if( verbose ) cout << "START training..." << endl;
|
|
|
|
for( size_t treeIdx = 0; treeIdx < (size_t)numTrees; treeIdx++ )
|
|
|
|
{
|
|
|
|
if( verbose ) cout << "< tree " << treeIdx << endl;
|
|
|
|
int globalPointIdx = 0;
|
|
|
|
int* treeLeafSampleCounters = &leafSampleCounters[treeIdx*numLeavesPerTree];
|
|
|
|
float* treePosteriors = &posteriors[treeIdx*numLeavesPerTree*signatureSize];
|
|
|
|
for( size_t imgIdx = 0; imgIdx < points.size(); imgIdx++ )
|
|
|
|
{
|
|
|
|
const Point2f* imgPoints = &points[imgIdx][0];
|
|
|
|
const int* imgLabels = labels.empty() ? 0 : &labels[imgIdx][0];
|
|
|
|
int last = -1, cur;
|
|
|
|
for( size_t pointIdx = 0; pointIdx < points[imgIdx].size(); pointIdx++, globalPointIdx++ )
|
|
|
|
{
|
|
|
|
int classID = imgLabels==0 ? globalPointIdx : imgLabels[pointIdx];
|
|
|
|
Point2f pt = imgPoints[pointIdx];
|
|
|
|
const Mat& src = refimgs[imgIdx];
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
if( verbose && (cur = (int)((float)globalPointIdx/numPoints*progressBarSize)) != last )
|
|
|
|
{
|
|
|
|
last = cur;
|
|
|
|
cout << ".";
|
|
|
|
cout.flush();
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
CV_Assert( classID >= 0 && classID < signatureSize );
|
|
|
|
for( int v = 0; v < numViews; v++ )
|
|
|
|
{
|
|
|
|
patchGenerator( src, pt, patch, size, rng );
|
|
|
|
// add sample
|
|
|
|
int leafIdx = getLeafIdx( treeIdx, patch );
|
|
|
|
treeLeafSampleCounters[leafIdx]++;
|
|
|
|
treePosteriors[leafIdx*signatureSize + classID]++;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
if( verbose ) cout << endl << ">" << endl;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
_compressedDim = std::max( 0, std::min(signatureSize, _compressedDim) );
|
|
|
|
_numQuantBits = std::max( 0, std::min((int)MAX_NUM_QUANT_BITS, _numQuantBits) );
|
|
|
|
finalize( _compressedDim, _compressType, _numQuantBits, leafSampleCounters );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
if( verbose ) cout << "END training." << endl;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int CalonderClassifier::getLeafIdx( int treeIdx, const Mat& patch ) const
|
|
|
|
{
|
|
|
|
const Node* treeNodes = &nodes[treeIdx*numNodesPerTree];
|
|
|
|
int idx = 0;
|
|
|
|
for( int d = 0; d < treeDepth-1; d++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
int offset = treeNodes[idx](patch);
|
|
|
|
idx = 2*idx + 1 + offset;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
return idx;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::finalize( int _compressedDim, int _compressType, int _numQuantBits,
|
|
|
|
const vector<int>& leafSampleCounters )
|
|
|
|
{
|
|
|
|
for( int ti = 0; ti < numTrees; ti++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
const int* treeLeafSampleCounters = &leafSampleCounters[ti*numLeavesPerTree];
|
|
|
|
float* treePosteriors = &posteriors[ti*numLeavesPerTree*signatureSize];
|
2010-05-12 01:44:00 +08:00
|
|
|
// Normalize by number of patches to reach each leaf
|
2010-07-26 16:58:46 +08:00
|
|
|
for( int li = 0; li < numLeavesPerTree; li++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
int sampleCount = treeLeafSampleCounters[li];
|
|
|
|
if( sampleCount != 0 )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
float normalizer = 1.0f / sampleCount;
|
|
|
|
int leafPosteriorIdx = li*signatureSize;
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
|
|
|
treePosteriors[leafPosteriorIdx + ci] *= normalizer;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
// apply compressive sensing
|
|
|
|
if( _compressedDim > 0 && _compressedDim < signatureSize )
|
|
|
|
compressLeaves( _compressedDim, _compressType );
|
|
|
|
else
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
if( verbose )
|
|
|
|
cout << endl << "[WARNING] NO compression to leaves applied, because _compressedDim=" << _compressedDim << endl;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
// convert float-posteriors to uchar-posteriors (quantization step)
|
|
|
|
#if QUANTIZATION_AVAILABLE
|
|
|
|
if( _numQuantBits > 0 )
|
|
|
|
quantizePosteriors( _numQuantBits );
|
|
|
|
else
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
if( verbose )
|
|
|
|
cout << endl << "[WARNING] NO quantization to posteriors, because _numQuantBits=" << _numQuantBits << endl;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
#endif
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
Mat createCompressionMatrix( int rows, int cols, int distrType )
|
|
|
|
{
|
|
|
|
Mat mtr( rows, cols, CV_32FC1 );
|
|
|
|
assert( rows <= cols );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
RNG rng(23);
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
if( distrType == CalonderClassifier::COMPRESS_DISTR_GAUSS )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
float sigma = 1./rows;
|
|
|
|
for( int y = 0; y < rows; y++ )
|
|
|
|
for( int x = 0; x < cols; x++ )
|
|
|
|
mtr.at<float>(y,x) = rng.gaussian( sigma );
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
else if( distrType == CalonderClassifier::COMPRESS_DISTR_BERNOULLI )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
float par = (float)(1./sqrt(rows));
|
|
|
|
for( int y = 0; y < rows; y++ )
|
|
|
|
for( int x = 0; x < cols; x++ )
|
|
|
|
mtr.at<float>(y,x) = rng(2)==0 ? par : -par;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
else if( distrType == CalonderClassifier::COMPRESS_DISTR_DBFRIENDLY )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
float par = (float)sqrt(3./rows);
|
|
|
|
for( int y = 0; y < rows; y++ )
|
|
|
|
for( int x = 0; x < cols; x++ )
|
|
|
|
{
|
|
|
|
int rng6 = rng(6);
|
|
|
|
mtr.at<float>(y,x) = rng6==0 ? par : (rng6==1 ? -par : 0.f);
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
else
|
|
|
|
CV_Assert( 0 );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
return mtr;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::compressLeaves( int _compressedDim, int _compressType )
|
|
|
|
{
|
|
|
|
if( verbose )
|
|
|
|
cout << endl << "[OK] compressing leaves with matrix " << _compressedDim << " x " << signatureSize << endl;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
Mat compressionMtrT = (createCompressionMatrix( _compressedDim, signatureSize, _compressType )).t();
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
vector<float> comprPosteriors( numTrees*numLeavesPerTree*_compressedDim, 0);
|
|
|
|
Mat( numTrees*numLeavesPerTree, _compressedDim, CV_32FC1, &comprPosteriors[0] ) =
|
|
|
|
Mat( numTrees*numLeavesPerTree, signatureSize, CV_32FC1, &posteriors[0]) * compressionMtrT;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
posteriors.resize( comprPosteriors.size() );
|
|
|
|
copy( comprPosteriors.begin(), comprPosteriors.end(), posteriors.begin() );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
signatureSize = _compressedDim;
|
|
|
|
compressType = _compressType;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
#if QUANTIZATION_AVAILABLE
|
|
|
|
static float percentile( const float* data, int n, float p )
|
|
|
|
{
|
|
|
|
assert( n>0 );
|
|
|
|
assert( p>=0 && p<=1 );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
vector<float> vec( data, data+n );
|
|
|
|
sort(vec.begin(), vec.end());
|
|
|
|
int ix = (int)(p*(n-1));
|
|
|
|
return vec[ix];
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void quantizeVector( const float* src, int dim, float fbounds[2], uchar ubounds[2], uchar* dst )
|
|
|
|
{
|
|
|
|
assert( fbounds[0] < fbounds[1] );
|
|
|
|
assert( ubounds[0] < ubounds[1] );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
float normFactor = 1.f/(fbounds[1] - fbounds[0]);
|
|
|
|
for( int i = 0; i < dim; i++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
float part = (src[i] - fbounds[0]) * normFactor;
|
|
|
|
assert( 0 <= part && part <= 1 ) ;
|
|
|
|
uchar val = ubounds[0] + (uchar)( part*ubounds[1] );
|
|
|
|
dst[i] = std::max( 0, (int)std::min(ubounds[1], val) );
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::quantizePosteriors( int _numQuantBits, bool isClearFloatPosteriors )
|
|
|
|
{
|
|
|
|
uchar ubounds[] = { 0, (uchar)((1<<_numQuantBits)-1) };
|
|
|
|
float fbounds[] = { 0.f, 0.f };
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int totalLeavesCount = numTrees*numLeavesPerTree;
|
|
|
|
for( int li = 0; li < totalLeavesCount; li++ ) // TODO for some random choosen leaves !
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
fbounds[0] += percentile( &posteriors[li*signatureSize], signatureSize, GET_LOWER_QUANT_PERC() );
|
|
|
|
fbounds[1] += percentile( &posteriors[li*signatureSize], signatureSize, GET_UPPER_QUANT_PERC() );
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
fbounds[0] /= totalLeavesCount;
|
|
|
|
fbounds[1] /= totalLeavesCount;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
quantizedPosteriors.resize( posteriors.size() );
|
|
|
|
quantizeVector( &posteriors[0], posteriors.size(), fbounds, ubounds, &quantizedPosteriors[0] );
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
if( isClearFloatPosteriors )
|
|
|
|
clearFloatPosteriors();
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::clearFloatPosteriors()
|
|
|
|
{
|
|
|
|
quantizedPosteriors.clear();
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
#endif
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<float>& signature, float thresh ) const
|
|
|
|
{
|
|
|
|
if( img.empty() || img.type() != CV_8UC1 )
|
2010-05-12 01:44:00 +08:00
|
|
|
return;
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
Mat patch;
|
|
|
|
getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
|
|
|
|
(*this)( patch, signature, thresh );
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::operator()( const Mat& patch, vector<float>& signature, float thresh ) const
|
|
|
|
{
|
|
|
|
if( posteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols < patchSize || patch.rows < patchSize )
|
|
|
|
return;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
int treePostSize = numLeavesPerTree*signatureSize;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
signature.resize( signatureSize, 0.f );
|
|
|
|
float* sig = &signature[0];
|
|
|
|
for( int ti = 0; ti < numTrees; ti++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
int leafIdx = getLeafIdx( ti, patch );
|
|
|
|
const float* post = &posteriors[ti*treePostSize + leafIdx*signatureSize];
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
|
|
|
sig[ci] += post[ci];
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
float coef = 1.f/numTrees;
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
sig[ci] *= coef;
|
|
|
|
if( sig[ci] < thresh )
|
|
|
|
sig[ci] = 0;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
#if QUANTIZATION_AVAILABLE
|
|
|
|
void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<uchar>& signature, uchar thresh ) const
|
|
|
|
{
|
|
|
|
if( img.empty() || img.type() != CV_8UC1 )
|
|
|
|
return;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
Mat patch;
|
|
|
|
getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
|
|
|
|
(*this)(patch, signature, thresh );
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::operator()( const Mat& patch, vector<uchar>& signature, uchar thresh ) const
|
|
|
|
{
|
|
|
|
if( quantizedPosteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols > patchSize || patch.rows > patchSize )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int treePostSize = numLeavesPerTree*signatureSize;
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
vector<float> sum( signatureSize, 0.f );
|
|
|
|
for( int ti = 0; ti < numTrees; ti++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
int leafIdx = getLeafIdx( ti, patch );
|
|
|
|
const uchar* post = &quantizedPosteriors[ti*treePostSize + leafIdx*signatureSize];
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
|
|
|
sum[ci] += post[ci];
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
float coef = 1.f/numTrees;
|
|
|
|
signature.resize( signatureSize );
|
|
|
|
uchar* sig = &signature[0];
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
sig[ci] = (uchar)(sum[ci]*coef);
|
|
|
|
if( sig[ci] < thresh )
|
|
|
|
sig[ci] = 0;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
}
|
|
|
|
#endif
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::read( const FileNode& fn )
|
|
|
|
{
|
|
|
|
prepare( fn["patchSize"], fn["signatureSize"], fn["numTrees"], fn["treeDepth"], fn["numViews"] );
|
|
|
|
origNumClasses = fn["origNumClasses"];
|
|
|
|
compressType = fn["compressType"];
|
|
|
|
int _numQuantBits = fn["numQuantBits"];
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
for( int ti = 0; ti < numTrees; ti++ )
|
|
|
|
{
|
|
|
|
stringstream treeName;
|
|
|
|
treeName << "tree" << ti;
|
|
|
|
FileNode treeFN = fn["trees"][treeName.str()];
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
Node* treeNodes = &nodes[ti*numNodesPerTree];
|
|
|
|
FileNodeIterator nodesFNIter = treeFN["nodes"].begin();
|
|
|
|
for( int ni = 0; ni < numNodesPerTree; ni++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
Node* node = treeNodes + ni;
|
|
|
|
nodesFNIter >> node->x1 >> node->y1 >> node->x2 >> node->y2;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
FileNode posteriorsFN = treeFN["posteriors"];
|
|
|
|
for( int li = 0; li < numLeavesPerTree; li++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
stringstream leafName;
|
|
|
|
leafName << "leaf" << li;
|
|
|
|
float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
|
|
|
|
FileNodeIterator leafFNIter = posteriorsFN[leafName.str()].begin();
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
|
|
|
leafFNIter >> post[ci];
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
#if QUANTIZATION_AVAILABLE
|
|
|
|
if( _numQuantBits )
|
|
|
|
quantizePosteriors(_numQuantBits);
|
2010-05-12 01:44:00 +08:00
|
|
|
#endif
|
2010-07-26 16:58:46 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
void CalonderClassifier::write( FileStorage& fs ) const
|
|
|
|
{
|
|
|
|
if( !fs.isOpened() )
|
|
|
|
return;
|
|
|
|
fs << "patchSize" << patchSize;
|
|
|
|
fs << "numTrees" << numTrees;
|
|
|
|
fs << "treeDepth" << treeDepth;
|
|
|
|
fs << "numViews" << numViews;
|
|
|
|
fs << "origNumClasses" << origNumClasses;
|
|
|
|
fs << "signatureSize" << signatureSize;
|
|
|
|
fs << "compressType" << compressType;
|
|
|
|
fs << "numQuantBits" << numQuantBits;
|
|
|
|
|
|
|
|
fs << "trees" << "{";
|
|
|
|
for( int ti = 0; ti < numTrees; ti++ )
|
|
|
|
{
|
|
|
|
stringstream treeName;
|
|
|
|
treeName << "tree" << ti;
|
|
|
|
fs << treeName.str() << "{";
|
|
|
|
|
|
|
|
fs << "nodes" << "[:";
|
|
|
|
const Node* treeNodes = &nodes[ti*numNodesPerTree];
|
|
|
|
for( int ni = 0; ni < numNodesPerTree; ni++ )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-07-26 16:58:46 +08:00
|
|
|
const Node* node = treeNodes + ni;
|
|
|
|
fs << node->x1 << node->y1 << node->x2 << node->y2;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
fs << "]"; // nodes
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-07-26 16:58:46 +08:00
|
|
|
fs << "posteriors" << "{";
|
|
|
|
for( int li = 0; li < numLeavesPerTree; li++ )
|
|
|
|
{
|
|
|
|
stringstream leafName;
|
|
|
|
leafName << "leaf" << li;
|
|
|
|
fs << leafName.str() << "[:";
|
|
|
|
const float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
|
|
|
|
for( int ci = 0; ci < signatureSize; ci++ )
|
|
|
|
{
|
|
|
|
fs << post[ci];
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
fs << "]"; // leaf
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
fs << "}"; // posteriors
|
|
|
|
fs << "}"; // tree
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2010-07-26 16:58:46 +08:00
|
|
|
fs << "}"; // trees
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
|
|
|
}
|