Commit 3f1cce24 authored by Vlad Shakhuro's avatar Vlad Shakhuro

Waldboost with LBP

parent c05a7e01
#ifndef _OPENCV_CASCADECLASSIFIER_H_
#define _OPENCV_CASCADECLASSIFIER_H_
#include <ctime>
#include "traincascade_features.h"
#include "lbpfeatures.h"
#define CC_CASCADE_FILENAME "cascade.xml"
#define CC_PARAMS_FILENAME "params.xml"
#define CC_CASCADE_PARAMS "cascadeParams"
#define CC_STAGE_TYPE "stageType"
#define CC_FEATURE_TYPE "featureType"
#define CC_HEIGHT "height"
#define CC_WIDTH "width"
#define CC_STAGE_NUM "stageNum"
#define CC_STAGES "stages"
#define CC_STAGE_PARAMS "stageParams"
#define CC_BOOST "BOOST"
#define CC_BOOST_TYPE "boostType"
#define CC_DISCRETE_BOOST "DAB"
#define CC_REAL_BOOST "RAB"
#define CC_LOGIT_BOOST "LB"
#define CC_GENTLE_BOOST "GAB"
#define CC_MINHITRATE "minHitRate"
#define CC_MAXFALSEALARM "maxFalseAlarm"
#define CC_TRIM_RATE "weightTrimRate"
#define CC_MAX_DEPTH "maxDepth"
#define CC_WEAK_COUNT "maxWeakCount"
#define CC_STAGE_THRESHOLD "stageThreshold"
#define CC_WEAK_CLASSIFIERS "weakClassifiers"
#define CC_INTERNAL_NODES "internalNodes"
#define CC_LEAF_VALUES "leafValues"
#define CC_FEATURES FEATURES
#define CC_FEATURE_PARAMS "featureParams"
#define CC_MAX_CAT_COUNT "maxCatCount"
#define CC_FEATURE_SIZE "featSize"
#define CC_HAAR "HAAR"
#define CC_MODE "mode"
#define CC_MODE_BASIC "BASIC"
#define CC_MODE_CORE "CORE"
#define CC_MODE_ALL "ALL"
#define CC_RECTS "rects"
#define CC_TILTED "tilted"
#define CC_LBP "LBP"
#define CC_RECT "rect"
#endif
#include <opencv2/opencv.hpp>
#include "traincascade_features.h"
#include "cascadeclassifier.h"
using namespace std;
using namespace cv;
float calcNormFactor( const Mat& sum, const Mat& sqSum )
{
CV_DbgAssert( sum.cols > 3 && sqSum.rows > 3 );
Rect normrect( 1, 1, sum.cols - 3, sum.rows - 3 );
size_t p0, p1, p2, p3;
CV_SUM_OFFSETS( p0, p1, p2, p3, normrect, sum.step1() )
double area = normrect.width * normrect.height;
const int *sp = sum.ptr<int>();
int valSum = sp[p0] - sp[p1] - sp[p2] + sp[p3];
const double *sqp = sqSum.ptr<double>();
double valSqSum = sqp[p0] - sqp[p1] - sqp[p2] + sqp[p3];
return (float) sqrt( (double) (area * valSqSum - (double)valSum * valSum) );
}
CvParams::CvParams() : name( "params" ) {}
void CvParams::printDefaults() const
{ cout << "--" << name << "--" << endl; }
void CvParams::printAttrs() const {}
bool CvParams::scanAttr( const string, const string ) { return false; }
//---------------------------- FeatureParams --------------------------------------
CvFeatureParams::CvFeatureParams() : maxCatCount( 0 ), featSize( 1 )
{
name = CC_FEATURE_PARAMS;
}
void CvFeatureParams::init( const CvFeatureParams& fp )
{
maxCatCount = fp.maxCatCount;
featSize = fp.featSize;
}
void CvFeatureParams::write( FileStorage &fs ) const
{
fs << CC_MAX_CAT_COUNT << maxCatCount;
fs << CC_FEATURE_SIZE << featSize;
}
bool CvFeatureParams::read( const FileNode &node )
{
if ( node.empty() )
return false;
maxCatCount = node[CC_MAX_CAT_COUNT];
featSize = node[CC_FEATURE_SIZE];
return ( maxCatCount >= 0 && featSize >= 1 );
}
Ptr<CvFeatureParams> CvFeatureParams::create( int featureType )
{
return Ptr<CvFeatureParams>(new CvLBPFeatureParams);
}
//------------------------------------- FeatureEvaluator ---------------------------------------
void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams,
int _maxSampleCount, Size _winSize )
{
CV_Assert(_maxSampleCount > 0);
featureParams = (CvFeatureParams *)_featureParams;
winSize = _winSize;
numFeatures = 0;
cls.create( (int)_maxSampleCount, 1, CV_32FC1 );
generateFeatures();
}
void CvFeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx, const std::vector<int>& feature_ind)
{
//CV_Assert(img.cols == winSize.width);
//CV_Assert(img.rows == winSize.height);
CV_Assert(idx < cls.rows);
cls.ptr<float>(idx)[0] = clsLabel;
}
Ptr<CvFeatureEvaluator> CvFeatureEvaluator::create(int type)
{
return Ptr<CvFeatureEvaluator>(new CvLBPEvaluator);
}
#include <opencv2/opencv.hpp>
#include "lbpfeatures.h"
#include "cascadeclassifier.h"
#include <iostream>
using namespace cv;
CvLBPFeatureParams::CvLBPFeatureParams()
{
maxCatCount = 256;
name = LBPF_NAME;
}
void CvLBPEvaluator::init(const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize)
{
CV_Assert( _maxSampleCount > 0);
sum.create((int)_maxSampleCount, (_winSize.width + 1) * (_winSize.height + 1), CV_32SC1);
CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );
}
void CvLBPEvaluator::setImage(const Mat &img, uchar clsLabel, int idx,
const std::vector<int> &feature_ind)
{
CV_DbgAssert( !sum.empty() );
CvFeatureEvaluator::setImage( img, clsLabel, idx, feature_ind );
integral( img, sum );
cur_sum = sum;
offset_ = int(sum.ptr<int>(1) - sum.ptr<int>());
for (size_t i = 0; i < feature_ind.size(); ++i) {
features[feature_ind[i]].calcPoints(offset_);
}
}
void CvLBPEvaluator::writeFeatures( FileStorage &fs, const Mat& featureMap ) const
{
_writeFeatures( features, fs, featureMap );
}
void CvLBPEvaluator::generateFeatures()
{
int offset = winSize.width + 1;
for( int x = 0; x < winSize.width; x++ )
for( int y = 0; y < winSize.height; y++ )
for( int w = 1; w <= winSize.width / 3; w++ )
for( int h = 1; h <= winSize.height / 3; h++ )
if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )
features.push_back( Feature(offset, x, y, w, h ) );
numFeatures = (int)features.size();
}
CvLBPEvaluator::Feature::Feature()
{
rect = cvRect(0, 0, 0, 0);
}
CvLBPEvaluator::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight )
{
x_ = x;
y_ = y;
block_w_ = _blockWidth;
block_h_ = _blockHeight;
offset_ = offset;
calcPoints(offset);
}
void CvLBPEvaluator::Feature::calcPoints(int offset)
{
Rect tr = rect = cvRect(x_, y_, block_w_, block_h_);
CV_SUM_OFFSETS( p[0], p[1], p[4], p[5], tr, offset )
tr.x += 2*rect.width;
CV_SUM_OFFSETS( p[2], p[3], p[6], p[7], tr, offset )
tr.y +=2*rect.height;
CV_SUM_OFFSETS( p[10], p[11], p[14], p[15], tr, offset )
tr.x -= 2*rect.width;
CV_SUM_OFFSETS( p[8], p[9], p[12], p[13], tr, offset )
offset_ = offset;
}
void CvLBPEvaluator::Feature::write(FileStorage &fs) const
{
fs << CC_RECT << "[:" << rect.x << rect.y << rect.width << rect.height << "]";
}
#ifndef _OPENCV_LBPFEATURES_H_
#define _OPENCV_LBPFEATURES_H_
#include "traincascade_features.h"
#define LBPF_NAME "lbpFeatureParams"
struct CvLBPFeatureParams : CvFeatureParams
{
CvLBPFeatureParams();
};
class CvLBPEvaluator : public CvFeatureEvaluator
{
public:
virtual ~CvLBPEvaluator() {}
virtual void init(const CvFeatureParams *_featureParams,
int _maxSampleCount, cv::Size _winSize );
virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx, const std::vector<int> &feature_ind);
virtual void setWindow(const cv::Point& p)
{ cur_sum = sum.rowRange(p.y, p.y + winSize.height).colRange(p.x, p.x + winSize.width); }
virtual float operator()(int featureIdx, int sampleIdx)
{ return (float)features[featureIdx].calc( cur_sum, offset_, sampleIdx); }
virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const;
protected:
virtual void generateFeatures();
class Feature
{
public:
Feature();
Feature( int offset, int x, int y, int _block_w, int _block_h );
uchar calc( const cv::Mat& _sum, int offset, size_t y );
void write( cv::FileStorage &fs ) const;
cv::Rect rect;
int p[16];
int x_, y_, block_w_, block_h_, offset_;
void calcPoints(int offset);
};
std::vector<Feature> features;
cv::Mat sum, cur_sum;
int offset_;
};
inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum, int offset, size_t y)
{
const int* psum = _sum.ptr<int>();
int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];
return (uchar)((psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) | // 0
(psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) | // 1
(psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) | // 2
(psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) | // 5
(psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8
(psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) | // 7
(psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) | // 6
(psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0)); // 3
}
#endif
#include <opencv2/opencv.hpp>
#include <vector>
#include <sstream>
#include <cassert>
#include <iostream>
#include <fstream>
#include <iomanip>
#include "lbpfeatures.h"
using namespace std;
using namespace cv;
static void compute_cdf(const Mat1b& data,
const Mat1f& weights,
Mat1f& cdf)
{
for (int i = 0; i < cdf.cols; ++i)
cdf(0, i) = 0;
for (int i = 0; i < weights.cols; ++i) {
cdf(0, data(0, i)) += weights(0, i);
}
for (int i = 1; i < cdf.cols; ++i) {
cdf(0, i) += cdf(0, i - 1);
}
}
void compute_min_step(const Mat &data_pos, const Mat &data_neg, size_t n_bins,
Mat &data_min, Mat &data_step)
{
// Check that quantized data will fit in unsigned char
assert(n_bins <= 256);
assert(data_pos.rows == data_neg.rows);
Mat reduced_pos, reduced_neg;
reduce(data_pos, reduced_pos, 1, CV_REDUCE_MIN);
reduce(data_neg, reduced_neg, 1, CV_REDUCE_MIN);
min(reduced_pos, reduced_neg, data_min);
data_min -= 0.01;
Mat data_max;
reduce(data_pos, reduced_pos, 1, CV_REDUCE_MAX);
reduce(data_neg, reduced_neg, 1, CV_REDUCE_MAX);
max(reduced_pos, reduced_neg, data_max);
data_max += 0.01;
data_step = (data_max - data_min) / (n_bins - 1);
}
void quantize_data(Mat &data, Mat1f &data_min, Mat1f &data_step)
{
#pragma omp parallel for
for (int col = 0; col < data.cols; ++col) {
data.col(col) -= data_min;
data.col(col) /= data_step;
}
data.convertTo(data, CV_8U);
}
class WaldBoost
{
public:
WaldBoost(int weak_count):
weak_count_(weak_count),
thresholds_(),
alphas_(),
feature_indices_(),
polarities_(),
cascade_thresholds_() {}
WaldBoost():
weak_count_(),
thresholds_(),
alphas_(),
feature_indices_(),
polarities_(),
cascade_thresholds_() {}
vector<int> get_feature_indices()
{
return feature_indices_;
}
void detect(Ptr<CvFeatureEvaluator> eval,
const Mat& img, const vector<float>& scales,
vector<Rect>& bboxes, Mat1f& confidences)
{
bboxes.clear();
confidences.release();
Mat resized_img;
int step = 4;
float h;
for (size_t i = 0; i < scales.size(); ++i) {
float scale = scales[i];
resize(img, resized_img, Size(), scale, scale);
eval->setImage(resized_img, 0, 0, feature_indices_);
int n_rows = 24 / scale;
int n_cols = 24 / scale;
for (int r = 0; r + 24 < resized_img.rows; r += step) {
for (int c = 0; c + 24 < resized_img.cols; c += step) {
//eval->setImage(resized_img(Rect(c, r, 24, 24)), 0, 0);
eval->setWindow(Point(c, r));
if (predict(eval, &h) == +1) {
int row = r / scale;
int col = c / scale;
bboxes.push_back(Rect(col, row, n_cols, n_rows));
confidences.push_back(h);
}
}
}
}
groupRectangles(bboxes, 3, 0.7);
}
void fit(Mat& data_pos, Mat& data_neg)
{
// data_pos: F x N_pos
// data_neg: F x N_neg
// every feature corresponds to row
// every sample corresponds to column
assert(data_pos.rows >= weak_count_);
assert(data_pos.rows == data_neg.rows);
vector<bool> feature_ignore;
for (int i = 0; i < data_pos.rows; ++i) {
feature_ignore.push_back(false);
}
Mat1f pos_weights(1, data_pos.cols, 1.0f / (2 * data_pos.cols));
Mat1f neg_weights(1, data_neg.cols, 1.0f / (2 * data_neg.cols));
Mat1f pos_trace(1, data_pos.cols, 0.0f);
Mat1f neg_trace(1, data_neg.cols, 0.0f);
bool quantize = false;
if (data_pos.type() != CV_8U) {
cerr << "quantize" << endl;
quantize = true;
}
Mat1f data_min, data_step;
int n_bins = 256;
if (quantize) {
compute_min_step(data_pos, data_neg, n_bins, data_min, data_step);
quantize_data(data_pos, data_min, data_step);
quantize_data(data_neg, data_min, data_step);
}
cerr << "pos=" << data_pos.cols << " neg=" << data_neg.cols << endl;
for (int i = 0; i < weak_count_; ++i) {
// Train weak learner with lowest error using weights
double min_err = DBL_MAX;
int min_feature_ind = -1;
int min_polarity = 0;
int threshold_q = 0;
float min_threshold = 0;
#pragma omp parallel for
for (int feat_i = 0; feat_i < data_pos.rows; ++feat_i) {
if (feature_ignore[feat_i])
continue;
// Construct cdf
Mat1f pos_cdf(1, n_bins), neg_cdf(1, n_bins);
compute_cdf(data_pos.row(feat_i), pos_weights, pos_cdf);
compute_cdf(data_neg.row(feat_i), neg_weights, neg_cdf);
float neg_total = sum(neg_weights)[0];
Mat1f err_direct = pos_cdf + neg_total - neg_cdf;
Mat1f err_backward = 1.0f - err_direct;
int idx1[2], idx2[2];
double err1, err2;
minMaxIdx(err_direct, &err1, NULL, idx1);
minMaxIdx(err_backward, &err2, NULL, idx2);
#pragma omp critical
{
if (min(err1, err2) < min_err) {
if (err1 < err2) {
min_err = err1;
min_polarity = +1;
threshold_q = idx1[1];
} else {
min_err = err2;
min_polarity = -1;
threshold_q = idx2[1];
}
min_feature_ind = feat_i;
if (quantize) {
min_threshold = data_min(feat_i, 0) + data_step(feat_i, 0) *
(threshold_q + .5f);
} else {
min_threshold = threshold_q + .5f;
}
}
}
}
float alpha = .5f * log((1 - min_err) / min_err);
alphas_.push_back(alpha);
feature_indices_.push_back(min_feature_ind);
thresholds_.push_back(min_threshold);
polarities_.push_back(min_polarity);
feature_ignore[min_feature_ind] = true;
double loss = 0;
// Update positive weights
for (int j = 0; j < data_pos.cols; ++j) {
int val = data_pos.at<unsigned char>(min_feature_ind, j);
int label = min_polarity * (val - threshold_q) >= 0 ? +1 : -1;
pos_weights(0, j) *= exp(-alpha * label);
pos_trace(0, j) += alpha * label;
loss += exp(-pos_trace(0, j)) / (2.0f * data_pos.cols);
}
double min_pos, max_neg = -100000;
minMaxIdx(pos_trace, &min_pos, NULL);
// Update negative weights
for (int j = 0; j < data_neg.cols; ++j) {
int val = data_neg.at<unsigned char>(min_feature_ind, j);
int label = min_polarity * (val - threshold_q) >= 0 ? +1 : -1;
neg_weights(0, j) *= exp(alpha * label);
neg_trace(0, j) += alpha * label;
loss += exp(+neg_trace(0, j)) / (2.0f * data_neg.cols);
}
// Compute threshold
double a = 0.02;
int col_pos = 0, col_neg = 0;
int cur_pos = 0, cur_neg = 0;
int max_col = -1;
bool max_pos = false;
for (;
col_pos < data_pos.cols &&
col_neg < data_neg.cols; ) {
bool pos = false;
if (data_pos.at<uchar>(min_feature_ind, col_pos) <
data_pos.at<uchar>(min_feature_ind, col_neg)) {
col_pos += 1;
cur_pos += 1;
pos = true;
} else {
col_neg += 1;
cur_neg += 1;
}
float p_neg = cur_neg / (float)data_neg.cols;
float p_pos = cur_pos / (float)data_pos.cols;
if (a * p_neg > p_pos) {
if (pos)
max_col = col_pos;
else
max_col = col_neg;
max_pos = pos;
}
}
if (max_pos) {
cascade_thresholds_.push_back(pos_trace(0, max_col));
} else {
cascade_thresholds_.push_back(neg_trace(0, max_col));
}
cerr << "i=" << setw(4) << i;
cerr << " feat=" << setw(5) << min_feature_ind;
cerr << " thr=" << setw(3) << threshold_q;
cerr << " alpha=" << fixed << setprecision(3)
<< alpha << " err=" << fixed << setprecision(3) << min_err
<< " loss=" << scientific << loss << endl;
if (loss < 1e-50 || min_err > 0.5) {
cerr << "Stopping early" << endl;
weak_count_ = i + 1;
break;
}
// Normalize weights
double z = (sum(pos_weights) + sum(neg_weights))[0];
pos_weights /= z;
neg_weights /= z;
}
}
int predict(Ptr<CvFeatureEvaluator> eval, float *h) const
{
const float thr = -2.5;
assert(feature_indices_.size() == size_t(weak_count_));
float res = 0;
for (int i = 0; i < weak_count_; ++i) {
float val = (*eval)(feature_indices_[i], 0);
int label = polarities_[i] * (val - thresholds_[i]) > 0 ? +1: -1;
res += alphas_[i] * label;
if (res < cascade_thresholds_[i]) {
return -1;
}
}
*h = res;
return res > thr ? +1 : -1;
}
void save(const string& filename)
{
ofstream f(filename.c_str());
f << weak_count_ << endl;
for (size_t i = 0; i < thresholds_.size(); ++i) {
f << thresholds_[i] << " ";
}
f << endl;
for (size_t i = 0; i < alphas_.size(); ++i) {
f << alphas_[i] << " ";
}
f << endl;
for (size_t i = 0; i < polarities_.size(); ++i) {
f << polarities_[i] << " ";
}
f << endl;
for (size_t i = 0; i < cascade_thresholds_.size(); ++i) {
f << cascade_thresholds_[i] << " ";
}
f << endl;
for (size_t i = 0; i < feature_indices_.size(); ++i) {
f << feature_indices_[i] << " ";
}
f << endl;
}
void load(const string& filename)
{
ifstream f(filename.c_str());
f >> weak_count_;
thresholds_.resize(weak_count_);
alphas_.resize(weak_count_);
polarities_.resize(weak_count_);
cascade_thresholds_.resize(weak_count_);
feature_indices_.resize(weak_count_);
for (int i = 0; i < weak_count_; ++i) {
f >> thresholds_[i];
}
for (int i = 0; i < weak_count_; ++i) {
f >> alphas_[i];
}
for (int i = 0; i < weak_count_; ++i) {
f >> polarities_[i];
}
for (int i = 0; i < weak_count_; ++i) {
f >> cascade_thresholds_[i];
}
for (int i = 0; i < weak_count_; ++i) {
f >> feature_indices_[i];
}
}
void reset(int weak_count)
{
weak_count_ = weak_count;
thresholds_.clear();
alphas_.clear();
feature_indices_.clear();
polarities_.clear();
cascade_thresholds_.clear();
}
~WaldBoost()
{
}
private:
int weak_count_;
vector<float> thresholds_;
vector<float> alphas_;
vector<int> feature_indices_;
vector<int> polarities_;
vector<float> cascade_thresholds_;
};
void test_boosting();
void train_boosting();
int main(int argc, char **argv)
{
string stage = argv[1];
if (stage == "train") {
train_boosting();
} else if (stage == "detect") {
test_boosting();
}
}
vector<Mat>
read_imgs(const string& path)
{
vector<String> filenames;
glob(path, filenames);
vector<Mat> imgs;
for (size_t i = 0; i < filenames.size(); ++i) {
imgs.push_back(imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE));
}
return imgs;
}
vector<Mat>
sample_patches(const string& path, int n_rows, int n_cols, int n_patches)
{
vector<String> filenames;
glob(path, filenames);
vector<Mat> patches;
int patch_count = 0;
for (size_t i = 0; i < filenames.size(); ++i) {
Mat img = imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE);
for (int row = 0; row + n_rows < img.rows; row += n_rows) {
for (int col = 0; col + n_cols < img.cols; col += n_cols) {
patches.push_back(img(Rect(col, row, n_cols, n_rows)).clone());
patch_count += 1;
if (patch_count == n_patches) {
goto sampling_finished;
}
}
}
}
sampling_finished:
return patches;
}
void train_boosting()
{
cerr << "read imgs" << endl;
vector<Mat> pos_imgs = read_imgs("/home/vlad/gsoc/lfw_faces");
assert(pos_imgs.size());
const char neg_path[] = "/home/vlad/gsoc/rtsd_bg";
vector<Mat> neg_imgs = sample_patches(neg_path, 24, 24, pos_imgs.size());
assert(neg_imgs.size());
int n_features;
Mat pos_data, neg_data;
Ptr<CvFeatureEvaluator> eval = CvFeatureEvaluator::create(CvFeatureParams::LBP);
eval->init(CvFeatureParams::create(CvFeatureParams::LBP), 1, Size(24, 24));
n_features = eval->getNumFeatures();
const int stages[] = {32, 64, 128, 256, 512, 1024, 2048, 4096};
const int stage_count = sizeof(stages) / sizeof(*stages);
const int stage_neg = 5000;
const int max_per_image = 25;
const float scales_arr[] = {.3, .4, .5, .6, .7, .8, .9, 1};
const vector<float> scales(scales_arr,
scales_arr + sizeof(scales_arr) / sizeof(*scales_arr));
WaldBoost boost;
vector<String> neg_filenames;
glob(neg_path, neg_filenames);
for (int i = 0; i < stage_count; ++i) {
cerr << "compute features" << endl;
pos_data = Mat1b(n_features, pos_imgs.size());
neg_data = Mat1b(n_features, neg_imgs.size());
for (size_t k = 0; k < pos_imgs.size(); ++k) {
eval->setImage(pos_imgs[k], +1, 0, boost.get_feature_indices());
for (int j = 0; j < n_features; ++j) {
pos_data.at<uchar>(j, k) = (*eval)(j, 0);
}
}
for (size_t k = 0; k < neg_imgs.size(); ++k) {
eval->setImage(neg_imgs[k], 0, 0, boost.get_feature_indices());
for (int j = 0; j < n_features; ++j) {
neg_data.at<uchar>(j, k) = (*eval)(j, 0);
}
}
boost.reset(stages[i]);
boost.fit(pos_data, neg_data);
if (i + 1 == stage_count) {
break;
}
int bootstrap_count = 0;
int img_i = 0;
for (; img_i < neg_filenames.size(); ++img_i) {
cerr << "win " << bootstrap_count << "/" << stage_neg
<< " img " << (img_i + 1) << "/" << neg_filenames.size() << "\r";
Mat img = imread(neg_filenames[img_i], CV_LOAD_IMAGE_GRAYSCALE);
vector<Rect> bboxes;
Mat1f confidences;
boost.detect(eval, img, scales, bboxes, confidences);
if (confidences.rows > 0) {
Mat1i indices;
sortIdx(confidences, indices,
CV_SORT_EVERY_COLUMN + CV_SORT_DESCENDING);
int win_count = min(max_per_image, confidences.rows);
win_count = min(win_count, stage_neg - bootstrap_count);
Mat window;
for (int k = 0; k < win_count; ++k) {
resize(img(bboxes[indices(k, 0)]), window, Size(24, 24));
neg_imgs.push_back(window.clone());
bootstrap_count += 1;
}
if (bootstrap_count >= stage_neg) {
break;
}
}
}
cerr << "bootstrapped " << bootstrap_count << " windows from "
<< (img_i + 1) << " images" << endl;
}
boost.save("models/model.txt");
vector<int> feature_indices = boost.get_feature_indices();
Mat1i feature_map(1, feature_indices.size());
for (size_t i = 0; i < feature_indices.size(); ++i) {
feature_map(0, i) = feature_indices[i];
}
FileStorage fs("models/features.yaml", FileStorage::WRITE);
eval->writeFeatures(fs, feature_map);
}
void test_boosting()
{
WaldBoost boost;
const char model_filename[] = "models/model.txt";
boost.load(model_filename);
Mat test_img = imread("imgs/test4.png", CV_LOAD_IMAGE_GRAYSCALE);
vector<Rect> bboxes;
Mat1f confidences;
vector<float> scales;
for (float scale = 0.2f; scale < 1.2f; scale *= 1.2) {
scales.push_back(scale);
}
Ptr<CvFeatureParams> params = CvFeatureParams::create(CvFeatureParams::LBP);
Ptr<CvFeatureEvaluator> eval = CvFeatureEvaluator::create(CvFeatureParams::LBP);
eval->init(params, 1, Size(24, 24));
boost.detect(eval, test_img, scales, bboxes, confidences);
cerr << "detected " << bboxes.size() << " objects" << endl;
for (size_t i = 0; i < bboxes.size(); ++i) {
rectangle(test_img, bboxes[i], Scalar(255, 0, 0));
}
imwrite("imgs/out.png", test_img);
}
#ifndef _OPENCV_FEATURES_H_
#define _OPENCV_FEATURES_H_
#include <stdio.h>
#define FEATURES "features"
#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
/* (x, y) */ \
(p0) = (rect).x + (step) * (rect).y; \
/* (x + w, y) */ \
(p1) = (rect).x + (rect).width + (step) * (rect).y; \
/* (x + w, y) */ \
(p2) = (rect).x + (step) * ((rect).y + (rect).height); \
/* (x + w, y + h) */ \
(p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
#define CV_TILTED_OFFSETS( p0, p1, p2, p3, rect, step ) \
/* (x, y) */ \
(p0) = (rect).x + (step) * (rect).y; \
/* (x - h, y + h) */ \
(p1) = (rect).x - (rect).height + (step) * ((rect).y + (rect).height);\
/* (x + w, y + w) */ \
(p2) = (rect).x + (rect).width + (step) * ((rect).y + (rect).width); \
/* (x + w - h, y + w + h) */ \
(p3) = (rect).x + (rect).width - (rect).height \
+ (step) * ((rect).y + (rect).width + (rect).height);
float calcNormFactor( const cv::Mat& sum, const cv::Mat& sqSum );
template<class Feature>
void _writeFeatures( const std::vector<Feature> features, cv::FileStorage &fs, const cv::Mat& featureMap )
{
fs << FEATURES << "[";
const cv::Mat_<int>& featureMap_ = (const cv::Mat_<int>&)featureMap;
for ( int fi = 0; fi < featureMap.cols; fi++ )
if ( featureMap_(0, fi) >= 0 )
{
fs << "{";
features[fi].write( fs );
fs << "}";
}
fs << "]";
}
class CvParams
{
public:
CvParams();
virtual ~CvParams() {}
// from|to file
virtual void write( cv::FileStorage &fs ) const = 0;
virtual bool read( const cv::FileNode &node ) = 0;
// from|to screen
virtual void printDefaults() const;
virtual void printAttrs() const;
virtual bool scanAttr( const std::string prmName, const std::string val );
std::string name;
};
class CvFeatureParams : public CvParams
{
public:
enum { HAAR = 0, LBP = 1, HOG = 2 };
CvFeatureParams();
virtual void init( const CvFeatureParams& fp );
virtual void write( cv::FileStorage &fs ) const;
virtual bool read( const cv::FileNode &node );
static cv::Ptr<CvFeatureParams> create( int featureType );
int maxCatCount; // 0 in case of numerical features
int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
};
class CvFeatureEvaluator
{
public:
virtual ~CvFeatureEvaluator() {}
virtual void init(const CvFeatureParams *_featureParams,
int _maxSampleCount, cv::Size _winSize );
virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx, const std::vector<int> &feature_ind);
virtual void setWindow(const cv::Point& p) = 0;
virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const = 0;
virtual float operator()(int featureIdx, int sampleIdx) = 0;
static cv::Ptr<CvFeatureEvaluator> create(int type);
int getNumFeatures() const { return numFeatures; }
int getMaxCatCount() const { return featureParams->maxCatCount; }
int getFeatureSize() const { return featureParams->featSize; }
const cv::Mat& getCls() const { return cls; }
float getCls(int si) const { return cls.at<float>(si, 0); }
protected:
virtual void generateFeatures() = 0;
int npos, nneg;
int numFeatures;
cv::Size winSize;
CvFeatureParams *featureParams;
cv::Mat cls;
};
#endif
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