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_element * | random_tree (int d, bool is_eq_branch=1) |
double | compute_temperature (int i, int imax) |
tree_element * | learn_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) |
tree_element* random_tree | ( | int | d, | |
bool | is_eq_branch = 1 | |||
) |
Generate a random tree, as part of a stochastic optimization scheme.
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.
i | The current iteration. | |
imax | The maximum number of iterations. |
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.
images | The training images | |
warps | Warps for evaluating the performance on the training images. |
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.
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.
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 }