app-SIFT-panorama.C

Go to the documentation of this file.
00001 /*! @file SIFT/app-SIFT-panorama.C Build a panorama from several overlapping images */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/SIFT/app-SIFT-panorama.C $
00035 // $Id: app-SIFT-panorama.C 14118 2010-10-09 07:36:25Z itti $
00036 //
00037 
00038 #include "Raster/Raster.H"
00039 #include "SIFT/Keypoint.H"
00040 #include "SIFT/VisualObject.H"
00041 #include "SIFT/VisualObjectMatch.H"
00042 #include "Image/ImageSet.H"
00043 #include "Util/Timer.H"
00044 #include <iostream>
00045 
00046 //! Stitch images into a panorama.
00047 /*! The images given on the command-line should overlap by some amount
00048   and should be taken in sequence; e.g., the first one may be the
00049   leftmost, the second one shifted slightly rightwards compared to the
00050   first, the third slightly shifted rightwards compared to the second,
00051   etc. Indeed this program is very simple and will just stitch the
00052   first image to the second, then the second to the third, etc,
00053   without enforcing any further consistency. */
00054 int main(const int argc, const char **argv)
00055 {
00056   MYLOGVERB = LOG_DEBUG;
00057 
00058   // check command-line args:
00059   if (argc < 4)
00060     LFATAL("USAGE: app-SIFT-panorama <result.png> "
00061            "<image1.png> ... <imageN.png>");
00062 
00063   // loop over the images and get all the SIFTaffines:
00064   ImageSet< PixRGB<byte> > images;
00065 
00066   const char *nam1 = argv[2];
00067   Image< PixRGB<byte> > im1 = Raster::ReadRGB(nam1);
00068   images.push_back(im1);
00069   rutz::shared_ptr<VisualObject> vo1(new VisualObject(nam1, "", im1));
00070   std::vector<SIFTaffine> affines;
00071   SIFTaffine comboaff; // default constructor is identity
00072   affines.push_back(comboaff);
00073   int minx = 0, miny = 0, maxx = im1.getWidth()-1, maxy = im1.getHeight()-1;
00074 
00075   for (int i = 3; i < argc; i ++)
00076     {
00077       const char *nam2 = argv[i];
00078       Image< PixRGB<byte> > im2 = Raster::ReadRGB(nam2);
00079       images.push_back(im2);
00080       rutz::shared_ptr<VisualObject> vo2(new VisualObject(nam2, "", im2));
00081 
00082       // compute the matching keypoints:
00083       Timer tim(1000000);
00084       VisualObjectMatch match(vo1, vo2, VOMA_SIMPLE);
00085       uint64 t = tim.get();
00086 
00087       LINFO("Found %u matches between %s and %s in %.3fms",
00088             match.size(), nam1, nam2, float(t) * 0.001F);
00089 
00090       // let's prune the matches:
00091       uint np = match.prune();
00092       LINFO("Pruned %u outlier matches.", np);
00093 
00094       // show our final affine transform:
00095       SIFTaffine aff = match.getSIFTaffine();
00096       std::cerr<<aff;
00097 
00098       // compose with the previous affines and store:
00099       comboaff = comboaff.compose(aff);
00100       affines.push_back(comboaff);
00101 
00102       // update panorama boundaries, using the inverse combo aff to
00103       // find the locations of the four corners of our image in the
00104       // panorama:
00105       if (comboaff.isInversible() == false) LFATAL("Oooops, singular affine!");
00106       SIFTaffine iaff = comboaff.inverse();
00107       const float ww = float(im2.getWidth() - 1);
00108       const float hh = float(im2.getHeight() - 1);
00109       float xx, yy; int x, y;
00110 
00111       iaff.transform(0.0F, 0.0F, xx, yy); x = int(xx+0.5F); y = int(yy+0.5F);
00112       if (x < minx) minx = x; if (x > maxx) maxx = x;
00113       if (y < miny) miny = y; if (y > maxy) maxy = y;
00114 
00115       iaff.transform(ww, 0.0F, xx, yy); x = int(xx+0.5F); y = int(yy+0.5F);
00116       if (x < minx) minx = x; if (x > maxx) maxx = x;
00117       if (y < miny) miny = y; if (y > maxy) maxy = y;
00118 
00119       iaff.transform(0.0F, hh, xx, yy); x = int(xx+0.5F); y = int(yy+0.5F);
00120       if (x < minx) minx = x; if (x > maxx) maxx = x;
00121       if (y < miny) miny = y; if (y > maxy) maxy = y;
00122 
00123       iaff.transform(ww, hh, xx, yy); x = int(xx+0.5F); y = int(yy+0.5F);
00124       if (x < minx) minx = x; if (x > maxx) maxx = x;
00125       if (y < miny) miny = y; if (y > maxy) maxy = y;
00126 
00127       // get ready for next pair:
00128       im1 = im2; vo1 = vo2; nam1 = nam2;
00129     }
00130 
00131   // all right, allocate the panorama:
00132   LINFO("x = [%d .. %d], y = [%d .. %d]", minx, maxx, miny, maxy);
00133   const int w = maxx - minx + 1, h = maxy - miny + 1;
00134   LINFO("Allocating %dx%d panorama...", w, h);
00135   if (w < 2 || h < 2) LFATAL("Oooops, panorama too small!");
00136   Image< PixRGB<byte> > pano(w, h, ZEROS);
00137   Image< PixRGB<byte> >::iterator p = pano.beginw();
00138 
00139   // let's stitch the images into the panorama. This code is similar
00140   // to that in VisualObjectMatch::getTransfTestImage() but modified
00141   // for a large panorama and many images:
00142   for (int j = 0; j < h; j ++)
00143     for (int i = 0; i < w; i ++)
00144       {
00145         // compute the value that should go into the current panorama
00146         // pixel based on all the images and affines; this is very
00147         // wasteful and may be optimized later:
00148         PixRGB<int> val(0); uint n = 0U;
00149 
00150         for (uint k = 0; k < images.size(); k ++)
00151           {
00152             // get transformed coordinates for image k:
00153             float u, v;
00154             affines[k].transform(float(i + minx), float(j + miny), u, v);
00155 
00156             // if we are within bounds of image k, accumulate the pix value:
00157             if (images[k].coordsOk(u, v))
00158               {
00159                 val += PixRGB<int>(images[k].getValInterp(u, v));
00160                 ++ n;
00161               }
00162           }
00163 
00164         if (n > 0) *p = PixRGB<byte>(val / n);
00165 
00166         ++ p;
00167       }
00168 
00169   // save final panorama:
00170   Raster::WriteRGB(pano, std::string(argv[1]));
00171   LINFO("Done.");
00172 
00173   return 0;
00174 }
Generated on Sun May 8 08:42:15 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3