diff --git a/app/br-download/br-download.cpp b/app/br-download/br-download.cpp index a15237e..0028da8 100644 --- a/app/br-download/br-download.cpp +++ b/app/br-download/br-download.cpp @@ -61,7 +61,7 @@ static bool processReply(QNetworkReply* reply) return false; const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5); - br_append_utemplate_contents(stdout, reinterpret_cast(hash.data()), reinterpret_cast(hash.data()), 3, data.size(), reinterpret_cast(data.data())); + br_append_utemplate_contents(stdout, reinterpret_cast(hash.data()), reinterpret_cast(hash.data()), 3, data.size(), reinterpret_cast(data.data())); return true; } diff --git a/app/br-enroll/br-enroll.cpp b/app/br-enroll/br-enroll.cpp index cb7ee65..3b10e44 100644 --- a/app/br-enroll/br-enroll.cpp +++ b/app/br-enroll/br-enroll.cpp @@ -54,7 +54,7 @@ static void enroll_utemplate(br_const_utemplate utemplate, br_callback_context) const Mat &m = t.m(); const uint32_t size = m.rows * m.cols * m.elemSize(); const QByteArray templateID = QCryptographicHash::hash(QByteArray((const char*) m.data, size), QCryptographicHash::Md5); - br_append_utemplate_contents(stdout, utemplate->imageID, (const int8_t*) templateID.data(), -1, size, m.data); + br_append_utemplate_contents(stdout, utemplate->imageID, (const unsigned char*) templateID.data(), -1, size, m.data); } } diff --git a/app/br-search/br-search.cpp b/app/br-search/br-search.cpp index 39a0888..b9b56e9 100644 --- a/app/br-search/br-search.cpp +++ b/app/br-search/br-search.cpp @@ -17,12 +17,17 @@ #include #include #include +#include +#include #include +#include +#include #include #include using namespace br; using namespace cv; +using namespace std; static void help() { @@ -34,52 +39,94 @@ static void help() "_br-search_ does retrieval by comparing query templates to target gallery(s).\n" "The search strategy is implementation defined.\n" "\n" - "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" - "The JSON objects include `AlgorithmID`, `QueryImageID`, `QueryTemplateID`, `TargetImageID`, `TargetTemplateID`, `Score`, and any algorithm-specific metadata fields set during _enroll_. \n" + "For every template read from _stdin_, search writes the top sorted matches a newline-terminated JSON object to _stdout_.\n" + "The JSON object will include at least `AlgorithmID`, (query) `ImageID`, (query) `TemplateID`, `Targets` and any algorithm-specific metadata fields set during _enroll_.\n" "\n" "Optional Arguments\n" "------------------\n" - "* -help - Print usage information.\n" - "* -limit - Maximum number of returns (20 otherwise).\n"); + "* -help - Print usage information.\n" + "* -limit - Maximum number of returns (20 otherwise).\n" + "* -threshold - Minimum similarity score (none otherwise)."); } -static int limit = 20; -static float threshold = -std::numeric_limits::max(); +static size_t limit = 20; +static float threshold = -numeric_limits::max(); -struct Result -{ - int8_t targetImageID[16], targetTemplateID[16], queryImageID[16], queryTemplateID[16]; - int32_t algorithmID; - float score; -}; - -struct TopTargets : QList< QPair > +struct SearchResults { + typedef pair Target; + vector topTargets; br_const_utemplate query; - TopTargets(br_const_utemplate query) + SearchResults(br_const_utemplate query) : query(query) {} - void tryAdd(br_const_utemplate target, float score) + virtual ~SearchResults() {} + + void consider(br_const_utemplate target) { - if ((score < threshold) || ((size() == limit) && (score < last().second))) + const float score = compare(target, query); + if ((score < threshold) || ((topTargets.size() == limit) && (score < topTargets.front().first))) return; - (void) target; + + topTargets.push_back(Target(score, target)); + make_heap(topTargets.begin(), topTargets.end()); + + if (topTargets.size() == limit + 1) + pop_heap(topTargets.begin(), topTargets.end()); } - void print() const + static void writeMD5asHex(const unsigned char *md5) { + cout << hex << setfill('0'); + for (int i=0; i<16; i++) + cout << setw(2) << md5[i]; + cout << dec; + } + void print() + { + sort_heap(topTargets.begin(), topTargets.end()); + + cout << "{ \"AlgorithmID\"=" << query->algorithmID; + cout << ", \"QueryImageID\"="; + writeMD5asHex(query->imageID); + cout << ", \"QueryTemplateID\"="; + writeMD5asHex(query->templateID); + printMetadata(query); + cout << ", \"Targets\"=[ "; + for (int i=topTargets.size()-1; i>=0; i--) { + Target &target = topTargets[i]; + cout << "{ \"ImageID\"="; + writeMD5asHex(target.second->imageID); + cout << ", \"TemplateID\"="; + writeMD5asHex(target.second->templateID); + cout << ", \"Score\"=" << target.first; + printMetadata(target.second); + cout << " }"; + if (i > 0) + cout << ", "; + } + cout << "]}\n" << flush; } + + virtual float compare(br_const_utemplate target, br_const_utemplate query) const = 0; + virtual void printMetadata(br_const_utemplate) const { return; } }; -struct FaceRecognitionResult : public Result +struct FaceRecognition : public SearchResults { - int32_t x, y, width, height; + QSharedPointer algorithm; + + FaceRecognition(br_const_utemplate query) + : SearchResults(query) + { + algorithm = Distance::fromAlgorithm("FaceRecognition"); + } - FaceRecognitionResult() + float compare(br_const_utemplate target, br_const_utemplate query) const { - algorithmID = -1; + return algorithm->compare(target->data, query->data, 768); } }; @@ -102,22 +149,21 @@ struct MappedGallery } }; -static QSharedPointer distance; static QList galleries; static void compare_utemplates(br_const_utemplate target, br_callback_context context) { - TopTargets *topTargets = (TopTargets*) context; - topTargets->tryAdd(target, distance->compare(target->data, topTargets->query->data, 768)); + SearchResults *searchResults = (SearchResults*) context; + searchResults->consider(target); } static void search_utemplate(br_const_utemplate query, br_callback_context) { - TopTargets *topTargets = new TopTargets(query); + SearchResults *searchResults = new FaceRecognition(query); foreach (const MappedGallery &gallery, galleries) - br_iterate_utemplates(reinterpret_cast(gallery.data), reinterpret_cast(gallery.data + gallery.size), compare_utemplates, topTargets); - topTargets->print(); - delete topTargets; + br_iterate_utemplates(reinterpret_cast(gallery.data), reinterpret_cast(gallery.data + gallery.size), compare_utemplates, searchResults); + searchResults->print(); + delete searchResults; } int main(int argc, char *argv[]) @@ -136,7 +182,6 @@ int main(int argc, char *argv[]) galleries.append(MappedGallery(url)); Globals->quiet = true; - distance = Distance::fromAlgorithm("FaceRecognition"); br_iterate_utemplates_file(stdin, search_utemplate, NULL); Context::finalize(); diff --git a/openbr/universal_template.cpp b/openbr/universal_template.cpp index 67fbfda..9b45e53 100644 --- a/openbr/universal_template.cpp +++ b/openbr/universal_template.cpp @@ -27,7 +27,7 @@ void br_append_utemplate(FILE *file, br_const_utemplate utemplate) br_append_utemplate_contents(file, utemplate->imageID, utemplate->templateID, utemplate->algorithmID, utemplate->size, utemplate->data); } -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) +void br_append_utemplate_contents(FILE *file, const unsigned char *imageID, const unsigned char *templateID, int32_t algorithmID, uint32_t size, const unsigned char *data) { static QMutex lock; QMutexLocker locker(&lock); diff --git a/openbr/universal_template.h b/openbr/universal_template.h index a71f711..748c795 100644 --- a/openbr/universal_template.h +++ b/openbr/universal_template.h @@ -30,8 +30,8 @@ extern "C" { */ struct br_universal_template { - int8_t imageID[16]; /*!< MD5 hash of the undecoded origin file. */ - int8_t templateID[16]; /*!< MD5 hash of _data_. */ + unsigned char imageID[16]; /*!< MD5 hash of the undecoded origin file. */ + unsigned char templateID[16]; /*!< MD5 hash of _data_. */ int32_t algorithmID; /*!< type of _data_. */ uint32_t size; /*!< length of _data_. */ unsigned char data[]; /*!< _size_-byte buffer. */ @@ -62,7 +62,7 @@ BR_EXPORT void br_append_utemplate(FILE *file, br_const_utemplate utemplate); * \brief Serialize a br_universal_template to a file. * \see br_append_utemplate */ -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); +BR_EXPORT void br_append_utemplate_contents(FILE *file, const unsigned char *imageID, const unsigned char *templateID, int32_t algorithmID, uint32_t size, const unsigned char *data); /*! * \brief br_universal_template iterator callback.