Commit 3e6d1cff5d31d5549def0bd27da140540cdb1542

Authored by bhklein
1 parent 70a3f508

add gtGallery, knnOutput and evalKNN

app/br/br.cpp
... ... @@ -168,6 +168,9 @@ public:
168 168 } else if (!strcmp(fun, "evalRegression")) {
169 169 check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'.");
170 170 br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : "");
  171 + } else if (!strcmp(fun, "evalKNN")) {
  172 + check(parc >=2 && parc < 4, "Incorrect parameter count for 'evalKNN'.");
  173 + br_eval_knn(parv[0], parv[1], parc >= 3 ? parv[2] : "");
171 174 } else if (!strcmp(fun, "pairwiseCompare")) {
172 175 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'.");
173 176 br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : "");
... ... @@ -276,6 +279,7 @@ private:
276 279 "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n"
277 280 "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n"
278 281 "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n"
  282 + "-evalKNN <knn_graph> <knn_truth> [{iet_file}]\n"
279 283 "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n"
280 284 "-inplaceEval <simmat> <target> <query> [{csv}]\n"
281 285 "-assertEval <simmat> <mask> <accuracy>\n"
... ...
openbr/core/eval.cpp
... ... @@ -1300,4 +1300,132 @@ void EvalRegression(const QString &amp;predictedGallery, const QString &amp;truthGallery
1300 1300 qDebug("MAE = %f", maeError/predicted.size());
1301 1301 }
1302 1302  
  1303 +void readKNN(size_t &templateCount, size_t &k, QVector<Candidate> &neighbors, const QString &fileName)
  1304 +{
  1305 + QFile file(fileName);
  1306 + if (!file.open(QFile::ReadOnly))
  1307 + qFatal("Failed to open k-NN file for reading!");
  1308 + file.read((char*) &templateCount, sizeof(size_t));
  1309 + file.read((char*) &k, sizeof(size_t));
  1310 + neighbors.resize(templateCount * k);
  1311 +
  1312 + file.read((char*) neighbors.data(), templateCount * k * sizeof(Candidate));
  1313 +}
  1314 +
  1315 +void readKNNTruth(size_t templateCount, QVector< QList<size_t> > &groundTruth, const QString &fileName)
  1316 +{
  1317 + groundTruth.reserve(templateCount);
  1318 + QFile truthFile(fileName);
  1319 + if (!truthFile.open(QFile::ReadOnly | QFile::Text))
  1320 + qFatal("Failed to open k-NN ground truth file for reading!");
  1321 + size_t i=0;
  1322 + while (!truthFile.atEnd()) {
  1323 + const QString line = truthFile.readLine().trimmed();
  1324 + if (!line.isEmpty())
  1325 + foreach (const QString &index, line.split('\t')) {
  1326 + bool ok;
  1327 + groundTruth[i].append(index.toLong(&ok));
  1328 + if (!ok)
  1329 + qFatal("Failed to parse long in k-NN ground truth!");
  1330 + }
  1331 + i++;
  1332 + }
  1333 + if (i != templateCount)
  1334 + qFatal("Invalid ground truth file!");
  1335 +}
  1336 +
  1337 +void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &iet)
  1338 +{
  1339 + qDebug("Evaluating k-NN of %s against %s", qPrintable(knnGraph), qPrintable(knnTruth));
  1340 +
  1341 + size_t templateCount;
  1342 + size_t k;
  1343 + QVector<Candidate> neighbors;
  1344 + readKNN(templateCount, k, neighbors, knnGraph);
  1345 +
  1346 + /*
  1347 + * Read the ground truth from disk.
  1348 + * Line i contains the template indicies of the mates for probe i.
  1349 + * See the `gtGallery` implementation for details.
  1350 + */
  1351 + QVector< QList<size_t> > truth(templateCount);
  1352 + readKNNTruth(templateCount, truth, knnTruth);
  1353 +
  1354 + /*
  1355 + * For each probe, record the similarity of the highest mate (if one exists) and the highest non-mate.
  1356 + */
  1357 + QList<float> matedSimilarities, unmatedSimilarities;
  1358 + size_t numMatedSearches = 0;
  1359 + for (size_t i=0; i<templateCount; i++) {
  1360 + const QList<size_t> &mates = truth[i];
  1361 + if (!mates.empty())
  1362 + numMatedSearches++;
  1363 +
  1364 + bool recordedHighestMatedSimilarity = false;
  1365 + bool recordedHighestUnmatedSimilarity = false;
  1366 + for (size_t j=0; j<k; j++) {
  1367 + const Candidate &neighbor = neighbors[i*k+j];
  1368 +
  1369 + if (mates.contains(neighbor.index)) {
  1370 + // Found a mate
  1371 + if (!recordedHighestMatedSimilarity) {
  1372 + matedSimilarities.append(neighbor.similarity);
  1373 + recordedHighestMatedSimilarity = true;
  1374 + }
  1375 + } else {
  1376 + // Found a non-mate
  1377 + if (!recordedHighestUnmatedSimilarity) {
  1378 + unmatedSimilarities.append(neighbor.similarity);
  1379 + recordedHighestUnmatedSimilarity = true;
  1380 + }
  1381 + }
  1382 +
  1383 + if (recordedHighestMatedSimilarity && recordedHighestUnmatedSimilarity)
  1384 + break; // we can stop scanning the candidate list for this probe
  1385 + }
  1386 + }
  1387 +
  1388 + // Sort the similarity scores lowest-to-highest
  1389 + std::sort(matedSimilarities.begin(), matedSimilarities.end());
  1390 + std::sort(unmatedSimilarities.begin(), unmatedSimilarities.end());
  1391 + const size_t numMatedSimilarities = matedSimilarities.size();
  1392 + const size_t numUnmatedSimilarities = unmatedSimilarities.size();
  1393 +
  1394 + if (numMatedSimilarities == 0)
  1395 + qFatal("No mated searches!");
  1396 +
  1397 + if (numUnmatedSimilarities == 0)
  1398 + qFatal("No unmated searches!");
  1399 +
  1400 + printf("Rank-%zu Return Rate: %g\n", k, double(numMatedSimilarities) / double(numMatedSearches));
  1401 +
  1402 + // Open the output file
  1403 + QFile ietFile(iet);
  1404 + if (!ietFile.open(QFile::WriteOnly | QFile::Text))
  1405 + qFatal("Failed to open IET file for writing!");
  1406 + ietFile.write("Threshold,FPIR,FNIR\n");
  1407 +
  1408 + /*
  1409 + * Iterate through the similarity scores highest-to-lowest,
  1410 + * for each threshold count the number mated and unmated searches,
  1411 + * record the corresponding FPIR and FNIR values for the threshold.
  1412 + */
  1413 + size_t matedCount = 0;
  1414 + size_t unmatedCount = 0;
  1415 + while (!matedSimilarities.empty()) {
  1416 + const float threshold = matedSimilarities.back();
  1417 + while (!matedSimilarities.empty() && (matedSimilarities.back() >= threshold)) {
  1418 + matedSimilarities.removeLast();
  1419 + matedCount++;
  1420 + }
  1421 + while (!unmatedSimilarities.empty() && (unmatedSimilarities.back() >= threshold)) {
  1422 + unmatedSimilarities.removeLast();
  1423 + unmatedCount++;
  1424 + }
  1425 + ietFile.write(qPrintable(QString::number(threshold) + "," +
  1426 + QString::number(double(unmatedCount) / double(numUnmatedSimilarities)) + "," +
  1427 + QString::number(1.0 - double(matedCount) / double(numMatedSimilarities)) + "\n"));
  1428 + }
  1429 +}
  1430 +
1303 1431 } // namespace br
... ...
openbr/core/eval.h
... ... @@ -33,6 +33,17 @@ namespace br
33 33 float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap
34 34 float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1, int sampleIndex = 0, int totalExamples = 5); // Return average error
35 35 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
  36 + void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &iet = "");
  37 +
  38 + struct Candidate
  39 + {
  40 + size_t index;
  41 + float similarity;
  42 +
  43 + Candidate() {}
  44 + Candidate(size_t index_, float similarity_)
  45 + : index(index_), similarity(similarity_) {}
  46 + };
36 47 }
37 48  
38 49 #endif // BR_EVAL_H
... ...
openbr/openbr.cpp
... ... @@ -145,6 +145,11 @@ void br_eval_regression(const char *predicted_gallery, const char *truth_gallery
145 145 EvalRegression(predicted_gallery, truth_gallery, predicted_property, truth_property);
146 146 }
147 147  
  148 +void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *iet)
  149 +{
  150 + EvalKNN(knnGraph, knnTruth, iet);
  151 +}
  152 +
148 153 void br_finalize()
149 154 {
150 155 Context::finalize();
... ...
openbr/openbr.h
... ... @@ -64,6 +64,8 @@ BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *t
64 64  
65 65 BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property = "", const char *truth_property = "");
66 66  
  67 +BR_EXPORT void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *iet = "");
  68 +
67 69 BR_EXPORT void br_finalize();
68 70  
69 71 BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[],
... ...
openbr/plugins/gallery/gt.cpp 0 → 100644
  1 +#include "openbr/plugins/openbr_internal.h"
  2 +#include "openbr/core/qtutils.h"
  3 +
  4 +using namespace br;
  5 +
  6 +/*!
  7 + * \ingroup galleries
  8 + * \brief Ground truth format for evaluating kNN
  9 + * \author Josh Klontz \cite jklontz
  10 + */
  11 +class gtGallery : public Gallery
  12 +{
  13 + Q_OBJECT
  14 +
  15 + TemplateList templates;
  16 +
  17 + ~gtGallery()
  18 + {
  19 + const QList<int> labels = File::get<int>(TemplateList::relabel(templates, "Label", false), "Label");
  20 +
  21 + QStringList lines;
  22 + for (int i=0; i<labels.size(); i++) {
  23 + QStringList words;
  24 + for (int j=0; j<labels.size(); j++) {
  25 + if ((labels[i] == labels[j]) && (i != j))
  26 + words.append(QString::number(j));
  27 + }
  28 + lines.append(words.join("\t"));
  29 + }
  30 +
  31 + QtUtils::writeFile(file.name, lines);
  32 + }
  33 +
  34 + TemplateList readBlock(bool *done)
  35 + {
  36 + *done = true;
  37 + qFatal("Not supported!");
  38 + return TemplateList();
  39 + }
  40 +
  41 + void write(const Template &t)
  42 + {
  43 + templates.append(t);
  44 + }
  45 +};
  46 +
  47 +BR_REGISTER(Gallery, gtGallery)
  48 +
  49 +#include "gt.moc"
... ...
openbr/plugins/output/knn.cpp 0 → 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +#include <openbr/core/common.h>
  3 +#include <openbr/core/opencvutils.h>
  4 +#include <openbr/core/eval.h>
  5 +
  6 +namespace br
  7 +{
  8 +
  9 +/*!
  10 + * \ingroup outputs
  11 + * \brief Outputs the k-Nearest Neighbors from the gallery for each probe.
  12 + * \author Ben Klein \cite bhklein
  13 + */
  14 +class knnOutput : public MatrixOutput
  15 +{
  16 + Q_OBJECT
  17 +
  18 + ~knnOutput()
  19 + {
  20 + size_t num_probes = (size_t)queryFiles.size();
  21 + if (targetFiles.isEmpty() || queryFiles.isEmpty()) return;
  22 + size_t k = file.get<size_t>("k", 20);
  23 +
  24 + if ((size_t)targetFiles.size() < k)
  25 + qFatal("Gallery size %s is smaller than k = %s.", qPrintable(QString::number(targetFiles.size())), qPrintable(QString::number(k)));
  26 +
  27 + QFile f(file);
  28 + if (!f.open(QFile::WriteOnly))
  29 + qFatal("Unable to open %s for writing.", qPrintable(file));
  30 + f.write((const char*) &num_probes, sizeof(size_t));
  31 + f.write((const char*) &k, sizeof(size_t));
  32 +
  33 + QVector<Candidate> neighbors; neighbors.reserve(num_probes*k);
  34 +
  35 + for (size_t i=0; i<num_probes; i++) {
  36 + typedef QPair<float,int> Pair;
  37 + size_t rank = 0;
  38 + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) {
  39 + if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) {
  40 + Candidate candidate((size_t)pair.second, pair.first);
  41 + neighbors.push_back(candidate);
  42 + if (++rank >= k) break;
  43 + }
  44 + }
  45 + }
  46 + f.write((const char*) neighbors.data(), num_probes * k * sizeof(Candidate));
  47 + f.close();
  48 + }
  49 +};
  50 +
  51 +BR_REGISTER(Output, knnOutput)
  52 +
  53 +} // namespace br
  54 +
  55 +#include "output/knn.moc"
... ...