Commit 6b5c39957ab3caf12c9b7c0aa6594f843ef1de9d

Authored by bklare
2 parents 50f1dd39 c73a5623

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

Showing 48 changed files with 635 additions and 560 deletions
3rdparty/stasm/stasm/CMakeLists.txt deleted
1   -# Stasm CMakeLists
2   -
3   -aux_source_directory(stasm/src STASM_SRC)
4   -include_directories(stasm/include)
5   -
6   -#aux_source_directory(tasm/src TASM_SRC)
7   -#include_directories(tasm/include)
8   -
9   -add_library(stasm SHARED ${STASM_SRC} ${TASM_SRC})
10   -qt5_use_modules(stasm ${QT_DEPENDENCIES})
11   -
12   -set_target_properties(stasm PROPERTIES
13   - DEFINE_SYMBOL STASM_LIBRARY
14   - VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}
15   - SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}
16   - LINK_INTERFACE_LIBRARIES ""
17   - AUTOMOC TRUE)
18   -
19   -target_link_libraries(stasm ${OpenCV_LIBS} ${Qt5Core_QTMAIN_LIBRARIES})
20   -
21   -file(GLOB STASM_HEADERS stasm/include/*.h)
22   -install(FILES ${STASM_HEADERS} DESTINATION include/stasm)
23   -file(GLOB CLASSIC_HEADERS stasm/include/classic/*.mh)
24   -install(FILES ${CLASSIC_HEADERS} DESTINATION include/stasm/classic)
25   -file(GLOB HAT_HEADERS stasm/include/hat/*.mh)
26   -install(FILES ${HAT_HEADERS} DESTINATION include/stasm/hat)
27   -#file(GLOB TASM_HEADERS tasm/include/*.h)
28   -#install(FILES ${TASM_HEADERS} DESTINATION include/tasm)
29   -
30   -install(TARGETS stasm RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
3rdparty/stasm/stasm/stasm/src/print.cpp deleted
1   -// print.cpp: printing and logging utilities for the Stasm library
2   -//
3   -// Copyright (C) 2005-2013, Stephen Milborrow
4   -
5   -#include "print.h"
6   -#include "err.h"
7   -#include "misc.h"
8   -
9   -#include <stdio.h>
10   -#include <sys/stat.h>
11   -#include <stdarg.h>
12   -
13   -namespace stasm
14   -{
15   -bool print_g; // true to allow output to stdout (but error msgs always printed)
16   -
17   -bool trace_g; // true to trace Stasm internal operation
18   -
19   -static FILE* logfile_g; // lprintfs go to this log file as well as stdout
20   -
21   -//-----------------------------------------------------------------------------
22   -
23   -// Open the log file. After this, when you call lprintf, you print to the log
24   -// file (as well as to stdout). This inits the global variable logfile_g.
25   -
26   -void OpenLogFile( // also inits the global variable logfile_g
27   - const char* path) // in: log file path, default is "stasm.log"
28   -{
29   - if (!logfile_g)
30   - {
31   - if (print_g)
32   - printf("Generating %s\n", path);
33   - logfile_g = fopen(path, "wb");
34   - if (!logfile_g)
35   - Err("Cannot open \"%s\"", path);
36   - // check that we can write to the log file
37   - if (fputs("log file\n", logfile_g) < 0)
38   - Err("Cannot write to \"%s\"", path);
39   - rewind(logfile_g); // rewind so above test msg is not in the log file
40   - }
41   -}
42   -
43   -// Like printf but only prints if print_g flag is set.
44   -// Also prints to the log file if it is open (regardless of print_g).
45   -
46   -void lprintf(const char* format, ...) // args like printf
47   -{
48   - char s[SBIG];
49   - va_list args;
50   - va_start(args, format);
51   - VSPRINTF(s, format, args);
52   - va_end(args);
53   - if (print_g)
54   - {
55   - printf("%s", s);
56   - fflush(stdout); // flush so if there is a crash we can see what happened
57   - }
58   - if (logfile_g)
59   - {
60   - // we don't check fputs here, to prevent recursive calls and msgs
61   - fputs(s, logfile_g);
62   - fflush(logfile_g);
63   - }
64   -}
65   -
66   -// Like printf but prints to the log file only (and not to stdout).
67   -// Used for detailed stuff that we don't usually want to see.
68   -
69   -void logprintf(const char* format, ...) // args like printf
70   -{
71   - if (logfile_g)
72   - {
73   - char s[SBIG];
74   - va_list args;
75   - va_start(args, format);
76   - VSPRINTF(s, format, args);
77   - va_end(args);
78   - // we don't check fputs here, to prevent recursive calls and msgs
79   - fputs(s, logfile_g);
80   - fflush(logfile_g);
81   - }
82   -}
83   -
84   -// Like lprintf but always prints even if print_g is false.
85   -
86   -void lprintf_always(const char* format, ...)
87   -{
88   - char s[SBIG];
89   - va_list args;
90   - va_start(args, format);
91   - VSPRINTF(s, format, args);
92   - va_end(args);
93   - printf("%s", s);
94   - fflush(stdout); // flush so if there is a crash we can see what happened
95   - if (logfile_g)
96   - {
97   - // we don't check fputs here, to prevent recursive calls and msgs
98   - fputs(s, logfile_g);
99   - fflush(logfile_g);
100   - }
101   -}
102   -
103   -// Like puts but prints to the log file as well if it is open,
104   -// and does not append a newline.
105   -
106   -void lputs(const char* s)
107   -{
108   - printf("%s", s);
109   - fflush(stdout); // flush so if there is a crash we can see what happened
110   - logprintf("%s", s);
111   -}
112   -
113   -// Print message only once on the screen, and only 100 times to the log file.
114   -// This is used when similar messages could be printed many times and it
115   -// suffices to let the user know just once. By convention the message is
116   -// printed followed by "..." so the user knows that just the first message
117   -// was printed. The user can look in the log file for further messages if
118   -// necessary (but we print only 100 times to the log file --- else all the
119   -// log prints make tasm slow).
120   -
121   -void PrintOnce(
122   - int& printed, // io: zero=print, nonzero=no print
123   - const char* format, ...) // in: args like printf
124   -{
125   - char s[SBIG];
126   - va_list args;
127   - va_start(args, format);
128   - VSPRINTF(s, format, args);
129   - va_end(args);
130   - if (printed == 0 && print_g)
131   - {
132   - printed = 1;
133   - printf("%s", s);
134   - fflush(stdout); // flush so if there is a crash we can see what happened
135   - }
136   - if (printed < 100 && logfile_g)
137   - {
138   - fputs(s, logfile_g);
139   - fflush(logfile_g);
140   - printed++;
141   - if (printed == 100)
142   - logprintf("no more prints of the above message (printed == 100)\n");
143   - }
144   -}
145   -
146   -} // namespace stasm
3rdparty/stasm4.0.0/stasm/MOD_1/facedet.cpp
... ... @@ -47,28 +47,16 @@ void DetectFaces( // all face rects into detpars
47 47 int minwidth,
48 48 cv::CascadeClassifier cascade) // in: as percent of img width
49 49 {
50   - int leftborder = 0, topborder = 0; // border size in pixels
51   - Image bordered_img(BORDER_FRAC == 0?
52   - img: EnborderImg(leftborder, topborder, img));
53   -
54   - // Detection results are very slightly better with equalization
55   - // (tested on the MUCT images, which are not pre-equalized), and
56   - // it's quick enough to equalize (roughly 10ms on a 1.6 GHz laptop).
57   -
58   - Image equalized_img; cv::equalizeHist(bordered_img, equalized_img);
59   -
60 50 CV_Assert(minwidth >= 1 && minwidth <= 100);
61 51  
62   - int minpix = MAX(100, cvRound(img.cols * minwidth / 100.));
63   -
64 52 // the params below are accurate but slow
65   - static const double SCALE_FACTOR = 1.1;
66   - static const int MIN_NEIGHBORS = 3;
  53 + static const double SCALE_FACTOR = 1.2;
  54 + static const int MIN_NEIGHBORS = 5;
67 55 static const int DETECTOR_FLAGS = 0;
68 56  
69 57 vec_Rect facerects = // all face rects in image
70   - Detect(equalized_img, &cascade, NULL,
71   - SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, minpix);
  58 + Detect(img, &cascade, NULL,
  59 + SCALE_FACTOR, MIN_NEIGHBORS, DETECTOR_FLAGS, 64);
72 60  
73 61 // copy face rects into the detpars vector
74 62  
... ... @@ -80,8 +68,6 @@ void DetectFaces( // all face rects into detpars
80 68 // detpar.x and detpar.y is the center of the face rectangle
81 69 detpar.x = facerect->x + facerect->width / 2.;
82 70 detpar.y = facerect->y + facerect->height / 2.;
83   - detpar.x -= leftborder; // discount the border we added earlier
84   - detpar.y -= topborder;
85 71 detpar.width = double(facerect->width);
86 72 detpar.height = double(facerect->height);
87 73 detpar.yaw = 0; // assume face has no yaw in this version of Stasm
... ... @@ -187,10 +173,10 @@ void FaceDet::DetectFaces_( // call once per image to find all the faces
187 173 //CV_Assert(!facedet_g.empty()); // check that OpenFaceDetector_ was called
188 174  
189 175 DetectFaces(detpars_, img, minwidth, cascade);
190   - DiscardMissizedFaces(detpars_);
  176 + //DiscardMissizedFaces(detpars_);
191 177 if (multiface) // order faces on increasing distance from left margin
192 178 {
193   - sort(detpars_.begin(), detpars_.end(), IncreasingLeftMargin);
  179 + sort(detpars_.begin(), detpars_.end(), DecreasingWidth);
194 180 }
195 181 else
196 182 {
... ...
3rdparty/stasm4.0.0/stasm/MOD_1/facedet.h
... ... @@ -38,13 +38,11 @@ public:
38 38  
39 39 FaceDet() {} // constructor
40 40  
41   -
42   -private:
43   - vector<DetPar> detpars_; // all the valid faces in the current image
44   -
45 41 int iface_; // index of current face for NextFace_
46 42 // indexes into detpars_
  43 + vector<DetPar> detpars_; // all the valid faces in the current image
47 44  
  45 +private:
48 46 DISALLOW_COPY_AND_ASSIGN(FaceDet);
49 47  
50 48 }; // end class FaceDet
... ...
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
... ... @@ -160,7 +160,8 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
160 160 const char* data,
161 161 const int width,
162 162 const int height,
163   - StasmCascadeClassifier cascade)
  163 + StasmCascadeClassifier cascade,
  164 + FaceDet &detection)
164 165 {
165 166 int returnval = 1; // assume success
166 167 *foundface = 0; // but assume no face found
... ... @@ -176,15 +177,14 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
176 177 // Allocate image
177 178 Image img = Image(height, width,(unsigned char*)data);
178 179  
179   - FaceDet facedet;
180   -
181 180 // call the face detector to detect the face rectangle(s)
182   - facedet.DetectFaces_(img, NULL, false, 10, NULL, cascade.faceCascade);
  181 + if (detection.detpars_.empty())
  182 + detection.DetectFaces_(img, NULL, true, 10, NULL, cascade.faceCascade);
183 183  
184 184 // Get the start shape for the next face in the image, and the ROI around it.
185 185 // The shape will be wrt the ROI frame.
186 186 if (NextStartShapeAndRoi(shape, face_roi, detpar_roi, detpar,
187   - img, mods_g, facedet, cascade))
  187 + img, mods_g, detection, cascade))
188 188 {
189 189 // now working with maybe flipped ROI and start shape in ROI frame
190 190 *foundface = 1;
... ... @@ -219,9 +219,10 @@ int stasm_search_auto(// call repeatedly to find all faces
219 219 const char *data,
220 220 const int width,
221 221 const int height,
222   - StasmCascadeClassifier cascade)
  222 + StasmCascadeClassifier cascade,
  223 + FaceDet &detection)
223 224 {
224   - return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade);
  225 + return stasm_search_auto_ext(foundface, landmarks, NULL, data, width, height, cascade, detection);
225 226 }
226 227  
227 228 int stasm_search_single( // wrapper for stasm_search_auto and friends
... ... @@ -237,7 +238,8 @@ int stasm_search_single( // wrapper for stasm_search_auto and friends
237 238 (void) datadir;
238 239 (void) imgpath;
239 240  
240   - return stasm_search_auto(foundface, landmarks, img, width, height, cascade);
  241 + FaceDet detection;
  242 + return stasm_search_auto(foundface, landmarks, img, width, height, cascade, detection);
241 243 }
242 244  
243 245 int stasm_search_pinned( // call after the user has pinned some points
... ...
3rdparty/stasm4.0.0/stasm/stasm_lib.h
... ... @@ -64,6 +64,10 @@
64 64  
65 65 #include "stasmcascadeclassifier.h"
66 66  
  67 +namespace stasm {
  68 + class FaceDet;
  69 +}
  70 +
67 71 static const int stasm_NLANDMARKS = 77; // number of landmarks
68 72  
69 73 extern const char* const stasm_VERSION;
... ... @@ -89,7 +93,8 @@ int stasm_search_auto( // call repeatedly to find all faces
89 93 const char* data,
90 94 const int width,
91 95 const int height,
92   - StasmCascadeClassifier cascade);
  96 + StasmCascadeClassifier cascade,
  97 + stasm::FaceDet &detection);
93 98  
94 99 extern "C"
95 100 int stasm_search_single( // wrapper for stasm_search_auto and friends
... ...
CMakeLists.txt
... ... @@ -13,7 +13,7 @@ if(NOT DEFINED CPACK_PACKAGE_VERSION_MAJOR)
13 13 set(CPACK_PACKAGE_VERSION_MAJOR 1)
14 14 endif()
15 15 if(NOT DEFINED CPACK_PACKAGE_VERSION_MINOR)
16   - set(CPACK_PACKAGE_VERSION_MINOR 0)
  16 + set(CPACK_PACKAGE_VERSION_MINOR 1)
17 17 endif()
18 18 if(NOT DEFINED CPACK_PACKAGE_VERSION_PATCH)
19 19 set(CPACK_PACKAGE_VERSION_PATCH 0)
... ... @@ -158,8 +158,8 @@ endif()
158 158  
159 159 # Download the models
160 160 ExternalProject_Add(models
161   - URL http://github.com/biometrics/openbr/releases/download/v1.0.0/models.tar.gz
162   - URL_MD5 0a7c79226d6629954aa32c835a1007b9
  161 + URL http://github.com/biometrics/openbr/releases/download/v1.1.0/models.tar.gz
  162 + URL_MD5 26cf71abd48cec8d7b2abf3e8f4ebfc4
163 163 SOURCE_DIR "${PROJECT_SOURCE_DIR}/share/openbr/models"
164 164 CONFIGURE_COMMAND ""
165 165 BUILD_COMMAND ""
... ...
app/br/br.cpp
... ... @@ -169,8 +169,8 @@ public:
169 169 check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'.");
170 170 br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : "");
171 171 } else if (!strcmp(fun, "evalKNN")) {
172   - check(parc == 3, "Incorrect parameter count for 'evalKNN'.");
173   - br_eval_knn(parv[0], parv[1], parv[2]);
  172 + check(parc >= 2 && parc <= 3, "Incorrect parameter count for 'evalKNN'.");
  173 + br_eval_knn(parv[0], parv[1], parc > 2 ? parv[2] : "");
174 174 } else if (!strcmp(fun, "pairwiseCompare")) {
175 175 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'pairwiseCompare'.");
176 176 br_pairwise_compare(parv[0], parv[1], parc == 3 ? parv[2] : "");
... ... @@ -186,6 +186,9 @@ public:
186 186 } else if (!strcmp(fun, "plotMetadata")) {
187 187 check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
188 188 br_plot_metadata(parc-1, parv, parv[parc-1], true);
  189 + } else if (!strcmp(fun, "plotKNN")) {
  190 + check(parc >=2, "Incorrect parameter count for 'plotKNN'.");
  191 + br_plot_knn(parc-1, parv, parv[parc-1], true);
189 192 } else if (!strcmp(fun, "project")) {
190 193 check(parc == 2, "Insufficient parameter count for 'project'.");
191 194 br_project(parv[0], parv[1]);
... ... @@ -279,13 +282,14 @@ private:
279 282 "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}] [{maxSize}]\n"
280 283 "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n"
281 284 "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n"
282   - "-evalKNN <knn_graph> <knn_truth> [{iet_file}]\n"
  285 + "-evalKNN <knn_graph> <knn_truth> [{csv}]\n"
283 286 "-pairwiseCompare <target_gallery> <query_gallery> [{output}]\n"
284 287 "-inplaceEval <simmat> <target> <query> [{csv}]\n"
285 288 "-assertEval <simmat> <mask> <accuracy>\n"
286 289 "-plotDetection <file> ... <file> {destination}\n"
287 290 "-plotLandmarking <file> ... <file> {destination}\n"
288 291 "-plotMetadata <file> ... <file> <columns>\n"
  292 + "-plotKNN <file> ... <file> {destination}\n"
289 293 "-project <input_gallery> {output_gallery}\n"
290 294 "-deduplicate <input_gallery> <output_gallery> <threshold>\n"
291 295 "-likely <input_type> <output_type> <output_likely_source>\n"
... ...
docs/docs/api_docs/cl_api.md
... ... @@ -59,6 +59,17 @@ DOCUMENT ME
59 59  
60 60 * **wraps:** [br_pairwise_compare](c_api/functions.md#br_pairwise_compare)
61 61  
  62 +### -crossValidate {: #crossvalidate }
  63 +
  64 +Either performs n fold cross validation (if nFolds > 0), or a single iteration of a train / test split with the first of the n folds as the test partition (nFolds < 0).
  65 +
  66 +* **arguments:**
  67 +
  68 + -crossValidate <nFolds> -algorithm "CrossValidate(description=$ALG,randomSeed=5):CrossValidate+Dist" -train <inputFile> <model>
  69 +
  70 + -crossValidate <nFolds> -algorithm <model> -enroll <inputFile> input.gal -compare input.gal . out.eval
  71 +
  72 +
62 73 ### -eval {: #eval }
63 74  
64 75 Evaluate a similarity matrix
... ...
docs/docs/install.md
... ... @@ -35,7 +35,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
35 35  
36 36 $ git clone https://github.com/biometrics/openbr.git
37 37 $ cd openbr
38   - $ git checkout 1.0
  38 + $ git checkout v1.1.0
39 39 $ git submodule init
40 40 $ git submodule update
41 41  
... ... @@ -116,7 +116,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
116 116  
117 117 $ git clone https://github.com/biometrics/openbr.git
118 118 $ cd openbr
119   - $ git checkout 1.0
  119 + $ git checkout v1.1.0
120 120 $ git submodule init
121 121 $ git submodule update
122 122  
... ... @@ -196,7 +196,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
196 196 $ cd /c
197 197 $ git clone https://github.com/biometrics/openbr.git
198 198 $ cd openbr
199   - $ git checkout 1.0
  199 + $ git checkout v1.1.0
200 200 $ git submodule init
201 201 $ git submodule update
202 202  
... ... @@ -277,7 +277,7 @@ A hacker&#39;s guide to building, editing, and running OpenBR.
277 277  
278 278 $ git clone https://github.com/biometrics/openbr.git
279 279 $ cd openbr
280   - $ git checkout 1.0
  280 + $ git checkout v1.1.0
281 281 $ git submodule init
282 282 $ git submodule update
283 283  
... ...
openbr/core/boost.cpp
... ... @@ -131,23 +131,16 @@ static CvMat* cvPreprocessIndexArray( const CvMat* idx_arr, int data_arr_size, b
131 131  
132 132 //------------------------------------- FeatureEvaluator ---------------------------------------
133 133  
134   -void FeatureEvaluator::init(Representation *_representation, int _maxSampleCount, int channels)
  134 +void FeatureEvaluator::init(Representation *_representation, int _maxSampleCount)
135 135 {
136 136 representation = _representation;
137   -
138   - int dx, dy;
139   - Size windowSize = representation->windowSize(&dx, &dy);
140   - data.create((int)_maxSampleCount, (windowSize.width + dx) * (windowSize.height + dy), CV_32SC(channels));
141 137 cls.create( (int)_maxSampleCount, 1, CV_32FC1 );
142 138 }
143 139  
144   -void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)
  140 +void FeatureEvaluator::setImage(const Template &src, uchar clsLabel, int idx)
145 141 {
146 142 cls.ptr<float>(idx)[0] = clsLabel;
147   -
148   - Mat pp;
149   - representation->preprocess(img, pp);
150   - pp.reshape(0, 1).copyTo(data.row(idx));
  143 + data.append(representation->preprocess(src));
151 144 }
152 145  
153 146 //----------------------------- CascadeBoostParams -------------------------------------------------
... ...
openbr/core/boost.h
... ... @@ -2,7 +2,6 @@
2 2 #define _BOOST_H_
3 3  
4 4 #include "ml.h"
5   -#include <time.h>
6 5 #include <openbr/openbr_plugin.h>
7 6  
8 7 #ifdef _WIN32
... ... @@ -17,9 +16,9 @@ namespace br
17 16 struct FeatureEvaluator
18 17 {
19 18 ~FeatureEvaluator() {}
20   - void init(Representation *_representation, int _maxSampleCount, int channels);
21   - void setImage(const cv::Mat& img, uchar clsLabel, int idx);
22   - float operator()(int featureIdx, int sampleIdx) const { return representation->evaluate(data.row(sampleIdx), featureIdx); }
  19 + void init(Representation *_representation, int _maxSampleCount);
  20 + void setImage(const Template &src, uchar clsLabel, int idx);
  21 + float operator()(int featureIdx, int sampleIdx) const { return representation->evaluate(data[sampleIdx], featureIdx); }
23 22  
24 23 int getNumFeatures() const { return representation->numFeatures(); }
25 24 int getMaxCatCount() const { return representation->maxCatCount(); }
... ... @@ -27,7 +26,8 @@ struct FeatureEvaluator
27 26 const cv::Mat& getCls() const { return cls; }
28 27 float getCls(int si) const { return cls.at<float>(si, 0); }
29 28  
30   - cv::Mat data, cls;
  29 + cv::Mat cls;
  30 + TemplateList data;
31 31 Representation *representation;
32 32 };
33 33  
... ...
openbr/core/eval.cpp
... ... @@ -825,16 +825,25 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec
825 825 if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) {
826 826 qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections);
827 827 qDebug("Confidence: %f", detection.confidence);
  828 + qDebug("TP vs. FP: %f to %f", TP, FP);
828 829 } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) {
829 830 qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections);
830 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);
831 837 }
  838 +
832 839 points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages));
833 840 prevFP = FP;
834 841 }
835 842 }
836 843 }
837 844  
  845 + if (discrete) qDebug("Total TP vs. FP: %f to %f", TP, FP);
  846 +
838 847 const int keep = qMin(points.size(), Max_Points);
839 848 if (keep < 1) qFatal("Insufficient points.");
840 849  
... ... @@ -1336,7 +1345,7 @@ void readKNNTruth(size_t probeCount, QVector&lt; QList&lt;size_t&gt; &gt; &amp;groundTruth, cons
1336 1345 qFatal("Invalid ground truth file!");
1337 1346 }
1338 1347  
1339   -void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &iet)
  1348 +void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &csv)
1340 1349 {
1341 1350 qDebug("Evaluating k-NN of %s against %s", qPrintable(knnGraph), qPrintable(knnTruth));
1342 1351  
... ... @@ -1406,19 +1415,14 @@ void EvalKNN(const QString &amp;knnGraph, const QString &amp;knnTruth, const QString &amp;ie
1406 1415 if (numUnmatedSearches == 0)
1407 1416 qFatal("No unmated searches!");
1408 1417  
1409   - printf("Rank-%i Return Rate: %g\n", 1, getCMC(firstGenuineReturns, 1, numMatedSearches));
  1418 +
  1419 + qDebug("Rank-%d Return Rate: %.3f", 1, getCMC(firstGenuineReturns, 1, numMatedSearches));
1410 1420 if (k >=5)
1411   - printf("Rank-%i Return Rate: %g\n", 5, getCMC(firstGenuineReturns, 5, numMatedSearches));
  1421 + qDebug("Rank-%d Return Rate: %.3f", 5, getCMC(firstGenuineReturns, 5, numMatedSearches));
1412 1422 if (k >=10)
1413   - printf("Rank-%i Return Rate: %g\n", 10, getCMC(firstGenuineReturns, 10, numMatedSearches));
1414   -
1415   - printf("Rank-%zu Return Rate: %g\n", k, double(numMatedSimilarities) / double(numMatedSearches));
  1423 + qDebug("Rank-%d Return Rate: %.3f", 10, getCMC(firstGenuineReturns, 10, numMatedSearches));
1416 1424  
1417   - // Open the output file
1418   - QFile ietFile(iet);
1419   - if (!ietFile.open(QFile::WriteOnly | QFile::Text))
1420   - qFatal("Failed to open IET file for writing!");
1421   - ietFile.write("Threshold,FPIR,FNIR\n");
  1425 + qDebug("Rank-%zu Return Rate: %.3f", k, double(numMatedSimilarities) / double(numMatedSearches));
1422 1426  
1423 1427 /*
1424 1428 * Iterate through the similarity scores highest-to-lowest,
... ... @@ -1447,10 +1451,28 @@ void EvalKNN(const QString &amp;knnGraph, const QString &amp;knnTruth, const QString &amp;ie
1447 1451 }
1448 1452 }
1449 1453  
1450   - foreach(const OperatingPoint &operatingPoint, operatingPoints)
1451   - ietFile.write(qPrintable(QString::number(operatingPoint.score) + "," +
1452   - QString::number(operatingPoint.FAR) + "," +
1453   - QString::number(operatingPoint.TAR) + "\n"));
  1454 + if (!csv.isEmpty()) {
  1455 + // Open the output file
  1456 + QFile ietFile(csv);
  1457 + if (!ietFile.open(QFile::WriteOnly | QFile::Text))
  1458 + qFatal("Failed to open IET file for writing!");
  1459 + ietFile.write("Plot,X,Y,Z\n");
  1460 + // Write CMC
  1461 + const int Max_Retrieval = min(200, (int)k);
  1462 + for (int i=1; i<=Max_Retrieval; i++) {
  1463 + const float retrievalRate = getCMC(firstGenuineReturns, i, numMatedSearches);
  1464 + ietFile.write(qPrintable(QString("CMC,%1,%2,0\n").arg(QString::number(i), QString::number(retrievalRate))));
  1465 + }
  1466 +
  1467 + foreach(const OperatingPoint &operatingPoint, operatingPoints)
  1468 + ietFile.write(qPrintable("IET," +
  1469 + QString::number(operatingPoint.FAR) + "," +
  1470 + QString::number(operatingPoint.TAR) + "," +
  1471 + QString::number(operatingPoint.score) + "\n"));
  1472 + }
  1473 +
  1474 + qDebug("FNIR @ FPIR = 0.1: %.3f", 1-getOperatingPointGivenFAR(operatingPoints, 0.1).TAR);
  1475 + qDebug("FNIR @ FPIR = 0.01: %.3f", 1-getOperatingPointGivenFAR(operatingPoints, 0.01).TAR);
1454 1476 }
1455 1477  
1456 1478 } // namespace br
... ...
openbr/core/eval.h
... ... @@ -33,7 +33,7 @@ namespace br
33 33 float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap
34 34 float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1, int sampleIndex = 0, int totalExamples = 5); // Return average error
35 35 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
36   - void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &iet);
  36 + void EvalKNN(const QString &knnGraph, const QString &knnTruth, const QString &csv = "");
37 37  
38 38 struct Candidate
39 39 {
... ...
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, float epsilon)
  439 +void OpenCVUtils::group(QList<Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon)
440 440 {
441 441 if (rects.isEmpty())
442 442 return;
... ... @@ -450,7 +450,7 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
450 450 vector<Rect> rrects(nClasses);
451 451  
452 452 // Total number of rects in each class
453   - vector<int> rweights(nClasses, 0);
  453 + vector<int> neighbors(nClasses, 0);
454 454 vector<float> rejectWeights(nClasses, -std::numeric_limits<float>::max());
455 455  
456 456 for (size_t i = 0; i < labels.size(); i++)
... ... @@ -460,7 +460,7 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
460 460 rrects[cls].y += rects[i].y;
461 461 rrects[cls].width += rects[i].width;
462 462 rrects[cls].height += rects[i].height;
463   - rweights[cls]++;
  463 + neighbors[cls]++;
464 464 }
465 465  
466 466 if (useConfidences)
... ... @@ -478,7 +478,7 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
478 478 for (int i = 0; i < nClasses; i++)
479 479 {
480 480 Rect r = rrects[i];
481   - float s = 1.f/rweights[i];
  481 + float s = 1.f/neighbors[i];
482 482 rrects[i] = Rect(saturate_cast<int>(r.x*s),
483 483 saturate_cast<int>(r.y*s),
484 484 saturate_cast<int>(r.width*s),
... ... @@ -488,7 +488,7 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
488 488 rects.clear();
489 489 confidences.clear();
490 490  
491   - // Aggregate by comparing average rectangles against other average rectangels
  491 + // Aggregate by comparing average rectangles against other average rectangles
492 492 for (int i = 0; i < nClasses; i++)
493 493 {
494 494 // Average rectangle
... ... @@ -496,19 +496,22 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
496 496  
497 497 // Used to eliminate rectangles with few neighbors in the case of no weights
498 498 // int n1 = levelWeights ? rejectLevels[i] : rweights[i];
499   - float w1 = rejectWeights[i];
  499 + const float w1 = rejectWeights[i];
500 500  
501 501 // Eliminate rectangle if it doesn't meet confidence criteria
502 502 if (w1 <= confidenceThreshold)
503 503 continue;
504 504  
  505 + const int n1 = neighbors[i];
  506 + if (n1 < minNeighbors)
  507 + continue;
  508 +
505 509 // filter out small face rectangles inside large rectangles
506 510 int j;
507 511 for (j = 0; j < nClasses; j++)
508 512 {
509   - float w2 = rejectWeights[j];
510   -
511   - if (j == i)
  513 + const int n2 = neighbors[j];
  514 + if (j == i || n2 < minNeighbors)
512 515 continue;
513 516  
514 517 Rect r2 = rrects[j];
... ... @@ -516,13 +519,11 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
516 519 int dx = saturate_cast<int>(r2.width * epsilon);
517 520 int dy = saturate_cast<int>(r2.height * epsilon);
518 521  
  522 + float w2 = rejectWeights[j];
  523 +
519 524 // If, r1 is within the r2 AND
520   - // the second rectangle reaches a later stage than the first
521   - // where both the first and the second must have a stage greater than three OR
522   - // the first doens't reach the third stage.
523   - // Changeto: second rectangle has a higher confidence than the first OR
524   - // the first has a low confidence.
525   - // Then, eliminate the first rectangle.
  525 + // r2 has a higher confidence than r1
  526 + // then, eliminate the r1
526 527 if(r1.x >= r2.x - dx &&
527 528 r1.y >= r2.y - dy &&
528 529 r1.x + r1.width <= r2.x + r2.width + dx &&
... ... @@ -531,7 +532,6 @@ void OpenCVUtils::group(QList&lt;Rect&gt; &amp;rects, QList&lt;float&gt; &amp;confidences, float con
531 532 break;
532 533 }
533 534  
534   - // Need to return rects and confidences
535 535 if( j == nClasses )
536 536 {
537 537 rects.append(r1);
... ...
openbr/core/opencvutils.h
... ... @@ -102,7 +102,7 @@ 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, float epsilon);
  105 + void group(QList<cv::Rect> &rects, QList<float> &confidences, float confidenceThreshold, int minNeighbors, float epsilon);
106 106 void flip(const br::Template &src, br::Template &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true);
107 107 void flip(const br::TemplateList &src, br::TemplateList &dst, int axis, bool flipMat=true, bool flipPoints=true, bool flipRects=true);
108 108  
... ...
openbr/core/plot.cpp
... ... @@ -346,4 +346,32 @@ bool PlotMetadata(const QStringList &amp;files, const QString &amp;columns, bool show)
346 346 return p.finalize(show);
347 347 }
348 348  
  349 +bool PlotKNN(const QStringList &files, const File &destination, bool show)
  350 +{
  351 + qDebug("Plotting %d k-NN file(s) to %s", files.size(), qPrintable(destination));
  352 + RPlot p(files, destination);
  353 + p.file.write("\nformatData(type=\"knn\")\n\n");
  354 +
  355 + QMap<QString,File> optMap;
  356 + optMap.insert("rocOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=True Positive Identification Rate (TPIR),xLog=true,yLog=false]")));
  357 + optMap.insert("ietOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=False Negative Identification Rate (FNIR),xLog=true,yLog=true]")));
  358 + optMap.insert("cmcOptions", File(QString("[xTitle=Rank,yTitle=Retrieval Rate,xLog=true,yLog=false,size=1,xLabels=(1,5,10,50,100),xBreaks=(1,5,10,50,100)]")));
  359 +
  360 + foreach (const QString &key, optMap.keys()) {
  361 + const QStringList options = destination.get<QStringList>(key, QStringList());
  362 + foreach (const QString &option, options) {
  363 + QStringList words = QtUtils::parse(option, '=');
  364 + QtUtils::checkArgsSize(words[0], words, 1, 2);
  365 + optMap[key].set(words[0], words[1]);
  366 + }
  367 + }
  368 +
  369 + QString plot = "plot <- plotLine(lineData=%1, options=list(%2), flipY=%3)\nplot\n";
  370 + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["rocOptions"]), "TRUE")));
  371 + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["ietOptions"]), "FALSE")));
  372 + p.file.write(qPrintable(QString(plot).arg("CMC", toRList(optMap["cmcOptions"]), "FALSE")));
  373 +
  374 + return p.finalize(show);
  375 +}
  376 +
349 377 } // namespace br
... ...
openbr/core/plot.h
... ... @@ -28,6 +28,7 @@ namespace br
28 28 bool PlotDetection(const QStringList &files, const File &destination, bool show = false);
29 29 bool PlotLandmarking(const QStringList &files, const File &destination, bool show = false);
30 30 bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false);
  31 + bool PlotKNN(const QStringList &files, const File &destination, bool show = false);
31 32 }
32 33  
33 34 #endif // BR_PLOT_H
... ...
openbr/openbr.cpp
... ... @@ -145,9 +145,9 @@ void br_eval_regression(const char *predicted_gallery, const char *truth_gallery
145 145 EvalRegression(predicted_gallery, truth_gallery, predicted_property, truth_property);
146 146 }
147 147  
148   -void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *iet)
  148 +void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *csv)
149 149 {
150   - EvalKNN(knnGraph, knnTruth, iet);
  150 + EvalKNN(knnGraph, knnTruth, csv);
151 151 }
152 152  
153 153 void br_finalize()
... ... @@ -221,6 +221,11 @@ bool br_plot_metadata(int num_files, const char *files[], const char *columns, b
221 221 return PlotMetadata(QtUtils::toStringList(num_files, files), columns, show);
222 222 }
223 223  
  224 +bool br_plot_knn(int num_files, const char *files[], const char *destination, bool show)
  225 +{
  226 + return PlotKNN(QtUtils::toStringList(num_files, files), destination, show);
  227 +}
  228 +
224 229 float br_progress()
225 230 {
226 231 return Globals->progress();
... ...
openbr/openbr.h
... ... @@ -64,7 +64,7 @@ BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *t
64 64  
65 65 BR_EXPORT void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property = "", const char *truth_property = "");
66 66  
67   -BR_EXPORT void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *iet);
  67 +BR_EXPORT void br_eval_knn(const char *knnGraph, const char *knnTruth, const char *csv = "");
68 68  
69 69 BR_EXPORT void br_finalize();
70 70  
... ... @@ -93,6 +93,8 @@ BR_EXPORT bool br_plot_landmarking(int num_files, const char *files[], const cha
93 93  
94 94 BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char *columns, bool show = false);
95 95  
  96 +BR_EXPORT bool br_plot_knn(int num_files, const char *files[], const char *destination, bool show = false);
  97 +
96 98 BR_EXPORT float br_progress();
97 99  
98 100 BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv);
... ...
openbr/openbr_plugin.h
... ... @@ -882,13 +882,11 @@ public:
882 882  
883 883 static Representation *make(QString str, QObject *parent); /*!< \brief Make a representation from a string. */
884 884  
885   - virtual void preprocess(const cv::Mat &src, cv::Mat &dst) const { dst = src; }
886   - virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) { (void) images; (void)labels; }
887   -
888   - virtual float evaluate(const cv::Mat &image, int idx) const = 0;
889   - // By convention, an empty indices list will result in all feature responses being calculated
890   - // and returned.
891   - virtual cv::Mat evaluate(const cv::Mat &image, const QList<int> &indices = QList<int>()) const = 0;
  885 + virtual Template preprocess(const Template &src) const { return src; }
  886 + virtual void train(const TemplateList &data) { (void)data; }
  887 + virtual float evaluate(const Template &src, int idx) const = 0;
  888 + // By convention passing an empty list evaluates all features in the representation
  889 + virtual cv::Mat evaluate(const Template &src, const QList<int> &indices = QList<int>()) const = 0;
892 890  
893 891 virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; // dx and dy should indicate the change to the original window size after preprocessing
894 892 virtual int numChannels() const { return 1; }
... ... @@ -905,13 +903,13 @@ public:
905 903  
906 904 static Classifier *make(QString str, QObject *parent);
907 905  
908   - virtual void train(const QList<cv::Mat> &images, const QList<float> &labels) = 0;
909   - virtual float classify(const cv::Mat &image, bool process = true, float *confidence = NULL) const = 0;
  906 + virtual void train(const TemplateList &data) { (void)data; }
  907 + virtual float classify(const Template &src, bool process = true, float *confidence = NULL) const = 0;
910 908  
911 909 // Slots for representations
912   - virtual cv::Mat preprocess(const cv::Mat &image) const = 0;
  910 + virtual Template preprocess(const Template &src) const { return src; }
913 911 virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0;
914   - virtual int numFeatures() const = 0;
  912 + virtual int numFeatures() const { return 0; }
915 913 };
916 914  
917 915  
... ...
openbr/plugins/classification/boostedforest.cpp
... ... @@ -125,20 +125,20 @@ private:
125 125 QList<Node*> classifiers;
126 126 float threshold;
127 127  
128   - void train(const QList<Mat> &images, const QList<float> &labels)
  128 + void train(const TemplateList &data)
129 129 {
130   - representation->train(images, labels);
  130 + representation->train(data);
131 131  
132 132 CascadeBoostParams params(type, minTAR, maxFAR, trimRate, maxDepth, maxWeakCount);
133 133  
134 134 FeatureEvaluator featureEvaluator;
135   - featureEvaluator.init(representation, images.size(), representation->numChannels());
  135 + featureEvaluator.init(representation, data.size());
136 136  
137   - for (int i = 0; i < images.size(); i++)
138   - featureEvaluator.setImage(images[i], labels[i], i);
  137 + for (int i = 0; i < data.size(); i++)
  138 + featureEvaluator.setImage(data[i], data[i].file.get<float>("Label"), i);
139 139  
140 140 CascadeBoost boost;
141   - boost.train(&featureEvaluator, images.size(), 1024, 1024, representation->numChannels(), params);
  141 + boost.train(&featureEvaluator, data.size(), 1024, 1024, representation->numChannels(), params);
142 142  
143 143 threshold = boost.getThreshold();
144 144  
... ... @@ -149,13 +149,9 @@ private:
149 149 }
150 150 }
151 151  
152   - float classify(const Mat &image, bool process, float *confidence) const
  152 + float classify(const Template &src, bool process, float *confidence) const
153 153 {
154   - Mat m;
155   - if (process)
156   - m = preprocess(image);
157   - else
158   - m = image;
  154 + Template t = process ? preprocess(src) : src;
159 155  
160 156 float sum = 0;
161 157 for (int i = 0; i < classifiers.size(); i++) {
... ... @@ -163,10 +159,10 @@ private:
163 159  
164 160 while (node->left) {
165 161 if (representation->maxCatCount() > 0) {
166   - int c = (int)representation->evaluate(m, node->featureIdx);
  162 + int c = (int)representation->evaluate(t, node->featureIdx);
167 163 node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right;
168 164 } else {
169   - double val = representation->evaluate(m, node->featureIdx);
  165 + double val = representation->evaluate(t, node->featureIdx);
170 166 node = val <= node->threshold ? node->left : node->right;
171 167 }
172 168 }
... ... @@ -184,11 +180,9 @@ private:
184 180 return representation->numFeatures();
185 181 }
186 182  
187   - Mat preprocess(const Mat &image) const
  183 + Template preprocess(const Template &src) const
188 184 {
189   - Mat dst;
190   - representation->preprocess(image, dst);
191   - return dst;
  185 + return representation->preprocess(src);
192 186 }
193 187  
194 188 Size windowSize(int *dx, int *dy) const
... ...
openbr/plugins/classification/cascade.cpp
1 1 #include <opencv2/imgproc/imgproc.hpp>
2   -#include <opencv2/highgui/highgui.hpp>
  2 +
3 3 #include <openbr/plugins/openbr_internal.h>
4 4 #include <openbr/core/common.h>
  5 +#include "openbr/core/opencvutils.h"
5 6  
6 7 #include <QtConcurrent>
7 8  
... ... @@ -37,7 +38,7 @@ struct Miner
37 38 Mat mine(bool *newImg)
38 39 {
39 40 // Copy region of winSize region of img into m
40   - Mat window(windowSize.height, windowSize.width, CV_8UC1,
  41 + Mat window(windowSize.height, windowSize.width, CV_8U,
41 42 (void*)(scaledSrc.data + point.y * scaledSrc.step + point.x * scaledSrc.elemSize()),
42 43 scaledSrc.step);
43 44  
... ... @@ -100,31 +101,30 @@ class CascadeClassifier : public Classifier
100 101 BR_PROPERTY(bool, requireAllStages, false)
101 102  
102 103 QList<Classifier *> stages;
103   - QList<Mat> posImages, negImages;
104   - QList<Mat> posSamples, negSamples;
  104 + TemplateList posImages, negImages;
  105 + TemplateList posSamples, negSamples;
105 106  
106 107 QList<int> indices;
107 108 int negIndex, posIndex, samplingRound;
108 109  
109   - QMutex samplingMutex, miningMutex, passedMutex;
  110 + QMutex samplingMutex, miningMutex;
110 111  
111 112 void init()
112 113 {
113 114 negIndex = posIndex = samplingRound = 0;
114 115 }
115 116  
116   - bool getPositive(Mat &img)
  117 + bool getPositive(Template &img)
117 118 {
118 119 if (posIndex >= posImages.size())
119 120 return false;
120   -
121   - posImages[indices[posIndex++]].copyTo(img);
  121 + img = posImages[indices[posIndex++]];
122 122 return true;
123 123 }
124 124  
125   - Mat getNegative(Point &offset)
  125 + Template getNegative(Point &offset)
126 126 {
127   - Mat negative;
  127 + Template negative;
128 128  
129 129 const Size size = windowSize();
130 130 // Grab negative from list
... ... @@ -136,9 +136,9 @@ class CascadeClassifier : public Classifier
136 136 samplingRound = samplingRound % (size.width * size.height);
137 137 negIndex %= count;
138 138  
139   - offset.x = qMin( (int)samplingRound % size.width, negative.cols - size.width);
140   - offset.y = qMin( (int)samplingRound / size.width, negative.rows - size.height);
141   - if (!negative.empty() && negative.type() == CV_8UC1
  139 + offset.x = qMin( (int)samplingRound % size.width, negative.m().cols - size.width);
  140 + offset.y = qMin( (int)samplingRound / size.width, negative.m().rows - size.height);
  141 + if (!negative.m().empty() && negative.m().type() == CV_8U
142 142 && offset.x >= 0 && offset.y >= 0)
143 143 break;
144 144 }
... ... @@ -146,35 +146,32 @@ class CascadeClassifier : public Classifier
146 146 return negative;
147 147 }
148 148  
149   - int mine()
  149 + uint64 mine()
150 150 {
151   - int passedNegatives = 0;
  151 + uint64 passedNegatives = 0;
152 152 forever {
153   - Mat negative;
  153 + Template negative;
154 154 Point offset;
155   - samplingMutex.lock();
  155 + QMutexLocker samplingLocker(&samplingMutex);
156 156 negative = getNegative(offset);
157   - samplingMutex.unlock();
  157 + samplingLocker.unlock();
158 158  
159   - Miner miner(negative, windowSize(), offset);
  159 + Miner miner(negative.m(), windowSize(), offset);
160 160 forever {
161 161 bool newImg;
162   - Mat sample = miner.mine(&newImg);
  162 + Template sample(negative.file, miner.mine(&newImg));
163 163 if (!newImg) {
164 164 if (negSamples.size() >= numNegs)
165 165 return passedNegatives;
166 166  
167 167 float confidence;
168 168 if (classify(sample, true, &confidence) != 0) {
169   - miningMutex.lock();
170   - if (negSamples.size() >= numNegs) {
171   - miningMutex.unlock();
  169 + QMutexLocker miningLocker(&miningMutex);
  170 + if (negSamples.size() >= numNegs)
172 171 return passedNegatives;
173   - }
174 172  
175 173 negSamples.append(sample);
176 174 printf("Negative samples: %d\r", negSamples.size());
177   - miningMutex.unlock();
178 175 }
179 176  
180 177 passedNegatives++;
... ... @@ -184,16 +181,16 @@ class CascadeClassifier : public Classifier
184 181 }
185 182 }
186 183  
187   - void train(const QList<Mat> &images, const QList<float> &labels)
  184 + void train(const TemplateList &data)
188 185 {
189   - for (int i = 0; i < images.size(); i++)
190   - labels[i] == 1 ? posImages.append(images[i]) : negImages.append(images[i]);
  186 + foreach (const Template &t, data)
  187 + t.file.get<float>("Label") == 1.0f ? posImages.append(t) : negImages.append(t);
191 188  
192   - qDebug() << "Total images:" << images.size()
  189 + qDebug() << "Total images:" << data.size()
193 190 << "\nTotal positive images:" << posImages.size()
194 191 << "\nTotal negative images:" << negImages.size();
195 192  
196   - indices = Common::RandSample(posImages.size(),posImages.size(),true);
  193 + indices = Common::RandSample(posImages.size(), posImages.size(), true);
197 194  
198 195 stages.reserve(numStages);
199 196 for (int i = 0; i < numStages; i++) {
... ... @@ -212,27 +209,17 @@ class CascadeClassifier : public Classifier
212 209 return;
213 210 }
214 211  
215   - QList<float> posLabels;
216   - posLabels.reserve(posSamples.size());
217   - for (int j=0; j<posSamples.size(); j++)
218   - posLabels.append(1);
219   -
220   - QList<float> negLabels;
221   - negLabels.reserve(negSamples.size());
222   - for (int j=0; j<negSamples.size(); j++)
223   - negLabels.append(0);
224   -
225   - stages[i]->train(posSamples+negSamples, posLabels+negLabels);
  212 + stages[i]->train(posSamples + negSamples);
226 213  
227 214 qDebug() << "END>";
228 215 }
229 216 }
230 217  
231   - float classify(const Mat &image, bool process, float *confidence) const
  218 + float classify(const Template &src, bool process, float *confidence) const
232 219 {
233 220 float stageConf = 0.0f;
234 221 foreach (const Classifier *stage, stages) {
235   - float result = stage->classify(image, process, &stageConf);
  222 + float result = stage->classify(src, process, &stageConf);
236 223 if (confidence)
237 224 *confidence += stageConf;
238 225 if (result == 0.0f)
... ... @@ -246,9 +233,9 @@ class CascadeClassifier : public Classifier
246 233 return stages.first()->numFeatures();
247 234 }
248 235  
249   - Mat preprocess(const Mat &image) const
  236 + Template preprocess(const Template &src) const
250 237 {
251   - return stages.first()->preprocess(image);
  238 + return stages.first()->preprocess(src);
252 239 }
253 240  
254 241 Size windowSize(int *dx = NULL, int *dy = NULL) const
... ... @@ -276,16 +263,13 @@ class CascadeClassifier : public Classifier
276 263 private:
277 264 float getSamples()
278 265 {
279   - posSamples.clear();
280   - posSamples.reserve(numPos);
281   - negSamples.clear();
282   - negSamples.reserve(numNegs);
  266 + posSamples.clear(); posSamples.reserve(numPos);
  267 + negSamples.clear(); negSamples.reserve(numNegs);
283 268 posIndex = 0;
284 269  
285 270 float confidence;
286 271 while (posSamples.size() < numPos) {
287   - Mat pos(windowSize(), CV_8UC1);
288   -
  272 + Template pos;
289 273 if (!getPositive(pos))
290 274 qFatal("Cannot get another positive sample!");
291 275  
... ... @@ -297,13 +281,13 @@ private:
297 281  
298 282 qDebug() << "POS count : consumed " << posSamples.size() << ":" << posIndex;
299 283  
300   - QFutureSynchronizer<int> futures;
  284 + QFutureSynchronizer<uint64> futures;
301 285 for (int i=0; i<QThread::idealThreadCount(); i++)
302 286 futures.addFuture(QtConcurrent::run(this, &CascadeClassifier::mine));
303 287 futures.waitForFinished();
304 288  
305   - int passedNegs = 0;
306   - QList<QFuture<int> > results = futures.futures();
  289 + uint64 passedNegs = 0;
  290 + QList<QFuture<uint64> > results = futures.futures();
307 291 for (int i=0; i<results.size(); i++)
308 292 passedNegs += results[i].result();
309 293  
... ...
openbr/plugins/classification/dlib.cpp
  1 +#include <opencv2/imgproc/imgproc.hpp>
1 2 #include <dlib/image_processing/frontal_face_detector.h>
2 3 #include <dlib/opencv.h>
3 4  
... ... @@ -45,14 +46,18 @@ private:
45 46 {
46 47 dst = src;
47 48  
48   - shape_predictor *sp = shapeResource.acquire();
  49 + shape_predictor *const sp = shapeResource.acquire();
49 50  
50   - cv_image<unsigned char> cimg(src.m());
  51 + cv::Mat cvImage = src.m();
  52 + if (cvImage.channels() == 3)
  53 + cv::cvtColor(cvImage, cvImage, CV_BGR2GRAY);
  54 +
  55 + cv_image<unsigned char> cimg(cvImage);
51 56 array2d<unsigned char> image;
52 57 assign_image(image,cimg);
53 58  
54 59 if (src.file.rects().isEmpty()) { //If the image has no rects assume the whole image is a face
55   - rectangle r(0, 0, src.m().cols, src.m().rows);
  60 + rectangle r(0, 0, cvImage.cols, cvImage.rows);
56 61 full_object_detection shape = (*sp)(image, r);
57 62 for (size_t i = 0; i < shape.num_parts(); i++)
58 63 dst.file.appendPoint(QPointF(shape.part(i)(0),shape.part(i)(1)));
... ...
openbr/plugins/classification/liblinear.cpp
... ... @@ -138,7 +138,7 @@ private:
138 138 param.weight = NULL;
139 139 }
140 140  
141   - m = *train_svm(&prob, &param);
  141 + //m = *train_svm(&prob, &param);
142 142  
143 143 delete[] param.weight;
144 144 delete[] param.weight_label;
... ...
openbr/plugins/core/algorithms.cpp
... ... @@ -39,7 +39,7 @@ class AlgorithmsInitializer : public Initializer
39 39 Globals->abbreviations.insert("FR_Mouth", "(CropFromLandmarks([59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76])+Resize(24,48))");
40 40 Globals->abbreviations.insert("FR_Nose", "(CropFromLandmarks([16,17,18,19,20,21,22,23,24,25,26,27],padding=3)+Resize(36,36))");
41 41 Globals->abbreviations.insert("FR_Face", "(Crop(24,24,88,88)+Resize(44,44))");
42   - Globals->abbreviations.insert("FR_Detect", "(FaceDetection+Stasm+Rename(StasmLeftEye,Affine_1,true)+Rename(StasmRightEye,Affine_0,true)+Affine(136,136,0.35,0.35,warpPoints=true))");
  42 + Globals->abbreviations.insert("FR_Detect", "(Open+Cvt(Gray)+Cascade+Stasm+Rename(StasmLeftEye,Affine_1,true)+Rename(StasmRightEye,Affine_0,true)+Affine(136,136,0.35,0.35,warpPoints=true))");
43 43 Globals->abbreviations.insert("FR_Represent", "((DenseHOG/DenseLBP)+Cat+LDA(.98)+Normalize(L2))");
44 44  
45 45 Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard");
... ...
openbr/plugins/distance/unit.cpp
... ... @@ -76,12 +76,18 @@ class UnitDistance : public Distance
76 76  
77 77 float compare(const Template &target, const Template &query) const
78 78 {
79   - return a * (distance->compare(target, query) - b);
  79 + return normalize(distance->compare(target, query));
80 80 }
81 81  
82 82 float compare(const cv::Mat &target, const cv::Mat &query) const
83 83 {
84   - return a * (distance->compare(target, query) - b);
  84 + return normalize(distance->compare(target, query));
  85 + }
  86 +
  87 + float normalize(float score) const
  88 + {
  89 + if (!Globals->scoreNormalization) return score;
  90 + return a * (score - b);
85 91 }
86 92 };
87 93  
... ...
openbr/plugins/gallery/keyframes.cpp
... ... @@ -26,6 +26,12 @@ extern &quot;C&quot;
26 26 #include <libswscale/swscale.h>
27 27 }
28 28  
  29 +// http://stackoverflow.com/questions/24057248/ffmpeg-undefined-references-to-av-frame-alloc
  30 +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
  31 +#define av_frame_alloc avcodec_alloc_frame
  32 +#define av_frame_free avcodec_free_frame
  33 +#endif
  34 +
29 35 using namespace cv;
30 36  
31 37 namespace br
... ...
openbr/plugins/imgproc/equalizehist.cpp
... ... @@ -18,6 +18,8 @@
18 18  
19 19 #include <openbr/plugins/openbr_internal.h>
20 20  
  21 +using namespace cv;
  22 +
21 23 namespace br
22 24 {
23 25  
... ... @@ -32,7 +34,20 @@ class EqualizeHistTransform : public UntrainableTransform
32 34  
33 35 void project(const Template &src, Template &dst) const
34 36 {
35   - cv::equalizeHist(src, dst);
  37 + if (src.m().channels() == 1) {
  38 + equalizeHist(src, dst);
  39 + } else if (src.m().channels() == 3) {
  40 + // http://stackoverflow.com/questions/15007304/histogram-equalization-not-working-on-color-image-opencv
  41 + Mat ycrcb;
  42 + cvtColor(src, ycrcb, CV_BGR2YCrCb);
  43 + vector<Mat> channels;
  44 + split(ycrcb, channels);
  45 + equalizeHist(channels[0], channels[0]);
  46 + merge(channels, ycrcb);
  47 + cvtColor(ycrcb, dst, CV_YCrCb2BGR);
  48 + } else {
  49 + qFatal("Invalid channel count!");
  50 + }
36 51 }
37 52 };
38 53  
... ...
openbr/plugins/imgproc/pad.cpp
  1 +#include <opencv2/imgproc/imgproc.hpp>
1 2 #include <openbr/plugins/openbr_internal.h>
2 3  
3 4 using namespace cv;
... ... @@ -5,24 +6,36 @@ using namespace cv;
5 6 namespace br
6 7 {
7 8  
  9 +/*!
  10 + * \ingroup transforms
  11 + * \brief Pads an image.
  12 + * \author Scott Klum \cite sklum
  13 + */
8 14 class PadTransform : public UntrainableTransform
9 15 {
10 16 Q_OBJECT
11   -
12   - Q_PROPERTY(int padSize READ get_padSize WRITE set_padSize RESET reset_padSize STORED false)
13   - Q_PROPERTY(int padValue READ get_padValue WRITE set_padValue RESET reset_padValue STORED false)
14   - BR_PROPERTY(int, padSize, 0)
15   - BR_PROPERTY(int, padValue, 0)
  17 + Q_ENUMS(Method)
  18 +
  19 +public:
  20 + /*!< */
  21 + enum Border { Replicate = BORDER_REPLICATE,
  22 + Reflect = BORDER_REFLECT_101,
  23 + Constant = BORDER_CONSTANT};
  24 +
  25 +private:
  26 + Q_PROPERTY(Border border READ get_border WRITE set_border RESET reset_border STORED false)
  27 + Q_PROPERTY(float percent READ get_percent WRITE set_percent RESET reset_percent STORED false)
  28 + Q_PROPERTY(int value READ get_value WRITE set_value RESET reset_value STORED false)
  29 + BR_PROPERTY(Border, border, Replicate)
  30 + BR_PROPERTY(float, percent, .1)
  31 + BR_PROPERTY(float, value, 0)
16 32  
17 33 void project(const Template &src, Template &dst) const
18 34 {
19   - dst.file = src.file;
20   -
21   - foreach (const Mat &m, src) {
22   - Mat padded = padValue * Mat::ones(m.rows + 2*padSize, m.cols + 2*padSize, m.type());
23   - padded(Rect(padSize, padSize, padded.cols - padSize, padded.rows - padSize)) = m;
24   - dst += padded;
25   - }
  35 + int top, bottom, left, right;
  36 + top = percent*src.m().rows; bottom = percent*src.m().rows;
  37 + left = percent*src.m().cols; right = percent*src.m().cols;
  38 + copyMakeBorder(src, dst, top, bottom, left, right, border, Scalar(value));
26 39 }
27 40 };
28 41  
... ...
openbr/plugins/imgproc/resize.cpp
... ... @@ -55,9 +55,15 @@ private:
55 55  
56 56 void project(const Template &src, Template &dst) const
57 57 {
58   - if (!preserveAspect)
  58 + if (!preserveAspect) {
59 59 resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method);
60   - else {
  60 + const float rowScaleFactor = (float)rows/src.m().rows;
  61 + const float colScaleFactor = (float)columns/src.m().cols;
  62 + QList<QPointF> points = src.file.points();
  63 + for (int i=0; i<points.size(); i++)
  64 + points[i] = QPointF(points[i].x() * colScaleFactor,points[i].y() * rowScaleFactor);
  65 + dst.file.setPoints(points);
  66 + } else {
61 67 float inRatio = (float) src.m().rows / src.m().cols;
62 68 float outRatio = (float) rows / columns;
63 69 dst = Mat::zeros(rows, columns, src.m().type());
... ...
openbr/plugins/imgproc/rndrotate.cpp
... ... @@ -16,6 +16,7 @@
16 16  
17 17 #include <opencv2/imgproc/imgproc.hpp>
18 18 #include <openbr/plugins/openbr_internal.h>
  19 +#include <openbr/core/opencvutils.h>
19 20  
20 21 using namespace cv;
21 22  
... ... @@ -32,12 +33,14 @@ class RndRotateTransform : public UntrainableTransform
32 33 Q_OBJECT
33 34  
34 35 Q_PROPERTY(QList<int> range READ get_range WRITE set_range RESET reset_range STORED false)
  36 + Q_PROPERTY(int center READ get_center WRITE set_center RESET reset_center STORED false)
35 37 BR_PROPERTY(QList<int>, range, QList<int>() << -15 << 15)
  38 + BR_PROPERTY(int, center, -1)
36 39  
37 40 void project(const Template &src, Template &dst) const {
38 41 int span = range.first() - range.last();
39   - int angle = (rand() % span) + range.first();
40   - Mat rotMatrix = getRotationMatrix2D(Point2f(src.m().rows/2,src.m().cols/2),angle,1.0);
  42 + int angle = span == 0 ? range.first() : (rand() % span) + range.first();
  43 + Mat rotMatrix = getRotationMatrix2D(center == -1 ? Point2f(src.m().rows/2,src.m().cols/2) : OpenCVUtils::toPoint(src.file.points()[center]),angle,1.0);
41 44 warpAffine(src,dst,rotMatrix,Size(src.m().cols,src.m().rows),INTER_LINEAR,BORDER_REFLECT_101);
42 45  
43 46 QList<QPointF> points = src.file.points();
... ...
openbr/plugins/imgproc/roi.cpp
... ... @@ -34,8 +34,10 @@ class ROITransform : public UntrainableTransform
34 34 Q_OBJECT
35 35 Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false)
36 36 Q_PROPERTY(bool copyOnCrop READ get_copyOnCrop WRITE set_copyOnCrop RESET reset_copyOnCrop STORED false)
  37 + Q_PROPERTY(int shiftPoints READ get_shiftPoints WRITE set_shiftPoints RESET reset_shiftPoints STORED false)
37 38 BR_PROPERTY(QString, propName, "")
38 39 BR_PROPERTY(bool, copyOnCrop, false)
  40 + BR_PROPERTY(int, shiftPoints, -1)
39 41  
40 42 void project(const Template &src, Template &dst) const
41 43 {
... ... @@ -55,6 +57,16 @@ class ROITransform : public UntrainableTransform
55 57 if (Globals->verbose)
56 58 qWarning("No rects present in file.");
57 59 }
  60 +
  61 + if (shiftPoints != -1) {
  62 + // Shift the points to the rect with the index provided
  63 + QRectF rect = src.file.rects()[shiftPoints];
  64 + QList<QPointF> points = src.file.points();
  65 + for (int i=0; i<points.size(); i++)
  66 + points[i] -= rect.topLeft();
  67 + dst.file.setPoints(points);
  68 + }
  69 +
58 70 dst.file.clearRects();
59 71  
60 72 if (copyOnCrop)
... ...
openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -17,7 +17,6 @@
17 17 #include <openbr/plugins/openbr_internal.h>
18 18 #include <openbr/core/opencvutils.h>
19 19 #include <openbr/core/qtutils.h>
20   -#include <opencv2/highgui/highgui.hpp>
21 20  
22 21 #include <opencv2/imgproc/imgproc.hpp>
23 22  
... ... @@ -35,7 +34,6 @@ namespace br
35 34 * \br_property int minSize The smallest sized object to detect in pixels
36 35 * \br_property int maxSize The largest sized object to detect in pixels. A negative value will set maxSize == image size
37 36 * \br_property float scaleFactor The factor to scale the image by during each resize.
38   - * \br_property int minNeighbors Parameter for non-maximum supression
39 37 * \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.
40 38 * \br_property float eps Parameter for non-maximum supression
41 39 */
... ... @@ -50,6 +48,7 @@ class SlidingWindowTransform : public MetaTransform
50 48 Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
51 49 Q_PROPERTY(float confidenceThreshold READ get_confidenceThreshold WRITE set_confidenceThreshold RESET reset_confidenceThreshold STORED false)
52 50 Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false)
  51 + Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
53 52  
54 53 BR_PROPERTY(br::Classifier*, classifier, NULL)
55 54 BR_PROPERTY(int, minSize, 20)
... ... @@ -57,10 +56,11 @@ class SlidingWindowTransform : public MetaTransform
57 56 BR_PROPERTY(float, scaleFactor, 1.2)
58 57 BR_PROPERTY(float, confidenceThreshold, 10)
59 58 BR_PROPERTY(float, eps, 0.2)
  59 + BR_PROPERTY(int, minNeighbors, 3)
60 60  
61 61 void train(const TemplateList &data)
62 62 {
63   - classifier->train(data.data(), File::get<float>(data, "Label", -1));
  63 + classifier->train(data);
64 64 }
65 65  
66 66 void project(const Template &src, Template &dst) const
... ... @@ -123,15 +123,18 @@ class SlidingWindowTransform : public MetaTransform
123 123  
124 124 Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data);
125 125 resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR);
126   - Mat repImage = classifier->preprocess(scaledImage);
  126 +
  127 + Template repImage(t.file, scaledImage);
  128 + repImage = classifier->preprocess(repImage);
127 129  
128 130 int step = factor > 2. ? 1 : 2;
129 131 for (int y = 0; y < processingRectSize.height; y += step) {
130 132 for (int x = 0; x < processingRectSize.width; x += step) {
131   - Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone();
  133 + Mat window = repImage.m()(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone();
  134 + Template t(window);
132 135  
133 136 float confidence = 0;
134   - int result = classifier->classify(window, false, &confidence);
  137 + int result = classifier->classify(t, false, &confidence);
135 138  
136 139 if (result == 1) {
137 140 rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height));
... ... @@ -146,7 +149,7 @@ class SlidingWindowTransform : public MetaTransform
146 149 }
147 150 }
148 151  
149   - OpenCVUtils::group(rects, confidences, confidenceThreshold, eps);
  152 + OpenCVUtils::group(rects, confidences, confidenceThreshold, minNeighbors, eps);
150 153  
151 154 if (!enrollAll && rects.empty()) {
152 155 rects.append(Rect(0, 0, m.cols, m.rows));
... ...
openbr/plugins/metadata/averagepoints.cpp
... ... @@ -14,23 +14,14 @@ class AveragePointsTransform : public UntrainableMetadataTransform
14 14 Q_PROPERTY(QList<int> indices READ get_indices WRITE set_indices RESET reset_indices STORED false)
15 15 Q_PROPERTY(QString metaName READ get_metaName WRITE set_metaName RESET reset_metaName STORED true)
16 16 Q_PROPERTY(bool append READ get_append WRITE set_append RESET reset_append STORED true)
17   - Q_PROPERTY(int nLandmarks READ get_nLandmarks WRITE set_nLandmarks RESET reset_nLandmarks STORED true)
18 17 BR_PROPERTY(QList<int>, indices, QList<int>())
19 18 BR_PROPERTY(QString, metaName, "")
20 19 BR_PROPERTY(bool, append, false)
21   - BR_PROPERTY(int, nLandmarks, 51)
22 20  
23 21 void projectMetadata(const File &src, File &dst) const
24 22 {
25 23 dst = src;
26   - if (src.points().size() != nLandmarks) {
27   - if (Globals->verbose)
28   - qDebug() << "Warning: Face has " << src.points().size() << "points; should be " << nLandmarks;
29   - dst.fte = true;
30   - return;
31   - }
32   - int x1 = 0,
33   - y1 = 0;
  24 + float x1 = 0, y1 = 0;
34 25  
35 26 for (int i = 0; i < indices.size(); i++) {
36 27 x1 += src.points()[indices[i]].x();
... ...
openbr/plugins/metadata/cascade.cpp
... ... @@ -202,7 +202,8 @@ class CascadeTransform : public MetaTransform
202 202 Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
203 203 Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
204 204 Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false)
205   -
  205 + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
  206 +
206 207 // Training parameters
207 208 Q_PROPERTY(int numStages READ get_numStages WRITE set_numStages RESET reset_numStages STORED false)
208 209 Q_PROPERTY(int w READ get_w WRITE set_w RESET reset_w STORED false)
... ... @@ -227,7 +228,8 @@ class CascadeTransform : public MetaTransform
227 228 BR_PROPERTY(int, minSize, 64)
228 229 BR_PROPERTY(int, minNeighbors, 5)
229 230 BR_PROPERTY(bool, ROCMode, false)
230   -
  231 + BR_PROPERTY(float, scaleFactor, 1.2)
  232 +
231 233 // Training parameters - Default values provided trigger OpenCV defaults
232 234 BR_PROPERTY(int, numStages, -1)
233 235 BR_PROPERTY(int, w, -1)
... ... @@ -410,8 +412,19 @@ class CascadeTransform : public MetaTransform
410 412 std::vector<Rect> rects;
411 413 std::vector<int> rejectLevels;
412 414 std::vector<double> levelWeights;
413   - if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, flags | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true);
414   - else cascade->detectMultiScale(m, rects, 1.2, minNeighbors, flags, Size(minSize, minSize));
  415 + if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, scaleFactor, minNeighbors, flags | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true);
  416 + else cascade->detectMultiScale(m, rects, scaleFactor, minNeighbors, flags, Size(minSize, minSize));
  417 +
  418 + // It appears that flags is ignored for new model files:
  419 + // http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html#cascadeclassifier-detectmultiscale
  420 + if ((flags == CASCADE_FIND_BIGGEST_OBJECT) && (rects.size() > 1)) {
  421 + Rect biggest = rects[0];
  422 + for (size_t j=0; j<rects.size(); j++)
  423 + if (rects[j].area() > biggest.area())
  424 + biggest = rects[j];
  425 + rects.clear();
  426 + rects.push_back(biggest);
  427 + }
415 428  
416 429 bool empty = false;
417 430 if (!enrollAll && rects.empty()) {
... ... @@ -426,7 +439,7 @@ class CascadeTransform : public MetaTransform
426 439 u.file.set("Confidence",-std::numeric_limits<float>::max());
427 440 } else if (rejectLevels.size() > j)
428 441 u.file.set("Confidence", rejectLevels[j]*levelWeights[j]);
429   - else
  442 + else
430 443 u.file.set("Confidence", rects[j].area());
431 444 const QRectF rect = OpenCVUtils::fromRect(rects[j]);
432 445 u.file.appendRect(rect);
... ...
openbr/plugins/metadata/keytorect.cpp renamed to openbr/plugins/metadata/keytolandmark.cpp
... ... @@ -24,28 +24,35 @@ namespace br
24 24 * \brief Convert values of key_X, key_Y, key_Width, key_Height to a rect.
25 25 * \author Jordan Cheney \cite JordanCheney
26 26 */
27   -class KeyToRectTransform : public UntrainableMetadataTransform
  27 +class KeyToLandmarkTransform : public UntrainableMetadataTransform
28 28 {
29 29 Q_OBJECT
30 30 Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false)
  31 + Q_PROPERTY(bool point READ get_point WRITE set_point RESET reset_point STORED false)
31 32 BR_PROPERTY(QString, key, "")
  33 + BR_PROPERTY(bool, point, false)
32 34  
33 35 void projectMetadata(const File &src, File &dst) const
34 36 {
35 37 dst = src;
36 38  
37   - if (src.contains(QStringList() << key + "_X" << key + "_Y" << key + "_Width" << key + "_Height"))
38   - dst.appendRect(QRectF(src.get<int>(key + "_X"),
39   - src.get<int>(key + "_Y"),
40   - src.get<int>(key + "_Width"),
41   - src.get<int>(key + "_Height")));
  39 + if (point) {
  40 + if (src.contains(QStringList() << key + "_X" << key + "_Y"))
  41 + dst.appendPoint(QPointF(src.get<float>(key + "_X"),
  42 + src.get<float>(key + "_Y")));
  43 + } else {
  44 + if (src.contains(QStringList() << key + "_X" << key + "_Y" << key + "_Width" << key + "_Height"))
  45 + dst.appendRect(QRectF(src.get<float>(key + "_X"),
  46 + src.get<float>(key + "_Y"),
  47 + src.get<float>(key + "_Width"),
  48 + src.get<float>(key + "_Height")));
  49 + }
42 50  
43 51 }
44   -
45 52 };
46 53  
47   -BR_REGISTER(Transform, KeyToRectTransform)
  54 +BR_REGISTER(Transform, KeyToLandmarkTransform)
48 55  
49 56 } // namespace br
50 57  
51   -#include "metadata/keytorect.moc"
  58 +#include "metadata/keytolandmark.moc"
... ...
openbr/plugins/metadata/removetemplates.cpp
... ... @@ -31,15 +31,22 @@ class RemoveTemplatesTransform : public UntrainableMetaTransform
31 31 Q_OBJECT
32 32 Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false)
33 33 Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false)
  34 + Q_PROPERTY(bool keep READ get_keep WRITE set_keep RESET reset_keep STORED false)
34 35 BR_PROPERTY(QString, regexp, "")
35 36 BR_PROPERTY(QString, key, "")
  37 + BR_PROPERTY(bool, keep, false)
36 38  
37 39 void project(const Template &src, Template &dst) const
38 40 {
39   - const QRegularExpression re(regexp);
40   - const QRegularExpressionMatch match = re.match(key.isEmpty() ? src.file.suffix() : src.file.get<QString>(key));
41   - if (match.hasMatch()) dst = Template();
42   - else dst = src;
  41 + dst = src;
  42 + QRegExp re(regexp);
  43 + re.setPatternSyntax(QRegExp::Wildcard);
  44 + bool match = re.exactMatch(key.isEmpty() ? src.file.suffix() : src.file.get<QString>(key));
  45 +
  46 + if (keep && !match)
  47 + dst.file.fte = true;
  48 + else if (!keep && match)
  49 + dst.file.fte = true;
43 50 }
44 51 };
45 52  
... ...
openbr/plugins/metadata/stasm4.cpp
1 1 #include <QString>
2   -#include <stasm_lib.h>
3 2 #include <stasmcascadeclassifier.h>
  3 +#include <stasm_lib.h>
  4 +#include <stasm.h>
4 5 #include <opencv2/opencv.hpp>
5 6  
6 7 #include <openbr/plugins/openbr_internal.h>
... ... @@ -56,18 +57,18 @@ BR_REGISTER(Initializer, StasmInitializer)
56 57 * \brief Wraps STASM key point detector
57 58 * \author Scott Klum \cite sklum
58 59 */
59   -class StasmTransform : public UntrainableTransform
  60 +class StasmTransform : public UntrainableMetaTransform
60 61 {
61 62 Q_OBJECT
62 63  
63 64 Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false)
64 65 BR_PROPERTY(bool, stasm3Format, false)
65   - Q_PROPERTY(bool clearLandmarks READ get_clearLandmarks WRITE set_clearLandmarks RESET reset_clearLandmarks STORED false)
66   - BR_PROPERTY(bool, clearLandmarks, false)
67 66 Q_PROPERTY(QList<float> pinPoints READ get_pinPoints WRITE set_pinPoints RESET reset_pinPoints STORED false)
68 67 BR_PROPERTY(QList<float>, pinPoints, QList<float>())
69 68 Q_PROPERTY(QStringList pinLabels READ get_pinLabels WRITE set_pinLabels RESET reset_pinLabels STORED false)
70 69 BR_PROPERTY(QStringList, pinLabels, QStringList())
  70 + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false)
  71 + BR_PROPERTY(QString, inputVariable, "FrontalFace")
71 72  
72 73 Resource<StasmCascadeClassifier> stasmCascadeResource;
73 74  
... ... @@ -77,86 +78,119 @@ class StasmTransform : public UntrainableTransform
77 78 stasmCascadeResource.setResourceMaker(new StasmResourceMaker());
78 79 }
79 80  
80   - void project(const Template &src, Template &dst) const
  81 + QList<QPointF> convertLandmarks(int nLandmarks, float *landmarks) const
81 82 {
82   - Mat stasmSrc(src);
83   - if (src.m().channels() == 3)
84   - cvtColor(src, stasmSrc, CV_BGR2GRAY);
85   - else if (src.m().channels() != 1)
86   - qFatal("Stasm expects single channel matrices.");
87   -
88   - if (!stasmSrc.isContinuous())
89   - qFatal("Stasm expects continuous matrix data.");
90   - dst = src;
91   -
92   - int foundFace = 0;
93   - int nLandmarks = stasm_NLANDMARKS;
94   - float landmarks[2 * stasm_NLANDMARKS];
95   -
96   - bool searchPinned = false;
97   -
98   - QPointF rightEye, leftEye;
99   - /* Two use cases are accounted for:
100   - * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively.
101   - * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively.
102   - * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for
103   - * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform).
104   - * If both cases fail, we default to stasm_search_single. */
105   -
106   - if (!pinPoints.isEmpty() && src.file.contains("Affine_0") && src.file.contains("Affine_1")) {
107   - rightEye = QPointF(pinPoints.at(0), pinPoints.at(1));
108   - leftEye = QPointF(pinPoints.at(2), pinPoints.at(3));
109   - searchPinned = true;
110   - } else if (!pinLabels.isEmpty()) {
111   - rightEye = src.file.get<QPointF>(pinLabels.at(0), QPointF());
112   - leftEye = src.file.get<QPointF>(pinLabels.at(1), QPointF());
113   - searchPinned = true;
114   - }
115   -
116   - if (searchPinned) {
117   - float pins[2 * stasm_NLANDMARKS];
118   -
119   - for (int i = 0; i < nLandmarks; i++) {
120   - if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); }
121   - else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); }
122   - else { pins[2*i] = 0; pins[2*i+1] = 0; }
123   - }
  83 + if (stasm3Format)
  84 + stasm_convert_shape(landmarks, 76);
124 85  
125   - stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL);
126   -
127   - // The ASM in Stasm is guaranteed to converge in this case
128   - foundFace = 1;
  86 + QList<QPointF> points;
  87 + for (int i = 0; i < nLandmarks; i++) {
  88 + QPointF point(landmarks[2 * i], landmarks[2 * i + 1]);
  89 + points.append(point);
129 90 }
130 91  
131   - if (!foundFace) {
132   - StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire();
133   - stasm_search_single(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, NULL, NULL);
134   - stasmCascadeResource.release(stasmCascade);
135   - }
  92 + return points;
  93 + }
136 94  
137   - if (stasm3Format) {
138   - nLandmarks = 76;
139   - stasm_convert_shape(landmarks, nLandmarks);
140   - }
  95 + void project(const Template &src, Template &dst) const
  96 + {
  97 + TemplateList temp;
  98 + project(TemplateList() << src, temp);
  99 + if (!temp.isEmpty()) dst = temp.first();
  100 + }
141 101  
142   - // For convenience, if these are the only points/rects we want to deal with as the algorithm progresses
143   - if (clearLandmarks) {
144   - dst.file.clearPoints();
145   - dst.file.clearRects();
146   - }
  102 + void project(const TemplateList &src, TemplateList &dst) const
  103 + {
  104 + foreach (const Template &t, src) {
  105 + Mat stasmSrc(t);
  106 + if (t.m().channels() == 3)
  107 + cvtColor(t, stasmSrc, CV_BGR2GRAY);
  108 + else if (t.m().channels() != 1)
  109 + qFatal("Stasm expects single channel matrices.");
  110 +
  111 + if (!stasmSrc.isContinuous())
  112 + qFatal("Stasm expects continuous matrix data.");
  113 +
  114 + int foundFace = 0;
  115 + int nLandmarks = stasm_NLANDMARKS;
  116 + float landmarks[2 * stasm_NLANDMARKS];
  117 +
  118 + bool searchPinned = false;
  119 +
  120 + QPointF rightEye, leftEye;
  121 + /* Two use cases are accounted for:
  122 + * 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively.
  123 + * 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively.
  124 + * Currently, we only support normalization with a transformation such that the src file contains Affine_0 and Affine_1. Checking for
  125 + * these keys prevents us from pinning eyes on a face that wasn't actually transformed (see AffineTransform).
  126 + * If both cases fail, we default to stasm_search_single. */
  127 +
  128 + if (!pinPoints.isEmpty() && t.file.contains("Affine_0") && t.file.contains("Affine_1")) {
  129 + rightEye = QPointF(pinPoints.at(0), pinPoints.at(1));
  130 + leftEye = QPointF(pinPoints.at(2), pinPoints.at(3));
  131 + searchPinned = true;
  132 + } else if (!pinLabels.isEmpty()) {
  133 + rightEye = t.file.get<QPointF>(pinLabels.at(0), QPointF());
  134 + leftEye = t.file.get<QPointF>(pinLabels.at(1), QPointF());
  135 + searchPinned = true;
  136 + }
  137 +
  138 + if (searchPinned) {
  139 + float pins[2 * stasm_NLANDMARKS];
  140 +
  141 + for (int i = 0; i < nLandmarks; i++) {
  142 + if (i == 38) /* Stasm Right Eye */ { pins[2*i] = rightEye.x(); pins[2*i+1] = rightEye.y(); }
  143 + else if (i == 39) /* Stasm Left Eye */ { pins[2*i] = leftEye.x(); pins[2*i+1] = leftEye.y(); }
  144 + else { pins[2*i] = 0; pins[2*i+1] = 0; }
  145 + }
  146 +
  147 + stasm_search_pinned(landmarks, pins, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, NULL);
  148 +
  149 + // The ASM in Stasm is guaranteed to converge in this case
  150 + foundFace = 1;
  151 + Template u(t.file, t.m());
  152 + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks);
  153 + u.file.set("StasmRightEye", points[38]);
  154 + u.file.set("StasmLeftEye", points[39]);
  155 + u.file.appendPoints(points);
  156 + dst.append(u);
  157 + }
147 158  
148   - if (!foundFace) {
149   - if (Globals->verbose) qWarning("No face found in %s.", qPrintable(src.file.fileName()));
150   - dst.file.fte = true;
151   - } else {
152   - QList<QPointF> points;
153   - for (int i = 0; i < nLandmarks; i++) {
154   - QPointF point(landmarks[2 * i], landmarks[2 * i + 1]);
155   - points.append(point);
  159 + if (!foundFace) {
  160 + StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire();
  161 + foundFace = 1;
  162 + stasm::FaceDet detection;
  163 +
  164 + QList<QRectF> rects = t.file.contains(inputVariable) ? QList<QRectF>() << t.file.get<QRectF>(inputVariable) : t.file.rects();
  165 + if (!rects.isEmpty()) detection.iface_ = 0;
  166 + for (int i=0; i<rects.size(); i++) {
  167 + Rect rect = OpenCVUtils::toRect(rects[i]);
  168 + stasm::DetPar detpar;
  169 + detpar.x = rect.x + rect.width / 2.;
  170 + detpar.y = rect.y + rect.height / 2.;
  171 + detpar.width = double(rect.width);
  172 + detpar.height = double(rect.height);
  173 + detpar.yaw = 0;
  174 + detpar.eyaw = stasm::EYAW00;
  175 + detection.detpars_.push_back(detpar);
  176 + }
  177 +
  178 + while (foundFace) {
  179 + stasm_search_auto(&foundFace, landmarks, reinterpret_cast<const char*>(stasmSrc.data), stasmSrc.cols, stasmSrc.rows, *stasmCascade, detection);
  180 + if (foundFace) {
  181 + Template u(t.file, t.m());
  182 + QList<QPointF> points = convertLandmarks(nLandmarks, landmarks);
  183 + u.file.set("StasmRightEye", points[38]);
  184 + u.file.set("StasmLeftEye", points[39]);
  185 + u.file.appendPoints(points);
  186 + dst.append(u);
  187 + }
  188 +
  189 + if (!Globals->enrollAll)
  190 + break;
  191 + }
  192 + stasmCascadeResource.release(stasmCascade);
156 193 }
157   - dst.file.set("StasmRightEye", points[38]);
158   - dst.file.set("StasmLeftEye", points[39]);
159   - dst.file.appendPoints(points);
160 194 }
161 195 }
162 196 };
... ...
openbr/plugins/output/knn.cpp
... ... @@ -6,44 +6,80 @@
6 6 namespace br
7 7 {
8 8  
  9 +typedef QPair<float,int> Pair;
  10 +
9 11 /*!
10 12 * \ingroup outputs
11 13 * \brief Outputs the k-Nearest Neighbors from the gallery for each probe.
12 14 * \author Ben Klein \cite bhklein
13 15 */
14   -class knnOutput : public MatrixOutput
  16 +class knnOutput : public Output
15 17 {
16 18 Q_OBJECT
17 19  
  20 + int rowBlock, columnBlock;
  21 + size_t headerSize, k;
  22 + cv::Mat blockScores;
  23 +
18 24 ~knnOutput()
19 25 {
20   - size_t num_probes = (size_t)queryFiles.size();
21   - if (targetFiles.isEmpty() || queryFiles.isEmpty()) return;
22   - size_t k = file.get<size_t>("k", 20);
  26 + writeBlock();
  27 + }
23 28  
24   - if ((size_t)targetFiles.size() < k)
25   - qFatal("Gallery size %s is smaller than k = %s.", qPrintable(QString::number(targetFiles.size())), qPrintable(QString::number(k)));
  29 + void setBlock(int rowBlock, int columnBlock)
  30 + {
  31 + if ((rowBlock == 0) && (columnBlock == 0)) {
  32 + k = file.get<size_t>("k", 20);
  33 + QFile f(file);
  34 + if (!f.open(QFile::WriteOnly))
  35 + qFatal("Unable to open %s for writing.", qPrintable(file));
  36 + size_t querySize = (size_t)queryFiles.size();
  37 + f.write((const char*) &querySize, sizeof(size_t));
  38 + f.write((const char*) &k, sizeof(size_t));
  39 + headerSize = 2 * sizeof(size_t);
  40 + } else {
  41 + writeBlock();
  42 + }
26 43  
27   - QFile f(file);
28   - if (!f.open(QFile::WriteOnly))
29   - qFatal("Unable to open %s for writing.", qPrintable(file));
30   - f.write((const char*) &num_probes, sizeof(size_t));
31   - f.write((const char*) &k, sizeof(size_t));
  44 + this->rowBlock = rowBlock;
  45 + this->columnBlock = columnBlock;
  46 +
  47 + int matrixRows = std::min(queryFiles.size()-rowBlock*this->blockRows, blockRows);
  48 + int matrixCols = std::min(targetFiles.size()-columnBlock*this->blockCols, blockCols);
  49 +
  50 + blockScores = cv::Mat(matrixRows, matrixCols, CV_32FC1);
  51 + }
32 52  
33   - QVector<Candidate> neighbors; neighbors.reserve(num_probes*k);
  53 + void setRelative(float value, int i, int j)
  54 + {
  55 + blockScores.at<float>(i,j) = value;
  56 + }
  57 +
  58 + void set(float value, int i, int j)
  59 + {
  60 + (void) value; (void) i; (void) j;
  61 + qFatal("Logic error.");
  62 + }
  63 +
  64 + void writeBlock()
  65 + {
  66 + QFile f(file);
  67 + if (!f.open(QFile::ReadWrite))
  68 + qFatal("Unable to open %s for modifying.", qPrintable(file));
  69 + QVector<Candidate> neighbors; neighbors.reserve(k * blockScores.rows);
34 70  
35   - for (size_t i=0; i<num_probes; i++) {
36   - typedef QPair<float,int> Pair;
  71 + for (int i=0; i<blockScores.rows; i++) {
37 72 size_t rank = 0;
38   - foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) {
39   - if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) {
  73 + foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(blockScores.row(i)), true)) {
  74 + if (QString(targetFiles[pair.second]) != QString(queryFiles[rowBlock*this->blockRows+i])) {
40 75 Candidate candidate((size_t)pair.second, pair.first);
41 76 neighbors.push_back(candidate);
42 77 if (++rank >= k) break;
43 78 }
44 79 }
45 80 }
46   - f.write((const char*) neighbors.data(), num_probes * k * sizeof(Candidate));
  81 + f.seek(headerSize + sizeof(Candidate)*quint64(rowBlock*this->blockRows)*k);
  82 + f.write((const char*) neighbors.data(), blockScores.rows * k * sizeof(Candidate));
47 83 f.close();
48 84 }
49 85 };
... ...
openbr/plugins/representation/haar.cpp
... ... @@ -80,23 +80,25 @@ class HaarRepresentation : public Representation
80 80 }
81 81 }
82 82  
83   - void preprocess(const Mat &src, Mat &dst) const
  83 + Template preprocess(const Template &src) const
84 84 {
  85 + Template dst;
85 86 integral(src, dst);
  87 + return dst;
86 88 }
87 89  
88   - float evaluate(const Mat &image, int idx) const
  90 + float evaluate(const Template &src, int idx) const
89 91 {
90   - return (float)features[idx].calc(image);
  92 + return (float)features[idx].calc(src.m());
91 93 }
92 94  
93   - Mat evaluate(const Mat &image, const QList<int> &indices) const
  95 + Mat evaluate(const Template &src, const QList<int> &indices) const
94 96 {
95 97 int size = indices.empty() ? numFeatures() : indices.size();
96 98  
97 99 Mat result(1, size, CV_32FC1);
98 100 for (int i = 0; i < size; i++)
99   - result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  101 + result.at<float>(i) = evaluate(src, indices.empty() ? i : indices[i]);
100 102 return result;
101 103 }
102 104  
... ...
openbr/plugins/representation/mblbp.cpp
... ... @@ -39,32 +39,36 @@ class MBLBPRepresentation : public Representation
39 39  
40 40 void init()
41 41 {
42   - int offset = winWidth + 1;
43   - for (int x = 0; x < winWidth; x++ )
44   - for (int y = 0; y < winHeight; y++ )
45   - for (int w = 1; w <= winWidth / 3; w++ )
46   - for (int h = 1; h <= winHeight / 3; h++ )
47   - if ((x+3*w <= winWidth) && (y+3*h <= winHeight) )
48   - features.append(Feature(offset, x, y, w, h ) );
  42 + if (features.isEmpty()) {
  43 + int offset = winWidth + 1;
  44 + for (int x = 0; x < winWidth; x++ )
  45 + for (int y = 0; y < winHeight; y++ )
  46 + for (int w = 1; w <= winWidth / 3; w++ )
  47 + for (int h = 1; h <= winHeight / 3; h++ )
  48 + if ((x+3*w <= winWidth) && (y+3*h <= winHeight) )
  49 + features.append(Feature(offset, x, y, w, h ) );
  50 + }
49 51 }
50 52  
51   - void preprocess(const Mat &src, Mat &dst) const
  53 + Template preprocess(const Template &src) const
52 54 {
  55 + Template dst;
53 56 integral(src, dst);
  57 + return dst;
54 58 }
55 59  
56   - float evaluate(const Mat &image, int idx) const
  60 + float evaluate(const Template &src, int idx) const
57 61 {
58   - return (float)features[idx].calc(image);
  62 + return (float)features[idx].calc(src.m());
59 63 }
60 64  
61   - Mat evaluate(const Mat &image, const QList<int> &indices) const
  65 + Mat evaluate(const Template &src, const QList<int> &indices) const
62 66 {
63 67 int size = indices.empty() ? numFeatures() : indices.size();
64 68  
65 69 Mat result(1, size, CV_32FC1);
66 70 for (int i = 0; i < size; i++)
67   - result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  71 + result.at<float>(i) = evaluate(src, indices.empty() ? i : indices[i]);
68 72 return result;
69 73 }
70 74  
... ...
openbr/plugins/representation/npd.cpp
... ... @@ -16,23 +16,25 @@ class NPDRepresentation : public Representation
16 16  
17 17 void init()
18 18 {
19   - for (int p1 = 0; p1 < (winWidth * winHeight); p1++)
20   - for (int p2 = p1; p2 < (winWidth * winHeight); p2++)
21   - features.append(Feature(p1, p2));
  19 + if (features.isEmpty()) {
  20 + for (int p1 = 0; p1 < (winWidth * winHeight); p1++)
  21 + for (int p2 = p1; p2 < (winWidth * winHeight); p2++)
  22 + features.append(Feature(p1, p2));
  23 + }
22 24 }
23 25  
24   - float evaluate(const Mat &image, int idx) const
  26 + float evaluate(const Template &src, int idx) const
25 27 {
26   - return features[idx].calc(image);
  28 + return features[idx].calc(src.m());
27 29 }
28 30  
29   - Mat evaluate(const Mat &image, const QList<int> &indices) const
  31 + Mat evaluate(const Template &src, const QList<int> &indices) const
30 32 {
31 33 int size = indices.empty() ? numFeatures() : indices.size();
32 34  
33 35 Mat result(1, size, CV_32FC1);
34 36 for (int i = 0; i < size; i++)
35   - result.at<float>(i) = evaluate(image, indices.empty() ? i : indices[i]);
  37 + result.at<float>(i) = evaluate(src, indices.empty() ? i : indices[i]);
36 38 return result;
37 39 }
38 40  
... ... @@ -49,10 +51,10 @@ class NPDRepresentation : public Representation
49 51 struct Feature
50 52 {
51 53 Feature() {}
52   - Feature(int p1, int p2) { p[0] = p1; p[1] = p2; }
  54 + Feature(int p0, int p1) : p0(p0), p1(p1) {}
53 55 float calc(const Mat &image) const;
54 56  
55   - int p[2];
  57 + int p0, p1;
56 58 };
57 59 QList<Feature> features;
58 60 };
... ... @@ -61,9 +63,9 @@ BR_REGISTER(Representation, NPDRepresentation)
61 63  
62 64 inline float NPDRepresentation::Feature::calc(const Mat &image) const
63 65 {
64   - const int *ptr = image.ptr<int>();
65   - int v1 = ptr[p[0]], v2 = ptr[p[1]];
66   - return v1 == 0 && v2 == 0 ? 0 : ((float)(v1 - v2)) / (v1 + v2);
  66 + const uchar *ptr = image.ptr();
  67 + int v1 = (int)ptr[p0], v2 = (int)ptr[p1];
  68 + return (v1 + v2) == 0 ? 0 : (1.0 * (v1 - v2)) / (v1 + v2);
67 69 }
68 70  
69 71 } // namespace br
... ...
openbr/plugins/representation/random.cpp
... ... @@ -28,9 +28,9 @@ class RandomRepresentation : public Representation
28 28  
29 29 QList<int> features;
30 30  
31   - void train(const QList<Mat> &images, const QList<float> &labels)
  31 + void train(const TemplateList &data)
32 32 {
33   - representation->train(images, labels);
  33 + representation->train(data);
34 34  
35 35 const int nFeatures = representation->numFeatures();
36 36  
... ... @@ -40,17 +40,17 @@ class RandomRepresentation : public Representation
40 40 features = Common::RandSample(count,nFeatures,0,true);
41 41 }
42 42  
43   - void preprocess(const Mat &src, Mat &dst) const
  43 + Template preprocess(const Template &src) const
44 44 {
45   - representation->preprocess(src,dst);
  45 + return representation->preprocess(src);
46 46 }
47 47  
48   - float evaluate(const Mat &image, int idx) const
  48 + float evaluate(const Template &src, int idx) const
49 49 {
50   - return representation->evaluate(image,features[idx]);
  50 + return representation->evaluate(src, features[idx]);
51 51 }
52 52  
53   - Mat evaluate(const Mat &image, const QList<int> &indices) const
  53 + Mat evaluate(const Template &src, const QList<int> &indices) const
54 54 {
55 55 QList<int> newIndices;
56 56 if (indices.empty())
... ... @@ -59,7 +59,7 @@ class RandomRepresentation : public Representation
59 59 for (int i = 0; i < indices.size(); i++)
60 60 newIndices.append(features[indices[i]]);
61 61  
62   - return representation->evaluate(image,newIndices);
  62 + return representation->evaluate(src, newIndices);
63 63 }
64 64  
65 65 int numFeatures() const
... ... @@ -108,6 +108,3 @@ BR_REGISTER(Representation, RandomRepresentation)
108 108 } // namespace br
109 109  
110 110 #include "representation/random.moc"
111   -
112   -
113   -
... ...
scripts/face_cluster_viz.py renamed to scripts/brpy/face_cluster_viz.py
1 1 #!/usr/bin/env python
2 2  
3   -from PIL import Image
4 3 import csv, sys, json, argparse
  4 +from brpy.html_viz import crop_to_bb
5 5  
6 6 parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.')
7 7 parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)')
... ... @@ -16,24 +16,15 @@ clustmap = dict()
16 16 with open(args.input_file) as f:
17 17 for line in csv.DictReader(f):
18 18 c = int(line[args.cluster_key])
  19 + if c not in clustmap:
  20 + clustmap[c] = []
19 21 x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ]
20 22 imname = '%s/%s' % (args.img_loc, line['File'])
21 23 try:
22   - img = Image.open(imname)
23   - imwidth, imheight = img.size
  24 + html = crop_to_bb(x,y,width,height,imname,maxheight=400)
24 25 except IOError:
25 26 print('problem with %s' % imname)
26 27 continue
27   - ratio = maxheight / height
28   - # note for future me:
29   - # image is cropped with div width/height + overflow:hidden,
30   - # resized with img height,
31   - # and positioned with img margin
32   - html = '<div style="overflow:hidden;display:inline-block;width:%ipx;height:%ipx;">' % (width*ratio, maxheight)
33   - html += '<img src="%s" style="height:%ipx;margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio)
34   - html += '</div>'
35   - if c not in clustmap:
36   - clustmap[c] = []
37 28 clustmap[c].append(html)
38 29  
39 30 # browsers crash for a DOM with this many img tags,
... ...
scripts/brpy/html_viz.py 0 → 100644
  1 +'''
  2 +Some funcs to generate HTML visualizations.
  3 +Run from the folder you intend to save the HTML page so
  4 +the relative paths in the HTML file are correct and PIL can find the images on disk.
  5 +Requires local images, but should be pretty easy to set up an apache server (or whatev)
  6 +and host them as long as the relative paths remain the same on ya serva.
  7 +'''
  8 +
  9 +from PIL import Image
  10 +
  11 +def crop_to_bb(x, y, width, height, imname, maxheight=None):
  12 + '''
  13 + Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels.
  14 + A maxheight of None will keep the original size (default).
  15 + When two crops are put next to each other, they will be inline. To make each crop its own line, wrap it in a div.
  16 + '''
  17 + img = Image.open(imname)
  18 + imwidth, imheight = img.size
  19 + if not maxheight:
  20 + maxheight = height
  21 + ratio = maxheight / height
  22 + # note for future me:
  23 + # image is cropped with div width/height + overflow:hidden,
  24 + # resized with img height,
  25 + # and positioned with img margin
  26 + html = '<div style="overflow:hidden; display:inline-block; width:%ipx; height:%ipx;">' % (width*ratio, maxheight)
  27 + html += '<img src="%s" style="height:%ipx; margin:-%ipx 0 0 -%ipx;"/>' % (imname, imheight*ratio, y*ratio, x*ratio)
  28 + html += '</div>'
  29 + return html
  30 +
  31 +def bbs_for_image(imname, bbs, maxheight=None, colors=None):
  32 + '''
  33 + Generates an HTML string for an image with bounding boxes.
  34 + bbs: iterable of (x,y,width,height) bounding box tuples
  35 + '''
  36 + img = Image.open(imname)
  37 + imwidth, imheight = img.size
  38 + if not maxheight:
  39 + maxheight = imheight
  40 + ratio = maxheight/imheight
  41 + html = [
  42 + '<div style="position:relative">',
  43 + '<img src="%s" style="height:%ipx" />' % (imname, maxheight)
  44 + ]
  45 + if not colors:
  46 + colors = ['green']*len(bbs)
  47 + html.extend([ bb(*box, ratio=ratio, color=color) for color,box in zip(colors,bbs) ])
  48 + html.append('</div>')
  49 + return '\n'.join(html)
  50 +
  51 +
  52 +def bb(x, y, width, height, ratio=1.0, color='green'):
  53 + '''
  54 + Generates an HTML string bounding box.
  55 + '''
  56 + html = '<div style="position:absolute; border:2px solid %s; color:%s; left:%ipx; top:%ipx; width:%ipx; height:%ipx;"></div>'
  57 + return html % (color, color, x*ratio, y*ratio, width*ratio, height*ratio)
... ...
share/openbr/cmake/InstallDependencies.cmake
... ... @@ -86,12 +86,14 @@ endfunction()
86 86 function(install_qt_platforms)
87 87 if(${BR_INSTALL_DEPENDENCIES})
88 88 if(CMAKE_HOST_WIN32)
89   - #TODO
  89 + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/qwindows.dll
  90 + DESTINATION bin/platforms)
90 91 elseif(CMAKE_HOST_APPLE)
91 92 install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqcocoa.dylib
92 93 DESTINATION bin/platforms)
93 94 else()
94   - #TODO
  95 + install(FILES ${_qt5Core_install_prefix}/plugins/platforms/libqlinuxfb.so
  96 + DESTINATION bin/platforms)
95 97 endif()
96 98 endif()
97 99 endfunction()
... ...
share/openbr/plotting/plot_utils.R
... ... @@ -217,6 +217,12 @@ formatData &lt;- function(type=&quot;eval&quot;) {
217 217 NormLength <<- data[grep("NormLength",data$Plot),-c(1)]
218 218 sample <<- readImageData(Sample)
219 219 rows <<- sample[[1]]$value
  220 + } else if (type == "knn") {
  221 + # Split data into individual plots
  222 + IET <<- data[grep("IET",data$Plot),-c(1)]
  223 + IET$Y <<- as.numeric(as.character(IET$Y))
  224 + CMC <<- data[grep("CMC",data$Plot),-c(1)]
  225 + CMC$Y <<- as.numeric(as.character(CMC$Y))
220 226 }
221 227 }
222 228  
... ...