learn_detector.cc

Go to the documentation of this file.
00001 /*
00002 
00003     This file is part of the FAST-ER machine learning system.
00004     Copyright (C) 2008  Edward Rosten and Los Alamos National Laboratory
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License along
00017     with this program; if not, write to the Free Software Foundation, Inc.,
00018     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00019 */
00020 /** 
00021 @file learn_detector.cc Main file for the \c learn_detector executable.
00022 
00023 \code
00024 learn_detector [--var value] ... [--exec config_file] ...
00025 \endcode
00026 \c learn_detector reads configuration data from \c learn_detector.cfg in the
00027 current directory and acceps standard GVars3 command line arguments for setting
00028 variables and running other configuration files.
00029 
00030 The tree is serialized by the function  ::tree_element::print().  This tree can
00031 be extracted from the output with the following command:
00032 
00033 <code>awk 'a&&!NF{exit}a;/Final tree/{a=1}' </code><i>filename</i>
00034 
00035 This file contains a direct implementation of section V of the accompanying
00036 paper, in the function ::learn_detector. For more information, refer to the
00037 section on \link gOptimize optimization\endlink.
00038 
00039 
00040 \section ldConf Configuration.
00041 
00042 The default parameters for \c learn_detector are in \c learn_detector.cfg, which
00043 are the parameters described in to paper.  They are: 
00044 
00045 \include learn_detector.cfg
00046 
00047 
00048 Variables can be overridden using the \c --varname \c value commandline syntax.
00049 For details on how the data loading and so on operated, refer to
00050 ::run_learn_detector.
00051 
00052 
00053 */
00054 
00055 #include <iostream>
00056 #include <fstream>
00057 #include <climits>
00058 #include <float.h>
00059 #include <cstring>
00060 #include <cerrno>
00061 #include <cmath>
00062 #include <vector>
00063 #include <utility>
00064 #include <algorithm>
00065 
00066 #include <cvd/image_io.h>
00067 #include <cvd/random.h>
00068 #include <cvd/vector_image_ref.h>
00069 
00070 #include <tag/tuple.h>
00071 #include <tag/stdpp.h>
00072 #include <tag/fn.h>
00073 #include <tag/printf.h>
00074 
00075 #include <TooN/TooN.h>
00076 
00077 #include "gvars_vector.h"
00078 #include "faster_tree.h"
00079 #include "faster_bytecode.h"
00080 #include "offsets.h"
00081 #include "utility.h"
00082 #include "load_data.h"
00083 
00084 ///\cond never
00085 using namespace std;
00086 using namespace CVD;
00087 using namespace tag;
00088 using namespace GVars3;
00089 using namespace TooN;
00090 ///\endcond
00091 ////////////////////////////////////////////////////////////////////////////////
00092 //
00093 // Utility functions
00094 //
00095 
00096 ///Square a number
00097 ///@param d Number to square
00098 ///@return  $d^2$
00099 ///@ingroup gUtility
00100 double sq(double d)
00101 {
00102     return d*d;
00103 }
00104 
00105 
00106 ///Populate a std::vector with the numbers 0,1,...,num
00107 ///@param num Size if the range
00108 ///@return the populated vector.
00109 ///@ingroup gUtility
00110 vector<int> range(int num)
00111 {
00112     vector<int> r;
00113 
00114     for(int i=0; i < num; i++)
00115         r.push_back(i);
00116     return r;
00117 }
00118 
00119 
00120 
00121 ////////////////////////////////////////////////////////////////////////////////
00122 //
00123 //  Functions related to repeatability 
00124 //
00125 
00126 
00127 ///Generate a disc of ImageRefs.
00128 ///@param radius Radius of the disc
00129 ///@return the disc of ImageRefs
00130 ///@ingroup gRepeatability
00131 vector<ImageRef> generate_disc(int radius)
00132 {
00133     vector<ImageRef> ret;
00134     ImageRef p;
00135 
00136     for(p.y = -radius; p.y <= radius; p.y++)
00137         for(p.x = -radius; p.x <= radius; p.x++)
00138             if((int)p.mag_squared() <= radius)
00139                 ret.push_back(p);
00140     return ret;
00141 }
00142 
00143 
00144 ///Paint shapes (a vector<ImageRef>) safely in to an image
00145 ///This is used to paint discs at corner locations in order to 
00146 ///perform rapid proximity checking.
00147 ///
00148 /// @param corners  Locations to paint shapes
00149 /// @param circle   Shape to paint
00150 /// @param size     Image size to be painted in to
00151 /// @return         Image with shapes painted in to it.
00152 ///@ingroup gRepeatability
00153 Image<bool> paint_circles(const vector<ImageRef>& corners, const vector<ImageRef>& circle, ImageRef size)
00154 {   
00155     Image<bool> im(size, 0);
00156 
00157 
00158     for(unsigned int i=0; i < corners.size(); i++)
00159         for(unsigned int j=0; j < circle.size(); j++)
00160             if(im.in_image(corners[i] + circle[j]))
00161                 im[corners[i] + circle[j]] = 1;
00162 
00163     return im;
00164 }
00165 
00166 ///Computes repeatability the quick way, by caching, but has small rounding errors. This 
00167 ///function paints a disc of <code>true</code> around each detected corner in to an image. 
00168 ///If a corner warps to a pixel which has the value <code>true</code> then it is a repeat.
00169 ///
00170 /// @param warps    Every warping where warps[i][j] specifies warp from image i to image j.
00171 /// @param corners  Detected corners
00172 /// @param r        A corner must be as close as this to be considered repeated
00173 /// @param size     Size of the region for cacheing. All images must be this size.
00174 /// @return         The repeatability.
00175 /// @ingroup gRepeatability
00176 float compute_repeatability(const vector<vector<Image<array<float, 2> > > >& warps, const vector<vector<ImageRef> >& corners, int r, ImageRef size)
00177 {
00178     unsigned int n = corners.size();
00179 
00180     vector<ImageRef> disc = generate_disc(r);
00181 
00182     vector<Image<bool> >  detected;
00183     for(unsigned int i=0; i < n; i++)
00184         detected.push_back(paint_circles(corners[i], disc, size));
00185     
00186     int corners_tested = 0;
00187     int good_corners = 0;
00188 
00189     for(unsigned int i=0; i < n; i++)
00190         for(unsigned int j=0; j < n; j++)
00191         {
00192             if(i==j)
00193                 continue;
00194             
00195             for(unsigned int k=0; k < corners[i].size(); k++)
00196             {   
00197                 ImageRef dest = ir_rounded(warps[i][j][corners[i][k]]);
00198 
00199                 if(dest.x != -1)
00200                 {
00201                     corners_tested++;
00202                     if(detected[j][dest])
00203                         good_corners++;
00204                 }
00205             }
00206         }
00207     
00208     return 1.0 * good_corners / (DBL_EPSILON + corners_tested);
00209 }
00210 
00211 
00212 /// Generate a random tree, as part of a stochastic optimization scheme.
00213 ///
00214 /// @param d Depth of tree to generate
00215 /// @param is_eq_branch Whether eq-branch constraints should be applied. This should
00216 ///                     always be true when the function is called.
00217 /// @ingroup gOptimize
00218 tree_element* random_tree(int d, bool is_eq_branch=1)
00219 {
00220     //Recursively generate a tree of depth d
00221     //
00222     //Generated trees respect invariant 1
00223     if(d== 0)
00224         if(is_eq_branch)
00225             return new tree_element(0);
00226         else
00227             return new tree_element(rand()%2);
00228     else
00229         return new tree_element(random_tree(d-1, 0), random_tree(d-1, 1), random_tree(d-1, 0), rand()%num_offsets );
00230 }
00231 
00232 
00233 
00234 ///Compute the current temperature from parameters in the 
00235 ///configuration file.
00236 ///
00237 ///@ingroup gOptimize
00238 ///@param i The current iteration.
00239 ///@param imax The maximum number of iterations.
00240 ///@return The temperature.
00241 double compute_temperature(int i, int imax)
00242 {
00243     double scale=GV3::get<double>("Temperature.expo.scale");
00244     double alpha = GV3::get<double>("Temperature.expo.alpha");
00245 
00246     return scale * exp(-alpha * i / imax);
00247 }
00248 
00249 
00250 
00251 
00252 ///Generate an optimized corner detector.
00253 ///
00254 ///@ingroup gOptimize
00255 ///@param images The training images
00256 ///@param warps  Warps for evaluating the performance on the training images.
00257 ///@return An optimized detector.
00258 tree_element* learn_detector(const vector<Image<byte> >& images, const vector<vector<Image<array<float,2> > > >& warps)
00259 {
00260     unsigned int  iterations=GV3::get<unsigned int>("iterations");       // Number of iterations of simulated annealing.
00261     int threshold = GV3::get<int>("FAST_threshold");                     // Threshold at which to perform detection
00262     int fuzz_radius=GV3::get<int>("fuzz");                               // A point must be this close to be repeated (\varepsilon)
00263     double repeatability_scale = GV3::get<double>("repeatability_scale");// w_r 
00264     double num_cost =   GV3::get<double>("num_cost");                    // w_n
00265     int max_nodes = GV3::get<int>("max_nodes");                          // w_s
00266 
00267     bool first_time = 1;
00268     double old_cost = HUGE_VAL;                                          //This will store the final score on the previous iteration: \hat{k}_{I-1}
00269 
00270     ImageRef image_size = images[0].size();
00271 
00272     set<int> debug_triggers = GV3::get<set<int> >("triggers");           //Allow artitrary GVars code to be executed at a given iteration.
00273     
00274     //Preallocated space for nonmax-suppression. See tree_detect_corners()
00275     Image<int> scratch_scores(image_size, 0);
00276 
00277     //Start with an initial random tree
00278     tree_element* tree = random_tree(GV3::get<int>("initial_tree_depth"));
00279     
00280     for(unsigned int itnum=0; itnum < iterations; itnum++)
00281     {
00282         if(debug_triggers.count(itnum))
00283             GUI.ParseLine(GV3::get<string>(sPrintf("trigger.%i", itnum)));
00284 
00285         /* Trees:
00286 
00287             Invariants:
00288                 1:     eq->{0,0,0,(0,0),0}          //Leafs of an eq pointer must not be corners
00289 
00290             Operations:
00291                 Leaves:
00292                     1: Splat on a random subtree of depth 1 (respect invariant 1)
00293                     2: Flip  class   (respect invariant 1)
00294 
00295                 Nodes:
00296                     3: Copy one subtree to another subtree (no invariants need be respected)
00297                     4: Randomize offset (no invariants need be respected)
00298                     5: Splat a subtree in to a single node.
00299 
00300 
00301         Cost:
00302         
00303           (1 + (#nodes/max_nodes)^2) * (1 - repeatability)^2 * Sum_{frames} exp(- (fast_9_num-detected_num)^2/2sigma^2)
00304          
00305         */
00306 
00307 
00308         //Deep copy in to new_tree and work with the copy.
00309         tree_element* new_tree = tree->copy();
00310 
00311         cout << "\n\n-------------------------------------\n";
00312         cout << print << "Iteration" << itnum;
00313 
00314         if(GV3::get<bool>("debug.print_old_tree"))
00315         {
00316             cout << "Old tree is:" << endl;
00317             tree->print(cout);
00318         }
00319     
00320         //Skip tree modification first time so that the randomly generated
00321         //initial tree can be evaluated
00322         if(!first_time)
00323         {
00324 
00325             //Create a tree permutation
00326             tree_element* node;
00327             bool node_is_eq;
00328             
00329 
00330             //Select a random node
00331             int nnum = rand() % new_tree->num_nodes();
00332             rpair(node, node_is_eq) = new_tree->nth_element(nnum);
00333 
00334             cout << "Permuting tree at node " << nnum << endl;
00335             cout << print << "Node" << node << node_is_eq;
00336             
00337 
00338             //See section 4 in the paper.
00339             if(node->eq == NULL) //A leaf
00340             {
00341                 if(rand() % 2 || node_is_eq)  //Operation 1, invariant 1
00342                 {
00343                     cout << "Growing a subtree:\n";
00344                     //Grow a subtree
00345                     tree_element* stub = random_tree(1);
00346 
00347                     stub->print(cout);
00348 
00349                     //Splice it on manually (ick)
00350                     *node = *stub;
00351                     stub->lt = stub->eq = stub->gt = 0;
00352                     delete stub;
00353 
00354                 }
00355                 else //Operation 2
00356                 {
00357                     cout << "Flipping the classification\n";
00358                     node->is_corner  = ! node->is_corner;
00359                 }
00360             }
00361             else //A node
00362             {
00363                 double d = rand_u();
00364 
00365                 if(d < 1./3.) //Randomize the test
00366                 {
00367                     cout << "Randomizing the test\n";
00368                     node->offset_index = rand() % num_offsets;
00369                 }
00370                 else if(d < 2./3.)
00371                 {
00372                     //Select r, c \in {0, 1, 2} without replacement
00373                     int r = rand() % 3; //Remove
00374                     int c;              //Copy
00375                     while((c = rand()%3) == r){}
00376 
00377                     cout << "Copying branches " << c << " to " << r <<endl;
00378 
00379                     //Deep copy node c: it's a tree, not a graph.
00380                     tree_element* tmp;
00381 
00382                     if(c == 0)
00383                         tmp = node->lt->copy();
00384                     else if(c == 1)
00385                         tmp = node->eq->copy();
00386                     else
00387                         tmp = node->gt->copy();
00388                     
00389                     //Delete r and put the copy of c in its place
00390                     if(r == 0)
00391                     {
00392                         delete node->lt;
00393                         node->lt = tmp;
00394                     }
00395                     else if(r == 1)
00396                     {
00397                         delete node->eq;
00398                         node->eq = tmp;
00399                     }
00400                     else 
00401                     {
00402                         delete node->gt;
00403                         node->gt = tmp;
00404                     }
00405 
00406                     //NB BUG!!!
00407                     //At this point the invariant can be broken,
00408                     //since a "corner" leaf could have been copied
00409                     //to an "eq" branch.
00410 
00411                     //Oh dear. This bug made it in to the paper.
00412                     //Fortunately, the bytecode compiler ignores the tree
00413                     //when it can decuce its structure from the invariant.
00414 
00415                     //The following line should have been present in the paper:
00416                     if(node->eq->is_leaf())
00417                         node->eq->is_corner = 0;
00418 
00419                     //Happily, because the bytecode compiler deduces this
00420                     //it behaves as if this line was present, at evaluation time.
00421                     //Of course, the presense of this line will produce different
00422                     //results later if the node is subsequently copied back in one
00423                     //of these operations.
00424                 }
00425                 else //Splat!!! ie delete a subtree
00426                 {
00427                     cout << "Splat!!!1\n";
00428                     delete node->lt;
00429                     delete node->eq;
00430                     delete node->gt;
00431                     node->lt = node->eq = node->gt = 0;
00432 
00433                     if(node_is_eq) //Maintain invariant 1
00434                         node->is_corner = 0;
00435                     else
00436                         node->is_corner = rand()%2;
00437                 }
00438             }
00439         }
00440         first_time=0;
00441 
00442         if(GV3::get<bool>("debug.print_new_tree"))
00443         {
00444             cout << "New tree is: "<< endl;
00445             new_tree->print(cout);
00446         }
00447     
00448         
00449         //Detect all corners in all images
00450         vector<vector<ImageRef> > detected_corners;
00451         for(unsigned int i=0; i < images.size(); i++)
00452             detected_corners.push_back(tree_detect_corners(images[i], new_tree, threshold, scratch_scores));
00453 
00454 
00455         //Compute repeatability and assosciated cost
00456         double repeatability = compute_repeatability(warps, detected_corners, fuzz_radius, image_size);
00457         double repeatability_cost = 1 + sq(repeatability_scale/repeatability);
00458 
00459         //Compute cost associated with the total number of detected corners.
00460         float number_cost=0;
00461         for(unsigned int i=0; i < detected_corners.size(); i++)
00462         {
00463             double cost = sq(detected_corners[i].size() / num_cost);
00464             cout << print << "Image" << i << detected_corners[i].size()<< cost;
00465             number_cost += cost;
00466         }
00467         number_cost = 1 + number_cost / detected_corners.size();
00468         cout << print << "Number cost" << number_cost;
00469 
00470         //Cost associated with tree size
00471         double size_cost = 1 + sq(1.0 * new_tree->num_nodes()/max_nodes);
00472         
00473         //The overall cost function
00474         double cost = size_cost * repeatability_cost * number_cost;
00475 
00476         double temperature = compute_temperature(itnum,iterations);
00477         
00478 
00479         //The Boltzmann acceptance criterion:
00480         //If cost < old cost, then old_cost - cost > 0
00481         //so exp(.) > 1
00482         //so drand48() < exp(.) == 1
00483         double liklihood=exp((old_cost-cost) / temperature);
00484 
00485         
00486         cout << print << "Temperature" << temperature;
00487         cout << print << "Number cost" << number_cost;
00488         cout << print << "Repeatability" << repeatability << repeatability_cost;
00489         cout << print << "Nodes" << new_tree->num_nodes() << size_cost;
00490         cout << print << "Cost" << cost;
00491         cout << print << "Old cost" << old_cost;
00492         cout << print << "Liklihood" << liklihood;
00493         
00494         //Make the Boltzmann decision
00495         if(rand_u() < liklihood)
00496         {
00497             cout << "Keeping change" << endl;
00498             old_cost = cost;
00499             delete tree;
00500             tree = new_tree;
00501         }
00502         else
00503         {
00504             cout << "Rejecting change" << endl;
00505             delete new_tree;
00506         }
00507         
00508         cout << print << "Final cost" << old_cost;
00509 
00510     }
00511 
00512 
00513     return tree;
00514 }
00515 
00516 
00517 ///Load configuration and data and learn a detector.
00518 ///
00519 ///@param argc Number of command line arguments
00520 ///@param argv Vector of command line arguments
00521 ///@ingroup gOptimize
00522 void run_learn_detector(int argc, char** argv)
00523 {
00524 
00525     //Process configuration information
00526     GUI.LoadFile("learn_detector.cfg");
00527     GUI.parseArguments(argc, argv);
00528 
00529     
00530     //Load a ransom seed.
00531     if(GV3::get<int>("random_seed") != -1)
00532         srand(GV3::get<int>("random_seed"));
00533 
00534     //Initialize the global information for the tree    
00535     create_offsets();
00536     draw_offsets();
00537 
00538     
00539     //Load the training set
00540     string dir=GV3::get<string>("repeatability_dataset.directory");
00541     string format=GV3::get<string>("repeatability_dataset.format");
00542     int num=GV3::get<int>("repeatability_dataset.size");
00543 
00544     vector<Image<byte> > images;
00545     vector<vector<Image<array<float, 2> > > > warps;
00546     
00547     rpair(images, warps) = load_data(dir, num, format);
00548 
00549     prune_warps(warps, images[0].size());
00550 
00551 
00552     //Learn a detector
00553     tree_element* tree = learn_detector(images, warps);
00554 
00555     //Print out the results
00556     cout << "Final tree is:" << endl;
00557     tree->print(cout);
00558     cout << endl;
00559 
00560     cout << "Final block detector is:" << endl;
00561     {
00562         block_bytecode f = tree->make_fast_detector(9999);
00563         f.print(cout, 9999);
00564     }
00565 }
00566 
00567 ///Driver wrapper.
00568 ///
00569 ///@param argc Number of command line arguments
00570 ///@param argv Vector of command line arguments
00571 ///@ingroup gOptimize
00572 int main(int argc, char** argv)
00573 {
00574     try
00575     {   
00576         run_learn_detector(argc, argv);
00577     }
00578     catch(Exceptions::All w)
00579     {   
00580         cerr << "Error: " << w.what << endl;
00581     }
00582 }
00583 
00584 
00585 
00586 
00587 
00588 
00589 

Generated on Mon Mar 2 12:47:12 2009 for FAST-ER by  doxygen 1.5.3