Commit 9e34c2c0d6a61e09b76d10ff7788f439b4d940b5

Authored by Josh Klontz
1 parent 6349b9cf

started a page describing the algorithm grammar

openbr/core/core.cpp
@@ -291,14 +291,18 @@ private: @@ -291,14 +291,18 @@ private:
291 if (Globals->abbreviations.contains(description)) 291 if (Globals->abbreviations.contains(description))
292 return init(Globals->abbreviations[description]); 292 return init(Globals->abbreviations[description]);
293 293
  294 + //! [Parsing the algorithm description]
294 QStringList words = QtUtils::parse(description.flat(), ':'); 295 QStringList words = QtUtils::parse(description.flat(), ':');
295 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); 296 if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format.");
  297 + //! [Parsing the algorithm description]
296 298
297 if (description.getBool("distribute", true)) 299 if (description.getBool("distribute", true))
298 words[0] = "DistributeTemplate(" + words[0] + ")"; 300 words[0] = "DistributeTemplate(" + words[0] + ")";
299 301
  302 + //! [Creating the template generation and comparison methods]
300 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL)); 303 transform = QSharedPointer<Transform>(Transform::make(words[0], NULL));
301 if (words.size() > 1) distance = QSharedPointer<Distance>(Distance::make(words[1], NULL)); 304 if (words.size() > 1) distance = QSharedPointer<Distance>(Distance::make(words[1], NULL));
  305 + //! [Creating the template generation and comparison methods]
302 } 306 }
303 }; 307 };
304 308
openbr/openbr_export.cpp
@@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
31 * - \ref qmake_integration - \copybrief qmake_integration 31 * - \ref qmake_integration - \copybrief qmake_integration
32 * 32 *
33 * \section learn_more Learn More 33 * \section learn_more Learn More
  34 + * - \ref algorithm_grammar - \copybrief algorithm_grammar
34 * - \ref cli - \copybrief cli 35 * - \ref cli - \copybrief cli
35 * - \ref c_sdk - \copybrief c_sdk 36 * - \ref c_sdk - \copybrief c_sdk
36 * - \ref cpp_plugin_sdk - \copybrief cpp_plugin_sdk 37 * - \ref cpp_plugin_sdk - \copybrief cpp_plugin_sdk
@@ -401,6 +402,67 @@ $ br -help @@ -401,6 +402,67 @@ $ br -help
401 */ 402 */
402 403
403 /*! 404 /*!
  405 + * \page algorithm_grammar Algorithm Grammar
  406 + * \brief How algorithms are constructed from string descriptions.
  407 + *
  408 + * <b>So you've run <tt>scripts/helloWorld.sh</tt> and it generally makes sense, except you have no idea what <tt>'Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.33,0.45)+CvtFloat+PCA(0.95):Dist(L2)'</tt> means or how it is executed.</b>
  409 + * Well if this is the case, you've found the right documentation.
  410 + * Let's get started!
  411 + *
  412 + * In OpenBR an <i>algorithm</i> is a technique for enrolling templates associated with a technique for comparing them.
  413 + * Recall that our ultimate goal is to be able to say how similar two face images are (or two fingerprints, irises, etc.).
  414 + * Instead of storing the entire raw image for comparison, it is common practice to store an optimized representation, or <i>template</i>, of the image for the task at hand.
  415 + * The process of generating this optimized representatation is called <i>template enrollment</i> or <i>template generation</i>.
  416 + * Given two templates, <i>template comparison</i> computes the similarity between them, where the higher values indicate more probable matches and the threshold for determining what constitutes an adaquate match is determined operationally.
  417 + * The goal of template generation is to design templates that are small, accurate, and fast to compare.
  418 + * Ok, you probably knew all of this already, let's move on.
  419 + *
  420 + * The only way of creating an algorithm in OpenBR is from a text string that describes it.
  421 + * We call this string the <i>algorithm description</i>.
  422 + * The algorithm description is seperated into two parts by a ':', with the left hand side indicating how to generate templates and the right hand side indicating how to compare them.
  423 + * Some algorithms, like \ref cpp_gender_estimation and \ref cpp_age_estimation are <i>classifiers</i> that don't create templates.
  424 + * In these cases, the colon and the template comparison technique can be omitted from the algorithm description.
  425 + *
  426 + * There are several motivations for mandating that algorithms are defined from these strings, here are the most important:
  427 + * -# It ensures good software development practices by forcibly decoupling the development of each step in an algorithm, facilitating the modification of algorithms and the re-use of individual steps.
  428 + * -# It spares the creation and maintainance of a lot of very similar header files that would otherwise be needed for each step in an algorithm, observe the abscence of headers in <tt>openbr/plugins</tt>.
  429 + * -# It allows for algorithm parameter tuning without recompiling.
  430 + * -# It is completely unambiguous, both the OpenBR interpreter and anyone familiar with the project can understand exactly what your algorithm does just from this description.
  431 + *
  432 + * Let's look at some of the important parts of the code base that make this possible!
  433 + *
  434 + * In <tt>AlgorithmCore::init()</tt> in <tt>openbr/core/core.cpp</tt> you can see the code for splitting the algorithm description at the colon:
  435 + * \snippet openbr/core/core.cpp Parsing the algorithm description
  436 + *
  437 + * Shortly thereafter in this function we <i>make</i> the template generation and comparison methods:
  438 + * \snippet openbr/core/core.cpp Creating the template generation and comparison methods
  439 + * These make calls are defined in the public \ref cpp_plugin_sdk and can also be called from end user code.
  440 + *
  441 + * Below we discuss some of the source code for \ref br::Transform::make in <tt>openbr/openbr_plugin.cpp</tt>.
  442 + * Note, \ref br::Distance::make is similar in spirit and will not be covered.
  443 + *
  444 + * One of the first steps when converting the template enrollment description into a \ref br::Transform is to replace the operators, like '+', with their full form:
  445 + * \snippet openbr/openbr_plugin.cpp Make a pipe
  446 + * A pipe (see \ref br::PipeTransform in <tt>openbr/plugins/meta.cpp</tt>) is the standard way of chaining together multiple steps in series to form more sophisticated algorithms.
  447 + * PipeTransform takes a list of transforms, and <i>projects</i> templates through each transform in order.
  448 + *
  449 + * After operator expansion, the template enrollment description forms a tree, and the transform is constructed from this description starting recurively starting at the root of the tree:
  450 + * \snippet openbr/openbr_plugin.cpp Construct the root transform
  451 + *
  452 + * At this point we reach arguably the most important code in the entire framework, the <i>object factory</i> in <tt>openbr/openbr_plugin.h</tt>.
  453 + * The \ref br::Factory class is responsible for constructing an object from a string:
  454 + * \snippet openbr/openbr_plugin.h Factory make
  455 + *
  456 + * Going back to our original example, a \ref br::PipeTransform will be created with \ref br::OpenTransform, \ref br::CvtTransform, \ref br::CascadeTransform, \ref br::ASEFEyesTransform, \ref br::AffineTransform, \ref br::CvtFloatTransform, and \ref br::PCATransform as its children.
  457 + * If you want all the tedious details about what exactly this algoritm does, then you should read the \ref br::Transform::project function implemented by each of these plugins.
  458 + * The brief explanation is that it <i>reads the image from disk, converts it to grayscale, runs the face detector, runs the eye detector on any detected faces, uses the eye locations to normalize the face for rotation and scale, converts to floating point format, and then embeds it in a PCA subspaced trained on face images</i>.
  459 + * If you are familiar with face recognition, you will likely recognize this as the Eigenfaces \cite turk1991eigenfaces algorithm.
  460 + *
  461 + * As a final note, the Eigenfaces algorithms uses the Euclidean distance (or L2-norm) to compare templates.
  462 + * Since OpenBR expects <i>similarity</i> values when comparing templates, and not <i>distances</i>, \ref br::DistDistance will return <i>-log(distance+1)</i> so that larger values indicate more similarity.
  463 + */
  464 +
  465 +/*!
404 * \page bee Biometric Evaluation Environment 466 * \page bee Biometric Evaluation Environment
405 * \brief The <i>Biometric Evaluation Environment</i> (BEE) is a <a href="http://www.nist.gov/index.html">NIST</a> standard for evaluating biometric algorithms. 467 * \brief The <i>Biometric Evaluation Environment</i> (BEE) is a <a href="http://www.nist.gov/index.html">NIST</a> standard for evaluating biometric algorithms.
406 * 468 *
openbr/openbr_plugin.cpp
@@ -1090,11 +1090,13 @@ Transform *Transform::make(QString str, QObject *parent) @@ -1090,11 +1090,13 @@ Transform *Transform::make(QString str, QObject *parent)
1090 str.replace("!","+Expand+"); 1090 str.replace("!","+Expand+");
1091 } 1091 }
1092 1092
  1093 + //! [Make a pipe]
1093 { // Check for use of '+' as shorthand for Pipe(...) 1094 { // Check for use of '+' as shorthand for Pipe(...)
1094 QStringList words = parse(str, '+'); 1095 QStringList words = parse(str, '+');
1095 if (words.size() > 1) 1096 if (words.size() > 1)
1096 return make("Pipe([" + words.join(",") + "])", parent); 1097 return make("Pipe([" + words.join(",") + "])", parent);
1097 } 1098 }
  1099 + //! [Make a pipe]
1098 1100
1099 { // Check for use of '/' as shorthand for Fork(...) 1101 { // Check for use of '/' as shorthand for Fork(...)
1100 QStringList words = parse(str, '/'); 1102 QStringList words = parse(str, '/');
@@ -1114,8 +1116,9 @@ Transform *Transform::make(QString str, QObject *parent) @@ -1114,8 +1116,9 @@ Transform *Transform::make(QString str, QObject *parent)
1114 if (str.startsWith('(') && str.endsWith(')')) 1116 if (str.startsWith('(') && str.endsWith(')'))
1115 return make(str.mid(1, str.size()-2), parent); 1117 return make(str.mid(1, str.size()-2), parent);
1116 1118
1117 - File f = "." + str;  
1118 - Transform *transform = Factory<Transform>::make(f); 1119 + //! [Construct the root transform]
  1120 + Transform *transform = Factory<Transform>::make("." + str);
  1121 + //! [Construct the root transform]
1119 1122
1120 if (transform->independent) { 1123 if (transform->independent) {
1121 File independent(".Independent"); 1124 File independent(".Independent");
openbr/openbr_plugin.h
@@ -783,6 +783,7 @@ struct Factory @@ -783,6 +783,7 @@ struct Factory
783 /*! 783 /*!
784 * \brief Constructs a plugin from a file. 784 * \brief Constructs a plugin from a file.
785 */ 785 */
  786 + //! [Factory make]
786 static T *make(const File &file) 787 static T *make(const File &file)
787 { 788 {
788 QString name = file.suffix(); 789 QString name = file.suffix();
@@ -791,11 +792,11 @@ struct Factory @@ -791,11 +792,11 @@ struct Factory
791 else if (names().contains("Default")) name = "Default"; 792 else if (names().contains("Default")) name = "Default";
792 else qFatal("%s registry does not contain object named: %s", qPrintable(baseClassName()), qPrintable(name)); 793 else qFatal("%s registry does not contain object named: %s", qPrintable(baseClassName()), qPrintable(name));
793 } 794 }
794 - if (registry->contains("_"+name)) name.prepend('_'); // Hook to override with "native" implementation  
795 T *object = registry->value(name)->_make(); 795 T *object = registry->value(name)->_make();
796 object->init(file); 796 object->init(file);
797 return object; 797 return object;
798 } 798 }
  799 + //! [Factory make]
799 800
800 /*! 801 /*!
801 * \brief Constructs all the available plugins. 802 * \brief Constructs all the available plugins.