Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

BrickOps.cc

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

DualCoding 5.1CVS
Generated Mon May 9 04:56:25 2016 by Doxygen 1.6.3