00001
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <assert.h>
00033
00034 #include "FindN8Lines.h"
00035 #include <vigra/copyimage.hxx>
00036 #include <vigra/pixelneighborhood.hxx>
00037
00038 using namespace vigra;
00039 using namespace std;
00040
00041 namespace HuginLines
00042 {
00043
00044 #define N8_bg 255
00045 #define N8_end 1
00046 #define N8_mid 96
00047
00048 BImage edgeMap2linePts(BImage & input)
00049 {
00050 BImage edge(input.width(),input.height(),N8_bg);
00051 BImage line(input.size());
00052
00053 vigra::copyImage(input.upperLeft()+Diff2D(1,1),input.lowerRight()-Diff2D(1,1),input.accessor(),edge.upperLeft(),edge.accessor());
00054
00055 int width = input.width(),
00056 height = input.height();
00057
00058 BImage::traverser eul, lul;
00059
00060
00061
00062
00063
00064
00065
00066 eul = edge.upperLeft() + Diff2D( 1, 1 );
00067 for(int y=1; y<height-1; ++y, ++eul.y )
00068 {
00069 BImage::traverser ix = eul;
00070 for(int x=1; x<width-1; ++x, ++ix.x )
00071 {
00072
00073 if( *ix != N8_bg )
00074 {
00075 int n = 0, z1 = 0, z2 = 0;
00076 int nh = 0, nv = 0, nu = 0, nd = 0;
00077 if( ix( 1, 0 ) != N8_bg ) ++nh, ++n;
00078 if( ix( 1, -1 ) != N8_bg ) ++nu, ++n;
00079 if( ix( 0, -1 ) != N8_bg ) ++nv, ++n;
00080 if( ix( -1, -1 ) != N8_bg ) ++nd, ++n;
00081 if( ix( -1, 0 ) != N8_bg ) ++nh, ++n;
00082 if( ix( -1, 1 ) != N8_bg ) ++nu, ++n;
00083 if( ix( 0, 1 ) != N8_bg ) ++nv, ++n;
00084 if( ix( 1, 1 ) != N8_bg ) ++nd, ++n;
00085
00086 if( nh == 1 && nv == 1 && n > 1 && n < 5 )
00087 {
00088 *ix = N8_bg;
00089 }
00090 }
00091 }
00092 }
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 eul = (edge.upperLeft() + Diff2D(1,1));
00104 lul = (line.upperLeft() + Diff2D(1,1));
00105 line.init(N8_bg);
00106
00107 for(int y=1; y<height-1; ++y, ++eul.y, ++lul.y)
00108 {
00109 BImage::traverser ix = eul;
00110 BImage::traverser ox = lul;
00111 for(int x=1; x<width-1; ++x, ++ix.x, ++ox.x)
00112 {
00113 if( *ix != N8_bg )
00114 {
00115 int n = 0;
00116 int nh = 0, nv = 0, nu = 0, nd = 0;
00117
00118 if( ix( 1, 0 ) != N8_bg ) ++nh, ++n;
00119 if( ix( 1, -1 ) != N8_bg ) ++nu, ++n;
00120 if( ix( 0, -1 ) != N8_bg ) ++nv, ++n;
00121 if( ix( -1, -1 ) != N8_bg ) ++nd, ++n;
00122 if( ix( -1, 0 ) != N8_bg ) ++nh, ++n;
00123 if( ix( -1, 1 ) != N8_bg ) ++nu, ++n;
00124 if( ix( 0, 1 ) != N8_bg ) ++nv, ++n;
00125 if( ix( 1, 1 ) != N8_bg ) ++nd, ++n;
00126
00127
00128 int code = 0;
00129 if( n == 1 )
00130 {
00131 if( nh ) code = 1;
00132 else if( nu ) code = 3;
00133 else if( nv ) code = 5;
00134 else code = 7;
00135 }
00136 else
00137 if( n == 2 )
00138 {
00139 if( nh == 2 ) code = 1;
00140 else if( nu == 2 ) code = 3;
00141 else if( nv == 2 ) code = 5;
00142 else if( nd == 2 ) code = 7;
00143 else if( nh && nu ) code = 2;
00144 else if( nh && nd ) code = 8;
00145 else if( nv && nu ) code = 4;
00146 else if( nv && nd ) code = 6;
00147 }
00148 assert( code < 9 );
00149 *ox = code ? code : N8_bg;
00150 }
00151 }
00152 }
00153
00154
00155
00156
00157
00158 lul = (line.upperLeft() + Diff2D(1,1));
00159
00160 for(int y=1; y<height-1; ++y, ++lul.y)
00161 {
00162 BImage::traverser ix = lul;
00163 for(int x=1; x<width-1; ++x, ++ix.x )
00164 {
00165 int c = *ix;
00166 if( c != N8_bg )
00167 {
00168 assert( c > 0 && c < 9 );
00169 int n = 0, omin = 9, omax = 0;
00170 NeighborhoodCirculator<BImage::traverser, EightNeighborCode>
00171 circulator(ix),
00172 end(circulator);
00173 do {
00174 int nc = *circulator;
00175 if( nc != N8_bg )
00176 {
00177 assert( nc > 0 && nc < 9 );
00178 if( nc < omin ) omin = nc;
00179 if( nc > omax ) omax = nc;
00180 ++n;
00181 }
00182 } while(++circulator != end);
00183
00184 if( n == 2 )
00185 {
00186 int d = omax - omin;
00187 assert( d < 8 );
00188 if( d > 4 )
00189 d = 7 - d;
00190 if ( d > 1 )
00191 *ix = N8_bg;
00192 }
00193 }
00194 }
00195 }
00196
00197
00198
00199
00200 lul = (line.upperLeft() + Diff2D(1,1));
00201
00202 for(int y=1; y<height-1; ++y, ++lul.y)
00203 {
00204 BImage::traverser ix = lul;
00205 for(int x=1; x<width-1; ++x, ++ix.x )
00206 {
00207 int c = *ix;
00208 if( c != N8_bg )
00209 {
00210 int n = 0;
00211 NeighborhoodCirculator<BImage::traverser, EightNeighborCode> circulator(ix);
00212 NeighborhoodCirculator<BImage::traverser, EightNeighborCode> end(circulator);
00213 do {
00214 int nc = *circulator;
00215 if( nc != N8_bg )
00216 {
00217 ++n;
00218 }
00219 } while(++circulator != end);
00220
00221 if( n == 0 ) *ix = N8_bg;
00222 else if( n == 1 ) *ix = 1;
00223 else *ix = 2;
00224 }
00225 }
00226 }
00227
00228
00229
00230
00231
00232 lul = (line.upperLeft() + Diff2D(1,1));
00233 eul = (edge.upperLeft() + Diff2D(1,1));
00234 edge.init(N8_bg);
00235
00236 for(int y=1; y<height-1; ++y, ++lul.y, ++eul.y )
00237 {
00238 BImage::traverser ox = eul;
00239 BImage::traverser ix = lul;
00240 for(int x=1; x<width-1; ++x, ++ix.x, ++ox.x )
00241 {
00242 int c = *ix;
00243 if( c != N8_bg )
00244 {
00245 int n = 0;
00246 NeighborhoodCirculator<BImage::traverser, EightNeighborCode> circulator(ix);
00247 NeighborhoodCirculator<BImage::traverser, EightNeighborCode> end(circulator);
00248 do {
00249 int nc = *circulator;
00250 if( nc != N8_bg )
00251 {
00252 ++n;
00253 if( c == 1 && nc == 1 ) c = 0;
00254 }
00255 } while(++circulator != end);
00256
00257
00258 if( c )
00259 {
00260 if( n == 1 )
00261 {
00262 *ox = N8_end;
00263 }
00264 else
00265 {
00266 *ox = N8_mid;
00267 }
00268 }
00269 }
00270 }
00271 }
00272 return edge;
00273 }
00274
00275
00276 inline float ccdist( int dx, int dy )
00277 {
00278 dx = abs(dx); dy = abs(dy);
00279 return float(max(dx,dy)) + float(0.41421 * min(dx,dy));
00280 }
00281
00282
00283 inline float eudist( int dx, int dy )
00284 {
00285 register float x = dx, y = dy;
00286 return sqrt( x*x + y*y );
00287 }
00288
00289
00290 static double CtoAcc( vector<Point2D> & pts,
00291 int start, int count,
00292 double & C, double & A)
00293 {
00294 int n = (int)pts.size() - start;
00295 if( count > n ) count = n;
00296 if( count < 3 ) return 1;
00297 int xl = pts.at(start).x, yl = pts.at(start).y;
00298 int xr = pts.at(start + count - 1).x,
00299 yr = pts.at(start + count - 1).y;
00300 C = ccdist( xr - xl, yr - yl );
00301 A = 0;
00302 for(int i = start + 1; i < count; i++ )
00303 {
00304 xr = pts.at(i).x; yr = pts.at(i).y;
00305 A += ccdist( xr - xl, yr - yl );
00306 xl = xr; yl = yr;
00307 }
00308 return C/A;
00309 }
00310
00311
00312 static double CtoAeu( vector<Point2D> & pts,
00313 int start, int count,
00314 double & C, double & A)
00315 {
00316 int n = (int)pts.size() - start;
00317 if( count > n ) count = n;
00318 if( count < 3 ) return 1;
00319 int xl = pts.at(start).x, yl = pts.at(start).y;
00320 int xr = pts.at(start + count - 1).x,
00321 yr = pts.at(start + count - 1).y;
00322 C = eudist( xr - xl, yr - yl );
00323 A = 0;
00324 for(int i = start + 1; i < count; i++ )
00325 {
00326 xr = pts.at(i).x; yr = pts.at(i).y;
00327 A += eudist( xr - xl, yr - yl );
00328 xl = xr; yl = yr;
00329 }
00330 return C/A;
00331 }
00332
00333
00334
00335
00336 inline float scurv( Point2D & l, Point2D & m, Point2D & r )
00337 {
00338 return float(m.x - l.x) * float(r.y - l.y)
00339 + float(m.y - l.y) * float(l.x - r.x );
00340 }
00341
00342
00343
00344
00345 inline void vcurv ( Point2D & l, Point2D & m, Point2D & r, float & vx, float & vy )
00346 {
00347 vx = 0.5 * (l.x + r.x) - float( m.x );
00348 vy = 0.5 * (l.y + r.y) - float( m.y );
00349 }
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365 static int lineFilter( vector< Point2D > & pts,
00366 int lmin,
00367 double flpix,
00368 double xcen, double ycen
00369 )
00370 {
00371
00372 const double crcmin = 0.20;
00373
00374 const double cvrmax = 3.0;
00375
00376 double r90 = 1.57 * flpix;
00377
00378 int n = (int)pts.size();
00379
00380 if( n < lmin )
00381 return 0;
00382
00383
00384 double x0 = pts.at(0).x,
00385 y0 = pts.at(0).y;
00386 double dx = pts.at(n-1).x - x0,
00387 dy = pts.at(n-1).y - y0;
00388 double chord = sqrt( dx*dx + dy*dy );
00389 if( chord < 0.6 * n )
00390 return 0;
00391
00392 double ccx = x0 + 0.5 * dx - xcen,
00393 ccy = y0 + 0.5 * dy - ycen;
00394 double ccd = sqrt( ccx*ccx + ccy *ccy );
00395
00396
00397 double acx = pts.at(n/2).x - xcen,
00398 acy = pts.at(n/2).y - ycen;
00399 double acd = sqrt( acx*acx + acy*acy );
00400
00401 double cvx = acx - ccx,
00402 cvy = acy - ccy;
00403 double cvd = sqrt( cvx*cvx + cvy*cvy );
00404
00405 double cosrc = fabs( acx * dy - acy * dx ) / (chord * acd);
00406 if( fabs(cosrc) < crcmin )
00407 return -1;
00408
00409
00410
00411 double S = 1.2 * chord * cosrc / r90;
00412 double C = r90 * (1 - sqrt( 1 - S * S ));
00413
00414 if( cvd / C > cvrmax )
00415 return -2;
00416
00417
00418 return n;
00419 }
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445 int linePts2lineList( BImage & img, int minsize, double flpix, Lines& lines)
00446 {
00447 int nadd = 0, nrejL = 0;
00448
00449 static Diff2D offs[8] = {
00450 Diff2D( 1, 0 ),
00451 Diff2D( 1, -1 ),
00452 Diff2D( 0, -1 ),
00453 Diff2D( -1, -1 ),
00454 Diff2D( -1, 0 ),
00455 Diff2D( -1, 1 ),
00456 Diff2D( 0, 1 ),
00457 Diff2D( 1, 1 )
00458 };
00459
00460
00461 const int span = 10;
00462 const float maxacd = 1.4;
00463
00464 if(minsize < span) minsize = span;
00465
00466 int width = img.width(),
00467 height = img.height();
00468 double xcen = 0.5 * width,
00469 ycen = 0.5 * height;
00470
00471
00472 BImage::traverser ul(img.upperLeft() + Diff2D( 1, 1 ));
00473 for(int y=1; y<height-1; ++y, ++ul.y)
00474 {
00475 BImage::traverser ix = ul;
00476 for(int x=1; x<width-1; ++x, ++ix.x )
00477 {
00478 int cd = *ix;
00479 if( cd == N8_end )
00480 {
00481
00482 BImage::traverser tr = ix;
00483 Point2D pos( x, y );
00484 vector<Point2D> pts;
00485 pts.push_back(pos);
00486 *tr = N8_bg;
00487 int dir;
00488 Diff2D dif;
00489 do {
00490 for( dir = 0; dir < 8; dir++ )
00491 {
00492 dif = offs[dir];
00493 if( tr[dif] != N8_bg ) break;
00494 }
00495 assert( dir < 8 );
00496 tr += dif;
00497 cd = *tr;
00498 *tr = N8_bg;
00499 pos += dif;
00500 pts.push_back(pos);
00501 } while( cd != N8_end );
00502
00503 bool ok = true;
00504 if( pts.size() < minsize )
00505 {
00506 ok = false;
00507 ++nrejL;
00508 }
00509 else
00510 if( flpix > 0 )
00511 {
00512 int ncopy = 0, nskip = 0;
00513
00514
00515
00516
00517
00518 int ip = 0, np = (int)pts.size();
00519
00520 vector<Point2D> tmp;
00521 float ccd[32];
00522 int isql = 0, isqr = 0;
00523 int xl = pts.at( 0 ).x,
00524 yl = pts.at( 0 ).y;
00525 float dx = pts.at(span-1).x - xl,
00526 dy = pts.at(span-1).y - yl;
00527 float Chord = sqrt(dx*dx + dy*dy);
00528 float Arc = 0;
00529
00530 for( isqr = 0; isqr < span-1; isqr++ )
00531 {
00532 register int x = pts.at( isqr + 1).x,
00533 y = pts.at( isqr + 1 ).y;
00534 float d = ccdist( x - xl, y - yl );
00535 ccd[isqr] = d;
00536 Arc += d;
00537 xl = x; yl = y;
00538 }
00539
00540 if( Arc - Chord <= maxacd )
00541 {
00542 for( int i = 0; i < span; i++ )
00543 {
00544 tmp.push_back(Point2D( pts.at(i).x, pts.at(i).y ));
00545 }
00546 }
00547
00548
00549 for( ip = 1; ip < np - span; ip++ )
00550 {
00551 int irgt = ip + span - 1;
00552
00553 Arc -= ccd[isql];
00554 isql = (++isql)&31;
00555 isqr = (++isqr)&31;
00556 int x = pts.at( irgt ).x,
00557 y = pts.at( irgt ).y;
00558 float d = ccdist( x - xl, y - yl );
00559 ccd[isqr] = d;
00560 Arc += d;
00561 xl = x; yl = y;
00562 dx = x - pts.at(ip).x; dy = y - pts.at(ip).y;
00563 Chord = sqrt( dx*dx + dy*dy );
00564
00565
00566
00567 if( Arc - Chord <= maxacd )
00568 {
00569 ++ncopy;
00570 tmp.push_back(Point2D( pts.at(irgt).x, pts.at(irgt).y ));
00571 }
00572 else
00573 {
00574 int q = lineFilter( tmp, minsize, flpix, xcen, ycen );
00575 SingleLine line;
00576 line.line=tmp;
00577 if( q > 0 )
00578 {
00579 ++nadd;
00580 line.status=valid_line;
00581 }
00582 else if( q == 0 ) line.status=bad_length;
00583 else if( q == -1 ) line.status=bad_orientation;
00584 else if( q == -2 ) line.status=bad_curvature;
00585 lines.push_back(line);
00586 tmp.clear();
00587 ++nskip;
00588 }
00589 }
00590
00591 int q = lineFilter( tmp, minsize, flpix, xcen, ycen );
00592 SingleLine line;
00593 line.line=tmp;
00594 if( q > 0 )
00595 {
00596 ++nadd;
00597 line.status=valid_line;
00598 }
00599 else if( q == 0 ) line.status=bad_length;
00600 else if( q == -1 ) line.status=bad_orientation;
00601 else if( q == -2 ) line.status=bad_curvature;
00602 lines.push_back(line);
00603 }
00604 else
00605 {
00606 SingleLine line;
00607 line.line=pts;
00608 line.status=valid_line;
00609 lines.push_back(line);
00610 ++nadd;
00611 }
00612 }
00613 }
00614 }
00615
00616 return nadd;
00617 }
00618
00619 };