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,6 +32,9 @@ share/openbr/models
32 *.RData 32 *.RData
33 *.Rhistory 33 *.Rhistory
34 34
  35 +### Python ###
  36 +*.pyc
  37 +
35 ### Subversion ### 38 ### Subversion ###
36 *.svn* 39 *.svn*
37 40
CMakeLists.txt
@@ -86,7 +86,7 @@ set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Qt5Core_QTMAIN_LIBRARIES}) @@ -86,7 +86,7 @@ set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Qt5Core_QTMAIN_LIBRARIES})
86 86
87 # Find OpenCV 87 # Find OpenCV
88 find_package(OpenCV 2.4.5 REQUIRED) 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 set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${OPENCV_DEPENDENCIES}) 90 set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${OPENCV_DEPENDENCIES})
91 91
92 # Find Alphanum 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,6 +46,14 @@ The Python API is a light wrapper of the C API. It creates an object that has [a
46 br.br_free_template_list(query) 46 br.br_free_template_list(query)
47 br.br_finalize() 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 To enable the module, add `-DBR_INSTALL_BRPY=ON` to your cmake command (or use the ccmake GUI - highly recommended). 57 To enable the module, add `-DBR_INSTALL_BRPY=ON` to your cmake command (or use the ccmake GUI - highly recommended).
50 58
51 Currently only OS X and Linux are supported. 59 Currently only OS X and Linux are supported.
openbr/CMakeLists.txt
@@ -34,10 +34,7 @@ if(NOT BR_EMBEDDED) @@ -34,10 +34,7 @@ if(NOT BR_EMBEDDED)
34 install(FILES ${HEADERS} DESTINATION include/openbr/gui) 34 install(FILES ${HEADERS} DESTINATION include/openbr/gui)
35 endif() 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 qt5_use_modules(openbr ${QT_DEPENDENCIES}) 38 qt5_use_modules(openbr ${QT_DEPENDENCIES})
42 set_target_properties(openbr PROPERTIES 39 set_target_properties(openbr PROPERTIES
43 DEFINE_SYMBOL BR_LIBRARY 40 DEFINE_SYMBOL BR_LIBRARY
openbr/core/bee.h
@@ -34,21 +34,21 @@ namespace BEE @@ -34,21 +34,21 @@ namespace BEE
34 const MaskValue DontCare(0x00); 34 const MaskValue DontCare(0x00);
35 35
36 // Sigset 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 // Matrix 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 // Mask 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 #endif // BEE_BEE_H 54 #endif // BEE_BEE_H
openbr/core/boost.cpp
@@ -788,14 +788,17 @@ void CascadeBoostTrainData::precalculate() @@ -788,14 +788,17 @@ void CascadeBoostTrainData::precalculate()
788 { 788 {
789 int minNum = MIN( numPrecalcVal, numPrecalcIdx); 789 int minNum = MIN( numPrecalcVal, numPrecalcIdx);
790 790
791 - double proctime = -TIME( 0 ); 791 + QTime time;
  792 + time.start();
  793 +
792 parallel_for_( Range(numPrecalcVal, numPrecalcIdx), 794 parallel_for_( Range(numPrecalcVal, numPrecalcIdx),
793 FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) ); 795 FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) );
794 parallel_for_( Range(0, minNum), 796 parallel_for_( Range(0, minNum),
795 FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) ); 797 FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) );
796 parallel_for_( Range(minNum, numPrecalcVal), 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 //-------------------------------- CascadeBoostTree ---------------------------------------- 804 //-------------------------------- CascadeBoostTree ----------------------------------------
openbr/core/boost.h
@@ -4,12 +4,6 @@ @@ -4,12 +4,6 @@
4 #include "ml.h" 4 #include "ml.h"
5 #include <openbr/openbr_plugin.h> 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 namespace br 7 namespace br
14 { 8 {
15 9
openbr/core/eval.cpp
@@ -19,10 +19,13 @@ @@ -19,10 +19,13 @@
19 #include "openbr/core/common.h" 19 #include "openbr/core/common.h"
20 #include "openbr/core/qtutils.h" 20 #include "openbr/core/qtutils.h"
21 #include "openbr/core/opencvutils.h" 21 #include "openbr/core/opencvutils.h"
  22 +#include "openbr/core/evalutils.h"
22 #include <QMapIterator> 23 #include <QMapIterator>
23 #include <cmath> 24 #include <cmath>
  25 +#include <opencv2/highgui/highgui.hpp>
24 26
25 using namespace cv; 27 using namespace cv;
  28 +using namespace EvalUtils;
26 29
27 namespace br 30 namespace br
28 { 31 {
@@ -752,298 +755,6 @@ void EvalClassification(const QString &amp;predictedGallery, const QString &amp;truthGal @@ -752,298 +755,6 @@ void EvalClassification(const QString &amp;predictedGallery, const QString &amp;truthGal
752 qDebug("Overall Accuracy = %f", (float)tpc / (float)(tpc + fnc)); 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 float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize, int minSize, int maxSize) 758 float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize, int minSize, int maxSize)
1048 { 759 {
1049 qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); 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,51 +764,13 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
1053 // Remove any bounding boxes with a side smaller than minSize 764 // Remove any bounding boxes with a side smaller than minSize
1054 if (minSize > 0) { 765 if (minSize > 0) {
1055 qDebug("Removing boxes smaller than %d\n", minSize); 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 // Remove any bounding boxes with no side smaller than maxSize 770 // Remove any bounding boxes with no side smaller than maxSize
1079 if (maxSize > 0) { 771 if (maxSize > 0) {
1080 qDebug("Removing boxes larger than %d\n", maxSize); 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 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections; 776 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;
@@ -1117,11 +790,27 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -1117,11 +790,27 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
1117 falseNegativeDetections.clear(); 790 falseNegativeDetections.clear();
1118 totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations); 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 std::sort(resolvedDetections.begin(), resolvedDetections.end()); 807 std::sort(resolvedDetections.begin(), resolvedDetections.end());
1121 QStringList lines; 808 QStringList lines;
1122 lines.append("Plot, X, Y"); 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 float averageOverlap; 815 float averageOverlap;
1127 { // Overlap Density 816 { // Overlap Density
@@ -1209,7 +898,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle @@ -1209,7 +898,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1209 { 898 {
1210 QScopedPointer<Transform> t(Transform::make("Open+Draw(verbose,rects=false,location=false)",NULL)); 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 projectAndWrite(t.data(), truth[sampleIndex],filePath); 902 projectAndWrite(t.data(), truth[sampleIndex],filePath);
1214 lines.append("Sample,"+filePath+","+QString::number(truth[sampleIndex].file.points().size())); 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,26 +906,26 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1217 // Get best and worst performing examples 906 // Get best and worst performing examples
1218 QList< QPair<float,int> > exampleIndices = Common::Sort(imageErrors,true); 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 for (int i=0; i<totalExamples; i++) { 911 for (int i=0; i<totalExamples; i++) {
1223 QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName(); 912 QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName();
1224 projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath); 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 filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName(); 916 filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName();
1228 projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath); 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 for (int i=exampleIndices.size()-1; i>exampleIndices.size()-totalExamples-1; i--) { 921 for (int i=exampleIndices.size()-1; i>exampleIndices.size()-totalExamples-1; i--) {
1233 QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName(); 922 QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName();
1234 projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath); 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 filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName(); 926 filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName();
1238 projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath); 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 for (int i=0; i<pointErrors.size(); i++) { 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,7 +436,7 @@ public:
436 }; 436 };
437 437
438 // TODO: Make sure case where no confidences are inputted works. 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 if (rects.isEmpty()) 441 if (rects.isEmpty())
442 return; 442 return;
@@ -448,8 +448,9 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con @@ -448,8 +448,9 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
448 vector<Rect> rrects(nClasses); 448 vector<Rect> rrects(nClasses);
449 449
450 // Total number of rects in each class 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 for (size_t i = 0; i < labels.size(); i++) 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,18 +460,26 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
459 rrects[cls].width += rects[i].width; 460 rrects[cls].width += rects[i].width;
460 rrects[cls].height += rects[i].height; 461 rrects[cls].height += rects[i].height;
461 neighbors[cls]++; 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 // Find average rectangle for all classes 472 // Find average rectangle for all classes
466 for (int i = 0; i < nClasses; i++) 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 rects.clear(); 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,7 +489,7 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
480 for (int i = 0; i < nClasses; i++) 489 for (int i = 0; i < nClasses; i++)
481 { 490 {
482 // Average rectangle 491 // Average rectangle
483 - Rect r1 = rrects[i]; 492 + const Rect r1 = rrects[i];
484 493
485 // Used to eliminate rectangles with few neighbors in the case of no weights 494 // Used to eliminate rectangles with few neighbors in the case of no weights
486 const float w1 = classConfidence[i]; 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,21 +510,19 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
501 if (j == i || n2 < minNeighbors) 510 if (j == i || n2 < minNeighbors)
502 continue; 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 if(r1.x >= r2.x - dx && 520 if(r1.x >= r2.x - dx &&
515 r1.y >= r2.y - dy && 521 r1.y >= r2.y - dy &&
516 r1.x + r1.width <= r2.x + r2.width + dx && 522 r1.x + r1.width <= r2.x + r2.width + dx &&
517 r1.y + r1.height <= r2.y + r2.height + dy && 523 r1.y + r1.height <= r2.y + r2.height + dy &&
518 - (w2 > std::max(confidenceThreshold, w1))) 524 + (w2 > w1) &&
  525 + (n2 > n1))
519 break; 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,11 +530,49 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
523 { 530 {
524 rects.append(r1); 531 rects.append(r1);
525 confidences.append(w1); 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 Mat rotMatrix = getRotationMatrix2D(Point2f(src.m().rows/2,src.m().cols/2),degrees,1.0); 577 Mat rotMatrix = getRotationMatrix2D(Point2f(src.m().rows/2,src.m().cols/2),degrees,1.0);
533 if (rotateMat) { 578 if (rotateMat) {
@@ -579,7 +624,7 @@ void OpenCVUtils::rotate(const br::Template &amp;src, br::Template &amp;dst, int degrees @@ -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 for (int i=0; i<src.size(); i++) { 629 for (int i=0; i<src.size(); i++) {
585 br::Template t; 630 br::Template t;
openbr/core/opencvutils.h
@@ -102,9 +102,11 @@ namespace OpenCVUtils @@ -102,9 +102,11 @@ namespace OpenCVUtils
102 float overlap(const QRectF &rect1, const QRectF &rect2); 102 float overlap(const QRectF &rect1, const QRectF &rect2);
103 103
104 // Misc 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 void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); 110 void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true);
109 void flip(const br::TemplateList &src, br::TemplateList &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true); 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,9 +318,9 @@ bool PlotLandmarking(const QStringList &amp;files, const File &amp;destination, bool sho
318 qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); 318 qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination));
319 RPlot p(files, destination); 319 RPlot p(files, destination);
320 p.file.write("\nformatData(type=\"landmarking\")\n\n"); 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 p.file.write("algs <- algs[!duplicated(algs)]\n"); 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 p.file.write("plotLandmarkTables(tableData=Box)\n"); 324 p.file.write("plotLandmarkTables(tableData=Box)\n");
325 325
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(), 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,9 +330,6 @@ bool PlotLandmarking(const QStringList &amp;files, const File &amp;destination, bool sho
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()) + 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 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"))); 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 return p.finalize(show); 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,7 +1210,8 @@ bool br::Context::checkSDKPath(const QString &amp;sdkPath)
1210 return QFileInfo(sdkPath + "/share/openbr/openbr.bib").exists(); 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 static QCoreApplication *application = NULL; 1215 static QCoreApplication *application = NULL;
1215 1216
1216 void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool useGui) 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,7 +1231,6 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
1230 // We take in argc as a reference due to: 1231 // We take in argc as a reference due to:
1231 // https://bugreports.qt-project.org/browse/QTBUG-5637 1232 // https://bugreports.qt-project.org/browse/QTBUG-5637
1232 // QApplication should be initialized before anything else. 1233 // QApplication should be initialized before anything else.
1233 - // Since we can't ensure that it gets deleted last, we never delete it.  
1234 if (QCoreApplication::instance() == NULL) { 1234 if (QCoreApplication::instance() == NULL) {
1235 #ifndef BR_EMBEDDED 1235 #ifndef BR_EMBEDDED
1236 if (useGui) application = new QApplication(argc, argv); 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,6 +1245,7 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
1245 if (sdkPath.isEmpty()) { 1245 if (sdkPath.isEmpty()) {
1246 QStringList checkPaths; checkPaths << QCoreApplication::applicationDirPath() << QDir::currentPath(); 1246 QStringList checkPaths; checkPaths << QCoreApplication::applicationDirPath() << QDir::currentPath();
1247 checkPaths << QString(getenv("PATH")).split(sep, QString::SkipEmptyParts); 1247 checkPaths << QString(getenv("PATH")).split(sep, QString::SkipEmptyParts);
  1248 + QSet<QString> checkedPaths; // Avoid infinite loops from symlinks
1248 1249
1249 bool foundSDK = false; 1250 bool foundSDK = false;
1250 foreach (const QString &path, checkPaths) { 1251 foreach (const QString &path, checkPaths) {
@@ -1252,6 +1253,8 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG @@ -1252,6 +1253,8 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
1252 QDir dir(path); 1253 QDir dir(path);
1253 do { 1254 do {
1254 sdkPath = dir.absolutePath(); 1255 sdkPath = dir.absolutePath();
  1256 + if (checkedPaths.contains(sdkPath)) break;
  1257 + else checkedPaths.insert(sdkPath);
1255 foundSDK = checkSDKPath(sdkPath); 1258 foundSDK = checkSDKPath(sdkPath);
1256 dir.cdUp(); 1259 dir.cdUp();
1257 } while (!foundSDK && !dir.isRoot()); 1260 } while (!foundSDK && !dir.isRoot());
@@ -1316,9 +1319,6 @@ void br::Context::finalize() @@ -1316,9 +1319,6 @@ void br::Context::finalize()
1316 1319
1317 delete Globals; 1320 delete Globals;
1318 Globals = NULL; 1321 Globals = NULL;
1319 -  
1320 - delete application;  
1321 - application = NULL;  
1322 } 1322 }
1323 1323
1324 QString br::Context::about() 1324 QString br::Context::about()
@@ -1602,7 +1602,8 @@ Transform *Transform::make(QString str, QObject *parent) @@ -1602,7 +1602,8 @@ Transform *Transform::make(QString str, QObject *parent)
1602 // Base name not found? Try constructing it via LoadStore 1602 // Base name not found? Try constructing it via LoadStore
1603 if (!Factory<Transform>::names().contains(parsed.suffix()) 1603 if (!Factory<Transform>::names().contains(parsed.suffix())
1604 && (QFileInfo(parsed.suffix()).exists() 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 Transform *tform = make("<"+parsed.suffix()+">", parent); 1607 Transform *tform = make("<"+parsed.suffix()+">", parent);
1607 applyAdditionalProperties(parsed, tform); 1608 applyAdditionalProperties(parsed, tform);
1608 return tform; 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,4 +323,4 @@ BR_REGISTER(Classifier, CascadeClassifier)
323 323
324 } // namespace br 324 } // namespace br
325 325
326 -#include "classification/cascade.moc" 326 +#include "classification/cascade_classifier.moc"
openbr/plugins/classification/dlib.cpp
1 #include <opencv2/imgproc/imgproc.hpp> 1 #include <opencv2/imgproc/imgproc.hpp>
2 #include <dlib/image_processing/frontal_face_detector.h> 2 #include <dlib/image_processing/frontal_face_detector.h>
  3 +#include <dlib/image_processing.h>
3 #include <dlib/opencv.h> 4 #include <dlib/opencv.h>
4 5
5 #include "openbr/plugins/openbr_internal.h" 6 #include "openbr/plugins/openbr_internal.h"
@@ -34,29 +35,29 @@ class DLandmarkerTransform : public UntrainableTransform @@ -34,29 +35,29 @@ class DLandmarkerTransform : public UntrainableTransform
34 Q_OBJECT 35 Q_OBJECT
35 36
36 private: 37 private:
37 -  
38 Resource<shape_predictor> shapeResource; 38 Resource<shape_predictor> shapeResource;
39 39
40 void init() 40 void init()
41 { 41 {
42 shapeResource.setResourceMaker(new DLibShapeResourceMaker()); 42 shapeResource.setResourceMaker(new DLibShapeResourceMaker());
  43 + shapeResource.release(shapeResource.acquire()); // Pre-load one instance of the model
43 } 44 }
44 45
45 QPointF averagePoints(const QList<QPointF> &points, int rangeBegin, int rangeEnd) const 46 QPointF averagePoints(const QList<QPointF> &points, int rangeBegin, int rangeEnd) const
46 { 47 {
47 - QPointF point; 48 + QPointF point;
48 for (int i=rangeBegin; i<rangeEnd; i++) 49 for (int i=rangeBegin; i<rangeEnd; i++)
49 point += points[i]; 50 point += points[i];
50 point /= (rangeEnd-rangeBegin); 51 point /= (rangeEnd-rangeBegin);
51 - return point; 52 + return point;
52 } 53 }
53 54
54 void setFacePoints(Template &dst) const 55 void setFacePoints(Template &dst) const
55 { 56 {
56 const QList<QPointF> points = dst.file.points(); 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 void project(const Template &src, Template &dst) const 63 void project(const Template &src, Template &dst) const
@@ -73,25 +74,21 @@ private: @@ -73,25 +74,21 @@ private:
73 array2d<unsigned char> image; 74 array2d<unsigned char> image;
74 assign_image(image,cimg); 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 shapeResource.release(sp); 92 shapeResource.release(sp);
96 } 93 }
97 }; 94 };
openbr/plugins/cmake/opencv.cmake
@@ -35,6 +35,16 @@ else() @@ -35,6 +35,16 @@ else()
35 plugins/imgproc/sift.cpp) 35 plugins/imgproc/sift.cpp)
36 endif() 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 option(BR_WITH_OPENCV_PHOTO "Build with OpenCV photo plugins." ON) 48 option(BR_WITH_OPENCV_PHOTO "Build with OpenCV photo plugins." ON)
39 if(${BR_WITH_OPENCV_PHOTO}) 49 if(${BR_WITH_OPENCV_PHOTO})
40 set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} opencv_photo) 50 set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} opencv_photo)
openbr/plugins/cmake/stasm4.cmake
@@ -6,5 +6,4 @@ if(${BR_WITH_STASM4}) @@ -6,5 +6,4 @@ if(${BR_WITH_STASM4})
6 install(DIRECTORY ${Stasm_DIR}/data/ DESTINATION share/openbr/models/stasm) 6 install(DIRECTORY ${Stasm_DIR}/data/ DESTINATION share/openbr/models/stasm)
7 else() 7 else()
8 set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/stasm4.cpp) 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 endif() 9 endif()
openbr/plugins/core/loadstore.cpp
@@ -66,10 +66,12 @@ private: @@ -66,10 +66,12 @@ private:
66 if (transform != NULL) return; 66 if (transform != NULL) return;
67 if (fileName.isEmpty()) fileName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString); 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 transform = make(transformString); 70 transform = make(transformString);
71 - else 71 + trainable = transform->trainable;
  72 + } else {
72 trainable = false; 73 trainable = false;
  74 + }
73 } 75 }
74 76
75 bool timeVarying() const 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,4 +160,4 @@ BR_REGISTER(Format, DefaultFormat)
160 160
161 } // namespace br 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,7 +112,19 @@ class lmdbGallery : public Gallery
112 foreach(const Template &t, working) { 112 foreach(const Template &t, working) {
113 // add current image to transaction 113 // add current image to transaction
114 caffe::Datum datum; 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 QVariant base_label = t.file.value("Label"); 129 QVariant base_label = t.file.value("Label");
118 QString label_str = base_label.toString(); 130 QString label_str = base_label.toString();
openbr/plugins/gallery/txt.cpp
@@ -61,7 +61,7 @@ class txtGallery : public FileGallery @@ -61,7 +61,7 @@ class txtGallery : public FileGallery
61 61
62 if (!line.isEmpty()){ 62 if (!line.isEmpty()){
63 int splitIndex = line.lastIndexOf(' '); 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 else templates.append(File(line.mid(0, splitIndex), line.mid(splitIndex+1))); 65 else templates.append(File(line.mid(0, splitIndex), line.mid(splitIndex+1)));
66 templates.last().file.set("progress", this->position()); 66 templates.last().file.set("progress", this->position());
67 } 67 }
openbr/plugins/gui/draw.cpp
@@ -38,6 +38,7 @@ class DrawTransform : public UntrainableTransform @@ -38,6 +38,7 @@ class DrawTransform : public UntrainableTransform
38 Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) 38 Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false)
39 Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) 39 Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false)
40 Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) 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 Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) 42 Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false)
42 Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false) 43 Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false)
43 BR_PROPERTY(bool, verbose, false) 44 BR_PROPERTY(bool, verbose, false)
@@ -45,6 +46,7 @@ class DrawTransform : public UntrainableTransform @@ -45,6 +46,7 @@ class DrawTransform : public UntrainableTransform
45 BR_PROPERTY(bool, rects, true) 46 BR_PROPERTY(bool, rects, true)
46 BR_PROPERTY(bool, inPlace, false) 47 BR_PROPERTY(bool, inPlace, false)
47 BR_PROPERTY(int, lineThickness, 1) 48 BR_PROPERTY(int, lineThickness, 1)
  49 + BR_PROPERTY(int, pointRadius, 3)
48 BR_PROPERTY(bool, named, true) 50 BR_PROPERTY(bool, named, true)
49 BR_PROPERTY(bool, location, true) 51 BR_PROPERTY(bool, location, true)
50 52
@@ -58,7 +60,7 @@ class DrawTransform : public UntrainableTransform @@ -58,7 +60,7 @@ class DrawTransform : public UntrainableTransform
58 const QList<Point2f> pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points()); 60 const QList<Point2f> pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points());
59 for (int i=0; i<pointsList.size(); i++) { 61 for (int i=0; i<pointsList.size(); i++) {
60 const Point2f &point = pointsList[i]; 62 const Point2f &point = pointsList[i];
61 - circle(dst, point, 3, color, -1); 63 + circle(dst, point, pointRadius, color, -1);
62 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)); 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 if (verbose) putText(dst, label.toStdString(), point, FONT_HERSHEY_SIMPLEX, 0.5, verboseColor, 1); 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,6 +34,7 @@ class AffineTransform : public UntrainableTransform
34 { 34 {
35 Q_OBJECT 35 Q_OBJECT
36 Q_ENUMS(Method) 36 Q_ENUMS(Method)
  37 + Q_ENUMS(BorderMode)
37 38
38 public: 39 public:
39 /*!< */ 40 /*!< */
@@ -41,7 +42,14 @@ public: @@ -41,7 +42,14 @@ public:
41 Area = INTER_AREA, 42 Area = INTER_AREA,
42 Bilin = INTER_LINEAR, 43 Bilin = INTER_LINEAR,
43 Cubic = INTER_CUBIC, 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 private: 54 private:
47 Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) 55 Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false)
@@ -53,6 +61,7 @@ private: @@ -53,6 +61,7 @@ private:
53 Q_PROPERTY(float x3 READ get_x3 WRITE set_x3 RESET reset_x3 STORED false) 61 Q_PROPERTY(float x3 READ get_x3 WRITE set_x3 RESET reset_x3 STORED false)
54 Q_PROPERTY(float y3 READ get_y3 WRITE set_y3 RESET reset_y3 STORED false) 62 Q_PROPERTY(float y3 READ get_y3 WRITE set_y3 RESET reset_y3 STORED false)
55 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) 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 Q_PROPERTY(bool storeAffine READ get_storeAffine WRITE set_storeAffine RESET reset_storeAffine STORED false) 65 Q_PROPERTY(bool storeAffine READ get_storeAffine WRITE set_storeAffine RESET reset_storeAffine STORED false)
57 Q_PROPERTY(bool warpPoints READ get_warpPoints WRITE set_warpPoints RESET reset_warpPoints STORED false) 66 Q_PROPERTY(bool warpPoints READ get_warpPoints WRITE set_warpPoints RESET reset_warpPoints STORED false)
58 BR_PROPERTY(int, width, 64) 67 BR_PROPERTY(int, width, 64)
@@ -64,6 +73,7 @@ private: @@ -64,6 +73,7 @@ private:
64 BR_PROPERTY(float, x3, -1) 73 BR_PROPERTY(float, x3, -1)
65 BR_PROPERTY(float, y3, -1) 74 BR_PROPERTY(float, y3, -1)
66 BR_PROPERTY(Method, method, Bilin) 75 BR_PROPERTY(Method, method, Bilin)
  76 + BR_PROPERTY(BorderMode, borderMode, Constant)
67 BR_PROPERTY(bool, storeAffine, false) 77 BR_PROPERTY(bool, storeAffine, false)
68 BR_PROPERTY(bool, warpPoints, false) 78 BR_PROPERTY(bool, warpPoints, false)
69 79
@@ -106,7 +116,7 @@ private: @@ -106,7 +116,7 @@ private:
106 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]); 116 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]);
107 117
108 Mat affineTransform = getAffineTransform(srcPoints, dstPoints); 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 if (warpPoints) { 121 if (warpPoints) {
112 QList<QPointF> points = src.file.points(); 122 QList<QPointF> points = src.file.points();
openbr/plugins/imgproc/cropfromlandmarks.cpp
@@ -19,26 +19,33 @@ class CropFromLandmarksTransform : public UntrainableTransform @@ -19,26 +19,33 @@ class CropFromLandmarksTransform : public UntrainableTransform
19 Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false) 19 Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false)
20 Q_PROPERTY(float paddingHorizontal READ get_paddingHorizontal WRITE set_paddingHorizontal RESET reset_paddingHorizontal STORED false) 20 Q_PROPERTY(float paddingHorizontal READ get_paddingHorizontal WRITE set_paddingHorizontal RESET reset_paddingHorizontal STORED false)
21 Q_PROPERTY(float paddingVertical READ get_paddingVertical WRITE set_paddingVertical RESET reset_paddingVertical STORED false) 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 BR_PROPERTY(QList<int>, indices, QList<int>()) 23 BR_PROPERTY(QList<int>, indices, QList<int>())
23 BR_PROPERTY(float, paddingHorizontal, .1) 24 BR_PROPERTY(float, paddingHorizontal, .1)
24 BR_PROPERTY(float, paddingVertical, .1) 25 BR_PROPERTY(float, paddingVertical, .1)
  26 + BR_PROPERTY(bool, shiftPoints, false)
25 27
26 void project(const Template &src, Template &dst) const 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 int minX = src.m().cols - 1, 35 int minX = src.m().cols - 1,
29 maxX = 1, 36 maxX = 1,
30 minY = src.m().rows - 1, 37 minY = src.m().rows - 1,
31 maxY = 1; 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 int padW = qRound((maxX - minX) * (paddingHorizontal / 2)); 51 int padW = qRound((maxX - minX) * (paddingHorizontal / 2));
@@ -50,6 +57,13 @@ class CropFromLandmarksTransform : public UntrainableTransform @@ -50,6 +57,13 @@ class CropFromLandmarksTransform : public UntrainableTransform
50 if (rect.x() + rect.width() > src.m().cols) rect.setWidth(src.m().cols - rect.x()); 57 if (rect.x() + rect.width() > src.m().cols) rect.setWidth(src.m().cols - rect.x());
51 if (rect.y() + rect.width() > src.m().rows) rect.setHeight(src.m().rows - rect.y()); 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 dst = Mat(src, OpenCVUtils::toRect(rect)); 67 dst = Mat(src, OpenCVUtils::toRect(rect));
54 } 68 }
55 }; 69 };
openbr/plugins/imgproc/if.cpp
@@ -64,6 +64,19 @@ public: @@ -64,6 +64,19 @@ public:
64 dst = src; 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 BR_REGISTER(Transform, IfTransform) 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 #include <opencv2/imgproc/imgproc.hpp> 1 #include <opencv2/imgproc/imgproc.hpp>
2 #include <openbr/plugins/openbr_internal.h> 2 #include <openbr/plugins/openbr_internal.h>
  3 +#include <openbr/core/opencvutils.h>
3 4
4 using namespace cv; 5 using namespace cv;
5 6
@@ -35,7 +36,7 @@ private: @@ -35,7 +36,7 @@ private:
35 int top, bottom, left, right; 36 int top, bottom, left, right;
36 top = percent*src.m().rows; bottom = percent*src.m().rows; 37 top = percent*src.m().rows; bottom = percent*src.m().rows;
37 left = percent*src.m().cols; right = percent*src.m().cols; 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,8 +30,10 @@ namespace br
30 class QuantizeTransform : public Transform 30 class QuantizeTransform : public Transform
31 { 31 {
32 Q_OBJECT 32 Q_OBJECT
  33 + Q_PROPERTY(float c READ get_c WRITE set_c RESET reset_c STORED false)
33 Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a) 34 Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a)
34 Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b) 35 Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b)
  36 + BR_PROPERTY(float, c, 1)
35 BR_PROPERTY(float, a, 1) 37 BR_PROPERTY(float, a, 1)
36 BR_PROPERTY(float, b, 0) 38 BR_PROPERTY(float, b, 0)
37 39
@@ -46,7 +48,7 @@ class QuantizeTransform : public Transform @@ -46,7 +48,7 @@ class QuantizeTransform : public Transform
46 48
47 void project(const Template &src, Template &dst) const 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 QByteArray likely(const QByteArray &indentation) const 54 QByteArray likely(const QByteArray &indentation) const
openbr/plugins/imgproc/resize.cpp
@@ -48,21 +48,35 @@ private: @@ -48,21 +48,35 @@ private:
48 Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) 48 Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false)
49 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) 49 Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
50 Q_PROPERTY(bool preserveAspect READ get_preserveAspect WRITE set_preserveAspect RESET reset_preserveAspect STORED false) 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 BR_PROPERTY(int, rows, -1) 52 BR_PROPERTY(int, rows, -1)
52 BR_PROPERTY(int, columns, -1) 53 BR_PROPERTY(int, columns, -1)
53 BR_PROPERTY(Method, method, Bilin) 54 BR_PROPERTY(Method, method, Bilin)
54 BR_PROPERTY(bool, preserveAspect, false) 55 BR_PROPERTY(bool, preserveAspect, false)
  56 + BR_PROPERTY(bool, pad, true)
55 57
56 void project(const Template &src, Template &dst) const 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 if (!preserveAspect) { 65 if (!preserveAspect) {
59 resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method); 66 resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method);
60 const float rowScaleFactor = (float)rows/src.m().rows; 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 QList<QPointF> points = src.file.points(); 69 QList<QPointF> points = src.file.points();
63 for (int i=0; i<points.size(); i++) 70 for (int i=0; i<points.size(); i++)
64 points[i] = QPointF(points[i].x() * colScaleFactor,points[i].y() * rowScaleFactor); 71 points[i] = QPointF(points[i].x() * colScaleFactor,points[i].y() * rowScaleFactor);
65 dst.file.setPoints(points); 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 } else { 80 } else {
67 float inRatio = (float) src.m().rows / src.m().cols; 81 float inRatio = (float) src.m().rows / src.m().cols;
68 float outRatio = (float) rows / columns; 82 float outRatio = (float) rows / columns;
openbr/plugins/imgproc/rndtranslate.cpp
@@ -38,7 +38,7 @@ class RndTranslateTransform : public UntrainableMetaTransform @@ -38,7 +38,7 @@ class RndTranslateTransform : public UntrainableMetaTransform
38 Q_PROPERTY(int nStages READ get_nStages WRITE set_nStages RESET reset_nStages STORED false) 38 Q_PROPERTY(int nStages READ get_nStages WRITE set_nStages RESET reset_nStages STORED false)
39 BR_PROPERTY(int, nStages, 3) 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 qFatal("Shoult not be here (RndTranslate)"); 43 qFatal("Shoult not be here (RndTranslate)");
44 } 44 }
openbr/plugins/imgproc/slidingwindow.cpp
@@ -35,7 +35,7 @@ namespace br @@ -35,7 +35,7 @@ namespace br
35 * \br_property int minSize The smallest sized object to detect in pixels 35 * \br_property int minSize The smallest sized object to detect in pixels
36 * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size 36 * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size
37 * \br_property float scaleFactor The factor to scale the image by during each resize. 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 * \br_property float eps Parameter for non-maximum supression 39 * \br_property float eps Parameter for non-maximum supression
40 * \br_property int minNeighbors Parameter for non-maximum supression 40 * \br_property int minNeighbors Parameter for non-maximum supression
41 * \br_property bool group If false, non-maxima supression will not be performed 41 * \br_property bool group If false, non-maxima supression will not be performed
@@ -51,23 +51,28 @@ class SlidingWindowTransform : public MetaTransform @@ -51,23 +51,28 @@ class SlidingWindowTransform : public MetaTransform
51 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) 51 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
52 Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false) 52 Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
53 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) 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 Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) 55 Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false)
56 Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) 56 Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
57 Q_PROPERTY(bool group READ get_group WRITE set_group RESET reset_group STORED false) 57 Q_PROPERTY(bool group READ get_group WRITE set_group RESET reset_group STORED false)
58 Q_PROPERTY(int shrinkingFactor READ get_shrinkingFactor WRITE set_shrinkingFactor RESET reset_shrinkingFactor STORED false) 58 Q_PROPERTY(int shrinkingFactor READ get_shrinkingFactor WRITE set_shrinkingFactor RESET reset_shrinkingFactor STORED false)
59 Q_PROPERTY(bool clone READ get_clone WRITE set_clone RESET reset_clone STORED false) 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 BR_PROPERTY(br::Classifier*, classifier, NULL) 63 BR_PROPERTY(br::Classifier*, classifier, NULL)
62 BR_PROPERTY(int, minSize, 20) 64 BR_PROPERTY(int, minSize, 20)
63 BR_PROPERTY(int, maxSize, -1) 65 BR_PROPERTY(int, maxSize, -1)
64 BR_PROPERTY(float, scaleFactor, 1.2) 66 BR_PROPERTY(float, scaleFactor, 1.2)
65 - BR_PROPERTY(float, confidenceThreshold, 10) 67 + BR_PROPERTY(float, minGroupingConfidence, -std::numeric_limits<float>::max())
66 BR_PROPERTY(float, eps, 0.2) 68 BR_PROPERTY(float, eps, 0.2)
67 BR_PROPERTY(int, minNeighbors, 3) 69 BR_PROPERTY(int, minNeighbors, 3)
68 BR_PROPERTY(bool, group, true) 70 BR_PROPERTY(bool, group, true)
69 BR_PROPERTY(int, shrinkingFactor, 1) 71 BR_PROPERTY(int, shrinkingFactor, 1)
70 BR_PROPERTY(bool, clone, true) 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 void train(const TemplateList &data) 77 void train(const TemplateList &data)
73 { 78 {
@@ -85,9 +90,9 @@ class SlidingWindowTransform : public MetaTransform @@ -85,9 +90,9 @@ class SlidingWindowTransform : public MetaTransform
85 { 90 {
86 foreach (const Template &t, src) { 91 foreach (const Template &t, src) {
87 // As a special case, skip detection if the appropriate metadata already exists 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 Template u = t; 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 u.file.set("Confidence", t.file.get<float>("Confidence", 1)); 96 u.file.set("Confidence", t.file.get<float>("Confidence", 1));
92 dst.append(u); 97 dst.append(u);
93 continue; 98 continue;
@@ -106,6 +111,9 @@ class SlidingWindowTransform : public MetaTransform @@ -106,6 +111,9 @@ class SlidingWindowTransform : public MetaTransform
106 // different channels of the same image! 111 // different channels of the same image!
107 const Size imageSize = t.m().size(); 112 const Size imageSize = t.m().size();
108 const int minSize = t.file.get<int>("MinSize", this->minSize); 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 QList<Rect> rects; 117 QList<Rect> rects;
110 QList<float> confidences; 118 QList<float> confidences;
111 119
@@ -163,19 +171,43 @@ class SlidingWindowTransform : public MetaTransform @@ -163,19 +171,43 @@ class SlidingWindowTransform : public MetaTransform
163 } 171 }
164 172
165 if (group) 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 u.file.appendRect(rect); 210 u.file.appendRect(rect);
178 - u.file.set("Face", rect);  
179 dst.append(u); 211 dst.append(u);
180 } 212 }
181 } 213 }
openbr/plugins/io/write.cpp
@@ -10,6 +10,7 @@ namespace br @@ -10,6 +10,7 @@ namespace br
10 * \ingroup transforms 10 * \ingroup transforms
11 * \brief Write all mats to disk as images. 11 * \brief Write all mats to disk as images.
12 * \author Brendan Klare \cite bklare 12 * \author Brendan Klare \cite bklare
  13 + * \br_property bool preserveFilename Writes image to original filename.
13 * \br_property QString outputDirectory Top level directory to write images to. 14 * \br_property QString outputDirectory Top level directory to write images to.
14 * \br_property QString underscore 15 * \br_property QString underscore
15 * \br_property QString imgExtension Extension to save images as. 16 * \br_property QString imgExtension Extension to save images as.
@@ -19,11 +20,13 @@ namespace br @@ -19,11 +20,13 @@ namespace br
19 class WriteTransform : public TimeVaryingTransform 20 class WriteTransform : public TimeVaryingTransform
20 { 21 {
21 Q_OBJECT 22 Q_OBJECT
  23 + Q_PROPERTY(bool preserveFilename READ get_preserveFilename WRITE set_preserveFilename RESET reset_preserveFilename STORED false)
22 Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false) 24 Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false)
23 Q_PROPERTY(QString underscore READ get_underscore WRITE set_underscore RESET reset_underscore STORED false) 25 Q_PROPERTY(QString underscore READ get_underscore WRITE set_underscore RESET reset_underscore STORED false)
24 Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false) 26 Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false)
25 Q_PROPERTY(int padding READ get_padding WRITE set_padding RESET reset_padding STORED false) 27 Q_PROPERTY(int padding READ get_padding WRITE set_padding RESET reset_padding STORED false)
26 Q_PROPERTY(QString subDir READ get_subDir WRITE set_subDir RESET reset_subDir STORED false) 28 Q_PROPERTY(QString subDir READ get_subDir WRITE set_subDir RESET reset_subDir STORED false)
  29 + BR_PROPERTY(bool, preserveFilename, false)
27 BR_PROPERTY(QString, outputDirectory, "Temp") 30 BR_PROPERTY(QString, outputDirectory, "Temp")
28 BR_PROPERTY(QString, underscore, "") 31 BR_PROPERTY(QString, underscore, "")
29 BR_PROPERTY(QString, imgExtension, "jpg") 32 BR_PROPERTY(QString, imgExtension, "jpg")
@@ -46,11 +49,19 @@ class WriteTransform : public TimeVaryingTransform @@ -46,11 +49,19 @@ class WriteTransform : public TimeVaryingTransform
46 QString dir = src.file.get<QString>(subDir, "Temp"); 49 QString dir = src.file.get<QString>(subDir, "Temp");
47 QString path = QString("%1/%2/").arg(outputDirectory, dir); 50 QString path = QString("%1/%2/").arg(outputDirectory, dir);
48 int value = numImages.value(dir, 0); 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 numImages[dir] = ++value; 57 numImages[dir] = ++value;
51 OpenCVUtils::saveImage(dst.m(), path); 58 OpenCVUtils::saveImage(dst.m(), path);
52 } else { 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 OpenCVUtils::saveImage(dst.m(), path); 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 * Licensed under the Apache License, Version 2.0 (the "License"); * 4 * Licensed under the Apache License, Version 2.0 (the "License"); *
5 * you may not use this file except in compliance with the License. * 5 * you may not use this file except in compliance with the License. *
@@ -15,31 +15,28 @@ @@ -15,31 +15,28 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <openbr/plugins/openbr_internal.h> 17 #include <openbr/plugins/openbr_internal.h>
18 -#include <openbr/core/distance_sse.h>  
19 -  
20 -using namespace cv;  
21 18
22 namespace br 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 Q_OBJECT 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 } // namespace br 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,7 +21,7 @@ namespace br
21 21
22 /*! 22 /*!
23 * \ingroup transforms 23 * \ingroup transforms
24 - * \brief Clear Templates without the required metadata. 24 + * \brief Remove templates without the required metadata.
25 * \author Josh Klontz \cite jklontz 25 * \author Josh Klontz \cite jklontz
26 */ 26 */
27 class IfMetadataTransform : public UntrainableMetadataTransform 27 class IfMetadataTransform : public UntrainableMetadataTransform
@@ -34,8 +34,10 @@ class IfMetadataTransform : public UntrainableMetadataTransform @@ -34,8 +34,10 @@ class IfMetadataTransform : public UntrainableMetadataTransform
34 34
35 void projectMetadata(const File &src, File &dst) const 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,7 +21,7 @@ class RandomRectsTransform : public UntrainableMetaTransform
21 BR_PROPERTY(int, numRects, 135) 21 BR_PROPERTY(int, numRects, 135)
22 BR_PROPERTY(int, minSize, 24) 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 qFatal("NOT SUPPORTED"); 26 qFatal("NOT SUPPORTED");
27 } 27 }
openbr/plugins/metadata/randomtemplates.cpp
@@ -14,7 +14,7 @@ class RandomTemplatesTransform : public UntrainableMetaTransform @@ -14,7 +14,7 @@ class RandomTemplatesTransform : public UntrainableMetaTransform
14 Q_PROPERTY(float percent READ get_percent WRITE set_percent RESET reset_percent) 14 Q_PROPERTY(float percent READ get_percent WRITE set_percent RESET reset_percent)
15 BR_PROPERTY(float, percent, .01) 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 qFatal("Not supported in RandomTemplates."); 18 qFatal("Not supported in RandomTemplates.");
19 } 19 }
20 20
openbr/plugins/metadata/removefte.cpp
@@ -6,13 +6,14 @@ namespace br @@ -6,13 +6,14 @@ namespace br
6 /*! 6 /*!
7 * \ingroup transforms 7 * \ingroup transforms
8 * \author Brendan Klare \cite bklare 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 class RemoveFTETransform : public UntrainableMetaTransform 12 class RemoveFTETransform : public UntrainableMetaTransform
12 { 13 {
13 Q_OBJECT 14 Q_OBJECT
14 15
15 - void project(const Template &src, Template &dst) const 16 + void project(const Template &, Template &) const
16 { 17 {
17 qFatal("Not supported in RemoveFTE."); 18 qFatal("Not supported in RemoveFTE.");
18 } 19 }
openbr/plugins/metadata/savemat.cpp
@@ -59,7 +59,11 @@ class JustTransform : public UntrainableMetaTransform @@ -59,7 +59,11 @@ class JustTransform : public UntrainableMetaTransform
59 Template tmp; 59 Template tmp;
60 transform->project(src, tmp); 60 transform->project(src, tmp);
61 foreach (const QString &key, keys) 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,37 +40,51 @@ class HaarRepresentation : public Representation
40 void init() 40 void init()
41 { 41 {
42 if (features.isEmpty()) { 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 for (int x = 0; x < winWidth; x++) { 58 for (int x = 0; x < winWidth; x++) {
45 for (int y = 0; y < winHeight; y++) { 59 for (int y = 0; y < winHeight; y++) {
46 for (int dx = 1; dx <= winWidth; dx++) { 60 for (int dx = 1; dx <= winWidth; dx++) {
47 for (int dy = 1; dy <= winHeight; dy++) { 61 for (int dy = 1; dy <= winHeight; dy++) {
48 // haar_x2 62 // haar_x2
49 if ((x+dx*2 <= winWidth) && (y+dy <= winHeight)) 63 if ((x+dx*2 <= winWidth) && (y+dy <= winHeight))
50 - features.append(Feature(offset, 64 + features[index++] = Feature(offset,
51 x, y, dx*2, dy, -1, 65 x, y, dx*2, dy, -1,
52 - x+dx, y, dx , dy, +2)); 66 + x+dx, y, dx , dy, +2);
53 // haar_y2 67 // haar_y2
54 if ((x+dx <= winWidth) && (y+dy*2 <= winHeight)) 68 if ((x+dx <= winWidth) && (y+dy*2 <= winHeight))
55 - features.append(Feature(offset, 69 + features[index++] = Feature(offset,
56 x, y, dx, dy*2, -1, 70 x, y, dx, dy*2, -1,
57 - x, y+dy, dx, dy, +2)); 71 + x, y+dy, dx, dy, +2);
58 // haar_x3 72 // haar_x3
59 if ((x+dx*3 <= winWidth) && (y+dy <= winHeight)) 73 if ((x+dx*3 <= winWidth) && (y+dy <= winHeight))
60 - features.append(Feature(offset, 74 + features[index++] = Feature(offset,
61 x, y, dx*3, dy, -1, 75 x, y, dx*3, dy, -1,
62 - x+dx, y, dx , dy, +3)); 76 + x+dx, y, dx , dy, +3);
63 // haar_y3 77 // haar_y3
64 if ((x+dx <= winWidth) && (y+dy*3 <= winHeight)) 78 if ((x+dx <= winWidth) && (y+dy*3 <= winHeight))
65 - features.append(Feature(offset, 79 + features[index++] = Feature(offset,
66 x, y, dx, dy*3, -1, 80 x, y, dx, dy*3, -1,
67 - x, y+dy, dx, dy, +3)); 81 + x, y+dy, dx, dy, +3);
68 // x2_y2 82 // x2_y2
69 if ((x+dx*2 <= winWidth) && (y+dy*2 <= winHeight)) 83 if ((x+dx*2 <= winWidth) && (y+dy*2 <= winHeight))
70 - features.append(Feature(offset, 84 + features[index++] = Feature(offset,
71 x, y, dx*2, dy*2, -1, 85 x, y, dx*2, dy*2, -1,
72 x, y, dx, dy, +2, 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,7 +103,7 @@ class HaarRepresentation : public Representation
89 103
90 float evaluate(const Template &src, int idx) const 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 Mat evaluate(const Template &src, const QList<int> &indices) const 109 Mat evaluate(const Template &src, const QList<int> &indices) const
@@ -126,24 +140,23 @@ class HaarRepresentation : public Representation @@ -126,24 +140,23 @@ class HaarRepresentation : public Representation
126 float calc(const Mat &img) const; 140 float calc(const Mat &img) const;
127 141
128 struct { 142 struct {
129 - Rect r;  
130 float weight; 143 float weight;
131 - } rect[3];  
132 -  
133 - struct {  
134 int p0, p1, p2, p3; 144 int p0, p1, p2, p3;
135 } fastRect[3]; 145 } fastRect[3];
136 }; 146 };
137 147
138 - QList<Feature> features; 148 + QVector<Feature> features;
139 }; 149 };
140 150
141 BR_REGISTER(Representation, HaarRepresentation) 151 BR_REGISTER(Representation, HaarRepresentation)
142 152
143 HaarRepresentation::Feature::Feature() 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 HaarRepresentation::Feature::Feature(int offset, 162 HaarRepresentation::Feature::Feature(int offset,
@@ -151,38 +164,21 @@ HaarRepresentation::Feature::Feature(int offset, @@ -151,38 +164,21 @@ HaarRepresentation::Feature::Feature(int offset,
151 int x1, int y1, int w1, int h1, float wt1, 164 int x1, int y1, int w1, int h1, float wt1,
152 int x2, int y2, int w2, int h2, float wt2) 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 inline float HaarRepresentation::Feature::calc(const Mat &img) const 175 inline float HaarRepresentation::Feature::calc(const Mat &img) const
180 { 176 {
181 const int* ptr = img.ptr<int>(); 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 return ret; 182 return ret;
187 } 183 }
188 184
scripts/brpy/__init__.py
@@ -53,6 +53,10 @@ def _handle_string_func(func, *moretypes): @@ -53,6 +53,10 @@ def _handle_string_func(func, *moretypes):
53 return msg 53 return msg
54 return call_func 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 def init_brpy(br_loc='/usr/local/lib'): 60 def init_brpy(br_loc='/usr/local/lib'):
57 """Initializes all function inputs and outputs for the br ctypes lib object""" 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,24 +8,57 @@ and host them as long as the relative paths remain the same on ya serva.
8 8
9 from PIL import Image 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 def crop_to_bb(x, y, width, height, imname, maxheight=None): 40 def crop_to_bb(x, y, width, height, imname, maxheight=None):
12 ''' 41 '''
13 Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels. 42 Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels.
14 A maxheight of None will keep the original size (default). 43 A maxheight of None will keep the original size (default).
15 When two crops are put next to each other, they will be inline. To make each crop its own line, wrap it in a div. 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 img = Image.open(imname) 51 img = Image.open(imname)
18 imwidth, imheight = img.size 52 imwidth, imheight = img.size
19 if not maxheight: 53 if not maxheight:
20 maxheight = height 54 maxheight = height
21 - ratio = maxheight / height 55 + ratio = float(maxheight) / height
22 # note for future me: 56 # note for future me:
23 # image is cropped with div width/height + overflow:hidden, 57 # image is cropped with div width/height + overflow:hidden,
24 # resized with img height, 58 # resized with img height,
25 # and positioned with img margin 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 html += '<img src="%s" style="height:%ipx; margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio) 61 html += '<img src="%s" style="height:%ipx; margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio)
28 - html += '</div>'  
29 return html 62 return html
30 63
31 def bbs_for_image(imname, bbs, maxheight=None, colors=None): 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,7 +70,7 @@ def bbs_for_image(imname, bbs, maxheight=None, colors=None):
37 imwidth, imheight = img.size 70 imwidth, imheight = img.size
38 if not maxheight: 71 if not maxheight:
39 maxheight = imheight 72 maxheight = imheight
40 - ratio = maxheight/imheight 73 + ratio = float(maxheight)/imheight
41 html = [ 74 html = [
42 '<div style="position:relative">', 75 '<div style="position:relative">',
43 '<img src="%s" style="height:%ipx" />' % (imname, maxheight) 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,10 +2,8 @@ set(BR_INSTALL_DEPENDENCIES OFF CACHE BOOL &quot;Install runtime dependencies.&quot;)
2 2
3 # OpenCV Libs 3 # OpenCV Libs
4 function(install_opencv_library lib) 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 if(${CMAKE_BUILD_TYPE} MATCHES Debug) 7 if(${CMAKE_BUILD_TYPE} MATCHES Debug)
10 set(BR_INSTALL_DEPENDENCIES_SUFFIX "d") 8 set(BR_INSTALL_DEPENDENCIES_SUFFIX "d")
11 endif() 9 endif()
@@ -65,7 +63,10 @@ endfunction() @@ -65,7 +63,10 @@ endfunction()
65 function(install_qt_imageformats) 63 function(install_qt_imageformats)
66 if(${BR_INSTALL_DEPENDENCIES}) 64 if(${BR_INSTALL_DEPENDENCIES})
67 set(IMAGE_FORMATS_DIR "${_qt5Core_install_prefix}/plugins/imageformats") 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 set(INSTALL_DEPENDENCIES_PREFIX "") 70 set(INSTALL_DEPENDENCIES_PREFIX "")
70 set(INSTALL_DEPENDENCIES_EXTENSION ".dll") 71 set(INSTALL_DEPENDENCIES_EXTENSION ".dll")
71 elseif(CMAKE_HOST_APPLE) 72 elseif(CMAKE_HOST_APPLE)
@@ -118,6 +119,11 @@ function(install_qt_misc) @@ -118,6 +119,11 @@ function(install_qt_misc)
118 file(GLOB d3dcomp ${_qt5Core_install_prefix}/bin/d3dcompiler_*.dll) 119 file(GLOB d3dcomp ${_qt5Core_install_prefix}/bin/d3dcompiler_*.dll)
119 install(FILES ${d3dcomp} DESTINATION bin) 120 install(FILES ${d3dcomp} DESTINATION bin)
120 install(FILES ${_qt5Core_install_prefix}/plugins/platforms/qwindows${BR_INSTALL_DEPENDENCIES_SUFFIX}.dll DESTINATION bin/platforms) 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 endif() 127 endif()
122 endfunction() 128 endfunction()
123 129
share/openbr/plotting/plot_utils.R
@@ -216,13 +216,13 @@ formatData &lt;- function(type=&quot;eval&quot;) { @@ -216,13 +216,13 @@ formatData &lt;- function(type=&quot;eval&quot;) {
216 Box$X <<- factor(Box$X, levels = Box$X, ordered = TRUE) 216 Box$X <<- factor(Box$X, levels = Box$X, ordered = TRUE)
217 Sample <<- data[grep("Sample",data$Plot),-c(1)] 217 Sample <<- data[grep("Sample",data$Plot),-c(1)]
218 Sample$X <<- as.character(Sample$X) 218 Sample$X <<- as.character(Sample$X)
  219 + displaySample <<- readImageData(Sample)
  220 + rows <<- displaySample[[1]]$value
219 EXT <<- data[grep("EXT",data$Plot),-c(1)] 221 EXT <<- data[grep("EXT",data$Plot),-c(1)]
220 EXT$X <<- as.character(EXT$X) 222 EXT$X <<- as.character(EXT$X)
221 EXP <<- data[grep("EXP",data$Plot),-c(1)] 223 EXP <<- data[grep("EXP",data$Plot),-c(1)]
222 EXP$X <<- as.character(EXP$X) 224 EXP$X <<- as.character(EXP$X)
223 NormLength <<- data[grep("NormLength",data$Plot),-c(1)] 225 NormLength <<- data[grep("NormLength",data$Plot),-c(1)]
224 - sample <<- readImageData(Sample)  
225 - rows <<- sample[[1]]$value  
226 } else if (type == "knn") { 226 } else if (type == "knn") {
227 # Split data into individual plots 227 # Split data into individual plots
228 IET <<- data[grep("IET",data$Plot),-c(1)] 228 IET <<- data[grep("IET",data$Plot),-c(1)]
@@ -325,14 +325,15 @@ plotEERSamples &lt;- function(imData=NULL, gmData=NULL) { @@ -325,14 +325,15 @@ plotEERSamples &lt;- function(imData=NULL, gmData=NULL) {
325 printImages(gmData, "Genuine") 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 if (nrow(EXT) != 0 && nrow(EXP)) { 331 if (nrow(EXT) != 0 && nrow(EXP)) {
331 for (j in 1:length(algs)) { 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 for (i in 1:length(predictedSample)) { 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,20 +342,20 @@ plotLandmarkSamples &lt;- function(samples=NULL, expData=NULL, extData=NULL) {
341 readImageData <- function(data) { 342 readImageData <- function(data) {
342 examples <- list() 343 examples <- list()
343 for (i in 1:nrow(data)) { 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 value <- data[i,2] 347 value <- data[i,2]
346 - file <- unlist(strsplit(path, "[.]"))[1]  
347 - ext <- unlist(strsplit(path, "[.]"))[2] 348 + ext <- unlist(strsplit(examplePath, "[.]"))[2]
348 if (ext == "jpg" || ext == "JPEG" || ext == "jpeg" || ext == "JPG") { 349 if (ext == "jpg" || ext == "JPEG" || ext == "jpeg" || ext == "JPG") {
349 - img <- readJPEG(path) 350 + img <- readJPEG(examplePath)
350 } else if (ext == "PNG" || ext == "png") { 351 } else if (ext == "PNG" || ext == "png") {
351 - img <- readPNG(path) 352 + img <- readPNG(examplePath)
352 } else if (ext == "TIFF" || ext == "tiff" || ext == "TIF" || ext == "tif") { 353 } else if (ext == "TIFF" || ext == "tiff" || ext == "TIF" || ext == "tif") {
353 - img <- readTIFF(path) 354 + img <- readTIFF(examplePath)
354 }else { 355 }else {
355 next 356 next
356 } 357 }
357 - example <- list(file = file, value = value, image = img) 358 + example <- list(path = path, value = value, image = img)
358 examples[[i]] <- example 359 examples[[i]] <- example
359 } 360 }
360 return(examples) 361 return(examples)