faster_tree.cc

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 #include <tag/stdpp.h>
00021 #include <memory>
00022 #include <gvars3/instances.h>
00023  
00024 #include "faster_tree.h"
00025 
00026 ///\cond never
00027 using namespace std;
00028 using namespace tag;
00029 using namespace CVD;
00030 using namespace GVars3;
00031 ///\endcond
00032 
00033 ///Detect corners without nonmaximal suppression in an image. This contains a large amount of
00034 ///configurable debugging code to verify the correctness of the detector by comparing different
00035 ///implementations. High speed is achieved by converting the detector in to \link gFastTree bytecode
00036 ///and JIT-compiling if possible\endlink.
00037 ///
00038 ///The function recognises the following GVars:
00039 /// - \c debug.verify_detections Veryify JIT or bytecode detected corners using tree_element::detect_corner
00040 ///
00041 ///@param im The image to detect corners in.
00042 ///@param detector The corner detector.
00043 ///@param threshold The detector threshold.
00044 ///@ingroup gTree
00045 vector<ImageRef> tree_detect_corners_all(const Image<byte>& im, const tree_element* detector, int threshold)
00046 {
00047     ImageRef tl, br, s;
00048     rpair(tl,br) = detector->bbox();
00049     s = im.size();
00050 
00051     int ymin = 1 - tl.y, ymax = s.y - 1 - br.y;
00052     int xmin = 1 - tl.x, xmax = s.x - 1 - br.x;
00053 
00054     ImageRef pos;
00055     
00056     vector<int> corners;
00057     
00058     block_bytecode f2 = detector->make_fast_detector(im.size().x);
00059 
00060     f2.detect(im, corners, threshold, xmin, xmax, ymin, ymax);
00061     
00062 
00063     if(GV3::get<bool>("debug.verify_detections"))
00064     {
00065         //Detect corners using slowest, but most obvious detector, since it's most likely to 
00066         //be correct.
00067         vector<ImageRef> t;
00068         for(pos.y = ymin; pos.y < ymax; pos.y++)
00069         {
00070             for(pos.x = xmin; pos.x < xmax; pos.x++)
00071                 if(detector->detect_corner(im, pos, threshold))
00072                     t.push_back(pos);
00073         }
00074 
00075         //Verify detected corners against this result
00076         if(t.size() == corners.size())
00077         {
00078             for(unsigned int i=0; i < corners.size(); i++)
00079                 if(im.data() + corners[i] != & im[t[i]])
00080                 {
00081                     cerr << "Fatal error: standard and fast detectors do not match!\n";
00082                     cerr << "Same number of corners, but different positions.\n";
00083                     exit(1);
00084                 }
00085         }
00086         else
00087         {
00088             cerr << "Fatal error: standard and fast detectors do not match!\n";
00089             cerr << "Different number of corners detected.\n";
00090             cerr << corners.size() << " " << t.size() << endl;
00091             exit(1);
00092         }
00093     }
00094 
00095     vector<ImageRef> ret;
00096 
00097     int d = im.size().x;
00098     for(unsigned int i=0; i < corners.size(); i++)
00099     {
00100         int o = corners[i];
00101         ret.push_back(ImageRef(o %d, o/d));
00102     }
00103 
00104     return ret;
00105 }
00106 
00107 
00108 
00109 ///Detect corners with nonmaximal suppression in an image. This contains a large amount of
00110 ///configurable debugging code to verify the correctness of the detector by comparing different
00111 ///implementations. High speed is achieved by converting the detector in to \link gFastTree bytecode
00112 ///and JIT-compiling if possible\endlink.
00113 ///
00114 ///The function recognises the following GVars:
00115 /// - \c debug.verify_detections Veryify JIT or bytecode detected corners using tree_element::detect_corner
00116 /// - \c debug.verify_scores     Veryify bytecode computed scores using tree_element::detect_corner
00117 ///
00118 ///@param im The image to detect corners in.
00119 ///@param detector The corner detector.
00120 ///@param threshold The detector threshold.
00121 ///@param scores This image will be used to store the corner scores for nonmaximal suppression and is
00122 ///              the same size as im. It is passed as a parameter since allocation of an image of this
00123 ///              size is a significant expense.
00124 ///@ingroup gTree
00125 vector<ImageRef> tree_detect_corners(const Image<byte>& im, const tree_element* detector, int threshold, Image<int> scores)
00126 {
00127     ImageRef tl, br, s;
00128     rpair(tl,br) = detector->bbox();
00129     s = im.size();
00130 
00131     int ymin = 1 - tl.y, ymax = s.y - 1 - br.y;
00132     int xmin = 1 - tl.x, xmax = s.x - 1 - br.x;
00133 
00134     ImageRef pos;
00135     scores.zero();
00136     
00137     vector<int> corners;
00138     
00139     block_bytecode f2 = detector->make_fast_detector(im.size().x);
00140 
00141     f2.detect(im, corners, threshold, xmin, xmax, ymin, ymax);
00142     
00143 
00144     if(GV3::get<bool>("debug.verify_detections"))
00145     {
00146         //Detect corners using slowest, but most obvious detector, since it's most likely to 
00147         //be correct.
00148         vector<ImageRef> t;
00149         for(pos.y = ymin; pos.y < ymax; pos.y++)
00150         {
00151             for(pos.x = xmin; pos.x < xmax; pos.x++)
00152                 if(detector->detect_corner(im, pos, threshold))
00153                     t.push_back(pos);
00154         }
00155 
00156         //Verify detected corners against this result
00157         if(t.size() == corners.size())
00158         {
00159             for(unsigned int i=0; i < corners.size(); i++)
00160                 if(im.data() + corners[i] != & im[t[i]])
00161                 {
00162                     cerr << "Fatal error: standard and fast detectors do not match!\n";
00163                     cerr << "Same number of corners, but different positions.\n";
00164                     exit(1);
00165                 }
00166         }
00167         else
00168         {
00169             cerr << "Fatal error: standard and fast detectors do not match!\n";
00170             cerr << "Different number of corners detected.\n";
00171             cerr << corners.size() << " " << t.size() << endl;
00172             exit(1);
00173         }
00174     }
00175 
00176 
00177 
00178     //Compute scores
00179     for(unsigned int j=0; j < corners.size(); j++)
00180     {
00181         int i=threshold + 1;
00182         while(1)
00183         {
00184             int n = f2.detect(im.data() + corners[j], i);
00185             if(n != 0)
00186                 i += n;
00187             else
00188                 break;
00189         }
00190         scores.data()[corners[j]] = i-1;
00191     }
00192 
00193     if(GV3::get<bool>("debug.verify_scores"))
00194     {
00195         //Compute scores using the obvious, but slow recursive implementation.
00196         //This can be used to test the no obvious FAST implementation and the
00197         //non obviouser JIT implementation, if it ever exists.
00198         for(unsigned int j=0; j < corners.size(); j++)
00199         {
00200             int i=threshold + 1;
00201             ImageRef pos =  im.pos(im.data() + corners[j]);
00202             while(1)
00203             {
00204                 int n = detector->detect_corner(im, pos, i);
00205                 if(n != 0)
00206                     i += n;
00207                 else
00208                     break;
00209             }
00210 
00211             if(scores.data()[corners[j]] != i-1)
00212             {
00213                 cerr << "Fatal error: standard and fast scores do not match!\n";
00214                 cerr << "Different score detected at  " << pos << endl;
00215                 exit(1);
00216             }
00217         }
00218     }
00219 
00220     
00221     //Perform non-max suppression the simple way
00222     vector<ImageRef> nonmax;
00223     int d = im.size().x;
00224     for(unsigned int i=0; i < corners.size(); i++)
00225     {
00226         int o = corners[i];
00227         int v = scores.data()[o];
00228 
00229         if( v > *(scores.data() + o + 1    )  &&
00230             v > *(scores.data() + o - 1    )  &&
00231             v > *(scores.data() + o +d + 1 )  &&
00232             v > *(scores.data() + o +d     )  &&
00233             v > *(scores.data() + o +d - 1 )  &&
00234             v > *(scores.data() + o -d + 1 )  &&
00235             v > *(scores.data() + o -d     )  &&
00236             v > *(scores.data() + o -d - 1))
00237         {
00238             nonmax.push_back(ImageRef(o %d, o/d));
00239         }
00240     }
00241 
00242     return nonmax;
00243 }
00244 
00245 ///Tokenise a string.
00246 ///@param s String to be split
00247 ///@return Tokens
00248 ///@ingroup gUtility
00249 vector<string> split(const string& s)
00250 {
00251     istringstream i(s);
00252 
00253     vector<string> v;
00254 
00255     while(!i.eof())
00256     {
00257         string s;
00258         i >> s;
00259         if(s != "")
00260             v.push_back(s);
00261     }
00262     return v;
00263 }
00264 
00265 ///String to some class. Name modelled on atoi.
00266 ///@param s String to parse
00267 ///@return class the string was parsed in to.
00268 ///@ingroup gUtility
00269 template<class C> C ato(const string & s)
00270 {
00271     istringstream i(s);
00272     C c;
00273     i >> c;
00274 
00275     if(i.bad())
00276         throw   ParseError();
00277     
00278     return c;
00279 }
00280 
00281 ///Parses a tree from an istream. This will deserialize a tree serialized by ::tree_element::print().
00282 ///On error, ParseError is thrown.
00283 ///@param i The stream to parse
00284 ///@param eq_branch Is it an EQ branch? Check the invariant.
00285 ///@return An allocated tree. Ownership is passed to the callee.
00286 ///@ingroup gTree
00287 tree_element* load_a_tree(istream& i, bool eq_branch)
00288 {   
00289     string line;
00290     getline(i, line);
00291 
00292     vector<string> tok = split(line);
00293 
00294     if(tok.size() == 0)
00295         throw ParseError();
00296 
00297     if(tok[0] == "Is")
00298     {
00299         if(tok.size() != 7)
00300             throw ParseError();
00301 
00302         bool is_corner = ato<bool>(tok[2]);
00303 
00304         if(eq_branch && is_corner)
00305         {
00306             cerr << "Warning: Fixing invariant in tree\n";
00307             is_corner=0;
00308         }
00309 
00310         return new tree_element(is_corner);
00311     }
00312     else
00313     {
00314         if(tok.size() != 5)
00315             throw ParseError();
00316 
00317         int offset = ato<int>(tok[0]);
00318 
00319         auto_ptr<tree_element> t1(load_a_tree(i, false));
00320         auto_ptr<tree_element> t2(load_a_tree(i, true));
00321         auto_ptr<tree_element> t3(load_a_tree(i, false));
00322         auto_ptr<tree_element> ret(new tree_element(t1.release(), t2.release(), t3.release(), offset)); 
00323 
00324         return ret.release();
00325 
00326     }
00327 }
00328 
00329 ///Parses a tree from an istream. This will deserialize a tree serialized by ::tree_element::print().
00330 ///On error, ParseError is thrown.
00331 ///@param i The stream to parse
00332 ///@return An allocated tree. Ownership is passed to the callee.
00333 ///@ingroup gTree
00334 tree_element* load_a_tree(istream& i)
00335 {
00336     return load_a_tree(i, true);
00337 }
00338 
00339 
00340 

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