From 3f1cce24ba249b8616c08584c2293edfdc283370 Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Wed, 1 Jul 2015 08:07:30 +0300 Subject: [PATCH 1/9] Waldboost with LBP --- modules/xobjdetect/src/cascadeclassifier.h | 53 ++ modules/xobjdetect/src/features.cpp | 87 +++ modules/xobjdetect/src/lbpfeatures.cpp | 84 +++ modules/xobjdetect/src/lbpfeatures.h | 65 ++ modules/xobjdetect/src/main.cpp | 556 ++++++++++++++++++ .../xobjdetect/src/traincascade_features.h | 101 ++++ 6 files changed, 946 insertions(+) create mode 100644 modules/xobjdetect/src/cascadeclassifier.h create mode 100644 modules/xobjdetect/src/features.cpp create mode 100644 modules/xobjdetect/src/lbpfeatures.cpp create mode 100644 modules/xobjdetect/src/lbpfeatures.h create mode 100644 modules/xobjdetect/src/main.cpp create mode 100644 modules/xobjdetect/src/traincascade_features.h diff --git a/modules/xobjdetect/src/cascadeclassifier.h b/modules/xobjdetect/src/cascadeclassifier.h new file mode 100644 index 000000000..0e8af29c0 --- /dev/null +++ b/modules/xobjdetect/src/cascadeclassifier.h @@ -0,0 +1,53 @@ +#ifndef _OPENCV_CASCADECLASSIFIER_H_ +#define _OPENCV_CASCADECLASSIFIER_H_ + +#include +#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 diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/features.cpp new file mode 100644 index 000000000..37e3375c9 --- /dev/null +++ b/modules/xobjdetect/src/features.cpp @@ -0,0 +1,87 @@ +#include + +#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 valSum = sp[p0] - sp[p1] - sp[p2] + sp[p3]; + const double *sqp = sqSum.ptr(); + 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::create( int featureType ) +{ + return Ptr(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& feature_ind) +{ + //CV_Assert(img.cols == winSize.width); + //CV_Assert(img.rows == winSize.height); + CV_Assert(idx < cls.rows); + cls.ptr(idx)[0] = clsLabel; +} + +Ptr CvFeatureEvaluator::create(int type) +{ + return Ptr(new CvLBPEvaluator); +} diff --git a/modules/xobjdetect/src/lbpfeatures.cpp b/modules/xobjdetect/src/lbpfeatures.cpp new file mode 100644 index 000000000..48a23f008 --- /dev/null +++ b/modules/xobjdetect/src/lbpfeatures.cpp @@ -0,0 +1,84 @@ +#include + +#include "lbpfeatures.h" +#include "cascadeclassifier.h" + +#include + +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 &feature_ind) +{ + CV_DbgAssert( !sum.empty() ); + CvFeatureEvaluator::setImage( img, clsLabel, idx, feature_ind ); + integral( img, sum ); + cur_sum = sum; + offset_ = int(sum.ptr(1) - sum.ptr()); + 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 << "]"; +} diff --git a/modules/xobjdetect/src/lbpfeatures.h b/modules/xobjdetect/src/lbpfeatures.h new file mode 100644 index 000000000..db0719db5 --- /dev/null +++ b/modules/xobjdetect/src/lbpfeatures.h @@ -0,0 +1,65 @@ +#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 &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 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 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 diff --git a/modules/xobjdetect/src/main.cpp b/modules/xobjdetect/src/main.cpp new file mode 100644 index 000000000..e816c2657 --- /dev/null +++ b/modules/xobjdetect/src/main.cpp @@ -0,0 +1,556 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 get_feature_indices() + { + return feature_indices_; + } + + void detect(Ptr eval, + const Mat& img, const vector& scales, + vector& 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 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(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(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(min_feature_ind, col_pos) < + data_pos.at(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 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 thresholds_; + vector alphas_; + vector feature_indices_; + vector polarities_; + vector 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 +read_imgs(const string& path) +{ + vector filenames; + glob(path, filenames); + vector imgs; + for (size_t i = 0; i < filenames.size(); ++i) { + imgs.push_back(imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE)); + } + return imgs; +} +vector +sample_patches(const string& path, int n_rows, int n_cols, int n_patches) +{ + vector filenames; + glob(path, filenames); + vector 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 pos_imgs = read_imgs("/home/vlad/gsoc/lfw_faces"); + assert(pos_imgs.size()); + + const char neg_path[] = "/home/vlad/gsoc/rtsd_bg"; + vector neg_imgs = sample_patches(neg_path, 24, 24, pos_imgs.size()); + assert(neg_imgs.size()); + + int n_features; + Mat pos_data, neg_data; + + Ptr 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 scales(scales_arr, + scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); + + WaldBoost boost; + vector 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(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(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 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 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 bboxes; + Mat1f confidences; + vector scales; + for (float scale = 0.2f; scale < 1.2f; scale *= 1.2) { + scales.push_back(scale); + } + Ptr params = CvFeatureParams::create(CvFeatureParams::LBP); + Ptr 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); +} + diff --git a/modules/xobjdetect/src/traincascade_features.h b/modules/xobjdetect/src/traincascade_features.h new file mode 100644 index 000000000..59cda4081 --- /dev/null +++ b/modules/xobjdetect/src/traincascade_features.h @@ -0,0 +1,101 @@ +#ifndef _OPENCV_FEATURES_H_ +#define _OPENCV_FEATURES_H_ + +#include + +#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 +void _writeFeatures( const std::vector features, cv::FileStorage &fs, const cv::Mat& featureMap ) +{ + fs << FEATURES << "["; + const cv::Mat_& featureMap_ = (const cv::Mat_&)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 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 &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 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(si, 0); } +protected: + virtual void generateFeatures() = 0; + + int npos, nneg; + int numFeatures; + cv::Size winSize; + CvFeatureParams *featureParams; + cv::Mat cls; +}; + +#endif From d06d7e291841d92fb2c9249bc251348a89bbda9c Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Mon, 3 Aug 2015 17:14:28 +0300 Subject: [PATCH 2/9] Change of interface and multiple fixes --- modules/adas/CMakeLists.txt | 8 - modules/adas/README.md | 2 - modules/adas/include/opencv2/adas.hpp | 44 -- modules/adas/src/adas.cpp | 0 modules/adas/tools/CMakeLists.txt | 2 - modules/adas/tools/fcw_detect/CMakeLists.txt | 35 - modules/adas/tools/fcw_detect/fcw_detect.cpp | 132 ---- modules/adas/tools/fcw_train/fcw_train.cpp | 187 ------ modules/xobjdetect/CMakeLists.txt | 3 +- .../xobjdetect/include/opencv2/xobjdetect.hpp | 196 +----- .../include/opencv2/xobjdetect/private.hpp | 88 --- modules/xobjdetect/src/acffeature.cpp | 344 ---------- modules/xobjdetect/src/cascadeclassifier.h | 44 ++ modules/xobjdetect/src/detector.cpp | 215 ++++++ modules/xobjdetect/src/features.cpp | 47 +- modules/xobjdetect/src/icfdetector.cpp | 316 --------- modules/xobjdetect/src/lbpfeatures.cpp | 48 +- modules/xobjdetect/src/lbpfeatures.h | 44 ++ modules/xobjdetect/src/main.cpp | 556 ---------------- modules/xobjdetect/src/precomp.hpp | 39 +- modules/xobjdetect/src/stump.cpp | 236 ------- .../xobjdetect/src/traincascade_features.h | 44 ++ modules/xobjdetect/src/waldboost.cpp | 615 +++++++++--------- modules/xobjdetect/src/waldboost.hpp | 88 +++ modules/xobjdetect/tools/CMakeLists.txt | 1 + .../tools/waldboost_detector}/CMakeLists.txt | 2 +- .../waldboost_detector/waldboost_detector.cpp | 33 + 27 files changed, 913 insertions(+), 2456 deletions(-) delete mode 100644 modules/adas/CMakeLists.txt delete mode 100644 modules/adas/README.md delete mode 100644 modules/adas/include/opencv2/adas.hpp delete mode 100644 modules/adas/src/adas.cpp delete mode 100644 modules/adas/tools/CMakeLists.txt delete mode 100644 modules/adas/tools/fcw_detect/CMakeLists.txt delete mode 100644 modules/adas/tools/fcw_detect/fcw_detect.cpp delete mode 100644 modules/adas/tools/fcw_train/fcw_train.cpp delete mode 100644 modules/xobjdetect/include/opencv2/xobjdetect/private.hpp delete mode 100644 modules/xobjdetect/src/acffeature.cpp create mode 100644 modules/xobjdetect/src/detector.cpp delete mode 100644 modules/xobjdetect/src/icfdetector.cpp delete mode 100644 modules/xobjdetect/src/main.cpp delete mode 100644 modules/xobjdetect/src/stump.cpp create mode 100644 modules/xobjdetect/src/waldboost.hpp create mode 100644 modules/xobjdetect/tools/CMakeLists.txt rename modules/{adas/tools/fcw_train => xobjdetect/tools/waldboost_detector}/CMakeLists.txt (97%) create mode 100644 modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp diff --git a/modules/adas/CMakeLists.txt b/modules/adas/CMakeLists.txt deleted file mode 100644 index fb122143a..000000000 --- a/modules/adas/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -if(IOS) - ocv_module_disable(adas) -endif() - -set(the_description "Automatic driver assistance algorithms") -ocv_define_module(adas opencv_xobjdetect) - -add_subdirectory(tools) diff --git a/modules/adas/README.md b/modules/adas/README.md deleted file mode 100644 index 7ccfb3a0f..000000000 --- a/modules/adas/README.md +++ /dev/null @@ -1,2 +0,0 @@ -ADAS: Advanced Driver Assistance Systems module with Forward Collision Warning -============================================================================== \ No newline at end of file diff --git a/modules/adas/include/opencv2/adas.hpp b/modules/adas/include/opencv2/adas.hpp deleted file mode 100644 index d41ca0054..000000000 --- a/modules/adas/include/opencv2/adas.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - -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 - (3-clause BSD License) - -Copyright (C) 2013, OpenCV Foundation, 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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -/** @defgroup adas Advanced Driver Assistance -*/ - diff --git a/modules/adas/src/adas.cpp b/modules/adas/src/adas.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/modules/adas/tools/CMakeLists.txt b/modules/adas/tools/CMakeLists.txt deleted file mode 100644 index 3cf5d7763..000000000 --- a/modules/adas/tools/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(fcw_train) -add_subdirectory(fcw_detect) diff --git a/modules/adas/tools/fcw_detect/CMakeLists.txt b/modules/adas/tools/fcw_detect/CMakeLists.txt deleted file mode 100644 index d2f2a2e11..000000000 --- a/modules/adas/tools/fcw_detect/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(name fcw_detect) -set(the_target opencv_${name}) - -set(OPENCV_${the_target}_DEPS opencv_core opencv_imgcodecs opencv_videoio - opencv_highgui opencv_xobjdetect) - -ocv_check_dependencies(${OPENCV_${the_target}_DEPS}) - -if(NOT OCV_DEPENDENCIES_FOUND) - return() -endif() - -project(${the_target}) - -ocv_include_directories("${OpenCV_SOURCE_DIR}/include/opencv") -ocv_include_modules_recurse(${OPENCV_${the_target}_DEPS}) - -file(GLOB ${the_target}_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - -add_executable(${the_target} ${${the_target}_SOURCES}) - -target_link_libraries(${the_target} ${OPENCV_${the_target}_DEPS}) - -set_target_properties(${the_target} PROPERTIES - DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" - ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} - RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} - INSTALL_NAME_DIR lib - OUTPUT_NAME ${the_target}) - -if(ENABLE_SOLUTION_FOLDERS) - set_target_properties(${the_target} PROPERTIES FOLDER "applications") -endif() - -install(TARGETS ${the_target} OPTIONAL RUNTIME DESTINATION bin COMPONENT main) diff --git a/modules/adas/tools/fcw_detect/fcw_detect.cpp b/modules/adas/tools/fcw_detect/fcw_detect.cpp deleted file mode 100644 index 318393a55..000000000 --- a/modules/adas/tools/fcw_detect/fcw_detect.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -using std::string; - -#include -using std::vector; - -#include -using std::cerr; -using std::endl; - -#include -using cv::Rect; -using cv::Size; -using cv::Mat; -using cv::Mat_; -using cv::Vec3b; - -#include -using cv::imread; -using cv::imwrite; - -#include -using cv::CommandLineParser; -using cv::FileStorage; - -#include -using cv::xobjdetect::ICFDetector; - -static Mat visualize(const Mat &image, const vector &objects) -{ - CV_Assert(image.type() == CV_8UC3); - Mat_ img = image.clone(); - for( size_t j = 0; j < objects.size(); ++j ) - { - Rect obj = objects[j]; - int x = obj.x; - int y = obj.y; - int width = obj.width; - int height = obj.height; - for( int i = y; i <= y + height; ++i ) { - img(i, x) = Vec3b(255, 0, 0); - img(i, x + width) = Vec3b(255, 0, 0); - } - for( int i = x; i <= x + width; ++i) { - img(y, i) = Vec3b(255, 0, 0); - img(y + height, i) = Vec3b(255, 0, 0); - } - } - return img; -} -static bool read_window_size(const char *str, int *rows, int *cols) -{ - int pos = 0; - if( sscanf(str, "%dx%d%n", rows, cols, &pos) != 2 || str[pos] != '\0' || - *rows <= 0 || *cols <= 0) - { - return false; - } - return true; -} - -int main(int argc, char *argv[]) -{ - const string keys = - "{help | | print this message}" - "{model_filename | model.xml | filename for reading model}" - "{image_path | test.png | path to image for detection}" - "{out_image_path | out.png | path to image for output}" - "{threshold | 0.0 | threshold for cascade}" - "{step | 8 | sliding window step}" - "{min_window_size | 40x40 | min window size in pixels}" - "{max_window_size | 300x300 | max window size in pixels}" - "{is_grayscale | false | read the image as grayscale}" - ; - - CommandLineParser parser(argc, argv, keys); - parser.about("FCW detection"); - - if( parser.has("help") || argc == 1) - { - parser.printMessage(); - return 0; - } - - string model_filename = parser.get("model_filename"); - string image_path = parser.get("image_path"); - string out_image_path = parser.get("out_image_path"); - bool is_grayscale = parser.get("is_grayscale"); - float threshold = parser.get("threshold"); - int step = parser.get("step"); - - int min_rows, min_cols, max_rows, max_cols; - string min_window_size = parser.get("min_window_size"); - if( !read_window_size(min_window_size.c_str(), &min_rows, - &min_cols) ) - { - cerr << "Error reading min window size from `" << min_window_size << "`" << endl; - return 1; - } - string max_window_size = parser.get("max_window_size"); - if( !read_window_size(max_window_size.c_str(), &max_rows, - &max_cols) ) - { - cerr << "Error reading max window size from `" << max_window_size << "`" << endl; - return 1; - } - - int color; - if(is_grayscale == false) - color = cv::IMREAD_COLOR; - else - color = cv::IMREAD_GRAYSCALE; - - - if( !parser.check() ) - { - parser.printErrors(); - return 1; - } - - ICFDetector detector; - FileStorage fs(model_filename, FileStorage::READ); - detector.read(fs["icfdetector"]); - fs.release(); - vector objects; - Mat img = imread(image_path, color); - std::vector values; - detector.detect(img, objects, 1.1f, Size(min_cols, min_rows), Size(max_cols, max_rows), threshold, step, values); - imwrite(out_image_path, visualize(img, objects)); - - -} diff --git a/modules/adas/tools/fcw_train/fcw_train.cpp b/modules/adas/tools/fcw_train/fcw_train.cpp deleted file mode 100644 index a7f0e81d0..000000000 --- a/modules/adas/tools/fcw_train/fcw_train.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include - -#include -using std::string; - -#include -using std::vector; - -#include -using std::ifstream; -using std::getline; - -#include -using std::stringstream; - -#include -using std::cerr; -using std::endl; - -#include -using cv::Rect; -using cv::Size; -#include -using cv::imread; -#include -using cv::CommandLineParser; -using cv::FileStorage; -#include - -#include // std::time -#include // std::rand, std::srand - -#include - - -using cv::xobjdetect::ICFDetectorParams; -using cv::xobjdetect::ICFDetector; -using cv::xobjdetect::WaldBoost; -using cv::xobjdetect::WaldBoostParams; -using cv::Mat; - -static bool read_model_size(const char *str, int *rows, int *cols) -{ - int pos = 0; - if( sscanf(str, "%dx%d%n", rows, cols, &pos) != 2 || str[pos] != '\0' || - *rows <= 0 || *cols <= 0) - { - return false; - } - return true; -} - -static int randomPred (int i) { return std::rand()%i;} - -int main(int argc, char *argv[]) -{ - - const string keys = - "{help | | print this message}" - "{pos_path | pos | path to training object samples}" - "{bg_path | bg | path to background images}" - "{bg_per_image | 5 | number of windows to sample per bg image}" - "{feature_count | 10000 | number of features to generate}" - "{weak_count | 100 | number of weak classifiers in cascade}" - "{model_size | 40x40 | model size in pixels}" - "{model_filename | model.xml | filename for saving model}" - "{features_type | icf | features type, \"icf\" or \"acf\"}" - "{alpha | 0.02 | alpha value}" - "{is_grayscale | false | read the image as grayscale}" - "{use_fast_log | false | use fast log function}" - "{limit_ps | -1 | limit to positive samples (-1 means all)}" - "{limit_bg | -1 | limit to negative samples (-1 means all)}" - ; - - - CommandLineParser parser(argc, argv, keys); - parser.about("FCW trainer"); - - if( parser.has("help") || argc == 1) - { - parser.printMessage(); - return 0; - } - - string pos_path = parser.get("pos_path"); - string bg_path = parser.get("bg_path"); - string model_filename = parser.get("model_filename"); - - ICFDetectorParams params; - params.feature_count = parser.get("feature_count"); - params.weak_count = parser.get("weak_count"); - params.bg_per_image = parser.get("bg_per_image"); - params.features_type = parser.get("features_type"); - params.alpha = parser.get("alpha"); - params.is_grayscale = parser.get("is_grayscale"); - params.use_fast_log = parser.get("use_fast_log"); - - int limit_ps = parser.get("limit_ps"); - int limit_bg = parser.get("limit_bg"); - - string model_size = parser.get("model_size"); - if( !read_model_size(model_size.c_str(), ¶ms.model_n_rows, - ¶ms.model_n_cols) ) - { - cerr << "Error reading model size from `" << model_size << "`" << endl; - return 1; - } - - if( params.feature_count <= 0 ) - { - cerr << "feature_count must be positive number" << endl; - return 1; - } - - if( params.weak_count <= 0 ) - { - cerr << "weak_count must be positive number" << endl; - return 1; - } - - if( params.features_type != "icf" && params.features_type != "acf" ) - { - cerr << "features_type must be \"icf\" or \"acf\"" << endl; - return 1; - } - if( params.alpha <= 0 ) - { - cerr << "alpha must be positive float number" << endl; - return 1; - } - if( !parser.check() ) - { - parser.printErrors(); - return 1; - } - - std::vector pos_filenames; - glob(pos_path, pos_filenames); - - std::vector bg_filenames; - glob(bg_path, bg_filenames); - - if(limit_ps != -1 && (int)pos_filenames.size() > limit_ps) - pos_filenames.erase(pos_filenames.begin()+limit_ps, pos_filenames.end()); - if(limit_bg != -1 && (int)bg_filenames.size() > limit_bg) - bg_filenames.erase(bg_filenames.begin()+limit_bg, bg_filenames.end()); - - //random pick input images - bool random_shuffle = false; - if(random_shuffle) - { - std::srand ( unsigned ( std::time(0) ) ); - std::random_shuffle ( pos_filenames.begin(), pos_filenames.end(), randomPred ); - std::random_shuffle ( bg_filenames.begin(), bg_filenames.end(), randomPred ); - } - - int samples_size = (int)((params.bg_per_image * bg_filenames.size()) + pos_filenames.size()); - int features_size = params.feature_count; - int max_features_allowed = (int)(INT_MAX/(sizeof(int)* samples_size)); - int max_samples_allowed = (int)(INT_MAX/(sizeof(int)* features_size)); - int total_samples = (int)((params.bg_per_image * bg_filenames.size()) + pos_filenames.size()); - - - if(total_samples >max_samples_allowed) - { - CV_Error_(1, ("exceeded maximum number of samples. Maximum number of samples with %d features is %d, you have %d (%d positive samples + (%d bg * %d bg_per_image))\n",features_size,max_samples_allowed,total_samples,pos_filenames.size(),bg_filenames.size(),params.bg_per_image )); - } - - if(params.feature_count >max_features_allowed) - { - CV_Error_(1, ("exceeded maximum number of features. Maximum number of features with %d samples is %d, you have %d\n",samples_size,max_features_allowed, features_size )); - } - - std::cout<& channels); - -/** @brief Feature evaluation interface - */ -class CV_EXPORTS FeatureEvaluator : public Algorithm -{ -public: - /** @brief Set channels for feature evaluation - - @param channels array of channels to be set - */ - virtual void setChannels(InputArrayOfArrays channels) = 0; - - /** @brief Set window position to sample features with shift. By default position is (0, 0). - - @param position position to be set - */ - virtual void setPosition(Size position) = 0; - - /** @brief Evaluate feature value with given index for current channels and window position. - - @param feature_ind index of feature to be evaluated - */ - virtual int evaluate(size_t feature_ind) const = 0; - - /** @brief Evaluate all features for current channels and window position. - - @param feature_values matrix-column of evaluated feature values - */ - virtual void evaluateAll(OutputArray feature_values) const = 0; - - virtual void assertChannels() = 0; -}; - -/** @brief Construct feature evaluator. - -@param features features for evaluation -@param type feature type. Can be "icf" or "acf" - */ -CV_EXPORTS Ptr -createFeatureEvaluator(const std::vector >& features, - const std::string& type); - -/** @brief Generate integral features. Returns vector of features. - -@param window_size size of window in which features should be evaluated -@param type feature type. Can be "icf" or "acf" -@param count number of features to generate. -@param channel_count number of feature channels - */ -std::vector > -generateFeatures(Size window_size, const std::string& type, - int count = INT_MAX, int channel_count = 10); - -//sort in-place of columns of the input matrix -void sort_columns_without_copy(Mat& m, Mat indices = Mat()); - -/** @brief Parameters for WaldBoost. weak_count — number of weak learners, alpha — cascade thresholding param. - */ -struct CV_EXPORTS WaldBoostParams -{ - int weak_count; - float alpha; - - WaldBoostParams(): weak_count(100), alpha(0.02f) - {} -}; - -/** @brief WaldBoost object detector from @cite Sochman05 . -*/ -class CV_EXPORTS WaldBoost : public Algorithm -{ +class CV_EXPORTS WBDetector { public: - /** @brief Train WaldBoost cascade for given data. - - Returns feature indices chosen for cascade. Feature enumeration starts from 0. - @param data matrix of feature values, size M x N, one feature per row - @param labels matrix of samples class labels, size 1 x N. Labels can be from {-1, +1} - @param use_fast_log - */ - virtual std::vector train(Mat& data, - const Mat& labels, bool use_fast_log=false) = 0; - - /** @brief Predict objects class given object that can compute object features. - - Returns unnormed confidence value — measure of confidence that object is from class +1. - @param feature_evaluator object that can compute features by demand - */ - virtual float predict( - const Ptr& feature_evaluator) const = 0; -}; + WBDetector(const std::string& model_filename); -/** @brief Construct WaldBoost object. - */ -CV_EXPORTS Ptr -createWaldBoost(const WaldBoostParams& params = WaldBoostParams()); + void train( + const std::string& pos_samples, + const std::string& neg_imgs); -/** @brief Params for ICFDetector training. - */ -struct CV_EXPORTS ICFDetectorParams -{ - int feature_count; - int weak_count; - int model_n_rows; - int model_n_cols; - int bg_per_image; - std::string features_type; - float alpha; - bool is_grayscale; - bool use_fast_log; - - ICFDetectorParams(): feature_count(UINT_MAX), weak_count(100), - model_n_rows(56), model_n_cols(56), bg_per_image(5), alpha(0.02f), is_grayscale(false), use_fast_log(false) - {} -}; - -/** @brief Integral Channel Features from @cite Dollar09 . -*/ -class CV_EXPORTS ICFDetector -{ -public: - - ICFDetector(): waldboost_(), features_(), ftype_() {} - - /** @brief Train detector. - - @param pos_filenames path to folder with images of objects (wildcards like /my/path/\*.png are allowed) - @param bg_filenames path to folder with background images - @param params parameters for detector training - */ - void train(const std::vector& pos_filenames, - const std::vector& bg_filenames, - ICFDetectorParams params = ICFDetectorParams()); - - /** @brief Detect objects on image. - @param image image for detection - @param objects output array of bounding boxes - @param scaleFactor scale between layers in detection pyramid - @param minSize min size of objects in pixels - @param maxSize max size of objects in pixels - @param threshold - @param slidingStep sliding window step - @param values output vector with values of positive samples - */ - void detect(const Mat& image, std::vector& objects, - float scaleFactor, Size minSize, Size maxSize, float threshold, int slidingStep, std::vector& values); - - /** @brief Detect objects on image. - @param img image for detection - @param objects output array of bounding boxes - @param minScaleFactor min factor by which the image will be resized - @param maxScaleFactor max factor by which the image will be resized - @param factorStep scaling factor is incremented each pyramid layer according to this parameter - @param threshold - @param slidingStep sliding window step - @param values output vector with values of positive samples - */ - void detect(const Mat& img, std::vector& objects, float minScaleFactor, float maxScaleFactor, float factorStep, float threshold, int slidingStep, std::vector& values); - - /** @brief Write detector to FileStorage. - @param fs FileStorage for output - */ - void write(FileStorage &fs) const; - - /** @brief Write ICFDetector to FileNode - @param node FileNode for reading - */ - void read(const FileNode &node); + void detect( + const Mat& img, + std::vector &bboxes, + std::vector &confidences); private: - Ptr waldboost_; - std::vector > features_; - int model_n_rows_; - int model_n_cols_; - std::string ftype_; + std::string model_filename_; }; -CV_EXPORTS void write(FileStorage& fs, String&, const ICFDetector& detector); - -CV_EXPORTS void read(const FileNode& node, ICFDetector& d, - const ICFDetector& default_value = ICFDetector()); - -//! @} - } /* namespace xobjdetect */ } /* namespace cv */ diff --git a/modules/xobjdetect/include/opencv2/xobjdetect/private.hpp b/modules/xobjdetect/include/opencv2/xobjdetect/private.hpp deleted file mode 100644 index 3873413db..000000000 --- a/modules/xobjdetect/include/opencv2/xobjdetect/private.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef __OPENCV_XOBJDETECT_PRIVATE_HPP__ -#define __OPENCV_XOBJDETECT_PRIVATE_HPP__ - -#ifndef __OPENCV_BUILD -# error this is a private header, do not include it outside OpenCV -#endif - -#include - -namespace cv -{ -namespace xobjdetect -{ - -class CV_EXPORTS Stump -{ -public: - - /* Initialize zero stump */ - Stump(): threshold_(0), polarity_(1), pos_value_(1), neg_value_(-1) {} - - /* Initialize stump with given threshold, polarity - and classification values */ - Stump(int threshold, int polarity, float pos_value, float neg_value): - threshold_(threshold), polarity_(polarity), - pos_value_(pos_value), neg_value_(neg_value) {} - - /* Train stump for given data - - data — matrix of feature values, size M x N, one feature per row - - labels — matrix of sample class labels, size 1 x N. Labels can be from - {-1, +1} - - weights — matrix of sample weights, size 1 x N - - visited_features: vector of already visited features (ignored in successive calls) - - Returns chosen feature index. Feature enumeration starts from 0 - */ - int train(const Mat& data, const Mat& labels, const Mat& weights, const std::vector& visited_features, bool use_fast_log = false); - - /* Predict object class given - - value — feature value. Feature must be the same as was chosen - during training stump - - Returns real value, sign(value) means class - */ - float predict(int value) const; - - /* Write stump in FileStorage */ - void write(FileStorage& fs) const - { - fs << "{" - << "threshold" << threshold_ - << "polarity" << polarity_ - << "pos_value" << pos_value_ - << "neg_value" << neg_value_ - << "}"; - } - - /* Read stump */ - void read(const FileNode& node) - { - threshold_ = (int)node["threshold"]; - polarity_ = (int)node["polarity"]; - pos_value_ = (float)node["pos_value"]; - neg_value_ = (float)node["neg_value"]; - } - -private: - /* Stump decision threshold */ - int threshold_; - /* Stump polarity, can be from {-1, +1} */ - int polarity_; - /* Classification values for positive and negative classes */ - float pos_value_, neg_value_; -}; - -void read(const FileNode& node, Stump& s, const Stump& default_value=Stump()); - -void write(FileStorage& fs, String&, const Stump& s); - -} /* namespace xobjdetect */ -} /* namespace cv */ - -#endif // __OPENCV_XOBJDETECT_PRIVATE_HPP__ diff --git a/modules/xobjdetect/src/acffeature.cpp b/modules/xobjdetect/src/acffeature.cpp deleted file mode 100644 index aac2601ef..000000000 --- a/modules/xobjdetect/src/acffeature.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - -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 - (3-clause BSD License) - -Copyright (C) 2013, OpenCV Foundation, 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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -#include "precomp.hpp" - -using std::vector; - -using std::min; - -#include -using std::cout; -using std::endl; - -namespace cv -{ -namespace xobjdetect -{ - -static bool isNull(const Mat_ &m) -{ - bool null_data = true; - for( int row = 0; row < m.rows; ++row ) - { - for( int col = 0; col < m.cols; ++col ) - if( m.at(row, col) ) - null_data = false; - } - return null_data; -} - -class FeatureEvaluatorImpl : public FeatureEvaluator -{ -public: - FeatureEvaluatorImpl(const vector >& features): - features_(features), channels_(), position_() - { - CV_Assert(features.size() > 0); - } - - virtual void assertChannels() - { - bool null_data = true; - for( size_t i = 0; i < channels_.size(); ++i ) - null_data &= isNull(channels_[i]); - CV_Assert(!null_data); - } - - virtual void evaluateAll(OutputArray feature_values) const - { - Mat_ feature_vals(1, (int)features_.size()); - for( int i = 0; i < (int)features_.size(); ++i ) - { - feature_vals(0, i) = evaluate(i); - } - feature_values.assign(feature_vals); - } - -protected: - /* Features to evaluate */ - vector > features_; - /* Channels for feature evaluation */ - std::vector channels_; - /* Channels window position */ - Size position_; -}; - -class ICFFeatureEvaluatorImpl : public FeatureEvaluatorImpl -{ -public: - ICFFeatureEvaluatorImpl(const vector >& features): - FeatureEvaluatorImpl(features) - { - } - - virtual void setChannels(InputArrayOfArrays channels); - virtual void setPosition(Size position); - virtual int evaluate(size_t feature_ind) const; -}; - -void ICFFeatureEvaluatorImpl::setChannels(InputArrayOfArrays channels) -{ - channels_.clear(); - vector ch; - channels.getMatVector(ch); - - for( size_t i = 0; i < ch.size(); ++i ) - { - const Mat &channel = ch[i]; - Mat integral_channel; - integral(channel, integral_channel, CV_32F); - integral_channel.convertTo(integral_channel, CV_32S); - channels_.push_back(integral_channel.clone()); - } -} - -void ICFFeatureEvaluatorImpl::setPosition(Size position) -{ - position_ = position; -} - - -int ICFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const -{ - /* - - //following return is equal to this commented code, left here for readability. The new code runs much faster. - * - const vector& feature = features_[feature_ind]; - int x = feature[0] + position_.height; - int y = feature[1] + position_.width; - int x_to = feature[2] + position_.height; - int y_to = feature[3] + position_.width; - int n = feature[4]; - const Mat_& ch = channels_[n]; - return ch(y_to + 1, x_to + 1) - ch(y, x_to + 1) - ch(y_to + 1, x) + ch(y, x); - */ - - CV_Assert(feature_ind < features_.size()); - - return *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][3] + position_.width+1))+ features_[feature_ind][2] + position_.height + 1)) - - *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][1] + position_.width))+ features_[feature_ind][2] + position_.height + 1)) - - *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][3] + position_.width+1))+ features_[feature_ind][0] + position_.height)) + - *(channels_[features_[feature_ind][4]].ptr()+((channels_[features_[feature_ind][4]].cols*(features_[feature_ind][1] + position_.width))+ features_[feature_ind][0] + position_.height)); -} - -class ACFFeatureEvaluatorImpl : public FeatureEvaluatorImpl -{ -public: - ACFFeatureEvaluatorImpl(const vector >& features): - FeatureEvaluatorImpl(features) - { - } - - virtual void setChannels(InputArrayOfArrays channels); - virtual void setPosition(Size position); - virtual int evaluate(size_t feature_ind) const; -}; - -void ACFFeatureEvaluatorImpl::setChannels(InputArrayOfArrays channels) -{ - channels_.clear(); - vector ch; - channels.getMatVector(ch); - - for( size_t i = 0; i < ch.size(); ++i ) - { - const Mat &channel = ch[i]; - Mat_ acf_channel = Mat_::zeros(channel.rows / 4, channel.cols / 4); - for( int row = 0; row < channel.rows; row += 4 ) - { - for( int col = 0; col < channel.cols; col += 4 ) - { - int sum = 0; - for( int cell_row = row; cell_row < row + 4; ++cell_row ) - for( int cell_col = col; cell_col < col + 4; ++cell_col ) - sum += (int)channel.at(cell_row, cell_col); - - acf_channel(row / 4, col / 4) = sum; - } - } - - channels_.push_back(acf_channel.clone()); - } -} - -void ACFFeatureEvaluatorImpl::setPosition(Size position) -{ - position_ = Size(position.width / 4, position.height / 4); -} - -int ACFFeatureEvaluatorImpl::evaluate(size_t feature_ind) const -{ - CV_Assert(feature_ind < features_.size()); - - const vector& feature = features_[feature_ind]; - int x = feature[0]; - int y = feature[1]; - int n = feature[2]; - return channels_[n].at(y + position_.width, x + position_.height); -} - -Ptr createFeatureEvaluator( - const vector >& features, const std::string& type) -{ - FeatureEvaluator *evaluator = NULL; - if( type == "acf" ) - evaluator = new ACFFeatureEvaluatorImpl(features); - else if( type == "icf" ) - evaluator = new ICFFeatureEvaluatorImpl(features); - else - CV_Error(CV_StsBadArg, "type value is either acf or icf"); - - return Ptr(evaluator); -} - -vector > generateFeatures(Size window_size, const std::string& type, - int count, int channel_count) -{ - CV_Assert(count > 0); - vector > features; - if( type == "acf" ) - { - int cur_count = 0; - int max_count = window_size.width * window_size.height / 16; - count = min(count, max_count); - for( int x = 0; x < window_size.width / 4; ++x ) - for( int y = 0; y < window_size.height / 4; ++y ) - for( int n = 0; n < channel_count; ++n ) - { - int f[] = {x, y, n}; - vector feature(f, f + sizeof(f) / sizeof(*f)); - features.push_back(feature); - if( (cur_count += 1) == count ) - break; - } - } - else if( type == "icf" ) - { - RNG rng; - for( int i = 0; i < count; ++i ) - { - int x = rng.uniform(0, window_size.width - 1); - int y = rng.uniform(0, window_size.height - 1); - int x_to = rng.uniform(x, window_size.width - 1); - int y_to = rng.uniform(y, window_size.height - 1); - int n = rng.uniform(0, channel_count - 1); - int f[] = {x, y, x_to, y_to, n}; - vector feature(f, f + sizeof(f) / sizeof(*f)); - features.push_back(feature); - } - } - else - CV_Error(CV_StsBadArg, "type value is either acf or icf"); - - return features; -} - -void computeChannels(InputArray image, vector& channels) -{ - Mat_ grad; - Mat_ angles; - Mat luv, gray, src; - - if(image.getMat().channels() > 1) - { - src = Mat(image.getMat().rows, image.getMat().cols, CV_32FC3); - image.getMat().convertTo(src, CV_32FC3, 1./255); - - cvtColor(src, gray, CV_RGB2GRAY); - cvtColor(src, luv, CV_RGB2Luv); - } - else - { - src = Mat(image.getMat().rows, image.getMat().cols, CV_32FC1); - image.getMat().convertTo(src, CV_32FC1, 1./255); - src.copyTo(gray); - } - - Mat_ row_der, col_der; - Sobel(gray, row_der, CV_32F, 0, 1); - Sobel(gray, col_der, CV_32F, 1, 0); - - cartToPolar(col_der, row_der, grad, angles, true); - //magnitude(row_der, col_der, grad); - - Mat_ hist = Mat_::zeros(grad.rows, grad.cols); - //const float to_deg = 180 / 3.1415926f; - for (int row = 0; row < grad.rows; ++row) { - for (int col = 0; col < grad.cols; ++col) { - //float angle = atan2(row_der(row, col), col_der(row, col)) * to_deg; - float angle = angles(row, col); - if (angle > 180) - angle -= 180; - if (angle < 0) - angle += 180; - int ind = (int)(angle / 30); - - // If angle == 180, prevent index overflow - if (ind == 6) - ind = 5; - - hist(row, col)[ind] = grad(row, col) * 255; - } - } - - channels.clear(); - - if(image.getMat().channels() > 1) - { - Mat luv_channels[3]; - split(luv, luv_channels); - for( int i = 0; i < 3; ++i ) - channels.push_back(luv_channels[i]); - } - - channels.push_back(grad); - - vector hist_channels; - split(hist, hist_channels); - - for( size_t i = 0; i < hist_channels.size(); ++i ) - channels.push_back(hist_channels[i]); -} - -} /* namespace xobjdetect */ -} /* namespace cv */ diff --git a/modules/xobjdetect/src/cascadeclassifier.h b/modules/xobjdetect/src/cascadeclassifier.h index 0e8af29c0..25f836478 100644 --- a/modules/xobjdetect/src/cascadeclassifier.h +++ b/modules/xobjdetect/src/cascadeclassifier.h @@ -1,3 +1,47 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + #ifndef _OPENCV_CASCADECLASSIFIER_H_ #define _OPENCV_CASCADECLASSIFIER_H_ diff --git a/modules/xobjdetect/src/detector.cpp b/modules/xobjdetect/src/detector.cpp new file mode 100644 index 000000000..e755c6dc3 --- /dev/null +++ b/modules/xobjdetect/src/detector.cpp @@ -0,0 +1,215 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#include "precomp.hpp" + +using std::cerr; +using std::endl; +using std::vector; +using std::string; + +namespace cv { +namespace xobjdetect { + +WBDetector::WBDetector(const string& model_filename): + model_filename_(model_filename) +{} + +static vector sample_patches( + const string& path, + int n_rows, + int n_cols, + int n_patches) +{ + vector filenames; + glob(path, filenames); + vector 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; +} + +static vector read_imgs(const string& path) +{ + vector filenames; + glob(path, filenames); + vector imgs; + for (size_t i = 0; i < filenames.size(); ++i) { + imgs.push_back(imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE)); + } + return imgs; +} + +void WBDetector::train( + const string& pos_samples_path, + const string& neg_imgs_path) +{ + + vector pos_imgs = read_imgs(pos_samples_path); + vector neg_imgs = sample_patches(neg_imgs_path, 24, 24, pos_imgs.size()); + + assert(pos_imgs.size()); + assert(neg_imgs.size()); + + int n_features; + Mat pos_data, neg_data; + + Ptr 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 scales(scales_arr, + scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); + + WaldBoost boost; + vector neg_filenames; + glob(neg_imgs_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(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(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; + size_t 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 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(model_filename_); +} + +void WBDetector::detect( + const Mat& img, + vector &bboxes, + vector &confidences) +{ + WaldBoost boost; + boost.load(model_filename_); + + Mat test_img = img.clone(); + bboxes.clear(); + confidences.clear(); + vector scales; + for (float scale = 0.2f; scale < 1.2f; scale *= 1.1) { + scales.push_back(scale); + } + Ptr params = CvFeatureParams::create(CvFeatureParams::LBP); + Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); + eval->init(params, 1, Size(24, 24)); + boost.detect(eval, img, scales, bboxes, confidences); + assert(confidences.size() == bboxes.size()); +} + +} +} diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/features.cpp index 37e3375c9..38ba51a7e 100644 --- a/modules/xobjdetect/src/features.cpp +++ b/modules/xobjdetect/src/features.cpp @@ -1,7 +1,48 @@ -#include +/* +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. -#include "traincascade_features.h" -#include "cascadeclassifier.h" + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#include "precomp.hpp" using namespace std; using namespace cv; diff --git a/modules/xobjdetect/src/icfdetector.cpp b/modules/xobjdetect/src/icfdetector.cpp deleted file mode 100644 index aa7829233..000000000 --- a/modules/xobjdetect/src/icfdetector.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* - -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 - (3-clause BSD License) - -Copyright (C) 2013, OpenCV Foundation, 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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -#include -using std::ostringstream; - -#include -using std::setfill; -using std::setw; - -#include -using std::cout; -using std::endl; - -#include "precomp.hpp" - -using std::vector; -using std::string; - -using std::min; -using std::max; - - -namespace cv -{ - -namespace xobjdetect -{ - - -void ICFDetector::train(const vector& pos_filenames, - const vector& bg_filenames, - ICFDetectorParams params) -{ - - int color; - if(params.is_grayscale == false) - color = IMREAD_COLOR; - else - color = IMREAD_GRAYSCALE; - - model_n_rows_ = params.model_n_rows; - model_n_cols_ = params.model_n_cols; - ftype_ = params.features_type; - - Size model_size(params.model_n_cols, params.model_n_rows); - - vector samples; /* positive samples + negative samples */ - Mat sample, resized_sample; - int pos_count = 0; - - for( size_t i = 0; i < pos_filenames.size(); ++i, ++pos_count ) - { - cout << setw(6) << (i + 1) << "/" << pos_filenames.size() << "\r"; - Mat img = imread(pos_filenames[i], color); - resize(img, resized_sample, model_size); - samples.push_back(resized_sample.clone()); - } - cout << "\n"; - - int neg_count = 0; - RNG rng; - for( size_t i = 0; i < bg_filenames.size(); ++i ) - { - cout << setw(6) << (i + 1) << "/" << bg_filenames.size() << "\r"; - Mat img = imread(bg_filenames[i], color); - for( int j = 0; j < params.bg_per_image; ++j, ++neg_count) - { - Rect r; - r.x = rng.uniform(0, img.cols-model_size.width); - r.width = model_size.width; - r.y = rng.uniform(0, img.rows-model_size.height); - r.height = model_size.height; - sample = img.colRange(r.x, r.x + r.width).rowRange(r.y, r.y + r.height); - samples.push_back(sample.clone()); - } - } - cout << "\n"; - - Mat_ labels(1, pos_count + neg_count); - for( int i = 0; i < pos_count; ++i) - labels(0, i) = 1; - for( int i = pos_count; i < pos_count + neg_count; ++i ) - labels(0, i) = -1; - - - vector > features; - if(params.is_grayscale == false) - features = generateFeatures(model_size, params.features_type, params.feature_count, 10); - else - features = generateFeatures(model_size, params.features_type, params.feature_count, 7); - - Ptr evaluator = createFeatureEvaluator(features, params.features_type); - - - Mat_ data = Mat_::zeros((int)features.size(), (int)samples.size()); - Mat_ feature_col(1, (int)samples.size()); - - vector channels; - for( int i = 0; i < (int)samples.size(); ++i ) - { - cout << setw(6) << i << "/" << samples.size() << "\r"; - computeChannels(samples[i], channels); - evaluator->setChannels(channels); - //evaluator->assertChannels(); - evaluator->evaluateAll(feature_col); - - CV_Assert(feature_col.cols == (int)features.size()); - - for( int j = 0; j < feature_col.cols; ++j ) - data(j, i) = feature_col(0, j); - } - cout << "\n"; - samples.clear(); - - WaldBoostParams wparams; - wparams.weak_count = params.weak_count; - wparams.alpha = params.alpha; - - waldboost_ = createWaldBoost(wparams); - vector indices = waldboost_->train(data, labels, params.use_fast_log); - cout << "indices: "; - for( size_t i = 0; i < indices.size(); ++i ) - cout << indices[i] << " "; - cout << endl; - - features_.clear(); - for( size_t i = 0; i < indices.size(); ++i ) - features_.push_back(features[indices[i]]); -} - -void ICFDetector::write(FileStorage& fs) const -{ - fs << "{"; - fs << "model_n_rows" << model_n_rows_; - fs << "model_n_cols" << model_n_cols_; - fs << "ftype" << String(ftype_.c_str()); - fs << "waldboost"; - waldboost_->write(fs); - fs << "features" << "["; - for( size_t i = 0; i < features_.size(); ++i ) - { - fs << features_[i]; - } - fs << "]"; - fs << "}"; -} - -void ICFDetector::read(const FileNode& node) -{ - waldboost_ = Ptr(createWaldBoost(WaldBoostParams())); - String f_temp; - node["model_n_rows"] >> model_n_rows_; - node["model_n_cols"] >> model_n_cols_; - f_temp = (String)node["ftype"]; - this->ftype_ = (string)f_temp.c_str(); - waldboost_->read(node["waldboost"]); - FileNode features = node["features"]; - features_.clear(); - vector p; - for( FileNodeIterator n = features.begin(); n != features.end(); ++n ) - { - (*n) >> p; - features_.push_back(p); - } -} - -void ICFDetector::detect(const Mat& img, vector& objects, - float scaleFactor, Size minSize, Size maxSize, float threshold, int slidingStep, std::vector& values) -{ - - - float scale_from = min(model_n_cols_ / (float)maxSize.width, - model_n_rows_ / (float)maxSize.height); - float scale_to = max(model_n_cols_ / (float)minSize.width, - model_n_rows_ / (float)minSize.height); - objects.clear(); - Ptr evaluator = createFeatureEvaluator(features_, ftype_); - Mat rescaled_image; - vector channels; - - for( float scale = scale_from; scale < scale_to + 0.001; scale *= scaleFactor ) - { - int new_width = int(img.cols * scale); - new_width -= new_width % 4; - int new_height = int(img.rows * scale); - new_height -= new_height % 4; - - resize(img, rescaled_image, Size(new_width, new_height)); - computeChannels(rescaled_image, channels); - evaluator->setChannels(channels); - for( int row = 0; row <= rescaled_image.rows - model_n_rows_; row += slidingStep) - { - for( int col = 0; col <= rescaled_image.cols - model_n_cols_; - col += slidingStep ) - { - evaluator->setPosition(Size(row, col)); - float value = waldboost_->predict(evaluator); - if( value > threshold ) - { - values.push_back(value); - int x = (int)(col / scale); - int y = (int)(row / scale); - int width = (int)(model_n_cols_ / scale); - int height = (int)(model_n_rows_ / scale); - objects.push_back(Rect(x, y, width, height)); - } - } - } - - } - -} - -void ICFDetector::detect(const Mat& img, vector& objects, - float minScaleFactor, float maxScaleFactor, float factorStep, float threshold, int slidingStep, std::vector& values) -{ - - if(factorStep <= 0) - { - CV_Error_(CV_StsBadArg, ("factorStep must be > 0")); - } - - objects.clear(); - Ptr evaluator = createFeatureEvaluator(features_, ftype_); - Mat rescaled_image; - vector channels; - - for( float scale = minScaleFactor; scale < maxScaleFactor + 0.001; scale += factorStep ) - { - if(scale < 1.0) - resize(img, rescaled_image, Size(),scale, scale, INTER_AREA); - else if (scale > 1.0) - resize(img, rescaled_image, Size(),scale, scale, INTER_CUBIC); - else //scale == 1.0 - img.copyTo(rescaled_image); - - computeChannels(rescaled_image, channels); - evaluator->setChannels(channels); - for( int row = 0; row <= rescaled_image.rows - model_n_rows_; row += slidingStep) - { - for( int col = 0; col <= rescaled_image.cols - model_n_cols_; - col += slidingStep ) - { - evaluator->setPosition(Size(row, col)); - float value = waldboost_->predict(evaluator); - if( value > threshold ) - { - values.push_back(value); - int x = (int)(col / scale); - int y = (int)(row / scale); - int width = (int)(model_n_cols_ / scale); - int height = (int)(model_n_rows_ / scale); - objects.push_back(Rect(x, y, width, height)); - } - } - } - - } - -} - -void write(FileStorage& fs, String&, const ICFDetector& detector) -{ - detector.write(fs); -} - -void read(const FileNode& node, ICFDetector& d, - const ICFDetector& default_value) -{ - if( node.empty() ) - d = default_value; - else - d.read(node); -} - -} /* namespace xobjdetect */ -} /* namespace cv */ diff --git a/modules/xobjdetect/src/lbpfeatures.cpp b/modules/xobjdetect/src/lbpfeatures.cpp index 48a23f008..bb0c27e64 100644 --- a/modules/xobjdetect/src/lbpfeatures.cpp +++ b/modules/xobjdetect/src/lbpfeatures.cpp @@ -1,9 +1,49 @@ -#include -#include "lbpfeatures.h" -#include "cascadeclassifier.h" +/* +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. -#include + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#include "precomp.hpp" using namespace cv; diff --git a/modules/xobjdetect/src/lbpfeatures.h b/modules/xobjdetect/src/lbpfeatures.h index db0719db5..c9de6ec2c 100644 --- a/modules/xobjdetect/src/lbpfeatures.h +++ b/modules/xobjdetect/src/lbpfeatures.h @@ -1,3 +1,47 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + #ifndef _OPENCV_LBPFEATURES_H_ #define _OPENCV_LBPFEATURES_H_ diff --git a/modules/xobjdetect/src/main.cpp b/modules/xobjdetect/src/main.cpp deleted file mode 100644 index e816c2657..000000000 --- a/modules/xobjdetect/src/main.cpp +++ /dev/null @@ -1,556 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#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 get_feature_indices() - { - return feature_indices_; - } - - void detect(Ptr eval, - const Mat& img, const vector& scales, - vector& 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 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(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(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(min_feature_ind, col_pos) < - data_pos.at(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 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 thresholds_; - vector alphas_; - vector feature_indices_; - vector polarities_; - vector 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 -read_imgs(const string& path) -{ - vector filenames; - glob(path, filenames); - vector imgs; - for (size_t i = 0; i < filenames.size(); ++i) { - imgs.push_back(imread(filenames[i], CV_LOAD_IMAGE_GRAYSCALE)); - } - return imgs; -} -vector -sample_patches(const string& path, int n_rows, int n_cols, int n_patches) -{ - vector filenames; - glob(path, filenames); - vector 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 pos_imgs = read_imgs("/home/vlad/gsoc/lfw_faces"); - assert(pos_imgs.size()); - - const char neg_path[] = "/home/vlad/gsoc/rtsd_bg"; - vector neg_imgs = sample_patches(neg_path, 24, 24, pos_imgs.size()); - assert(neg_imgs.size()); - - int n_features; - Mat pos_data, neg_data; - - Ptr 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 scales(scales_arr, - scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); - - WaldBoost boost; - vector 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(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(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 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 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 bboxes; - Mat1f confidences; - vector scales; - for (float scale = 0.2f; scale < 1.2f; scale *= 1.2) { - scales.push_back(scale); - } - Ptr params = CvFeatureParams::create(CvFeatureParams::LBP); - Ptr 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); -} - diff --git a/modules/xobjdetect/src/precomp.hpp b/modules/xobjdetect/src/precomp.hpp index 811329940..d8e5be196 100644 --- a/modules/xobjdetect/src/precomp.hpp +++ b/modules/xobjdetect/src/precomp.hpp @@ -1,7 +1,6 @@ /* - -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, +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. @@ -9,7 +8,12 @@ copy or use the software. For Open Source Computer Vision Library (3-clause BSD License) -Copyright (C) 2013, OpenCV Foundation, all rights reserved. +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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, @@ -28,31 +32,48 @@ are permitted provided that the following conditions are met: 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 copyright holders or contributors be liable for -any direct, indirect, incidental, special, exemplary, or consequential damages +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders 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. - */ #ifndef __OPENCV_XOBJDETECT_PRECOMP_HPP__ #define __OPENCV_XOBJDETECT_PRECOMP_HPP__ #include -#include #include #include #include +#include +#include + #include +#include + +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include + +#include "cascadeclassifier.h" +#include "traincascade_features.h" +#include "lbpfeatures.h" +#include "waldboost.hpp" #endif /* __OPENCV_XOBJDETECT_PRECOMP_HPP__ */ diff --git a/modules/xobjdetect/src/stump.cpp b/modules/xobjdetect/src/stump.cpp deleted file mode 100644 index 221445a29..000000000 --- a/modules/xobjdetect/src/stump.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - -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 - (3-clause BSD License) - -Copyright (C) 2013, OpenCV Foundation, 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: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions 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. - - * Neither the names of the copyright holders nor the names of the contributors - may 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 copyright holders 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. - -*/ - -#include "precomp.hpp" - -namespace cv -{ -namespace xobjdetect -{ - -/* Cumulative sum by rows */ -static void cumsum(const Mat_& src, Mat_ dst) -{ - CV_Assert(src.cols > 0); - - for( int row = 0; row < src.rows; ++row ) - { - dst(row, 0) = src(row, 0); - for( int col = 1; col < src.cols; ++col ) - { - dst(row, col) = dst(row, col - 1) + src(row, col); - } - } -} - -//fast log implementation. A bit less accurate but ~5x faster -inline float fast_log2 (float val) -{ - int * const exp_ptr = reinterpret_cast (&val); - int x = *exp_ptr; - const int log_2 = ((x >> 23) & 255) - 128; - x &= ~(255 << 23); - x += 127 << 23; - *exp_ptr = x; - - val = ((-1.0f/3) * val + 2) * val - 2.0f/3; // (1) - - return (val + log_2); -} - -inline float fast_log (const float &val) -{ - return (fast_log2 (val) * 0.69314718f); -} - -int Stump::train(const Mat& data, const Mat& labels, const Mat& weights, const std::vector& visited_features, bool use_fast_log) -{ - CV_Assert(labels.rows == 1 && labels.cols == data.cols); - CV_Assert(weights.rows == 1 && weights.cols == data.cols); - CV_Assert(data.cols > 1); - /* Assert that data and labels have int type */ - /* Assert that weights have float type */ - - Mat_ d = Mat_::zeros(1, data.cols); - const Mat_& l = labels; - const Mat_& w = weights; - - Mat_ indices(1, l.cols); - - Mat_ sorted_d(1, data.cols); - Mat_ sorted_l(1, l.cols); - Mat_ sorted_w(1, w.cols); - - - Mat_ pos_c_w = Mat_::zeros(1, w.cols); - Mat_ neg_c_w = Mat_::zeros(1, w.cols); - - - float min_err = FLT_MAX; - int min_row = -1; - int min_thr = -1; - int min_pol = -1; - float min_pos = 1; - float min_neg = -1; - float eps = 1.0f / (4 * l.cols); - - /* For every feature */ - for( int row = 0; row < data.rows; ++row ) - { - if(std::find(visited_features.begin(), visited_features.end(), row) != visited_features.end()) { - //feature discarded - continue; - } - data.row(row).copyTo(d.row(0)); - - sortIdx(d, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - for( int col = 0; col < indices.cols; ++col ) - { - int ind = indices(0, col); - sorted_d(0, col) = d(0, ind); - sorted_l(0, col) = l(0, ind); - sorted_w(0, col) = w(0, ind); - } - - Mat_ pos_w = Mat_::zeros(1, w.cols); - Mat_ neg_w = Mat_::zeros(1, w.cols); - for( int col = 0; col < d.cols; ++col ) - { - float weight = sorted_w(0, col); - if( sorted_l(0, col) == +1) - pos_w(0, col) = weight; - else - neg_w(0, col) = weight; - } - - cumsum(pos_w, pos_c_w); - cumsum(neg_w, neg_c_w); - - float pos_total_w = pos_c_w(0, w.cols - 1); - float neg_total_w = neg_c_w(0, w.cols - 1); - - for( int col = 0; col < w.cols - 1; ++col ) - { - float err, h_pos, h_neg; - float pos_wrong, pos_right; - float neg_wrong, neg_right; - - /* Direct polarity */ - - pos_wrong = pos_c_w(0, col); - pos_right = pos_total_w - pos_wrong; - - neg_right = neg_c_w(0, col); - neg_wrong = neg_total_w - neg_right; - - err = sqrt(pos_right * neg_wrong) + sqrt(pos_wrong * neg_right); - - if(use_fast_log) - { - h_pos = .5f * fast_log((pos_right + eps) / (pos_wrong + eps)); - h_neg = .5f * fast_log((neg_wrong + eps) / (neg_right + eps)); - } - else - { - h_pos = .5f * log((pos_right + eps) / (pos_wrong + eps)); - h_neg = .5f * log((neg_wrong + eps) / (neg_right + eps)); - } - - if( err < min_err ) - { - min_err = err; - min_row = row; - min_thr = (sorted_d(0, col) + sorted_d(0, col + 1)) / 2; - min_pol = +1; - min_pos = h_pos; - min_neg = h_neg; - } - - /* Opposite polarity */ - - swap(pos_right, pos_wrong); - swap(neg_right, neg_wrong); - - err = sqrt(pos_right * neg_wrong) + sqrt(pos_wrong * neg_right); - - if( err < min_err ) - { - min_err = err; - min_row = row; - min_thr = (sorted_d(0, col) + sorted_d(0, col + 1)) / 2; - min_pol = -1; - min_pos = -h_pos; - min_neg = -h_neg; - } - - } - } - - threshold_ = min_thr; - polarity_ = min_pol; - pos_value_ = min_pos; - neg_value_ = min_neg; - - return min_row; -} - -float Stump::predict(int value) const -{ - return polarity_ * (value - threshold_) > 0 ? pos_value_ : neg_value_; -} - - -void read(const FileNode& node, Stump& s, const Stump& default_value) -{ - if( node.empty() ) - s = default_value; - else - s.read(node); -} - -void write(FileStorage& fs, String&, const Stump& s) -{ - s.write(fs); -} - -} /* namespace xobjdetect */ -} /* namespace cv */ diff --git a/modules/xobjdetect/src/traincascade_features.h b/modules/xobjdetect/src/traincascade_features.h index 59cda4081..3e9b36748 100644 --- a/modules/xobjdetect/src/traincascade_features.h +++ b/modules/xobjdetect/src/traincascade_features.h @@ -1,3 +1,47 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + #ifndef _OPENCV_FEATURES_H_ #define _OPENCV_FEATURES_H_ diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 8866ab260..de95220bd 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -1,7 +1,6 @@ /* - -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, +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. @@ -9,7 +8,12 @@ copy or use the software. For Open Source Computer Vision Library (3-clause BSD License) -Copyright (C) 2013, OpenCV Foundation, all rights reserved. +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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, @@ -28,360 +32,367 @@ are permitted provided that the following conditions are met: 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 copyright holders or contributors be liable for -any direct, indirect, incidental, special, exemplary, or consequential damages +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders 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. - */ #include "precomp.hpp" -using std::swap; - -using std::vector; - -#include -using std::cout; -using std::endl; - - - - - - namespace cv { -namespace xobjdetect +static void compute_cdf(const Mat1b& data, + const Mat1f& weights, + Mat1f& cdf) { - //sort in-place of columns of the input matrix - void sort_columns_without_copy(Mat& m, Mat indices) - { - - if(indices.data == 0) - sortIdx(m, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - Mat indices_of_indices; - sortIdx(indices, indices_of_indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - std::vector visited; - for(int c = 0; c(0,ind_v))).copyTo(column); - } - else - { - temp_column.copyTo(column); - } - - - if(indices_of_indices.at(0,next) != next) //value is in the right place - { - //store the next value to change - (m.col(indices_of_indices.at(0,next))).copyTo(temp_column); - //insert the value to change at the right place - column.copyTo(m.col(indices_of_indices.at(0,next))); - - //find the index of the next value to change - next = indices_of_indices.at(0,next); - //if the idenx is not visited yet - if(visited[next] == false) - { - //then mark it as visited, it will be computed in the next round - visited[next] = true; - } - else - { - //find first non visited index - int i = 0; - while(i<(int)visited.size() && visited[i] == true) - { - i++; - } - ind_v = i; - next = i; - temp_column = Mat(); - - } - } - else // value is already at the right place - { - visited[next] = true; - int i = 0; - while(i<(int)visited.size() && visited[i] == true) - { - i++; - } - next = i; - temp_column = Mat(); - ind_v = i; - } - - + 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); } - - - } +} -class WaldBoostImpl : public WaldBoost +static void compute_min_step(const Mat &data_pos, const Mat &data_neg, size_t n_bins, + Mat &data_min, Mat &data_step) { -public: - /* Initialize WaldBoost cascade with default of specified parameters */ - WaldBoostImpl(const WaldBoostParams& params): - params_(params) - {} + // Check that quantized data will fit in unsigned char + assert(n_bins <= 256); - virtual std::vector train(Mat& data, - const Mat& labels, bool use_fast_log=false); + assert(data_pos.rows == data_neg.rows); - virtual float predict( - const Ptr& feature_evaluator) const; + Mat reduced_pos, reduced_neg; - virtual void write(FileStorage& fs) const; + 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; - virtual void read(const FileNode& node); + 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; -private: - /* Parameters for cascade training */ - WaldBoostParams params_; - /* Stumps in cascade */ - std::vector stumps_; - /* Rejection thresholds for linear combination at every stump evaluation */ - std::vector thresholds_; -}; + data_step = (data_max - data_min) / (n_bins - 1); +} -static int count(const Mat_ &m, int elem) +static void quantize_data(Mat &data, Mat1f &data_min, Mat1f &data_step) { - int res = 0; - for( int row = 0; row < m.rows; ++row ) - for( int col = 0; col < m.cols; ++col ) - if( m(row, col) == elem) - res += 1; - return res; +//#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); } -void WaldBoostImpl::read(const FileNode& node) +WaldBoost::WaldBoost(int weak_count): + weak_count_(weak_count), + thresholds_(), + alphas_(), + feature_indices_(), + polarities_(), + cascade_thresholds_() {} + +WaldBoost::WaldBoost(): + weak_count_(), + thresholds_(), + alphas_(), + feature_indices_(), + polarities_(), + cascade_thresholds_() {} + +std::vector WaldBoost::get_feature_indices() { - FileNode params = node["waldboost_params"]; - params_.weak_count = (int)(params["weak_count"]); - params_.alpha = (float)(params["alpha"]); - - FileNode stumps = node["waldboost_stumps"]; - stumps_.clear(); - for( FileNodeIterator n = stumps.begin(); n != stumps.end(); ++n ) - { - Stump s; - *n >> s; - stumps_.push_back(s); - } + return feature_indices_; +} - FileNode thresholds = node["waldboost_thresholds"]; - thresholds_.clear(); - for( FileNodeIterator n = thresholds.begin(); n != thresholds.end(); ++n ) - { - float t; - *n >> t; - thresholds_.push_back(t); +void WaldBoost::detect(Ptr eval, + const Mat& img, const std::vector& scales, + std::vector& 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 WaldBoostImpl::write(FileStorage& fs) const +void WaldBoost::detect(Ptr eval, + const Mat& img, const std::vector& scales, + std::vector& bboxes, std::vector& confidences) { - fs << "{"; - fs << "waldboost_params" << "{" - << "weak_count" << params_.weak_count - << "alpha" << params_.alpha - << "}"; - - fs << "waldboost_stumps" << "["; - for( size_t i = 0; i < stumps_.size(); ++i ) - fs << stumps_[i]; - fs << "]"; - - fs << "waldboost_thresholds" << "["; - for( size_t i = 0; i < thresholds_.size(); ++i ) - fs << thresholds_[i]; - fs << "]"; - fs << "}"; - + bboxes.clear(); + confidences.clear(); + + 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->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); + } + } + } + } + std::vector levels(bboxes.size(), 0); + groupRectangles(bboxes, levels, confidences, 3, 0.7); } -vector WaldBoostImpl::train(Mat& data, const Mat& labels_, bool use_fast_log) +void WaldBoost::fit(Mat& data_pos, Mat& data_neg) { - CV_Assert(labels_.rows == 1 && labels_.cols == data.cols); - CV_Assert(data.rows >= params_.weak_count); - - Mat labels; - labels_.copyTo(labels); - - bool null_data = true; - for( int row = 0; row < data.rows; ++row ) - { - for( int col = 0; col < data.cols; ++col ) - if( data.at(row, col) ) - null_data = false; - } - CV_Assert(!null_data); - - int pos_count = count(labels, +1); - int neg_count = count(labels, -1); - - Mat_ weights(labels.rows, labels.cols); - float pos_weight = 1.0f / (2 * pos_count); - float neg_weight = 1.0f / (2 * neg_count); - for( int col = 0; col < weights.cols; ++col ) - { - if( labels.at(0, col) == +1 ) - weights.at(0, col) = pos_weight; - else - weights.at(0, col) = neg_weight; + // 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); + + std::vector feature_ignore; + for (int i = 0; i < data_pos.rows; ++i) { + feature_ignore.push_back(false); } - vector feature_indices_pool; - for( int ind = 0; ind < data.rows; ++ind ) - feature_indices_pool.push_back(ind); + 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); - vector feature_indices; - vector visited_features; - Mat_ trace = Mat_::zeros(labels.rows, labels.cols); - stumps_.clear(); - thresholds_.clear(); - for( int i = 0; i < params_.weak_count; ++i) - { - Stump s; - int feature_ind = s.train(data, labels, weights, visited_features, use_fast_log); - stumps_.push_back(s); - int ind = feature_indices_pool[feature_ind]; - //we don't need to erase the feature index anymore, because we ignore them if already visited - //feature_indices_pool.erase(feature_indices_pool.begin() + feature_ind); - feature_indices.push_back(ind); - - // Recompute weights - for( int col = 0; col < weights.cols; ++col ) - { - float h = s.predict(data.at(feature_ind, col)); - trace(0, col) += h; - int label = labels.at(0, col); - weights.at(0, col) *= exp(-label * h); - } + bool quantize = false; + if (data_pos.type() != CV_8U) { + std::cerr << "quantize" << std::endl; + quantize = true; + } - // set to zero row for feature in data - for(int jc = 0; jc(feature_ind, jc) = 0; - } - visited_features.push_back(feature_ind); + 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); + } + std::cerr << "pos=" << data_pos.cols << " neg=" << data_neg.cols << std::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; + } + } + } + } - // Normalize weights - float z = (float)sum(weights)[0]; - for( int col = 0; col < weights.cols; ++col) - { - weights.at(0, col) /= z; + 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; + cascade_thresholds_.push_back(-1); + + double loss = 0; + // Update positive weights + for (int j = 0; j < data_pos.cols; ++j) { + int val = data_pos.at(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); } - // Sort trace - Mat indices; - sortIdx(trace, indices, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - Mat new_weights = Mat_::zeros(weights.rows, weights.cols); - Mat new_labels = Mat_::zeros(labels.rows, labels.cols); - Mat new_trace; - for( int col = 0; col < new_weights.cols; ++col ) - { - new_weights.at(0, col) = - weights.at(0, indices.at(0, col)); - new_labels.at(0, col) = - labels.at(0, indices.at(0, col)); + // Update negative weights + for (int j = 0; j < data_neg.cols; ++j) { + int val = data_neg.at(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); } - - //sort in-place to save memory - sort_columns_without_copy(data, indices); - sort(trace, new_trace, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); - - // Compute threshold for trace - /* - int col = 0; - for( int pos_i = 0; - pos_i < pos_count * params_.alpha && col < new_labels.cols; - ++col ) - { - if( new_labels.at(0, col) == +1 ) - ++pos_i; - } - */ - - int cur_pos = 0, cur_neg = 0; - int max_col = 0; - for( int col = 0; col < new_labels.cols - 1; ++col ) { - if (new_labels.at(0, col) == +1 ) - ++cur_pos; - else - ++cur_neg; - - float p_neg = cur_neg / (float)neg_count; - float p_pos = cur_pos / (float)pos_count; - if( params_.alpha * p_neg > p_pos ) - max_col = col; + std::cerr << "i=" << std::setw(4) << i; + std::cerr << " feat=" << std::setw(5) << min_feature_ind; + std::cerr << " thr=" << std::setw(3) << threshold_q; + std::cerr << " alpha=" << std::fixed << std::setprecision(3) + << alpha << " err=" << std::fixed << std::setprecision(3) << min_err + << " loss=" << std::scientific << loss << std::endl; + + if (loss < 1e-50 || min_err > 0.5) { + std::cerr << "Stopping early" << std::endl; + weak_count_ = i + 1; + break; } - thresholds_.push_back(new_trace.at(0, max_col)); - - // Drop samples below threshold - //uses Rois instead of copyTo to save memory - data = data(Rect(max_col, 0, data.cols - max_col, data.rows)); - new_trace.colRange(max_col, new_trace.cols).copyTo(trace); - new_weights.colRange(max_col, new_weights.cols).copyTo(weights); - new_labels.colRange(max_col, new_labels.cols).copyTo(labels); - - pos_count = count(labels, +1); - neg_count = count(labels, -1); + // Normalize weights + double z = (sum(pos_weights) + sum(neg_weights))[0]; + pos_weights /= z; + neg_weights /= z; + } +} - if( data.cols < 2 || neg_count == 0) - { - break; +int WaldBoost::predict(Ptr eval, float *h) const +{ + double THR = -6; + 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 < THR) { + return -1; } } - return feature_indices; + *h = res; + return res > THR ? +1 : -1; } -float WaldBoostImpl::predict( - const Ptr& feature_evaluator) const +void WaldBoost::save(const std::string& filename) { - float trace = 0; - CV_Assert(stumps_.size() == thresholds_.size()); - for( size_t i = 0; i < stumps_.size(); ++i ) - { - int value = feature_evaluator->evaluate(i); - trace += stumps_[i].predict(value); - - if( trace < thresholds_[i] ) - return -1; + std::ofstream f(filename.c_str()); + f << weak_count_ << std::endl; + for (size_t i = 0; i < thresholds_.size(); ++i) { + f << thresholds_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < alphas_.size(); ++i) { + f << alphas_[i] << " "; } - return trace; + f << std::endl; + for (size_t i = 0; i < polarities_.size(); ++i) { + f << polarities_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < cascade_thresholds_.size(); ++i) { + f << cascade_thresholds_[i] << " "; + } + f << std::endl; + for (size_t i = 0; i < feature_indices_.size(); ++i) { + f << feature_indices_[i] << " "; + } + f << std::endl; } -Ptr -createWaldBoost(const WaldBoostParams& params) +void WaldBoost::load(const std::string& filename) { - return Ptr(new WaldBoostImpl(params)); + std::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 WaldBoost::reset(int weak_count) +{ + weak_count_ = weak_count; + thresholds_.clear(); + alphas_.clear(); + feature_indices_.clear(); + polarities_.clear(); + cascade_thresholds_.clear(); +} -} /* namespace xobjdetect */ -} /* namespace cv */ +WaldBoost::~WaldBoost() +{ +} + +}; diff --git a/modules/xobjdetect/src/waldboost.hpp b/modules/xobjdetect/src/waldboost.hpp new file mode 100644 index 000000000..4e9aaa6e6 --- /dev/null +++ b/modules/xobjdetect/src/waldboost.hpp @@ -0,0 +1,88 @@ +/* +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 + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. +*/ + +#ifndef __OPENCV_XOBJDETECT_WALDBOOST_HPP__ +#define __OPENCV_XOBJDETECT_WALDBOOST_HPP__ + +#include "precomp.hpp" + +namespace cv { + +class WaldBoost { +public: + WaldBoost(int weak_count); + WaldBoost(); + std::vector get_feature_indices(); + + void detect(Ptr eval, + const Mat& img, + const std::vector& scales, + std::vector& bboxes, + Mat1f& confidences); + + void detect(Ptr eval, + const Mat& img, + const std::vector& scales, + std::vector& bboxes, + std::vector& confidences); + + void fit(Mat& data_pos, Mat& data_neg); + int predict(Ptr eval, float *h) const; + void save(const std::string& filename); + void load(const std::string& filename); + void reset(int weak_count); + ~WaldBoost(); + +private: + int weak_count_; + std::vector thresholds_; + std::vector alphas_; + std::vector feature_indices_; + std::vector polarities_; + std::vector cascade_thresholds_; +}; + +} + +#endif diff --git a/modules/xobjdetect/tools/CMakeLists.txt b/modules/xobjdetect/tools/CMakeLists.txt new file mode 100644 index 000000000..5c40af12e --- /dev/null +++ b/modules/xobjdetect/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(waldboost_detector) diff --git a/modules/adas/tools/fcw_train/CMakeLists.txt b/modules/xobjdetect/tools/waldboost_detector/CMakeLists.txt similarity index 97% rename from modules/adas/tools/fcw_train/CMakeLists.txt rename to modules/xobjdetect/tools/waldboost_detector/CMakeLists.txt index 5b1d78303..5f34f1e71 100644 --- a/modules/adas/tools/fcw_train/CMakeLists.txt +++ b/modules/xobjdetect/tools/waldboost_detector/CMakeLists.txt @@ -1,4 +1,4 @@ -set(name fcw_train) +set(name waldboost_detector) set(the_target opencv_${name}) set(OPENCV_${the_target}_DEPS opencv_core opencv_imgcodecs opencv_videoio diff --git a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp new file mode 100644 index 000000000..f0bbf87fb --- /dev/null +++ b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +using namespace std; +using namespace cv; +using namespace cv::xobjdetect; + +int main(int argc, char **argv) +{ + if (argc < 5) { + cerr << "Usage: " << argv[0] << " train " << endl; + cerr << " " << argv[0] << " detect " << endl; + return 0; + } + + string mode = argv[1]; + WBDetector detector(argv[2]); + if (mode == "train") { + detector.train(argv[3], argv[4]); + } else if (mode == "detect") { + cerr << "detect" << endl; + vector bboxes; + vector confidences; + Mat img = imread(argv[3], CV_LOAD_IMAGE_GRAYSCALE); + detector.detect(img, bboxes, confidences); + for (size_t i = 0; i < bboxes.size(); ++i) { + rectangle(img, bboxes[i], Scalar(255, 0, 0)); + } + imwrite(argv[4], img); + } +} + From 81d44b7e2270ba457b3ac750538ecb9d7af185cb Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Fri, 7 Aug 2015 15:48:47 +0300 Subject: [PATCH 3/9] Fix warnings --- modules/xobjdetect/src/detector.cpp | 12 ++++++------ modules/xobjdetect/src/features.cpp | 6 +++--- modules/xobjdetect/src/lbpfeatures.h | 8 ++++---- modules/xobjdetect/src/traincascade_features.h | 6 +++--- modules/xobjdetect/src/waldboost.cpp | 2 +- .../tools/waldboost_detector/waldboost_detector.cpp | 1 - 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/modules/xobjdetect/src/detector.cpp b/modules/xobjdetect/src/detector.cpp index e755c6dc3..d35847300 100644 --- a/modules/xobjdetect/src/detector.cpp +++ b/modules/xobjdetect/src/detector.cpp @@ -107,8 +107,8 @@ void WBDetector::train( int n_features; Mat pos_data, neg_data; - Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); - eval->init(CvFeatureParams::create(CvFeatureParams::LBP), 1, Size(24, 24)); + Ptr eval = CvFeatureEvaluator::create(); + eval->init(CvFeatureParams::create(), 1, Size(24, 24)); n_features = eval->getNumFeatures(); const int stages[] = {32, 64, 128, 256, 512, 1024, 2048, 4096}; @@ -135,14 +135,14 @@ void WBDetector::train( 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(j, k) = (*eval)(j, 0); + pos_data.at(j, k) = (*eval)(j); } } 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(j, k) = (*eval)(j, 0); + neg_data.at(j, k) = (*eval)(j); } } @@ -204,8 +204,8 @@ void WBDetector::detect( for (float scale = 0.2f; scale < 1.2f; scale *= 1.1) { scales.push_back(scale); } - Ptr params = CvFeatureParams::create(CvFeatureParams::LBP); - Ptr eval = CvFeatureEvaluator::create(CvFeatureParams::LBP); + Ptr params = CvFeatureParams::create(); + Ptr eval = CvFeatureEvaluator::create(); eval->init(params, 1, Size(24, 24)); boost.detect(eval, img, scales, bboxes, confidences); assert(confidences.size() == bboxes.size()); diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/features.cpp index 38ba51a7e..50b72b34c 100644 --- a/modules/xobjdetect/src/features.cpp +++ b/modules/xobjdetect/src/features.cpp @@ -96,7 +96,7 @@ bool CvFeatureParams::read( const FileNode &node ) return ( maxCatCount >= 0 && featSize >= 1 ); } -Ptr CvFeatureParams::create( int featureType ) +Ptr CvFeatureParams::create() { return Ptr(new CvLBPFeatureParams); } @@ -114,7 +114,7 @@ void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams, generateFeatures(); } -void CvFeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx, const std::vector& feature_ind) +void CvFeatureEvaluator::setImage(const Mat &, uchar clsLabel, int idx, const std::vector& feature_ind) { //CV_Assert(img.cols == winSize.width); //CV_Assert(img.rows == winSize.height); @@ -122,7 +122,7 @@ void CvFeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx, const cls.ptr(idx)[0] = clsLabel; } -Ptr CvFeatureEvaluator::create(int type) +Ptr CvFeatureEvaluator::create() { return Ptr(new CvLBPEvaluator); } diff --git a/modules/xobjdetect/src/lbpfeatures.h b/modules/xobjdetect/src/lbpfeatures.h index c9de6ec2c..aa47836d7 100644 --- a/modules/xobjdetect/src/lbpfeatures.h +++ b/modules/xobjdetect/src/lbpfeatures.h @@ -64,8 +64,8 @@ public: virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx, const std::vector &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 float operator()(int featureIdx) + { return (float)features[featureIdx].calc( cur_sum ); } virtual void writeFeatures( cv::FileStorage &fs, const cv::Mat& featureMap ) const; protected: virtual void generateFeatures(); @@ -75,7 +75,7 @@ protected: 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 ); + uchar calc( const cv::Mat& _sum ); void write( cv::FileStorage &fs ) const; cv::Rect rect; @@ -90,7 +90,7 @@ protected: int offset_; }; -inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum, int offset, size_t y) +inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum) { const int* psum = _sum.ptr(); diff --git a/modules/xobjdetect/src/traincascade_features.h b/modules/xobjdetect/src/traincascade_features.h index 3e9b36748..788cde8c7 100644 --- a/modules/xobjdetect/src/traincascade_features.h +++ b/modules/xobjdetect/src/traincascade_features.h @@ -110,7 +110,7 @@ public: virtual void init( const CvFeatureParams& fp ); virtual void write( cv::FileStorage &fs ) const; virtual bool read( const cv::FileNode &node ); - static cv::Ptr create( int featureType ); + static cv::Ptr create(); 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 }; @@ -124,8 +124,8 @@ public: virtual void setImage(const cv::Mat& img, uchar clsLabel, int idx, const std::vector &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 create(int type); + virtual float operator()(int featureIdx) = 0; + static cv::Ptr create(); int getNumFeatures() const { return numFeatures; } int getMaxCatCount() const { return featureParams->maxCatCount; } diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index de95220bd..0c73a3ff2 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -317,7 +317,7 @@ int WaldBoost::predict(Ptr eval, float *h) const 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); + float val = (*eval)(feature_indices_[i]); int label = polarities_[i] * (val - thresholds_[i]) > 0 ? +1: -1; res += alphas_[i] * label; if (res < THR) { diff --git a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp index f0bbf87fb..cde6c2475 100644 --- a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp +++ b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp @@ -30,4 +30,3 @@ int main(int argc, char **argv) imwrite(argv[4], img); } } - From 4b161803b49c125c3bf9b124013356aa050b843e Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Fri, 7 Aug 2015 16:12:59 +0300 Subject: [PATCH 4/9] Warning fixes --- modules/xobjdetect/src/detector.cpp | 14 +++++++------- modules/xobjdetect/src/features.cpp | 2 +- modules/xobjdetect/src/waldboost.cpp | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/modules/xobjdetect/src/detector.cpp b/modules/xobjdetect/src/detector.cpp index d35847300..ccab2d6a6 100644 --- a/modules/xobjdetect/src/detector.cpp +++ b/modules/xobjdetect/src/detector.cpp @@ -60,12 +60,12 @@ static vector sample_patches( const string& path, int n_rows, int n_cols, - int n_patches) + size_t n_patches) { vector filenames; glob(path, filenames); vector patches; - int patch_count = 0; + size_t 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) { @@ -116,7 +116,7 @@ void WBDetector::train( const int stage_neg = 5000; const int max_per_image = 25; - const float scales_arr[] = {.3, .4, .5, .6, .7, .8, .9, 1}; + const float scales_arr[] = {.3f, .4f, .5f, .6f, .7f, .8f, .9f, 1.0f}; const vector scales(scales_arr, scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); @@ -129,8 +129,8 @@ void WBDetector::train( cerr << "compute features" << endl; - pos_data = Mat1b(n_features, pos_imgs.size()); - neg_data = Mat1b(n_features, neg_imgs.size()); + pos_data = Mat1b(n_features, (int)pos_imgs.size()); + neg_data = Mat1b(n_features, (int)neg_imgs.size()); for (size_t k = 0; k < pos_imgs.size(); ++k) { eval->setImage(pos_imgs[k], +1, 0, boost.get_feature_indices()); @@ -142,7 +142,7 @@ void WBDetector::train( 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(j, k) = (*eval)(j); + neg_data.at(j, (int)k) = (*eval)(j); } } @@ -201,7 +201,7 @@ void WBDetector::detect( bboxes.clear(); confidences.clear(); vector scales; - for (float scale = 0.2f; scale < 1.2f; scale *= 1.1) { + for (float scale = 0.2f; scale < 1.2f; scale *= 1.1f) { scales.push_back(scale); } Ptr params = CvFeatureParams::create(); diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/features.cpp index 50b72b34c..741f79891 100644 --- a/modules/xobjdetect/src/features.cpp +++ b/modules/xobjdetect/src/features.cpp @@ -114,7 +114,7 @@ void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams, generateFeatures(); } -void CvFeatureEvaluator::setImage(const Mat &, uchar clsLabel, int idx, const std::vector& feature_ind) +void CvFeatureEvaluator::setImage(const Mat &, uchar clsLabel, int idx, const std::vector&) { //CV_Assert(img.cols == winSize.width); //CV_Assert(img.rows == winSize.height); diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 0c73a3ff2..7fac4244f 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -84,7 +84,7 @@ static void compute_min_step(const Mat &data_pos, const Mat &data_neg, size_t n_ max(reduced_pos, reduced_neg, data_max); data_max += 0.01; - data_step = (data_max - data_min) / (n_bins - 1); + data_step = (data_max - data_min) / (double)(n_bins - 1); } static void quantize_data(Mat &data, Mat1f &data_min, Mat1f &data_step) @@ -132,15 +132,15 @@ void WaldBoost::detect(Ptr eval, 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; + int n_rows = (int)(24 / scale); + int n_cols = (int)(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; + int row = (int)(r / scale); + int col = (int)(c / scale); bboxes.push_back(Rect(col, row, n_cols, n_rows)); confidences.push_back(h); } @@ -164,14 +164,14 @@ void WaldBoost::detect(Ptr eval, 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; + int n_rows = (int)(24 / scale); + int n_cols = (int)(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->setWindow(Point(c, r)); if (predict(eval, &h) == +1) { - int row = r / scale; - int col = c / scale; + int row = (int)(r / scale); + int col = (int)(c / scale); bboxes.push_back(Rect(col, row, n_cols, n_rows)); confidences.push_back(h); } @@ -233,7 +233,7 @@ void WaldBoost::fit(Mat& data_pos, Mat& data_neg) 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]; + float neg_total = (float)sum(neg_weights)[0]; Mat1f err_direct = pos_cdf + neg_total - neg_cdf; Mat1f err_backward = 1.0f - err_direct; @@ -265,7 +265,7 @@ void WaldBoost::fit(Mat& data_pos, Mat& data_neg) } - float alpha = .5f * log((1 - min_err) / min_err); + float alpha = .5f * (float)log((1 - min_err) / min_err); alphas_.push_back(alpha); feature_indices_.push_back(min_feature_ind); thresholds_.push_back(min_threshold); From 5992dedffff5c436b2c75f2c423b8d556845dee0 Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Fri, 7 Aug 2015 16:27:41 +0300 Subject: [PATCH 5/9] Warning fixes --- modules/xobjdetect/src/detector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/xobjdetect/src/detector.cpp b/modules/xobjdetect/src/detector.cpp index ccab2d6a6..7fe540f45 100644 --- a/modules/xobjdetect/src/detector.cpp +++ b/modules/xobjdetect/src/detector.cpp @@ -135,14 +135,14 @@ void WBDetector::train( 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(j, k) = (*eval)(j); + pos_data.at(j, (int)k) = (uchar)(*eval)(j); } } 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(j, (int)k) = (*eval)(j); + neg_data.at(j, (int)k) = (uchar)(*eval)(j); } } From 09286322064725fb2093267f4136e6c5c7374716 Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Tue, 11 Aug 2015 10:39:11 +0300 Subject: [PATCH 6/9] Add namespaces --- modules/xobjdetect/src/features.cpp | 11 +++++++---- modules/xobjdetect/src/lbpfeatures.cpp | 6 +++++- modules/xobjdetect/src/lbpfeatures.h | 7 +++++++ modules/xobjdetect/src/traincascade_features.h | 6 ++++++ modules/xobjdetect/src/waldboost.cpp | 7 ++++--- modules/xobjdetect/src/waldboost.hpp | 2 ++ 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/features.cpp index 741f79891..41dfbf5e0 100644 --- a/modules/xobjdetect/src/features.cpp +++ b/modules/xobjdetect/src/features.cpp @@ -44,8 +44,8 @@ the use of this software, even if advised of the possibility of such damage. #include "precomp.hpp" -using namespace std; -using namespace cv; +namespace cv { +namespace xobjdetect { float calcNormFactor( const Mat& sum, const Mat& sqSum ) { @@ -63,9 +63,9 @@ float calcNormFactor( const Mat& sum, const Mat& sqSum ) CvParams::CvParams() : name( "params" ) {} void CvParams::printDefaults() const -{ cout << "--" << name << "--" << endl; } +{ std::cout << "--" << name << "--" << std::endl; } void CvParams::printAttrs() const {} -bool CvParams::scanAttr( const string, const string ) { return false; } +bool CvParams::scanAttr( const std::string, const std::string ) { return false; } //---------------------------- FeatureParams -------------------------------------- @@ -126,3 +126,6 @@ Ptr CvFeatureEvaluator::create() { return Ptr(new CvLBPEvaluator); } + +} +} diff --git a/modules/xobjdetect/src/lbpfeatures.cpp b/modules/xobjdetect/src/lbpfeatures.cpp index bb0c27e64..3f67ea88a 100644 --- a/modules/xobjdetect/src/lbpfeatures.cpp +++ b/modules/xobjdetect/src/lbpfeatures.cpp @@ -45,7 +45,8 @@ the use of this software, even if advised of the possibility of such damage. #include "precomp.hpp" -using namespace cv; +namespace cv { +namespace xobjdetect { CvLBPFeatureParams::CvLBPFeatureParams() { @@ -122,3 +123,6 @@ void CvLBPEvaluator::Feature::write(FileStorage &fs) const { fs << CC_RECT << "[:" << rect.x << rect.y << rect.width << rect.height << "]"; } + +} +} diff --git a/modules/xobjdetect/src/lbpfeatures.h b/modules/xobjdetect/src/lbpfeatures.h index aa47836d7..af50a321d 100644 --- a/modules/xobjdetect/src/lbpfeatures.h +++ b/modules/xobjdetect/src/lbpfeatures.h @@ -49,6 +49,10 @@ the use of this software, even if advised of the possibility of such damage. #define LBPF_NAME "lbpFeatureParams" + +namespace cv { +namespace xobjdetect { + struct CvLBPFeatureParams : CvFeatureParams { CvLBPFeatureParams(); @@ -106,4 +110,7 @@ inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum) (psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0)); // 3 } +} +} + #endif diff --git a/modules/xobjdetect/src/traincascade_features.h b/modules/xobjdetect/src/traincascade_features.h index 788cde8c7..2b5920233 100644 --- a/modules/xobjdetect/src/traincascade_features.h +++ b/modules/xobjdetect/src/traincascade_features.h @@ -70,6 +70,9 @@ the use of this software, even if advised of the possibility of such damage. (p3) = (rect).x + (rect).width - (rect).height \ + (step) * ((rect).y + (rect).width + (rect).height); +namespace cv { +namespace xobjdetect { + float calcNormFactor( const cv::Mat& sum, const cv::Mat& sqSum ); template @@ -142,4 +145,7 @@ protected: cv::Mat cls; }; +} +} + #endif diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 7fac4244f..092c89667 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -44,8 +44,8 @@ the use of this software, even if advised of the possibility of such damage. #include "precomp.hpp" -namespace cv -{ +namespace cv { +namespace xobjdetect { static void compute_cdf(const Mat1b& data, const Mat1f& weights, @@ -395,4 +395,5 @@ WaldBoost::~WaldBoost() { } -}; +} +} diff --git a/modules/xobjdetect/src/waldboost.hpp b/modules/xobjdetect/src/waldboost.hpp index 4e9aaa6e6..13b2a1883 100644 --- a/modules/xobjdetect/src/waldboost.hpp +++ b/modules/xobjdetect/src/waldboost.hpp @@ -48,6 +48,7 @@ the use of this software, even if advised of the possibility of such damage. #include "precomp.hpp" namespace cv { +namespace xobjdetect { class WaldBoost { public: @@ -83,6 +84,7 @@ private: std::vector cascade_thresholds_; }; +} } #endif From 13053d6b6fb9c831277b198a4762e94c4e18b705 Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Mon, 24 Aug 2015 01:27:26 +0300 Subject: [PATCH 7/9] Interface and naming fixes --- .../xobjdetect/include/opencv2/xobjdetect.hpp | 16 +- modules/xobjdetect/src/cascadeclassifier.h | 4 +- .../{features.cpp => feature_evaluator.cpp} | 0 ...scade_features.h => feature_evaluator.hpp} | 0 modules/xobjdetect/src/lbpfeatures.h | 2 +- modules/xobjdetect/src/precomp.hpp | 4 +- modules/xobjdetect/src/waldboost.cpp | 140 ++++++++++++------ modules/xobjdetect/src/waldboost.hpp | 4 + .../src/{detector.cpp => wbdetector.cpp} | 50 ++++--- modules/xobjdetect/src/wbdetector.hpp | 77 ++++++++++ .../waldboost_detector/waldboost_detector.cpp | 23 ++- 11 files changed, 235 insertions(+), 85 deletions(-) rename modules/xobjdetect/src/{features.cpp => feature_evaluator.cpp} (100%) rename modules/xobjdetect/src/{traincascade_features.h => feature_evaluator.hpp} (100%) rename modules/xobjdetect/src/{detector.cpp => wbdetector.cpp} (89%) create mode 100644 modules/xobjdetect/src/wbdetector.hpp diff --git a/modules/xobjdetect/include/opencv2/xobjdetect.hpp b/modules/xobjdetect/include/opencv2/xobjdetect.hpp index 9467bff2b..da4ecf451 100644 --- a/modules/xobjdetect/include/opencv2/xobjdetect.hpp +++ b/modules/xobjdetect/include/opencv2/xobjdetect.hpp @@ -56,21 +56,23 @@ namespace xobjdetect class CV_EXPORTS WBDetector { public: - WBDetector(const std::string& model_filename); + virtual void read(const FileNode &node) = 0; + virtual void write(FileStorage &fs) const = 0; - void train( + virtual void train( const std::string& pos_samples, - const std::string& neg_imgs); + const std::string& neg_imgs) = 0; - void detect( + virtual void detect( const Mat& img, std::vector &bboxes, - std::vector &confidences); + std::vector &confidences) = 0; -private: - std::string model_filename_; + virtual ~WBDetector(){} }; +CV_EXPORTS Ptr create_wbdetector(); + } /* namespace xobjdetect */ } /* namespace cv */ diff --git a/modules/xobjdetect/src/cascadeclassifier.h b/modules/xobjdetect/src/cascadeclassifier.h index 25f836478..b948480dd 100644 --- a/modules/xobjdetect/src/cascadeclassifier.h +++ b/modules/xobjdetect/src/cascadeclassifier.h @@ -45,9 +45,7 @@ the use of this software, even if advised of the possibility of such damage. #ifndef _OPENCV_CASCADECLASSIFIER_H_ #define _OPENCV_CASCADECLASSIFIER_H_ -#include -#include "traincascade_features.h" -#include "lbpfeatures.h" +#include "precomp.hpp" #define CC_CASCADE_FILENAME "cascade.xml" #define CC_PARAMS_FILENAME "params.xml" diff --git a/modules/xobjdetect/src/features.cpp b/modules/xobjdetect/src/feature_evaluator.cpp similarity index 100% rename from modules/xobjdetect/src/features.cpp rename to modules/xobjdetect/src/feature_evaluator.cpp diff --git a/modules/xobjdetect/src/traincascade_features.h b/modules/xobjdetect/src/feature_evaluator.hpp similarity index 100% rename from modules/xobjdetect/src/traincascade_features.h rename to modules/xobjdetect/src/feature_evaluator.hpp diff --git a/modules/xobjdetect/src/lbpfeatures.h b/modules/xobjdetect/src/lbpfeatures.h index af50a321d..0fc564a8b 100644 --- a/modules/xobjdetect/src/lbpfeatures.h +++ b/modules/xobjdetect/src/lbpfeatures.h @@ -45,7 +45,7 @@ the use of this software, even if advised of the possibility of such damage. #ifndef _OPENCV_LBPFEATURES_H_ #define _OPENCV_LBPFEATURES_H_ -#include "traincascade_features.h" +#include "precomp.hpp" #define LBPF_NAME "lbpFeatureParams" diff --git a/modules/xobjdetect/src/precomp.hpp b/modules/xobjdetect/src/precomp.hpp index d8e5be196..a83f78715 100644 --- a/modules/xobjdetect/src/precomp.hpp +++ b/modules/xobjdetect/src/precomp.hpp @@ -72,8 +72,10 @@ the use of this software, even if advised of the possibility of such damage. #include #include "cascadeclassifier.h" -#include "traincascade_features.h" +#include "feature_evaluator.hpp" #include "lbpfeatures.h" #include "waldboost.hpp" +#include "wbdetector.hpp" +#include #endif /* __OPENCV_XOBJDETECT_PRECOMP_HPP__ */ diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 092c89667..41ac99020 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -271,7 +271,6 @@ void WaldBoost::fit(Mat& data_pos, Mat& data_neg) thresholds_.push_back(min_threshold); polarities_.push_back(min_polarity); feature_ignore[min_feature_ind] = true; - cascade_thresholds_.push_back(-1); double loss = 0; // Update positive weights @@ -291,13 +290,48 @@ void WaldBoost::fit(Mat& data_pos, Mat& data_neg) neg_trace(0, j) += alpha * label; loss += exp(+neg_trace(0, j)) / (2.0f * data_neg.cols); } + double cascade_threshold = -1; + minMaxIdx(pos_trace, &cascade_threshold); + cascade_thresholds_.push_back(cascade_threshold); + std::cerr << "i=" << std::setw(4) << i; std::cerr << " feat=" << std::setw(5) << min_feature_ind; std::cerr << " thr=" << std::setw(3) << threshold_q; + std::cerr << " casthr=" << std::fixed << std::setprecision(3) + << cascade_threshold; std::cerr << " alpha=" << std::fixed << std::setprecision(3) << alpha << " err=" << std::fixed << std::setprecision(3) << min_err << " loss=" << std::scientific << loss << std::endl; + //int pos = 0; + //for (int j = 0; j < data_pos.cols; ++j) { + // if (pos_trace(0, j) > cascade_threshold - 0.5) { + // pos_trace(0, pos) = pos_trace(0, j); + // data_pos.col(j).copyTo(data_pos.col(pos)); + // pos_weights(0, pos) = pos_weights(0, j); + // pos += 1; + // } + //} + //std::cerr << "pos " << data_pos.cols << "/" << pos << std::endl; + //pos_trace = pos_trace.colRange(0, pos); + //data_pos = data_pos.colRange(0, pos); + //pos_weights = pos_weights.colRange(0, pos); + + int pos = 0; + for (int j = 0; j < data_neg.cols; ++j) { + if (neg_trace(0, j) > cascade_threshold - 0.5) { + neg_trace(0, pos) = neg_trace(0, j); + data_neg.col(j).copyTo(data_neg.col(pos)); + neg_weights(0, pos) = neg_weights(0, j); + pos += 1; + } + } + std::cerr << "neg " << data_neg.cols << "/" << pos << std::endl; + neg_trace = neg_trace.colRange(0, pos); + data_neg = data_neg.colRange(0, pos); + neg_weights = neg_weights.colRange(0, pos); + + if (loss < 1e-50 || min_err > 0.5) { std::cerr << "Stopping early" << std::endl; weak_count_ = i + 1; @@ -313,72 +347,86 @@ void WaldBoost::fit(Mat& data_pos, Mat& data_neg) int WaldBoost::predict(Ptr eval, float *h) const { - double THR = -6; assert(feature_indices_.size() == size_t(weak_count_)); + assert(cascade_thresholds_.size() == size_t(weak_count_)); float res = 0; - for (int i = 0; i < weak_count_; ++i) { + int count = weak_count_; + for (int i = 0; i < count; ++i) { float val = (*eval)(feature_indices_[i]); int label = polarities_[i] * (val - thresholds_[i]) > 0 ? +1: -1; res += alphas_[i] * label; - if (res < THR) { + if (res < cascade_thresholds_[i]) { return -1; } } *h = res; - return res > THR ? +1 : -1; + return res > cascade_thresholds_[count - 1] ? +1 : -1; } -void WaldBoost::save(const std::string& filename) +void WaldBoost::write(FileStorage &fs) const { - std::ofstream f(filename.c_str()); - f << weak_count_ << std::endl; - for (size_t i = 0; i < thresholds_.size(); ++i) { - f << thresholds_[i] << " "; - } - f << std::endl; - for (size_t i = 0; i < alphas_.size(); ++i) { - f << alphas_[i] << " "; - } - f << std::endl; - for (size_t i = 0; i < polarities_.size(); ++i) { - f << polarities_[i] << " "; - } - f << std::endl; - for (size_t i = 0; i < cascade_thresholds_.size(); ++i) { - f << cascade_thresholds_[i] << " "; - } - f << std::endl; - for (size_t i = 0; i < feature_indices_.size(); ++i) { - f << feature_indices_[i] << " "; - } - f << std::endl; + fs << "waldboost" << "{"; + fs << "waldboost_params" + << "{" << "weak_count" << weak_count_ << "}"; + + fs << "thresholds" << "["; + for (size_t i = 0; i < thresholds_.size(); ++i) + fs << thresholds_[i]; + fs << "]"; + + fs << "alphas" << "["; + for (size_t i = 0; i < alphas_.size(); ++i) + fs << alphas_[i]; + fs << "]"; + + fs << "polarities" << "["; + for (size_t i = 0; i < polarities_.size(); ++i) + fs << polarities_[i]; + fs << "]"; + + fs << "cascade_thresholds" << "["; + for (size_t i = 0; i < cascade_thresholds_.size(); ++i) + fs << cascade_thresholds_[i]; + fs << "]"; + + fs << "feature_indices" << "["; + for (size_t i = 0; i < feature_indices_.size(); ++i) + fs << feature_indices_[i]; + fs << "]"; + + fs << "}"; } -void WaldBoost::load(const std::string& filename) +void WaldBoost::read(const FileNode &node) { - std::ifstream f(filename.c_str()); - f >> weak_count_; + weak_count_ = (int)(node["waldboost_params"]["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]; - } + FileNodeIterator n; + + n = node["thresholds"].begin(); + for (int i = 0; i < weak_count_; ++i, ++n) + *n >> thresholds_[i]; + + n = node["alphas"].begin(); + for (int i = 0; i < weak_count_; ++i, ++n) + *n >> alphas_[i]; + + n = node["polarities"].begin(); + for (int i = 0; i < weak_count_; ++i, ++n) + *n >> polarities_[i]; + + n = node["cascade_thresholds"].begin(); + for (int i = 0; i < weak_count_; ++i, ++n) + *n >> cascade_thresholds_[i]; + + n = node["feature_indices"].begin(); + for (int i = 0; i < weak_count_; ++i, ++n) + *n >> feature_indices_[i]; } void WaldBoost::reset(int weak_count) diff --git a/modules/xobjdetect/src/waldboost.hpp b/modules/xobjdetect/src/waldboost.hpp index 13b2a1883..9247619e4 100644 --- a/modules/xobjdetect/src/waldboost.hpp +++ b/modules/xobjdetect/src/waldboost.hpp @@ -72,6 +72,10 @@ public: int predict(Ptr eval, float *h) const; void save(const std::string& filename); void load(const std::string& filename); + + void read(const FileNode &node); + void write(FileStorage &fs) const; + void reset(int weak_count); ~WaldBoost(); diff --git a/modules/xobjdetect/src/detector.cpp b/modules/xobjdetect/src/wbdetector.cpp similarity index 89% rename from modules/xobjdetect/src/detector.cpp rename to modules/xobjdetect/src/wbdetector.cpp index 7fe540f45..0c0359616 100644 --- a/modules/xobjdetect/src/detector.cpp +++ b/modules/xobjdetect/src/wbdetector.cpp @@ -52,10 +52,6 @@ using std::string; namespace cv { namespace xobjdetect { -WBDetector::WBDetector(const string& model_filename): - model_filename_(model_filename) -{} - static vector sample_patches( const string& path, int n_rows, @@ -93,13 +89,24 @@ static vector read_imgs(const string& path) return imgs; } -void WBDetector::train( +void WBDetectorImpl::read(const FileNode& node) +{ + boost_.read(node); +} + + +void WBDetectorImpl::write(FileStorage &fs) const +{ + boost_.write(fs); +} + +void WBDetectorImpl::train( const string& pos_samples_path, const string& neg_imgs_path) { vector pos_imgs = read_imgs(pos_samples_path); - vector neg_imgs = sample_patches(neg_imgs_path, 24, 24, pos_imgs.size()); + vector neg_imgs = sample_patches(neg_imgs_path, 24, 24, pos_imgs.size() * 10); assert(pos_imgs.size()); assert(neg_imgs.size()); @@ -111,16 +118,15 @@ void WBDetector::train( eval->init(CvFeatureParams::create(), 1, Size(24, 24)); n_features = eval->getNumFeatures(); - const int stages[] = {32, 64, 128, 256, 512, 1024, 2048, 4096}; + const int stages[] = {64, 128, 256, 512, 1024}; const int stage_count = sizeof(stages) / sizeof(*stages); - const int stage_neg = 5000; - const int max_per_image = 25; + const int stage_neg = pos_imgs.size() * 5; + const int max_per_image = 100; const float scales_arr[] = {.3f, .4f, .5f, .6f, .7f, .8f, .9f, 1.0f}; const vector scales(scales_arr, scales_arr + sizeof(scales_arr) / sizeof(*scales_arr)); - WaldBoost boost; vector neg_filenames; glob(neg_imgs_path, neg_filenames); @@ -133,22 +139,22 @@ void WBDetector::train( neg_data = Mat1b(n_features, (int)neg_imgs.size()); for (size_t k = 0; k < pos_imgs.size(); ++k) { - eval->setImage(pos_imgs[k], +1, 0, boost.get_feature_indices()); + eval->setImage(pos_imgs[k], +1, 0, boost_.get_feature_indices()); for (int j = 0; j < n_features; ++j) { pos_data.at(j, (int)k) = (uchar)(*eval)(j); } } for (size_t k = 0; k < neg_imgs.size(); ++k) { - eval->setImage(neg_imgs[k], 0, 0, boost.get_feature_indices()); + eval->setImage(neg_imgs[k], 0, 0, boost_.get_feature_indices()); for (int j = 0; j < n_features; ++j) { neg_data.at(j, (int)k) = (uchar)(*eval)(j); } } - boost.reset(stages[i]); - boost.fit(pos_data, neg_data); + boost_.reset(stages[i]); + boost_.fit(pos_data, neg_data); if (i + 1 == stage_count) { break; @@ -162,7 +168,7 @@ void WBDetector::train( Mat img = imread(neg_filenames[img_i], CV_LOAD_IMAGE_GRAYSCALE); vector bboxes; Mat1f confidences; - boost.detect(eval, img, scales, bboxes, confidences); + boost_.detect(eval, img, scales, bboxes, confidences); if (confidences.rows > 0) { Mat1i indices; @@ -185,18 +191,13 @@ void WBDetector::train( cerr << "bootstrapped " << bootstrap_count << " windows from " << (img_i + 1) << " images" << endl; } - - boost.save(model_filename_); } -void WBDetector::detect( +void WBDetectorImpl::detect( const Mat& img, vector &bboxes, vector &confidences) { - WaldBoost boost; - boost.load(model_filename_); - Mat test_img = img.clone(); bboxes.clear(); confidences.clear(); @@ -207,9 +208,14 @@ void WBDetector::detect( Ptr params = CvFeatureParams::create(); Ptr eval = CvFeatureEvaluator::create(); eval->init(params, 1, Size(24, 24)); - boost.detect(eval, img, scales, bboxes, confidences); + boost_.detect(eval, img, scales, bboxes, confidences); assert(confidences.size() == bboxes.size()); } +Ptr create_wbdetector() +{ + return Ptr(new WBDetectorImpl()); +} + } } diff --git a/modules/xobjdetect/src/wbdetector.hpp b/modules/xobjdetect/src/wbdetector.hpp new file mode 100644 index 000000000..86247ea0f --- /dev/null +++ b/modules/xobjdetect/src/wbdetector.hpp @@ -0,0 +1,77 @@ +/* + +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 + (3-clause BSD License) + +Copyright (C) 2013, OpenCV Foundation, 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions 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. + + * Neither the names of the copyright holders nor the names of the contributors + may 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 copyright holders 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. + +*/ + +#ifndef __OPENCV_XOBJDETECT_DETECTOR_HPP__ +#define __OPENCV_XOBJDETECT_DETECTOR_HPP__ + +#include +#include +#include +#include +#include "precomp.hpp" + +namespace cv +{ +namespace xobjdetect +{ + +class WBDetectorImpl : public WBDetector { +public: + virtual void read(const FileNode &node); + virtual void write(FileStorage &fs) const; + + virtual void train( + const std::string& pos_samples, + const std::string& neg_imgs); + + virtual void detect( + const Mat& img, + std::vector &bboxes, + std::vector &confidences); + +private: + WaldBoost boost_; +}; + +} /* namespace xobjdetect */ +} /* namespace cv */ + +#endif /* __OPENCV_XOBJDETECT_DETECTOR_HPP__ */ diff --git a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp index cde6c2475..51f9cfd5b 100644 --- a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp +++ b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp @@ -2,6 +2,7 @@ #include #include #include +#include using namespace std; using namespace cv; using namespace cv::xobjdetect; @@ -10,20 +11,32 @@ int main(int argc, char **argv) { if (argc < 5) { cerr << "Usage: " << argv[0] << " train " << endl; - cerr << " " << argv[0] << " detect " << endl; + cerr << " " << argv[0] << " detect " << endl; return 0; } string mode = argv[1]; - WBDetector detector(argv[2]); + Ptr detector = create_wbdetector(); if (mode == "train") { - detector.train(argv[3], argv[4]); + assert(argc == 5); + detector->train(argv[3], argv[4]); + FileStorage fs(argv[2], FileStorage::WRITE); + detector->write(fs); } else if (mode == "detect") { - cerr << "detect" << endl; + assert(argc == 6); vector bboxes; vector confidences; Mat img = imread(argv[3], CV_LOAD_IMAGE_GRAYSCALE); - detector.detect(img, bboxes, confidences); + FileStorage fs(argv[2], FileStorage::READ); + detector->read(fs["waldboost"]); + detector->detect(img, bboxes, confidences); + + FILE *fhandle = fopen(argv[5], "a"); + for (size_t i = 0; i < bboxes.size(); ++i) { + Rect o = bboxes[i]; + fprintf(fhandle, "%s;%u;%u;%u;%u;%lf\n", + argv[3], o.x, o.y, o.width, o.height, confidences[i]); + } for (size_t i = 0; i < bboxes.size(); ++i) { rectangle(img, bboxes[i], Scalar(255, 0, 0)); } From a546ba96903b49b4e76dfb90aa5c6b100358f495 Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Mon, 24 Aug 2015 02:16:08 +0300 Subject: [PATCH 8/9] Fix warnings --- modules/xobjdetect/src/waldboost.cpp | 2 +- modules/xobjdetect/src/wbdetector.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 41ac99020..1e97a5bec 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -292,7 +292,7 @@ void WaldBoost::fit(Mat& data_pos, Mat& data_neg) } double cascade_threshold = -1; minMaxIdx(pos_trace, &cascade_threshold); - cascade_thresholds_.push_back(cascade_threshold); + cascade_thresholds_.push_back((float)cascade_threshold); std::cerr << "i=" << std::setw(4) << i; std::cerr << " feat=" << std::setw(5) << min_feature_ind; diff --git a/modules/xobjdetect/src/wbdetector.cpp b/modules/xobjdetect/src/wbdetector.cpp index 0c0359616..82122e4b7 100644 --- a/modules/xobjdetect/src/wbdetector.cpp +++ b/modules/xobjdetect/src/wbdetector.cpp @@ -120,7 +120,7 @@ void WBDetectorImpl::train( const int stages[] = {64, 128, 256, 512, 1024}; const int stage_count = sizeof(stages) / sizeof(*stages); - const int stage_neg = pos_imgs.size() * 5; + const int stage_neg = (int)(pos_imgs.size() * 5); const int max_per_image = 100; const float scales_arr[] = {.3f, .4f, .5f, .6f, .7f, .8f, .9f, 1.0f}; From ab72b24a007027f7a96092ed1b72e1d8b6cbd33c Mon Sep 17 00:00:00 2001 From: Vlad Shakhuro Date: Wed, 26 Aug 2015 10:30:18 +0300 Subject: [PATCH 9/9] Fix interface, add documentation --- .../xobjdetect/include/opencv2/xobjdetect.hpp | 28 ++++++++++++++++++- modules/xobjdetect/src/waldboost.cpp | 2 +- modules/xobjdetect/src/wbdetector.cpp | 3 +- .../waldboost_detector/waldboost_detector.cpp | 5 ++-- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/modules/xobjdetect/include/opencv2/xobjdetect.hpp b/modules/xobjdetect/include/opencv2/xobjdetect.hpp index da4ecf451..45038ab24 100644 --- a/modules/xobjdetect/include/opencv2/xobjdetect.hpp +++ b/modules/xobjdetect/include/opencv2/xobjdetect.hpp @@ -53,25 +53,51 @@ namespace cv { namespace xobjdetect { +//! @addtogroup xobjdetect +//! @{ + +/** @brief WaldBoost detector +*/ class CV_EXPORTS WBDetector { public: + /** @brief Read detector from FileNode. + @param node FileNode for input + */ virtual void read(const FileNode &node) = 0; + + /** @brief Write detector to FileStorage. + @param fs FileStorage for output + */ virtual void write(FileStorage &fs) const = 0; + /** @brief Train WaldBoost detector + @param pos_samples Path to directory with cropped positive samples + @param neg_imgs Path to directory with negative (background) images + */ virtual void train( const std::string& pos_samples, const std::string& neg_imgs) = 0; + /** @brief Detect objects on image using WaldBoost detector + @param img Input image for detection + @param bboxes Bounding boxes coordinates output vector + @param confidences Confidence values for bounding boxes output vector + */ virtual void detect( const Mat& img, std::vector &bboxes, std::vector &confidences) = 0; + /** @brief Create instance of WBDetector + */ + static Ptr create(); + virtual ~WBDetector(){} }; -CV_EXPORTS Ptr create_wbdetector(); + +//! @} } /* namespace xobjdetect */ } /* namespace cv */ diff --git a/modules/xobjdetect/src/waldboost.cpp b/modules/xobjdetect/src/waldboost.cpp index 1e97a5bec..8c1394621 100644 --- a/modules/xobjdetect/src/waldboost.cpp +++ b/modules/xobjdetect/src/waldboost.cpp @@ -365,7 +365,7 @@ int WaldBoost::predict(Ptr eval, float *h) const void WaldBoost::write(FileStorage &fs) const { - fs << "waldboost" << "{"; + fs << "{"; fs << "waldboost_params" << "{" << "weak_count" << weak_count_ << "}"; diff --git a/modules/xobjdetect/src/wbdetector.cpp b/modules/xobjdetect/src/wbdetector.cpp index 82122e4b7..395ea1e48 100644 --- a/modules/xobjdetect/src/wbdetector.cpp +++ b/modules/xobjdetect/src/wbdetector.cpp @@ -212,7 +212,8 @@ void WBDetectorImpl::detect( assert(confidences.size() == bboxes.size()); } -Ptr create_wbdetector() +Ptr +WBDetector::create() { return Ptr(new WBDetectorImpl()); } diff --git a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp index 51f9cfd5b..490cdd585 100644 --- a/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp +++ b/modules/xobjdetect/tools/waldboost_detector/waldboost_detector.cpp @@ -16,11 +16,12 @@ int main(int argc, char **argv) } string mode = argv[1]; - Ptr detector = create_wbdetector(); + Ptr detector = WBDetector::create(); if (mode == "train") { assert(argc == 5); detector->train(argv[3], argv[4]); FileStorage fs(argv[2], FileStorage::WRITE); + fs << "waldboost"; detector->write(fs); } else if (mode == "detect") { assert(argc == 6); @@ -28,7 +29,7 @@ int main(int argc, char **argv) vector confidences; Mat img = imread(argv[3], CV_LOAD_IMAGE_GRAYSCALE); FileStorage fs(argv[2], FileStorage::READ); - detector->read(fs["waldboost"]); + detector->read(fs.getFirstTopLevelNode()); detector->detect(img, bboxes, confidences); FILE *fhandle = fopen(argv[5], "a");