diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b1995a..039c678 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 2.8.9) # Global settings set(BR_SHARE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/share/openbr") +set(BR_SCRIPTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts") set(CMAKE_AUTOMOC ON) set(CPACK_PACKAGE_NAME "OpenBR") set(CPACK_PACKAGE_VENDOR "OpenBiometrics") @@ -117,7 +118,7 @@ if(BUILD_TESTING) endif() # Build the SDK -include_directories(.) +include_directories(BEFORE .) # Find the local headers first add_subdirectory(openbr) # Build applications @@ -138,6 +139,14 @@ install(FILES CHANGELOG.md LICENSE.txt README.md DESTINATION .) install(DIRECTORY share DESTINATION .) install(DIRECTORY ${BR_THIRDPARTY_SHARE} DESTINATION share) +# install brpy +option(BR_INSTALL_BRPY "Install brpy, the Python wrapper to the C API (requires Python)") +if(${BR_INSTALL_BRPY}) + find_package(PythonInterp REQUIRED) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import site, sys; sys.stdout.write(site.getsitepackages()[-1])" OUTPUT_VARIABLE PYTHON_SITE_DIR) + install(DIRECTORY ${BR_SCRIPTS_DIR}/brpy DESTINATION ${PYTHON_SITE_DIR}) +endif() + # Package set(CPACK_PACKAGE_EXECUTABLES "OpenBR" "OpenBR") set(CPACK_CREATE_DESKTOP_LINKS "OpenBR") diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index 13b4ae3..2d6e5c9 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -56,11 +56,6 @@ struct AlgorithmCore TemplateList data(TemplateList::fromGallery(input)); - // set the Train bool metadata, in case a Transform's project - // needs to know if it's called during train or enroll - for (int i=0; icurrentStep = 0; + Globals->totalSteps = data.length(); + // Trust me, this makes complete sense. // We're just going to make a pipe with a placeholder first transform - Globals->totalSteps = data.length(); QString pipeDesc = "Identity+GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(data.length())+")+Discard"; QScopedPointer basePipe(Transform::make(pipeDesc,NULL)); @@ -430,6 +428,13 @@ void br::Compare(const File &targetGallery, const File &queryGallery, const File AlgorithmManager::getAlgorithm(output.get("algorithm"))->compare(targetGallery, queryGallery, output); } +void br::CompareTemplateLists(const TemplateList &target, const TemplateList &query, Output *output) +{ + QString alg = output->file.get("algorithm"); + QSharedPointer dist = Distance::fromAlgorithm(alg); + dist->compare(target, query, output); +} + void br::PairwiseCompare(const File &targetGallery, const File &queryGallery, const File &output) { AlgorithmManager::getAlgorithm(output.get("algorithm"))->pairwiseCompare(targetGallery, queryGallery, output); @@ -456,7 +461,7 @@ void br::Convert(const File &fileType, const File &inputFile, const File &output if ((targetFiles.size() != m.cols || queryFiles.size() != m.rows) && (m.cols != 1 || targetFiles.size() != m.rows || queryFiles.size() != m.rows)) - qFatal("Similarity matrix and file size mismatch."); + qFatal("Similarity matrix (%d, %d) and header (%d, %d) size mismatch.", m.rows, m.cols, queryFiles.size(), targetFiles.size()); QSharedPointer o(Factory::make(outputFile)); o->initialize(targetFiles, queryFiles); diff --git a/openbr/core/fuse.cpp b/openbr/core/fuse.cpp index f2a6e53..146fa90 100644 --- a/openbr/core/fuse.cpp +++ b/openbr/core/fuse.cpp @@ -30,6 +30,9 @@ using namespace cv; static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) { + if (matrix.rows != mask.rows && matrix.cols != mask.cols) + qFatal("Similarity matrix (%d, %d) and mask (%d, %d) size mismatch.", matrix.rows, matrix.cols, mask.rows, mask.cols); + if (method == "None") return; QList vals; vals.reserve(matrix.rows*matrix.cols); diff --git a/openbr/gui/progress.cpp b/openbr/gui/progress.cpp index fbe2bf6..c162b5d 100644 --- a/openbr/gui/progress.cpp +++ b/openbr/gui/progress.cpp @@ -26,7 +26,7 @@ br::Progress::Progress(QWidget *parent) void br::Progress::checkProgress() { const int progress = 100 * br_progress(); - const bool visible = progress >= 0; + const bool visible = progress >= 0 && progress < 100; if (visible) { showMessage(br_most_recent_message()); diff --git a/openbr/gui/utility.cpp b/openbr/gui/utility.cpp index ff30e33..77d6d25 100644 --- a/openbr/gui/utility.cpp +++ b/openbr/gui/utility.cpp @@ -1,12 +1,11 @@ -#include #include #include #include +#include "utility.h" using namespace cv; -/**** STATIC ****/ -QImage toQImage(const Mat &mat) +QImage br::toQImage(const Mat &mat) { // Convert to 8U depth Mat mat8u; diff --git a/openbr/gui/utility.h b/openbr/gui/utility.h index f4c3d60..da5a147 100644 --- a/openbr/gui/utility.h +++ b/openbr/gui/utility.h @@ -3,7 +3,13 @@ #include #include +#include -QImage toQImage(const cv::Mat &mat); +namespace br +{ + +BR_EXPORT QImage toQImage(const cv::Mat &mat); + +} // namespace br #endif // BR_UTILITY_H diff --git a/openbr/janus b/openbr/janus index b79bdf7..0eaa00f 160000 --- a/openbr/janus +++ b/openbr/janus @@ -1 +1 @@ -Subproject commit b79bdf71d86b1d5967fa636c6727bebf898cf2ed +Subproject commit 0eaa00f19256fee2f24033ff68b526ea6320624d diff --git a/openbr/janus.cpp b/openbr/janus.cpp index b5f6e4f..3f6f75f 100644 --- a/openbr/janus.cpp +++ b/openbr/janus.cpp @@ -80,7 +80,7 @@ janus_error janus_finalize_template(janus_incomplete_template incomplete_templat templateBytes = currentTemplateBytes; if (templateBytes != currentTemplateBytes) return JANUS_UNKNOWN_ERROR; - if (*bytes + templateBytes > JANUS_MAX_TEMPLATE_SIZE) + if (*bytes + templateBytes > janus_max_template_size()) break; memcpy(pos, m.data, templateBytes); *bytes += templateBytes; @@ -94,21 +94,49 @@ janus_error janus_finalize_template(janus_incomplete_template incomplete_templat return JANUS_SUCCESS; } -janus_error janus_verify(const janus_template a, const janus_template b, float *similarity) +janus_error janus_verify(const janus_template a, const size_t a_bytes, const janus_template b, const size_t b_bytes, double *similarity) { - size_t a_bytes, a_templates, b_bytes, b_templates; - a_bytes = *(reinterpret_cast(a)+0); + (void) a_bytes; + (void) b_bytes; + + size_t a_template_bytes, a_templates, b_template_bytes, b_templates; + a_template_bytes = *(reinterpret_cast(a)+0); a_templates = *(reinterpret_cast(a)+1); - b_bytes = *(reinterpret_cast(b)+0); + b_template_bytes = *(reinterpret_cast(b)+0); b_templates = *(reinterpret_cast(b)+1); - if (a_bytes != b_bytes) + if (a_template_bytes != b_template_bytes) return JANUS_UNKNOWN_ERROR; float dist = 0; for (size_t i=0; icompare(cv::Mat(1, a_bytes, CV_8UC1, a+2*sizeof(size_t)+i*a_bytes), - cv::Mat(1, b_bytes, CV_8UC1, b+2*sizeof(size_t)+i*b_bytes)); + dist += distance->compare(cv::Mat(1, a_template_bytes, CV_8UC1, a+2*sizeof(size_t)+i*a_template_bytes), + cv::Mat(1, b_template_bytes, CV_8UC1, b+2*sizeof(size_t)+i*b_template_bytes)); *similarity = a_templates * b_templates / dist; return JANUS_SUCCESS; } + +struct janus_incomplete_gallery_type +{ + QList< QPair > templates; +}; + +janus_error janus_initialize_gallery(janus_incomplete_gallery *incomplete_gallery) +{ + *incomplete_gallery = new janus_incomplete_gallery_type(); + return JANUS_SUCCESS; +} + +janus_error janus_add_template(const janus_template template_, const size_t bytes, const janus_template_id template_id, janus_incomplete_gallery incomplete_gallery) +{ + (void) bytes; + incomplete_gallery->templates.append(QPair(template_, template_id)); + return JANUS_SUCCESS; +} + +janus_error janus_finalize_gallery(janus_incomplete_gallery incomplete_gallery, const char *gallery_file) +{ + (void) incomplete_gallery; + (void) gallery_file; + return JANUS_SUCCESS; +} diff --git a/openbr/openbr.cpp b/openbr/openbr.cpp index 981d000..1addb9a 100644 --- a/openbr/openbr.cpp +++ b/openbr/openbr.cpp @@ -53,6 +53,12 @@ void br_compare(const char *target_gallery, const char *query_gallery, const cha Compare(File(target_gallery), File(query_gallery), File(output)); } +void br_compare_n(int num_targets, const char *target_galleries[], const char *query_gallery, const char *output) +{ + if (num_targets > 1) Compare(QtUtils::toStringList(num_targets, target_galleries).join(";")+"(separator=;)", File(query_gallery), File(output)); + else Compare(File(target_galleries[0]), File(query_gallery), File(output)); +} + void br_pairwise_compare(const char *target_gallery, const char *query_gallery, const char *output) { PairwiseCompare(File(target_gallery), File(query_gallery), File(output)); @@ -323,6 +329,14 @@ unsigned char *br_unload_img(br_template tmpl) return t->m().data; } +br_template_list br_template_list_from_buffer(const char *buf, int len) +{ + QByteArray arr(buf, len); + TemplateList *tl = new TemplateList(); + *tl = TemplateList::fromBuffer(arr); + return (br_template_list)tl; +} + void br_free_template(br_template tmpl) { Template *t = reinterpret_cast(tmpl); @@ -335,6 +349,12 @@ void br_free_template_list(br_template_list tl) delete realTL; } +void br_free_output(br_matrix_output output) +{ + MatrixOutput *matOut = reinterpret_cast(output); + delete matOut; +} + int br_img_rows(br_template tmpl) { Template *t = reinterpret_cast(tmpl); @@ -359,6 +379,12 @@ bool br_img_is_empty(br_template tmpl) return t->m().empty(); } +const char* br_get_filename(br_template tmpl) +{ + Template *t = reinterpret_cast(tmpl); + return t->file.name.toStdString().c_str(); +} + void br_set_filename(br_template tmpl, const char *filename) { Template *t = reinterpret_cast(tmpl); @@ -391,6 +417,21 @@ void br_enroll_template_list(br_template_list tl) Enroll(*realTL); } +br_matrix_output br_compare_template_lists(br_template_list target, br_template_list query) +{ + TemplateList *targetTL = reinterpret_cast(target); + TemplateList *queryTL = reinterpret_cast(query); + MatrixOutput *output = MatrixOutput::make(targetTL->files(), queryTL->files()); + CompareTemplateLists(*targetTL, *queryTL, output); + return (br_matrix_output)output; +} + +float br_get_matrix_output_at(br_matrix_output output, int row, int col) +{ + MatrixOutput *matOut = reinterpret_cast(output); + return matOut->data.at(row, col); +} + br_template br_get_template(br_template_list tl, int index) { TemplateList *realTL = reinterpret_cast(tl); @@ -412,7 +453,7 @@ br_gallery br_make_gallery(const char *gallery) br_template_list br_load_from_gallery(br_gallery gallery) { Gallery *gal = reinterpret_cast(gallery); - TemplateList *tl = static_cast(malloc(sizeof(TemplateList))); + TemplateList *tl = new TemplateList(); *tl = gal->read(); return (br_template_list)tl; } diff --git a/openbr/openbr.h b/openbr/openbr.h index fdecfc7..4fc9a7c 100644 --- a/openbr/openbr.h +++ b/openbr/openbr.h @@ -102,6 +102,12 @@ BR_EXPORT void br_combine_masks(int num_input_masks, const char *input_masks[], */ BR_EXPORT void br_compare(const char *target_gallery, const char *query_gallery, const char *output = ""); +/*! + * \brief Convenience function for comparing to multiple targets. + * \see br_compare + */ +BR_EXPORT void br_compare_n(int num_targets, const char *target_galleries[], const char *query_gallery, const char *output); + BR_EXPORT void br_pairwise_compare(const char *target_gallery, const char *query_gallery, const char *output = ""); /*! @@ -437,6 +443,7 @@ BR_EXPORT void br_slave_process(const char * baseKey); typedef void* br_template; typedef void* br_template_list; typedef void* br_gallery; +typedef void* br_matrix_output; /*! * \brief Load an image from a string buffer. * Easy way to pass an image in memory from another programming language to openbr. @@ -452,6 +459,12 @@ BR_EXPORT br_template br_load_img(const char *data, int len); */ BR_EXPORT unsigned char* br_unload_img(br_template tmpl); /*! + * \brief Deserialize a br::TemplateList from a buffer. + * Can be the buffer for a .gal file, + * since they are just a TemplateList serialized to disk. + */ +BR_EXPORT br_template_list br_template_list_from_buffer(const char *buf, int len); +/*! * \brief Free a br::Template's memory. */ BR_EXPORT void br_free_template(br_template tmpl); @@ -460,6 +473,10 @@ BR_EXPORT void br_free_template(br_template tmpl); */ BR_EXPORT void br_free_template_list(br_template_list tl); /*! + * \brief Free a br::Output's memory. + */ +BR_EXPORT void br_free_output(br_matrix_output output); +/*! * \brief Get the number of rows in an image. * \param tmpl Pointer to a br::Template. */ @@ -479,7 +496,11 @@ BR_EXPORT int br_img_channels(br_template tmpl); */ BR_EXPORT bool br_img_is_empty(br_template tmpl); /*! - * \brief Set the filename for a template. + * \brief Get the filename for a br::Template + */ +BR_EXPORT const char* br_get_filename(br_template tmpl); +/*! + * \brief Set the filename for a br::Template. */ BR_EXPORT void br_set_filename(br_template tmpl, const char *filename); /*! @@ -493,10 +514,19 @@ BR_EXPORT const char* br_get_metadata_string(br_template, const char *key); BR_EXPORT br_template_list br_enroll_template(br_template tmpl); /*! * \brief Enroll a br::TemplateList from the C API! - * \param tmpl Pointer to a br::TemplateList. + * \param tl Pointer to a br::TemplateList. */ BR_EXPORT void br_enroll_template_list(br_template_list tl); /*! + * \brief Compare br::TemplateLists from the C API! + * \return Pointer to a br::MatrixOutput. + */ +BR_EXPORT br_matrix_output br_compare_template_lists(br_template_list target, br_template_list query); +/*! + * \brief Get a value in the br::MatrixOutput. + */ +BR_EXPORT float br_get_matrix_output_at(br_matrix_output output, int row, int col); +/*! * \brief Get a pointer to a br::Template at a specified index. * \param tl Pointer to a br::TemplateList. * \param index The index of the br::Template. diff --git a/openbr/openbr_plugin.cpp b/openbr/openbr_plugin.cpp index f0e0e9c..d4df6d1 100644 --- a/openbr/openbr_plugin.cpp +++ b/openbr/openbr_plugin.cpp @@ -863,7 +863,7 @@ void br::Context::printStatus() const float p = progress(); if (p < 1) { int s = timeRemaining(); - fprintf(stderr, "%05.2f%% REMAINING=%s COUNT=%g \r", 100 * p, QtUtils::toTime(s/1000.0f).toStdString().c_str(), totalSteps); + fprintf(stderr, "%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g/%g \r", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(s).toStdString().c_str(), Globals->currentStep, Globals->totalSteps); } } diff --git a/openbr/openbr_plugin.h b/openbr/openbr_plugin.h index 1d9f7cd..d74d87e 100644 --- a/openbr/openbr_plugin.h +++ b/openbr/openbr_plugin.h @@ -332,6 +332,20 @@ private: void init(const QString &file); }; +/*!< \brief Specialization for boolean type. */ +template <> +inline bool File::get(const QString &key, const bool &defaultValue) const +{ + return getBool(key, defaultValue); +} + +/*!< \brief Specialization for boolean type. */ +template <> +inline bool File::get(const QString &key) const +{ + return getBool(key); +} + BR_EXPORT QDebug operator<<(QDebug dbg, const File &file); /*!< \brief Prints br::File::flat() to \c stderr. */ BR_EXPORT QDataStream &operator<<(QDataStream &stream, const File &file); /*!< \brief Serializes the file to a stream. */ BR_EXPORT QDataStream &operator>>(QDataStream &stream, File &file); /*!< \brief Deserializes the file from a stream. */ @@ -469,7 +483,7 @@ struct TemplateList : public QList