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 }