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