Commit 60939e7a4a01d1e6ca2af4e60195e6200627dc3b
1 parent
3836f4a2
Removed OpenCV detection dependency entirely
Showing
4 changed files
with
137 additions
and
14 deletions
openbr/core/opencvutils.cpp
| @@ -382,6 +382,131 @@ bool OpenCVUtils::overlaps(const QList<Rect> &posRects, const Rect &negRect, dou | @@ -382,6 +382,131 @@ bool OpenCVUtils::overlaps(const QList<Rect> &posRects, const Rect &negRect, dou | ||
| 382 | return false; | 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 | QDataStream &operator<<(QDataStream &stream, const Mat &m) | 510 | QDataStream &operator<<(QDataStream &stream, const Mat &m) |
| 386 | { | 511 | { |
| 387 | // Write header | 512 | // Write header |
openbr/core/opencvutils.h
| @@ -98,6 +98,9 @@ namespace OpenCVUtils | @@ -98,6 +98,9 @@ namespace OpenCVUtils | ||
| 98 | float overlap(const cv::Rect &rect1, const cv::Rect &rect2); | 98 | float overlap(const cv::Rect &rect1, const cv::Rect &rect2); |
| 99 | float overlap(const QRectF &rect1, const QRectF &rect2); | 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 | int getFourcc(); | 104 | int getFourcc(); |
| 102 | } | 105 | } |
| 103 | 106 |
openbr/plugins/classification/cascade.cpp
| @@ -157,11 +157,10 @@ class CascadeClassifier : public Classifier | @@ -157,11 +157,10 @@ class CascadeClassifier : public Classifier | ||
| 157 | float stageConf = 0.0f; | 157 | float stageConf = 0.0f; |
| 158 | foreach (const Classifier *stage, stages) { | 158 | foreach (const Classifier *stage, stages) { |
| 159 | float result = stage->classify(image, process, &stageConf); | 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 | return 0.0f; | 163 | return 0.0f; |
| 164 | - } | ||
| 165 | } | 164 | } |
| 166 | return 1.0f; | 165 | return 1.0f; |
| 167 | } | 166 | } |
openbr/plugins/imgproc/slidingwindow.cpp
| @@ -116,7 +116,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -116,7 +116,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 116 | for (int x = 0; x < processingRectSize.width; x += step) { | 116 | for (int x = 0; x < processingRectSize.width; x += step) { |
| 117 | Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone(); | 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 | int result = classifier->classify(window, false, &confidence); | 120 | int result = classifier->classify(window, false, &confidence); |
| 121 | 121 | ||
| 122 | if (result == 1) { | 122 | if (result == 1) { |
| @@ -124,7 +124,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -124,7 +124,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 124 | confidences.push_back(confidence); | 124 | confidences.push_back(confidence); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | - // Add ROC mode | 127 | + // TODO: Add ROC mode |
| 128 | 128 | ||
| 129 | if (result == 0) | 129 | if (result == 0) |
| 130 | x += step; | 130 | x += step; |
| @@ -132,18 +132,14 @@ class SlidingWindowTransform : public MetaTransform | @@ -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 | if (!enrollAll && rects.empty()) | 137 | if (!enrollAll && rects.empty()) |
| 139 | rects.push_back(Rect(0, 0, m.cols, m.rows)); | 138 | rects.push_back(Rect(0, 0, m.cols, m.rows)); |
| 140 | 139 | ||
| 141 | for (size_t j=0; j<rects.size(); j++) { | 140 | for (size_t j=0; j<rects.size(); j++) { |
| 142 | Template u(t.file, m); | 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 | const QRectF rect = OpenCVUtils::fromRect(rects[j]); | 143 | const QRectF rect = OpenCVUtils::fromRect(rects[j]); |
| 148 | u.file.appendRect(rect); | 144 | u.file.appendRect(rect); |
| 149 | u.file.set(model, rect); | 145 | u.file.set(model, rect); |
| @@ -157,7 +153,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -157,7 +153,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 157 | { | 153 | { |
| 158 | (void)stream; | 154 | (void)stream; |
| 159 | 155 | ||
| 160 | - QString filename = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model + "/cascade.xml"; | 156 | + QString filename = model + "/cascade.xml"; |
| 161 | FileStorage fs(filename.toStdString(), FileStorage::READ); | 157 | FileStorage fs(filename.toStdString(), FileStorage::READ); |
| 162 | if (!fs.isOpened()) | 158 | if (!fs.isOpened()) |
| 163 | return; | 159 | return; |
| @@ -169,7 +165,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -169,7 +165,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 169 | { | 165 | { |
| 170 | (void) stream; | 166 | (void) stream; |
| 171 | 167 | ||
| 172 | - QString path = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model; | 168 | + QString path = model; |
| 173 | QtUtils::touchDir(QDir(path)); | 169 | QtUtils::touchDir(QDir(path)); |
| 174 | 170 | ||
| 175 | QString filename = path + "/cascade.xml"; | 171 | QString filename = path + "/cascade.xml"; |