From 267d140bfeb43b1c47734e554c37c0b1db81787f Mon Sep 17 00:00:00 2001
From: "marina.kolpakova" <marina.kolpakova@itseez.com>
Date: Thu, 20 Sep 2012 16:22:10 +0400
Subject: [PATCH] soft cascade: gpu representation

---
 modules/gpu/include/opencv2/gpu/gpu.hpp |   9 +-
 modules/gpu/src/cuda/isf-sc.cu          |  43 +++++
 modules/gpu/src/icf.hpp                 | 118 ++++++++++++
 modules/gpu/src/softcascade.cpp         | 236 +++++++++++++++++++++++-
 modules/gpu/test/test_softcascade.cpp   |  73 ++++++++
 5 files changed, 473 insertions(+), 6 deletions(-)
 create mode 100644 modules/gpu/src/cuda/isf-sc.cu
 create mode 100644 modules/gpu/src/icf.hpp
 create mode 100644 modules/gpu/test/test_softcascade.cpp

diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp
index 4a2d88aa0..61f6006c5 100644
--- a/modules/gpu/include/opencv2/gpu/gpu.hpp
+++ b/modules/gpu/include/opencv2/gpu/gpu.hpp
@@ -1554,9 +1554,14 @@ public:
 
     virtual ~SoftCascade();
 
-    //! return vector of bounding boxes. Each box contains one detected object
+    //! detect specific objects on in the input frame for all scales computed flom minScale and maxscale values
+    //! Param image is input frame for detector. Cascade will be applied to it.
+    //! Param rois is a mask
+    //! Param objects 4-channel matrix thet contain detected rectangles
+    //! Param rejectfactor used for final object box computing
+    //! Param stream
     virtual void detectMultiScale(const GpuMat& image, const GpuMat& rois, GpuMat& objects,
-    int rejectfactor = 1, Stream stream = Stream::Null()); // ToDo store objects in GPU mem
+    int rejectfactor = 1, Stream stream = Stream::Null());
 
 protected:
     enum { BOOST = 0 };
diff --git a/modules/gpu/src/cuda/isf-sc.cu b/modules/gpu/src/cuda/isf-sc.cu
new file mode 100644
index 000000000..f36f86f96
--- /dev/null
+++ b/modules/gpu/src/cuda/isf-sc.cu
@@ -0,0 +1,43 @@
+/*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 <icf.hpp>
\ No newline at end of file
diff --git a/modules/gpu/src/icf.hpp b/modules/gpu/src/icf.hpp
new file mode 100644
index 000000000..110890232
--- /dev/null
+++ b/modules/gpu/src/icf.hpp
@@ -0,0 +1,118 @@
+/*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*/
+
+#ifndef __OPENCV_ICF_HPP__
+#define __OPENCV_ICF_HPP__
+
+#if defined __CUDACC__
+# define __hd__ __host__ __device__ __forceinline__
+#else
+# define __hd__
+#endif
+
+
+namespace icf {
+
+    struct Cascade
+    {
+
+    };
+
+    struct ChannelStorage
+    {
+
+    };
+
+    struct __align__(16) Octave
+    {
+        ushort index;
+        ushort stages;
+        ushort shrinkage;
+        ushort2 size;
+        float scale;
+
+        Octave(const ushort i, const ushort s, const ushort sh, const ushort2 sz, const float sc)
+        : index(i), stages(s), shrinkage(sh), size(sz), scale(sc) {}
+    };
+
+    struct __align__(8) Node
+    {
+        int feature;
+        float threshold;
+
+        Node(const int f, const float t) : feature(f), threshold(t) {}
+    };
+
+    struct __align__(8) Feature
+    {
+        int channel;
+        uchar4 rect;
+
+        Feature(const int c, const uchar4 r) : channel(c), rect(r) {}
+    };
+
+    struct __align__(8) Level //is actually 24 bytes
+    {
+        int octave;
+
+        // float origScale; //not actually used
+        float relScale;
+        float shrScale;   // used for marking detection
+        float scaling[2]; // calculated according to Dollal paper
+
+        // for 640x480 we can not get overflow
+        uchar2 workRect;
+        uchar2 objSize;
+
+        Level(int idx, const Octave& oct, const float scale, const int w, const int h)
+        :  octave(idx), relScale(scale / oct.scale), shrScale (relScale / (float)oct.shrinkage)
+        {
+            workRect.x = round(w / (float)oct.shrinkage);
+            workRect.y = round(h / (float)oct.shrinkage);
+
+            objSize.x  = round(oct.size.x * relScale);
+            objSize.y  = round(oct.size.y * relScale);
+        }
+    };
+}
+
+#endif
\ No newline at end of file
diff --git a/modules/gpu/src/softcascade.cpp b/modules/gpu/src/softcascade.cpp
index 509e3f501..04b68539c 100644
--- a/modules/gpu/src/softcascade.cpp
+++ b/modules/gpu/src/softcascade.cpp
@@ -56,12 +56,242 @@ void cv::gpu::SoftCascade::detectMultiScale(const GpuMat&, const GpuMat&, GpuMat
 
 #else
 
+#include <icf.hpp>
+
 struct cv::gpu::SoftCascade::Filds
 {
-    bool fill(const FileNode &root, const float mins, const float maxs){return true;}
-    void calcLevels(int frameW, int frameH, int scales) {}
+    // scales range
+    float minScale;
+    float maxScale;
+
+    int origObjWidth;
+    int origObjHeight;
+
+    GpuMat octaves;
+    GpuMat stages;
+    GpuMat nodes;
+    GpuMat leaves;
+    GpuMat features;
+
+    std::vector<float> scales;
+
+    icf::Cascade cascade;
+
+    bool fill(const FileNode &root, const float mins, const float maxs);
+
+private:
+    void calcLevels(const std::vector<icf::Octave>& octs,
+                                                    int frameW, int frameH, int nscales);
+
+    typedef std::vector<icf::Octave>::const_iterator  octIt_t;
+    int fitOctave(const std::vector<icf::Octave>& octs, const float& logFactor)
+    {
+        float minAbsLog = FLT_MAX;
+        int res =  0;
+        for (int oct = 0; oct < (int)octs.size(); ++oct)
+        {
+            const icf::Octave& octave =octs[oct];
+            float logOctave = ::log(octave.scale);
+            float logAbsScale = ::fabs(logFactor - logOctave);
+
+            if(logAbsScale < minAbsLog)
+            {
+                res = oct;
+                minAbsLog = logAbsScale;
+            }
+        }
+        return res;
+    }
 };
 
+inline bool cv::gpu::SoftCascade::Filds::fill(const FileNode &root, const float mins, const float maxs)
+{
+    minScale = mins;
+    maxScale = maxs;
+
+    // cascade properties
+    static const char *const SC_STAGE_TYPE          = "stageType";
+    static const char *const SC_BOOST               = "BOOST";
+
+    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";
+
+    static const char *const SC_OCT_SCALE           = "scale";
+    static const char *const SC_OCT_STAGES          = "stageNum";
+    static const char *const SC_OCT_SHRINKAGE       = "shrinkingFactor";
+
+    static const char *const SC_STAGE_THRESHOLD     = "stageThreshold";
+
+    static const char * const SC_F_CHANNEL          = "channel";
+    static const char * const SC_F_RECT             = "rect";
+
+    // only Ada 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);
+
+    origObjWidth = (int)root[SC_ORIG_W];
+    CV_Assert(origObjWidth == SoftCascade::ORIG_OBJECT_WIDTH);
+
+    origObjHeight = (int)root[SC_ORIG_H];
+    CV_Assert(origObjHeight == SoftCascade::ORIG_OBJECT_HEIGHT);
+
+    FileNode fn = root[SC_OCTAVES];
+        if (fn.empty()) return false;
+
+    std::vector<icf::Octave>  voctaves;
+    std::vector<float>        vstages;
+    std::vector<icf::Node>    vnodes;
+    std::vector<float>        vleaves;
+    std::vector<icf::Feature> vfeatures;
+    scales.clear();
+
+    // std::vector<Level> levels;
+
+    FileNodeIterator it = fn.begin(), it_end = fn.end();
+    int feature_offset = 0;
+    ushort octIndex = 0;
+
+    for (; it != it_end; ++it)
+    {
+        FileNode fns = *it;
+        float scale = (float)fns[SC_OCT_SCALE];
+        scales.push_back(scale);
+        ushort nstages = saturate_cast<ushort>((int)fn[SC_OCT_STAGES]);
+        ushort2 size;
+        size.x = cvRound(SoftCascade::ORIG_OBJECT_WIDTH * scale);
+        size.y = cvRound(SoftCascade::ORIG_OBJECT_HEIGHT * scale);
+        ushort shrinkage = saturate_cast<ushort>((int)fn[SC_OCT_SHRINKAGE]);
+
+        icf::Octave octave(octIndex, nstages, shrinkage, size, scale);
+        CV_Assert(octave.stages > 0);
+        voctaves.push_back(octave);
+
+        FileNode ffs = fns[SC_FEATURES];
+        if (ffs.empty()) return false;
+
+        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;
+            vstages.push_back((float)fn[SC_STAGE_THRESHOLD]);
+
+            fns = fns[SC_WEEK];
+            FileNodeIterator ftr = fns.begin(), ft_end = fns.end();
+            for (; ftr != ft_end; ++ftr)
+            {
+                fns = (*ftr)[SC_INTERNAL];
+                FileNodeIterator inIt = fns.begin(), inIt_end = fns.end();
+                for (; inIt != inIt_end;)
+                {
+                    int feature = (int)(*(inIt +=2)++) + feature_offset;
+                    vnodes.push_back(icf::Node(feature, (float)(*(inIt++))));
+                }
+
+                fns = (*ftr)[SC_LEAF];
+                inIt = fns.begin(), inIt_end = fns.end();
+                for (; inIt != inIt_end; ++inIt)
+                    vleaves.push_back((float)(*inIt));
+            }
+        }
+
+        st = ffs.begin(), st_end = ffs.end();
+        for (; st != st_end; ++st )
+        {
+            cv::FileNode rn = (*st)[SC_F_RECT];
+            cv::FileNodeIterator r_it = rn.begin();
+            uchar4 rect;
+            rect.x = saturate_cast<uchar>((int)*(r_it++));
+            rect.y = saturate_cast<uchar>((int)*(r_it++));
+            rect.z = saturate_cast<uchar>((int)*(r_it++));
+            rect.w = saturate_cast<uchar>((int)*(r_it++));
+            vfeatures.push_back(icf::Feature((int)(*st)[SC_F_CHANNEL], rect));
+        }
+
+        feature_offset += octave.stages * 3;
+        ++octIndex;
+    }
+
+    // upload in gpu memory
+    octaves.upload(cv::Mat(1, voctaves.size() * sizeof(icf::Octave), CV_8UC1, (uchar*)&(voctaves[0]) ));
+    CV_Assert(!octaves.empty());
+
+    stages.upload(cv::Mat(vstages).reshape(1,1));
+    CV_Assert(!stages.empty());
+
+    nodes.upload(cv::Mat(1, vnodes.size() * sizeof(icf::Node), CV_8UC1, (uchar*)&(vnodes[0]) ));
+    CV_Assert(!nodes.empty());
+
+    leaves.upload(cv::Mat(vleaves).reshape(1,1));
+    CV_Assert(!leaves.empty());
+
+    features.upload(cv::Mat(1, vfeatures.size() * sizeof(icf::Feature), CV_8UC1, (uchar*)&(vfeatures[0]) ));
+    CV_Assert(!features.empty());
+
+    // compute levels
+    calcLevels(voctaves, (int)SoftCascade::FRAME_WIDTH, (int)SoftCascade::FRAME_HEIGHT, (int)SoftCascade::TOTAL_SCALES);
+
+    return true;
+}
+
+inline void cv::gpu::SoftCascade::Filds::calcLevels(const std::vector<icf::Octave>& octs,
+                                                    int frameW, int frameH, int nscales)
+{
+    CV_Assert(nscales > 1);
+
+    std::vector<icf::Level> levels;
+    float logFactor = (::log(maxScale) - ::log(minScale)) / (nscales -1);
+
+    float scale = minScale;
+    for (int sc = 0; sc < nscales; ++sc)
+    {
+        int width  = ::std::max(0.0f, frameW - (origObjWidth  * scale));
+        int height = ::std::max(0.0f, frameH - (origObjHeight * scale));
+
+        float logScale = ::log(scale);
+        int fit = fitOctave(octs, logScale);
+
+        icf::Level level(fit, octs[fit], scale, 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 " << 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;
+    }
+}
+
 cv::gpu::SoftCascade::SoftCascade() : filds(0) {}
 
 cv::gpu::SoftCascade::SoftCascade( const string& filename, const float minScale, const float maxScale) : filds(0)
@@ -86,8 +316,6 @@ bool cv::gpu::SoftCascade::load( const string& filename, const float minScale, c
     filds = new Filds;
     Filds& flds = *filds;
     if (!flds.fill(fs.getFirstTopLevelNode(), minScale, maxScale)) return false;
-    flds.calcLevels(FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SCALES);
-
     return true;
 }
 
diff --git a/modules/gpu/test/test_softcascade.cpp b/modules/gpu/test/test_softcascade.cpp
new file mode 100644
index 000000000..821a2b140
--- /dev/null
+++ b/modules/gpu/test/test_softcascade.cpp
@@ -0,0 +1,73 @@
+/*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 <test_precomp.hpp>
+
+#ifdef HAVE_CUDA
+
+using cv::gpu::GpuMat;
+
+TEST(SoftCascade, readCascade)
+{
+    std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/icf-template.xml";
+    cv::gpu::SoftCascade cascade;
+    ASSERT_TRUE(cascade.load(xml));
+
+}
+
+TEST(SoftCascade, detect)
+{
+    std::string xml =  cvtest::TS::ptr()->get_data_path() + "cascadeandhog/sc_cvpr_2012_to_opencv.xml";
+    cv::gpu::SoftCascade cascade;
+    ASSERT_TRUE(cascade.load(xml));
+
+    cv::Mat coloredCpu = cv::imread(cvtest::TS::ptr()->get_data_path() + "cascadeandhog/bahnhof/image_00000000_0.png");
+    ASSERT_FALSE(coloredCpu.empty());
+    GpuMat colored(coloredCpu), objectBoxes, rois;
+
+    // ASSERT_NO_THROW(
+    // {
+        cascade.detectMultiScale(colored, rois, objectBoxes);
+    // });
+}
+
+#endif
\ No newline at end of file