Commit d61c72e9f96b5ba3d4e2ed30e6f6b8ffff1968d5
Merge pull request #182 from biometrics/evalDetection
generalized -evalDetection
Showing
1 changed file
with
64 additions
and
42 deletions
openbr/core/eval.cpp
| ... | ... | @@ -433,84 +433,106 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec |
| 433 | 433 | return lines; |
| 434 | 434 | } |
| 435 | 435 | |
| 436 | -QString getDetectKey(const TemplateList &templates) | |
| 436 | +struct DetectionKey : public QString | |
| 437 | 437 | { |
| 438 | - const File &f = templates.first().file; | |
| 439 | - foreach (const QString &key, f.localKeys()) { | |
| 440 | - // first check for single detections | |
| 438 | + enum Type { | |
| 439 | + Invalid, | |
| 440 | + Rect, | |
| 441 | + RectList, | |
| 442 | + XYWidthHeight | |
| 443 | + } type; | |
| 444 | + | |
| 445 | + DetectionKey(const QString &key = "", Type type = Invalid) | |
| 446 | + : QString(key), type(type) {} | |
| 447 | +}; | |
| 448 | + | |
| 449 | +static DetectionKey getDetectKey(const FileList &files) | |
| 450 | +{ | |
| 451 | + if (files.empty()) | |
| 452 | + return DetectionKey(); | |
| 453 | + | |
| 454 | + const File &f = files.first(); | |
| 455 | + const QStringList localKeys = f.localKeys(); | |
| 456 | + | |
| 457 | + // first check for single detections | |
| 458 | + foreach (const QString &key, localKeys) | |
| 441 | 459 | if (!f.get<QRectF>(key, QRectF()).isNull()) |
| 442 | - return key; | |
| 443 | - } | |
| 460 | + return DetectionKey(key, DetectionKey::Rect); | |
| 461 | + | |
| 444 | 462 | // and then multiple |
| 445 | 463 | if (!f.rects().empty()) |
| 446 | - return "Rects"; | |
| 447 | - return ""; | |
| 448 | -} | |
| 464 | + return DetectionKey("Rects", DetectionKey::RectList); | |
| 449 | 465 | |
| 450 | -bool detectKeyIsList(QString key, const TemplateList &templates) | |
| 451 | -{ | |
| 452 | - return templates.first().file.get<QRectF>(key, QRectF()).isNull(); | |
| 466 | + // check for <Key>_X, <Key>_Y, <Key>_Width, <Key>_Height | |
| 467 | + foreach (const QString &localKey, localKeys) { | |
| 468 | + if (!localKey.endsWith("_X")) | |
| 469 | + continue; | |
| 470 | + const QString key = localKey.mid(0, localKey.size()-2); | |
| 471 | + if (localKeys.contains(key+"_Y") && | |
| 472 | + localKeys.contains(key+"_Width") && | |
| 473 | + localKeys.contains(key+"_Height")) | |
| 474 | + return DetectionKey(key, DetectionKey::XYWidthHeight); | |
| 475 | + } | |
| 476 | + | |
| 477 | + return DetectionKey(); | |
| 453 | 478 | } |
| 454 | 479 | |
| 455 | -// return a list of detections whether the template holds | |
| 456 | -// multiple detections or a single detection | |
| 457 | -QList<Detection> getDetections(QString key, const Template &t, bool isList, bool isTruth) | |
| 480 | +// return a list of detections independent of the detection key format | |
| 481 | +static QList<Detection> getDetections(const DetectionKey &key, const File &f, bool isTruth) | |
| 458 | 482 | { |
| 459 | - File f = t.file; | |
| 460 | 483 | QList<Detection> dets; |
| 461 | - if (isList) { | |
| 484 | + if (key.type == DetectionKey::RectList) { | |
| 462 | 485 | QList<QRectF> rects = f.rects(); |
| 463 | 486 | QList<float> confidences = f.getList<float>("Confidences", QList<float>()); |
| 464 | 487 | if (!isTruth && rects.size() != confidences.size()) |
| 465 | 488 | qFatal("You don't have enough confidence. I mean, your detections don't all have confidence measures."); |
| 466 | 489 | for (int i=0; i<rects.size(); i++) { |
| 467 | 490 | if (isTruth) |
| 468 | - dets.append(Detection(rects.at(i))); | |
| 491 | + dets.append(Detection(rects[i])); | |
| 469 | 492 | else |
| 470 | - dets.append(Detection(rects.at(i), confidences.at(i))); | |
| 493 | + dets.append(Detection(rects[i], confidences[i])); | |
| 471 | 494 | } |
| 472 | - } else { | |
| 473 | - if (isTruth) | |
| 474 | - dets.append(Detection(f.get<QRectF>(key))); | |
| 475 | - else | |
| 476 | - dets.append(Detection(f.get<QRectF>(key), f.get<float>("Confidence", -1))); | |
| 495 | + } else if (key.type == DetectionKey::Rect) { | |
| 496 | + dets.append(Detection(f.get<QRectF>(key), isTruth ? -1 : f.get<float>("Confidence", -1))); | |
| 497 | + } else if (key.type == DetectionKey::XYWidthHeight) { | |
| 498 | + const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height")); | |
| 499 | + dets.append(Detection(rect, isTruth ? -1 : f.get<float>("Confidence", -1))); | |
| 477 | 500 | } |
| 478 | 501 | return dets; |
| 479 | 502 | } |
| 480 | 503 | |
| 481 | -QMap<QString, Detections> getDetections(const TemplateList &predicted, const TemplateList &truth) | |
| 504 | +static QMap<QString, Detections> getDetections(const File &predictedGallery, const File &truthGallery) | |
| 482 | 505 | { |
| 506 | + const FileList predicted = TemplateList::fromGallery(predictedGallery).files(); | |
| 507 | + const FileList truth = TemplateList::fromGallery(truthGallery).files(); | |
| 508 | + | |
| 483 | 509 | // Figure out which metadata field contains a bounding box |
| 484 | - QString truthDetectKey = getDetectKey(truth); | |
| 485 | - if (truthDetectKey.isEmpty()) qFatal("No suitable ground truth metadata key found."); | |
| 486 | - QString predictedDetectKey = getDetectKey(predicted); | |
| 487 | - if (predictedDetectKey.isEmpty()) qFatal("No suitable predicted metadata key found."); | |
| 510 | + DetectionKey truthDetectKey = getDetectKey(truth); | |
| 511 | + if (truthDetectKey.isEmpty()) | |
| 512 | + qFatal("No suitable ground truth metadata key found."); | |
| 513 | + | |
| 514 | + DetectionKey predictedDetectKey = getDetectKey(predicted); | |
| 515 | + if (predictedDetectKey.isEmpty()) | |
| 516 | + qFatal("No suitable predicted metadata key found."); | |
| 517 | + | |
| 488 | 518 | qDebug("Using metadata key: %s%s", |
| 489 | 519 | qPrintable(predictedDetectKey), |
| 490 | 520 | qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey)); |
| 491 | 521 | |
| 492 | 522 | QMap<QString, Detections> allDetections; |
| 493 | - bool predKeyIsList = detectKeyIsList(predictedDetectKey, predicted); | |
| 494 | - bool truthKeyIsList = detectKeyIsList(truthDetectKey, truth); | |
| 495 | - foreach (const Template &t, predicted) { | |
| 496 | - QList<Detection> dets = getDetections(predictedDetectKey, t, predKeyIsList, false); | |
| 497 | - allDetections[t.file.baseName()].predicted.append(dets); | |
| 498 | - } | |
| 499 | - foreach (const Template &t, truth) { | |
| 500 | - QList<Detection> dets = getDetections(truthDetectKey, t, truthKeyIsList, true); | |
| 501 | - allDetections[t.file.baseName()].truth.append(dets); | |
| 502 | - } | |
| 523 | + foreach (const File &f, predicted) | |
| 524 | + allDetections[f.baseName()].predicted.append(getDetections(predictedDetectKey, f, false)); | |
| 525 | + foreach (const File &f, truth) | |
| 526 | + allDetections[f.baseName()].truth.append(getDetections(truthDetectKey, f, true)); | |
| 503 | 527 | return allDetections; |
| 504 | 528 | } |
| 505 | 529 | |
| 506 | 530 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv) |
| 507 | 531 | { |
| 508 | 532 | qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); |
| 509 | - const TemplateList predicted(TemplateList::fromGallery(predictedGallery)); | |
| 510 | - const TemplateList truth(TemplateList::fromGallery(truthGallery)); | |
| 511 | 533 | |
| 512 | 534 | // Organized by file, QMap used to preserve order |
| 513 | - QMap<QString, Detections> allDetections = getDetections(predicted, truth); | |
| 535 | + QMap<QString, Detections> allDetections = getDetections(predictedGallery, truthGallery); | |
| 514 | 536 | |
| 515 | 537 | QList<ResolvedDetection> resolvedDetections, falseNegativeDetections; |
| 516 | 538 | int totalTrueDetections = 0; | ... | ... |