Commit 60939e7a4a01d1e6ca2af4e60195e6200627dc3b

Authored by Scott Klum
1 parent 3836f4a2

Removed OpenCV detection dependency entirely

openbr/core/opencvutils.cpp
... ... @@ -382,6 +382,131 @@ bool OpenCVUtils::overlaps(const QList<Rect> &posRects, const Rect &negRect, dou
382 382 return false;
383 383 }
384 384  
  385 +// class for grouping object candidates, detected by Cascade Classifier, HOG etc.
  386 +// instance of the class is to be passed to cv::partition (see cxoperations.hpp)
  387 +class SimilarRects
  388 +{
  389 +public:
  390 + SimilarRects(double _eps) : eps(_eps) {}
  391 + inline bool operator()(const Rect& r1, const Rect& r2) const
  392 + {
  393 + double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;
  394 + return std::abs(r1.x - r2.x) <= delta &&
  395 + std::abs(r1.y - r2.y) <= delta &&
  396 + std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&
  397 + std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;
  398 + }
  399 + double eps;
  400 +};
  401 +
  402 +// TODO: Make sure case where no confidences are inputted works.
  403 +void OpenCVUtils::group(vector<Rect> &rects, vector<float> &confidences, float epsilon)
  404 +{
  405 + if (rects.empty())
  406 + return;
  407 +
  408 + const bool useConfidences = !confidences.empty();
  409 +
  410 + vector<int> labels;
  411 + int nClasses = cv::partition(rects, labels, SimilarRects(epsilon));
  412 +
  413 + // Rect for each class (class meaning identity assigned by partition)
  414 + vector<Rect> rrects(nClasses);
  415 +
  416 + // Total number of rects in each class
  417 + vector<int> rweights(nClasses, 0);
  418 + vector<double> rejectWeights(nClasses, -std::numeric_limits<double>::max());
  419 +
  420 + for (int i = 0; i < labels.size(); i++)
  421 + {
  422 + int cls = labels[i];
  423 + rrects[cls].x += rects[i].x;
  424 + rrects[cls].y += rects[i].y;
  425 + rrects[cls].width += rects[i].width;
  426 + rrects[cls].height += rects[i].height;
  427 + rweights[cls]++;
  428 + }
  429 +
  430 + if (useConfidences)
  431 + {
  432 + // For each class, find maximum confidence
  433 + for (int i = 0; i < labels.size(); i++)
  434 + {
  435 + int cls = labels[i];
  436 + if (confidences[i] > rejectWeights[cls])
  437 + rejectWeights[cls] = confidences[i];
  438 + }
  439 + }
  440 +
  441 + // Find average rectangle for all classes
  442 + for (int i = 0; i < nClasses; i++)
  443 + {
  444 + Rect r = rrects[i];
  445 + float s = 1.f/rweights[i];
  446 + rrects[i] = Rect(saturate_cast<int>(r.x*s),
  447 + saturate_cast<int>(r.y*s),
  448 + saturate_cast<int>(r.width*s),
  449 + saturate_cast<int>(r.height*s));
  450 + }
  451 +
  452 + rects.clear();
  453 + confidences.clear();
  454 +
  455 + const double threshold = 2;
  456 +
  457 + // Aggregate by comparing average rectangles against other average rectangels
  458 + for (int i = 0; i < nClasses; i++)
  459 + {
  460 + // Average rectangle
  461 + Rect r1 = rrects[i];
  462 +
  463 + // Used to eliminate rectangles with few neighbors in the case of no weights
  464 + // int n1 = levelWeights ? rejectLevels[i] : rweights[i];
  465 + double w1 = rejectWeights[i];
  466 +
  467 + // Eliminate rectangle if it doesn't meet confidence criteria
  468 + if (w1 <= threshold)
  469 + continue;
  470 +
  471 + // filter out small face rectangles inside large rectangles
  472 + int j;
  473 + for (j = 0; j < nClasses; j++)
  474 + {
  475 + double w2 = rejectWeights[j];
  476 +
  477 + if (j == i) //|| n2 <= groupThreshold )
  478 + continue;
  479 +
  480 + Rect r2 = rrects[j];
  481 +
  482 + int dx = saturate_cast<int>(r2.width * epsilon);
  483 + int dy = saturate_cast<int>(r2.height * epsilon);
  484 +
  485 + // If, r1 is within the r2 AND
  486 + // the second rectangle reaches a later stage than the first
  487 + // where both the first and the second must have a stage greater than three OR
  488 + // the first doens't reach the third stage.
  489 + // Changeto: second rectangle has a higher confidence than the first OR
  490 + // the first has a low confidence.
  491 + // Then, eliminate the first rectangle.
  492 + if(r1.x >= r2.x - dx &&
  493 + r1.y >= r2.y - dy &&
  494 + r1.x + r1.width <= r2.x + r2.width + dx &&
  495 + r1.y + r1.height <= r2.y + r2.height + dy &&
  496 + (w2 > std::max(threshold, w1)))
  497 + break;
  498 + }
  499 +
  500 + // Need to return rects and confidences
  501 + if( j == nClasses )
  502 + {
  503 + rects.push_back(r1);
  504 + if (useConfidences)
  505 + confidences.push_back(w1);
  506 + }
  507 + }
  508 +}
  509 +
385 510 QDataStream &operator<<(QDataStream &stream, const Mat &m)
386 511 {
387 512 // Write header
... ...
openbr/core/opencvutils.h
... ... @@ -98,6 +98,9 @@ namespace OpenCVUtils
98 98 float overlap(const cv::Rect &rect1, const cv::Rect &rect2);
99 99 float overlap(const QRectF &rect1, const QRectF &rect2);
100 100  
  101 + // Misc
  102 + void group(std::vector<cv::Rect> &rects, std::vector<float> &confidences, float epsilon);
  103 +
101 104 int getFourcc();
102 105 }
103 106  
... ...
openbr/plugins/classification/cascade.cpp
... ... @@ -157,11 +157,10 @@ class CascadeClassifier : public Classifier
157 157 float stageConf = 0.0f;
158 158 foreach (const Classifier *stage, stages) {
159 159 float result = stage->classify(image, process, &stageConf);
160   - if (result == 0.0f) {
161   - if (confidence)
162   - *confidence += stageConf;
  160 + if (confidence)
  161 + *confidence += stageConf;
  162 + if (result == 0.0f)
163 163 return 0.0f;
164   - }
165 164 }
166 165 return 1.0f;
167 166 }
... ...
openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -116,7 +116,7 @@ class SlidingWindowTransform : public MetaTransform
116 116 for (int x = 0; x < processingRectSize.width; x += step) {
117 117 Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone();
118 118  
119   - float confidence;
  119 + float confidence = 0;
120 120 int result = classifier->classify(window, false, &confidence);
121 121  
122 122 if (result == 1) {
... ... @@ -124,7 +124,7 @@ class SlidingWindowTransform : public MetaTransform
124 124 confidences.push_back(confidence);
125 125 }
126 126  
127   - // Add ROC mode
  127 + // TODO: Add ROC mode
128 128  
129 129 if (result == 0)
130 130 x += step;
... ... @@ -132,18 +132,14 @@ class SlidingWindowTransform : public MetaTransform
132 132 }
133 133 }
134 134  
135   - // groupRectangles(rects, confidences, eps);
136   - groupRectangles(rects, rejectLevels, levelWeights, minNeighbors, eps);
  135 + OpenCVUtils::group(rects, confidences, eps);
137 136  
138 137 if (!enrollAll && rects.empty())
139 138 rects.push_back(Rect(0, 0, m.cols, m.rows));
140 139  
141 140 for (size_t j=0; j<rects.size(); j++) {
142 141 Template u(t.file, m);
143   - if (rejectLevels.size() > j)
144   - u.file.set("Confidence", rejectLevels[j]*levelWeights[j]);
145   - else
146   - u.file.set("Confidence", 1);
  142 + u.file.set("Confidence", confidences[j]);
147 143 const QRectF rect = OpenCVUtils::fromRect(rects[j]);
148 144 u.file.appendRect(rect);
149 145 u.file.set(model, rect);
... ... @@ -157,7 +153,7 @@ class SlidingWindowTransform : public MetaTransform
157 153 {
158 154 (void)stream;
159 155  
160   - QString filename = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model + "/cascade.xml";
  156 + QString filename = model + "/cascade.xml";
161 157 FileStorage fs(filename.toStdString(), FileStorage::READ);
162 158 if (!fs.isOpened())
163 159 return;
... ... @@ -169,7 +165,7 @@ class SlidingWindowTransform : public MetaTransform
169 165 {
170 166 (void) stream;
171 167  
172   - QString path = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model;
  168 + QString path = model;
173 169 QtUtils::touchDir(QDir(path));
174 170  
175 171 QString filename = path + "/cascade.xml";
... ...