Commit 0239196ada49e19f60023aecd32b1ff6607233ea

Authored by Josh Klontz
1 parent a301132a

removed cascade classifier

openbr/plugins/classification/cascade_classifier.cpp deleted
1   -#include <opencv2/imgproc/imgproc.hpp>
2   -#include <opencv2/highgui/highgui.hpp>
3   -
4   -#include <openbr/plugins/openbr_internal.h>
5   -#include <openbr/core/common.h>
6   -#include "openbr/core/opencvutils.h"
7   -
8   -#include <QtConcurrent>
9   -
10   -using namespace cv;
11   -
12   -namespace br
13   -{
14   -
15   -struct Miner
16   -{
17   - Template src;
18   - Template scaledSrc;
19   - Size windowSize;
20   - Point offset, point;
21   - float scale, scaleFactor, stepFactor;
22   -
23   - Miner(const Template &t, const Size &windowSize, const Point &offset) :
24   - src(t),
25   - windowSize(windowSize),
26   - offset(offset),
27   - point(offset)
28   - {
29   - scale = 1.0F;
30   - scaleFactor = 1.4142135623730950488016887242097F;
31   - stepFactor = 0.5F;
32   -
33   - scale = max(((float)windowSize.width + point.x) / ((float)src.m().cols),
34   - ((float)windowSize.height + point.y) / ((float)src.m().rows));
35   - Size size((int)(scale*src.m().cols + 0.5F), (int)(scale*src.m().rows + 0.5F));
36   - scaledSrc = resize(src, size);
37   - }
38   -
39   - Template resize(const Template &src, const Size &size)
40   - {
41   - Template dst(src.file);
42   - for (int i=0; i<src.size(); i++) {
43   - Mat buffer;
44   - cv::resize(src[i], buffer, size);
45   - dst.append(buffer);
46   - }
47   - return dst;
48   - }
49   -
50   - Template mine(bool *newImg)
51   - {
52   - Template dst(src.file);
53   - // Copy region of winSize region of img into m
54   - for (int i=0; i<scaledSrc.size(); i++) {
55   - Mat window(windowSize.height, windowSize.width, CV_8U,
56   - (void*)(scaledSrc[i].data + point.y * scaledSrc[i].step + point.x * scaledSrc[i].elemSize()),
57   - scaledSrc[i].step);
58   - Mat sample;
59   - window.copyTo(sample);
60   - dst.append(sample);
61   - }
62   -
63   - if ((int)(point.x + (1.0F + stepFactor) * windowSize.width) < scaledSrc.m().cols)
64   - point.x += (int)(stepFactor * windowSize.width);
65   - else {
66   - point.x = offset.x;
67   - if ((int)(point.y + (1.0F + stepFactor) * windowSize.height) < scaledSrc.m().rows)
68   - point.y += (int)(stepFactor * windowSize.height);
69   - else {
70   - point.y = offset.y;
71   - scale *= scaleFactor;
72   - if (scale <= 1.0F) {
73   - Size size((int)(scale*src.m().cols), (int)(scale*src.m().rows));
74   - scaledSrc = resize(src, size);
75   - } else {
76   - *newImg = true;
77   - return dst;
78   - }
79   - }
80   - }
81   -
82   - *newImg = false;
83   - return dst;
84   - }
85   -};
86   -
87   -/*!
88   - * \brief A meta Classifier that creates a cascade of another Classifier. The cascade is a series of stages, each with its own instance of a given classifier. A sample can only reach the next stage if it is classified as positive by the previous stage.
89   - * \author Jordan Cheney \cite jcheney
90   - * \author Scott Klum \cite sklum
91   - * \br_property int numStages The number of stages in the cascade
92   - * \br_property int numPos The number of positives to feed each stage during training
93   - * \br_property int numNegs The number of negatives to feed each stage during training. A negative sample must have been classified by the previous stages in the cascade as positive to be fed to the next stage during training.
94   - * \br_property float maxFAR A termination parameter. Calculated as (number of passed negatives) / (total number of checked negatives) for a given stage during training. If that number is below the given maxFAR cascade training is terminated early. This can help prevent overfitting.
95   - * \br_property bool requireAllStages If true, the cascade will train until it has the number of stages specified by numStages even if the FAR at a given is lower than maxFAR.
96   - * \br_property int maxStage Parameter to limit the stages used at test time. If -1 (default), all numStages stages will be used.
97   - * \br_paper Paul Viola, Michael Jones
98   - * Rapid Object Detection using a Boosted Cascade of Simple Features
99   - * CVPR, 2001
100   - * \br_link Rapid Object Detection using a Boosted Cascade of Simple Features https://www.cs.cmu.edu/~efros/courses/LBMV07/Papers/viola-cvpr-01.pdf
101   - */
102   -class CascadeClassifier : public Classifier
103   -{
104   - Q_OBJECT
105   -
106   - Q_PROPERTY(QString stageDescription READ get_stageDescription WRITE set_stageDescription RESET reset_stageDescription STORED false)
107   - Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
108   - Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false)
109   - Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false)
110   - Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
111   - Q_PROPERTY(bool requireAllStages READ get_requireAllStages WRITE set_requireAllStages RESET reset_requireAllStages STORED false)
112   - Q_PROPERTY(int maxStage READ get_maxStage WRITE set_maxStage RESET reset_maxStage STORED false)
113   - Q_PROPERTY(QList<br::Classifier*> stages READ get_stages WRITE set_stages RESET reset_stages STORED false)
114   -
115   - BR_PROPERTY(QString, stageDescription, "")
116   - BR_PROPERTY(int, numStages, 20)
117   - BR_PROPERTY(int, numPos, 1000)
118   - BR_PROPERTY(int, numNegs, 1000)
119   - BR_PROPERTY(float, maxFAR, pow(0.5, numStages))
120   - BR_PROPERTY(bool, requireAllStages, false)
121   - BR_PROPERTY(int, maxStage, -1)
122   - BR_PROPERTY(QList<br::Classifier*>, stages, QList<br::Classifier*>())
123   -
124   - TemplateList posImages, negImages;
125   - TemplateList posSamples, negSamples;
126   -
127   - QList<int> negIndices, posIndices;
128   - int negIndex, posIndex, samplingRound;
129   -
130   - QMutex samplingMutex, miningMutex;
131   -
132   - void init()
133   - {
134   - negIndex = posIndex = samplingRound = 0;
135   - }
136   -
137   - bool getPositive(Template &img)
138   - {
139   - if (posIndex >= posImages.size())
140   - return false;
141   - img = posImages[posIndices[posIndex++]];
142   - return true;
143   - }
144   -
145   - Template getNegative(Point &offset)
146   - {
147   - Template negative;
148   -
149   - const Size size = windowSize();
150   - // Grab negative from list
151   - int count = negImages.size();
152   - for (int i = 0; i < count; i++) {
153   - negative = negImages[negIndices[negIndex++]];
154   -
155   - samplingRound += negIndex / count;
156   - samplingRound = samplingRound % (size.width * size.height);
157   - negIndex %= count;
158   -
159   - offset.x = qMin( (int)samplingRound % size.width, negative.m().cols - size.width);
160   - offset.y = qMin( (int)samplingRound / size.width, negative.m().rows - size.height);
161   - if (!negative.m().empty() && negative.m().type() == CV_8U
162   - && offset.x >= 0 && offset.y >= 0)
163   - break;
164   - }
165   -
166   - return negative;
167   - }
168   -
169   - uint64 mine()
170   - {
171   - uint64 passedNegatives = 0;
172   - forever {
173   - Template negative;
174   - Point offset;
175   - QMutexLocker samplingLocker(&samplingMutex);
176   - negative = getNegative(offset);
177   - samplingLocker.unlock();
178   -
179   - Miner miner(negative, windowSize(), offset);
180   - forever {
181   - bool newImg;
182   - Template sample = miner.mine(&newImg);
183   - if (!newImg) {
184   - if (negSamples.size() >= numNegs)
185   - return passedNegatives;
186   -
187   - float confidence;
188   - if (classify(sample, true, &confidence) != 0) {
189   - QMutexLocker miningLocker(&miningMutex);
190   - if (negSamples.size() >= numNegs)
191   - return passedNegatives;
192   - negSamples.append(sample);
193   - printf("Negative samples: %d\r", negSamples.size());
194   - }
195   -
196   - passedNegatives++;
197   - } else
198   - break;
199   - }
200   - }
201   - }
202   -
203   - void train(const TemplateList &data)
204   - {
205   - foreach (const Template &t, data)
206   - t.file.get<float>("Label") == 1.0f ? posImages.append(t) : negImages.append(t);
207   -
208   - qDebug() << "Total images:" << data.size()
209   - << "\nTotal positive images:" << posImages.size()
210   - << "\nTotal negative images:" << negImages.size();
211   -
212   - posIndices = Common::RandSample(posImages.size(), posImages.size(), true);
213   - negIndices = Common::RandSample(negImages.size(), negImages.size(), true);
214   -
215   - stages.reserve(numStages);
216   - for (int i = 0; i < numStages; i++) {
217   - qDebug() << "===== TRAINING" << i << "stage =====";
218   - qDebug() << "<BEGIN";
219   -
220   - Classifier *next_stage = Classifier::make(stageDescription, NULL);
221   - stages.append(next_stage);
222   -
223   - float currFAR = getSamples();
224   -
225   - if (currFAR < maxFAR && !requireAllStages) {
226   - qDebug() << "FAR is below required level! Terminating early";
227   - return;
228   - }
229   -
230   - stages.last()->train(posSamples + negSamples);
231   -
232   - qDebug() << "END>";
233   - }
234   - }
235   -
236   - float classify(const Template &src, bool process, float *confidence) const
237   - {
238   - float stageConf = 0.0f;
239   - const int stopStage = maxStage == -1 ? numStages : maxStage;
240   - int stageIndex = 0;
241   - foreach (const Classifier *stage, stages) {
242   - if (stageIndex++ == stopStage)
243   - break;
244   - float result = stage->classify(src, process, &stageConf);
245   - if (confidence)
246   - *confidence += stageConf;
247   - if (result == 0.0f)
248   - return 0.0f;
249   - }
250   - return 1.0f;
251   - }
252   -
253   - int numFeatures() const
254   - {
255   - return stages.first()->numFeatures();
256   - }
257   -
258   - Template preprocess(const Template &src) const
259   - {
260   - return stages.first()->preprocess(src);
261   - }
262   -
263   - Size windowSize(int *dx = NULL, int *dy = NULL) const
264   - {
265   - return stages.first()->windowSize(dx, dy);
266   - }
267   -
268   - void load(QDataStream &stream)
269   - {
270   - int numStages; stream >> numStages;
271   - for (int i = 0; i < numStages; i++) {
272   - Classifier *nextStage = Classifier::make(stageDescription, NULL);
273   - nextStage->load(stream);
274   - stages.append(nextStage);
275   - }
276   - }
277   -
278   - void store(QDataStream &stream) const
279   - {
280   - stream << stages.size();
281   - foreach (const Classifier *stage, stages)
282   - stage->store(stream);
283   - }
284   -
285   -private:
286   - float getSamples()
287   - {
288   - posSamples.clear(); posSamples.reserve(numPos);
289   - negSamples.clear(); negSamples.reserve(numNegs);
290   - posIndex = 0;
291   -
292   - float confidence;
293   - while (posSamples.size() < numPos) {
294   - Template pos;
295   - if (!getPositive(pos))
296   - qFatal("Cannot get another positive sample!");
297   -
298   - if (classify(pos, true, &confidence) > 0.0f) {
299   - printf("POS current samples: %d\r", posSamples.size());
300   - posSamples.append(pos);
301   - }
302   - }
303   -
304   - qDebug() << "POS count : consumed " << posSamples.size() << ":" << posIndex;
305   -
306   - QFutureSynchronizer<uint64> futures;
307   - for (int i=0; i<QThread::idealThreadCount(); i++)
308   - futures.addFuture(QtConcurrent::run(this, &CascadeClassifier::mine));
309   - futures.waitForFinished();
310   -
311   - uint64 passedNegs = 0;
312   - QList<QFuture<uint64> > results = futures.futures();
313   - for (int i=0; i<results.size(); i++)
314   - passedNegs += results[i].result();
315   -
316   - double acceptanceRatio = negSamples.size() / (double)passedNegs;
317   - qDebug() << "NEG count : acceptanceRatio " << negSamples.size() << ":" << acceptanceRatio;
318   -
319   - return acceptanceRatio;
320   - }
321   -};
322   -
323   -BR_REGISTER(Classifier, CascadeClassifier)
324   -
325   -} // namespace br
326   -
327   -#include "classification/cascade_classifier.moc"