Commit 66458eae97a35cd770399d24d620c7f893d49cf6
1 parent
66cb21a5
added partially complete br-search implementation
Showing
10 changed files
with
173 additions
and
17 deletions
app/CMakeLists.txt
app/br-download/br-download.cpp
| ... | ... | @@ -61,7 +61,7 @@ static bool processReply(QNetworkReply* reply) |
| 61 | 61 | return false; |
| 62 | 62 | |
| 63 | 63 | const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5); |
| 64 | - br_append_utemplate_contents(stdout, reinterpret_cast<const int8_t*>(hash.data()), reinterpret_cast<const int8_t*>(hash.data()), 3, data.size(), reinterpret_cast<const int8_t*>(data.data())); | |
| 64 | + br_append_utemplate_contents(stdout, reinterpret_cast<const int8_t*>(hash.data()), reinterpret_cast<const int8_t*>(hash.data()), 3, data.size(), reinterpret_cast<const unsigned char*>(data.data())); | |
| 65 | 65 | return true; |
| 66 | 66 | } |
| 67 | 67 | ... | ... |
app/br-enroll/br-enroll.cpp
| ... | ... | @@ -41,7 +41,7 @@ static void help() |
| 41 | 41 | |
| 42 | 42 | static QSharedPointer<Transform> algorithm; |
| 43 | 43 | |
| 44 | -static void enroll_utemplate(br_const_utemplate utemplate) | |
| 44 | +static void enroll_utemplate(br_const_utemplate utemplate, br_callback_context) | |
| 45 | 45 | { |
| 46 | 46 | if (utemplate->algorithmID != 3) |
| 47 | 47 | qFatal("Expected an encoded image."); |
| ... | ... | @@ -54,7 +54,7 @@ static void enroll_utemplate(br_const_utemplate utemplate) |
| 54 | 54 | const Mat &m = t.m(); |
| 55 | 55 | const uint32_t size = m.rows * m.cols * m.elemSize(); |
| 56 | 56 | const QByteArray templateID = QCryptographicHash::hash(QByteArray((const char*) m.data, size), QCryptographicHash::Md5); |
| 57 | - br_append_utemplate_contents(stdout, utemplate->imageID, (const int8_t*) templateID.data(), -1, size, (const int8_t*) m.data); | |
| 57 | + br_append_utemplate_contents(stdout, utemplate->imageID, (const int8_t*) templateID.data(), -1, size, m.data); | |
| 58 | 58 | } |
| 59 | 59 | } |
| 60 | 60 | |
| ... | ... | @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) |
| 66 | 66 | Context::initialize(argc, argv, "", false); |
| 67 | 67 | Globals->quiet = true; |
| 68 | 68 | algorithm = Transform::fromAlgorithm("FaceRecognition"); |
| 69 | - br_iterate_utemplates_file(stdin, enroll_utemplate); | |
| 69 | + br_iterate_utemplates_file(stdin, enroll_utemplate, NULL); | |
| 70 | 70 | Context::finalize(); |
| 71 | 71 | return EXIT_SUCCESS; |
| 72 | 72 | } | ... | ... |
app/br-search/CMakeLists.txt
0 → 100644
app/br-search/br-search.cpp
0 → 100644
| 1 | +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
| 2 | + * Copyright 2014 Noblis * | |
| 3 | + * * | |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); * | |
| 5 | + * you may not use this file except in compliance with the License. * | |
| 6 | + * You may obtain a copy of the License at * | |
| 7 | + * * | |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 * | |
| 9 | + * * | |
| 10 | + * Unless required by applicable law or agreed to in writing, software * | |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, * | |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * | |
| 13 | + * See the License for the specific language governing permissions and * | |
| 14 | + * limitations under the License. * | |
| 15 | + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
| 16 | + | |
| 17 | +#include <QtCore> | |
| 18 | +#include <cstdio> | |
| 19 | +#include <cstring> | |
| 20 | +#include <limits> | |
| 21 | +#include <openbr/openbr_plugin.h> | |
| 22 | +#include <openbr/universal_template.h> | |
| 23 | + | |
| 24 | +using namespace br; | |
| 25 | +using namespace cv; | |
| 26 | + | |
| 27 | +static void help() | |
| 28 | +{ | |
| 29 | + printf("br-search URL(s) [args]\n" | |
| 30 | + "=======================\n" | |
| 31 | + "* __stdin__ - Templates (feature vectors)\n" | |
| 32 | + "* __stdout__ - JSON\n" | |
| 33 | + "\n" | |
| 34 | + "_br-search_ does retrieval by comparing query templates to target gallery(s).\n" | |
| 35 | + "The search strategy is implementation defined.\n" | |
| 36 | + "\n" | |
| 37 | + "For every template read from _stdin_, search writes the top sorted matches as JSON objects to _stdout_ by comparing the query template against gallery URLs.\n" | |
| 38 | + "The JSON objects include `AlgorithmID`, `QueryImageID`, `QueryTemplateID`, `TargetImageID`, `TargetTemplateID`, `Score`, and any algorithm-specific metadata fields set during _enroll_. \n" | |
| 39 | + "\n" | |
| 40 | + "Optional Arguments\n" | |
| 41 | + "------------------\n" | |
| 42 | + "* -help - Print usage information.\n" | |
| 43 | + "* -limit <int> - Maximum number of returns (20 otherwise).\n"); | |
| 44 | +} | |
| 45 | + | |
| 46 | +static int limit = 20; | |
| 47 | +static float threshold = -std::numeric_limits<float>::max(); | |
| 48 | + | |
| 49 | +struct Result | |
| 50 | +{ | |
| 51 | + int8_t targetImageID[16], targetTemplateID[16], queryImageID[16], queryTemplateID[16]; | |
| 52 | + int32_t algorithmID; | |
| 53 | + float score; | |
| 54 | +}; | |
| 55 | + | |
| 56 | +struct TopTargets : QList< QPair<br_const_utemplate, float> > | |
| 57 | +{ | |
| 58 | + br_const_utemplate query; | |
| 59 | + | |
| 60 | + TopTargets(br_const_utemplate query) | |
| 61 | + : query(query) {} | |
| 62 | + | |
| 63 | + void tryAdd(br_const_utemplate target, float score) | |
| 64 | + { | |
| 65 | + if ((score < threshold) || ((size() == limit) && (score < last().second))) | |
| 66 | + return; | |
| 67 | + (void) target; | |
| 68 | + } | |
| 69 | + | |
| 70 | + void print() const | |
| 71 | + { | |
| 72 | + | |
| 73 | + } | |
| 74 | +}; | |
| 75 | + | |
| 76 | +struct FaceRecognitionResult : public Result | |
| 77 | +{ | |
| 78 | + int32_t x, y, width, height; | |
| 79 | + | |
| 80 | + FaceRecognitionResult() | |
| 81 | + { | |
| 82 | + algorithmID = -1; | |
| 83 | + } | |
| 84 | +}; | |
| 85 | + | |
| 86 | +struct MappedGallery | |
| 87 | +{ | |
| 88 | + QSharedPointer<QFile> file; | |
| 89 | + qint64 size; | |
| 90 | + uchar *data; | |
| 91 | + | |
| 92 | + MappedGallery(QString url) | |
| 93 | + { | |
| 94 | + if (url.startsWith("file://")) | |
| 95 | + url = url.mid(7); | |
| 96 | + file.reset(new QFile(url)); | |
| 97 | + file->open(QFile::ReadOnly); | |
| 98 | + size = file->size(); | |
| 99 | + data = file->map(0, size); | |
| 100 | + if (data == NULL) | |
| 101 | + qFatal("Unable to map gallery: %s", qPrintable(url)); | |
| 102 | + } | |
| 103 | +}; | |
| 104 | + | |
| 105 | +static QSharedPointer<Distance> distance; | |
| 106 | +static QList<MappedGallery> galleries; | |
| 107 | + | |
| 108 | +static void compare_utemplates(br_const_utemplate target, br_callback_context context) | |
| 109 | +{ | |
| 110 | + TopTargets *topTargets = (TopTargets*) context; | |
| 111 | + topTargets->tryAdd(target, distance->compare(target->data, topTargets->query->data, 768)); | |
| 112 | +} | |
| 113 | + | |
| 114 | +static void search_utemplate(br_const_utemplate query, br_callback_context) | |
| 115 | +{ | |
| 116 | + TopTargets *topTargets = new TopTargets(query); | |
| 117 | + foreach (const MappedGallery &gallery, galleries) | |
| 118 | + br_iterate_utemplates(reinterpret_cast<br_const_utemplate>(gallery.data), reinterpret_cast<br_const_utemplate>(gallery.data + gallery.size), compare_utemplates, topTargets); | |
| 119 | + topTargets->print(); | |
| 120 | + delete topTargets; | |
| 121 | +} | |
| 122 | + | |
| 123 | +int main(int argc, char *argv[]) | |
| 124 | +{ | |
| 125 | + QStringList urls; | |
| 126 | + for (int i=1; i<argc; i++) { | |
| 127 | + if (!strcmp(argv[i], "-help" )) { help(); exit(EXIT_SUCCESS); } | |
| 128 | + else if (!strcmp(argv[i], "-limit")) limit = atoi(argv[++i]); | |
| 129 | + else if (!strcmp(argv[i], "-threshold")) threshold = atof(argv[++i]); | |
| 130 | + else urls.append(argv[i]); | |
| 131 | + } | |
| 132 | + | |
| 133 | + Context::initialize(argc, argv, "", false); | |
| 134 | + | |
| 135 | + foreach (const QString &url, urls) | |
| 136 | + galleries.append(MappedGallery(url)); | |
| 137 | + | |
| 138 | + Globals->quiet = true; | |
| 139 | + distance = Distance::fromAlgorithm("FaceRecognition"); | |
| 140 | + br_iterate_utemplates_file(stdin, search_utemplate, NULL); | |
| 141 | + | |
| 142 | + Context::finalize(); | |
| 143 | + return EXIT_SUCCESS; | |
| 144 | +} | ... | ... |
openbr/openbr_plugin.cpp
| ... | ... | @@ -1405,7 +1405,12 @@ float Distance::compare(const Template &a, const Template &b) const |
| 1405 | 1405 | return similarity; |
| 1406 | 1406 | } |
| 1407 | 1407 | |
| 1408 | -float Distance::compare(const cv::Mat &, const cv::Mat &) const | |
| 1408 | +float Distance::compare(const cv::Mat &a, const cv::Mat &b) const | |
| 1409 | +{ | |
| 1410 | + return compare(a.data, b.data, a.rows * a.cols * a.elemSize()); | |
| 1411 | +} | |
| 1412 | + | |
| 1413 | +float Distance::compare(const uchar *, const uchar *, size_t) const | |
| 1409 | 1414 | { |
| 1410 | 1415 | qFatal("Logic error: %s did not implement a comparison function or was accessed at an unsupported level of abstraction.", metaObject()->className()); |
| 1411 | 1416 | return -std::numeric_limits<float>::max(); | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -1335,6 +1335,7 @@ public: |
| 1335 | 1335 | virtual QList<float> compare(const TemplateList &targets, const Template &query) const; /*!< \brief Compute the normalized distance between a template and a template list. */ |
| 1336 | 1336 | virtual float compare(const Template &a, const Template &b) const; /*!< \brief Compute the distance between two templates. */ |
| 1337 | 1337 | virtual float compare(const cv::Mat &a, const cv::Mat &b) const; /*!< \brief Compute the distance between two biometric signatures. */ |
| 1338 | + virtual float compare(const uchar *a, const uchar *b, size_t size) const; /*!< \brief Compute the distance between two buffers. */ | |
| 1338 | 1339 | |
| 1339 | 1340 | protected: |
| 1340 | 1341 | inline Distance *make(const QString &description) { return make(description, this); } /*!< \brief Make a subdistance. */ | ... | ... |
openbr/plugins/distance.cpp
| ... | ... | @@ -282,9 +282,9 @@ class ByteL1Distance : public Distance |
| 282 | 282 | { |
| 283 | 283 | Q_OBJECT |
| 284 | 284 | |
| 285 | - float compare(const Mat &a, const Mat &b) const | |
| 285 | + float compare(const unsigned char *a, const unsigned char *b, size_t size) const | |
| 286 | 286 | { |
| 287 | - return l1(a.data, b.data, a.total()); | |
| 287 | + return l1(a, b, size); | |
| 288 | 288 | } |
| 289 | 289 | }; |
| 290 | 290 | ... | ... |
openbr/universal_template.cpp
| ... | ... | @@ -27,7 +27,7 @@ void br_append_utemplate(FILE *file, br_const_utemplate utemplate) |
| 27 | 27 | br_append_utemplate_contents(file, utemplate->imageID, utemplate->templateID, utemplate->algorithmID, utemplate->size, utemplate->data); |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | -void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_t *templateID, int32_t algorithmID, uint32_t size, const int8_t *data) | |
| 30 | +void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_t *templateID, int32_t algorithmID, uint32_t size, const unsigned char *data) | |
| 31 | 31 | { |
| 32 | 32 | static QMutex lock; |
| 33 | 33 | QMutexLocker locker(&lock); |
| ... | ... | @@ -39,15 +39,15 @@ void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_ |
| 39 | 39 | fwrite(data, 1, size, file); |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | -void br_iterate_utemplates(br_const_utemplate begin, br_const_utemplate end, br_utemplate_callback callback) | |
| 42 | +void br_iterate_utemplates(br_const_utemplate begin, br_const_utemplate end, br_utemplate_callback callback, br_callback_context context) | |
| 43 | 43 | { |
| 44 | 44 | while (begin != end) { |
| 45 | - callback(begin); | |
| 45 | + callback(begin, context); | |
| 46 | 46 | begin = reinterpret_cast<br_const_utemplate>(reinterpret_cast<const char*>(begin) + sizeof(br_const_utemplate) + begin->size); |
| 47 | 47 | } |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | -void br_iterate_utemplates_file(FILE *file, br_utemplate_callback callback) | |
| 50 | +void br_iterate_utemplates_file(FILE *file, br_utemplate_callback callback, br_callback_context context) | |
| 51 | 51 | { |
| 52 | 52 | while (!feof(file)) { |
| 53 | 53 | br_utemplate t = (br_utemplate) malloc(sizeof(br_universal_template)); |
| ... | ... | @@ -56,7 +56,7 @@ void br_iterate_utemplates_file(FILE *file, br_utemplate_callback callback) |
| 56 | 56 | t = (br_utemplate) realloc(t, sizeof(br_universal_template) + t->size); |
| 57 | 57 | if (fread(t+1, 1, t->size, file) != t->size) |
| 58 | 58 | qFatal("Unexepected EOF when reading universal template data."); |
| 59 | - callback(t); | |
| 59 | + callback(t, context); | |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | free(t); | ... | ... |
openbr/universal_template.h
| ... | ... | @@ -34,7 +34,7 @@ struct br_universal_template |
| 34 | 34 | int8_t templateID[16]; /*!< MD5 hash of _data_. */ |
| 35 | 35 | int32_t algorithmID; /*!< type of _data_. */ |
| 36 | 36 | uint32_t size; /*!< length of _data_. */ |
| 37 | - int8_t data[]; /*!< _size_-byte buffer. */ | |
| 37 | + unsigned char data[]; /*!< _size_-byte buffer. */ | |
| 38 | 38 | }; |
| 39 | 39 | |
| 40 | 40 | typedef struct br_universal_template *br_utemplate; |
| ... | ... | @@ -62,25 +62,26 @@ BR_EXPORT void br_append_utemplate(FILE *file, br_const_utemplate utemplate); |
| 62 | 62 | * \brief Serialize a br_universal_template to a file. |
| 63 | 63 | * \see br_append_utemplate |
| 64 | 64 | */ |
| 65 | -BR_EXPORT void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_t *templateID, int32_t algorithmID, uint32_t size, const int8_t *data); | |
| 65 | +BR_EXPORT void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_t *templateID, int32_t algorithmID, uint32_t size, const unsigned char *data); | |
| 66 | 66 | |
| 67 | 67 | /*! |
| 68 | 68 | * \brief br_universal_template iterator callback. |
| 69 | 69 | * \see br_iterate_utemplates |
| 70 | 70 | */ |
| 71 | -typedef void (*br_utemplate_callback)(br_const_utemplate); | |
| 71 | +typedef void *br_callback_context; | |
| 72 | +typedef void (*br_utemplate_callback)(br_const_utemplate, br_callback_context); | |
| 72 | 73 | |
| 73 | 74 | /*! |
| 74 | 75 | * \brief Iterate over an inplace array of br_universal_template. |
| 75 | 76 | * \see br_iterate_utemplates_file |
| 76 | 77 | */ |
| 77 | -BR_EXPORT void br_iterate_utemplates(br_const_utemplate begin, br_const_utemplate end, br_utemplate_callback callback); | |
| 78 | +BR_EXPORT void br_iterate_utemplates(br_const_utemplate begin, br_const_utemplate end, br_utemplate_callback callback, br_callback_context context); | |
| 78 | 79 | |
| 79 | 80 | /*! |
| 80 | 81 | * \brief Iterate over br_universal_template in a file. |
| 81 | 82 | * \see br_iterate_utemplates |
| 82 | 83 | */ |
| 83 | -BR_EXPORT void br_iterate_utemplates_file(FILE *file, br_utemplate_callback callback); | |
| 84 | +BR_EXPORT void br_iterate_utemplates_file(FILE *file, br_utemplate_callback callback, br_callback_context context); | |
| 84 | 85 | |
| 85 | 86 | #ifdef __cplusplus |
| 86 | 87 | } | ... | ... |