Commit 107e229c5dd19347d4dc046ceaa2156ac9c7b2e0

Authored by Scott Klum
2 parents 848a7e70 9b345841

Merge branch 'master' of https://github.com/biometrics/openbr

CMakeLists.txt
... ... @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR)
13 13 set(CPACK_PACKAGE_VERSION_MAJOR 1)
14 14 endif()
15 15 if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR)
16   - set(CPACK_PACKAGE_VERSION_MINOR 0)
  16 + set(CPACK_PACKAGE_VERSION_MINOR 1)
17 17 endif()
18 18 if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH)
19 19 set(CPACK_PACKAGE_VERSION_PATCH 0)
... ... @@ -158,8 +158,8 @@ endif()
158 158  
159 159 # Download the models
160 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 163 SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models"
164 164 CONFIGURE_COMMAND ""
165 165 BUILD_COMMAND ""
... ...
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 <= 3, "Incorrect parameter count for 'evalKNN'.");
  173 + br_eval_knn(parv[0], parv[1], parc > 2 ? 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] : "");
... ... @@ -183,6 +186,9 @@ public:
183 186 } else if (!strcmp(fun, "plotMetadata")) {
184 187 check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
185 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 192 } else if (!strcmp(fun, "project")) {
187 193 check(parc == 2, "Insufficient parameter count for 'project'.");
188 194 br_project(parv[0], parv[1]);
... ... @@ -276,12 +282,14 @@ private:
276 282 "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n"
277 283 "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n"
278 284 "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n"
  285 + "-evalKNN <knn_graph> <knn_truth> [{csv}]\n"
279 286 "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n"
280 287 "-inplaceEval <simmat> <target> <query> [{csv}]\n"
281 288 "-assertEval <simmat> <mask> <accuracy>\n"
282 289 "-plotDetection <file> ... <file> {destination}\n"
283 290 "-plotLandmarking <file> ... <file> {destination}\n"
284 291 "-plotMetadata <file> ... <file> <columns>\n"
  292 + "-plotKNN <file> ... <file> {destination}\n"
285 293 "-project <input_gallery> {output_gallery}\n"
286 294 "-deduplicate <input_gallery> <output_gallery> <threshold>\n"
287 295 "-likely <input_type> <output_type> <output_likely_source>\n"
... ...
docs/docs/install.md
... ... @@ -35,7 +35,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
35 35  
36 36 $ git clone https://github.com/biometrics/openbr.git
37 37 $ cd openbr
38   - $ git checkout 1.0
  38 + $ git checkout v1.1.0
39 39 $ git submodule init
40 40 $ git submodule update
41 41  
... ... @@ -116,7 +116,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
116 116  
117 117 $ git clone https://github.com/biometrics/openbr.git
118 118 $ cd openbr
119   - $ git checkout 1.0
  119 + $ git checkout v1.1.0
120 120 $ git submodule init
121 121 $ git submodule update
122 122  
... ... @@ -196,7 +196,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
196 196 $ cd /c
197 197 $ git clone https://github.com/biometrics/openbr.git
198 198 $ cd openbr
199   - $ git checkout 1.0
  199 + $ git checkout v1.1.0
200 200 $ git submodule init
201 201 $ git submodule update
202 202  
... ... @@ -277,7 +277,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
277 277  
278 278 $ git clone https://github.com/biometrics/openbr.git
279 279 $ cd openbr
280   - $ git checkout 1.0
  280 + $ git checkout v1.1.0
281 281 $ git submodule init
282 282 $ git submodule update
283 283  
... ...
openbr/core/eval.cpp
... ... @@ -102,12 +102,14 @@ static OperatingPoint getOperatingPointGivenTAR(const QList&lt;OperatingPoint&gt; &amp;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 109 foreach (int firstGenuineReturn, firstGenuineReturns) {
109 110 if (firstGenuineReturn > 0) {
110   - possibleReturns++;
  111 + if (calcPossible)
  112 + possibleReturns++;
111 113 if (firstGenuineReturn <= rank) realizedReturns++;
112 114 }
113 115 }
... ... @@ -1300,4 +1302,168 @@ void EvalRegression(const QString &amp;predictedGallery, const QString &amp;truthGallery
1300 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 1469 } // 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 &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 49 #endif // BR_EVAL_H
... ...
openbr/core/plot.cpp
... ... @@ -346,4 +346,32 @@ bool PlotMetadata(const QStringList &amp;files, const QString &amp;columns, bool show)
346 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 377 } // namespace br
... ...
openbr/core/plot.h
... ... @@ -28,6 +28,7 @@ namespace br
28 28 bool PlotDetection(const QStringList &files, const File &destination, bool show = false);
29 29 bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false);
30 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 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 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 153 void br_finalize()
149 154 {
150 155 Context::finalize();
... ... @@ -216,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b
216 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 229 float br_progress()
220 230 {
221 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 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 *csv = "");
  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[],
... ... @@ -91,6 +93,8 @@ BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const cha
91 93  
92 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 98 BR_EXPORT float br_progress();
95 99  
96 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 2 #include <dlib/image_processing/frontal_face_detector.h>
2 3 #include <dlib/opencv.h>
3 4  
... ... @@ -45,14 +46,18 @@ private:
45 46 {
46 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 56 array2d<unsigned char> image;
52 57 assign_image(image,cimg);
53 58  
54 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 61 full_object_detection shape = (*sp)(image, r);
57 62 for (size_t i = 0; i < shape.num_parts(); i++)
58 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 31 void initialize() const
32 32 {
33 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 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 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 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 59 fps = 0.f;
60 60 time_base = 0.f;
61 61 idx = 0;
  62 + idxOffset = -1;
62 63 }
63 64  
64 65 ~keyframesGallery()
... ... @@ -95,6 +96,9 @@ public:
95 96 frame = av_frame_alloc();
96 97 cvt_frame = av_frame_alloc();
97 98  
  99 + av_init_packet(&packet);
  100 + packet.data = NULL;
  101 + packet.size = 0;
98 102 // Get fps, stream time_base and allocate space for frame buffer with av_malloc.
99 103 fps = (float)avFormatCtx->streams[streamID]->avg_frame_rate.num /
100 104 (float)avFormatCtx->streams[streamID]->avg_frame_rate.den;
... ... @@ -128,26 +132,50 @@ public:
128 132 Template output;
129 133 output.file = file;
130 134  
131   - AVPacket packet;
132   - av_init_packet(&packet);
  135 +
133 136 int ret = 0;
134 137 while (!ret) {
135 138 if (av_read_frame(avFormatCtx, &packet) >= 0) {
136 139 if (packet.stream_index == streamID) {
137 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 148 av_free_packet(&packet);
140 149 } else {
141 150 av_free_packet(&packet);
142 151 }
143 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 179 // Convert from native format
152 180 sws_scale(avSwsCtx,
153 181 frame->data,
... ... @@ -164,9 +192,9 @@ public:
164 192 avcodec_flush_buffers(avCodecCtx);
165 193  
166 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 198 TemplateList dst;
171 199 dst.append(output);
172 200 return dst;
... ... @@ -204,7 +232,10 @@ protected:
204 232 AVCodec *avCodec;
205 233 AVFrame *frame;
206 234 AVFrame *cvt_frame;
  235 + AVPacket packet;
207 236 uint8_t *buffer;
  237 +
  238 + int64_t idxOffset;
208 239 bool opened;
209 240 int streamID;
210 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 1 #!/usr/bin/env python
2 2  
3   -from PIL import Image
4 3 import csv, sys, json, argparse
  4 +from brpy.html_viz import crop_to_bb
5 5  
6 6 parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.')
7 7 parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)')
... ... @@ -16,24 +16,15 @@ clustmap = dict()
16 16 with open(args.input_file) as f:
17 17 for line in csv.DictReader(f):
18 18 c = int(line[args.cluster_key])
  19 + if c not in clustmap:
  20 + clustmap[c] = []
19 21 x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ]
20 22 imname = '%s/%s' % (args.img_loc, line['File'])
21 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 25 except IOError:
25 26 print('problem with %s' % imname)
26 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 28 clustmap[c].append(html)
38 29  
39 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 83 endif()
84 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 99 # Qt Other
87 100 function(install_qt_misc)
88 101 if(MSVC)
... ...
share/openbr/plotting/plot_utils.R
... ... @@ -217,6 +217,12 @@ formatData &lt;- function(type=&quot;eval&quot;) {
217 217 NormLength <<- data[grep("NormLength",data$Plot),-c(1)]
218 218 sample <<- readImageData(Sample)
219 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  
... ...