LFLineFitter.cpp

00001 /*
00002 Copyright 2010, Ming-Yu Liu
00003 
00004 All Rights Reserved 
00005 
00006 Permission to use, copy, modify, and distribute this software and 
00007 its documentation for any non-commercial purpose is hereby granted 
00008 without fee, provided that the above copyright notice appear in 
00009 all copies and that both that copyright notice and this permission 
00010 notice appear in supporting documentation, and that the name of 
00011 the author not be used in advertising or publicity pertaining to 
00012 distribution of the software without specific, written prior 
00013 permission. 
00014 
00015 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 
00016 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
00017 ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
00018 ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
00019 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
00020 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 
00021 OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
00022 
00023 Lior Elazary: Changed to work with the INVT
00024 */
00025 
00026 
00027 
00028 #include "LFLineFitter.h"
00029 
00030 
00031 LFLineFitter::LFLineFitter()
00032 {
00033         localWindSize_ = 50;
00034         smallLocalWindowSize_ = max(localWindSize_/10,5);
00035         nMinEdges_ = 2;
00036         nMaxWindPoints_ = 4*(localWindSize_+1)*(localWindSize_+1);
00037         minLength_ = 2;
00038 
00039         nLinesToFitInStage_[0] = 300;
00040         nLinesToFitInStage_[1] = 2000;
00041         nTrialsPerLineInStage_[0] = 300;
00042         nTrialsPerLineInStage_[1] = 1;
00043         sigmaFitALine_ = 0.75;
00044         sigmaFindSupport_ = 0.75;
00045         maxGap_ = 1.5;
00046 
00047         outEdgeMap_ = NULL;
00048         rpoints_ = NULL;
00049         rProjection_ = NULL;
00050         absRProjection_ = NULL;
00051         idx_ = NULL;
00052 }
00053 
00054 void LFLineFitter::Configure(double sigmaFitALine,double sigmaFindSupport, double maxGap,
00055         int nLayer,int *nLinesToFitInStage,int *nTrialsPerLineInStage)
00056 {
00057         sigmaFitALine_ = sigmaFitALine;
00058         sigmaFindSupport_ = sigmaFindSupport;
00059         maxGap_ = maxGap;
00060         for(int i=0;i<nLayer;i++)
00061         {
00062                 nLinesToFitInStage_[i] = nLinesToFitInStage[i];
00063                 nTrialsPerLineInStage_[i] = nTrialsPerLineInStage[i];
00064         }
00065 }
00066 
00067 void LFLineFitter::Configure(const char *fileName)
00068 {
00069 
00070         for(int i=0;i<50;i++)
00071                 cout<<"*";
00072         cout<<endl;
00073 
00074         cout<<" LFLineFitting"<<endl;
00075 
00076         for(int i=0;i<50;i++)
00077                 cout<<"*";
00078         cout<<endl;
00079 
00080 
00081         char str[256];
00082         FILE *fp=NULL;
00083         fp = fopen(fileName,"rt");
00084         if(fp==NULL)
00085         {
00086                 cerr<<"[ERROR] Cannot read file "<<fileName<<"\n!!!";
00087                 exit(0);
00088         }
00089         
00090         cout<<"Load Configuration from "<<fileName<<endl;
00091         
00092   int ret=0;
00093         ret=fscanf(fp,"%s\n",str);
00094         cout<<str<<" = ";
00095         ret=fscanf(fp,"%s\n",str);
00096         sigmaFitALine_ = atof(str);
00097         cout<<sigmaFitALine_<<endl;
00098 
00099         ret=fscanf(fp,"%s\n",str);
00100         cout<<str<<" = ";
00101         ret=fscanf(fp,"%s\n",str);
00102         sigmaFindSupport_ = atof(str);
00103         cout<<sigmaFindSupport_<<endl;
00104 
00105         ret=fscanf(fp,"%s\n",str);
00106         cout<<str<<" = ";
00107         ret=fscanf(fp,"%s\n",str);
00108         maxGap_ = atof(str);
00109         cout<<maxGap_<<endl;
00110 
00111         for(int i=0;i<2;i++)
00112         {
00113                 ret=fscanf(fp,"%s\n",str);
00114                 cout<<str<<" = ";
00115                 ret=fscanf(fp,"%s\n",str);
00116                 nLinesToFitInStage_[i] = (int)atof(str);
00117                 cout<<nLinesToFitInStage_[i]<<endl;
00118 
00119                 ret=fscanf(fp,"%s\n",str);
00120                 cout<<str<<" = ";
00121                 ret=fscanf(fp,"%s\n",str);
00122                 nTrialsPerLineInStage_[i] = (int)atof(str);
00123                 cout<<nTrialsPerLineInStage_[i]<<endl;
00124         }
00125 
00126         fclose(fp);
00127         cout<<endl<<endl;
00128 }
00129 
00130 
00131 LFLineFitter::~LFLineFitter()
00132 {
00133         SafeRelease();
00134 }
00135 
00136 void LFLineFitter::SafeRelease()
00137 {
00138         if(outEdgeMap_)
00139                 delete [] outEdgeMap_;
00140         if(rpoints_)
00141                 delete [] rpoints_;
00142         if(rProjection_)
00143                 delete [] rProjection_;
00144         if(absRProjection_)
00145                 delete [] absRProjection_;
00146         if(idx_)
00147                 delete [] idx_;
00148         outEdgeMap_ = NULL;
00149         rpoints_ = NULL;
00150         rProjection_ = NULL;
00151         absRProjection_ = NULL;
00152         idx_ = NULL;
00153 
00154 
00155 }
00156 
00157 void LFLineFitter::Init()
00158 {
00159         outEdgeMap_ = new LFLineSegment[nLinesToFitInStage_[0]+nLinesToFitInStage_[1]];
00160         rpoints_ = new CvPoint [nMaxWindPoints_];
00161         rProjection_ = new double [nMaxWindPoints_];
00162         absRProjection_ = new double [nMaxWindPoints_];
00163         idx_ = new int [nMaxWindPoints_];
00164         
00165 }
00166 
00167 void LFLineFitter::FitLine(IplImage *inputImage)
00168 {
00169         //long int t1, t2, f;
00170         //QueryPerformanceFrequency(&f);
00171         //QueryPerformanceCounter(&t1);
00172 
00173 
00174   //cvNamedWindow("debug",1);
00175   //cvShowImage("debug",inputImage);
00176   //cvWaitKey(0);
00177 
00178         width_ = inputImage->width;
00179         height_ = inputImage->height;
00180 
00181         map<int,CvPoint> edgeMap;
00182 
00183         int i,j,k;
00184         int x0,y0;
00185         int width,height;
00186         int index=0;
00187         int nPixels=0;
00188         int nEdges=0;
00189         int maxSupport=0;       
00190         LFLineSegment tmpLs,bestLs;
00191         CvPoint2D64f lnormal;
00192         int nWindPoints=0,nWaitingKillingList=0,nProposedKillingList=0;
00193         CvPoint *windPoints,*waitingKillingList,*proposedKillingList;
00194         windPoints = new CvPoint [nMaxWindPoints_];
00195         waitingKillingList = new CvPoint[nMaxWindPoints_];
00196         proposedKillingList = new CvPoint[nMaxWindPoints_];
00197 
00198         width = inputImage->width;
00199         height = inputImage->height;
00200         nPixels = width*height;
00201 
00202         for(int y=0;y<height;y++)
00203         {
00204                 for(int x=0;x<width;x++)
00205                 {
00206                         i = x + y*width;
00207                         if(cvGetReal1D(inputImage,i)!=0)
00208                         {
00209                                 edgeMap.insert(pair<int,CvPoint>(i,cvPoint(x,y)));
00210                                 nEdges++;
00211                         }
00212                 }
00213         }
00214 
00215 
00216         nInputEdges_ = nEdges;
00217         nLineSegments_=0;
00218         for(k=0;k<2;k++)
00219         {
00220                 if(nEdges<nMinEdges_)
00221                         break;
00222                 for(i=0;i<nLinesToFitInStage_[k];i++)
00223                 {
00224                         maxSupport = 0;
00225                         for(j=0;j<nTrialsPerLineInStage_[k];j++)
00226                         {
00227                                 // Sample a point
00228                                 index = SampleAPixel(&edgeMap,inputImage,nPixels);
00229                                 y0 = index/width;
00230                                 x0 = index - y0*width;
00231                                 
00232                                 // Locate the subwindow
00233                                 Find(x0,y0,windPoints,nWindPoints,inputImage,smallLocalWindowSize_);                            
00234                                 
00235                                 // Infer a line direction
00236                                 FitALine(nWindPoints,windPoints,sigmaFitALine_,lnormal);
00237 
00238                                 // Locate the subwindow                 
00239                                 Find(&edgeMap,x0,y0,windPoints,nWindPoints,inputImage,localWindSize_);
00240 
00241                                 // Find the support                     
00242                                 FindSupport(nWindPoints,windPoints,lnormal,sigmaFindSupport_,maxGap_,
00243                                         tmpLs,proposedKillingList,nProposedKillingList,x0,y0);
00244                                 
00245                                 // Check if need to update
00246                                 if(tmpLs.nSupport_ > maxSupport)
00247                                 {
00248                                         maxSupport = tmpLs.nSupport_;
00249                                         nWaitingKillingList = nProposedKillingList;
00250                                         memcpy(waitingKillingList,proposedKillingList,sizeof(CvPoint)*nWaitingKillingList);
00251                                         bestLs = tmpLs;
00252                                 }
00253                         }
00254 
00255                         // Remove points
00256                         for(j=0;j<maxSupport;j++)
00257                         {
00258                                 cvSetReal2D(inputImage,waitingKillingList[j].y,waitingKillingList[j].x,0.0);
00259 
00260                                 edgeMap.erase(waitingKillingList[j].y*width+waitingKillingList[j].x);
00261                         }
00262                         nEdges -= bestLs.nSupport_;
00263                         bestLs.len_ = sqrt( (bestLs.sx_-bestLs.ex_)*(bestLs.sx_-bestLs.ex_) + (bestLs.sy_-bestLs.ey_)*(bestLs.sy_-bestLs.ey_));
00264                         outEdgeMap_[nLineSegments_] = bestLs;
00265                         nLineSegments_++;
00266 
00267                         if(nEdges<nMinEdges_)
00268                                 break;
00269                 }
00270         }
00271 
00272 
00273         qsort(outEdgeMap_,nLineSegments_,sizeof(LFLineSegment),LFLineSegment::Compare);
00274         std::cout<<"Num. lines in test image = "<<nLineSegments_<<std::endl;
00275 
00276 
00277         delete [] windPoints;
00278         delete [] waitingKillingList;
00279         delete [] proposedKillingList;
00280         edgeMap.clear();
00281 
00282   std::cout<<"Line Ftting Done"<<std::endl;
00283         //QueryPerformanceCounter(&t2);
00284         //std::cout<<"Line Fitting Time "<<setiosflags(ios::fixed)<<setprecision(6)<<(t2.QuadPart - t1.QuadPart)/(1.0*f.QuadPart)<<std::endl;
00285 }
00286 
00287 void LFLineFitter::SaveEdgeMap(const char *filename)
00288 {
00289         FILE *fp;
00290         qsort(outEdgeMap_,nLineSegments_,sizeof(LFLineSegment),LFLineSegment::Compare);
00291         fp = fopen(filename,"wt");
00292 
00293         fprintf(fp,"%d %d\n",width_,height_);
00294         fprintf(fp,"%d\n",nLineSegments_);
00295 
00296         double ratio=0;
00297         double count=0;
00298         for(int i=0;i<nLineSegments_;i++)
00299         {
00300                 count += (double)outEdgeMap_[i].nSupport_;
00301                 ratio = count/nInputEdges_;
00302                 fprintf(fp,"%d %d %d %d\n",(int)outEdgeMap_[i].sx_,(int)outEdgeMap_[i].sy_,(int)outEdgeMap_[i].ex_,(int)outEdgeMap_[i].ey_);
00303         }
00304 
00305         fclose(fp);
00306 
00307 
00308 }
00309 
00310 
00311 void LFLineFitter::DisplayEdgeMap(IplImage *inputImage,const char *outputImageName)
00312 {
00313         // debug
00314         IplImage *debugImage = cvCreateImage( cvSize(inputImage->width,inputImage->height), IPL_DEPTH_8U,1);
00315         cvZero(debugImage);
00316 
00317         for(int i=0;i<nLineSegments_;i++)
00318         {
00319                 cvLine(debugImage,cvPoint((int)outEdgeMap_[i].sx_,(int)outEdgeMap_[i].sy_),
00320                         cvPoint((int)outEdgeMap_[i].ex_,(int)outEdgeMap_[i].ey_),cvScalar(255));
00321         }
00322 
00323         //if(outputImageName!=NULL)
00324         //{
00325         //      printf("Save Image %s\n\n",outputImageName);
00326         //      cvSaveImage(outputImageName,debugImage);
00327         //}
00328         //else
00329         //{             
00330         //      cvNamedWindow("debug",1);
00331         //      cvShowImage("debug",debugImage);
00332         //      cvWaitKey(0);
00333         //}
00334 
00335         cvReleaseImage(&debugImage);
00336 }
00337 
00338 void LFLineFitter::FindSupport(const int nWindPoints,CvPoint *windPoints,CvPoint2D64f &lnormal,
00339                                                          double sigmaFindSupport,double maxGap,LFLineSegment &ls,
00340                                                          CvPoint *proposedKillingList,int &nProposedKillingList,int x0,int y0)
00341 {
00342         int i,j;        
00343         int nRindices = 0;
00344         int zeroIndex=0;
00345         double residuals;
00346         CvPoint2D64f ldirection;
00347         
00348         // Find the point within the threshold by taking dot product
00349         for(i=0;i<nWindPoints;i++)
00350         {
00351                 residuals = fabs( windPoints[i].x*lnormal.x + windPoints[i].y*lnormal.y );
00352                 if(residuals<sigmaFindSupport)
00353                 {
00354                         rpoints_[nRindices] = windPoints[i];
00355                         nRindices++;
00356                 }
00357         }
00358         ldirection.x = -lnormal.y;
00359         ldirection.y = lnormal.x;
00360 
00361 
00362         if(nRindices<minLength_)
00363         {
00364                 ls.nSupport_ = -1;
00365                 return;
00366         }
00367 
00368 
00369         // Project to the line
00370         for(i=0;i<nRindices;i++)
00371         {
00372                 rProjection_[i] = rpoints_[i].x*ldirection.x+ rpoints_[i].y*ldirection.y;               
00373                 idx_[i] = i;
00374         }
00375         
00376         // Sort the projection and find the starting and ending points  
00377         ISort(rProjection_,nRindices,idx_);
00378 
00379         for(i=0;i<nRindices;i++)
00380                 absRProjection_[i] = fabs(rProjection_[i]);
00381 
00382         for(i=0;i<nRindices;i++)
00383         {
00384                 if(absRProjection_[i]==0)
00385                 {
00386                         zeroIndex = i;
00387                         break;
00388                 }
00389         }
00390         
00391         int maxIndex = nRindices-1;
00392         for( i=zeroIndex; i<(nRindices-1); i++)
00393         {       
00394                 if((rProjection_[i+1]-rProjection_[i])>maxGap)
00395                 {       
00396                         maxIndex = i;
00397                         break;
00398                 }
00399         }
00400 
00401         int minIndex = 0;
00402         for( i=zeroIndex; i>0; i--)
00403         {       
00404                 if((rProjection_[i]-rProjection_[i-1])>maxGap)
00405                 {       
00406                         minIndex = i;
00407                         break;
00408                 }
00409         }
00410 
00411         ls.nSupport_ = maxIndex-minIndex+1;
00412         ls.sx_ = (double)rpoints_[ idx_[minIndex] ].x+x0;
00413         ls.sy_ = (double)rpoints_[ idx_[minIndex] ].y+y0;
00414         ls.ex_ = (double)rpoints_[ idx_[maxIndex] ].x+x0;
00415         ls.ey_ = (double)rpoints_[ idx_[maxIndex] ].y+y0;
00416         
00417         j = 0;
00418         for(i=minIndex;i<=maxIndex;i++)
00419         {
00420                 proposedKillingList[j].x = rpoints_[ idx_[i] ].x + x0;
00421                 proposedKillingList[j].y = rpoints_[ idx_[i] ].y + y0;
00422                 j++;
00423         }
00424         nProposedKillingList = j;
00425 
00426         ls.normal_ = lnormal;
00427 }
00428 
00429 
00430 int LFLineFitter::FitALine(const int nWindPoints,CvPoint *windPoints,const double sigmaFitALine,CvPoint2D64f &lnormal)
00431 {
00432         double inlierRatio = 0.9;       
00433         double outlierRatio = 0.9;
00434         double gamma = 0.05;    
00435         int nMaxTry = 29; //ceil(log(0.05)/log(0.9))
00436 
00437         int i=0,j=0,index=0;
00438         int cscore;
00439         double tmpScore;
00440         double norm;
00441         int bestscore = -1;     
00442         CvPoint2D64f cdirection,cnormal;
00443 
00444         while(i<nMaxTry)
00445         {
00446                 index = (int)floor(rand()/(double)RAND_MAX*(double)nWindPoints);
00447                 norm = sqrt( 1.0*windPoints[index].x*windPoints[index].x + windPoints[index].y*windPoints[index].y );
00448                 
00449                 if(norm>0)
00450                 {
00451                         cdirection.x = windPoints[index].x/norm;
00452                         cdirection.y = windPoints[index].y/norm;
00453 
00454                         cnormal.x = -cdirection.y;
00455                         cnormal.y = cdirection.x;
00456                         
00457                         cscore = 0;
00458                         for(j=0;j<nWindPoints;j++)
00459                         {
00460                                 tmpScore = fabs(windPoints[j].x*cnormal.x+windPoints[j].y*cnormal.y);
00461                                 if(tmpScore<sigmaFitALine)
00462                                         cscore++;
00463                         }
00464 
00465                         if( ((double)cscore)/nWindPoints > inlierRatio)
00466                         {
00467                                 bestscore = cscore;
00468                                 lnormal = cnormal;
00469                                 return bestscore;
00470                         }
00471 
00472                         if( (1.0-((double)cscore)/nWindPoints) < outlierRatio)
00473                         {
00474                                 outlierRatio = 1.0-((double)cscore)/nWindPoints;
00475                                 nMaxTry = (int)ceil(log(gamma)/log(outlierRatio));
00476                         }
00477 
00478                         if(cscore>bestscore)
00479                         {
00480                                 bestscore = cscore;
00481                                 lnormal = cnormal;
00482                         }
00483                 }
00484                 i = i+1;
00485         }
00486 
00487         return bestscore;
00488 }
00489 
00490 
00491 
00492 
00493 void LFLineFitter::Find(int x0,int y0,CvPoint *windPoints,int &nWindPoints,IplImage *inputImage,int localWindSize)
00494 {
00495         int x,y;
00496         nWindPoints = 0;
00497 
00498         for(y=max(y0-localWindSize,0);y<min(y0+localWindSize,inputImage->height);y++)
00499                 for(x=max(x0-localWindSize,0);x<min(x0+localWindSize,inputImage->width);x++)
00500                 {
00501                         if(cvGetReal2D(inputImage,y,x)!=0)
00502                         {
00503                                 windPoints[nWindPoints].x = x - x0;
00504                                 windPoints[nWindPoints].y = y - y0;
00505                                 nWindPoints++;
00506                         }
00507                 }
00508 }
00509 
00510 
00511 void LFLineFitter::Find(map<int,CvPoint> *edgeMap,int x0,int y0,CvPoint *windPoints,int &nWindPoints,IplImage *inputImage,int localWindSize)
00512 {
00513         nWindPoints = 0;
00514         map<int,CvPoint>::iterator it;
00515 
00516         int left = max(x0-localWindSize,0);
00517         int right = min(x0+localWindSize,inputImage->width);
00518         int top = max(y0-localWindSize,0);
00519         int bottom = min(y0+localWindSize,inputImage->height);
00520 
00521         for(it = edgeMap->begin();it!=edgeMap->end();++it)
00522         {
00523                 if( it->second.x > left && it->second.x < right && 
00524                         it->second.y > top && it->second.y < bottom )
00525                 {
00526                         windPoints[nWindPoints].x = it->second.x - x0;
00527                         windPoints[nWindPoints].y = it->second.y - y0;
00528                         nWindPoints++;
00529                 }
00530         }
00531 }
00532 
00533 
00534 int LFLineFitter::SampleAPixel(map<int,CvPoint> *edgeMap,IplImage *inputImage,int nPixels)
00535 {
00536         int index = (int)(floor(rand()/(double)RAND_MAX*(double)(edgeMap->size()-1)));
00537         map<int,CvPoint>::iterator it;
00538         it=edgeMap->begin();
00539         for(int i=0;i<index;i++)
00540                 it++;
00541         return (*it).first;
00542 }
00543 
00544 void LFLineFitter::ISort(double* ra, int nVec, int* ira)
00545 {
00546    unsigned long n, l, ir, i, j;
00547    n = nVec;
00548    double rra;
00549    int irra;
00550    
00551    if (n<2)
00552       return;
00553    l = (n>>1)+1;
00554    ir = n;
00555    for (;;)
00556    {
00557       if (l>1)
00558       {
00559          irra = ira[(--l)-1];
00560          rra = ra[l-1];
00561       }
00562       else
00563       {
00564          irra = ira[ir-1];
00565          rra = ra[ir-1];
00566 
00567          ira[ir-1] = ira[1-1];
00568          ra[ir-1] = ra[1-1];
00569 
00570          if (--ir==1)
00571          {
00572             ira[1-1] = irra;
00573             ra[1-1] = rra;
00574             break;
00575          }
00576       }
00577       i = l;
00578       j = l+l;
00579       while (j<=ir)
00580       {
00581          if (j<ir && ra[j-1]<ra[j+1-1])
00582             j++;
00583          if (rra<ra[j-1])
00584          {
00585             ira[i-1] = ira[j-1];
00586             ra[i-1] = ra[j-1];
00587 
00588             i = j;
00589             j <<= 1;
00590          }
00591          else
00592             j = ir+1;
00593       }
00594       ira[i-1] = irra;
00595       ra[i-1] = rra;
00596    }
00597 
00598 }
Generated on Sun May 8 08:41:09 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3