Commit 3f516c6d9292b0d1277c90ac2a54297b15cec2a3

Authored by Jordan Cheney
1 parent 6b74e8ac

Fixed merge issue and reverted back to the wisdom of @sklum

openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -15,10 +15,11 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <openbr/plugins/openbr_internal.h>
18   -#include <openbr/core/cascade.h>
19 18 #include <openbr/core/opencvutils.h>
20 19 #include <openbr/core/qtutils.h>
21 20  
  21 +#include <opencv2/imgproc/imgproc.hpp>
  22 +
22 23 using namespace cv;
23 24  
24 25 namespace br
... ... @@ -40,6 +41,7 @@ class SlidingWindowTransform : public MetaTransform
40 41 Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
41 42 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
42 43 Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
  44 + Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false)
43 45 Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false)
44 46  
45 47 BR_PROPERTY(br::Classifier *, classifier, NULL)
... ... @@ -47,6 +49,7 @@ class SlidingWindowTransform : public MetaTransform
47 49 BR_PROPERTY(int, maxSize, -1)
48 50 BR_PROPERTY(float, scaleFactor, 1.2)
49 51 BR_PROPERTY(int, minNeighbors, 5)
  52 + BR_PROPERTY(float, confidenceThreshold, 2)
50 53 BR_PROPERTY(float, eps, 0.2)
51 54  
52 55 void train(const TemplateList &data)
... ... @@ -128,7 +131,7 @@ class SlidingWindowTransform : public MetaTransform
128 131 }
129 132 }
130 133  
131   - OpenCVUtils::group(rects, confidences, eps);
  134 + OpenCVUtils::group(rects, confidences, confidenceThreshold, eps);
132 135  
133 136 if (!enrollAll && rects.empty())
134 137 rects.push_back(Rect(0, 0, m.cols, m.rows));
... ...
openbr/plugins/metadata/cascade.cpp
... ... @@ -15,20 +15,150 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16 #include <QProcess>
17 17 #include <QTemporaryFile>
18   -#include <fstream>
  18 +#include <opencv2/objdetect/objdetect.hpp>
19 19  
20 20 #include <openbr/plugins/openbr_internal.h>
21   -#include <openbr/core/cascade.h>
22 21 #include <openbr/core/opencvutils.h>
23 22 #include <openbr/core/resource.h>
24 23 #include <openbr/core/qtutils.h>
25 24  
26 25 using namespace cv;
  26 +
  27 +struct TrainParams
  28 +{
  29 + QString data; // REQUIRED: Filepath to store trained classifier
  30 + QString vec; // REQUIRED: Filepath to store vector of positive samples, default "vector"
  31 + QString img; // Filepath to source object image. Either this or info is REQUIRED
  32 + QString info; // Description file of source images. Either this or img is REQUIRED
  33 + QString bg; // REQUIRED: Filepath to background list file
  34 + int num; // Number of samples to generate
  35 + int bgcolor; // Background color supplied image (via img)
  36 + int bgthresh; // Threshold to determine bgcolor match
  37 + bool inv; // Invert colors
  38 + bool randinv; // Randomly invert colors
  39 + int maxidev; // Max intensity deviation of foreground pixels
  40 + double maxxangle; // Maximum rotation angle (X)
  41 + double maxyangle; // Maximum rotation angle (Y)
  42 + double maxzangle; // Maximum rotation angle (Z)
  43 + bool show; // Show generated samples
  44 + int w; // REQUIRED: Sample width
  45 + int h; // REQUIRED: Sample height
  46 + int numPos; // Number of positive samples
  47 + int numNeg; // Number of negative samples
  48 + int numStages; // Number of stages
  49 + int precalcValBufSize; // Precalculated val buffer size in Mb
  50 + int precalcIdxBufSize; // Precalculated index buffer size in Mb
  51 + bool baseFormatSave; // Save in old format
  52 + QString stageType; // Stage type (BOOST)
  53 + QString featureType; // Feature type (HAAR, LBP)
  54 + QString bt; // Boosted classifier type (DAB, RAB, LB, GAB)
  55 + double minHitRate; // Minimal hit rate per stage
  56 + double maxFalseAlarmRate; // Max false alarm rate per stage
  57 + double weightTrimRate; // Weight for trimming
  58 + int maxDepth; // Max weak tree depth
  59 + int maxWeakCount; // Max weak tree count per stage
  60 + QString mode; // Haar feature mode (BASIC, CORE, ALL)
  61 +
  62 + TrainParams()
  63 + {
  64 + num = -1;
  65 + maxidev = -1;
  66 + maxxangle = -1;
  67 + maxyangle = -1;
  68 + maxzangle = -1;
  69 + w = -1;
  70 + h = -1;
  71 + numPos = -1;
  72 + numNeg = -1;
  73 + numStages = -1;
  74 + precalcValBufSize = -1;
  75 + precalcIdxBufSize = -1;
  76 + minHitRate = -1;
  77 + maxFalseAlarmRate = -1;
  78 + weightTrimRate = -1;
  79 + maxDepth = -1;
  80 + maxWeakCount = -1;
  81 + inv = false;
  82 + randinv = false;
  83 + show = false;
  84 + baseFormatSave = false;
  85 + vec = "vector.vec";
  86 + bgcolor = -1;
  87 + bgthresh = -1;
  88 + }
  89 +};
  90 +
  91 +static QStringList buildTrainingArgs(const TrainParams &params)
  92 +{
  93 + QStringList args;
  94 + if (params.data != "") args << "-data" << params.data;
  95 + else qFatal("Must specify storage location for cascade");
  96 + if (params.vec != "") args << "-vec" << params.vec;
  97 + else qFatal("Must specify location of positive vector");
  98 + if (params.bg != "") args << "-bg" << params.bg;
  99 + else qFatal("Must specify negative images");
  100 + if (params.numPos >= 0) args << "-numPos" << QString::number(params.numPos);
  101 + if (params.numNeg >= 0) args << "-numNeg" << QString::number(params.numNeg);
  102 + if (params.numStages >= 0) args << "-numStages" << QString::number(params.numStages);
  103 + if (params.precalcValBufSize >= 0) args << "-precalcValBufSize" << QString::number(params.precalcValBufSize);
  104 + if (params.precalcIdxBufSize >= 0) args << "-precalcIdxBufSize" << QString::number(params.precalcIdxBufSize);
  105 + if (params.baseFormatSave) args << "-baseFormatSave";
  106 + if (params.stageType != "") args << "-stageType" << params.stageType;
  107 + if (params.featureType != "") args << "-featureType" << params.featureType;
  108 + if (params.w >= 0) args << "-w" << QString::number(params.w);
  109 + else qFatal("Must specify width");
  110 + if (params.h >= 0) args << "-h" << QString::number(params.h);
  111 + else qFatal("Must specify height");
  112 + if (params.bt != "") args << "-bt" << params.bt;
  113 + if (params.minHitRate >= 0) args << "-minHitRate" << QString::number(params.minHitRate);
  114 + if (params.maxFalseAlarmRate >= 0) args << "-maxFalseAlarmRate" << QString::number(params.maxFalseAlarmRate);
  115 + if (params.weightTrimRate >= 0) args << "-weightTrimRate" << QString::number(params.weightTrimRate);
  116 + if (params.maxDepth >= 0) args << "-maxDepth" << QString::number(params.maxDepth);
  117 + if (params.maxWeakCount >= 0) args << "-maxWeakCount" << QString::number(params.maxWeakCount);
  118 + if (params.mode != "") args << "-mode" << params.mode;
  119 + return args;
  120 +}
  121 +
  122 +static QStringList buildSampleArgs(const TrainParams &params)
  123 +{
  124 + QStringList args;
  125 + if (params.vec != "") args << "-vec" << params.vec;
  126 + else qFatal("Must specify location of positive vector");
  127 + if (params.img != "") args << "-img" << params.img;
  128 + else if (params.info != "") args << "-info" << params.info;
  129 + else qFatal("Must specify positive images");
  130 + if (params.bg != "") args << "-bg" << params.bg;
  131 + if (params.num > 0) args << "-num" << QString::number(params.num);
  132 + if (params.bgcolor >=0 ) args << "-bgcolor" << QString::number(params.bgcolor);
  133 + if (params.bgthresh >= 0) args << "-bgthresh" << QString::number(params.bgthresh);
  134 + if (params.maxidev >= 0) args << "-maxidev" << QString::number(params.maxidev);
  135 + if (params.maxxangle >= 0) args << "-maxxangle" << QString::number(params.maxxangle);
  136 + if (params.maxyangle >= 0) args << "-maxyangle" << QString::number(params.maxyangle);
  137 + if (params.maxzangle >= 0) args << "-maxzangle" << QString::number(params.maxzangle);
  138 + if (params.w >= 0) args << "-w" << QString::number(params.w);
  139 + if (params.h >= 0) args << "-h" << QString::number(params.h);
  140 + if (params.show) args << "-show";
  141 + if (params.inv) args << "-inv";
  142 + if (params.randinv) args << "-randinv";
  143 + return args;
  144 +}
  145 +
  146 +static void genSamples(const TrainParams &params)
  147 +{
  148 + const QStringList cmdArgs = buildSampleArgs(params);
  149 + QProcess::execute("opencv_createsamples",cmdArgs);
  150 +}
  151 +
  152 +static void trainCascade(const TrainParams &params)
  153 +{
  154 + const QStringList cmdArgs = buildTrainingArgs(params);
  155 + QProcess::execute("opencv_traincascade", cmdArgs);
  156 +}
27 157  
28 158 namespace br
29 159 {
30 160  
31   -class CascadeResourceMaker : public ResourceMaker<_CascadeClassifier>
  161 +class CascadeResourceMaker : public ResourceMaker<CascadeClassifier>
32 162 {
33 163 QString file;
34 164  
... ... @@ -49,9 +179,9 @@ public:
49 179 }
50 180  
51 181 private:
52   - _CascadeClassifier *make() const
  182 + CascadeClassifier *make() const
53 183 {
54   - _CascadeClassifier *cascade = new _CascadeClassifier();
  184 + CascadeClassifier *cascade = new CascadeClassifier();
55 185 if (!cascade->load(file.toStdString()))
56 186 qFatal("Failed to load: %s", qPrintable(file));
57 187 return cascade;
... ... @@ -64,20 +194,60 @@ private:
64 194 * \author Josh Klontz \cite jklontz
65 195 * \author David Crouse \cite dgcrouse
66 196 */
67   -class CascadeTransform : public UntrainableMetaTransform
  197 +class CascadeTransform : public MetaTransform
68 198 {
69 199 Q_OBJECT
70 200 Q_PROPERTY(QString model READ get_model WRITE set_model RESET reset_model STORED false)
71 201 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
72 202 Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
73 203 Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false)
  204 +
  205 + // Training parameters
  206 + Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
  207 + Q_PROPERTY(int w READ get_w WRITE set_w RESET reset_w STORED false)
  208 + Q_PROPERTY(int h READ get_h WRITE set_h RESET reset_h STORED false)
  209 + Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false)
  210 + Q_PROPERTY(int numNeg READ get_numNeg WRITE set_numNeg RESET reset_numNeg STORED false)
  211 + Q_PROPERTY(int precalcValBufSize READ get_precalcValBufSize WRITE set_precalcValBufSize RESET reset_precalcValBufSize STORED false)
  212 + Q_PROPERTY(int precalcIdxBufSize READ get_precalcIdxBufSize WRITE set_precalcIdxBufSize RESET reset_precalcIdxBufSize STORED false)
  213 + Q_PROPERTY(double minHitRate READ get_minHitRate WRITE set_minHitRate RESET reset_minHitRate STORED false)
  214 + Q_PROPERTY(double maxFalseAlarmRate READ get_maxFalseAlarmRate WRITE set_maxFalseAlarmRate RESET reset_maxFalseAlarmRate STORED false)
  215 + Q_PROPERTY(double weightTrimRate READ get_weightTrimRate WRITE set_weightTrimRate RESET reset_weightTrimRate STORED false)
  216 + Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false)
  217 + Q_PROPERTY(int maxWeakCount READ get_maxWeakCount WRITE set_maxWeakCount RESET reset_maxWeakCount STORED false)
  218 + Q_PROPERTY(QString stageType READ get_stageType WRITE set_stageType RESET reset_stageType STORED false)
  219 + Q_PROPERTY(QString featureType READ get_featureType WRITE set_featureType RESET reset_featureType STORED false)
  220 + Q_PROPERTY(QString bt READ get_bt WRITE set_bt RESET reset_bt STORED false)
  221 + Q_PROPERTY(QString mode READ get_mode WRITE set_mode RESET reset_mode STORED false)
  222 + Q_PROPERTY(bool show READ get_show WRITE set_show RESET reset_show STORED false)
  223 + Q_PROPERTY(bool baseFormatSave READ get_baseFormatSave WRITE set_baseFormatSave RESET reset_baseFormatSave STORED false)
74 224  
75 225 BR_PROPERTY(QString, model, "FrontalFace")
76 226 BR_PROPERTY(int, minSize, 64)
77 227 BR_PROPERTY(int, minNeighbors, 5)
78 228 BR_PROPERTY(bool, ROCMode, false)
  229 +
  230 + // Training parameters - Default values provided trigger OpenCV defaults
  231 + BR_PROPERTY(int, numStages, -1)
  232 + BR_PROPERTY(int, w, -1)
  233 + BR_PROPERTY(int, h, -1)
  234 + BR_PROPERTY(int, numPos, -1)
  235 + BR_PROPERTY(int, numNeg, -1)
  236 + BR_PROPERTY(int, precalcValBufSize, -1)
  237 + BR_PROPERTY(int, precalcIdxBufSize, -1)
  238 + BR_PROPERTY(double, minHitRate, -1)
  239 + BR_PROPERTY(double, maxFalseAlarmRate, -1)
  240 + BR_PROPERTY(double, weightTrimRate, -1)
  241 + BR_PROPERTY(int, maxDepth, -1)
  242 + BR_PROPERTY(int, maxWeakCount, -1)
  243 + BR_PROPERTY(QString, stageType, "")
  244 + BR_PROPERTY(QString, featureType, "")
  245 + BR_PROPERTY(QString, bt, "")
  246 + BR_PROPERTY(QString, mode, "")
  247 + BR_PROPERTY(bool, show, false)
  248 + BR_PROPERTY(bool, baseFormatSave, false)
79 249  
80   - Resource<_CascadeClassifier> cascadeResource;
  250 + Resource<CascadeClassifier> cascadeResource;
81 251  
82 252 void init()
83 253 {
... ... @@ -85,6 +255,120 @@ class CascadeTransform : public UntrainableMetaTransform
85 255 if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace")
86 256 this->trainable = false;
87 257 }
  258 +
  259 + // Train transform
  260 + void train(const TemplateList& data)
  261 + {
  262 + // Don't train if we're using OpenCV's prebuilt cascades
  263 + if (model == "Ear" || model == "Eye" || model == "FrontalFace" || model == "ProfileFace")
  264 + return;
  265 +
  266 + // Open positive and negative list temporary files
  267 + QTemporaryFile posFile;
  268 + QTemporaryFile negFile;
  269 +
  270 + posFile.open();
  271 + negFile.open();
  272 +
  273 + QTextStream posStream(&posFile);
  274 + QTextStream negStream(&negFile);
  275 +
  276 + TrainParams params;
  277 +
  278 + // Fill in from params (param defaults are same as struct defaults, so no checks are needed)
  279 + params.numStages = numStages;
  280 + params.w = w;
  281 + params.h = h;
  282 + params.numPos = numPos;
  283 + params.numNeg = numNeg;
  284 + params.precalcValBufSize = precalcValBufSize;
  285 + params.precalcIdxBufSize = precalcIdxBufSize;
  286 + params.minHitRate = minHitRate;
  287 + params.maxFalseAlarmRate = maxFalseAlarmRate;
  288 + params.weightTrimRate = weightTrimRate;
  289 + params.maxDepth = maxDepth;
  290 + params.maxWeakCount = maxWeakCount;
  291 + params.stageType = stageType;
  292 + params.featureType = featureType;
  293 + params.bt = bt;
  294 + params.mode = mode;
  295 + params.show = show;
  296 + params.baseFormatSave = baseFormatSave;
  297 + if (params.w < 0) params.w = minSize;
  298 + if (params.h < 0) params.h = minSize;
  299 +
  300 + int posCount = 0;
  301 + int negCount = 0;
  302 +
  303 + bool buildPos = false; // If true, build positive vector from single image
  304 +
  305 + const FileList files = data.files();
  306 +
  307 + for (int i = 0; i < files.length(); i++) {
  308 + File f = files[i];
  309 + if (f.contains("training-set")) {
  310 + QString tset = f.get<QString>("training-set",QString()).toLower();
  311 +
  312 + // Negative samples
  313 + if (tset == "neg") {
  314 + negStream << f.path() << QDir::separator() << f.fileName() << endl;
  315 + negCount++;
  316 + // Positive samples for crop/rescale
  317 + } else if (tset == "pos") {
  318 + QString buffer = "";
  319 +
  320 + // Extract rectangles
  321 + QList<QRectF> rects = f.rects();
  322 + for (int j = 0; j < rects.size(); j++) {
  323 + QRectF r = rects[j];
  324 + buffer += " " + QString::number(r.x()) + " " + QString::number(r.y()) + " " + QString::number(r.width()) + " "+ QString::number(r.height());
  325 + posCount++;
  326 + }
  327 +
  328 + posStream << f.path() << QDir::separator() << f.fileName() << " " << f.rects().length() << " " << buffer << endl;
  329 +
  330 + // Single positive sample for background removal and overlay on negatives
  331 + } else if (tset == "pos-base") {
  332 + buildPos = true;
  333 + params.img = f.path() + QDir::separator() + f.fileName();
  334 +
  335 + // Parse settings (unique to this one tag)
  336 + if (f.contains("num")) params.num = f.get<int>("num");
  337 + if (f.contains("bgcolor")) params.bgcolor = f.get<int>("bgcolor");
  338 + if (f.contains("bgthresh")) params.bgthresh =f.get<int>("bgthresh");
  339 + if (f.contains("inv")) params.inv = f.get<bool>("inv",false);
  340 + if (f.contains("randinv")) params.randinv = f.get<bool>("randinv",false);
  341 + if (f.contains("maxidev")) params.maxidev = f.get<int>("maxidev");
  342 + if (f.contains("maxxangle")) params.maxxangle = f.get<double>("maxxangle");
  343 + if (f.contains("maxyangle")) params.maxyangle = f.get<double>("maxyangle");
  344 + if (f.contains("maxzangle")) params.maxzangle = f.get<double>("maxzangle");
  345 + }
  346 + }
  347 + }
  348 +
  349 + posFile.close();
  350 + negFile.close();
  351 +
  352 + // Fill in remaining params conditionally
  353 + if (buildPos) {
  354 + if (params.numPos < 0) {
  355 + if (params.num > 0) params.numPos = params.num*.95;
  356 + else params.numPos = 950;
  357 + }
  358 + } else {
  359 + params.info = posFile.fileName();
  360 + if (params.numPos < 0) params.numPos = posCount*.95;
  361 + }
  362 +
  363 + if (params.num < 0) params.num = posCount;
  364 + if (params.numNeg < 0) params.numNeg = negCount*10;
  365 +
  366 + params.bg = negFile.fileName();
  367 + params.data = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model + "/cascade.xml";
  368 +
  369 + genSamples(params);
  370 + trainCascade(params);
  371 + }
88 372  
89 373 void project(const Template &src, Template &dst) const
90 374 {
... ... @@ -95,7 +379,7 @@ class CascadeTransform : public UntrainableMetaTransform
95 379  
96 380 void project(const TemplateList &src, TemplateList &dst) const
97 381 {
98   - _CascadeClassifier *cascade = cascadeResource.acquire();
  382 + CascadeClassifier *cascade = cascadeResource.acquire();
99 383 foreach (const Template &t, src) {
100 384 const bool enrollAll = t.file.getBool("enrollAll");
101 385  
... ... @@ -112,7 +396,8 @@ class CascadeTransform : public UntrainableMetaTransform
112 396 std::vector<Rect> rects;
113 397 std::vector<int> rejectLevels;
114 398 std::vector<double> levelWeights;
115   - cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, Size(minSize, minSize), Size());
  399 + if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, (enrollAll ? 0 : CASCADE_FIND_BIGGEST_OBJECT) | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true);
  400 + else cascade->detectMultiScale(m, rects, 1.2, minNeighbors, enrollAll ? 0 : CASCADE_FIND_BIGGEST_OBJECT, Size(minSize, minSize));
116 401  
117 402 if (!enrollAll && rects.empty())
118 403 rects.push_back(Rect(0, 0, m.cols, m.rows));
... ...