diff --git a/apps/sft/config.cpp b/apps/sft/config.cpp index 81b433f043..3cc64c7fec 100644 --- a/apps/sft/config.cpp +++ b/apps/sft/config.cpp @@ -69,6 +69,7 @@ void sft::Config::write(cv::FileStorage& fs) const << "outXmlPath" << outXmlPath << "seed" << seed + << "featureType" << featureType << "}"; } @@ -99,6 +100,7 @@ void sft::Config::read(const cv::FileNode& node) outXmlPath = (std::string)node["outXmlPath"]; seed = (int)node["seed"]; + featureType = (std::string)node["featureType"]; } void sft::write(cv::FileStorage& fs, const string&, const Config& x) @@ -153,7 +155,8 @@ std::ostream& sft::operator<<(std::ostream& out, const Config& m) << std::setw(14) << std::left << "cascadeName" << m.cascadeName << std::endl << std::setw(14) << std::left << "outXmlPath" << m.outXmlPath << std::endl - << std::setw(14) << std::left << "seed" << m.seed << std::endl; + << std::setw(14) << std::left << "seed" << m.seed << std::endl + << std::setw(14) << std::left << "featureType" << m.featureType << std::endl; return out; } \ No newline at end of file diff --git a/apps/sft/include/sft/config.hpp b/apps/sft/include/sft/config.hpp index 6bdb861bbf..3d39d32722 100644 --- a/apps/sft/include/sft/config.hpp +++ b/apps/sft/include/sft/config.hpp @@ -121,6 +121,9 @@ struct Config // seed for random generation int seed; + // channel feature type + string featureType; + // // bounding rectangle for actual example into example window // cv::Rect exampleWindow; }; diff --git a/apps/sft/sft.cpp b/apps/sft/sft.cpp index 5981c96773..7b51387638 100644 --- a/apps/sft/sft.cpp +++ b/apps/sft/sft.cpp @@ -104,7 +104,7 @@ int main(int argc, char** argv) fso << cfg.cascadeName << "{" << "stageType" << "BOOST" - << "featureType" << "ICF" + << "featureType" << cfg.featureType << "octavesNum" << (int)cfg.octaves.size() << "width" << cfg.modelWinSize.width << "height" << cfg.modelWinSize.height @@ -118,7 +118,12 @@ int main(int argc, char** argv) int nfeatures = cfg.poolSize; cv::Size model = cfg.model(it); std::cout << "Model " << model << std::endl; - cv::Ptr pool = cv::FeaturePool::create(model, nfeatures); + + int nchannels = (cfg.featureType == "HOG6MagLuv") ? 10: 8; + + std::cout << "number of feature channels is " << nchannels << std::endl; + + cv::Ptr pool = cv::FeaturePool::create(model, nfeatures, nchannels); nfeatures = pool->size(); @@ -130,7 +135,9 @@ int main(int argc, char** argv) typedef cv::Octave Octave; - cv::Ptr boost = Octave::create(boundingBox, npositives, nnegatives, *it, shrinkage, nfeatures); + cv::Ptr builder = cv::ChannelFeatureBuilder::create(cfg.featureType); + std::cout << "Channel builder " << builder->info()->name() << std::endl; + cv::Ptr boost = Octave::create(boundingBox, npositives, nnegatives, *it, shrinkage, builder); std::string path = cfg.trainPath; sft::ScaledDataset dataset(path, *it); diff --git a/modules/softcascade/include/opencv2/softcascade/softcascade.hpp b/modules/softcascade/include/opencv2/softcascade/softcascade.hpp index 6e405af077..945b61f01a 100644 --- a/modules/softcascade/include/opencv2/softcascade/softcascade.hpp +++ b/modules/softcascade/include/opencv2/softcascade/softcascade.hpp @@ -87,7 +87,7 @@ public: virtual void write( cv::FileStorage& fs, int index) const = 0; virtual ~FeaturePool(); - static cv::Ptr create(const cv::Size& model, int nfeatures); + static cv::Ptr create(const cv::Size& model, int nfeatures, int nchannels ); }; // ========================================================================== // @@ -128,7 +128,10 @@ public: // apply channels to source frame CV_WRAP_AS(compute) virtual void operator()(InputArray src, CV_OUT OutputArray channels, cv::Size channelsSize = cv::Size()) const = 0; - CV_WRAP static cv::Ptr create(); + CV_WRAP virtual int totalChannels() const = 0; + virtual cv::AlgorithmInfo* info() const = 0; + + CV_WRAP static cv::Ptr create(const std::string& featureType); }; // ========================================================================== // @@ -199,7 +202,7 @@ public: virtual ~Octave(); static cv::Ptr create(cv::Rect boundingBox, int npositives, int nnegatives, - int logScale, int shrinkage, int poolSize); + int logScale, int shrinkage, cv::Ptr builder); virtual bool train(const Dataset* dataset, const FeaturePool* pool, int weaks, int treeDepth) = 0; virtual void setRejectThresholds(OutputArray thresholds) = 0; diff --git a/modules/softcascade/misc/scale_caltech.py b/modules/softcascade/misc/scale_caltech.py index 959a1f3ada..29d9707c86 100755 --- a/modules/softcascade/misc/scale_caltech.py +++ b/modules/softcascade/misc/scale_caltech.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -import sys, os, os.path, glob, math, cv2 +import sys, os, os.path, glob, math, cv2, sft from datetime import datetime from optparse import OptionParser import re diff --git a/modules/softcascade/src/integral_channel_builder.cpp b/modules/softcascade/src/integral_channel_builder.cpp index 38779797f1..6959b6e7a9 100644 --- a/modules/softcascade/src/integral_channel_builder.cpp +++ b/modules/softcascade/src/integral_channel_builder.cpp @@ -46,11 +46,15 @@ namespace { using namespace cv::softcascade; -class ICFBuilder : public ChannelFeatureBuilder +class HOG6MagLuv : public ChannelFeatureBuilder { - virtual ~ICFBuilder() {} + enum {N_CHANNELS = 10}; +public: + virtual ~HOG6MagLuv() {} virtual cv::AlgorithmInfo* info() const; + virtual int totalChannels() const {return N_CHANNELS; } + virtual void operator()(cv::InputArray _frame, CV_OUT cv::OutputArray _integrals, cv::Size channelsSize) const { CV_Assert(_frame.type() == CV_8UC3); @@ -60,16 +64,16 @@ class ICFBuilder : public ChannelFeatureBuilder int w = frame.cols; if (channelsSize != cv::Size()) - _integrals.create(channelsSize.height * 10 + 1, channelsSize.width + 1, CV_32SC1); + _integrals.create(channelsSize.height * N_CHANNELS + 1, channelsSize.width + 1, CV_32SC1); if(_integrals.empty()) - _integrals.create(frame.rows * 10 + 1, frame.cols + 1, CV_32SC1); + _integrals.create(frame.rows * N_CHANNELS + 1, frame.cols + 1, CV_32SC1); cv::Mat& integrals = _integrals.getMatRef(); cv::Mat channels, gray; - channels.create(h * 10, w, CV_8UC1); + channels.create(h * N_CHANNELS, w, CV_8UC1); channels.setTo(0); cvtColor(frame, gray, CV_BGR2GRAY); @@ -81,7 +85,7 @@ class ICFBuilder : public ChannelFeatureBuilder cv::cartToPolar(df_dx, df_dy, mag, angle, true); mag *= (1.f / (8 * sqrt(2.f))); - cv::Mat nmag; + cv::Mat nmag = channels(cv::Rect(0, h * (N_CHANNELS - 4), w, h)); mag.convertTo(nmag, CV_8UC1); angle *= 6 / 360.f; @@ -114,14 +118,13 @@ class ICFBuilder : public ChannelFeatureBuilder using cv::softcascade::ChannelFeatureBuilder; using cv::softcascade::ChannelFeature; -CV_INIT_ALGORITHM(ICFBuilder, "ChannelFeatureBuilder.ICFBuilder", ); +CV_INIT_ALGORITHM(HOG6MagLuv, "ChannelFeatureBuilder.HOG6MagLuv", ); ChannelFeatureBuilder::~ChannelFeatureBuilder() {} -cv::Ptr ChannelFeatureBuilder::create() +cv::Ptr ChannelFeatureBuilder::create(const std::string& featureType) { - cv::Ptr builder(new ICFBuilder()); - return builder; + return Algorithm::create("ChannelFeatureBuilder." + featureType); } ChannelFeature::ChannelFeature(int x, int y, int w, int h, int ch) @@ -175,9 +178,9 @@ using namespace cv::softcascade; class ChannelFeaturePool : public FeaturePool { public: - ChannelFeaturePool(cv::Size m, int n) : FeaturePool(), model(m) + ChannelFeaturePool(cv::Size m, int n, int ch) : FeaturePool(), model(m), N_CHANNELS(ch) { - CV_Assert(m != cv::Size() && n > 0); + CV_Assert(m != cv::Size() && n > 0 && (ch == 10 || ch == 8)); fill(n); } @@ -193,7 +196,7 @@ private: cv::Size model; std::vector pool; - enum { N_CHANNELS = 10 }; + int N_CHANNELS; }; float ChannelFeaturePool::apply(int fi, int si, const cv::Mat& integrals) const @@ -203,7 +206,8 @@ float ChannelFeaturePool::apply(int fi, int si, const cv::Mat& integrals) const void ChannelFeaturePool::write( cv::FileStorage& fs, int index) const { - CV_Assert((index > 0) && (index < (int)pool.size())); + + CV_Assert((index >= 0) && (index < (int)pool.size())); fs << pool[index]; } @@ -240,12 +244,12 @@ void ChannelFeaturePool::fill(int desired) // the old behavior: // http://www.boost.org/doc/libs/1_47_0/boost/random/uniform_int.hpp int w = 1 + wRand( - eng, + eng, // This extra "- 1" appears to be necessary, based on the Boost docs. - Random::uniform::param_type(0, (model.width - x - 1) - 1)); + Random::uniform::param_type(0, (model.width - x - 1) - 1)); int h = 1 + hRand( - eng, - Random::uniform::param_type(0, (model.height - y - 1) - 1)); + eng, + Random::uniform::param_type(0, (model.height - y - 1) - 1)); #else int w = 1 + wRand(eng, model.width - x - 1); int h = 1 + hRand(eng, model.height - y - 1); @@ -270,8 +274,8 @@ void ChannelFeaturePool::fill(int desired) } -cv::Ptr FeaturePool::create(const cv::Size& model, int nfeatures) +cv::Ptr FeaturePool::create(const cv::Size& model, int nfeatures, int nchannels ) { - cv::Ptr pool(new ChannelFeaturePool(model, nfeatures)); + cv::Ptr pool(new ChannelFeaturePool(model, nfeatures, nchannels)); return pool; } diff --git a/modules/softcascade/src/soft_cascade_octave.cpp b/modules/softcascade/src/soft_cascade_octave.cpp index f7b3b3fb04..5e37a29644 100644 --- a/modules/softcascade/src/soft_cascade_octave.cpp +++ b/modules/softcascade/src/soft_cascade_octave.cpp @@ -63,7 +63,7 @@ class BoostedSoftCascadeOctave : public cv::Boost, public Octave public: BoostedSoftCascadeOctave(cv::Rect boundingBox = cv::Rect(), int npositives = 0, int nnegatives = 0, int logScale = 0, - int shrinkage = 1, int poolSize = 0); + int shrinkage = 1, cv::Ptr builder = ChannelFeatureBuilder::create("HOG6MagLuv")); virtual ~BoostedSoftCascadeOctave(); virtual cv::AlgorithmInfo* info() const; virtual bool train(const Dataset* dataset, const FeaturePool* pool, int weaks, int treeDepth); @@ -101,7 +101,8 @@ private: cv::Ptr builder; }; -BoostedSoftCascadeOctave::BoostedSoftCascadeOctave(cv::Rect bb, int np, int nn, int ls, int shr, int poolSize) +BoostedSoftCascadeOctave::BoostedSoftCascadeOctave(cv::Rect bb, int np, int nn, int ls, int shr, + cv::Ptr _builder) : logScale(ls), boundingBox(bb), npositives(np), nnegatives(nn), shrinkage(shr) { int maxSample = npositives + nnegatives; @@ -130,12 +131,12 @@ BoostedSoftCascadeOctave::BoostedSoftCascadeOctave(cv::Rect bb, int np, int nn, params = _params; - builder = ChannelFeatureBuilder::create(); + builder = _builder; int w = boundingBox.width; int h = boundingBox.height; - integrals.create(poolSize, (w / shrinkage + 1) * (h / shrinkage * 10 + 1), CV_32SC1); + integrals.create(npositives + nnegatives, (w / shrinkage + 1) * (h / shrinkage * builder->totalChannels() + 1), CV_32SC1); } BoostedSoftCascadeOctave::~BoostedSoftCascadeOctave(){} @@ -204,7 +205,7 @@ void BoostedSoftCascadeOctave::processPositives(const Dataset* dataset) { cv::Mat sample = dataset->get( Dataset::POSITIVE, curr); - cv::Mat channels = integrals.row(total).reshape(0, h / shrinkage * 10 + 1); + cv::Mat channels = integrals.row(total).reshape(0, h / shrinkage * builder->totalChannels() + 1); sample = sample(boundingBox); _builder(sample, channels); @@ -249,7 +250,7 @@ void BoostedSoftCascadeOctave::generateNegatives(const Dataset* dataset) frame = frame(cv::Rect(dx, dy, boundingBox.width, boundingBox.height)); - cv::Mat channels = integrals.row(i).reshape(0, h / shrinkage * 10 + 1); + cv::Mat channels = integrals.row(i).reshape(0, h / shrinkage * builder->totalChannels() + 1); _builder(frame, channels); // // if (predict(sum)) @@ -442,14 +443,14 @@ void BoostedSoftCascadeOctave::write( CvFileStorage* fs, std::string _name) cons } -CV_INIT_ALGORITHM(BoostedSoftCascadeOctave, "SoftCascadeOctave.BoostedSoftCascadeOctave", ); +CV_INIT_ALGORITHM(BoostedSoftCascadeOctave, "Octave.BoostedSoftCascadeOctave", ); Octave::~Octave(){} cv::Ptr Octave::create(cv::Rect boundingBox, int npositives, int nnegatives, - int logScale, int shrinkage, int poolSize) + int logScale, int shrinkage, cv::Ptr builder) { cv::Ptr octave( - new BoostedSoftCascadeOctave(boundingBox, npositives, nnegatives, logScale, shrinkage, poolSize)); + new BoostedSoftCascadeOctave(boundingBox, npositives, nnegatives, logScale, shrinkage, builder)); return octave; } diff --git a/modules/softcascade/src/softcascade.cpp b/modules/softcascade/src/softcascade.cpp index 465deb489a..43772449ac 100644 --- a/modules/softcascade/src/softcascade.cpp +++ b/modules/softcascade/src/softcascade.cpp @@ -187,11 +187,12 @@ struct ChannelStorage enum {HOG_BINS = 6, HOG_LUV_BINS = 10}; - ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) + ChannelStorage(const cv::Mat& colored, int shr, std::string featureTypeStr) : shrinkage(shr) { model_height = cvRound(colored.rows / (float)shrinkage); + if (featureTypeStr == "ICF") featureTypeStr = "HOG6MagLuv"; - builder = ChannelFeatureBuilder::create(); + builder = ChannelFeatureBuilder::create(featureTypeStr); (*builder)(colored, hog, cv::Size(cvRound(colored.cols / (float)shrinkage), model_height)); step = hog.step1(); @@ -201,8 +202,8 @@ struct ChannelStorage { const int *ptr = hog.ptr(0) + model_height * channel * step + offset; - int a = ptr[area.y * step + area.x]; - int b = ptr[area.y * step + area.width]; + int a = ptr[area.y * step + area.x]; + int b = ptr[area.y * step + area.width]; int c = ptr[area.height * step + area.width]; int d = ptr[area.height * step + area.x]; @@ -224,7 +225,7 @@ struct Detector::Fields int shrinkage; - std::vector octaves; + std::vector octaves; std::vector weaks; std::vector nodes; std::vector leaves; @@ -237,6 +238,8 @@ struct Detector::Fields typedef std::vector::iterator octIt_t; typedef std::vector dvector; + std::string featureTypeStr; + void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage, dvector& detections) const { float detectionScore = 0.f; @@ -341,6 +344,7 @@ struct Detector::Fields static const char *const SC_BOOST = "BOOST"; static const char *const SC_FEATURE_TYPE = "featureType"; + static const char *const SC_HOG6_MAG_LUV = "HOG6MagLuv"; static const char *const SC_ICF = "ICF"; static const char *const SC_ORIG_W = "width"; @@ -365,8 +369,8 @@ struct Detector::Fields bool useBoxes = (fformat == "BOX"); // only HOG-like integral channel features supported - std::string featureTypeStr = (std::string)root[SC_FEATURE_TYPE]; - CV_Assert(featureTypeStr == SC_ICF); + featureTypeStr = (std::string)root[SC_FEATURE_TYPE]; + CV_Assert(featureTypeStr == SC_ICF || featureTypeStr == SC_HOG6_MAG_LUV); origObjWidth = (int)root[SC_ORIG_W]; origObjHeight = (int)root[SC_ORIG_H]; @@ -491,7 +495,7 @@ void Detector::detectNoRoi(const cv::Mat& image, std::vector& objects { Fields& fld = *fields; // create integrals - ChannelStorage storage(image, fld.shrinkage); + ChannelStorage storage(image, fld.shrinkage, fld.featureTypeStr); typedef std::vector::const_iterator lIt; for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) @@ -539,7 +543,7 @@ void Detector::detect(cv::InputArray _image, cv::InputArray _rois, std::vector::const_iterator lIt; for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) diff --git a/modules/softcascade/test/test_channel_features.cpp b/modules/softcascade/test/test_channel_features.cpp index 0a8cbf43ba..07b8957a50 100644 --- a/modules/softcascade/test/test_channel_features.cpp +++ b/modules/softcascade/test/test_channel_features.cpp @@ -46,13 +46,13 @@ using namespace cv::softcascade; TEST(ChannelFeatureBuilderTest, info) { - cv::Ptr builder = ChannelFeatureBuilder::create(); + cv::Ptr builder = ChannelFeatureBuilder::create("HOG6MagLuv"); ASSERT_TRUE(builder->info() != 0); } TEST(ChannelFeatureBuilderTest, compute) { - cv::Ptr builder = ChannelFeatureBuilder::create(); + cv::Ptr builder = ChannelFeatureBuilder::create("HOG6MagLuv"); cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/images/image_00000000_0.png"); cv::Mat ints; diff --git a/modules/softcascade/test/test_training.cpp b/modules/softcascade/test/test_training.cpp index 301bab35c6..a03ab8abd5 100644 --- a/modules/softcascade/test/test_training.cpp +++ b/modules/softcascade/test/test_training.cpp @@ -212,7 +212,7 @@ TEST(DISABLED_SoftCascade, training) float octave = powf(2.f, (float)(*it)); cv::Size model = cv::Size( cvRound(64 * octave) / shrinkage, cvRound(128 * octave) / shrinkage ); - cv::Ptr pool = FeaturePool::create(model, nfeatures); + cv::Ptr pool = FeaturePool::create(model, nfeatures, 10); nfeatures = pool->size(); int npositives = 20; int nnegatives = 40; @@ -220,7 +220,8 @@ TEST(DISABLED_SoftCascade, training) cv::Rect boundingBox = cv::Rect( cvRound(20 * octave), cvRound(20 * octave), cvRound(64 * octave), cvRound(128 * octave)); - cv::Ptr boost = Octave::create(boundingBox, npositives, nnegatives, *it, shrinkage, nfeatures); + cv::Ptr builder = ChannelFeatureBuilder::create("HOG6MagLuv"); + cv::Ptr boost = Octave::create(boundingBox, npositives, nnegatives, *it, shrinkage, builder); std::string path = cvtest::TS::ptr()->get_data_path() + "softcascade/sample_training_set"; ScaledDataset dataset(path, *it);