Commit 2453671339ad4b60282cea9a8f2f030d4e636e10
Merge branch 'master' of github.com:biometrics/openbr
Showing
44 changed files
with
856 additions
and
605 deletions
.gitignore
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 &predictedGallery, const QString &truthGal | @@ -752,298 +755,6 @@ void EvalClassification(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGallery | @@ -1053,51 +764,13 @@ float EvalDetection(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGallery | @@ -1117,11 +790,27 @@ float EvalDetection(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGalle | @@ -1209,7 +898,7 @@ float EvalLandmarking(const QString &predictedGallery, const QString &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 &predictedGallery, const QString &truthGalle | @@ -1217,26 +906,26 @@ float EvalLandmarking(const QString &predictedGallery, const QString &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<Rect> &rects, QList<float> &confidences, float con | @@ -448,8 +448,9 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &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<Rect> &rects, QList<float> &confidences, float con | @@ -459,18 +460,26 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &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<Rect> &rects, QList<float> &confidences, float con | @@ -480,7 +489,7 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &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<Rect> &rects, QList<float> &confidences, float con | @@ -501,21 +510,19 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &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<Rect> &rects, QList<float> &confidences, float con | @@ -523,11 +530,49 @@ void OpenCVUtils::group(QList<Rect> &rects, QList<float> &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 &src, br::Template &dst, int degrees | @@ -579,7 +624,7 @@ void OpenCVUtils::rotate(const br::Template &src, br::Template &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 &files, const File &destination, bool sho | @@ -318,9 +318,9 @@ bool PlotLandmarking(const QStringList &files, const File &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 &files, const File &destination, bool sho | @@ -330,9 +330,6 @@ bool PlotLandmarking(const QStringList &files, const File &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 &sdkPath) | @@ -1210,7 +1210,8 @@ bool br::Context::checkSDKPath(const QString &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 &argc, char *argv[], QString sdkPath, bool useG | @@ -1230,7 +1231,6 @@ void br::Context::initialize(int &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 &argc, char *argv[], QString sdkPath, bool useG | @@ -1245,6 +1245,7 @@ void br::Context::initialize(int &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 &argc, char *argv[], QString sdkPath, bool useG | @@ -1252,6 +1253,8 @@ void br::Context::initialize(int &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
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
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 "Install runtime dependencies.") | @@ -2,10 +2,8 @@ set(BR_INSTALL_DEPENDENCIES OFF CACHE BOOL "Install runtime dependencies.") | ||
| 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 <- function(type="eval") { | @@ -216,13 +216,13 @@ formatData <- function(type="eval") { | ||
| 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 <- function(imData=NULL, gmData=NULL) { | @@ -325,14 +325,15 @@ plotEERSamples <- 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 <- function(samples=NULL, expData=NULL, extData=NULL) { | @@ -341,20 +342,20 @@ plotLandmarkSamples <- 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) |