From 14ac8a528e83e40bc34fcdd3fc99304e8eb545c5 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 30 Jan 2013 13:34:34 +0400 Subject: [PATCH] final refactoring and test for training --- apps/sft/CMakeLists.txt | 4 - apps/sft/config.cpp | 6 +- apps/sft/{fpool.cpp => dataset.cpp} | 22 +- apps/sft/include/sft/common.hpp | 3 +- apps/sft/include/sft/config.hpp | 4 +- .../include/sft/{fpool.hpp => dataset.hpp} | 4 - apps/sft/sft.cpp | 2 +- modules/ml/include/opencv2/ml/ml.hpp | 2 +- modules/ml/src/boost.cpp | 4 +- modules/softcascade/src/_random.hpp | 23 ++ .../src/integral_channel_builder.cpp | 23 -- modules/softcascade/src/precomp.hpp | 3 +- .../softcascade/src/soft_cascade_octave.cpp | 43 +-- modules/softcascade/test/test_training.cpp | 246 ++++++++++++++++++ 14 files changed, 287 insertions(+), 102 deletions(-) rename apps/sft/{fpool.cpp => dataset.cpp} (95%) rename apps/sft/include/sft/{fpool.hpp => dataset.hpp} (95%) create mode 100644 modules/softcascade/test/test_training.cpp diff --git a/apps/sft/CMakeLists.txt b/apps/sft/CMakeLists.txt index bc7c1ebed9..7197519039 100644 --- a/apps/sft/CMakeLists.txt +++ b/apps/sft/CMakeLists.txt @@ -1,7 +1,3 @@ -if(IOS OR ANDROID) - return() -endif() - set(name sft) set(the_target opencv_${name}) diff --git a/apps/sft/config.cpp b/apps/sft/config.cpp index d9937220e4..3fc113974c 100644 --- a/apps/sft/config.cpp +++ b/apps/sft/config.cpp @@ -108,9 +108,9 @@ void sft::write(cv::FileStorage& fs, const string&, const Config& x) void sft::read(const cv::FileNode& node, Config& x, const Config& default_value) { - if(node.empty()) - x = default_value; - else + x = default_value; + + if(!node.empty()) x.read(node); } diff --git a/apps/sft/fpool.cpp b/apps/sft/dataset.cpp similarity index 95% rename from apps/sft/fpool.cpp rename to apps/sft/dataset.cpp index 825d88d12f..52ec8ac840 100644 --- a/apps/sft/fpool.cpp +++ b/apps/sft/dataset.cpp @@ -40,27 +40,16 @@ // //M*/ -#include +#include +#include #include #include -// ============ Dataset ============ // -namespace { -using namespace sft; - -string itoa(long i) -{ - char s[65]; - sprintf(s, "%ld", i); - return std::string(s); -} - -} +inline std::string itoa(long i) { return cv::format("%ld", i); } #if !defined (_WIN32) && ! defined(__MINGW32__) - -#include +# include namespace { using namespace sft; @@ -84,7 +73,7 @@ void glob(const string& path, svector& ret) } #else -#include +# include namespace { using namespace sft; void glob(const string& refRoot, const string& refExt, svector &refvecFiles) @@ -138,7 +127,6 @@ void glob(const string& refRoot, const string& refExt, svector &refvecFiles) ScaledDataset::ScaledDataset(const string& path, const int oct) { dprintf("%s\n", "get dataset file names..."); - dprintf("%s\n", "Positives globing..."); #if !defined (_WIN32) && ! defined(__MINGW32__) diff --git a/apps/sft/include/sft/common.hpp b/apps/sft/include/sft/common.hpp index 4ddc0a3306..1371875ce6 100644 --- a/apps/sft/include/sft/common.hpp +++ b/apps/sft/include/sft/common.hpp @@ -44,6 +44,7 @@ #define __SFT_COMMON_HPP__ #include +#include namespace sft { @@ -58,7 +59,7 @@ namespace sft } // used for noisy printfs -#define WITH_DEBUG_OUT +//#define WITH_DEBUG_OUT #if defined WITH_DEBUG_OUT # include diff --git a/apps/sft/include/sft/config.hpp b/apps/sft/include/sft/config.hpp index 5be5ebd6df..6bdb861bbf 100644 --- a/apps/sft/include/sft/config.hpp +++ b/apps/sft/include/sft/config.hpp @@ -75,9 +75,7 @@ struct Config string resPath(ivector::const_iterator it) const { - char s[65]; - sprintf(s, "%d", *it); - return std::string(cascadeName) + s + ".xml"; + return cv::format("%s%d.xml",cascadeName.c_str(), *it); } // Paths to a rescaled data diff --git a/apps/sft/include/sft/fpool.hpp b/apps/sft/include/sft/dataset.hpp similarity index 95% rename from apps/sft/include/sft/fpool.hpp rename to apps/sft/include/sft/dataset.hpp index ad0f56d304..14cffa7975 100644 --- a/apps/sft/include/sft/fpool.hpp +++ b/apps/sft/include/sft/dataset.hpp @@ -44,10 +44,6 @@ #define __SFT_OCTAVE_HPP__ #include - -#include -#include -#include namespace sft { diff --git a/apps/sft/sft.cpp b/apps/sft/sft.cpp index 67c56fc6ab..f3c8481007 100644 --- a/apps/sft/sft.cpp +++ b/apps/sft/sft.cpp @@ -44,7 +44,7 @@ #include #include -#include +#include #include #include diff --git a/modules/ml/include/opencv2/ml/ml.hpp b/modules/ml/include/opencv2/ml/ml.hpp index 93123dc2e8..7bb553d5f1 100644 --- a/modules/ml/include/opencv2/ml/ml.hpp +++ b/modules/ml/include/opencv2/ml/ml.hpp @@ -1251,7 +1251,7 @@ protected: virtual void write_params( CvFileStorage* fs ) const; virtual void read_params( CvFileStorage* fs, CvFileNode* node ); - virtual void initial_weights(double (&p)[2]); + virtual void initialize_weights(double (&p)[2]); CvDTreeTrainData* data; CvBoostParams params; diff --git a/modules/ml/src/boost.cpp b/modules/ml/src/boost.cpp index 0dc0d28404..4b27dcfd2e 100644 --- a/modules/ml/src/boost.cpp +++ b/modules/ml/src/boost.cpp @@ -1115,7 +1115,7 @@ bool CvBoost::train( CvMLData* _data, return result; } -void CvBoost::initial_weights(double (&p)[2]) +void CvBoost::initialize_weights(double (&p)[2]) { p[0] = 1.; p[1] = 1.; @@ -1166,7 +1166,7 @@ CvBoost::update_weights( CvBoostTree* tree ) double w0 = 1./ n; double p[2] = { 1., 1. }; - initial_weights(p); + initialize_weights(p); cvReleaseMat( &orig_response ); cvReleaseMat( &sum_response ); diff --git a/modules/softcascade/src/_random.hpp b/modules/softcascade/src/_random.hpp index 3a3be8bbc7..b7a402d177 100644 --- a/modules/softcascade/src/_random.hpp +++ b/modules/softcascade/src/_random.hpp @@ -114,4 +114,27 @@ struct Random #endif +#if defined _WIN32 && (_WIN32 || _WIN64) +# if _WIN64 +# define USE_LONG_SEEDS +# endif +#endif +#if defined (__GNUC__) &&__GNUC__ +# if defined(__x86_64__) || defined(__ppc64__) +# define USE_LONG_SEEDS +# endif +#endif + +#if defined USE_LONG_SEEDS +# define FEATURE_RECT_SEED 8854342234LU +# define INDEX_ENGINE_SEED 764224349868LU +#else +# define FEATURE_RECT_SEED 88543422LU +# define INDEX_ENGINE_SEED 76422434LU +#endif +#undef USE_LONG_SEEDS + +#define DCHANNELS_SEED 314152314LU +#define DX_DY_SEED 65633343LU + #endif \ No newline at end of file diff --git a/modules/softcascade/src/integral_channel_builder.cpp b/modules/softcascade/src/integral_channel_builder.cpp index 539642a870..ad5bc7ec5b 100644 --- a/modules/softcascade/src/integral_channel_builder.cpp +++ b/modules/softcascade/src/integral_channel_builder.cpp @@ -41,7 +41,6 @@ //M*/ #include "precomp.hpp" -#include "_random.hpp" namespace { @@ -199,25 +198,6 @@ void ChannelFeaturePool::write( cv::FileStorage& fs, int index) const fs << pool[index]; } -#if defined _WIN32 && (_WIN32 || _WIN64) -# if _WIN64 -# define USE_LONG_SEEDS -# endif -#endif -#if defined (__GNUC__) &&__GNUC__ -# if defined(__x86_64__) || defined(__ppc64__) -# define USE_LONG_SEEDS -# endif -#endif - -#if defined USE_LONG_SEEDS -# define FEATURE_RECT_SEED 8854342234LU -#else -# define FEATURE_RECT_SEED 88543422LU -#endif -# define DCHANNELS_SEED 314152314LU -#undef USE_LONG_SEEDS - void ChannelFeaturePool::fill(int desired) { int mw = model.width; @@ -226,8 +206,6 @@ void ChannelFeaturePool::fill(int desired) int maxPoolSize = (mw -1) * mw / 2 * (mh - 1) * mh / 2 * N_CHANNELS; int nfeatures = std::min(desired, maxPoolSize); - // dprintf("Requeste feature pool %d max %d suggested %d\n", desired, maxPoolSize, nfeatures); - pool.reserve(nfeatures); sft::Random::engine eng(FEATURE_RECT_SEED); @@ -262,7 +240,6 @@ void ChannelFeaturePool::fill(int desired) if (std::find(pool.begin(), pool.end(),f) == pool.end()) { pool.push_back(f); - std::cout << f << std::endl; } } } diff --git a/modules/softcascade/src/precomp.hpp b/modules/softcascade/src/precomp.hpp index 2706319219..cb20a268e9 100644 --- a/modules/softcascade/src/precomp.hpp +++ b/modules/softcascade/src/precomp.hpp @@ -53,7 +53,6 @@ #include "opencv2/core/core_c.h" #include "opencv2/core/internal.hpp" #include "opencv2/ml/ml.hpp" - -#include "opencv2/opencv_modules.hpp" +#include "_random.hpp" #endif diff --git a/modules/softcascade/src/soft_cascade_octave.cpp b/modules/softcascade/src/soft_cascade_octave.cpp index 43473940aa..41e4a9b562 100644 --- a/modules/softcascade/src/soft_cascade_octave.cpp +++ b/modules/softcascade/src/soft_cascade_octave.cpp @@ -43,17 +43,6 @@ #include "precomp.hpp" #include #include -#include "_random.hpp" - -#define WITH_DEBUG_OUT - -#if defined WITH_DEBUG_OUT -# include -# define dprintf(format, ...) printf(format, ##__VA_ARGS__) -#else -# define dprintf(format, ...) -#endif - using cv::Dataset; using cv::FeaturePool; @@ -90,7 +79,7 @@ protected: float predict( const Mat& _sample, const cv::Range range) const; private: void traverse(const CvBoostTree* tree, cv::FileStorage& fs, int& nfeatures, int* used, const double* th) const; - virtual void initial_weights(double (&p)[2]); + virtual void initialize_weights(double (&p)[2]); int logScale; cv::Rect boundingBox; @@ -159,8 +148,6 @@ bool BoostedSoftCascadeOctave::train( const cv::Mat& _trainData, const cv::Mat& void BoostedSoftCascadeOctave::setRejectThresholds(cv::OutputArray _thresholds) { - dprintf("set thresholds according to DBP strategy\n"); - // labels decided by classifier cv::Mat desisions(responses.cols, responses.rows, responses.type()); float* dptr = desisions.ptr(0); @@ -223,33 +210,10 @@ void BoostedSoftCascadeOctave::processPositives(const Dataset* dataset) if (++total >= npositives) break; } - - dprintf("Processing positives finished:\n\trequested %d positives, collected %d samples.\n", npositives, total); - npositives = total; nnegatives = cvRound(nnegatives * total / (double)npositives); } -#if defined _WIN32 && (_WIN32 || _WIN64) -# if _WIN64 -# define USE_LONG_SEEDS -# endif -#endif -#if defined (__GNUC__) &&__GNUC__ -# if defined(__x86_64__) || defined(__ppc64__) -# define USE_LONG_SEEDS -# endif -#endif - -#if defined USE_LONG_SEEDS -# define INDEX_ENGINE_SEED 764224349868LU -#else -# define INDEX_ENGINE_SEED 76422434LU -#endif -# define DX_DY_SEED 65633343LU -#undef USE_LONG_SEEDS - - void BoostedSoftCascadeOctave::generateNegatives(const Dataset* dataset) { // ToDo: set seed, use offsets @@ -285,15 +249,12 @@ void BoostedSoftCascadeOctave::generateNegatives(const Dataset* dataset) cv::Mat channels = integrals.row(i).reshape(0, h / shrinkage * 10 + 1); _builder(frame, channels); - dprintf("generated %d %d\n", dx, dy); // // if (predict(sum)) { responses.ptr(i)[0] = 0.f; ++i; } } - - dprintf("Processing negatives finished:\n\trequested %d negatives, viewed %d samples.\n", nnegatives, total); } @@ -390,7 +351,7 @@ void BoostedSoftCascadeOctave::write( cv::FileStorage &fso, const FeaturePool* p << "}"; } -void BoostedSoftCascadeOctave::initial_weights(double (&p)[2]) +void BoostedSoftCascadeOctave::initialize_weights(double (&p)[2]) { double n = data->sample_count; p[0] = n / (2. * (double)(nnegatives)); diff --git a/modules/softcascade/test/test_training.cpp b/modules/softcascade/test/test_training.cpp new file mode 100644 index 0000000000..b6a282c498 --- /dev/null +++ b/modules/softcascade/test/test_training.cpp @@ -0,0 +1,246 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and / or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#if !defined(ANDROID) + +#include +#include +#include + +#include "test_precomp.hpp" +#if !defined (_WIN32) && ! defined(__MINGW32__) +# include +#else +# include +#endif + +using namespace std; + +namespace { + +typedef vector svector; +class ScaledDataset : public cv::Dataset +{ +public: + ScaledDataset(const string& path, const int octave); + + virtual cv::Mat get(SampleType type, int idx) const; + virtual int available(SampleType type) const; + virtual ~ScaledDataset(); + +private: + svector pos; + svector neg; +}; + +string itoa(long i) +{ + char s[65]; + sprintf(s, "%ld", i); + return std::string(s); +} + + +#if !defined (_WIN32) && ! defined(__MINGW32__) + +void glob(const string& path, svector& ret) +{ + glob_t glob_result; + glob(path.c_str(), GLOB_TILDE, 0, &glob_result); + + ret.clear(); + ret.reserve(glob_result.gl_pathc); + + for(unsigned int i = 0; i < glob_result.gl_pathc; ++i) + { + ret.push_back(std::string(glob_result.gl_pathv[i])); + } + + globfree(&glob_result); +} + +#else + +void glob(const string& refRoot, const string& refExt, svector &refvecFiles) +{ + std::string strFilePath; // File path + std::string strExtension; // Extension + + std::string strPattern = refRoot + "\\*.*"; + + WIN32_FIND_DATA FileInformation; // File information + HANDLE hFile = ::FindFirstFile(strPattern.c_str(), &FileInformation); + + if(hFile == INVALID_HANDLE_VALUE) + CV_Error(CV_StsBadArg, "Your dataset search path is incorrect"); + + do + { + if(FileInformation.cFileName[0] != '.') + { + strFilePath.erase(); + strFilePath = refRoot + "\\" + FileInformation.cFileName; + + if( !(FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) + { + // Check extension + strExtension = FileInformation.cFileName; + strExtension = strExtension.substr(strExtension.rfind(".") + 1); + + if(strExtension == refExt) + // Save filename + refvecFiles.push_back(strFilePath); + } + } + } + while(::FindNextFile(hFile, &FileInformation) == TRUE); + + // Close handle + ::FindClose(hFile); + + DWORD dwError = ::GetLastError(); + if(dwError != ERROR_NO_MORE_FILES) + CV_Error(CV_StsBadArg, "Your dataset search path is incorrect"); +} + +#endif + +ScaledDataset::ScaledDataset(const string& path, const int oct) +{ + +#if !defined (_WIN32) && ! defined(__MINGW32__) + glob(path + "/pos/octave_" + itoa(oct) + "/*.png", pos); +#else + glob(path + "/pos/octave_" + itoa(oct), "png", pos); +#endif + +#if !defined (_WIN32) && ! defined(__MINGW32__) + glob(path + "/neg/octave_" + itoa(oct) + "/*.png", neg); +#else + glob(path + "/neg/octave_" + itoa(oct), "png", neg); +#endif + + // Check: files not empty + CV_Assert(pos.size() != size_t(0)); + CV_Assert(neg.size() != size_t(0)); +} + +cv::Mat ScaledDataset::get(SampleType type, int idx) const +{ + const std::string& src = (type == POSITIVE)? pos[idx]: neg[idx]; + return cv::imread(src); +} + +int ScaledDataset::available(SampleType type) const +{ + return (int)((type == POSITIVE)? pos.size():neg.size()); +} + +ScaledDataset::~ScaledDataset(){} + +} + +TEST(DISABLED_SoftCascade, training) +{ + // // 2. check and open output file + string outXmlPath = cv::tempfile(".xml"); + cv::FileStorage fso(outXmlPath, cv::FileStorage::WRITE); + + ASSERT_TRUE(fso.isOpened()); + + std::vector octaves; + { + octaves.push_back(-1); + octaves.push_back(0); + } + + fso << "regression-cascade" + << "{" + << "stageType" << "BOOST" + << "featureType" << "ICF" + << "octavesNum" << 2 + << "width" << 64 + << "height" << 128 + << "shrinkage" << 4 + << "octaves" << "["; + + for (std::vector::const_iterator it = octaves.begin(); it != octaves.end(); ++it) + { + int nfeatures = 100; + int shrinkage = 4; + float octave = powf(2.f, (float)(*it)); + cv::Size model = cv::Size( cvRound(64 * octave) / shrinkage, cvRound(128 * octave) / shrinkage ); + + cv::Ptr pool = cv::FeaturePool::create(model, nfeatures); + nfeatures = pool->size(); + int npositives = 20; + int nnegatives = 40; + + cv::Rect boundingBox = cv::Rect( cvRound(20 * octave), cvRound(20 * octave), + cvRound(64 * octave), cvRound(128 * octave)); + + typedef cv::SoftCascadeOctave Octave; + cv::Ptr boost = Octave::create(boundingBox, npositives, nnegatives, *it, shrinkage, nfeatures); + + std::string path = cvtest::TS::ptr()->get_data_path() + "softcascade/sample_training_set"; + ScaledDataset dataset(path, *it); + + if (boost->train(&dataset, pool, 3, 2)) + { + cv::Mat thresholds; + boost->setRejectThresholds(thresholds); + boost->write(fso, pool, thresholds); + } + } + + fso << "]" << "}"; + fso.release(); + + + cv::FileStorage actual(outXmlPath, cv::FileStorage::READ); + cv::FileNode root = actual.getFirstTopLevelNode(); + + cv::FileNode fn = root["octaves"]; + ASSERT_FALSE(fn.empty()); +} + +#endif \ No newline at end of file