2010-05-12 01:44:00 +08:00
|
|
|
/*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.
|
|
|
|
//
|
|
|
|
//
|
2013-12-13 01:58:42 +08:00
|
|
|
// License Agreement
|
2010-05-12 01:44:00 +08:00
|
|
|
// For Open Source Computer Vision Library
|
|
|
|
//
|
2013-12-13 01:58:42 +08:00
|
|
|
// Copyright (C) 2008-2013, Itseez Inc., all rights reserved.
|
2010-05-12 01:44:00 +08:00
|
|
|
// 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.
|
|
|
|
//
|
2013-12-13 01:58:42 +08:00
|
|
|
// * The name of Itseez Inc. may not be used to endorse or promote products
|
2010-05-12 01:44:00 +08:00
|
|
|
// 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.
|
2013-12-13 01:58:42 +08:00
|
|
|
// In no event shall the copyright holders or contributors be liable for any direct,
|
2010-05-12 01:44:00 +08:00
|
|
|
// 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"
|
|
|
|
#include <cstdio>
|
|
|
|
|
2011-07-29 22:18:45 +08:00
|
|
|
#include "cascadedetect.hpp"
|
2013-04-12 16:11:11 +08:00
|
|
|
#include "opencv2/objdetect/objdetect_c.h"
|
2013-12-18 00:55:49 +08:00
|
|
|
#include "opencl_kernels.hpp"
|
2011-07-29 22:18:45 +08:00
|
|
|
|
2012-11-24 22:10:29 +08:00
|
|
|
#if defined (LOG_CASCADE_STATISTIC)
|
2012-07-13 23:47:09 +08:00
|
|
|
struct Logger
|
|
|
|
{
|
2012-07-15 12:11:46 +08:00
|
|
|
enum { STADIES_NUM = 20 };
|
2012-07-13 23:47:09 +08:00
|
|
|
|
2012-07-15 12:11:46 +08:00
|
|
|
int gid;
|
|
|
|
cv::Mat mask;
|
|
|
|
cv::Size sz0;
|
|
|
|
int step;
|
2012-07-13 23:47:09 +08:00
|
|
|
|
2012-07-15 12:11:46 +08:00
|
|
|
|
|
|
|
Logger() : gid (0), step(2) {}
|
|
|
|
void setImage(const cv::Mat& image)
|
|
|
|
{
|
2012-07-13 23:47:09 +08:00
|
|
|
if (gid == 0)
|
2012-07-15 12:11:46 +08:00
|
|
|
sz0 = image.size();
|
|
|
|
|
|
|
|
mask.create(image.rows, image.cols * (STADIES_NUM + 1) + STADIES_NUM, CV_8UC1);
|
|
|
|
mask = cv::Scalar(0);
|
|
|
|
cv::Mat roi = mask(cv::Rect(cv::Point(0,0), image.size()));
|
|
|
|
image.copyTo(roi);
|
|
|
|
|
|
|
|
printf("%d) Size = (%d, %d)\n", gid, image.cols, image.rows);
|
|
|
|
|
|
|
|
for(int i = 0; i < STADIES_NUM; ++i)
|
|
|
|
{
|
|
|
|
int x = image.cols + i * (image.cols + 1);
|
|
|
|
cv::line(mask, cv::Point(x, 0), cv::Point(x, mask.rows-1), cv::Scalar(255));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sz0.width/image.cols > 2 && sz0.height/image.rows > 2)
|
|
|
|
step = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setPoint(const cv::Point& p, int passed_stadies)
|
|
|
|
{
|
|
|
|
int cols = mask.cols / (STADIES_NUM + 1);
|
|
|
|
|
|
|
|
passed_stadies = -passed_stadies;
|
|
|
|
passed_stadies = (passed_stadies == -1) ? STADIES_NUM : passed_stadies;
|
|
|
|
|
|
|
|
unsigned char* ptr = mask.ptr<unsigned char>(p.y) + cols + 1 + p.x;
|
|
|
|
for(int i = 0; i < passed_stadies; ++i, ptr += cols + 1)
|
|
|
|
{
|
|
|
|
*ptr = 255;
|
|
|
|
|
|
|
|
if (step == 2)
|
|
|
|
{
|
|
|
|
ptr[1] = 255;
|
|
|
|
ptr[mask.step] = 255;
|
|
|
|
ptr[mask.step + 1] = 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void write()
|
|
|
|
{
|
|
|
|
char buf[4096];
|
|
|
|
sprintf(buf, "%04d.png", gid++);
|
|
|
|
cv::imwrite(buf, mask);
|
|
|
|
}
|
2012-07-13 23:47:09 +08:00
|
|
|
|
|
|
|
} logger;
|
2012-11-24 22:10:29 +08:00
|
|
|
#endif
|
2012-07-13 23:47:09 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
namespace cv
|
|
|
|
{
|
2013-12-17 18:29:30 +08:00
|
|
|
|
|
|
|
template<typename _Tp> void copyVectorToUMat(const std::vector<_Tp>& v, UMat& um)
|
|
|
|
{
|
|
|
|
if(v.empty())
|
|
|
|
um.release();
|
|
|
|
Mat(1, (int)(v.size()*sizeof(v[0])), CV_8U, (void*)&v[0]).copyTo(um);
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps, std::vector<int>* weights, std::vector<double>* levelWeights)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
if( groupThreshold <= 0 || rectList.empty() )
|
|
|
|
{
|
|
|
|
if( weights )
|
|
|
|
{
|
|
|
|
size_t i, sz = rectList.size();
|
|
|
|
weights->resize(sz);
|
|
|
|
for( i = 0; i < sz; i++ )
|
|
|
|
(*weights)[i] = 1;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<int> labels;
|
2010-05-12 01:44:00 +08:00
|
|
|
int nclasses = partition(rectList, labels, SimilarRects(eps));
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Rect> rrects(nclasses);
|
|
|
|
std::vector<int> rweights(nclasses, 0);
|
|
|
|
std::vector<int> rejectLevels(nclasses, 0);
|
|
|
|
std::vector<double> rejectWeights(nclasses, DBL_MIN);
|
2010-05-12 01:44:00 +08:00
|
|
|
int i, j, nlabels = (int)labels.size();
|
|
|
|
for( i = 0; i < nlabels; i++ )
|
|
|
|
{
|
|
|
|
int cls = labels[i];
|
|
|
|
rrects[cls].x += rectList[i].x;
|
|
|
|
rrects[cls].y += rectList[i].y;
|
|
|
|
rrects[cls].width += rectList[i].width;
|
|
|
|
rrects[cls].height += rectList[i].height;
|
|
|
|
rweights[cls]++;
|
|
|
|
}
|
2011-04-22 18:03:05 +08:00
|
|
|
if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
|
2012-06-09 23:00:04 +08:00
|
|
|
{
|
|
|
|
for( i = 0; i < nlabels; i++ )
|
|
|
|
{
|
|
|
|
int cls = labels[i];
|
2011-04-22 18:03:05 +08:00
|
|
|
if( (*weights)[i] > rejectLevels[cls] )
|
|
|
|
{
|
|
|
|
rejectLevels[cls] = (*weights)[i];
|
|
|
|
rejectWeights[cls] = (*levelWeights)[i];
|
|
|
|
}
|
|
|
|
else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
|
|
|
|
rejectWeights[cls] = (*levelWeights)[i];
|
2012-06-09 23:00:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
for( i = 0; i < nclasses; i++ )
|
|
|
|
{
|
|
|
|
Rect r = rrects[i];
|
|
|
|
float s = 1.f/rweights[i];
|
|
|
|
rrects[i] = Rect(saturate_cast<int>(r.x*s),
|
|
|
|
saturate_cast<int>(r.y*s),
|
|
|
|
saturate_cast<int>(r.width*s),
|
|
|
|
saturate_cast<int>(r.height*s));
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
rectList.clear();
|
|
|
|
if( weights )
|
|
|
|
weights->clear();
|
2012-06-09 23:00:04 +08:00
|
|
|
if( levelWeights )
|
|
|
|
levelWeights->clear();
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
for( i = 0; i < nclasses; i++ )
|
|
|
|
{
|
|
|
|
Rect r1 = rrects[i];
|
2013-06-01 08:34:20 +08:00
|
|
|
int n1 = rweights[i];
|
2012-06-09 23:00:04 +08:00
|
|
|
double w1 = rejectWeights[i];
|
2013-06-01 08:34:20 +08:00
|
|
|
int l1 = rejectLevels[i];
|
2013-08-27 17:57:24 +08:00
|
|
|
|
2013-06-01 08:34:20 +08:00
|
|
|
// filter out rectangles which don't have enough similar rectangles
|
2010-05-12 01:44:00 +08:00
|
|
|
if( n1 <= groupThreshold )
|
|
|
|
continue;
|
|
|
|
// filter out small face rectangles inside large rectangles
|
|
|
|
for( j = 0; j < nclasses; j++ )
|
|
|
|
{
|
|
|
|
int n2 = rweights[j];
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
if( j == i || n2 <= groupThreshold )
|
|
|
|
continue;
|
|
|
|
Rect r2 = rrects[j];
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
int dx = saturate_cast<int>( r2.width * eps );
|
|
|
|
int dy = saturate_cast<int>( r2.height * eps );
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
if( i != j &&
|
|
|
|
r1.x >= r2.x - dx &&
|
|
|
|
r1.y >= r2.y - dy &&
|
|
|
|
r1.x + r1.width <= r2.x + r2.width + dx &&
|
|
|
|
r1.y + r1.height <= r2.y + r2.height + dy &&
|
|
|
|
(n2 > std::max(3, n1) || n1 < 3) )
|
|
|
|
break;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
if( j == nclasses )
|
|
|
|
{
|
|
|
|
rectList.push_back(r1);
|
|
|
|
if( weights )
|
2013-06-01 08:34:20 +08:00
|
|
|
weights->push_back(l1);
|
2012-06-09 23:00:04 +08:00
|
|
|
if( levelWeights )
|
|
|
|
levelWeights->push_back(w1);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-23 00:11:35 +08:00
|
|
|
class MeanshiftGrouping
|
|
|
|
{
|
|
|
|
public:
|
2013-02-25 00:14:01 +08:00
|
|
|
MeanshiftGrouping(const Point3d& densKer, const std::vector<Point3d>& posV,
|
|
|
|
const std::vector<double>& wV, double eps, int maxIter = 20)
|
2011-04-23 00:11:35 +08:00
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
densityKernel = densKer;
|
2011-04-23 00:11:35 +08:00
|
|
|
weightsV = wV;
|
|
|
|
positionsV = posV;
|
2011-07-19 20:27:07 +08:00
|
|
|
positionsCount = (int)posV.size();
|
2012-06-09 23:00:04 +08:00
|
|
|
meanshiftV.resize(positionsCount);
|
2011-04-23 00:11:35 +08:00
|
|
|
distanceV.resize(positionsCount);
|
2012-06-09 23:00:04 +08:00
|
|
|
iterMax = maxIter;
|
2013-01-24 18:40:35 +08:00
|
|
|
modeEps = eps;
|
2012-06-09 23:00:04 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0; i<positionsV.size(); i++)
|
|
|
|
{
|
|
|
|
meanshiftV[i] = getNewValue(positionsV[i]);
|
|
|
|
distanceV[i] = moveToMode(meanshiftV[i]);
|
|
|
|
meanshiftV[i] -= positionsV[i];
|
|
|
|
}
|
2011-04-23 00:11:35 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
void getModes(std::vector<Point3d>& modesV, std::vector<double>& resWeightsV, const double eps)
|
2011-04-23 00:11:35 +08:00
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
for (size_t i=0; i <distanceV.size(); i++)
|
|
|
|
{
|
|
|
|
bool is_found = false;
|
|
|
|
for(size_t j=0; j<modesV.size(); j++)
|
|
|
|
{
|
|
|
|
if ( getDistance(distanceV[i], modesV[j]) < eps)
|
|
|
|
{
|
|
|
|
is_found=true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!is_found)
|
|
|
|
{
|
|
|
|
modesV.push_back(distanceV[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resWeightsV.resize(modesV.size());
|
|
|
|
|
|
|
|
for (size_t i=0; i<modesV.size(); i++)
|
|
|
|
{
|
|
|
|
resWeightsV[i] = getResultWeight(modesV[i]);
|
|
|
|
}
|
2011-04-23 00:11:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Point3d> positionsV;
|
|
|
|
std::vector<double> weightsV;
|
2011-04-23 00:11:35 +08:00
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
Point3d densityKernel;
|
|
|
|
int positionsCount;
|
2011-04-23 00:11:35 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Point3d> meanshiftV;
|
|
|
|
std::vector<Point3d> distanceV;
|
2012-06-09 23:00:04 +08:00
|
|
|
int iterMax;
|
|
|
|
double modeEps;
|
2011-04-23 00:11:35 +08:00
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
Point3d getNewValue(const Point3d& inPt) const
|
2011-04-23 00:11:35 +08:00
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
Point3d resPoint(.0);
|
|
|
|
Point3d ratPoint(.0);
|
|
|
|
for (size_t i=0; i<positionsV.size(); i++)
|
|
|
|
{
|
|
|
|
Point3d aPt= positionsV[i];
|
|
|
|
Point3d bPt = inPt;
|
|
|
|
Point3d sPt = densityKernel;
|
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
sPt.x *= std::exp(aPt.z);
|
|
|
|
sPt.y *= std::exp(aPt.z);
|
2012-06-09 23:00:04 +08:00
|
|
|
|
|
|
|
aPt.x /= sPt.x;
|
|
|
|
aPt.y /= sPt.y;
|
|
|
|
aPt.z /= sPt.z;
|
|
|
|
|
|
|
|
bPt.x /= sPt.x;
|
|
|
|
bPt.y /= sPt.y;
|
|
|
|
bPt.z /= sPt.z;
|
|
|
|
|
|
|
|
double w = (weightsV[i])*std::exp(-((aPt-bPt).dot(aPt-bPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
|
|
|
|
|
|
|
|
resPoint += w*aPt;
|
|
|
|
|
|
|
|
ratPoint.x += w/sPt.x;
|
|
|
|
ratPoint.y += w/sPt.y;
|
|
|
|
ratPoint.z += w/sPt.z;
|
|
|
|
}
|
|
|
|
resPoint.x /= ratPoint.x;
|
|
|
|
resPoint.y /= ratPoint.y;
|
|
|
|
resPoint.z /= ratPoint.z;
|
|
|
|
return resPoint;
|
2011-04-23 00:11:35 +08:00
|
|
|
}
|
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
double getResultWeight(const Point3d& inPt) const
|
2011-04-23 00:11:35 +08:00
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
double sumW=0;
|
|
|
|
for (size_t i=0; i<positionsV.size(); i++)
|
|
|
|
{
|
|
|
|
Point3d aPt = positionsV[i];
|
|
|
|
Point3d sPt = densityKernel;
|
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
sPt.x *= std::exp(aPt.z);
|
|
|
|
sPt.y *= std::exp(aPt.z);
|
2012-06-09 23:00:04 +08:00
|
|
|
|
|
|
|
aPt -= inPt;
|
|
|
|
|
|
|
|
aPt.x /= sPt.x;
|
|
|
|
aPt.y /= sPt.y;
|
|
|
|
aPt.z /= sPt.z;
|
|
|
|
|
|
|
|
sumW+=(weightsV[i])*std::exp(-(aPt.dot(aPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
|
|
|
|
}
|
|
|
|
return sumW;
|
2011-04-23 00:11:35 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
|
|
|
Point3d moveToMode(Point3d aPt) const
|
2011-04-23 00:11:35 +08:00
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
Point3d bPt;
|
|
|
|
for (int i = 0; i<iterMax; i++)
|
|
|
|
{
|
|
|
|
bPt = aPt;
|
|
|
|
aPt = getNewValue(bPt);
|
|
|
|
if ( getDistance(aPt, bPt) <= modeEps )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return aPt;
|
2011-04-23 00:11:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
double getDistance(Point3d p1, Point3d p2) const
|
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
Point3d ns = densityKernel;
|
2013-02-25 00:14:01 +08:00
|
|
|
ns.x *= std::exp(p2.z);
|
|
|
|
ns.y *= std::exp(p2.z);
|
2012-06-09 23:00:04 +08:00
|
|
|
p2 -= p1;
|
|
|
|
p2.x /= ns.x;
|
|
|
|
p2.y /= ns.y;
|
|
|
|
p2.z /= ns.z;
|
|
|
|
return p2.dot(p2);
|
2011-04-23 00:11:35 +08:00
|
|
|
}
|
|
|
|
};
|
2011-04-19 17:05:15 +08:00
|
|
|
//new grouping function with using meanshift
|
2013-02-25 00:14:01 +08:00
|
|
|
static void groupRectangles_meanshift(std::vector<Rect>& rectList, double detectThreshold, std::vector<double>* foundWeights,
|
|
|
|
std::vector<double>& scales, Size winDetSize)
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
2011-07-19 20:27:07 +08:00
|
|
|
int detectionCount = (int)rectList.size();
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Point3d> hits(detectionCount), resultHits;
|
|
|
|
std::vector<double> hitWeights(detectionCount), resultWeights;
|
2011-04-19 17:05:15 +08:00
|
|
|
Point2d hitCenter;
|
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
for (int i=0; i < detectionCount; i++)
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
|
|
|
hitWeights[i] = (*foundWeights)[i];
|
|
|
|
hitCenter = (rectList[i].tl() + rectList[i].br())*(0.5); //center of rectangles
|
|
|
|
hits[i] = Point3d(hitCenter.x, hitCenter.y, std::log(scales[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
rectList.clear();
|
|
|
|
if (foundWeights)
|
|
|
|
foundWeights->clear();
|
|
|
|
|
|
|
|
double logZ = std::log(1.3);
|
|
|
|
Point3d smothing(8, 16, logZ);
|
|
|
|
|
|
|
|
MeanshiftGrouping msGrouping(smothing, hits, hitWeights, 1e-5, 100);
|
|
|
|
|
|
|
|
msGrouping.getModes(resultHits, resultWeights, 1);
|
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
for (unsigned i=0; i < resultHits.size(); ++i)
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
double scale = std::exp(resultHits[i].z);
|
2011-04-19 17:05:15 +08:00
|
|
|
hitCenter.x = resultHits[i].x;
|
|
|
|
hitCenter.y = resultHits[i].y;
|
|
|
|
Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );
|
2012-06-09 23:00:04 +08:00
|
|
|
Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),
|
|
|
|
int(s.width), int(s.height) );
|
2011-04-19 17:05:15 +08:00
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
if (resultWeights[i] > detectThreshold)
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
|
|
|
rectList.push_back(resultRect);
|
|
|
|
foundWeights->push_back(resultWeights[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2011-04-19 17:05:15 +08:00
|
|
|
groupRectangles(rectList, groupThreshold, eps, 0, 0);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2011-04-19 17:05:15 +08:00
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& weights, int groupThreshold, double eps)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2011-04-19 17:05:15 +08:00
|
|
|
groupRectangles(rectList, groupThreshold, eps, &weights, 0);
|
|
|
|
}
|
2011-04-22 18:03:05 +08:00
|
|
|
//used for cascade detection algorithm for ROC-curve calculating
|
2013-02-25 00:14:01 +08:00
|
|
|
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& rejectLevels, std::vector<double>& levelWeights, int groupThreshold, double eps)
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
2011-04-22 18:03:05 +08:00
|
|
|
groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
|
2011-04-19 17:05:15 +08:00
|
|
|
}
|
2011-04-22 18:03:05 +08:00
|
|
|
//can be used for HOG detection algorithm only
|
2013-02-25 00:14:01 +08:00
|
|
|
void groupRectangles_meanshift(std::vector<Rect>& rectList, std::vector<double>& foundWeights,
|
|
|
|
std::vector<double>& foundScales, double detectThreshold, Size winDetSize)
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
2012-06-09 23:00:04 +08:00
|
|
|
groupRectangles_meanshift(rectList, detectThreshold, &foundWeights, foundScales, winDetSize);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
FeatureEvaluator::~FeatureEvaluator() {}
|
|
|
|
bool FeatureEvaluator::read(const FileNode&) {return true;}
|
|
|
|
Ptr<FeatureEvaluator> FeatureEvaluator::clone() const { return Ptr<FeatureEvaluator>(); }
|
|
|
|
int FeatureEvaluator::getFeatureType() const {return -1;}
|
2013-12-17 18:29:30 +08:00
|
|
|
bool FeatureEvaluator::setImage(InputArray, Size, Size) {return true;}
|
2010-05-12 01:44:00 +08:00
|
|
|
bool FeatureEvaluator::setWindow(Point) { return true; }
|
|
|
|
double FeatureEvaluator::calcOrd(int) const { return 0.; }
|
|
|
|
int FeatureEvaluator::calcCat(int) const { return 0; }
|
|
|
|
|
|
|
|
//---------------------------------------------- HaarEvaluator ---------------------------------------
|
|
|
|
|
|
|
|
bool HaarEvaluator::Feature :: read( const FileNode& node )
|
|
|
|
{
|
|
|
|
FileNode rnode = node[CC_RECTS];
|
|
|
|
FileNodeIterator it = rnode.begin(), it_end = rnode.end();
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
int ri;
|
|
|
|
for( ri = 0; ri < RECT_NUM; ri++ )
|
|
|
|
{
|
|
|
|
rect[ri].r = Rect();
|
|
|
|
rect[ri].weight = 0.f;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
for(ri = 0; it != it_end; ++it, ri++)
|
|
|
|
{
|
|
|
|
FileNodeIterator it2 = (*it).begin();
|
|
|
|
it2 >> rect[ri].r.x >> rect[ri].r.y >>
|
|
|
|
rect[ri].r.width >> rect[ri].r.height >> rect[ri].weight;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
tilted = (int)node[CC_TILTED] != 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
HaarEvaluator::HaarEvaluator()
|
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
optfeaturesPtr = 0;
|
|
|
|
pwin = 0;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
HaarEvaluator::~HaarEvaluator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HaarEvaluator::read(const FileNode& node)
|
|
|
|
{
|
2013-12-11 02:54:37 +08:00
|
|
|
size_t i, n = node.size();
|
|
|
|
CV_Assert(n > 0);
|
2013-12-17 18:29:30 +08:00
|
|
|
if(features.empty())
|
|
|
|
features = makePtr<std::vector<Feature> >();
|
|
|
|
if(optfeatures.empty())
|
|
|
|
optfeatures = makePtr<std::vector<OptFeature> >();
|
2013-12-13 01:58:42 +08:00
|
|
|
features->resize(n);
|
2013-12-11 02:54:37 +08:00
|
|
|
FileNodeIterator it = node.begin();
|
2010-05-12 01:44:00 +08:00
|
|
|
hasTiltedFeatures = false;
|
2013-12-18 00:55:49 +08:00
|
|
|
std::vector<Feature>& ff = *features;
|
2013-12-17 18:29:30 +08:00
|
|
|
sumSize0 = Size();
|
|
|
|
ufbuf.release();
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-11 02:54:37 +08:00
|
|
|
for(i = 0; i < n; i++, ++it)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
if(!ff[i].read(*it))
|
2010-05-12 01:44:00 +08:00
|
|
|
return false;
|
2013-12-13 01:58:42 +08:00
|
|
|
if( ff[i].tilted )
|
2010-05-12 01:44:00 +08:00
|
|
|
hasTiltedFeatures = true;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
Ptr<FeatureEvaluator> HaarEvaluator::clone() const
|
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
Ptr<HaarEvaluator> ret = makePtr<HaarEvaluator>();
|
2010-05-12 01:44:00 +08:00
|
|
|
ret->origWinSize = origWinSize;
|
|
|
|
ret->features = features;
|
2013-12-13 01:58:42 +08:00
|
|
|
ret->optfeatures = optfeatures;
|
|
|
|
ret->optfeaturesPtr = optfeatures->empty() ? 0 : &(*(ret->optfeatures))[0];
|
2010-05-12 01:44:00 +08:00
|
|
|
ret->hasTiltedFeatures = hasTiltedFeatures;
|
2013-12-13 01:58:42 +08:00
|
|
|
ret->sum0 = sum0; ret->sqsum0 = sqsum0;
|
2013-12-17 18:29:30 +08:00
|
|
|
ret->sum = sum; ret->sqsum = sqsum;
|
|
|
|
ret->usum0 = usum0; ret->usqsum0 = usqsum0; ret->ufbuf = ufbuf;
|
2010-05-12 01:44:00 +08:00
|
|
|
ret->normrect = normrect;
|
2013-12-13 01:58:42 +08:00
|
|
|
memcpy( ret->nofs, nofs, 4*sizeof(nofs[0]) );
|
2013-12-17 18:29:30 +08:00
|
|
|
ret->pwin = pwin;
|
2012-06-09 23:00:04 +08:00
|
|
|
ret->varianceNormFactor = varianceNormFactor;
|
2010-05-12 01:44:00 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
bool HaarEvaluator::setImage( InputArray _image, Size _origWinSize, Size _sumSize )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
Size imgsz = _image.size();
|
2013-12-17 18:29:30 +08:00
|
|
|
int cols = imgsz.width, rows = imgsz.height;
|
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
if (imgsz.width < origWinSize.width || imgsz.height < origWinSize.height)
|
2010-05-12 01:44:00 +08:00
|
|
|
return false;
|
2013-12-13 01:58:42 +08:00
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
origWinSize = _origWinSize;
|
|
|
|
normrect = Rect(1, 1, origWinSize.width-2, origWinSize.height-2);
|
|
|
|
|
|
|
|
int rn = _sumSize.height, cn = _sumSize.width, rn_scale = hasTiltedFeatures ? 2 : 1;
|
|
|
|
int sumStep, tofs = 0;
|
|
|
|
CV_Assert(rn >= rows+1 && cn >= cols+1);
|
|
|
|
|
|
|
|
if( _image.isUMat() )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-12-17 18:29:30 +08:00
|
|
|
usum0.create(rn*rn_scale, cn, CV_32S);
|
|
|
|
usqsum0.create(rn, cn, CV_32S);
|
|
|
|
usum = UMat(usum0, Rect(0, 0, cols+1, rows+1));
|
|
|
|
usqsum = UMat(usqsum0, Rect(0, 0, cols, rows));
|
|
|
|
|
|
|
|
if( hasTiltedFeatures )
|
|
|
|
{
|
|
|
|
UMat utilted(usum0, Rect(0, _sumSize.height, cols+1, rows+1));
|
|
|
|
integral(_image, usum, noArray(), utilted, CV_32S);
|
|
|
|
tofs = (int)((utilted.offset - usum.offset)/sizeof(int));
|
|
|
|
}
|
|
|
|
else
|
2013-12-18 00:55:49 +08:00
|
|
|
{
|
2013-12-17 18:29:30 +08:00
|
|
|
integral(_image, usum, noArray(), noArray(), CV_32S);
|
2013-12-18 00:55:49 +08:00
|
|
|
}
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
sqrBoxFilter(_image, usqsum, CV_32S,
|
|
|
|
Size(normrect.width, normrect.height),
|
|
|
|
Point(0, 0), false);
|
2013-12-18 00:55:49 +08:00
|
|
|
/*sqrBoxFilter(_image.getMat(), sqsum, CV_32S,
|
|
|
|
Size(normrect.width, normrect.height),
|
|
|
|
Point(0, 0), false);
|
|
|
|
sqsum.copyTo(usqsum);*/
|
2013-12-17 18:29:30 +08:00
|
|
|
sumStep = (int)(usum.step/usum.elemSize());
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2013-12-17 18:29:30 +08:00
|
|
|
else
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-12-17 18:29:30 +08:00
|
|
|
sum0.create(rn*rn_scale, cn, CV_32S);
|
2013-12-18 00:55:49 +08:00
|
|
|
sqsum0.create(rn, cn, CV_32S);
|
2013-12-17 18:29:30 +08:00
|
|
|
sum = sum0(Rect(0, 0, cols+1, rows+1));
|
2013-12-18 00:55:49 +08:00
|
|
|
sqsum = sqsum0(Rect(0, 0, cols, rows));
|
2013-12-17 18:29:30 +08:00
|
|
|
|
|
|
|
if( hasTiltedFeatures )
|
|
|
|
{
|
|
|
|
Mat tilted = sum0(Rect(0, _sumSize.height, cols+1, rows+1));
|
2013-12-18 00:55:49 +08:00
|
|
|
integral(_image, sum, noArray(), tilted, CV_32S);
|
2013-12-17 18:29:30 +08:00
|
|
|
tofs = (int)((tilted.data - sum.data)/sizeof(int));
|
|
|
|
}
|
|
|
|
else
|
2013-12-18 00:55:49 +08:00
|
|
|
integral(_image, sum, noArray(), noArray(), CV_32S);
|
|
|
|
sqrBoxFilter(_image, sqsum, CV_32S,
|
2013-12-17 18:29:30 +08:00
|
|
|
Size(normrect.width, normrect.height),
|
2013-12-18 00:55:49 +08:00
|
|
|
Point(0, 0), false);
|
2013-12-17 18:29:30 +08:00
|
|
|
sumStep = (int)(sum.step/sum.elemSize());
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
CV_SUM_OFS( nofs[0], nofs[1], nofs[2], nofs[3], 0, normrect, sumStep );
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
size_t fi, nfeatures = features->size();
|
|
|
|
const std::vector<Feature>& ff = *features;
|
2013-12-17 18:29:30 +08:00
|
|
|
|
|
|
|
if( sumSize0 != _sumSize )
|
|
|
|
{
|
|
|
|
optfeatures->resize(nfeatures);
|
|
|
|
optfeaturesPtr = &(*optfeatures)[0];
|
|
|
|
for( fi = 0; fi < nfeatures; fi++ )
|
|
|
|
optfeaturesPtr[fi].setOffsets( ff[fi], sumStep, tofs );
|
|
|
|
}
|
|
|
|
if( _image.isUMat() && (sumSize0 != _sumSize || ufbuf.empty()) )
|
2013-12-18 00:55:49 +08:00
|
|
|
copyVectorToUMat(*optfeatures, ufbuf);
|
2013-12-17 18:29:30 +08:00
|
|
|
sumSize0 = _sumSize;
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
bool HaarEvaluator::setWindow( Point pt )
|
|
|
|
{
|
|
|
|
if( pt.x < 0 || pt.y < 0 ||
|
2011-06-10 20:19:23 +08:00
|
|
|
pt.x + origWinSize.width >= sum.cols ||
|
|
|
|
pt.y + origWinSize.height >= sum.rows )
|
2010-05-12 01:44:00 +08:00
|
|
|
return false;
|
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
const int* p = &sum.at<int>(pt);
|
|
|
|
int valsum = CALC_SUM_OFS(nofs, p);
|
2013-12-18 00:55:49 +08:00
|
|
|
double valsqsum = sqsum.at<int>(pt.y + normrect.y, pt.x + normrect.x);
|
2010-05-12 01:44:00 +08:00
|
|
|
|
|
|
|
double nf = (double)normrect.area() * valsqsum - (double)valsum * valsum;
|
|
|
|
if( nf > 0. )
|
2013-02-25 00:14:01 +08:00
|
|
|
nf = std::sqrt(nf);
|
2010-05-12 01:44:00 +08:00
|
|
|
else
|
|
|
|
nf = 1.;
|
|
|
|
varianceNormFactor = 1./nf;
|
2013-12-13 01:58:42 +08:00
|
|
|
pwin = p;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
return true;
|
|
|
|
}
|
2013-12-17 18:29:30 +08:00
|
|
|
|
|
|
|
Rect HaarEvaluator::getNormRect() const
|
|
|
|
{
|
|
|
|
return normrect;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HaarEvaluator::getUMats(std::vector<UMat>& bufs)
|
|
|
|
{
|
|
|
|
bufs.clear();
|
|
|
|
bufs.push_back(usum);
|
|
|
|
bufs.push_back(usqsum);
|
|
|
|
bufs.push_back(ufbuf);
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
|
|
|
//---------------------------------------------- LBPEvaluator -------------------------------------
|
|
|
|
bool LBPEvaluator::Feature :: read(const FileNode& node )
|
|
|
|
{
|
|
|
|
FileNode rnode = node[CC_RECT];
|
|
|
|
FileNodeIterator it = rnode.begin();
|
|
|
|
it >> rect.x >> rect.y >> rect.width >> rect.height;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
LBPEvaluator::LBPEvaluator()
|
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
features = makePtr<std::vector<Feature> >();
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
LBPEvaluator::~LBPEvaluator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LBPEvaluator::read( const FileNode& node )
|
|
|
|
{
|
|
|
|
features->resize(node.size());
|
|
|
|
featuresPtr = &(*features)[0];
|
|
|
|
FileNodeIterator it = node.begin(), it_end = node.end();
|
|
|
|
for(int i = 0; it != it_end; ++it, i++)
|
|
|
|
{
|
|
|
|
if(!featuresPtr[i].read(*it))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ptr<FeatureEvaluator> LBPEvaluator::clone() const
|
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
Ptr<LBPEvaluator> ret = makePtr<LBPEvaluator>();
|
2010-05-12 01:44:00 +08:00
|
|
|
ret->origWinSize = origWinSize;
|
|
|
|
ret->features = features;
|
|
|
|
ret->featuresPtr = &(*ret->features)[0];
|
|
|
|
ret->sum0 = sum0, ret->sum = sum;
|
|
|
|
ret->normrect = normrect;
|
|
|
|
ret->offset = offset;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
bool LBPEvaluator::setImage( InputArray _image, Size _origWinSize, Size )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
Mat image = _image.getMat();
|
2010-05-12 01:44:00 +08:00
|
|
|
int rn = image.rows+1, cn = image.cols+1;
|
|
|
|
origWinSize = _origWinSize;
|
|
|
|
|
|
|
|
if( image.cols < origWinSize.width || image.rows < origWinSize.height )
|
|
|
|
return false;
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
if( sum0.rows < rn || sum0.cols < cn )
|
|
|
|
sum0.create(rn, cn, CV_32S);
|
|
|
|
sum = Mat(rn, cn, CV_32S, sum0.data);
|
|
|
|
integral(image, sum);
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
size_t fi, nfeatures = features->size();
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
for( fi = 0; fi < nfeatures; fi++ )
|
|
|
|
featuresPtr[fi].updatePtrs( sum );
|
|
|
|
return true;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
bool LBPEvaluator::setWindow( Point pt )
|
|
|
|
{
|
|
|
|
if( pt.x < 0 || pt.y < 0 ||
|
2011-06-10 20:19:23 +08:00
|
|
|
pt.x + origWinSize.width >= sum.cols ||
|
|
|
|
pt.y + origWinSize.height >= sum.rows )
|
2010-05-12 01:44:00 +08:00
|
|
|
return false;
|
|
|
|
offset = pt.y * ((int)sum.step/sizeof(int)) + pt.x;
|
|
|
|
return true;
|
2012-06-09 23:00:04 +08:00
|
|
|
}
|
2011-09-22 02:03:53 +08:00
|
|
|
|
|
|
|
//---------------------------------------------- HOGEvaluator ---------------------------------------
|
|
|
|
bool HOGEvaluator::Feature :: read( const FileNode& node )
|
|
|
|
{
|
|
|
|
FileNode rnode = node[CC_RECT];
|
|
|
|
FileNodeIterator it = rnode.begin();
|
|
|
|
it >> rect[0].x >> rect[0].y >> rect[0].width >> rect[0].height >> featComponent;
|
|
|
|
rect[1].x = rect[0].x + rect[0].width;
|
|
|
|
rect[1].y = rect[0].y;
|
|
|
|
rect[2].x = rect[0].x;
|
|
|
|
rect[2].y = rect[0].y + rect[0].height;
|
|
|
|
rect[3].x = rect[0].x + rect[0].width;
|
|
|
|
rect[3].y = rect[0].y + rect[0].height;
|
|
|
|
rect[1].width = rect[2].width = rect[3].width = rect[0].width;
|
|
|
|
rect[1].height = rect[2].height = rect[3].height = rect[0].height;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
HOGEvaluator::HOGEvaluator()
|
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
features = makePtr<std::vector<Feature> >();
|
2011-09-22 02:03:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
HOGEvaluator::~HOGEvaluator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HOGEvaluator::read( const FileNode& node )
|
|
|
|
{
|
|
|
|
features->resize(node.size());
|
|
|
|
featuresPtr = &(*features)[0];
|
|
|
|
FileNodeIterator it = node.begin(), it_end = node.end();
|
|
|
|
for(int i = 0; it != it_end; ++it, i++)
|
|
|
|
{
|
|
|
|
if(!featuresPtr[i].read(*it))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ptr<FeatureEvaluator> HOGEvaluator::clone() const
|
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
Ptr<HOGEvaluator> ret = makePtr<HOGEvaluator>();
|
2011-09-22 02:03:53 +08:00
|
|
|
ret->origWinSize = origWinSize;
|
|
|
|
ret->features = features;
|
|
|
|
ret->featuresPtr = &(*ret->features)[0];
|
|
|
|
ret->offset = offset;
|
|
|
|
ret->hist = hist;
|
2012-06-09 23:00:04 +08:00
|
|
|
ret->normSum = normSum;
|
2011-09-22 02:03:53 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
bool HOGEvaluator::setImage( InputArray _image, Size winSize, Size )
|
2011-09-22 02:03:53 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
Mat image = _image.getMat();
|
2011-09-22 02:03:53 +08:00
|
|
|
int rows = image.rows + 1;
|
|
|
|
int cols = image.cols + 1;
|
|
|
|
origWinSize = winSize;
|
|
|
|
if( image.cols < origWinSize.width || image.rows < origWinSize.height )
|
|
|
|
return false;
|
|
|
|
hist.clear();
|
|
|
|
for( int bin = 0; bin < Feature::BIN_NUM; bin++ )
|
|
|
|
{
|
|
|
|
hist.push_back( Mat(rows, cols, CV_32FC1) );
|
|
|
|
}
|
|
|
|
normSum.create( rows, cols, CV_32FC1 );
|
|
|
|
|
|
|
|
integralHistogram( image, hist, normSum, Feature::BIN_NUM );
|
|
|
|
|
|
|
|
size_t featIdx, featCount = features->size();
|
|
|
|
|
|
|
|
for( featIdx = 0; featIdx < featCount; featIdx++ )
|
|
|
|
{
|
|
|
|
featuresPtr[featIdx].updatePtrs( hist, normSum );
|
|
|
|
}
|
|
|
|
return true;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2011-09-22 02:03:53 +08:00
|
|
|
bool HOGEvaluator::setWindow(Point pt)
|
|
|
|
{
|
|
|
|
if( pt.x < 0 || pt.y < 0 ||
|
|
|
|
pt.x + origWinSize.width >= hist[0].cols-2 ||
|
|
|
|
pt.y + origWinSize.height >= hist[0].rows-2 )
|
|
|
|
return false;
|
|
|
|
offset = pt.y * ((int)hist[0].step/sizeof(float)) + pt.x;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
void HOGEvaluator::integralHistogram(const Mat &img, std::vector<Mat> &histogram, Mat &norm, int nbins) const
|
2011-09-22 02:03:53 +08:00
|
|
|
{
|
|
|
|
CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 );
|
|
|
|
int x, y, binIdx;
|
|
|
|
|
|
|
|
Size gradSize(img.size());
|
|
|
|
Size histSize(histogram[0].size());
|
|
|
|
Mat grad(gradSize, CV_32F);
|
|
|
|
Mat qangle(gradSize, CV_8U);
|
|
|
|
|
|
|
|
AutoBuffer<int> mapbuf(gradSize.width + gradSize.height + 4);
|
|
|
|
int* xmap = (int*)mapbuf + 1;
|
|
|
|
int* ymap = xmap + gradSize.width + 2;
|
|
|
|
|
|
|
|
const int borderType = (int)BORDER_REPLICATE;
|
|
|
|
|
|
|
|
for( x = -1; x < gradSize.width + 1; x++ )
|
|
|
|
xmap[x] = borderInterpolate(x, gradSize.width, borderType);
|
|
|
|
for( y = -1; y < gradSize.height + 1; y++ )
|
|
|
|
ymap[y] = borderInterpolate(y, gradSize.height, borderType);
|
|
|
|
|
|
|
|
int width = gradSize.width;
|
|
|
|
AutoBuffer<float> _dbuf(width*4);
|
|
|
|
float* dbuf = _dbuf;
|
|
|
|
Mat Dx(1, width, CV_32F, dbuf);
|
|
|
|
Mat Dy(1, width, CV_32F, dbuf + width);
|
|
|
|
Mat Mag(1, width, CV_32F, dbuf + width*2);
|
|
|
|
Mat Angle(1, width, CV_32F, dbuf + width*3);
|
|
|
|
|
|
|
|
float angleScale = (float)(nbins/CV_PI);
|
|
|
|
|
|
|
|
for( y = 0; y < gradSize.height; y++ )
|
|
|
|
{
|
|
|
|
const uchar* currPtr = img.data + img.step*ymap[y];
|
|
|
|
const uchar* prevPtr = img.data + img.step*ymap[y-1];
|
|
|
|
const uchar* nextPtr = img.data + img.step*ymap[y+1];
|
|
|
|
float* gradPtr = (float*)grad.ptr(y);
|
|
|
|
uchar* qanglePtr = (uchar*)qangle.ptr(y);
|
|
|
|
|
|
|
|
for( x = 0; x < width; x++ )
|
|
|
|
{
|
|
|
|
dbuf[x] = (float)(currPtr[xmap[x+1]] - currPtr[xmap[x-1]]);
|
|
|
|
dbuf[width + x] = (float)(nextPtr[xmap[x]] - prevPtr[xmap[x]]);
|
|
|
|
}
|
|
|
|
cartToPolar( Dx, Dy, Mag, Angle, false );
|
|
|
|
for( x = 0; x < width; x++ )
|
|
|
|
{
|
|
|
|
float mag = dbuf[x+width*2];
|
|
|
|
float angle = dbuf[x+width*3];
|
|
|
|
angle = angle*angleScale - 0.5f;
|
|
|
|
int bidx = cvFloor(angle);
|
|
|
|
angle -= bidx;
|
|
|
|
if( bidx < 0 )
|
|
|
|
bidx += nbins;
|
|
|
|
else if( bidx >= nbins )
|
|
|
|
bidx -= nbins;
|
|
|
|
|
|
|
|
qanglePtr[x] = (uchar)bidx;
|
|
|
|
gradPtr[x] = mag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
integral(grad, norm, grad.depth());
|
|
|
|
|
|
|
|
float* histBuf;
|
|
|
|
const float* magBuf;
|
|
|
|
const uchar* binsBuf;
|
|
|
|
|
|
|
|
int binsStep = (int)( qangle.step / sizeof(uchar) );
|
|
|
|
int histStep = (int)( histogram[0].step / sizeof(float) );
|
|
|
|
int magStep = (int)( grad.step / sizeof(float) );
|
|
|
|
for( binIdx = 0; binIdx < nbins; binIdx++ )
|
|
|
|
{
|
|
|
|
histBuf = (float*)histogram[binIdx].data;
|
|
|
|
magBuf = (const float*)grad.data;
|
|
|
|
binsBuf = (const uchar*)qangle.data;
|
|
|
|
|
|
|
|
memset( histBuf, 0, histSize.width * sizeof(histBuf[0]) );
|
|
|
|
histBuf += histStep + 1;
|
|
|
|
for( y = 0; y < qangle.rows; y++ )
|
2012-06-09 23:00:04 +08:00
|
|
|
{
|
2011-09-22 02:03:53 +08:00
|
|
|
histBuf[-1] = 0.f;
|
|
|
|
float strSum = 0.f;
|
|
|
|
for( x = 0; x < qangle.cols; x++ )
|
|
|
|
{
|
|
|
|
if( binsBuf[x] == binIdx )
|
|
|
|
strSum += magBuf[x];
|
|
|
|
histBuf[x] = histBuf[-histStep + x] + strSum;
|
|
|
|
}
|
|
|
|
histBuf += histStep;
|
|
|
|
binsBuf += binsStep;
|
|
|
|
magBuf += magStep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :
|
2012-06-09 23:00:04 +08:00
|
|
|
featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :
|
2011-09-22 02:03:53 +08:00
|
|
|
featureType == HOG ? Ptr<FeatureEvaluator>(new HOGEvaluator) :
|
|
|
|
Ptr<FeatureEvaluator>();
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2011-09-22 02:03:53 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
//---------------------------------------- Classifier Cascade --------------------------------------------
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
CascadeClassifierImpl::CascadeClassifierImpl()
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
CascadeClassifierImpl::~CascadeClassifierImpl()
|
2012-06-09 23:00:04 +08:00
|
|
|
{
|
2011-10-05 21:21:28 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
bool CascadeClassifierImpl::empty() const
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
return !oldCascade && data.stages.empty();
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
bool CascadeClassifierImpl::load(const String& filename)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
oldCascade.release();
|
2011-07-07 19:43:21 +08:00
|
|
|
data = Data();
|
|
|
|
featureEvaluator.release();
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
FileStorage fs(filename, FileStorage::READ);
|
|
|
|
if( !fs.isOpened() )
|
|
|
|
return false;
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
if( read_(fs.getFirstTopLevelNode()) )
|
2010-05-12 01:44:00 +08:00
|
|
|
return true;
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
fs.release();
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-08-13 21:45:29 +08:00
|
|
|
oldCascade.reset((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
|
2010-05-12 01:44:00 +08:00
|
|
|
return !oldCascade.empty();
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void CascadeClassifierImpl::read(const FileNode& node)
|
|
|
|
{
|
|
|
|
read_(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CascadeClassifierImpl::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, double& weight )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-08-13 21:45:29 +08:00
|
|
|
CV_Assert( !oldCascade );
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2011-09-22 02:03:53 +08:00
|
|
|
assert( data.featureType == FeatureEvaluator::HAAR ||
|
|
|
|
data.featureType == FeatureEvaluator::LBP ||
|
|
|
|
data.featureType == FeatureEvaluator::HOG );
|
2010-12-09 23:09:34 +08:00
|
|
|
|
2012-06-09 23:00:04 +08:00
|
|
|
if( !evaluator->setWindow(pt) )
|
2011-09-22 02:03:53 +08:00
|
|
|
return -1;
|
2013-12-19 18:09:44 +08:00
|
|
|
if( data.isStumpBased() )
|
2011-09-22 02:03:53 +08:00
|
|
|
{
|
|
|
|
if( data.featureType == FeatureEvaluator::HAAR )
|
2012-06-09 23:00:04 +08:00
|
|
|
return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );
|
2011-09-22 02:03:53 +08:00
|
|
|
else if( data.featureType == FeatureEvaluator::LBP )
|
2012-06-09 23:00:04 +08:00
|
|
|
return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );
|
2011-09-22 02:03:53 +08:00
|
|
|
else if( data.featureType == FeatureEvaluator::HOG )
|
2012-06-09 23:00:04 +08:00
|
|
|
return predictOrderedStump<HOGEvaluator>( *this, evaluator, weight );
|
2011-09-22 02:03:53 +08:00
|
|
|
else
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( data.featureType == FeatureEvaluator::HAAR )
|
2012-06-09 23:00:04 +08:00
|
|
|
return predictOrdered<HaarEvaluator>( *this, evaluator, weight );
|
2011-09-22 02:03:53 +08:00
|
|
|
else if( data.featureType == FeatureEvaluator::LBP )
|
2012-06-09 23:00:04 +08:00
|
|
|
return predictCategorical<LBPEvaluator>( *this, evaluator, weight );
|
2011-09-22 02:03:53 +08:00
|
|
|
else if( data.featureType == FeatureEvaluator::HOG )
|
2012-06-09 23:00:04 +08:00
|
|
|
return predictOrdered<HOGEvaluator>( *this, evaluator, weight );
|
2011-09-22 02:03:53 +08:00
|
|
|
else
|
|
|
|
return -2;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void CascadeClassifierImpl::setMaskGenerator(const Ptr<MaskGenerator>& _maskGenerator)
|
2011-10-05 21:21:28 +08:00
|
|
|
{
|
|
|
|
maskGenerator=_maskGenerator;
|
|
|
|
}
|
2013-12-04 23:00:39 +08:00
|
|
|
Ptr<CascadeClassifierImpl::MaskGenerator> CascadeClassifierImpl::getMaskGenerator()
|
2011-10-05 21:21:28 +08:00
|
|
|
{
|
|
|
|
return maskGenerator;
|
|
|
|
}
|
|
|
|
|
2013-12-05 01:56:35 +08:00
|
|
|
Ptr<BaseCascadeClassifier::MaskGenerator> createFaceDetectionMaskGenerator()
|
2011-10-05 21:21:28 +08:00
|
|
|
{
|
|
|
|
#ifdef HAVE_TEGRA_OPTIMIZATION
|
2013-12-04 23:00:39 +08:00
|
|
|
return tegra::getCascadeClassifierMaskGenerator(*this);
|
2011-10-05 21:21:28 +08:00
|
|
|
#else
|
2013-12-05 01:56:35 +08:00
|
|
|
return Ptr<BaseCascadeClassifier::MaskGenerator>();
|
2011-10-05 21:21:28 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-09-25 16:18:33 +08:00
|
|
|
class CascadeClassifierInvoker : public ParallelLoopBody
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2012-09-25 16:18:33 +08:00
|
|
|
public:
|
2013-12-04 23:00:39 +08:00
|
|
|
CascadeClassifierInvoker( CascadeClassifierImpl& _cc, Size _sz1, int _stripSize, int _yStep, double _factor,
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Rect>& _vec, std::vector<int>& _levels, std::vector<double>& _weights, bool outputLevels, const Mat& _mask, Mutex* _mtx)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-12-09 23:09:34 +08:00
|
|
|
classifier = &_cc;
|
2010-12-14 18:17:45 +08:00
|
|
|
processingRectSize = _sz1;
|
2010-05-12 01:44:00 +08:00
|
|
|
stripSize = _stripSize;
|
|
|
|
yStep = _yStep;
|
2010-12-09 23:09:34 +08:00
|
|
|
scalingFactor = _factor;
|
|
|
|
rectangles = &_vec;
|
2012-09-25 16:18:33 +08:00
|
|
|
rejectLevels = outputLevels ? &_levels : 0;
|
|
|
|
levelWeights = outputLevels ? &_weights : 0;
|
|
|
|
mask = _mask;
|
|
|
|
mtx = _mtx;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2012-09-25 16:18:33 +08:00
|
|
|
void operator()(const Range& range) const
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2010-12-14 18:17:45 +08:00
|
|
|
Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
|
2011-09-29 05:14:20 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor),
|
|
|
|
cvRound(classifier->data.origWinSize.height * scalingFactor));
|
2010-12-09 23:09:34 +08:00
|
|
|
|
2012-09-25 16:18:33 +08:00
|
|
|
int y1 = range.start * stripSize;
|
2013-02-25 00:14:01 +08:00
|
|
|
int y2 = std::min(range.end * stripSize, processingRectSize.height);
|
2010-05-12 01:44:00 +08:00
|
|
|
for( int y = y1; y < y2; y += yStep )
|
2010-12-09 23:09:34 +08:00
|
|
|
{
|
2010-12-14 18:17:45 +08:00
|
|
|
for( int x = 0; x < processingRectSize.width; x += yStep )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2011-10-05 21:21:28 +08:00
|
|
|
if ( (!mask.empty()) && (mask.at<uchar>(Point(x,y))==0)) {
|
2011-09-29 05:14:20 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-04-22 18:03:05 +08:00
|
|
|
double gypWeight;
|
|
|
|
int result = classifier->runAt(evaluator, Point(x, y), gypWeight);
|
2012-07-13 23:47:09 +08:00
|
|
|
|
2012-07-15 12:11:46 +08:00
|
|
|
#if defined (LOG_CASCADE_STATISTIC)
|
|
|
|
|
|
|
|
logger.setPoint(Point(x, y), result);
|
|
|
|
#endif
|
2011-04-19 17:05:15 +08:00
|
|
|
if( rejectLevels )
|
|
|
|
{
|
|
|
|
if( result == 1 )
|
2011-07-19 20:27:07 +08:00
|
|
|
result = -(int)classifier->data.stages.size();
|
2013-06-01 08:34:20 +08:00
|
|
|
if( classifier->data.stages.size() + result == 0 )
|
2011-04-19 17:05:15 +08:00
|
|
|
{
|
2012-09-25 16:18:33 +08:00
|
|
|
mtx->lock();
|
2012-06-09 23:00:04 +08:00
|
|
|
rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
|
2011-04-19 17:05:15 +08:00
|
|
|
rejectLevels->push_back(-result);
|
2011-04-22 18:03:05 +08:00
|
|
|
levelWeights->push_back(gypWeight);
|
2012-12-27 15:43:16 +08:00
|
|
|
mtx->unlock();
|
2011-04-19 17:05:15 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
}
|
2011-04-19 17:05:15 +08:00
|
|
|
else if( result > 0 )
|
2012-09-25 16:18:33 +08:00
|
|
|
{
|
|
|
|
mtx->lock();
|
2010-12-09 23:09:34 +08:00
|
|
|
rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
|
2010-12-14 18:17:45 +08:00
|
|
|
winSize.width, winSize.height));
|
2012-09-25 16:18:33 +08:00
|
|
|
mtx->unlock();
|
|
|
|
}
|
2010-12-09 23:09:34 +08:00
|
|
|
if( result == 0 )
|
2010-05-12 01:44:00 +08:00
|
|
|
x += yStep;
|
|
|
|
}
|
2010-12-09 23:09:34 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
CascadeClassifierImpl* classifier;
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Rect>* rectangles;
|
2010-12-14 18:17:45 +08:00
|
|
|
Size processingRectSize;
|
2010-05-12 01:44:00 +08:00
|
|
|
int stripSize, yStep;
|
2010-12-09 23:09:34 +08:00
|
|
|
double scalingFactor;
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<int> *rejectLevels;
|
|
|
|
std::vector<double> *levelWeights;
|
2011-10-05 21:21:28 +08:00
|
|
|
Mat mask;
|
2012-09-25 16:18:33 +08:00
|
|
|
Mutex* mtx;
|
2010-05-12 01:44:00 +08:00
|
|
|
};
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };
|
2013-06-14 08:25:17 +08:00
|
|
|
struct getNeighbors { int operator ()(const CvAvgComp& e) const { return e.neighbors; } };
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2012-07-13 23:47:09 +08:00
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
bool CascadeClassifierImpl::detectSingleScale( InputArray _image, Size processingRectSize,
|
|
|
|
int yStep, double factor, std::vector<Rect>& candidates,
|
2013-12-17 18:29:30 +08:00
|
|
|
std::vector<int>& levels, std::vector<double>& weights,
|
|
|
|
Size sumSize0, bool outputRejectLevels )
|
2010-12-14 18:17:45 +08:00
|
|
|
{
|
2013-12-17 18:29:30 +08:00
|
|
|
if( !featureEvaluator->setImage(_image, data.origWinSize, sumSize0) )
|
2010-12-14 18:17:45 +08:00
|
|
|
return false;
|
|
|
|
|
2012-07-15 12:11:46 +08:00
|
|
|
#if defined (LOG_CASCADE_STATISTIC)
|
2012-07-13 23:47:09 +08:00
|
|
|
logger.setImage(image);
|
2012-07-15 12:11:46 +08:00
|
|
|
#endif
|
2012-07-13 23:47:09 +08:00
|
|
|
|
2011-10-05 21:21:28 +08:00
|
|
|
Mat currentMask;
|
2013-08-13 21:45:29 +08:00
|
|
|
if (maskGenerator) {
|
2013-12-13 01:58:42 +08:00
|
|
|
Mat image = _image.getMat();
|
2011-10-05 21:21:28 +08:00
|
|
|
currentMask=maskGenerator->generateMask(image);
|
|
|
|
}
|
|
|
|
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<Rect> candidatesVector;
|
|
|
|
std::vector<int> rejectLevels;
|
|
|
|
std::vector<double> levelWeights;
|
2013-12-13 01:58:42 +08:00
|
|
|
|
|
|
|
int stripCount, stripSize;
|
|
|
|
|
|
|
|
const int PTS_PER_THREAD = 1000;
|
|
|
|
stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
|
|
|
|
stripCount = std::min(std::max(stripCount, 1), 100);
|
|
|
|
stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yStep;
|
|
|
|
|
2011-04-19 17:05:15 +08:00
|
|
|
if( outputRejectLevels )
|
|
|
|
{
|
2012-09-25 16:18:33 +08:00
|
|
|
parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
|
|
|
|
candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));
|
2011-04-19 17:05:15 +08:00
|
|
|
levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );
|
2011-04-22 18:03:05 +08:00
|
|
|
weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );
|
2011-04-19 17:05:15 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-25 16:18:33 +08:00
|
|
|
parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
|
|
|
|
candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
|
2011-04-19 17:05:15 +08:00
|
|
|
}
|
2012-09-25 16:18:33 +08:00
|
|
|
candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2012-07-15 12:11:46 +08:00
|
|
|
#if defined (LOG_CASCADE_STATISTIC)
|
|
|
|
logger.write();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
2010-12-14 18:17:45 +08:00
|
|
|
}
|
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
|
|
|
|
bool CascadeClassifierImpl::ocl_detectSingleScale( InputArray _image, Size processingRectSize,
|
2013-12-17 18:29:30 +08:00
|
|
|
int yStep, double factor, Size sumSize0 )
|
2013-12-13 01:58:42 +08:00
|
|
|
{
|
2013-12-19 18:29:28 +08:00
|
|
|
const int VECTOR_SIZE = 1;
|
2013-12-13 01:58:42 +08:00
|
|
|
Ptr<HaarEvaluator> haar = featureEvaluator.dynamicCast<HaarEvaluator>();
|
|
|
|
if( haar.empty() )
|
|
|
|
return false;
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
haar->setImage(_image, data.origWinSize, sumSize0);
|
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
if( cascadeKernel.empty() )
|
|
|
|
{
|
2013-12-19 18:29:28 +08:00
|
|
|
cascadeKernel.create("runHaarClassifierStump", ocl::objdetect::cascadedetect_oclsrc,
|
2013-12-19 18:09:44 +08:00
|
|
|
format("-D VECTOR_SIZE=%d", VECTOR_SIZE));
|
2013-12-13 01:58:42 +08:00
|
|
|
if( cascadeKernel.empty() )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ustages.empty() )
|
|
|
|
{
|
2013-12-17 18:29:30 +08:00
|
|
|
copyVectorToUMat(data.stages, ustages);
|
2013-12-19 18:09:44 +08:00
|
|
|
copyVectorToUMat(data.stumps, ustumps);
|
2013-12-13 01:58:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<UMat> bufs;
|
|
|
|
haar->getUMats(bufs);
|
2013-12-17 18:29:30 +08:00
|
|
|
CV_Assert(bufs.size() == 3);
|
2013-12-18 00:55:49 +08:00
|
|
|
|
|
|
|
Rect normrect = haar->getNormRect();
|
|
|
|
|
|
|
|
//processingRectSize = Size(yStep, yStep);
|
2013-12-19 18:09:44 +08:00
|
|
|
size_t globalsize[] = { (processingRectSize.width/yStep + VECTOR_SIZE-1)/VECTOR_SIZE, processingRectSize.height/yStep };
|
2013-12-13 01:58:42 +08:00
|
|
|
|
2013-12-18 00:55:49 +08:00
|
|
|
cascadeKernel.args(ocl::KernelArg::ReadOnlyNoSize(bufs[0]), // sum
|
|
|
|
ocl::KernelArg::ReadOnlyNoSize(bufs[1]), // sqsum
|
2013-12-13 01:58:42 +08:00
|
|
|
ocl::KernelArg::PtrReadOnly(bufs[2]), // optfeatures
|
|
|
|
|
|
|
|
// cascade classifier
|
2013-12-18 00:55:49 +08:00
|
|
|
(int)data.stages.size(),
|
2013-12-13 01:58:42 +08:00
|
|
|
ocl::KernelArg::PtrReadOnly(ustages),
|
2013-12-19 18:09:44 +08:00
|
|
|
ocl::KernelArg::PtrReadOnly(ustumps),
|
2013-12-13 01:58:42 +08:00
|
|
|
|
2013-12-18 00:55:49 +08:00
|
|
|
ocl::KernelArg::PtrWriteOnly(ufacepos), // positions
|
|
|
|
processingRectSize,
|
|
|
|
yStep, (float)factor,
|
2013-12-19 18:09:44 +08:00
|
|
|
normrect, data.origWinSize, MAX_FACES);
|
2013-12-18 00:55:49 +08:00
|
|
|
bool ok = cascadeKernel.run(2, globalsize, 0, true);
|
|
|
|
//CV_Assert(ok);
|
|
|
|
return ok;
|
2013-12-13 01:58:42 +08:00
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
bool CascadeClassifierImpl::isOldFormatCascade() const
|
2010-12-14 18:17:45 +08:00
|
|
|
{
|
|
|
|
return !oldCascade.empty();
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
int CascadeClassifierImpl::getFeatureType() const
|
2010-12-14 18:17:45 +08:00
|
|
|
{
|
|
|
|
return featureEvaluator->getFeatureType();
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
Size CascadeClassifierImpl::getOriginalWindowSize() const
|
2010-12-14 18:17:45 +08:00
|
|
|
{
|
|
|
|
return data.origWinSize;
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void* CascadeClassifierImpl::getOldCascade()
|
|
|
|
{
|
|
|
|
return oldCascade;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:25:17 +08:00
|
|
|
static void detectMultiScaleOldFormat( const Mat& image, Ptr<CvHaarClassifierCascade> oldCascade,
|
|
|
|
std::vector<Rect>& objects,
|
|
|
|
std::vector<int>& rejectLevels,
|
|
|
|
std::vector<double>& levelWeights,
|
|
|
|
std::vector<CvAvgComp>& vecAvgComp,
|
|
|
|
double scaleFactor, int minNeighbors,
|
|
|
|
int flags, Size minObjectSize, Size maxObjectSize,
|
|
|
|
bool outputRejectLevels = false )
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-06-14 08:25:17 +08:00
|
|
|
MemStorage storage(cvCreateMemStorage(0));
|
|
|
|
CvMat _image = image;
|
|
|
|
CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
|
|
|
|
minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
|
|
|
|
Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
|
|
|
|
objects.resize(vecAvgComp.size());
|
|
|
|
std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
|
|
|
|
}
|
2010-11-18 17:10:47 +08:00
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
|
|
|
|
void CascadeClassifierImpl::detectMultiScaleNoGrouping( InputArray _image, std::vector<Rect>& candidates,
|
2013-06-14 08:25:17 +08:00
|
|
|
std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
|
|
|
|
double scaleFactor, Size minObjectSize, Size maxObjectSize,
|
|
|
|
bool outputRejectLevels )
|
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
Size imgsz = _image.size();
|
|
|
|
int imgtype = _image.type();
|
|
|
|
|
|
|
|
Mat grayImage, imageBuffer;
|
|
|
|
|
2013-06-14 08:25:17 +08:00
|
|
|
candidates.clear();
|
2013-12-13 01:58:42 +08:00
|
|
|
rejectLevels.clear();
|
|
|
|
levelWeights.clear();
|
|
|
|
|
2010-12-09 23:09:34 +08:00
|
|
|
if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
|
2013-12-13 01:58:42 +08:00
|
|
|
maxObjectSize = imgsz;
|
|
|
|
|
2013-12-18 00:55:49 +08:00
|
|
|
bool use_ocl = ocl::useOpenCL() &&
|
2013-12-13 01:58:42 +08:00
|
|
|
getFeatureType() == FeatureEvaluator::HAAR &&
|
|
|
|
!isOldFormatCascade() &&
|
2013-12-19 18:09:44 +08:00
|
|
|
data.isStumpBased() &&
|
2013-12-13 01:58:42 +08:00
|
|
|
maskGenerator.empty() &&
|
|
|
|
!outputRejectLevels &&
|
2013-12-18 00:55:49 +08:00
|
|
|
tryOpenCL;
|
2013-12-13 01:58:42 +08:00
|
|
|
|
|
|
|
if( !use_ocl )
|
|
|
|
{
|
|
|
|
Mat image = _image.getMat();
|
|
|
|
if (maskGenerator)
|
|
|
|
maskGenerator->initializeMask(image);
|
|
|
|
|
|
|
|
grayImage = image;
|
|
|
|
if( CV_MAT_CN(imgtype) > 1 )
|
|
|
|
{
|
|
|
|
Mat temp;
|
|
|
|
cvtColor(grayImage, temp, COLOR_BGR2GRAY);
|
|
|
|
grayImage = temp;
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
imageBuffer.create(imgsz.height + 1, imgsz.width + 1, CV_8U);
|
|
|
|
}
|
|
|
|
else
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
UMat uimage = _image.getUMat();
|
|
|
|
if( CV_MAT_CN(imgtype) > 1 )
|
|
|
|
cvtColor(uimage, ugrayImage, COLOR_BGR2GRAY);
|
|
|
|
else
|
|
|
|
uimage.copyTo(ugrayImage);
|
|
|
|
uimageBuffer.create(imgsz.height + 1, imgsz.width + 1, CV_8U);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2013-12-17 18:29:30 +08:00
|
|
|
|
|
|
|
Size sumSize0((imgsz.width + SUM_ALIGN) & -SUM_ALIGN, imgsz.height+1);
|
2013-12-18 00:55:49 +08:00
|
|
|
|
|
|
|
if( use_ocl )
|
|
|
|
{
|
|
|
|
ufacepos.create(1, MAX_FACES*4 + 1, CV_32S);
|
|
|
|
UMat ufacecount(ufacepos, Rect(0,0,1,1));
|
|
|
|
ufacecount.setTo(Scalar::all(0));
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
for( double factor = 1; ; factor *= scaleFactor )
|
|
|
|
{
|
2010-12-14 18:17:45 +08:00
|
|
|
Size originalWindowSize = getOriginalWindowSize();
|
2010-12-09 23:09:34 +08:00
|
|
|
|
2010-12-14 18:17:45 +08:00
|
|
|
Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
|
2013-12-18 00:55:49 +08:00
|
|
|
Size scaledImageSize( cvRound( imgsz.width/factor ), cvRound( imgsz.height/factor ) );
|
2013-12-13 01:58:42 +08:00
|
|
|
Size processingRectSize( scaledImageSize.width - originalWindowSize.width,
|
|
|
|
scaledImageSize.height - originalWindowSize.height );
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-12-14 18:17:45 +08:00
|
|
|
if( processingRectSize.width <= 0 || processingRectSize.height <= 0 )
|
2010-05-12 01:44:00 +08:00
|
|
|
break;
|
2010-12-09 23:09:34 +08:00
|
|
|
if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height )
|
2010-11-18 17:10:47 +08:00
|
|
|
break;
|
2010-12-09 23:09:34 +08:00
|
|
|
if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
|
2010-05-12 01:44:00 +08:00
|
|
|
continue;
|
2013-12-13 01:58:42 +08:00
|
|
|
|
2011-09-22 02:03:53 +08:00
|
|
|
int yStep;
|
|
|
|
if( getFeatureType() == cv::FeatureEvaluator::HOG )
|
|
|
|
{
|
|
|
|
yStep = 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
yStep = factor > 2. ? 1 : 2;
|
|
|
|
}
|
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
if( use_ocl )
|
|
|
|
{
|
|
|
|
UMat uscaledImage(uimageBuffer, Rect(0, 0, scaledImageSize.width, scaledImageSize.height));
|
|
|
|
resize( ugrayImage, uscaledImage, scaledImageSize, 0, 0, INTER_LINEAR );
|
|
|
|
|
2013-12-17 18:29:30 +08:00
|
|
|
if( ocl_detectSingleScale( uscaledImage, processingRectSize, yStep, factor, sumSize0 ) )
|
2013-12-13 01:58:42 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/////// if the OpenCL branch has been executed but failed, fall back to CPU: /////
|
|
|
|
|
|
|
|
tryOpenCL = false; // for this cascade do not try OpenCL anymore
|
|
|
|
|
|
|
|
// since we may already have some partial results from OpenCL code (unlikely, but still),
|
|
|
|
// we just recursively call the function again, but with tryOpenCL==false it will
|
|
|
|
// go with CPU route, so there is no infinite recursion
|
|
|
|
detectMultiScaleNoGrouping( _image, candidates, rejectLevels, levelWeights,
|
|
|
|
scaleFactor, minObjectSize, maxObjectSize,
|
|
|
|
outputRejectLevels);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
|
|
|
|
resize( grayImage, scaledImage, scaledImageSize, 0, 0, INTER_LINEAR );
|
|
|
|
|
|
|
|
if( !detectSingleScale( scaledImage, processingRectSize, yStep, factor, candidates,
|
2013-12-17 18:29:30 +08:00
|
|
|
rejectLevels, levelWeights, sumSize0, outputRejectLevels ) )
|
2013-12-13 01:58:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2013-12-17 18:29:30 +08:00
|
|
|
|
|
|
|
if( use_ocl && tryOpenCL )
|
|
|
|
{
|
|
|
|
Mat facepos = ufacepos.getMat(ACCESS_READ);
|
|
|
|
const int* fptr = facepos.ptr<int>();
|
|
|
|
int i, nfaces = fptr[0];
|
|
|
|
for( i = 0; i < nfaces; i++ )
|
|
|
|
{
|
|
|
|
candidates.push_back(Rect(fptr[i*4+1], fptr[i*4+2], fptr[i*4+3], fptr[i*4+4]));
|
|
|
|
}
|
|
|
|
}
|
2013-06-14 08:25:17 +08:00
|
|
|
}
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
|
2013-06-14 08:25:17 +08:00
|
|
|
std::vector<int>& rejectLevels,
|
|
|
|
std::vector<double>& levelWeights,
|
|
|
|
double scaleFactor, int minNeighbors,
|
|
|
|
int flags, Size minObjectSize, Size maxObjectSize,
|
|
|
|
bool outputRejectLevels )
|
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
CV_Assert( scaleFactor > 1 && _image.depth() == CV_8U );
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-06-14 08:25:17 +08:00
|
|
|
if( empty() )
|
|
|
|
return;
|
2010-12-09 23:09:34 +08:00
|
|
|
|
2013-06-14 08:25:17 +08:00
|
|
|
if( isOldFormatCascade() )
|
2011-04-22 18:03:05 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
Mat image = _image.getMat();
|
2013-06-14 08:25:17 +08:00
|
|
|
std::vector<CvAvgComp> fakeVecAvgComp;
|
|
|
|
detectMultiScaleOldFormat( image, oldCascade, objects, rejectLevels, levelWeights, fakeVecAvgComp, scaleFactor,
|
|
|
|
minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
|
2011-04-22 18:03:05 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
detectMultiScaleNoGrouping( _image, objects, rejectLevels, levelWeights, scaleFactor, minObjectSize, maxObjectSize,
|
2013-06-14 08:25:17 +08:00
|
|
|
outputRejectLevels );
|
|
|
|
const double GROUP_EPS = 0.2;
|
|
|
|
if( outputRejectLevels )
|
|
|
|
{
|
|
|
|
groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
groupRectangles( objects, minNeighbors, GROUP_EPS );
|
|
|
|
}
|
2011-04-22 18:03:05 +08:00
|
|
|
}
|
2011-04-19 20:31:35 +08:00
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
|
2011-04-19 20:31:35 +08:00
|
|
|
double scaleFactor, int minNeighbors,
|
|
|
|
int flags, Size minObjectSize, Size maxObjectSize)
|
|
|
|
{
|
2013-11-19 00:48:00 +08:00
|
|
|
Mat image = _image.getMat();
|
2013-02-25 00:14:01 +08:00
|
|
|
std::vector<int> fakeLevels;
|
|
|
|
std::vector<double> fakeWeights;
|
2012-06-09 23:00:04 +08:00
|
|
|
detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor,
|
2013-06-14 08:25:17 +08:00
|
|
|
minNeighbors, flags, minObjectSize, maxObjectSize );
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
|
2013-06-14 08:25:17 +08:00
|
|
|
std::vector<int>& numDetections, double scaleFactor,
|
|
|
|
int minNeighbors, int flags, Size minObjectSize,
|
|
|
|
Size maxObjectSize )
|
|
|
|
{
|
2013-11-19 00:48:00 +08:00
|
|
|
Mat image = _image.getMat();
|
2013-06-14 08:25:17 +08:00
|
|
|
CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
|
|
|
|
|
|
|
|
if( empty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<int> fakeLevels;
|
|
|
|
std::vector<double> fakeWeights;
|
|
|
|
if( isOldFormatCascade() )
|
|
|
|
{
|
|
|
|
std::vector<CvAvgComp> vecAvgComp;
|
|
|
|
detectMultiScaleOldFormat( image, oldCascade, objects, fakeLevels, fakeWeights, vecAvgComp, scaleFactor,
|
|
|
|
minNeighbors, flags, minObjectSize, maxObjectSize );
|
|
|
|
numDetections.resize(vecAvgComp.size());
|
|
|
|
std::transform(vecAvgComp.begin(), vecAvgComp.end(), numDetections.begin(), getNeighbors());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
detectMultiScaleNoGrouping( image, objects, fakeLevels, fakeWeights, scaleFactor, minObjectSize, maxObjectSize );
|
|
|
|
const double GROUP_EPS = 0.2;
|
|
|
|
groupRectangles( objects, numDetections, minNeighbors, GROUP_EPS );
|
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2013-12-19 18:09:44 +08:00
|
|
|
|
|
|
|
CascadeClassifierImpl::Data::Data()
|
|
|
|
{
|
|
|
|
stageType = featureType = ncategories = maxNodesPerTree = 0;
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
bool CascadeClassifierImpl::Data::read(const FileNode &root)
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
2011-05-19 20:41:48 +08:00
|
|
|
static const float THRESHOLD_EPS = 1e-5f;
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
// load stage params
|
2013-03-23 00:37:49 +08:00
|
|
|
String stageTypeStr = (String)root[CC_STAGE_TYPE];
|
2010-05-12 01:44:00 +08:00
|
|
|
if( stageTypeStr == CC_BOOST )
|
|
|
|
stageType = BOOST;
|
|
|
|
else
|
|
|
|
return false;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2013-03-23 00:37:49 +08:00
|
|
|
String featureTypeStr = (String)root[CC_FEATURE_TYPE];
|
2010-05-12 01:44:00 +08:00
|
|
|
if( featureTypeStr == CC_HAAR )
|
|
|
|
featureType = FeatureEvaluator::HAAR;
|
|
|
|
else if( featureTypeStr == CC_LBP )
|
|
|
|
featureType = FeatureEvaluator::LBP;
|
2011-09-22 02:03:53 +08:00
|
|
|
else if( featureTypeStr == CC_HOG )
|
|
|
|
featureType = FeatureEvaluator::HOG;
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
else
|
|
|
|
return false;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
origWinSize.width = (int)root[CC_WIDTH];
|
|
|
|
origWinSize.height = (int)root[CC_HEIGHT];
|
|
|
|
CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
// load feature params
|
|
|
|
FileNode fn = root[CC_FEATURE_PARAMS];
|
|
|
|
if( fn.empty() )
|
|
|
|
return false;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
ncategories = fn[CC_MAX_CAT_COUNT];
|
|
|
|
int subsetSize = (ncategories + 31)/32,
|
|
|
|
nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
// load stages
|
|
|
|
fn = root[CC_STAGES];
|
|
|
|
if( fn.empty() )
|
|
|
|
return false;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
stages.reserve(fn.size());
|
|
|
|
classifiers.clear();
|
|
|
|
nodes.clear();
|
2013-12-19 18:09:44 +08:00
|
|
|
stumps.clear();
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
FileNodeIterator it = fn.begin(), it_end = fn.end();
|
2013-12-19 18:09:44 +08:00
|
|
|
maxNodesPerTree = 0;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
for( int si = 0; it != it_end; si++, ++it )
|
|
|
|
{
|
|
|
|
FileNode fns = *it;
|
|
|
|
Stage stage;
|
2011-05-19 20:41:48 +08:00
|
|
|
stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
|
2010-05-12 01:44:00 +08:00
|
|
|
fns = fns[CC_WEAK_CLASSIFIERS];
|
|
|
|
if(fns.empty())
|
|
|
|
return false;
|
|
|
|
stage.ntrees = (int)fns.size();
|
|
|
|
stage.first = (int)classifiers.size();
|
|
|
|
stages.push_back(stage);
|
|
|
|
classifiers.reserve(stages[si].first + stages[si].ntrees);
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
FileNodeIterator it1 = fns.begin(), it1_end = fns.end();
|
|
|
|
for( ; it1 != it1_end; ++it1 ) // weak trees
|
|
|
|
{
|
|
|
|
FileNode fnw = *it1;
|
|
|
|
FileNode internalNodes = fnw[CC_INTERNAL_NODES];
|
|
|
|
FileNode leafValues = fnw[CC_LEAF_VALUES];
|
|
|
|
if( internalNodes.empty() || leafValues.empty() )
|
|
|
|
return false;
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
DTree tree;
|
|
|
|
tree.nodeCount = (int)internalNodes.size()/nodeStep;
|
2013-12-19 18:09:44 +08:00
|
|
|
maxNodesPerTree = std::max(maxNodesPerTree, tree.nodeCount);
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
classifiers.push_back(tree);
|
2010-12-14 18:17:45 +08:00
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
nodes.reserve(nodes.size() + tree.nodeCount);
|
|
|
|
leaves.reserve(leaves.size() + leafValues.size());
|
|
|
|
if( subsetSize > 0 )
|
|
|
|
subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);
|
2010-12-14 18:17:45 +08:00
|
|
|
|
|
|
|
FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();
|
|
|
|
|
|
|
|
for( ; internalNodesIter != internalNodesEnd; ) // nodes
|
2010-05-12 01:44:00 +08:00
|
|
|
{
|
|
|
|
DTreeNode node;
|
2010-12-14 18:17:45 +08:00
|
|
|
node.left = (int)*internalNodesIter; ++internalNodesIter;
|
|
|
|
node.right = (int)*internalNodesIter; ++internalNodesIter;
|
|
|
|
node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
|
2010-05-12 01:44:00 +08:00
|
|
|
if( subsetSize > 0 )
|
|
|
|
{
|
2010-12-14 18:17:45 +08:00
|
|
|
for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
|
|
|
|
subsets.push_back((int)*internalNodesIter);
|
2010-05-12 01:44:00 +08:00
|
|
|
node.threshold = 0.f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-12-14 18:17:45 +08:00
|
|
|
node.threshold = (float)*internalNodesIter; ++internalNodesIter;
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
nodes.push_back(node);
|
|
|
|
}
|
2010-12-14 18:17:45 +08:00
|
|
|
|
|
|
|
internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();
|
|
|
|
|
|
|
|
for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves
|
|
|
|
leaves.push_back((float)*internalNodesIter);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
|
|
|
}
|
2013-12-19 18:09:44 +08:00
|
|
|
|
|
|
|
if( isStumpBased() )
|
|
|
|
{
|
|
|
|
int nodeOfs = 0, leafOfs = 0;
|
|
|
|
size_t nstages = stages.size();
|
|
|
|
for( size_t stageIdx = 0; stageIdx < nstages; stageIdx++ )
|
|
|
|
{
|
|
|
|
const Stage& stage = stages[stageIdx];
|
|
|
|
|
|
|
|
int ntrees = stage.ntrees;
|
|
|
|
for( int i = 0; i < ntrees; i++, nodeOfs++, leafOfs+= 2 )
|
|
|
|
{
|
|
|
|
const DTreeNode& node = nodes[nodeOfs];
|
|
|
|
stumps.push_back(Stump(node.featureIdx, node.threshold,
|
|
|
|
leaves[leafOfs], leaves[leafOfs+1]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2010-12-14 18:17:45 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-12-13 01:58:42 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
bool CascadeClassifierImpl::read_(const FileNode& root)
|
2010-12-14 18:17:45 +08:00
|
|
|
{
|
2013-12-13 01:58:42 +08:00
|
|
|
tryOpenCL = true;
|
|
|
|
cascadeKernel = ocl::Kernel();
|
|
|
|
ustages.release();
|
2013-12-19 18:09:44 +08:00
|
|
|
ustumps.release();
|
2010-12-14 18:17:45 +08:00
|
|
|
if( !data.read(root) )
|
|
|
|
return false;
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
// load features
|
2010-12-14 18:17:45 +08:00
|
|
|
featureEvaluator = FeatureEvaluator::create(data.featureType);
|
|
|
|
FileNode fn = root[CC_FEATURES];
|
2010-05-12 01:44:00 +08:00
|
|
|
if( fn.empty() )
|
|
|
|
return false;
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2010-12-14 18:17:45 +08:00
|
|
|
return featureEvaluator->read(fn);
|
2010-05-12 01:44:00 +08:00
|
|
|
}
|
2012-06-09 23:00:04 +08:00
|
|
|
|
2013-08-13 21:45:29 +08:00
|
|
|
template<> void DefaultDeleter<CvHaarClassifierCascade>::operator ()(CvHaarClassifierCascade* obj) const
|
2012-06-09 23:00:04 +08:00
|
|
|
{ cvReleaseHaarClassifierCascade(&obj); }
|
2010-05-12 01:44:00 +08:00
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
|
|
|
|
BaseCascadeClassifier::~BaseCascadeClassifier()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CascadeClassifier::CascadeClassifier() {}
|
|
|
|
CascadeClassifier::CascadeClassifier(const String& filename)
|
|
|
|
{
|
|
|
|
load(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
CascadeClassifier::~CascadeClassifier()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CascadeClassifier::empty() const
|
|
|
|
{
|
|
|
|
return cc.empty() || cc->empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CascadeClassifier::load( const String& filename )
|
|
|
|
{
|
|
|
|
cc = makePtr<CascadeClassifierImpl>();
|
|
|
|
if(!cc->load(filename))
|
|
|
|
cc.release();
|
|
|
|
return !empty();
|
|
|
|
}
|
|
|
|
|
2013-12-05 01:56:35 +08:00
|
|
|
bool CascadeClassifier::read(const FileNode &root)
|
|
|
|
{
|
|
|
|
Ptr<CascadeClassifierImpl> ccimpl;
|
|
|
|
bool ok = ccimpl->read_(root);
|
|
|
|
if( ok )
|
|
|
|
cc = ccimpl.staticCast<BaseCascadeClassifier>();
|
|
|
|
else
|
|
|
|
cc.release();
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2013-12-04 23:00:39 +08:00
|
|
|
void CascadeClassifier::detectMultiScale( InputArray image,
|
|
|
|
CV_OUT std::vector<Rect>& objects,
|
|
|
|
double scaleFactor,
|
|
|
|
int minNeighbors, int flags,
|
|
|
|
Size minSize,
|
|
|
|
Size maxSize )
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
cc->detectMultiScale(image, objects, scaleFactor, minNeighbors, flags, minSize, maxSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CascadeClassifier::detectMultiScale( InputArray image,
|
|
|
|
CV_OUT std::vector<Rect>& objects,
|
|
|
|
CV_OUT std::vector<int>& numDetections,
|
|
|
|
double scaleFactor,
|
|
|
|
int minNeighbors, int flags,
|
|
|
|
Size minSize, Size maxSize )
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
cc->detectMultiScale(image, objects, numDetections,
|
|
|
|
scaleFactor, minNeighbors, flags, minSize, maxSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CascadeClassifier::detectMultiScale( InputArray image,
|
|
|
|
CV_OUT std::vector<Rect>& objects,
|
|
|
|
CV_OUT std::vector<int>& rejectLevels,
|
|
|
|
CV_OUT std::vector<double>& levelWeights,
|
|
|
|
double scaleFactor,
|
|
|
|
int minNeighbors, int flags,
|
|
|
|
Size minSize, Size maxSize,
|
|
|
|
bool outputRejectLevels )
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
cc->detectMultiScale(image, objects, rejectLevels, levelWeights,
|
|
|
|
scaleFactor, minNeighbors, flags,
|
|
|
|
minSize, maxSize, outputRejectLevels);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CascadeClassifier::isOldFormatCascade() const
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
return cc->isOldFormatCascade();
|
|
|
|
}
|
|
|
|
|
|
|
|
Size CascadeClassifier::getOriginalWindowSize() const
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
return cc->getOriginalWindowSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
int CascadeClassifier::getFeatureType() const
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
return cc->getFeatureType();
|
|
|
|
}
|
|
|
|
|
|
|
|
void* CascadeClassifier::getOldCascade()
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
return cc->getOldCascade();
|
|
|
|
}
|
|
|
|
|
2013-12-05 01:56:35 +08:00
|
|
|
void CascadeClassifier::setMaskGenerator(const Ptr<BaseCascadeClassifier::MaskGenerator>& maskGenerator)
|
2013-12-04 23:00:39 +08:00
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
cc->setMaskGenerator(maskGenerator);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ptr<BaseCascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
|
|
|
|
{
|
|
|
|
CV_Assert(!empty());
|
|
|
|
return cc->getMaskGenerator();
|
|
|
|
}
|
|
|
|
|
2010-05-12 01:44:00 +08:00
|
|
|
} // namespace cv
|