Commit a3d263f0f600b94f2a72b247b0f22529f1dfc701
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
51 changed files
with
1114 additions
and
235 deletions
.gitignore
README.md
app/CMakeLists.txt
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 "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 &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 &targets, const br::FileList &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 &targets, const br::FileList &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
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 &inputGalleries, const QString &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
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 &algorithm, const QString &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 &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
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
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 "C" { |
| 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 "C" { |
| 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<QRectF> 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 &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 &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 &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 &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 &fileType, const File &inputFile, const File & |
| 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
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
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='/usr/local/lib'): |
| 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='/usr/local/lib'): |
| 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='/usr/local/lib'): |
| 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}) | ... | ... |