Commit a3d263f0f600b94f2a72b247b0f22529f1dfc701

Authored by Brendan Klare
2 parents 3ced7334 4fb48f36

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

.gitignore
@@ -41,4 +41,9 @@ scripts/results @@ -41,4 +41,9 @@ scripts/results
41 data/INRIAPerson/sigset 41 data/INRIAPerson/sigset
42 data/KTH/sigset 42 data/KTH/sigset
43 data/CaltechPedestrians/annotations 43 data/CaltechPedestrians/annotations
  44 +data/CaltechPedestrians/*.xml
44 45
  46 +### Sublime ###
  47 +*.check_cache
  48 +*.sublime-project
  49 +*.sublime-workspace
README.md
@@ -14,4 +14,3 @@ To optionally check out a particular [tagged release](https://github.com/biometr @@ -14,4 +14,3 @@ To optionally check out a particular [tagged release](https://github.com/biometr
14 14
15 15
16 [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/biometrics/openbr/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 16 [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/biometrics/openbr/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
17 -  
app/CMakeLists.txt
@@ -5,4 +5,6 @@ add_subdirectory(br) @@ -5,4 +5,6 @@ add_subdirectory(br)
5 add_subdirectory(examples) 5 add_subdirectory(examples)
6 6
7 # Build OpenBR GUI application 7 # Build OpenBR GUI application
8 -add_subdirectory(br-gui) 8 +if(NOT ${BR_EMBEDDED})
  9 + add_subdirectory(br-gui)
  10 +endif()
app/br/br.cpp
@@ -158,15 +158,24 @@ public: @@ -158,15 +158,24 @@ public:
158 } else if (!strcmp(fun, "plotMetadata")) { 158 } else if (!strcmp(fun, "plotMetadata")) {
159 check(parc >= 2, "Incorrect parameter count for 'plotMetadata'."); 159 check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
160 br_plot_metadata(parc-1, parv, parv[parc-1], true); 160 br_plot_metadata(parc-1, parv, parv[parc-1], true);
  161 + } else if (!strcmp(fun, "deduplicate")) {
  162 + check(parc == 3, "Incorrect parameter count for 'deduplicate'.");
  163 + br_deduplicate(parv[0], parv[1], parv[2]);
161 } 164 }
162 165
163 // Miscellaneous 166 // Miscellaneous
164 else if (!strcmp(fun, "help")) { 167 else if (!strcmp(fun, "help")) {
165 check(parc == 0, "No parameters expected for 'help'."); 168 check(parc == 0, "No parameters expected for 'help'.");
166 help(); 169 help();
  170 + } else if (!strcmp(fun, "gui")) {
  171 + // Do nothing because we checked for this flag prior to initialization
167 } else if (!strcmp(fun, "objects")) { 172 } else if (!strcmp(fun, "objects")) {
168 check(parc <= 2, "Incorrect parameter count for 'objects'."); 173 check(parc <= 2, "Incorrect parameter count for 'objects'.");
169 - printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*")); 174 + int size = br_objects(NULL, 0, parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*");
  175 + char * temp = new char[size];
  176 + br_objects(temp, size, parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*");
  177 + printf("%s\n", temp);
  178 + delete [] temp;
170 } else if (!strcmp(fun, "about")) { 179 } else if (!strcmp(fun, "about")) {
171 check(parc == 0, "No parameters expected for 'about'."); 180 check(parc == 0, "No parameters expected for 'about'.");
172 printf("%s\n", br_about()); 181 printf("%s\n", br_about());
@@ -177,11 +186,10 @@ public: @@ -177,11 +186,10 @@ public:
177 check(parc == 1, "Incorrect parameter count for 'daemon'."); 186 check(parc == 1, "Incorrect parameter count for 'daemon'.");
178 daemon = true; 187 daemon = true;
179 daemon_pipe = parv[0]; 188 daemon_pipe = parv[0];
180 - } else if (!strcmp(fun,"slave")) { 189 + } else if (!strcmp(fun, "slave")) {
181 check(parc == 1, "Incorrect parameter count for 'slave'"); 190 check(parc == 1, "Incorrect parameter count for 'slave'");
182 br_slave_process(parv[0]); 191 br_slave_process(parv[0]);
183 - }  
184 - else if (!strcmp(fun, "exit")) { 192 + } else if (!strcmp(fun, "exit")) {
185 check(parc == 0, "No parameters expected for 'exit'."); 193 check(parc == 0, "No parameters expected for 'exit'.");
186 daemon = false; 194 daemon = false;
187 } else if (!strcmp(fun, "getHeader")) { 195 } else if (!strcmp(fun, "getHeader")) {
@@ -245,6 +253,7 @@ private: @@ -245,6 +253,7 @@ private:
245 "\n" 253 "\n"
246 "==== Miscellaneous ====\n" 254 "==== Miscellaneous ====\n"
247 "-help\n" 255 "-help\n"
  256 + "-gui\n"
248 "-objects [abstraction [implementation]]\n" 257 "-objects [abstraction [implementation]]\n"
249 "-about\n" 258 "-about\n"
250 "-version\n" 259 "-version\n"
@@ -255,7 +264,7 @@ private: @@ -255,7 +264,7 @@ private:
255 264
256 int main(int argc, char *argv[]) 265 int main(int argc, char *argv[])
257 { 266 {
258 - br_initialize(argc, argv); 267 + br_initialize(argc, argv, "", argc >= 2 && !strcmp(argv[1], "-gui"));
259 268
260 FakeMain *fakeMain = new FakeMain(argc, argv); 269 FakeMain *fakeMain = new FakeMain(argc, argv);
261 QThreadPool::globalInstance()->start(fakeMain); 270 QThreadPool::globalInstance()->start(fakeMain);
openbr/CMakeLists.txt
@@ -34,7 +34,7 @@ set(JANUS_BUILD_PP5_WRAPPER ${BR_WITH_PP5} CACHE BOOL &quot;Build Janus implementatio @@ -34,7 +34,7 @@ set(JANUS_BUILD_PP5_WRAPPER ${BR_WITH_PP5} CACHE BOOL &quot;Build Janus implementatio
34 set(JANUS_BUILD_DOCS ${BR_BUILD_DOCUMENTATION} CACHE BOOL "Build Janus HTML Doxygen documentation") 34 set(JANUS_BUILD_DOCS ${BR_BUILD_DOCUMENTATION} CACHE BOOL "Build Janus HTML Doxygen documentation")
35 mark_as_advanced(JANUS_BUILD_PP5_WRAPPER) 35 mark_as_advanced(JANUS_BUILD_PP5_WRAPPER)
36 mark_as_advanced(JANUS_BUILD_DOCS) 36 mark_as_advanced(JANUS_BUILD_DOCS)
37 -set(JANUS_TEST_IMPLEMENTATION openbr) 37 +set(JANUS_IMPLEMENTATION openbr)
38 add_subdirectory(janus) 38 add_subdirectory(janus)
39 39
40 # Install 40 # Install
openbr/core/bee.cpp
@@ -172,6 +172,8 @@ Mat BEE::readMat(const br::File &amp;matrix, QString *targetSigset, QString *querySi @@ -172,6 +172,8 @@ Mat BEE::readMat(const br::File &amp;matrix, QString *targetSigset, QString *querySi
172 qint64 read = file.read((char*)m.data, bytesExpected); 172 qint64 read = file.read((char*)m.data, bytesExpected);
173 if (read != bytesExpected) 173 if (read != bytesExpected)
174 qFatal("Invalid matrix size."); 174 qFatal("Invalid matrix size.");
  175 + if (!file.atEnd())
  176 + qFatal("Expected matrix end of file.");
175 file.close(); 177 file.close();
176 178
177 Mat result; 179 Mat result;
@@ -303,11 +305,14 @@ cv::Mat BEE::makeMask(const br::FileList &amp;targets, const br::FileList &amp;queries, @@ -303,11 +305,14 @@ cv::Mat BEE::makeMask(const br::FileList &amp;targets, const br::FileList &amp;queries,
303 QList<int> targetPartitions = targets.crossValidationPartitions(); 305 QList<int> targetPartitions = targets.crossValidationPartitions();
304 QList<int> queryPartitions = queries.crossValidationPartitions(); 306 QList<int> queryPartitions = queries.crossValidationPartitions();
305 307
  308 + QList<bool> targetsOnly = File::get<bool>(queries, "targetOnly", false);
  309 +
306 Mat mask(queries.size(), targets.size(), CV_8UC1); 310 Mat mask(queries.size(), targets.size(), CV_8UC1);
307 for (int i=0; i<queries.size(); i++) { 311 for (int i=0; i<queries.size(); i++) {
308 const QString &fileA = queries[i]; 312 const QString &fileA = queries[i];
309 const QString labelA = queryLabels[i]; 313 const QString labelA = queryLabels[i];
310 const int partitionA = queryPartitions[i]; 314 const int partitionA = queryPartitions[i];
  315 + const bool targetOnly = targetsOnly[i];
311 316
312 for (int j=0; j<targets.size(); j++) { 317 for (int j=0; j<targets.size(); j++) {
313 const QString &fileB = targets[j]; 318 const QString &fileB = targets[j];
@@ -316,6 +321,7 @@ cv::Mat BEE::makeMask(const br::FileList &amp;targets, const br::FileList &amp;queries, @@ -316,6 +321,7 @@ cv::Mat BEE::makeMask(const br::FileList &amp;targets, const br::FileList &amp;queries,
316 321
317 Mask_t val; 322 Mask_t val;
318 if (fileA == fileB) val = DontCare; 323 if (fileA == fileB) val = DontCare;
  324 + else if (targetOnly) val = DontCare;
319 else if (labelA == "-1") val = DontCare; 325 else if (labelA == "-1") val = DontCare;
320 else if (labelB == "-1") val = DontCare; 326 else if (labelB == "-1") val = DontCare;
321 else if (partitionA != partition) val = DontCare; 327 else if (partitionA != partition) val = DontCare;
openbr/core/cluster.cpp
@@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
21 #include <QSet> 21 #include <QSet>
22 #include <limits> 22 #include <limits>
23 #include <openbr/openbr_plugin.h> 23 #include <openbr/openbr_plugin.h>
  24 +#include <assert.h>
24 25
25 #include "openbr/core/bee.h" 26 #include "openbr/core/bee.h"
26 #include "openbr/core/cluster.h" 27 #include "openbr/core/cluster.h"
openbr/core/core.cpp
@@ -65,6 +65,9 @@ struct AlgorithmCore @@ -65,6 +65,9 @@ struct AlgorithmCore
65 downcast->train(data); 65 downcast->train(data);
66 66
67 if (!distance.isNull()) { 67 if (!distance.isNull()) {
  68 + if (Globals->crossValidate > 0)
  69 + for (int i=data.size()-1; i>=0; i--) if (data[i].file.get<bool>("allPartitions",false)) data.removeAt(i);
  70 +
68 qDebug("Projecting Enrollment"); 71 qDebug("Projecting Enrollment");
69 downcast->projectUpdate(data,data); 72 downcast->projectUpdate(data,data);
70 73
@@ -251,6 +254,56 @@ struct AlgorithmCore @@ -251,6 +254,56 @@ struct AlgorithmCore
251 Globals->blockSize = old_block_size; 254 Globals->blockSize = old_block_size;
252 } 255 }
253 256
  257 + void deduplicate(const File &inputGallery, const File &outputGallery, const float threshold)
  258 + {
  259 + qDebug("Deduplicating %s to %s with a score threshold of %f", qPrintable(inputGallery.flat()), qPrintable(outputGallery.flat()), threshold);
  260 +
  261 + if (distance.isNull()) qFatal("Null distance.");
  262 +
  263 + QScopedPointer<Gallery> i;
  264 + FileList inputFiles;
  265 + retrieveOrEnroll(inputGallery, i, inputFiles);
  266 +
  267 + TemplateList t = i->read();
  268 +
  269 + Output *o = Output::make(QString("buffer.tail[selfSimilar,threshold=%1,atLeast=0]").arg(QString::number(threshold)),inputFiles,inputFiles);
  270 +
  271 + // Compare to global tail output
  272 + distance->compare(t,t,o);
  273 +
  274 + delete o;
  275 +
  276 + QString buffer(Globals->buffer);
  277 +
  278 + QStringList tail = buffer.split("\n");
  279 +
  280 + // Remove header
  281 + tail.removeFirst();
  282 +
  283 + QStringList toRemove;
  284 + foreach(const QString &s, tail)
  285 + toRemove.append(s.split(',').at(1));
  286 +
  287 + QSet<QString> duplicates = QSet<QString>::fromList(toRemove);
  288 +
  289 + QStringList fileNames = inputFiles.names();
  290 +
  291 + QList<int> indices;
  292 + foreach(const QString &d, duplicates)
  293 + indices.append(fileNames.indexOf(d));
  294 +
  295 + std::sort(indices.begin(),indices.end(),std::greater<float>());
  296 +
  297 + qDebug("\n%d duplicates removed.", indices.size());
  298 +
  299 + for (int i=0; i<indices.size(); i++)
  300 + inputFiles.removeAt(indices[i]);
  301 +
  302 + QScopedPointer<Gallery> og(Gallery::make(outputGallery));
  303 +
  304 + og->writeBlock(inputFiles);
  305 + }
  306 +
254 void compare(File targetGallery, File queryGallery, File output) 307 void compare(File targetGallery, File queryGallery, File output)
255 { 308 {
256 qDebug("Comparing %s and %s%s", qPrintable(targetGallery.flat()), 309 qDebug("Comparing %s and %s%s", qPrintable(targetGallery.flat()),
@@ -496,6 +549,14 @@ void br::Cat(const QStringList &amp;inputGalleries, const QString &amp;outputGallery) @@ -496,6 +549,14 @@ void br::Cat(const QStringList &amp;inputGalleries, const QString &amp;outputGallery)
496 } 549 }
497 } 550 }
498 551
  552 +void br::Deduplicate(const File &inputGallery, const File &outputGallery, const QString &threshold)
  553 +{
  554 + bool ok;
  555 + float thresh = threshold.toFloat(&ok);
  556 + if (ok) AlgorithmManager::getAlgorithm(inputGallery.get<QString>("algorithm"))->deduplicate(inputGallery, outputGallery, thresh);
  557 + else qFatal("Unable to convert deduplication threshold to float.");
  558 +}
  559 +
499 QSharedPointer<br::Transform> br::Transform::fromAlgorithm(const QString &algorithm, bool preprocess) 560 QSharedPointer<br::Transform> br::Transform::fromAlgorithm(const QString &algorithm, bool preprocess)
500 { 561 {
501 if (!preprocess) 562 if (!preprocess)
openbr/core/opencvutils.cpp
@@ -15,7 +15,9 @@ @@ -15,7 +15,9 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/highgui/highgui.hpp> 17 #include <opencv2/highgui/highgui.hpp>
  18 +#include <opencv2/highgui/highgui_c.h>
18 #include <opencv2/imgproc/imgproc.hpp> 19 #include <opencv2/imgproc/imgproc.hpp>
  20 +#include <opencv2/imgproc/imgproc_c.h>
19 #include <openbr/openbr_plugin.h> 21 #include <openbr/openbr_plugin.h>
20 22
21 #include "opencvutils.h" 23 #include "opencvutils.h"
openbr/core/opencvutils.h
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 #include <QString> 22 #include <QString>
23 #include <QStringList> 23 #include <QStringList>
24 #include <opencv2/core/core.hpp> 24 #include <opencv2/core/core.hpp>
  25 +#include <assert.h>
25 26
26 namespace OpenCVUtils 27 namespace OpenCVUtils
27 { 28 {
openbr/gui/algorithm.cpp
1 #include <QStringList> 1 #include <QStringList>
2 #include <openbr/openbr.h> 2 #include <openbr/openbr.h>
  3 +#include <openbr/openbr_plugin.h>
3 4
4 #include "algorithm.h" 5 #include "algorithm.h"
5 6
@@ -18,7 +19,7 @@ bool br::Algorithm::addAlgorithm(const QString &amp;algorithm, const QString &amp;displa @@ -18,7 +19,7 @@ bool br::Algorithm::addAlgorithm(const QString &amp;algorithm, const QString &amp;displa
18 { 19 {
19 static QStringList availableAlgorithms; 20 static QStringList availableAlgorithms;
20 if (availableAlgorithms.isEmpty()) 21 if (availableAlgorithms.isEmpty())
21 - availableAlgorithms = QString(br_objects("Abbreviation", ".*", false)).split("\n"); 22 + availableAlgorithms = br::Context::objects("Abbreviation", ".*", false);
22 23
23 if (!availableAlgorithms.contains(algorithm)) 24 if (!availableAlgorithms.contains(algorithm))
24 return false; 25 return false;
openbr/gui/gallerytoolbar.cpp
@@ -84,7 +84,7 @@ void br::GalleryToolBar::_enroll(const br::File &amp;input) @@ -84,7 +84,7 @@ void br::GalleryToolBar::_enroll(const br::File &amp;input)
84 galleryLock.lock(); 84 galleryLock.lock();
85 this->input = input; 85 this->input = input;
86 if (input.suffix() == "gal") gallery = input.name + ".mem"; 86 if (input.suffix() == "gal") gallery = input.name + ".mem";
87 - else gallery = QString("%1/galleries/%2.gal[cache]").arg(br_scratch_path(), qPrintable(input.baseName()+input.hash())); 87 + else gallery = QString("%1/galleries/%2.gal[cache]").arg(br::Globals->scratchPath(), qPrintable(input.baseName()+input.hash()));
88 files = br::Enroll(input.flat(), gallery.flat()); 88 files = br::Enroll(input.flat(), gallery.flat());
89 galleryLock.unlock(); 89 galleryLock.unlock();
90 } 90 }
@@ -148,7 +148,7 @@ void br::GalleryToolBar::home() @@ -148,7 +148,7 @@ void br::GalleryToolBar::home()
148 148
149 void br::GalleryToolBar::mean() 149 void br::GalleryToolBar::mean()
150 { 150 {
151 - const QString file = QString("%1/mean/%2.png").arg(br_scratch_path(), input.baseName()+input.hash()); 151 + const QString file = QString("%1/mean/%2.png").arg(br::Globals->scratchPath(), input.baseName()+input.hash());
152 br_set_property("CENTER_TRAIN_B", qPrintable(file)); 152 br_set_property("CENTER_TRAIN_B", qPrintable(file));
153 br::File trainingFile = input; 153 br::File trainingFile = input;
154 br_train(qPrintable(trainingFile.flat()), "[algorithm=MedianFace]"); 154 br_train(qPrintable(trainingFile.flat()), "[algorithm=MedianFace]");
openbr/gui/progress.cpp
1 #include <openbr/openbr.h> 1 #include <openbr/openbr.h>
  2 +#include <openbr/openbr_plugin.h>
2 3
3 #include "progress.h" 4 #include "progress.h"
4 5
@@ -29,7 +30,7 @@ void br::Progress::checkProgress() @@ -29,7 +30,7 @@ void br::Progress::checkProgress()
29 const bool visible = progress >= 0 && progress < 100; 30 const bool visible = progress >= 0 && progress < 100;
30 31
31 if (visible) { 32 if (visible) {
32 - showMessage(br_most_recent_message()); 33 + showMessage(Globals->mostRecentMessage);
33 pbProgress.setValue(progress); 34 pbProgress.setValue(progress);
34 if (progress > 100) pbProgress.setMaximum(0); 35 if (progress > 100) pbProgress.setMaximum(0);
35 else pbProgress.setMaximum(100); 36 else pbProgress.setMaximum(100);
openbr/gui/tail.cpp
1 #include <QtConcurrentRun> 1 #include <QtConcurrentRun>
2 2
3 #include "tail.h" 3 #include "tail.h"
  4 +#include <assert.h>
4 5
5 using namespace br; 6 using namespace br;
6 7
openbr/gui/templateviewer.cpp
@@ -72,7 +72,7 @@ void TemplateViewer::refreshImage() @@ -72,7 +72,7 @@ void TemplateViewer::refreshImage()
72 if (file.isNull() || (format == "Photo")) { 72 if (file.isNull() || (format == "Photo")) {
73 setImage(file, true); 73 setImage(file, true);
74 } else { 74 } else {
75 - const QString path = QString(br_scratch_path()) + "/thumbnails"; 75 + const QString path = QString(br::Globals->scratchPath()) + "/thumbnails";
76 const QString hash = file.hash()+format; 76 const QString hash = file.hash()+format;
77 const QString processedFile = path+"/"+file.baseName()+hash+".png"; 77 const QString processedFile = path+"/"+file.baseName()+hash+".png";
78 if (!QFileInfo(processedFile).exists()) { 78 if (!QFileInfo(processedFile).exists()) {
openbr/gui/transformeditor.cpp
@@ -24,7 +24,7 @@ using namespace br; @@ -24,7 +24,7 @@ using namespace br;
24 br::TransformEditor::TransformEditor(Transform *transform, QWidget *parent) 24 br::TransformEditor::TransformEditor(Transform *transform, QWidget *parent)
25 : QWidget(parent) 25 : QWidget(parent)
26 { 26 {
27 - name.addItems(QString(br_objects("Transform", ".*", false)).split('\n')); 27 + name.addItems(br::Context::objects("Transform", ".*", false));
28 layout.addWidget(&name); 28 layout.addWidget(&name);
29 setLayout(&layout); 29 setLayout(&layout);
30 30
openbr/gui/utility.cpp
1 #include <limits> 1 #include <limits>
2 #include <vector> 2 #include <vector>
  3 +#include <assert.h>
3 #include <opencv2/imgproc/imgproc.hpp> 4 #include <opencv2/imgproc/imgproc.hpp>
  5 +#include <opencv2/imgproc/imgproc_c.h>
4 #include "utility.h" 6 #include "utility.h"
5 7
6 using namespace cv; 8 using namespace cv;
1 -Subproject commit 0eaa00f19256fee2f24033ff68b526ea6320624d 1 +Subproject commit f8d9c869c821cc032d092ab1274da8f3cabf5eb7
openbr/janus.cpp
@@ -3,140 +3,138 @@ @@ -3,140 +3,138 @@
3 #endif 3 #endif
4 4
5 #include "janus.h" 5 #include "janus.h"
  6 +#include "janus_io.h"
6 #include "openbr_plugin.h" 7 #include "openbr_plugin.h"
7 8
8 -// Use the provided default implementation of some functions  
9 -#include "janus/src/janus.cpp"  
10 -  
11 using namespace br; 9 using namespace br;
12 10
13 static QSharedPointer<Transform> transform; 11 static QSharedPointer<Transform> transform;
14 static QSharedPointer<Distance> distance; 12 static QSharedPointer<Distance> distance;
15 13
  14 +size_t janus_max_template_size()
  15 +{
  16 + return 33554432; // 32 MB
  17 +}
  18 +
16 janus_error janus_initialize(const char *sdk_path, const char *model_file) 19 janus_error janus_initialize(const char *sdk_path, const char *model_file)
17 { 20 {
18 int argc = 1; 21 int argc = 1;
19 const char *argv[1] = { "janus" }; 22 const char *argv[1] = { "janus" };
20 Context::initialize(argc, (char**)argv, sdk_path); 23 Context::initialize(argc, (char**)argv, sdk_path);
21 QString algorithm = model_file; 24 QString algorithm = model_file;
22 - if (algorithm.isEmpty()) algorithm = "Cvt(Gray)+Affine(88,88,0.25,0.35)+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>:ByteL1";  
23 - transform = Transform::fromAlgorithm(algorithm, false);  
24 - distance = Distance::fromAlgorithm(algorithm); 25 + if (algorithm.isEmpty()) {
  26 + transform = Transform::fromAlgorithm("Cvt(Gray)+Affine(88,88,0.25,0.35)+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>", false);
  27 + distance = Distance::fromAlgorithm("FaceRecognition");
  28 + } else if (algorithm == "PP5") {
  29 + transform.reset(Transform::make("PP5Enroll", NULL));
  30 + distance.reset(Distance::make("PP5Compare", NULL));
  31 + } else {
  32 + transform = Transform::fromAlgorithm(algorithm, false);
  33 + distance = Distance::fromAlgorithm(algorithm);
  34 + }
25 return JANUS_SUCCESS; 35 return JANUS_SUCCESS;
26 } 36 }
27 37
28 janus_error janus_finalize() 38 janus_error janus_finalize()
29 { 39 {
  40 + transform.reset();
  41 + distance.reset();
30 Context::finalize(); 42 Context::finalize();
31 return JANUS_SUCCESS; 43 return JANUS_SUCCESS;
32 } 44 }
33 45
34 -struct janus_incomplete_template_type  
35 -{  
36 - QList<cv::Mat> data;  
37 -}; 46 +struct janus_template_type : public Template
  47 +{};
38 48
39 -janus_error janus_initialize_template(janus_incomplete_template *incomplete_template) 49 +janus_error janus_initialize_template(janus_template *template_)
40 { 50 {
41 - *incomplete_template = new janus_incomplete_template_type(); 51 + *template_ = new janus_template_type();
42 return JANUS_SUCCESS; 52 return JANUS_SUCCESS;
43 } 53 }
44 54
45 -janus_error janus_add_image(const janus_image image, const janus_attribute_list attributes, janus_incomplete_template incomplete_template) 55 +janus_error janus_augment(const janus_image image, const janus_attribute_list attributes, janus_template template_)
46 { 56 {
47 Template t; 57 Template t;
48 t.append(cv::Mat(image.height, 58 t.append(cv::Mat(image.height,
49 image.width, 59 image.width,
50 - image.color_space == JANUS_GRAY8 ? CV_8UC1 : CV_8UC1, 60 + image.color_space == JANUS_GRAY8 ? CV_8UC1 : CV_8UC3,
51 image.data)); 61 image.data));
52 for (size_t i=0; i<attributes.size; i++) 62 for (size_t i=0; i<attributes.size; i++)
53 t.file.set(janus_attribute_to_string(attributes.attributes[i]), attributes.values[i]); 63 t.file.set(janus_attribute_to_string(attributes.attributes[i]), attributes.values[i]);
54 64
55 - if (!t.file.contains("JANUS_RIGHT_EYE_X") ||  
56 - !t.file.contains("JANUS_RIGHT_EYE_Y") ||  
57 - !t.file.contains("JANUS_LEFT_EYE_X") ||  
58 - !t.file.contains("JANUS_LEFT_EYE_Y")) 65 + if (!t.file.contains("RIGHT_EYE_X") ||
  66 + !t.file.contains("RIGHT_EYE_Y") ||
  67 + !t.file.contains("LEFT_EYE_X") ||
  68 + !t.file.contains("LEFT_EYE_Y"))
59 return JANUS_SUCCESS; 69 return JANUS_SUCCESS;
60 70
61 - t.file.set("Affine_0", QPointF(t.file.get<float>("JANUS_RIGHT_EYE_X"), t.file.get<float>("JANUS_RIGHT_EYE_Y")));  
62 - t.file.set("Affine_1", QPointF(t.file.get<float>("JANUS_LEFT_EYE_X"), t.file.get<float>("JANUS_LEFT_EYE_Y"))); 71 + t.file.set("Affine_0", QPointF(t.file.get<float>("RIGHT_EYE_X"), t.file.get<float>("RIGHT_EYE_Y")));
  72 + t.file.set("Affine_1", QPointF(t.file.get<float>("LEFT_EYE_X"), t.file.get<float>("LEFT_EYE_Y")));
63 Template u; 73 Template u;
64 transform->project(t, u); 74 transform->project(t, u);
65 - incomplete_template->data.append(u); 75 + template_->append(u);
66 return JANUS_SUCCESS; 76 return JANUS_SUCCESS;
67 } 77 }
68 78
69 -janus_error janus_finalize_template(janus_incomplete_template incomplete_template, janus_template template_, size_t *bytes) 79 +janus_error janus_finalize_template(janus_template template_, janus_flat_template flat_template, size_t *bytes)
70 { 80 {
71 - size_t templateBytes = 0;  
72 - size_t numTemplates = 0;  
73 - *bytes = sizeof(templateBytes) + sizeof(numTemplates);  
74 - janus_template pos = template_ + *bytes;  
75 -  
76 - foreach (const cv::Mat &m, incomplete_template->data) { 81 + foreach (const cv::Mat &m, *template_) {
77 assert(m.isContinuous()); 82 assert(m.isContinuous());
78 - const size_t currentTemplateBytes = m.rows * m.cols * m.elemSize();  
79 - if (templateBytes == 0)  
80 - templateBytes = currentTemplateBytes;  
81 - if (templateBytes != currentTemplateBytes)  
82 - return JANUS_UNKNOWN_ERROR;  
83 - if (*bytes + templateBytes > janus_max_template_size()) 83 + const size_t templateBytes = m.rows * m.cols * m.elemSize();
  84 + if (*bytes + sizeof(size_t) + templateBytes > janus_max_template_size())
84 break; 85 break;
85 - memcpy(pos, m.data, templateBytes);  
86 - *bytes += templateBytes;  
87 - pos = pos + templateBytes;  
88 - numTemplates++; 86 + memcpy(flat_template, &templateBytes, sizeof(templateBytes));
  87 + flat_template += sizeof(templateBytes);
  88 + memcpy(flat_template, m.data, templateBytes);
  89 + flat_template += templateBytes;
  90 + *bytes += sizeof(size_t) + templateBytes;
89 } 91 }
90 92
91 - *(reinterpret_cast<size_t*>(template_)+0) = templateBytes;  
92 - *(reinterpret_cast<size_t*>(template_)+1) = numTemplates;  
93 - delete incomplete_template; 93 + delete template_;
94 return JANUS_SUCCESS; 94 return JANUS_SUCCESS;
95 } 95 }
96 96
97 -janus_error janus_verify(const janus_template a, const size_t a_bytes, const janus_template b, const size_t b_bytes, double *similarity) 97 +janus_error janus_verify(const janus_flat_template a, const size_t a_bytes, const janus_flat_template b, const size_t b_bytes, double *similarity)
98 { 98 {
99 - (void) a_bytes;  
100 - (void) b_bytes;  
101 -  
102 - size_t a_template_bytes, a_templates, b_template_bytes, b_templates;  
103 - a_template_bytes = *(reinterpret_cast<size_t*>(a)+0);  
104 - a_templates = *(reinterpret_cast<size_t*>(a)+1);  
105 - b_template_bytes = *(reinterpret_cast<size_t*>(b)+0);  
106 - b_templates = *(reinterpret_cast<size_t*>(b)+1);  
107 - if (a_template_bytes != b_template_bytes)  
108 - return JANUS_UNKNOWN_ERROR; 99 + *similarity = 0;
109 100
110 - float dist = 0;  
111 - for (size_t i=0; i<a_templates; i++)  
112 - for (size_t j=0; j<b_templates; j++)  
113 - dist += distance->compare(cv::Mat(1, a_template_bytes, CV_8UC1, a+2*sizeof(size_t)+i*a_template_bytes),  
114 - cv::Mat(1, b_template_bytes, CV_8UC1, b+2*sizeof(size_t)+i*b_template_bytes));  
115 - *similarity = a_templates * b_templates / dist;  
116 - return JANUS_SUCCESS;  
117 -} 101 + int comparisons = 0;
  102 + janus_flat_template a_template = a;
  103 + while (a_template < a + a_bytes) {
  104 + const size_t a_template_bytes = *reinterpret_cast<size_t*>(a_template);
  105 + a_template += sizeof(a_template_bytes);
118 106
119 -struct janus_incomplete_gallery_type  
120 -{  
121 - QList< QPair<janus_template, janus_template_id> > templates;  
122 -}; 107 + janus_flat_template b_template = b;
  108 + while (b_template < b + b_bytes) {
  109 + const size_t b_template_bytes = *reinterpret_cast<size_t*>(b_template);
  110 + b_template += sizeof(b_template_bytes);
123 111
124 -janus_error janus_initialize_gallery(janus_incomplete_gallery *incomplete_gallery)  
125 -{  
126 - *incomplete_gallery = new janus_incomplete_gallery_type();  
127 - return JANUS_SUCCESS;  
128 -} 112 + *similarity += distance->compare(cv::Mat(1, a_template_bytes, CV_8UC1, a_template),
  113 + cv::Mat(1, b_template_bytes, CV_8UC1, b_template));
  114 + comparisons++;
129 115
130 -janus_error janus_add_template(const janus_template template_, const size_t bytes, const janus_template_id template_id, janus_incomplete_gallery incomplete_gallery)  
131 -{  
132 - (void) bytes;  
133 - incomplete_gallery->templates.append(QPair<janus_template, janus_template_id>(template_, template_id)); 116 + b_template += b_template_bytes;
  117 + }
  118 +
  119 + a_template += a_template_bytes;
  120 + }
  121 +
  122 + if (*similarity != *similarity) // True for NaN
  123 + return JANUS_UNKNOWN_ERROR;
  124 +
  125 + *similarity /= comparisons;
134 return JANUS_SUCCESS; 126 return JANUS_SUCCESS;
135 } 127 }
136 128
137 -janus_error janus_finalize_gallery(janus_incomplete_gallery incomplete_gallery, const char *gallery_file) 129 +janus_error janus_enroll(const janus_template template_, const janus_template_id template_id, janus_gallery gallery)
138 { 130 {
139 - (void) incomplete_gallery;  
140 - (void) gallery_file; 131 + template_->file.set("TEMPLATE_ID", template_id);
  132 + QFile file(gallery);
  133 + if (!file.open(QFile::WriteOnly | QFile::Append))
  134 + return JANUS_WRITE_ERROR;
  135 + QDataStream stream(&file);
  136 + stream << *template_;
  137 + file.close();
  138 + delete template_;
141 return JANUS_SUCCESS; 139 return JANUS_SUCCESS;
142 } 140 }
openbr/openbr.cpp
@@ -24,12 +24,31 @@ @@ -24,12 +24,31 @@
24 #include "core/qtutils.h" 24 #include "core/qtutils.h"
25 #include "plugins/openbr_internal.h" 25 #include "plugins/openbr_internal.h"
26 #include <opencv2/highgui/highgui.hpp> 26 #include <opencv2/highgui/highgui.hpp>
  27 +#include <opencv2/highgui/highgui_c.h>
27 28
28 using namespace br; 29 using namespace br;
29 30
  31 +static int partialCopy(const QString & string, char * buffer, int buffer_length)
  32 +{
  33 +
  34 + QByteArray byteArray = string.toLocal8Bit();
  35 +
  36 + int copyLength = std::min(buffer_length-1, byteArray.size());
  37 + if (copyLength < 0)
  38 + return byteArray.size() + 1;
  39 +
  40 + memcpy(buffer, byteArray.data(), copyLength);
  41 + buffer[copyLength] = '\0';
  42 +
  43 + return byteArray.size() + 1;
  44 +}
  45 +
30 const char *br_about() 46 const char *br_about()
31 { 47 {
  48 + static QMutex aboutLock;
  49 + QMutexLocker lock(&aboutLock);
32 static QByteArray about = Context::about().toLocal8Bit(); 50 static QByteArray about = Context::about().toLocal8Bit();
  51 +
33 return about.data(); 52 return about.data();
34 } 53 }
35 54
@@ -121,9 +140,9 @@ void br_fuse(int num_input_simmats, const char *input_simmats[], @@ -121,9 +140,9 @@ void br_fuse(int num_input_simmats, const char *input_simmats[],
121 Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), normalization, fusion, output_simmat); 140 Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), normalization, fusion, output_simmat);
122 } 141 }
123 142
124 -void br_initialize(int &argc, char *argv[], const char *sdk_path) 143 +void br_initialize(int &argc, char *argv[], const char *sdk_path, bool use_gui)
125 { 144 {
126 - Context::initialize(argc, argv, sdk_path); 145 + Context::initialize(argc, argv, sdk_path, use_gui);
127 } 146 }
128 147
129 void br_initialize_default() 148 void br_initialize_default()
@@ -149,53 +168,14 @@ void br_make_pairwise_mask(const char *target_input, const char *query_input, co @@ -149,53 +168,14 @@ void br_make_pairwise_mask(const char *target_input, const char *query_input, co
149 BEE::makePairwiseMask(target_input, query_input, mask); 168 BEE::makePairwiseMask(target_input, query_input, mask);
150 } 169 }
151 170
152 -const char *br_most_recent_message() 171 +int br_most_recent_message(char * buffer, int buffer_length)
153 { 172 {
154 - static QByteArray byteArray;  
155 - byteArray = Globals->mostRecentMessage.toLocal8Bit();  
156 - return byteArray.data(); 173 + return partialCopy(Globals->mostRecentMessage, buffer, buffer_length);
157 } 174 }
158 175
159 -const char *br_objects(const char *abstractions, const char *implementations, bool parameters) 176 +int br_objects(char * buffer, int buffer_length, const char *abstractions, const char *implementations, bool parameters)
160 { 177 {
161 - static QByteArray objects;  
162 -  
163 - QStringList objectList;  
164 - QRegExp abstractionsRegExp(abstractions);  
165 - QRegExp implementationsRegExp(implementations);  
166 -  
167 - if (abstractionsRegExp.exactMatch("Abbreviation"))  
168 - foreach (const QString &name, Globals->abbreviations.keys())  
169 - if (implementationsRegExp.exactMatch(name))  
170 - objectList.append(name + (parameters ? "\t" + Globals->abbreviations[name] : ""));  
171 -  
172 - if (abstractionsRegExp.exactMatch("Distance"))  
173 - foreach (const QString &name, Factory<Distance>::names())  
174 - if (implementationsRegExp.exactMatch(name))  
175 - objectList.append(name + (parameters ? "\t" + Factory<Distance>::parameters(name) : ""));  
176 -  
177 - if (abstractionsRegExp.exactMatch("Format"))  
178 - foreach (const QString &name, Factory<Format>::names())  
179 - if (implementationsRegExp.exactMatch(name))  
180 - objectList.append(name + (parameters ? "\t" + Factory<Format>::parameters(name) : ""));  
181 -  
182 - if (abstractionsRegExp.exactMatch("Initializer"))  
183 - foreach (const QString &name, Factory<Initializer>::names())  
184 - if (implementationsRegExp.exactMatch(name))  
185 - objectList.append(name + (parameters ? "\t" + Factory<Initializer>::parameters(name) : ""));  
186 -  
187 - if (abstractionsRegExp.exactMatch("Output"))  
188 - foreach (const QString &name, Factory<Output>::names())  
189 - if (implementationsRegExp.exactMatch(name))  
190 - objectList.append(name + (parameters ? "\t" + Factory<Output>::parameters(name) : ""));  
191 -  
192 - if (abstractionsRegExp.exactMatch("Transform"))  
193 - foreach (const QString &name, Factory<Transform>::names())  
194 - if (implementationsRegExp.exactMatch(name))  
195 - objectList.append(name + (parameters ? "\t" + Factory<Transform>::parameters(name) : ""));  
196 -  
197 - objects = objectList.join("\n").toLocal8Bit();  
198 - return objects.data(); 178 + return partialCopy(br::Context::objects(abstractions, implementations, parameters).join('\n'), buffer, buffer_length);
199 } 179 }
200 180
201 bool br_plot(int num_files, const char *files[], const char *destination, bool show) 181 bool br_plot(int num_files, const char *files[], const char *destination, bool show)
@@ -250,15 +230,15 @@ void br_read_pipe(const char *pipe, int *argc, char ***argv) @@ -250,15 +230,15 @@ void br_read_pipe(const char *pipe, int *argc, char ***argv)
250 *argv = rawCharArrayList.data(); 230 *argv = rawCharArrayList.data();
251 } 231 }
252 232
253 -const char *br_scratch_path() 233 +int br_scratch_path(char * buffer, int buffer_length)
254 { 234 {
255 - static QByteArray byteArray;  
256 - byteArray = Context::scratchPath().toLocal8Bit();  
257 - return byteArray.data(); 235 + return partialCopy(Context::scratchPath(), buffer, buffer_length);
258 } 236 }
259 237
260 const char *br_sdk_path() 238 const char *br_sdk_path()
261 { 239 {
  240 + static QMutex sdkLock;
  241 + QMutexLocker lock(&sdkLock);
262 static QByteArray sdkPath = QDir(Globals->sdkPath).absolutePath().toLocal8Bit(); 242 static QByteArray sdkPath = QDir(Globals->sdkPath).absolutePath().toLocal8Bit();
263 return sdkPath.data(); 243 return sdkPath.data();
264 } 244 }
@@ -302,6 +282,8 @@ void br_train_n(int num_inputs, const char *inputs[], const char *model) @@ -302,6 +282,8 @@ void br_train_n(int num_inputs, const char *inputs[], const char *model)
302 282
303 const char *br_version() 283 const char *br_version()
304 { 284 {
  285 + static QMutex versionLock;
  286 + QMutexLocker lock(&versionLock);
305 static QByteArray version = Context::version().toLocal8Bit(); 287 static QByteArray version = Context::version().toLocal8Bit();
306 return version.data(); 288 return version.data();
307 } 289 }
@@ -379,10 +361,9 @@ bool br_img_is_empty(br_template tmpl) @@ -379,10 +361,9 @@ bool br_img_is_empty(br_template tmpl)
379 return t->m().empty(); 361 return t->m().empty();
380 } 362 }
381 363
382 -const char* br_get_filename(br_template tmpl) 364 +int br_get_filename(char * buffer, int buffer_length, br_template tmpl)
383 { 365 {
384 - Template *t = reinterpret_cast<Template*>(tmpl);  
385 - return t->file.name.toStdString().c_str(); 366 + return partialCopy(reinterpret_cast<Template*>(tmpl)->file.name, buffer, buffer_length);
386 } 367 }
387 368
388 void br_set_filename(br_template tmpl, const char *filename) 369 void br_set_filename(br_template tmpl, const char *filename)
@@ -391,15 +372,11 @@ void br_set_filename(br_template tmpl, const char *filename) @@ -391,15 +372,11 @@ void br_set_filename(br_template tmpl, const char *filename)
391 t->file.name = filename; 372 t->file.name = filename;
392 } 373 }
393 374
394 -const char* br_get_metadata_string(br_template tmpl, const char *key) 375 +int br_get_metadata_string(char * buffer, int buffer_length, br_template tmpl, const char *key)
395 { 376 {
396 Template *t = reinterpret_cast<Template*>(tmpl); 377 Template *t = reinterpret_cast<Template*>(tmpl);
397 - // need an object outside of this scope  
398 - // so the char pointer is valid  
399 - static QByteArray result;  
400 QVariant qvar = t->file.value(key); 378 QVariant qvar = t->file.value(key);
401 - result = QtUtils::toString(qvar).toUtf8();  
402 - return result.data(); 379 + return partialCopy(QtUtils::toString(qvar), buffer, buffer_length);
403 } 380 }
404 381
405 br_template_list br_enroll_template(br_template tmpl) 382 br_template_list br_enroll_template(br_template tmpl)
@@ -470,3 +447,8 @@ void br_close_gallery(br_gallery gallery) @@ -470,3 +447,8 @@ void br_close_gallery(br_gallery gallery)
470 Gallery *gal = reinterpret_cast<Gallery*>(gallery); 447 Gallery *gal = reinterpret_cast<Gallery*>(gallery);
471 delete gal; 448 delete gal;
472 } 449 }
  450 +
  451 +void br_deduplicate(const char *input_gallery, const char *output_gallery, const char *threshold)
  452 +{
  453 + br::Deduplicate(input_gallery, output_gallery, threshold);
  454 +}
openbr/openbr.h
@@ -41,6 +41,10 @@ extern &quot;C&quot; { @@ -41,6 +41,10 @@ extern &quot;C&quot; {
41 * \section managed_return_value Managed Return Value 41 * \section managed_return_value Managed Return Value
42 * Memory for <tt>const char*</tt> return values is managed internally and guaranteed until the next call to the function. 42 * Memory for <tt>const char*</tt> return values is managed internally and guaranteed until the next call to the function.
43 * 43 *
  44 + * \section input_string_buffer Input String Buffer
  45 + * Users should input a char * buffer and the size of that buffer. String data will be copied into the buffer, if the buffer is too
  46 + * small, only part of the string will be copied. Returns the buffer size required to contain the complete string.
  47 + *
44 * \section examples Examples 48 * \section examples Examples
45 * - \ref c_face_recognition_evaluation 49 * - \ref c_face_recognition_evaluation
46 * 50 *
@@ -56,7 +60,6 @@ extern &quot;C&quot; { @@ -56,7 +60,6 @@ extern &quot;C&quot; {
56 60
57 /*! 61 /*!
58 * \brief Wraps br::Context::about() 62 * \brief Wraps br::Context::about()
59 - * \note \ref managed_return_value  
60 * \see br_version 63 * \see br_version
61 */ 64 */
62 BR_EXPORT const char *br_about(); 65 BR_EXPORT const char *br_about();
@@ -67,6 +70,17 @@ BR_EXPORT const char *br_about(); @@ -67,6 +70,17 @@ BR_EXPORT const char *br_about();
67 BR_EXPORT void br_cat(int num_input_galleries, const char *input_galleries[], const char *output_gallery); 70 BR_EXPORT void br_cat(int num_input_galleries, const char *input_galleries[], const char *output_gallery);
68 71
69 /*! 72 /*!
  73 + * \brief Removes duplicate templates in a gallery.
  74 + * \param input_gallery Gallery to be deduplicated.
  75 + * \param output_gallery Deduplicated gallery.
  76 + * \param threshold Comparisons with a match score >= this value are designated to be duplicates.
  77 + * \note If a gallery contains n duplicates, the first n-1 duplicates in the gallery will be removed and the nth will be kept.
  78 + * \note Users are encouraged to use binary gallery formats as the entire gallery is read into memory in one call to Gallery::read.
  79 + */
  80 +
  81 +BR_EXPORT void br_deduplicate(const char *input_gallery, const char *output_gallery, const char *threshold);
  82 +
  83 +/*!
70 * \brief Clusters one or more similarity matrices into a list of subjects. 84 * \brief Clusters one or more similarity matrices into a list of subjects.
71 * 85 *
72 * A similarity matrix is a type of br::Output. The current clustering algorithm is a simplified implementation of \cite zhu11. 86 * A similarity matrix is a type of br::Output. The current clustering algorithm is a simplified implementation of \cite zhu11.
@@ -213,7 +227,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], @@ -213,7 +227,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[],
213 * \brief Wraps br::Context::initialize() 227 * \brief Wraps br::Context::initialize()
214 * \see br_finalize 228 * \see br_finalize
215 */ 229 */
216 -BR_EXPORT void br_initialize(int &argc, char *argv[], const char *sdk_path = ""); 230 +BR_EXPORT void br_initialize(int &argc, char *argv[], const char *sdk_path = "", bool use_gui = false);
217 /*! 231 /*!
218 * \brief Wraps br::Context::initialize() with default arguments. 232 * \brief Wraps br::Context::initialize() with default arguments.
219 * \see br_finalize 233 * \see br_finalize
@@ -245,10 +259,10 @@ BR_EXPORT void br_make_pairwise_mask(const char *target_input, const char *query @@ -245,10 +259,10 @@ BR_EXPORT void br_make_pairwise_mask(const char *target_input, const char *query
245 259
246 /*! 260 /*!
247 * \brief Returns the most recent line sent to stderr. 261 * \brief Returns the most recent line sent to stderr.
248 - * \note \ref managed_return_value 262 + * \note \ref input_string_buffer
249 * \see br_progress br_time_remaining 263 * \see br_progress br_time_remaining
250 */ 264 */
251 -BR_EXPORT const char *br_most_recent_message(); 265 +BR_EXPORT int br_most_recent_message(char * buffer, int buffer_length);
252 266
253 /*! 267 /*!
254 * \brief Returns names and parameters for the requested objects. 268 * \brief Returns names and parameters for the requested objects.
@@ -257,10 +271,10 @@ BR_EXPORT const char *br_most_recent_message(); @@ -257,10 +271,10 @@ BR_EXPORT const char *br_most_recent_message();
257 * \param abstractions Regular expression of the abstractions to search. 271 * \param abstractions Regular expression of the abstractions to search.
258 * \param implementations Regular expression of the implementations to search. 272 * \param implementations Regular expression of the implementations to search.
259 * \param parameters Include parameters after object name. 273 * \param parameters Include parameters after object name.
260 - * \note \ref managed_return_value 274 + * \note \ref input_string_buffer
261 * \note This function uses Qt's <a href="http://doc.qt.digia.com/stable/qregexp.html">QRegExp</a> syntax. 275 * \note This function uses Qt's <a href="http://doc.qt.digia.com/stable/qregexp.html">QRegExp</a> syntax.
262 */ 276 */
263 -BR_EXPORT const char *br_objects(const char *abstractions = ".*", const char *implementations = ".*", bool parameters = true); 277 +BR_EXPORT int br_objects(char * buffer, int buffer_length, const char *abstractions = ".*", const char *implementations = ".*", bool parameters = true);
264 278
265 /*! 279 /*!
266 * \brief Renders recognition performance figures for a set of <tt>.csv</tt> files created by \ref br_eval. 280 * \brief Renders recognition performance figures for a set of <tt>.csv</tt> files created by \ref br_eval.
@@ -365,14 +379,14 @@ BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv); @@ -365,14 +379,14 @@ BR_EXPORT void br_read_pipe(const char *pipe, int *argc, char ***argv);
365 379
366 /*! 380 /*!
367 * \brief Wraps br::Context::scratchPath() 381 * \brief Wraps br::Context::scratchPath()
368 - * \note \ref managed_return_value 382 + * \note \ref input_string_buffer
369 * \see br_version 383 * \see br_version
370 */ 384 */
371 -BR_EXPORT const char *br_scratch_path(); 385 +BR_EXPORT int br_scratch_path(char * buffer, int buffer_length);
  386 +
372 387
373 /*! 388 /*!
374 * \brief Returns the full path to the root of the SDK. 389 * \brief Returns the full path to the root of the SDK.
375 - * \note \ref managed_return_value  
376 * \see br_initialize 390 * \see br_initialize
377 */ 391 */
378 BR_EXPORT const char *br_sdk_path(); 392 BR_EXPORT const char *br_sdk_path();
@@ -425,7 +439,6 @@ BR_EXPORT void br_train_n(int num_inputs, const char *inputs[], const char *mode @@ -425,7 +439,6 @@ BR_EXPORT void br_train_n(int num_inputs, const char *inputs[], const char *mode
425 439
426 /*! 440 /*!
427 * \brief Wraps br::Context::version() 441 * \brief Wraps br::Context::version()
428 - * \note \ref managed_return_value  
429 * \see br_about br_scratch_path 442 * \see br_about br_scratch_path
430 */ 443 */
431 BR_EXPORT const char *br_version(); 444 BR_EXPORT const char *br_version();
@@ -497,16 +510,18 @@ BR_EXPORT int br_img_channels(br_template tmpl); @@ -497,16 +510,18 @@ BR_EXPORT int br_img_channels(br_template tmpl);
497 BR_EXPORT bool br_img_is_empty(br_template tmpl); 510 BR_EXPORT bool br_img_is_empty(br_template tmpl);
498 /*! 511 /*!
499 * \brief Get the filename for a br::Template 512 * \brief Get the filename for a br::Template
  513 + * \note \ref input_string_buffer
500 */ 514 */
501 -BR_EXPORT const char* br_get_filename(br_template tmpl); 515 +BR_EXPORT int br_get_filename(char * buffer, int buffer_length, br_template tmpl);
502 /*! 516 /*!
503 * \brief Set the filename for a br::Template. 517 * \brief Set the filename for a br::Template.
504 */ 518 */
505 BR_EXPORT void br_set_filename(br_template tmpl, const char *filename); 519 BR_EXPORT void br_set_filename(br_template tmpl, const char *filename);
506 /*! 520 /*!
507 * \brief Get metadata as a string for the given key in the given template. 521 * \brief Get metadata as a string for the given key in the given template.
  522 + * \note \ref input_string_buffer
508 */ 523 */
509 -BR_EXPORT const char* br_get_metadata_string(br_template, const char *key); 524 +BR_EXPORT int br_get_metadata_string(char * buffer, int buffer_length, br_template tmpl, const char *key);
510 /*! 525 /*!
511 * \brief Enroll a br::Template from the C API! Returns a pointer to a br::TemplateList 526 * \brief Enroll a br::Template from the C API! Returns a pointer to a br::TemplateList
512 * \param tmpl Pointer to a br::Template. 527 * \param tmpl Pointer to a br::Template.
openbr/openbr_plugin.cpp
@@ -203,6 +203,13 @@ QList&lt;QRectF&gt; File::namedRects() const @@ -203,6 +203,13 @@ QList&lt;QRectF&gt; File::namedRects() const
203 const QVariant &variant = m_metadata[key]; 203 const QVariant &variant = m_metadata[key];
204 if (variant.canConvert<QRectF>()) 204 if (variant.canConvert<QRectF>())
205 rects.append(variant.value<QRectF>()); 205 rects.append(variant.value<QRectF>());
  206 + else if(variant.canConvert<QList<QRectF> >()) {
  207 + QList<QRectF> list = variant.value<QList<QRectF> >();
  208 + for (int i=0;i < list.size();i++)
  209 + {
  210 + rects.append(list[i]);
  211 + }
  212 + }
206 } 213 }
207 return rects; 214 return rects;
208 } 215 }
@@ -392,7 +399,12 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery) @@ -392,7 +399,12 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
392 399
393 const int crossValidate = gallery.get<int>("crossValidate"); 400 const int crossValidate = gallery.get<int>("crossValidate");
394 401
395 - if (gallery.getBool("leaveOneImageOut")) { 402 + // The leaveOneImageOut flag is used when we want to train on n-1 of a subject's images
  403 + // Thus, we find all the images for a particular subject, and set their partitions based on
  404 + // the crossValidate parameter
  405 + // Note that when the number of images per subject varies from subject to subject
  406 + // the number of subjects will decrease as the partition increases
  407 + if (gallery.getBool("leaveOneImageOut") && crossValidate > 0) {
396 QStringList labels; 408 QStringList labels;
397 for (int i=newTemplates.size()-1; i>=0; i--) { 409 for (int i=newTemplates.size()-1; i>=0; i--) {
398 newTemplates[i].file.set("Index", i+templates.size()); 410 newTemplates[i].file.set("Index", i+templates.size());
@@ -414,7 +426,7 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery) @@ -414,7 +426,7 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
414 Template leaveOneImageOutTemplate = newTemplates[labelIndices[j]]; 426 Template leaveOneImageOutTemplate = newTemplates[labelIndices[j]];
415 if (k!=leaveOneImageOutTemplate.file.get<int>("Partition")) { 427 if (k!=leaveOneImageOutTemplate.file.get<int>("Partition")) {
416 leaveOneImageOutTemplate.file.set("Partition", k); 428 leaveOneImageOutTemplate.file.set("Partition", k);
417 - leaveOneImageOutTemplate.file.set("testOnly", true); 429 + leaveOneImageOutTemplate.file.set("targetOnly", true);
418 newTemplates.insert(i+1,leaveOneImageOutTemplate); 430 newTemplates.insert(i+1,leaveOneImageOutTemplate);
419 } 431 }
420 } 432 }
@@ -899,8 +911,12 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG @@ -899,8 +911,12 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
899 { 911 {
900 qInstallMessageHandler(messageHandler); 912 qInstallMessageHandler(messageHandler);
901 913
  914 + QString sep;
902 #ifndef _WIN32 915 #ifndef _WIN32
903 useGui = useGui && (getenv("DISPLAY") != NULL); 916 useGui = useGui && (getenv("DISPLAY") != NULL);
  917 + sep = ":";
  918 +#else
  919 + sep = ";";
904 #endif // not _WIN32 920 #endif // not _WIN32
905 921
906 // We take in argc as a reference due to: 922 // We take in argc as a reference due to:
@@ -944,6 +960,7 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG @@ -944,6 +960,7 @@ void br::Context::initialize(int &amp;argc, char *argv[], QString sdkPath, bool useG
944 // Search for SDK 960 // Search for SDK
945 if (sdkPath.isEmpty()) { 961 if (sdkPath.isEmpty()) {
946 QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath(); 962 QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath();
  963 + checkPaths << QString(getenv("PATH")).split(sep, QString::SkipEmptyParts);
947 964
948 bool foundSDK = false; 965 bool foundSDK = false;
949 foreach (const QString &path, checkPaths) { 966 foreach (const QString &path, checkPaths) {
@@ -999,6 +1016,47 @@ QString br::Context::scratchPath() @@ -999,6 +1016,47 @@ QString br::Context::scratchPath()
999 return QString("%1/%2-%3.%4").arg(QDir::homePath(), PRODUCT_NAME, QString::number(PRODUCT_VERSION_MAJOR), QString::number(PRODUCT_VERSION_MINOR)); 1016 return QString("%1/%2-%3.%4").arg(QDir::homePath(), PRODUCT_NAME, QString::number(PRODUCT_VERSION_MAJOR), QString::number(PRODUCT_VERSION_MINOR));
1000 } 1017 }
1001 1018
  1019 +
  1020 +QStringList br::Context::objects(const char *abstractions, const char *implementations, bool parameters)
  1021 +{
  1022 + QStringList objectList;
  1023 + QRegExp abstractionsRegExp(abstractions);
  1024 + QRegExp implementationsRegExp(implementations);
  1025 +
  1026 + if (abstractionsRegExp.exactMatch("Abbreviation"))
  1027 + foreach (const QString &name, Globals->abbreviations.keys())
  1028 + if (implementationsRegExp.exactMatch(name))
  1029 + objectList.append(name + (parameters ? "\t" + Globals->abbreviations[name] : ""));
  1030 +
  1031 + if (abstractionsRegExp.exactMatch("Distance"))
  1032 + foreach (const QString &name, Factory<Distance>::names())
  1033 + if (implementationsRegExp.exactMatch(name))
  1034 + objectList.append(name + (parameters ? "\t" + Factory<Distance>::parameters(name) : ""));
  1035 +
  1036 + if (abstractionsRegExp.exactMatch("Format"))
  1037 + foreach (const QString &name, Factory<Format>::names())
  1038 + if (implementationsRegExp.exactMatch(name))
  1039 + objectList.append(name + (parameters ? "\t" + Factory<Format>::parameters(name) : ""));
  1040 +
  1041 + if (abstractionsRegExp.exactMatch("Initializer"))
  1042 + foreach (const QString &name, Factory<Initializer>::names())
  1043 + if (implementationsRegExp.exactMatch(name))
  1044 + objectList.append(name + (parameters ? "\t" + Factory<Initializer>::parameters(name) : ""));
  1045 +
  1046 + if (abstractionsRegExp.exactMatch("Output"))
  1047 + foreach (const QString &name, Factory<Output>::names())
  1048 + if (implementationsRegExp.exactMatch(name))
  1049 + objectList.append(name + (parameters ? "\t" + Factory<Output>::parameters(name) : ""));
  1050 +
  1051 + if (abstractionsRegExp.exactMatch("Transform"))
  1052 + foreach (const QString &name, Factory<Transform>::names())
  1053 + if (implementationsRegExp.exactMatch(name))
  1054 + objectList.append(name + (parameters ? "\t" + Factory<Transform>::parameters(name) : ""));
  1055 +
  1056 +
  1057 + return objectList;
  1058 +}
  1059 +
1002 void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) 1060 void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
1003 { 1061 {
1004 // Something about this method is not thread safe, and will lead to crashes if qDebug 1062 // Something about this method is not thread safe, and will lead to crashes if qDebug
openbr/openbr_plugin.h
@@ -41,6 +41,7 @@ @@ -41,6 +41,7 @@
41 #include <QVector> 41 #include <QVector>
42 #include <opencv2/core/core.hpp> 42 #include <opencv2/core/core.hpp>
43 #include <openbr/openbr.h> 43 #include <openbr/openbr.h>
  44 +#include <assert.h>
44 45
45 /*! 46 /*!
46 * \defgroup cpp_plugin_sdk C++ Plugin SDK 47 * \defgroup cpp_plugin_sdk C++ Plugin SDK
@@ -829,6 +830,18 @@ public: @@ -829,6 +830,18 @@ public:
829 */ 830 */
830 static QString scratchPath(); 831 static QString scratchPath();
831 832
  833 + /*!
  834 + * \brief Returns names and parameters for the requested objects.
  835 + *
  836 + * Each object is \c \\n seperated. Arguments are seperated from the object name with a \c \\t.
  837 + * \param abstractions Regular expression of the abstractions to search.
  838 + * \param implementations Regular expression of the implementations to search.
  839 + * \param parameters Include parameters after object name.
  840 + * \note \ref managed_return_value
  841 + * \note This function uses Qt's <a href="http://doc.qt.digia.com/stable/qregexp.html">QRegExp</a> syntax.
  842 + */
  843 + static QStringList objects(const char *abstractions = ".*", const char *implementations = ".*", bool parameters = true);
  844 +
832 private: 845 private:
833 static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); 846 static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
834 }; 847 };
@@ -1371,6 +1384,14 @@ BR_EXPORT void Convert(const File &amp;fileType, const File &amp;inputFile, const File &amp; @@ -1371,6 +1384,14 @@ BR_EXPORT void Convert(const File &amp;fileType, const File &amp;inputFile, const File &amp;
1371 */ 1384 */
1372 BR_EXPORT void Cat(const QStringList &inputGalleries, const QString &outputGallery); 1385 BR_EXPORT void Cat(const QStringList &inputGalleries, const QString &outputGallery);
1373 1386
  1387 +/*!
  1388 + * \brief Deduplicate a gallery.
  1389 + * \param inputGallery Gallery to deduplicate.
  1390 + * \param outputGallery Gallery to store the deduplicated result.
  1391 + * \param threshold Match score threshold to determine duplicates.
  1392 + */
  1393 +BR_EXPORT void Deduplicate(const File &inputGallery, const File &outputGallery, const QString &threshold);
  1394 +
1374 /*! @}*/ 1395 /*! @}*/
1375 1396
1376 } // namespace br 1397 } // namespace br
openbr/plugins/algorithms.cpp
@@ -50,6 +50,9 @@ class AlgorithmsInitializer : public Initializer @@ -50,6 +50,9 @@ class AlgorithmsInitializer : public Initializer
50 Globals->abbreviations.insert("DisplayVideo", "Stream(FPSLimit(30)+Show(false,[FrameNumber])+Discard)"); 50 Globals->abbreviations.insert("DisplayVideo", "Stream(FPSLimit(30)+Show(false,[FrameNumber])+Discard)");
51 Globals->abbreviations.insert("PerFrameDetection", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard)"); 51 Globals->abbreviations.insert("PerFrameDetection", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard)");
52 Globals->abbreviations.insert("AgeGenderDemo", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard)"); 52 Globals->abbreviations.insert("AgeGenderDemo", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard)");
  53 + Globals->abbreviations.insert("ShowOpticalFlowField", "Stream(SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard)");
  54 + Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "Stream(AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard)");
  55 + Globals->abbreviations.insert("ShowMotionSegmentation", "Stream(DropFrames(5)+AggregateFrames(2)+OpticalFlow+CvtUChar+WatershedSegmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard)");
53 56
54 Globals->abbreviations.insert("HOG", "Stream(DropFrames(5)+Cvt(Gray)+Grid(5,5)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); 57 Globals->abbreviations.insert("HOG", "Stream(DropFrames(5)+Cvt(Gray)+Grid(5,5)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
55 Globals->abbreviations.insert("HOF", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)"); 58 Globals->abbreviations.insert("HOF", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)");
@@ -76,7 +79,7 @@ class AlgorithmsInitializer : public Initializer @@ -76,7 +79,7 @@ class AlgorithmsInitializer : public Initializer
76 Globals->abbreviations.insert("ColoredLBP", "Open+Affine(128,128,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+ColoredU2"); 79 Globals->abbreviations.insert("ColoredLBP", "Open+Affine(128,128,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+ColoredU2");
77 80
78 // Transforms 81 // Transforms
79 - Globals->abbreviations.insert("FaceDetection", "(Open+Cvt(Gray)+Cascade(FrontalFace))"); 82 + Globals->abbreviations.insert("FaceDetection", "Open+Cvt(Gray)+Cascade(FrontalFace)");
80 Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))"); 83 Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))");
81 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)"); 84 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)");
82 Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+DownsampleTraining(FTE(DFFS),instances=1))"); 85 Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+DownsampleTraining(FTE(DFFS),instances=1))");
openbr/plugins/cascade.cpp
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/objdetect/objdetect.hpp> 17 #include <opencv2/objdetect/objdetect.hpp>
  18 +//#include <opencv2/objdetect/objdetect_c.h>
18 #include "openbr_internal.h" 19 #include "openbr_internal.h"
19 #include "openbr/core/opencvutils.h" 20 #include "openbr/core/opencvutils.h"
20 #include "openbr/core/resource.h" 21 #include "openbr/core/resource.h"
@@ -86,11 +87,11 @@ class CascadeTransform : public UntrainableMetaTransform @@ -86,11 +87,11 @@ class CascadeTransform : public UntrainableMetaTransform
86 87
87 for (int i=0; i<t.size(); i++) { 88 for (int i=0; i<t.size(); i++) {
88 const Mat &m = t[i]; 89 const Mat &m = t[i];
89 - vector<Rect> rects;  
90 - vector<int> rejectLevels;  
91 - vector<double> levelWeights;  
92 - if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, 5, (enrollAll ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT) | CV_HAAR_SCALE_IMAGE, Size(minSize, minSize), Size(), true);  
93 - else cascade->detectMultiScale(m, rects, 1.2, 5, enrollAll ? 0 : CV_HAAR_FIND_BIGGEST_OBJECT, Size(minSize, minSize)); 90 + std::vector<Rect> rects;
  91 + std::vector<int> rejectLevels;
  92 + std::vector<double> levelWeights;
  93 + if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, 5, (enrollAll ? 0 : CASCADE_FIND_BIGGEST_OBJECT) | CASCADE_SCALE_IMAGE, Size(minSize, minSize), Size(), true);
  94 + else cascade->detectMultiScale(m, rects, 1.2, 5, enrollAll ? 0 : CASCADE_FIND_BIGGEST_OBJECT, Size(minSize, minSize));
94 95
95 if (!enrollAll && rects.empty()) 96 if (!enrollAll && rects.empty())
96 rects.push_back(Rect(0, 0, m.cols, m.rows)); 97 rects.push_back(Rect(0, 0, m.cols, m.rows));
openbr/plugins/cvt.cpp
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 * limitations under the License. * 14 * limitations under the License. *
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
  17 +#include <opencv2/imgproc/imgproc_c.h>
17 #include <opencv2/imgproc/imgproc.hpp> 18 #include <opencv2/imgproc/imgproc.hpp>
18 #include "openbr_internal.h" 19 #include "openbr_internal.h"
19 #include "openbr/core/opencvutils.h" 20 #include "openbr/core/opencvutils.h"
@@ -44,7 +45,8 @@ public: @@ -44,7 +45,8 @@ public:
44 Luv = CV_BGR2Luv, 45 Luv = CV_BGR2Luv,
45 RGB = CV_BGR2RGB, 46 RGB = CV_BGR2RGB,
46 XYZ = CV_BGR2XYZ, 47 XYZ = CV_BGR2XYZ,
47 - YCrCb = CV_BGR2YCrCb }; 48 + YCrCb = CV_BGR2YCrCb,
  49 + Color = CV_GRAY2BGR };
48 50
49 private: 51 private:
50 BR_PROPERTY(ColorSpace, colorSpace, Gray) 52 BR_PROPERTY(ColorSpace, colorSpace, Gray)
@@ -52,8 +54,8 @@ private: @@ -52,8 +54,8 @@ private:
52 54
53 void project(const Template &src, Template &dst) const 55 void project(const Template &src, Template &dst) const
54 { 56 {
55 - if (src.m().channels() > 1) cvtColor(src, dst, colorSpace);  
56 - else dst = src; 57 + if (src.m().channels() > 1 || colorSpace == CV_GRAY2BGR) cvtColor(src, dst, colorSpace);
  58 + else dst = src;
57 59
58 if (channel != -1) { 60 if (channel != -1) {
59 std::vector<Mat> mv; 61 std::vector<Mat> mv;
openbr/plugins/distance.cpp
@@ -18,10 +18,12 @@ @@ -18,10 +18,12 @@
18 #include <QtConcurrentRun> 18 #include <QtConcurrentRun>
19 #include <numeric> 19 #include <numeric>
20 #include <opencv2/imgproc/imgproc.hpp> 20 #include <opencv2/imgproc/imgproc.hpp>
  21 +#include <opencv2/imgproc/imgproc_c.h>
21 #include "openbr_internal.h" 22 #include "openbr_internal.h"
22 23
23 #include "openbr/core/distance_sse.h" 24 #include "openbr/core/distance_sse.h"
24 #include "openbr/core/qtutils.h" 25 #include "openbr/core/qtutils.h"
  26 +#include "openbr/core/opencvutils.h"
25 27
26 using namespace cv; 28 using namespace cv;
27 29
@@ -61,6 +63,7 @@ private: @@ -61,6 +63,7 @@ private:
61 (a.m().type() != b.m().type())) 63 (a.m().type() != b.m().type()))
62 return -std::numeric_limits<float>::max(); 64 return -std::numeric_limits<float>::max();
63 65
  66 +// TODO: this max value is never returned based on the switch / default
64 float result = std::numeric_limits<float>::max(); 67 float result = std::numeric_limits<float>::max();
65 switch (metric) { 68 switch (metric) {
66 case Correlation: 69 case Correlation:
@@ -386,5 +389,103 @@ class OnlineDistance : public Distance @@ -386,5 +389,103 @@ class OnlineDistance : public Distance
386 389
387 BR_REGISTER(Distance, OnlineDistance) 390 BR_REGISTER(Distance, OnlineDistance)
388 391
  392 +/*!
  393 + * \ingroup distances
  394 + * \brief Attenuation function based distance from attributes
  395 + * \author Scott Klum \cite sklum
  396 + */
  397 +class AttributeDistance : public Distance
  398 +{
  399 + Q_OBJECT
  400 + Q_PROPERTY(QString attribute READ get_attribute WRITE set_attribute RESET reset_attribute STORED false)
  401 + BR_PROPERTY(QString, attribute, QString())
  402 +
  403 + float compare(const Template &target, const Template &query) const
  404 + {
  405 + float queryValue = query.file.get<float>(attribute);
  406 + float targetValue = target.file.get<float>(attribute);
  407 +
  408 + // TODO: Set this magic number to something meaningful
  409 + float stddev = 1;
  410 +
  411 + if (queryValue == targetValue) return 1;
  412 + else return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((targetValue-queryValue)/stddev, 2));
  413 + }
  414 +};
  415 +
  416 +BR_REGISTER(Distance, AttributeDistance)
  417 +
  418 +/*!
  419 + * \ingroup distances
  420 + * \brief Sum match scores across multiple distances
  421 + * \author Scott Klum \cite sklum
  422 + */
  423 +class SumDistance : public Distance
  424 +{
  425 + Q_OBJECT
  426 + Q_PROPERTY(QList<br::Distance*> distances READ get_distances WRITE set_distances RESET reset_distances)
  427 + BR_PROPERTY(QList<br::Distance*>, distances, QList<br::Distance*>())
  428 +
  429 + void train(const TemplateList &data)
  430 + {
  431 + QFutureSynchronizer<void> futures;
  432 + foreach (br::Distance *distance, distances)
  433 + futures.addFuture(QtConcurrent::run(distance, &Distance::train, data));
  434 + futures.waitForFinished();
  435 + }
  436 +
  437 + float compare(const Template &target, const Template &query) const
  438 + {
  439 + float result = 0;
  440 +
  441 + foreach (br::Distance *distance, distances) {
  442 + result += distance->compare(target, query);
  443 +
  444 + if (result == -std::numeric_limits<float>::max())
  445 + return result;
  446 + }
  447 +
  448 + return result;
  449 + }
  450 +};
  451 +
  452 +BR_REGISTER(Distance, SumDistance)
  453 +
  454 +/*!
  455 + * \ingroup transforms
  456 + * \brief Compare each template to a fixed gallery (with name = galleryName), using the specified distance.
  457 + * dst will contain a 1 by n vector of scores.
  458 + * \author Charles Otto \cite caotto
  459 + */
  460 +class GalleryCompareTransform : public Transform
  461 +{
  462 + Q_OBJECT
  463 + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED false)
  464 + Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false)
  465 + BR_PROPERTY(br::Distance*, distance, NULL)
  466 + BR_PROPERTY(QString, galleryName, "")
  467 +
  468 + TemplateList gallery;
  469 +
  470 + void project(const Template &src, Template &dst) const
  471 + {
  472 + dst = src;
  473 + if (gallery.isEmpty())
  474 + return;
  475 +
  476 + QList<float> line = distance->compare(gallery, src);
  477 + dst.m() = OpenCVUtils::toMat(line, 1);
  478 + }
  479 +
  480 + void init()
  481 + {
  482 + if (!galleryName.isEmpty())
  483 + gallery = TemplateList::fromGallery(galleryName);
  484 + }
  485 +};
  486 +
  487 +BR_REGISTER(Transform, GalleryCompareTransform)
  488 +
  489 +
389 } // namespace br 490 } // namespace br
390 #include "distance.moc" 491 #include "distance.moc"
openbr/plugins/draw.cpp
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/highgui/highgui.hpp> 17 #include <opencv2/highgui/highgui.hpp>
  18 +#include <opencv2/highgui/highgui_c.h>
18 #include <opencv2/imgproc/imgproc.hpp> 19 #include <opencv2/imgproc/imgproc.hpp>
19 #include <vector> 20 #include <vector>
20 #include "openbr_internal.h" 21 #include "openbr_internal.h"
@@ -345,6 +346,75 @@ class AdjacentOverlayTransform : public Transform @@ -345,6 +346,75 @@ class AdjacentOverlayTransform : public Transform
345 346
346 BR_REGISTER(Transform, AdjacentOverlayTransform) 347 BR_REGISTER(Transform, AdjacentOverlayTransform)
347 348
  349 +/*!
  350 + * \ingroup transforms
  351 + * \brief Draw a line representing the direction and magnitude of optical flow at the specified points.
  352 + * \author Austin Blanton \cite imaus10
  353 + */
  354 +class DrawOpticalFlow : public UntrainableTransform
  355 +{
  356 + Q_OBJECT
  357 + Q_PROPERTY(QString original READ get_original WRITE set_original RESET reset_original STORED false)
  358 + BR_PROPERTY(QString, original, "original")
  359 +
  360 + void project(const Template &src, Template &dst) const
  361 + {
  362 + const Scalar color(0,255,0);
  363 + Mat flow = src.m();
  364 + dst = src;
  365 + if (!dst.file.contains(original)) qFatal("The original img must be saved in the metadata with SaveMat.");
  366 + dst.m() = dst.file.get<Mat>(original);
  367 + dst.file.remove(original);
  368 + foreach (const Point2f &pt, OpenCVUtils::toPoints(dst.file.points())) {
  369 + Point2f dxy = flow.at<Point2f>(pt.y, pt.x);
  370 + Point2f newPt(pt.x+dxy.x, pt.y+dxy.y);
  371 + line(dst, pt, newPt, color);
  372 + }
  373 + }
  374 +};
  375 +BR_REGISTER(Transform, DrawOpticalFlow)
  376 +
  377 +/*!
  378 + * \ingroup transforms
  379 + * \brief Fill in the segmentations or draw a line between intersecting segments.
  380 + * \author Austin Blanton \cite imaus10
  381 + */
  382 +class DrawSegmentation : public UntrainableTransform
  383 +{
  384 + Q_OBJECT
  385 + Q_PROPERTY(bool fillSegment READ get_fillSegment WRITE set_fillSegment RESET reset_fillSegment STORED false)
  386 + BR_PROPERTY(bool, fillSegment, true)
  387 +
  388 + void project(const Template &src, Template &dst) const
  389 + {
  390 + if (!src.file.contains("SegmentsMask") || !src.file.contains("NumSegments")) qFatal("Must supply a Contours object in the metadata to drawContours.");
  391 + Mat segments = src.file.get<Mat>("SegmentsMask");
  392 + int numSegments = src.file.get<int>("NumSegments");
  393 +
  394 + dst.file = src.file;
  395 + Mat drawn = fillSegment ? Mat(segments.size(), CV_8UC3, Scalar::all(0)) : src.m();
  396 +
  397 + for (int i=1; i<numSegments+1; i++) {
  398 + Mat mask = segments == i;
  399 + if (fillSegment) { // color the whole segment
  400 + // set to a random color - get ready for a craaaazy acid trip
  401 + int b = theRNG().uniform(0, 255);
  402 + int g = theRNG().uniform(0, 255);
  403 + int r = theRNG().uniform(0, 255);
  404 + drawn.setTo(Scalar(r,g,b), mask);
  405 + } else { // draw lines where there's a color change
  406 + vector<vector<Point> > contours;
  407 + Scalar color(0,255,0);
  408 + findContours(mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
  409 + drawContours(drawn, contours, -1, color);
  410 + }
  411 + }
  412 +
  413 + dst.m() = drawn;
  414 + }
  415 +};
  416 +BR_REGISTER(Transform, DrawSegmentation)
  417 +
348 // TODO: re-implement EditTransform using Qt 418 // TODO: re-implement EditTransform using Qt
349 #if 0 419 #if 0
350 /*! 420 /*!
openbr/plugins/eyes.cpp
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 */ 34 */
35 35
36 #include <opencv2/imgproc/imgproc.hpp> 36 #include <opencv2/imgproc/imgproc.hpp>
  37 +#include <opencv2/imgproc/imgproc_c.h>
37 #include "openbr_internal.h" 38 #include "openbr_internal.h"
38 #include "openbr/core/opencvutils.h" 39 #include "openbr/core/opencvutils.h"
39 40
openbr/plugins/format.cpp
@@ -16,10 +16,12 @@ @@ -16,10 +16,12 @@
16 16
17 #include <QDate> 17 #include <QDate>
18 #include <QSize> 18 #include <QSize>
  19 +#include <QChar>
19 #ifndef BR_EMBEDDED 20 #ifndef BR_EMBEDDED
20 #include <QtXml> 21 #include <QtXml>
21 #endif // BR_EMBEDDED 22 #endif // BR_EMBEDDED
22 #include <opencv2/highgui/highgui.hpp> 23 #include <opencv2/highgui/highgui.hpp>
  24 +#include <opencv2/highgui/highgui_c.h>
23 #include "openbr_internal.h" 25 #include "openbr_internal.h"
24 26
25 #include "openbr/core/bee.h" 27 #include "openbr/core/bee.h"
@@ -33,7 +35,7 @@ namespace br @@ -33,7 +35,7 @@ namespace br
33 35
34 /*! 36 /*!
35 * \ingroup formats 37 * \ingroup formats
36 - * \brief Read all frames of a video using OpenCV 38 + * \brief Read all frames of a video using OpenCV
37 * \author Charles Otto \cite caotto 39 * \author Charles Otto \cite caotto
38 */ 40 */
39 class videoFormat : public Format 41 class videoFormat : public Format
@@ -45,11 +47,11 @@ public: @@ -45,11 +47,11 @@ public:
45 { 47 {
46 if (!file.exists() ) 48 if (!file.exists() )
47 return Template(); 49 return Template();
48 - 50 +
49 VideoCapture videoSource(file.name.toStdString()); 51 VideoCapture videoSource(file.name.toStdString());
50 videoSource.open(file.name.toStdString() ); 52 videoSource.open(file.name.toStdString() );
51 -  
52 - 53 +
  54 +
53 Template frames; 55 Template frames;
54 if (!videoSource.isOpened()) { 56 if (!videoSource.isOpened()) {
55 qWarning("video file open failed"); 57 qWarning("video file open failed");
@@ -71,7 +73,7 @@ public: @@ -71,7 +73,7 @@ public:
71 73
72 void write(const Template &t) const 74 void write(const Template &t) const
73 { 75 {
74 - int fourcc = OpenCVUtils::getFourcc(); 76 + int fourcc = OpenCVUtils::getFourcc();
75 VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size()); 77 VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size());
76 78
77 // Did we successfully open the output file? 79 // Did we successfully open the output file?
@@ -719,7 +721,7 @@ BR_REGISTER(Format, xmlFormat) @@ -719,7 +721,7 @@ BR_REGISTER(Format, xmlFormat)
719 /*! 721 /*!
720 * \ingroup formats 722 * \ingroup formats
721 * \brief Reads in scores or ground truth from a text table. 723 * \brief Reads in scores or ground truth from a text table.
722 - * \author Josh Klontz 724 + * \author Josh Klontz \cite jklontz
723 * 725 *
724 * Example of the format: 726 * Example of the format:
725 * \code 727 * \code
@@ -768,6 +770,191 @@ class scoresFormat : public Format @@ -768,6 +770,191 @@ class scoresFormat : public Format
768 770
769 BR_REGISTER(Format, scoresFormat) 771 BR_REGISTER(Format, scoresFormat)
770 772
  773 +/*!
  774 + * \ingroup formats
  775 + * \brief Reads FBI EBTS transactions.
  776 + * \author Scott Klum \cite sklum
  777 + * https://www.fbibiospecs.org/ebts.html
  778 + */
  779 +class ebtsFormat : public Format
  780 +{
  781 + Q_OBJECT
  782 +
  783 + struct Field {
  784 + int type;
  785 + QList<QByteArray> data;
  786 + };
  787 +
  788 + struct Record {
  789 + int type;
  790 + quint32 bytes;
  791 + int position; // Starting position of record
  792 +
  793 + QHash<int,QList<QByteArray> > fields;
  794 + };
  795 +
  796 + quint32 recordBytes(const QByteArray &byteArray, const float recordType, int from) const
  797 + {
  798 + bool ok;
  799 + quint32 size;
  800 +
  801 + if (recordType == 4 || recordType == 7) {
  802 + // read first four bytes
  803 + ok = true;
  804 + size = qFromBigEndian<quint32>((const uchar*)byteArray.mid(from,4).constData());
  805 + } else {
  806 + int index = byteArray.indexOf(QChar(0x1D), from);
  807 + size = byteArray.mid(from, index-from).split(':').last().toInt(&ok);
  808 + }
  809 +
  810 + return ok ? size : -1;
  811 + }
  812 +
  813 + void parseRecord(const QByteArray &byteArray, Record &record) const
  814 + {
  815 + if (record.type == 4 || record.type == 7) {
  816 + // Just a binary blob
  817 + // Read everything after the first four bytes
  818 + // Not current supported
  819 + } else {
  820 + // Continue reading fields until we get all the data
  821 + unsigned int position = record.position;
  822 + while (position < record.position + record.bytes) {
  823 + int index = byteArray.indexOf(QChar(0x1D), position);
  824 + Field field = parseField(byteArray.mid(position, index-position),QChar(0x1F));
  825 + if (field.type == 999 ) {
  826 + // Data begin after the field identifier and the colon
  827 + int dataBegin = byteArray.indexOf(':', position)+1;
  828 + field.data.clear();
  829 + field.data.append(byteArray.mid(dataBegin, record.bytes-(dataBegin-record.position)));
  830 +
  831 + // Data fields are always last in the record
  832 + record.fields.insert(field.type,field.data);
  833 + break;
  834 + }
  835 + // Advance the position accounting for the separator
  836 + position += index-position+1;
  837 + record.fields.insert(field.type,field.data);
  838 + }
  839 + }
  840 + }
  841 +
  842 + Field parseField(const QByteArray &byteArray, const QChar &sep) const
  843 + {
  844 + bool ok;
  845 + Field f;
  846 +
  847 + QList<QByteArray> data = byteArray.split(':');
  848 +
  849 + f.type = data.first().split('.').last().toInt(&ok);
  850 + f.data = data.last().split(sep.toLatin1());
  851 +
  852 + return f;
  853 + }
  854 +
  855 + Template read() const
  856 + {
  857 + QByteArray byteArray;
  858 + QtUtils::readFile(file, byteArray);
  859 +
  860 + Template t;
  861 +
  862 + Mat m;
  863 +
  864 + QList<Record> records;
  865 +
  866 + // Read the type one record (every EBTS file will have one of these)
  867 + Record r1;
  868 + r1.type = 1;
  869 + r1.position = 0;
  870 + r1.bytes = recordBytes(byteArray,r1.type,r1.position);
  871 +
  872 + // The fields in a type 1 record are strictly defined
  873 + QList<QByteArray> data = byteArray.mid(r1.position,r1.bytes).split(QChar(0x1D).toLatin1());
  874 + foreach (const QByteArray &datum, data) {
  875 + Field f = parseField(datum,QChar(0x1F));
  876 + r1.fields.insert(f.type,f.data);
  877 + }
  878 +
  879 + records.append(r1);
  880 +
  881 + // Read the type two record (every EBTS file will have one of these)
  882 + Record r2;
  883 + r2.type = 2;
  884 + r2.position = r1.bytes;
  885 + r2.bytes = recordBytes(byteArray,r2.type,r2.position);
  886 +
  887 + // The fields in a type 2 record are strictly defined
  888 + data = byteArray.mid(r2.position,r2.bytes).split(QChar(0x1D).toLatin1());
  889 + foreach (const QByteArray &datum, data) {
  890 + Field f = parseField(datum,QChar(0x1F));
  891 + r2.fields.insert(f.type,f.data);
  892 + }
  893 +
  894 + // Demographics
  895 + if (r2.fields.contains(18)) {
  896 + QString name = r2.fields.value(18).first();
  897 + QStringList names = name.split(',');
  898 + t.file.set("FIRSTNAME", names.at(1));
  899 + t.file.set("LASTNAME", names.at(0));
  900 + }
  901 +
  902 + if (r2.fields.contains(22)) t.file.set("DOB", r2.fields.value(22).first().toInt());
  903 + if (r2.fields.contains(24)) t.file.set("GENDER", QString(r2.fields.value(24).first()));
  904 + if (r2.fields.contains(25)) t.file.set("RACE", QString(r2.fields.value(25).first()));
  905 +
  906 + if (t.file.contains("DOB")) {
  907 + const QDate dob = QDate::fromString(t.file.get<QString>("DOB"), "yyyyMMdd");
  908 + const QDate current = QDate::currentDate();
  909 + int age = current.year() - dob.year();
  910 + if (current.month() < dob.month()) age--;
  911 + t.file.set("Age", age);
  912 + }
  913 +
  914 + records.append(r2);
  915 +
  916 + // The third field of the first record contains informations about all the remaining records in the transaction
  917 + // We don't care about the first two and the final items
  918 + QList<QByteArray> recordTypes = r1.fields.value(3);
  919 + for (int i=2; i<recordTypes.size()-1; i++) {
  920 + // The first two bytes indicate the record index (and we don't want the separator), but we only care about the type
  921 + QByteArray recordType = recordTypes[i].mid(3);
  922 + Record r;
  923 + r.type = recordType.toInt();
  924 + records.append(r);
  925 + }
  926 +
  927 + QList<int> frontalIdxs;
  928 + int position = r1.bytes + r2.bytes;
  929 + for (int i=2; i<records.size(); i++) {
  930 + records[i].position = position;
  931 + records[i].bytes = recordBytes(byteArray,records[i].type,position);
  932 +
  933 + parseRecord(byteArray, records[i]);
  934 + if (records[i].type == 10) frontalIdxs.append(i);
  935 + position += records[i].bytes;
  936 + }
  937 +
  938 + if (!frontalIdxs.isEmpty()) {
  939 + // We use the first type 10 record to get the frontal
  940 + QByteArray frontal = records[frontalIdxs.first()].fields.value(999).first();
  941 + m = imdecode(Mat(3, frontal.size(), CV_8UC3, frontal.data()), CV_LOAD_IMAGE_COLOR);
  942 + if (!m.data) qWarning("ebtsFormat::read failed to decode image data.");
  943 + t.m() = m;
  944 + } else qWarning("ebtsFormat::cannot find image data within file.");
  945 +
  946 + return t;
  947 + }
  948 +
  949 + void write(const Template &t) const
  950 + {
  951 + (void) t;
  952 + qFatal("Writing EBTS files is not supported.");
  953 + }
  954 +};
  955 +
  956 +BR_REGISTER(Format, ebtsFormat)
  957 +
771 } // namespace br 958 } // namespace br
772 959
773 #include "format.moc" 960 #include "format.moc"
openbr/plugins/gallery.cpp
@@ -985,7 +985,7 @@ class vbbGallery : public Gallery @@ -985,7 +985,7 @@ class vbbGallery : public Gallery
985 void init() 985 void init()
986 { 986 {
987 MatlabIO matio; 987 MatlabIO matio;
988 - QString filename = file.name; 988 + QString filename = (Globals->path.isEmpty() ? "" : Globals->path + "/") + file.name;
989 bool ok = matio.open(filename.toStdString(), "r"); 989 bool ok = matio.open(filename.toStdString(), "r");
990 if (!ok) qFatal("Couldn't open the vbb file"); 990 if (!ok) qFatal("Couldn't open the vbb file");
991 991
openbr/plugins/gui.cpp
1 #include <QApplication> 1 #include <QApplication>
2 #include <QLabel> 2 #include <QLabel>
3 #include <QElapsedTimer> 3 #include <QElapsedTimer>
  4 +#include <QInputDialog>
4 #include <QWaitCondition> 5 #include <QWaitCondition>
5 #include <QMutex> 6 #include <QMutex>
6 #include <QMouseEvent> 7 #include <QMouseEvent>
@@ -12,6 +13,7 @@ @@ -12,6 +13,7 @@
12 #include <QLineEdit> 13 #include <QLineEdit>
13 14
14 #include <opencv2/imgproc/imgproc.hpp> 15 #include <opencv2/imgproc/imgproc.hpp>
  16 +#include <opencv2/imgproc/imgproc_c.h>
15 #include "openbr_internal.h" 17 #include "openbr_internal.h"
16 #include "openbr/gui/utility.h" 18 #include "openbr/gui/utility.h"
17 19
@@ -181,6 +183,149 @@ public slots: @@ -181,6 +183,149 @@ public slots:
181 } 183 }
182 }; 184 };
183 185
  186 +class RectMarkingWindow : public DisplayWindow
  187 +{
  188 +public:
  189 + RectMarkingWindow() : DisplayWindow()
  190 + {
  191 + drawingRect = false;
  192 + }
  193 +
  194 + bool drawingRect;
  195 + QVector<QRectF> rects;
  196 + QList<QString> rectLabels;
  197 +
  198 + QPointF rectOrigin;
  199 + QPointF currentEnd;
  200 + QRectF currentRect;
  201 + bool disableAccept;
  202 +
  203 + bool eventFilter(QObject *obj, QEvent *event)
  204 + {
  205 + if (disableAccept)
  206 + return QObject::eventFilter(obj, event);
  207 +
  208 +
  209 + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove)
  210 + {
  211 + event->accept();
  212 +
  213 + QMouseEvent *mouseEvent = (QMouseEvent*)event;
  214 +
  215 + if (event->type() == QEvent::MouseButtonPress)
  216 + {
  217 +
  218 + if (mouseEvent->button() == Qt::LeftButton) {
  219 + if (!drawingRect)
  220 + {
  221 + drawingRect = true;
  222 + rectOrigin = mouseEvent->pos();
  223 + return true;
  224 + }
  225 + else
  226 + {
  227 + drawingRect = false;
  228 +
  229 + rects.append(QRectF(rectOrigin, mouseEvent->pos()));
  230 + rects.last() = rects.last().normalized();
  231 + // If no labels were provided, we store everything as anonymous rectangles
  232 + if (promptKeys.empty())
  233 + rectLabels.append("rects");
  234 + // otherwise, prompt the user to select a label
  235 + else
  236 + {
  237 + // get a label from the user
  238 + bool ok = false;
  239 +
  240 + // Don't intercept events while the sub-dialog is up (if we take the events, then it will not work correctly)
  241 + disableAccept = true;
  242 + QString res = QInputDialog::getItem(this, "Select a label", "", promptKeys, next_idx, false, &ok);
  243 +
  244 + disableAccept = false;
  245 + if (ok) {
  246 + rectLabels.append(res);
  247 + for (int i=0; i < promptKeys.size(); i++)
  248 + {
  249 + if (res == promptKeys[i]) {
  250 + next_idx = (i + 1) % promptKeys.size();
  251 + break;
  252 + }
  253 + }
  254 + }
  255 + else {
  256 + rects.remove(rects.size()-1);
  257 + }
  258 + }
  259 + }
  260 + }
  261 + // rclick -- reset state if drawing, remove last rect if done
  262 + else if (mouseEvent->button() == Qt::RightButton && (!rects.isEmpty() || drawingRect))
  263 + {
  264 + if(drawingRect)
  265 + drawingRect = false;
  266 + else
  267 + {
  268 + rects.remove(rects.size()-1);
  269 + rectLabels.removeLast();
  270 + }
  271 + }
  272 + }
  273 + else
  274 + currentEnd = mouseEvent->pos();
  275 + QPixmap pixmapBuffer = pixmap;
  276 +
  277 + QPainter painter(&pixmapBuffer);
  278 + painter.setPen(Qt::red);
  279 +
  280 + painter.drawRects(rects);
  281 +
  282 + if (drawingRect)
  283 + {
  284 + currentRect = QRectF(rectOrigin, currentEnd);
  285 + painter.setPen(Qt::green);
  286 + painter.drawRect(currentRect);
  287 + }
  288 +
  289 + setPixmap(pixmapBuffer);
  290 +
  291 + return true;
  292 + } else {
  293 + if (event->type() == QEvent::KeyPress)
  294 + {
  295 + QKeyEvent * kevent = (QKeyEvent *) event;
  296 + if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) {
  297 + event->accept();
  298 + return true;
  299 + }
  300 + }
  301 + return DisplayWindow::eventFilter(obj, event);
  302 + }
  303 + }
  304 +
  305 +
  306 + QList<QPointF> waitForKey()
  307 + {
  308 +
  309 + rects.clear();
  310 + drawingRect = false;
  311 + disableAccept = false;
  312 + next_idx = 0;
  313 + DisplayWindow::waitForKey();
  314 +
  315 + return QList<QPointF>();
  316 + }
  317 +
  318 + void setKeys(const QStringList & keys)
  319 + {
  320 + promptKeys = keys;
  321 + }
  322 +
  323 + int next_idx;
  324 +private:
  325 + QStringList promptKeys;
  326 +
  327 +};
  328 +
184 class PointMarkingWindow : public DisplayWindow 329 class PointMarkingWindow : public DisplayWindow
185 { 330 {
186 bool eventFilter(QObject *obj, QEvent *event) 331 bool eventFilter(QObject *obj, QEvent *event)
@@ -555,6 +700,91 @@ BR_REGISTER(Transform, ManualTransform) @@ -555,6 +700,91 @@ BR_REGISTER(Transform, ManualTransform)
555 700
556 /*! 701 /*!
557 * \ingroup transforms 702 * \ingroup transforms
  703 + * \brief Manual select rectangular regions on an image.
  704 + * Stores marked rectangles as anonymous rectangles, or if a set of labels is provided, prompt the user
  705 + * to select one of those labels after drawing each rectangle.
  706 + * \author Charles Otto \cite caotto
  707 + */
  708 +class ManualRectsTransform : public ShowTransform
  709 +{
  710 + Q_OBJECT
  711 +
  712 +public:
  713 +
  714 + Q_PROPERTY(QStringList labels READ get_labels WRITE set_labels RESET reset_labels STORED false)
  715 + BR_PROPERTY(QStringList, labels, QStringList())
  716 +
  717 + void projectUpdate(const TemplateList &src, TemplateList &dst)
  718 + {
  719 +
  720 + dst = src;
  721 +
  722 + if (!Globals->useGui)
  723 + return;
  724 + if (src.empty())
  725 + return;
  726 +
  727 + for (int i = 0; i < dst.size(); i++) {
  728 + foreach(const cv::Mat &m, dst[i]) {
  729 + qImageBuffer = toQImage(m);
  730 + displayBuffer->convertFromImage(qImageBuffer);
  731 +
  732 + emit updateImage(displayBuffer->copy(displayBuffer->rect()));
  733 +
  734 + // Blocking wait for a key-press
  735 + if (this->waitInput) {
  736 + window->waitForKey();
  737 + QVector<QRectF> rectSet = trueWindow->rects;
  738 + QList<QString> labelSet= trueWindow->rectLabels;
  739 +
  740 + for (int idx = 0; idx < rectSet.size(); idx++)
  741 + {
  742 + if (dst[i].file.contains(labelSet[idx]))
  743 + {
  744 + QVariant currentProp = dst[i].file.value(labelSet[idx]);
  745 + QList<QVariant> currentPropList;
  746 +
  747 + if (currentProp.canConvert<QList<QVariant> >() )
  748 + {
  749 + currentPropList = currentProp.toList();
  750 + }
  751 + else if (currentProp.canConvert<QRectF>())
  752 + {
  753 + currentPropList.append(currentProp);
  754 + }
  755 + else
  756 + {
  757 + qFatal("Unknown type of property");
  758 + }
  759 +
  760 + currentPropList.append(rectSet[idx]);
  761 + dst[i].file.set(labelSet[idx], QVariant::fromValue(currentPropList));
  762 + }
  763 + else
  764 + {
  765 + dst[i].file.set(labelSet[idx], rectSet[idx]);
  766 + }
  767 + }
  768 + }
  769 +
  770 + }
  771 + }
  772 + }
  773 + RectMarkingWindow * trueWindow;
  774 + void init()
  775 + {
  776 + if (!Globals->useGui)
  777 + return;
  778 + initActual<RectMarkingWindow>();
  779 + trueWindow = dynamic_cast<RectMarkingWindow *> (this->window);
  780 + trueWindow->setKeys(this->keys);
  781 + }
  782 +};
  783 +
  784 +BR_REGISTER(Transform, ManualRectsTransform)
  785 +
  786 +/*!
  787 + * \ingroup transforms
558 * \brief Elicits metadata for templates in a pretty GUI 788 * \brief Elicits metadata for templates in a pretty GUI
559 * \author Scott Klum \cite sklum 789 * \author Scott Klum \cite sklum
560 */ 790 */
openbr/plugins/hist.cpp
@@ -94,7 +94,7 @@ class BinTransform : public UntrainableTransform @@ -94,7 +94,7 @@ class BinTransform : public UntrainableTransform
94 } else if (channels == 2) { 94 } else if (channels == 2) {
95 // If there are two channels, the first is channel is assumed to be a weight vector 95 // If there are two channels, the first is channel is assumed to be a weight vector
96 // and the second channel contains the vectors we would like to bin. 96 // and the second channel contains the vectors we would like to bin.
97 - vector<Mat> mv; 97 + std::vector<Mat> mv;
98 cv::split(src, mv); 98 cv::split(src, mv);
99 weights = mv[0]; 99 weights = mv[0];
100 weights.convertTo(weights, CV_32F); 100 weights.convertTo(weights, CV_32F);
openbr/plugins/integral.cpp
1 #include <opencv2/imgproc/imgproc.hpp> 1 #include <opencv2/imgproc/imgproc.hpp>
  2 +#include <opencv2/imgproc/imgproc_c.h>
2 #include <Eigen/Core> 3 #include <Eigen/Core>
3 #include "openbr_internal.h" 4 #include "openbr_internal.h"
4 5
@@ -293,7 +294,7 @@ private: @@ -293,7 +294,7 @@ private:
293 Sobel(src, dx, CV_32F, 1, 0, CV_SCHARR); 294 Sobel(src, dx, CV_32F, 1, 0, CV_SCHARR);
294 Sobel(src, dy, CV_32F, 0, 1, CV_SCHARR); 295 Sobel(src, dy, CV_32F, 0, 1, CV_SCHARR);
295 cartToPolar(dx, dy, magnitude, angle, true); 296 cartToPolar(dx, dy, magnitude, angle, true);
296 - vector<Mat> mv; 297 + std::vector<Mat> mv;
297 if ((channel == Magnitude) || (channel == MagnitudeAndAngle)) { 298 if ((channel == Magnitude) || (channel == MagnitudeAndAngle)) {
298 const float theoreticalMaxMagnitude = sqrt(2*pow(float(2*(3+10+3)*255), 2.f)); 299 const float theoreticalMaxMagnitude = sqrt(2*pow(float(2*(3+10+3)*255), 2.f));
299 mv.push_back(magnitude / theoreticalMaxMagnitude); 300 mv.push_back(magnitude / theoreticalMaxMagnitude);
openbr/plugins/landmarks.cpp
@@ -290,16 +290,18 @@ class DrawDelaunayTransform : public UntrainableTransform @@ -290,16 +290,18 @@ class DrawDelaunayTransform : public UntrainableTransform
290 290
291 void project(const Template &src, Template &dst) const 291 void project(const Template &src, Template &dst) const
292 { 292 {
293 - QList<Point2f> validTriangles = OpenCVUtils::toPoints(src.file.getList<QPointF>("DelaunayTriangles")); 293 + dst = src;
294 294
295 - // Clone the matrix do draw on it  
296 - dst.m() = src.m().clone(); 295 + if (src.file.contains("DelaunayTriangles")) {
  296 + QList<Point2f> validTriangles = OpenCVUtils::toPoints(src.file.getList<QPointF>("DelaunayTriangles"));
297 297
298 - for (int i = 0; i < validTriangles.size(); i+=3) {  
299 - line(dst.m(), validTriangles[i], validTriangles[i+1], Scalar(0,0,0), 1);  
300 - line(dst.m(), validTriangles[i+1], validTriangles[i+2], Scalar(0,0,0), 1);  
301 - line(dst.m(), validTriangles[i+2], validTriangles[i], Scalar(0,0,0), 1);  
302 - } 298 + // Clone the matrix do draw on it
  299 + for (int i = 0; i < validTriangles.size(); i+=3) {
  300 + line(dst, validTriangles[i], validTriangles[i+1], Scalar(0,0,0), 1);
  301 + line(dst, validTriangles[i+1], validTriangles[i+2], Scalar(0,0,0), 1);
  302 + line(dst, validTriangles[i+2], validTriangles[i], Scalar(0,0,0), 1);
  303 + }
  304 + } else qWarning("Template does not contain Delaunay triangulation.");
303 } 305 }
304 }; 306 };
305 307
openbr/plugins/lbp.cpp
@@ -15,7 +15,9 @@ @@ -15,7 +15,9 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/imgproc/imgproc.hpp> 17 #include <opencv2/imgproc/imgproc.hpp>
  18 +#include <opencv2/imgproc/imgproc_c.h>
18 #include <opencv2/highgui/highgui.hpp> 19 #include <opencv2/highgui/highgui.hpp>
  20 +#include <opencv2/highgui/highgui_c.h>
19 #include <limits> 21 #include <limits>
20 #include "openbr_internal.h" 22 #include "openbr_internal.h"
21 23
openbr/plugins/ltp.cpp
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/imgproc/imgproc.hpp> 17 #include <opencv2/imgproc/imgproc.hpp>
  18 +#include <opencv2/imgproc/imgproc_c.h>
18 #include <limits> 19 #include <limits>
19 #include "openbr_internal.h" 20 #include "openbr_internal.h"
20 21
openbr/plugins/mask.cpp
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <opencv2/imgproc/imgproc.hpp> 17 #include <opencv2/imgproc/imgproc.hpp>
  18 +#include <opencv2/imgproc/imgproc_c.h>
18 #include "openbr_internal.h" 19 #include "openbr_internal.h"
19 20
20 using namespace cv; 21 using namespace cv;
@@ -164,7 +165,7 @@ class LargestConvexAreaTransform : public UntrainableTransform @@ -164,7 +165,7 @@ class LargestConvexAreaTransform : public UntrainableTransform
164 void project(const Template &src, Template &dst) const 165 void project(const Template &src, Template &dst) const
165 { 166 {
166 std::vector< std::vector<Point> > contours; 167 std::vector< std::vector<Point> > contours;
167 - findContours(src.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); 168 + findContours(src.m().clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
168 double maxArea = 0; 169 double maxArea = 0;
169 foreach (const std::vector<Point> &contour, contours) { 170 foreach (const std::vector<Point> &contour, contours) {
170 std::vector<Point> hull; 171 std::vector<Point> hull;
openbr/plugins/misc.cpp
@@ -537,7 +537,7 @@ class ProgressCounterTransform : public TimeVaryingTransform @@ -537,7 +537,7 @@ class ProgressCounterTransform : public TimeVaryingTransform
537 { 537 {
538 (void) data; 538 (void) data;
539 float p = br_progress(); 539 float p = br_progress();
540 - qDebug("%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g/%g \r", p*100., QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep, Globals->totalSteps); 540 + qDebug("%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g/%g \r", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep, Globals->totalSteps);
541 } 541 }
542 542
543 void init() 543 void init()
openbr/plugins/motion.cpp
@@ -23,6 +23,7 @@ class OpticalFlowTransform : public UntrainableMetaTransform @@ -23,6 +23,7 @@ class OpticalFlowTransform : public UntrainableMetaTransform
23 Q_PROPERTY(int poly_n READ get_poly_n WRITE set_poly_n RESET reset_poly_n STORED false) 23 Q_PROPERTY(int poly_n READ get_poly_n WRITE set_poly_n RESET reset_poly_n STORED false)
24 Q_PROPERTY(double poly_sigma READ get_poly_sigma WRITE set_poly_sigma RESET reset_poly_sigma STORED false) 24 Q_PROPERTY(double poly_sigma READ get_poly_sigma WRITE set_poly_sigma RESET reset_poly_sigma STORED false)
25 Q_PROPERTY(int flags READ get_flags WRITE set_flags RESET reset_flags STORED false) 25 Q_PROPERTY(int flags READ get_flags WRITE set_flags RESET reset_flags STORED false)
  26 + Q_PROPERTY(bool useMagnitude READ get_useMagnitude WRITE set_useMagnitude RESET reset_useMagnitude STORED false)
26 // these defaults are optimized for KTH 27 // these defaults are optimized for KTH
27 BR_PROPERTY(double, pyr_scale, 0.1) 28 BR_PROPERTY(double, pyr_scale, 0.1)
28 BR_PROPERTY(int, levels, 1) 29 BR_PROPERTY(int, levels, 1)
@@ -31,22 +32,27 @@ class OpticalFlowTransform : public UntrainableMetaTransform @@ -31,22 +32,27 @@ class OpticalFlowTransform : public UntrainableMetaTransform
31 BR_PROPERTY(int, poly_n, 7) 32 BR_PROPERTY(int, poly_n, 7)
32 BR_PROPERTY(double, poly_sigma, 1.1) 33 BR_PROPERTY(double, poly_sigma, 1.1)
33 BR_PROPERTY(int, flags, 0) 34 BR_PROPERTY(int, flags, 0)
  35 + BR_PROPERTY(bool, useMagnitude, true)
34 36
35 void project(const Template &src, Template &dst) const 37 void project(const Template &src, Template &dst) const
36 { 38 {
37 // get the two images put there by AggregateFrames 39 // get the two images put there by AggregateFrames
38 if (src.size() != 2) qFatal("Optical Flow requires two images."); 40 if (src.size() != 2) qFatal("Optical Flow requires two images.");
39 - Mat prevImg = src[0], nextImg = src[1], flow, flowOneCh; 41 + Mat prevImg = src[0], nextImg = src[1], flow;
40 if (src[0].channels() != 1) OpenCVUtils::cvtGray(src[0], prevImg); 42 if (src[0].channels() != 1) OpenCVUtils::cvtGray(src[0], prevImg);
41 if (src[1].channels() != 1) OpenCVUtils::cvtGray(src[1], nextImg); 43 if (src[1].channels() != 1) OpenCVUtils::cvtGray(src[1], nextImg);
42 calcOpticalFlowFarneback(prevImg, nextImg, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags); 44 calcOpticalFlowFarneback(prevImg, nextImg, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags);
43 45
44 - // the result is two channels  
45 - std::vector<Mat> channels(2);  
46 - split(flow, channels);  
47 - magnitude(channels[0], channels[1], flowOneCh);  
48 -  
49 - dst += flowOneCh; 46 + if (useMagnitude) {
  47 + // the result is two channels
  48 + Mat flowOneCh;
  49 + std::vector<Mat> channels(2);
  50 + split(flow, channels);
  51 + magnitude(channels[0], channels[1], flowOneCh);
  52 + dst += flowOneCh;
  53 + } else {
  54 + dst += flow;
  55 + }
50 dst.file = src.file; 56 dst.file = src.file;
51 } 57 }
52 }; 58 };
@@ -62,7 +68,8 @@ class SubtractBackgroundTransform : public TimeVaryingTransform @@ -62,7 +68,8 @@ class SubtractBackgroundTransform : public TimeVaryingTransform
62 { 68 {
63 Q_OBJECT 69 Q_OBJECT
64 70
65 - BackgroundSubtractorMOG2 mog; 71 + // TODO: This is broken.
  72 + // BackgroundSubtractorMOG2 mog;
66 73
67 public: 74 public:
68 SubtractBackgroundTransform() : TimeVaryingTransform(false, false) {} 75 SubtractBackgroundTransform() : TimeVaryingTransform(false, false) {}
@@ -72,7 +79,8 @@ private: @@ -72,7 +79,8 @@ private:
72 { 79 {
73 dst = src; 80 dst = src;
74 Mat mask; 81 Mat mask;
75 - mog(src, mask); 82 + // TODO: broken
  83 + // mog(src, mask);
76 erode(mask, mask, Mat()); 84 erode(mask, mask, Mat());
77 dilate(mask, mask, Mat()); 85 dilate(mask, mask, Mat());
78 dst.file.set("Mask", QVariant::fromValue(mask)); 86 dst.file.set("Mask", QVariant::fromValue(mask));
@@ -86,7 +94,8 @@ private: @@ -86,7 +94,8 @@ private:
86 void finalize(TemplateList &output) 94 void finalize(TemplateList &output)
87 { 95 {
88 (void) output; 96 (void) output;
89 - mog = BackgroundSubtractorMOG2(); 97 + // TODO: Broken
  98 + // mog = BackgroundSubtractorMOG2();
90 } 99 }
91 }; 100 };
92 101
openbr/plugins/normalize.cpp
@@ -60,24 +60,29 @@ class NormalizeTransform : public UntrainableTransform @@ -60,24 +60,29 @@ class NormalizeTransform : public UntrainableTransform
60 60
61 Q_PROPERTY(bool ByRow READ get_ByRow WRITE set_ByRow RESET reset_ByRow STORED false) 61 Q_PROPERTY(bool ByRow READ get_ByRow WRITE set_ByRow RESET reset_ByRow STORED false)
62 BR_PROPERTY(bool, ByRow, false) 62 BR_PROPERTY(bool, ByRow, false)
  63 + Q_PROPERTY(int alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false)
  64 + BR_PROPERTY(int, alpha, 1)
  65 + Q_PROPERTY(int beta READ get_beta WRITE set_beta RESET reset_beta STORED false)
  66 + BR_PROPERTY(int, beta, 0)
63 67
64 public: 68 public:
65 /*!< */ 69 /*!< */
66 enum NormType { Inf = NORM_INF, 70 enum NormType { Inf = NORM_INF,
67 L1 = NORM_L1, 71 L1 = NORM_L1,
68 - L2 = NORM_L2 }; 72 + L2 = NORM_L2,
  73 + Range = NORM_MINMAX };
69 74
70 private: 75 private:
71 BR_PROPERTY(NormType, normType, L2) 76 BR_PROPERTY(NormType, normType, L2)
72 77
73 void project(const Template &src, Template &dst) const 78 void project(const Template &src, Template &dst) const
74 { 79 {
75 - if (!ByRow) normalize(src, dst, 1, 0, normType, CV_32F); 80 + if (!ByRow) normalize(src, dst, alpha, beta, normType, CV_32F);
76 else { 81 else {
77 dst = src; 82 dst = src;
78 for (int i=0; i<dst.m().rows; i++) { 83 for (int i=0; i<dst.m().rows; i++) {
79 Mat temp; 84 Mat temp;
80 - cv::normalize(dst.m().row(i), temp, 1, 0, normType); 85 + cv::normalize(dst.m().row(i), temp, alpha, beta, normType);
81 temp.copyTo(dst.m().row(i)); 86 temp.copyTo(dst.m().row(i));
82 } 87 }
83 } 88 }
@@ -132,7 +137,7 @@ private: @@ -132,7 +137,7 @@ private:
132 const QList<int> labels = data.indexProperty(inputVariable); 137 const QList<int> labels = data.indexProperty(inputVariable);
133 const int dims = m.cols; 138 const int dims = m.cols;
134 139
135 - vector<Mat> mv, av, bv; 140 + std::vector<Mat> mv, av, bv;
136 split(m, mv); 141 split(m, mv);
137 for (size_t c = 0; c < mv.size(); c++) { 142 for (size_t c = 0; c < mv.size(); c++) {
138 av.push_back(Mat(1, dims, CV_64FC1)); 143 av.push_back(Mat(1, dims, CV_64FC1));
openbr/plugins/output.cpp
@@ -268,7 +268,7 @@ class rrOutput : public MatrixOutput @@ -268,7 +268,7 @@ class rrOutput : public MatrixOutput
268 268
269 for (int i=0; i<queryFiles.size(); i++) { 269 for (int i=0; i<queryFiles.size(); i++) {
270 QStringList files; 270 QStringList files;
271 - if (simple) files.append(queryFiles[i]); 271 + if (simple) files.append(queryFiles[i].fileName());
272 272
273 typedef QPair<float,int> Pair; 273 typedef QPair<float,int> Pair;
274 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) { 274 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) {
@@ -276,7 +276,7 @@ class rrOutput : public MatrixOutput @@ -276,7 +276,7 @@ class rrOutput : public MatrixOutput
276 if (pair.first < threshold) break; 276 if (pair.first < threshold) break;
277 File target = targetFiles[pair.second]; 277 File target = targetFiles[pair.second];
278 target.set("Score", QString::number(pair.first)); 278 target.set("Score", QString::number(pair.first));
279 - if (simple) files.append(target.baseName() + " " + QString::number(pair.first)); 279 + if (simple) files.append(target.fileName() + " " + QString::number(pair.first));
280 else files.append(target.flat()); 280 else files.append(target.flat());
281 } 281 }
282 } 282 }
@@ -528,7 +528,7 @@ class tailOutput : public Output @@ -528,7 +528,7 @@ class tailOutput : public Output
528 } else { 528 } else {
529 // General case 529 // General case
530 for (int k=0; k<comparisons.size(); k++) { 530 for (int k=0; k<comparisons.size(); k++) {
531 - if (comparisons[k].value < value) { 531 + if (comparisons[k].value <= value) {
532 comparisons.insert(k, Comparison(queryFiles[i], targetFiles[j], value)); 532 comparisons.insert(k, Comparison(queryFiles[i], targetFiles[j], value));
533 break; 533 break;
534 } 534 }
@@ -539,6 +539,7 @@ class tailOutput : public Output @@ -539,6 +539,7 @@ class tailOutput : public Output
539 comparisons.removeLast(); 539 comparisons.removeLast();
540 while ((comparisons.size() > atLeast) && (comparisons.last().value < threshold)) 540 while ((comparisons.size() > atLeast) && (comparisons.last().value < threshold))
541 comparisons.removeLast(); 541 comparisons.removeLast();
  542 +
542 lastValue = comparisons.last().value; 543 lastValue = comparisons.last().value;
543 comparisonsLock.unlock(); 544 comparisonsLock.unlock();
544 } 545 }
openbr/plugins/segmentation.cpp 0 → 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +#include "openbr_internal.h"
  3 +#include "openbr/core/opencvutils.h"
  4 +
  5 +using namespace cv;
  6 +
  7 +namespace br
  8 +{
  9 +
  10 +/*!
  11 + * \ingroup transforms
  12 + * \brief Applies watershed segmentation.
  13 + * \author Austin Blanton \cite imaus10
  14 + */
  15 +class WatershedSegmentationTransform : public UntrainableTransform
  16 +{
  17 + Q_OBJECT
  18 + void project(const Template &src, Template &dst) const
  19 + {
  20 + dst = src;
  21 +
  22 + Mat mod;
  23 +// adaptiveThreshold(src.m(), src.m(), 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 33, 5);
  24 + threshold(src.m(), mod, 0, 255, THRESH_BINARY+THRESH_OTSU);
  25 +
  26 + // findContours requires an 8-bit 1-channel image
  27 + // and modifies its source image
  28 + if (mod.depth() != CV_8U) OpenCVUtils::cvtUChar(mod, mod);
  29 + if (mod.channels() != 1) OpenCVUtils::cvtGray(mod, mod);
  30 + vector<vector<Point> > contours;
  31 + vector<Vec4i> hierarchy;
  32 + findContours(mod, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
  33 +
  34 + // draw the contour delineations as 1,2,3... for input to watershed
  35 + Mat markers = Mat::zeros(mod.size(), CV_32S);
  36 + int compCount=0;
  37 + for (int idx=0; idx>=0; idx=hierarchy[idx][0], compCount++) {
  38 + drawContours(markers, contours, idx, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
  39 + }
  40 +
  41 + Mat orig = src.m();
  42 + // watershed requires a 3-channel 8-bit image
  43 + if (orig.channels() == 1) cvtColor(orig, orig, CV_GRAY2BGR);
  44 + watershed(orig, markers);
  45 + dst.file.set("SegmentsMask", QVariant::fromValue(markers));
  46 + dst.file.set("NumSegments", compCount);
  47 + }
  48 +};
  49 +BR_REGISTER(Transform, WatershedSegmentationTransform)
  50 +
  51 +} // namespace br
  52 +
  53 +#include "segmentation.moc"
openbr/plugins/stasm4.cpp
@@ -38,8 +38,10 @@ class StasmInitializer : public Initializer @@ -38,8 +38,10 @@ class StasmInitializer : public Initializer
38 Globals->abbreviations.insert("RectFromStasmEyes","RectFromPoints([28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],0.3,5.3)"); 38 Globals->abbreviations.insert("RectFromStasmEyes","RectFromPoints([28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],0.3,5.3)");
39 Globals->abbreviations.insert("RectFromStasmBrow","RectFromPoints([16,17,18,19,20,21,22,23,24,25,26,27],0.15,5)"); 39 Globals->abbreviations.insert("RectFromStasmBrow","RectFromPoints([16,17,18,19,20,21,22,23,24,25,26,27],0.15,5)");
40 Globals->abbreviations.insert("RectFromStasmNose","RectFromPoints([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58],0.15,1.15)"); 40 Globals->abbreviations.insert("RectFromStasmNose","RectFromPoints([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58],0.15,1.15)");
  41 + Globals->abbreviations.insert("RectFromStasmNoseWithBridge", "RectFromPoints([21, 22, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58],0.15,.6)");
41 Globals->abbreviations.insert("RectFromStasmMouth","RectFromPoints([59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76],0.3,2)"); 42 Globals->abbreviations.insert("RectFromStasmMouth","RectFromPoints([59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76],0.3,2)");
42 Globals->abbreviations.insert("RectFromStasmHair", "RectFromPoints([13,14,15],1.75,1.5)"); 43 Globals->abbreviations.insert("RectFromStasmHair", "RectFromPoints([13,14,15],1.75,1.5)");
  44 + Globals->abbreviations.insert("RectFromStasmJaw", "RectFromPoints([2,3,4,5,6,7,8,9,10],.25,1.6)");
43 } 45 }
44 }; 46 };
45 47
openbr/plugins/stream.cpp
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 #include <QQueue> 7 #include <QQueue>
8 #include <QtConcurrent> 8 #include <QtConcurrent>
9 #include <opencv/highgui.h> 9 #include <opencv/highgui.h>
  10 +#include <opencv2/highgui/highgui.hpp>
10 #include "openbr_internal.h" 11 #include "openbr_internal.h"
11 #include "openbr/core/common.h" 12 #include "openbr/core/common.h"
12 #include "openbr/core/opencvutils.h" 13 #include "openbr/core/opencvutils.h"
@@ -346,10 +347,11 @@ public: @@ -346,10 +347,11 @@ public:
346 } 347 }
347 348
348 // first 4 bytes store 0xEDFE, next 24 store 'Norpix seq ' 349 // first 4 bytes store 0xEDFE, next 24 store 'Norpix seq '
349 - char *firstFour = new char[4], *nextTwentyFour; 350 + char firstFour[4];
350 seqFile.seekg(0, ios::beg); 351 seqFile.seekg(0, ios::beg);
351 seqFile.read(firstFour, 4); 352 seqFile.read(firstFour, 4);
352 - nextTwentyFour = readText(24); 353 + char nextTwentyFour[24];
  354 + readText(24, nextTwentyFour);
353 if (firstFour[0] != (char)0xED || firstFour[1] != (char)0xFE || strncmp(nextTwentyFour, "Norpix seq", 10) != 0) { 355 if (firstFour[0] != (char)0xED || firstFour[1] != (char)0xFE || strncmp(nextTwentyFour, "Norpix seq", 10) != 0) {
354 qDebug("Invalid header in seq file"); 356 qDebug("Invalid header in seq file");
355 return false; 357 return false;
@@ -362,7 +364,8 @@ public: @@ -362,7 +364,8 @@ public:
362 qDebug("Invalid header size"); 364 qDebug("Invalid header size");
363 return false; 365 return false;
364 } 366 }
365 - char *desc = readText(512); 367 + char desc[512];
  368 + readText(512, desc);
366 basis.file.set("Description", QString(desc)); 369 basis.file.set("Description", QString(desc));
367 370
368 width = readInt(); 371 width = readInt();
@@ -413,9 +416,9 @@ public: @@ -413,9 +416,9 @@ public:
413 // but there might be 16 extra bytes instead of 8... 416 // but there might be 16 extra bytes instead of 8...
414 if (i == 1) { 417 if (i == 1) {
415 seqFile.seekg(s, ios::beg); 418 seqFile.seekg(s, ios::beg);
416 - char *zero = new char[1];  
417 - seqFile.read(zero, 1);  
418 - if (zero[0] == 0) { 419 + char zero;
  420 + seqFile.read(&zero, 1);
  421 + if (zero == 0) {
419 s += 8; 422 s += 8;
420 extra += 8; 423 extra += 8;
421 } 424 }
@@ -430,10 +433,12 @@ public: @@ -430,10 +433,12 @@ public:
430 } 433 }
431 434
432 #ifdef CVMATIO 435 #ifdef CVMATIO
433 - QString f = basis.file.name;  
434 - QString vbb = f.replace(f.lastIndexOf("."), 4, ".vbb");  
435 - vbb.replace(vbb.lastIndexOf("vid"), 3, "annotations");  
436 - annotations = TemplateList::fromGallery(File(vbb)); 436 + if (basis.file.contains("vbb")) {
  437 + QString vbb = basis.file.get<QString>("vbb");
  438 + annotations = TemplateList::fromGallery(File(vbb));
  439 + }
  440 +#else
  441 + qWarning("cvmatio not installed, bounding boxes will not be available. Add -DBR_WITH_CVMATIO cmake flag to install.");
437 #endif 442 #endif
438 443
439 return true; 444 return true;
@@ -497,14 +502,13 @@ private: @@ -497,14 +502,13 @@ private:
497 // apparently the text in seq files is 16 bit characters (UTF-16?) 502 // apparently the text in seq files is 16 bit characters (UTF-16?)
498 // since we don't really need the last byte, snad since it gets interpreted as 503 // since we don't really need the last byte, snad since it gets interpreted as
499 // a terminating char, let's just grab the first byte for storage 504 // a terminating char, let's just grab the first byte for storage
500 - char* readText(int bytes) 505 + void readText(int bytes, char * buffer)
501 { 506 {
502 - char *text = new char[bytes], *ret = new char[bytes/2];  
503 - seqFile.read(text, bytes); 507 + seqFile.read(buffer, bytes);
504 for (int i=0; i<bytes; i+=2) { 508 for (int i=0; i<bytes; i+=2) {
505 - ret[i/2] = text[i]; 509 + buffer[i/2] = buffer[i];
506 } 510 }
507 - return ret; 511 + buffer[bytes/2] = '\0';
508 } 512 }
509 513
510 protected: 514 protected:
@@ -736,7 +740,6 @@ protected: @@ -736,7 +740,6 @@ protected:
736 frameSource = new VideoReader(); 740 frameSource = new VideoReader();
737 } 741 }
738 } 742 }
739 -  
740 open_res = frameSource->open(curr); 743 open_res = frameSource->open(curr);
741 if (!open_res) 744 if (!open_res)
742 { 745 {
@@ -1420,6 +1423,7 @@ public: @@ -1420,6 +1423,7 @@ public:
1420 { 1423 {
1421 // Delete all the stages 1424 // Delete all the stages
1422 for (int i = 0; i < processingStages.size(); i++) { 1425 for (int i = 0; i < processingStages.size(); i++) {
  1426 +// TODO: Are we releasing memory which is already freed?
1423 delete processingStages[i]; 1427 delete processingStages[i];
1424 } 1428 }
1425 processingStages.clear(); 1429 processingStages.clear();
openbr/plugins/validate.cpp
@@ -65,9 +65,9 @@ class CrossValidateTransform : public MetaTransform @@ -65,9 +65,9 @@ class CrossValidateTransform : public MetaTransform
65 const QString label = partitionedData.at(j).file.get<QString>("Label"); 65 const QString label = partitionedData.at(j).file.get<QString>("Label");
66 QList<int> subjectIndices = partitionedData.find("Label",label); 66 QList<int> subjectIndices = partitionedData.find("Label",label);
67 QList<int> removed; 67 QList<int> removed;
68 - // Remove test only data 68 + // Remove target only data
69 for (int k=subjectIndices.size()-1; k>=0; k--) 69 for (int k=subjectIndices.size()-1; k>=0; k--)
70 - if (partitionedData[subjectIndices[k]].file.getBool("testOnly")) { 70 + if (partitionedData[subjectIndices[k]].file.getBool("targetOnly")) {
71 removed.append(subjectIndices[k]); 71 removed.append(subjectIndices[k]);
72 subjectIndices.removeAt(k); 72 subjectIndices.removeAt(k);
73 } 73 }
@@ -108,6 +108,7 @@ class CrossValidateTransform : public MetaTransform @@ -108,6 +108,7 @@ class CrossValidateTransform : public MetaTransform
108 // If we want to duplicate templates but use the same training data 108 // If we want to duplicate templates but use the same training data
109 // for all partitions (i.e. transforms.size() == 1), we need to 109 // for all partitions (i.e. transforms.size() == 1), we need to
110 // restrict the partition 110 // restrict the partition
  111 +
111 int partition = src.file.get<int>("Partition", 0); 112 int partition = src.file.get<int>("Partition", 0);
112 partition = (partition >= transforms.size()) ? 0 : partition; 113 partition = (partition >= transforms.size()) ? 0 : partition;
113 transforms[partition]->project(src, dst); 114 transforms[partition]->project(src, dst);
scripts/brpy/__init__.py
@@ -12,6 +12,14 @@ def _var_string_args(n): @@ -12,6 +12,14 @@ def _var_string_args(n):
12 s.extend(_string_args(n)) 12 s.extend(_string_args(n))
13 return s 13 return s
14 14
  15 +def _handle_string_func(func):
  16 + def call_func(*args):
  17 + howlong = func('', 0, *args)
  18 + msg = 'x'*(howlong-1)
  19 + func(msg, howlong, *args)
  20 + return msg
  21 + return call_func
  22 +
15 def init_brpy(br_loc='/usr/local/lib'): 23 def init_brpy(br_loc='/usr/local/lib'):
16 """Takes the ctypes lib object for br and initializes all function inputs and outputs""" 24 """Takes the ctypes lib object for br and initializes all function inputs and outputs"""
17 br_loc += '/libopenbr.%s' 25 br_loc += '/libopenbr.%s'
@@ -48,9 +56,14 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;): @@ -48,9 +56,14 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
48 br.br_is_classifier.restype = c_bool 56 br.br_is_classifier.restype = c_bool
49 br.br_make_mask.argtypes = _string_args(3) 57 br.br_make_mask.argtypes = _string_args(3)
50 br.br_make_pairwise_mask.argtypes = _string_args(3) 58 br.br_make_pairwise_mask.argtypes = _string_args(3)
51 - br.br_most_recent_message.restype = c_char_p  
52 - br.br_objects.argtypes = _string_args(2) + [c_bool]  
53 - br.br_objects.restype = c_char_p 59 + br.br_most_recent_message.argtypes = [c_char_p, c_int]
  60 + br.br_most_recent_message.restype = c_int
  61 + func = br.br_most_recent_message.__call__
  62 + br.br_most_recent_message = _handle_string_func(func)
  63 + br.br_objects.argtypes = [c_char_p, c_int] + _string_args(2) + [c_bool]
  64 + br.br_objects.restype = c_int
  65 + func2 = br.br_objects.__call__
  66 + br.br_objects = _handle_string_func(func2)
54 br.br_plot.argtypes = plot_args 67 br.br_plot.argtypes = plot_args
55 br.br_plot.restype = c_bool 68 br.br_plot.restype = c_bool
56 br.br_plot_detection.argtypes = plot_args 69 br.br_plot_detection.argtypes = plot_args
@@ -61,7 +74,10 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;): @@ -61,7 +74,10 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
61 br.br_plot_metadata.restype = c_bool 74 br.br_plot_metadata.restype = c_bool
62 br.br_progress.restype = c_float 75 br.br_progress.restype = c_float
63 br.br_read_pipe.argtypes = [c_char_p, POINTER(c_int), POINTER(POINTER(c_char_p))] 76 br.br_read_pipe.argtypes = [c_char_p, POINTER(c_int), POINTER(POINTER(c_char_p))]
64 - br.br_scratch_path.restype = c_char_p 77 + br.br_scratch_path.argtypes = [c_char_p, c_int]
  78 + br.br_scratch_path.restype = c_int
  79 + func3 = br.br_scratch_path.__call__
  80 + br.br_scratch_path = _handle_string_func(func3)
65 br.br_sdk_path.restype = c_char_p 81 br.br_sdk_path.restype = c_char_p
66 br.br_get_header.argtypes = [c_char_p, POINTER(c_char_p), POINTER(c_char_p)] 82 br.br_get_header.argtypes = [c_char_p, POINTER(c_char_p), POINTER(c_char_p)]
67 br.br_set_header.argtypes = _string_args(3) 83 br.br_set_header.argtypes = _string_args(3)
@@ -88,11 +104,15 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;): @@ -88,11 +104,15 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
88 br.br_img_channels.restype = c_int 104 br.br_img_channels.restype = c_int
89 br.br_img_is_empty.argtypes = [c_void_p] 105 br.br_img_is_empty.argtypes = [c_void_p]
90 br.br_img_is_empty.restype = c_bool 106 br.br_img_is_empty.restype = c_bool
91 - br.br_get_filename.argtypes = [c_void_p]  
92 - br.br_get_filename.restype = c_char_p 107 + br.br_get_filename.argtypes = [c_char_p, c_int, c_void_p]
  108 + br.br_get_filename.restype = c_int
  109 + func4 = br.br_get_filename.__call__
  110 + br.br_get_filename = _handle_string_func(func4)
93 br.br_set_filename.argtypes = [c_void_p, c_char_p] 111 br.br_set_filename.argtypes = [c_void_p, c_char_p]
94 - br.br_get_metadata_string.argtypes = [c_void_p, c_char_p]  
95 - br.br_get_metadata_string.restype = c_char_p 112 + br.br_get_metadata_string.argtypes = [c_char_p, c_int, c_void_p, c_char_p]
  113 + br.br_get_metadata_string.restype = c_int
  114 + func5 = br.br_get_metadata_string.__call__
  115 + br.br_get_metadata_string = _handle_string_func(func5)
96 br.br_enroll_template.argtypes = [c_void_p] 116 br.br_enroll_template.argtypes = [c_void_p]
97 br.br_enroll_template.restype = c_void_p 117 br.br_enroll_template.restype = c_void_p
98 br.br_enroll_template_list.argtypes = [c_void_p] 118 br.br_enroll_template_list.argtypes = [c_void_p]
scripts/downloadDatasets.sh
@@ -51,6 +51,8 @@ if [ ! -d ../data/CaltechPedestrians/vid ]; then @@ -51,6 +51,8 @@ if [ ! -d ../data/CaltechPedestrians/vid ]; then
51 tar -xf $fname 51 tar -xf $fname
52 done 52 done
53 rm *.tar 53 rm *.tar
  54 + ./writeCaltechPedestrianSigset.sh 0 5 train > ../data/CaltechPedestrians/train.xml
  55 + ./writeCaltechPedestrianSigset.sh 6 10 test > ../data/CaltechPedestrians/test.xml
54 mv set* ../data/CaltechPedestrians/vid 56 mv set* ../data/CaltechPedestrians/vid
55 if hash curl 2>/dev/null; then 57 if hash curl 2>/dev/null; then
56 curl -OL "$prefix/annotations.zip" 58 curl -OL "$prefix/annotations.zip"
scripts/writeCaltechPedestrianSigset.sh 0 → 100755
  1 +#!/bin/bash
  2 +
  3 +echo '<?xml version="1.0" encoding="UTF-8"?>'
  4 +echo '<biometric-signature-set>'
  5 +for ((set=$1; set <= $2; set++)); do
  6 + echo -e "\t<biometric-signature name=\"\">"
  7 + for vid in `printf "set%02d/*.seq" $set`; do
  8 + if [ $3 == "train" ]; then
  9 + vbb=`echo "vbb=\"annotations/$vid\"" | sed s/seq/vbb/`
  10 + fi
  11 + printf "\t\t<presentation file-name=\"vid/$vid\" $vbb />\n"
  12 + done
  13 + echo -e "\t</biometric-signature>"
  14 +done
  15 +echo '</biometric-signature-set>'
scripts/writeKTHSigset.sh 100644 → 100755
share/openbr/cmake/Findcvmatio.cmake
1 set(CVMATIO_DIR "${BR_THIRDPARTY_DIR}/cvmatio") 1 set(CVMATIO_DIR "${BR_THIRDPARTY_DIR}/cvmatio")
2 if(NOT EXISTS ${CVMATIO_DIR}) 2 if(NOT EXISTS ${CVMATIO_DIR})
3 # download source from github 3 # download source from github
4 - execute_process(COMMAND "git" "clone" "https://github.com/biometrics/cvmatio.git" WORKING_DIRECTORY ${BR_THIRDPARTY_DIR}) 4 + execute_process(COMMAND "git" "clone" "https://github.com/hbristow/cvmatio.git" WORKING_DIRECTORY ${BR_THIRDPARTY_DIR})
5 else() 5 else()
6 # update the source 6 # update the source
7 execute_process(COMMAND "git" "pull" WORKING_DIRECTORY ${CVMATIO_DIR}) 7 execute_process(COMMAND "git" "pull" WORKING_DIRECTORY ${CVMATIO_DIR})