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 41 data/INRIAPerson/sigset
42 42 data/KTH/sigset
43 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 14  
15 15  
16 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 5 add_subdirectory(examples)
6 6  
7 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 158 } else if (!strcmp(fun, "plotMetadata")) {
159 159 check(parc >= 2, "Incorrect parameter count for 'plotMetadata'.");
160 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 166 // Miscellaneous
164 167 else if (!strcmp(fun, "help")) {
165 168 check(parc == 0, "No parameters expected for 'help'.");
166 169 help();
  170 + } else if (!strcmp(fun, "gui")) {
  171 + // Do nothing because we checked for this flag prior to initialization
167 172 } else if (!strcmp(fun, "objects")) {
168 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 179 } else if (!strcmp(fun, "about")) {
171 180 check(parc == 0, "No parameters expected for 'about'.");
172 181 printf("%s\n", br_about());
... ... @@ -177,11 +186,10 @@ public:
177 186 check(parc == 1, "Incorrect parameter count for 'daemon'.");
178 187 daemon = true;
179 188 daemon_pipe = parv[0];
180   - } else if (!strcmp(fun,"slave")) {
  189 + } else if (!strcmp(fun, "slave")) {
181 190 check(parc == 1, "Incorrect parameter count for 'slave'");
182 191 br_slave_process(parv[0]);
183   - }
184   - else if (!strcmp(fun, "exit")) {
  192 + } else if (!strcmp(fun, "exit")) {
185 193 check(parc == 0, "No parameters expected for 'exit'.");
186 194 daemon = false;
187 195 } else if (!strcmp(fun, "getHeader")) {
... ... @@ -245,6 +253,7 @@ private:
245 253 "\n"
246 254 "==== Miscellaneous ====\n"
247 255 "-help\n"
  256 + "-gui\n"
248 257 "-objects [abstraction [implementation]]\n"
249 258 "-about\n"
250 259 "-version\n"
... ... @@ -255,7 +264,7 @@ private:
255 264  
256 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 269 FakeMain *fakeMain = new FakeMain(argc, argv);
261 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 34 set(JANUS_BUILD_DOCS ${BR_BUILD_DOCUMENTATION} CACHE BOOL "Build Janus HTML Doxygen documentation")
35 35 mark_as_advanced(JANUS_BUILD_PP5_WRAPPER)
36 36 mark_as_advanced(JANUS_BUILD_DOCS)
37   -set(JANUS_TEST_IMPLEMENTATION openbr)
  37 +set(JANUS_IMPLEMENTATION openbr)
38 38 add_subdirectory(janus)
39 39  
40 40 # Install
... ...
openbr/core/bee.cpp
... ... @@ -172,6 +172,8 @@ Mat BEE::readMat(const br::File &amp;matrix, QString *targetSigset, QString *querySi
172 172 qint64 read = file.read((char*)m.data, bytesExpected);
173 173 if (read != bytesExpected)
174 174 qFatal("Invalid matrix size.");
  175 + if (!file.atEnd())
  176 + qFatal("Expected matrix end of file.");
175 177 file.close();
176 178  
177 179 Mat result;
... ... @@ -303,11 +305,14 @@ cv::Mat BEE::makeMask(const br::FileList &amp;targets, const br::FileList &amp;queries,
303 305 QList<int> targetPartitions = targets.crossValidationPartitions();
304 306 QList<int> queryPartitions = queries.crossValidationPartitions();
305 307  
  308 + QList<bool> targetsOnly = File::get<bool>(queries, "targetOnly", false);
  309 +
306 310 Mat mask(queries.size(), targets.size(), CV_8UC1);
307 311 for (int i=0; i<queries.size(); i++) {
308 312 const QString &fileA = queries[i];
309 313 const QString labelA = queryLabels[i];
310 314 const int partitionA = queryPartitions[i];
  315 + const bool targetOnly = targetsOnly[i];
311 316  
312 317 for (int j=0; j<targets.size(); j++) {
313 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 321  
317 322 Mask_t val;
318 323 if (fileA == fileB) val = DontCare;
  324 + else if (targetOnly) val = DontCare;
319 325 else if (labelA == "-1") val = DontCare;
320 326 else if (labelB == "-1") val = DontCare;
321 327 else if (partitionA != partition) val = DontCare;
... ...
openbr/core/cluster.cpp
... ... @@ -21,6 +21,7 @@
21 21 #include <QSet>
22 22 #include <limits>
23 23 #include <openbr/openbr_plugin.h>
  24 +#include <assert.h>
24 25  
25 26 #include "openbr/core/bee.h"
26 27 #include "openbr/core/cluster.h"
... ...
openbr/core/core.cpp
... ... @@ -65,6 +65,9 @@ struct AlgorithmCore
65 65 downcast->train(data);
66 66  
67 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 71 qDebug("Projecting Enrollment");
69 72 downcast->projectUpdate(data,data);
70 73  
... ... @@ -251,6 +254,56 @@ struct AlgorithmCore
251 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 307 void compare(File targetGallery, File queryGallery, File output)
255 308 {
256 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 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 560 QSharedPointer<br::Transform> br::Transform::fromAlgorithm(const QString &algorithm, bool preprocess)
500 561 {
501 562 if (!preprocess)
... ...
openbr/core/opencvutils.cpp
... ... @@ -15,7 +15,9 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/highgui/highgui.hpp>
  18 +#include <opencv2/highgui/highgui_c.h>
18 19 #include <opencv2/imgproc/imgproc.hpp>
  20 +#include <opencv2/imgproc/imgproc_c.h>
19 21 #include <openbr/openbr_plugin.h>
20 22  
21 23 #include "opencvutils.h"
... ...
openbr/core/opencvutils.h
... ... @@ -22,6 +22,7 @@
22 22 #include <QString>
23 23 #include <QStringList>
24 24 #include <opencv2/core/core.hpp>
  25 +#include <assert.h>
25 26  
26 27 namespace OpenCVUtils
27 28 {
... ...
openbr/gui/algorithm.cpp
1 1 #include <QStringList>
2 2 #include <openbr/openbr.h>
  3 +#include <openbr/openbr_plugin.h>
3 4  
4 5 #include "algorithm.h"
5 6  
... ... @@ -18,7 +19,7 @@ bool br::Algorithm::addAlgorithm(const QString &amp;algorithm, const QString &amp;displa
18 19 {
19 20 static QStringList availableAlgorithms;
20 21 if (availableAlgorithms.isEmpty())
21   - availableAlgorithms = QString(br_objects("Abbreviation", ".*", false)).split("\n");
  22 + availableAlgorithms = br::Context::objects("Abbreviation", ".*", false);
22 23  
23 24 if (!availableAlgorithms.contains(algorithm))
24 25 return false;
... ...
openbr/gui/gallerytoolbar.cpp
... ... @@ -84,7 +84,7 @@ void br::GalleryToolBar::_enroll(const br::File &amp;input)
84 84 galleryLock.lock();
85 85 this->input = input;
86 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 88 files = br::Enroll(input.flat(), gallery.flat());
89 89 galleryLock.unlock();
90 90 }
... ... @@ -148,7 +148,7 @@ void br::GalleryToolBar::home()
148 148  
149 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 152 br_set_property("CENTER_TRAIN_B", qPrintable(file));
153 153 br::File trainingFile = input;
154 154 br_train(qPrintable(trainingFile.flat()), "[algorithm=MedianFace]");
... ...
openbr/gui/progress.cpp
1 1 #include <openbr/openbr.h>
  2 +#include <openbr/openbr_plugin.h>
2 3  
3 4 #include "progress.h"
4 5  
... ... @@ -29,7 +30,7 @@ void br::Progress::checkProgress()
29 30 const bool visible = progress >= 0 && progress < 100;
30 31  
31 32 if (visible) {
32   - showMessage(br_most_recent_message());
  33 + showMessage(Globals->mostRecentMessage);
33 34 pbProgress.setValue(progress);
34 35 if (progress > 100) pbProgress.setMaximum(0);
35 36 else pbProgress.setMaximum(100);
... ...
openbr/gui/tail.cpp
1 1 #include <QtConcurrentRun>
2 2  
3 3 #include "tail.h"
  4 +#include <assert.h>
4 5  
5 6 using namespace br;
6 7  
... ...
openbr/gui/templateviewer.cpp
... ... @@ -72,7 +72,7 @@ void TemplateViewer::refreshImage()
72 72 if (file.isNull() || (format == "Photo")) {
73 73 setImage(file, true);
74 74 } else {
75   - const QString path = QString(br_scratch_path()) + "/thumbnails";
  75 + const QString path = QString(br::Globals->scratchPath()) + "/thumbnails";
76 76 const QString hash = file.hash()+format;
77 77 const QString processedFile = path+"/"+file.baseName()+hash+".png";
78 78 if (!QFileInfo(processedFile).exists()) {
... ...
openbr/gui/transformeditor.cpp
... ... @@ -24,7 +24,7 @@ using namespace br;
24 24 br::TransformEditor::TransformEditor(Transform *transform, QWidget *parent)
25 25 : QWidget(parent)
26 26 {
27   - name.addItems(QString(br_objects("Transform", ".*", false)).split('\n'));
  27 + name.addItems(br::Context::objects("Transform", ".*", false));
28 28 layout.addWidget(&name);
29 29 setLayout(&layout);
30 30  
... ...
openbr/gui/utility.cpp
1 1 #include <limits>
2 2 #include <vector>
  3 +#include <assert.h>
3 4 #include <opencv2/imgproc/imgproc.hpp>
  5 +#include <opencv2/imgproc/imgproc_c.h>
4 6 #include "utility.h"
5 7  
6 8 using namespace cv;
... ...
1   -Subproject commit 0eaa00f19256fee2f24033ff68b526ea6320624d
  1 +Subproject commit f8d9c869c821cc032d092ab1274da8f3cabf5eb7
... ...
openbr/janus.cpp
... ... @@ -3,140 +3,138 @@
3 3 #endif
4 4  
5 5 #include "janus.h"
  6 +#include "janus_io.h"
6 7 #include "openbr_plugin.h"
7 8  
8   -// Use the provided default implementation of some functions
9   -#include "janus/src/janus.cpp"
10   -
11 9 using namespace br;
12 10  
13 11 static QSharedPointer<Transform> transform;
14 12 static QSharedPointer<Distance> distance;
15 13  
  14 +size_t janus_max_template_size()
  15 +{
  16 + return 33554432; // 32 MB
  17 +}
  18 +
16 19 janus_error janus_initialize(const char *sdk_path, const char *model_file)
17 20 {
18 21 int argc = 1;
19 22 const char *argv[1] = { "janus" };
20 23 Context::initialize(argc, (char**)argv, sdk_path);
21 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 35 return JANUS_SUCCESS;
26 36 }
27 37  
28 38 janus_error janus_finalize()
29 39 {
  40 + transform.reset();
  41 + distance.reset();
30 42 Context::finalize();
31 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 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 57 Template t;
48 58 t.append(cv::Mat(image.height,
49 59 image.width,
50   - image.color_space == JANUS_GRAY8 ? CV_8UC1 : CV_8UC1,
  60 + image.color_space == JANUS_GRAY8 ? CV_8UC1 : CV_8UC3,
51 61 image.data));
52 62 for (size_t i=0; i<attributes.size; i++)
53 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 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 73 Template u;
64 74 transform->project(t, u);
65   - incomplete_template->data.append(u);
  75 + template_->append(u);
66 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 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 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 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 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 139 return JANUS_SUCCESS;
142 140 }
... ...
openbr/openbr.cpp
... ... @@ -24,12 +24,31 @@
24 24 #include "core/qtutils.h"
25 25 #include "plugins/openbr_internal.h"
26 26 #include <opencv2/highgui/highgui.hpp>
  27 +#include <opencv2/highgui/highgui_c.h>
27 28  
28 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 46 const char *br_about()
31 47 {
  48 + static QMutex aboutLock;
  49 + QMutexLocker lock(&aboutLock);
32 50 static QByteArray about = Context::about().toLocal8Bit();
  51 +
33 52 return about.data();
34 53 }
35 54  
... ... @@ -121,9 +140,9 @@ void br_fuse(int num_input_simmats, const char *input_simmats[],
121 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 148 void br_initialize_default()
... ... @@ -149,53 +168,14 @@ void br_make_pairwise_mask(const char *target_input, const char *query_input, co
149 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 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 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 238 const char *br_sdk_path()
261 239 {
  240 + static QMutex sdkLock;
  241 + QMutexLocker lock(&sdkLock);
262 242 static QByteArray sdkPath = QDir(Globals->sdkPath).absolutePath().toLocal8Bit();
263 243 return sdkPath.data();
264 244 }
... ... @@ -302,6 +282,8 @@ void br_train_n(int num_inputs, const char *inputs[], const char *model)
302 282  
303 283 const char *br_version()
304 284 {
  285 + static QMutex versionLock;
  286 + QMutexLocker lock(&versionLock);
305 287 static QByteArray version = Context::version().toLocal8Bit();
306 288 return version.data();
307 289 }
... ... @@ -379,10 +361,9 @@ bool br_img_is_empty(br_template tmpl)
379 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 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 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 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 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 382 br_template_list br_enroll_template(br_template tmpl)
... ... @@ -470,3 +447,8 @@ void br_close_gallery(br_gallery gallery)
470 447 Gallery *gal = reinterpret_cast<Gallery*>(gallery);
471 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 41 * \section managed_return_value Managed Return Value
42 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 48 * \section examples Examples
45 49 * - \ref c_face_recognition_evaluation
46 50 *
... ... @@ -56,7 +60,6 @@ extern &quot;C&quot; {
56 60  
57 61 /*!
58 62 * \brief Wraps br::Context::about()
59   - * \note \ref managed_return_value
60 63 * \see br_version
61 64 */
62 65 BR_EXPORT const char *br_about();
... ... @@ -67,6 +70,17 @@ BR_EXPORT const char *br_about();
67 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 84 * \brief Clusters one or more similarity matrices into a list of subjects.
71 85 *
72 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 227 * \brief Wraps br::Context::initialize()
214 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 232 * \brief Wraps br::Context::initialize() with default arguments.
219 233 * \see br_finalize
... ... @@ -245,10 +259,10 @@ BR_EXPORT void br_make_pairwise_mask(const char *target_input, const char *query
245 259  
246 260 /*!
247 261 * \brief Returns the most recent line sent to stderr.
248   - * \note \ref managed_return_value
  262 + * \note \ref input_string_buffer
249 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 268 * \brief Returns names and parameters for the requested objects.
... ... @@ -257,10 +271,10 @@ BR_EXPORT const char *br_most_recent_message();
257 271 * \param abstractions Regular expression of the abstractions to search.
258 272 * \param implementations Regular expression of the implementations to search.
259 273 * \param parameters Include parameters after object name.
260   - * \note \ref managed_return_value
  274 + * \note \ref input_string_buffer
261 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 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 379  
366 380 /*!
367 381 * \brief Wraps br::Context::scratchPath()
368   - * \note \ref managed_return_value
  382 + * \note \ref input_string_buffer
369 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 389 * \brief Returns the full path to the root of the SDK.
375   - * \note \ref managed_return_value
376 390 * \see br_initialize
377 391 */
378 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 439  
426 440 /*!
427 441 * \brief Wraps br::Context::version()
428   - * \note \ref managed_return_value
429 442 * \see br_about br_scratch_path
430 443 */
431 444 BR_EXPORT const char *br_version();
... ... @@ -497,16 +510,18 @@ BR_EXPORT int br_img_channels(br_template tmpl);
497 510 BR_EXPORT bool br_img_is_empty(br_template tmpl);
498 511 /*!
499 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 517 * \brief Set the filename for a br::Template.
504 518 */
505 519 BR_EXPORT void br_set_filename(br_template tmpl, const char *filename);
506 520 /*!
507 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 526 * \brief Enroll a br::Template from the C API! Returns a pointer to a br::TemplateList
512 527 * \param tmpl Pointer to a br::Template.
... ...
openbr/openbr_plugin.cpp
... ... @@ -203,6 +203,13 @@ QList&lt;QRectF&gt; File::namedRects() const
203 203 const QVariant &variant = m_metadata[key];
204 204 if (variant.canConvert<QRectF>())
205 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 214 return rects;
208 215 }
... ... @@ -392,7 +399,12 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
392 399  
393 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 408 QStringList labels;
397 409 for (int i=newTemplates.size()-1; i>=0; i--) {
398 410 newTemplates[i].file.set("Index", i+templates.size());
... ... @@ -414,7 +426,7 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
414 426 Template leaveOneImageOutTemplate = newTemplates[labelIndices[j]];
415 427 if (k!=leaveOneImageOutTemplate.file.get<int>("Partition")) {
416 428 leaveOneImageOutTemplate.file.set("Partition", k);
417   - leaveOneImageOutTemplate.file.set("testOnly", true);
  429 + leaveOneImageOutTemplate.file.set("targetOnly", true);
418 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 911 {
900 912 qInstallMessageHandler(messageHandler);
901 913  
  914 + QString sep;
902 915 #ifndef _WIN32
903 916 useGui = useGui && (getenv("DISPLAY") != NULL);
  917 + sep = ":";
  918 +#else
  919 + sep = ";";
904 920 #endif // not _WIN32
905 921  
906 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 960 // Search for SDK
945 961 if (sdkPath.isEmpty()) {
946 962 QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath();
  963 + checkPaths << QString(getenv("PATH")).split(sep, QString::SkipEmptyParts);
947 964  
948 965 bool foundSDK = false;
949 966 foreach (const QString &path, checkPaths) {
... ... @@ -999,6 +1016,47 @@ QString br::Context::scratchPath()
999 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 1060 void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
1003 1061 {
1004 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 41 #include <QVector>
42 42 #include <opencv2/core/core.hpp>
43 43 #include <openbr/openbr.h>
  44 +#include <assert.h>
44 45  
45 46 /*!
46 47 * \defgroup cpp_plugin_sdk C++ Plugin SDK
... ... @@ -829,6 +830,18 @@ public:
829 830 */
830 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 845 private:
833 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 1384 */
1372 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 1397 } // namespace br
... ...
openbr/plugins/algorithms.cpp
... ... @@ -50,6 +50,9 @@ class AlgorithmsInitializer : public Initializer
50 50 Globals->abbreviations.insert("DisplayVideo", "Stream(FPSLimit(30)+Show(false,[FrameNumber])+Discard)");
51 51 Globals->abbreviations.insert("PerFrameDetection", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard)");
52 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 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 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 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 81 // Transforms
79   - Globals->abbreviations.insert("FaceDetection", "(Open+Cvt(Gray)+Cascade(FrontalFace))");
  82 + Globals->abbreviations.insert("FaceDetection", "Open+Cvt(Gray)+Cascade(FrontalFace)");
80 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 84 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)");
82 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 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/objdetect/objdetect.hpp>
  18 +//#include <opencv2/objdetect/objdetect_c.h>
18 19 #include "openbr_internal.h"
19 20 #include "openbr/core/opencvutils.h"
20 21 #include "openbr/core/resource.h"
... ... @@ -86,11 +87,11 @@ class CascadeTransform : public UntrainableMetaTransform
86 87  
87 88 for (int i=0; i<t.size(); i++) {
88 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 96 if (!enrollAll && rects.empty())
96 97 rects.push_back(Rect(0, 0, m.cols, m.rows));
... ...
openbr/plugins/cvt.cpp
... ... @@ -14,6 +14,7 @@
14 14 * limitations under the License. *
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
  17 +#include <opencv2/imgproc/imgproc_c.h>
17 18 #include <opencv2/imgproc/imgproc.hpp>
18 19 #include "openbr_internal.h"
19 20 #include "openbr/core/opencvutils.h"
... ... @@ -44,7 +45,8 @@ public:
44 45 Luv = CV_BGR2Luv,
45 46 RGB = CV_BGR2RGB,
46 47 XYZ = CV_BGR2XYZ,
47   - YCrCb = CV_BGR2YCrCb };
  48 + YCrCb = CV_BGR2YCrCb,
  49 + Color = CV_GRAY2BGR };
48 50  
49 51 private:
50 52 BR_PROPERTY(ColorSpace, colorSpace, Gray)
... ... @@ -52,8 +54,8 @@ private:
52 54  
53 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 60 if (channel != -1) {
59 61 std::vector<Mat> mv;
... ...
openbr/plugins/distance.cpp
... ... @@ -18,10 +18,12 @@
18 18 #include <QtConcurrentRun>
19 19 #include <numeric>
20 20 #include <opencv2/imgproc/imgproc.hpp>
  21 +#include <opencv2/imgproc/imgproc_c.h>
21 22 #include "openbr_internal.h"
22 23  
23 24 #include "openbr/core/distance_sse.h"
24 25 #include "openbr/core/qtutils.h"
  26 +#include "openbr/core/opencvutils.h"
25 27  
26 28 using namespace cv;
27 29  
... ... @@ -61,6 +63,7 @@ private:
61 63 (a.m().type() != b.m().type()))
62 64 return -std::numeric_limits<float>::max();
63 65  
  66 +// TODO: this max value is never returned based on the switch / default
64 67 float result = std::numeric_limits<float>::max();
65 68 switch (metric) {
66 69 case Correlation:
... ... @@ -386,5 +389,103 @@ class OnlineDistance : public Distance
386 389  
387 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 490 } // namespace br
390 491 #include "distance.moc"
... ...
openbr/plugins/draw.cpp
... ... @@ -15,6 +15,7 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/highgui/highgui.hpp>
  18 +#include <opencv2/highgui/highgui_c.h>
18 19 #include <opencv2/imgproc/imgproc.hpp>
19 20 #include <vector>
20 21 #include "openbr_internal.h"
... ... @@ -345,6 +346,75 @@ class AdjacentOverlayTransform : public Transform
345 346  
346 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 418 // TODO: re-implement EditTransform using Qt
349 419 #if 0
350 420 /*!
... ...
openbr/plugins/eyes.cpp
... ... @@ -34,6 +34,7 @@
34 34 */
35 35  
36 36 #include <opencv2/imgproc/imgproc.hpp>
  37 +#include <opencv2/imgproc/imgproc_c.h>
37 38 #include "openbr_internal.h"
38 39 #include "openbr/core/opencvutils.h"
39 40  
... ...
openbr/plugins/format.cpp
... ... @@ -16,10 +16,12 @@
16 16  
17 17 #include <QDate>
18 18 #include <QSize>
  19 +#include <QChar>
19 20 #ifndef BR_EMBEDDED
20 21 #include <QtXml>
21 22 #endif // BR_EMBEDDED
22 23 #include <opencv2/highgui/highgui.hpp>
  24 +#include <opencv2/highgui/highgui_c.h>
23 25 #include "openbr_internal.h"
24 26  
25 27 #include "openbr/core/bee.h"
... ... @@ -33,7 +35,7 @@ namespace br
33 35  
34 36 /*!
35 37 * \ingroup formats
36   - * \brief Read all frames of a video using OpenCV
  38 + * \brief Read all frames of a video using OpenCV
37 39 * \author Charles Otto \cite caotto
38 40 */
39 41 class videoFormat : public Format
... ... @@ -45,11 +47,11 @@ public:
45 47 {
46 48 if (!file.exists() )
47 49 return Template();
48   -
  50 +
49 51 VideoCapture videoSource(file.name.toStdString());
50 52 videoSource.open(file.name.toStdString() );
51   -
52   -
  53 +
  54 +
53 55 Template frames;
54 56 if (!videoSource.isOpened()) {
55 57 qWarning("video file open failed");
... ... @@ -71,7 +73,7 @@ public:
71 73  
72 74 void write(const Template &t) const
73 75 {
74   - int fourcc = OpenCVUtils::getFourcc();
  76 + int fourcc = OpenCVUtils::getFourcc();
75 77 VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size());
76 78  
77 79 // Did we successfully open the output file?
... ... @@ -719,7 +721,7 @@ BR_REGISTER(Format, xmlFormat)
719 721 /*!
720 722 * \ingroup formats
721 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 726 * Example of the format:
725 727 * \code
... ... @@ -768,6 +770,191 @@ class scoresFormat : public Format
768 770  
769 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 958 } // namespace br
772 959  
773 960 #include "format.moc"
... ...
openbr/plugins/gallery.cpp
... ... @@ -985,7 +985,7 @@ class vbbGallery : public Gallery
985 985 void init()
986 986 {
987 987 MatlabIO matio;
988   - QString filename = file.name;
  988 + QString filename = (Globals->path.isEmpty() ? "" : Globals->path + "/") + file.name;
989 989 bool ok = matio.open(filename.toStdString(), "r");
990 990 if (!ok) qFatal("Couldn't open the vbb file");
991 991  
... ...
openbr/plugins/gui.cpp
1 1 #include <QApplication>
2 2 #include <QLabel>
3 3 #include <QElapsedTimer>
  4 +#include <QInputDialog>
4 5 #include <QWaitCondition>
5 6 #include <QMutex>
6 7 #include <QMouseEvent>
... ... @@ -12,6 +13,7 @@
12 13 #include <QLineEdit>
13 14  
14 15 #include <opencv2/imgproc/imgproc.hpp>
  16 +#include <opencv2/imgproc/imgproc_c.h>
15 17 #include "openbr_internal.h"
16 18 #include "openbr/gui/utility.h"
17 19  
... ... @@ -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 329 class PointMarkingWindow : public DisplayWindow
185 330 {
186 331 bool eventFilter(QObject *obj, QEvent *event)
... ... @@ -555,6 +700,91 @@ BR_REGISTER(Transform, ManualTransform)
555 700  
556 701 /*!
557 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 788 * \brief Elicits metadata for templates in a pretty GUI
559 789 * \author Scott Klum \cite sklum
560 790 */
... ...
openbr/plugins/hist.cpp
... ... @@ -94,7 +94,7 @@ class BinTransform : public UntrainableTransform
94 94 } else if (channels == 2) {
95 95 // If there are two channels, the first is channel is assumed to be a weight vector
96 96 // and the second channel contains the vectors we would like to bin.
97   - vector<Mat> mv;
  97 + std::vector<Mat> mv;
98 98 cv::split(src, mv);
99 99 weights = mv[0];
100 100 weights.convertTo(weights, CV_32F);
... ...
openbr/plugins/integral.cpp
1 1 #include <opencv2/imgproc/imgproc.hpp>
  2 +#include <opencv2/imgproc/imgproc_c.h>
2 3 #include <Eigen/Core>
3 4 #include "openbr_internal.h"
4 5  
... ... @@ -293,7 +294,7 @@ private:
293 294 Sobel(src, dx, CV_32F, 1, 0, CV_SCHARR);
294 295 Sobel(src, dy, CV_32F, 0, 1, CV_SCHARR);
295 296 cartToPolar(dx, dy, magnitude, angle, true);
296   - vector<Mat> mv;
  297 + std::vector<Mat> mv;
297 298 if ((channel == Magnitude) || (channel == MagnitudeAndAngle)) {
298 299 const float theoreticalMaxMagnitude = sqrt(2*pow(float(2*(3+10+3)*255), 2.f));
299 300 mv.push_back(magnitude / theoreticalMaxMagnitude);
... ...
openbr/plugins/landmarks.cpp
... ... @@ -290,16 +290,18 @@ class DrawDelaunayTransform : public UntrainableTransform
290 290  
291 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 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/imgproc/imgproc.hpp>
  18 +#include <opencv2/imgproc/imgproc_c.h>
18 19 #include <opencv2/highgui/highgui.hpp>
  20 +#include <opencv2/highgui/highgui_c.h>
19 21 #include <limits>
20 22 #include "openbr_internal.h"
21 23  
... ...
openbr/plugins/ltp.cpp
... ... @@ -15,6 +15,7 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/imgproc/imgproc.hpp>
  18 +#include <opencv2/imgproc/imgproc_c.h>
18 19 #include <limits>
19 20 #include "openbr_internal.h"
20 21  
... ...
openbr/plugins/mask.cpp
... ... @@ -15,6 +15,7 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <opencv2/imgproc/imgproc.hpp>
  18 +#include <opencv2/imgproc/imgproc_c.h>
18 19 #include "openbr_internal.h"
19 20  
20 21 using namespace cv;
... ... @@ -164,7 +165,7 @@ class LargestConvexAreaTransform : public UntrainableTransform
164 165 void project(const Template &src, Template &dst) const
165 166 {
166 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 169 double maxArea = 0;
169 170 foreach (const std::vector<Point> &contour, contours) {
170 171 std::vector<Point> hull;
... ...
openbr/plugins/misc.cpp
... ... @@ -537,7 +537,7 @@ class ProgressCounterTransform : public TimeVaryingTransform
537 537 {
538 538 (void) data;
539 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 543 void init()
... ...
openbr/plugins/motion.cpp
... ... @@ -23,6 +23,7 @@ class OpticalFlowTransform : public UntrainableMetaTransform
23 23 Q_PROPERTY(int poly_n READ get_poly_n WRITE set_poly_n RESET reset_poly_n STORED false)
24 24 Q_PROPERTY(double poly_sigma READ get_poly_sigma WRITE set_poly_sigma RESET reset_poly_sigma STORED false)
25 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 27 // these defaults are optimized for KTH
27 28 BR_PROPERTY(double, pyr_scale, 0.1)
28 29 BR_PROPERTY(int, levels, 1)
... ... @@ -31,22 +32,27 @@ class OpticalFlowTransform : public UntrainableMetaTransform
31 32 BR_PROPERTY(int, poly_n, 7)
32 33 BR_PROPERTY(double, poly_sigma, 1.1)
33 34 BR_PROPERTY(int, flags, 0)
  35 + BR_PROPERTY(bool, useMagnitude, true)
34 36  
35 37 void project(const Template &src, Template &dst) const
36 38 {
37 39 // get the two images put there by AggregateFrames
38 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 42 if (src[0].channels() != 1) OpenCVUtils::cvtGray(src[0], prevImg);
41 43 if (src[1].channels() != 1) OpenCVUtils::cvtGray(src[1], nextImg);
42 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 56 dst.file = src.file;
51 57 }
52 58 };
... ... @@ -62,7 +68,8 @@ class SubtractBackgroundTransform : public TimeVaryingTransform
62 68 {
63 69 Q_OBJECT
64 70  
65   - BackgroundSubtractorMOG2 mog;
  71 + // TODO: This is broken.
  72 + // BackgroundSubtractorMOG2 mog;
66 73  
67 74 public:
68 75 SubtractBackgroundTransform() : TimeVaryingTransform(false, false) {}
... ... @@ -72,7 +79,8 @@ private:
72 79 {
73 80 dst = src;
74 81 Mat mask;
75   - mog(src, mask);
  82 + // TODO: broken
  83 + // mog(src, mask);
76 84 erode(mask, mask, Mat());
77 85 dilate(mask, mask, Mat());
78 86 dst.file.set("Mask", QVariant::fromValue(mask));
... ... @@ -86,7 +94,8 @@ private:
86 94 void finalize(TemplateList &output)
87 95 {
88 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 60  
61 61 Q_PROPERTY(bool ByRow READ get_ByRow WRITE set_ByRow RESET reset_ByRow STORED false)
62 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 68 public:
65 69 /*!< */
66 70 enum NormType { Inf = NORM_INF,
67 71 L1 = NORM_L1,
68   - L2 = NORM_L2 };
  72 + L2 = NORM_L2,
  73 + Range = NORM_MINMAX };
69 74  
70 75 private:
71 76 BR_PROPERTY(NormType, normType, L2)
72 77  
73 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 81 else {
77 82 dst = src;
78 83 for (int i=0; i<dst.m().rows; i++) {
79 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 86 temp.copyTo(dst.m().row(i));
82 87 }
83 88 }
... ... @@ -132,7 +137,7 @@ private:
132 137 const QList<int> labels = data.indexProperty(inputVariable);
133 138 const int dims = m.cols;
134 139  
135   - vector<Mat> mv, av, bv;
  140 + std::vector<Mat> mv, av, bv;
136 141 split(m, mv);
137 142 for (size_t c = 0; c < mv.size(); c++) {
138 143 av.push_back(Mat(1, dims, CV_64FC1));
... ...
openbr/plugins/output.cpp
... ... @@ -268,7 +268,7 @@ class rrOutput : public MatrixOutput
268 268  
269 269 for (int i=0; i<queryFiles.size(); i++) {
270 270 QStringList files;
271   - if (simple) files.append(queryFiles[i]);
  271 + if (simple) files.append(queryFiles[i].fileName());
272 272  
273 273 typedef QPair<float,int> Pair;
274 274 foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) {
... ... @@ -276,7 +276,7 @@ class rrOutput : public MatrixOutput
276 276 if (pair.first < threshold) break;
277 277 File target = targetFiles[pair.second];
278 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 280 else files.append(target.flat());
281 281 }
282 282 }
... ... @@ -528,7 +528,7 @@ class tailOutput : public Output
528 528 } else {
529 529 // General case
530 530 for (int k=0; k<comparisons.size(); k++) {
531   - if (comparisons[k].value < value) {
  531 + if (comparisons[k].value <= value) {
532 532 comparisons.insert(k, Comparison(queryFiles[i], targetFiles[j], value));
533 533 break;
534 534 }
... ... @@ -539,6 +539,7 @@ class tailOutput : public Output
539 539 comparisons.removeLast();
540 540 while ((comparisons.size() > atLeast) && (comparisons.last().value < threshold))
541 541 comparisons.removeLast();
  542 +
542 543 lastValue = comparisons.last().value;
543 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 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 39 Globals->abbreviations.insert("RectFromStasmBrow","RectFromPoints([16,17,18,19,20,21,22,23,24,25,26,27],0.15,5)");
40 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 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 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 7 #include <QQueue>
8 8 #include <QtConcurrent>
9 9 #include <opencv/highgui.h>
  10 +#include <opencv2/highgui/highgui.hpp>
10 11 #include "openbr_internal.h"
11 12 #include "openbr/core/common.h"
12 13 #include "openbr/core/opencvutils.h"
... ... @@ -346,10 +347,11 @@ public:
346 347 }
347 348  
348 349 // first 4 bytes store 0xEDFE, next 24 store 'Norpix seq '
349   - char *firstFour = new char[4], *nextTwentyFour;
  350 + char firstFour[4];
350 351 seqFile.seekg(0, ios::beg);
351 352 seqFile.read(firstFour, 4);
352   - nextTwentyFour = readText(24);
  353 + char nextTwentyFour[24];
  354 + readText(24, nextTwentyFour);
353 355 if (firstFour[0] != (char)0xED || firstFour[1] != (char)0xFE || strncmp(nextTwentyFour, "Norpix seq", 10) != 0) {
354 356 qDebug("Invalid header in seq file");
355 357 return false;
... ... @@ -362,7 +364,8 @@ public:
362 364 qDebug("Invalid header size");
363 365 return false;
364 366 }
365   - char *desc = readText(512);
  367 + char desc[512];
  368 + readText(512, desc);
366 369 basis.file.set("Description", QString(desc));
367 370  
368 371 width = readInt();
... ... @@ -413,9 +416,9 @@ public:
413 416 // but there might be 16 extra bytes instead of 8...
414 417 if (i == 1) {
415 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 422 s += 8;
420 423 extra += 8;
421 424 }
... ... @@ -430,10 +433,12 @@ public:
430 433 }
431 434  
432 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 442 #endif
438 443  
439 444 return true;
... ... @@ -497,14 +502,13 @@ private:
497 502 // apparently the text in seq files is 16 bit characters (UTF-16?)
498 503 // since we don't really need the last byte, snad since it gets interpreted as
499 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 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 514 protected:
... ... @@ -736,7 +740,6 @@ protected:
736 740 frameSource = new VideoReader();
737 741 }
738 742 }
739   -
740 743 open_res = frameSource->open(curr);
741 744 if (!open_res)
742 745 {
... ... @@ -1420,6 +1423,7 @@ public:
1420 1423 {
1421 1424 // Delete all the stages
1422 1425 for (int i = 0; i < processingStages.size(); i++) {
  1426 +// TODO: Are we releasing memory which is already freed?
1423 1427 delete processingStages[i];
1424 1428 }
1425 1429 processingStages.clear();
... ...
openbr/plugins/validate.cpp
... ... @@ -65,9 +65,9 @@ class CrossValidateTransform : public MetaTransform
65 65 const QString label = partitionedData.at(j).file.get<QString>("Label");
66 66 QList<int> subjectIndices = partitionedData.find("Label",label);
67 67 QList<int> removed;
68   - // Remove test only data
  68 + // Remove target only data
69 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 71 removed.append(subjectIndices[k]);
72 72 subjectIndices.removeAt(k);
73 73 }
... ... @@ -108,6 +108,7 @@ class CrossValidateTransform : public MetaTransform
108 108 // If we want to duplicate templates but use the same training data
109 109 // for all partitions (i.e. transforms.size() == 1), we need to
110 110 // restrict the partition
  111 +
111 112 int partition = src.file.get<int>("Partition", 0);
112 113 partition = (partition >= transforms.size()) ? 0 : partition;
113 114 transforms[partition]->project(src, dst);
... ...
scripts/brpy/__init__.py
... ... @@ -12,6 +12,14 @@ def _var_string_args(n):
12 12 s.extend(_string_args(n))
13 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 23 def init_brpy(br_loc='/usr/local/lib'):
16 24 """Takes the ctypes lib object for br and initializes all function inputs and outputs"""
17 25 br_loc += '/libopenbr.%s'
... ... @@ -48,9 +56,14 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
48 56 br.br_is_classifier.restype = c_bool
49 57 br.br_make_mask.argtypes = _string_args(3)
50 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 67 br.br_plot.argtypes = plot_args
55 68 br.br_plot.restype = c_bool
56 69 br.br_plot_detection.argtypes = plot_args
... ... @@ -61,7 +74,10 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
61 74 br.br_plot_metadata.restype = c_bool
62 75 br.br_progress.restype = c_float
63 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 81 br.br_sdk_path.restype = c_char_p
66 82 br.br_get_header.argtypes = [c_char_p, POINTER(c_char_p), POINTER(c_char_p)]
67 83 br.br_set_header.argtypes = _string_args(3)
... ... @@ -88,11 +104,15 @@ def init_brpy(br_loc=&#39;/usr/local/lib&#39;):
88 104 br.br_img_channels.restype = c_int
89 105 br.br_img_is_empty.argtypes = [c_void_p]
90 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 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 116 br.br_enroll_template.argtypes = [c_void_p]
97 117 br.br_enroll_template.restype = c_void_p
98 118 br.br_enroll_template_list.argtypes = [c_void_p]
... ...
scripts/downloadDatasets.sh
... ... @@ -51,6 +51,8 @@ if [ ! -d ../data/CaltechPedestrians/vid ]; then
51 51 tar -xf $fname
52 52 done
53 53 rm *.tar
  54 + ./writeCaltechPedestrianSigset.sh 0 5 train > ../data/CaltechPedestrians/train.xml
  55 + ./writeCaltechPedestrianSigset.sh 6 10 test > ../data/CaltechPedestrians/test.xml
54 56 mv set* ../data/CaltechPedestrians/vid
55 57 if hash curl 2>/dev/null; then
56 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 1 set(CVMATIO_DIR "${BR_THIRDPARTY_DIR}/cvmatio")
2 2 if(NOT EXISTS ${CVMATIO_DIR})
3 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 5 else()
6 6 # update the source
7 7 execute_process(COMMAND "git" "pull" WORKING_DIRECTORY ${CVMATIO_DIR})
... ...