00001 /*!@file BeoSub/BeoMap.C Class for stitching images */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 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/BeoSub/BeoMap.C $ 00035 // $Id: BeoMap.C 14376 2011-01-11 02:44:34Z pez $ 00036 // 00037 00038 #include "BeoSub/BeoMap.H" 00039 00040 // ###################################################################### 00041 BeoMap::BeoMap(float cut_thresh, int size_thresh, bool toCut) 00042 { 00043 CUT_THRESHOLD = cut_thresh; 00044 SIZE_THRESH = size_thresh; 00045 globalcounter = 0; 00046 pw = 0; 00047 ph = 0; 00048 switchcnt = 0; 00049 cut = toCut; 00050 } 00051 00052 // ###################################################################### 00053 BeoMap::~BeoMap() 00054 { 00055 00056 } 00057 00058 // ###################################################################### 00059 void BeoMap::makePanorama(const char* nam1, const char* nam2) 00060 { 00061 MYLOGVERB = LOG_INFO; 00062 00063 // check command-line args: 00064 //if (argc < 4) 00065 //LFATAL("USAGE: app-SIFT-panorama <result.png> " 00066 // "<image1.png> ... <imageN.png>"); 00067 00068 // loop over the images and get all the SIFTaffines: 00069 ImageSet< PixRGB<byte> > images; 00070 00071 //const char *nam1 = fm; 00072 Image< PixRGB<byte> > im1 = Raster::ReadRGB(nam1); 00073 images.push_back(im1); 00074 rutz::shared_ptr<VisualObject> vo1(new VisualObject(nam1, "", im1)); 00075 LINFO("keypoint extractions completed for input image (%d keypoints)", 00076 vo1->numKeypoints()); 00077 00078 00079 std::vector<SIFTaffine> affines; 00080 SIFTaffine comboaff; // default constructor is identity 00081 affines.push_back(comboaff); 00082 int minx = 0, miny = 0, maxx = im1.getWidth()-1, maxy = im1.getHeight()-1; 00083 for (int i = 1; i < 2; i ++) 00084 { 00085 Image< PixRGB<byte> > im2 = Raster::ReadRGB(nam2); 00086 images.push_back(im2); 00087 // LINFO("just before initializing im2"); 00088 rutz::shared_ptr<VisualObject> vo2; 00089 // LINFO("asd"); 00090 LINFO("Pass #%d\n", globalcounter); 00091 /* 00092 if(!cut){ 00093 rutz::shared_ptr<VisualObject> 00094 votemp( new VisualObject(nam2, "", im2)); 00095 vo2 = votemp; 00096 } 00097 else{ 00098 rutz::shared_ptr<VisualObject> 00099 votemp( new VisualObject(nam2, "", im3)); 00100 vo2 = votemp; 00101 } 00102 */ 00103 00104 if(!cut){ 00105 rutz::shared_ptr<VisualObject> 00106 votemp( new VisualObject(nam2, "", im2)); 00107 vo2 = votemp; 00108 } 00109 else{ 00110 rutz::shared_ptr<VisualObject> 00111 votemp( new VisualObject(nam2, "", im3)); 00112 vo2 = votemp; 00113 } 00114 00115 00116 00117 00118 00119 LINFO("keypoint extractions completed for map (%d keypoints)", 00120 vo2->numKeypoints()); 00121 00122 00123 /**/ maxx = im2.getWidth()-1, maxy = im2.getHeight()-1; 00124 // compute the matching keypoints: 00125 //Timer tim(1000000); 00126 VisualObjectMatch match(vo1, vo2, VOMA_SIMPLE); 00127 LINFO("%d keypoints matched at pass %d", match.size(), globalcounter); 00128 00129 00130 //uint64 t = tim.get(); 00131 00132 // LINFO("Found %u matches between %s and %s in %.3fms", 00133 // match.size(), nam1, nam2, float(t) * 0.001F); 00134 00135 // let's prune the matches: 00136 //uint np = 00137 match.prune(); 00138 //LINFO("Pruned %u outlier matches.", np); 00139 00140 // show our final affine transform: 00141 SIFTaffine aff = match.getSIFTaffine(); 00142 std::cerr<<aff; 00143 00144 // compose with the previous affines and store: 00145 comboaff = comboaff.compose(aff); 00146 affines.push_back(comboaff); 00147 00148 // update panorama boundaries, using the inverse combo aff to 00149 // find the locations of the four corners of our image in the 00150 // panorama: 00151 if (comboaff.isInversible() == false) LFATAL("Oooops, singular affine!"); 00152 SIFTaffine iaff = comboaff.inverse(); 00153 const float ww = float(im2.getWidth() - 1); 00154 const float hh = float(im2.getHeight() - 1); 00155 float xx, yy; int x, y; 00156 00157 iaff.transform(0.0F, 0.0F, xx, yy); x = int(xx); y = int(yy); 00158 if (x < minx) {minx = x; } 00159 if (x > maxx) {maxx = x; } 00160 if (y < miny) {miny = y; } 00161 if (y > maxy) {maxy = y; } 00162 00163 iaff.transform(ww, 0.0F, xx, yy); x = int(xx); y = int(yy); 00164 if (x < minx) {minx = x; } 00165 if (x > maxx) {maxx = x; } 00166 if (y < miny) {miny = y; } 00167 if (y > maxy) {maxy = y; } 00168 00169 00170 iaff.transform(0.0F, hh, xx, yy); x = int(xx); y = int(yy); 00171 if (x < minx) {minx = x; } 00172 if (x > maxx) {maxx = x; } 00173 if (y < miny) {miny = y; } 00174 if (y > maxy) {maxy = y; } 00175 00176 00177 iaff.transform(ww, hh, xx, yy); x = int(xx); y = int(yy); 00178 if (x < minx) {minx = x; } 00179 if (x > maxx) {maxx = x; } 00180 if (y < miny) {miny = y; } 00181 if (y > maxy) {maxy = y; } 00182 00183 //LINFO("modMinX %d, modMaxX %d, modMinY %d, modMaxY %d", 00184 //modMinX, modMaxX, modMinY, modMaxY); 00185 // get ready for next pair: 00186 im1 = im2; vo1 = vo2; nam1 = nam2; 00187 } 00188 00189 // all right, allocate the panorama: 00190 //LINFO("x = [%d .. %d], y = [%d .. %d]", minx, maxx, miny, maxy); 00191 int w = maxx - minx + 1, h = maxy - miny + 1; 00192 00193 00194 if(globalcounter==0){ 00195 pw=w; 00196 ph=h; 00197 } 00198 00199 // 00200 // if the map size is within acceptable range. 00201 // 00202 if(w <= pw && h <= ph) 00203 { 00204 00205 //LINFO("Allocating %dx%d panorama...", w, h); 00206 if (w < 2 || h < 2) LFATAL("Oooops, panorama too small!"); 00207 Image< PixRGB<byte> > pano(w, h, ZEROS); 00208 Image< PixRGB<byte> >::iterator p = pano.beginw(); 00209 00210 int minStitchedX=0, maxStitchedX=0, 00211 minStitchedY=0, maxStitchedY=0, 00212 counterStitching = 0; 00213 int minStitchedX2=0, maxStitchedX2=0, 00214 minStitchedY2=0, maxStitchedY2=0, 00215 counterStitching2 = 0; 00216 // let's stitch the images into the panorama. This code is similar 00217 // to that in VisualObjectMatch::getTransfTestImage() but modified 00218 // for a large panorama and many images: 00219 00220 00221 for (int j = 0; j < h; j ++) 00222 for (int i = 0; i < w; i ++) 00223 { 00224 // compute the value that should go into the current panorama 00225 // pixel based on all the images and affines; this is very 00226 // wasteful and may be optimized later: 00227 PixRGB<int> val(0); uint n = 0U; 00228 //LINFO("images.size is %d",images.size()); 00229 for (uint k = 0; k < images.size(); k ++) 00230 { 00231 // get transformed coordinates for image k: 00232 float u, v; 00233 affines[k].transform(float(i + minx), float(j + miny), u, v); 00234 // LINFO("K is %d, i+minx is %d, i+miny is %d, u is %f, v is %f",k,i+minx,i+miny,u,v); 00235 // if we are within bounds of image k, accumulate the pix value: 00236 if (images[k].coordsOk(u, v)) 00237 { 00238 val += PixRGB<int>(images[k].getValInterp(u, v)); 00239 //the if-else statement below gathers the min/max coordinates on the resulting image the input image is. 00240 if(k == 0 && counterStitching == 0) 00241 { 00242 minStitchedX = maxStitchedX = i; 00243 minStitchedY = maxStitchedY = j; 00244 counterStitching++; 00245 } 00246 else if(k==0) 00247 { 00248 if(minStitchedX > i) 00249 minStitchedX = i; 00250 if(maxStitchedX < i) 00251 maxStitchedX = i; 00252 if(minStitchedY > j) 00253 minStitchedY = j; 00254 if(maxStitchedY < j) 00255 maxStitchedY = j; 00256 counterStitching++; 00257 } 00258 if(counterStitching2 == 0) 00259 { 00260 minStitchedX2 = maxStitchedX2 = i; 00261 minStitchedY2 = maxStitchedY2 = j; 00262 counterStitching2++; 00263 } 00264 else 00265 { 00266 if(minStitchedX2 > i) 00267 minStitchedX2 = i; 00268 if(maxStitchedX2 < i) 00269 maxStitchedX2 = i; 00270 if(minStitchedY2 > j) 00271 minStitchedY2 = j; 00272 if(maxStitchedY2 < j) 00273 maxStitchedY2 = j; 00274 counterStitching2++; 00275 } 00276 00277 ++ n; 00278 } 00279 } 00280 00281 if (n > 0) *p = PixRGB<byte>(val / n); 00282 00283 ++ p; 00284 } 00285 00286 LINFO("Pixel coordinates of the area Stitched: xmin %d, xmax %d, ymin %d, ymax %d",minStitchedX, maxStitchedX, minStitchedY, maxStitchedY); 00287 LINFO("Pixel coordinates of the area Stitched: xmin2 %d, xmax2 %d, ymin2 %d, ymax2 %d",minStitchedX2, maxStitchedX2, minStitchedY2, maxStitchedY2); 00288 00289 00290 //if(maxStitchedX2 + 10 < w &&maxStitchedY2+10 < h){ 00291 Image< PixRGB<byte> > pano2((maxStitchedX2+10<w)?maxStitchedX2+10:w, (maxStitchedY2+10<h)?maxStitchedY2+10:h, ZEROS); 00292 for(int i = 0; i < pano2.getWidth(); i++) 00293 for(int j = 0; j<pano2.getHeight(); j++) 00294 { 00295 pano2.setVal(i,j,pano.getVal(i,j)); 00296 00297 } 00298 // } 00299 00300 // w = maxStitchedX2+1; 00301 // h = maxStitchedY2+1; 00302 if( (pano2.getHeight()*pano2.getWidth()/*w*h*/) / (float)((maxStitchedX - minStitchedX+1)*(maxStitchedY - minStitchedY+1)) >= CUT_THRESHOLD ) 00303 { 00304 // LINFO("GO CUT THE MAP!\n"); 00305 00306 // 00307 // Simple map cutting algorithm. 00308 // 00309 // Coded on Feb/8/2006. 00310 00311 00312 //LINFO("===================================================\n"); 00313 00314 // calculate cutting position. 00315 00316 medge.rightedge = maxStitchedX + SIZE_THRESH; 00317 medge.leftedge = minStitchedX - SIZE_THRESH; 00318 medge.upperedge = minStitchedY - SIZE_THRESH; 00319 medge.loweredge = maxStitchedY + SIZE_THRESH; 00320 00321 if( (pano2.getWidth()/*w*/ - medge.rightedge) < 20){ 00322 medge.rightedge = -1; // -1 means dont cut a map. 00323 } 00324 00325 if(medge.leftedge < 20){ 00326 medge.leftedge = -1; // -1 means dont cut a map. 00327 } 00328 00329 if( (medge.upperedge) <= 20){ 00330 medge.upperedge = -1; // -1 means dont cut a map. 00331 } 00332 00333 if( (pano2.getHeight()/*h*/ - medge.loweredge) < 20){ 00334 medge.loweredge = -1; // -1 means dont cut a map. 00335 } 00336 00337 00338 LINFO("minX %d , maxX %d, minY %d, maxY %d", minStitchedX, maxStitchedX, minStitchedY, maxStitchedY); 00339 00340 00341 if(medge.upperedge > 0) LINFO("UPPER-SIDE : %d \n", medge.upperedge); 00342 else LINFO("UPPER-SIDE : No cut"); 00343 00344 00345 if(medge.rightedge > 0) LINFO("RIGHT-SIDE : %d \n", medge.rightedge); 00346 else LINFO("RIGHT-SIDE : No cut"); 00347 00348 if(medge.leftedge > 0) LINFO("LEFT-SIDE : %d \n", medge.leftedge); 00349 else LINFO("LEFT-SIDE : No cut"); 00350 00351 00352 00353 if(medge.loweredge > 0) LINFO("LOWER-SIDE : %d \n", medge.loweredge); 00354 else LINFO("LOWER-SIDE : No cut"); 00355 00356 LINFO("Map size : (Xsize, Ysize) = (%d, %d)\n", w, h); 00357 00358 // LINFO("===================================================\n"); 00359 00360 int widthtmp(pano2.getWidth()/*w*/); int hswitch(0); 00361 00362 00363 /////////////////////////////////////////////////////////////////////////////// 00364 //////// X-Direction cut. 00365 00366 if((medge.rightedge != -1) && (medge.leftedge != -1)){ 00367 im3.resize(widthtmp = (medge.rightedge+1) - medge.leftedge, pano2.getHeight()); 00368 //LINFO("size of im3 is %d,%d",im3.getWidth(), im3.getHeight()); 00369 for(int i = 0; i<im3.getWidth();i++) 00370 for(int j = 0; j <im3.getHeight();j++) 00371 { 00372 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j)); 00373 } 00374 cut = true; 00375 hswitch = 1; 00376 } 00377 else if((medge.rightedge != -1) && (medge.leftedge==-1)) 00378 { 00379 im3.resize(widthtmp = (medge.rightedge+1), pano2.getHeight()); 00380 // LINFO("size of im3 is %d,%d",im3.getWidth(), im3.getHeight()); 00381 for(int i = 0; i<im3.getWidth();i++) 00382 for(int j = 0; j <im3.getHeight();j++) 00383 { 00384 im3.setVal(i,j,pano2.getVal(i, j)); 00385 } 00386 cut = true; 00387 hswitch = 2; 00388 } 00389 else if((medge.rightedge == -1) && (medge.leftedge!=-1)) 00390 { 00391 im3.resize(widthtmp = (pano2.getWidth()-medge.leftedge-1), pano2.getHeight()); 00392 //LINFO("size of im3 is %d,%d",im3.getWidth(), im3.getHeight()); 00393 for(int i = 0; i<im3.getWidth();i++) 00394 for(int j = 0; j <im3.getHeight();j++) 00395 { 00396 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j)); 00397 } 00398 cut = true; 00399 hswitch = 3; 00400 } 00401 else{ 00402 cut = false; 00403 hswitch = 0; 00404 widthtmp = pano2.getWidth(); 00405 } 00406 00407 /////////////////////////////////////////////////////////////////////////////// 00408 //////// Y-Direction cut. 00409 00410 if((medge.upperedge != -1) && (medge.loweredge != -1)){// upper lower both cut. 00411 im3.resize(widthtmp, medge.loweredge - medge.upperedge ); 00412 //LINFO("size of im3 is %d,%d",im3.getWidth(), im3.getHeight()); 00413 for(int i = 0; i<im3.getWidth();i++) 00414 for(int j = 0; j <im3.getHeight();j++) 00415 { 00416 if(hswitch == 0)// left right no cut 00417 im3.setVal(i,j,pano2.getVal(i, j+medge.upperedge)); 00418 if(hswitch == 1)// left right both cut 00419 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j+medge.upperedge)); 00420 if(hswitch == 2)// left no cut, right cut 00421 im3.setVal(i,j,pano2.getVal(i, j+medge.upperedge)); 00422 if(hswitch == 3)// left cut right no cut. 00423 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j+medge.upperedge)); 00424 } 00425 cut = true; 00426 } 00427 else if((medge.upperedge != -1) && (medge.loweredge==-1)){// upper cut lower no cut 00428 im3.resize(widthtmp, pano2.getHeight() - medge.upperedge); 00429 //LINFO("size of im3 is %d,%d",im3.getWidth(), im3.getHeight()); 00430 for(int i = 0; i<im3.getWidth();i++) 00431 for(int j = 0; j <im3.getHeight();j++) 00432 { 00433 // im3.setVal(i,j,pano.getVal(i, j)); 00434 if(hswitch == 0)// left right no cut 00435 im3.setVal(i,j,pano2.getVal(i, j+medge.upperedge)); 00436 if(hswitch == 1)// left right both cut 00437 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j+medge.upperedge)); 00438 if(hswitch == 2)// left no cut, right cut 00439 im3.setVal(i,j,pano2.getVal(i, j+medge.upperedge)); 00440 if(hswitch == 3)// left cut right no cut. 00441 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j+medge.upperedge)); 00442 } 00443 cut = true; 00444 } 00445 else if((medge.upperedge == -1) && (medge.loweredge !=-1)){// upper no cut lower cut 00446 im3.resize(widthtmp, medge.loweredge+1); 00447 //LINFO("size of im3 is %d,%d",im3.getWidth(), im3.getHeight()); 00448 for(int i = 0; i<im3.getWidth();i++) 00449 for(int j = 0; j <im3.getHeight();j++) 00450 { 00451 // im3.setVal(i,j,pano.getVal(i+medge.leftedge, j)); 00452 // im3.setVal(i,j,pano.getVal(i, j)); 00453 if(hswitch == 0)// left right no cut 00454 im3.setVal(i,j,pano2.getVal(i, j)); 00455 if(hswitch == 1)// left right both cut 00456 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j)); 00457 if(hswitch == 2)// left no cut, right cut 00458 im3.setVal(i,j,pano2.getVal(i, j)); 00459 if(hswitch == 3)// left cut right no cut. 00460 im3.setVal(i,j,pano2.getVal(i+medge.leftedge, j)); 00461 } 00462 cut = true; 00463 } 00464 else{ 00465 cut = false; 00466 } 00467 00468 00469 00470 /////////// 00471 00472 } 00473 else 00474 { 00475 cut = false; 00476 } 00477 00478 00479 // pano.resize(maxStitchedX2+1, maxStitchedY2+1,false); 00480 // save final panorama: 00481 //pano = rescale(pano, maxStitchedX2+1, maxStitchedY2+1); 00482 00483 Raster::WriteRGB(pano2, nam2); 00484 // LINFO("Done."); 00485 00486 // to avid making a map extremely big. 00487 pw = (int)(pano2.getWidth()*1.5);//w*1.5); 00488 ph = (int)(pano2.getHeight()*1.5);//h*1.5); 00489 globalcounter++; 00490 } 00491 }