From 108fd169f7e253de7435437293c726f4ae195c5f Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 28 Aug 2012 17:42:15 +0400 Subject: [PATCH 01/56] dummy soft cascade --- .../include/opencv2/objdetect/objdetect.hpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index c9f740764d..fbcc6ba5c8 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -411,7 +411,7 @@ protected: enum { DO_CANNY_PRUNING = 1, SCALE_IMAGE = 2, FIND_BIGGEST_OBJECT = 4, DO_ROUGH_SEARCH = 8 }; - friend class CascadeClassifierInvoker; + friend struct CascadeClassifierInvoker; template friend int predictOrdered( CascadeClassifier& cascade, Ptr &featureEvaluator, double& weight); @@ -488,6 +488,28 @@ protected: Ptr maskGenerator; }; +// ======================== soft cascade version ===================== // + +class CV_EXPORTS SoftCascade +{ +public: + SoftCascade(); + SoftCascade( const string& filename ); + virtual ~SoftCascade(); + bool load( const string& filename ); + + virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, double factor = 1.05, int step = 4, int rejectfactor = 1); + +protected: + virtual void detectForOctave(int octave); + // virtual bool detectSingleScale( const Mat& image, int stripCount, Size processingRectSize, + // int stripSize, int yStep, double factor, vector& candidates, + // vector& rejectLevels, vector& levelWeights, bool outputRejectLevels=false); + enum { BOOST = 0 }; +private: + struct Filds; + Filds* filds; +}; //////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector ////////////// From 9966d7feba7a3f35196f99e1a4263841cdac634d Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 28 Aug 2012 20:01:40 +0400 Subject: [PATCH 02/56] add structures for cascade representation --- .../include/opencv2/objdetect/objdetect.hpp | 12 +++ modules/objdetect/src/softcascade.cpp | 74 +++++++++++++++++++ modules/objdetect/test/test_softcascade.cpp | 47 ++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 modules/objdetect/src/softcascade.cpp create mode 100644 modules/objdetect/test/test_softcascade.cpp diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index fbcc6ba5c8..690d63bbc5 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -506,7 +506,19 @@ protected: // int stripSize, int yStep, double factor, vector& candidates, // vector& rejectLevels, vector& levelWeights, bool outputRejectLevels=false); enum { BOOST = 0 }; + private: + struct Feature + { + cv::Rect rect; + int channel; + }; + + stuct Stamp + { + + }; + struct Filds; Filds* filds; }; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp new file mode 100644 index 0000000000..1d2604c1b0 --- /dev/null +++ b/modules/objdetect/src/softcascade.cpp @@ -0,0 +1,74 @@ +/*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-2011, 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: +// +// * 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. +// +// * 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*/ + +struct Filds +{ + // cv::Mat luv; + // std::vector bins; + // cv::Mat magnitude; + // double scaleFactor; + // int windowStep; +}; + + +SoftCascade::SoftCascade() : filds(0) {} + +SoftCascade::SoftCascade( const string& filename ) +{ + filds = new filds; + load(filename); +} +virtual SoftCascade::~SoftCascade() +{ + delete filds; +} + +bool SoftCascade::load( const string& filename ) +{ + return true; +} + +virtual void SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, + const double factor = 1.05, const int step = 4, const int rejectfactor = 1) +{} + +virtual void SoftCascade::detectForOctave(const int octave) +{} \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp new file mode 100644 index 0000000000..fc7baaad24 --- /dev/null +++ b/modules/objdetect/test/test_softcascade.cpp @@ -0,0 +1,47 @@ +/*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-2011, 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: +// +// * 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. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +//M*/ + +#include "test_precomp.hpp" + +TEST(SoftCascade, HOG) +{ + +} \ No newline at end of file From cd301e530fbed9ef00cf15e78e0b1a20145e7aa5 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 30 Aug 2012 19:48:12 +0400 Subject: [PATCH 03/56] minor in train application --- apps/traincascade/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/traincascade/CMakeLists.txt b/apps/traincascade/CMakeLists.txt index 350200fc49..b92beb69bd 100644 --- a/apps/traincascade/CMakeLists.txt +++ b/apps/traincascade/CMakeLists.txt @@ -2,7 +2,7 @@ if(IOS OR ANDROID) return() endif() -SET(OPENCV_TRAINCASCADE_DEPS opencv_core opencv_ml opencv_imgproc opencv_objdetect opencv_highgui opencv_calib3d opencv_video opencv_features2d opencv_flann opencv_legacy) +set(OPENCV_TRAINCASCADE_DEPS opencv_core opencv_ml opencv_imgproc opencv_objdetect opencv_highgui opencv_calib3d opencv_video opencv_features2d opencv_flann opencv_legacy) ocv_check_dependencies(${OPENCV_TRAINCASCADE_DEPS}) if(NOT OCV_DEPENDENCIES_FOUND) From fb113e5ce42079e4bc3bd741ec5c402acc7f1e92 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 30 Aug 2012 19:48:59 +0400 Subject: [PATCH 04/56] scale pyramid calculations --- .../include/opencv2/objdetect/objdetect.hpp | 3 +- modules/objdetect/src/softcascade.cpp | 137 ++++++++++++++++-- 2 files changed, 130 insertions(+), 10 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 690d63bbc5..c528c8c40a 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -506,6 +506,7 @@ protected: // int stripSize, int yStep, double factor, vector& candidates, // vector& rejectLevels, vector& levelWeights, bool outputRejectLevels=false); enum { BOOST = 0 }; + enum { FRAME_WIDTH = 640, FRAME_HEIGHT = 480, TOTAL_SCALES = 55, CLASSIFIERS = 5}; private: struct Feature @@ -514,7 +515,7 @@ private: int channel; }; - stuct Stamp + struct Stamp { }; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 1d2604c1b0..61a1197a7c 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -39,8 +39,15 @@ // the use of this software, even if advised of the possibility of such damage. //M*/ -struct Filds +#include +#include + +#include + + +struct cv::SoftCascade::Filds { + std::vector octaves; // cv::Mat luv; // std::vector bins; // cv::Mat magnitude; @@ -48,27 +55,139 @@ struct Filds // int windowStep; }; +namespace { -SoftCascade::SoftCascade() : filds(0) {} +struct Cascade { + int logOctave; + float octave; + cv::Size objSize; +}; -SoftCascade::SoftCascade( const string& filename ) +struct Level { + int index; + float factor; + float logFactor; + int width; + int height; + float octave; + cv::Size objSize; + + Level(int i,float f, float lf, int w, int h) : index(i), factor(f), logFactor(lf), width(w), height(h), octave(0.f) {} + + void assign(float o, int detW, int detH) + { + octave = o; + objSize = cv::Size(cv::saturate_cast(detW * o), cv::saturate_cast(detH * o)); + } + + float relScale() {return (factor / octave); } +}; + // compute levels of full pyramid + void pyrLevels(int frameW, int frameH, int detW, int detH, int scales, float minScale, float maxScale, std::vector levels) + { + CV_Assert(scales > 1); + levels.clear(); + float logFactor = (log(maxScale) - log(minScale)) / (scales -1); + + float scale = minScale; + for (int sc = 0; sc < scales; ++sc) + { + Level level(sc, scale, log(scale) + logFactor, std::max(0.0f, frameW - (detW * scale)), std::max(0.0f, frameH - (detH * scale))); + if (!level.width || !level.height) + break; + else + levels.push_back(level); + + if (fabs(scale - maxScale) < FLT_EPSILON) break; + scale = std::min(maxScale, expf(log(scale) + logFactor)); + } + + } + + // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper + struct CascadeIntrinsics { + static const float lambda = 1.099f/ 0.301029996f, a = 0.89f; + static const float intrinsics[10][4]; + + static float getFor(int chennel, int scaling, int ab) + { + CV_Assert(chennel < 10 && scaling < 2 && ab < 2); + return intrinsics[chennel][(scaling << 1) + ab]; + } + + }; + + const float CascadeIntrinsics::intrinsics[10][4] = + { //da, db, ua, ub + // hog-like orientation bins + {a, lambda, 1, 2}, + {a, lambda, 1, 2}, + {a, lambda, 1, 2}, + {a, lambda, 1, 2}, + {a, lambda, 1, 2}, + {a, lambda, 1, 2}, + // gradient magnitude + {a, lambda / log(2), 1, 2}, + // luv -color chennels + {1, 2, 1, 2}, + {1, 2, 1, 2}, + {1, 2, 1, 2} + }; +} + + + + +cv::SoftCascade::SoftCascade() : filds(0) {} + +cv::SoftCascade::SoftCascade( const string& filename ) { - filds = new filds; + filds = new Filds; load(filename); } -virtual SoftCascade::~SoftCascade() +cv::SoftCascade::~SoftCascade() { delete filds; } -bool SoftCascade::load( const string& filename ) +bool cv::SoftCascade::load( const string& filename ) { + // temp fixture + Filds& flds = *filds; + flds.octaves.push_back(0.5f); + flds.octaves.push_back(1.0f); + flds.octaves.push_back(2.0f); + flds.octaves.push_back(4.0f); + flds.octaves.push_back(8.0f); + + // scales calculations + int origObjectW = 64; + int origObjectH = 128; + float maxScale = 5.f, minScale = 0.4f; + std::vector levels; + + pyrLevels(FRAME_WIDTH, FRAME_HEIGHT, origObjectW, origObjectH, TOTAL_SCALES, minScale, maxScale,levels); + + for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) + { + float minAbsLog = FLT_MAX; + for (std::vector::iterator oct = flds.octaves.begin(); oct < flds.octaves.end(); ++oct) + { + float logOctave = log(*oct); + float logAbsScale = fabs((*level).logFactor - logOctave); + + if(logAbsScale < minAbsLog) + (*level).assign(*oct, origObjectW, origObjectH); + + } + } + return true; } -virtual void SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - const double factor = 1.05, const int step = 4, const int rejectfactor = 1) +void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, + const double factor, const int step, const int rejectfactor) {} -virtual void SoftCascade::detectForOctave(const int octave) +void cv::SoftCascade::detectForOctave(const int octave) {} \ No newline at end of file From 7290d8576d11330f38f38b44bbd559db923dae39 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 30 Aug 2012 20:24:45 +0400 Subject: [PATCH 05/56] add ICF feature --- modules/objdetect/src/softcascade.cpp | 41 ++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 61a1197a7c..b79ec2bf34 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -106,13 +106,18 @@ struct Level { // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper struct CascadeIntrinsics { - static const float lambda = 1.099f/ 0.301029996f, a = 0.89f; + static const float lambda = 1.099f, a = 0.89f; static const float intrinsics[10][4]; - static float getFor(int chennel, int scaling, int ab) + static float getFor(int chennel, float scaling) { - CV_Assert(chennel < 10 && scaling < 2 && ab < 2); - return intrinsics[chennel][(scaling << 1) + ab]; + CV_Assert(chennel < 10); + + if ((scaling - 1.f) < FLT_EPSILON) + return 1.f; + + int ud = (int)(scaling < 1.f); + return intrinsics[chennel][(ud << 1)] * pow(scaling, intrinsics[chennel][(ud << 1) + 1]); } }; @@ -120,12 +125,12 @@ struct Level { const float CascadeIntrinsics::intrinsics[10][4] = { //da, db, ua, ub // hog-like orientation bins - {a, lambda, 1, 2}, - {a, lambda, 1, 2}, - {a, lambda, 1, 2}, - {a, lambda, 1, 2}, - {a, lambda, 1, 2}, - {a, lambda, 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, // gradient magnitude {a, lambda / log(2), 1, 2}, // luv -color chennels @@ -133,6 +138,22 @@ struct Level { {1, 2, 1, 2}, {1, 2, 1, 2} }; + + struct Feature + { + cv::Rect rect; + int channel; + float threshold; + + Feature(int x, int y, int w, int h, int c, float t) : rect(cv::Rect(x, y, w, h)), channel(c), threshold(t) {} + Feature(cv::Rect r, int c, float t) : rect(r), channel(c), threshold(t) {} + + Feature rescale(float relScale) + { + cv::Rect r(cvRound(rect.x * relScale), cvRound(rect.y * relScale), cvRound(rect.width * relScale), cvRound(rect.height * relScale)); + return Feature( r, channel, threshold * CascadeIntrinsics::getFor(channel, relScale)); + } + }; } From fe2c38be8007c675dd4fe5d81e2236bd71d7ae04 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 4 Sep 2012 13:52:07 +0400 Subject: [PATCH 06/56] add method to fill soft cascade --- .../include/opencv2/objdetect/objdetect.hpp | 14 +++- modules/objdetect/src/softcascade.cpp | 73 ++++++++++++++++--- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index c528c8c40a..04accc3bcd 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -494,9 +494,9 @@ class CV_EXPORTS SoftCascade { public: SoftCascade(); - SoftCascade( const string& filename ); + SoftCascade( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); virtual ~SoftCascade(); - bool load( const string& filename ); + bool load( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, double factor = 1.05, int step = 4, int rejectfactor = 1); @@ -506,7 +506,15 @@ protected: // int stripSize, int yStep, double factor, vector& candidates, // vector& rejectLevels, vector& levelWeights, bool outputRejectLevels=false); enum { BOOST = 0 }; - enum { FRAME_WIDTH = 640, FRAME_HEIGHT = 480, TOTAL_SCALES = 55, CLASSIFIERS = 5}; + enum + { + FRAME_WIDTH = 640, + FRAME_HEIGHT = 480, + TOTAL_SCALES = 55, + CLASSIFIERS = 5, + ORIG_OBJECT_WIDTH = 64, + ORIG_OBJECT_HEIGHT = 128 + }; private: struct Feature diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index b79ec2bf34..8463180cad 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -41,9 +41,10 @@ #include #include +#include #include - +#include struct cv::SoftCascade::Filds { @@ -53,6 +54,50 @@ struct cv::SoftCascade::Filds // cv::Mat magnitude; // double scaleFactor; // int windowStep; + float minScale; + float maxScale; + int noctaves; + + bool fill(const FileNode &root, const float mins, const float maxs) + { + minScale = mins; + maxScale = maxs; + + const char *SC_STAGE_TYPE = "stageType"; + const char *SC_FEATURE_TYPE = "featureType"; + const char *SC_BOOST = "BOOST"; + const char *SC_ICF = "ICF"; + const char *SC_NUM_OCTAVES = "octavesNum"; + const char* SC_CASCADES = "cascades"; + const char *SC_HEIGHT = "height"; + const char *SC_WIDTH = "width"; + const char *SC_MAX_DEPTH = "maxDepth"; + const char *SC_STAGES = "stages"; + const char *SC_STAGE_THRESHOLD = "stageThreshold"; + + // only boost supported + std::string stageTypeStr = (string)root[SC_STAGE_TYPE]; + CV_Assert(stageTypeStr == SC_BOOST); + + // only HOG-like integral channel features cupported + string featureTypeStr = (string)root[SC_FEATURE_TYPE]; + CV_Assert(featureTypeStr == SC_ICF); + + noctaves = (int)root[SC_NUM_OCTAVES]; + CV_Assert(noctaves > 0); + + // const char *SC_WEAK_CLASSIFIERS = "weakClassifiers"; + // const char *SC_INTERNAL_NODES = "internalNodes"; + // const char *SC_LEAF_VALUES = "leafValues"; + // const char *SC_FEATURES = "features"; + // const char *SC_RECT = "rect"; + + // const char *SC_STAGE_PARAMS = "stageParams"; + // const char *SC_FEATURE_PARAMS = "featureParams"; + // const char *SC_MAX_CAT_COUNT = "maxCatCount"; + + return true; + } }; namespace { @@ -161,18 +206,28 @@ struct Level { cv::SoftCascade::SoftCascade() : filds(0) {} -cv::SoftCascade::SoftCascade( const string& filename ) +cv::SoftCascade::SoftCascade( const string& filename, const float minScale, const float maxScale) { filds = new Filds; - load(filename); + load(filename, minScale, maxScale); } cv::SoftCascade::~SoftCascade() { delete filds; } -bool cv::SoftCascade::load( const string& filename ) +bool cv::SoftCascade::load( const string& filename, const float minScale, const float maxScale) { + delete filds; + filds = 0; + + cv::FileStorage fs(filename, FileStorage::READ); + if (!fs.isOpened()) return false; + + filds = new Filds; + if (!(*filds).fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; + + //////////////// // temp fixture Filds& flds = *filds; flds.octaves.push_back(0.5f); @@ -182,12 +237,9 @@ bool cv::SoftCascade::load( const string& filename ) flds.octaves.push_back(8.0f); // scales calculations - int origObjectW = 64; - int origObjectH = 128; - float maxScale = 5.f, minScale = 0.4f; std::vector levels; - pyrLevels(FRAME_WIDTH, FRAME_HEIGHT, origObjectW, origObjectH, TOTAL_SCALES, minScale, maxScale,levels); + pyrLevels(FRAME_WIDTH, FRAME_HEIGHT, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT, TOTAL_SCALES, minScale, maxScale, levels); for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) { @@ -198,11 +250,14 @@ bool cv::SoftCascade::load( const string& filename ) float logAbsScale = fabs((*level).logFactor - logOctave); if(logAbsScale < minAbsLog) - (*level).assign(*oct, origObjectW, origObjectH); + (*level).assign(*oct, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); } } + // load cascade from xml + // read(const FileNode &root) + return true; } From a54d456ad0aa06e3f4f01f417dfad0451118b8fb Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 4 Sep 2012 18:03:10 +0400 Subject: [PATCH 07/56] parse soft cascade from xml --- .../include/opencv2/objdetect/objdetect.hpp | 11 - modules/objdetect/src/softcascade.cpp | 343 +++++++++++------- modules/objdetect/test/test_softcascade.cpp | 5 +- 3 files changed, 222 insertions(+), 137 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 04accc3bcd..9bdc914627 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -517,17 +517,6 @@ protected: }; private: - struct Feature - { - cv::Rect rect; - int channel; - }; - - struct Stamp - { - - }; - struct Filds; Filds* filds; }; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 8463180cad..dae352c560 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -45,35 +45,94 @@ #include #include +#include + +namespace { + + static const char* SC_OCT_SCALE = "scale"; + static const char* SC_OCT_STAGES = "stageNum"; + + struct Octave + { + float scale; + int stages; + + Octave(){} + Octave(const cv::FileNode& fn) : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]) + {printf("octave: %f %d\n", scale, stages);} + }; + + static const char *SC_STAGE_THRESHOLD = "stageThreshold"; + static const char *SC_STAGE_WEIGHT = "weight"; + + struct Stage + { + float threshold; + float weight; + + Stage(){} + Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]), weight((float)fn[SC_STAGE_WEIGHT]) + {printf(" stage: %f %f\n",threshold, weight);} + }; + + static const char *SC_F_THRESHOLD = "threshold"; + static const char *SC_F_DIRECTION = "direction"; + static const char *SC_F_CHANNEL = "chennel"; + static const char *SC_F_RECT = "rect"; + + struct Feature + { + float threshold; + int direction; + int chennel; + cv::Rect rect; + + Feature() {} + Feature(const cv::FileNode& fn) + : threshold((float)fn[SC_F_THRESHOLD]), direction((int)fn[SC_F_DIRECTION]), + chennel((int)fn[SC_F_CHANNEL]) + { + cv::FileNode rn = fn[SC_F_RECT]; + cv::FileNodeIterator r_it = rn.begin(); + rect = cv::Rect(*(r_it++), *(r_it++), *(r_it++), *(r_it++)); + printf(" feature: %f %d %d [%d %d %d %d]\n",threshold, direction, chennel, rect.x, rect.y, rect.width, rect.height);} + }; +} struct cv::SoftCascade::Filds { - std::vector octaves; - // cv::Mat luv; - // std::vector bins; - // cv::Mat magnitude; - // double scaleFactor; - // int windowStep; float minScale; float maxScale; + + int origObjWidth; + int origObjHeight; + int noctaves; + std::vector octaves; + std::vector stages; + std::vector features; + bool fill(const FileNode &root, const float mins, const float maxs) { minScale = mins; maxScale = maxs; + // cascade properties const char *SC_STAGE_TYPE = "stageType"; - const char *SC_FEATURE_TYPE = "featureType"; const char *SC_BOOST = "BOOST"; + const char *SC_FEATURE_TYPE = "featureType"; const char *SC_ICF = "ICF"; + const char *SC_TREE_TYPE = "stageTreeType"; + const char *SC_STAGE_TH2 = "TH2"; const char *SC_NUM_OCTAVES = "octavesNum"; - const char* SC_CASCADES = "cascades"; - const char *SC_HEIGHT = "height"; - const char *SC_WIDTH = "width"; - const char *SC_MAX_DEPTH = "maxDepth"; + const char *SC_ORIG_W = "origObjWidth"; + const char *SC_ORIG_H = "origObjHeight"; + + const char* SC_OCTAVES = "octaves"; const char *SC_STAGES = "stages"; - const char *SC_STAGE_THRESHOLD = "stageThreshold"; + const char *SC_FEATURES = "features"; + // only boost supported std::string stageTypeStr = (string)root[SC_STAGE_TYPE]; @@ -83,123 +142,157 @@ struct cv::SoftCascade::Filds string featureTypeStr = (string)root[SC_FEATURE_TYPE]; CV_Assert(featureTypeStr == SC_ICF); + // only trees of height 2 + string stageTreeTypeStr = (string)root[SC_TREE_TYPE]; + CV_Assert(stageTreeTypeStr == SC_STAGE_TH2); + + // not empty noctaves = (int)root[SC_NUM_OCTAVES]; CV_Assert(noctaves > 0); - // const char *SC_WEAK_CLASSIFIERS = "weakClassifiers"; - // const char *SC_INTERNAL_NODES = "internalNodes"; - // const char *SC_LEAF_VALUES = "leafValues"; - // const char *SC_FEATURES = "features"; - // const char *SC_RECT = "rect"; + origObjWidth = (int)root[SC_ORIG_W]; + CV_Assert(origObjWidth == SoftCascade::ORIG_OBJECT_WIDTH); - // const char *SC_STAGE_PARAMS = "stageParams"; - // const char *SC_FEATURE_PARAMS = "featureParams"; - // const char *SC_MAX_CAT_COUNT = "maxCatCount"; + origObjHeight = (int)root[SC_ORIG_H]; + CV_Assert(origObjHeight == SoftCascade::ORIG_OBJECT_HEIGHT); + // for each octave (~ one cascade in classic OpenCV xml) + FileNode fn = root[SC_OCTAVES]; + if (fn.empty()) return false; + + octaves.reserve(noctaves); + FileNodeIterator it = fn.begin(), it_end = fn.end(); + for (; it != it_end; ++it) + { + FileNode fns = *it; + Octave octave = Octave(fns); + CV_Assert(octave.stages > 0); + octaves.push_back(octave); + stages.reserve(stages.size() + octave.stages); + + fns = fns[SC_STAGES]; + if (fn.empty()) return false; + + // for each stage (~ decision tree with H = 2) + FileNodeIterator st = fns.begin(), st_end = fns.end(); + for (; st != st_end; ++st ) + { + fns = *st; + stages.push_back(Stage(fns)); + + fns = fns[SC_FEATURES]; + // for each feature for tree. features stored in order {root, left, right} + FileNodeIterator ftr = fns.begin(), ft_end = fns.end(); + for (; ftr != ft_end; ++ftr) + { + features.push_back(Feature(*ftr)); + } + } + } return true; } }; -namespace { +// namespace { -struct Cascade { - int logOctave; - float octave; - cv::Size objSize; -}; +// struct Cascade { +// int logOctave; +// float octave; +// cv::Size objSize; +// }; -struct Level { - int index; - float factor; - float logFactor; - int width; - int height; - float octave; - cv::Size objSize; +// struct Level { +// int index; +// float factor; +// float logFactor; +// int width; +// int height; +// float octave; +// cv::Size objSize; - Level(int i,float f, float lf, int w, int h) : index(i), factor(f), logFactor(lf), width(w), height(h), octave(0.f) {} +// Level(int i,float f, float lf, int w, int h) : index(i), factor(f), logFactor(lf), width(w), height(h), octave(0.f) {} - void assign(float o, int detW, int detH) - { - octave = o; - objSize = cv::Size(cv::saturate_cast(detW * o), cv::saturate_cast(detH * o)); - } +// void assign(float o, int detW, int detH) +// { +// octave = o; +// objSize = cv::Size(cv::saturate_cast(detW * o), cv::saturate_cast(detH * o)); +// } - float relScale() {return (factor / octave); } -}; - // compute levels of full pyramid - void pyrLevels(int frameW, int frameH, int detW, int detH, int scales, float minScale, float maxScale, std::vector levels) - { - CV_Assert(scales > 1); - levels.clear(); - float logFactor = (log(maxScale) - log(minScale)) / (scales -1); +// float relScale() {return (factor / octave); } +// }; +// // compute levels of full pyramid +// void pyrLevels(int frameW, int frameH, int detW, int detH, int scales, float minScale, float maxScale, std::vector levels) +// { +// CV_Assert(scales > 1); +// levels.clear(); +// float logFactor = (log(maxScale) - log(minScale)) / (scales -1); - float scale = minScale; - for (int sc = 0; sc < scales; ++sc) - { - Level level(sc, scale, log(scale) + logFactor, std::max(0.0f, frameW - (detW * scale)), std::max(0.0f, frameH - (detH * scale))); - if (!level.width || !level.height) - break; - else - levels.push_back(level); +// float scale = minScale; +// for (int sc = 0; sc < scales; ++sc) +// { +// Level level(sc, scale, log(scale) + logFactor, std::max(0.0f, frameW - (detW * scale)), std::max(0.0f, frameH - (detH * scale))); +// if (!level.width || !level.height) +// break; +// else +// levels.push_back(level); - if (fabs(scale - maxScale) < FLT_EPSILON) break; - scale = std::min(maxScale, expf(log(scale) + logFactor)); - } +// if (fabs(scale - maxScale) < FLT_EPSILON) break; +// scale = std::min(maxScale, expf(log(scale) + logFactor)); +// } - } +// } - // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper - struct CascadeIntrinsics { - static const float lambda = 1.099f, a = 0.89f; - static const float intrinsics[10][4]; +// // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper +// struct CascadeIntrinsics { +// static const float lambda = 1.099f, a = 0.89f; +// static const float intrinsics[10][4]; - static float getFor(int chennel, float scaling) - { - CV_Assert(chennel < 10); +// static float getFor(int chennel, float scaling) +// { +// CV_Assert(chennel < 10); - if ((scaling - 1.f) < FLT_EPSILON) - return 1.f; +// if ((scaling - 1.f) < FLT_EPSILON) +// return 1.f; - int ud = (int)(scaling < 1.f); - return intrinsics[chennel][(ud << 1)] * pow(scaling, intrinsics[chennel][(ud << 1) + 1]); - } +// int ud = (int)(scaling < 1.f); +// return intrinsics[chennel][(ud << 1)] * pow(scaling, intrinsics[chennel][(ud << 1) + 1]); +// } - }; +// }; - const float CascadeIntrinsics::intrinsics[10][4] = - { //da, db, ua, ub - // hog-like orientation bins - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - // gradient magnitude - {a, lambda / log(2), 1, 2}, - // luv -color chennels - {1, 2, 1, 2}, - {1, 2, 1, 2}, - {1, 2, 1, 2} - }; +// const float CascadeIntrinsics::intrinsics[10][4] = +// { //da, db, ua, ub +// // hog-like orientation bins +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// // gradient magnitude +// {a, lambda / log(2), 1, 2}, +// // luv -color chennels +// {1, 2, 1, 2}, +// {1, 2, 1, 2}, +// {1, 2, 1, 2} +// }; - struct Feature - { - cv::Rect rect; - int channel; - float threshold; +// struct Feature +// { +// cv::Rect rect; +// int channel; +// float threshold; - Feature(int x, int y, int w, int h, int c, float t) : rect(cv::Rect(x, y, w, h)), channel(c), threshold(t) {} - Feature(cv::Rect r, int c, float t) : rect(r), channel(c), threshold(t) {} +// Feature(int x, int y, int w, int h, int c, float t) : rect(cv::Rect(x, y, w, h)), channel(c), threshold(t) {} +// Feature(cv::Rect r, int c, float t) : rect(r), channel(c), threshold(t) {} - Feature rescale(float relScale) - { - cv::Rect r(cvRound(rect.x * relScale), cvRound(rect.y * relScale), cvRound(rect.width * relScale), cvRound(rect.height * relScale)); - return Feature( r, channel, threshold * CascadeIntrinsics::getFor(channel, relScale)); - } - }; -} +// Feature rescale(float relScale) +// { +// cv::Rect r(cvRound(rect.x * relScale), cvRound(rect.y * relScale), cvRound(rect.width * relScale), cvRound(rect.height * relScale)); +// return Feature( r, channel, threshold * CascadeIntrinsics::getFor(channel, relScale)); +// } +// }; +// } @@ -227,33 +320,33 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const filds = new Filds; if (!(*filds).fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - //////////////// - // temp fixture - Filds& flds = *filds; - flds.octaves.push_back(0.5f); - flds.octaves.push_back(1.0f); - flds.octaves.push_back(2.0f); - flds.octaves.push_back(4.0f); - flds.octaves.push_back(8.0f); + // //////////////// + // // temp fixture + // Filds& flds = *filds; + // flds.octaves.push_back(0.5f); + // flds.octaves.push_back(1.0f); + // flds.octaves.push_back(2.0f); + // flds.octaves.push_back(4.0f); + // flds.octaves.push_back(8.0f); - // scales calculations - std::vector levels; + // // scales calculations + // std::vector levels; - pyrLevels(FRAME_WIDTH, FRAME_HEIGHT, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT, TOTAL_SCALES, minScale, maxScale, levels); + // pyrLevels(FRAME_WIDTH, FRAME_HEIGHT, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT, TOTAL_SCALES, minScale, maxScale, levels); - for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) - { - float minAbsLog = FLT_MAX; - for (std::vector::iterator oct = flds.octaves.begin(); oct < flds.octaves.end(); ++oct) - { - float logOctave = log(*oct); - float logAbsScale = fabs((*level).logFactor - logOctave); + // for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) + // { + // float minAbsLog = FLT_MAX; + // for (std::vector::iterator oct = flds.octaves.begin(); oct < flds.octaves.end(); ++oct) + // { + // float logOctave = log(*oct); + // float logAbsScale = fabs((*level).logFactor - logOctave); - if(logAbsScale < minAbsLog) - (*level).assign(*oct, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); + // if(logAbsScale < minAbsLog) + // (*level).assign(*oct, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); - } - } + // } + // } // load cascade from xml // read(const FileNode &root) diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index fc7baaad24..a7b8548de8 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -41,7 +41,10 @@ #include "test_precomp.hpp" -TEST(SoftCascade, HOG) +TEST(SoftCascade, readCascade) { + std::string xml = "/home/kellan/icf-template.xml"; + cv::SoftCascade cascade; + ASSERT_TRUE(cascade.load(xml)); } \ No newline at end of file From f01c5d9033944c7c766eed60fc1468a5ee78e097 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 4 Sep 2012 19:08:52 +0400 Subject: [PATCH 08/56] compute scales pyramid --- modules/objdetect/src/softcascade.cpp | 250 ++++++++++++-------------- 1 file changed, 110 insertions(+), 140 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index dae352c560..065fb0d8b5 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -75,27 +75,93 @@ namespace { {printf(" stage: %f %f\n",threshold, weight);} }; + // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper + struct CascadeIntrinsics + { + static const float lambda = 1.099f, a = 0.89f; + static const float intrinsics[10][4]; + + static float getFor(int channel, float scaling) + { + CV_Assert(channel < 10); + + if ((scaling - 1.f) < FLT_EPSILON) + return 1.f; + + int ud = (int)(scaling < 1.f); + return intrinsics[channel][(ud << 1)] * pow(scaling, intrinsics[channel][(ud << 1) + 1]); + } + + }; + + const float CascadeIntrinsics::intrinsics[10][4] = + { //da, db, ua, ub + // hog-like orientation bins + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + {a, lambda / log(2), 1, 2}, + // gradient magnitude + {a, lambda / log(2), 1, 2}, + // luv color channels + {1, 2, 1, 2}, + {1, 2, 1, 2}, + {1, 2, 1, 2} + }; + static const char *SC_F_THRESHOLD = "threshold"; static const char *SC_F_DIRECTION = "direction"; - static const char *SC_F_CHANNEL = "chennel"; + static const char *SC_F_CHANNEL = "channel"; static const char *SC_F_RECT = "rect"; struct Feature { float threshold; int direction; - int chennel; + int channel; cv::Rect rect; Feature() {} Feature(const cv::FileNode& fn) : threshold((float)fn[SC_F_THRESHOLD]), direction((int)fn[SC_F_DIRECTION]), - chennel((int)fn[SC_F_CHANNEL]) + channel((int)fn[SC_F_CHANNEL]) { cv::FileNode rn = fn[SC_F_RECT]; cv::FileNodeIterator r_it = rn.begin(); rect = cv::Rect(*(r_it++), *(r_it++), *(r_it++), *(r_it++)); - printf(" feature: %f %d %d [%d %d %d %d]\n",threshold, direction, chennel, rect.x, rect.y, rect.width, rect.height);} + printf(" feature: %f %d %d [%d %d %d %d]\n",threshold, direction, channel, rect.x, rect.y, rect.width, rect.height);} + + Feature rescale(float relScale) + { + Feature res(*this); + res.rect = cv::Rect (cvRound(rect.x * relScale), cvRound(rect.y * relScale), + cvRound(rect.width * relScale), cvRound(rect.height * relScale)); + res.threshold = threshold * CascadeIntrinsics::getFor(channel, relScale); + return res; + } + }; + + struct Level + { + int index; + float factor; + float logFactor; + int width; + int height; + float octave; + cv::Size objSize; + + Level(int i,float f, float lf, int w, int h): index(i), factor(f), logFactor(lf), width(w), height(h), octave(0.f) {} + + void assign(float o, int detW, int detH) + { + octave = o; + objSize = cv::Size(cv::saturate_cast(detW * o), cv::saturate_cast(detH * o)); + } + + float relScale() {return (factor / octave); } }; } @@ -112,6 +178,43 @@ struct cv::SoftCascade::Filds std::vector octaves; std::vector stages; std::vector features; + std::vector levels; + + // compute levels of full pyramid + void calcLevels(int frameW, int frameH, int scales) + { + CV_Assert(scales > 1); + levels.clear(); + float logFactor = (log(maxScale) - log(minScale)) / (scales -1); + + float scale = minScale; + for (int sc = 0; sc < scales; ++sc) + { + Level level(sc, scale, log(scale) + logFactor, + std::max(0.0f, frameW - (origObjWidth * scale)), std::max(0.0f, frameH - (origObjHeight * scale))); + if (!level.width || !level.height) + break; + else + levels.push_back(level); + + if (fabs(scale - maxScale) < FLT_EPSILON) break; + scale = std::min(maxScale, expf(log(scale) + logFactor)); + } + + for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) + { + float minAbsLog = FLT_MAX; + for (std::vector::iterator oct = octaves.begin(); oct < octaves.end(); ++oct) + { + const Octave& octave =*oct; + float logOctave = log(octave.scale); + float logAbsScale = fabs((*level).logFactor - logOctave); + + if(logAbsScale < minAbsLog) + (*level).assign(octave.scale, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); + } + } + } bool fill(const FileNode &root, const float mins, const float maxs) { @@ -193,110 +296,6 @@ struct cv::SoftCascade::Filds } }; -// namespace { - -// struct Cascade { -// int logOctave; -// float octave; -// cv::Size objSize; -// }; - -// struct Level { -// int index; -// float factor; -// float logFactor; -// int width; -// int height; -// float octave; -// cv::Size objSize; - -// Level(int i,float f, float lf, int w, int h) : index(i), factor(f), logFactor(lf), width(w), height(h), octave(0.f) {} - -// void assign(float o, int detW, int detH) -// { -// octave = o; -// objSize = cv::Size(cv::saturate_cast(detW * o), cv::saturate_cast(detH * o)); -// } - -// float relScale() {return (factor / octave); } -// }; -// // compute levels of full pyramid -// void pyrLevels(int frameW, int frameH, int detW, int detH, int scales, float minScale, float maxScale, std::vector levels) -// { -// CV_Assert(scales > 1); -// levels.clear(); -// float logFactor = (log(maxScale) - log(minScale)) / (scales -1); - -// float scale = minScale; -// for (int sc = 0; sc < scales; ++sc) -// { -// Level level(sc, scale, log(scale) + logFactor, std::max(0.0f, frameW - (detW * scale)), std::max(0.0f, frameH - (detH * scale))); -// if (!level.width || !level.height) -// break; -// else -// levels.push_back(level); - -// if (fabs(scale - maxScale) < FLT_EPSILON) break; -// scale = std::min(maxScale, expf(log(scale) + logFactor)); -// } - -// } - -// // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper -// struct CascadeIntrinsics { -// static const float lambda = 1.099f, a = 0.89f; -// static const float intrinsics[10][4]; - -// static float getFor(int chennel, float scaling) -// { -// CV_Assert(chennel < 10); - -// if ((scaling - 1.f) < FLT_EPSILON) -// return 1.f; - -// int ud = (int)(scaling < 1.f); -// return intrinsics[chennel][(ud << 1)] * pow(scaling, intrinsics[chennel][(ud << 1) + 1]); -// } - -// }; - -// const float CascadeIntrinsics::intrinsics[10][4] = -// { //da, db, ua, ub -// // hog-like orientation bins -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// // gradient magnitude -// {a, lambda / log(2), 1, 2}, -// // luv -color chennels -// {1, 2, 1, 2}, -// {1, 2, 1, 2}, -// {1, 2, 1, 2} -// }; - -// struct Feature -// { -// cv::Rect rect; -// int channel; -// float threshold; - -// Feature(int x, int y, int w, int h, int c, float t) : rect(cv::Rect(x, y, w, h)), channel(c), threshold(t) {} -// Feature(cv::Rect r, int c, float t) : rect(r), channel(c), threshold(t) {} - -// Feature rescale(float relScale) -// { -// cv::Rect r(cvRound(rect.x * relScale), cvRound(rect.y * relScale), cvRound(rect.width * relScale), cvRound(rect.height * relScale)); -// return Feature( r, channel, threshold * CascadeIntrinsics::getFor(channel, relScale)); -// } -// }; -// } - - - - cv::SoftCascade::SoftCascade() : filds(0) {} cv::SoftCascade::SoftCascade( const string& filename, const float minScale, const float maxScale) @@ -318,38 +317,9 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const if (!fs.isOpened()) return false; filds = new Filds; - if (!(*filds).fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - - // //////////////// - // // temp fixture - // Filds& flds = *filds; - // flds.octaves.push_back(0.5f); - // flds.octaves.push_back(1.0f); - // flds.octaves.push_back(2.0f); - // flds.octaves.push_back(4.0f); - // flds.octaves.push_back(8.0f); - - // // scales calculations - // std::vector levels; - - // pyrLevels(FRAME_WIDTH, FRAME_HEIGHT, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT, TOTAL_SCALES, minScale, maxScale, levels); - - // for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) - // { - // float minAbsLog = FLT_MAX; - // for (std::vector::iterator oct = flds.octaves.begin(); oct < flds.octaves.end(); ++oct) - // { - // float logOctave = log(*oct); - // float logAbsScale = fabs((*level).logFactor - logOctave); - - // if(logAbsScale < minAbsLog) - // (*level).assign(*oct, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); - - // } - // } - - // load cascade from xml - // read(const FileNode &root) + Filds& flds = *filds; + if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; + // flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); return true; } From b0b85f36f61b4b5a79ff7cbe035d3829556f0401 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 5 Sep 2012 12:36:37 +0400 Subject: [PATCH 09/56] add test for soft cascade detect method --- .../include/opencv2/objdetect/objdetect.hpp | 10 ++++++++-- modules/objdetect/src/softcascade.cpp | 12 +++++++++--- modules/objdetect/test/test_softcascade.cpp | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 9bdc914627..9fab7410ae 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -493,12 +493,18 @@ protected: class CV_EXPORTS SoftCascade { public: + //! empty cascade will be created. SoftCascade(); + + //! cascade will be loaded from file "filename" SoftCascade( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); - virtual ~SoftCascade(); bool load( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); - virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, double factor = 1.05, int step = 4, int rejectfactor = 1); + virtual ~SoftCascade(); + + //! return vector of bounding boxes. Each box contains detected object + virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, + int step = 4, int rejectfactor = 1); protected: virtual void detectForOctave(int octave); diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 065fb0d8b5..62d58f2860 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -319,14 +319,20 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const filds = new Filds; Filds& flds = *filds; if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - // flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); + flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); return true; } void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - const double factor, const int step, const int rejectfactor) -{} + const int step, const int rejectfactor) +{ + // only color images are supperted + CV_Assert(image.type() == CV_8UC3); + + // only this window size allowed + CV_Assert(image.cols == 640 && image.rows == 480); +} void cv::SoftCascade::detectForOctave(const int octave) {} \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index a7b8548de8..d283d06085 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -47,4 +47,23 @@ TEST(SoftCascade, readCascade) cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(xml)); +} + +TEST(SoftCascade, Detect) +{ + std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/softcascade.xml"; + std::cout << "PATH: "<< xml << std::endl; + cv::SoftCascade cascade; + ASSERT_TRUE(cascade.load(xml)); + + cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000006_0.png"); + ASSERT_FALSE(colored.empty()); + + std::vector objectBoxes; + std::vector rois; + rois.push_back(cv::Rect(0, 0, 640, 480)); + ASSERT_NO_THROW( + { + cascade.detectMultiScale(colored, rois, objectBoxes); + }); } \ No newline at end of file From 695827050f3b94500e99e9128850ab4daab9d7f9 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 5 Sep 2012 17:44:28 +0400 Subject: [PATCH 10/56] Integral images for ICF --- .../include/opencv2/objdetect/objdetect.hpp | 1 + modules/objdetect/src/softcascade.cpp | 88 ++++++++++++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 9fab7410ae..0ef391d042 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -507,6 +507,7 @@ public: int step = 4, int rejectfactor = 1); protected: + virtual void detectInRoi(); virtual void detectForOctave(int octave); // virtual bool detectSingleScale( const Mat& image, int stripCount, Size processingRectSize, // int stripSize, int yStep, double factor, vector& candidates, diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 62d58f2860..cace66799f 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -59,7 +59,7 @@ namespace { Octave(){} Octave(const cv::FileNode& fn) : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]) - {printf("octave: %f %d\n", scale, stages);} + {/*printf("octave: %f %d\n", scale, stages);*/} }; static const char *SC_STAGE_THRESHOLD = "stageThreshold"; @@ -72,7 +72,7 @@ namespace { Stage(){} Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]), weight((float)fn[SC_STAGE_WEIGHT]) - {printf(" stage: %f %f\n",threshold, weight);} + {/*printf(" stage: %f %f\n",threshold, weight);*/} }; // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper @@ -131,7 +131,8 @@ namespace { cv::FileNode rn = fn[SC_F_RECT]; cv::FileNodeIterator r_it = rn.begin(); rect = cv::Rect(*(r_it++), *(r_it++), *(r_it++), *(r_it++)); - printf(" feature: %f %d %d [%d %d %d %d]\n",threshold, direction, channel, rect.x, rect.y, rect.width, rect.height);} + // printf(" feature: %f %d %d [%d %d %d %d]\n",threshold, direction, channel, rect.x, rect.y, rect.width, rect.height); + } Feature rescale(float relScale) { @@ -324,14 +325,95 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } +namespace { + + void calcHistBins(const cv::Mat& grey, std::vector& histInts, const int bins) + { + CV_Assert( grey.type() == CV_8U); + const int rows = grey.rows + 1; + const int cols = grey.cols + 1; + cv::Size intSumSize(cols, rows); + + histInts.clear(); + std::vector hist; + for (int bin = 0; bin < bins; ++bin) + { + hist.push_back(cv::Mat(rows, cols, CV_32FC1)); + } + cv::Mat df_dx, df_dy, mag, angle; + cv::Sobel(grey, df_dx, CV_32F, 1, 0); + cv::Sobel(grey, df_dy, CV_32F, 0, 1); + + cv::cartToPolar(df_dx, df_dy, mag, angle, true); + + const float magnitudeScaling = 1.0 / sqrt(2); + mag *= magnitudeScaling; + angle /= 60; + + for (int h = 0; h < mag.rows; ++h) + { + float* magnitude = mag.ptr(h); + float* ang = angle.ptr(h); + + for (int w = 0; w < mag.cols; ++w) + { + hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; + } + } + + for (int bin = 0; bin < bins; ++bin) + { + cv::Mat sum; + cv::integral(hist[bin], sum); + histInts.push_back(sum); + } + + cv::Mat magIntegral; + cv::integral(mag, magIntegral, mag.depth()); + } + + struct Integrals + { + /* data */ + }; +} + +void cv::SoftCascade::detectInRoi() +{} + + void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, const int step, const int rejectfactor) { + typedef std::vector::const_iterator RIter_t; // only color images are supperted CV_Assert(image.type() == CV_8UC3); // only this window size allowed CV_Assert(image.cols == 640 && image.rows == 480); + + objects.clear(); + + // create integrals + cv::Mat luv; + cv::cvtColor(image, luv, CV_BGR2Luv); + + cv::Mat luvIntegral; + cv::integral(luv, luvIntegral); + + cv::Mat grey; + cv::cvtColor(image, grey, CV_RGB2GRAY); + + std::vector hist; + const int bins = 6; + calcHistBins(grey, hist, bins); + + for (RIter_t it = rois.begin(); it != rois.end(); ++it) + { + const cv::Rect& roi = *it; + // detectInRoi(roi, objects, step); + } + } void cv::SoftCascade::detectForOctave(const int octave) From c04725b6812902c16e8027787365e66be5ac16ae Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 5 Sep 2012 18:29:13 +0400 Subject: [PATCH 11/56] add apply cascade method --- modules/gpu/CMakeLists.txt | 8 ++-- .../include/opencv2/objdetect/objdetect.hpp | 1 - modules/objdetect/src/softcascade.cpp | 46 ++++++++++++++----- modules/objdetect/test/test_softcascade.cpp | 6 +-- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/modules/gpu/CMakeLists.txt b/modules/gpu/CMakeLists.txt index 580e7dfb99..78aafcf928 100644 --- a/modules/gpu/CMakeLists.txt +++ b/modules/gpu/CMakeLists.txt @@ -15,10 +15,10 @@ file(GLOB lib_cuda_hdrs "src/cuda/*.hpp" "src/cuda/*.h") file(GLOB lib_srcs "src/*.cpp") file(GLOB lib_cuda "src/cuda/*.cu*") -source_group("Include" FILES ${lib_hdrs}) -source_group("Src\\Host" FILES ${lib_srcs} ${lib_int_hdrs}) -source_group("Src\\Cuda" FILES ${lib_cuda} ${lib_cuda_hdrs}) -source_group("Device" FILES ${lib_device_hdrs}) +source_group("Include" FILES ${lib_hdrs}) +source_group("Src\\Host" FILES ${lib_srcs} ${lib_int_hdrs}) +source_group("Src\\Cuda" FILES ${lib_cuda} ${lib_cuda_hdrs}) +source_group("Device" FILES ${lib_device_hdrs}) source_group("Device\\Detail" FILES ${lib_device_hdrs_detail}) if (HAVE_CUDA) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 0ef391d042..9fab7410ae 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -507,7 +507,6 @@ public: int step = 4, int rejectfactor = 1); protected: - virtual void detectInRoi(); virtual void detectForOctave(int octave); // virtual bool detectSingleScale( const Mat& image, int stripCount, Size processingRectSize, // int stripSize, int yStep, double factor, vector& candidates, diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index cace66799f..fa94052783 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -164,6 +164,15 @@ namespace { float relScale() {return (factor / octave); } }; + + struct Integral + { + cv::Mat magnitude; + std::vector hist; + cv::Mat luv; + + Integral(cv::Mat m, std::vector h, cv::Mat l) : magnitude(m), hist(h), luv(l) {} + }; } struct cv::SoftCascade::Filds @@ -181,6 +190,26 @@ struct cv::SoftCascade::Filds std::vector features; std::vector levels; + typedef std::vector::iterator stIter_t; + + // carrently roi must be save for out of ranges. + void detectInRoi(const cv::Rect& roi, const Integral& ints, std::vector& objects, const int step) + { + for (int dy = roi.y; dy < roi.height; dy+=step) + for (int dx = roi.x; dx < roi.width; dx += step) + { + applyCascade(ints, dx, dy); + } + } + + void applyCascade(const Integral& ints, const int x, const int y) + { + for (stIter_t sIt = sIt.begin(); sIt != stages.end(); ++sIt) + { + Stage stage& = *sIt; + } + } + // compute levels of full pyramid void calcLevels(int frameW, int frameH, int scales) { @@ -327,7 +356,7 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const namespace { - void calcHistBins(const cv::Mat& grey, std::vector& histInts, const int bins) + void calcHistBins(const cv::Mat& grey, cv::Mat magIntegral, std::vector& histInts, const int bins) { CV_Assert( grey.type() == CV_8U); const int rows = grey.rows + 1; @@ -368,18 +397,10 @@ namespace { histInts.push_back(sum); } - cv::Mat magIntegral; cv::integral(mag, magIntegral, mag.depth()); } - - struct Integrals - { - /* data */ - }; } -void cv::SoftCascade::detectInRoi() -{} void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, @@ -405,13 +426,16 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector hist; + cv::Mat magnitude; const int bins = 6; - calcHistBins(grey, hist, bins); + calcHistBins(grey, magnitude, hist, bins); + + Integral integrals(magnitude, hist, luv); for (RIter_t it = rois.begin(); it != rois.end(); ++it) { const cv::Rect& roi = *it; - // detectInRoi(roi, objects, step); + (*filds).detectInRoi(roi, integrals, objects, step); } } diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index d283d06085..bb02a091ce 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -62,8 +62,8 @@ TEST(SoftCascade, Detect) std::vector objectBoxes; std::vector rois; rois.push_back(cv::Rect(0, 0, 640, 480)); - ASSERT_NO_THROW( - { + // ASSERT_NO_THROW( + // { cascade.detectMultiScale(colored, rois, objectBoxes); - }); + // }); } \ No newline at end of file From dc74ce20abc3002d7806809e40560785099bd470 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 12 Sep 2012 20:30:21 +0400 Subject: [PATCH 12/56] OpenCV friendly xml format for soft cascade --- .../include/opencv2/objdetect/objdetect.hpp | 28 +- modules/objdetect/src/softcascade.cpp | 487 ++++++++++-------- modules/objdetect/test/test_softcascade.cpp | 5 +- 3 files changed, 279 insertions(+), 241 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 9fab7410ae..97d6122fe2 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -493,32 +493,36 @@ protected: class CV_EXPORTS SoftCascade { public: - //! empty cascade will be created. + //! An empty cascade will be created. SoftCascade(); - //! cascade will be loaded from file "filename" + //! Cascade will be created from file for scales from minScale to maxScale. + //! Param filename is a path to xml-serialized cascade. + //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. + //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. SoftCascade( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); + + //! cascade will be loaded from file "filename". The previous cascade will be destroyed. + //! Param filename is a path to xml-serialized cascade. + //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. + //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. bool load( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); virtual ~SoftCascade(); - //! return vector of bounding boxes. Each box contains detected object + //! return vector of bounding boxes. Each box contains one detected object virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, int step = 4, int rejectfactor = 1); protected: - virtual void detectForOctave(int octave); - // virtual bool detectSingleScale( const Mat& image, int stripCount, Size processingRectSize, - // int stripSize, int yStep, double factor, vector& candidates, - // vector& rejectLevels, vector& levelWeights, bool outputRejectLevels=false); enum { BOOST = 0 }; enum { - FRAME_WIDTH = 640, - FRAME_HEIGHT = 480, - TOTAL_SCALES = 55, - CLASSIFIERS = 5, - ORIG_OBJECT_WIDTH = 64, + FRAME_WIDTH = 640, + FRAME_HEIGHT = 480, + TOTAL_SCALES = 55, + CLASSIFIERS = 5, + ORIG_OBJECT_WIDTH = 64, ORIG_OBJECT_HEIGHT = 128 }; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index fa94052783..c00285a699 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -45,134 +45,160 @@ #include #include -#include +#include namespace { - static const char* SC_OCT_SCALE = "scale"; - static const char* SC_OCT_STAGES = "stageNum"; - struct Octave { float scale; int stages; + cv::Size size; + int shrinkage; + static const char *const SC_OCT_SCALE; + static const char *const SC_OCT_STAGES; + static const char *const SC_OCT_SHRINKAGE; Octave(){} - Octave(const cv::FileNode& fn) : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]) - {/*printf("octave: %f %d\n", scale, stages);*/} + Octave(cv::Size origObjSize, const cv::FileNode& fn) + : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), + size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), + shrinkage((int)fn[SC_OCT_SHRINKAGE]) + {} }; - static const char *SC_STAGE_THRESHOLD = "stageThreshold"; - static const char *SC_STAGE_WEIGHT = "weight"; + const char *const Octave::SC_OCT_SCALE = "scale"; + const char *const Octave::SC_OCT_STAGES = "stageNum"; + const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; + struct Stage { float threshold; - float weight; + + static const char *const SC_STAGE_THRESHOLD; Stage(){} - Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]), weight((float)fn[SC_STAGE_WEIGHT]) - {/*printf(" stage: %f %f\n",threshold, weight);*/} + Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]) + { std::cout << " stage: " << threshold << std::endl; } }; - // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper - struct CascadeIntrinsics + const char *const Stage::SC_STAGE_THRESHOLD = "stageThreshold"; + + struct Node { - static const float lambda = 1.099f, a = 0.89f; - static const float intrinsics[10][4]; - - static float getFor(int channel, float scaling) - { - CV_Assert(channel < 10); - - if ((scaling - 1.f) < FLT_EPSILON) - return 1.f; - - int ud = (int)(scaling < 1.f); - return intrinsics[channel][(ud << 1)] * pow(scaling, intrinsics[channel][(ud << 1) + 1]); - } + int feature; + float threshold; + Node(){} + Node(cv::FileNodeIterator& fIt) : feature((int)(*(fIt +=2)++)), threshold((float)(*(fIt++))) + { std::cout << " Node: " << feature << " " << threshold << std::endl; } }; - const float CascadeIntrinsics::intrinsics[10][4] = - { //da, db, ua, ub - // hog-like orientation bins - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - {a, lambda / log(2), 1, 2}, - // gradient magnitude - {a, lambda / log(2), 1, 2}, - // luv color channels - {1, 2, 1, 2}, - {1, 2, 1, 2}, - {1, 2, 1, 2} - }; - - static const char *SC_F_THRESHOLD = "threshold"; - static const char *SC_F_DIRECTION = "direction"; - static const char *SC_F_CHANNEL = "channel"; - static const char *SC_F_RECT = "rect"; struct Feature { - float threshold; - int direction; int channel; cv::Rect rect; + static const char * const SC_F_CHANNEL; + static const char * const SC_F_RECT; + Feature() {} - Feature(const cv::FileNode& fn) - : threshold((float)fn[SC_F_THRESHOLD]), direction((int)fn[SC_F_DIRECTION]), - channel((int)fn[SC_F_CHANNEL]) + Feature(const cv::FileNode& fn) : channel((int)fn[SC_F_CHANNEL]) { cv::FileNode rn = fn[SC_F_RECT]; - cv::FileNodeIterator r_it = rn.begin(); - rect = cv::Rect(*(r_it++), *(r_it++), *(r_it++), *(r_it++)); - // printf(" feature: %f %d %d [%d %d %d %d]\n",threshold, direction, channel, rect.x, rect.y, rect.width, rect.height); + cv::FileNodeIterator r_it = rn.end(); + rect = cv::Rect(*(--r_it), *(--r_it), *(--r_it), *(--r_it)); + std::cout << "feature: " << rect.x << " " << rect.y << " " << rect.width << " " << rect.height << " " << channel << std::endl; } - Feature rescale(float relScale) - { - Feature res(*this); - res.rect = cv::Rect (cvRound(rect.x * relScale), cvRound(rect.y * relScale), - cvRound(rect.width * relScale), cvRound(rect.height * relScale)); - res.threshold = threshold * CascadeIntrinsics::getFor(channel, relScale); - return res; - } +// Feature rescale(float relScale) +// { +// Feature res(*this); +// res.rect = cv::Rect (cvRound(rect.x * relScale), cvRound(rect.y * relScale), +// cvRound(rect.width * relScale), cvRound(rect.height * relScale)); +// res.threshold = threshold * CascadeIntrinsics::getFor(channel, relScale); +// return res; +// } }; - struct Level - { - int index; - float factor; - float logFactor; - int width; - int height; - float octave; - cv::Size objSize; + const char * const Feature::SC_F_CHANNEL = "channel"; + const char * const Feature::SC_F_RECT = "rect"; +// // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper +// struct CascadeIntrinsics +// { +// static const float lambda = 1.099f, a = 0.89f; +// static const float intrinsics[10][4]; - Level(int i,float f, float lf, int w, int h): index(i), factor(f), logFactor(lf), width(w), height(h), octave(0.f) {} +// static float getFor(int channel, float scaling) +// { +// CV_Assert(channel < 10); - void assign(float o, int detW, int detH) - { - octave = o; - objSize = cv::Size(cv::saturate_cast(detW * o), cv::saturate_cast(detH * o)); - } +// if ((scaling - 1.f) < FLT_EPSILON) +// return 1.f; - float relScale() {return (factor / octave); } - }; +// int ud = (int)(scaling < 1.f); +// return intrinsics[channel][(ud << 1)] * pow(scaling, intrinsics[channel][(ud << 1) + 1]); +// } - struct Integral - { - cv::Mat magnitude; - std::vector hist; - cv::Mat luv; +// }; - Integral(cv::Mat m, std::vector h, cv::Mat l) : magnitude(m), hist(h), luv(l) {} - }; +// const float CascadeIntrinsics::intrinsics[10][4] = +// { //da, db, ua, ub +// // hog-like orientation bins +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// {a, lambda / log(2), 1, 2}, +// // gradient magnitude +// {a, lambda / log(2), 1, 2}, +// // luv color channels +// {1, 2, 1, 2}, +// {1, 2, 1, 2}, +// {1, 2, 1, 2} +// }; + +// struct Level +// { +// int index; + +// float factor; +// float logFactor; + +// int width; +// int height; + +// Octave octave; + +// cv::Size objSize; +// cv::Size dWinSize; + +// static const float shrinkage = 0.25; + +// Level(int i,float f, float lf, int w, int h): index(i), factor(f), logFactor(lf), width(w), height(h), octave(Octave()) +// {} + +// void assign(const Octave& o, int detW, int detH) +// { +// octave = o; +// objSize = cv::Size(cv::saturate_cast(detW * o.scale), cv::saturate_cast(detH * o.scale)); +// } + +// float relScale() {return (factor / octave.scale); } +// float srScale() {return (factor / octave.scale * shrinkage); } +// }; + +// struct Integral +// { +// cv::Mat magnitude; +// std::vector hist; +// cv::Mat luv; + +// Integral(cv::Mat m, std::vector h, cv::Mat l) : magnitude(m), hist(h), luv(l) {} +// }; } struct cv::SoftCascade::Filds @@ -183,68 +209,72 @@ struct cv::SoftCascade::Filds int origObjWidth; int origObjHeight; - int noctaves; - std::vector octaves; std::vector stages; + std::vector nodes; + std::vector leaves; + std::vector features; - std::vector levels; - typedef std::vector::iterator stIter_t; + // typedef std::vector::iterator stIter_t; - // carrently roi must be save for out of ranges. - void detectInRoi(const cv::Rect& roi, const Integral& ints, std::vector& objects, const int step) - { - for (int dy = roi.y; dy < roi.height; dy+=step) - for (int dx = roi.x; dx < roi.width; dx += step) - { - applyCascade(ints, dx, dy); - } - } + // // carrently roi must be save for out of ranges. + // void detectInRoi(const cv::Rect& roi, const Integral& ints, std::vector& objects, const int step) + // { + // for (int dy = roi.y; dy < roi.height; dy+=step) + // for (int dx = roi.x; dx < roi.width; dx += step) + // { + // applyCascade(ints, dx, dy); + // } + // } - void applyCascade(const Integral& ints, const int x, const int y) - { - for (stIter_t sIt = sIt.begin(); sIt != stages.end(); ++sIt) - { - Stage stage& = *sIt; - } - } + // void applyCascade(const Integral& ints, const int x, const int y) + // { + // for (stIter_t sIt = stages.begin(); sIt != stages.end(); ++sIt) + // { + // Stage& stage = *sIt; + // } + // } - // compute levels of full pyramid - void calcLevels(int frameW, int frameH, int scales) - { - CV_Assert(scales > 1); - levels.clear(); - float logFactor = (log(maxScale) - log(minScale)) / (scales -1); + // // compute levels of full pyramid + // void calcLevels(int frameW, int frameH, int scales) + // { + // CV_Assert(scales > 1); + // levels.clear(); + // float logFactor = (log(maxScale) - log(minScale)) / (scales -1); - float scale = minScale; - for (int sc = 0; sc < scales; ++sc) - { - Level level(sc, scale, log(scale) + logFactor, - std::max(0.0f, frameW - (origObjWidth * scale)), std::max(0.0f, frameH - (origObjHeight * scale))); - if (!level.width || !level.height) - break; - else - levels.push_back(level); + // float scale = minScale; + // for (int sc = 0; sc < scales; ++sc) + // { + // Level level(sc, scale, log(scale), std::max(0.0f, frameW - (origObjWidth * scale)), std::max(0.0f, frameH - (origObjHeight * scale))); + // if (!level.width || !level.height) + // break; + // else + // levels.push_back(level); - if (fabs(scale - maxScale) < FLT_EPSILON) break; - scale = std::min(maxScale, expf(log(scale) + logFactor)); - } + // if (fabs(scale - maxScale) < FLT_EPSILON) break; + // scale = std::min(maxScale, expf(log(scale) + logFactor)); + // } - for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) - { - float minAbsLog = FLT_MAX; - for (std::vector::iterator oct = octaves.begin(); oct < octaves.end(); ++oct) - { - const Octave& octave =*oct; - float logOctave = log(octave.scale); - float logAbsScale = fabs((*level).logFactor - logOctave); + // for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) + // { + // float minAbsLog = FLT_MAX; + // for (std::vector::iterator oct = octaves.begin(); oct < octaves.end(); ++oct) + // { + // const Octave& octave =*oct; + // float logOctave = log(octave.scale); + // float logAbsScale = fabs((*level).logFactor - logOctave); - if(logAbsScale < minAbsLog) - (*level).assign(octave.scale, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); - } - } - } + + // if(logAbsScale < minAbsLog) + // { + // printf("######### %f %f %f %f\n", octave.scale, logOctave, logAbsScale, (*level).logFactor); + // minAbsLog = logAbsScale; + // (*level).assign(octave, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); + // } + // } + // } + // } bool fill(const FileNode &root, const float mins, const float maxs) { @@ -252,19 +282,22 @@ struct cv::SoftCascade::Filds maxScale = maxs; // cascade properties - const char *SC_STAGE_TYPE = "stageType"; - const char *SC_BOOST = "BOOST"; - const char *SC_FEATURE_TYPE = "featureType"; - const char *SC_ICF = "ICF"; - const char *SC_TREE_TYPE = "stageTreeType"; - const char *SC_STAGE_TH2 = "TH2"; - const char *SC_NUM_OCTAVES = "octavesNum"; - const char *SC_ORIG_W = "origObjWidth"; - const char *SC_ORIG_H = "origObjHeight"; + static const char *const SC_STAGE_TYPE = "stageType"; + static const char *const SC_BOOST = "BOOST"; - const char* SC_OCTAVES = "octaves"; - const char *SC_STAGES = "stages"; - const char *SC_FEATURES = "features"; + static const char *const SC_FEATURE_TYPE = "featureType"; + static const char *const SC_ICF = "ICF"; + + static const char *const SC_ORIG_W = "width"; + static const char *const SC_ORIG_H = "height"; + + static const char *const SC_OCTAVES = "octaves"; + static const char *const SC_STAGES = "stages"; + static const char *const SC_FEATURES = "features"; + + static const char *const SC_WEEK = "weakClassifiers"; + static const char *const SC_INTERNAL = "internalNodes"; + static const char *const SC_LEAF = "leafValues"; // only boost supported @@ -275,14 +308,6 @@ struct cv::SoftCascade::Filds string featureTypeStr = (string)root[SC_FEATURE_TYPE]; CV_Assert(featureTypeStr == SC_ICF); - // only trees of height 2 - string stageTreeTypeStr = (string)root[SC_TREE_TYPE]; - CV_Assert(stageTreeTypeStr == SC_STAGE_TH2); - - // not empty - noctaves = (int)root[SC_NUM_OCTAVES]; - CV_Assert(noctaves > 0); - origObjWidth = (int)root[SC_ORIG_W]; CV_Assert(origObjWidth == SoftCascade::ORIG_OBJECT_WIDTH); @@ -293,15 +318,17 @@ struct cv::SoftCascade::Filds FileNode fn = root[SC_OCTAVES]; if (fn.empty()) return false; - octaves.reserve(noctaves); + // octaves.reserve(noctaves); FileNodeIterator it = fn.begin(), it_end = fn.end(); for (; it != it_end; ++it) { FileNode fns = *it; - Octave octave = Octave(fns); + Octave octave(cv::Size(SoftCascade::ORIG_OBJECT_WIDTH, SoftCascade::ORIG_OBJECT_HEIGHT), fns); CV_Assert(octave.stages > 0); octaves.push_back(octave); - stages.reserve(stages.size() + octave.stages); + + FileNode ffs = fns[SC_FEATURES]; + if (ffs.empty()) return false; fns = fns[SC_STAGES]; if (fn.empty()) return false; @@ -313,14 +340,25 @@ struct cv::SoftCascade::Filds fns = *st; stages.push_back(Stage(fns)); - fns = fns[SC_FEATURES]; - // for each feature for tree. features stored in order {root, left, right} + fns = fns[SC_WEEK]; FileNodeIterator ftr = fns.begin(), ft_end = fns.end(); for (; ftr != ft_end; ++ftr) { - features.push_back(Feature(*ftr)); + fns = (*ftr)[SC_INTERNAL]; + FileNodeIterator inIt = fns.begin(), inIt_end = fns.end(); + for (; inIt != inIt_end;) + nodes.push_back(Node(inIt)); + + fns = (*ftr)[SC_LEAF]; + inIt = fns.begin(), inIt_end = fns.end(); + for (; inIt != inIt_end; ++inIt) + leaves.push_back((float)(*inIt)); } } + + st = ffs.begin(), st_end = ffs.end(); + for (; st != st_end; ++st ) + features.push_back(Feature(*st)); } return true; } @@ -349,7 +387,7 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const filds = new Filds; Filds& flds = *filds; if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); + // // flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); return true; } @@ -358,87 +396,84 @@ namespace { void calcHistBins(const cv::Mat& grey, cv::Mat magIntegral, std::vector& histInts, const int bins) { - CV_Assert( grey.type() == CV_8U); - const int rows = grey.rows + 1; - const int cols = grey.cols + 1; - cv::Size intSumSize(cols, rows); + // CV_Assert( grey.type() == CV_8U); + // const int rows = grey.rows + 1; + // const int cols = grey.cols + 1; + // cv::Size intSumSize(cols, rows); - histInts.clear(); - std::vector hist; - for (int bin = 0; bin < bins; ++bin) - { - hist.push_back(cv::Mat(rows, cols, CV_32FC1)); - } - cv::Mat df_dx, df_dy, mag, angle; - cv::Sobel(grey, df_dx, CV_32F, 1, 0); - cv::Sobel(grey, df_dy, CV_32F, 0, 1); + // histInts.clear(); + // std::vector hist; + // for (int bin = 0; bin < bins; ++bin) + // { + // hist.push_back(cv::Mat(rows, cols, CV_32FC1)); + // } + // cv::Mat df_dx, df_dy, mag, angle; + // cv::Sobel(grey, df_dx, CV_32F, 1, 0); + // cv::Sobel(grey, df_dy, CV_32F, 0, 1); - cv::cartToPolar(df_dx, df_dy, mag, angle, true); + // cv::cartToPolar(df_dx, df_dy, mag, angle, true); - const float magnitudeScaling = 1.0 / sqrt(2); - mag *= magnitudeScaling; - angle /= 60; + // const float magnitudeScaling = 1.0 / sqrt(2); + // mag *= magnitudeScaling; + // angle /= 60; - for (int h = 0; h < mag.rows; ++h) - { - float* magnitude = mag.ptr(h); - float* ang = angle.ptr(h); + // for (int h = 0; h < mag.rows; ++h) + // { + // float* magnitude = mag.ptr(h); + // float* ang = angle.ptr(h); - for (int w = 0; w < mag.cols; ++w) - { - hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; - } - } + // for (int w = 0; w < mag.cols; ++w) + // { + // hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; + // } + // } - for (int bin = 0; bin < bins; ++bin) - { - cv::Mat sum; - cv::integral(hist[bin], sum); - histInts.push_back(sum); - } + // for (int bin = 0; bin < bins; ++bin) + // { + // cv::Mat sum; + // cv::integral(hist[bin], sum); + // histInts.push_back(sum); + // } - cv::integral(mag, magIntegral, mag.depth()); + // cv::integral(mag, magIntegral, mag.depth()); } } void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - const int step, const int rejectfactor) + const int step, const int rejectfactor)// add step scaling { - typedef std::vector::const_iterator RIter_t; - // only color images are supperted - CV_Assert(image.type() == CV_8UC3); + // typedef std::vector::const_iterator RIter_t; + // // only color images are supperted + // CV_Assert(image.type() == CV_8UC3); - // only this window size allowed - CV_Assert(image.cols == 640 && image.rows == 480); + // // only this window size allowed + // CV_Assert(image.cols == 640 && image.rows == 480); - objects.clear(); + // objects.clear(); - // create integrals - cv::Mat luv; - cv::cvtColor(image, luv, CV_BGR2Luv); + // // create integrals + // cv::Mat luv; + // cv::cvtColor(image, luv, CV_BGR2Luv); - cv::Mat luvIntegral; - cv::integral(luv, luvIntegral); + // cv::Mat luvIntegral; + // cv::integral(luv, luvIntegral); - cv::Mat grey; - cv::cvtColor(image, grey, CV_RGB2GRAY); + // cv::Mat grey; + // cv::cvtColor(image, grey, CV_RGB2GRAY); - std::vector hist; - cv::Mat magnitude; - const int bins = 6; - calcHistBins(grey, magnitude, hist, bins); + // std::vector hist; + // cv::Mat magnitude; + // const int bins = 6; + // calcHistBins(grey, magnitude, hist, bins); - Integral integrals(magnitude, hist, luv); + // Integral integrals(magnitude, hist, luv); - for (RIter_t it = rois.begin(); it != rois.end(); ++it) - { - const cv::Rect& roi = *it; - (*filds).detectInRoi(roi, integrals, objects, step); - } + // for (RIter_t it = rois.begin(); it != rois.end(); ++it) + // { + // const cv::Rect& roi = *it; + // (*filds).detectInRoi(roi, integrals, objects, step); + // } -} - -void cv::SoftCascade::detectForOctave(const int octave) -{} \ No newline at end of file +} \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index bb02a091ce..ccf713957b 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -43,16 +43,15 @@ TEST(SoftCascade, readCascade) { - std::string xml = "/home/kellan/icf-template.xml"; + std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/icf-template.xml"; cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(xml)); } -TEST(SoftCascade, Detect) +TEST(SoftCascade, detect) { std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/softcascade.xml"; - std::cout << "PATH: "<< xml << std::endl; cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(xml)); From 6f53be4102d59340198d682b2e7a71e4b57400b2 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 13 Sep 2012 15:15:26 +0400 Subject: [PATCH 13/56] shrinking before integral calculation --- modules/objdetect/src/softcascade.cpp | 166 ++++++++++++-------- modules/objdetect/test/test_softcascade.cpp | 2 +- 2 files changed, 99 insertions(+), 69 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index c00285a699..6cb9d693fe 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -161,8 +161,8 @@ namespace { // {1, 2, 1, 2} // }; -// struct Level -// { + struct Level + { // int index; // float factor; @@ -189,7 +189,7 @@ namespace { // float relScale() {return (factor / octave.scale); } // float srScale() {return (factor / octave.scale * shrinkage); } -// }; + }; // struct Integral // { @@ -209,6 +209,8 @@ struct cv::SoftCascade::Filds int origObjWidth; int origObjHeight; + int shrinkage; + std::vector octaves; std::vector stages; std::vector nodes; @@ -216,6 +218,8 @@ struct cv::SoftCascade::Filds std::vector features; + std::vector levels; + // typedef std::vector::iterator stIter_t; // // carrently roi must be save for out of ranges. @@ -236,11 +240,11 @@ struct cv::SoftCascade::Filds // } // } - // // compute levels of full pyramid - // void calcLevels(int frameW, int frameH, int scales) - // { - // CV_Assert(scales > 1); - // levels.clear(); + // compute levels of full pyramid + void calcLevels(int frameW, int frameH, int scales) + { + CV_Assert(scales > 1); + levels.clear(); // float logFactor = (log(maxScale) - log(minScale)) / (scales -1); // float scale = minScale; @@ -274,7 +278,7 @@ struct cv::SoftCascade::Filds // } // } // } - // } + } bool fill(const FileNode &root, const float mins, const float maxs) { @@ -360,15 +364,16 @@ struct cv::SoftCascade::Filds for (; st != st_end; ++st ) features.push_back(Feature(*st)); } + + shrinkage = octaves[0].shrinkage; return true; } }; cv::SoftCascade::SoftCascade() : filds(0) {} -cv::SoftCascade::SoftCascade( const string& filename, const float minScale, const float maxScale) +cv::SoftCascade::SoftCascade( const string& filename, const float minScale, const float maxScale) : filds(0) { - filds = new Filds; load(filename, minScale, maxScale); } cv::SoftCascade::~SoftCascade() @@ -378,7 +383,8 @@ cv::SoftCascade::~SoftCascade() bool cv::SoftCascade::load( const string& filename, const float minScale, const float maxScale) { - delete filds; + if (filds) + delete filds; filds = 0; cv::FileStorage fs(filename, FileStorage::READ); @@ -387,56 +393,92 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const filds = new Filds; Filds& flds = *filds; if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - // // flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); + flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); return true; } namespace { - void calcHistBins(const cv::Mat& grey, cv::Mat magIntegral, std::vector& histInts, const int bins) + void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector& histInts, const int bins, int shrinkage) { - // CV_Assert( grey.type() == CV_8U); - // const int rows = grey.rows + 1; - // const int cols = grey.cols + 1; - // cv::Size intSumSize(cols, rows); + CV_Assert( grey.type() == CV_8U); - // histInts.clear(); - // std::vector hist; - // for (int bin = 0; bin < bins; ++bin) - // { - // hist.push_back(cv::Mat(rows, cols, CV_32FC1)); - // } - // cv::Mat df_dx, df_dy, mag, angle; - // cv::Sobel(grey, df_dx, CV_32F, 1, 0); - // cv::Sobel(grey, df_dy, CV_32F, 0, 1); + float scale = 1.f / shrinkage; - // cv::cartToPolar(df_dx, df_dy, mag, angle, true); + const int rows = grey.rows + 1; + const int cols = grey.cols + 1; + cv::Size intSumSize(cols, rows); - // const float magnitudeScaling = 1.0 / sqrt(2); - // mag *= magnitudeScaling; - // angle /= 60; + histInts.clear(); + std::vector hist; + for (int bin = 0; bin < bins; ++bin) + { + hist.push_back(cv::Mat(rows, cols, CV_32FC1)); + } - // for (int h = 0; h < mag.rows; ++h) - // { - // float* magnitude = mag.ptr(h); - // float* ang = angle.ptr(h); + cv::Mat df_dx, df_dy, mag, angle; + cv::Sobel(grey, df_dx, CV_32F, 1, 0); + cv::Sobel(grey, df_dy, CV_32F, 0, 1); - // for (int w = 0; w < mag.cols; ++w) - // { - // hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; - // } - // } + cv::cartToPolar(df_dx, df_dy, mag, angle, true); - // for (int bin = 0; bin < bins; ++bin) - // { - // cv::Mat sum; - // cv::integral(hist[bin], sum); - // histInts.push_back(sum); - // } + const float magnitudeScaling = 1.0 / sqrt(2); + mag *= magnitudeScaling; + angle /= 60; - // cv::integral(mag, magIntegral, mag.depth()); + for (int h = 0; h < mag.rows; ++h) + { + float* magnitude = mag.ptr(h); + float* ang = angle.ptr(h); + + for (int w = 0; w < mag.cols; ++w) + { + hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; + } + } + + for (int bin = 0; bin < bins; ++bin) + { + cv::Mat shrunk, sum; + cv::resize(hist[bin], shrunk, cv::Size(), scale, scale, cv::INTER_AREA); + cv::integral(shrunk, sum); + histInts.push_back(sum); + } + + cv::Mat shrMag; + cv::resize(mag, shrMag, cv::Size(), scale, scale, cv::INTER_AREA); + + cv::integral(shrMag, magIntegral, mag.depth()); } + + struct ChannelStorage + { + std::vector hog; + cv::Mat luv; + cv::Mat magnitude; + + int shrinkage; + + enum {HOG_BINS = 6}; + + ChannelStorage() {} + ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) + { + cv::Mat _luv; + cv::cvtColor(colored, _luv, CV_BGR2Luv); + + cv::integral(luv, luv); + + cv::Mat grey; + cv::cvtColor(colored, grey, CV_RGB2GRAY); + + calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); + std::cout << magnitude.cols << " " << magnitude.rows << std::endl; + cv::imshow("1", magnitude); + cv::waitKey(0); + } + }; } @@ -444,31 +486,19 @@ namespace { void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, const int step, const int rejectfactor)// add step scaling { - // typedef std::vector::const_iterator RIter_t; - // // only color images are supperted - // CV_Assert(image.type() == CV_8UC3); + typedef std::vector::const_iterator RIter_t; + // only color images are supperted + CV_Assert(image.type() == CV_8UC3); - // // only this window size allowed - // CV_Assert(image.cols == 640 && image.rows == 480); + // only this window size allowed + CV_Assert(image.cols == 640 && image.rows == 480); - // objects.clear(); + objects.clear(); - // // create integrals - // cv::Mat luv; - // cv::cvtColor(image, luv, CV_BGR2Luv); + const Filds& fld = *filds; - // cv::Mat luvIntegral; - // cv::integral(luv, luvIntegral); - - // cv::Mat grey; - // cv::cvtColor(image, grey, CV_RGB2GRAY); - - // std::vector hist; - // cv::Mat magnitude; - // const int bins = 6; - // calcHistBins(grey, magnitude, hist, bins); - - // Integral integrals(magnitude, hist, luv); + // create integrals + ChannelStorage storage(image, fld.shrinkage); // for (RIter_t it = rois.begin(); it != rois.end(); ++it) // { diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index ccf713957b..7311ad2914 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -51,7 +51,7 @@ TEST(SoftCascade, readCascade) TEST(SoftCascade, detect) { - std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/softcascade.xml"; + std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(xml)); From 801368ee82d68c1a502e97c19f8a621042050e90 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 13 Sep 2012 18:58:14 +0400 Subject: [PATCH 14/56] refactoring --- modules/objdetect/src/softcascade.cpp | 268 +++++++++++++------------- 1 file changed, 137 insertions(+), 131 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 6cb9d693fe..8dfe14b6a1 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -49,69 +49,93 @@ namespace { - struct Octave +struct Octave +{ + float scale; + int stages; + cv::Size size; + int shrinkage; + + static const char *const SC_OCT_SCALE; + static const char *const SC_OCT_STAGES; + static const char *const SC_OCT_SHRINKAGE; + + Octave() : scale(0), stages(0), size(cv::Size()), shrinkage(0) {} + Octave(cv::Size origObjSize, const cv::FileNode& fn) + : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), + size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), + shrinkage((int)fn[SC_OCT_SHRINKAGE]) + {} +}; + +const char *const Octave::SC_OCT_SCALE = "scale"; +const char *const Octave::SC_OCT_STAGES = "stageNum"; +const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; + + +struct Stage +{ + float threshold; + + static const char *const SC_STAGE_THRESHOLD; + + Stage(){} + Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]){} +}; + +const char *const Stage::SC_STAGE_THRESHOLD = "stageThreshold"; + +struct Node +{ + int feature; + float threshold; + + Node(){} + Node(cv::FileNodeIterator& fIt) : feature((int)(*(fIt +=2)++)), threshold((float)(*(fIt++))){} +}; + + +struct Feature +{ + int channel; + cv::Rect rect; + + static const char * const SC_F_CHANNEL; + static const char * const SC_F_RECT; + + Feature() {} + Feature(const cv::FileNode& fn) : channel((int)fn[SC_F_CHANNEL]) { - float scale; - int stages; - cv::Size size; - int shrinkage; - static const char *const SC_OCT_SCALE; - static const char *const SC_OCT_STAGES; - static const char *const SC_OCT_SHRINKAGE; + cv::FileNode rn = fn[SC_F_RECT]; + cv::FileNodeIterator r_it = rn.end(); + rect = cv::Rect(*(--r_it), *(--r_it), *(--r_it), *(--r_it)); + // std::cout << "feature: " << rect.x << " " << rect.y << " " << rect.width + //<< " " << rect.height << " " << channel << std::endl; + } +}; - Octave(){} - Octave(cv::Size origObjSize, const cv::FileNode& fn) - : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), - size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), - shrinkage((int)fn[SC_OCT_SHRINKAGE]) - {} - }; +const char * const Feature::SC_F_CHANNEL = "channel"; +const char * const Feature::SC_F_RECT = "rect"; - const char *const Octave::SC_OCT_SCALE = "scale"; - const char *const Octave::SC_OCT_STAGES = "stageNum"; - const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; +struct Level +{ + const Octave* octave; + + float origScale; + float relScale; + float shrScale; + + cv::Size workRect; + cv::Size objSize; - struct Stage - { - float threshold; - - static const char *const SC_STAGE_THRESHOLD; - - Stage(){} - Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]) - { std::cout << " stage: " << threshold << std::endl; } - }; - - const char *const Stage::SC_STAGE_THRESHOLD = "stageThreshold"; - - struct Node - { - int feature; - float threshold; - - Node(){} - Node(cv::FileNodeIterator& fIt) : feature((int)(*(fIt +=2)++)), threshold((float)(*(fIt++))) - { std::cout << " Node: " << feature << " " << threshold << std::endl; } - }; - - - struct Feature - { - int channel; - cv::Rect rect; - - static const char * const SC_F_CHANNEL; - static const char * const SC_F_RECT; - - Feature() {} - Feature(const cv::FileNode& fn) : channel((int)fn[SC_F_CHANNEL]) - { - cv::FileNode rn = fn[SC_F_RECT]; - cv::FileNodeIterator r_it = rn.end(); - rect = cv::Rect(*(--r_it), *(--r_it), *(--r_it), *(--r_it)); - std::cout << "feature: " << rect.x << " " << rect.y << " " << rect.width << " " << rect.height << " " << channel << std::endl; - } + // TiDo not reounding + Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) + : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / shrinkage), + workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), + objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) + {} +}; // Feature rescale(float relScale) // { @@ -121,10 +145,7 @@ namespace { // res.threshold = threshold * CascadeIntrinsics::getFor(channel, relScale); // return res; // } - }; - const char * const Feature::SC_F_CHANNEL = "channel"; - const char * const Feature::SC_F_RECT = "rect"; // // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper // struct CascadeIntrinsics // { @@ -161,44 +182,6 @@ namespace { // {1, 2, 1, 2} // }; - struct Level - { -// int index; - -// float factor; -// float logFactor; - -// int width; -// int height; - -// Octave octave; - -// cv::Size objSize; -// cv::Size dWinSize; - -// static const float shrinkage = 0.25; - -// Level(int i,float f, float lf, int w, int h): index(i), factor(f), logFactor(lf), width(w), height(h), octave(Octave()) -// {} - -// void assign(const Octave& o, int detW, int detH) -// { -// octave = o; -// objSize = cv::Size(cv::saturate_cast(detW * o.scale), cv::saturate_cast(detH * o.scale)); -// } - -// float relScale() {return (factor / octave.scale); } -// float srScale() {return (factor / octave.scale * shrinkage); } - }; - -// struct Integral -// { -// cv::Mat magnitude; -// std::vector hist; -// cv::Mat luv; - -// Integral(cv::Mat m, std::vector h, cv::Mat l) : magnitude(m), hist(h), luv(l) {} -// }; } struct cv::SoftCascade::Filds @@ -240,44 +223,70 @@ struct cv::SoftCascade::Filds // } // } + typedef std::vector::iterator octIt_t; + + octIt_t fitOctave(const float& logFactor) + { + float minAbsLog = FLT_MAX; + octIt_t res = octaves.begin(); + for (octIt_t oct = octaves.begin(); oct < octaves.end(); ++oct) + { + const Octave& octave =*oct; + float logOctave = log(octave.scale); + float logAbsScale = fabs(logFactor - logOctave); + + if(logAbsScale < minAbsLog) + { + res = oct; + minAbsLog = logAbsScale; + } + } + return res; + } + // compute levels of full pyramid void calcLevels(int frameW, int frameH, int scales) { CV_Assert(scales > 1); levels.clear(); - // float logFactor = (log(maxScale) - log(minScale)) / (scales -1); + float logFactor = (log(maxScale) - log(minScale)) / (scales -1); - // float scale = minScale; - // for (int sc = 0; sc < scales; ++sc) - // { - // Level level(sc, scale, log(scale), std::max(0.0f, frameW - (origObjWidth * scale)), std::max(0.0f, frameH - (origObjHeight * scale))); - // if (!level.width || !level.height) - // break; - // else - // levels.push_back(level); + float scale = minScale; + for (int sc = 0; sc < scales; ++sc) + { + int width = std::max(0.0f, frameW - (origObjWidth * scale)); + int height = std::max(0.0f, frameH - (origObjHeight * scale)); - // if (fabs(scale - maxScale) < FLT_EPSILON) break; - // scale = std::min(maxScale, expf(log(scale) + logFactor)); - // } - - // for (std::vector::iterator level = levels.begin(); level < levels.end(); ++level) - // { - // float minAbsLog = FLT_MAX; - // for (std::vector::iterator oct = octaves.begin(); oct < octaves.end(); ++oct) - // { - // const Octave& octave =*oct; - // float logOctave = log(octave.scale); - // float logAbsScale = fabs((*level).logFactor - logOctave); + float logScale = log(scale); + octIt_t fit = fitOctave(logScale); - // if(logAbsScale < minAbsLog) - // { - // printf("######### %f %f %f %f\n", octave.scale, logOctave, logAbsScale, (*level).logFactor); - // minAbsLog = logAbsScale; - // (*level).assign(octave, ORIG_OBJECT_WIDTH, ORIG_OBJECT_HEIGHT); - // } - // } - // } + Level level(*fit, scale, shrinkage, width, height); + + if (!width || !height) + break; + else + levels.push_back(level); + + if (fabs(scale - maxScale) < FLT_EPSILON) break; + scale = std::min(maxScale, expf(log(scale) + logFactor)); + + // std::cout << "level scale " + // << levels[sc].origScale + // << " octeve " + // << levels[sc].octave->scale + // << " " + // << levels[sc].relScale + // << " " << levels[sc].shrScale + // << " [" << levels[sc].objSize.width + // << " " << levels[sc].objSize.height << "] [" + // << levels[sc].workRect.width << " " << levels[sc].workRect.height << std::endl; + } + + return; + + + std::cout << std::endl << std::endl << std::endl; } bool fill(const FileNode &root, const float mins, const float maxs) @@ -474,9 +483,6 @@ namespace { cv::cvtColor(colored, grey, CV_RGB2GRAY); calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); - std::cout << magnitude.cols << " " << magnitude.rows << std::endl; - cv::imshow("1", magnitude); - cv::waitKey(0); } }; } From 8d90b973b0517cfbc1cc2a568a6b10bd5f4d906f Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 13 Sep 2012 21:19:01 +0400 Subject: [PATCH 15/56] add detectAt to soft cascade --- modules/objdetect/src/softcascade.cpp | 239 ++++++++++++++------------ 1 file changed, 130 insertions(+), 109 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 8dfe14b6a1..6e8c6ee369 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -66,6 +66,8 @@ struct Octave size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), shrinkage((int)fn[SC_OCT_SHRINKAGE]) {} + + int index() const {return (int)log(scale);} }; const char *const Octave::SC_OCT_SCALE = "scale"; @@ -182,6 +184,89 @@ struct Level // {1, 2, 1, 2} // }; + +void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector& histInts, + const int bins, int shrinkage) +{ + CV_Assert( grey.type() == CV_8U); + + float scale = 1.f / shrinkage; + + const int rows = grey.rows + 1; + const int cols = grey.cols + 1; + cv::Size intSumSize(cols, rows); + + histInts.clear(); + std::vector hist; + for (int bin = 0; bin < bins; ++bin) + { + hist.push_back(cv::Mat(rows, cols, CV_32FC1)); + } + + cv::Mat df_dx, df_dy, mag, angle; + cv::Sobel(grey, df_dx, CV_32F, 1, 0); + cv::Sobel(grey, df_dy, CV_32F, 0, 1); + + cv::cartToPolar(df_dx, df_dy, mag, angle, true); + + const float magnitudeScaling = 1.0 / sqrt(2); + mag *= magnitudeScaling; + angle /= 60; + + for (int h = 0; h < mag.rows; ++h) + { + float* magnitude = mag.ptr(h); + float* ang = angle.ptr(h); + + for (int w = 0; w < mag.cols; ++w) + { + hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; + } + } + + for (int bin = 0; bin < bins; ++bin) + { + cv::Mat shrunk, sum; + cv::resize(hist[bin], shrunk, cv::Size(), scale, scale, cv::INTER_AREA); + cv::integral(shrunk, sum); + histInts.push_back(sum); + } + + cv::Mat shrMag; + cv::resize(mag, shrMag, cv::Size(), scale, scale, cv::INTER_AREA); + + cv::integral(shrMag, magIntegral, mag.depth()); +} + +struct ChannelStorage +{ + std::vector hog; + cv::Mat magnitude; + cv::Mat luv; + + int shrinkage; + + enum {HOG_BINS = 6}; + + ChannelStorage() {} + ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) + { + cv::Mat _luv; + cv::cvtColor(colored, _luv, CV_BGR2Luv); + + cv::integral(luv, luv); + + cv::Mat grey; + cv::cvtColor(colored, grey, CV_RGB2GRAY); + + calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); + } + + float get(int chennel, cv::Rect area) const + { + return 1.f; + } +}; } struct cv::SoftCascade::Filds @@ -203,28 +288,38 @@ struct cv::SoftCascade::Filds std::vector levels; - // typedef std::vector::iterator stIter_t; - - // // carrently roi must be save for out of ranges. - // void detectInRoi(const cv::Rect& roi, const Integral& ints, std::vector& objects, const int step) - // { - // for (int dy = roi.y; dy < roi.height; dy+=step) - // for (int dx = roi.x; dx < roi.width; dx += step) - // { - // applyCascade(ints, dx, dy); - // } - // } - - // void applyCascade(const Integral& ints, const int x, const int y) - // { - // for (stIter_t sIt = stages.begin(); sIt != stages.end(); ++sIt) - // { - // Stage& stage = *sIt; - // } - // } - typedef std::vector::iterator octIt_t; + void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, + const std::vector& detections) const + { + float detectionScore = 0.f; + + const Octave& octave = *(level.octave); + int stBegin = octave.index() * octave.stages, stEnd = stBegin + octave.stages; + for(int st = stBegin; st < stEnd; ++st) + { + const Stage& stage = stages[st]; + if (detectionScore > stage.threshold) + { + int nId = st * 3; + const Node& node = nodes[nId]; + const Feature& feature = features[node.feature]; + + float sum = storage.get(feature.channel, feature.rect); + int next = (sum >= node.threshold)? 2 : 1; + + const Node& leaf = nodes[nId + next]; + const Feature& fLeaf = features[node.feature]; + sum = storage.get(feature.channel, feature.rect); + + int lShift = (next - 1) * 2 + (sum >= leaf.threshold) ? 1 : 0; + float impact = leaves[nId + lShift]; + detectionScore += impact; + } + } + } + octIt_t fitOctave(const float& logFactor) { float minAbsLog = FLT_MAX; @@ -407,90 +502,9 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -namespace { - - void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector& histInts, const int bins, int shrinkage) - { - CV_Assert( grey.type() == CV_8U); - - float scale = 1.f / shrinkage; - - const int rows = grey.rows + 1; - const int cols = grey.cols + 1; - cv::Size intSumSize(cols, rows); - - histInts.clear(); - std::vector hist; - for (int bin = 0; bin < bins; ++bin) - { - hist.push_back(cv::Mat(rows, cols, CV_32FC1)); - } - - cv::Mat df_dx, df_dy, mag, angle; - cv::Sobel(grey, df_dx, CV_32F, 1, 0); - cv::Sobel(grey, df_dy, CV_32F, 0, 1); - - cv::cartToPolar(df_dx, df_dy, mag, angle, true); - - const float magnitudeScaling = 1.0 / sqrt(2); - mag *= magnitudeScaling; - angle /= 60; - - for (int h = 0; h < mag.rows; ++h) - { - float* magnitude = mag.ptr(h); - float* ang = angle.ptr(h); - - for (int w = 0; w < mag.cols; ++w) - { - hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; - } - } - - for (int bin = 0; bin < bins; ++bin) - { - cv::Mat shrunk, sum; - cv::resize(hist[bin], shrunk, cv::Size(), scale, scale, cv::INTER_AREA); - cv::integral(shrunk, sum); - histInts.push_back(sum); - } - - cv::Mat shrMag; - cv::resize(mag, shrMag, cv::Size(), scale, scale, cv::INTER_AREA); - - cv::integral(shrMag, magIntegral, mag.depth()); - } - - struct ChannelStorage - { - std::vector hog; - cv::Mat luv; - cv::Mat magnitude; - - int shrinkage; - - enum {HOG_BINS = 6}; - - ChannelStorage() {} - ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) - { - cv::Mat _luv; - cv::cvtColor(colored, _luv, CV_BGR2Luv); - - cv::integral(luv, luv); - - cv::Mat grey; - cv::cvtColor(colored, grey, CV_RGB2GRAY); - - calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); - } - }; -} - - - -void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - const int step, const int rejectfactor)// add step scaling +void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, + std::vector& objects, + const int step, const int rejectfactor) { typedef std::vector::const_iterator RIter_t; // only color images are supperted @@ -506,10 +520,17 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector detections; + typedef std::vector::const_iterator lIt; + for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) + { + const Level& level = *it; + for (int dy = 0; dy < level.workRect.height; ++dy) + for (int dx = 0; dx < level.workRect.width; ++dx) + fld.detectAt(level, dx, dy, storage, detections); + } + + std::swap(detections, objects); } \ No newline at end of file From ba27d89173d253cbcc34671bffda31ea450328c8 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Fri, 14 Sep 2012 19:22:08 +0400 Subject: [PATCH 16/56] add feature rescaling according to Dollal's paper FPDW --- modules/objdetect/src/softcascade.cpp | 196 ++++++++++++++++++-------- 1 file changed, 134 insertions(+), 62 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 6e8c6ee369..64620fe2c9 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -1,31 +1,31 @@ /*M/////////////////////////////////////////////////////////////////////////////////////// // -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// 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. +// 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 +// License Agreement +// For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. +// Copyright (C) 2008-2012, 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: // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. +// * Redistribution's 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. +// * 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. +// * 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 @@ -37,6 +37,7 @@ // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. +// //M*/ #include @@ -137,6 +138,43 @@ struct Level workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) {} + + void markDetection(const int x, const int dx, std::vector& detections) const + { + + } +}; + + +struct CascadeIntrinsics +{ + static const float lambda = 1.099f, a = 0.89f; + + static float getFor(int channel, float scaling) + { + CV_Assert(channel < 10); + + if ((scaling - 1.f) < FLT_EPSILON) + return 1.f; + + // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper + static const float A[2][2] = + { //channel <= 6, otherwise + { 0.89f, 1.f}, // down + { 1.00f, 1.f} // up + }; + + static const float B[2][2] = + { //channel <= 6, otherwise + { 1.099f / log(2), 2.f}, // down + { 2.f, 2.f} // up + }; + + float a = A[(int)(scaling >= 1)][(int)(channel >= 6)]; + float b = B[(int)(scaling >= 1)][(int)(channel >= 6)]; + + return a * pow(scaling, b); + } }; // Feature rescale(float relScale) @@ -148,42 +186,6 @@ struct Level // return res; // } -// // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper -// struct CascadeIntrinsics -// { -// static const float lambda = 1.099f, a = 0.89f; -// static const float intrinsics[10][4]; - -// static float getFor(int channel, float scaling) -// { -// CV_Assert(channel < 10); - -// if ((scaling - 1.f) < FLT_EPSILON) -// return 1.f; - -// int ud = (int)(scaling < 1.f); -// return intrinsics[channel][(ud << 1)] * pow(scaling, intrinsics[channel][(ud << 1) + 1]); -// } - -// }; - -// const float CascadeIntrinsics::intrinsics[10][4] = -// { //da, db, ua, ub -// // hog-like orientation bins -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// {a, lambda / log(2), 1, 2}, -// // gradient magnitude -// {a, lambda / log(2), 1, 2}, -// // luv color channels -// {1, 2, 1, 2}, -// {1, 2, 1, 2}, -// {1, 2, 1, 2} -// }; - void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector& histInts, const int bins, int shrinkage) @@ -236,6 +238,7 @@ void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector splited; + split(luv, splited); cv::Mat grey; cv::cvtColor(colored, grey, CV_RGB2GRAY); calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); + + hog.insert(hog.end(), splited.begin(), splited.end()); } - float get(int chennel, cv::Rect area) const + float get(const int x, const int y, const int channel, const cv::Rect& area) const { - return 1.f; + CV_Assert(channel < HOG_LUV_BINS); + + const cv::Mat m = hog[channel]; + + float a = m.ptr(y + area.y)[x + area.x]; + float b = m.ptr(y + area.y)[x + area.width]; + float c = m.ptr(y + area.height)[x + area.width]; + float d = m.ptr(y + area.height)[x + area.x]; + + return (a - b + c - d); } }; } @@ -291,33 +309,87 @@ struct cv::SoftCascade::Filds typedef std::vector::iterator octIt_t; void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, - const std::vector& detections) const + std::vector& detections) const { float detectionScore = 0.f; const Octave& octave = *(level.octave); int stBegin = octave.index() * octave.stages, stEnd = stBegin + octave.stages; - for(int st = stBegin; st < stEnd; ++st) + int st = stBegin; + for(; st < stEnd; ++st) { const Stage& stage = stages[st]; - if (detectionScore > stage.threshold) { int nId = st * 3; + + // work with root node const Node& node = nodes[nId]; const Feature& feature = features[node.feature]; - float sum = storage.get(feature.channel, feature.rect); - int next = (sum >= node.threshold)? 2 : 1; + // rescaling + float scaling = CascadeIntrinsics::getFor(feature.channel, level.relScale); + cv::Rect scaledRect = feature.rect; + float farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + // rescale + scaledRect.x = cvRound(scaling * scaledRect.x); + scaledRect.y = cvRound(scaling * scaledRect.y); + scaledRect.width = cvRound(scaling * scaledRect.width); + scaledRect.height = cvRound(scaling * scaledRect.height); + float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + + float approx = 1.f; + if ((farea - 0.f) > FLT_EPSILON && (farea - 0.f) > FLT_EPSILON) + { + const float expected_new_area = farea*level.relScale*level.relScale; + approx = expected_new_area / sarea; + } + + float rootThreshold = node.threshold / approx; // ToDo check + rootThreshold *= scaling; + + // use rescaled + float sum = storage.get(dx, dy, feature.channel, scaledRect); + int next = (sum >= rootThreshold)? 2 : 1; + + // leaces const Node& leaf = nodes[nId + next]; const Feature& fLeaf = features[node.feature]; - sum = storage.get(feature.channel, feature.rect); - int lShift = (next - 1) * 2 + (sum >= leaf.threshold) ? 1 : 0; + // rescaling + scaling = CascadeIntrinsics::getFor(fLeaf.channel, level.relScale); + scaledRect = fLeaf.rect; + farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + // rescale + scaledRect.x = cvRound(scaling * scaledRect.x); + scaledRect.y = cvRound(scaling * scaledRect.y); + scaledRect.width = cvRound(scaling * scaledRect.width); + scaledRect.height = cvRound(scaling * scaledRect.height); + + sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + + approx = 1.f; + if ((farea - 0.f) > FLT_EPSILON && (farea - 0.f) > FLT_EPSILON) + { + const float expected_new_area = farea*level.relScale*level.relScale; + approx = expected_new_area / sarea; + } + + rootThreshold = leaf.threshold / approx; // ToDo check + rootThreshold *= scaling; + + sum = storage.get(dx, dy, feature.channel, scaledRect); + + int lShift = (next - 1) * 2 + (sum >= rootThreshold) ? 1 : 0; float impact = leaves[nId + lShift]; detectionScore += impact; } + + if (detectionScore <= stage.threshold) break; } + + if (st == octave.stages - 1) + level.markDetection(dx, dy, detections); } octIt_t fitOctave(const float& logFactor) From 765dea9ddf50ec1b47e68802665642cdd27496a5 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 19 Sep 2012 09:31:02 +0400 Subject: [PATCH 17/56] fix bugs in the soft cascade detect method; add options for debug logging - WITH_DEBUG_OUT for logging cascade scales - DEBUG_STORE_IMAGES for xml matrix serialization - DEBUG_SHOW_RESULT to see detection result --- .../include/opencv2/objdetect/objdetect.hpp | 2 +- modules/objdetect/src/softcascade.cpp | 565 +++++++++++++----- modules/objdetect/test/test_softcascade.cpp | 2 +- 3 files changed, 421 insertions(+), 148 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 97d6122fe2..bb4240f59b 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -512,7 +512,7 @@ public: //! return vector of bounding boxes. Each box contains one detected object virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - int step = 4, int rejectfactor = 1); + int rejectfactor = 1); protected: enum { BOOST = 0 }; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 64620fe2c9..9e1c550d7d 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -47,11 +47,23 @@ #include #include #include +#include +#include namespace { +char *itoa(long i, char* s, int /*dummy_radix*/) +{ + sprintf(s, "%ld", i); + return s; +} + +// used for noisy printfs +// #define WITH_DEBUG_OUT + struct Octave { + int index; float scale; int stages; cv::Size size; @@ -61,21 +73,17 @@ struct Octave static const char *const SC_OCT_STAGES; static const char *const SC_OCT_SHRINKAGE; - Octave() : scale(0), stages(0), size(cv::Size()), shrinkage(0) {} - Octave(cv::Size origObjSize, const cv::FileNode& fn) - : scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), + Octave(const int i, cv::Size origObjSize, const cv::FileNode& fn) + : index(i), scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), shrinkage((int)fn[SC_OCT_SHRINKAGE]) {} - - int index() const {return (int)log(scale);} }; const char *const Octave::SC_OCT_SCALE = "scale"; const char *const Octave::SC_OCT_STAGES = "stageNum"; const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; - struct Stage { float threshold; @@ -94,10 +102,10 @@ struct Node float threshold; Node(){} - Node(cv::FileNodeIterator& fIt) : feature((int)(*(fIt +=2)++)), threshold((float)(*(fIt++))){} + Node(const int offset, cv::FileNodeIterator& fIt) + : feature((int)(*(fIt +=2)++) + offset), threshold((float)(*(fIt++))){} }; - struct Feature { int channel; @@ -112,40 +120,48 @@ struct Feature cv::FileNode rn = fn[SC_F_RECT]; cv::FileNodeIterator r_it = rn.end(); rect = cv::Rect(*(--r_it), *(--r_it), *(--r_it), *(--r_it)); - // std::cout << "feature: " << rect.x << " " << rect.y << " " << rect.width - //<< " " << rect.height << " " << channel << std::endl; } }; const char * const Feature::SC_F_CHANNEL = "channel"; const char * const Feature::SC_F_RECT = "rect"; +struct Object +{ + enum Class{PEDESTRIAN}; + cv::Rect rect; + float confidence; + Class detType; + + Object(const cv::Rect& r, const float c, Class dt = PEDESTRIAN) : rect(r), confidence(c), detType(dt) {} +}; + struct Level { const Octave* octave; float origScale; float relScale; - float shrScale; + float shrScale; // used for marking detection cv::Size workRect; cv::Size objSize; - - // TiDo not reounding Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) - : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / shrinkage), + : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / (float)shrinkage), workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) {} - void markDetection(const int x, const int dx, std::vector& detections) const + void markDetection(const int x, const int y, float confidence, std::vector& detections) const { + int shrinkage = (*octave).shrinkage; + cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height); + detections.push_back(Object(rect, confidence)); } }; - struct CascadeIntrinsics { static const float lambda = 1.099f, a = 0.89f; @@ -157,7 +173,7 @@ struct CascadeIntrinsics if ((scaling - 1.f) < FLT_EPSILON) return 1.f; - // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool paper + // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool's and Dallal's papers static const float A[2][2] = { //channel <= 6, otherwise { 0.89f, 1.f}, // down @@ -167,77 +183,164 @@ struct CascadeIntrinsics static const float B[2][2] = { //channel <= 6, otherwise { 1.099f / log(2), 2.f}, // down - { 2.f, 2.f} // up + { 0.f, 2.f} // up }; - float a = A[(int)(scaling >= 1)][(int)(channel >= 6)]; - float b = B[(int)(scaling >= 1)][(int)(channel >= 6)]; + float a = A[(int)(scaling >= 1)][(int)(channel > 6)]; + float b = B[(int)(scaling >= 1)][(int)(channel > 6)]; +#if defined WITH_DEBUG_OUT + printf("scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); +#endif return a * pow(scaling, b); } }; -// Feature rescale(float relScale) -// { -// Feature res(*this); -// res.rect = cv::Rect (cvRound(rect.x * relScale), cvRound(rect.y * relScale), -// cvRound(rect.width * relScale), cvRound(rect.height * relScale)); -// res.threshold = threshold * CascadeIntrinsics::getFor(channel, relScale); -// return res; -// } +int qangle6(float dfdx, float dfdy) +{ + static const float vectors[6][2] = + { + {std::cos(0), std::sin(0) }, + {std::cos(M_PI / 6.f), std::sin(M_PI / 6.f) }, + {std::cos(M_PI / 3.f), std::sin(M_PI / 3.f) }, + {std::cos(M_PI / 2.f), std::sin(M_PI / 2.f) }, + {std::cos(2.f * M_PI / 3.f), std::sin(2.f * M_PI / 3.f)}, + {std::cos(5.f * M_PI / 6.f), std::sin(5.f * M_PI / 6.f)} + }; + + int index = 0; + + float dot = fabs(dfdx * vectors[0][0] + dfdy * vectors[0][1]); + + for(int i = 1; i < 6; ++i) + { + const float curr = fabs(dfdx * vectors[i][0] + dfdy * vectors[i][1]); + + if(curr > dot) + { + dot = curr; + index = i; + } + } + + return index; +} + +//ToDo void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector& histInts, const int bins, int shrinkage) { + static const float magnitudeScaling = 1.f / sqrt(2); + CV_Assert( grey.type() == CV_8U); float scale = 1.f / shrinkage; const int rows = grey.rows + 1; const int cols = grey.cols + 1; - cv::Size intSumSize(cols, rows); + + cv::Mat df_dx(grey.rows, grey.cols, CV_32F), + df_dy(grey.rows, grey.cols, CV_32F), mag, angle; + // cv::Sobel(grey, df_dx, CV_32F, 1, 0); + // cv::Sobel(grey, df_dy, CV_32F, 0, 1); + + for (int y = 1; y < grey.rows -1; ++y) + { + float* dx = df_dx.ptr(y); + float* dy = df_dy.ptr(y); + + const uchar* gr = grey.ptr(y); + const uchar* gr_down = grey.ptr(y - 1); + const uchar* gr_up = grey.ptr(y + 1); + for (int x = 1; x < grey.cols - 1; ++x) + { + float dx_a = gr[x + 1]; + float dx_b = gr[x - 1]; + dx[x] = dx_a - dx_b; + + float dy_a = gr_up[x]; + float dy_b = gr_down[x]; + dy[x] = dy_a - dy_b; + } + } + + cv::cartToPolar(df_dx, df_dy, mag, angle, true); + + mag *= magnitudeScaling; + + cv::Mat saturatedMag(grey.rows, grey.cols, CV_8UC1); + for (int y = 0; y < grey.rows; ++y) + { + float* rm = mag.ptr(y); + uchar* mg = saturatedMag.ptr(y); + for (int x = 0; x < grey.cols; ++x) + { + mg[x] = cv::saturate_cast(rm[x]); + } + } + + mag = saturatedMag; histInts.clear(); std::vector hist; for (int bin = 0; bin < bins; ++bin) { - hist.push_back(cv::Mat(rows, cols, CV_32FC1)); + hist.push_back(cv::Mat(rows, cols, CV_8UC1)); } - cv::Mat df_dx, df_dy, mag, angle; - cv::Sobel(grey, df_dx, CV_32F, 1, 0); - cv::Sobel(grey, df_dy, CV_32F, 0, 1); - - cv::cartToPolar(df_dx, df_dy, mag, angle, true); - - const float magnitudeScaling = 1.0 / sqrt(2); - mag *= magnitudeScaling; - angle /= 60; - - for (int h = 0; h < mag.rows; ++h) + for (int h = 0; h < saturatedMag.rows; ++h) { - float* magnitude = mag.ptr(h); - float* ang = angle.ptr(h); + uchar* magnitude = saturatedMag.ptr(h); + float* dfdx = df_dx.ptr(h); + float* dfdy = df_dy.ptr(h); - for (int w = 0; w < mag.cols; ++w) + for (int w = 0; w < saturatedMag.cols; ++w) { - hist[(int)ang[w]].ptr(h)[w] = magnitude[w]; + hist[ qangle6(dfdx[w], dfdy[w]) ].ptr(h)[w] = magnitude[w]; } } + angle /= 60; + + + // for (int h = 0; h < saturatedMag.rows; ++h) + // { + // uchar* magnitude = saturatedMag.ptr(h); + // float* ang = angle.ptr(h); + + // for (int w = 0; w < saturatedMag.cols; ++w) + // { + // hist[ (int)ang[w] ].ptr(h)[w] = magnitude[w]; + // } + // } + char buffer[33]; + for (int bin = 0; bin < bins; ++bin) { cv::Mat shrunk, sum; + cv::imshow(std::string("hist[bin]") + itoa(bin, buffer, 10), hist[bin]); cv::resize(hist[bin], shrunk, cv::Size(), scale, scale, cv::INTER_AREA); + cv::imshow(std::string("shrunk") + itoa(bin, buffer, 10), shrunk); cv::integral(shrunk, sum); + cv::imshow(std::string("sum") + itoa(bin, buffer, 10), sum); histInts.push_back(sum); + + // std::cout << shrunk << std::endl << std::endl; } cv::Mat shrMag; + cv::imshow("mag", mag); cv::resize(mag, shrMag, cv::Size(), scale, scale, cv::INTER_AREA); + cv::FileStorage fs("/home/kellan/actualChannels.xml", cv::FileStorage::WRITE); + cv::imshow("shrunk_channel", shrMag); + fs << "shrunk_channel6" << shrMag; + + // cv::imshow("shrMag", shrMag); cv::integral(shrMag, magIntegral, mag.depth()); + // cv::imshow("magIntegral", magIntegral); histInts.push_back(magIntegral); } @@ -252,39 +355,92 @@ struct ChannelStorage enum {HOG_BINS = 6, HOG_LUV_BINS = 10}; ChannelStorage() {} - ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) + ChannelStorage(cv::Mat& colored, int shr) : shrinkage(shr) { - cv::Mat _luv, shrLuv; - cv::cvtColor(colored, _luv, CV_BGR2Luv); - cv::resize(_luv, shrLuv, cv::Size(), 1.f / shr, 1.f / shr, cv::INTER_AREA); + hog.clear(); + cv::FileStorage fs("/home/kellan/testInts.xml", cv::FileStorage::READ); + char buff[33]; + float scale = 1.f / shrinkage; + for(int i = 0; i < 10; ++i) + { + cv::Mat channel; + fs[std::string("channel") + itoa(i, buff, 10)] >> channel; - cv::integral(shrLuv, luv); - - std::vector splited; - split(luv, splited); - - cv::Mat grey; - cv::cvtColor(colored, grey, CV_RGB2GRAY); - - calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); - - hog.insert(hog.end(), splited.begin(), splited.end()); + cv::Mat shrunk, sum; + // cv::resize(channel, shrunk, cv::Size(), scale, scale, cv::INTER_AREA); + // cv::imshow(std::string("channel") + itoa(i, buff, 10), shrunk); + // cv::waitKey(0); + // cv::integral(channel, sum); + // if (i == 1) + // std::cout << channel << std::endl; + hog.push_back(channel); + } + // exit(1); } + // { + // // add gauss + // cv::Mat gauss; + // cv::GaussianBlur(colored, gauss, cv::Size(3,3), 0 ,0); + + // colored = gauss; + // // cv::imshow("colored", colored); + + // cv::Mat _luv, shrLuv; + // cv::cvtColor(colored, _luv, CV_BGR2Luv); + + // // cv::imshow("_luv", _luv); + + // cv::resize(_luv, shrLuv, cv::Size(), 1.f / shr, 1.f / shr, cv::INTER_AREA); + + // // cv::imshow("shrLuv", shrLuv); + + // cv::integral(shrLuv, luv); + + // // cv::imshow("luv", luv); + + // std::vector splited; + // split(luv, splited); + + // char buffer[33]; + + // for (int i = 0; i < (int)splited.size(); i++) + // { + // // cv::imshow(itoa(i,buffer,10), splited[i]); + // } + + // cv::Mat grey; + // cv::cvtColor(colored, grey, CV_RGB2GRAY); + + // // cv::imshow("grey", grey); + + // calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); + + // hog.insert(hog.end(), splited.begin(), splited.end()); + // } float get(const int x, const int y, const int channel, const cv::Rect& area) const { CV_Assert(channel < HOG_LUV_BINS); - const cv::Mat m = hog[channel]; - float a = m.ptr(y + area.y)[x + area.x]; - float b = m.ptr(y + area.y)[x + area.width]; - float c = m.ptr(y + area.height)[x + area.width]; - float d = m.ptr(y + area.height)[x + area.x]; +#if defined WITH_DEBUG_OUT + printf("feature box %d %d %d %d ", area.x, area.y, area.width, area.height); + printf("get for channel %d\n", channel); + printf("!! %d\n", m.depth()); +#endif + int a = m.ptr(y + area.y)[x + area.x]; + int b = m.ptr(y + area.y)[x + area.width]; + int c = m.ptr(y + area.height)[x + area.width]; + int d = m.ptr(y + area.height)[x + area.x]; + +#if defined WITH_DEBUG_OUT + printf(" retruved integral values: %d %d %d %d\n", a, b, c, d); +#endif return (a - b + c - d); } }; + } struct cv::SoftCascade::Filds @@ -299,25 +455,91 @@ struct cv::SoftCascade::Filds std::vector octaves; std::vector stages; - std::vector nodes; - std::vector leaves; - + std::vector nodes; + std::vector leaves; std::vector features; std::vector levels; typedef std::vector::iterator octIt_t; - void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, - std::vector& detections) const + float rescale(const Feature& feature, const float relScale, cv::Rect& scaledRect, const float threshold) const { + float scaling = CascadeIntrinsics::getFor(feature.channel, relScale); + scaledRect = feature.rect; + +#if defined WITH_DEBUG_OUT + printf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, + scaledRect.width, scaledRect.height); + + std::cout << "rescale: " << feature.channel << " " << relScale << " " << scaling << std::endl; +#endif + + float farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + // rescale + scaledRect.x = cvRound(relScale * scaledRect.x); + scaledRect.y = cvRound(relScale * scaledRect.y); + scaledRect.width = cvRound(relScale * scaledRect.width); + scaledRect.height = cvRound(relScale * scaledRect.height); + +#if defined WITH_DEBUG_OUT + printf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, + scaledRect.width, scaledRect.height); + + std::cout << " new rect: " << scaledRect.x << " " << scaledRect.y + << " " << scaledRect.width << " " << scaledRect.height << " "; +#endif + + float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + + float approx = 1.f; + if ((farea - 0.f) > FLT_EPSILON && (farea - 0.f) > FLT_EPSILON) + { + const float expected_new_area = farea * relScale * relScale; + approx = expected_new_area / sarea; + +#if defined WITH_DEBUG_OUT + std::cout << " rel areas " << expected_new_area << " " << sarea << std::endl; +#endif + + } + + // compensation areas rounding + float rootThreshold = threshold / approx;/ + rootThreshold *= scaling; + +#if defined WITH_DEBUG_OUT + std::cout << "approximation " << approx << " " << threshold << " -> " << rootThreshold + << " " << scaling << std::endl; +#endif + + return rootThreshold; + } + + void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, + std::vector& detections) const + { +#if defined WITH_DEBUG_OUT + std::cout << "detect at: " << dx << " " << dy << std::endl; +#endif float detectionScore = 0.f; const Octave& octave = *(level.octave); - int stBegin = octave.index() * octave.stages, stEnd = stBegin + octave.stages; + int stBegin = octave.index * octave.stages, stEnd = stBegin + octave.stages; + +#if defined WITH_DEBUG_OUT + std::cout << " octave stages: " << stBegin << " to " << stEnd << " index " << octave.index << " " + << octave.scale << " level " << level.origScale << std::endl; +#endif + int st = stBegin; for(; st < stEnd; ++st) { + +#if defined WITH_DEBUG_OUT + printf("index: %d\n", st); +#endif + const Stage& stage = stages[st]; { int nId = st * 3; @@ -325,71 +547,55 @@ struct cv::SoftCascade::Filds // work with root node const Node& node = nodes[nId]; const Feature& feature = features[node.feature]; + cv::Rect scaledRect; + float threshold = rescale(feature, level.relScale, scaledRect, node.threshold); - // rescaling - float scaling = CascadeIntrinsics::getFor(feature.channel, level.relScale); - cv::Rect scaledRect = feature.rect; - float farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - // rescale - scaledRect.x = cvRound(scaling * scaledRect.x); - scaledRect.y = cvRound(scaling * scaledRect.y); - scaledRect.width = cvRound(scaling * scaledRect.width); - scaledRect.height = cvRound(scaling * scaledRect.height); - float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - - float approx = 1.f; - if ((farea - 0.f) > FLT_EPSILON && (farea - 0.f) > FLT_EPSILON) - { - const float expected_new_area = farea*level.relScale*level.relScale; - approx = expected_new_area / sarea; - } - - float rootThreshold = node.threshold / approx; // ToDo check - rootThreshold *= scaling; - - // use rescaled float sum = storage.get(dx, dy, feature.channel, scaledRect); - int next = (sum >= rootThreshold)? 2 : 1; - // leaces +#if defined WITH_DEBUG_OUT + printf("root feature %d %f\n",feature.channel, sum); +#endif + + int next = (sum >= threshold)? 2 : 1; + +#if defined WITH_DEBUG_OUT + printf("go: %d (%f >= %f)\n\n" ,next, sum, threshold); +#endif + + // leaves const Node& leaf = nodes[nId + next]; - const Feature& fLeaf = features[node.feature]; + const Feature& fLeaf = features[leaf.feature]; - // rescaling - scaling = CascadeIntrinsics::getFor(fLeaf.channel, level.relScale); - scaledRect = fLeaf.rect; - farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - // rescale - scaledRect.x = cvRound(scaling * scaledRect.x); - scaledRect.y = cvRound(scaling * scaledRect.y); - scaledRect.width = cvRound(scaling * scaledRect.width); - scaledRect.height = cvRound(scaling * scaledRect.height); + threshold = rescale(fLeaf, level.relScale, scaledRect, leaf.threshold); + sum = storage.get(dx, dy, fLeaf.channel, scaledRect); - sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - approx = 1.f; - if ((farea - 0.f) > FLT_EPSILON && (farea - 0.f) > FLT_EPSILON) - { - const float expected_new_area = farea*level.relScale*level.relScale; - approx = expected_new_area / sarea; - } - - rootThreshold = leaf.threshold / approx; // ToDo check - rootThreshold *= scaling; - - sum = storage.get(dx, dy, feature.channel, scaledRect); - - int lShift = (next - 1) * 2 + (sum >= rootThreshold) ? 1 : 0; - float impact = leaves[nId + lShift]; + int lShift = (next - 1) * 2 + ((sum >= threshold) ? 1 : 0); + float impact = leaves[(st * 4) + lShift]; +#if defined WITH_DEBUG_OUT + printf("decided: %d (%f >= %f) %d %f\n\n" ,next, sum, threshold, lShift, impact); +#endif detectionScore += impact; } +#if defined WITH_DEBUG_OUT + printf("extracted stage:\n"); + printf("ct %f\n", stage.threshold); + printf("computed score %f\n\n", detectionScore); + // if (st - stBegin > 100) break; +#endif + if (detectionScore <= stage.threshold) break; } - if (st == octave.stages - 1) - level.markDetection(dx, dy, detections); + printf("x %d y %d: %d\n", dx, dy, st - stBegin); + + if (st == stEnd) + { + std::cout << " got " << st << std::endl; + level.markDetection(dx, dy, detectionScore, detections); + } } octIt_t fitOctave(const float& logFactor) @@ -438,22 +644,17 @@ struct cv::SoftCascade::Filds if (fabs(scale - maxScale) < FLT_EPSILON) break; scale = std::min(maxScale, expf(log(scale) + logFactor)); - // std::cout << "level scale " - // << levels[sc].origScale - // << " octeve " - // << levels[sc].octave->scale - // << " " - // << levels[sc].relScale - // << " " << levels[sc].shrScale - // << " [" << levels[sc].objSize.width - // << " " << levels[sc].objSize.height << "] [" - // << levels[sc].workRect.width << " " << levels[sc].workRect.height << std::endl; + std::cout << "level " << sc << " scale " + << levels[sc].origScale + << " octeve " + << levels[sc].octave->scale + << " " + << levels[sc].relScale + << " " << levels[sc].shrScale + << " [" << levels[sc].objSize.width + << " " << levels[sc].objSize.height << "] [" + << levels[sc].workRect.width << " " << levels[sc].workRect.height << "]" << std::endl; } - - return; - - - std::cout << std::endl << std::endl << std::endl; } bool fill(const FileNode &root, const float mins, const float maxs) @@ -500,10 +701,12 @@ struct cv::SoftCascade::Filds // octaves.reserve(noctaves); FileNodeIterator it = fn.begin(), it_end = fn.end(); + int feature_offset = 0; + int octIndex = 0; for (; it != it_end; ++it) { FileNode fns = *it; - Octave octave(cv::Size(SoftCascade::ORIG_OBJECT_WIDTH, SoftCascade::ORIG_OBJECT_HEIGHT), fns); + Octave octave(octIndex, cv::Size(SoftCascade::ORIG_OBJECT_WIDTH, SoftCascade::ORIG_OBJECT_HEIGHT), fns); CV_Assert(octave.stages > 0); octaves.push_back(octave); @@ -527,7 +730,7 @@ struct cv::SoftCascade::Filds fns = (*ftr)[SC_INTERNAL]; FileNodeIterator inIt = fns.begin(), inIt_end = fns.end(); for (; inIt != inIt_end;) - nodes.push_back(Node(inIt)); + nodes.push_back(Node(feature_offset, inIt)); fns = (*ftr)[SC_LEAF]; inIt = fns.begin(), inIt_end = fns.end(); @@ -539,9 +742,31 @@ struct cv::SoftCascade::Filds st = ffs.begin(), st_end = ffs.end(); for (; st != st_end; ++st ) features.push_back(Feature(*st)); + + feature_offset += octave.stages * 3; + ++octIndex; } shrinkage = octaves[0].shrinkage; + + //debug print + // std::cout << "collected " << stages.size() << " stages" << std::endl; + // for (int i = 0; i < (int)stages.size(); ++i) + // { + // std::cout << "stage " << i << ": " << stages[i].threshold << std::endl; + // } + + // std::cout << "collected " << nodes.size() << " nodes" << std::endl; + // for (int i = 0; i < (int)nodes.size(); ++i) + // { + // std::cout << "node " << i << ": " << nodes[i].threshold << " " << nodes[i].feature << std::endl; + // } + + // std::cout << "collected " << leaves.size() << " leaves" << std::endl; + // for (int i = 0; i < (int)leaves.size(); ++i) + // { + // std::cout << "leaf " << i << ": " << leaves[i] << std::endl; + // } return true; } }; @@ -574,9 +799,11 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& rois, - std::vector& objects, - const int step, const int rejectfactor) +#define DEBUG_STORE_IMAGES +#define DEBUG_SHOW_RESULT + +void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, + std::vector& objects, const int /*rejectfactor*/) { typedef std::vector::const_iterator RIter_t; // only color images are supperted @@ -589,20 +816,66 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector> doppia; + + cv::Mat diff; + cv::absdiff(image1, doppia, diff); + + fs << "absdiff" << diff; + fs.release(); +#if defined DEBUG_STORE_IMAGES + // create integrals - ChannelStorage storage(image, fld.shrinkage); + ChannelStorage storage(image1, fld.shrinkage); // object candidates - std::vector detections; + std::vector detections; typedef std::vector::const_iterator lIt; - for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) + int total = 0, l = 0; + for (lIt it = fld.levels.begin() + 26; it != fld.levels.end(); ++it) { const Level& level = *it; + +#if defined WITH_DEBUG_OUT + std::cout << "================================ " << l++ << std::endl; +#endif for (int dy = 0; dy < level.workRect.height; ++dy) + { for (int dx = 0; dx < level.workRect.width; ++dx) + { fld.detectAt(level, dx, dy, storage, detections); + total++; + // break; + } + // break; + } + break; } - std::swap(detections, objects); + cv::Mat out = image.clone(); + +#if defined DEBUG_SHOW_RESULT + + printf("TOTAL: %d from %d\n", (int)detections.size(),total) ; + + for(int i = 0; i < (int)detections.size(); ++i) + { + cv::rectangle(out, detections[i].rect, cv::Scalar(255, 0, 0, 255), 2); + } + + cv::imshow("out", out); + cv::waitKey(0); +#endif + // std::swap(detections, objects); } \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index 7311ad2914..9c316fd906 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -55,7 +55,7 @@ TEST(SoftCascade, detect) cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(xml)); - cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000006_0.png"); + cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); ASSERT_FALSE(colored.empty()); std::vector objectBoxes; From efd26158448b3fed82f03f883fbd301ed36d90ef Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 19 Sep 2012 17:58:44 +0400 Subject: [PATCH 18/56] fix floating point bug --- modules/objdetect/src/softcascade.cpp | 166 ++++++++++++++++++++------ 1 file changed, 131 insertions(+), 35 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 9e1c550d7d..24c055de67 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -169,8 +169,12 @@ struct CascadeIntrinsics static float getFor(int channel, float scaling) { CV_Assert(channel < 10); +#if defined WITH_DEBUG_OUT + printf("QQQQQQQQQQQQQQQq: %f %f\n", scaling, fabs(scaling - 1.f)); +#endif - if ((scaling - 1.f) < FLT_EPSILON) + if (fabs(scaling - 1.f) < FLT_EPSILON) + // if (scaling == 1.f) return 1.f; // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool's and Dallal's papers @@ -190,7 +194,7 @@ struct CascadeIntrinsics float b = B[(int)(scaling >= 1)][(int)(channel > 6)]; #if defined WITH_DEBUG_OUT - printf("scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); + printf("!!! scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); #endif return a * pow(scaling, b); } @@ -344,6 +348,47 @@ void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector +struct Decimate { + int shrinkage; + + Decimate(const int sr) : shrinkage(sr) {} + + void operator()(const cv::Mat& in, cv::Mat& out) const + { + int cols = in.cols / shrinkage; + int rows = in.rows / shrinkage; + out.create(rows, cols, in.type()); + + CV_Assert(cols * shrinkage == in.cols); + CV_Assert(rows * shrinkage == in.rows); + + std::cout << "type: " << out.type() << std::endl; + + for (int outIdx_y = 0; outIdx_y < rows; ++outIdx_y) + { + T* outPtr = out.ptr(outIdx_y); + for (int outIdx_x = 0; outIdx_x < cols; ++outIdx_x) + { + // do desimate + int inIdx_y = outIdx_y * shrinkage; + int inIdx_x = outIdx_x * shrinkage; + int sum = 0; + + for (int y = inIdx_y; y < inIdx_y + shrinkage; ++y) + for (int x = inIdx_x; x < inIdx_x + shrinkage; ++x) + sum += in.at(y, x); + + sum /= shrinkage * shrinkage; + outPtr[outIdx_x] = cv::saturate_cast(sum); + } + } + } + +}; + +//#define USE_REFERENCE_VALUES + struct ChannelStorage { std::vector hog; @@ -358,24 +403,65 @@ struct ChannelStorage ChannelStorage(cv::Mat& colored, int shr) : shrinkage(shr) { hog.clear(); - cv::FileStorage fs("/home/kellan/testInts.xml", cv::FileStorage::READ); - char buff[33]; - float scale = 1.f / shrinkage; - for(int i = 0; i < 10; ++i) - { - cv::Mat channel; - fs[std::string("channel") + itoa(i, buff, 10)] >> channel; + Decimate decimate(shr); - cv::Mat shrunk, sum; - // cv::resize(channel, shrunk, cv::Size(), scale, scale, cv::INTER_AREA); - // cv::imshow(std::string("channel") + itoa(i, buff, 10), shrunk); - // cv::waitKey(0); - // cv::integral(channel, sum); - // if (i == 1) - // std::cout << channel << std::endl; - hog.push_back(channel); +#if defined USE_REFERENCE_VALUES + cv::FileStorage imgs("/home/kellan/testInts.xml", cv::FileStorage::READ); +#else + + // add gauss + cv::Mat gauss; + cv::GaussianBlur(colored, gauss, cv::Size(3,3), 0 ,0); + + // convert to luv + cv::Mat luv; + cv::cvtColor(colored, luv, CV_RGB2Luv); + + // split to 3 one channel matrix + std::vector splited, luvs; + split(luv, splited); + + // shrink and integrate + for (int i = 0; i < (int)splited.size(); i++) + { + cv::Mat shrunk, sum; + decimate(splited[i], shrunk); + cv::integral(shrunk, sum, cv::noArray(), CV_32S); + luvs.push_back(sum); } - // exit(1); + + // calculate magnitude + static const float magnitudeScaling = 1.f / sqrt(2); + cv::Mat grey; + cv::cvtColor(colored, grey, CV_RGB2GRAY); + + // channels + cv::FileStorage imgs("/home/kellan/testImgs.xml", cv::FileStorage::READ); +#endif + + char buff[33]; + for(int i = 0; i < 7; ++i) + { + cv::Mat channel, shrunk, sum; + imgs[std::string("channel") + itoa(i, buff, 10)] >> channel; + +#if defined USE_REFERENCE_VALUES + hog.push_back(channel); +#else + + decimate(channel, shrunk); + // cv::resize(channel, shrunk, cv::Size(), 1.f / shr, 1.f / shr, cv::INTER_AREA); + cv::integral(shrunk, sum, cv::noArray(), CV_32S); + + hog.push_back(sum); +#endif + } + +#if !defined USE_REFERENCE_VALUES + hog.insert(hog.end(), luvs.begin(), luvs.end()); + CV_Assert(hog.size() == 10); +#endif + // exit(10); } // { // // add gauss @@ -427,6 +513,12 @@ struct ChannelStorage printf("feature box %d %d %d %d ", area.x, area.y, area.width, area.height); printf("get for channel %d\n", channel); printf("!! %d\n", m.depth()); + + printf("extract feature for: [%d %d] [%d %d] [%d %d] [%d %d]\n", + x + area.x, y + area.y, x + area.width,y + area.y, x + area.width,y + area.height, + x + area.x, y + area.height); + + printf("at point %d %d with offset %d\n", x, y, 0); #endif int a = m.ptr(y + area.y)[x + area.x]; @@ -493,7 +585,7 @@ struct cv::SoftCascade::Filds float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); float approx = 1.f; - if ((farea - 0.f) > FLT_EPSILON && (farea - 0.f) > FLT_EPSILON) + if (fabs(farea - 0.f) > FLT_EPSILON && fabs(farea - 0.f) > FLT_EPSILON) { const float expected_new_area = farea * relScale * relScale; approx = expected_new_area / sarea; @@ -505,7 +597,7 @@ struct cv::SoftCascade::Filds } // compensation areas rounding - float rootThreshold = threshold / approx;/ + float rootThreshold = threshold / approx; rootThreshold *= scaling; #if defined WITH_DEBUG_OUT @@ -583,7 +675,7 @@ struct cv::SoftCascade::Filds printf("extracted stage:\n"); printf("ct %f\n", stage.threshold); printf("computed score %f\n\n", detectionScore); - // if (st - stBegin > 100) break; + if (st - stBegin > 50 ) break; #endif if (detectionScore <= stage.threshold) break; @@ -681,7 +773,7 @@ struct cv::SoftCascade::Filds static const char *const SC_LEAF = "leafValues"; - // only boost supported + // only Ada Boost supported std::string stageTypeStr = (string)root[SC_STAGE_TYPE]; CV_Assert(stageTypeStr == SC_BOOST); @@ -799,7 +891,7 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -#define DEBUG_STORE_IMAGES +// #define DEBUG_STORE_IMAGES #define DEBUG_SHOW_RESULT void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, @@ -833,7 +925,8 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector::const_iterator lIt; int total = 0, l = 0; - for (lIt it = fld.levels.begin() + 26; it != fld.levels.end(); ++it) + for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) { const Level& level = *it; #if defined WITH_DEBUG_OUT std::cout << "================================ " << l++ << std::endl; #endif + // int dx = 79; int dy = 76; for (int dy = 0; dy < level.workRect.height; ++dy) { for (int dx = 0; dx < level.workRect.width; ++dx) @@ -860,22 +954,24 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Wed, 19 Sep 2012 18:47:56 +0400 Subject: [PATCH 19/56] fix hog channels --- modules/objdetect/src/softcascade.cpp | 116 +++++++++++++------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 24c055de67..989b53659f 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -387,14 +387,11 @@ struct Decimate { }; -//#define USE_REFERENCE_VALUES +#define USE_REFERENCE_VALUES struct ChannelStorage { std::vector hog; - cv::Mat magnitude; - cv::Mat luv; - int shrinkage; enum {HOG_BINS = 6, HOG_LUV_BINS = 10}; @@ -407,8 +404,8 @@ struct ChannelStorage #if defined USE_REFERENCE_VALUES cv::FileStorage imgs("/home/kellan/testInts.xml", cv::FileStorage::READ); + char buff[33]; #else - // add gauss cv::Mat gauss; cv::GaussianBlur(colored, gauss, cv::Size(3,3), 0 ,0); @@ -430,79 +427,84 @@ struct ChannelStorage luvs.push_back(sum); } - // calculate magnitude - static const float magnitudeScaling = 1.f / sqrt(2); + // convert to grey cv::Mat grey; cv::cvtColor(colored, grey, CV_RGB2GRAY); - // channels - cv::FileStorage imgs("/home/kellan/testImgs.xml", cv::FileStorage::READ); -#endif + // get derivative + cv::Mat df_dx, df_dy, mag, angle; + cv::Sobel(grey, df_dx, CV_32F, 1, 0); + cv::Sobel(grey, df_dy, CV_32F, 0, 1); - char buff[33]; - for(int i = 0; i < 7; ++i) + // normalize + df_dx /= 4; + df_dy /= 4; + + // calculate magnitude + cv::cartToPolar(df_dx, df_dy, mag, angle, true); + + // normalize to avoid uchar overflow + static const float magnitudeScaling = 1.f / sqrt(2); + mag *= magnitudeScaling; + + // convert to uchar + cv::Mat saturatedMag(grey.rows, grey.cols, CV_8UC1), shrMag; + for (int y = 0; y < grey.rows; ++y) + { + float* rm = mag.ptr(y); + uchar* mg = saturatedMag.ptr(y); + for (int x = 0; x < grey.cols; ++x) + { + mg[x] = cv::saturate_cast(rm[x]); + } + } + + // srink and integrate + decimate(saturatedMag, shrMag); + cv::integral(shrMag, mag, cv::noArray(), CV_32S); + + // create hog channels + angle /= 60; + + std::vector hist; + for (int bin = 0; bin < 6; ++bin) + { + hist.push_back(cv::Mat(colored.rows, colored.cols, CV_8UC1)); + } + + for (int y = 0; y < saturatedMag.rows; ++y) + { + uchar* magnitude = saturatedMag.ptr(y); + float* ang = angle.ptr(y); + + for (int x = 0; x < saturatedMag.cols; ++x) + { + hist[ (int)ang[x] ].ptr(y)[x] = magnitude[x]; + } + } + +#endif + for(int i = 0; i < 6; ++i) { cv::Mat channel, shrunk, sum; - imgs[std::string("channel") + itoa(i, buff, 10)] >> channel; #if defined USE_REFERENCE_VALUES + imgs[std::string("channel") + itoa(i, buff, 10)] >> channel; hog.push_back(channel); #else - - decimate(channel, shrunk); - // cv::resize(channel, shrunk, cv::Size(), 1.f / shr, 1.f / shr, cv::INTER_AREA); + decimate(hist[i], shrunk); cv::integral(shrunk, sum, cv::noArray(), CV_32S); - hog.push_back(sum); #endif } #if !defined USE_REFERENCE_VALUES + hog.push_back(mag); hog.insert(hog.end(), luvs.begin(), luvs.end()); CV_Assert(hog.size() == 10); #endif // exit(10); } - // { - // // add gauss - // cv::Mat gauss; - // cv::GaussianBlur(colored, gauss, cv::Size(3,3), 0 ,0); - - // colored = gauss; - // // cv::imshow("colored", colored); - - // cv::Mat _luv, shrLuv; - // cv::cvtColor(colored, _luv, CV_BGR2Luv); - - // // cv::imshow("_luv", _luv); - - // cv::resize(_luv, shrLuv, cv::Size(), 1.f / shr, 1.f / shr, cv::INTER_AREA); - - // // cv::imshow("shrLuv", shrLuv); - - // cv::integral(shrLuv, luv); - - // // cv::imshow("luv", luv); - - // std::vector splited; - // split(luv, splited); - - // char buffer[33]; - - // for (int i = 0; i < (int)splited.size(); i++) - // { - // // cv::imshow(itoa(i,buffer,10), splited[i]); - // } - - // cv::Mat grey; - // cv::cvtColor(colored, grey, CV_RGB2GRAY); - - // // cv::imshow("grey", grey); - - // calcHistBins(grey, magnitude, hog, HOG_BINS, shrinkage); - - // hog.insert(hog.end(), splited.begin(), splited.end()); - // } float get(const int x, const int y, const int channel, const cv::Rect& area) const { From b6081438fa20f07ce8312d3bcf342a4e9fd6edc7 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 19 Sep 2012 18:57:59 +0400 Subject: [PATCH 20/56] clean code --- modules/objdetect/src/softcascade.cpp | 138 ++------------------------ 1 file changed, 10 insertions(+), 128 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 989b53659f..8e8a0ce4af 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -232,122 +232,6 @@ int qangle6(float dfdx, float dfdy) return index; } -//ToDo -void calcHistBins(const cv::Mat& grey, cv::Mat& magIntegral, std::vector& histInts, - const int bins, int shrinkage) -{ - static const float magnitudeScaling = 1.f / sqrt(2); - - CV_Assert( grey.type() == CV_8U); - - float scale = 1.f / shrinkage; - - const int rows = grey.rows + 1; - const int cols = grey.cols + 1; - - cv::Mat df_dx(grey.rows, grey.cols, CV_32F), - df_dy(grey.rows, grey.cols, CV_32F), mag, angle; - // cv::Sobel(grey, df_dx, CV_32F, 1, 0); - // cv::Sobel(grey, df_dy, CV_32F, 0, 1); - - for (int y = 1; y < grey.rows -1; ++y) - { - float* dx = df_dx.ptr(y); - float* dy = df_dy.ptr(y); - - const uchar* gr = grey.ptr(y); - const uchar* gr_down = grey.ptr(y - 1); - const uchar* gr_up = grey.ptr(y + 1); - for (int x = 1; x < grey.cols - 1; ++x) - { - float dx_a = gr[x + 1]; - float dx_b = gr[x - 1]; - dx[x] = dx_a - dx_b; - - float dy_a = gr_up[x]; - float dy_b = gr_down[x]; - dy[x] = dy_a - dy_b; - } - } - - cv::cartToPolar(df_dx, df_dy, mag, angle, true); - - mag *= magnitudeScaling; - - cv::Mat saturatedMag(grey.rows, grey.cols, CV_8UC1); - for (int y = 0; y < grey.rows; ++y) - { - float* rm = mag.ptr(y); - uchar* mg = saturatedMag.ptr(y); - for (int x = 0; x < grey.cols; ++x) - { - mg[x] = cv::saturate_cast(rm[x]); - } - } - - mag = saturatedMag; - - histInts.clear(); - std::vector hist; - for (int bin = 0; bin < bins; ++bin) - { - hist.push_back(cv::Mat(rows, cols, CV_8UC1)); - } - - for (int h = 0; h < saturatedMag.rows; ++h) - { - uchar* magnitude = saturatedMag.ptr(h); - float* dfdx = df_dx.ptr(h); - float* dfdy = df_dy.ptr(h); - - for (int w = 0; w < saturatedMag.cols; ++w) - { - hist[ qangle6(dfdx[w], dfdy[w]) ].ptr(h)[w] = magnitude[w]; - } - } - - angle /= 60; - - - // for (int h = 0; h < saturatedMag.rows; ++h) - // { - // uchar* magnitude = saturatedMag.ptr(h); - // float* ang = angle.ptr(h); - - // for (int w = 0; w < saturatedMag.cols; ++w) - // { - // hist[ (int)ang[w] ].ptr(h)[w] = magnitude[w]; - // } - // } - char buffer[33]; - - for (int bin = 0; bin < bins; ++bin) - { - cv::Mat shrunk, sum; - cv::imshow(std::string("hist[bin]") + itoa(bin, buffer, 10), hist[bin]); - cv::resize(hist[bin], shrunk, cv::Size(), scale, scale, cv::INTER_AREA); - cv::imshow(std::string("shrunk") + itoa(bin, buffer, 10), shrunk); - cv::integral(shrunk, sum); - cv::imshow(std::string("sum") + itoa(bin, buffer, 10), sum); - histInts.push_back(sum); - - // std::cout << shrunk << std::endl << std::endl; - } - - cv::Mat shrMag; - cv::imshow("mag", mag); - cv::resize(mag, shrMag, cv::Size(), scale, scale, cv::INTER_AREA); - - cv::FileStorage fs("/home/kellan/actualChannels.xml", cv::FileStorage::WRITE); - cv::imshow("shrunk_channel", shrMag); - fs << "shrunk_channel6" << shrMag; - - // cv::imshow("shrMag", shrMag); - cv::integral(shrMag, magIntegral, mag.depth()); - // cv::imshow("magIntegral", magIntegral); - histInts.push_back(magIntegral); -} - template< typename T> struct Decimate { int shrinkage; @@ -363,8 +247,6 @@ struct Decimate { CV_Assert(cols * shrinkage == in.cols); CV_Assert(rows * shrinkage == in.rows); - std::cout << "type: " << out.type() << std::endl; - for (int outIdx_y = 0; outIdx_y < rows; ++outIdx_y) { T* outPtr = out.ptr(outIdx_y); @@ -387,7 +269,7 @@ struct Decimate { }; -#define USE_REFERENCE_VALUES +// #define USE_REFERENCE_VALUES struct ChannelStorage { @@ -405,6 +287,13 @@ struct ChannelStorage #if defined USE_REFERENCE_VALUES cv::FileStorage imgs("/home/kellan/testInts.xml", cv::FileStorage::READ); char buff[33]; + + for(int i = 0; i < HOG_LUV_BINS; ++i) + { + cv::Mat channel; + imgs[std::string("channel") + itoa(i, buff, 10)] >> channel; + hog.push_back(channel); + } #else // add gauss cv::Mat gauss; @@ -483,22 +372,15 @@ struct ChannelStorage } } -#endif - for(int i = 0; i < 6; ++i) + for(int i = 0; i < HOG_BINS; ++i) { - cv::Mat channel, shrunk, sum; + cv::Mat shrunk, sum; -#if defined USE_REFERENCE_VALUES - imgs[std::string("channel") + itoa(i, buff, 10)] >> channel; - hog.push_back(channel); -#else decimate(hist[i], shrunk); cv::integral(shrunk, sum, cv::noArray(), CV_32S); hog.push_back(sum); -#endif } -#if !defined USE_REFERENCE_VALUES hog.push_back(mag); hog.insert(hog.end(), luvs.begin(), luvs.end()); CV_Assert(hog.size() == 10); From 26af7d7389b70724c71e8bd8fcf20dd4fc993b3b Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 25 Sep 2012 22:09:45 +0400 Subject: [PATCH 21/56] refactor logs --- modules/objdetect/src/softcascade.cpp | 126 +++++++++++--------------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 8e8a0ce4af..cea31b0b32 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -47,8 +47,8 @@ #include #include #include -#include #include +#include namespace { @@ -61,6 +61,13 @@ char *itoa(long i, char* s, int /*dummy_radix*/) // used for noisy printfs // #define WITH_DEBUG_OUT +#if defined WITH_DEBUG_OUT +# define dprintf(format, ...) \ + do { printf(format, __VA_ARGS__); } while (0) +#else +# define dprintf(format, ...) +#endif + struct Octave { int index; @@ -169,9 +176,6 @@ struct CascadeIntrinsics static float getFor(int channel, float scaling) { CV_Assert(channel < 10); -#if defined WITH_DEBUG_OUT - printf("QQQQQQQQQQQQQQQq: %f %f\n", scaling, fabs(scaling - 1.f)); -#endif if (fabs(scaling - 1.f) < FLT_EPSILON) // if (scaling == 1.f) @@ -193,9 +197,7 @@ struct CascadeIntrinsics float a = A[(int)(scaling >= 1)][(int)(channel > 6)]; float b = B[(int)(scaling >= 1)][(int)(channel > 6)]; -#if defined WITH_DEBUG_OUT - printf("!!! scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); -#endif + dprintf("scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); return a * pow(scaling, b); } }; @@ -269,6 +271,7 @@ struct Decimate { }; +// use previous stored integrals for regression testing // #define USE_REFERENCE_VALUES struct ChannelStorage @@ -279,14 +282,14 @@ struct ChannelStorage enum {HOG_BINS = 6, HOG_LUV_BINS = 10}; ChannelStorage() {} - ChannelStorage(cv::Mat& colored, int shr) : shrinkage(shr) + ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) { hog.clear(); Decimate decimate(shr); #if defined USE_REFERENCE_VALUES - cv::FileStorage imgs("/home/kellan/testInts.xml", cv::FileStorage::READ); char buff[33]; + cv::FileStorage imgs("/home/kellan/testInts.xml", cv::FileStorage::READ); for(int i = 0; i < HOG_LUV_BINS; ++i) { @@ -310,10 +313,10 @@ struct ChannelStorage // shrink and integrate for (int i = 0; i < (int)splited.size(); i++) { - cv::Mat shrunk, sum; - decimate(splited[i], shrunk); - cv::integral(shrunk, sum, cv::noArray(), CV_32S); - luvs.push_back(sum); + cv::Mat shrunk, sum; + decimate(splited[i], shrunk); + cv::integral(shrunk, sum, cv::noArray(), CV_32S); + luvs.push_back(sum); } // convert to grey @@ -353,12 +356,12 @@ struct ChannelStorage cv::integral(shrMag, mag, cv::noArray(), CV_32S); // create hog channels - angle /= 60; + angle /= 60.f; std::vector hist; - for (int bin = 0; bin < 6; ++bin) + for (int bin = 0; bin < HOG_BINS; ++bin) { - hist.push_back(cv::Mat(colored.rows, colored.cols, CV_8UC1)); + hist.push_back(cv::Mat::zeros(saturatedMag.rows, saturatedMag.cols, CV_8UC1)); } for (int y = 0; y < saturatedMag.rows; ++y) @@ -375,7 +378,6 @@ struct ChannelStorage for(int i = 0; i < HOG_BINS; ++i) { cv::Mat shrunk, sum; - decimate(hist[i], shrunk); cv::integral(shrunk, sum, cv::noArray(), CV_32S); hog.push_back(sum); @@ -385,7 +387,6 @@ struct ChannelStorage hog.insert(hog.end(), luvs.begin(), luvs.end()); CV_Assert(hog.size() == 10); #endif - // exit(10); } float get(const int x, const int y, const int channel, const cv::Rect& area) const @@ -393,26 +394,23 @@ struct ChannelStorage CV_Assert(channel < HOG_LUV_BINS); const cv::Mat m = hog[channel]; -#if defined WITH_DEBUG_OUT - printf("feature box %d %d %d %d ", area.x, area.y, area.width, area.height); - printf("get for channel %d\n", channel); - printf("!! %d\n", m.depth()); + dprintf("feature box %d %d %d %d ", area.x, area.y, area.width, area.height); + dprintf("get for channel %d\n", channel); + dprintf("!! %d\n", m.depth()); - printf("extract feature for: [%d %d] [%d %d] [%d %d] [%d %d]\n", + dprintf("extract feature for: [%d %d] [%d %d] [%d %d] [%d %d]\n", x + area.x, y + area.y, x + area.width,y + area.y, x + area.width,y + area.height, x + area.x, y + area.height); - printf("at point %d %d with offset %d\n", x, y, 0); -#endif + dprintf("at point %d %d with offset %d\n", x, y, 0); int a = m.ptr(y + area.y)[x + area.x]; int b = m.ptr(y + area.y)[x + area.width]; int c = m.ptr(y + area.height)[x + area.width]; int d = m.ptr(y + area.height)[x + area.x]; -#if defined WITH_DEBUG_OUT - printf(" retruved integral values: %d %d %d %d\n", a, b, c, d); -#endif + dprintf(" retruved integral values: %d %d %d %d\n", a, b, c, d); + return (a - b + c - d); } }; @@ -444,12 +442,10 @@ struct cv::SoftCascade::Filds float scaling = CascadeIntrinsics::getFor(feature.channel, relScale); scaledRect = feature.rect; -#if defined WITH_DEBUG_OUT - printf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, + dprintf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, scaledRect.width, scaledRect.height); - std::cout << "rescale: " << feature.channel << " " << relScale << " " << scaling << std::endl; -#endif + dprintf("rescale: %d %f %f\n",feature.channel, relScale, scaling); float farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); // rescale @@ -458,14 +454,9 @@ struct cv::SoftCascade::Filds scaledRect.width = cvRound(relScale * scaledRect.width); scaledRect.height = cvRound(relScale * scaledRect.height); -#if defined WITH_DEBUG_OUT - printf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, + dprintf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, scaledRect.width, scaledRect.height); - std::cout << " new rect: " << scaledRect.x << " " << scaledRect.y - << " " << scaledRect.width << " " << scaledRect.height << " "; -#endif - float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); float approx = 1.f; @@ -474,20 +465,14 @@ struct cv::SoftCascade::Filds const float expected_new_area = farea * relScale * relScale; approx = expected_new_area / sarea; -#if defined WITH_DEBUG_OUT - std::cout << " rel areas " << expected_new_area << " " << sarea << std::endl; -#endif - + dprintf(" rel areas %f %f\n", expected_new_area, sarea); } // compensation areas rounding float rootThreshold = threshold / approx; rootThreshold *= scaling; -#if defined WITH_DEBUG_OUT - std::cout << "approximation " << approx << " " << threshold << " -> " << rootThreshold - << " " << scaling << std::endl; -#endif + dprintf("approximation %f %f -> %f %f\n", approx, threshold, rootThreshold, scaling); return rootThreshold; } @@ -495,26 +480,21 @@ struct cv::SoftCascade::Filds void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, std::vector& detections) const { -#if defined WITH_DEBUG_OUT - std::cout << "detect at: " << dx << " " << dy << std::endl; -#endif + dprintf("detect at: %d %d\n", dx, dy); + float detectionScore = 0.f; const Octave& octave = *(level.octave); int stBegin = octave.index * octave.stages, stEnd = stBegin + octave.stages; -#if defined WITH_DEBUG_OUT - std::cout << " octave stages: " << stBegin << " to " << stEnd << " index " << octave.index << " " - << octave.scale << " level " << level.origScale << std::endl; -#endif + dprintf(" octave stages: %d to %d index %d %f level %f\n", + stBegin, stEnd, octave.index, octave.scale, level.origScale); int st = stBegin; for(; st < stEnd; ++st) { -#if defined WITH_DEBUG_OUT - printf("index: %d\n", st); -#endif + dprintf("index: %d\n", st); const Stage& stage = stages[st]; { @@ -529,15 +509,11 @@ struct cv::SoftCascade::Filds float sum = storage.get(dx, dy, feature.channel, scaledRect); -#if defined WITH_DEBUG_OUT - printf("root feature %d %f\n",feature.channel, sum); -#endif + dprintf("root feature %d %f\n",feature.channel, sum); int next = (sum >= threshold)? 2 : 1; -#if defined WITH_DEBUG_OUT - printf("go: %d (%f >= %f)\n\n" ,next, sum, threshold); -#endif + dprintf("go: %d (%f >= %f)\n\n" ,next, sum, threshold); // leaves const Node& leaf = nodes[nId + next]; @@ -549,23 +525,24 @@ struct cv::SoftCascade::Filds int lShift = (next - 1) * 2 + ((sum >= threshold) ? 1 : 0); float impact = leaves[(st * 4) + lShift]; -#if defined WITH_DEBUG_OUT - printf("decided: %d (%f >= %f) %d %f\n\n" ,next, sum, threshold, lShift, impact); -#endif + + dprintf("decided: %d (%f >= %f) %d %f\n\n" ,next, sum, threshold, lShift, impact); + detectionScore += impact; } + dprintf("extracted stage:\n"); + dprintf("ct %f\n", stage.threshold); + dprintf("computed score %f\n\n", detectionScore); + #if defined WITH_DEBUG_OUT - printf("extracted stage:\n"); - printf("ct %f\n", stage.threshold); - printf("computed score %f\n\n", detectionScore); if (st - stBegin > 50 ) break; #endif if (detectionScore <= stage.threshold) break; } - printf("x %d y %d: %d\n", dx, dy, st - stBegin); + dprintf("x %d y %d: %d\n", dx, dy, st - stBegin); if (st == stEnd) { @@ -793,7 +770,7 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector detections; @@ -826,6 +806,8 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Fri, 28 Sep 2012 10:21:22 +0400 Subject: [PATCH 22/56] add perfomance test for CPU soft cascade --- .../objdetect/perf/perf_cascadeclassifier.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/modules/objdetect/perf/perf_cascadeclassifier.cpp b/modules/objdetect/perf/perf_cascadeclassifier.cpp index 98007e45d3..cb214154ce 100644 --- a/modules/objdetect/perf/perf_cascadeclassifier.cpp +++ b/modules/objdetect/perf/perf_cascadeclassifier.cpp @@ -52,3 +52,26 @@ PERF_TEST_P(ImageName_MinSize, CascadeClassifierLBPFrontalFace, std::sort(faces.begin(), faces.end(), comparators::RectLess()); SANITY_CHECK(faces, 3.001 * faces.size()); } + +typedef std::tr1::tuple fixture; +typedef perf::TestBaseWithParam detect; + +PERF_TEST_P(detect, SoftCascade, + testing::Combine(testing::Values(std::string("cv/cascadeandhog/sc_cvpr_2012_to_opencv.xml")), + testing::Values(std::string("cv/cascadeandhog/bahnhof/image_00000000_0.png")))) +{ + cv::Mat colored = imread(getDataPath(get<1>(GetParam()))); + ASSERT_FALSE(colored.empty()); + + cv::SoftCascade cascade; + ASSERT_TRUE(cascade.load(getDataPath(get<0>(GetParam())))); + + std::vector rois, objectBoxes; + cascade.detectMultiScale(colored, rois, objectBoxes); + + TEST_CYCLE() + { + cascade.detectMultiScale(colored, rois, objectBoxes); + } + SANITY_CHECK(objectBoxes); +} From 0ff8a4633d156d794f4143d5e3ca717ee710b521 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Fri, 28 Sep 2012 16:32:33 +0400 Subject: [PATCH 23/56] remove pow calculations --- modules/objdetect/src/softcascade.cpp | 154 ++++++++------------------ 1 file changed, 48 insertions(+), 106 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index cea31b0b32..6844336087 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -50,6 +50,11 @@ #include #include +// use previous stored integrals for regression testing +// #define USE_REFERENCE_VALUES + +#if defined USE_REFERENCE_VALUES + namespace { char *itoa(long i, char* s, int /*dummy_radix*/) @@ -58,6 +63,8 @@ char *itoa(long i, char* s, int /*dummy_radix*/) return s; } +#endif + // used for noisy printfs // #define WITH_DEBUG_OUT @@ -68,6 +75,8 @@ char *itoa(long i, char* s, int /*dummy_radix*/) # define dprintf(format, ...) #endif +namespace { + struct Octave { int index; @@ -143,32 +152,6 @@ struct Object Object(const cv::Rect& r, const float c, Class dt = PEDESTRIAN) : rect(r), confidence(c), detType(dt) {} }; -struct Level -{ - const Octave* octave; - - float origScale; - float relScale; - float shrScale; // used for marking detection - - cv::Size workRect; - cv::Size objSize; - - Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) - : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / (float)shrinkage), - workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), - objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) - {} - - void markDetection(const int x, const int y, float confidence, std::vector& detections) const - { - int shrinkage = (*octave).shrinkage; - cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height); - - detections.push_back(Object(rect, confidence)); - } -}; - struct CascadeIntrinsics { static const float lambda = 1.099f, a = 0.89f; @@ -202,37 +185,36 @@ struct CascadeIntrinsics } }; - -int qangle6(float dfdx, float dfdy) +struct Level { - static const float vectors[6][2] = + const Octave* octave; + + float origScale; + float relScale; + float shrScale; // used for marking detection + + cv::Size workRect; + cv::Size objSize; + + float scaling[2]; + + Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) + : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / (float)shrinkage), + workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), + objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) { - {std::cos(0), std::sin(0) }, - {std::cos(M_PI / 6.f), std::sin(M_PI / 6.f) }, - {std::cos(M_PI / 3.f), std::sin(M_PI / 3.f) }, - - {std::cos(M_PI / 2.f), std::sin(M_PI / 2.f) }, - {std::cos(2.f * M_PI / 3.f), std::sin(2.f * M_PI / 3.f)}, - {std::cos(5.f * M_PI / 6.f), std::sin(5.f * M_PI / 6.f)} - }; - - int index = 0; - - float dot = fabs(dfdx * vectors[0][0] + dfdy * vectors[0][1]); - - for(int i = 1; i < 6; ++i) - { - const float curr = fabs(dfdx * vectors[i][0] + dfdy * vectors[i][1]); - - if(curr > dot) - { - dot = curr; - index = i; - } + scaling[0] = CascadeIntrinsics::getFor(0, relScale); + scaling[1] = CascadeIntrinsics::getFor(9, relScale); } - return index; -} + void markDetection(const int x, const int y, float confidence, std::vector& detections) const + { + int shrinkage = (*octave).shrinkage; + cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height); + + detections.push_back(Object(rect, confidence)); + } +}; template< typename T> struct Decimate { @@ -271,9 +253,6 @@ struct Decimate { }; -// use previous stored integrals for regression testing -// #define USE_REFERENCE_VALUES - struct ChannelStorage { std::vector hog; @@ -437,9 +416,10 @@ struct cv::SoftCascade::Filds typedef std::vector::iterator octIt_t; - float rescale(const Feature& feature, const float relScale, cv::Rect& scaledRect, const float threshold) const + float rescale(const Feature& feature, const float scaling, const float relScale, + cv::Rect& scaledRect, const float threshold) const { - float scaling = CascadeIntrinsics::getFor(feature.channel, relScale); + // float scaling = CascadeIntrinsics::getFor(feature.channel, relScale); scaledRect = feature.rect; dprintf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, @@ -460,16 +440,16 @@ struct cv::SoftCascade::Filds float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); float approx = 1.f; - if (fabs(farea - 0.f) > FLT_EPSILON && fabs(farea - 0.f) > FLT_EPSILON) + // if (fabs(farea - 0.f) > FLT_EPSILON && fabs(farea - 0.f) > FLT_EPSILON) { const float expected_new_area = farea * relScale * relScale; - approx = expected_new_area / sarea; + approx = sarea / expected_new_area; dprintf(" rel areas %f %f\n", expected_new_area, sarea); } // compensation areas rounding - float rootThreshold = threshold / approx; + float rootThreshold = threshold * approx; rootThreshold *= scaling; dprintf("approximation %f %f -> %f %f\n", approx, threshold, rootThreshold, scaling); @@ -504,7 +484,8 @@ struct cv::SoftCascade::Filds const Node& node = nodes[nId]; const Feature& feature = features[node.feature]; cv::Rect scaledRect; - float threshold = rescale(feature, level.relScale, scaledRect, node.threshold); + float threshold = rescale(feature, level.scaling[(int)(feature.channel > 6)], + level.relScale, scaledRect, node.threshold); float sum = storage.get(dx, dy, feature.channel, scaledRect); @@ -519,7 +500,8 @@ struct cv::SoftCascade::Filds const Node& leaf = nodes[nId + next]; const Feature& fLeaf = features[leaf.feature]; - threshold = rescale(fLeaf, level.relScale, scaledRect, leaf.threshold); + threshold = rescale(fLeaf, level.scaling[(int)(fLeaf.channel > 6)], + level.relScale, scaledRect, leaf.threshold); sum = storage.get(dx, dy, fLeaf.channel, scaledRect); @@ -546,7 +528,7 @@ struct cv::SoftCascade::Filds if (st == stEnd) { - std::cout << " got " << st << std::endl; + dprintf(" got %d\n", st); level.markDetection(dx, dy, detectionScore, detections); } } @@ -701,25 +683,6 @@ struct cv::SoftCascade::Filds } shrinkage = octaves[0].shrinkage; - - //debug print - // std::cout << "collected " << stages.size() << " stages" << std::endl; - // for (int i = 0; i < (int)stages.size(); ++i) - // { - // std::cout << "stage " << i << ": " << stages[i].threshold << std::endl; - // } - - // std::cout << "collected " << nodes.size() << " nodes" << std::endl; - // for (int i = 0; i < (int)nodes.size(); ++i) - // { - // std::cout << "node " << i << ": " << nodes[i].threshold << " " << nodes[i].feature << std::endl; - // } - - // std::cout << "collected " << leaves.size() << " leaves" << std::endl; - // for (int i = 0; i < (int)leaves.size(); ++i) - // { - // std::cout << "leaf " << i << ": " << leaves[i] << std::endl; - // } return true; } }; @@ -752,8 +715,7 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -// #define DEBUG_STORE_IMAGES -#define DEBUG_SHOW_RESULT +// #define DEBUG_SHOW_RESULT void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, std::vector& objects, const int /*rejectfactor*/) @@ -772,26 +734,6 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector> doppia; - - cv::Mat diff; - cv::absdiff(image1, doppia, diff); - - fs << "absdiff" << diff; - fs.release(); - -#endif - - cv::imshow("!!", image1); - cv::waitKey(0); - // create integrals ChannelStorage storage(image, fld.shrinkage); @@ -831,9 +773,9 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Fri, 5 Oct 2012 15:59:51 +0400 Subject: [PATCH 24/56] remove Mat copying --- modules/objdetect/src/softcascade.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 6844336087..dcdf2633ab 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -283,7 +283,7 @@ struct ChannelStorage // convert to luv cv::Mat luv; - cv::cvtColor(colored, luv, CV_RGB2Luv); + cv::cvtColor(colored, luv, CV_BGR2Luv); // split to 3 one channel matrix std::vector splited, luvs; @@ -300,7 +300,7 @@ struct ChannelStorage // convert to grey cv::Mat grey; - cv::cvtColor(colored, grey, CV_RGB2GRAY); + cv::cvtColor(colored, grey, CV_BGR2GRAY); // get derivative cv::Mat df_dx, df_dy, mag, angle; @@ -371,7 +371,7 @@ struct ChannelStorage float get(const int x, const int y, const int channel, const cv::Rect& area) const { CV_Assert(channel < HOG_LUV_BINS); - const cv::Mat m = hog[channel]; + const cv::Mat& m = hog[channel]; dprintf("feature box %d %d %d %d ", area.x, area.y, area.width, area.height); dprintf("get for channel %d\n", channel); @@ -731,9 +731,6 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Sat, 6 Oct 2012 15:15:59 +0400 Subject: [PATCH 25/56] add const qualifier to detectMultiScale method --- .../include/opencv2/objdetect/objdetect.hpp | 2 +- modules/objdetect/src/softcascade.cpp | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index bb4240f59b..fd48b32ffe 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -512,7 +512,7 @@ public: //! return vector of bounding boxes. Each box contains one detected object virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - int rejectfactor = 1); + int rejectfactor = 1) const; protected: enum { BOOST = 0 }; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index dcdf2633ab..d3c5821c32 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -419,7 +419,6 @@ struct cv::SoftCascade::Filds float rescale(const Feature& feature, const float scaling, const float relScale, cv::Rect& scaledRect, const float threshold) const { - // float scaling = CascadeIntrinsics::getFor(feature.channel, relScale); scaledRect = feature.rect; dprintf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, @@ -439,18 +438,13 @@ struct cv::SoftCascade::Filds float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - float approx = 1.f; - // if (fabs(farea - 0.f) > FLT_EPSILON && fabs(farea - 0.f) > FLT_EPSILON) - { - const float expected_new_area = farea * relScale * relScale; - approx = sarea / expected_new_area; + const float expected_new_area = farea * relScale * relScale; + float approx = sarea / expected_new_area; - dprintf(" rel areas %f %f\n", expected_new_area, sarea); - } + dprintf(" rel areas %f %f\n", expected_new_area, sarea); // compensation areas rounding - float rootThreshold = threshold * approx; - rootThreshold *= scaling; + float rootThreshold = threshold * approx * scaling; dprintf("approximation %f %f -> %f %f\n", approx, threshold, rootThreshold, scaling); @@ -715,10 +709,10 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -// #define DEBUG_SHOW_RESULT +//#define DEBUG_SHOW_RESULT void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, - std::vector& objects, const int /*rejectfactor*/) + std::vector& objects, const int /*rejectfactor*/) const { typedef std::vector::const_iterator RIter_t; // only color images are supperted From 2914f2452185d4ef976de1b556e04085e4836aad Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Sat, 6 Oct 2012 16:58:48 +0400 Subject: [PATCH 26/56] improve cpu version of SoftCascade: - remove division - remove cvRound - cache feature area --- modules/objdetect/src/softcascade.cpp | 77 ++++++++++++--------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index d3c5821c32..9eba9bd902 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -126,6 +126,7 @@ struct Feature { int channel; cv::Rect rect; + float rarea; static const char * const SC_F_CHANNEL; static const char * const SC_F_RECT; @@ -136,6 +137,9 @@ struct Feature cv::FileNode rn = fn[SC_F_RECT]; cv::FileNodeIterator r_it = rn.end(); rect = cv::Rect(*(--r_it), *(--r_it), *(--r_it), *(--r_it)); + + // 1 / area + rarea = 1.f / ((rect.width - rect.x) * (rect.height - rect.y)); } }; @@ -192,10 +196,13 @@ struct Level float origScale; float relScale; float shrScale; // used for marking detection + int scaleshift; cv::Size workRect; cv::Size objSize; + enum { R_SHIFT = 1 << 15 }; + float scaling[2]; Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) @@ -203,8 +210,9 @@ struct Level workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) { - scaling[0] = CascadeIntrinsics::getFor(0, relScale); - scaling[1] = CascadeIntrinsics::getFor(9, relScale); + scaling[0] = CascadeIntrinsics::getFor(0, relScale) / (relScale * relScale); + scaling[1] = CascadeIntrinsics::getFor(9, relScale) / (relScale * relScale); + scaleshift = relScale * (1 << 16); } void markDetection(const int x, const int y, float confidence, std::vector& detections) const @@ -214,6 +222,25 @@ struct Level detections.push_back(Object(rect, confidence)); } + + float rescale(cv::Rect& scaledRect, const float threshold, int idx) const + { + // rescale + // scaledRect.x = cvRound(relScale * scaledRect.x); + // scaledRect.y = cvRound(relScale * scaledRect.y); + // scaledRect.width = cvRound(relScale * scaledRect.width); + // scaledRect.height = cvRound(relScale * scaledRect.height); + + scaledRect.x = (scaleshift * scaledRect.x + R_SHIFT) >> 16; + scaledRect.y = (scaleshift * scaledRect.y + R_SHIFT) >> 16; + scaledRect.width = (scaleshift * scaledRect.width + R_SHIFT) >> 16; + scaledRect.height = (scaleshift * scaledRect.height + R_SHIFT) >> 16; + + float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + + // compensation areas rounding + return (threshold * scaling[idx] * sarea); + } }; template< typename T> @@ -416,41 +443,6 @@ struct cv::SoftCascade::Filds typedef std::vector::iterator octIt_t; - float rescale(const Feature& feature, const float scaling, const float relScale, - cv::Rect& scaledRect, const float threshold) const - { - scaledRect = feature.rect; - - dprintf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, - scaledRect.width, scaledRect.height); - - dprintf("rescale: %d %f %f\n",feature.channel, relScale, scaling); - - float farea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - // rescale - scaledRect.x = cvRound(relScale * scaledRect.x); - scaledRect.y = cvRound(relScale * scaledRect.y); - scaledRect.width = cvRound(relScale * scaledRect.width); - scaledRect.height = cvRound(relScale * scaledRect.height); - - dprintf("feature %d box %d %d %d %d\n", feature.channel, scaledRect.x, scaledRect.y, - scaledRect.width, scaledRect.height); - - float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); - - const float expected_new_area = farea * relScale * relScale; - float approx = sarea / expected_new_area; - - dprintf(" rel areas %f %f\n", expected_new_area, sarea); - - // compensation areas rounding - float rootThreshold = threshold * approx * scaling; - - dprintf("approximation %f %f -> %f %f\n", approx, threshold, rootThreshold, scaling); - - return rootThreshold; - } - void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, std::vector& detections) const { @@ -477,10 +469,9 @@ struct cv::SoftCascade::Filds // work with root node const Node& node = nodes[nId]; const Feature& feature = features[node.feature]; - cv::Rect scaledRect; - float threshold = rescale(feature, level.scaling[(int)(feature.channel > 6)], - level.relScale, scaledRect, node.threshold); + cv::Rect scaledRect(feature.rect); + float threshold = level.rescale(scaledRect, node.threshold,(int)(feature.channel > 6)) * feature.rarea; float sum = storage.get(dx, dy, feature.channel, scaledRect); @@ -494,10 +485,10 @@ struct cv::SoftCascade::Filds const Node& leaf = nodes[nId + next]; const Feature& fLeaf = features[leaf.feature]; - threshold = rescale(fLeaf, level.scaling[(int)(fLeaf.channel > 6)], - level.relScale, scaledRect, leaf.threshold); - sum = storage.get(dx, dy, fLeaf.channel, scaledRect); + scaledRect = fLeaf.rect; + threshold = level.rescale(scaledRect, leaf.threshold, (int)(fLeaf.channel > 6)) * fLeaf.rarea; + sum = storage.get(dx, dy, fLeaf.channel, scaledRect); int lShift = (next - 1) * 2 + ((sum >= threshold) ? 1 : 0); float impact = leaves[(st * 4) + lShift]; From 754fd7311b484aa05637a3d17aff509b79ae1209 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Sat, 6 Oct 2012 20:18:36 +0400 Subject: [PATCH 27/56] improve pointer usage --- modules/objdetect/src/softcascade.cpp | 59 +++++++++++---------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 9eba9bd902..73639084d3 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -226,11 +226,6 @@ struct Level float rescale(cv::Rect& scaledRect, const float threshold, int idx) const { // rescale - // scaledRect.x = cvRound(relScale * scaledRect.x); - // scaledRect.y = cvRound(relScale * scaledRect.y); - // scaledRect.width = cvRound(relScale * scaledRect.width); - // scaledRect.height = cvRound(relScale * scaledRect.height); - scaledRect.x = (scaleshift * scaledRect.x + R_SHIFT) >> 16; scaledRect.y = (scaleshift * scaledRect.y + R_SHIFT) >> 16; scaledRect.width = (scaleshift * scaledRect.width + R_SHIFT) >> 16; @@ -284,6 +279,8 @@ struct ChannelStorage { std::vector hog; int shrinkage; + int offset; + int step; enum {HOG_BINS = 6, HOG_LUV_BINS = 10}; @@ -391,31 +388,23 @@ struct ChannelStorage hog.push_back(mag); hog.insert(hog.end(), luvs.begin(), luvs.end()); - CV_Assert(hog.size() == 10); + + step = hog[0].cols; + + // CV_Assert(hog.size() == 10); #endif } - float get(const int x, const int y, const int channel, const cv::Rect& area) const + float get(const int channel, const cv::Rect& area) const { - CV_Assert(channel < HOG_LUV_BINS); + // CV_Assert(channel < HOG_LUV_BINS); const cv::Mat& m = hog[channel]; + int *ptr = ((int*)(m.data)) + offset; - dprintf("feature box %d %d %d %d ", area.x, area.y, area.width, area.height); - dprintf("get for channel %d\n", channel); - dprintf("!! %d\n", m.depth()); - - dprintf("extract feature for: [%d %d] [%d %d] [%d %d] [%d %d]\n", - x + area.x, y + area.y, x + area.width,y + area.y, x + area.width,y + area.height, - x + area.x, y + area.height); - - dprintf("at point %d %d with offset %d\n", x, y, 0); - - int a = m.ptr(y + area.y)[x + area.x]; - int b = m.ptr(y + area.y)[x + area.width]; - int c = m.ptr(y + area.height)[x + area.width]; - int d = m.ptr(y + area.height)[x + area.x]; - - dprintf(" retruved integral values: %d %d %d %d\n", a, b, c, d); + 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]; return (a - b + c - d); } @@ -443,8 +432,7 @@ struct cv::SoftCascade::Filds typedef std::vector::iterator octIt_t; - void detectAt(const Level& level, const int dx, const int dy, const ChannelStorage& storage, - std::vector& detections) const + void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage, std::vector& detections) const { dprintf("detect at: %d %d\n", dx, dy); @@ -473,7 +461,7 @@ struct cv::SoftCascade::Filds float threshold = level.rescale(scaledRect, node.threshold,(int)(feature.channel > 6)) * feature.rarea; - float sum = storage.get(dx, dy, feature.channel, scaledRect); + float sum = storage.get(feature.channel, scaledRect); dprintf("root feature %d %f\n",feature.channel, sum); @@ -488,7 +476,7 @@ struct cv::SoftCascade::Filds scaledRect = fLeaf.rect; threshold = level.rescale(scaledRect, leaf.threshold, (int)(fLeaf.channel > 6)) * fLeaf.rarea; - sum = storage.get(dx, dy, fLeaf.channel, scaledRect); + sum = storage.get(fLeaf.channel, scaledRect); int lShift = (next - 1) * 2 + ((sum >= threshold) ? 1 : 0); float impact = leaves[(st * 4) + lShift]; @@ -506,16 +494,13 @@ struct cv::SoftCascade::Filds if (st - stBegin > 50 ) break; #endif - if (detectionScore <= stage.threshold) break; + if (detectionScore <= stage.threshold) return; } dprintf("x %d y %d: %d\n", dx, dy, st - stBegin); + dprintf(" got %d\n", st); - if (st == stEnd) - { - dprintf(" got %d\n", st); - level.markDetection(dx, dy, detectionScore, detections); - } + level.markDetection(dx, dy, detectionScore, detections); } octIt_t fitOctave(const float& logFactor) @@ -738,13 +723,15 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Sat, 6 Oct 2012 21:43:25 +0400 Subject: [PATCH 28/56] add Detection struct to interface --- .../include/opencv2/objdetect/objdetect.hpp | 12 +++++++++++- modules/objdetect/perf/perf_cascadeclassifier.cpp | 4 +++- modules/objdetect/src/softcascade.cpp | 4 ++-- modules/objdetect/test/test_softcascade.cpp | 3 ++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index fd48b32ffe..3724eaadb0 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -493,6 +493,16 @@ protected: class CV_EXPORTS SoftCascade { public: + + struct CV_EXPORTS Detection + { + cv::Rect rect; + float confidence; + int kind; + + enum {PEDESTRIAN = 0}; + }; + //! An empty cascade will be created. SoftCascade(); @@ -511,7 +521,7 @@ public: virtual ~SoftCascade(); //! return vector of bounding boxes. Each box contains one detected object - virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, + virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, int rejectfactor = 1) const; protected: diff --git a/modules/objdetect/perf/perf_cascadeclassifier.cpp b/modules/objdetect/perf/perf_cascadeclassifier.cpp index cb214154ce..271ede0072 100644 --- a/modules/objdetect/perf/perf_cascadeclassifier.cpp +++ b/modules/objdetect/perf/perf_cascadeclassifier.cpp @@ -60,13 +60,15 @@ PERF_TEST_P(detect, SoftCascade, testing::Combine(testing::Values(std::string("cv/cascadeandhog/sc_cvpr_2012_to_opencv.xml")), testing::Values(std::string("cv/cascadeandhog/bahnhof/image_00000000_0.png")))) { + typedef cv::SoftCascade::Detection detection_t; cv::Mat colored = imread(getDataPath(get<1>(GetParam()))); ASSERT_FALSE(colored.empty()); cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(getDataPath(get<0>(GetParam())))); - std::vector rois, objectBoxes; + std::vector rois; + std::vector objectBoxes; cascade.detectMultiScale(colored, rois, objectBoxes); TEST_CYCLE() diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 73639084d3..8d1853c721 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -685,10 +685,10 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -//#define DEBUG_SHOW_RESULT +#define DEBUG_SHOW_RESULT void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, - std::vector& objects, const int /*rejectfactor*/) const + std::vector& objects, const int /*rejectfactor*/) const { typedef std::vector::const_iterator RIter_t; // only color images are supperted diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index 9c316fd906..00b45f02d1 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -51,6 +51,7 @@ TEST(SoftCascade, readCascade) TEST(SoftCascade, detect) { + typedef cv::SoftCascade::Detection detection_t; std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; cv::SoftCascade cascade; ASSERT_TRUE(cascade.load(xml)); @@ -58,7 +59,7 @@ TEST(SoftCascade, detect) cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); ASSERT_FALSE(colored.empty()); - std::vector objectBoxes; + std::vector objectBoxes; std::vector rois; rois.push_back(cv::Rect(0, 0, 640, 480)); // ASSERT_NO_THROW( From 565174378418b77d380dd09508d29732c4623b93 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Sat, 6 Oct 2012 22:22:53 +0400 Subject: [PATCH 29/56] remove debug imshow from code --- .../include/opencv2/objdetect/objdetect.hpp | 2 + modules/objdetect/src/softcascade.cpp | 44 ++++--------------- modules/objdetect/test/test_softcascade.cpp | 35 ++++++++++++--- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 3724eaadb0..f28d683e58 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -501,6 +501,8 @@ public: int kind; enum {PEDESTRIAN = 0}; + + Detection(const cv::Rect& r, const float c, int k = PEDESTRIAN) : rect(r), confidence(c), kind(k) {} }; //! An empty cascade will be created. diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 8d1853c721..cec7741f2b 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -204,6 +204,7 @@ struct Level enum { R_SHIFT = 1 << 15 }; float scaling[2]; + typedef cv::SoftCascade::Detection detection_t; Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / (float)shrinkage), @@ -215,12 +216,12 @@ struct Level scaleshift = relScale * (1 << 16); } - void markDetection(const int x, const int y, float confidence, std::vector& detections) const + void markDetection(const int x, const int y, float confidence, std::vector& detections) const { int shrinkage = (*octave).shrinkage; cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height); - detections.push_back(Object(rect, confidence)); + detections.push_back(detection_t(rect, confidence)); } float rescale(cv::Rect& scaledRect, const float threshold, int idx) const @@ -432,7 +433,8 @@ struct cv::SoftCascade::Filds typedef std::vector::iterator octIt_t; - void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage, std::vector& detections) const + void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage, + std::vector& detections) const { dprintf("detect at: %d %d\n", dx, dy); @@ -685,12 +687,11 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const return true; } -#define DEBUG_SHOW_RESULT - void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, std::vector& objects, const int /*rejectfactor*/) const { typedef std::vector::const_iterator RIter_t; + // only color images are supperted CV_Assert(image.type() == CV_8UC3); @@ -704,49 +705,20 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector detections; - typedef std::vector::const_iterator lIt; - int total = 0, l = 0; + for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) { const Level& level = *it; -#if defined WITH_DEBUG_OUT - std::cout << "================================ " << l++ << std::endl; -#else - (void)l; -#endif - // int dx = 79; int dy = 76; for (int dy = 0; dy < level.workRect.height; ++dy) { for (int dx = 0; dx < level.workRect.width; ++dx) { storage.offset = dy * storage.step + dx; - fld.detectAt(dx, dy, level, storage, detections); - - total++; + fld.detectAt(dx, dy, level, storage, objects); } } - -#if defined DEBUG_SHOW_RESULT - cv::Mat out = image.clone(); - - printf("TOTAL: %d from %d\n", (int)detections.size(),total) ; - - for(int i = 0; i < (int)detections.size(); ++i) - { - cv::rectangle(out, detections[i].rect, cv::Scalar(255, 0, 0, 255), 1); - } - - cv::imshow("out", out); - cv::waitKey(0); - std::cout << "work rect: " << level.workRect.width << " " << level.workRect.height << std::endl; -#endif - - detections.clear(); } - // std::swap(detections, objects); } \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index 00b45f02d1..59428ea5d9 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -59,11 +59,36 @@ TEST(SoftCascade, detect) cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); ASSERT_FALSE(colored.empty()); - std::vector objectBoxes; + std::vector objects; std::vector rois; rois.push_back(cv::Rect(0, 0, 640, 480)); - // ASSERT_NO_THROW( - // { - cascade.detectMultiScale(colored, rois, objectBoxes); - // }); + + cascade.detectMultiScale(colored, rois, objects); + + std::cout << "detected: " << (int)objects.size() << std::endl; + + cv::Mat out = colored.clone(); + int level = 0, total = 0; + int levelWidth = objects[0].rect.width; + + for(int i = 0 ; i < (int)objects.size(); ++i) + { + if (objects[i].rect.width != levelWidth) + { + std::cout << "Level: " << level << " total " << total << std::endl; + cv::imshow("out", out); + cv::waitKey(0); + + out = colored.clone(); + levelWidth = objects[i].rect.width; + total = 0; + level++; + } + cv::rectangle(out, objects[i].rect, cv::Scalar(255, 0, 0, 255), 1); + std::cout << "detection: " << objects[i].rect.x + << " " << objects[i].rect.y + << " " << objects[i].rect.width + << " " << objects[i].rect.height << std::endl; + total++; + } } \ No newline at end of file From 3d41846c390fdbbe7ed85295bc0187a26af03cf6 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 24 Oct 2012 23:10:15 +0400 Subject: [PATCH 30/56] move frame processing into separate class --- .../include/opencv2/objdetect/objdetect.hpp | 22 +++ modules/objdetect/src/isf.cpp | 155 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 modules/objdetect/src/isf.cpp diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index f28d683e58..b56d5f1db6 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -543,6 +543,28 @@ private: Filds* filds; }; +class CV_EXPORTS IntegralChannels +{ +public: + //! constrictor form resizing factor. + //! Param shr is a resizing factor. Resize is applied before integral sum computing + IntegralChannels(const int shr) : shrinkage(shr) {} + + //! Appends specified number of hog first order feature integrals into given vector. + //! Param gray is an input 1-chennel gray image. + //! Param integrals is a vector of integrals. Computed from frame frame hog-channels will be appended to it. + //! Param bins is a number of hog-bins + void createHogBins(const cv::Mat gray, std::vector& integrals, int bins) const; + + //! Converts 3-chennel BGR input frame to Luv and append each channel to the integrals. + //! Param frame is an input 3-chennel BGR colored image. + //! Param integrals is a vector of integrals. Computed from frame frame luv-channels will be appended to it. + void createLuvBins(const cv::Mat frame, std::vector& integrals) const; + +private: + int shrinkage; +}; + //////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector ////////////// // struct for detection region of interest (ROI) diff --git a/modules/objdetect/src/isf.cpp b/modules/objdetect/src/isf.cpp new file mode 100644 index 0000000000..48bfc89a8f --- /dev/null +++ b/modules/objdetect/src/isf.cpp @@ -0,0 +1,155 @@ +/*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-2012, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include +#include +#include + + +template +struct Decimate { + int shrinkage; + + Decimate(const int sr) : shrinkage(sr) {} + + void operator()(const cv::Mat& in, cv::Mat& out) const + { + int cols = in.cols / shrinkage; + int rows = in.rows / shrinkage; + out.create(rows, cols, in.type()); + + CV_Assert(cols * shrinkage == in.cols); + CV_Assert(rows * shrinkage == in.rows); + + for (int outIdx_y = 0; outIdx_y < rows; ++outIdx_y) + { + T* outPtr = out.ptr(outIdx_y); + for (int outIdx_x = 0; outIdx_x < cols; ++outIdx_x) + { + // do desimate + int inIdx_y = outIdx_y * shrinkage; + int inIdx_x = outIdx_x * shrinkage; + int sum = 0; + + for (int y = inIdx_y; y < inIdx_y + shrinkage; ++y) + for (int x = inIdx_x; x < inIdx_x + shrinkage; ++x) + sum += in.at(y, x); + + sum /= shrinkage * shrinkage; + outPtr[outIdx_x] = cv::saturate_cast(sum); + } + } + } + +}; + +void cv::IntegralChannels::createHogBins(const cv::Mat gray, std::vector& integrals, int bins) const +{ + CV_Assert(gray.type() == CV_8UC1); + int h = gray.rows; + int w = gray.cols; + CV_Assert(!(w % shrinkage) && !(h % shrinkage)); + + Decimate decimate(shrinkage); + + cv::Mat df_dx, df_dy, mag, angle; + cv::Sobel(gray, df_dx, CV_32F, 1, 0, 3, 0.125); + cv::Sobel(gray, df_dy, CV_32F, 0, 1, 3, 0.125); + + cv::cartToPolar(df_dx, df_dy, mag, angle, true); + mag *= (1.f / sqrt(2)); + + cv::Mat nmag; + mag.convertTo(nmag, CV_8UC1); + + angle /= 60.f; + + std::vector hist; + for (int bin = 0; bin < bins; ++bin) + hist.push_back(cv::Mat::zeros(h, w, CV_8UC1)); + + for (int y = 0; y < h; ++y) + { + uchar* magnitude = nmag.ptr(y); + float* ang = angle.ptr(y); + + for (int x = 0; x < w; ++x) + { + hist[ (int)ang[x] ].ptr(y)[x] = magnitude[x]; + } + } + + for(int i = 0; i < bins; ++i) + { + cv::Mat shrunk, sum; + decimate(hist[i], shrunk); + cv::integral(shrunk, sum, cv::noArray(), CV_32S); + integrals.push_back(sum); + } + + cv::Mat shrMag; + decimate(nmag, shrMag); + cv::integral(shrMag, mag, cv::noArray(), CV_32S); + integrals.push_back(mag); +} + +void cv::IntegralChannels::createLuvBins(const cv::Mat frame, std::vector& integrals) const +{ + CV_Assert(frame.type() == CV_8UC3); + CV_Assert(!(frame.cols % shrinkage) && !(frame.rows % shrinkage)); + + Decimate decimate(shrinkage); + + cv::Mat luv; + cv::cvtColor(frame, luv, CV_BGR2Luv); + + std::vector splited; + split(luv, splited); + + for (size_t i = 0; i < splited.size(); ++i) + { + cv::Mat shrunk, sum; + decimate(splited[i], shrunk); + cv::integral(shrunk, sum, cv::noArray(), CV_32S); + integrals.push_back(sum); + } +} \ No newline at end of file From 2d0fc80c9510fb75556e785f01840b6ee8293172 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 25 Oct 2012 01:56:22 +0400 Subject: [PATCH 31/56] use IntegralChannels class --- modules/objdetect/src/softcascade.cpp | 157 +------------------- modules/objdetect/test/test_softcascade.cpp | 3 +- 2 files changed, 6 insertions(+), 154 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index cec7741f2b..8d79b85e8a 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -50,21 +50,6 @@ #include #include -// use previous stored integrals for regression testing -// #define USE_REFERENCE_VALUES - -#if defined USE_REFERENCE_VALUES - -namespace { - -char *itoa(long i, char* s, int /*dummy_radix*/) -{ - sprintf(s, "%ld", i); - return s; -} - -#endif - // used for noisy printfs // #define WITH_DEBUG_OUT @@ -235,47 +220,10 @@ struct Level float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); // compensation areas rounding - return (threshold * scaling[idx] * sarea); + return (sarea == 0.0f)? threshold : (threshold * scaling[idx] * sarea); } }; -template< typename T> -struct Decimate { - int shrinkage; - - Decimate(const int sr) : shrinkage(sr) {} - - void operator()(const cv::Mat& in, cv::Mat& out) const - { - int cols = in.cols / shrinkage; - int rows = in.rows / shrinkage; - out.create(rows, cols, in.type()); - - CV_Assert(cols * shrinkage == in.cols); - CV_Assert(rows * shrinkage == in.rows); - - for (int outIdx_y = 0; outIdx_y < rows; ++outIdx_y) - { - T* outPtr = out.ptr(outIdx_y); - for (int outIdx_x = 0; outIdx_x < cols; ++outIdx_x) - { - // do desimate - int inIdx_y = outIdx_y * shrinkage; - int inIdx_x = outIdx_x * shrinkage; - int sum = 0; - - for (int y = inIdx_y; y < inIdx_y + shrinkage; ++y) - for (int x = inIdx_x; x < inIdx_x + shrinkage; ++x) - sum += in.at(y, x); - - sum /= shrinkage * shrinkage; - outPtr[outIdx_x] = cv::saturate_cast(sum); - } - } - } - -}; - struct ChannelStorage { std::vector hog; @@ -289,111 +237,16 @@ struct ChannelStorage ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) { hog.clear(); - Decimate decimate(shr); - -#if defined USE_REFERENCE_VALUES - char buff[33]; - cv::FileStorage imgs("/home/kellan/testInts.xml", cv::FileStorage::READ); - - for(int i = 0; i < HOG_LUV_BINS; ++i) - { - cv::Mat channel; - imgs[std::string("channel") + itoa(i, buff, 10)] >> channel; - hog.push_back(channel); - } -#else - // add gauss - cv::Mat gauss; - cv::GaussianBlur(colored, gauss, cv::Size(3,3), 0 ,0); - - // convert to luv - cv::Mat luv; - cv::cvtColor(colored, luv, CV_BGR2Luv); - - // split to 3 one channel matrix - std::vector splited, luvs; - split(luv, splited); - - // shrink and integrate - for (int i = 0; i < (int)splited.size(); i++) - { - cv::Mat shrunk, sum; - decimate(splited[i], shrunk); - cv::integral(shrunk, sum, cv::noArray(), CV_32S); - luvs.push_back(sum); - } + cv::IntegralChannels ints(shr); // convert to grey cv::Mat grey; cv::cvtColor(colored, grey, CV_BGR2GRAY); - // get derivative - cv::Mat df_dx, df_dy, mag, angle; - cv::Sobel(grey, df_dx, CV_32F, 1, 0); - cv::Sobel(grey, df_dy, CV_32F, 0, 1); - - // normalize - df_dx /= 4; - df_dy /= 4; - - // calculate magnitude - cv::cartToPolar(df_dx, df_dy, mag, angle, true); - - // normalize to avoid uchar overflow - static const float magnitudeScaling = 1.f / sqrt(2); - mag *= magnitudeScaling; - - // convert to uchar - cv::Mat saturatedMag(grey.rows, grey.cols, CV_8UC1), shrMag; - for (int y = 0; y < grey.rows; ++y) - { - float* rm = mag.ptr(y); - uchar* mg = saturatedMag.ptr(y); - for (int x = 0; x < grey.cols; ++x) - { - mg[x] = cv::saturate_cast(rm[x]); - } - } - - // srink and integrate - decimate(saturatedMag, shrMag); - cv::integral(shrMag, mag, cv::noArray(), CV_32S); - - // create hog channels - angle /= 60.f; - - std::vector hist; - for (int bin = 0; bin < HOG_BINS; ++bin) - { - hist.push_back(cv::Mat::zeros(saturatedMag.rows, saturatedMag.cols, CV_8UC1)); - } - - for (int y = 0; y < saturatedMag.rows; ++y) - { - uchar* magnitude = saturatedMag.ptr(y); - float* ang = angle.ptr(y); - - for (int x = 0; x < saturatedMag.cols; ++x) - { - hist[ (int)ang[x] ].ptr(y)[x] = magnitude[x]; - } - } - - for(int i = 0; i < HOG_BINS; ++i) - { - cv::Mat shrunk, sum; - decimate(hist[i], shrunk); - cv::integral(shrunk, sum, cv::noArray(), CV_32S); - hog.push_back(sum); - } - - hog.push_back(mag); - hog.insert(hog.end(), luvs.begin(), luvs.end()); + ints.createHogBins(grey, hog, 6); + ints.createLuvBins(colored, hog); step = hog[0].cols; - - // CV_Assert(hog.size() == 10); -#endif } float get(const int channel, const cv::Rect& area) const @@ -441,7 +294,7 @@ struct cv::SoftCascade::Filds float detectionScore = 0.f; const Octave& octave = *(level.octave); - int stBegin = octave.index * octave.stages, stEnd = stBegin + octave.stages; + int stBegin = octave.index * octave.stages, stEnd = stBegin + 1024;//octave.stages; dprintf(" octave stages: %d to %d index %d %f level %f\n", stBegin, stEnd, octave.index, octave.scale, level.origScale); diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index 59428ea5d9..811ad2c5ed 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -65,7 +65,6 @@ TEST(SoftCascade, detect) cascade.detectMultiScale(colored, rois, objects); - std::cout << "detected: " << (int)objects.size() << std::endl; cv::Mat out = colored.clone(); int level = 0, total = 0; @@ -78,7 +77,6 @@ TEST(SoftCascade, detect) std::cout << "Level: " << level << " total " << total << std::endl; cv::imshow("out", out); cv::waitKey(0); - out = colored.clone(); levelWidth = objects[i].rect.width; total = 0; @@ -91,4 +89,5 @@ TEST(SoftCascade, detect) << " " << objects[i].rect.height << std::endl; total++; } + std::cout << "detected: " << (int)objects.size() << std::endl; } \ No newline at end of file From 7b6da394f0ae87f1119cb1afabb740d7f85c423b Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 25 Oct 2012 02:03:46 +0400 Subject: [PATCH 32/56] rename Stage to Weak because there is no such term for Soft Cascades --- modules/objdetect/src/softcascade.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 8d79b85e8a..5e27e88313 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -85,17 +85,17 @@ const char *const Octave::SC_OCT_SCALE = "scale"; const char *const Octave::SC_OCT_STAGES = "stageNum"; const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; -struct Stage +struct Weak { float threshold; static const char *const SC_STAGE_THRESHOLD; - Stage(){} - Stage(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]){} + Weak(){} + Weak(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]){} }; -const char *const Stage::SC_STAGE_THRESHOLD = "stageThreshold"; +const char *const Weak::SC_STAGE_THRESHOLD = "stageThreshold"; struct Node { @@ -277,7 +277,7 @@ struct cv::SoftCascade::Filds int shrinkage; std::vector octaves; - std::vector stages; + std::vector stages; std::vector nodes; std::vector leaves; std::vector features; @@ -305,7 +305,7 @@ struct cv::SoftCascade::Filds dprintf("index: %d\n", st); - const Stage& stage = stages[st]; + const Weak& stage = stages[st]; { int nId = st * 3; @@ -481,7 +481,7 @@ struct cv::SoftCascade::Filds for (; st != st_end; ++st ) { fns = *st; - stages.push_back(Stage(fns)); + stages.push_back(Weak(fns)); fns = fns[SC_WEEK]; FileNodeIterator ftr = fns.begin(), ft_end = fns.end(); From 40c0c60e2bf8c30968ddf785b484f1a58033a428 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 25 Oct 2012 02:10:20 +0400 Subject: [PATCH 33/56] remove unused struct --- modules/objdetect/src/softcascade.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 5e27e88313..a155719bf4 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -131,16 +131,6 @@ struct Feature const char * const Feature::SC_F_CHANNEL = "channel"; const char * const Feature::SC_F_RECT = "rect"; -struct Object -{ - enum Class{PEDESTRIAN}; - cv::Rect rect; - float confidence; - Class detType; - - Object(const cv::Rect& r, const float c, Class dt = PEDESTRIAN) : rect(r), confidence(c), detType(dt) {} -}; - struct CascadeIntrinsics { static const float lambda = 1.099f, a = 0.89f; From a22ee13620d8539be81bacf6d27bd0659e388e28 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 25 Oct 2012 09:10:11 +0400 Subject: [PATCH 34/56] rename markDetection to addDetection --- modules/objdetect/src/softcascade.cpp | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index a155719bf4..c70e80c004 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -135,12 +135,9 @@ struct CascadeIntrinsics { static const float lambda = 1.099f, a = 0.89f; - static float getFor(int channel, float scaling) + static float getFor(bool isUp, float scaling) { - CV_Assert(channel < 10); - if (fabs(scaling - 1.f) < FLT_EPSILON) - // if (scaling == 1.f) return 1.f; // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool's and Dallal's papers @@ -156,8 +153,8 @@ struct CascadeIntrinsics { 0.f, 2.f} // up }; - float a = A[(int)(scaling >= 1)][(int)(channel > 6)]; - float b = B[(int)(scaling >= 1)][(int)(channel > 6)]; + float a = A[(int)(scaling >= 1)][(int)(isUp)]; + float b = B[(int)(scaling >= 1)][(int)(isUp)]; dprintf("scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); return a * pow(scaling, b); @@ -170,7 +167,6 @@ struct Level float origScale; float relScale; - float shrScale; // used for marking detection int scaleshift; cv::Size workRect; @@ -182,16 +178,16 @@ struct Level typedef cv::SoftCascade::Detection detection_t; Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) - : octave(&oct), origScale(scale), relScale(scale / oct.scale), shrScale (relScale / (float)shrinkage), + : octave(&oct), origScale(scale), relScale(scale / oct.scale), workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) { - scaling[0] = CascadeIntrinsics::getFor(0, relScale) / (relScale * relScale); - scaling[1] = CascadeIntrinsics::getFor(9, relScale) / (relScale * relScale); + scaling[0] = CascadeIntrinsics::getFor(false, relScale) / (relScale * relScale); + scaling[1] = CascadeIntrinsics::getFor(true, relScale) / (relScale * relScale); scaleshift = relScale * (1 << 16); } - void markDetection(const int x, const int y, float confidence, std::vector& detections) const + void addDetection(const int x, const int y, float confidence, std::vector& detections) const { int shrinkage = (*octave).shrinkage; cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height); @@ -345,7 +341,7 @@ struct cv::SoftCascade::Filds dprintf("x %d y %d: %d\n", dx, dy, st - stBegin); dprintf(" got %d\n", st); - level.markDetection(dx, dy, detectionScore, detections); + level.addDetection(dx, dy, detectionScore, detections); } octIt_t fitOctave(const float& logFactor) @@ -400,7 +396,6 @@ struct cv::SoftCascade::Filds << levels[sc].octave->scale << " " << levels[sc].relScale - << " " << levels[sc].shrScale << " [" << levels[sc].objSize.width << " " << levels[sc].objSize.height << "] [" << levels[sc].workRect.width << " " << levels[sc].workRect.height << "]" << std::endl; @@ -533,8 +528,6 @@ bool cv::SoftCascade::load( const string& filename, const float minScale, const void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, std::vector& objects, const int /*rejectfactor*/) const { - typedef std::vector::const_iterator RIter_t; - // only color images are supperted CV_Assert(image.type() == CV_8UC3); @@ -549,7 +542,6 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector::const_iterator lIt; - for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) { const Level& level = *it; @@ -563,5 +555,4 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Thu, 25 Oct 2012 09:40:16 +0400 Subject: [PATCH 35/56] load SoftCascade from FileStorage --- .../include/opencv2/objdetect/objdetect.hpp | 18 +++++++++++------- .../objdetect/perf/perf_cascadeclassifier.cpp | 3 ++- modules/objdetect/src/softcascade.cpp | 11 +++++------ modules/objdetect/test/test_softcascade.cpp | 6 ++++-- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index b56d5f1db6..85084a90c4 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -500,25 +500,29 @@ public: float confidence; int kind; - enum {PEDESTRIAN = 0}; + enum {PEDESTRIAN = 1}; + //! Create detection from an object bounding rectangle and confidence. Only PEDESTRIAN type carrently supported. + //! Param r is a boundinf rectangle + //! param c is a confidence that object belongs to class k + //! Paral k is an object class Detection(const cv::Rect& r, const float c, int k = PEDESTRIAN) : rect(r), confidence(c), kind(k) {} }; //! An empty cascade will be created. SoftCascade(); - //! Cascade will be created from file for scales from minScale to maxScale. - //! Param filename is a path to xml-serialized cascade. + //! Cascade will be created for scales from minScale to maxScale. + //! Param fs is a serialized sacsade. //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. - SoftCascade( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); + SoftCascade( const cv::FileStorage& fs, const float minScale = 0.4f, const float maxScale = 5.f); - //! cascade will be loaded from file "filename". The previous cascade will be destroyed. - //! Param filename is a path to xml-serialized cascade. + //! cascade will be loaded. The previous cascade will be destroyed. + //! Param fs is a serialized sacsade. //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. - bool load( const string& filename, const float minScale = 0.4f, const float maxScale = 5.f); + bool read( const cv::FileStorage& fs, const float minScale = 0.4f, const float maxScale = 5.f); virtual ~SoftCascade(); diff --git a/modules/objdetect/perf/perf_cascadeclassifier.cpp b/modules/objdetect/perf/perf_cascadeclassifier.cpp index 271ede0072..bf47d555c9 100644 --- a/modules/objdetect/perf/perf_cascadeclassifier.cpp +++ b/modules/objdetect/perf/perf_cascadeclassifier.cpp @@ -65,7 +65,8 @@ PERF_TEST_P(detect, SoftCascade, ASSERT_FALSE(colored.empty()); cv::SoftCascade cascade; - ASSERT_TRUE(cascade.load(getDataPath(get<0>(GetParam())))); + cv::FileStorage fs(getDataPath(get<0>(GetParam())), cv::FileStorage::READ); + ASSERT_TRUE(cascade.read(fs)); std::vector rois; std::vector objectBoxes; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index c70e80c004..7c735b21de 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -499,24 +499,23 @@ struct cv::SoftCascade::Filds cv::SoftCascade::SoftCascade() : filds(0) {} -cv::SoftCascade::SoftCascade( const string& filename, const float minScale, const float maxScale) : filds(0) +cv::SoftCascade::SoftCascade(const cv::FileStorage& fs, const float minScale, const float maxScale) : filds(0) { - load(filename, minScale, maxScale); + read(fs, minScale, maxScale); } cv::SoftCascade::~SoftCascade() { delete filds; } -bool cv::SoftCascade::load( const string& filename, const float minScale, const float maxScale) +bool cv::SoftCascade::read( const cv::FileStorage& fs, const float minScale, const float maxScale) { + if (!fs.isOpened()) return false; + if (filds) delete filds; filds = 0; - cv::FileStorage fs(filename, FileStorage::READ); - if (!fs.isOpened()) return false; - filds = new Filds; Filds& flds = *filds; if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index 811ad2c5ed..b75db7371f 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -45,7 +45,8 @@ TEST(SoftCascade, readCascade) { std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/icf-template.xml"; cv::SoftCascade cascade; - ASSERT_TRUE(cascade.load(xml)); + cv::FileStorage fs(xml, cv::FileStorage::READ); + ASSERT_TRUE(cascade.read(fs)); } @@ -54,7 +55,8 @@ TEST(SoftCascade, detect) typedef cv::SoftCascade::Detection detection_t; std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; cv::SoftCascade cascade; - ASSERT_TRUE(cascade.load(xml)); + cv::FileStorage fs(xml, cv::FileStorage::READ); + ASSERT_TRUE(cascade.read(fs)); cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); ASSERT_FALSE(colored.empty()); From 16dd09ccfc5faf6686f19f628afd39a6671477c3 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 25 Oct 2012 12:22:54 +0400 Subject: [PATCH 36/56] move scale related parameters to SoftCascade constructor --- .../include/opencv2/objdetect/objdetect.hpp | 17 ++++++++++------- modules/objdetect/src/softcascade.cpp | 9 +++++---- modules/objdetect/test/test_softcascade.cpp | 1 + 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 85084a90c4..53b2b11074 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -510,19 +510,18 @@ public: }; //! An empty cascade will be created. - SoftCascade(); + //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. + //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. + //! Param scales is a number of scales from minScale to maxScale. + SoftCascade( const float minScale = 0.4f, const float maxScale = 5.f, const int scales = 55); //! Cascade will be created for scales from minScale to maxScale. //! Param fs is a serialized sacsade. - //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. - //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. - SoftCascade( const cv::FileStorage& fs, const float minScale = 0.4f, const float maxScale = 5.f); + SoftCascade( const cv::FileStorage& fs); //! cascade will be loaded. The previous cascade will be destroyed. //! Param fs is a serialized sacsade. - //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. - //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. - bool read( const cv::FileStorage& fs, const float minScale = 0.4f, const float maxScale = 5.f); + bool read( const cv::FileStorage& fs); virtual ~SoftCascade(); @@ -545,6 +544,10 @@ protected: private: struct Filds; Filds* filds; + + float minScale; + float maxScale; + int scales; }; class CV_EXPORTS IntegralChannels diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 7c735b21de..4cf72e3cdf 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -497,18 +497,19 @@ struct cv::SoftCascade::Filds } }; -cv::SoftCascade::SoftCascade() : filds(0) {} +cv::SoftCascade::SoftCascade(const float mins, const float maxs, const int nsc) +: filds(0), minScale(mins), maxScale(maxs), scales(nsc) {} -cv::SoftCascade::SoftCascade(const cv::FileStorage& fs, const float minScale, const float maxScale) : filds(0) +cv::SoftCascade::SoftCascade(const cv::FileStorage& fs) : filds(0) { - read(fs, minScale, maxScale); + read(fs); } cv::SoftCascade::~SoftCascade() { delete filds; } -bool cv::SoftCascade::read( const cv::FileStorage& fs, const float minScale, const float maxScale) +bool cv::SoftCascade::read( const cv::FileStorage& fs) { if (!fs.isOpened()) return false; diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index b75db7371f..aa72a97193 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -92,4 +92,5 @@ TEST(SoftCascade, detect) total++; } std::cout << "detected: " << (int)objects.size() << std::endl; + ASSERT_EQ((int)objects.size(), 1501); } \ No newline at end of file From 2e8ed773831abbd1c20d0fc19a67d55a65ec7a28 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 25 Oct 2012 14:50:48 +0400 Subject: [PATCH 37/56] get rid of hard-coded values --- .../include/opencv2/objdetect/objdetect.hpp | 12 --------- modules/objdetect/src/softcascade.cpp | 27 +++++++++++-------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 53b2b11074..cfc4f22f0b 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -529,18 +529,6 @@ public: virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, int rejectfactor = 1) const; -protected: - enum { BOOST = 0 }; - enum - { - FRAME_WIDTH = 640, - FRAME_HEIGHT = 480, - TOTAL_SCALES = 55, - CLASSIFIERS = 5, - ORIG_OBJECT_WIDTH = 64, - ORIG_OBJECT_HEIGHT = 128 - }; - private: struct Filds; Filds* filds; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 4cf72e3cdf..c919fe2d0d 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -270,6 +270,10 @@ struct cv::SoftCascade::Filds std::vector levels; + cv::Size frameSize; + + enum { BOOST = 0 }; + typedef std::vector::iterator octIt_t; void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage, @@ -364,8 +368,11 @@ struct cv::SoftCascade::Filds } // compute levels of full pyramid - void calcLevels(int frameW, int frameH, int scales) + void calcLevels(const cv::Size& curr, int scales) { + if (frameSize == curr) return; + frameSize = curr; + CV_Assert(scales > 1); levels.clear(); float logFactor = (log(maxScale) - log(minScale)) / (scales -1); @@ -373,8 +380,8 @@ struct cv::SoftCascade::Filds float scale = minScale; for (int sc = 0; sc < scales; ++sc) { - int width = std::max(0.0f, frameW - (origObjWidth * scale)); - int height = std::max(0.0f, frameH - (origObjHeight * scale)); + int width = std::max(0.0f, frameSize.width - (origObjWidth * scale)); + int height = std::max(0.0f, frameSize.height - (origObjHeight * scale)); float logScale = log(scale); octIt_t fit = fitOctave(logScale); @@ -434,11 +441,8 @@ struct cv::SoftCascade::Filds string featureTypeStr = (string)root[SC_FEATURE_TYPE]; CV_Assert(featureTypeStr == SC_ICF); - origObjWidth = (int)root[SC_ORIG_W]; - CV_Assert(origObjWidth == SoftCascade::ORIG_OBJECT_WIDTH); - + origObjWidth = (int)root[SC_ORIG_W]; origObjHeight = (int)root[SC_ORIG_H]; - CV_Assert(origObjHeight == SoftCascade::ORIG_OBJECT_HEIGHT); // for each octave (~ one cascade in classic OpenCV xml) FileNode fn = root[SC_OCTAVES]; @@ -451,7 +455,7 @@ struct cv::SoftCascade::Filds for (; it != it_end; ++it) { FileNode fns = *it; - Octave octave(octIndex, cv::Size(SoftCascade::ORIG_OBJECT_WIDTH, SoftCascade::ORIG_OBJECT_HEIGHT), fns); + Octave octave(octIndex, cv::Size(origObjWidth, origObjHeight), fns); CV_Assert(octave.stages > 0); octaves.push_back(octave); @@ -520,7 +524,7 @@ bool cv::SoftCascade::read( const cv::FileStorage& fs) filds = new Filds; Filds& flds = *filds; if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES); + // flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, scales); return true; } @@ -534,9 +538,10 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Thu, 1 Nov 2012 06:11:09 +0400 Subject: [PATCH 38/56] brief soft cascade interface description --- .../include/opencv2/objdetect/objdetect.hpp | 22 ++++++++++++++----- modules/objdetect/src/softcascade.cpp | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index cfc4f22f0b..e0799f9b15 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -488,25 +488,31 @@ protected: Ptr maskGenerator; }; -// ======================== soft cascade version ===================== // - +/** + * \class SoftCascade + * \brief Implement soft (stageless) cascade. + */ class CV_EXPORTS SoftCascade { public: + /** + * \class Detection + * \brief Soft cascade detector result represintation. + */ struct CV_EXPORTS Detection { - cv::Rect rect; - float confidence; - int kind; - enum {PEDESTRIAN = 1}; //! Create detection from an object bounding rectangle and confidence. Only PEDESTRIAN type carrently supported. //! Param r is a boundinf rectangle //! param c is a confidence that object belongs to class k //! Paral k is an object class + Detection(const cv::Rect& r, const float c, int k = PEDESTRIAN) : rect(r), confidence(c), kind(k) {} + cv::Rect rect; + float confidence; + int kind; }; //! An empty cascade will be created. @@ -538,6 +544,10 @@ private: int scales; }; +/** + * \class IntegralChannels + * \brief Create channel integrals for Soft Cascade detector. + */ class CV_EXPORTS IntegralChannels { public: diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index c919fe2d0d..f3625f5d0c 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -55,7 +55,7 @@ #if defined WITH_DEBUG_OUT # define dprintf(format, ...) \ - do { printf(format, __VA_ARGS__); } while (0) + do { printf(format, ##__VA_ARGS__); } while (0) #else # define dprintf(format, ...) #endif From 3cb9afb4e760ccf1b20731c6def12437129ca81d Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 1 Nov 2012 14:26:28 +0400 Subject: [PATCH 39/56] test update because changed Sobel Normalization --- modules/objdetect/test/test_softcascade.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index aa72a97193..dfa02c02bf 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -92,5 +92,5 @@ TEST(SoftCascade, detect) total++; } std::cout << "detected: " << (int)objects.size() << std::endl; - ASSERT_EQ((int)objects.size(), 1501); + ASSERT_EQ((int)objects.size(), 1469); } \ No newline at end of file From ff8417db006279db294d4c6810880300c2502f21 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 1 Nov 2012 15:43:50 +0400 Subject: [PATCH 40/56] remove input frame size constraints --- .../objdetect/perf/perf_cascadeclassifier.cpp | 18 ++++++- modules/objdetect/src/isf.cpp | 48 ++----------------- modules/objdetect/src/softcascade.cpp | 18 +------ modules/objdetect/test/test_softcascade.cpp | 48 +++++++++---------- 4 files changed, 45 insertions(+), 87 deletions(-) diff --git a/modules/objdetect/perf/perf_cascadeclassifier.cpp b/modules/objdetect/perf/perf_cascadeclassifier.cpp index bf47d555c9..f91f2f8ec7 100644 --- a/modules/objdetect/perf/perf_cascadeclassifier.cpp +++ b/modules/objdetect/perf/perf_cascadeclassifier.cpp @@ -56,6 +56,18 @@ PERF_TEST_P(ImageName_MinSize, CascadeClassifierLBPFrontalFace, typedef std::tr1::tuple fixture; typedef perf::TestBaseWithParam detect; + +namespace { + typedef cv::SoftCascade::Detection detection_t; + + void extractRacts(std::vector objectBoxes, vector rects) + { + rects.clear(); + for (int i = 0; i < (int)objectBoxes.size(); ++i) + rects.push_back(objectBoxes[i].rect); + } +} + PERF_TEST_P(detect, SoftCascade, testing::Combine(testing::Values(std::string("cv/cascadeandhog/sc_cvpr_2012_to_opencv.xml")), testing::Values(std::string("cv/cascadeandhog/bahnhof/image_00000000_0.png")))) @@ -76,5 +88,9 @@ PERF_TEST_P(detect, SoftCascade, { cascade.detectMultiScale(colored, rois, objectBoxes); } - SANITY_CHECK(objectBoxes); + + vector rects; + extractRacts(objectBoxes, rects); + std::sort(rects.begin(), rects.end(), comparators::RectLess()); + SANITY_CHECK(rects); } diff --git a/modules/objdetect/src/isf.cpp b/modules/objdetect/src/isf.cpp index 48bfc89a8f..bbf4f27c0e 100644 --- a/modules/objdetect/src/isf.cpp +++ b/modules/objdetect/src/isf.cpp @@ -44,44 +44,6 @@ #include #include - -template -struct Decimate { - int shrinkage; - - Decimate(const int sr) : shrinkage(sr) {} - - void operator()(const cv::Mat& in, cv::Mat& out) const - { - int cols = in.cols / shrinkage; - int rows = in.rows / shrinkage; - out.create(rows, cols, in.type()); - - CV_Assert(cols * shrinkage == in.cols); - CV_Assert(rows * shrinkage == in.rows); - - for (int outIdx_y = 0; outIdx_y < rows; ++outIdx_y) - { - T* outPtr = out.ptr(outIdx_y); - for (int outIdx_x = 0; outIdx_x < cols; ++outIdx_x) - { - // do desimate - int inIdx_y = outIdx_y * shrinkage; - int inIdx_x = outIdx_x * shrinkage; - int sum = 0; - - for (int y = inIdx_y; y < inIdx_y + shrinkage; ++y) - for (int x = inIdx_x; x < inIdx_x + shrinkage; ++x) - sum += in.at(y, x); - - sum /= shrinkage * shrinkage; - outPtr[outIdx_x] = cv::saturate_cast(sum); - } - } - } - -}; - void cv::IntegralChannels::createHogBins(const cv::Mat gray, std::vector& integrals, int bins) const { CV_Assert(gray.type() == CV_8UC1); @@ -89,8 +51,6 @@ void cv::IntegralChannels::createHogBins(const cv::Mat gray, std::vector decimate(shrinkage); - cv::Mat df_dx, df_dy, mag, angle; cv::Sobel(gray, df_dx, CV_32F, 1, 0, 3, 0.125); cv::Sobel(gray, df_dy, CV_32F, 0, 1, 3, 0.125); @@ -121,13 +81,13 @@ void cv::IntegralChannels::createHogBins(const cv::Mat gray, std::vector decimate(shrinkage); - cv::Mat luv; cv::cvtColor(frame, luv, CV_BGR2Luv); @@ -148,7 +106,7 @@ void cv::IntegralChannels::createLuvBins(const cv::Mat frame, std::vectorscale - << " " - << levels[sc].relScale - << " [" << levels[sc].objSize.width - << " " << levels[sc].objSize.height << "] [" - << levels[sc].workRect.width << " " << levels[sc].workRect.height << "]" << std::endl; } } @@ -523,10 +513,7 @@ bool cv::SoftCascade::read( const cv::FileStorage& fs) filds = new Filds; Filds& flds = *filds; - if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false; - // flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, scales); - - return true; + return flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale); } void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, @@ -535,9 +522,6 @@ void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector Date: Mon, 5 Nov 2012 22:42:08 +0400 Subject: [PATCH 41/56] soft cascade become Algorithm --- .../include/opencv2/objdetect/objdetect.hpp | 63 +++++++------ .../objdetect/perf/perf_cascadeclassifier.cpp | 17 ++-- modules/objdetect/src/objdetect_init.cpp | 23 ++++- modules/objdetect/src/softcascade.cpp | 32 +++---- modules/objdetect/test/test_softcascade.cpp | 94 ++++++++++--------- 5 files changed, 122 insertions(+), 107 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index e0799f9b15..84f41e1490 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -488,52 +488,52 @@ protected: Ptr maskGenerator; }; -/** - * \class SoftCascade - * \brief Implement soft (stageless) cascade. - */ -class CV_EXPORTS SoftCascade + +// Implementation of soft (stageless) cascaded detector. +class CV_EXPORTS SCascade : public Algorithm { public: - /** - * \class Detection - * \brief Soft cascade detector result represintation. - */ + // Representation of detectors result. struct CV_EXPORTS Detection { + // Default object type. enum {PEDESTRIAN = 1}; - //! Create detection from an object bounding rectangle and confidence. Only PEDESTRIAN type carrently supported. - //! Param r is a boundinf rectangle - //! param c is a confidence that object belongs to class k - //! Paral k is an object class + // Creates Detection from an object bounding box and confidence. + // Param b is a bounding box + // Param c is a confidence that object belongs to class k + // Paral k is an object class + Detection(const cv::Rect& b, const float c, int k = PEDESTRIAN) : bb(b), confidence(c), kind(k) {} - Detection(const cv::Rect& r, const float c, int k = PEDESTRIAN) : rect(r), confidence(c), kind(k) {} - cv::Rect rect; + cv::Rect bb; float confidence; int kind; }; - //! An empty cascade will be created. - //! Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. - //! Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. - //! Param scales is a number of scales from minScale to maxScale. - SoftCascade( const float minScale = 0.4f, const float maxScale = 5.f, const int scales = 55); + // An empty cascade will be created. + // Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. + // Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. + // Param scales is a number of scales from minScale to maxScale. + // Param rejfactor is used for NMS. + SCascade(const float minScale = 0.4f, const float maxScale = 5.f, const int scales = 55, const int rejfactor = 1); - //! Cascade will be created for scales from minScale to maxScale. - //! Param fs is a serialized sacsade. - SoftCascade( const cv::FileStorage& fs); + virtual ~SCascade(); - //! cascade will be loaded. The previous cascade will be destroyed. - //! Param fs is a serialized sacsade. - bool read( const cv::FileStorage& fs); + cv::AlgorithmInfo* info() const; - virtual ~SoftCascade(); + // Load cascade from FileNode. + // Param fn is a root node for cascade. Should be . + virtual bool load(const FileNode& fn); - //! return vector of bounding boxes. Each box contains one detected object - virtual void detectMultiScale(const Mat& image, const std::vector& rois, std::vector& objects, - int rejectfactor = 1) const; + // Load cascade config. + virtual void read(const FileNode& fn); + + // Return the vector of Decection objcts. + // Param image is a frame on which detector will be applied. + // Param rois is a vector of regions of interest. Only the objects that fall into one of the regions will be returned. + // Param objects is an output array of Detections + virtual void detect(const Mat& image, const std::vector& rois, std::vector& objects) const; private: struct Filds; @@ -542,8 +542,11 @@ private: float minScale; float maxScale; int scales; + int rejfactor; }; +CV_EXPORTS bool initModule_objdetect(void); + /** * \class IntegralChannels * \brief Create channel integrals for Soft Cascade detector. diff --git a/modules/objdetect/perf/perf_cascadeclassifier.cpp b/modules/objdetect/perf/perf_cascadeclassifier.cpp index f91f2f8ec7..cc068e8ea3 100644 --- a/modules/objdetect/perf/perf_cascadeclassifier.cpp +++ b/modules/objdetect/perf/perf_cascadeclassifier.cpp @@ -58,35 +58,36 @@ typedef perf::TestBaseWithParam detect; namespace { - typedef cv::SoftCascade::Detection detection_t; + typedef cv::SCascade::Detection detection_t; void extractRacts(std::vector objectBoxes, vector rects) { rects.clear(); for (int i = 0; i < (int)objectBoxes.size(); ++i) - rects.push_back(objectBoxes[i].rect); + rects.push_back(objectBoxes[i].bb); } } -PERF_TEST_P(detect, SoftCascade, +PERF_TEST_P(detect, SCascade, testing::Combine(testing::Values(std::string("cv/cascadeandhog/sc_cvpr_2012_to_opencv.xml")), testing::Values(std::string("cv/cascadeandhog/bahnhof/image_00000000_0.png")))) { - typedef cv::SoftCascade::Detection detection_t; + typedef cv::SCascade::Detection Detection; cv::Mat colored = imread(getDataPath(get<1>(GetParam()))); ASSERT_FALSE(colored.empty()); - cv::SoftCascade cascade; + cv::SCascade cascade; cv::FileStorage fs(getDataPath(get<0>(GetParam())), cv::FileStorage::READ); - ASSERT_TRUE(cascade.read(fs)); + ASSERT_TRUE(fs.isOpened()); + ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); std::vector rois; std::vector objectBoxes; - cascade.detectMultiScale(colored, rois, objectBoxes); + cascade.detect(colored, rois, objectBoxes); TEST_CYCLE() { - cascade.detectMultiScale(colored, rois, objectBoxes); + cascade.detect(colored, rois, objectBoxes); } vector rects; diff --git a/modules/objdetect/src/objdetect_init.cpp b/modules/objdetect/src/objdetect_init.cpp index 317867a59f..a6ce81e809 100644 --- a/modules/objdetect/src/objdetect_init.cpp +++ b/modules/objdetect/src/objdetect_init.cpp @@ -7,11 +7,11 @@ // copy or use the software. // // -// License Agreement +// License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2008-2012, 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, @@ -40,4 +40,21 @@ // //M*/ -#include "precomp.hpp" +#include + +namespace cv +{ + +CV_INIT_ALGORITHM(SCascade, "CascadeDetector.SCascade", + obj.info()->addParam(obj, "minScale", obj.minScale)); + // obj.info()->addParam(obj, "maxScale", obj.maxScale); + // obj.info()->addParam(obj, "scales", obj.scales); + // obj.info()->addParam(obj, "rejfactor", obj.rejfactor)); + +bool initModule_objdetect(void) +{ + Ptr sc = createSCascade(); + return sc->info() != 0; +} + +} \ No newline at end of file diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index ec1899a4b7..5b4b031697 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -175,7 +175,7 @@ struct Level enum { R_SHIFT = 1 << 15 }; float scaling[2]; - typedef cv::SoftCascade::Detection detection_t; + typedef cv::SCascade::Detection detection_t; Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) : octave(&oct), origScale(scale), relScale(scale / oct.scale), @@ -252,7 +252,7 @@ struct ChannelStorage } -struct cv::SoftCascade::Filds +struct cv::SCascade::Filds { float minScale; float maxScale; @@ -491,33 +491,25 @@ struct cv::SoftCascade::Filds } }; -cv::SoftCascade::SoftCascade(const float mins, const float maxs, const int nsc) -: filds(0), minScale(mins), maxScale(maxs), scales(nsc) {} +cv::SCascade::SCascade(const float mins, const float maxs, const int nsc, const int rej) +: filds(0), minScale(mins), maxScale(maxs), scales(nsc), rejfactor(rej) {} -cv::SoftCascade::SoftCascade(const cv::FileStorage& fs) : filds(0) +cv::SCascade::~SCascade() { delete filds;} + +void cv::SCascade::read(const FileNode& fn) { - read(fs); -} -cv::SoftCascade::~SoftCascade() -{ - delete filds; + Algorithm::read(fn); } -bool cv::SoftCascade::read( const cv::FileStorage& fs) +bool cv::SCascade::load(const FileNode& fn) { - if (!fs.isOpened()) return false; - - if (filds) - delete filds; - filds = 0; + if (filds) delete filds; filds = new Filds; - Filds& flds = *filds; - return flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale); + return filds->fill(fn, minScale, maxScale); } -void cv::SoftCascade::detectMultiScale(const Mat& image, const std::vector& /*rois*/, - std::vector& objects, const int /*rejectfactor*/) const +void cv::SCascade::detect(const Mat& image, const std::vector& /*rois*/, std::vector& objects) const { // only color images are supperted CV_Assert(image.type() == CV_8UC3); diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index 4711c3ca09..b1f8b2da46 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -1,31 +1,31 @@ /*M/////////////////////////////////////////////////////////////////////////////////////// // -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// 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. +// 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 +// License Agreement +// For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. +// Copyright (C) 2008-2012, 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: // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. +// * Redistribution's 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. +// * 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. +// * 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 @@ -37,60 +37,62 @@ // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. +// //M*/ #include "test_precomp.hpp" -TEST(SoftCascade, readCascade) +TEST(SCascade, readCascade) { std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/icf-template.xml"; - cv::SoftCascade cascade; + cv::SCascade cascade; cv::FileStorage fs(xml, cv::FileStorage::READ); - ASSERT_TRUE(cascade.read(fs)); + ASSERT_TRUE(fs.isOpened()); + ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); } -TEST(SoftCascade, detect) +TEST(SCascade, detect) { - typedef cv::SoftCascade::Detection detection_t; + typedef cv::SCascade::Detection Detection; std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; - cv::SoftCascade cascade; + cv::SCascade cascade; cv::FileStorage fs(xml, cv::FileStorage::READ); - ASSERT_TRUE(cascade.read(fs)); + ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); ASSERT_FALSE(colored.empty()); - std::vector objects; + std::vector objects; std::vector rois; rois.push_back(cv::Rect(0, 0, 640, 480)); - cascade.detectMultiScale(colored, rois, objects); + cascade.detect(colored, rois, objects); - // cv::Mat out = colored.clone(); - // int level = 0, total = 0; - // int levelWidth = objects[0].rect.width; + cv::Mat out = colored.clone(); + int level = 0, total = 0; + int levelWidth = objects[0].bb.width; - // for(int i = 0 ; i < (int)objects.size(); ++i) - // { - // if (objects[i].rect.width != levelWidth) - // { - // std::cout << "Level: " << level << " total " << total << std::endl; - // cv::imshow("out", out); - // cv::waitKey(0); - // out = colored.clone(); - // levelWidth = objects[i].rect.width; - // total = 0; - // level++; - // } - // cv::rectangle(out, objects[i].rect, cv::Scalar(255, 0, 0, 255), 1); - // std::cout << "detection: " << objects[i].rect.x - // << " " << objects[i].rect.y - // << " " << objects[i].rect.width - // << " " << objects[i].rect.height << std::endl; - // total++; - // } - // std::cout << "detected: " << (int)objects.size() << std::endl; + for(int i = 0 ; i < (int)objects.size(); ++i) + { + if (objects[i].bb.width != levelWidth) + { + std::cout << "Level: " << level << " total " << total << std::endl; + cv::imshow("out", out); + cv::waitKey(0); + out = colored.clone(); + levelWidth = objects[i].bb.width; + total = 0; + level++; + } + cv::rectangle(out, objects[i].bb, cv::Scalar(255, 0, 0, 255), 1); + std::cout << "detection: " << objects[i].bb.x + << " " << objects[i].bb.y + << " " << objects[i].bb.width + << " " << objects[i].bb.height << std::endl; + total++; + } + std::cout << "detected: " << (int)objects.size() << std::endl; ASSERT_EQ((int)objects.size(), 3668); } \ No newline at end of file From 65543c53f6b93ffaf681e364a4f4a3280f357367 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 5 Nov 2012 22:56:23 +0400 Subject: [PATCH 42/56] update test according to resize usage --- modules/objdetect/test/test_softcascade.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index b1f8b2da46..f8dec6b45a 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -94,5 +94,5 @@ TEST(SCascade, detect) total++; } std::cout << "detected: " << (int)objects.size() << std::endl; - ASSERT_EQ((int)objects.size(), 3668); + ASSERT_EQ((int)objects.size(), 3498); } \ No newline at end of file From 6a3a72393867e8377523fd23ef8ae8c54ea8e626 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 5 Nov 2012 23:28:37 +0400 Subject: [PATCH 43/56] refactor integral channels --- .../include/opencv2/objdetect/objdetect.hpp | 49 +++++++++---------- modules/objdetect/src/{isf.cpp => icf.cpp} | 22 +++++---- modules/objdetect/src/softcascade.cpp | 7 +-- 3 files changed, 39 insertions(+), 39 deletions(-) rename modules/objdetect/src/{isf.cpp => icf.cpp} (86%) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 84f41e1490..4d6db66f28 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -511,6 +511,29 @@ public: int kind; }; + // Create channel integrals for Soft Cascade detector. + class CV_EXPORTS Channels + { + public: + // constrictor form resizing factor. + // Param shr is a resizing factor. Resize is applied before the computing integral sum + Channels(const int shrinkage); + + // Appends specified number of HOG first-order features integrals into given vector. + // Param gray is an input 1-channel gray image. + // Param integrals is a vector of integrals. Hog-channels will be appended to it. + // Param bins is a number of hog-bins + void appendHogBins(const cv::Mat gray, std::vector& integrals, int bins) const; + + // Converts 3-channel BGR input frame in Luv and appends each channel to the integrals. + // Param frame is an input 3-channel BGR colored image. + // Param integrals is a vector of integrals. Computed from the frame luv-channels will be appended to it. + void appendLuvBins(const cv::Mat frame, std::vector& integrals) const; + + private: + int shrinkage; + }; + // An empty cascade will be created. // Param minScale is a minimum scale relative to the original size of the image on which cascade will be applyed. // Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. @@ -547,32 +570,6 @@ private: CV_EXPORTS bool initModule_objdetect(void); -/** - * \class IntegralChannels - * \brief Create channel integrals for Soft Cascade detector. - */ -class CV_EXPORTS IntegralChannels -{ -public: - //! constrictor form resizing factor. - //! Param shr is a resizing factor. Resize is applied before integral sum computing - IntegralChannels(const int shr) : shrinkage(shr) {} - - //! Appends specified number of hog first order feature integrals into given vector. - //! Param gray is an input 1-chennel gray image. - //! Param integrals is a vector of integrals. Computed from frame frame hog-channels will be appended to it. - //! Param bins is a number of hog-bins - void createHogBins(const cv::Mat gray, std::vector& integrals, int bins) const; - - //! Converts 3-chennel BGR input frame to Luv and append each channel to the integrals. - //! Param frame is an input 3-chennel BGR colored image. - //! Param integrals is a vector of integrals. Computed from frame frame luv-channels will be appended to it. - void createLuvBins(const cv::Mat frame, std::vector& integrals) const; - -private: - int shrinkage; -}; - //////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector ////////////// // struct for detection region of interest (ROI) diff --git a/modules/objdetect/src/isf.cpp b/modules/objdetect/src/icf.cpp similarity index 86% rename from modules/objdetect/src/isf.cpp rename to modules/objdetect/src/icf.cpp index bbf4f27c0e..b6bd113d19 100644 --- a/modules/objdetect/src/isf.cpp +++ b/modules/objdetect/src/icf.cpp @@ -44,7 +44,9 @@ #include #include -void cv::IntegralChannels::createHogBins(const cv::Mat gray, std::vector& integrals, int bins) const +cv::SCascade::Channels::Channels(int shr) : shrinkage(shr) {} + +void cv::SCascade::Channels::appendHogBins(const cv::Mat gray, std::vector& integrals, int bins) const { CV_Assert(gray.type() == CV_8UC1); int h = gray.rows; @@ -52,11 +54,11 @@ void cv::IntegralChannels::createHogBins(const cv::Mat gray, std::vector& integrals) const +void cv::SCascade::Channels::appendLuvBins(const cv::Mat frame, std::vector& integrals) const { CV_Assert(frame.type() == CV_8UC3); CV_Assert(!(frame.cols % shrinkage) && !(frame.rows % shrinkage)); - cv::Mat luv; + cv::Mat luv, shrunk; cv::cvtColor(frame, luv, CV_BGR2Luv); + cv::resize(luv, shrunk, cv::Size(), 1.0 / shrinkage, 1.0 / shrinkage, CV_INTER_AREA); std::vector splited; - split(luv, splited); + split(shrunk, splited); for (size_t i = 0; i < splited.size(); ++i) { - cv::Mat shrunk, sum; - cv::resize(splited[i], shrunk, cv::Size(), 1.0 / shrinkage, 1.0 / shrinkage, CV_INTER_AREA); - cv::integral(shrunk, sum, cv::noArray(), CV_32S); + cv::Mat sum; + cv::integral(splited[i], sum, cv::noArray(), CV_32S); integrals.push_back(sum); } } \ No newline at end of file diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 5b4b031697..37ab091666 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -223,14 +223,15 @@ struct ChannelStorage ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) { hog.clear(); - cv::IntegralChannels ints(shr); + hog.reserve(10); + cv::SCascade::Channels ints(shr); // convert to grey cv::Mat grey; cv::cvtColor(colored, grey, CV_BGR2GRAY); - ints.createHogBins(grey, hog, 6); - ints.createLuvBins(colored, hog); + ints.appendHogBins(grey, hog, 6); + ints.appendLuvBins(colored, hog); step = hog[0].cols; } From 157ab66ab908b44d9f72438a651461a796ce52ca Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 6 Nov 2012 01:30:20 +0400 Subject: [PATCH 44/56] add ROI support --- .../include/opencv2/objdetect/objdetect.hpp | 4 +- .../objdetect/perf/perf_cascadeclassifier.cpp | 5 +- modules/objdetect/src/softcascade.cpp | 54 +++++++-- modules/objdetect/test/test_softcascade.cpp | 108 ++++++++++++++---- 4 files changed, 135 insertions(+), 36 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 4d6db66f28..8c1f29298b 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -556,9 +556,11 @@ public: // Param image is a frame on which detector will be applied. // Param rois is a vector of regions of interest. Only the objects that fall into one of the regions will be returned. // Param objects is an output array of Detections - virtual void detect(const Mat& image, const std::vector& rois, std::vector& objects) const; + virtual void detect(InputArray image, InputArray rois, std::vector& objects) const; private: + void detectNoRoi(const Mat& image, std::vector& objects) const; + struct Filds; Filds* filds; diff --git a/modules/objdetect/perf/perf_cascadeclassifier.cpp b/modules/objdetect/perf/perf_cascadeclassifier.cpp index cc068e8ea3..f52bd59af1 100644 --- a/modules/objdetect/perf/perf_cascadeclassifier.cpp +++ b/modules/objdetect/perf/perf_cascadeclassifier.cpp @@ -81,13 +81,12 @@ PERF_TEST_P(detect, SCascade, ASSERT_TRUE(fs.isOpened()); ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); - std::vector rois; std::vector objectBoxes; - cascade.detect(colored, rois, objectBoxes); + cascade.detect(colored, cv::noArray(), objectBoxes); TEST_CYCLE() { - cascade.detect(colored, rois, objectBoxes); + cascade.detect(colored, cv::noArray(), objectBoxes); } vector rects; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 37ab091666..2f7e697135 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -510,16 +510,9 @@ bool cv::SCascade::load(const FileNode& fn) return filds->fill(fn, minScale, maxScale); } -void cv::SCascade::detect(const Mat& image, const std::vector& /*rois*/, std::vector& objects) const +void cv::SCascade::detectNoRoi(const cv::Mat& image, std::vector& objects) const { - // only color images are supperted - CV_Assert(image.type() == CV_8UC3); - Filds& fld = *filds; - fld.calcLevels(image.size(), scales); - - objects.clear(); - // create integrals ChannelStorage storage(image, fld.shrinkage); @@ -537,4 +530,49 @@ void cv::SCascade::detect(const Mat& image, const std::vector& /*rois* } } } +} + +void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vector& objects) const +{ + // only color images are supperted + cv::Mat image = _image.getMat(); + CV_Assert(image.type() == CV_8UC3); + + Filds& fld = *filds; + fld.calcLevels(image.size(), scales); + + objects.clear(); + + if (_rois.kind() == cv::_InputArray::NONE) + return detectNoRoi(image, objects); + + cv::Mat roi = _rois.getMat(); + cv::Mat mask(image.rows / fld.shrinkage, image.cols / fld.shrinkage, CV_8UC1); + + mask.setTo(cv::Scalar::all(0)); + cv::Rect* r = roi.ptr(0); + for (int i = 0; i < (int)roi.cols; ++i) + cv::Mat(mask, cv::Rect(r[i].x / fld.shrinkage, r[i].y / fld.shrinkage, r[i].width / fld.shrinkage , r[i].height / fld.shrinkage)).setTo(cv::Scalar::all(1)); + + // create integrals + ChannelStorage storage(image, fld.shrinkage); + + typedef std::vector::const_iterator lIt; + for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) + { + const Level& level = *it; + + for (int dy = 0; dy < level.workRect.height; ++dy) + { + uchar* m = mask.ptr(dy); + for (int dx = 0; dx < level.workRect.width; ++dx) + { + if (m[dx]) + { + storage.offset = dy * storage.step + dx; + fld.detectAt(dx, dy, level, storage, objects); + } + } + } + } } \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index f8dec6b45a..d33d5892bf 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -63,36 +63,96 @@ TEST(SCascade, detect) cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); ASSERT_FALSE(colored.empty()); + std::vector objects; + + cascade.detect(colored, cv::noArray(), objects); + + // cv::Mat out = colored.clone(); + // int level = 0, total = 0; + // int levelWidth = objects[0].bb.width; + + // for(int i = 0 ; i < (int)objects.size(); ++i) + // { + // if (objects[i].bb.width != levelWidth) + // { + // std::cout << "Level: " << level << " total " << total << std::endl; + // cv::imshow("out", out); + // cv::waitKey(0); + // out = colored.clone(); + // levelWidth = objects[i].bb.width; + // total = 0; + // level++; + // } + // cv::rectangle(out, objects[i].bb, cv::Scalar(255, 0, 0, 255), 1); + // std::cout << "detection: " << objects[i].bb.x + // << " " << objects[i].bb.y + // << " " << objects[i].bb.width + // << " " << objects[i].bb.height << std::endl; + // total++; + // } + // std::cout << "detected: " << (int)objects.size() << std::endl; + ASSERT_EQ((int)objects.size(), 3498); +} + +TEST(SCascade, detectRoi) +{ + typedef cv::SCascade::Detection Detection; + std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; + cv::SCascade cascade; + cv::FileStorage fs(xml, cv::FileStorage::READ); + ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); + + cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); + ASSERT_FALSE(colored.empty()); + std::vector objects; std::vector rois; rois.push_back(cv::Rect(0, 0, 640, 480)); cascade.detect(colored, rois, objects); + // cv::Mat out = colored.clone(); + // int level = 0, total = 0; + // int levelWidth = objects[0].bb.width; - cv::Mat out = colored.clone(); - int level = 0, total = 0; - int levelWidth = objects[0].bb.width; - - for(int i = 0 ; i < (int)objects.size(); ++i) - { - if (objects[i].bb.width != levelWidth) - { - std::cout << "Level: " << level << " total " << total << std::endl; - cv::imshow("out", out); - cv::waitKey(0); - out = colored.clone(); - levelWidth = objects[i].bb.width; - total = 0; - level++; - } - cv::rectangle(out, objects[i].bb, cv::Scalar(255, 0, 0, 255), 1); - std::cout << "detection: " << objects[i].bb.x - << " " << objects[i].bb.y - << " " << objects[i].bb.width - << " " << objects[i].bb.height << std::endl; - total++; - } - std::cout << "detected: " << (int)objects.size() << std::endl; + // for(int i = 0 ; i < (int)objects.size(); ++i) + // { + // if (objects[i].bb.width != levelWidth) + // { + // std::cout << "Level: " << level << " total " << total << std::endl; + // cv::imshow("out", out); + // cv::waitKey(0); + // out = colored.clone(); + // levelWidth = objects[i].bb.width; + // total = 0; + // level++; + // } + // cv::rectangle(out, objects[i].bb, cv::Scalar(255, 0, 0, 255), 1); + // std::cout << "detection: " << objects[i].bb.x + // << " " << objects[i].bb.y + // << " " << objects[i].bb.width + // << " " << objects[i].bb.height << std::endl; + // total++; + // } + // std::cout << "detected: " << (int)objects.size() << std::endl; ASSERT_EQ((int)objects.size(), 3498); +} + +TEST(SCascade, detectNoRoi) +{ + typedef cv::SCascade::Detection Detection; + std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; + cv::SCascade cascade; + cv::FileStorage fs(xml, cv::FileStorage::READ); + ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); + + cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); + ASSERT_FALSE(colored.empty()); + + std::vector objects; + std::vector rois; + + cascade.detect(colored, rois, objects); + + ASSERT_EQ((int)objects.size(), 0); } \ No newline at end of file From 4207552e19e7758e5bda06e10b3d66293a2d6e11 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 6 Nov 2012 02:41:26 +0400 Subject: [PATCH 45/56] add object init --- modules/objdetect/src/objdetect_init.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/objdetect/src/objdetect_init.cpp b/modules/objdetect/src/objdetect_init.cpp index a6ce81e809..d53c9480db 100644 --- a/modules/objdetect/src/objdetect_init.cpp +++ b/modules/objdetect/src/objdetect_init.cpp @@ -46,10 +46,10 @@ namespace cv { CV_INIT_ALGORITHM(SCascade, "CascadeDetector.SCascade", - obj.info()->addParam(obj, "minScale", obj.minScale)); - // obj.info()->addParam(obj, "maxScale", obj.maxScale); - // obj.info()->addParam(obj, "scales", obj.scales); - // obj.info()->addParam(obj, "rejfactor", obj.rejfactor)); + obj.info()->addParam(obj, "minScale", obj.minScale); + obj.info()->addParam(obj, "maxScale", obj.maxScale); + obj.info()->addParam(obj, "scales", obj.scales); + obj.info()->addParam(obj, "rejfactor", obj.rejfactor)); bool initModule_objdetect(void) { From 31a073ca668eb4332e5c15ab04952d62345aa3ab Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 7 Nov 2012 01:50:33 +0400 Subject: [PATCH 46/56] documentation --- modules/objdetect/doc/objdetect.rst | 1 + modules/objdetect/doc/soft_cascade.rst | 89 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 modules/objdetect/doc/soft_cascade.rst diff --git a/modules/objdetect/doc/objdetect.rst b/modules/objdetect/doc/objdetect.rst index 4bab781b8a..381742e50b 100644 --- a/modules/objdetect/doc/objdetect.rst +++ b/modules/objdetect/doc/objdetect.rst @@ -8,4 +8,5 @@ objdetect. Object Detection :maxdepth: 2 cascade_classification + soft_cascade latent_svm diff --git a/modules/objdetect/doc/soft_cascade.rst b/modules/objdetect/doc/soft_cascade.rst new file mode 100644 index 0000000000..0ba7566012 --- /dev/null +++ b/modules/objdetect/doc/soft_cascade.rst @@ -0,0 +1,89 @@ +Soft Cascade Classifier +====================== + +.. highlight:: cpp + +Soft Cascade Classifier for Object Detection +---------------------------------------------------------- + +Cascade detectors have been shown to operate extremely rapidly, with high accuracy, and have important applications in different spheres. The initial goal for this cascade implementation was the fast and accurate pedestrian detector but it also useful in general. Soft cascade is trained with AdaBoost. But instead of training sequence of stages, the soft cascade is trained as a one long stage of T weak classifiers. Soft cascade is formulated as follows: + +.. math:: + \texttt{H}(x) = \sum _{\texttt{t}=1..\texttt{T}} {\texttt{s}_t(x)} + +where :math:`\texttt{s}_t(x) = \alpha_t\texttt{h}_t(x)` are the set of thresholded weak classifiers selected during AdaBoost training scaled by the associated weights. Let + +.. math:: + \texttt{H}_t(x) = \sum _{\texttt{i}=1..\texttt{t}} {\texttt{s}_i(x)} + +be the partial sum of sample responses before :math:`t`-the weak classifier will be applied. The funtcion :math:`\texttt{H}_t(x)` of :math:`t` for sample :math:`x` named *sample trace*. +After each weak classifier evaluation, the sample trace at the point :math:`t` is compared with the rejection threshold :math:`r_t`. The sequence of :math:`r_t` named *rejection trace*. + +The sample has been rejected if it fall rejection threshold. So stageless cascade allows to reject not-object sample as soon as possible. Another meaning of the sample trace is a confidence with that sample recognized as desired object. At each :math:`t` that confidence depend on all previous weak classifier. This feature of soft cascade is resulted in more accurate detection. The original formulation of soft cascade can be found in [BJ05]_. + +.. [BJ05] Lubomir Bourdev and Jonathan Brandt. tRobust Object Detection Via Soft Cascade. IEEE CVPR, 2005. +.. [BMTG12] Rodrigo Benenson, Markus Mathias, Radu Timofte and Luc Van Gool. Pedestrian detection at 100 frames per second. IEEE CVPR, 2012. + + +SCascade +---------------- +.. ocv:class:: SCascade + +Implementation of soft (stageless) cascaded detector. :: + + class CV_EXPORTS SCascade : public Algorithm + { + public: + SCascade(const float minScale = 0.4f, const float maxScale = 5.f, const int scales = 55, const int rejfactor = 1); + virtual ~SCascade(); + cv::AlgorithmInfo* info() const; + virtual bool load(const FileNode& fn); + virtual void detect(InputArray image, InputArray rois, std::vector& objects) const; + }; + + +SCascade::SCascade +-------------------------- +An empty cascade will be created. + +.. ocv:function:: bool SCascade::SCascade(const float minScale = 0.4f, const float maxScale = 5.f, const int scales = 55, const int rejfactor = 1) + + :param minScale: a minimum scale relative to the original size of the image on which cascade will be applyed. + + :param maxScale: a maximum scale relative to the original size of the image on which cascade will be applyed. + + :param scales: a number of scales from minScale to maxScale. + + :param rejfactor: used for non maximum suppression. + + + +SCascade::~SCascade +--------------------------- +Destructor for SCascade. + +.. ocv:function:: SCascade::~SCascade() + + + +SCascade::load +-------------------------- +Load cascade from FileNode. + +.. ocv:function:: bool SCascade::load(const FileNode& fn) + + :param fn: File node from which the soft cascade are read. + + + +SCascade::detect +-------------------------- +Apply cascade to an input frame and return the vector of Decection objcts. + +.. ocv:function:: bool SCascade::detect(InputArray image, InputArray rois, std::vector& objects) const + + :param image: a frame on which detector will be applied. + + :param rois: a vector of regions of interest. Only the objects that fall into one of the regions will be returned. + + :param objects: an output array of Detections. \ No newline at end of file From 5fb9f48360e4630233b8a8f57aa496702582ef96 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 7 Nov 2012 02:21:48 +0400 Subject: [PATCH 47/56] fix angle scaling --- modules/objdetect/src/icf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/objdetect/src/icf.cpp b/modules/objdetect/src/icf.cpp index b6bd113d19..885cebdd78 100644 --- a/modules/objdetect/src/icf.cpp +++ b/modules/objdetect/src/icf.cpp @@ -63,7 +63,7 @@ void cv::SCascade::Channels::appendHogBins(const cv::Mat gray, std::vector hist; for (int bin = 0; bin < bins; ++bin) From 465687216108ae2b4a2f94a250e83bf131ca4502 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 7 Nov 2012 02:27:07 +0400 Subject: [PATCH 48/56] fixed typo --- .../include/opencv2/objdetect/objdetect.hpp | 4 ++-- modules/objdetect/src/softcascade.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 8c1f29298b..e3307c995c 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -561,8 +561,8 @@ public: private: void detectNoRoi(const Mat& image, std::vector& objects) const; - struct Filds; - Filds* filds; + struct Fields; + Fields* fields; float minScale; float maxScale; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 2f7e697135..afe1304ed6 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -253,7 +253,7 @@ struct ChannelStorage } -struct cv::SCascade::Filds +struct cv::SCascade::Fields { float minScale; float maxScale; @@ -493,9 +493,9 @@ struct cv::SCascade::Filds }; cv::SCascade::SCascade(const float mins, const float maxs, const int nsc, const int rej) -: filds(0), minScale(mins), maxScale(maxs), scales(nsc), rejfactor(rej) {} +: fields(0), minScale(mins), maxScale(maxs), scales(nsc), rejfactor(rej) {} -cv::SCascade::~SCascade() { delete filds;} +cv::SCascade::~SCascade() { delete fields;} void cv::SCascade::read(const FileNode& fn) { @@ -504,15 +504,15 @@ void cv::SCascade::read(const FileNode& fn) bool cv::SCascade::load(const FileNode& fn) { - if (filds) delete filds; + if (fields) delete fields; - filds = new Filds; - return filds->fill(fn, minScale, maxScale); + fields = new Fields; + return fields->fill(fn, minScale, maxScale); } void cv::SCascade::detectNoRoi(const cv::Mat& image, std::vector& objects) const { - Filds& fld = *filds; + Fields& fld = *fields; // create integrals ChannelStorage storage(image, fld.shrinkage); @@ -538,7 +538,7 @@ void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vect cv::Mat image = _image.getMat(); CV_Assert(image.type() == CV_8UC3); - Filds& fld = *filds; + Fields& fld = *fields; fld.calcLevels(image.size(), scales); objects.clear(); From f1e36043e66930178c7eecb7ce3fcd34f07945ea Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 7 Nov 2012 03:14:15 +0400 Subject: [PATCH 49/56] clean code --- modules/objdetect/src/icf.cpp | 2 - modules/objdetect/src/softcascade.cpp | 161 +++++++------------------- 2 files changed, 43 insertions(+), 120 deletions(-) diff --git a/modules/objdetect/src/icf.cpp b/modules/objdetect/src/icf.cpp index 885cebdd78..a41ae7a323 100644 --- a/modules/objdetect/src/icf.cpp +++ b/modules/objdetect/src/icf.cpp @@ -41,8 +41,6 @@ //M*/ #include -#include -#include cv::SCascade::Channels::Channels(int shr) : shrinkage(shr) {} diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index afe1304ed6..c00e4e6ef1 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -41,29 +41,16 @@ //M*/ #include -#include -#include - -#include -#include -#include -#include -#include - -// used for noisy printfs -// #define WITH_DEBUG_OUT - -#if defined WITH_DEBUG_OUT -# define dprintf(format, ...) \ - do { printf(format, ##__VA_ARGS__); } while (0) -#else -# define dprintf(format, ...) -#endif namespace { struct Octave { + Octave(const int i, cv::Size origObjSize, const cv::FileNode& fn) + : index(i), scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), + size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), + shrinkage((int)fn[SC_OCT_SHRINKAGE]) {} + int index; float scale; int stages; @@ -73,49 +60,32 @@ struct Octave static const char *const SC_OCT_SCALE; static const char *const SC_OCT_STAGES; static const char *const SC_OCT_SHRINKAGE; - - Octave(const int i, cv::Size origObjSize, const cv::FileNode& fn) - : index(i), scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), - size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), - shrinkage((int)fn[SC_OCT_SHRINKAGE]) - {} }; -const char *const Octave::SC_OCT_SCALE = "scale"; -const char *const Octave::SC_OCT_STAGES = "stageNum"; -const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; struct Weak { + Weak(){} + Weak(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]){} + float threshold; static const char *const SC_STAGE_THRESHOLD; - - Weak(){} - Weak(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]){} }; -const char *const Weak::SC_STAGE_THRESHOLD = "stageThreshold"; struct Node { - int feature; - float threshold; - Node(){} Node(const int offset, cv::FileNodeIterator& fIt) : feature((int)(*(fIt +=2)++) + offset), threshold((float)(*(fIt++))){} + + int feature; + float threshold; }; struct Feature { - int channel; - cv::Rect rect; - float rarea; - - static const char * const SC_F_CHANNEL; - static const char * const SC_F_RECT; - Feature() {} Feature(const cv::FileNode& fn) : channel((int)fn[SC_F_CHANNEL]) { @@ -126,40 +96,22 @@ struct Feature // 1 / area rarea = 1.f / ((rect.width - rect.x) * (rect.height - rect.y)); } + + int channel; + cv::Rect rect; + float rarea; + + static const char *const SC_F_CHANNEL; + static const char *const SC_F_RECT; + }; -const char * const Feature::SC_F_CHANNEL = "channel"; -const char * const Feature::SC_F_RECT = "rect"; - -struct CascadeIntrinsics -{ - static const float lambda = 1.099f, a = 0.89f; - - static float getFor(bool isUp, float scaling) - { - if (fabs(scaling - 1.f) < FLT_EPSILON) - return 1.f; - - // according to R. Benenson, M. Mathias, R. Timofte and L. Van Gool's and Dallal's papers - static const float A[2][2] = - { //channel <= 6, otherwise - { 0.89f, 1.f}, // down - { 1.00f, 1.f} // up - }; - - static const float B[2][2] = - { //channel <= 6, otherwise - { 1.099f / log(2), 2.f}, // down - { 0.f, 2.f} // up - }; - - float a = A[(int)(scaling >= 1)][(int)(isUp)]; - float b = B[(int)(scaling >= 1)][(int)(isUp)]; - - dprintf("scaling: %f %f %f %f\n", scaling, a, b, a * pow(scaling, b)); - return a * pow(scaling, b); - } -}; +const char *const Octave::SC_OCT_SCALE = "scale"; +const char *const Octave::SC_OCT_STAGES = "stageNum"; +const char *const Octave::SC_OCT_SHRINKAGE = "shrinkingFactor"; +const char *const Weak::SC_STAGE_THRESHOLD = "stageThreshold"; +const char *const Feature::SC_F_CHANNEL = "channel"; +const char *const Feature::SC_F_RECT = "rect"; struct Level { @@ -172,37 +124,36 @@ struct Level cv::Size workRect; cv::Size objSize; - enum { R_SHIFT = 1 << 15 }; - - float scaling[2]; - typedef cv::SCascade::Detection detection_t; + float scaling[2]; // 0-th for channels <= 6, 1-st otherwise + typedef cv::SCascade::Detection Detection; Level(const Octave& oct, const float scale, const int shrinkage, const int w, const int h) : octave(&oct), origScale(scale), relScale(scale / oct.scale), workRect(cv::Size(cvRound(w / (float)shrinkage),cvRound(h / (float)shrinkage))), objSize(cv::Size(cvRound(oct.size.width * relScale), cvRound(oct.size.height * relScale))) { - scaling[0] = CascadeIntrinsics::getFor(false, relScale) / (relScale * relScale); - scaling[1] = CascadeIntrinsics::getFor(true, relScale) / (relScale * relScale); + scaling[0] = ((relScale >= 1.f)? 1.f : (0.89f * pow(relScale, 1.099f / log(2)))) / (relScale * relScale); + scaling[1] = 1.f; scaleshift = relScale * (1 << 16); } - void addDetection(const int x, const int y, float confidence, std::vector& detections) const + void addDetection(const int x, const int y, float confidence, std::vector& detections) const { int shrinkage = (*octave).shrinkage; cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height); - detections.push_back(detection_t(rect, confidence)); + detections.push_back(Detection(rect, confidence)); } float rescale(cv::Rect& scaledRect, const float threshold, int idx) const { +#define SSHIFT(a) ((a) + (1 << 15)) >> 16 // rescale - scaledRect.x = (scaleshift * scaledRect.x + R_SHIFT) >> 16; - scaledRect.y = (scaleshift * scaledRect.y + R_SHIFT) >> 16; - scaledRect.width = (scaleshift * scaledRect.width + R_SHIFT) >> 16; - scaledRect.height = (scaleshift * scaledRect.height + R_SHIFT) >> 16; - + scaledRect.x = SSHIFT(scaleshift * scaledRect.x); + scaledRect.y = SSHIFT(scaleshift * scaledRect.y); + scaledRect.width = SSHIFT(scaleshift * scaledRect.width); + scaledRect.height = SSHIFT(scaleshift * scaledRect.height); +#undef SSHIFT float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); // compensation areas rounding @@ -219,7 +170,6 @@ struct ChannelStorage enum {HOG_BINS = 6, HOG_LUV_BINS = 10}; - ChannelStorage() {} ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr) { hog.clear(); @@ -264,7 +214,7 @@ struct cv::SCascade::Fields int shrinkage; std::vector octaves; - std::vector stages; + std::vector stages; std::vector nodes; std::vector leaves; std::vector features; @@ -273,29 +223,19 @@ struct cv::SCascade::Fields cv::Size frameSize; - enum { BOOST = 0 }; - typedef std::vector::iterator octIt_t; void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage, std::vector& detections) const { - dprintf("detect at: %d %d\n", dx, dy); - float detectionScore = 0.f; const Octave& octave = *(level.octave); int stBegin = octave.index * octave.stages, stEnd = stBegin + 1024;//octave.stages; - dprintf(" octave stages: %d to %d index %d %f level %f\n", - stBegin, stEnd, octave.index, octave.scale, level.origScale); - int st = stBegin; for(; st < stEnd; ++st) { - - dprintf("index: %d\n", st); - const Weak& stage = stages[st]; { int nId = st * 3; @@ -309,12 +249,8 @@ struct cv::SCascade::Fields float sum = storage.get(feature.channel, scaledRect); - dprintf("root feature %d %f\n",feature.channel, sum); - int next = (sum >= threshold)? 2 : 1; - dprintf("go: %d (%f >= %f)\n\n" ,next, sum, threshold); - // leaves const Node& leaf = nodes[nId + next]; const Feature& fLeaf = features[leaf.feature]; @@ -327,25 +263,12 @@ struct cv::SCascade::Fields int lShift = (next - 1) * 2 + ((sum >= threshold) ? 1 : 0); float impact = leaves[(st * 4) + lShift]; - dprintf("decided: %d (%f >= %f) %d %f\n\n" ,next, sum, threshold, lShift, impact); - detectionScore += impact; } - dprintf("extracted stage:\n"); - dprintf("ct %f\n", stage.threshold); - dprintf("computed score %f\n\n", detectionScore); - -#if defined WITH_DEBUG_OUT - if (st - stBegin > 50 ) break; -#endif - if (detectionScore <= stage.threshold) return; } - dprintf("x %d y %d: %d\n", dx, dy, st - stBegin); - dprintf(" got %d\n", st); - level.addDetection(dx, dy, detectionScore, detections); } @@ -546,16 +469,18 @@ void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vect if (_rois.kind() == cv::_InputArray::NONE) return detectNoRoi(image, objects); + int shr = fld.shrinkage; + cv::Mat roi = _rois.getMat(); - cv::Mat mask(image.rows / fld.shrinkage, image.cols / fld.shrinkage, CV_8UC1); + cv::Mat mask(image.rows / shr, image.cols / shr, CV_8UC1); mask.setTo(cv::Scalar::all(0)); cv::Rect* r = roi.ptr(0); for (int i = 0; i < (int)roi.cols; ++i) - cv::Mat(mask, cv::Rect(r[i].x / fld.shrinkage, r[i].y / fld.shrinkage, r[i].width / fld.shrinkage , r[i].height / fld.shrinkage)).setTo(cv::Scalar::all(1)); + cv::Mat(mask, cv::Rect(r[i].x / shr, r[i].y / shr, r[i].width / shr , r[i].height / shr)).setTo(cv::Scalar::all(1)); // create integrals - ChannelStorage storage(image, fld.shrinkage); + ChannelStorage storage(image, shr); typedef std::vector::const_iterator lIt; for (lIt it = fld.levels.begin(); it != fld.levels.end(); ++it) From 1022094dc09895c2e6039e95754cc42e22c29f38 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 7 Nov 2012 04:20:35 +0400 Subject: [PATCH 50/56] fix levels computing --- modules/objdetect/src/softcascade.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index c00e4e6ef1..6c03ae1be4 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -46,7 +46,7 @@ namespace { struct Octave { - Octave(const int i, cv::Size origObjSize, const cv::FileNode& fn) + Octave(const int i, const cv::Size& origObjSize, const cv::FileNode& fn) : index(i), scale((float)fn[SC_OCT_SCALE]), stages((int)fn[SC_OCT_STAGES]), size(cvRound(origObjSize.width * scale), cvRound(origObjSize.height * scale)), shrinkage((int)fn[SC_OCT_SHRINKAGE]) {} @@ -207,6 +207,7 @@ struct cv::SCascade::Fields { float minScale; float maxScale; + int scales; int origObjWidth; int origObjHeight; @@ -292,12 +293,14 @@ struct cv::SCascade::Fields } // compute levels of full pyramid - void calcLevels(const cv::Size& curr, int scales) + void calcLevels(const cv::Size& curr, float mins, float maxs, int total) { - if (frameSize == curr) return; - frameSize = curr; - + if (frameSize == curr && maxs == maxScale && mins == minScale && total == scales) return; CV_Assert(scales > 1); + + frameSize = curr; + maxScale = maxs; minScale = mins; scales = total; + levels.clear(); float logFactor = (log(maxScale) - log(minScale)) / (scales -1); @@ -323,11 +326,8 @@ struct cv::SCascade::Fields } } - bool fill(const FileNode &root, const float mins, const float maxs) + bool fill(const FileNode &root) { - minScale = mins; - maxScale = maxs; - // cascade properties static const char *const SC_STAGE_TYPE = "stageType"; static const char *const SC_BOOST = "BOOST"; @@ -430,7 +430,7 @@ bool cv::SCascade::load(const FileNode& fn) if (fields) delete fields; fields = new Fields; - return fields->fill(fn, minScale, maxScale); + return fields->fill(fn); } void cv::SCascade::detectNoRoi(const cv::Mat& image, std::vector& objects) const @@ -462,7 +462,7 @@ void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vect CV_Assert(image.type() == CV_8UC3); Fields& fld = *fields; - fld.calcLevels(image.size(), scales); + fld.calcLevels(image.size(),minScale, maxScale, scales); objects.clear(); From f93cffaa0dd3263137ba3ff0d0fc52dd02422faa Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 8 Nov 2012 00:08:43 +0400 Subject: [PATCH 51/56] fix compilation under win --- modules/objdetect/src/icf.cpp | 2 +- modules/objdetect/src/softcascade.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/objdetect/src/icf.cpp b/modules/objdetect/src/icf.cpp index a41ae7a323..0401a06236 100644 --- a/modules/objdetect/src/icf.cpp +++ b/modules/objdetect/src/icf.cpp @@ -56,7 +56,7 @@ void cv::SCascade::Channels::appendHogBins(const cv::Mat gray, std::vector= 1.f)? 1.f : (0.89f * pow(relScale, 1.099f / log(2)))) / (relScale * relScale); + scaling[0] = ((relScale >= 1.f)? 1.f : (0.89f * pow(relScale, 1.099f / log(2.f)))) / (relScale * relScale); scaling[1] = 1.f; scaleshift = relScale * (1 << 16); } From e379771c039592e4ef879118b9bd194452162d08 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 8 Nov 2012 02:16:04 +0400 Subject: [PATCH 52/56] git warning --- modules/objdetect/include/opencv2/objdetect/objdetect.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index e3307c995c..0a07e32f3c 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -411,7 +411,7 @@ protected: enum { DO_CANNY_PRUNING = 1, SCALE_IMAGE = 2, FIND_BIGGEST_OBJECT = 4, DO_ROUGH_SEARCH = 8 }; - friend struct CascadeClassifierInvoker; + friend class CascadeClassifierInvoker; template friend int predictOrdered( CascadeClassifier& cascade, Ptr &featureEvaluator, double& weight); From 8a3e89799964caa01e958e852f04a7e9a675f627 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Thu, 8 Nov 2012 02:33:19 +0400 Subject: [PATCH 53/56] min and max params become double --- .../objdetect/include/opencv2/objdetect/objdetect.hpp | 11 ++++++----- modules/objdetect/src/icf.cpp | 4 ++-- modules/objdetect/src/softcascade.cpp | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 0a07e32f3c..98c4f0a79e 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -523,12 +523,12 @@ public: // Param gray is an input 1-channel gray image. // Param integrals is a vector of integrals. Hog-channels will be appended to it. // Param bins is a number of hog-bins - void appendHogBins(const cv::Mat gray, std::vector& integrals, int bins) const; + void appendHogBins(const cv::Mat& gray, std::vector& integrals, int bins) const; // Converts 3-channel BGR input frame in Luv and appends each channel to the integrals. // Param frame is an input 3-channel BGR colored image. // Param integrals is a vector of integrals. Computed from the frame luv-channels will be appended to it. - void appendLuvBins(const cv::Mat frame, std::vector& integrals) const; + void appendLuvBins(const cv::Mat& frame, std::vector& integrals) const; private: int shrinkage; @@ -539,7 +539,7 @@ public: // Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. // Param scales is a number of scales from minScale to maxScale. // Param rejfactor is used for NMS. - SCascade(const float minScale = 0.4f, const float maxScale = 5.f, const int scales = 55, const int rejfactor = 1); + SCascade(const double minScale = 0.4, const double maxScale = 5., const int scales = 55, const int rejfactor = 1); virtual ~SCascade(); @@ -564,8 +564,9 @@ private: struct Fields; Fields* fields; - float minScale; - float maxScale; + double minScale; + double maxScale; + int scales; int rejfactor; }; diff --git a/modules/objdetect/src/icf.cpp b/modules/objdetect/src/icf.cpp index 0401a06236..416e5ffff0 100644 --- a/modules/objdetect/src/icf.cpp +++ b/modules/objdetect/src/icf.cpp @@ -44,7 +44,7 @@ cv::SCascade::Channels::Channels(int shr) : shrinkage(shr) {} -void cv::SCascade::Channels::appendHogBins(const cv::Mat gray, std::vector& integrals, int bins) const +void cv::SCascade::Channels::appendHogBins(const cv::Mat& gray, std::vector& integrals, int bins) const { CV_Assert(gray.type() == CV_8UC1); int h = gray.rows; @@ -92,7 +92,7 @@ void cv::SCascade::Channels::appendHogBins(const cv::Mat gray, std::vector& integrals) const +void cv::SCascade::Channels::appendLuvBins(const cv::Mat& frame, std::vector& integrals) const { CV_Assert(frame.type() == CV_8UC3); CV_Assert(!(frame.cols % shrinkage) && !(frame.rows % shrinkage)); diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 985655a362..134fa9b2ca 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -296,10 +296,10 @@ struct cv::SCascade::Fields void calcLevels(const cv::Size& curr, float mins, float maxs, int total) { if (frameSize == curr && maxs == maxScale && mins == minScale && total == scales) return; - CV_Assert(scales > 1); frameSize = curr; maxScale = maxs; minScale = mins; scales = total; + CV_Assert(scales > 1); levels.clear(); float logFactor = (log(maxScale) - log(minScale)) / (scales -1); @@ -415,7 +415,7 @@ struct cv::SCascade::Fields } }; -cv::SCascade::SCascade(const float mins, const float maxs, const int nsc, const int rej) +cv::SCascade::SCascade(const double mins, const double maxs, const int nsc, const int rej) : fields(0), minScale(mins), maxScale(maxs), scales(nsc), rejfactor(rej) {} cv::SCascade::~SCascade() { delete fields;} @@ -462,7 +462,7 @@ void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vect CV_Assert(image.type() == CV_8UC3); Fields& fld = *fields; - fld.calcLevels(image.size(),minScale, maxScale, scales); + fld.calcLevels(image.size(),(float) minScale, (float)maxScale, (float)scales); objects.clear(); From 665bf430d5ea585fbf6b18e7fc2b02ea2ed2f2da Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Fri, 9 Nov 2012 00:07:32 +0400 Subject: [PATCH 54/56] fix warnings under win --- modules/objdetect/src/softcascade.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index 134fa9b2ca..bfda83426a 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -134,7 +134,7 @@ struct Level { scaling[0] = ((relScale >= 1.f)? 1.f : (0.89f * pow(relScale, 1.099f / log(2.f)))) / (relScale * relScale); scaling[1] = 1.f; - scaleshift = relScale * (1 << 16); + scaleshift = static_cast(relScale * (1 << 16)); } void addDetection(const int x, const int y, float confidence, std::vector& detections) const @@ -154,7 +154,7 @@ struct Level scaledRect.width = SSHIFT(scaleshift * scaledRect.width); scaledRect.height = SSHIFT(scaleshift * scaledRect.height); #undef SSHIFT - float sarea = (scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y); + float sarea = static_cast((scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y)); // compensation areas rounding return (sarea == 0.0f)? threshold : (threshold * scaling[idx] * sarea); @@ -197,7 +197,7 @@ struct ChannelStorage int c = ptr[area.height * step + area.width]; int d = ptr[area.height * step + area.x]; - return (a - b + c - d); + return static_cast(a - b + c - d); } }; @@ -307,8 +307,8 @@ struct cv::SCascade::Fields float scale = minScale; for (int sc = 0; sc < scales; ++sc) { - int width = std::max(0.0f, frameSize.width - (origObjWidth * scale)); - int height = std::max(0.0f, frameSize.height - (origObjHeight * scale)); + int width = static_cast(std::max(0.0f, frameSize.width - (origObjWidth * scale))); + int height = static_cast(std::max(0.0f, frameSize.height - (origObjHeight * scale))); float logScale = log(scale); octIt_t fit = fitOctave(logScale); @@ -462,7 +462,7 @@ void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vect CV_Assert(image.type() == CV_8UC3); Fields& fld = *fields; - fld.calcLevels(image.size(),(float) minScale, (float)maxScale, (float)scales); + fld.calcLevels(image.size(),(float) minScale, (float)maxScale, scales); objects.clear(); From bd9ca48fabd2bc03be2cd3aba6c635d830fb5b0f Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 13 Nov 2012 02:49:35 +0400 Subject: [PATCH 55/56] export to python/java --- modules/objdetect/doc/soft_cascade.rst | 11 ++- .../include/opencv2/objdetect/objdetect.hpp | 13 ++-- modules/objdetect/src/softcascade.cpp | 23 +++++++ modules/objdetect/test/test_softcascade.cpp | 67 +++++-------------- 4 files changed, 57 insertions(+), 57 deletions(-) diff --git a/modules/objdetect/doc/soft_cascade.rst b/modules/objdetect/doc/soft_cascade.rst index 0ba7566012..ac537b3a81 100644 --- a/modules/objdetect/doc/soft_cascade.rst +++ b/modules/objdetect/doc/soft_cascade.rst @@ -39,6 +39,7 @@ Implementation of soft (stageless) cascaded detector. :: cv::AlgorithmInfo* info() const; virtual bool load(const FileNode& fn); virtual void detect(InputArray image, InputArray rois, std::vector& objects) const; + virtual void detect(InputArray image, InputArray rois, OutputArray rects, OutputArray confs) const; }; @@ -80,10 +81,16 @@ SCascade::detect -------------------------- Apply cascade to an input frame and return the vector of Decection objcts. -.. ocv:function:: bool SCascade::detect(InputArray image, InputArray rois, std::vector& objects) const +.. ocv:function:: void SCascade::detect(InputArray image, InputArray rois, std::vector& objects) const + +.. ocv:function:: void SCascade::detect(InputArray image, InputArray rois, OutputArray rects, OutputArray confs) const :param image: a frame on which detector will be applied. :param rois: a vector of regions of interest. Only the objects that fall into one of the regions will be returned. - :param objects: an output array of Detections. \ No newline at end of file + :param objects: an output array of Detections. + + :param rects: an output array of bounding rectangles for detected objects. + + :param confs: an output array of confidence for detected objects. i-th bounding rectangle corresponds i-th configence. \ No newline at end of file diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 98c4f0a79e..a78bef56f1 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -490,7 +490,7 @@ protected: // Implementation of soft (stageless) cascaded detector. -class CV_EXPORTS SCascade : public Algorithm +class CV_EXPORTS_W SCascade : public Algorithm { public: @@ -539,24 +539,27 @@ public: // Param minScale is a maximum scale relative to the original size of the image on which cascade will be applyed. // Param scales is a number of scales from minScale to maxScale. // Param rejfactor is used for NMS. - SCascade(const double minScale = 0.4, const double maxScale = 5., const int scales = 55, const int rejfactor = 1); + CV_WRAP SCascade(const double minScale = 0.4, const double maxScale = 5., const int scales = 55, const int rejfactor = 1); - virtual ~SCascade(); + CV_WRAP virtual ~SCascade(); cv::AlgorithmInfo* info() const; // Load cascade from FileNode. // Param fn is a root node for cascade. Should be . - virtual bool load(const FileNode& fn); + CV_WRAP virtual bool load(const FileNode& fn); // Load cascade config. - virtual void read(const FileNode& fn); + CV_WRAP virtual void read(const FileNode& fn); // Return the vector of Decection objcts. // Param image is a frame on which detector will be applied. // Param rois is a vector of regions of interest. Only the objects that fall into one of the regions will be returned. // Param objects is an output array of Detections virtual void detect(InputArray image, InputArray rois, std::vector& objects) const; + // Param rects is an output array of bounding rectangles for detected objects. + // Param confs is an output array of confidence for detected objects. i-th bounding rectangle corresponds i-th configence. + CV_WRAP virtual void detect(InputArray image, InputArray rois, OutputArray rects, OutputArray confs) const; private: void detectNoRoi(const Mat& image, std::vector& objects) const; diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index bfda83426a..e3bcdfe67a 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -500,4 +500,27 @@ void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vect } } } +} + +void cv::SCascade::detect(InputArray _image, InputArray _rois, OutputArray _rects, OutputArray _confs) const +{ + std::vector objects; + detect( _image, _rois, objects); + + _rects.create(1, objects.size(), CV_32SC4); + cv::Mat_ rects = (cv::Mat_)_rects.getMat(); + cv::Rect* rectPtr = rects.ptr(0); + + _confs.create(1, objects.size(), CV_32F); + cv::Mat confs = _confs.getMat(); + float* confPtr = rects.ptr(0); + + typedef std::vector::const_iterator IDet; + + int i = 0; + for (IDet it = objects.begin(); it != objects.end(); ++it, ++i) + { + rectPtr[i] = (*it).bb; + confPtr[i] = (*it).confidence; + } } \ No newline at end of file diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index d33d5892bf..b5632400a5 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -66,34 +66,26 @@ TEST(SCascade, detect) std::vector objects; cascade.detect(colored, cv::noArray(), objects); - - // cv::Mat out = colored.clone(); - // int level = 0, total = 0; - // int levelWidth = objects[0].bb.width; - - // for(int i = 0 ; i < (int)objects.size(); ++i) - // { - // if (objects[i].bb.width != levelWidth) - // { - // std::cout << "Level: " << level << " total " << total << std::endl; - // cv::imshow("out", out); - // cv::waitKey(0); - // out = colored.clone(); - // levelWidth = objects[i].bb.width; - // total = 0; - // level++; - // } - // cv::rectangle(out, objects[i].bb, cv::Scalar(255, 0, 0, 255), 1); - // std::cout << "detection: " << objects[i].bb.x - // << " " << objects[i].bb.y - // << " " << objects[i].bb.width - // << " " << objects[i].bb.height << std::endl; - // total++; - // } - // std::cout << "detected: " << (int)objects.size() << std::endl; ASSERT_EQ((int)objects.size(), 3498); } +TEST(SCascade, detectSeparate) +{ + typedef cv::SCascade::Detection Detection; + std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml"; + cv::SCascade cascade; + cv::FileStorage fs(xml, cv::FileStorage::READ); + ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode())); + + cv::Mat colored = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png"); + ASSERT_FALSE(colored.empty()); + + cv::Mat rects, confs; + + cascade.detect(colored, cv::noArray(), rects, confs); + ASSERT_EQ(confs.cols, 3498); +} + TEST(SCascade, detectRoi) { typedef cv::SCascade::Detection Detection; @@ -110,31 +102,6 @@ TEST(SCascade, detectRoi) rois.push_back(cv::Rect(0, 0, 640, 480)); cascade.detect(colored, rois, objects); - - // cv::Mat out = colored.clone(); - // int level = 0, total = 0; - // int levelWidth = objects[0].bb.width; - - // for(int i = 0 ; i < (int)objects.size(); ++i) - // { - // if (objects[i].bb.width != levelWidth) - // { - // std::cout << "Level: " << level << " total " << total << std::endl; - // cv::imshow("out", out); - // cv::waitKey(0); - // out = colored.clone(); - // levelWidth = objects[i].bb.width; - // total = 0; - // level++; - // } - // cv::rectangle(out, objects[i].bb, cv::Scalar(255, 0, 0, 255), 1); - // std::cout << "detection: " << objects[i].bb.x - // << " " << objects[i].bb.y - // << " " << objects[i].bb.width - // << " " << objects[i].bb.height << std::endl; - // total++; - // } - // std::cout << "detected: " << (int)objects.size() << std::endl; ASSERT_EQ((int)objects.size(), 3498); } From 1edab12068fe04c87fee7cfbc976c0bbe0bfad3f Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Tue, 13 Nov 2012 23:08:19 +0400 Subject: [PATCH 56/56] fix for negative confidence --- modules/objdetect/src/softcascade.cpp | 5 +++-- modules/objdetect/test/test_softcascade.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/objdetect/src/softcascade.cpp b/modules/objdetect/src/softcascade.cpp index e3bcdfe67a..8f37c1be18 100644 --- a/modules/objdetect/src/softcascade.cpp +++ b/modules/objdetect/src/softcascade.cpp @@ -232,7 +232,7 @@ struct cv::SCascade::Fields float detectionScore = 0.f; const Octave& octave = *(level.octave); - int stBegin = octave.index * octave.stages, stEnd = stBegin + 1024;//octave.stages; + int stBegin = octave.index * octave.stages, stEnd = stBegin + octave.stages; int st = stBegin; for(; st < stEnd; ++st) @@ -270,7 +270,8 @@ struct cv::SCascade::Fields if (detectionScore <= stage.threshold) return; } - level.addDetection(dx, dy, detectionScore, detections); + if (detectionScore > 0) + level.addDetection(dx, dy, detectionScore, detections); } octIt_t fitOctave(const float& logFactor) diff --git a/modules/objdetect/test/test_softcascade.cpp b/modules/objdetect/test/test_softcascade.cpp index b5632400a5..f9141c2a9a 100644 --- a/modules/objdetect/test/test_softcascade.cpp +++ b/modules/objdetect/test/test_softcascade.cpp @@ -66,7 +66,7 @@ TEST(SCascade, detect) std::vector objects; cascade.detect(colored, cv::noArray(), objects); - ASSERT_EQ((int)objects.size(), 3498); + ASSERT_EQ(1459, (int)objects.size()); } TEST(SCascade, detectSeparate) @@ -83,7 +83,7 @@ TEST(SCascade, detectSeparate) cv::Mat rects, confs; cascade.detect(colored, cv::noArray(), rects, confs); - ASSERT_EQ(confs.cols, 3498); + ASSERT_EQ(1459, confs.cols); } TEST(SCascade, detectRoi) @@ -102,7 +102,7 @@ TEST(SCascade, detectRoi) rois.push_back(cv::Rect(0, 0, 640, 480)); cascade.detect(colored, rois, objects); - ASSERT_EQ((int)objects.size(), 3498); + ASSERT_EQ(1459, (int)objects.size()); } TEST(SCascade, detectNoRoi) @@ -121,5 +121,5 @@ TEST(SCascade, detectNoRoi) cascade.detect(colored, rois, objects); - ASSERT_EQ((int)objects.size(), 0); + ASSERT_EQ(0, (int)objects.size()); } \ No newline at end of file