Homepage Demos Overview Downloads Tutorials Reference
Credits

BrickOps.cc

Go to the documentation of this file.
00001 #include "BrickOps.h"
00002 #include "visops.h"
00003 
00004 namespace DualCoding {
00005 
00006   /* Begin functionality for distance-from-center methods of finding the corners of a blob*/
00007 
00008 
00009   /* Find the point at which the given line hits the edge of the rendered region */
00010   Point findEdgePoint(Point start, Point end, Sketch<bool>& rendering) 
00011   {
00012     int gap_tolerance=2;
00013 
00014     float startx = start.coordX();
00015     float starty = start.coordY();
00016     float dx = end.coordX() - startx;
00017     float dy = end.coordY() - starty;
00018 
00019     float dist = sqrt(dx*dx + dy*dy);
00020     dx = dx/dist;
00021     dy = dy/dist;
00022     int maxi=0, gap=0;
00023     int x,y;
00024     for (int i=0; i<dist; i++) {
00025       x = (int)(startx+i*dx);
00026       y = (int)(starty+i*dy);
00027       if (rendering(x,y)) {
00028   maxi = i;
00029   gap = 0;
00030       }
00031       else 
00032   gap++;
00033       if (gap>gap_tolerance)
00034   break;
00035     }
00036     float fx = startx+maxi*dx;
00037     float fy = starty+maxi*dy;
00038     Point result(fx,fy);
00039     return result;
00040   }
00041 
00042 
00043   /* 
00044    * Finds the edge points of the region by drawing lines out from the center to the edges
00045    * Points and distances returned are indexed by angle, so some overlap is possible. 
00046    * The return value is the index of the most distant point. 
00047    */
00048   int findRadialDistancesFromPoint(Point center, float radius, 
00049                Sketch<bool>& rendering,
00050                float distances[],
00051                Point points[])
00052   {
00053     NEW_SKETCH(circle, bool, visops::zeros(rendering));
00054 
00055     int maxi = 0, origmaxi = 0;
00056     float max = -1;
00057     bool stillmax = false;
00058     Point edge;
00059     for (int i=0; i<2*M_PI*radius; i++){
00060       float x = center.coordX()+radius*cos(i/radius);
00061       float y = center.coordY()+radius*sin(i/radius);
00062       if (x<0) x = 0; 
00063       else if (x>=rendering->getWidth()) x = rendering->getWidth()-1;
00064       if (y<0) y = 0;
00065       else if (y>=rendering->getHeight()) y = rendering->getHeight()-1;
00066       edge.setCoords(x,y,0);
00067       circle->at((int)(edge.coordX()), (int)(edge.coordY())) = 1;
00068       points[i] = findEdgePoint(center,edge, rendering);
00069       distances[i] = points[i].distanceFrom(center);
00070       if (distances[i] > max) {
00071   max = distances[i];
00072   maxi = i;
00073   origmaxi = i;
00074   stillmax = true;
00075       }
00076       else if (distances[i] == max && stillmax) {
00077   maxi = (origmaxi + i) / 2;
00078       }
00079       else {
00080   stillmax = false;
00081       }
00082       
00083     }
00084 
00085     return maxi;
00086   }
00087 
00088 
00089   /*
00090    * Takes the derivative of the x array, returned in dx
00091    */
00092   void takeDerivative(float x[], float dx[], int len) 
00093   {
00094     for (int i=0; i<len; i++) {
00095       dx[i] = x[(i+1)%len]+x[(i+2)%len]-x[(i-1+len)%len]-x[(i-2+len)%len];
00096     }
00097   }
00098 
00099   // Applies a "gaussian" to x, returned in gx
00100   // very lazy implementation
00101   // "gaussian" == averaging
00102   void applyGaussian(float x[], float gx[], int len)
00103   {
00104     int smooth_n = 5;
00105     float smooth_x = .2;
00106     for (int i=0; i<len; i++) {
00107       gx[i] = 0;
00108       for (int j=0; j<smooth_n; j++) {
00109   gx[i]+=x[(i+len+j-smooth_n/2)%len]*smooth_x;
00110       }
00111     }
00112   }
00113 
00114 
00115   /*
00116    * Draws a histogram of the array, to be viewed like a normal sketch. 
00117    * Purely for debugging / presentation output. 
00118    */
00119   void drawHist(float distances[], unsigned int len, Sketch<bool>& parent) 
00120   {
00121     float xscale = 1;
00122     if (len > parent->getWidth() - 20) xscale = (parent->getWidth()-20)/(1.0*len);
00123     float ymax = 0;
00124     for (unsigned int i=0; i<len; i++)
00125       ymax = max(ymax,abs(distances[i]));
00126     int const yscale = 2;
00127     ymax *= yscale; // scale up the histogram
00128     int const maxheight = 70;
00129     float const yshrink =  (ymax > maxheight) ? maxheight/ymax : 1.0;
00130     // Draw a histogram
00131     NEW_SKETCH(hist,bool, visops::zeros(parent));
00132     for(unsigned int i=0; i<len; i++) {
00133       if (distances[i] > 0) {
00134   for (unsigned int j=0; j<yscale*distances[i]; j++) {
00135     hist->at((int)(i*xscale+10), (int)(maxheight - j*yshrink + 5)) = 1;   
00136   }
00137       }
00138       else {
00139   for (int j=-1; j>yscale*distances[i]; j--) {
00140     hist->at((int)(i*xscale+10), (int)(maxheight - j*yshrink + 5)) = 1;   
00141   }
00142       }
00143     }
00144 
00145     hist->at(5,(int)(maxheight*(1-yshrink)+5)) = 1;
00146     hist->at(5,(int)(maxheight*(1+yshrink)+5)) = 1;
00147     hist->at(203,(int)(maxheight*(1-yshrink)+5)) = 1;
00148     hist->at(203,(int)(maxheight*(1+yshrink)+5)) = 1;
00149 
00150   }
00151 
00152 
00153   /* Helper functions for finding corners by fitting a quadrilateral to a blob */
00154 
00155 
00156   /*
00157    * Gets the score of the given set of corners as a fit to the blob. 
00158    * 
00159    * The score is a combination of the area of the quadrilateral and the number of edge pixels
00160    * that its lines lie on. 
00161    *
00162    * A lower score is better, scores can go negative
00163    */
00164   float getBoundingQuadrilateralScore(BlobData &blob, vector<Point>& corners, 
00165               Sketch<bool> edgeImage, 
00166               int& borderCount, ShapeSpace &space)
00167   {
00168     const float EDGE_SCALAR = 50;
00169 
00170     borderCount = countBorderPixelFit(blob, corners, edgeImage, space);
00171     return getQuadrilateralArea(corners) - EDGE_SCALAR*borderCount;
00172   }
00173 
00174 
00175 
00176   /*
00177    * assuming a convex quadrilateral
00178    * splits the quadrilateral into n-2 triangles and uses the
00179    * edge-length method to find the area of each triangle:
00180    * s = perimeter / 2
00181    * area = sqrt(s*(s-a)(s-b)(s-c))
00182    */
00183   float getQuadrilateralArea(vector<Point>& corners) 
00184   {
00185     float totalArea = 0;
00186     for (unsigned int i=2; i<corners.size(); i++) {
00187       float e1 = corners[0].distanceFrom(corners[i-1]);
00188       float e2 = corners[i-1].distanceFrom(corners[i]);
00189       float diag = corners[0].distanceFrom(corners[i]);
00190       float s1 = (e1+e2+diag)/2.0;
00191       totalArea += sqrt(s1*(s1-e1)*(s1-e2)*(s1-diag));
00192     }
00193     
00194     return totalArea;
00195   }
00196 
00197   
00198   /*
00199    * Computes the percentage of the blob points that are inside the quadrilateral
00200    *
00201    * A point is inside if it is on the same side of each line as one of the opposite corners
00202    * This is assuming a convex quadrilateral
00203    */
00204   float getBoundingQuadrilateralInteriorPointRatio(BlobData &blob, 
00205                vector<Point>& corners, 
00206                ShapeSpace &space)
00207   {
00208     int ncorners = corners.size();
00209 
00210     vector<LineData> lines;
00211     for (int i=0; i<ncorners; i++) {
00212       lines.push_back(LineData(space,corners[(i+1)%ncorners], corners[(i+2)%ncorners]));
00213     }
00214 
00215     int totalInside = 0;
00216     int totalPixelArea = 0;
00217 
00218     for ( std::vector<BlobData::run>::const_iterator it = blob.runvec.begin();
00219     it != blob.runvec.end(); it++ ) {
00220       const BlobData::run &r = *it;
00221       int const xstop = r.x + r.width;
00222 
00223       totalPixelArea += r.width;
00224 
00225       Point p1(r.x,r.y);
00226       Point p2(xstop, r.y);
00227       
00228       // Check if the endpoints are inside the polygon
00229       bool pt1Inside = true, pt2Inside = true;
00230       for (int i=0; i<ncorners; i++) {
00231   if (!lines[i].pointsOnSameSide(p1,corners[i])) {
00232     pt1Inside = false;
00233     break;
00234   }
00235       }
00236       for (int i=0; i<ncorners; i++) {
00237   if (!lines[i].pointsOnSameSide(p2,corners[i])) {
00238     pt2Inside = false;
00239     break;
00240   }
00241       }
00242 
00243       // if the whole run is inside, we can just add the whole run and move on
00244       if (pt1Inside) {
00245   if (pt2Inside) {
00246     totalInside += r.width;
00247   }
00248   else {
00249     // If one end of the run is inside the quadrilateral, walk along it until you enter
00250     // This could be made faster with a binary search
00251     // It could also be made faster by just checking the lines that the other endpoint was outside of
00252     totalInside++;
00253     for (int x=xstop-1; x>r.x; x--) {
00254       Point p(x,r.y);
00255       bool ptInside = true;
00256       for (int i=0; i<ncorners; i++) {
00257         if (!lines[i].pointsOnSameSide(p,corners[i])) {
00258     ptInside = false;
00259     break;
00260         }
00261       }
00262       if (ptInside) {
00263         totalInside+=x-r.x;
00264         break;
00265       }
00266     }
00267   }
00268       }
00269       else {
00270   // Same case as above, with the other endpoint
00271   if (pt2Inside) {
00272     totalInside++;
00273     for (int x=r.x+1; x<xstop; x++) {
00274       Point p(x,r.y);
00275       bool ptInside = true;
00276       for (int i=0; i<ncorners; i++) {
00277         if (!lines[i].pointsOnSameSide(p,corners[i])) {
00278     ptInside = false;
00279     break;
00280         }
00281       }
00282       if (ptInside) {
00283         totalInside+=xstop-x;
00284         break;
00285       }
00286     }
00287   }
00288   else {
00289     // If both endpoints are outside, we still need to check the whole run.
00290     bool beenInside = false;
00291     int xstart = xstop;
00292     for (int x=r.x+1; x<xstop; x++) {
00293       Point p(x,r.y);
00294       bool ptInside = true;
00295       for (int i=0; i<ncorners; i++) {
00296         if (!lines[i].pointsOnSameSide(p,corners[i])) {
00297     ptInside = false;
00298     break;
00299         }
00300       }
00301       if (ptInside) {
00302         xstart = x;
00303         beenInside = true;
00304         break;
00305       }
00306     }
00307     if (beenInside) {
00308       for (int x=xstop-1; x>=xstart; x--) {
00309         Point p(x,r.y);
00310         bool ptInside = true;
00311         for (int i=0; i<ncorners; i++) {
00312     if (!lines[i].pointsOnSameSide(p,corners[i])) {
00313       ptInside = false;
00314       break;
00315     }
00316         }
00317         if (ptInside) {
00318     totalInside+=x-xstart+1;
00319     break;
00320         }
00321       }
00322     }
00323   }
00324       }
00325   
00326     }
00327   
00328     
00329     return totalInside*1.0/totalPixelArea;
00330   }
00331 
00332 
00333   /*
00334    * Counts the number of border pixels of the blob that lie udner one of the lines
00335    */
00336   int countBorderPixelFit(BlobData &blob, const vector<Point> &corners, 
00337         Sketch<bool> edges, ShapeSpace &space)
00338   {
00339     int ncorners = corners.size();
00340 
00341     vector<LineData> lines;
00342     vector<float> dx, dy;
00343     for (int i=0; i<ncorners; i++) {
00344       lines.push_back(LineData(space, corners[(i+1)%ncorners], corners[(i+2)%ncorners]));
00345     }
00346 
00347     int count = 0;
00348 
00349     for (int x=max((int)blob.topLeft.coordX()-5,0); x<=min((int)blob.topRight.coordX()+5,edges.width); x++) {
00350       for (int y=max((int)blob.topLeft.coordY()-5,0); y<=min((int)blob.bottomRight.coordY()+5,edges.height); y++) {
00351   if (edges(x,y)) {
00352     Point p(x,y);
00353     bool onLine = false;
00354     for (int i=0; i<ncorners; i++) {
00355       if (lines[i].pointOnLine(p)) {
00356         onLine = true;
00357         break;
00358       }
00359     }
00360     if (onLine)
00361       count++;
00362   }
00363       }
00364     }
00365     
00366     return count;
00367   }
00368 
00369 
00370   /*
00371    * Probabilistically pick the next move based on the scores
00372    * Lower is better for the score
00373    */
00374   int pickMove(vector<float> scores, float weight) 
00375   {
00376     unsigned int i;
00377     float min = scores[0], max = scores[0];
00378     for (i=1; i<scores.size(); i++) {
00379       if (scores[i] < min)
00380   min = scores[i];
00381       else if (scores[i] > max)
00382   max = scores[i];
00383     }
00384     
00385     max -= min;
00386     for (i=0; i<scores.size(); i++) {
00387       scores[i]-=min;
00388       if (max > 0) {
00389   scores[i]*=weight/max;
00390       }
00391     }
00392 
00393     vector<float> exps;
00394     float expsum = 0;
00395     for (i=0; i<scores.size(); i++) {
00396       float curexp = exp(-scores[i]);
00397       exps.push_back(curexp);
00398       expsum+=curexp;
00399     }
00400 
00401     for (i=0; i<scores.size(); i++) {
00402       exps[i]/=expsum;
00403     }    
00404 
00405     float randval = (rand()%1000000)/1000000.0;
00406 
00407     for (i=0; i<scores.size(); i++) {
00408       randval -= exps[i];
00409       if (randval <= 0)
00410   return i;
00411     }
00412 
00413     return -1;
00414   }
00415 
00416 
00417 } // namespace
00418 

DualCoding 3.0beta
Generated Wed Oct 4 00:01:53 2006 by Doxygen 1.4.7