Optimization routines


Detailed Description

The functions in this section deal specifically with optimizing a decision tree detector for repeatability.

The code in learn_detector() is a direct implementation of the algorithm described in section V of the accompanying paper.

The manipulation of the tree is necessarily tied to the internal representation which is described in the tree representation.


Functions

tree_elementrandom_tree (int d, bool is_eq_branch=1)
double compute_temperature (int i, int imax)
tree_elementlearn_detector (const vector< Image< byte > > &images, const vector< vector< Image< array< float, 2 > > > > &warps)
void run_learn_detector (int argc, char **argv)
int main (int argc, char **argv)


Function Documentation

tree_element* random_tree ( int  d,
bool  is_eq_branch = 1 
)

Generate a random tree, as part of a stochastic optimization scheme.

Parameters:
d Depth of tree to generate
is_eq_branch Whether eq-branch constraints should be applied. This should always be true when the function is called.

Definition at line 218 of file learn_detector.cc.

References num_offsets.

Referenced by learn_detector().

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 }

double compute_temperature ( int  i,
int  imax 
)

Compute the current temperature from parameters in the configuration file.

Parameters:
i The current iteration.
imax The maximum number of iterations.
Returns:
The temperature.

Definition at line 241 of file learn_detector.cc.

Referenced by learn_detector().

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 }

tree_element* learn_detector ( const vector< Image< byte > > &  images,
const vector< vector< Image< array< float, 2 > > > > &  warps 
)

Generate an optimized corner detector.

Parameters:
images The training images
warps Warps for evaluating the performance on the training images.
Returns:
An optimized detector.

Definition at line 258 of file learn_detector.cc.

References compute_repeatability(), compute_temperature(), tree_element::copy(), tree_element::eq, tree_element::gt, tree_element::is_corner, tree_element::is_leaf(), tree_element::lt, tree_element::nth_element(), tree_element::num_nodes(), num_offsets, tree_element::offset_index, tree_element::print(), random_tree(), sq(), and tree_detect_corners().

Referenced by run_learn_detector().

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 }

void run_learn_detector ( int  argc,
char **  argv 
)

Load configuration and data and learn a detector.

Parameters:
argc Number of command line arguments
argv Vector of command line arguments

Definition at line 522 of file learn_detector.cc.

References create_offsets(), draw_offsets(), learn_detector(), load_data(), tree_element::make_fast_detector(), block_bytecode::print(), tree_element::print(), and prune_warps().

Referenced by main().

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 }

int main ( int  argc,
char **  argv 
)

Driver wrapper.

Parameters:
argc Number of command line arguments
argv Vector of command line arguments

Definition at line 572 of file learn_detector.cc.

References run_learn_detector().

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 }


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