Commit 107e229c5dd19347d4dc046ceaa2156ac9c7b2e0
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
18 changed files
with
473 additions
and
38 deletions
CMakeLists.txt
| @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR) | @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR) | ||
| 13 | set(CPACK_PACKAGE_VERSION_MAJOR 1) | 13 | set(CPACK_PACKAGE_VERSION_MAJOR 1) |
| 14 | endif() | 14 | endif() |
| 15 | if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR) | 15 | if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR) |
| 16 | - set(CPACK_PACKAGE_VERSION_MINOR 0) | 16 | + set(CPACK_PACKAGE_VERSION_MINOR 1) |
| 17 | endif() | 17 | endif() |
| 18 | if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH) | 18 | if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH) |
| 19 | set(CPACK_PACKAGE_VERSION_PATCH 0) | 19 | set(CPACK_PACKAGE_VERSION_PATCH 0) |
| @@ -158,8 +158,8 @@ endif() | @@ -158,8 +158,8 @@ endif() | ||
| 158 | 158 | ||
| 159 | # Download the models | 159 | # Download the models |
| 160 | ExternalProject_Add(models | 160 | ExternalProject_Add(models |
| 161 | - URL http://github.com/biometrics/openbr/releases/download/v1.0.0/models.tar.gz | ||
| 162 | - URL_MD5 0a7c79226d6629954aa32c835a1007b9 | 161 | + URL http://github.com/biometrics/openbr/releases/download/v1.1.0/models.tar.gz |
| 162 | + URL_MD5 d06927071120d5a50eee9475fe8da280 | ||
| 163 | SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models" | 163 | SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models" |
| 164 | CONFIGURE_COMMAND "" | 164 | CONFIGURE_COMMAND "" |
| 165 | BUILD_COMMAND "" | 165 | BUILD_COMMAND "" |
app/br/br.cpp
| @@ -168,6 +168,9 @@ public: | @@ -168,6 +168,9 @@ public: | ||
| 168 | } else if (!strcmp(fun, "evalRegression")) { | 168 | } else if (!strcmp(fun, "evalRegression")) { |
| 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); | 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); |
| 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); | 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 <= 3, "Incorrect parameter count for 'evalKNN'."); | ||
| 173 | + br_eval_knn(parv[0], parv[1], parc > 2 ? parv[2] : ""); | ||
| 171 | } else if (!strcmp(fun, "pairwiseCompare")) { | 174 | } else if (!strcmp(fun, "pairwiseCompare")) { |
| 172 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'."); | 175 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'."); |
| 173 | br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); | 176 | br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); |
| @@ -183,6 +186,9 @@ public: | @@ -183,6 +186,9 @@ public: | ||
| 183 | } else if (!strcmp(fun, "plotMetadata")) { | 186 | } else if (!strcmp(fun, "plotMetadata")) { |
| 184 | check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); | 187 | check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); |
| 185 | br_plot_metadata(parc-1, parv, parv[parc-1], true); | 188 | br_plot_metadata(parc-1, parv, parv[parc-1], true); |
| 189 | + } else if (!strcmp(fun, "plotKNN")) { | ||
| 190 | + check(parc >=2, "Incorrect parameter count for 'plotKNN'."); | ||
| 191 | + br_plot_knn(parc-1, parv, parv[parc-1], true); | ||
| 186 | } else if (!strcmp(fun, "project")) { | 192 | } else if (!strcmp(fun, "project")) { |
| 187 | check(parc == 2, "Insufficient parameter count for 'project'."); | 193 | check(parc == 2, "Insufficient parameter count for 'project'."); |
| 188 | br_project(parv[0], parv[1]); | 194 | br_project(parv[0], parv[1]); |
| @@ -276,12 +282,14 @@ private: | @@ -276,12 +282,14 @@ private: | ||
| 276 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n" | 282 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n" |
| 277 | "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" | 283 | "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" |
| 278 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" | 284 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" |
| 285 | + "-evalKNN <knn_graph> <knn_truth> [{csv}]\n" | ||
| 279 | "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n" | 286 | "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n" |
| 280 | "-inplaceEval <simmat> <target> <query> [{csv}]\n" | 287 | "-inplaceEval <simmat> <target> <query> [{csv}]\n" |
| 281 | "-assertEval <simmat> <mask> <accuracy>\n" | 288 | "-assertEval <simmat> <mask> <accuracy>\n" |
| 282 | "-plotDetection <file> ... <file> {destination}\n" | 289 | "-plotDetection <file> ... <file> {destination}\n" |
| 283 | "-plotLandmarking <file> ... <file> {destination}\n" | 290 | "-plotLandmarking <file> ... <file> {destination}\n" |
| 284 | "-plotMetadata <file> ... <file> <columns>\n" | 291 | "-plotMetadata <file> ... <file> <columns>\n" |
| 292 | + "-plotKNN <file> ... <file> {destination}\n" | ||
| 285 | "-project <input_gallery> {output_gallery}\n" | 293 | "-project <input_gallery> {output_gallery}\n" |
| 286 | "-deduplicate <input_gallery> <output_gallery> <threshold>\n" | 294 | "-deduplicate <input_gallery> <output_gallery> <threshold>\n" |
| 287 | "-likely <input_type> <output_type> <output_likely_source>\n" | 295 | "-likely <input_type> <output_type> <output_likely_source>\n" |
docs/docs/install.md
| @@ -35,7 +35,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -35,7 +35,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 35 | 35 | ||
| 36 | $ git clone https://github.com/biometrics/openbr.git | 36 | $ git clone https://github.com/biometrics/openbr.git |
| 37 | $ cd openbr | 37 | $ cd openbr |
| 38 | - $ git checkout 1.0 | 38 | + $ git checkout v1.1.0 |
| 39 | $ git submodule init | 39 | $ git submodule init |
| 40 | $ git submodule update | 40 | $ git submodule update |
| 41 | 41 | ||
| @@ -116,7 +116,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -116,7 +116,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 116 | 116 | ||
| 117 | $ git clone https://github.com/biometrics/openbr.git | 117 | $ git clone https://github.com/biometrics/openbr.git |
| 118 | $ cd openbr | 118 | $ cd openbr |
| 119 | - $ git checkout 1.0 | 119 | + $ git checkout v1.1.0 |
| 120 | $ git submodule init | 120 | $ git submodule init |
| 121 | $ git submodule update | 121 | $ git submodule update |
| 122 | 122 | ||
| @@ -196,7 +196,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -196,7 +196,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 196 | $ cd /c | 196 | $ cd /c |
| 197 | $ git clone https://github.com/biometrics/openbr.git | 197 | $ git clone https://github.com/biometrics/openbr.git |
| 198 | $ cd openbr | 198 | $ cd openbr |
| 199 | - $ git checkout 1.0 | 199 | + $ git checkout v1.1.0 |
| 200 | $ git submodule init | 200 | $ git submodule init |
| 201 | $ git submodule update | 201 | $ git submodule update |
| 202 | 202 | ||
| @@ -277,7 +277,7 @@ A hacker's guide to building, editing, and running OpenBR. | @@ -277,7 +277,7 @@ A hacker's guide to building, editing, and running OpenBR. | ||
| 277 | 277 | ||
| 278 | $ git clone https://github.com/biometrics/openbr.git | 278 | $ git clone https://github.com/biometrics/openbr.git |
| 279 | $ cd openbr | 279 | $ cd openbr |
| 280 | - $ git checkout 1.0 | 280 | + $ git checkout v1.1.0 |
| 281 | $ git submodule init | 281 | $ git submodule init |
| 282 | $ git submodule update | 282 | $ git submodule update |
| 283 | 283 |
openbr/core/eval.cpp
| @@ -102,12 +102,14 @@ static OperatingPoint getOperatingPointGivenTAR(const QList<OperatingPoint> &ope | @@ -102,12 +102,14 @@ static OperatingPoint getOperatingPointGivenTAR(const QList<OperatingPoint> &ope | ||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | 104 | ||
| 105 | -static float getCMC(const QVector<int> &firstGenuineReturns, int rank) | 105 | +static float getCMC(const QVector<int> &firstGenuineReturns, int rank, size_t possibleReturns = 0) |
| 106 | { | 106 | { |
| 107 | - int realizedReturns = 0, possibleReturns = 0; | 107 | + bool calcPossible = possibleReturns ? false : true; |
| 108 | + int realizedReturns = 0; | ||
| 108 | foreach (int firstGenuineReturn, firstGenuineReturns) { | 109 | foreach (int firstGenuineReturn, firstGenuineReturns) { |
| 109 | if (firstGenuineReturn > 0) { | 110 | if (firstGenuineReturn > 0) { |
| 110 | - possibleReturns++; | 111 | + if (calcPossible) |
| 112 | + possibleReturns++; | ||
| 111 | if (firstGenuineReturn <= rank) realizedReturns++; | 113 | if (firstGenuineReturn <= rank) realizedReturns++; |
| 112 | } | 114 | } |
| 113 | } | 115 | } |
| @@ -1300,4 +1302,168 @@ void EvalRegression(const QString &predictedGallery, const QString &truthGallery | @@ -1300,4 +1302,168 @@ void EvalRegression(const QString &predictedGallery, const QString &truthGallery | ||
| 1300 | qDebug("MAE = %f", maeError/predicted.size()); | 1302 | qDebug("MAE = %f", maeError/predicted.size()); |
| 1301 | } | 1303 | } |
| 1302 | 1304 | ||
| 1305 | +void readKNN(size_t &probeCount, size_t &k, QVector<Candidate> &neighbors, const QString &fileName) | ||
| 1306 | +{ | ||
| 1307 | + QFile file(fileName); | ||
| 1308 | + if (!file.open(QFile::ReadOnly)) | ||
| 1309 | + qFatal("Failed to open k-NN file for reading!"); | ||
| 1310 | + file.read((char*) &probeCount, sizeof(size_t)); | ||
| 1311 | + file.read((char*) &k, sizeof(size_t)); | ||
| 1312 | + neighbors.resize(probeCount * k); | ||
| 1313 | + | ||
| 1314 | + file.read((char*) neighbors.data(), probeCount * k * sizeof(Candidate)); | ||
| 1315 | +} | ||
| 1316 | + | ||
| 1317 | +void readKNNTruth(size_t probeCount, QVector< QList<size_t> > &groundTruth, const QString &fileName) | ||
| 1318 | +{ | ||
| 1319 | + groundTruth.reserve(probeCount); | ||
| 1320 | + QFile truthFile(fileName); | ||
| 1321 | + if (!truthFile.open(QFile::ReadOnly | QFile::Text)) | ||
| 1322 | + qFatal("Failed to open k-NN ground truth file for reading!"); | ||
| 1323 | + size_t i=0; | ||
| 1324 | + while (!truthFile.atEnd()) { | ||
| 1325 | + const QString line = truthFile.readLine().trimmed(); | ||
| 1326 | + if (!line.isEmpty()) | ||
| 1327 | + foreach (const QString &index, line.split('\t')) { | ||
| 1328 | + bool ok; | ||
| 1329 | + groundTruth[i].append(index.toLong(&ok)); | ||
| 1330 | + if (!ok) | ||
| 1331 | + qFatal("Failed to parse long in k-NN ground truth!"); | ||
| 1332 | + } | ||
| 1333 | + i++; | ||
| 1334 | + } | ||
| 1335 | + if (i != probeCount) | ||
| 1336 | + qFatal("Invalid ground truth file!"); | ||
| 1337 | +} | ||
| 1338 | + | ||
| 1339 | +void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &csv) | ||
| 1340 | +{ | ||
| 1341 | + qDebug("Evaluating k-NN of %s against %s", qPrintable(knnGraph), qPrintable(knnTruth)); | ||
| 1342 | + | ||
| 1343 | + size_t probeCount; | ||
| 1344 | + size_t k; | ||
| 1345 | + QVector<Candidate> neighbors; | ||
| 1346 | + readKNN(probeCount, k, neighbors, knnGraph); | ||
| 1347 | + | ||
| 1348 | + /* | ||
| 1349 | + * Read the ground truth from disk. | ||
| 1350 | + * Line i contains the template indicies of the mates for probe i. | ||
| 1351 | + * See the `gtGallery` implementation for details. | ||
| 1352 | + */ | ||
| 1353 | + QVector< QList<size_t> > truth(probeCount); | ||
| 1354 | + readKNNTruth(probeCount, truth, knnTruth); | ||
| 1355 | + | ||
| 1356 | + /* | ||
| 1357 | + * For each probe, record the similarity of the highest mate (if one exists) and the highest non-mate. | ||
| 1358 | + */ | ||
| 1359 | + QVector<int> firstGenuineReturns(probeCount, 0); | ||
| 1360 | + QList<float> matedSimilarities, unmatedSimilarities; | ||
| 1361 | + size_t numMatedSearches = 0, numUnmatedSearches = 0; | ||
| 1362 | + for (size_t i=0; i<probeCount; i++) { | ||
| 1363 | + const QList<size_t> &mates = truth[i]; | ||
| 1364 | + bool recordedHighestMatedSimilarity = false; | ||
| 1365 | + bool recordedHighestUnmatedSimilarity = false; | ||
| 1366 | + if (!mates.empty()) { | ||
| 1367 | + numMatedSearches++; | ||
| 1368 | + recordedHighestUnmatedSimilarity = true; | ||
| 1369 | + } else { | ||
| 1370 | + numUnmatedSearches++; | ||
| 1371 | + recordedHighestMatedSimilarity = true; | ||
| 1372 | + } | ||
| 1373 | + | ||
| 1374 | + for (size_t j=0; j<k; j++) { | ||
| 1375 | + const Candidate &neighbor = neighbors[i*k+j]; | ||
| 1376 | + | ||
| 1377 | + if (mates.contains(neighbor.index)) { | ||
| 1378 | + // Found a mate | ||
| 1379 | + if (!recordedHighestMatedSimilarity) { | ||
| 1380 | + matedSimilarities.append(neighbor.similarity); | ||
| 1381 | + recordedHighestMatedSimilarity = true; | ||
| 1382 | + } | ||
| 1383 | + if (firstGenuineReturns[i] < 1) firstGenuineReturns[i] = abs(firstGenuineReturns[i])+1; | ||
| 1384 | + } else { | ||
| 1385 | + // Found a non-mate | ||
| 1386 | + if (!recordedHighestUnmatedSimilarity) { | ||
| 1387 | + unmatedSimilarities.append(neighbor.similarity); | ||
| 1388 | + recordedHighestUnmatedSimilarity = true; | ||
| 1389 | + } | ||
| 1390 | + if (firstGenuineReturns[i] < 1) firstGenuineReturns[i]--; | ||
| 1391 | + } | ||
| 1392 | + | ||
| 1393 | + if (recordedHighestMatedSimilarity && recordedHighestUnmatedSimilarity) | ||
| 1394 | + break; // we can stop scanning the candidate list for this probe | ||
| 1395 | + } | ||
| 1396 | + } | ||
| 1397 | + | ||
| 1398 | + // Sort the similarity scores lowest-to-highest | ||
| 1399 | + std::sort(matedSimilarities.begin(), matedSimilarities.end()); | ||
| 1400 | + std::sort(unmatedSimilarities.begin(), unmatedSimilarities.end()); | ||
| 1401 | + const size_t numMatedSimilarities = matedSimilarities.size(); | ||
| 1402 | + | ||
| 1403 | + if (numMatedSearches == 0) | ||
| 1404 | + qFatal("No mated searches!"); | ||
| 1405 | + | ||
| 1406 | + if (numUnmatedSearches == 0) | ||
| 1407 | + qFatal("No unmated searches!"); | ||
| 1408 | + | ||
| 1409 | + | ||
| 1410 | + qDebug("Rank-%d Return Rate: %.3f", 1, getCMC(firstGenuineReturns, 1, numMatedSearches)); | ||
| 1411 | + if (k >=5) | ||
| 1412 | + qDebug("Rank-%d Return Rate: %.3f", 5, getCMC(firstGenuineReturns, 5, numMatedSearches)); | ||
| 1413 | + if (k >=10) | ||
| 1414 | + qDebug("Rank-%d Return Rate: %.3f", 10, getCMC(firstGenuineReturns, 10, numMatedSearches)); | ||
| 1415 | + | ||
| 1416 | + qDebug("Rank-%zu Return Rate: %.3f", k, double(numMatedSimilarities) / double(numMatedSearches)); | ||
| 1417 | + | ||
| 1418 | + /* | ||
| 1419 | + * Iterate through the similarity scores highest-to-lowest, | ||
| 1420 | + * for each threshold count the number mated and unmated searches, | ||
| 1421 | + * record the corresponding FPIR and FNIR values for the threshold. | ||
| 1422 | + */ | ||
| 1423 | + QList<OperatingPoint> operatingPoints; | ||
| 1424 | + size_t matedCount = 0, previousMatedCount = 0; | ||
| 1425 | + size_t unmatedCount = 0, previousUnmatedCount = 0; | ||
| 1426 | + while (!matedSimilarities.empty()) { | ||
| 1427 | + const float threshold = matedSimilarities.back(); | ||
| 1428 | + while (!matedSimilarities.empty() && (matedSimilarities.back() >= threshold)) { | ||
| 1429 | + matedSimilarities.removeLast(); | ||
| 1430 | + matedCount++; | ||
| 1431 | + } | ||
| 1432 | + while (!unmatedSimilarities.empty() && (unmatedSimilarities.back() >= threshold)) { | ||
| 1433 | + unmatedSimilarities.removeLast(); | ||
| 1434 | + unmatedCount++; | ||
| 1435 | + } | ||
| 1436 | + if ((unmatedCount > previousUnmatedCount) && (matedCount > previousMatedCount)) { | ||
| 1437 | + previousMatedCount = matedCount; | ||
| 1438 | + previousUnmatedCount = unmatedCount; | ||
| 1439 | + operatingPoints.append(OperatingPoint(threshold, | ||
| 1440 | + double(unmatedCount) / double(numUnmatedSearches), | ||
| 1441 | + 1.0 - double(matedCount) / double(numMatedSearches))); | ||
| 1442 | + } | ||
| 1443 | + } | ||
| 1444 | + | ||
| 1445 | + if (!csv.isEmpty()) { | ||
| 1446 | + // Open the output file | ||
| 1447 | + QFile ietFile(csv); | ||
| 1448 | + if (!ietFile.open(QFile::WriteOnly | QFile::Text)) | ||
| 1449 | + qFatal("Failed to open IET file for writing!"); | ||
| 1450 | + ietFile.write("Plot,X,Y,Z\n"); | ||
| 1451 | + // Write CMC | ||
| 1452 | + const int Max_Retrieval = min(200, (int)k); | ||
| 1453 | + for (int i=1; i<=Max_Retrieval; i++) { | ||
| 1454 | + const float retrievalRate = getCMC(firstGenuineReturns, i, numMatedSearches); | ||
| 1455 | + ietFile.write(qPrintable(QString("CMC,%1,%2,0\n").arg(QString::number(i), QString::number(retrievalRate)))); | ||
| 1456 | + } | ||
| 1457 | + | ||
| 1458 | + foreach(const OperatingPoint &operatingPoint, operatingPoints) | ||
| 1459 | + ietFile.write(qPrintable("IET," + | ||
| 1460 | + QString::number(operatingPoint.FAR) + "," + | ||
| 1461 | + QString::number(operatingPoint.TAR) + "," + | ||
| 1462 | + QString::number(operatingPoint.score) + "\n")); | ||
| 1463 | + } | ||
| 1464 | + | ||
| 1465 | + qDebug("FNIR @ FPIR = 0.1: %.3f", 1-getOperatingPointGivenFAR(operatingPoints, 0.1).TAR); | ||
| 1466 | + qDebug("FNIR @ FPIR = 0.01: %.3f", 1-getOperatingPointGivenFAR(operatingPoints, 0.01).TAR); | ||
| 1467 | +} | ||
| 1468 | + | ||
| 1303 | } // namespace br | 1469 | } // namespace br |
openbr/core/eval.h
| @@ -33,6 +33,17 @@ namespace br | @@ -33,6 +33,17 @@ namespace br | ||
| 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap | 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 | 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 | 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 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); | 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); |
| 36 | + void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &csv = ""); | ||
| 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 | #endif // BR_EVAL_H | 49 | #endif // BR_EVAL_H |
openbr/core/plot.cpp
| @@ -346,4 +346,32 @@ bool PlotMetadata(const QStringList &files, const QString &columns, bool show) | @@ -346,4 +346,32 @@ bool PlotMetadata(const QStringList &files, const QString &columns, bool show) | ||
| 346 | return p.finalize(show); | 346 | return p.finalize(show); |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | +bool PlotKNN(const QStringList &files, const File &destination, bool show) | ||
| 350 | +{ | ||
| 351 | + qDebug("Plotting %d k-NN file(s) to %s", files.size(), qPrintable(destination)); | ||
| 352 | + RPlot p(files, destination); | ||
| 353 | + p.file.write("\nformatData(type=\"knn\")\n\n"); | ||
| 354 | + | ||
| 355 | + QMap<QString,File> optMap; | ||
| 356 | + optMap.insert("rocOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=True Positive Identification Rate (TPIR),xLog=true,yLog=false]"))); | ||
| 357 | + optMap.insert("ietOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=False Negative Identification Rate (FNIR),xLog=true,yLog=true]"))); | ||
| 358 | + optMap.insert("cmcOptions", File(QString("[xTitle=Rank,yTitle=Retrieval Rate,xLog=true,yLog=false,size=1,xLabels=(1,5,10,50,100),xBreaks=(1,5,10,50,100)]"))); | ||
| 359 | + | ||
| 360 | + foreach (const QString &key, optMap.keys()) { | ||
| 361 | + const QStringList options = destination.get<QStringList>(key, QStringList()); | ||
| 362 | + foreach (const QString &option, options) { | ||
| 363 | + QStringList words = QtUtils::parse(option, '='); | ||
| 364 | + QtUtils::checkArgsSize(words[0], words, 1, 2); | ||
| 365 | + optMap[key].set(words[0], words[1]); | ||
| 366 | + } | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + QString plot = "plot <- plotLine(lineData=%1, options=list(%2), flipY=%3)\nplot\n"; | ||
| 370 | + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["rocOptions"]), "TRUE"))); | ||
| 371 | + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["ietOptions"]), "FALSE"))); | ||
| 372 | + p.file.write(qPrintable(QString(plot).arg("CMC", toRList(optMap["cmcOptions"]), "FALSE"))); | ||
| 373 | + | ||
| 374 | + return p.finalize(show); | ||
| 375 | +} | ||
| 376 | + | ||
| 349 | } // namespace br | 377 | } // namespace br |
openbr/core/plot.h
| @@ -28,6 +28,7 @@ namespace br | @@ -28,6 +28,7 @@ namespace br | ||
| 28 | bool PlotDetection(const QStringList &files, const File &destination, bool show = false); | 28 | bool PlotDetection(const QStringList &files, const File &destination, bool show = false); |
| 29 | bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false); | 29 | bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false); |
| 30 | bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); | 30 | bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); |
| 31 | + bool PlotKNN(const QStringList &files, const File &destination, bool show = false); | ||
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | #endif // BR_PLOT_H | 34 | #endif // BR_PLOT_H |
openbr/openbr.cpp
| @@ -145,6 +145,11 @@ void br_eval_regression(const char *predicted_gallery, const char *truth_gallery | @@ -145,6 +145,11 @@ void br_eval_regression(const char *predicted_gallery, const char *truth_gallery | ||
| 145 | EvalRegression(predicted_gallery, truth_gallery, predicted_property, truth_property); | 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 *csv) | ||
| 149 | +{ | ||
| 150 | + EvalKNN(knnGraph, knnTruth, csv); | ||
| 151 | +} | ||
| 152 | + | ||
| 148 | void br_finalize() | 153 | void br_finalize() |
| 149 | { | 154 | { |
| 150 | Context::finalize(); | 155 | Context::finalize(); |
| @@ -216,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b | @@ -216,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b | ||
| 216 | return PlotMetadata(QtUtils::toStringList(num_files, files), columns, show); | 221 | return PlotMetadata(QtUtils::toStringList(num_files, files), columns, show); |
| 217 | } | 222 | } |
| 218 | 223 | ||
| 224 | +bool br_plot_knn(int num_files, const char *files[], const char *destination, bool show) | ||
| 225 | +{ | ||
| 226 | + return PlotKNN(QtUtils::toStringList(num_files, files), destination, show); | ||
| 227 | +} | ||
| 228 | + | ||
| 219 | float br_progress() | 229 | float br_progress() |
| 220 | { | 230 | { |
| 221 | return Globals->progress(); | 231 | return Globals->progress(); |
openbr/openbr.h
| @@ -64,6 +64,8 @@ BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *t | @@ -64,6 +64,8 @@ BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *t | ||
| 64 | 64 | ||
| 65 | BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property = "", const char *truth_property = ""); | 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 *csv = ""); | ||
| 68 | + | ||
| 67 | BR_EXPORT void br_finalize(); | 69 | BR_EXPORT void br_finalize(); |
| 68 | 70 | ||
| 69 | BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], | 71 | BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], |
| @@ -91,6 +93,8 @@ BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const cha | @@ -91,6 +93,8 @@ BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const cha | ||
| 91 | 93 | ||
| 92 | BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char *columns, bool show = false); | 94 | BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char *columns, bool show = false); |
| 93 | 95 | ||
| 96 | +BR_EXPORT bool br_plot_knn(int num_files, const char *files[], const char *destination, bool show = false); | ||
| 97 | + | ||
| 94 | BR_EXPORT float br_progress(); | 98 | BR_EXPORT float br_progress(); |
| 95 | 99 | ||
| 96 | BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv); | 100 | BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv); |
openbr/plugins/classification/dlib.cpp
| 1 | +#include <opencv2/imgproc/imgproc.hpp> | ||
| 1 | #include <dlib/image_processing/frontal_face_detector.h> | 2 | #include <dlib/image_processing/frontal_face_detector.h> |
| 2 | #include <dlib/opencv.h> | 3 | #include <dlib/opencv.h> |
| 3 | 4 | ||
| @@ -45,14 +46,18 @@ private: | @@ -45,14 +46,18 @@ private: | ||
| 45 | { | 46 | { |
| 46 | dst = src; | 47 | dst = src; |
| 47 | 48 | ||
| 48 | - shape_predictor *sp = shapeResource.acquire(); | 49 | + shape_predictor *const sp = shapeResource.acquire(); |
| 49 | 50 | ||
| 50 | - cv_image<unsigned char> cimg(src.m()); | 51 | + cv::Mat cvImage = src.m(); |
| 52 | + if (cvImage.channels() == 3) | ||
| 53 | + cv::cvtColor(cvImage, cvImage, CV_BGR2GRAY); | ||
| 54 | + | ||
| 55 | + cv_image<unsigned char> cimg(cvImage); | ||
| 51 | array2d<unsigned char> image; | 56 | array2d<unsigned char> image; |
| 52 | assign_image(image,cimg); | 57 | assign_image(image,cimg); |
| 53 | 58 | ||
| 54 | if (src.file.rects().isEmpty()) { //If the image has no rects assume the whole image is a face | 59 | if (src.file.rects().isEmpty()) { //If the image has no rects assume the whole image is a face |
| 55 | - rectangle r(0, 0, src.m().cols, src.m().rows); | 60 | + rectangle r(0, 0, cvImage.cols, cvImage.rows); |
| 56 | full_object_detection shape = (*sp)(image, r); | 61 | full_object_detection shape = (*sp)(image, r); |
| 57 | for (size_t i = 0; i < shape.num_parts(); i++) | 62 | for (size_t i = 0; i < shape.num_parts(); i++) |
| 58 | dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1))); | 63 | dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1))); |
openbr/plugins/core/algorithms.cpp
| @@ -31,9 +31,9 @@ class AlgorithmsInitializer : public Initializer | @@ -31,9 +31,9 @@ class AlgorithmsInitializer : public Initializer | ||
| 31 | void initialize() const | 31 | void initialize() const |
| 32 | { | 32 | { |
| 33 | // Face | 33 | // Face |
| 34 | - Globals->abbreviations.insert("FaceRecognition", "FaceRecognition_1_0"); | 34 | + Globals->abbreviations.insert("FaceRecognition", "FaceRecognition_1_1"); |
| 35 | 35 | ||
| 36 | - Globals->abbreviations.insert("FaceRecognition_1_0", "FR_Detect+(FR_Eyes+FR_Represent)/(FR_Eyebrows+FR_Represent)/(FR_Mouth+FR_Represent)/(FR_Nose+FR_Represent)/(FR_Face+FR_Represent+ScaleMat(2.0))+Cat+LDA(768)+Normalize(L2):Dist(L2)"); | 36 | + Globals->abbreviations.insert("FaceRecognition_1_1", "FR_Detect+(FR_Eyes+FR_Represent)/(FR_Eyebrows+FR_Represent)/(FR_Mouth+FR_Represent)/(FR_Nose+FR_Represent)/(FR_Face+FR_Represent+ScaleMat(2.0))+Cat+LDA(768)+Normalize(L2):Unit(Dist(L2))"); |
| 37 | Globals->abbreviations.insert("FR_Eyes", "(CropFromLandmarks([30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],paddingVertical=.8,paddingHorizontal=.2)+Resize(24,48))"); | 37 | Globals->abbreviations.insert("FR_Eyes", "(CropFromLandmarks([30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],paddingVertical=.8,paddingHorizontal=.2)+Resize(24,48))"); |
| 38 | Globals->abbreviations.insert("FR_Eyebrows", "(CropFromLandmarks([16,17,18,19,20,21,22,23,24,25,26,27],paddingVertical=.8,paddingHorizontal=.2)+Resize(24,48))"); | 38 | Globals->abbreviations.insert("FR_Eyebrows", "(CropFromLandmarks([16,17,18,19,20,21,22,23,24,25,26,27],paddingVertical=.8,paddingHorizontal=.2)+Resize(24,48))"); |
| 39 | Globals->abbreviations.insert("FR_Mouth", "(CropFromLandmarks([59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76])+Resize(24,48))"); | 39 | Globals->abbreviations.insert("FR_Mouth", "(CropFromLandmarks([59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76])+Resize(24,48))"); |
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/gallery/keyframes.cpp
| @@ -59,6 +59,7 @@ public: | @@ -59,6 +59,7 @@ public: | ||
| 59 | fps = 0.f; | 59 | fps = 0.f; |
| 60 | time_base = 0.f; | 60 | time_base = 0.f; |
| 61 | idx = 0; | 61 | idx = 0; |
| 62 | + idxOffset = -1; | ||
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | ~keyframesGallery() | 65 | ~keyframesGallery() |
| @@ -95,6 +96,9 @@ public: | @@ -95,6 +96,9 @@ public: | ||
| 95 | frame = av_frame_alloc(); | 96 | frame = av_frame_alloc(); |
| 96 | cvt_frame = av_frame_alloc(); | 97 | cvt_frame = av_frame_alloc(); |
| 97 | 98 | ||
| 99 | + av_init_packet(&packet); | ||
| 100 | + packet.data = NULL; | ||
| 101 | + packet.size = 0; | ||
| 98 | // Get fps, stream time_base and allocate space for frame buffer with av_malloc. | 102 | // Get fps, stream time_base and allocate space for frame buffer with av_malloc. |
| 99 | fps = (float)avFormatCtx->streams[streamID]->avg_frame_rate.num / | 103 | fps = (float)avFormatCtx->streams[streamID]->avg_frame_rate.num / |
| 100 | (float)avFormatCtx->streams[streamID]->avg_frame_rate.den; | 104 | (float)avFormatCtx->streams[streamID]->avg_frame_rate.den; |
| @@ -128,26 +132,50 @@ public: | @@ -128,26 +132,50 @@ public: | ||
| 128 | Template output; | 132 | Template output; |
| 129 | output.file = file; | 133 | output.file = file; |
| 130 | 134 | ||
| 131 | - AVPacket packet; | ||
| 132 | - av_init_packet(&packet); | 135 | + |
| 133 | int ret = 0; | 136 | int ret = 0; |
| 134 | while (!ret) { | 137 | while (!ret) { |
| 135 | if (av_read_frame(avFormatCtx, &packet) >= 0) { | 138 | if (av_read_frame(avFormatCtx, &packet) >= 0) { |
| 136 | if (packet.stream_index == streamID) { | 139 | if (packet.stream_index == streamID) { |
| 137 | avcodec_decode_video2(avCodecCtx, frame, &ret, &packet); | 140 | avcodec_decode_video2(avCodecCtx, frame, &ret, &packet); |
| 138 | - idx = packet.dts; // decompression timestamp | 141 | + // Use presentation timestamp if available |
| 142 | + // Otherwise decode timestamp | ||
| 143 | + if (frame->pkt_pts != AV_NOPTS_VALUE) | ||
| 144 | + idx = frame->pkt_pts; | ||
| 145 | + else | ||
| 146 | + idx = frame->pkt_dts; | ||
| 147 | + | ||
| 139 | av_free_packet(&packet); | 148 | av_free_packet(&packet); |
| 140 | } else { | 149 | } else { |
| 141 | av_free_packet(&packet); | 150 | av_free_packet(&packet); |
| 142 | } | 151 | } |
| 143 | } else { | 152 | } else { |
| 144 | - av_free_packet(&packet); | ||
| 145 | - release(); | ||
| 146 | - *done = true; | ||
| 147 | - return TemplateList(); | 153 | + AVPacket empty_packet; |
| 154 | + av_init_packet(&empty_packet); | ||
| 155 | + empty_packet.data = NULL; | ||
| 156 | + empty_packet.size = 0; | ||
| 157 | + | ||
| 158 | + avcodec_decode_video2(avCodecCtx, frame, &ret, &empty_packet); | ||
| 159 | + if (frame->pkt_pts != AV_NOPTS_VALUE) | ||
| 160 | + idx = frame->pkt_pts; | ||
| 161 | + else if (frame->pkt_dts != AV_NOPTS_VALUE) | ||
| 162 | + idx = frame->pkt_dts; | ||
| 163 | + else // invalid frame | ||
| 164 | + ret = 0; | ||
| 165 | + | ||
| 166 | + if (!ret) { | ||
| 167 | + av_free_packet(&packet); | ||
| 168 | + av_free_packet(&empty_packet); | ||
| 169 | + release(); | ||
| 170 | + *done = true; | ||
| 171 | + return TemplateList(); | ||
| 172 | + } | ||
| 148 | } | 173 | } |
| 149 | } | 174 | } |
| 150 | 175 | ||
| 176 | + if (idxOffset < 0) { | ||
| 177 | + idxOffset = idx; | ||
| 178 | + } | ||
| 151 | // Convert from native format | 179 | // Convert from native format |
| 152 | sws_scale(avSwsCtx, | 180 | sws_scale(avSwsCtx, |
| 153 | frame->data, | 181 | frame->data, |
| @@ -164,9 +192,9 @@ public: | @@ -164,9 +192,9 @@ public: | ||
| 164 | avcodec_flush_buffers(avCodecCtx); | 192 | avcodec_flush_buffers(avCodecCtx); |
| 165 | 193 | ||
| 166 | QString URL = file.get<QString>("URL", file.name); | 194 | QString URL = file.get<QString>("URL", file.name); |
| 167 | - output.file.set("URL", URL + "#t=" + QString::number((int)(idx * time_base)) + "s"); | ||
| 168 | - output.file.set("timestamp", QString::number((int)(idx * time_base * 1000))); | ||
| 169 | - output.file.set("frame", QString::number(idx * time_base * fps)); | 195 | + output.file.set("URL", URL + "#t=" + QString::number((int)((idx-idxOffset) * time_base)) + "s"); |
| 196 | + output.file.set("timestamp", QString::number((int)((idx-idxOffset) * time_base * 1000))); | ||
| 197 | + output.file.set("frame", QString::number((idx-idxOffset) * time_base * fps)); | ||
| 170 | TemplateList dst; | 198 | TemplateList dst; |
| 171 | dst.append(output); | 199 | dst.append(output); |
| 172 | return dst; | 200 | return dst; |
| @@ -204,7 +232,10 @@ protected: | @@ -204,7 +232,10 @@ protected: | ||
| 204 | AVCodec *avCodec; | 232 | AVCodec *avCodec; |
| 205 | AVFrame *frame; | 233 | AVFrame *frame; |
| 206 | AVFrame *cvt_frame; | 234 | AVFrame *cvt_frame; |
| 235 | + AVPacket packet; | ||
| 207 | uint8_t *buffer; | 236 | uint8_t *buffer; |
| 237 | + | ||
| 238 | + int64_t idxOffset; | ||
| 208 | bool opened; | 239 | bool opened; |
| 209 | int streamID; | 240 | int streamID; |
| 210 | float fps; | 241 | float fps; |
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" |
scripts/face_cluster_viz.py renamed to scripts/brpy/face_cluster_viz.py
| 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
| 2 | 2 | ||
| 3 | -from PIL import Image | ||
| 4 | import csv, sys, json, argparse | 3 | import csv, sys, json, argparse |
| 4 | +from brpy.html_viz import crop_to_bb | ||
| 5 | 5 | ||
| 6 | parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') | 6 | parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') |
| 7 | parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') | 7 | parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') |
| @@ -16,24 +16,15 @@ clustmap = dict() | @@ -16,24 +16,15 @@ clustmap = dict() | ||
| 16 | with open(args.input_file) as f: | 16 | with open(args.input_file) as f: |
| 17 | for line in csv.DictReader(f): | 17 | for line in csv.DictReader(f): |
| 18 | c = int(line[args.cluster_key]) | 18 | c = int(line[args.cluster_key]) |
| 19 | + if c not in clustmap: | ||
| 20 | + clustmap[c] = [] | ||
| 19 | x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] | 21 | x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] |
| 20 | imname = '%s/%s' % (args.img_loc, line['File']) | 22 | imname = '%s/%s' % (args.img_loc, line['File']) |
| 21 | try: | 23 | try: |
| 22 | - img = Image.open(imname) | ||
| 23 | - imwidth, imheight = img.size | 24 | + html = crop_to_bb(x,y,width,height,imname,maxheight=400) |
| 24 | except IOError: | 25 | except IOError: |
| 25 | print('problem with %s' % imname) | 26 | print('problem with %s' % imname) |
| 26 | continue | 27 | continue |
| 27 | - ratio = maxheight / height | ||
| 28 | - # note for future me: | ||
| 29 | - # image is cropped with div width/height + overflow:hidden, | ||
| 30 | - # resized with img height, | ||
| 31 | - # and positioned with img margin | ||
| 32 | - html = '<div style="overflow:hidden;display:inline-block;width:%ipx;height:%ipx;">' % (width*ratio, maxheight) | ||
| 33 | - html += '<img src="%s" style="height:%ipx;margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio) | ||
| 34 | - html += '</div>' | ||
| 35 | - if c not in clustmap: | ||
| 36 | - clustmap[c] = [] | ||
| 37 | clustmap[c].append(html) | 28 | clustmap[c].append(html) |
| 38 | 29 | ||
| 39 | # browsers crash for a DOM with this many img tags, | 30 | # browsers crash for a DOM with this many img tags, |
scripts/brpy/html_viz.py
0 โ 100644
| 1 | +''' | ||
| 2 | +Some funcs to generate HTML visualizations. | ||
| 3 | +Run from the folder you intend to save the HTML page so | ||
| 4 | +the relative paths in the HTML file are correct and PIL can find the images on disk. | ||
| 5 | +Requires local images, but should be pretty easy to set up an apache server (or whatev) | ||
| 6 | +and host them as long as the relative paths remain the same on ya serva. | ||
| 7 | +''' | ||
| 8 | + | ||
| 9 | +from PIL import Image | ||
| 10 | + | ||
| 11 | +def crop_to_bb(x, y, width, height, imname, maxheight=None): | ||
| 12 | + ''' | ||
| 13 | + Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels. | ||
| 14 | + A maxheight of None will keep the original size (default). | ||
| 15 | + When two crops are put next to each other, they will be inline. To make each crop its own line, wrap it in a div. | ||
| 16 | + ''' | ||
| 17 | + img = Image.open(imname) | ||
| 18 | + imwidth, imheight = img.size | ||
| 19 | + if not maxheight: | ||
| 20 | + maxheight = height | ||
| 21 | + ratio = maxheight / height | ||
| 22 | + # note for future me: | ||
| 23 | + # image is cropped with div width/height + overflow:hidden, | ||
| 24 | + # resized with img height, | ||
| 25 | + # and positioned with img margin | ||
| 26 | + html = '<div style="overflow:hidden; display:inline-block; width:%ipx; height:%ipx;">' % (width*ratio, maxheight) | ||
| 27 | + html += '<img src="%s" style="height:%ipx; margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio) | ||
| 28 | + html += '</div>' | ||
| 29 | + return html | ||
| 30 | + | ||
| 31 | +def bbs_for_image(imname, bbs, maxheight=None, colors=None): | ||
| 32 | + ''' | ||
| 33 | + Generates an HTML string for an image with bounding boxes. | ||
| 34 | + bbs: iterable of (x,y,width,height) bounding box tuples | ||
| 35 | + ''' | ||
| 36 | + img = Image.open(imname) | ||
| 37 | + imwidth, imheight = img.size | ||
| 38 | + if not maxheight: | ||
| 39 | + maxheight = imheight | ||
| 40 | + ratio = maxheight/imheight | ||
| 41 | + html = [ | ||
| 42 | + '<div style="position:relative">', | ||
| 43 | + '<img src="%s" style="height:%ipx" />' % (imname, maxheight) | ||
| 44 | + ] | ||
| 45 | + if not colors: | ||
| 46 | + colors = ['green']*len(bbs) | ||
| 47 | + html.extend([ bb(*box, ratio=ratio, color=color) for color,box in zip(colors,bbs) ]) | ||
| 48 | + html.append('</div>') | ||
| 49 | + return '\n'.join(html) | ||
| 50 | + | ||
| 51 | + | ||
| 52 | +def bb(x, y, width, height, ratio=1.0, color='green'): | ||
| 53 | + ''' | ||
| 54 | + Generates an HTML string bounding box. | ||
| 55 | + ''' | ||
| 56 | + html = '<div style="position:absolute; border:2px solid %s; color:%s; left:%ipx; top:%ipx; width:%ipx; height:%ipx;"></div>' | ||
| 57 | + return html % (color, color, x*ratio, y*ratio, width*ratio, height*ratio) |
share/openbr/cmake/InstallDependencies.cmake
| @@ -83,6 +83,19 @@ function(install_qt_imageformats) | @@ -83,6 +83,19 @@ function(install_qt_imageformats) | ||
| 83 | endif() | 83 | endif() |
| 84 | endfunction() | 84 | endfunction() |
| 85 | 85 | ||
| 86 | +function(install_qt_platforms) | ||
| 87 | + if(${BR_INSTALL_DEPENDENCIES}) | ||
| 88 | + if(CMAKE_HOST_WIN32) | ||
| 89 | + #TODO | ||
| 90 | + elseif(CMAKE_HOST_APPLE) | ||
| 91 | + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqcocoa.dylib | ||
| 92 | + DESTINATION bin/platforms) | ||
| 93 | + else() | ||
| 94 | + #TODO | ||
| 95 | + endif() | ||
| 96 | + endif() | ||
| 97 | +endfunction() | ||
| 98 | + | ||
| 86 | # Qt Other | 99 | # Qt Other |
| 87 | function(install_qt_misc) | 100 | function(install_qt_misc) |
| 88 | if(MSVC) | 101 | if(MSVC) |
share/openbr/plotting/plot_utils.R
| @@ -217,6 +217,12 @@ formatData <- function(type="eval") { | @@ -217,6 +217,12 @@ formatData <- function(type="eval") { | ||
| 217 | NormLength <<- data[grep("NormLength",data$Plot),-c(1)] | 217 | NormLength <<- data[grep("NormLength",data$Plot),-c(1)] |
| 218 | sample <<- readImageData(Sample) | 218 | sample <<- readImageData(Sample) |
| 219 | rows <<- sample[[1]]$value | 219 | rows <<- sample[[1]]$value |
| 220 | + } else if (type == "knn") { | ||
| 221 | + # Split data into individual plots | ||
| 222 | + IET <<- data[grep("IET",data$Plot),-c(1)] | ||
| 223 | + IET$Y <<- as.numeric(as.character(IET$Y)) | ||
| 224 | + CMC <<- data[grep("CMC",data$Plot),-c(1)] | ||
| 225 | + CMC$Y <<- as.numeric(as.character(CMC$Y)) | ||
| 220 | } | 226 | } |
| 221 | } | 227 | } |
| 222 | 228 |