Commit 2453671339ad4b60282cea9a8f2f030d4e636e10

Authored by DepthDeluxe
2 parents 1dabf42d 0f9a2740

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

Showing 44 changed files with 856 additions and 605 deletions
.gitignore
... ... @@ -32,6 +32,9 @@ share/openbr/models
32 32 *.RData
33 33 *.Rhistory
34 34  
  35 +### Python ###
  36 +*.pyc
  37 +
35 38 ### Subversion ###
36 39 *.svn*
37 40  
... ...
CMakeLists.txt
... ... @@ -86,7 +86,7 @@ set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Qt5Core_QTMAIN_LIBRARIES})
86 86  
87 87 # Find OpenCV
88 88 find_package(OpenCV 2.4.5 REQUIRED)
89   -set(OPENCV_DEPENDENCIES opencv_core opencv_highgui opencv_imgproc opencv_ml opencv_objdetect)
  89 +set(OPENCV_DEPENDENCIES opencv_core opencv_highgui opencv_imgproc opencv_ml)
90 90 set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${OPENCV_DEPENDENCIES})
91 91  
92 92 # Find Alphanum
... ...
docs/docs/api_docs/python_api.md
... ... @@ -46,6 +46,14 @@ The Python API is a light wrapper of the C API. It creates an object that has [a
46 46 br.br_free_template_list(query)
47 47 br.br_finalize()
48 48  
  49 +Some functions were made more pythonic and thus differ slightly from the C API. In particular, the C functions that populate a string buffer have that logic abstracted away. For example, `br_get_filename(char * buffer, int buffer_length, br_template tmpl)` in C becomes `filename = br.br_get_filename(tmpl)` in Python. Here are the functions that differ from the C API:
  50 +
  51 +- br_most_recent_message
  52 +- br_objects
  53 +- br_scratch_path
  54 +- br_get_filename
  55 +- br_get_metadata_string
  56 +
49 57 To enable the module, add `-DBR_INSTALL_BRPY=ON` to your cmake command (or use the ccmake GUI - highly recommended).
50 58  
51 59 Currently only OS X and Linux are supported.
... ...
openbr/CMakeLists.txt
... ... @@ -34,10 +34,7 @@ if(NOT BR_EMBEDDED)
34 34 install(FILES ${HEADERS} DESTINATION include/openbr/gui)
35 35 endif()
36 36  
37   -# normal BR library declaration - added openbr-cuda library
38   -message(STATUS "BR_THIRDPARTY_SRC")
39   -message(STATUS ${BR_THIRDPARTY_SRC})
40   -cuda_add_library(openbr SHARED ${SRC} ${BR_CORE} ${BR_JANUS} ${BR_GUI} ${BR_ICONS} ${BR_THIRDPARTY_SRC} ${BR_RESOURCES} ${NATURALSTRINGCOMPARE_SRC})
  37 +cuda_add_library(openbr SHARED ${SRC} ${BR_CORE} ${BR_JANUS} ${BR_GUI} ${BR_ICONS} ${BR_THIRDPARTY_SRC} ${BR_RESOURCES} ${NATURALSTRINGCOMPARE_SRC} ${THIRDPARTY_RESOURCES})
41 38 qt5_use_modules(openbr ${QT_DEPENDENCIES})
42 39 set_target_properties(openbr PROPERTIES
43 40 DEFINE_SYMBOL BR_LIBRARY
... ...
openbr/core/bee.h
... ... @@ -34,21 +34,21 @@ namespace BEE
34 34 const MaskValue DontCare(0x00);
35 35  
36 36 // Sigset
37   - br::FileList readSigset(const br::File &sigset, bool ignoreMetadata = false);
38   - void writeSigset(const QString &sigset, const br::FileList &files, bool ignoreMetadata = false);
  37 + BR_EXPORT br::FileList readSigset(const br::File &sigset, bool ignoreMetadata = false);
  38 + BR_EXPORT void writeSigset(const QString &sigset, const br::FileList &files, bool ignoreMetadata = false);
39 39  
40 40 // Matrix
41   - cv::Mat readMatrix(const br::File &mat, QString *targetSigset = NULL, QString *querySigset = NULL);
42   - void writeMatrix(const cv::Mat &m, const QString &fileName, const QString &targetSigset = "Unknown_Target", const QString &querySigset = "Unknown_Query");
43   - void readMatrixHeader(const QString &matrix, QString *targetSigset, QString *querySigset);
44   - void writeMatrixHeader(const QString &matrix, const QString &targetSigset, const QString &querySigset);
  41 + BR_EXPORT cv::Mat readMatrix(const br::File &mat, QString *targetSigset = NULL, QString *querySigset = NULL);
  42 + BR_EXPORT void writeMatrix(const cv::Mat &m, const QString &fileName, const QString &targetSigset = "Unknown_Target", const QString &querySigset = "Unknown_Query");
  43 + BR_EXPORT void readMatrixHeader(const QString &matrix, QString *targetSigset, QString *querySigset);
  44 + BR_EXPORT void writeMatrixHeader(const QString &matrix, const QString &targetSigset, const QString &querySigset);
45 45  
46 46 // Mask
47   - void makeMask(const QString &targetInput, const QString &queryInput, const QString &mask);
48   - cv::Mat makeMask(const br::FileList &targets, const br::FileList &queries, int partition = 0);
49   - void makePairwiseMask(const QString &targetInput, const QString &queryInput, const QString &mask);
50   - cv::Mat makePairwiseMask(const br::FileList &targets, const br::FileList &queries, int partition = 0);
51   - void combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method);
  47 + BR_EXPORT void makeMask(const QString &targetInput, const QString &queryInput, const QString &mask);
  48 + BR_EXPORT cv::Mat makeMask(const br::FileList &targets, const br::FileList &queries, int partition = 0);
  49 + BR_EXPORT void makePairwiseMask(const QString &targetInput, const QString &queryInput, const QString &mask);
  50 + BR_EXPORT cv::Mat makePairwiseMask(const br::FileList &targets, const br::FileList &queries, int partition = 0);
  51 + BR_EXPORT void combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method);
52 52 }
53 53  
54 54 #endif // BEE_BEE_H
... ...
openbr/core/boost.cpp
... ... @@ -788,14 +788,17 @@ void CascadeBoostTrainData::precalculate()
788 788 {
789 789 int minNum = MIN( numPrecalcVal, numPrecalcIdx);
790 790  
791   - double proctime = -TIME( 0 );
  791 + QTime time;
  792 + time.start();
  793 +
792 794 parallel_for_( Range(numPrecalcVal, numPrecalcIdx),
793 795 FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) );
794 796 parallel_for_( Range(0, minNum),
795 797 FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) );
796 798 parallel_for_( Range(minNum, numPrecalcVal),
797   - FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) );
798   - cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl;
  799 + FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) );
  800 +
  801 + cout << "Precalculation time (ms): " << time.elapsed() << endl;
799 802 }
800 803  
801 804 //-------------------------------- CascadeBoostTree ----------------------------------------
... ...
openbr/core/boost.h
... ... @@ -4,12 +4,6 @@
4 4 #include "ml.h"
5 5 #include <openbr/openbr_plugin.h>
6 6  
7   -#ifdef _WIN32
8   -#define TIME( arg ) (((double) clock()) / CLOCKS_PER_SEC)
9   -#else
10   -#define TIME( arg ) (time( arg ))
11   -#endif
12   -
13 7 namespace br
14 8 {
15 9  
... ...
openbr/core/eval.cpp
... ... @@ -19,10 +19,13 @@
19 19 #include "openbr/core/common.h"
20 20 #include "openbr/core/qtutils.h"
21 21 #include "openbr/core/opencvutils.h"
  22 +#include "openbr/core/evalutils.h"
22 23 #include <QMapIterator>
23 24 #include <cmath>
  25 +#include <opencv2/highgui/highgui.hpp>
24 26  
25 27 using namespace cv;
  28 +using namespace EvalUtils;
26 29  
27 30 namespace br
28 31 {
... ... @@ -752,298 +755,6 @@ void EvalClassification(const QString &amp;predictedGallery, const QString &amp;truthGal
752 755 qDebug("Overall Accuracy = %f", (float)tpc / (float)(tpc + fnc));
753 756 }
754 757  
755   -struct Detection
756   -{
757   - QRectF boundingBox;
758   - float confidence;
759   - // The ignore flag is useful when certain faces in an image should be ignored
760   - // and should not effect detection performance. Predicted detections that overlap
761   - // with an ignored truth detection will not count as a true positive, false positive,
762   - // true negative, or false negative, it will simply be ignored.
763   - bool ignore;
764   -
765   - Detection() {}
766   - Detection(const QRectF &boundingBox_, float confidence_ = -1, bool ignore_ = false)
767   - : boundingBox(boundingBox_), confidence(confidence_), ignore(ignore_) {}
768   -
769   - float area() const { return boundingBox.width() * boundingBox.height(); }
770   - float overlap(const Detection &other) const
771   - {
772   - const Detection intersection(boundingBox.intersected(other.boundingBox));
773   - return intersection.area() / (area() + other.area() - intersection.area());
774   - }
775   -};
776   -
777   -struct SortedDetection
778   -{
779   - int truth_idx, predicted_idx;
780   - float overlap;
781   - SortedDetection() : truth_idx(-1), predicted_idx(-1), overlap(-1) {}
782   - SortedDetection(int truth_idx_, int predicted_idx_, float overlap_)
783   - : truth_idx(truth_idx_), predicted_idx(predicted_idx_), overlap(overlap_) {}
784   - inline bool operator<(const SortedDetection &other) const { return overlap > other.overlap; }
785   -};
786   -
787   -struct Detections
788   -{
789   - QList<Detection> predicted, truth;
790   -};
791   -
792   -struct ResolvedDetection
793   -{
794   - float confidence, overlap;
795   - ResolvedDetection() : confidence(-1), overlap(-1) {}
796   - ResolvedDetection(float confidence_, float overlap_) : confidence(confidence_), overlap(overlap_) {}
797   - inline bool operator<(const ResolvedDetection &other) const { return confidence > other.confidence; }
798   -};
799   -
800   -struct DetectionOperatingPoint
801   -{
802   - float Recall, FalsePositiveRate, Precision;
803   - DetectionOperatingPoint() : Recall(-1), FalsePositiveRate(-1), Precision(-1) {}
804   - DetectionOperatingPoint(float TP, float FP, float totalPositives, float numImages)
805   - : Recall(TP/totalPositives), FalsePositiveRate(FP/numImages), Precision(TP/(TP+FP)) {}
806   -};
807   -
808   -static QStringList computeDetectionResults(const QList<ResolvedDetection> &detections, int totalTrueDetections, int numImages, bool discrete)
809   -{
810   - QList<DetectionOperatingPoint> points;
811   - float TP = 0, FP = 0, prevFP = -1;
812   -
813   - for (int i=0; i<detections.size(); i++) {
814   - const ResolvedDetection &detection = detections[i];
815   - if (discrete) {
816   - // A 50% overlap is considered a true positive
817   - if (detection.overlap >= 0.5) TP++;
818   - else FP++;
819   - } else {
820   - TP += detection.overlap;
821   - FP += 1 - detection.overlap;
822   - }
823   - if ((i == detections.size()-1) || (detection.confidence > detections[i+1].confidence)) {
824   - if (FP > prevFP || (i == detections.size()-1)) {
825   - if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) {
826   - qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections);
827   - qDebug("Confidence: %f", detection.confidence);
828   - qDebug("TP vs. FP: %f to %f", TP, FP);
829   - } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) {
830   - qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections);
831   - qDebug("Confidence: %f", detection.confidence);
832   - qDebug("TP vs. FP: %f to %f", TP, FP);
833   - } else if (prevFP / numImages < 0.001 && FP / numImages > 0.001 && discrete) {
834   - qDebug("TAR @ FAR => %f : 0.001", TP / totalTrueDetections);
835   - qDebug("Confidence: %f", detection.confidence);
836   - qDebug("TP vs. FP: %f to %f", TP, FP);
837   - }
838   -
839   - points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages));
840   - prevFP = FP;
841   - }
842   - }
843   - }
844   -
845   - if (discrete) {
846   - qDebug("Total TP vs. FP: %f to %f", TP, FP);
847   - qDebug("Overall Recall (TP vs. possible TP): %f (%f vs. %d)", TP / totalTrueDetections, TP, totalTrueDetections);
848   - }
849   -
850   - const int keep = qMin(points.size(), Max_Points);
851   - if (keep < 1) qFatal("Insufficient points.");
852   -
853   - QStringList lines; lines.reserve(keep);
854   - if (keep == 1) {
855   - const DetectionOperatingPoint &point = points[0];
856   - lines.append(QString("%1ROC, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.FalsePositiveRate), QString::number(point.Recall)));
857   - lines.append(QString("%1PR, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.Recall), QString::number(point.Precision)));
858   - } else {
859   - for (int i=0; i<keep; i++) {
860   - const DetectionOperatingPoint &point = points[double(i) / double(keep-1) * double(points.size()-1)];
861   - lines.append(QString("%1ROC, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.FalsePositiveRate), QString::number(point.Recall)));
862   - lines.append(QString("%1PR, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.Recall), QString::number(point.Precision)));
863   - }
864   - }
865   - return lines;
866   -}
867   -
868   -struct DetectionKey : public QString
869   -{
870   - enum Type {
871   - Invalid,
872   - Rect,
873   - RectList,
874   - XYWidthHeight
875   - } type;
876   -
877   - DetectionKey(const QString &key = "", Type type = Invalid)
878   - : QString(key), type(type) {}
879   -};
880   -
881   -static DetectionKey getDetectKey(const FileList &files)
882   -{
883   - if (files.empty())
884   - return DetectionKey();
885   -
886   - const File &f = files.first();
887   - const QStringList localKeys = f.localKeys();
888   -
889   - // first check for single detections
890   - foreach (const QString &key, localKeys)
891   - if (!f.get<QRectF>(key, QRectF()).isNull())
892   - return DetectionKey(key, DetectionKey::Rect);
893   -
894   - // and then multiple
895   - if (!f.rects().empty())
896   - return DetectionKey("Rects", DetectionKey::RectList);
897   -
898   - // check for <Key>_X, <Key>_Y, <Key>_Width, <Key>_Height
899   - foreach (const QString &localKey, localKeys) {
900   - if (!localKey.endsWith("_X"))
901   - continue;
902   - const QString key = localKey.mid(0, localKey.size()-2);
903   - if (localKeys.contains(key+"_Y") &&
904   - localKeys.contains(key+"_Width") &&
905   - localKeys.contains(key+"_Height"))
906   - return DetectionKey(key, DetectionKey::XYWidthHeight);
907   - }
908   -
909   - return DetectionKey();
910   -}
911   -
912   -// return a list of detections independent of the detection key format
913   -static QList<Detection> getDetections(const DetectionKey &key, const File &f, bool isTruth)
914   -{
915   - QList<Detection> dets;
916   - if (key.type == DetectionKey::RectList) {
917   - QList<QRectF> rects = f.rects();
918   - QList<float> confidences = f.getList<float>("Confidences", QList<float>());
919   - if (!isTruth && rects.size() != confidences.size())
920   - qFatal("You don't have enough confidence. I mean, your detections don't all have confidence measures.");
921   - for (int i=0; i<rects.size(); i++) {
922   - if (isTruth)
923   - dets.append(Detection(rects[i]));
924   - else
925   - dets.append(Detection(rects[i], confidences[i]));
926   - }
927   - } else if (key.type == DetectionKey::Rect) {
928   - dets.append(Detection(f.get<QRectF>(key), isTruth ? -1 : f.get<float>("Confidence", -1)));
929   - } else if (key.type == DetectionKey::XYWidthHeight) {
930   - const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height"));
931   - dets.append(Detection(rect, isTruth ? -1 : f.get<float>("Confidence", -1), f.get<bool>("Ignore", false)));
932   - }
933   - return dets;
934   -}
935   -
936   -static QMap<QString, Detections> getDetections(const File &predictedGallery, const File &truthGallery)
937   -{
938   - const FileList predicted = TemplateList::fromGallery(predictedGallery).files();
939   - const FileList truth = TemplateList::fromGallery(truthGallery).files();
940   -
941   - // Figure out which metadata field contains a bounding box
942   - DetectionKey truthDetectKey = getDetectKey(truth);
943   - if (truthDetectKey.isEmpty())
944   - qFatal("No suitable ground truth metadata key found.");
945   -
946   - DetectionKey predictedDetectKey = getDetectKey(predicted);
947   - if (predictedDetectKey.isEmpty())
948   - qFatal("No suitable predicted metadata key found.");
949   -
950   - qDebug("Using metadata key: %s%s",
951   - qPrintable(predictedDetectKey),
952   - qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey));
953   -
954   - QMap<QString, Detections> allDetections;
955   - foreach (const File &f, truth)
956   - allDetections[f.name].truth.append(getDetections(truthDetectKey, f, true));
957   - foreach (const File &f, predicted)
958   - if (allDetections.contains(f.name)) allDetections[f.name].predicted.append(getDetections(predictedDetectKey, f, false));
959   - return allDetections;
960   -}
961   -
962   -static inline int getNumberOfImages(const QMap<QString, Detections> detections)
963   -{
964   - return detections.keys().size();
965   -}
966   -
967   -static int associateGroundTruthDetections(QList<ResolvedDetection> &resolved, QList<ResolvedDetection> &falseNegative, QMap<QString, Detections> &all, QRectF &offsets)
968   -{
969   - float dLeftTotal = 0.0, dRightTotal = 0.0, dTopTotal = 0.0, dBottomTotal = 0.0;
970   - int count = 0, totalTrueDetections = 0;
971   -
972   - foreach (Detections detections, all.values()) {
973   - for (int i=0; i<detections.truth.size(); i++)
974   - if (!detections.truth[i].ignore) totalTrueDetections++;
975   -
976   - // Try to associate ground truth detections with predicted detections
977   - QList<SortedDetection> sortedDetections; sortedDetections.reserve(detections.truth.size() * detections.predicted.size());
978   - for (int t = 0; t < detections.truth.size(); t++) {
979   - const Detection truth = detections.truth[t];
980   - for (int p = 0; p < detections.predicted.size(); p++) {
981   - Detection predicted = detections.predicted[p];
982   -
983   - float predictedWidth = predicted.boundingBox.width();
984   - float x, y, width, height;
985   - x = predicted.boundingBox.x() + offsets.x()*predictedWidth;
986   - y = predicted.boundingBox.y() + offsets.y()*predictedWidth;
987   - width = predicted.boundingBox.width() - offsets.width()*predictedWidth;
988   - height = predicted.boundingBox.height() - offsets.height()*predictedWidth;
989   - Detection newPredicted(QRectF(x, y, width, height), 0.0);
990   -
991   - const float overlap = truth.overlap(newPredicted);
992   - if (overlap > 0)
993   - sortedDetections.append(SortedDetection(t, p, overlap));
994   - }
995   - }
996   -
997   - std::sort(sortedDetections.begin(), sortedDetections.end());
998   -
999   - QList<int> removedTruth;
1000   - QList<int> removedPredicted;
1001   -
1002   - foreach (const SortedDetection &detection, sortedDetections) {
1003   - if (removedTruth.contains(detection.truth_idx) || removedPredicted.contains(detection.predicted_idx))
1004   - continue;
1005   -
1006   - const Detection truth = detections.truth[detection.truth_idx];
1007   - const Detection predicted = detections.predicted[detection.predicted_idx];
1008   -
1009   - if (!truth.ignore) resolved.append(ResolvedDetection(predicted.confidence, detection.overlap));
1010   -
1011   - removedTruth.append(detection.truth_idx);
1012   - removedPredicted.append(detection.predicted_idx);
1013   -
1014   - if (offsets.x() == 0 && detection.overlap > 0.3) {
1015   - count++;
1016   - float width = predicted.boundingBox.width();
1017   - dLeftTotal += (truth.boundingBox.left() - predicted.boundingBox.left()) / width;
1018   - dRightTotal += (truth.boundingBox.right() - predicted.boundingBox.right()) / width;
1019   - dTopTotal += (truth.boundingBox.top() - predicted.boundingBox.top()) / width;
1020   - dBottomTotal += (truth.boundingBox.bottom() - predicted.boundingBox.bottom()) / width;
1021   - }
1022   - }
1023   -
1024   - for (int i = 0; i < detections.predicted.size(); i++)
1025   - if (!removedPredicted.contains(i)) resolved.append(ResolvedDetection(detections.predicted[i].confidence, 0));
1026   - for (int i = 0; i < detections.truth.size(); i++)
1027   - if (!removedTruth.contains(i) && !detections.truth[i].ignore) falseNegative.append(ResolvedDetection(-std::numeric_limits<float>::max(), 0));
1028   - }
1029   -
1030   - if (offsets.x() == 0) {
1031   - // Calculate average differences in each direction
1032   - float dRight = dRightTotal / count;
1033   - float dBottom = dBottomTotal / count;
1034   - float dX = dLeftTotal / count;
1035   - float dY = dTopTotal / count;
1036   - float dWidth = dX - dRight;
1037   - float dHeight = dY - dBottom;
1038   -
1039   - offsets.setX(dX);
1040   - offsets.setY(dY);
1041   - offsets.setWidth(dWidth);
1042   - offsets.setHeight(dHeight);
1043   - }
1044   - return totalTrueDetections;
1045   -}
1046   -
1047 758 float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize, int minSize, int maxSize)
1048 759 {
1049 760 qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery));
... ... @@ -1053,51 +764,13 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
1053 764 // Remove any bounding boxes with a side smaller than minSize
1054 765 if (minSize > 0) {
1055 766 qDebug("Removing boxes smaller than %d\n", minSize);
1056   - QMap<QString, Detections> allFilteredDetections;
1057   - foreach (QString key, allDetections.keys()) {
1058   - Detections detections = allDetections[key];
1059   - Detections filteredDetections;
1060   - for (int i = 0; i < detections.predicted.size(); i++) {
1061   - QRectF box = detections.predicted[i].boundingBox;
1062   - if (min(box.width(), box.height()) > sqrt(0.5 * pow(minSize, 2))) {
1063   - filteredDetections.predicted.append(detections.predicted[i]);
1064   - }
1065   - }
1066   -
1067   - for (int i = 0; i < detections.truth.size(); i++) {
1068   - QRectF box = detections.truth[i].boundingBox;
1069   - if (min(box.width(), box.height()) < minSize)
1070   - detections.truth[i].ignore = true;
1071   - filteredDetections.truth.append(detections.truth[i]);
1072   - }
1073   - if (!filteredDetections.truth.empty()) allFilteredDetections[key] = filteredDetections;
1074   - }
1075   - allDetections = allFilteredDetections;
  767 + allDetections = filterDetections(allDetections,minSize);
1076 768 }
1077 769  
1078 770 // Remove any bounding boxes with no side smaller than maxSize
1079 771 if (maxSize > 0) {
1080 772 qDebug("Removing boxes larger than %d\n", maxSize);
1081   - QMap<QString, Detections> allFilteredDetections;
1082   - foreach (QString key, allDetections.keys()) {
1083   - Detections detections = allDetections[key];
1084   - Detections filteredDetections;
1085   - for (int i = 0; i < detections.predicted.size(); i++) {
1086   - QRectF box = detections.predicted[i].boundingBox;
1087   - if (min(box.width(), box.height()) < sqrt(0.5 * pow(maxSize, 2))) {
1088   - filteredDetections.predicted.append(detections.predicted[i]);
1089   - }
1090   - }
1091   -
1092   - for (int i = 0; i < detections.truth.size(); i++) {
1093   - QRectF box = detections.truth[i].boundingBox;
1094   - if (min(box.width(), box.height()) > maxSize)
1095   - detections.truth[i].ignore = true;
1096   - filteredDetections.truth.append(detections.truth[i]);
1097   - }
1098   - if (!filteredDetections.truth.empty()) allFilteredDetections[key] = filteredDetections;
1099   - }
1100   - allDetections = allFilteredDetections;
  773 + allDetections = filterDetections(allDetections,maxSize,false);
1101 774 }
1102 775  
1103 776 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;
... ... @@ -1117,11 +790,27 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
1117 790 falseNegativeDetections.clear();
1118 791 totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations);
1119 792 }
  793 +
  794 + if (Globals->verbose) {
  795 + qDebug("Total False negatives:");
  796 + const int numFalseNegatives = 50;
  797 + for (int i=0; i<numFalseNegatives; i++) {
  798 + Mat img = imread(qPrintable(Globals->path + "/" + falseNegativeDetections[i].filePath));
  799 + qDebug() << falseNegativeDetections[i];
  800 + const Scalar color(0,255,0);
  801 + rectangle(img, OpenCVUtils::toRect(falseNegativeDetections[i].boundingBox), color, 1);
  802 + QtUtils::touchDir(QDir("./falseNegs"));
  803 + imwrite(qPrintable(QString("./falseNegs/falseNeg%1.jpg").arg(QString::number(i))), img);
  804 + }
  805 + }
  806 +
1120 807 std::sort(resolvedDetections.begin(), resolvedDetections.end());
1121 808 QStringList lines;
1122 809 lines.append("Plot, X, Y");
1123   - lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(allDetections), true));
1124   - lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(allDetections), false));
  810 + QList<DetectionOperatingPoint> points;
  811 + lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(allDetections), true, points));
  812 + points.clear();
  813 + lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(allDetections), false, points));
1125 814  
1126 815 float averageOverlap;
1127 816 { // Overlap Density
... ... @@ -1209,7 +898,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1209 898 {
1210 899 QScopedPointer<Transform> t(Transform::make("Open+Draw(verbose,rects=false,location=false)",NULL));
1211 900  
1212   - QString filePath = "landmarking_examples_truth/"+truth[sampleIndex].file.fileName();
  901 + QString filePath = "landmarking_examples_truth/sample.jpg";
1213 902 projectAndWrite(t.data(), truth[sampleIndex],filePath);
1214 903 lines.append("Sample,"+filePath+","+QString::number(truth[sampleIndex].file.points().size()));
1215 904 }
... ... @@ -1217,26 +906,26 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1217 906 // Get best and worst performing examples
1218 907 QList< QPair<float,int> > exampleIndices = Common::Sort(imageErrors,true);
1219 908  
1220   - QScopedPointer<Transform> t(Transform::make("Open+Draw(rects=false)",NULL));
  909 + QScopedPointer<Transform> t(Transform::make("Open+CropFromLandmarks(paddingHorizontal=.3,paddingVertical=.3,shiftPoints=true)+Resize(128,method=Area)+Draw(rects=false,pointRadius=2)",NULL));
1221 910  
1222 911 for (int i=0; i<totalExamples; i++) {
1223 912 QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName();
1224 913 projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath);
1225   - lines.append("EXT,"+filePath+","+QString::number(exampleIndices[i].first));
  914 + lines.append("EXT,"+filePath+":"+truth[exampleIndices[i].second].file.name+","+QString::number(exampleIndices[i].first));
1226 915  
1227 916 filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName();
1228 917 projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath);
1229   - lines.append("EXP,"+filePath+","+QString::number(exampleIndices[i].first));
  918 + lines.append("EXP,"+filePath+":"+ predicted[exampleIndices[i].second].file.name+","+QString::number(exampleIndices[i].first));
1230 919 }
1231 920  
1232 921 for (int i=exampleIndices.size()-1; i>exampleIndices.size()-totalExamples-1; i--) {
1233 922 QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName();
1234 923 projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath);
1235   - lines.append("EXT,"+filePath+","+QString::number(exampleIndices[i].first));
  924 + lines.append("EXT,"+filePath+":"+truth[exampleIndices[i].second].file.name+","+QString::number(exampleIndices[i].first));
1236 925  
1237 926 filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName();
1238 927 projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath);
1239   - lines.append("EXP,"+filePath+","+QString::number(exampleIndices[i].first));
  928 + lines.append("EXP,"+filePath+":"+predicted[exampleIndices[i].second].file.name+","+QString::number(exampleIndices[i].first));
1240 929 }
1241 930  
1242 931 for (int i=0; i<pointErrors.size(); i++) {
... ...
openbr/core/evalutils.cpp 0 โ†’ 100644
  1 +#include "openbr/core/evalutils.h"
  2 +#include "openbr/core/qtutils.h"
  3 +#include "openbr/core/opencvutils.h"
  4 +
  5 +#include <opencv2/highgui/highgui.hpp>
  6 +
  7 +using namespace std;
  8 +using namespace br;
  9 +using namespace cv;
  10 +
  11 +static const int Max_Points = 500; // Maximum number of points to render on plots
  12 +
  13 +DetectionKey EvalUtils::getDetectKey(const FileList &files)
  14 +{
  15 + if (files.empty())
  16 + return DetectionKey();
  17 +
  18 + const File &f = files.first();
  19 + const QStringList localKeys = f.localKeys();
  20 +
  21 + // first check for single detections
  22 + foreach (const QString &key, localKeys)
  23 + if (!f.get<QRectF>(key, QRectF()).isNull())
  24 + return DetectionKey(key, DetectionKey::Rect);
  25 +
  26 + // and then multiple
  27 + if (!f.rects().empty())
  28 + return DetectionKey("Rects", DetectionKey::RectList);
  29 +
  30 + // check for <Key>_X, <Key>_Y, <Key>_Width, <Key>_Height
  31 + foreach (const QString &localKey, localKeys) {
  32 + if (!localKey.endsWith("_X"))
  33 + continue;
  34 + const QString key = localKey.mid(0, localKey.size()-2);
  35 + if (localKeys.contains(key+"_Y") &&
  36 + localKeys.contains(key+"_Width") &&
  37 + localKeys.contains(key+"_Height"))
  38 + return DetectionKey(key, DetectionKey::XYWidthHeight);
  39 + }
  40 +
  41 + return DetectionKey();
  42 +}
  43 +
  44 +// return a list of detections independent of the detection key format
  45 +QList<Detection> EvalUtils::getDetections(const DetectionKey &key, const File &f, bool isTruth)
  46 +{
  47 + const QString filePath = f.path() + "/" + f.fileName();
  48 + QList<Detection> dets;
  49 + if (key.type == DetectionKey::RectList) {
  50 + QList<QRectF> rects = f.rects();
  51 + QList<float> confidences = f.getList<float>("Confidences", QList<float>());
  52 + if (!isTruth && rects.size() != confidences.size())
  53 + qFatal("You don't have enough confidence. I mean, your detections don't all have confidence measures.");
  54 + for (int i=0; i<rects.size(); i++) {
  55 + if (isTruth)
  56 + dets.append(Detection(rects[i], filePath));
  57 + else
  58 + dets.append(Detection(rects[i], filePath, confidences[i]));
  59 + }
  60 + } else if (key.type == DetectionKey::Rect) {
  61 + dets.append(Detection(f.get<QRectF>(key), filePath, isTruth ? -1 : f.get<float>("Confidence", -1)));
  62 + } else if (key.type == DetectionKey::XYWidthHeight) {
  63 + const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height"));
  64 + dets.append(Detection(rect, filePath, isTruth ? -1 : f.get<float>("Confidence", -1), f.get<bool>("Ignore", false)));
  65 + }
  66 + return dets;
  67 +}
  68 +
  69 +QMap<QString, Detections> EvalUtils::getDetections(const File &predictedGallery, const File &truthGallery)
  70 +{
  71 + const FileList predicted = TemplateList::fromGallery(predictedGallery).files();
  72 + const FileList truth = TemplateList::fromGallery(truthGallery).files();
  73 +
  74 + // Figure out which metadata field contains a bounding box
  75 + DetectionKey truthDetectKey = getDetectKey(truth);
  76 + if (truthDetectKey.isEmpty())
  77 + qFatal("No suitable ground truth metadata key found.");
  78 +
  79 + DetectionKey predictedDetectKey = getDetectKey(predicted);
  80 + if (predictedDetectKey.isEmpty())
  81 + qFatal("No suitable predicted metadata key found.");
  82 +
  83 + qDebug("Using metadata key: %s%s",
  84 + qPrintable(predictedDetectKey),
  85 + qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey));
  86 +
  87 + QMap<QString, Detections> allDetections;
  88 + foreach (const File &f, truth)
  89 + allDetections[f.name].truth.append(getDetections(truthDetectKey, f, true));
  90 + foreach (const File &f, predicted)
  91 + if (allDetections.contains(f.name)) allDetections[f.name].predicted.append(getDetections(predictedDetectKey, f, false));
  92 + return allDetections;
  93 +}
  94 +
  95 +QMap<QString, Detections> EvalUtils::filterDetections(const QMap<QString, Detections> &allDetections, int threshold, bool useMin)
  96 +{
  97 + QMap<QString, Detections> allFilteredDetections;
  98 + foreach (QString key, allDetections.keys()) {
  99 + Detections detections = allDetections[key];
  100 + Detections filteredDetections;
  101 + for (int i = 0; i < detections.predicted.size(); i++) {
  102 + const QRectF box = detections.predicted[i].boundingBox;
  103 + const qreal minBoxDim = min(box.width(), box.height());
  104 + const double t = sqrt(0.5 * pow(threshold, 2));
  105 + if (useMin ? minBoxDim > t : minBoxDim < t)
  106 + filteredDetections.predicted.append(detections.predicted[i]);
  107 + }
  108 +
  109 + for (int i = 0; i < detections.truth.size(); i++) {
  110 + const QRectF box = detections.truth[i].boundingBox;
  111 + const qreal minBoxDim = min(box.width(), box.height());
  112 + if (useMin ? minBoxDim < threshold : minBoxDim > threshold)
  113 + detections.truth[i].ignore = true;
  114 + filteredDetections.truth.append(detections.truth[i]);
  115 + }
  116 + if (!filteredDetections.truth.empty()) allFilteredDetections[key] = filteredDetections;
  117 + }
  118 + return allFilteredDetections;
  119 +}
  120 +
  121 +int EvalUtils::associateGroundTruthDetections(QList<ResolvedDetection> &resolved, QList<ResolvedDetection> &falseNegative, QMap<QString, Detections> &all, QRectF &offsets)
  122 +{
  123 + float dLeftTotal = 0.0, dRightTotal = 0.0, dTopTotal = 0.0, dBottomTotal = 0.0;
  124 + int count = 0, totalTrueDetections = 0;
  125 +
  126 + foreach (Detections detections, all.values()) {
  127 + for (int i=0; i<detections.truth.size(); i++)
  128 + if (!detections.truth[i].ignore) totalTrueDetections++;
  129 +
  130 + // Try to associate ground truth detections with predicted detections
  131 + QList<SortedDetection> sortedDetections; sortedDetections.reserve(detections.truth.size() * detections.predicted.size());
  132 + for (int t = 0; t < detections.truth.size(); t++) {
  133 + const Detection truth = detections.truth[t];
  134 + for (int p = 0; p < detections.predicted.size(); p++) {
  135 + Detection predicted = detections.predicted[p];
  136 +
  137 + float predictedWidth = predicted.boundingBox.width();
  138 + float x, y, width, height;
  139 + x = predicted.boundingBox.x() + offsets.x()*predictedWidth;
  140 + y = predicted.boundingBox.y() + offsets.y()*predictedWidth;
  141 + width = predicted.boundingBox.width() - offsets.width()*predictedWidth;
  142 + height = predicted.boundingBox.height() - offsets.height()*predictedWidth;
  143 + Detection newPredicted(QRectF(x, y, width, height), predicted.filePath, 0.0);
  144 +
  145 + const float overlap = truth.overlap(newPredicted);
  146 +
  147 + if (overlap > 0)
  148 + sortedDetections.append(SortedDetection(t, p, overlap));
  149 + }
  150 + }
  151 +
  152 + std::sort(sortedDetections.begin(), sortedDetections.end());
  153 +
  154 + QList<int> removedTruth;
  155 + QList<int> removedPredicted;
  156 +
  157 + foreach (const SortedDetection &detection, sortedDetections) {
  158 + if (removedTruth.contains(detection.truth_idx) || removedPredicted.contains(detection.predicted_idx))
  159 + continue;
  160 +
  161 + const Detection truth = detections.truth[detection.truth_idx];
  162 + const Detection predicted = detections.predicted[detection.predicted_idx];
  163 +
  164 + if (!truth.ignore)
  165 + resolved.append(ResolvedDetection(predicted.filePath, predicted.boundingBox, predicted.confidence, detection.overlap));
  166 +
  167 + removedTruth.append(detection.truth_idx);
  168 + removedPredicted.append(detection.predicted_idx);
  169 +
  170 + if (offsets.x() == 0 && detection.overlap > 0.3) {
  171 + count++;
  172 + float width = predicted.boundingBox.width();
  173 + dLeftTotal += (truth.boundingBox.left() - predicted.boundingBox.left()) / width;
  174 + dRightTotal += (truth.boundingBox.right() - predicted.boundingBox.right()) / width;
  175 + dTopTotal += (truth.boundingBox.top() - predicted.boundingBox.top()) / width;
  176 + dBottomTotal += (truth.boundingBox.bottom() - predicted.boundingBox.bottom()) / width;
  177 + }
  178 + }
  179 +
  180 + for (int i = 0; i < detections.predicted.size(); i++)
  181 + if (!removedPredicted.contains(i)) resolved.append(ResolvedDetection(detections.predicted[i].filePath, detections.predicted[i].boundingBox, detections.predicted[i].confidence, 0));
  182 + for (int i = 0; i < detections.truth.size(); i++)
  183 + if (!removedTruth.contains(i) && !detections.truth[i].ignore) falseNegative.append(ResolvedDetection(detections.truth[i].filePath, detections.truth[i].boundingBox, -std::numeric_limits<float>::max(), 0));
  184 + }
  185 +
  186 + if (offsets.x() == 0) {
  187 + // Calculate average differences in each direction
  188 + float dRight = dRightTotal / count;
  189 + float dBottom = dBottomTotal / count;
  190 + float dX = dLeftTotal / count;
  191 + float dY = dTopTotal / count;
  192 + float dWidth = dX - dRight;
  193 + float dHeight = dY - dBottom;
  194 +
  195 + offsets.setX(dX);
  196 + offsets.setY(dY);
  197 + offsets.setWidth(dWidth);
  198 + offsets.setHeight(dHeight);
  199 + }
  200 + return totalTrueDetections;
  201 +}
  202 +
  203 +QStringList EvalUtils::computeDetectionResults(const QList<ResolvedDetection> &detections, int totalTrueDetections, int numImages, bool discrete, QList<DetectionOperatingPoint> &points)
  204 +{
  205 + float TP = 0, FP = 0, prevFP = -1, prevTP = -1;
  206 +
  207 + const int detectionsToKeep = 50;
  208 + QList<ResolvedDetection> topFalsePositives, bottomTruePositives;
  209 + for (int i=0; i<detections.size(); i++) {
  210 + const ResolvedDetection &detection = detections[i];
  211 + if (discrete) {
  212 + // A 50% overlap is considered a true positive
  213 + if (detection.overlap >= 0.5) TP++;
  214 + else FP++;
  215 + } else {
  216 + TP += detection.overlap;
  217 + FP += 1 - detection.overlap;
  218 + }
  219 + if ((i == detections.size()-1) || (detection.confidence > detections[i+1].confidence)) {
  220 + if (FP > prevFP || (i == detections.size()-1)) {
  221 + if (prevFP / numImages < 1 && FP / numImages >= 1 && discrete) {
  222 + qDebug("TAR @ FAR => %f : 1", TP / totalTrueDetections);
  223 + qDebug("Confidence: %f", detection.confidence);
  224 + qDebug("TP vs. FP: %f to %f", TP, FP);
  225 + } else if (prevFP / numImages < 0.5 && FP / numImages >= 0.5 && discrete) {
  226 + qDebug("TAR @ FAR => %f : 0.5", TP / totalTrueDetections);
  227 + qDebug("Confidence: %f", detection.confidence);
  228 + qDebug("TP vs. FP: %f to %f", TP, FP);
  229 + } else if (prevFP / numImages < 0.2 && FP / numImages >= 0.2 && discrete) {
  230 + qDebug("TAR @ FAR => %f : 0.2", TP / totalTrueDetections);
  231 + qDebug("Confidence: %f", detection.confidence);
  232 + qDebug("TP vs. FP: %f to %f", TP, FP);
  233 + } else if (prevFP / numImages < 0.1 && FP / numImages >= 0.1 && discrete) {
  234 + qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections);
  235 + qDebug("Confidence: %f", detection.confidence);
  236 + qDebug("TP vs. FP: %f to %f", TP, FP);
  237 + } else if (prevFP / numImages < 0.02 && FP / numImages >= 0.02 && discrete) {
  238 + qDebug("TAR @ FAR => %f : 0.02", TP / totalTrueDetections);
  239 + qDebug("Confidence: %f", detection.confidence);
  240 + qDebug("TP vs. FP: %f to %f", TP, FP);
  241 + } else if (prevFP / numImages < 0.01 && FP / numImages >= 0.01 && discrete) {
  242 + qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections);
  243 + qDebug("Confidence: %f", detection.confidence);
  244 + qDebug("TP vs. FP: %f to %f", TP, FP);
  245 + } else if (prevFP / numImages < 0.001 && FP / numImages >= 0.001 && discrete) {
  246 + qDebug("TAR @ FAR => %f : 0.001", TP / totalTrueDetections);
  247 + qDebug("Confidence: %f", detection.confidence);
  248 + qDebug("TP vs. FP: %f to %f", TP, FP);
  249 + }
  250 +
  251 + if (detection.overlap < 0.5 && topFalsePositives.size() < detectionsToKeep)
  252 + topFalsePositives.append(detection);
  253 +
  254 + points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages, detection.confidence));
  255 + prevFP = FP;
  256 + }
  257 +
  258 + if (TP > prevTP) {
  259 + bottomTruePositives.append(detection);
  260 + if (bottomTruePositives.size() > detectionsToKeep)
  261 + bottomTruePositives.removeFirst();
  262 + prevTP = TP;
  263 + }
  264 + }
  265 + }
  266 +
  267 + if (discrete) {
  268 + qDebug("Total TP vs. FP: %f to %f", TP, FP);
  269 + qDebug("Overall Recall (TP vs. possible TP): %f (%f vs. %d)", TP / totalTrueDetections, TP, totalTrueDetections);
  270 +
  271 + if (Globals->verbose) {
  272 + QtUtils::touchDir(QDir("./falsePos"));
  273 + qDebug("Highest Scoring False Positives:");
  274 + for (int i=0; i<detectionsToKeep; i++) {
  275 + Mat img = imread(qPrintable(Globals->path + "/" + topFalsePositives[i].filePath));
  276 + qDebug() << topFalsePositives[i];
  277 + const Scalar color(0,255,0);
  278 + rectangle(img, OpenCVUtils::toRect(topFalsePositives[i].boundingBox), color, 1);
  279 + imwrite(qPrintable(QString("./falsePos/falsePos%1.jpg").arg(QString::number(i))), img);
  280 + }
  281 + qDebug("Lowest Scoring True Positives:");
  282 + qDebug() << bottomTruePositives;
  283 + }
  284 + }
  285 +
  286 + const int keep = qMin(points.size(), Max_Points);
  287 + if (keep < 1) qFatal("Insufficient points.");
  288 +
  289 + QStringList lines; lines.reserve(keep);
  290 + if (keep == 1) {
  291 + const DetectionOperatingPoint &point = points[0];
  292 + lines.append(QString("%1ROC, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.FalsePositiveRate), QString::number(point.Recall)));
  293 + lines.append(QString("%1PR, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.Recall), QString::number(point.Precision)));
  294 + } else {
  295 + for (int i=0; i<keep; i++) {
  296 + const DetectionOperatingPoint &point = points[double(i) / double(keep-1) * double(points.size()-1)];
  297 + lines.append(QString("%1ROC, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.FalsePositiveRate), QString::number(point.Recall)));
  298 + lines.append(QString("%1PR, %2, %3").arg(discrete ? "Discrete" : "Continuous", QString::number(point.Recall), QString::number(point.Precision)));
  299 + }
  300 + }
  301 + return lines;
  302 +}
  303 +
  304 +QDebug operator<<(QDebug dbg, const ResolvedDetection &d)
  305 +{
  306 + return dbg.nospace() << "(FilePath: " << d.filePath << " Bounding Box: " << d.boundingBox << ", Overlap: " << d.overlap << ", Confidence: " << d.confidence << ")";
  307 +}
  308 +
... ...
openbr/core/evalutils.h 0 โ†’ 100644
  1 +#ifndef EVALUTILS_EVALUTILS_H
  2 +#define EVALUTILS_EVALUTILS_H
  3 +
  4 +#include <openbr/openbr_plugin.h>
  5 +
  6 +struct Detection
  7 +{
  8 + QRectF boundingBox;
  9 + QString filePath;
  10 + float confidence;
  11 + // The ignore flag is useful when certain faces in an image should be ignored
  12 + // and should not effect detection performance. Predicted detections that overlap
  13 + // with an ignored truth detection will not count as a true positive, false positive,
  14 + // true negative, or false negative, it will simply be ignored.
  15 + bool ignore;
  16 +
  17 + Detection() {}
  18 + Detection(const QRectF &boundingBox_, const QString &filePath = QString(), float confidence_ = -1, bool ignore_ = false)
  19 + : boundingBox(boundingBox_), filePath(filePath), confidence(confidence_), ignore(ignore_) {}
  20 +
  21 + float area() const { return boundingBox.width() * boundingBox.height(); }
  22 + float overlap(const Detection &other) const
  23 + {
  24 + const Detection intersection(boundingBox.intersected(other.boundingBox));
  25 + return intersection.area() / (area() + other.area() - intersection.area());
  26 + }
  27 +};
  28 +
  29 +struct SortedDetection
  30 +{
  31 + int truth_idx, predicted_idx;
  32 + float overlap;
  33 + SortedDetection() : truth_idx(-1), predicted_idx(-1), overlap(-1) {}
  34 + SortedDetection(int truth_idx_, int predicted_idx_, float overlap_)
  35 + : truth_idx(truth_idx_), predicted_idx(predicted_idx_), overlap(overlap_) {}
  36 + inline bool operator<(const SortedDetection &other) const { return overlap > other.overlap; }
  37 +};
  38 +
  39 +struct ResolvedDetection
  40 +{
  41 + QString filePath;
  42 + QRectF boundingBox;
  43 + float confidence, overlap;
  44 + ResolvedDetection() : confidence(-1), overlap(-1) {}
  45 + ResolvedDetection(const QString &filePath, const QRectF &boundingBox, float confidence_, float overlap_) :
  46 + filePath(filePath), boundingBox(boundingBox), confidence(confidence_), overlap(overlap_) {}
  47 + inline bool operator<(const ResolvedDetection &other) const { return confidence > other.confidence; }
  48 +};
  49 +
  50 +struct Detections
  51 +{
  52 + QList<Detection> predicted, truth;
  53 +};
  54 +
  55 +struct DetectionKey : public QString
  56 +{
  57 + enum Type {
  58 + Invalid,
  59 + Rect,
  60 + RectList,
  61 + XYWidthHeight
  62 + } type;
  63 +
  64 + DetectionKey(const QString &key = "", Type type = Invalid)
  65 + : QString(key), type(type) {}
  66 +};
  67 +
  68 +struct DetectionOperatingPoint
  69 +{
  70 + float Recall, FalsePositiveRate, Precision, Confidence;
  71 + DetectionOperatingPoint() : Recall(-1), FalsePositiveRate(-1), Precision(-1) {}
  72 + DetectionOperatingPoint(float TP, float FP, float totalPositives, float numImages, float confidence)
  73 + : Recall(TP/totalPositives), FalsePositiveRate(FP/numImages), Precision(TP/(TP+FP)), Confidence(confidence) {}
  74 +};
  75 +
  76 +namespace EvalUtils
  77 +{
  78 + // Detection
  79 + DetectionKey getDetectKey(const br::FileList &files);
  80 + QList<Detection> getDetections(const DetectionKey &key, const br::File &f, bool isTruth);
  81 + QMap<QString, Detections> getDetections(const br::File &predictedGallery, const br::File &truthGallery);
  82 + QMap<QString, Detections> filterDetections(const QMap<QString, Detections> &allDetections, int threshold, bool useMin=true);
  83 + int associateGroundTruthDetections(QList<ResolvedDetection> &resolved, QList<ResolvedDetection> &falseNegative, QMap<QString, Detections> &all, QRectF &offsets);
  84 + QStringList computeDetectionResults(const QList<ResolvedDetection> &detections, int totalTrueDetections, int numImages, bool discrete, QList<DetectionOperatingPoint> &points);
  85 + inline int getNumberOfImages(const QMap<QString, Detections> detections)
  86 + {
  87 + return detections.keys().size();
  88 + }
  89 +}
  90 +
  91 +QDebug operator<<(QDebug dbg, const ResolvedDetection &d);
  92 +
  93 +#endif // EVALUTILS_EVALUTILS_H
... ...
openbr/core/opencvutils.cpp
... ... @@ -436,7 +436,7 @@ public:
436 436 };
437 437  
438 438 // TODO: Make sure case where no confidences are inputted works.
439   -void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon)
  439 +void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon, bool useMax, QList<int> *maxIndices)
440 440 {
441 441 if (rects.isEmpty())
442 442 return;
... ... @@ -448,8 +448,9 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
448 448 vector<Rect> rrects(nClasses);
449 449  
450 450 // Total number of rects in each class
451   - vector<int> neighbors(nClasses, 0);
452   - vector<float> classConfidence(nClasses, 0);
  451 + vector<int> neighbors(nClasses, -1);
  452 + vector<float> classConfidence(nClasses, useMax ? -std::numeric_limits<float>::max() : 0);
  453 + vector<int> classMax(nClasses, 0);
453 454  
454 455 for (size_t i = 0; i < labels.size(); i++)
455 456 {
... ... @@ -459,18 +460,26 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
459 460 rrects[cls].width += rects[i].width;
460 461 rrects[cls].height += rects[i].height;
461 462 neighbors[cls]++;
462   - classConfidence[cls] += confidences[i];
  463 + if (useMax) {
  464 + if (confidences[i] > classConfidence[cls]) {
  465 + classConfidence[cls] = confidences[i];
  466 + classMax[cls] = i;
  467 + }
  468 + } else
  469 + classConfidence[cls] += confidences[i];
463 470 }
464 471  
465 472 // Find average rectangle for all classes
466 473 for (int i = 0; i < nClasses; i++)
467 474 {
468   - Rect r = rrects[i];
469   - float s = 1.f/neighbors[i];
470   - rrects[i] = Rect(saturate_cast<int>(r.x*s),
471   - saturate_cast<int>(r.y*s),
472   - saturate_cast<int>(r.width*s),
473   - saturate_cast<int>(r.height*s));
  475 + if (neighbors[i] > 0) {
  476 + Rect r = rrects[i];
  477 + float s = 1.f/(neighbors[i]+1);
  478 + rrects[i] = Rect(saturate_cast<int>(r.x*s),
  479 + saturate_cast<int>(r.y*s),
  480 + saturate_cast<int>(r.width*s),
  481 + saturate_cast<int>(r.height*s));
  482 + }
474 483 }
475 484  
476 485 rects.clear();
... ... @@ -480,7 +489,7 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
480 489 for (int i = 0; i < nClasses; i++)
481 490 {
482 491 // Average rectangle
483   - Rect r1 = rrects[i];
  492 + const Rect r1 = rrects[i];
484 493  
485 494 // Used to eliminate rectangles with few neighbors in the case of no weights
486 495 const float w1 = classConfidence[i];
... ... @@ -501,21 +510,19 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
501 510 if (j == i || n2 < minNeighbors)
502 511 continue;
503 512  
504   - Rect r2 = rrects[j];
  513 + const Rect r2 = rrects[j];
505 514  
506   - int dx = saturate_cast<int>(r2.width * epsilon);
507   - int dy = saturate_cast<int>(r2.height * epsilon);
  515 + const int dx = saturate_cast<int>(r2.width * epsilon);
  516 + const int dy = saturate_cast<int>(r2.height * epsilon);
508 517  
509   - float w2 = classConfidence[j];
  518 + const float w2 = classConfidence[j];
510 519  
511   - // If, r1 is within the r2 AND
512   - // r2 has a higher confidence than r1
513   - // then, eliminate the r1
514 520 if(r1.x >= r2.x - dx &&
515 521 r1.y >= r2.y - dy &&
516 522 r1.x + r1.width <= r2.x + r2.width + dx &&
517 523 r1.y + r1.height <= r2.y + r2.height + dy &&
518   - (w2 > std::max(confidenceThreshold, w1)))
  524 + (w2 > w1) &&
  525 + (n2 > n1))
519 526 break;
520 527 }
521 528  
... ... @@ -523,11 +530,49 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
523 530 {
524 531 rects.append(r1);
525 532 confidences.append(w1);
  533 + if (maxIndices)
  534 + maxIndices->append(classMax[i]);
526 535 }
527 536 }
528 537 }
529 538  
530   -void OpenCVUtils::rotate(const br::Template &src, br::Template &dst, int degrees, bool rotateMat, bool rotatePoints, bool rotateRects)
  539 +void OpenCVUtils::pad(const br::Template &src, br::Template &dst, bool padMat, const QList<int> &padding, bool padPoints, bool padRects, int border, int value)
  540 +{
  541 + // Padding is expected to be top, bottom, left, right
  542 + if (padMat)
  543 + copyMakeBorder(src, dst, padding[0], padding[1], padding[2], padding[3], border, Scalar(value));
  544 + else
  545 + dst = src;
  546 +
  547 + if (padPoints) {
  548 + QList<QPointF> points = src.file.points();
  549 + QList<QPointF> paddedPoints;
  550 + for (int i=0; i<points.size(); i++)
  551 + paddedPoints.append(points[i] += QPointF(padding[2],padding[0]));
  552 + dst.file.setPoints(paddedPoints);
  553 + }
  554 +
  555 + if (padRects) {
  556 + QList<QRectF> rects = src.file.rects();
  557 + QList<QRectF> paddedRects;
  558 + for (int i=0; i<rects.size(); i++)
  559 + paddedRects.append(rects[i].translated(QPointF(padding[2],padding[0])));
  560 + dst.file.setRects(paddedRects);
  561 + }
  562 +
  563 +
  564 +}
  565 +
  566 +void OpenCVUtils::pad(const br::TemplateList &src, br::TemplateList &dst, bool padMat, const QList<int> &padding, bool padPoints, bool padRects, int border, int value)
  567 +{
  568 + for (int i=0; i<src.size(); i++) {
  569 + br::Template t;
  570 + pad(src[i], t, padMat, padding, padPoints, padRects, border, value);
  571 + dst.append(t);
  572 + }
  573 +}
  574 +
  575 +void OpenCVUtils::rotate(const br::Template &src, br::Template &dst, float degrees, bool rotateMat, bool rotatePoints, bool rotateRects)
531 576 {
532 577 Mat rotMatrix = getRotationMatrix2D(Point2f(src.m().rows/2,src.m().cols/2),degrees,1.0);
533 578 if (rotateMat) {
... ... @@ -579,7 +624,7 @@ void OpenCVUtils::rotate(const br::Template &amp;src, br::Template &amp;dst, int degrees
579 624 }
580 625 }
581 626  
582   -void OpenCVUtils::rotate(const br::TemplateList &src, br::TemplateList &dst, int degrees, bool rotateMat, bool rotatePoints, bool rotateRects)
  627 +void OpenCVUtils::rotate(const br::TemplateList &src, br::TemplateList &dst, float degrees, bool rotateMat, bool rotatePoints, bool rotateRects)
583 628 {
584 629 for (int i=0; i<src.size(); i++) {
585 630 br::Template t;
... ...
openbr/core/opencvutils.h
... ... @@ -102,9 +102,11 @@ namespace OpenCVUtils
102 102 float overlap(const QRectF &rect1, const QRectF &rect2);
103 103  
104 104 // Misc
105   - void group(QList<cv::Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon);
106   - void rotate(const br::Template &src, br::Template &dst, int degrees, bool rotateMat=true, bool rotatePoints=true, bool rotateRects=true);
107   - void rotate(const br::TemplateList &src, br::TemplateList &dst, int degrees, bool rotateMat=true, bool rotatePoint=true, bool rotateRects=true);
  105 + void group(QList<cv::Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon, bool useMax=false, QList<int> *maxIndices=NULL);
  106 + void pad(const br::Template &src, br::Template &dst, bool padMat, const QList<int> &padding, bool padPoints, bool padRects, int border=0, int value=0);
  107 + void pad(const br::TemplateList &src, br::TemplateList &dst, bool padMat, const QList<int> &padding, bool padPoints, bool padRects, int border=0, int value=0);
  108 + void rotate(const br::Template &src, br::Template &dst, float degrees, bool rotateMat=true, bool rotatePoints=true, bool rotateRects=true);
  109 + void rotate(const br::TemplateList &src, br::TemplateList &dst, float degrees, bool rotateMat=true, bool rotatePoint=true, bool rotateRects=true);
108 110 void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true);
109 111 void flip(const br::TemplateList &src, br::TemplateList &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true);
110 112  
... ...
openbr/core/plot.cpp
... ... @@ -318,9 +318,9 @@ bool PlotLandmarking(const QStringList &amp;files, const File &amp;destination, bool sho
318 318 qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination));
319 319 RPlot p(files, destination);
320 320 p.file.write("\nformatData(type=\"landmarking\")\n\n");
321   - p.file.write(qPrintable(QString("algs <- uniqueBox$%1)\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header))));
  321 + p.file.write(qPrintable(QString("algs <- unique(Box$%1)\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header))));
322 322 p.file.write("algs <- algs[!duplicated(algs)]\n");
323   - p.file.write("plotLandmarkSamples(samples=sample, expData=EXP, extData=EXT)\n");
  323 + p.file.write("plotLandmarkSamples(displaySample=displaySample, expData=EXP, extData=EXT)\n");
324 324 p.file.write("plotLandmarkTables(tableData=Box)\n");
325 325  
326 326 p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(),
... ... @@ -330,9 +330,6 @@ bool PlotLandmarking(const QStringList &amp;files, const File &amp;destination, bool sho
330 330 p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
331 331 QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + theme_minimal()\n\n")));
332 332  
333   - p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
334   - QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n")));
335   -
336 333 return p.finalize(show);
337 334 }
338 335  
... ...
openbr/openbr_plugin.cpp
... ... @@ -1210,7 +1210,8 @@ bool br::Context::checkSDKPath(const QString &amp;sdkPath)
1210 1210 return QFileInfo(sdkPath + "/share/openbr/openbr.bib").exists();
1211 1211 }
1212 1212  
1213   -// We create our own when the user hasn't
  1213 +// We create our own when the user hasn't.
  1214 +// Since we can't ensure that it gets deleted last, we never delete it.
1214 1215 static QCoreApplication *application = NULL;
1215 1216  
1216 1217 void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool useGui)
... ... @@ -1230,7 +1231,6 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
1230 1231 // We take in argc as a reference due to:
1231 1232 // https://bugreports.qt-project.org/browse/QTBUG-5637
1232 1233 // QApplication should be initialized before anything else.
1233   - // Since we can't ensure that it gets deleted last, we never delete it.
1234 1234 if (QCoreApplication::instance() == NULL) {
1235 1235 #ifndef BR_EMBEDDED
1236 1236 if (useGui) application = new QApplication(argc, argv);
... ... @@ -1245,6 +1245,7 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
1245 1245 if (sdkPath.isEmpty()) {
1246 1246 QStringList checkPaths; checkPaths << QCoreApplication::applicationDirPath() << QDir::currentPath();
1247 1247 checkPaths << QString(getenv("PATH")).split(sep, QString::SkipEmptyParts);
  1248 + QSet<QString> checkedPaths; // Avoid infinite loops from symlinks
1248 1249  
1249 1250 bool foundSDK = false;
1250 1251 foreach (const QString &path, checkPaths) {
... ... @@ -1252,6 +1253,8 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
1252 1253 QDir dir(path);
1253 1254 do {
1254 1255 sdkPath = dir.absolutePath();
  1256 + if (checkedPaths.contains(sdkPath)) break;
  1257 + else checkedPaths.insert(sdkPath);
1255 1258 foundSDK = checkSDKPath(sdkPath);
1256 1259 dir.cdUp();
1257 1260 } while (!foundSDK && !dir.isRoot());
... ... @@ -1316,9 +1319,6 @@ void br::Context::finalize()
1316 1319  
1317 1320 delete Globals;
1318 1321 Globals = NULL;
1319   -
1320   - delete application;
1321   - application = NULL;
1322 1322 }
1323 1323  
1324 1324 QString br::Context::about()
... ... @@ -1602,7 +1602,8 @@ Transform *Transform::make(QString str, QObject *parent)
1602 1602 // Base name not found? Try constructing it via LoadStore
1603 1603 if (!Factory<Transform>::names().contains(parsed.suffix())
1604 1604 && (QFileInfo(parsed.suffix()).exists()
1605   - || QFileInfo(Globals->sdkPath + "/share/openbr/models/transforms/"+parsed.suffix()).exists())) {
  1605 + || QFileInfo(Globals->sdkPath + "/share/openbr/models/transforms/"+parsed.suffix()).exists()
  1606 + || QFileInfo(Globals->sdkPath + "/../share/openbr/models/transforms/"+parsed.suffix()).exists())) {
1606 1607 Transform *tform = make("<"+parsed.suffix()+">", parent);
1607 1608 applyAdditionalProperties(parsed, tform);
1608 1609 return tform;
... ...
openbr/plugins/classification/cascade.cpp renamed to openbr/plugins/classification/cascade_classifier.cpp
... ... @@ -323,4 +323,4 @@ BR_REGISTER(Classifier, CascadeClassifier)
323 323  
324 324 } // namespace br
325 325  
326   -#include "classification/cascade.moc"
  326 +#include "classification/cascade_classifier.moc"
... ...
openbr/plugins/classification/dlib.cpp
1 1 #include <opencv2/imgproc/imgproc.hpp>
2 2 #include <dlib/image_processing/frontal_face_detector.h>
  3 +#include <dlib/image_processing.h>
3 4 #include <dlib/opencv.h>
4 5  
5 6 #include "openbr/plugins/openbr_internal.h"
... ... @@ -34,29 +35,29 @@ class DLandmarkerTransform : public UntrainableTransform
34 35 Q_OBJECT
35 36  
36 37 private:
37   -
38 38 Resource<shape_predictor> shapeResource;
39 39  
40 40 void init()
41 41 {
42 42 shapeResource.setResourceMaker(new DLibShapeResourceMaker());
  43 + shapeResource.release(shapeResource.acquire()); // Pre-load one instance of the model
43 44 }
44 45  
45 46 QPointF averagePoints(const QList<QPointF> &points, int rangeBegin, int rangeEnd) const
46 47 {
47   - QPointF point;
  48 + QPointF point;
48 49 for (int i=rangeBegin; i<rangeEnd; i++)
49 50 point += points[i];
50 51 point /= (rangeEnd-rangeBegin);
51   - return point;
  52 + return point;
52 53 }
53 54  
54 55 void setFacePoints(Template &dst) const
55 56 {
56 57 const QList<QPointF> points = dst.file.points();
57   - dst.file.set("LeftEye",averagePoints(points,36,42));
58   - dst.file.set("RightEye",averagePoints(points,42,48));
59   - dst.file.set("Chin",points[8]);
  58 + dst.file.set("RightEye", averagePoints(points, 36, 42));
  59 + dst.file.set("LeftEye" , averagePoints(points, 42, 48));
  60 + dst.file.set("Chin", points[8]);
60 61 }
61 62  
62 63 void project(const Template &src, Template &dst) const
... ... @@ -73,25 +74,21 @@ private:
73 74 array2d<unsigned char> image;
74 75 assign_image(image,cimg);
75 76  
76   - if (src.file.rects().isEmpty()) { //If the image has no rects assume the whole image is a face
77   - rectangle r(0, 0, cvImage.cols, cvImage.rows);
78   - full_object_detection shape = (*sp)(image, r);
79   - for (size_t i = 0; i < shape.num_parts(); i++)
80   - dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1)));
81   - setFacePoints(dst);
82   - }
83   - else { // Crop the image on the rects
84   - for (int j=0; j<src.file.rects().size(); ++j)
85   - {
86   - QRectF rect = src.file.rects()[j];
87   - rectangle r(rect.left(),rect.top(),rect.right(),rect.bottom());
88   - full_object_detection shape = (*sp)(image, r);
89   - for (size_t i=0; i<shape.num_parts(); i++)
90   - dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1)));
91   - setFacePoints(dst);
92   - }
  77 + rectangle r;
  78 + if (src.file.rects().isEmpty()) { // If the image has no rects assume the whole image is a face
  79 + r = rectangle(0, 0, cvImage.cols, cvImage.rows);
  80 + } else { // Crop the image on the first rect
  81 + const QRectF rect = src.file.rects().first();
  82 + r = rectangle(rect.left(), rect.top(), rect.right(), rect.bottom());
93 83 }
94 84  
  85 + full_object_detection shape = (*sp)(image, r);
  86 + QList<QPointF> points;
  87 + for (size_t i=0; i<shape.num_parts(); i++)
  88 + points.append(QPointF(shape.part(i)(0), shape.part(i)(1)));
  89 + dst.file.setPoints(points);
  90 + setFacePoints(dst);
  91 +
95 92 shapeResource.release(sp);
96 93 }
97 94 };
... ...
openbr/plugins/cmake/opencv.cmake
... ... @@ -35,6 +35,16 @@ else()
35 35 plugins/imgproc/sift.cpp)
36 36 endif()
37 37  
  38 +option(BR_WITH_OPENCV_OBJDETECT "Build with OpenCV objdetect plugins." ON)
  39 +if(${BR_WITH_OPENCV_OBJDETECT})
  40 + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} opencv_objdetect)
  41 + set(OPENCV_DEPENDENCIES ${OPENCV_DEPENDENCIES} opencv_objdetect)
  42 +else()
  43 + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/hog.cpp
  44 + plugins/metadata/cascade.cpp
  45 + plugins/metadata/hogpersondetector.cpp)
  46 +endif()
  47 +
38 48 option(BR_WITH_OPENCV_PHOTO "Build with OpenCV photo plugins." ON)
39 49 if(${BR_WITH_OPENCV_PHOTO})
40 50 set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} opencv_photo)
... ...
openbr/plugins/cmake/stasm4.cmake
... ... @@ -6,5 +6,4 @@ if(${BR_WITH_STASM4})
6 6 install(DIRECTORY ${Stasm_DIR}/data/ DESTINATION share/openbr/models/stasm)
7 7 else()
8 8 set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/stasm4.cpp)
9   - set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/revertaffine.cpp)
10 9 endif()
... ...
openbr/plugins/core/loadstore.cpp
... ... @@ -66,10 +66,12 @@ private:
66 66 if (transform != NULL) return;
67 67 if (fileName.isEmpty()) fileName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString);
68 68  
69   - if (!tryLoad())
  69 + if (!tryLoad()) {
70 70 transform = make(transformString);
71   - else
  71 + trainable = transform->trainable;
  72 + } else {
72 73 trainable = false;
  74 + }
73 75 }
74 76  
75 77 bool timeVarying() const
... ...
openbr/plugins/format/video.cpp renamed to openbr/plugins/format/videoFormats.cpp
... ... @@ -160,4 +160,4 @@ BR_REGISTER(Format, DefaultFormat)
160 160  
161 161 } // namespace br
162 162  
163   -#include "format/video.moc"
  163 +#include "format/videoFormats.moc"
... ...
openbr/plugins/gallery/lmdb.cpp
... ... @@ -112,7 +112,19 @@ class lmdbGallery : public Gallery
112 112 foreach(const Template &t, working) {
113 113 // add current image to transaction
114 114 caffe::Datum datum;
115   - caffe::CVMatToDatum(t.m(), &datum);
  115 +
  116 + const cv::Mat &m = t.m();
  117 + if (m.depth() == CV_32F) {
  118 + datum.set_channels(m.channels());
  119 + datum.set_height(m.rows);
  120 + datum.set_width(m.cols);
  121 + for (int i=0; i<m.channels(); i++) // Follow Caffe's channel-major ordering convention
  122 + for (int j=0; j<m.rows; j++)
  123 + for (int k=0; k<m.cols; k++)
  124 + datum.add_float_data(m.ptr<float>(j)[k*m.channels()+i]);
  125 + } else {
  126 + caffe::CVMatToDatum(m, &datum);
  127 + }
116 128  
117 129 QVariant base_label = t.file.value("Label");
118 130 QString label_str = base_label.toString();
... ...
openbr/plugins/gallery/txt.cpp
... ... @@ -61,7 +61,7 @@ class txtGallery : public FileGallery
61 61  
62 62 if (!line.isEmpty()){
63 63 int splitIndex = line.lastIndexOf(' ');
64   - if (splitIndex == -1) templates.append(File(line));
  64 + if (splitIndex == -1) templates.append(File(line, QFileInfo(line).dir().dirName()));
65 65 else templates.append(File(line.mid(0, splitIndex), line.mid(splitIndex+1)));
66 66 templates.last().file.set("progress", this->position());
67 67 }
... ...
openbr/plugins/gui/draw.cpp
... ... @@ -38,6 +38,7 @@ class DrawTransform : public UntrainableTransform
38 38 Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false)
39 39 Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false)
40 40 Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false)
  41 + Q_PROPERTY(int pointRadius READ get_pointRadius WRITE set_pointRadius RESET reset_pointRadius STORED false)
41 42 Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false)
42 43 Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false)
43 44 BR_PROPERTY(bool, verbose, false)
... ... @@ -45,6 +46,7 @@ class DrawTransform : public UntrainableTransform
45 46 BR_PROPERTY(bool, rects, true)
46 47 BR_PROPERTY(bool, inPlace, false)
47 48 BR_PROPERTY(int, lineThickness, 1)
  49 + BR_PROPERTY(int, pointRadius, 3)
48 50 BR_PROPERTY(bool, named, true)
49 51 BR_PROPERTY(bool, location, true)
50 52  
... ... @@ -58,7 +60,7 @@ class DrawTransform : public UntrainableTransform
58 60 const QList<Point2f> pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points());
59 61 for (int i=0; i<pointsList.size(); i++) {
60 62 const Point2f &point = pointsList[i];
61   - circle(dst, point, 3, color, -1);
  63 + circle(dst, point, pointRadius, color, -1);
62 64 QString label = (location) ? QString("%1,(%2,%3)").arg(QString::number(i),QString::number(point.x),QString::number(point.y)) : QString("%1").arg(QString::number(i));
63 65 if (verbose) putText(dst, label.toStdString(), point, FONT_HERSHEY_SIMPLEX, 0.5, verboseColor, 1);
64 66 }
... ...
openbr/plugins/imgproc/affine.cpp
... ... @@ -34,6 +34,7 @@ class AffineTransform : public UntrainableTransform
34 34 {
35 35 Q_OBJECT
36 36 Q_ENUMS(Method)
  37 + Q_ENUMS(BorderMode)
37 38  
38 39 public:
39 40 /*!< */
... ... @@ -41,7 +42,14 @@ public:
41 42 Area = INTER_AREA,
42 43 Bilin = INTER_LINEAR,
43 44 Cubic = INTER_CUBIC,
44   - Lanczo = INTER_LANCZOS4};
  45 + Lanczo = INTER_LANCZOS4 };
  46 + enum BorderMode { Replicate = BORDER_REPLICATE,
  47 + Constant = BORDER_CONSTANT,
  48 + Reflect = BORDER_REFLECT,
  49 + Wrap = BORDER_WRAP,
  50 + Reflect_101 = BORDER_REFLECT_101,
  51 + Transparent = BORDER_TRANSPARENT,
  52 + Isolated = BORDER_ISOLATED };
45 53  
46 54 private:
47 55 Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false)
... ... @@ -53,6 +61,7 @@ private:
53 61 Q_PROPERTY(float x3 READ get_x3 WRITE set_x3 RESET reset_x3 STORED false)
54 62 Q_PROPERTY(float y3 READ get_y3 WRITE set_y3 RESET reset_y3 STORED false)
55 63 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
  64 + Q_PROPERTY(BorderMode borderMode READ get_borderMode WRITE set_borderMode RESET reset_borderMode STORED false)
56 65 Q_PROPERTY(bool storeAffine READ get_storeAffine WRITE set_storeAffine RESET reset_storeAffine STORED false)
57 66 Q_PROPERTY(bool warpPoints READ get_warpPoints WRITE set_warpPoints RESET reset_warpPoints STORED false)
58 67 BR_PROPERTY(int, width, 64)
... ... @@ -64,6 +73,7 @@ private:
64 73 BR_PROPERTY(float, x3, -1)
65 74 BR_PROPERTY(float, y3, -1)
66 75 BR_PROPERTY(Method, method, Bilin)
  76 + BR_PROPERTY(BorderMode, borderMode, Constant)
67 77 BR_PROPERTY(bool, storeAffine, false)
68 78 BR_PROPERTY(bool, warpPoints, false)
69 79  
... ... @@ -106,7 +116,7 @@ private:
106 116 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]);
107 117  
108 118 Mat affineTransform = getAffineTransform(srcPoints, dstPoints);
109   - warpAffine(src, dst, affineTransform, Size(width, height), method);
  119 + warpAffine(src, dst, affineTransform, Size(width, height), method, borderMode);
110 120  
111 121 if (warpPoints) {
112 122 QList<QPointF> points = src.file.points();
... ...
openbr/plugins/imgproc/cropfromlandmarks.cpp
... ... @@ -19,26 +19,33 @@ class CropFromLandmarksTransform : public UntrainableTransform
19 19 Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false)
20 20 Q_PROPERTY(float paddingHorizontal READ get_paddingHorizontal WRITE set_paddingHorizontal RESET reset_paddingHorizontal STORED false)
21 21 Q_PROPERTY(float paddingVertical READ get_paddingVertical WRITE set_paddingVertical RESET reset_paddingVertical STORED false)
  22 + Q_PROPERTY(bool shiftPoints READ get_shiftPoints WRITE set_shiftPoints RESET reset_shiftPoints STORED false)
22 23 BR_PROPERTY(QList<int>, indices, QList<int>())
23 24 BR_PROPERTY(float, paddingHorizontal, .1)
24 25 BR_PROPERTY(float, paddingVertical, .1)
  26 + BR_PROPERTY(bool, shiftPoints, false)
25 27  
26 28 void project(const Template &src, Template &dst) const
27 29 {
  30 + QList<int> cropIndices = indices;
  31 + if (cropIndices.isEmpty() && !src.file.points().isEmpty())
  32 + for (int i=0; i<src.file.points().size(); i++)
  33 + cropIndices.append(i);
  34 +
28 35 int minX = src.m().cols - 1,
29 36 maxX = 1,
30 37 minY = src.m().rows - 1,
31 38 maxY = 1;
32 39  
33   - for (int i = 0; i <indices.size(); i++) {
34   - if (minX > src.file.points()[indices[i]].x())
35   - minX = src.file.points()[indices[i]].x();
36   - if (minY > src.file.points()[indices[i]].y())
37   - minY = src.file.points()[indices[i]].y();
38   - if (maxX < src.file.points()[indices[i]].x())
39   - maxX = src.file.points()[indices[i]].x();
40   - if (maxY < src.file.points()[indices[i]].y())
41   - maxY = src.file.points()[indices[i]].y();
  40 + for (int i = 0; i <cropIndices.size(); i++) {
  41 + if (minX > src.file.points()[cropIndices[i]].x())
  42 + minX = src.file.points()[cropIndices[i]].x();
  43 + if (minY > src.file.points()[cropIndices[i]].y())
  44 + minY = src.file.points()[cropIndices[i]].y();
  45 + if (maxX < src.file.points()[cropIndices[i]].x())
  46 + maxX = src.file.points()[cropIndices[i]].x();
  47 + if (maxY < src.file.points()[cropIndices[i]].y())
  48 + maxY = src.file.points()[cropIndices[i]].y();
42 49 }
43 50  
44 51 int padW = qRound((maxX - minX) * (paddingHorizontal / 2));
... ... @@ -50,6 +57,13 @@ class CropFromLandmarksTransform : public UntrainableTransform
50 57 if (rect.x() + rect.width() > src.m().cols) rect.setWidth(src.m().cols - rect.x());
51 58 if (rect.y() + rect.width() > src.m().rows) rect.setHeight(src.m().rows - rect.y());
52 59  
  60 + if (shiftPoints) {
  61 + QList<QPointF> points = src.file.points();
  62 + for (int i=0; i<points.size(); i++)
  63 + points[i] -= rect.topLeft();
  64 + dst.file.setPoints(points);
  65 + }
  66 +
53 67 dst = Mat(src, OpenCVUtils::toRect(rect));
54 68 }
55 69 };
... ...
openbr/plugins/imgproc/if.cpp
... ... @@ -64,6 +64,19 @@ public:
64 64 dst = src;
65 65 }
66 66  
  67 + void project(const TemplateList &src, TemplateList &dst) const {
  68 + TemplateList ifTrue, ifFalse;
  69 + foreach (const Template &t, src) {
  70 + if (compare(t.file.get<QString>(key)))
  71 + ifTrue.append(t);
  72 + else
  73 + ifFalse.append(t);
  74 + }
  75 +
  76 + transform->project(ifTrue,dst);
  77 + dst.append(ifFalse);
  78 + }
  79 +
67 80 };
68 81 BR_REGISTER(Transform, IfTransform)
69 82  
... ...
openbr/plugins/imgproc/pack.cpp deleted
1   -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2   - * Copyright 2012 The MITRE Corporation *
3   - * *
4   - * Licensed under the Apache License, Version 2.0 (the "License"); *
5   - * you may not use this file except in compliance with the License. *
6   - * You may obtain a copy of the License at *
7   - * *
8   - * http://www.apache.org/licenses/LICENSE-2.0 *
9   - * *
10   - * Unless required by applicable law or agreed to in writing, software *
11   - * distributed under the License is distributed on an "AS IS" BASIS, *
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13   - * See the License for the specific language governing permissions and *
14   - * limitations under the License. *
15   - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16   -
17   -#include <openbr/plugins/openbr_internal.h>
18   -
19   -using namespace cv;
20   -
21   -namespace br
22   -{
23   -
24   -/*!
25   - * \ingroup transforms
26   - * \brief Compress two uchar into one uchar.
27   - * \author Josh Klontz \cite jklontz
28   - */
29   -class PackTransform : public UntrainableTransform
30   -{
31   - Q_OBJECT
32   -
33   - void project(const Template &src, Template &dst) const
34   - {
35   - const Mat &m = src;
36   - if ((m.cols % 2 != 0) || (m.type() != CV_8UC1))
37   - qFatal("Invalid template format.");
38   -
39   - Mat n(m.rows, m.cols/2, CV_8UC1);
40   - for (int i=0; i<m.rows; i++)
41   - for (int j=0; j<m.cols/2; j++)
42   - n.at<uchar>(i,j) = ((m.at<uchar>(i,2*j+0) >> 4) << 4) +
43   - ((m.at<uchar>(i,2*j+1) >> 4) << 0);
44   - dst = n;
45   - }
46   -};
47   -
48   -BR_REGISTER(Transform, PackTransform)
49   -
50   -} // namespace br
51   -
52   -#include "imgproc/pack.moc"
openbr/plugins/imgproc/pad.cpp
1 1 #include <opencv2/imgproc/imgproc.hpp>
2 2 #include <openbr/plugins/openbr_internal.h>
  3 +#include <openbr/core/opencvutils.h>
3 4  
4 5 using namespace cv;
5 6  
... ... @@ -35,7 +36,7 @@ private:
35 36 int top, bottom, left, right;
36 37 top = percent*src.m().rows; bottom = percent*src.m().rows;
37 38 left = percent*src.m().cols; right = percent*src.m().cols;
38   - copyMakeBorder(src, dst, top, bottom, left, right, border, Scalar(value));
  39 + OpenCVUtils::pad(src,dst,true,QList<int>() << top << bottom << left << right,true,true,border,value);
39 40 }
40 41 };
41 42  
... ...
openbr/plugins/imgproc/quantize.cpp
... ... @@ -30,8 +30,10 @@ namespace br
30 30 class QuantizeTransform : public Transform
31 31 {
32 32 Q_OBJECT
  33 + Q_PROPERTY(float c READ get_c WRITE set_c RESET reset_c STORED false)
33 34 Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a)
34 35 Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b)
  36 + BR_PROPERTY(float, c, 1)
35 37 BR_PROPERTY(float, a, 1)
36 38 BR_PROPERTY(float, b, 0)
37 39  
... ... @@ -46,7 +48,7 @@ class QuantizeTransform : public Transform
46 48  
47 49 void project(const Template &src, Template &dst) const
48 50 {
49   - src.m().convertTo(dst, CV_8U, a, b);
  51 + src.m().convertTo(dst, CV_8U, a / c, b / c);
50 52 }
51 53  
52 54 QByteArray likely(const QByteArray &indentation) const
... ...
openbr/plugins/imgproc/resize.cpp
... ... @@ -48,21 +48,35 @@ private:
48 48 Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false)
49 49 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
50 50 Q_PROPERTY(bool preserveAspect READ get_preserveAspect WRITE set_preserveAspect RESET reset_preserveAspect STORED false)
  51 + Q_PROPERTY(bool pad READ get_pad WRITE set_pad RESET reset_pad STORED false)
51 52 BR_PROPERTY(int, rows, -1)
52 53 BR_PROPERTY(int, columns, -1)
53 54 BR_PROPERTY(Method, method, Bilin)
54 55 BR_PROPERTY(bool, preserveAspect, false)
  56 + BR_PROPERTY(bool, pad, true)
55 57  
56 58 void project(const Template &src, Template &dst) const
57 59 {
  60 + if ((rows == -1) && (columns == -1)) {
  61 + dst = src;
  62 + return;
  63 + }
  64 +
58 65 if (!preserveAspect) {
59 66 resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method);
60 67 const float rowScaleFactor = (float)rows/src.m().rows;
61   - const float colScaleFactor = (float)columns/src.m().cols;
  68 + const float colScaleFactor = (columns == -1) ? rowScaleFactor : (float)columns/src.m().cols;
62 69 QList<QPointF> points = src.file.points();
63 70 for (int i=0; i<points.size(); i++)
64 71 points[i] = QPointF(points[i].x() * colScaleFactor,points[i].y() * rowScaleFactor);
65 72 dst.file.setPoints(points);
  73 + } else if (!pad) {
  74 + const int size = std::max(rows, columns);
  75 + float ratio = (float) src.m().rows / src.m().cols;
  76 + if (src.m().rows > src.m().cols)
  77 + resize(src, dst, Size(size/ratio, size), 0, 0, method);
  78 + else
  79 + resize(src, dst, Size(size, size*ratio), 0, 0, method);
66 80 } else {
67 81 float inRatio = (float) src.m().rows / src.m().cols;
68 82 float outRatio = (float) rows / columns;
... ...
openbr/plugins/imgproc/rndtranslate.cpp
... ... @@ -38,7 +38,7 @@ class RndTranslateTransform : public UntrainableMetaTransform
38 38 Q_PROPERTY(int nStages READ get_nStages WRITE set_nStages RESET reset_nStages STORED false)
39 39 BR_PROPERTY(int, nStages, 3)
40 40  
41   - void project(const Template &src, Template &dst) const
  41 + void project(const Template &, Template &) const
42 42 {
43 43 qFatal("Shoult not be here (RndTranslate)");
44 44 }
... ...
openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -35,7 +35,7 @@ namespace br
35 35 * \br_property int minSize The smallest sized object to detect in pixels
36 36 * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size
37 37 * \br_property float scaleFactor The factor to scale the image by during each resize.
38   - * \br_property float confidenceThreshold A threshold for positive detections. Positive detections returned by the classifier that have confidences below this threshold are considered negative detections.
  38 + * \br_property float minGroupingConfidence A threshold for positive detections. Positive detections returned by the classifier that have confidences below this threshold are considered negative detections.
39 39 * \br_property float eps Parameter for non-maximum supression
40 40 * \br_property int minNeighbors Parameter for non-maximum supression
41 41 * \br_property bool group If false, non-maxima supression will not be performed
... ... @@ -51,23 +51,28 @@ class SlidingWindowTransform : public MetaTransform
51 51 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
52 52 Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
53 53 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
54   - Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false)
  54 + Q_PROPERTY(float minGroupingConfidence READ get_minGroupingConfidence WRITE set_minGroupingConfidence RESET reset_minGroupingConfidence STORED false)
55 55 Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false)
56 56 Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
57 57 Q_PROPERTY(bool group READ get_group WRITE set_group RESET reset_group STORED false)
58 58 Q_PROPERTY(int shrinkingFactor READ get_shrinkingFactor WRITE set_shrinkingFactor RESET reset_shrinkingFactor STORED false)
59 59 Q_PROPERTY(bool clone READ get_clone WRITE set_clone RESET reset_clone STORED false)
60   -
  60 + Q_PROPERTY(float minConfidence READ get_minConfidence WRITE set_minConfidence RESET reset_minConfidence STORED false)
  61 + Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false)
  62 + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false)
61 63 BR_PROPERTY(br::Classifier*, classifier, NULL)
62 64 BR_PROPERTY(int, minSize, 20)
63 65 BR_PROPERTY(int, maxSize, -1)
64 66 BR_PROPERTY(float, scaleFactor, 1.2)
65   - BR_PROPERTY(float, confidenceThreshold, 10)
  67 + BR_PROPERTY(float, minGroupingConfidence, -std::numeric_limits<float>::max())
66 68 BR_PROPERTY(float, eps, 0.2)
67 69 BR_PROPERTY(int, minNeighbors, 3)
68 70 BR_PROPERTY(bool, group, true)
69 71 BR_PROPERTY(int, shrinkingFactor, 1)
70 72 BR_PROPERTY(bool, clone, true)
  73 + BR_PROPERTY(float, minConfidence, 0)
  74 + BR_PROPERTY(bool, ROCMode, false)
  75 + BR_PROPERTY(QString, outputVariable, "Face")
71 76  
72 77 void train(const TemplateList &data)
73 78 {
... ... @@ -85,9 +90,9 @@ class SlidingWindowTransform : public MetaTransform
85 90 {
86 91 foreach (const Template &t, src) {
87 92 // As a special case, skip detection if the appropriate metadata already exists
88   - if (t.file.contains("Face")) {
  93 + if (t.file.contains(outputVariable)) {
89 94 Template u = t;
90   - u.file.setRects(QList<QRectF>() << t.file.get<QRectF>("Face"));
  95 + u.file.setRects(QList<QRectF>() << t.file.get<QRectF>(outputVariable));
91 96 u.file.set("Confidence", t.file.get<float>("Confidence", 1));
92 97 dst.append(u);
93 98 continue;
... ... @@ -106,6 +111,9 @@ class SlidingWindowTransform : public MetaTransform
106 111 // different channels of the same image!
107 112 const Size imageSize = t.m().size();
108 113 const int minSize = t.file.get<int>("MinSize", this->minSize);
  114 + const int maxDetections = t.file.get<int>("MaxDetections", std::numeric_limits<int>::max());
  115 + const bool findMostConfident = (enrollAll && (maxDetections != 1)) ? false : true;
  116 +
109 117 QList<Rect> rects;
110 118 QList<float> confidences;
111 119  
... ... @@ -163,19 +171,43 @@ class SlidingWindowTransform : public MetaTransform
163 171 }
164 172  
165 173 if (group)
166   - OpenCVUtils::group(rects, confidences, confidenceThreshold, minNeighbors, eps);
  174 + OpenCVUtils::group(rects, confidences, minGroupingConfidence, minNeighbors, eps);
  175 +
  176 + if (!ROCMode && findMostConfident && !rects.isEmpty()) {
  177 + Rect rect = rects.first();
  178 + float maxConfidence = confidences.first();
  179 + for (int i=0; i<rects.size(); i++)
  180 + if (confidences[i] > maxConfidence) {
  181 + rect = rects[i];
  182 + maxConfidence = confidences[i];
  183 + }
  184 + rects.clear();
  185 + confidences.clear();
  186 + rects.append(rect);
  187 + confidences.append(maxConfidence);
  188 + }
167 189  
168   - if (!enrollAll && rects.empty()) {
169   - rects.append(Rect(0, 0, imageSize.width, imageSize.height));
170   - confidences.append(-std::numeric_limits<float>::max());
  190 + const float minConfidence = t.file.get<float>("MinConfidence", this->minConfidence);
  191 + QList<QRectF> rectsAboveMinConfidence;
  192 + QList<float> confidencesAboveMinConfidence;
  193 + for (int i=0; i<rects.size(); i++) {
  194 + if (ROCMode || confidences[i] >= minConfidence) {
  195 + rectsAboveMinConfidence.append(OpenCVUtils::fromRect(rects[i]));
  196 + confidencesAboveMinConfidence.append(confidences[i]);
  197 + }
171 198 }
172 199  
173   - for (int j=0; j<rects.size(); j++) {
174   - Template u = t;
175   - u.file.set("Confidence", confidences[j]);
176   - const QRectF rect = OpenCVUtils::fromRect(rects[j]);
  200 + if (!enrollAll && rectsAboveMinConfidence.isEmpty()) {
  201 + rectsAboveMinConfidence.append(QRectF(0, 0, t.m().cols, t.m().rows));
  202 + confidencesAboveMinConfidence.append(-std::numeric_limits<float>::max());
  203 + }
  204 +
  205 + for (int i=0; i<rectsAboveMinConfidence.size(); i++) {
  206 + Template u(t.file, t.m());
  207 + u.file.set("Confidence", confidencesAboveMinConfidence[i]);
  208 + const QRectF rect = rectsAboveMinConfidence[i];
  209 + u.file.set(outputVariable, rect);
177 210 u.file.appendRect(rect);
178   - u.file.set("Face", rect);
179 211 dst.append(u);
180 212 }
181 213 }
... ...
openbr/plugins/io/write.cpp
... ... @@ -10,6 +10,7 @@ namespace br
10 10 * \ingroup transforms
11 11 * \brief Write all mats to disk as images.
12 12 * \author Brendan Klare \cite bklare
  13 + * \br_property bool preserveFilename Writes image to original filename.
13 14 * \br_property QString outputDirectory Top level directory to write images to.
14 15 * \br_property QString underscore
15 16 * \br_property QString imgExtension Extension to save images as.
... ... @@ -19,11 +20,13 @@ namespace br
19 20 class WriteTransform : public TimeVaryingTransform
20 21 {
21 22 Q_OBJECT
  23 + Q_PROPERTY(bool preserveFilename READ get_preserveFilename WRITE set_preserveFilename RESET reset_preserveFilename STORED false)
22 24 Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false)
23 25 Q_PROPERTY(QString underscore READ get_underscore WRITE set_underscore RESET reset_underscore STORED false)
24 26 Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false)
25 27 Q_PROPERTY(int padding READ get_padding WRITE set_padding RESET reset_padding STORED false)
26 28 Q_PROPERTY(QString subDir READ get_subDir WRITE set_subDir RESET reset_subDir STORED false)
  29 + BR_PROPERTY(bool, preserveFilename, false)
27 30 BR_PROPERTY(QString, outputDirectory, "Temp")
28 31 BR_PROPERTY(QString, underscore, "")
29 32 BR_PROPERTY(QString, imgExtension, "jpg")
... ... @@ -46,11 +49,19 @@ class WriteTransform : public TimeVaryingTransform
46 49 QString dir = src.file.get<QString>(subDir, "Temp");
47 50 QString path = QString("%1/%2/").arg(outputDirectory, dir);
48 51 int value = numImages.value(dir, 0);
49   - path += QString("%1_%2.%3").arg(src.file.get<QString>(subDir, "Image")).arg(value, padding, 10, QChar('0')).arg(imgExtension);
  52 + path += preserveFilename ? QString("%1.%2").arg(src.file.baseName().split('.')[0], imgExtension)
  53 + : QString("%1_%2.%3").arg(src.file.get<QString>(subDir, "Image")).arg(value, padding, 10, QChar('0')).arg(imgExtension);
  54 + if ((QDir::currentPath() + "/" + path) == src.file.name)
  55 + qFatal("Attempted to overwrite image!");
  56 +
50 57 numImages[dir] = ++value;
51 58 OpenCVUtils::saveImage(dst.m(), path);
52 59 } else {
53   - QString path = QString("%1/image%2%3.%4").arg(outputDirectory).arg(cnt++, padding, 10, QChar('0')).arg(underscore.isEmpty() ? "" : "_" + underscore).arg(imgExtension);
  60 + QString path = preserveFilename ? QString("%1/%2.%3").arg(outputDirectory, src.file.baseName().split('.')[0], imgExtension)
  61 + : QString("%1/image%2%3.%4").arg(outputDirectory).arg(cnt++, padding, 10, QChar('0')).arg(underscore.isEmpty() ? "" : "_" + underscore).arg(imgExtension);
  62 + if ((QDir::currentPath() + "/" + path) == src.file.name)
  63 + qFatal("Attempted to overwrite image!");
  64 +
54 65 OpenCVUtils::saveImage(dst.m(), path);
55 66 }
56 67 }
... ...
openbr/plugins/distance/halfbyteL1.cpp renamed to openbr/plugins/metadata/clearrects.cpp
1 1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2   - * Copyright 2012 The MITRE Corporation *
  2 + * Copyright 2016 Rank One Computing Corporation.
3 3 * *
4 4 * Licensed under the Apache License, Version 2.0 (the "License"); *
5 5 * you may not use this file except in compliance with the License. *
... ... @@ -15,31 +15,28 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <openbr/plugins/openbr_internal.h>
18   -#include <openbr/core/distance_sse.h>
19   -
20   -using namespace cv;
21 18  
22 19 namespace br
23 20 {
24 21  
25 22 /*!
26   - * \ingroup distances
27   - * \brief Fast 4-bit L1 distance
28   - * \author Josh Klontz \cite jklontz
  23 + * \ingroup transforms
  24 + * \brief Clears the rects from a Template
  25 + * \author Brendan Klare \cite bklare
29 26 */
30   -class HalfByteL1Distance : public UntrainableDistance
  27 +class ClearRectsTransform : public UntrainableMetadataTransform
31 28 {
32 29 Q_OBJECT
33 30  
34   - float compare(const Mat &a, const Mat &b) const
  31 + void projectMetadata(const File &src, File &dst) const
35 32 {
36   - return packed_l1(a.data, b.data, a.total());
  33 + dst = src;
  34 + dst.clearRects();
37 35 }
38 36 };
39 37  
40   -BR_REGISTER(Distance, HalfByteL1Distance)
41   -
  38 +BR_REGISTER(Transform, ClearRectsTransform)
42 39  
43 40 } // namespace br
44 41  
45   -#include "distance/halfbyteL1.moc"
  42 +#include "metadata/clearrects.moc"
... ...
openbr/plugins/metadata/ifmetadata.cpp
... ... @@ -21,7 +21,7 @@ namespace br
21 21  
22 22 /*!
23 23 * \ingroup transforms
24   - * \brief Clear Templates without the required metadata.
  24 + * \brief Remove templates without the required metadata.
25 25 * \author Josh Klontz \cite jklontz
26 26 */
27 27 class IfMetadataTransform : public UntrainableMetadataTransform
... ... @@ -34,8 +34,10 @@ class IfMetadataTransform : public UntrainableMetadataTransform
34 34  
35 35 void projectMetadata(const File &src, File &dst) const
36 36 {
37   - if (src.get<QString>(key, "") == value)
38   - dst = src;
  37 + if (key == "_basename")
  38 + dst.fte = src.baseName() != value;
  39 + else
  40 + dst.fte = src.get<QString>(key, "") != value;
39 41 }
40 42 };
41 43  
... ...
openbr/plugins/metadata/randomrects.cpp
... ... @@ -21,7 +21,7 @@ class RandomRectsTransform : public UntrainableMetaTransform
21 21 BR_PROPERTY(int, numRects, 135)
22 22 BR_PROPERTY(int, minSize, 24)
23 23  
24   - void project(const Template &src, Template &dst) const
  24 + void project(const Template &, Template &) const
25 25 {
26 26 qFatal("NOT SUPPORTED");
27 27 }
... ...
openbr/plugins/metadata/randomtemplates.cpp
... ... @@ -14,7 +14,7 @@ class RandomTemplatesTransform : public UntrainableMetaTransform
14 14 Q_PROPERTY(float percent READ get_percent WRITE set_percent RESET reset_percent)
15 15 BR_PROPERTY(float, percent, .01)
16 16  
17   - void project(const Template &src, Template &dst) const {
  17 + void project(const Template &, Template &) const {
18 18 qFatal("Not supported in RandomTemplates.");
19 19 }
20 20  
... ...
openbr/plugins/metadata/removefte.cpp
... ... @@ -6,13 +6,14 @@ namespace br
6 6 /*!
7 7 * \ingroup transforms
8 8 * \author Brendan Klare \cite bklare
9   - * \brief Remove any templates that failed to enroll (FTE)
  9 + * \brief Remove any templates that failed to enroll (FTE).
  10 + * Important note: this will not work without the global enrollAll being true
10 11 */
11 12 class RemoveFTETransform : public UntrainableMetaTransform
12 13 {
13 14 Q_OBJECT
14 15  
15   - void project(const Template &src, Template &dst) const
  16 + void project(const Template &, Template &) const
16 17 {
17 18 qFatal("Not supported in RemoveFTE.");
18 19 }
... ...
openbr/plugins/metadata/savemat.cpp
... ... @@ -59,7 +59,11 @@ class JustTransform : public UntrainableMetaTransform
59 59 Template tmp;
60 60 transform->project(src, tmp);
61 61 foreach (const QString &key, keys)
62   - dst.file.set(key, tmp.file.value(key));
  62 + if (key == "_Points") {
  63 + dst.file.setPoints(tmp.file.points());
  64 + } else {
  65 + dst.file.set(key, tmp.file.value(key));
  66 + }
63 67 }
64 68 };
65 69  
... ...
openbr/plugins/representation/haar.cpp
... ... @@ -40,37 +40,51 @@ class HaarRepresentation : public Representation
40 40 void init()
41 41 {
42 42 if (features.isEmpty()) {
43   - int offset = winWidth + 1;
  43 + // Pre-determine the size of features to avoid reallocations
  44 + int numFeatures = 0;
  45 + for (int x = 0; x < winWidth; x++)
  46 + for (int y = 0; y < winHeight; y++)
  47 + for (int dx = 1; dx <= winWidth; dx++)
  48 + for (int dy = 1; dy <= winHeight; dy++)
  49 + numFeatures += ((x+dx*2 <= winWidth) && (y+dy <= winHeight))
  50 + + ((x+dx <= winWidth) && (y+dy*2 <= winHeight))
  51 + + ((x+dx*3 <= winWidth) && (y+dy <= winHeight))
  52 + + ((x+dx <= winWidth) && (y+dy*3 <= winHeight))
  53 + + ((x+dx*2 <= winWidth) && (y+dy*2 <= winHeight));
  54 + features.reserve(numFeatures);
  55 +
  56 + const int offset = winWidth + 1;
  57 + int index = 0;
44 58 for (int x = 0; x < winWidth; x++) {
45 59 for (int y = 0; y < winHeight; y++) {
46 60 for (int dx = 1; dx <= winWidth; dx++) {
47 61 for (int dy = 1; dy <= winHeight; dy++) {
48 62 // haar_x2
49 63 if ((x+dx*2 <= winWidth) && (y+dy <= winHeight))
50   - features.append(Feature(offset,
  64 + features[index++] = Feature(offset,
51 65 x, y, dx*2, dy, -1,
52   - x+dx, y, dx , dy, +2));
  66 + x+dx, y, dx , dy, +2);
53 67 // haar_y2
54 68 if ((x+dx <= winWidth) && (y+dy*2 <= winHeight))
55   - features.append(Feature(offset,
  69 + features[index++] = Feature(offset,
56 70 x, y, dx, dy*2, -1,
57   - x, y+dy, dx, dy, +2));
  71 + x, y+dy, dx, dy, +2);
58 72 // haar_x3
59 73 if ((x+dx*3 <= winWidth) && (y+dy <= winHeight))
60   - features.append(Feature(offset,
  74 + features[index++] = Feature(offset,
61 75 x, y, dx*3, dy, -1,
62   - x+dx, y, dx , dy, +3));
  76 + x+dx, y, dx , dy, +3);
63 77 // haar_y3
64 78 if ((x+dx <= winWidth) && (y+dy*3 <= winHeight))
65   - features.append(Feature(offset,
  79 + features[index++] = Feature(offset,
66 80 x, y, dx, dy*3, -1,
67   - x, y+dy, dx, dy, +3));
  81 + x, y+dy, dx, dy, +3);
68 82 // x2_y2
69 83 if ((x+dx*2 <= winWidth) && (y+dy*2 <= winHeight))
70   - features.append(Feature(offset,
  84 + features[index++] = Feature(offset,
71 85 x, y, dx*2, dy*2, -1,
72 86 x, y, dx, dy, +2,
73   - x+dx, y+dy, dx, dy, +2));
  87 + x+dx, y+dy, dx, dy, +2);
74 88  
75 89  
76 90 }
... ... @@ -89,7 +103,7 @@ class HaarRepresentation : public Representation
89 103  
90 104 float evaluate(const Template &src, int idx) const
91 105 {
92   - return (float)features[idx].calc(src.m());
  106 + return features[idx].calc(src.m());
93 107 }
94 108  
95 109 Mat evaluate(const Template &src, const QList<int> &indices) const
... ... @@ -126,24 +140,23 @@ class HaarRepresentation : public Representation
126 140 float calc(const Mat &img) const;
127 141  
128 142 struct {
129   - Rect r;
130 143 float weight;
131   - } rect[3];
132   -
133   - struct {
134 144 int p0, p1, p2, p3;
135 145 } fastRect[3];
136 146 };
137 147  
138   - QList<Feature> features;
  148 + QVector<Feature> features;
139 149 };
140 150  
141 151 BR_REGISTER(Representation, HaarRepresentation)
142 152  
143 153 HaarRepresentation::Feature::Feature()
144 154 {
145   - rect[0].r = rect[1].r = rect[2].r = Rect(0,0,0,0);
146   - rect[0].weight = rect[1].weight = rect[2].weight = 0;
  155 + fastRect[0].p0 = fastRect[1].p0 = fastRect[2].p0 = 0;
  156 + fastRect[0].p1 = fastRect[1].p1 = fastRect[2].p1 = 0;
  157 + fastRect[0].p2 = fastRect[1].p2 = fastRect[2].p2 = 0;
  158 + fastRect[0].p3 = fastRect[1].p3 = fastRect[2].p3 = 0;
  159 + fastRect[0].weight = fastRect[1].weight = fastRect[2].weight = 0;
147 160 }
148 161  
149 162 HaarRepresentation::Feature::Feature(int offset,
... ... @@ -151,38 +164,21 @@ HaarRepresentation::Feature::Feature(int offset,
151 164 int x1, int y1, int w1, int h1, float wt1,
152 165 int x2, int y2, int w2, int h2, float wt2)
153 166 {
154   - rect[0].r.x = x0;
155   - rect[0].r.y = y0;
156   - rect[0].r.width = w0;
157   - rect[0].r.height = h0;
158   - rect[0].weight = wt0;
159   -
160   - rect[1].r.x = x1;
161   - rect[1].r.y = y1;
162   - rect[1].r.width = w1;
163   - rect[1].r.height = h1;
164   - rect[1].weight = wt1;
165   -
166   - rect[2].r.x = x2;
167   - rect[2].r.y = y2;
168   - rect[2].r.width = w2;
169   - rect[2].r.height = h2;
170   - rect[2].weight = wt2;
171   -
172   - for (int j = 0; j < 3; j++) {
173   - if( rect[j].weight == 0.0F )
174   - break;
175   - CV_SUM_OFFSETS(fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset)
176   - }
  167 + CV_SUM_OFFSETS(fastRect[0].p0, fastRect[0].p1, fastRect[0].p2, fastRect[0].p3, Rect(x0, y0, w0, h0), offset)
  168 + CV_SUM_OFFSETS(fastRect[1].p0, fastRect[1].p1, fastRect[1].p2, fastRect[1].p3, Rect(x1, y1, w1, h1), offset)
  169 + CV_SUM_OFFSETS(fastRect[2].p0, fastRect[2].p1, fastRect[2].p2, fastRect[2].p3, Rect(x2, y2, w2, h2), offset)
  170 + fastRect[0].weight = wt0;
  171 + fastRect[1].weight = wt1;
  172 + fastRect[2].weight = wt2;
177 173 }
178 174  
179 175 inline float HaarRepresentation::Feature::calc(const Mat &img) const
180 176 {
181 177 const int* ptr = img.ptr<int>();
182   - float ret = rect[0].weight * (ptr[fastRect[0].p0] - ptr[fastRect[0].p1] - ptr[fastRect[0].p2] + ptr[fastRect[0].p3]) +
183   - rect[1].weight * (ptr[fastRect[1].p0] - ptr[fastRect[1].p1] - ptr[fastRect[1].p2] + ptr[fastRect[1].p3]);
184   - if (rect[2].weight != 0.0f)
185   - ret += rect[2].weight * (ptr[fastRect[2].p0] - ptr[fastRect[2].p1] - ptr[fastRect[2].p2] + ptr[fastRect[2].p3]);
  178 + float ret = fastRect[0].weight * (ptr[fastRect[0].p0] - ptr[fastRect[0].p1] - ptr[fastRect[0].p2] + ptr[fastRect[0].p3]) +
  179 + fastRect[1].weight * (ptr[fastRect[1].p0] - ptr[fastRect[1].p1] - ptr[fastRect[1].p2] + ptr[fastRect[1].p3]);
  180 + if (fastRect[2].weight != 0.0f)
  181 + ret += fastRect[2].weight * (ptr[fastRect[2].p0] - ptr[fastRect[2].p1] - ptr[fastRect[2].p2] + ptr[fastRect[2].p3]);
186 182 return ret;
187 183 }
188 184  
... ...
scripts/brpy/__init__.py
... ... @@ -53,6 +53,10 @@ def _handle_string_func(func, *moretypes):
53 53 return msg
54 54 return call_func
55 55  
  56 +# TODO: small wrapper around any objects that need br_free_*
  57 +# so when python object dies, user doesn't need to call br_free_*
  58 +# TODO: iterator for templatelist object
  59 +# (or mimic C++ obj API via C? overkill?)
56 60 def init_brpy(br_loc='/usr/local/lib'):
57 61 """Initializes all function inputs and outputs for the br ctypes lib object"""
58 62  
... ...
scripts/brpy/html_viz.py
... ... @@ -8,24 +8,57 @@ and host them as long as the relative paths remain the same on ya serva.
8 8  
9 9 from PIL import Image
10 10  
  11 +LMSIZE = 8
  12 +
  13 +def landmarks_on_crop(landmarks, x, y, width, height, imname, maxheight=None, color='green'):
  14 + '''
  15 + Generates an HTML string that shows landmarks within a given cropped image.
  16 + When two landmarked crops are put next to each other, they will be inline. To make each crop its own line, wrap it in a div.
  17 + '''
  18 + html = _bbcrop(x, y, width, height, imname, maxheight)
  19 + if not maxheight:
  20 + maxheight = height
  21 + ratio = float(maxheight) / height
  22 + print(ratio)
  23 + if len(landmarks) > 0 and len(landmarks[0]) != 3:
  24 + landmarks = [ lm + (color,) for lm in landmarks ]
  25 + for lmx,lmy,col in landmarks:
  26 + html += landmark((lmx-x)*ratio - (LMSIZE/2), (lmy-y)*ratio - (LMSIZE/2), col)
  27 + html += '</div>'
  28 + return html
  29 +
  30 +def landmark_img(x, y, img, color='green'):
  31 + html = '<div style="position:relative;">'
  32 + html += '<img src="%s" />' % img
  33 + html += landmark(x,y,color)
  34 + html += '</div>'
  35 + return html
  36 +
  37 +def landmark(x, y, color='green'):
  38 + return '<div style="position:absolute; top:{0}px; left:{1}px; color:{2}; background-color:{2}; width:{3}px; height:{3}px;"></div>'.format(y,x,color,LMSIZE)
  39 +
11 40 def crop_to_bb(x, y, width, height, imname, maxheight=None):
12 41 '''
13 42 Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels.
14 43 A maxheight of None will keep the original size (default).
15 44 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 45 '''
  46 + html = _bbcrop(x, y, width, height, imname, maxheight)
  47 + html += '</div>'
  48 + return html
  49 +
  50 +def _bbcrop(x, y, width, height, imname, maxheight):
17 51 img = Image.open(imname)
18 52 imwidth, imheight = img.size
19 53 if not maxheight:
20 54 maxheight = height
21   - ratio = maxheight / height
  55 + ratio = float(maxheight) / height
22 56 # note for future me:
23 57 # image is cropped with div width/height + overflow:hidden,
24 58 # resized with img height,
25 59 # and positioned with img margin
26   - html = '<div style="overflow:hidden; display:inline-block; width:%ipx; height:%ipx;">' % (width*ratio, maxheight)
  60 + html = '<div style="overflow:hidden; display:inline-block; position:relative; width:%ipx; height:%ipx;">' % (width*ratio, maxheight)
27 61 html += '<img src="%s" style="height:%ipx; margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio)
28   - html += '</div>'
29 62 return html
30 63  
31 64 def bbs_for_image(imname, bbs, maxheight=None, colors=None):
... ... @@ -37,7 +70,7 @@ def bbs_for_image(imname, bbs, maxheight=None, colors=None):
37 70 imwidth, imheight = img.size
38 71 if not maxheight:
39 72 maxheight = imheight
40   - ratio = maxheight/imheight
  73 + ratio = float(maxheight)/imheight
41 74 html = [
42 75 '<div style="position:relative">',
43 76 '<img src="%s" style="height:%ipx" />' % (imname, maxheight)
... ...
share/openbr/cmake/InstallDependencies.cmake
... ... @@ -2,10 +2,8 @@ set(BR_INSTALL_DEPENDENCIES OFF CACHE BOOL &quot;Install runtime dependencies.&quot;)
2 2  
3 3 # OpenCV Libs
4 4 function(install_opencv_library lib)
5   - if(${BR_INSTALL_DEPENDENCIES})
6   - if(ANDROID)
7   - # Do nothing assuming we are using OpenCV static libs
8   - elseif(CMAKE_HOST_WIN32)
  5 + if(${BR_INSTALL_DEPENDENCIES} AND ${OpenCV_SHARED})
  6 + if(CMAKE_HOST_WIN32)
9 7 if(${CMAKE_BUILD_TYPE} MATCHES Debug)
10 8 set(BR_INSTALL_DEPENDENCIES_SUFFIX "d")
11 9 endif()
... ... @@ -65,7 +63,10 @@ endfunction()
65 63 function(install_qt_imageformats)
66 64 if(${BR_INSTALL_DEPENDENCIES})
67 65 set(IMAGE_FORMATS_DIR "${_qt5Core_install_prefix}/plugins/imageformats")
68   - if(CMAKE_HOST_WIN32)
  66 + if(ANDROID)
  67 + set(INSTALL_DEPENDENCIES_PREFIX "lib")
  68 + set(INSTALL_DEPENDENCIES_EXTENSION ".so")
  69 + elseif(CMAKE_HOST_WIN32)
69 70 set(INSTALL_DEPENDENCIES_PREFIX "")
70 71 set(INSTALL_DEPENDENCIES_EXTENSION ".dll")
71 72 elseif(CMAKE_HOST_APPLE)
... ... @@ -118,6 +119,11 @@ function(install_qt_misc)
118 119 file(GLOB d3dcomp ${_qt5Core_install_prefix}/bin/d3dcompiler_*.dll)
119 120 install(FILES ${d3dcomp} DESTINATION bin)
120 121 install(FILES ${_qt5Core_install_prefix}/plugins/platforms/qwindows${BR_INSTALL_DEPENDENCIES_SUFFIX}.dll DESTINATION bin/platforms)
  122 + elseif(ANDROID)
  123 + install(FILES ${__libstl} DESTINATION lib)
  124 + elseif(UNIX AND NOT APPLE)
  125 + file(GLOB icudlls ${_qt5Core_install_prefix}/lib/libicu*.so*)
  126 + install(FILES ${icudlls} DESTINATION lib)
121 127 endif()
122 128 endfunction()
123 129  
... ...
share/openbr/plotting/plot_utils.R
... ... @@ -216,13 +216,13 @@ formatData &lt;- function(type=&quot;eval&quot;) {
216 216 Box$X <<- factor(Box$X, levels = Box$X, ordered = TRUE)
217 217 Sample <<- data[grep("Sample",data$Plot),-c(1)]
218 218 Sample$X <<- as.character(Sample$X)
  219 + displaySample <<- readImageData(Sample)
  220 + rows <<- displaySample[[1]]$value
219 221 EXT <<- data[grep("EXT",data$Plot),-c(1)]
220 222 EXT$X <<- as.character(EXT$X)
221 223 EXP <<- data[grep("EXP",data$Plot),-c(1)]
222 224 EXP$X <<- as.character(EXP$X)
223 225 NormLength <<- data[grep("NormLength",data$Plot),-c(1)]
224   - sample <<- readImageData(Sample)
225   - rows <<- sample[[1]]$value
226 226 } else if (type == "knn") {
227 227 # Split data into individual plots
228 228 IET <<- data[grep("IET",data$Plot),-c(1)]
... ... @@ -325,14 +325,15 @@ plotEERSamples &lt;- function(imData=NULL, gmData=NULL) {
325 325 printImages(gmData, "Genuine")
326 326 }
327 327  
328   -plotLandmarkSamples <- function(samples=NULL, expData=NULL, extData=NULL) {
329   - print(plotImage(samples[[1]], "Sample Landmarks", sprintf("Total Landmarks: %s", samples[[1]]$value)))
  328 +plotLandmarkSamples <- function(displaySample=NULL, expData=NULL, extData=NULL) {
  329 + print(plotImage(displaySample[[1]], "Sample Landmarks", sprintf("Total Landmarks: %s", displaySample[[1]]$value)))
  330 + column <- if(majorSize > 1) majorHeader else "File"
330 331 if (nrow(EXT) != 0 && nrow(EXP)) {
331 332 for (j in 1:length(algs)) {
332   - truthSample <- readData(EXT[EXT$. == algs[[j]],])
333   - predictedSample <- readData(EXP[EXP$. == algs[[j]],])
  333 + truthSample <- readImageData(EXT[EXT[,column] == algs[[j]],])
  334 + predictedSample <- readImageData(EXP[EXP[,column] == algs[[j]],])
334 335 for (i in 1:length(predictedSample)) {
335   - multiplot(plotImage(predictedSample[[i]], sprintf("%s\nPredicted Landmarks", algs[[j]]), sprintf("Average Landmark Error: %.3f", predictedSample[[i]]$value)), plotImage(truthSample[[i]], "Ground Truth\nLandmarks", ""), cols=2)
  336 + multiplot(plotImage(predictedSample[[i]], sprintf("%s\nPredicted Landmarks", algs[[j]]), sprintf("Average Landmark Error: %.3f", predictedSample[[i]]$value)), plotImage(truthSample[[i]], "Ground Truth\nLandmarks", truthSample[[i]]$path), cols=2)
336 337 }
337 338 }
338 339 }
... ... @@ -341,20 +342,20 @@ plotLandmarkSamples &lt;- function(samples=NULL, expData=NULL, extData=NULL) {
341 342 readImageData <- function(data) {
342 343 examples <- list()
343 344 for (i in 1:nrow(data)) {
344   - path <- data[i,1]
  345 + examplePath <- unlist(strsplit(data[i,1], "[:]"))[1]
  346 + path <- unlist(strsplit(data[i,1], "[:]"))[2]
345 347 value <- data[i,2]
346   - file <- unlist(strsplit(path, "[.]"))[1]
347   - ext <- unlist(strsplit(path, "[.]"))[2]
  348 + ext <- unlist(strsplit(examplePath, "[.]"))[2]
348 349 if (ext == "jpg" || ext == "JPEG" || ext == "jpeg" || ext == "JPG") {
349   - img <- readJPEG(path)
  350 + img <- readJPEG(examplePath)
350 351 } else if (ext == "PNG" || ext == "png") {
351   - img <- readPNG(path)
  352 + img <- readPNG(examplePath)
352 353 } else if (ext == "TIFF" || ext == "tiff" || ext == "TIF" || ext == "tif") {
353   - img <- readTIFF(path)
  354 + img <- readTIFF(examplePath)
354 355 }else {
355 356 next
356 357 }
357   - example <- list(file = file, value = value, image = img)
  358 + example <- list(path = path, value = value, image = img)
358 359 examples[[i]] <- example
359 360 }
360 361 return(examples)
... ...