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"