Merge remote-tracking branch 'cuda-geek/soft-cascade-cpu'

This commit is contained in:
marina.kolpakova 2012-11-13 23:37:44 +04:00
commit be40bd5c74
10 changed files with 1016 additions and 8 deletions

View File

@ -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)

View File

@ -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)

View File

@ -8,4 +8,5 @@ objdetect. Object Detection
:maxdepth: 2
cascade_classification
soft_cascade
latent_svm

View File

@ -0,0 +1,96 @@
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<Detection>& objects) const;
virtual void detect(InputArray image, InputArray rois, OutputArray rects, OutputArray confs) 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:: void SCascade::detect(InputArray image, InputArray rois, std::vector<Detection>& 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.
: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.

View File

@ -489,6 +489,93 @@ protected:
};
// Implementation of soft (stageless) cascaded detector.
class CV_EXPORTS_W SCascade : public Algorithm
{
public:
// Representation of detectors result.
struct CV_EXPORTS Detection
{
// Default object type.
enum {PEDESTRIAN = 1};
// 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) {}
cv::Rect bb;
float confidence;
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<cv::Mat>& 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<cv::Mat>& 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.
// Param scales is a number of scales from minScale to maxScale.
// Param rejfactor is used for NMS.
CV_WRAP SCascade(const double minScale = 0.4, const double maxScale = 5., const int scales = 55, const int rejfactor = 1);
CV_WRAP virtual ~SCascade();
cv::AlgorithmInfo* info() const;
// Load cascade from FileNode.
// Param fn is a root node for cascade. Should be <cascade>.
CV_WRAP virtual bool load(const FileNode& fn);
// Load cascade config.
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<Detection>& 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<Detection>& objects) const;
struct Fields;
Fields* fields;
double minScale;
double maxScale;
int scales;
int rejfactor;
};
CV_EXPORTS bool initModule_objdetect(void);
//////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector //////////////
// struct for detection region of interest (ROI)

View File

@ -52,3 +52,45 @@ 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<std::string, std::string> fixture;
typedef perf::TestBaseWithParam<fixture> detect;
namespace {
typedef cv::SCascade::Detection detection_t;
void extractRacts(std::vector<detection_t> objectBoxes, vector<Rect> rects)
{
rects.clear();
for (int i = 0; i < (int)objectBoxes.size(); ++i)
rects.push_back(objectBoxes[i].bb);
}
}
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::SCascade::Detection Detection;
cv::Mat colored = imread(getDataPath(get<1>(GetParam())));
ASSERT_FALSE(colored.empty());
cv::SCascade cascade;
cv::FileStorage fs(getDataPath(get<0>(GetParam())), cv::FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode()));
std::vector<detection_t> objectBoxes;
cascade.detect(colored, cv::noArray(), objectBoxes);
TEST_CYCLE()
{
cascade.detect(colored, cv::noArray(), objectBoxes);
}
vector<Rect> rects;
extractRacts(objectBoxes, rects);
std::sort(rects.begin(), rects.end(), comparators::RectLess());
SANITY_CHECK(rects);
}

View File

@ -0,0 +1,113 @@
/*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 <precomp.hpp>
cv::SCascade::Channels::Channels(int shr) : shrinkage(shr) {}
void cv::SCascade::Channels::appendHogBins(const cv::Mat& gray, std::vector<cv::Mat>& integrals, int bins) const
{
CV_Assert(gray.type() == CV_8UC1);
int h = gray.rows;
int w = gray.cols;
CV_Assert(!(w % shrinkage) && !(h % shrinkage));
cv::Mat df_dx, df_dy, mag, angle;
cv::Sobel(gray, df_dx, CV_32F, 1, 0);
cv::Sobel(gray, df_dy, CV_32F, 0, 1);
cv::cartToPolar(df_dx, df_dy, mag, angle, true);
mag *= (1.f / (8 * sqrt(2.f)));
cv::Mat nmag;
mag.convertTo(nmag, CV_8UC1);
angle *= bins/360.f;
std::vector<cv::Mat> 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<uchar>(y);
float* ang = angle.ptr<float>(y);
for (int x = 0; x < w; ++x)
{
hist[ (int)ang[x] ].ptr<uchar>(y)[x] = magnitude[x];
}
}
for(int i = 0; i < bins; ++i)
{
cv::Mat shrunk, sum;
cv::resize(hist[i], shrunk, cv::Size(), 1.0 / shrinkage, 1.0 / shrinkage, CV_INTER_AREA);
cv::integral(shrunk, sum, cv::noArray(), CV_32S);
integrals.push_back(sum);
}
cv::Mat shrMag;
cv::resize(nmag, shrMag, cv::Size(), 1.0 / shrinkage, 1.0 / shrinkage, CV_INTER_AREA);
cv::integral(shrMag, mag, cv::noArray(), CV_32S);
integrals.push_back(mag);
}
void cv::SCascade::Channels::appendLuvBins(const cv::Mat& frame, std::vector<cv::Mat>& integrals) const
{
CV_Assert(frame.type() == CV_8UC3);
CV_Assert(!(frame.cols % shrinkage) && !(frame.rows % shrinkage));
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<cv::Mat> splited;
split(shrunk, splited);
for (size_t i = 0; i < splited.size(); ++i)
{
cv::Mat sum;
cv::integral(splited[i], sum, cv::noArray(), CV_32S);
integrals.push_back(sum);
}
}

View File

@ -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 <precomp.hpp>
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<Algorithm> sc = createSCascade();
return sc->info() != 0;
}
}

View File

@ -0,0 +1,527 @@
/*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 <precomp.hpp>
namespace {
struct Octave
{
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]) {}
int index;
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;
};
struct Weak
{
Weak(){}
Weak(const cv::FileNode& fn) : threshold((float)fn[SC_STAGE_THRESHOLD]){}
float threshold;
static const char *const SC_STAGE_THRESHOLD;
};
struct Node
{
Node(){}
Node(const int offset, cv::FileNodeIterator& fIt)
: feature((int)(*(fIt +=2)++) + offset), threshold((float)(*(fIt++))){}
int feature;
float threshold;
};
struct Feature
{
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));
// 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 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
{
const Octave* octave;
float origScale;
float relScale;
int scaleshift;
cv::Size workRect;
cv::Size objSize;
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] = ((relScale >= 1.f)? 1.f : (0.89f * pow(relScale, 1.099f / log(2.f)))) / (relScale * relScale);
scaling[1] = 1.f;
scaleshift = static_cast<int>(relScale * (1 << 16));
}
void addDetection(const int x, const int y, float confidence, std::vector<Detection>& detections) const
{
int shrinkage = (*octave).shrinkage;
cv::Rect rect(cvRound(x * shrinkage), cvRound(y * shrinkage), objSize.width, objSize.height);
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 = 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 = static_cast<float>((scaledRect.width - scaledRect.x) * (scaledRect.height - scaledRect.y));
// compensation areas rounding
return (sarea == 0.0f)? threshold : (threshold * scaling[idx] * sarea);
}
};
struct ChannelStorage
{
std::vector<cv::Mat> hog;
int shrinkage;
int offset;
int step;
enum {HOG_BINS = 6, HOG_LUV_BINS = 10};
ChannelStorage(const cv::Mat& colored, int shr) : shrinkage(shr)
{
hog.clear();
hog.reserve(10);
cv::SCascade::Channels ints(shr);
// convert to grey
cv::Mat grey;
cv::cvtColor(colored, grey, CV_BGR2GRAY);
ints.appendHogBins(grey, hog, 6);
ints.appendLuvBins(colored, hog);
step = hog[0].cols;
}
float get(const int channel, const cv::Rect& area) const
{
// CV_Assert(channel < HOG_LUV_BINS);
const cv::Mat& m = hog[channel];
int *ptr = ((int*)(m.data)) + offset;
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 static_cast<float>(a - b + c - d);
}
};
}
struct cv::SCascade::Fields
{
float minScale;
float maxScale;
int scales;
int origObjWidth;
int origObjHeight;
int shrinkage;
std::vector<Octave> octaves;
std::vector<Weak> stages;
std::vector<Node> nodes;
std::vector<float> leaves;
std::vector<Feature> features;
std::vector<Level> levels;
cv::Size frameSize;
typedef std::vector<Octave>::iterator octIt_t;
void detectAt(const int dx, const int dy, const Level& level, const ChannelStorage& storage,
std::vector<Detection>& detections) const
{
float detectionScore = 0.f;
const Octave& octave = *(level.octave);
int stBegin = octave.index * octave.stages, stEnd = stBegin + octave.stages;
int st = stBegin;
for(; st < stEnd; ++st)
{
const Weak& stage = stages[st];
{
int nId = st * 3;
// work with root node
const Node& node = nodes[nId];
const Feature& feature = features[node.feature];
cv::Rect scaledRect(feature.rect);
float threshold = level.rescale(scaledRect, node.threshold,(int)(feature.channel > 6)) * feature.rarea;
float sum = storage.get(feature.channel, scaledRect);
int next = (sum >= threshold)? 2 : 1;
// leaves
const Node& leaf = nodes[nId + next];
const Feature& fLeaf = features[leaf.feature];
scaledRect = fLeaf.rect;
threshold = level.rescale(scaledRect, leaf.threshold, (int)(fLeaf.channel > 6)) * fLeaf.rarea;
sum = storage.get(fLeaf.channel, scaledRect);
int lShift = (next - 1) * 2 + ((sum >= threshold) ? 1 : 0);
float impact = leaves[(st * 4) + lShift];
detectionScore += impact;
}
if (detectionScore <= stage.threshold) return;
}
if (detectionScore > 0)
level.addDetection(dx, dy, detectionScore, detections);
}
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(const cv::Size& curr, float mins, float maxs, int total)
{
if (frameSize == curr && maxs == maxScale && mins == minScale && total == scales) return;
frameSize = curr;
maxScale = maxs; minScale = mins; scales = total;
CV_Assert(scales > 1);
levels.clear();
float logFactor = (log(maxScale) - log(minScale)) / (scales -1);
float scale = minScale;
for (int sc = 0; sc < scales; ++sc)
{
int width = static_cast<int>(std::max(0.0f, frameSize.width - (origObjWidth * scale)));
int height = static_cast<int>(std::max(0.0f, frameSize.height - (origObjHeight * scale)));
float logScale = log(scale);
octIt_t fit = fitOctave(logScale);
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));
}
}
bool fill(const FileNode &root)
{
// 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";
// 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];
origObjHeight = (int)root[SC_ORIG_H];
// 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();
int feature_offset = 0;
int octIndex = 0;
for (; it != it_end; ++it)
{
FileNode fns = *it;
Octave octave(octIndex, cv::Size(origObjWidth, origObjHeight), fns);
CV_Assert(octave.stages > 0);
octaves.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;
stages.push_back(Weak(fns));
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;)
nodes.push_back(Node(feature_offset, 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));
feature_offset += octave.stages * 3;
++octIndex;
}
shrinkage = octaves[0].shrinkage;
return true;
}
};
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;}
void cv::SCascade::read(const FileNode& fn)
{
Algorithm::read(fn);
}
bool cv::SCascade::load(const FileNode& fn)
{
if (fields) delete fields;
fields = new Fields;
return fields->fill(fn);
}
void cv::SCascade::detectNoRoi(const cv::Mat& image, std::vector<Detection>& objects) const
{
Fields& fld = *fields;
// create integrals
ChannelStorage storage(image, fld.shrinkage);
typedef std::vector<Level>::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)
{
storage.offset = dy * storage.step + dx;
fld.detectAt(dx, dy, level, storage, objects);
}
}
}
}
void cv::SCascade::detect(cv::InputArray _image, cv::InputArray _rois, std::vector<Detection>& objects) const
{
// only color images are supperted
cv::Mat image = _image.getMat();
CV_Assert(image.type() == CV_8UC3);
Fields& fld = *fields;
fld.calcLevels(image.size(),(float) minScale, (float)maxScale, scales);
objects.clear();
if (_rois.kind() == cv::_InputArray::NONE)
return detectNoRoi(image, objects);
int shr = fld.shrinkage;
cv::Mat roi = _rois.getMat();
cv::Mat mask(image.rows / shr, image.cols / shr, CV_8UC1);
mask.setTo(cv::Scalar::all(0));
cv::Rect* r = roi.ptr<cv::Rect>(0);
for (int i = 0; i < (int)roi.cols; ++i)
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, shr);
typedef std::vector<Level>::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<uchar>(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);
}
}
}
}
}
void cv::SCascade::detect(InputArray _image, InputArray _rois, OutputArray _rects, OutputArray _confs) const
{
std::vector<Detection> objects;
detect( _image, _rois, objects);
_rects.create(1, objects.size(), CV_32SC4);
cv::Mat_<cv::Rect> rects = (cv::Mat_<cv::Rect>)_rects.getMat();
cv::Rect* rectPtr = rects.ptr<cv::Rect>(0);
_confs.create(1, objects.size(), CV_32F);
cv::Mat confs = _confs.getMat();
float* confPtr = rects.ptr<float>(0);
typedef std::vector<Detection>::const_iterator IDet;
int i = 0;
for (IDet it = objects.begin(); it != objects.end(); ++it, ++i)
{
rectPtr[i] = (*it).bb;
confPtr[i] = (*it).confidence;
}
}

View File

@ -0,0 +1,125 @@
/*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"
TEST(SCascade, readCascade)
{
std::string xml = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/icf-template.xml";
cv::SCascade cascade;
cv::FileStorage fs(xml, cv::FileStorage::READ);
ASSERT_TRUE(fs.isOpened());
ASSERT_TRUE(cascade.load(fs.getFirstTopLevelNode()));
}
TEST(SCascade, detect)
{
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<Detection> objects;
cascade.detect(colored, cv::noArray(), objects);
ASSERT_EQ(1459, (int)objects.size());
}
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(1459, confs.cols);
}
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<Detection> objects;
std::vector<cv::Rect> rois;
rois.push_back(cv::Rect(0, 0, 640, 480));
cascade.detect(colored, rois, objects);
ASSERT_EQ(1459, (int)objects.size());
}
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<Detection> objects;
std::vector<cv::Rect> rois;
cascade.detect(colored, rois, objects);
ASSERT_EQ(0, (int)objects.size());
}