Commit 66458eae97a35cd770399d24d620c7f893d49cf6
1 parent
66cb21a5
added partially complete br-search implementation
Showing
10 changed files
with
173 additions
and
17 deletions
app/CMakeLists.txt
| @@ -10,4 +10,5 @@ if(NOT ${BR_EMBEDDED}) | @@ -10,4 +10,5 @@ if(NOT ${BR_EMBEDDED}) | ||
| 10 | add_subdirectory(br-crawl) | 10 | add_subdirectory(br-crawl) |
| 11 | add_subdirectory(br-enroll) | 11 | add_subdirectory(br-enroll) |
| 12 | add_subdirectory(br-gui) | 12 | add_subdirectory(br-gui) |
| 13 | + add_subdirectory(br-search) | ||
| 13 | endif() | 14 | endif() |
app/br-download/br-download.cpp
| @@ -61,7 +61,7 @@ static bool processReply(QNetworkReply* reply) | @@ -61,7 +61,7 @@ static bool processReply(QNetworkReply* reply) | ||
| 61 | return false; | 61 | return false; |
| 62 | 62 | ||
| 63 | const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5); | 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 | return true; | 65 | return true; |
| 66 | } | 66 | } |
| 67 | 67 |
app/br-enroll/br-enroll.cpp
| @@ -41,7 +41,7 @@ static void help() | @@ -41,7 +41,7 @@ static void help() | ||
| 41 | 41 | ||
| 42 | static QSharedPointer<Transform> algorithm; | 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 | if (utemplate->algorithmID != 3) | 46 | if (utemplate->algorithmID != 3) |
| 47 | qFatal("Expected an encoded image."); | 47 | qFatal("Expected an encoded image."); |
| @@ -54,7 +54,7 @@ static void enroll_utemplate(br_const_utemplate utemplate) | @@ -54,7 +54,7 @@ static void enroll_utemplate(br_const_utemplate utemplate) | ||
| 54 | const Mat &m = t.m(); | 54 | const Mat &m = t.m(); |
| 55 | const uint32_t size = m.rows * m.cols * m.elemSize(); | 55 | const uint32_t size = m.rows * m.cols * m.elemSize(); |
| 56 | const QByteArray templateID = QCryptographicHash::hash(QByteArray((const char*) m.data, size), QCryptographicHash::Md5); | 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,7 +66,7 @@ int main(int argc, char *argv[]) | ||
| 66 | Context::initialize(argc, argv, "", false); | 66 | Context::initialize(argc, argv, "", false); |
| 67 | Globals->quiet = true; | 67 | Globals->quiet = true; |
| 68 | algorithm = Transform::fromAlgorithm("FaceRecognition"); | 68 | algorithm = Transform::fromAlgorithm("FaceRecognition"); |
| 69 | - br_iterate_utemplates_file(stdin, enroll_utemplate); | 69 | + br_iterate_utemplates_file(stdin, enroll_utemplate, NULL); |
| 70 | Context::finalize(); | 70 | Context::finalize(); |
| 71 | return EXIT_SUCCESS; | 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,7 +1405,12 @@ float Distance::compare(const Template &a, const Template &b) const | ||
| 1405 | return similarity; | 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 | qFatal("Logic error: %s did not implement a comparison function or was accessed at an unsupported level of abstraction.", metaObject()->className()); | 1415 | qFatal("Logic error: %s did not implement a comparison function or was accessed at an unsupported level of abstraction.", metaObject()->className()); |
| 1411 | return -std::numeric_limits<float>::max(); | 1416 | return -std::numeric_limits<float>::max(); |
openbr/openbr_plugin.h
| @@ -1335,6 +1335,7 @@ public: | @@ -1335,6 +1335,7 @@ public: | ||
| 1335 | virtual QList<float> compare(const TemplateList &targets, const Template &query) const; /*!< \brief Compute the normalized distance between a template and a template list. */ | 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 | virtual float compare(const Template &a, const Template &b) const; /*!< \brief Compute the distance between two templates. */ | 1336 | virtual float compare(const Template &a, const Template &b) const; /*!< \brief Compute the distance between two templates. */ |
| 1337 | virtual float compare(const cv::Mat &a, const cv::Mat &b) const; /*!< \brief Compute the distance between two biometric signatures. */ | 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 | protected: | 1340 | protected: |
| 1340 | inline Distance *make(const QString &description) { return make(description, this); } /*!< \brief Make a subdistance. */ | 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,9 +282,9 @@ class ByteL1Distance : public Distance | ||
| 282 | { | 282 | { |
| 283 | Q_OBJECT | 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,7 +27,7 @@ void br_append_utemplate(FILE *file, br_const_utemplate utemplate) | ||
| 27 | br_append_utemplate_contents(file, utemplate->imageID, utemplate->templateID, utemplate->algorithmID, utemplate->size, utemplate->data); | 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 | static QMutex lock; | 32 | static QMutex lock; |
| 33 | QMutexLocker locker(&lock); | 33 | QMutexLocker locker(&lock); |
| @@ -39,15 +39,15 @@ void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_ | @@ -39,15 +39,15 @@ void br_append_utemplate_contents(FILE *file, const int8_t *imageID, const int8_ | ||
| 39 | fwrite(data, 1, size, file); | 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 | while (begin != end) { | 44 | while (begin != end) { |
| 45 | - callback(begin); | 45 | + callback(begin, context); |
| 46 | begin = reinterpret_cast<br_const_utemplate>(reinterpret_cast<const char*>(begin) + sizeof(br_const_utemplate) + begin->size); | 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 | while (!feof(file)) { | 52 | while (!feof(file)) { |
| 53 | br_utemplate t = (br_utemplate) malloc(sizeof(br_universal_template)); | 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,7 +56,7 @@ void br_iterate_utemplates_file(FILE *file, br_utemplate_callback callback) | ||
| 56 | t = (br_utemplate) realloc(t, sizeof(br_universal_template) + t->size); | 56 | t = (br_utemplate) realloc(t, sizeof(br_universal_template) + t->size); |
| 57 | if (fread(t+1, 1, t->size, file) != t->size) | 57 | if (fread(t+1, 1, t->size, file) != t->size) |
| 58 | qFatal("Unexepected EOF when reading universal template data."); | 58 | qFatal("Unexepected EOF when reading universal template data."); |
| 59 | - callback(t); | 59 | + callback(t, context); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | free(t); | 62 | free(t); |
openbr/universal_template.h
| @@ -34,7 +34,7 @@ struct br_universal_template | @@ -34,7 +34,7 @@ struct br_universal_template | ||
| 34 | int8_t templateID[16]; /*!< MD5 hash of _data_. */ | 34 | int8_t templateID[16]; /*!< MD5 hash of _data_. */ |
| 35 | int32_t algorithmID; /*!< type of _data_. */ | 35 | int32_t algorithmID; /*!< type of _data_. */ |
| 36 | uint32_t size; /*!< length of _data_. */ | 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 | typedef struct br_universal_template *br_utemplate; | 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,25 +62,26 @@ BR_EXPORT void br_append_utemplate(FILE *file, br_const_utemplate utemplate); | ||
| 62 | * \brief Serialize a br_universal_template to a file. | 62 | * \brief Serialize a br_universal_template to a file. |
| 63 | * \see br_append_utemplate | 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 | * \brief br_universal_template iterator callback. | 68 | * \brief br_universal_template iterator callback. |
| 69 | * \see br_iterate_utemplates | 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 | * \brief Iterate over an inplace array of br_universal_template. | 75 | * \brief Iterate over an inplace array of br_universal_template. |
| 75 | * \see br_iterate_utemplates_file | 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 | * \brief Iterate over br_universal_template in a file. | 81 | * \brief Iterate over br_universal_template in a file. |
| 81 | * \see br_iterate_utemplates | 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 | #ifdef __cplusplus | 86 | #ifdef __cplusplus |
| 86 | } | 87 | } |