Generated on Fri Dec 3 10:18:48 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/functions_vars.html
================================================
LSD: Compound Member Index
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/globals.html
================================================
LSD: File Member Index
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/globals_defs.html
================================================
LSD: File Member Index
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/globals_func.html
================================================
LSD: File Member Index
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/globals_type.html
================================================
LSD: File Member Index
This page explains how to interpret the graphs that are generated by doxygen.
Consider the following example:
/*! Invisible class because of truncation */class Invisible { };
/*! Truncated class, inheritance relation is hidden */class Truncated : publicInvisible { };
/* Class not documented with doxygen comments */class Undocumented { };
/*! Class that is inherited using public inheritance */class PublicBase : publicTruncated { };
/*! A template class */template<class T> class Templ { };
/*! Class that is inherited using protected inheritance */class ProtectedBase { };
/*! Class that is inherited using private inheritance */class PrivateBase { };
/*! Class that is used by the Inherited class */class Used { };
/*! Super class that inherits a number of other classes */class Inherited : publicPublicBase,
protectedProtectedBase,
privatePrivateBase,
publicUndocumentedpublicTempl<int>
{
private:
Used *m_usedClass;
};
If the MAX_DOT_GRAPH_HEIGHT tag in the configuration file is set to 240 this will result in the following graph:
The boxes in the above graph have the following meaning:
A filled black box represents the struct or class for which the graph is generated.
A box with a black border denotes a documented struct or class.
A box with a grey border denotes an undocumented struct or class.
A box with a red border denotes a documented struct or class forwhich not all inheritance/containment relations are shown. A graph is truncated if it does not fit within the specified boundaries.
The arrows have the following meaning:
A dark blue arrow is used to visualize a public inheritance relation between two classes.
A dark green arrow is used for protected inheritance.
A dark red arrow is used for private inheritance.
A purple dashed arrow is used if a class is contained or used by another class. The arrow is labeled with the variable(s) through which the pointed class or struct is accessible.
A yellow dashed arrow denotes a relation between a template instance and the template class it was instantiated from. The arrow is labeled with the template parameters of the instance.
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/index.html
================================================
LSD: LSD code documentation
This is an implementation of the Line Segment Detector described in the paper:
"LSD: A Fast Line Segment Detector with a False Detection Control" by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall, IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010.
and in more details in the CMLA Technical Report:
"LSD: A Line Segment Detector, Technical Report", by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel, Gregory Randall, CMLA, ENS Cachan, 2010.
The version implemented here includes some further improvements described on the LSD page at www.ipol.im. That same page includes more information, including this code and an online demo version:
Generated on Fri Dec 3 10:18:17 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/lsd_8c-source.html
================================================
LSD: lsd.c Source File
00001 /*----------------------------------------------------------------------------
00002
00003 LSD - Line Segment Detector on digital images
00004
00005 Copyright 2007-2010 rafael grompone von gioi (grompone@gmail.com)
00006
00007 This program is free software: you can redistribute it and/or modify
00008 it under the terms of the GNU Affero General Public License as
00009 published by the Free Software Foundation, either version 3 of the
00010 License, or (at your option) any later version.
00011
00012 This program is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015 GNU Affero General Public License for more details.
00016
00017 You should have received a copy of the GNU Affero General Public License
00018 along with this program. If not, see <http://www.gnu.org/licenses/>.
00019
00020 ----------------------------------------------------------------------------*/
00021
00022 /*----------------------------------------------------------------------------*/
00023 /** @file lsd.c
00024 LSD module code
00025 @author rafael grompone von gioi (grompone@gmail.com)
00026 */
00027 /*----------------------------------------------------------------------------*/
00028
00029 /*----------------------------------------------------------------------------*/
00030 /** @mainpage LSD code documentation
00031
00032 This is an implementation of the Line Segment Detector described
00033 in the paper:
00034
00035 "LSD: A Fast Line Segment Detector with a False Detection Control"
00036 by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
00037 and Gregory Randall, IEEE Transactions on Pattern Analysis and
00038 Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010.
00039
00040 and in more details in the CMLA Technical Report:
00041
00042 "LSD: A Line Segment Detector, Technical Report",
00043 by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
00044 Gregory Randall, CMLA, ENS Cachan, 2010.
00045
00046 The version implemented here includes some further improvements
00047 described on the LSD page at www.ipol.im. That same page includes
00048 more information, including this code and an online demo version:
00049
00050 http://www.ipol.im/pub/algo/gjmr_line_segment_detector
00051
00052 The module's main function is lsd().
00053
00054 The source code is contained in two files: lsd.h and lsd.c.
00055
00056 HISTORY:
00057 - version 1.5 - dic 2010: Changes in 'refine', -W option added,
00058 and more comments added.
00059 - version 1.4 - jul 2010: lsd_scale interface added and doxygen doc.
00060 - version 1.3 - feb 2010: Multiple bug correction and improved code.
00061 - version 1.2 - dic 2009: First full Ansi C Language version.
00062 - version 1.1 - sep 2009: Systematic subsampling to scale 0.8 and
00063 correction to partially handle"angle problem".
00064 - version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language
00065 version.
00066
00067 @author rafael grompone von gioi (grompone@gmail.com)
00068 */
00069 /*----------------------------------------------------------------------------*/
00070
00071 #include <stdio.h>
00072 #include <stdlib.h>
00073 #include <math.h>
00074 #include <limits.h>
00075 #include <float.h>
00076 #include "lsd.h"
00077
00078 /** ln(10) */
00079 #ifndef M_LN1000080#define M_LN10 2.30258509299404568402
00081 #endif /* !M_LN10 */
00082
00083 /** PI */
00084 #ifndef M_PI00085#define M_PI 3.14159265358979323846
00086 #endif /* !M_PI */
00087
00088 #ifndef FALSE00089#define FALSE 0
00090 #endif /* !FALSE */
00091
00092 #ifndef TRUE00093#define TRUE 1
00094 #endif /* !TRUE */
00095
00096 /** Label for pixels with undefined gradient. */00097#define NOTDEF -1024.0
00098
00099 /** 3/2 pi */00100#define M_3_2_PI 4.71238898038
00101
00102 /** 2 pi */00103#define M_2__PI 6.28318530718
00104
00105 /** Label for pixels not used in yet. */00106#define NOTUSED 0
00107
00108 /** Label for pixels already used in detection. */00109#define USED 1
00110
00111 /*----------------------------------------------------------------------------*/
00112 /** Chained list of coordinates.
00113 */00114struct coorlist
00115 {
00116intx,y;
00117struct coorlist * next;
00118 };
00119
00120 /*----------------------------------------------------------------------------*/
00121 /** A point (or pixel).
00122 */00123struct point {intx,y;};
00124
00125
00126 /*----------------------------------------------------------------------------*/
00127 /*------------------------- Miscellaneous functions --------------------------*/
00128 /*----------------------------------------------------------------------------*/
00129
00130 /*----------------------------------------------------------------------------*/
00131 /** Fatal error, print a message to standard-error output and exit.
00132 */00133staticvoiderror(char * msg)
00134 {
00135 fprintf(stderr,"LSD Error: %s\n",msg);
00136 exit(EXIT_FAILURE);
00137 }
00138
00139 /*----------------------------------------------------------------------------*/
00140 /** Doubles relative error factor
00141 */00142#define RELATIVE_ERROR_FACTOR 100.0
00143
00144 /*----------------------------------------------------------------------------*/
00145 /** Compare doubles by relative error.
00146
00147 The resulting rounding error after floating point computations
00148 depend on the specific operations done. The same number computed by
00149 different algorithms could present different rounding errors. For a
00150 useful comparison, an estimation of the relative rounding error
00151 should be considered and compared to a factor times EPS. The factor
00152 should be related to the cumulated rounding error in the chain of
00153 computation. Here, as a simplification, a fixed factor is used.
00154 */00155staticintdouble_equal(double a, double b)
00156 {
00157 double abs_diff,aa,bb,abs_max;
00158
00159 /* trivial case */
00160 if( a == b ) returnTRUE;
00161
00162 abs_diff = fabs(a-b);
00163 aa = fabs(a);
00164 bb = fabs(b);
00165 abs_max = aa > bb ? aa : bb;
00166
00167 /* DBL_MIN is the smallest normalized number, thus, the smallest
00168 number whose relative error is bounded by DBL_EPSILON. For
00169 smaller numbers, the same quantization steps as for DBL_MIN
00170 are used. Then, for smaller numbers, a meaningful "relative"
00171 error should be computed by dividing the difference by DBL_MIN. */
00172 if( abs_max < DBL_MIN ) abs_max = DBL_MIN;
00173
00174 /* equal if relative error <= factor x eps */
00175 return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
00176 }
00177
00178 /*----------------------------------------------------------------------------*/
00179 /** Computes Euclidean distance between point (x1,y1) and point (x2,y2).
00180 */00181staticdoubledist(double x1, double y1, double x2, double y2)
00182 {
00183 return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
00184 }
00185
00186
00187 /*----------------------------------------------------------------------------*/
00188 /*----------------------- 'list of n-tuple' data type ------------------------*/
00189 /*----------------------------------------------------------------------------*/
00190
00191 /*----------------------------------------------------------------------------*/
00192 /** Free memory used in n-tuple 'in'.
00193 */00194voidfree_ntuple_list(ntuple_list in)
00195 {
00196 if( in == NULL || in->values == NULL )
00197 error("free_ntuple_list: invalid n-tuple input.");
00198 free( (void *) in->values );
00199 free( (void *) in );
00200 }
00201
00202 /*----------------------------------------------------------------------------*/
00203 /** Create an n-tuple list and allocate memory for one element.
00204 @param dim the dimension (n) of the n-tuple.
00205 */00206ntuple_listnew_ntuple_list(unsignedint dim)
00207 {
00208 ntuple_list n_tuple;
00209
00210 /* check parameters */
00211 if( dim == 0 ) error("new_ntuple_list: 'dim' must be positive.");
00212
00213 /* get memory for list structure */
00214 n_tuple = (ntuple_list) malloc( sizeof(structntuple_list_s) );
00215 if( n_tuple == NULL ) error("not enough memory.");
00216
00217 /* initialize list */
00218 n_tuple->size = 0;
00219 n_tuple->max_size = 1;
00220 n_tuple->dim = dim;
00221
00222 /* get memory for tuples */
00223 n_tuple->values = (double *) malloc( dim*n_tuple->max_size * sizeof(double) );
00224 if( n_tuple->values == NULL ) error("not enough memory.");
00225
00226 return n_tuple;
00227 }
00228
00229 /*----------------------------------------------------------------------------*/
00230 /** Enlarge the allocated memory of an n-tuple list.
00231 */00232staticvoidenlarge_ntuple_list(ntuple_list n_tuple)
00233 {
00234 /* check parameters */
00235 if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size == 0 )
00236 error("enlarge_ntuple_list: invalid n-tuple.");
00237
00238 /* duplicate number of tuples */
00239 n_tuple->max_size *= 2;
00240
00241 /* realloc memory */
00242 n_tuple->values = (double *) realloc( (void *) n_tuple->values,
00243 n_tuple->dim * n_tuple->max_size * sizeof(double) );
00244 if( n_tuple->values == NULL ) error("not enough memory.");
00245 }
00246
00247 /*----------------------------------------------------------------------------*/
00248 /** Add a 5-tuple to an n-tuple list.
00249 */00250staticvoidadd_5tuple( ntuple_list out, double v1, double v2,
00251 double v3, double v4, double v5 )
00252 {
00253 /* check parameters */
00254 if( out == NULL ) error("add_5tuple: invalid n-tuple input.");
00255 if( out->dim != 5 ) error("add_5tuple: the n-tuple must be a 5-tuple.");
00256
00257 /* if needed, alloc more tuples to 'out' */
00258 if( out->size == out->max_size ) enlarge_ntuple_list(out);
00259 if( out->values == NULL ) error("add_5tuple: invalid n-tuple input.");
00260
00261 /* add new 5-tuple */
00262 out->values[ out->size * out->dim + 0 ] = v1;
00263 out->values[ out->size * out->dim + 1 ] = v2;
00264 out->values[ out->size * out->dim + 2 ] = v3;
00265 out->values[ out->size * out->dim + 3 ] = v4;
00266 out->values[ out->size * out->dim + 4 ] = v5;
00267
00268 /* update number of tuples counter */
00269 out->size++;
00270 }
00271
00272
00273 /*----------------------------------------------------------------------------*/
00274 /*----------------------------- Image Data Types -----------------------------*/
00275 /*----------------------------------------------------------------------------*/
00276
00277 /*----------------------------------------------------------------------------*/
00278 /** Free memory used in image_char 'i'.
00279 */00280voidfree_image_char(image_char i)
00281 {
00282 if( i == NULL || i->data == NULL )
00283 error("free_image_char: invalid input image.");
00284 free( (void *) i->data );
00285 free( (void *) i );
00286 }
00287
00288 /*----------------------------------------------------------------------------*/
00289 /** Create a new image_char of size 'xsize' times 'ysize'.
00290 */00291image_charnew_image_char(unsignedint xsize, unsignedint ysize)
00292 {
00293 image_char image;
00294
00295 /* check parameters */
00296 if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size.");
00297
00298 /* get memory */
00299 image = (image_char) malloc( sizeof(structimage_char_s) );
00300 if( image == NULL ) error("not enough memory.");
00301 image->data = (unsignedchar *) calloc( (size_t) (xsize*ysize),
00302 sizeof(unsignedchar) );
00303 if( image->data == NULL ) error("not enough memory.");
00304
00305 /* set image size */
00306 image->xsize = xsize;
00307 image->ysize = ysize;
00308
00309 return image;
00310 }
00311
00312 /*----------------------------------------------------------------------------*/
00313 /** Create a new image_char of size 'xsize' times 'ysize',
00314 initialized to the value 'fill_value'.
00315 */00316image_charnew_image_char_ini( unsignedint xsize, unsignedint ysize,
00317 unsignedchar fill_value )
00318 {
00319 image_char image = new_image_char(xsize,ysize); /* create image */
00320 unsignedint N = xsize*ysize;
00321 unsignedint i;
00322
00323 /* check parameters */
00324 if( image == NULL || image->data == NULL )
00325 error("new_image_char_ini: invalid image.");
00326
00327 /* initialize */
00328 for(i=0; i<N; i++) image->data[i] = fill_value;
00329
00330 return image;
00331 }
00332
00333 /*----------------------------------------------------------------------------*/
00334 /** Free memory used in image_int 'i'.
00335 */00336voidfree_image_int(image_int i)
00337 {
00338 if( i == NULL || i->data == NULL )
00339 error("free_image_int: invalid input image.");
00340 free( (void *) i->data );
00341 free( (void *) i );
00342 }
00343
00344 /*----------------------------------------------------------------------------*/
00345 /** Create a new image_int of size 'xsize' times 'ysize'.
00346 */00347image_intnew_image_int(unsignedint xsize, unsignedint ysize)
00348 {
00349 image_int image;
00350
00351 /* check parameters */
00352 if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size.");
00353
00354 /* get memory */
00355 image = (image_int) malloc( sizeof(structimage_int_s) );
00356 if( image == NULL ) error("not enough memory.");
00357 image->data = (int *) calloc( (size_t) (xsize*ysize), sizeof(int) );
00358 if( image->data == NULL ) error("not enough memory.");
00359
00360 /* set image size */
00361 image->xsize = xsize;
00362 image->ysize = ysize;
00363
00364 return image;
00365 }
00366
00367 /*----------------------------------------------------------------------------*/
00368 /** Create a new image_int of size 'xsize' times 'ysize',
00369 initialized to the value 'fill_value'.
00370 */00371image_intnew_image_int_ini( unsignedint xsize, unsignedint ysize,
00372 int fill_value )
00373 {
00374 image_int image = new_image_int(xsize,ysize); /* create image */
00375 unsignedint N = xsize*ysize;
00376 unsignedint i;
00377
00378 /* initialize */
00379 for(i=0; i<N; i++) image->data[i] = fill_value;
00380
00381 return image;
00382 }
00383
00384 /*----------------------------------------------------------------------------*/
00385 /** Free memory used in image_double 'i'.
00386 */00387voidfree_image_double(image_double i)
00388 {
00389 if( i == NULL || i->data == NULL )
00390 error("free_image_double: invalid input image.");
00391 free( (void *) i->data );
00392 free( (void *) i );
00393 }
00394
00395 /*----------------------------------------------------------------------------*/
00396 /** Create a new image_double of size 'xsize' times 'ysize'.
00397 */00398image_doublenew_image_double(unsignedint xsize, unsignedint ysize)
00399 {
00400 image_double image;
00401
00402 /* check parameters */
00403 if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size.");
00404
00405 /* get memory */
00406 image = (image_double) malloc( sizeof(structimage_double_s) );
00407 if( image == NULL ) error("not enough memory.");
00408 image->data = (double *) calloc( (size_t) (xsize*ysize), sizeof(double) );
00409 if( image->data == NULL ) error("not enough memory.");
00410
00411 /* set image size */
00412 image->xsize = xsize;
00413 image->ysize = ysize;
00414
00415 return image;
00416 }
00417
00418 /*----------------------------------------------------------------------------*/
00419 /** Create a new image_double of size 'xsize' times 'ysize',
00420 initialized to the value 'fill_value'.
00421 */00422image_doublenew_image_double_ini( unsignedint xsize, unsignedint ysize,
00423 double fill_value )
00424 {
00425 image_double image = new_image_double(xsize,ysize); /* create image */
00426 unsignedint N = xsize*ysize;
00427 unsignedint i;
00428
00429 /* initialize */
00430 for(i=0; i<N; i++) image->data[i] = fill_value;
00431
00432 return image;
00433 }
00434
00435
00436 /*----------------------------------------------------------------------------*/
00437 /*----------------------------- Gaussian filter ------------------------------*/
00438 /*----------------------------------------------------------------------------*/
00439
00440 /*----------------------------------------------------------------------------*/
00441 /** Compute a Gaussian kernel of length 'kernel->dim',
00442 standard deviation 'sigma', and centered at value 'mean'.
00443
00444 For example, if mean=0.5, the Gaussian will be centered
00445 in the middle point between values 'kernel->values[0]'
00446 and 'kernel->values[1]'.
00447 */00448staticvoidgaussian_kernel(ntuple_list kernel, double sigma, double mean)
00449 {
00450 double sum = 0.0;
00451 double val;
00452 unsignedint i;
00453
00454 /* check parameters */
00455 if( kernel == NULL || kernel->values == NULL )
00456 error("gaussian_kernel: invalid n-tuple 'kernel'.");
00457 if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive.");
00458
00459 /* compute Gaussian kernel */
00460 if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel);
00461 kernel->size = 1;
00462 for(i=0;i<kernel->dim;i++)
00463 {
00464 val = ( (double) i - mean ) / sigma;
00465 kernel->values[i] = exp( -0.5 * val * val );
00466 sum += kernel->values[i];
00467 }
00468
00469 /* normalization */
00470 if( sum >= 0.0 ) for(i=0;i<kernel->dim;i++) kernel->values[i] /= sum;
00471 }
00472
00473 /*----------------------------------------------------------------------------*/
00474 /** Scale the input image 'in' by a factor 'scale' by Gaussian sub-sampling.
00475
00476 For example, scale=0.8 will give a result at 80% of the original size.
00477
00478 The image is convolved with a Gaussian kernel
00479 @f[
00480 G(x,y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}}
00481 @f]
00482 before the sub-sampling to prevent aliasing.
00483
00484 The standard deviation sigma given by:
00485 - sigma = sigma_scale / scale, if scale < 1.0
00486 - sigma = sigma_scale, if scale >= 1.0
00487
00488 To be able to sub-sample at non-integer steps, some interpolation
00489 is needed. In this implementation, the interpolation is done by
00490 the Gaussian kernel, so both operations (filtering and sampling)
00491 are done at the same time. The Gaussian kernel is computed
00492 centered on the coordinates of the required sample. In this way,
00493 when applied, it gives directly the result of convolving the image
00494 with the kernel and interpolated to that particular position.
00495
00496 A fast algorithm is done using the separability of the Gaussian
00497 kernel. Applying the 2D Gaussian kernel is equivalent to applying
00498 first a horizontal 1D Gaussian kernel and then a vertical 1D
00499 Gaussian kernel (or the other way round). The reason is that
00500 @f[
00501 G(x,y) = G(x) * G(y)
00502 @f]
00503 where
00504 @f[
00505 G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{x^2}{2\sigma^2}}.
00506 @f]
00507 The algorithm first apply a combined Gaussian kernel and sampling
00508 in the x axis, and then the combined Gaussian kernel and sampling
00509 in the y axis.
00510 */00511staticimage_doublegaussian_sampler( image_double in, double scale,
00512 double sigma_scale )
00513 {
00514 image_double aux,out;
00515 ntuple_list kernel;
00516 unsignedint N,M,h,n,x,y,i;
00517 int xc,yc,j,double_x_size,double_y_size;
00518 double sigma,xx,yy,sum,prec;
00519
00520 /* check parameters */
00521 if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
00522 error("gaussian_sampler: invalid image.");
00523 if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive.");
00524 if( sigma_scale <= 0.0 )
00525 error("gaussian_sampler: 'sigma_scale' must be positive.");
00526
00527 /* get memory for images */
00528 if( in->xsize * scale > (double) UINT_MAX ||
00529 in->ysize * scale > (double) UINT_MAX )
00530 error("gaussian_sampler: the output image size exceeds the handled size.");
00531 N = (unsignedint) floor( in->xsize * scale );
00532 M = (unsignedint) floor( in->ysize * scale );
00533 aux = new_image_double(N,in->ysize);
00534 out = new_image_double(N,M);
00535
00536 /* sigma, kernel size and memory for the kernel */
00537 sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale;
00538 /*
00539 The size of the kernel is selected to guarantee that the
00540 the first discarded term is at least 10^prec times smaller
00541 than the central value. For that, h should be larger than x, with
00542 e^(-x^2/2sigma^2) = 1/10^prec.
00543 Then,
00544 x = sigma * sqrt( 2 * prec * ln(10) ).
00545 */
00546 prec = 3.0;
00547 h = (unsignedint) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) );
00548 n = 1+2*h; /* kernel size */
00549 kernel = new_ntuple_list(n);
00550
00551 /* auxiliary double image size variables */
00552 double_x_size = (int) (2 * in->xsize);
00553 double_y_size = (int) (2 * in->ysize);
00554
00555 /* First subsampling: x axis */
00556 for(x=0;x<aux->xsize;x++)
00557 {
00558 /*
00559 x is the coordinate in the new image.
00560 xx is the corresponding x-value in the original size image.
00561 xc is the integer value, the pixel coordinate of xx.
00562 */
00563 xx = (double) x / scale;
00564 /* coordinate (0.0,0.0) is in the center of pixel (0,0),
00565 so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */
00566 xc = (int) floor( xx + 0.5 );
00567 gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc );
00568 /* the kernel must be computed for each x because the fine
00569 offset xx-xc is different in each case */
00570
00571 for(y=0;y<aux->ysize;y++)
00572 {
00573 sum = 0.0;
00574 for(i=0;i<kernel->dim;i++)
00575 {
00576 j = xc - h + i;
00577
00578 /* symmetry boundary condition */
00579 while( j < 0 ) j += double_x_size;
00580 while( j >= double_x_size ) j -= double_x_size;
00581 if( j >= (int) in->xsize ) j = double_x_size-1-j;
00582
00583 sum += in->data[ j + y * in->xsize ] * kernel->values[i];
00584 }
00585 aux->data[ x + y * aux->xsize ] = sum;
00586 }
00587 }
00588
00589 /* Second subsampling: y axis */
00590 for(y=0;y<out->ysize;y++)
00591 {
00592 /*
00593 y is the coordinate in the new image.
00594 yy is the corresponding x-value in the original size image.
00595 yc is the integer value, the pixel coordinate of xx.
00596 */
00597 yy = (double) y / scale;
00598 /* coordinate (0.0,0.0) is in the center of pixel (0,0),
00599 so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */
00600 yc = (int) floor( yy + 0.5 );
00601 gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc );
00602 /* the kernel must be computed for each y because the fine
00603 offset yy-yc is different in each case */
00604
00605 for(x=0;x<out->xsize;x++)
00606 {
00607 sum = 0.0;
00608 for(i=0;i<kernel->dim;i++)
00609 {
00610 j = yc - h + i;
00611
00612 /* symmetry boundary condition */
00613 while( j < 0 ) j += double_y_size;
00614 while( j >= double_y_size ) j -= double_y_size;
00615 if( j >= (int) in->ysize ) j = double_y_size-1-j;
00616
00617 sum += aux->data[ x + j * aux->xsize ] * kernel->values[i];
00618 }
00619 out->data[ x + y * out->xsize ] = sum;
00620 }
00621 }
00622
00623 /* free memory */
00624 free_ntuple_list(kernel);
00625 free_image_double(aux);
00626
00627 return out;
00628 }
00629
00630
00631 /*----------------------------------------------------------------------------*/
00632 /*--------------------------------- Gradient ---------------------------------*/
00633 /*----------------------------------------------------------------------------*/
00634
00635 /*----------------------------------------------------------------------------*/
00636 /** Computes the direction of the level line of 'in' at each point.
00637
00638 The result is:
00639 - an image_double with the angle at each pixel, or NOTDEF if not defined.
00640 - the image_double 'modgrad' (a pointer is passed as argument)
00641 with the gradient magnitude at each point.
00642 - a list of pixels 'list_p' roughly ordered by decreasing
00643 gradient magnitude. (The order is made by classifying points
00644 into bins by gradient magnitude. The parameters 'n_bins' and
00645 'max_grad' specify the number of bins and the gradient modulus
00646 at the highest bin. The pixels in the list would be in
00647 decreasing gradient magnitude, up to a precision of the size of
00648 the bins.)
00649 - a pointer 'mem_p' to the memory used by 'list_p' to be able to
00650 free the memory when it is not used anymore.
00651 */00652staticimage_doublell_angle( image_double in, double threshold,
00653 structcoorlist ** list_p, void ** mem_p,
00654 image_double * modgrad, unsignedint n_bins,
00655 double max_grad )
00656 {
00657 image_double g;
00658 unsignedint n,p,x,y,adr,i;
00659 double com1,com2,gx,gy,norm,norm2;
00660 /* the rest of the variables are used for pseudo-ordering
00661 the gradient magnitude values */
00662 int list_count = 0;
00663 struct coorlist * list;
00664 struct coorlist ** range_l_s; /* array of pointers to start of bin list */
00665 struct coorlist ** range_l_e; /* array of pointers to end of bin list */
00666 struct coorlist * start;
00667 struct coorlist * end;
00668
00669 /* check parameters */
00670 if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
00671 error("ll_angle: invalid image.");
00672 if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive.");
00673 if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'.");
00674 if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'.");
00675 if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'.");
00676 if( n_bins == 0 ) error("ll_angle: 'n_bins' must be positive.");
00677 if( max_grad <= 0.0 ) error("ll_angle: 'max_grad' must be positive.");
00678
00679 /* image size shortcuts */
00680 n = in->ysize;
00681 p = in->xsize;
00682
00683 /* allocate output image */
00684 g = new_image_double(in->xsize,in->ysize);
00685
00686 /* get memory for the image of gradient modulus */
00687 *modgrad = new_image_double(in->xsize,in->ysize);
00688
00689 /* get memory for "ordered" list of pixels */
00690 list = (struct coorlist *) calloc( (size_t) (n*p), sizeof(structcoorlist) );
00691 *mem_p = (void *) list;
00692 range_l_s = (struct coorlist **) calloc( (size_t) n_bins,
00693 sizeof(structcoorlist *) );
00694 range_l_e = (struct coorlist **) calloc( (size_t) n_bins,
00695 sizeof(structcoorlist *) );
00696 if( list == NULL || range_l_s == NULL || range_l_e == NULL )
00697 error("not enough memory.");
00698 for(i=0;i<n_bins;i++) range_l_s[i] = range_l_e[i] = NULL;
00699
00700 /* 'undefined' on the down and right boundaries */
00701 for(x=0;x<p;x++) g->data[(n-1)*p+x] = NOTDEF;
00702 for(y=0;y<n;y++) g->data[p*y+p-1] = NOTDEF;
00703
00704 /* compute gradient on the remaining pixels */
00705 for(x=0;x<p-1;x++)
00706 for(y=0;y<n-1;y++)
00707 {
00708 adr = y*p+x;
00709
00710 /*
00711 Norm 2 computation using 2x2 pixel window:
00712 A B
00713 C D
00714 and
00715 com1 = D-A, com2 = B-C.
00716 Then
00717 gx = B+D - (A+C) horizontal difference
00718 gy = C+D - (A+B) vertical difference
00719 com1 and com2 are just to avoid 2 additions.
00720 */
00721 com1 = in->data[adr+p+1] - in->data[adr];
00722 com2 = in->data[adr+1] - in->data[adr+p];
00723
00724 gx = com1+com2; /* gradient x component */
00725 gy = com1-com2; /* gradient y component */
00726 norm2 = gx*gx+gy*gy;
00727 norm = sqrt( norm2 / 4.0 ); /* gradient norm */
00728
00729 (*modgrad)->data[adr] = norm; /* store gradient norm */
00730
00731 if( norm <= threshold ) /* norm too small, gradient no defined */
00732 g->data[adr] = NOTDEF; /* gradient angle not defined */
00733 else
00734 {
00735 /* gradient angle computation */
00736 g->data[adr] = atan2(gx,-gy);
00737
00738 /* store the point in the right bin according to its norm */
00739 i = (unsignedint) (norm * (double) n_bins / max_grad);
00740 if( i >= n_bins ) i = n_bins-1;
00741 if( range_l_e[i] == NULL )
00742 range_l_s[i] = range_l_e[i] = list+list_count++;
00743 else
00744 {
00745 range_l_e[i]->next = list+list_count;
00746 range_l_e[i] = list+list_count++;
00747 }
00748 range_l_e[i]->x = (int) x;
00749 range_l_e[i]->y = (int) y;
00750 range_l_e[i]->next = NULL;
00751 }
00752 }
00753
00754 /* Make the list of pixels (almost) ordered by norm value.
00755 It starts by the larger bin, so the list starts by the
00756 pixels with higher gradient value. Pixels would be ordered
00757 by norm value, up to a precision given by max_grad/n_bins.
00758 */
00759 for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--);
00760 start = range_l_s[i];
00761 end = range_l_e[i];
00762 if( start != NULL )
00763 for(i--;i>0; i--)
00764 if( range_l_s[i] != NULL )
00765 {
00766 end->next = range_l_s[i];
00767 end = range_l_e[i];
00768 }
00769 *list_p = start;
00770
00771 /* free memory */
00772 free( (void *) range_l_s );
00773 free( (void *) range_l_e );
00774
00775 return g;
00776 }
00777
00778 /*----------------------------------------------------------------------------*/
00779 /** Is point (x,y) aligned to angle theta, up to precision 'prec'?
00780 */00781staticintisaligned( int x, int y, image_double angles, double theta,
00782 double prec )
00783 {
00784 double a;
00785
00786 /* check parameters */
00787 if( angles == NULL || angles->data == NULL )
00788 error("isaligned: invalid image 'angles'.");
00789 if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
00790 error("isaligned: (x,y) out of the image.");
00791 if( prec < 0.0 ) error("isaligned: 'prec' must be positive.");
00792
00793 /* angle at pixel (x,y) */
00794 a = angles->data[ x + y * angles->xsize ];
00795
00796 /* pixels whose level-line angle is not defined
00797 are considered as NON-aligned */
00798 if( a == NOTDEF ) returnFALSE; /* there is no need to call the function
00799 'double_equal' here because there is
00800 no risk of problems related to the
00801 comparison doubles, we are only
00802 interested in the exact NOTDEF value */
00803
00804 /* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */
00805 theta -= a;
00806 if( theta < 0.0 ) theta = -theta;
00807 if( theta > M_3_2_PI )
00808 {
00809 theta -= M_2__PI;
00810 if( theta < 0.0 ) theta = -theta;
00811 }
00812
00813 return theta < prec;
00814 }
00815
00816 /*----------------------------------------------------------------------------*/
00817 /** Absolute value angle difference.
00818 */00819staticdoubleangle_diff(double a, double b)
00820 {
00821 a -= b;
00822 while( a <= -M_PI ) a += M_2__PI;
00823 while( a > M_PI ) a -= M_2__PI;
00824 if( a < 0.0 ) a = -a;
00825 return a;
00826 }
00827
00828 /*----------------------------------------------------------------------------*/
00829 /** Signed angle difference.
00830 */00831staticdoubleangle_diff_signed(double a, double b)
00832 {
00833 a -= b;
00834 while( a <= -M_PI ) a += M_2__PI;
00835 while( a > M_PI ) a -= M_2__PI;
00836 return a;
00837 }
00838
00839
00840 /*----------------------------------------------------------------------------*/
00841 /*----------------------------- NFA computation ------------------------------*/
00842 /*----------------------------------------------------------------------------*/
00843
00844 /*----------------------------------------------------------------------------*/
00845 /** Computes the natural logarithm of the absolute value of
00846 the gamma function of x using the Lanczos approximation.
00847 See http://www.rskey.org/gamma.htm
00848
00849 The formula used is
00850 @f[
00851 \Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) }
00852 (x+5.5)^{x+0.5} e^{-(x+5.5)}
00853 @f]
00854 so
00855 @f[
00856 \log\Gamma(x) = \log\left( \sum_{n=0}^{N} q_n x^n \right)
00857 + (x+0.5) \log(x+5.5) - (x+5.5) - \sum_{n=0}^{N} \log(x+n)
00858 @f]
00859 and
00860 q0 = 75122.6331530,
00861 q1 = 80916.6278952,
00862 q2 = 36308.2951477,
00863 q3 = 8687.24529705,
00864 q4 = 1168.92649479,
00865 q5 = 83.8676043424,
00866 q6 = 2.50662827511.
00867 */00868staticdoublelog_gamma_lanczos(double x)
00869 {
00870 staticdouble q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
00871 8687.24529705, 1168.92649479, 83.8676043424,
00872 2.50662827511 };
00873 double a = (x+0.5) * log(x+5.5) - (x+5.5);
00874 double b = 0.0;
00875 int n;
00876
00877 for(n=0;n<7;n++)
00878 {
00879 a -= log( x + (double) n );
00880 b += q[n] * pow( x, (double) n );
00881 }
00882 return a + log(b);
00883 }
00884
00885 /*----------------------------------------------------------------------------*/
00886 /** Computes the natural logarithm of the absolute value of
00887 the gamma function of x using Windschitl method.
00888 See http://www.rskey.org/gamma.htm
00889
00890 The formula used is
00891 @f[
00892 \Gamma(x) = \sqrt{\frac{2\pi}{x}} \left( \frac{x}{e}
00893 \sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } \right)^x
00894 @f]
00895 so
00896 @f[
00897 \log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x
00898 + 0.5x\log\left( x\sinh(1/x) + \frac{1}{810x^6} \right).
00899 @f]
00900 This formula is a good approximation when x > 15.
00901 */00902staticdoublelog_gamma_windschitl(double x)
00903 {
00904 return 0.918938533204673 + (x-0.5)*log(x) - x
00905 + 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) );
00906 }
00907
00908 /*----------------------------------------------------------------------------*/
00909 /** Computes the natural logarithm of the absolute value of
00910 the gamma function of x. When x>15 use log_gamma_windschitl(),
00911 otherwise use log_gamma_lanczos().
00912 */00913#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
00914
00915 /*----------------------------------------------------------------------------*/
00916 /** Size of the table to store already computed inverse values.
00917 */00918#define TABSIZE 100000
00919
00920 /*----------------------------------------------------------------------------*/
00921 /** Computes -log10(NFA).
00922
00923 NFA stands for Number of False Alarms:
00924 @f[
00925 \mathrm{NFA} = NT \cdot B(n,k,p)
00926 @f]
00927
00928 - NT - number of tests
00929 - B(n,k,p) - tail of binomial distribution with parameters n,k and p:
00930 @f[
00931 B(n,k,p) = \sum_{j=k}^n
00932 \left(\begin{array}{c}n\\j\end{array}\right)
00933 p^{j} (1-p)^{n-j}
00934 @f]
00935
00936 The value -log10(NFA) is equivalent but more intuitive than NFA:
00937 - -1 corresponds to 10 mean false alarms
00938 - 0 corresponds to 1 mean false alarm
00939 - 1 corresponds to 0.1 mean false alarms
00940 - 2 corresponds to 0.01 mean false alarms
00941 - ...
00942
00943 Used this way, the bigger the value, better the detection,
00944 and a logarithmic scale is used.
00945
00946 @param n,k,p binomial parameters.
00947 @param logNT logarithm of Number of Tests
00948
00949 The computation is based in the gamma function by the following
00950 relation:
00951 @f[
00952 \left(\begin{array}{c}n\\k\end{array}\right)
00953 = \frac{ \Gamma(n+1) }{ \Gamma(k+1) \cdot \Gamma(n-k+1) }.
00954 @f]
00955 We use efficient algorithms to compute the logarithm of
00956 the gamma function.
00957
00958 To make the computation faster, not all the sum is computed, part
00959 of the terms are neglected based on a bound to the error obtained
00960 (an error of 10% in the result is accepted).
00961 */00962staticdoublenfa(int n, int k, double p, double logNT)
00963 {
00964 staticdouble inv[TABSIZE]; /* table to keep computed inverse values */
00965 double tolerance = 0.1; /* an error of 10% in the result is accepted */
00966 double log1term,term,bin_term,mult_term,bin_tail,err,p_term;
00967 int i;
00968
00969 /* check parameters */
00970 if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 )
00971 error("nfa: wrong n, k or p values.");
00972
00973 /* trivial cases */
00974 if( n==0 || k==0 ) return -logNT;
00975 if( n==k ) return -logNT - (double) n * log10(p);
00976
00977 /* probability term */
00978 p_term = p / (1.0-p);
00979
00980 /* compute the first term of the series */
00981 /*
00982 binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
00983 where bincoef(n,i) are the binomial coefficients.
00984 But
00985 bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
00986 We use this to compute the first term. Actually the log of it.
00987 */
00988 log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 )
00989 - log_gamma( (double) (n-k) + 1.0 )
00990 + (double) k * log(p) + (double) (n-k) * log(1.0-p);
00991 term = exp(log1term);
00992
00993 /* in some cases no more computations are needed */
00994 if( double_equal(term,0.0) ) /* the first term is almost zero */
00995 {
00996 if( (double) k > (double) n * p ) /* at begin or end of the tail? */
00997 return -log1term / M_LN10 - logNT; /* end: use just the first term */
00998 else
00999 return -logNT; /* begin: the tail is roughly 1 */
01000 }
01001
01002 /* compute more terms if needed */
01003 bin_tail = term;
01004 for(i=k+1;i<=n;i++)
01005 {
01006 /*
01007 As
01008 term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
01009 and
01010 bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
01011 then,
01012 term_i / term_i-1 = (n-i+1)/i * p/(1-p)
01013 and
01014 term_i = term_i-1 * (n-i+1)/i * p/(1-p).
01015 1/i is stored in a table as they are computed,
01016 because divisions are expensive.
01017 p/(1-p) is computed only once and stored in 'p_term'.
01018 */
01019 bin_term = (double) (n-i+1) * ( i<TABSIZE ?
01020 ( inv[i]!=0.0 ? inv[i] : ( inv[i] = 1.0 / (double) i ) ) :
01021 1.0 / (double) i );
01022
01023 mult_term = bin_term * p_term;
01024 term *= mult_term;
01025 bin_tail += term;
01026 if(bin_term<1.0)
01027 {
01028 /* When bin_term<1 then mult_term_j<mult_term_i for j>i.
01029 Then, the error on the binomial tail when truncated at
01030 the i term can be bounded by a geometric series of form
01031 term_i * sum mult_term_i^j. */
01032 err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) /
01033 (1.0-mult_term) - 1.0 );
01034
01035 /* One wants an error at most of tolerance*final_result, or:
01036 tolerance * abs(-log10(bin_tail)-logNT).
01037 Now, the error that can be accepted on bin_tail is
01038 given by tolerance*final_result divided by the derivative
01039 of -log10(x) when x=bin_tail. that is:
01040 tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
01041 Finally, we truncate the tail if the error is less than:
01042 tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
01043 if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break;
01044 }
01045 }
01046 return -log10(bin_tail) - logNT;
01047 }
01048
01049
01050 /*----------------------------------------------------------------------------*/
01051 /*--------------------------- Rectangle structure ----------------------------*/
01052 /*----------------------------------------------------------------------------*/
01053
01054 /*----------------------------------------------------------------------------*/
01055 /** Rectangle structure: line segment with width.
01056 */01057struct rect
01058 {
01059doublex1,y1,x2,y2; /* first and second point of the line segment */01060doublewidth; /* rectangle width */01061doublex,y; /* center of the rectangle */01062doubletheta; /* angle */01063doubledx,dy; /* vector with the line segment angle */01064doubleprec; /* tolerance angle */01065doublep; /* probability of a point with angle within 'prec' */
01066 };
01067
01068 /*----------------------------------------------------------------------------*/
01069 /** Copy one rectangle structure to another.
01070 */01071staticvoidrect_copy(structrect * in, structrect * out)
01072 {
01073 /* check parameters */
01074 if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'.");
01075
01076 /* copy values */
01077 out->x1 = in->x1;
01078 out->y1 = in->y1;
01079 out->x2 = in->x2;
01080 out->y2 = in->y2;
01081 out->width = in->width;
01082 out->x = in->x;
01083 out->y = in->y;
01084 out->theta = in->theta;
01085 out->dx = in->dx;
01086 out->dy = in->dy;
01087 out->prec = in->prec;
01088 out->p = in->p;
01089 }
01090
01091 /*----------------------------------------------------------------------------*/
01092 /** Rectangle points iterator.
01093
01094 The integer coordinates of pixels inside a rectangle are
01095 iteratively explored. This structure keep track of the process and
01096 functions ri_ini(), ri_inc(), ri_end(), and ri_del() are used in
01097 the process. An example of how to use the iterator is as follows:
01098 \code
01099
01100 struct rect * rec = XXX; // some rectangle
01101 rect_iter * i;
01102 for( i=ri_ini(rec); !ri_end(i); ri_inc(i) )
01103 {
01104 // your code, using 'i->x' and 'i->y' as coordinates
01105 }
01106 ri_del(i); // delete iterator
01107
01108 \endcode
01109 The pixels are explored 'column' by 'column', where we call
01110 'column' a set of pixels with the same x value that are inside the
01111 rectangle. The following is an schematic representation of a
01112 rectangle, the 'column' being explored is marked by colons, and
01113 the current pixel being explored is 'x,y'.
01114 \verbatim
01115
01116 vx[1],vy[1]
01117 * *
01118 * *
01119 * *
01120 * ye
01121 * : *
01122 vx[0],vy[0] : *
01123 * : *
01124 * x,y *
01125 * : *
01126 * : vx[2],vy[2]
01127 * : *
01128 y ys *
01129 ^ * *
01130 | * *
01131 | * *
01132 +---> x vx[3],vy[3]
01133
01134 \endverbatim
01135 The first 'column' to be explored is the one with the smaller x
01136 value. Each 'column' is explored starting from the pixel of the
01137 'column' (inside the rectangle) with the smaller y value.
01138
01139 The four corners of the rectangle are stored in order that rotates
01140 around the corners at the arrays 'vx[]' and 'vy[]'. The first
01141 point is always the one with smaller x value.
01142
01143 'x' and 'y' are the coordinates of the pixel being explored. 'ys'
01144 and 'ye' are the start and end values of the current column being
01145 explored. So, 'ys' < 'ye'.
01146 */01147typedefstruct
01148 {
01149double vx[4]; /* rectangle's corner X coordinates in circular order */01150double vy[4]; /* rectangle's corner Y coordinates in circular order */01151double ys,ye; /* start and end Y values of current 'column' */01152int x,y; /* coordinates of currently explored pixel */
01153 } rect_iter;
01154
01155 /*----------------------------------------------------------------------------*/
01156 /** Interpolate y value corresponding to 'x' value given, in
01157 the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the smaller
01158 of 'y1' and 'y2'.
01159
01160 The following restrictions are required:
01161 - x1 <= x2
01162 - x1 <= x
01163 - x <= x2
01164 */01165staticdoubleinter_low(double x, double x1, double y1, double x2, double y2)
01166 {
01167 /* check parameters */
01168 if( x1 > x2 || x < x1 || x > x2 )
01169 error("inter_low: unsuitable input, 'x1>x2' or 'x<x1' or 'x>x2'.");
01170
01171 /* interpolation */
01172 if( double_equal(x1,x2) && y1<y2 ) return y1;
01173 if( double_equal(x1,x2) && y1>y2 ) return y2;
01174 return y1 + (x-x1) * (y2-y1) / (x2-x1);
01175 }
01176
01177 /*----------------------------------------------------------------------------*/
01178 /** Interpolate y value corresponding to 'x' value given, in
01179 the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the larger
01180 of 'y1' and 'y2'.
01181
01182 The following restrictions are required:
01183 - x1 <= x2
01184 - x1 <= x
01185 - x <= x2
01186 */01187staticdoubleinter_hi(double x, double x1, double y1, double x2, double y2)
01188 {
01189 /* check parameters */
01190 if( x1 > x2 || x < x1 || x > x2 )
01191 error("inter_hi: unsuitable input, 'x1>x2' or 'x<x1' or 'x>x2'.");
01192
01193 /* interpolation */
01194 if( double_equal(x1,x2) && y1<y2 ) return y2;
01195 if( double_equal(x1,x2) && y1>y2 ) return y1;
01196 return y1 + (x-x1) * (y2-y1) / (x2-x1);
01197 }
01198
01199 /*----------------------------------------------------------------------------*/
01200 /** Free memory used by a rectangle iterator.
01201 */01202staticvoidri_del(rect_iter * iter)
01203 {
01204 if( iter == NULL ) error("ri_del: NULL iterator.");
01205 free( (void *) iter );
01206 }
01207
01208 /*----------------------------------------------------------------------------*/
01209 /** Check if the iterator finished the full iteration.
01210
01211 See details in \ref rect_iter
01212 */01213staticintri_end(rect_iter * i)
01214 {
01215 /* check input */
01216 if( i == NULL ) error("ri_end: NULL iterator.");
01217
01218 /* if the current x value is larger than the larger
01219 x value in the rectangle (vx[2]), we know the full
01220 exploration of the rectangle is finished. */
01221 return (double)(i->x) > i->vx[2];
01222 }
01223
01224 /*----------------------------------------------------------------------------*/
01225 /** Increment a rectangle iterator.
01226
01227 See details in \ref rect_iter
01228 */01229staticvoidri_inc(rect_iter * i)
01230 {
01231 /* check input */
01232 if( i == NULL ) error("ri_inc: NULL iterator.");
01233
01234 /* if not at end of exploration,
01235 increase y value for next pixel in the 'column' */
01236 if( !ri_end(i) ) i->y++;
01237
01238 /* if the end of the current 'column' is reached,
01239 and it is not the end of exploration,
01240 advance to the next 'column' */
01241 while( (double) (i->y) > i->ye && !ri_end(i) )
01242 {
01243 /* increase x, next 'column' */
01244 i->x++;
01245
01246 /* if end of exploration, return */
01247 if( ri_end(i) ) return;
01248
01249 /* update lower y limit (start) for the new 'column'.
01250
01251 We need to interpolate the y value that corresponds to the
01252 lower side of the rectangle. The first thing is to decide if
01253 the corresponding side is
01254
01255 vx[0],vy[0] to vx[3],vy[3] or
01256 vx[3],vy[3] to vx[2],vy[2]
01257
01258 Then, the side is interpolated for the x value of the
01259 'column'. But, if the side is vertical (as it could happen if
01260 the rectangle is vertical and we are dealing with the first
01261 or last 'columns') then we pick the lower value of the side
01262 by using 'inter_low'.
01263 */
01264 if( (double) i->x < i->vx[3] )
01265 i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]);
01266 else
01267 i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]);
01268
01269 /* update upper y limit (end) for the new 'column'.
01270
01271 We need to interpolate the y value that corresponds to the
01272 upper side of the rectangle. The first thing is to decide if
01273 the corresponding side is
01274
01275 vx[0],vy[0] to vx[1],vy[1] or
01276 vx[1],vy[1] to vx[2],vy[2]
01277
01278 Then, the side is interpolated for the x value of the
01279 'column'. But, if the side is vertical (as it could happen if
01280 the rectangle is vertical and we are dealing with the first
01281 or last 'columns') then we pick the lower value of the side
01282 by using 'inter_low'.
01283 */
01284 if( (double)i->x < i->vx[1] )
01285 i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]);
01286 else
01287 i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]);
01288
01289 /* new y */
01290 i->y = (int) ceil(i->ys);
01291 }
01292 }
01293
01294 /*----------------------------------------------------------------------------*/
01295 /** Create and initialize a rectangle iterator.
01296
01297 See details in \ref rect_iter
01298 */01299staticrect_iter * ri_ini(structrect * r)
01300 {
01301 double vx[4],vy[4];
01302 int n,offset;
01303 rect_iter * i;
01304
01305 /* check parameters */
01306 if( r == NULL ) error("ri_ini: invalid rectangle.");
01307
01308 /* get memory */
01309 i = (rect_iter *) malloc(sizeof(rect_iter));
01310 if( i == NULL ) error("ri_ini: Not enough memory.");
01311
01312 /* build list of rectangle corners ordered
01313 in a circular way around the rectangle */
01314 vx[0] = r->x1 - r->dy * r->width / 2.0;
01315 vy[0] = r->y1 + r->dx * r->width / 2.0;
01316 vx[1] = r->x2 - r->dy * r->width / 2.0;
01317 vy[1] = r->y2 + r->dx * r->width / 2.0;
01318 vx[2] = r->x2 + r->dy * r->width / 2.0;
01319 vy[2] = r->y2 - r->dx * r->width / 2.0;
01320 vx[3] = r->x1 + r->dy * r->width / 2.0;
01321 vy[3] = r->y1 - r->dx * r->width / 2.0;
01322
01323 /* compute rotation of index of corners needed so that the first
01324 point has the smaller x.
01325
01326 if one side is vertical, thus two corners have the same smaller x
01327 value, the one with the largest y value is selected as the first.
01328 */
01329 if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0;
01330 elseif( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1;
01331 elseif( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2;
01332 else offset = 3;
01333
01334 /* apply rotation of index. */
01335 for(n=0; n<4; n++)
01336 {
01337 i->vx[n] = vx[(offset+n)%4];
01338 i->vy[n] = vy[(offset+n)%4];
01339 }
01340
01341 /* Set a initial condition.
01342
01343 The values are set to values that will cause 'ri_inc' (that will
01344 be called immediately) to initialize correctly the first 'column'
01345 and compute the limits 'ys' and 'ye'.
01346
01347 'y' is set to the integer value of vy[0], the starting corner.
01348
01349 'ys' and 'ye' are set to very small values, so 'ri_inc' will
01350 notice that it needs to start a new 'column'.
01351
01352 The smaller integer coordinate inside of the rectangle is
01353 'ceil(vx[0])'. The current 'x' value is set to that value minus
01354 one, so 'ri_inc' (that will increase x by one) will advance to
01355 the first 'column'.
01356 */
01357 i->x = (int) ceil(i->vx[0]) - 1;
01358 i->y = (int) ceil(i->vy[0]);
01359 i->ys = i->ye = -DBL_MAX;
01360
01361 /* advance to the first pixel */
01362 ri_inc(i);
01363
01364 return i;
01365 }
01366
01367 /*----------------------------------------------------------------------------*/
01368 /** Compute a rectangle's NFA value.
01369 */01370staticdoublerect_nfa(structrect * rec, image_double angles, double logNT)
01371 {
01372 rect_iter * i;
01373 int pts = 0;
01374 int alg = 0;
01375
01376 /* check parameters */
01377 if( rec == NULL ) error("rect_nfa: invalid rectangle.");
01378 if( angles == NULL ) error("rect_nfa: invalid 'angles'.");
01379
01380 /* compute the total number of pixels and of aligned points in 'rec' */
01381 for(i=ri_ini(rec); !ri_end(i); ri_inc(i)) /* rectangle iterator */
01382 if( i->x >= 0 && i->y >= 0 &&
01383 i->x < (int) angles->xsize && i->y < (int) angles->ysize )
01384 {
01385 ++pts; /* total number of pixels counter */
01386 if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) )
01387 ++alg; /* aligned points counter */
01388 }
01389 ri_del(i); /* delete iterator */
01390
01391 returnnfa(pts,alg,rec->p,logNT); /* compute NFA value */
01392 }
01393
01394
01395 /*----------------------------------------------------------------------------*/
01396 /*---------------------------------- Regions ---------------------------------*/
01397 /*----------------------------------------------------------------------------*/
01398
01399 /*----------------------------------------------------------------------------*/
01400 /** Compute region's angle as the principal inertia axis of the region.
01401
01402 The following is the region inertia matrix A:
01403 @f[
01404
01405 A = \left(\begin{array}{cc}
01406 Ixx & Ixy \\
01407 Ixy & Iyy \\
01408 \end{array}\right)
01409
01410 @f]
01411 where
01412
01413 Ixx = sum_i G(i).(y_i - cx)^2
01414
01415 Iyy = sum_i G(i).(x_i - cy)^2
01416
01417 Ixy = - sum_i G(i).(x_i - cx).(y_i - cy)
01418
01419 and
01420 - G(i) is the gradient norm at pixel i, used as pixel's weight.
01421 - x_i and y_i are the coordinates of pixel i.
01422 - cx and cy are the coordinates of the center of th region.
01423
01424 lambda1 and lambda2 are the eigenvalues of matrix A,
01425 with lambda1 >= lambda2. They are found by solving the
01426 characteristic polynomial:
01427
01428 det( lambda I - A) = 0
01429
01430 that gives:
01431
01432 lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
01433
01434 lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
01435
01436 To get the line segment direction we want to get the angle the
01437 eigenvector assotiated to the smaller eigenvalue. We have to solve
01438 a,b in:
01439
01440 a.Ixx + b.Ixy = a.lambda2
01441
01442 a.Ixy + b.Iyy = b.lambda2
01443
01444 We want the angle theta = atan(b/a). It can be computed with
01445 any of the two equations:
01446
01447 theta = atan( (lambda2-Ixx) / Ixy )
01448
01449 or
01450
01451 theta = atan( Ixy / (lambda2-Iyy) )
01452
01453 When |Ixx| > |Iyy| we use the first, otherwise the second (just to
01454 get better numeric precision).
01455 */01456staticdoubleget_theta( structpoint * reg, int reg_size, double x, double y,
01457 image_double modgrad, double reg_angle, double prec )
01458 {
01459 double lambda,theta,weight;
01460 double Ixx = 0.0;
01461 double Iyy = 0.0;
01462 double Ixy = 0.0;
01463 int i;
01464
01465 /* check parameters */
01466 if( reg == NULL ) error("get_theta: invalid region.");
01467 if( reg_size <= 1 ) error("get_theta: region size <= 1.");
01468 if( modgrad == NULL || modgrad->data == NULL )
01469 error("get_theta: invalid 'modgrad'.");
01470 if( prec < 0.0 ) error("get_theta: 'prec' must be positive.");
01471
01472 /* compute inertia matrix */
01473 for(i=0; i<reg_size; i++)
01474 {
01475 weight = modgrad->data[ reg[i].x + reg[i].y * modgrad->xsize ];
01476 Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight;
01477 Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight;
01478 Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight;
01479 }
01480 if( double_equal(Ixx,0.0) && double_equal(Iyy,0.0) && double_equal(Ixy,0.0) )
01481 error("get_theta: null inertia matrix.");
01482
01483 /* compute smallest eigenvalue */
01484 lambda = 0.5 * ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) );
01485
01486 /* compute angle */
01487 theta = fabs(Ixx)>fabs(Iyy) ? atan2(lambda-Ixx,Ixy) : atan2(Ixy,lambda-Iyy);
01488
01489 /* The previous procedure don't cares about orientation,
01490 so it could be wrong by 180 degrees. Here is corrected if necessary. */
01491 if( angle_diff(theta,reg_angle) > prec ) theta += M_PI;
01492
01493 return theta;
01494 }
01495
01496 /*----------------------------------------------------------------------------*/
01497 /** Computes a rectangle that covers a region of points.
01498 */01499staticvoidregion2rect( structpoint * reg, int reg_size,
01500 image_double modgrad, double reg_angle,
01501 double prec, double p, structrect * rec )
01502 {
01503 double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max;
01504 int i;
01505
01506 /* check parameters */
01507 if( reg == NULL ) error("region2rect: invalid region.");
01508 if( reg_size <= 1 ) error("region2rect: region size <= 1.");
01509 if( modgrad == NULL || modgrad->data == NULL )
01510 error("region2rect: invalid image 'modgrad'.");
01511 if( rec == NULL ) error("region2rect: invalid 'rec'.");
01512
01513 /* center of the region:
01514
01515 It is computed as the weighted sum of the coordinates
01516 of all the pixels in the region. The norm of the gradient
01517 is used as the weight of a pixel. The sum is as follows:
01518 cx = \sum_i G(i).x_i
01519 cy = \sum_i G(i).y_i
01520 where G(i) is the norm of the gradient of pixel i
01521 and x_i,y_i are its coordinates.
01522 */
01523 x = y = sum = 0.0;
01524 for(i=0; i<reg_size; i++)
01525 {
01526 weight = modgrad->data[ reg[i].x + reg[i].y * modgrad->xsize ];
01527 x += (double) reg[i].x * weight;
01528 y += (double) reg[i].y * weight;
01529 sum += weight;
01530 }
01531 if( sum <= 0.0 ) error("region2rect: weights sum equal to zero.");
01532 x /= sum;
01533 y /= sum;
01534
01535 /* theta */
01536 theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec);
01537
01538 /* length and width:
01539
01540 'l' and 'w' are computed as the distance from the center of the
01541 region to pixel i, projected along the rectangle axis (dx,dy) and
01542 to the orthogonal axis (-dy,dx), respectively.
01543
01544 The length of the rectangle goes from l_min to l_max, where l_min
01545 and l_max are the minimum and maximum values of l in the region.
01546 Analogously, the width is selected from w_min to w_max, where
01547 w_min and w_max are the minimum and maximum of w for the pixels
01548 in the region.
01549 */
01550 dx = cos(theta);
01551 dy = sin(theta);
01552 l_min = l_max = w_min = w_max = 0.0;
01553 for(i=0; i<reg_size; i++)
01554 {
01555 l = ( (double) reg[i].x - x) * dx + ( (double) reg[i].y - y) * dy;
01556 w = -( (double) reg[i].x - x) * dy + ( (double) reg[i].y - y) * dx;
01557
01558 if( l > l_max ) l_max = l;
01559 if( l < l_min ) l_min = l;
01560 if( w > w_max ) w_max = w;
01561 if( w < w_min ) w_min = w;
01562 }
01563
01564 /* store values */
01565 rec->x1 = x + l_min * dx;
01566 rec->y1 = y + l_min * dy;
01567 rec->x2 = x + l_max * dx;
01568 rec->y2 = y + l_max * dy;
01569 rec->width = w_max - w_min;
01570 rec->x = x;
01571 rec->y = y;
01572 rec->theta = theta;
01573 rec->dx = dx;
01574 rec->dy = dy;
01575 rec->prec = prec;
01576 rec->p = p;
01577
01578 /* we impose a minimal width of one pixel
01579
01580 A sharp horizontal or vertical step would produce a perfectly
01581 horizontal or vertical region. The width computed would be
01582 zero. But that corresponds to a one pixels width transition in
01583 the image.
01584 */
01585 if( rec->width < 1.0 ) rec->width = 1.0;
01586 }
01587
01588 /*----------------------------------------------------------------------------*/
01589 /** Build a region of pixels that share the same angle, up to a
01590 tolerance 'prec', starting at point (x,y).
01591 */01592staticvoidregion_grow( int x, int y, image_double angles, structpoint * reg,
01593 int * reg_size, double * reg_angle, image_char used,
01594 double prec )
01595 {
01596 double sumdx,sumdy;
01597 int xx,yy,i;
01598
01599 /* check parameters */
01600 if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
01601 error("region_grow: (x,y) out of the image.");
01602 if( angles == NULL || angles->data == NULL )
01603 error("region_grow: invalid image 'angles'.");
01604 if( reg == NULL ) error("region_grow: invalid 'reg'.");
01605 if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'.");
01606 if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'.");
01607 if( used == NULL || used->data == NULL )
01608 error("region_grow: invalid image 'used'.");
01609
01610 /* first point of the region */
01611 *reg_size = 1;
01612 reg[0].x = x;
01613 reg[0].y = y;
01614 *reg_angle = angles->data[x+y*angles->xsize]; /* region's angle */
01615 sumdx = cos(*reg_angle);
01616 sumdy = sin(*reg_angle);
01617 used->data[x+y*used->xsize] = USED;
01618
01619 /* try neighbors as new region points */
01620 for(i=0; i<*reg_size; i++)
01621 for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++)
01622 for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++)
01623 if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize &&
01624 used->data[xx+yy*used->xsize] != USED &&
01625 isaligned(xx,yy,angles,*reg_angle,prec) )
01626 {
01627 /* add point */
01628 used->data[xx+yy*used->xsize] = USED;
01629 reg[*reg_size].x = xx;
01630 reg[*reg_size].y = yy;
01631 ++(*reg_size);
01632
01633 /* update region's angle */
01634 sumdx += cos( angles->data[xx+yy*angles->xsize] );
01635 sumdy += sin( angles->data[xx+yy*angles->xsize] );
01636 *reg_angle = atan2(sumdy,sumdx);
01637 }
01638 }
01639
01640 /*----------------------------------------------------------------------------*/
01641 /** Try some rectangles variations to improve NFA value. Only if the
01642 rectangle is not meaningful (i.e., log_nfa <= eps).
01643 */01644staticdoublerect_improve( structrect * rec, image_double angles,
01645 double logNT, double eps )
01646 {
01647 struct rect r;
01648 double log_nfa,log_nfa_new;
01649 double delta = 0.5;
01650 double delta_2 = delta / 2.0;
01651 int n;
01652
01653 log_nfa = rect_nfa(rec,angles,logNT);
01654
01655 if( log_nfa > eps ) return log_nfa;
01656
01657 /* try finer precisions */
01658 rect_copy(rec,&r);
01659 for(n=0; n<5; n++)
01660 {
01661 r.p /= 2.0;
01662 r.prec = r.p * M_PI;
01663 log_nfa_new = rect_nfa(&r,angles,logNT);
01664 if( log_nfa_new > log_nfa )
01665 {
01666 log_nfa = log_nfa_new;
01667 rect_copy(&r,rec);
01668 }
01669 }
01670
01671 if( log_nfa > eps ) return log_nfa;
01672
01673 /* try to reduce width */
01674 rect_copy(rec,&r);
01675 for(n=0; n<5; n++)
01676 {
01677 if( (r.width - delta) >= 0.5 )
01678 {
01679 r.width -= delta;
01680 log_nfa_new = rect_nfa(&r,angles,logNT);
01681 if( log_nfa_new > log_nfa )
01682 {
01683 rect_copy(&r,rec);
01684 log_nfa = log_nfa_new;
01685 }
01686 }
01687 }
01688
01689 if( log_nfa > eps ) return log_nfa;
01690
01691 /* try to reduce one side of the rectangle */
01692 rect_copy(rec,&r);
01693 for(n=0; n<5; n++)
01694 {
01695 if( (r.width - delta) >= 0.5 )
01696 {
01697 r.x1 += -r.dy * delta_2;
01698 r.y1 += r.dx * delta_2;
01699 r.x2 += -r.dy * delta_2;
01700 r.y2 += r.dx * delta_2;
01701 r.width -= delta;
01702 log_nfa_new = rect_nfa(&r,angles,logNT);
01703 if( log_nfa_new > log_nfa )
01704 {
01705 rect_copy(&r,rec);
01706 log_nfa = log_nfa_new;
01707 }
01708 }
01709 }
01710
01711 if( log_nfa > eps ) return log_nfa;
01712
01713 /* try to reduce the other side of the rectangle */
01714 rect_copy(rec,&r);
01715 for(n=0; n<5; n++)
01716 {
01717 if( (r.width - delta) >= 0.5 )
01718 {
01719 r.x1 -= -r.dy * delta_2;
01720 r.y1 -= r.dx * delta_2;
01721 r.x2 -= -r.dy * delta_2;
01722 r.y2 -= r.dx * delta_2;
01723 r.width -= delta;
01724 log_nfa_new = rect_nfa(&r,angles,logNT);
01725 if( log_nfa_new > log_nfa )
01726 {
01727 rect_copy(&r,rec);
01728 log_nfa = log_nfa_new;
01729 }
01730 }
01731 }
01732
01733 if( log_nfa > eps ) return log_nfa;
01734
01735 /* try even finer precisions */
01736 rect_copy(rec,&r);
01737 for(n=0; n<5; n++)
01738 {
01739 r.p /= 2.0;
01740 r.prec = r.p * M_PI;
01741 log_nfa_new = rect_nfa(&r,angles,logNT);
01742 if( log_nfa_new > log_nfa )
01743 {
01744 log_nfa = log_nfa_new;
01745 rect_copy(&r,rec);
01746 }
01747 }
01748
01749 return log_nfa;
01750 }
01751
01752 /*----------------------------------------------------------------------------*/
01753 /** Reduce the region size, by elimination the points far from the
01754 starting point, until that leads to rectangle with the right
01755 density of region points or to discard the region if too small.
01756 */01757staticintreduce_region_radius( structpoint * reg, int * reg_size,
01758 image_double modgrad, double reg_angle,
01759 double prec, double p, structrect * rec,
01760 image_char used, image_double angles,
01761 double density_th )
01762 {
01763 double density,rad1,rad2,rad,xc,yc;
01764 int i;
01765
01766 /* check parameters */
01767 if( reg == NULL ) error("reduce_region_radius: invalid pointer 'reg'.");
01768 if( reg_size == NULL )
01769 error("reduce_region_radius: invalid pointer 'reg_size'.");
01770 if( prec < 0.0 ) error("reduce_region_radius: 'prec' must be positive.");
01771 if( rec == NULL ) error("reduce_region_radius: invalid pointer 'rec'.");
01772 if( used == NULL || used->data == NULL )
01773 error("reduce_region_radius: invalid image 'used'.");
01774 if( angles == NULL || angles->data == NULL )
01775 error("reduce_region_radius: invalid image 'angles'.");
01776
01777 /* compute region points density */
01778 density = (double) *reg_size /
01779 ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
01780
01781 /* if the density criterion is satisfied there is nothing to do */
01782 if( density >= density_th ) returnTRUE;
01783
01784 /* compute region's radius */
01785 xc = (double) reg[0].x;
01786 yc = (double) reg[0].y;
01787 rad1 = dist( xc, yc, rec->x1, rec->y1 );
01788 rad2 = dist( xc, yc, rec->x2, rec->y2 );
01789 rad = rad1 > rad2 ? rad1 : rad2;
01790
01791 /* while the density criterion is not satisfied, remove farther pixels */
01792 while( density < density_th )
01793 {
01794 rad *= 0.75; /* reduce region's radius to 75% of its value */
01795
01796 /* remove points from the region and update 'used' map */
01797 for(i=0; i<*reg_size; i++)
01798 if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad )
01799 {
01800 /* point not kept, mark it as NOTUSED */
01801 used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
01802 /* remove point from the region */
01803 reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */
01804 reg[i].y = reg[*reg_size-1].y;
01805 --(*reg_size);
01806 --i; /* to avoid skipping one point */
01807 }
01808
01809 /* reject if the region is too small.
01810 2 is the minimal region size for 'region2rect' to work. */
01811 if( *reg_size < 2 ) returnFALSE;
01812
01813 /* re-compute rectangle */
01814 region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
01815
01816 /* re-compute region points density */
01817 density = (double) *reg_size /
01818 ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
01819 }
01820
01821 /* if this point is reached, the density criterion is satisfied */
01822 returnTRUE;
01823 }
01824
01825 /*----------------------------------------------------------------------------*/
01826 /** Refine a rectangle.
01827
01828 For that, an estimation of the angle tolerance is performed by the
01829 standard deviation of the angle at points near the region's
01830 starting point. Then, a new region is grown starting from the same
01831 point, but using the estimated angle tolerance. If this fails to
01832 produce a rectangle with the right density of region points,
01833 'reduce_region_radius' is called to try to satisfy this condition.
01834 */01835staticintrefine( structpoint * reg, int * reg_size, image_double modgrad,
01836 double reg_angle, double prec, double p, structrect * rec,
01837 image_char used, image_double angles, double density_th )
01838 {
01839 double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum;
01840 int i,n;
01841
01842 /* check parameters */
01843 if( reg == NULL ) error("refine: invalid pointer 'reg'.");
01844 if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'.");
01845 if( prec < 0.0 ) error("refine: 'prec' must be positive.");
01846 if( rec == NULL ) error("refine: invalid pointer 'rec'.");
01847 if( used == NULL || used->data == NULL )
01848 error("refine: invalid image 'used'.");
01849 if( angles == NULL || angles->data == NULL )
01850 error("refine: invalid image 'angles'.");
01851
01852 /* compute region points density */
01853 density = (double) *reg_size /
01854 ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
01855
01856 /* if the density criterion is satisfied there is nothing to do */
01857 if( density >= density_th ) returnTRUE;
01858
01859 /*------ First try: reduce angle tolerance ------*/
01860
01861 /* compute the new mean angle and tolerance */
01862 xc = (double) reg[0].x;
01863 yc = (double) reg[0].y;
01864 ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ];
01865 sum = s_sum = 0.0;
01866 n = 0;
01867 for(i=0; i<*reg_size; i++)
01868 {
01869 used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
01870 if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width )
01871 {
01872 angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ];
01873 ang_d = angle_diff_signed(angle,ang_c);
01874 sum += ang_d;
01875 s_sum += ang_d * ang_d;
01876 ++n;
01877 }
01878 }
01879 mean_angle = sum / (double) n;
01880 tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n
01881 + mean_angle*mean_angle ); /* 2 * standard deviation */
01882
01883 /* find a new region from the same starting point and new angle tolerance */
01884 region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau);
01885
01886 /* if the region is too small, reject */
01887 if( *reg_size < 2 ) returnFALSE;
01888
01889 /* re-compute rectangle */
01890 region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
01891
01892 /* re-compute region points density */
01893 density = (double) *reg_size /
01894 ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
01895
01896 /*------ Second try: reduce region radius ------*/
01897 if( density < density_th )
01898 returnreduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p,
01899 rec, used, angles, density_th );
01900
01901 /* if this point is reached, the density criterion is satisfied */
01902 returnTRUE;
01903 }
01904
01905
01906 /*----------------------------------------------------------------------------*/
01907 /*-------------------------- Line Segment Detector ---------------------------*/
01908 /*----------------------------------------------------------------------------*/
01909
01910 /*----------------------------------------------------------------------------*/
01911 /** LSD full interface.
01912 */01913ntuple_listLineSegmentDetection( image_double image, double scale,
01914 double sigma_scale, double quant,
01915 double ang_th, double eps, double density_th,
01916 int n_bins, double max_grad,
01917 image_int * region )
01918 {
01919 ntuple_list out = new_ntuple_list(5);
01920 image_double scaled_image,angles,modgrad;
01921 image_char used;
01922 struct coorlist * list_p;
01923 void * mem_p;
01924 struct rect rec;
01925 struct point * reg;
01926 int reg_size,min_reg_size,i;
01927 unsignedint xsize,ysize;
01928 double rho,reg_angle,prec,p,log_nfa,logNT;
01929 int ls_count = 0; /* line segments are numbered 1,2,3,... */
01930
01931
01932 /* check parameters */
01933 if( image==NULL || image->data==NULL || image->xsize==0 || image->ysize==0 )
01934 error("invalid image input.");
01935 if( scale <= 0.0 ) error("'scale' value must be positive.");
01936 if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
01937 if( quant < 0.0 ) error("'quant' value must be positive.");
01938 if( ang_th <= 0.0 || ang_th >= 180.0 )
01939 error("'ang_th' value must be in the range (0,180).");
01940 if( density_th < 0.0 || density_th > 1.0 )
01941 error("'density_th' value must be in the range [0,1].");
01942 if( n_bins <= 0 ) error("'n_bins' value must be positive.");
01943 if( max_grad <= 0.0 ) error("'max_grad' value must be positive.");
01944
01945
01946 /* angle tolerance */
01947 prec = M_PI * ang_th / 180.0;
01948 p = ang_th / 180.0;
01949 rho = quant / sin(prec); /* gradient magnitude threshold */
01950
01951
01952 /* scale image (if necessary) and compute angle at each pixel */
01953 if( scale != 1.0 )
01954 {
01955 scaled_image = gaussian_sampler( image, scale, sigma_scale );
01956 angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
01957 &modgrad, (unsignedint) n_bins, max_grad );
01958 free_image_double(scaled_image);
01959 }
01960 else
01961 angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
01962 (unsignedint) n_bins, max_grad );
01963 xsize = angles->xsize;
01964 ysize = angles->ysize;
01965 logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0;
01966 min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
01967 that can give a meaningful event */
01968
01969
01970 /* initialize some structures */
01971 if( region != NULL ) /* image to output pixel region number, if asked */
01972 *region = new_image_int_ini(angles->xsize,angles->ysize,0);
01973 used = new_image_char_ini(xsize,ysize,NOTUSED);
01974 reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(structpoint) );
01975 if( reg == NULL ) error("not enough memory!");
01976
01977
01978 /* search for line segments */
01979 for(; list_p != NULL; list_p = list_p->next )
01980 if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
01981 angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
01982 /* there is no risk of double comparison problems here
01983 because we are only interested in the exact NOTDEF value */
01984 {
01985 /* find the region of connected point and ~equal angle */
01986 region_grow( list_p->x, list_p->y, angles, reg, ®_size,
01987 ®_angle, used, prec );
01988
01989 /* reject small regions */
01990 if( reg_size < min_reg_size ) continue;
01991
01992 /* construct rectangular approximation for the region */
01993 region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
01994
01995 /* Check if the rectangle exceeds the minimal density of
01996 region points. If not, try to improve the region.
01997 The rectangle will be rejected if the final one does
01998 not fulfill the minimal density condition.
01999 This is an addition to the original LSD algorithm published in
02000 "LSD: A Fast Line Segment Detector with a False Detection Control"
02001 by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
02002 The original algorithm is obtained with density_th = 0.0.
02003 */
02004 if( !refine( reg, ®_size, modgrad, reg_angle,
02005 prec, p, &rec, used, angles, density_th ) ) continue;
02006
02007 /* compute NFA value */
02008 log_nfa = rect_improve(&rec,angles,logNT,eps);
02009 if( log_nfa <= eps ) continue;
02010
02011 /* A New Line Segment was found! */
02012 ++ls_count; /* increase line segment counter */
02013
02014 /*
02015 The gradient was computed with a 2x2 mask, its value corresponds to
02016 points with an offset of (0.5,0.5), that should be added to output.
02017 The coordinates origin is at the center of pixel (0,0).
02018 */
02019 rec.x1 += 0.5; rec.y1 += 0.5;
02020 rec.x2 += 0.5; rec.y2 += 0.5;
02021
02022 /* scale the result values if a subsampling was performed */
02023 if( scale != 1.0 )
02024 {
02025 rec.x1 /= scale; rec.y1 /= scale;
02026 rec.x2 /= scale; rec.y2 /= scale;
02027 rec.width /= scale;
02028 }
02029
02030 /* add line segment found to output */
02031 add_5tuple(out, rec.x1, rec.y1, rec.x2, rec.y2, rec.width);
02032
02033 /* add region number to 'region' image if needed */
02034 if( region != NULL )
02035 for(i=0; i<reg_size; i++)
02036 (*region)->data[reg[i].x+reg[i].y*(*region)->xsize] = ls_count;
02037 }
02038
02039
02040 /* free memory */
02041 free_image_double(angles);
02042 free_image_double(modgrad);
02043 free_image_char(used);
02044 free( (void *) reg );
02045 free( (void *) mem_p );
02046
02047 return out;
02048 }
02049
02050 /*----------------------------------------------------------------------------*/
02051 /** LSD Simple Interface with Scale.
02052 */02053ntuple_listlsd_scale(image_double image, double scale)
02054 {
02055 /* LSD parameters */
02056 double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as
02057 sigma = sigma_scale/scale. */
02058 double quant = 2.0; /* Bound to the quantization error on the
02059 gradient norm. */
02060 double ang_th = 22.5; /* Gradient angle tolerance in degrees. */
02061 double eps = 0.0; /* Detection threshold, -log10(NFA). */
02062 double density_th = 0.7; /* Minimal density of region points in rectangle. */
02063 int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient
02064 modulus. */
02065 double max_grad = 255.0; /* Gradient modulus in the highest bin. The
02066 default value corresponds to the highest
02067 gradient modulus on images with gray
02068 levels in [0,255]. */
02069
02070 returnLineSegmentDetection( image, scale, sigma_scale, quant, ang_th, eps,
02071 density_th, n_bins, max_grad, NULL );
02072 }
02073
02074 /*----------------------------------------------------------------------------*/
02075 /** LSD Simple Interface.
02076 */02077ntuple_listlsd(image_double image)
02078 {
02079 /* LSD parameters */
02080 double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */
02081
02082 returnlsd_scale(image,scale);
02083 }
02084 /*----------------------------------------------------------------------------*/
Generated on Fri Dec 3 10:18:18 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/lsd_8c.html
================================================
LSD: lsd.c File Reference
Reduce the region size, by elimination the points far from the starting point, until that leads to rectangle with the right density of region points or to discard the region if too small.
The resulting rounding error after floating point computations depend on the specific operations done. The same number computed by different algorithms could present different rounding errors. For a useful comparison, an estimation of the relative rounding error should be considered and compared to a factor times EPS. The factor should be related to the cumulated rounding error in the chain of computation. Here, as a simplification, a fixed factor is used.
00156 {
00157 double abs_diff,aa,bb,abs_max;
00158
00159 /* trivial case */
00160 if( a == b ) returnTRUE;
00161
00162 abs_diff = fabs(a-b);
00163 aa = fabs(a);
00164 bb = fabs(b);
00165 abs_max = aa > bb ? aa : bb;
00166
00167 /* DBL_MIN is the smallest normalized number, thus, the smallest
00168 number whose relative error is bounded by DBL_EPSILON. For
00169 smaller numbers, the same quantization steps as for DBL_MIN
00170 are used. Then, for smaller numbers, a meaningful "relative"
00171 error should be computed by dividing the difference by DBL_MIN. */
00172 if( abs_max < DBL_MIN ) abs_max = DBL_MIN;
00173
00174 /* equal if relative error <= factor x eps */
00175 return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
00176 }
Scale the input image 'in' by a factor 'scale' by Gaussian sub-sampling.
For example, scale=0.8 will give a result at 80% of the original size.
The image is convolved with a Gaussian kernel
before the sub-sampling to prevent aliasing.
The standard deviation sigma given by:
sigma = sigma_scale / scale, if scale < 1.0
sigma = sigma_scale, if scale >= 1.0
To be able to sub-sample at non-integer steps, some interpolation is needed. In this implementation, the interpolation is done by the Gaussian kernel, so both operations (filtering and sampling) are done at the same time. The Gaussian kernel is computed centered on the coordinates of the required sample. In this way, when applied, it gives directly the result of convolving the image with the kernel and interpolated to that particular position.
A fast algorithm is done using the separability of the Gaussian kernel. Applying the 2D Gaussian kernel is equivalent to applying first a horizontal 1D Gaussian kernel and then a vertical 1D Gaussian kernel (or the other way round). The reason is that
where
The algorithm first apply a combined Gaussian kernel and sampling in the x axis, and then the combined Gaussian kernel and sampling in the y axis.
00513 {
00514 image_double aux,out;
00515 ntuple_list kernel;
00516 unsignedint N,M,h,n,x,y,i;
00517 int xc,yc,j,double_x_size,double_y_size;
00518 double sigma,xx,yy,sum,prec;
00519
00520 /* check parameters */
00521 if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
00522 error("gaussian_sampler: invalid image.");
00523 if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive.");
00524 if( sigma_scale <= 0.0 )
00525 error("gaussian_sampler: 'sigma_scale' must be positive.");
00526
00527 /* get memory for images */
00528 if( in->xsize * scale > (double) UINT_MAX ||
00529 in->ysize * scale > (double) UINT_MAX )
00530 error("gaussian_sampler: the output image size exceeds the handled size.");
00531 N = (unsignedint) floor( in->xsize * scale );
00532 M = (unsignedint) floor( in->ysize * scale );
00533 aux = new_image_double(N,in->ysize);
00534 out = new_image_double(N,M);
00535
00536 /* sigma, kernel size and memory for the kernel */
00537 sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale;
00538 /*
00539 The size of the kernel is selected to guarantee that the
00540 the first discarded term is at least 10^prec times smaller
00541 than the central value. For that, h should be larger than x, with
00542 e^(-x^2/2sigma^2) = 1/10^prec.
00543 Then,
00544 x = sigma * sqrt( 2 * prec * ln(10) ).
00545 */
00546 prec = 3.0;
00547 h = (unsignedint) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) );
00548 n = 1+2*h; /* kernel size */
00549 kernel = new_ntuple_list(n);
00550
00551 /* auxiliary double image size variables */
00552 double_x_size = (int) (2 * in->xsize);
00553 double_y_size = (int) (2 * in->ysize);
00554
00555 /* First subsampling: x axis */
00556 for(x=0;x<aux->xsize;x++)
00557 {
00558 /*
00559 x is the coordinate in the new image.
00560 xx is the corresponding x-value in the original size image.
00561 xc is the integer value, the pixel coordinate of xx.
00562 */
00563 xx = (double) x / scale;
00564 /* coordinate (0.0,0.0) is in the center of pixel (0,0),
00565 so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */
00566 xc = (int) floor( xx + 0.5 );
00567 gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc );
00568 /* the kernel must be computed for each x because the fine
00569 offset xx-xc is different in each case */
00570
00571 for(y=0;y<aux->ysize;y++)
00572 {
00573 sum = 0.0;
00574 for(i=0;i<kernel->dim;i++)
00575 {
00576 j = xc - h + i;
00577
00578 /* symmetry boundary condition */
00579 while( j < 0 ) j += double_x_size;
00580 while( j >= double_x_size ) j -= double_x_size;
00581 if( j >= (int) in->xsize ) j = double_x_size-1-j;
00582
00583 sum += in->data[ j + y * in->xsize ] * kernel->values[i];
00584 }
00585 aux->data[ x + y * aux->xsize ] = sum;
00586 }
00587 }
00588
00589 /* Second subsampling: y axis */
00590 for(y=0;y<out->ysize;y++)
00591 {
00592 /*
00593 y is the coordinate in the new image.
00594 yy is the corresponding x-value in the original size image.
00595 yc is the integer value, the pixel coordinate of xx.
00596 */
00597 yy = (double) y / scale;
00598 /* coordinate (0.0,0.0) is in the center of pixel (0,0),
00599 so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */
00600 yc = (int) floor( yy + 0.5 );
00601 gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc );
00602 /* the kernel must be computed for each y because the fine
00603 offset yy-yc is different in each case */
00604
00605 for(x=0;x<out->xsize;x++)
00606 {
00607 sum = 0.0;
00608 for(i=0;i<kernel->dim;i++)
00609 {
00610 j = yc - h + i;
00611
00612 /* symmetry boundary condition */
00613 while( j < 0 ) j += double_y_size;
00614 while( j >= double_y_size ) j -= double_y_size;
00615 if( j >= (int) in->ysize ) j = double_y_size-1-j;
00616
00617 sum += aux->data[ x + j * aux->xsize ] * kernel->values[i];
00618 }
00619 out->data[ x + y * out->xsize ] = sum;
00620 }
00621 }
00622
00623 /* free memory */
00624 free_ntuple_list(kernel);
00625 free_image_double(aux);
00626
00627 return out;
00628 }
00783 {
00784 double a;
00785
00786 /* check parameters */
00787 if( angles == NULL || angles->data == NULL )
00788 error("isaligned: invalid image 'angles'.");
00789 if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
00790 error("isaligned: (x,y) out of the image.");
00791 if( prec < 0.0 ) error("isaligned: 'prec' must be positive.");
00792
00793 /* angle at pixel (x,y) */
00794 a = angles->data[ x + y * angles->xsize ];
00795
00796 /* pixels whose level-line angle is not defined
00797 are considered as NON-aligned */
00798 if( a == NOTDEF ) returnFALSE; /* there is no need to call the function
00799 'double_equal' here because there is
00800 no risk of problems related to the
00801 comparison doubles, we are only
00802 interested in the exact NOTDEF value */
00803
00804 /* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */
00805 theta -= a;
00806 if( theta < 0.0 ) theta = -theta;
00807 if( theta > M_3_2_PI )
00808 {
00809 theta -= M_2__PI;
00810 if( theta < 0.0 ) theta = -theta;
00811 }
00812
00813 return theta < prec;
00814 }
When different than 1.0, LSD will scale the image by Gaussian filtering. Example: is scale=0.8, the input image will be subsampled to 80% of its size, and then the line segment detector will be applied. Suggested value: 0.8
sigma_scale
When scale!=1.0, the sigma of the Gaussian filter is: sigma = sigma_scale / scale, if scale < 1.0 sigma = sigma_scale, if scale >= 1.0 Suggested value: 0.6
quant
Bound to the quantization error on the gradient norm. Example: if gray level is quantized to integer steps, the gradient (computed by finite differences) error due to quantization will be bounded by 2.0, as the worst case is when the error are 1 and -1, that gives an error of 2.0. Suggested value: 2.0
ang_th
Gradient angle tolerance in the region growing algorithm, in degrees. Suggested value: 22.5
eps
Detection threshold, -log10(NFA). The bigger, the more strict the detector is, and will result in less detections. (Note that the 'minus sign' makes that this behavior is opposite to the one of NFA.) The value -log10(NFA) is equivalent but more intuitive than NFA:
-1.0 corresponds to 10 mean false alarms
0.0 corresponds to 1 mean false alarm
1.0 corresponds to 0.1 mean false alarms
2.0 corresponds to 0.01 mean false alarms
Suggested value: 0.0
density_th
Minimal proportion of region points in a rectangle. Suggested value: 0.7
n_bins
Number of bins used in the pseudo-ordering of gradient modulus. Suggested value: 1024
max_grad
Gradient modulus in the highest bin. For example, for images with integer gray levels in [0,255], the maximum possible gradient value is 255.0. Suggested value: 255.0
region
Optional output: an int image where the pixels used in some line support region are marked. Unused pixels have the value '0' while the used ones have the number of the line segment, numbered 1,2,3,... If desired, a non NULL pointer to an image_int should be used. The resulting image has the size of the image used for the processing, that is, the size of the input image scaled by the given factor 'scale'. Suggested value: NULL
Returns:
A 5-tuple list, where each 5-tuple corresponds to a detected line segment. The five values are:
x1,y1,x2,y2,width
for a line segment from (x1,y1) to (x2,y2) and a width 'width'.
01918 {
01919 ntuple_list out = new_ntuple_list(5);
01920 image_double scaled_image,angles,modgrad;
01921 image_char used;
01922 struct coorlist * list_p;
01923 void * mem_p;
01924 struct rect rec;
01925 struct point * reg;
01926 int reg_size,min_reg_size,i;
01927 unsignedint xsize,ysize;
01928 double rho,reg_angle,prec,p,log_nfa,logNT;
01929 int ls_count = 0; /* line segments are numbered 1,2,3,... */
01930
01931
01932 /* check parameters */
01933 if( image==NULL || image->data==NULL || image->xsize==0 || image->ysize==0 )
01934 error("invalid image input.");
01935 if( scale <= 0.0 ) error("'scale' value must be positive.");
01936 if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
01937 if( quant < 0.0 ) error("'quant' value must be positive.");
01938 if( ang_th <= 0.0 || ang_th >= 180.0 )
01939 error("'ang_th' value must be in the range (0,180).");
01940 if( density_th < 0.0 || density_th > 1.0 )
01941 error("'density_th' value must be in the range [0,1].");
01942 if( n_bins <= 0 ) error("'n_bins' value must be positive.");
01943 if( max_grad <= 0.0 ) error("'max_grad' value must be positive.");
01944
01945
01946 /* angle tolerance */
01947 prec = M_PI * ang_th / 180.0;
01948 p = ang_th / 180.0;
01949 rho = quant / sin(prec); /* gradient magnitude threshold */
01950
01951
01952 /* scale image (if necessary) and compute angle at each pixel */
01953 if( scale != 1.0 )
01954 {
01955 scaled_image = gaussian_sampler( image, scale, sigma_scale );
01956 angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
01957 &modgrad, (unsignedint) n_bins, max_grad );
01958 free_image_double(scaled_image);
01959 }
01960 else
01961 angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
01962 (unsignedint) n_bins, max_grad );
01963 xsize = angles->xsize;
01964 ysize = angles->ysize;
01965 logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0;
01966 min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
01967 that can give a meaningful event */
01968
01969
01970 /* initialize some structures */
01971 if( region != NULL ) /* image to output pixel region number, if asked */
01972 *region = new_image_int_ini(angles->xsize,angles->ysize,0);
01973 used = new_image_char_ini(xsize,ysize,NOTUSED);
01974 reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(structpoint) );
01975 if( reg == NULL ) error("not enough memory!");
01976
01977
01978 /* search for line segments */
01979 for(; list_p != NULL; list_p = list_p->next )
01980 if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
01981 angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
01982 /* there is no risk of double comparison problems here
01983 because we are only interested in the exact NOTDEF value */
01984 {
01985 /* find the region of connected point and ~equal angle */
01986 region_grow( list_p->x, list_p->y, angles, reg, ®_size,
01987 ®_angle, used, prec );
01988
01989 /* reject small regions */
01990 if( reg_size < min_reg_size ) continue;
01991
01992 /* construct rectangular approximation for the region */
01993 region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
01994
01995 /* Check if the rectangle exceeds the minimal density of
01996 region points. If not, try to improve the region.
01997 The rectangle will be rejected if the final one does
01998 not fulfill the minimal density condition.
01999 This is an addition to the original LSD algorithm published in
02000 "LSD: A Fast Line Segment Detector with a False Detection Control"
02001 by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
02002 The original algorithm is obtained with density_th = 0.0.
02003 */
02004 if( !refine( reg, ®_size, modgrad, reg_angle,
02005 prec, p, &rec, used, angles, density_th ) ) continue;
02006
02007 /* compute NFA value */
02008 log_nfa = rect_improve(&rec,angles,logNT,eps);
02009 if( log_nfa <= eps ) continue;
02010
02011 /* A New Line Segment was found! */
02012 ++ls_count; /* increase line segment counter */
02013
02014 /*
02015 The gradient was computed with a 2x2 mask, its value corresponds to
02016 points with an offset of (0.5,0.5), that should be added to output.
02017 The coordinates origin is at the center of pixel (0,0).
02018 */
02019 rec.x1 += 0.5; rec.y1 += 0.5;
02020 rec.x2 += 0.5; rec.y2 += 0.5;
02021
02022 /* scale the result values if a subsampling was performed */
02023 if( scale != 1.0 )
02024 {
02025 rec.x1 /= scale; rec.y1 /= scale;
02026 rec.x2 /= scale; rec.y2 /= scale;
02027 rec.width /= scale;
02028 }
02029
02030 /* add line segment found to output */
02031 add_5tuple(out, rec.x1, rec.y1, rec.x2, rec.y2, rec.width);
02032
02033 /* add region number to 'region' image if needed */
02034 if( region != NULL )
02035 for(i=0; i<reg_size; i++)
02036 (*region)->data[reg[i].x+reg[i].y*(*region)->xsize] = ls_count;
02037 }
02038
02039
02040 /* free memory */
02041 free_image_double(angles);
02042 free_image_double(modgrad);
02043 free_image_char(used);
02044 free( (void *) reg );
02045 free( (void *) mem_p );
02046
02047 return out;
02048 }
Computes the direction of the level line of 'in' at each point.
The result is:
an image_double with the angle at each pixel, or NOTDEF if not defined.
the image_double 'modgrad' (a pointer is passed as argument) with the gradient magnitude at each point.
a list of pixels 'list_p' roughly ordered by decreasing gradient magnitude. (The order is made by classifying points into bins by gradient magnitude. The parameters 'n_bins' and 'max_grad' specify the number of bins and the gradient modulus at the highest bin. The pixels in the list would be in decreasing gradient magnitude, up to a precision of the size of the bins.)
a pointer 'mem_p' to the memory used by 'list_p' to be able to free the memory when it is not used anymore.
When different than 1.0, LSD will scale the image by Gaussian filtering. Example: is scale=0.8, the input image will be subsampled to 80% of its size, and then the line segment detector will be applied. Suggested value: 0.8
B(n,k,p) - tail of binomial distribution with parameters n,k and p:
The value -log10(NFA) is equivalent but more intuitive than NFA:
-1 corresponds to 10 mean false alarms
0 corresponds to 1 mean false alarm
1 corresponds to 0.1 mean false alarms
2 corresponds to 0.01 mean false alarms
...
Used this way, the bigger the value, better the detection, and a logarithmic scale is used.
Parameters:
n,k,p
binomial parameters.
logNT
logarithm of Number of Tests
The computation is based in the gamma function by the following relation:
We use efficient algorithms to compute the logarithm of the gamma function.
To make the computation faster, not all the sum is computed, part of the terms are neglected based on a bound to the error obtained (an error of 10% in the result is accepted).
00963 {
00964 staticdouble inv[TABSIZE]; /* table to keep computed inverse values */
00965 double tolerance = 0.1; /* an error of 10% in the result is accepted */
00966 double log1term,term,bin_term,mult_term,bin_tail,err,p_term;
00967 int i;
00968
00969 /* check parameters */
00970 if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 )
00971 error("nfa: wrong n, k or p values.");
00972
00973 /* trivial cases */
00974 if( n==0 || k==0 ) return -logNT;
00975 if( n==k ) return -logNT - (double) n * log10(p);
00976
00977 /* probability term */
00978 p_term = p / (1.0-p);
00979
00980 /* compute the first term of the series */
00981 /*
00982 binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
00983 where bincoef(n,i) are the binomial coefficients.
00984 But
00985 bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
00986 We use this to compute the first term. Actually the log of it.
00987 */
00988 log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 )
00989 - log_gamma( (double) (n-k) + 1.0 )
00990 + (double) k * log(p) + (double) (n-k) * log(1.0-p);
00991 term = exp(log1term);
00992
00993 /* in some cases no more computations are needed */
00994 if( double_equal(term,0.0) ) /* the first term is almost zero */
00995 {
00996 if( (double) k > (double) n * p ) /* at begin or end of the tail? */
00997 return -log1term / M_LN10 - logNT; /* end: use just the first term */
00998 else
00999 return -logNT; /* begin: the tail is roughly 1 */
01000 }
01001
01002 /* compute more terms if needed */
01003 bin_tail = term;
01004 for(i=k+1;i<=n;i++)
01005 {
01006 /*
01007 As
01008 term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
01009 and
01010 bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
01011 then,
01012 term_i / term_i-1 = (n-i+1)/i * p/(1-p)
01013 and
01014 term_i = term_i-1 * (n-i+1)/i * p/(1-p).
01015 1/i is stored in a table as they are computed,
01016 because divisions are expensive.
01017 p/(1-p) is computed only once and stored in 'p_term'.
01018 */
01019 bin_term = (double) (n-i+1) * ( i<TABSIZE ?
01020 ( inv[i]!=0.0 ? inv[i] : ( inv[i] = 1.0 / (double) i ) ) :
01021 1.0 / (double) i );
01022
01023 mult_term = bin_term * p_term;
01024 term *= mult_term;
01025 bin_tail += term;
01026 if(bin_term<1.0)
01027 {
01028 /* When bin_term<1 then mult_term_j<mult_term_i for j>i.
01029 Then, the error on the binomial tail when truncated at
01030 the i term can be bounded by a geometric series of form
01031 term_i * sum mult_term_i^j. */
01032 err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) /
01033 (1.0-mult_term) - 1.0 );
01034
01035 /* One wants an error at most of tolerance*final_result, or:
01036 tolerance * abs(-log10(bin_tail)-logNT).
01037 Now, the error that can be accepted on bin_tail is
01038 given by tolerance*final_result divided by the derivative
01039 of -log10(x) when x=bin_tail. that is:
01040 tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
01041 Finally, we truncate the tail if the error is less than:
01042 tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
01043 if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break;
01044 }
01045 }
01046 return -log10(bin_tail) - logNT;
01047 }
Reduce the region size, by elimination the points far from the starting point, until that leads to rectangle with the right density of region points or to discard the region if too small.
For that, an estimation of the angle tolerance is performed by the standard deviation of the angle at points near the region's starting point. Then, a new region is grown starting from the same point, but using the estimated angle tolerance. If this fails to produce a rectangle with the right density of region points, 'reduce_region_radius' is called to try to satisfy this condition.
01502 {
01503 double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max;
01504 int i;
01505
01506 /* check parameters */
01507 if( reg == NULL ) error("region2rect: invalid region.");
01508 if( reg_size <= 1 ) error("region2rect: region size <= 1.");
01509 if( modgrad == NULL || modgrad->data == NULL )
01510 error("region2rect: invalid image 'modgrad'.");
01511 if( rec == NULL ) error("region2rect: invalid 'rec'.");
01512
01513 /* center of the region:
01514
01515 It is computed as the weighted sum of the coordinates
01516 of all the pixels in the region. The norm of the gradient
01517 is used as the weight of a pixel. The sum is as follows:
01518 cx = \sum_i G(i).x_i
01519 cy = \sum_i G(i).y_i
01520 where G(i) is the norm of the gradient of pixel i
01521 and x_i,y_i are its coordinates.
01522 */
01523 x = y = sum = 0.0;
01524 for(i=0; i<reg_size; i++)
01525 {
01526 weight = modgrad->data[ reg[i].x + reg[i].y * modgrad->xsize ];
01527 x += (double) reg[i].x * weight;
01528 y += (double) reg[i].y * weight;
01529 sum += weight;
01530 }
01531 if( sum <= 0.0 ) error("region2rect: weights sum equal to zero.");
01532 x /= sum;
01533 y /= sum;
01534
01535 /* theta */
01536 theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec);
01537
01538 /* length and width:
01539
01540 'l' and 'w' are computed as the distance from the center of the
01541 region to pixel i, projected along the rectangle axis (dx,dy) and
01542 to the orthogonal axis (-dy,dx), respectively.
01543
01544 The length of the rectangle goes from l_min to l_max, where l_min
01545 and l_max are the minimum and maximum values of l in the region.
01546 Analogously, the width is selected from w_min to w_max, where
01547 w_min and w_max are the minimum and maximum of w for the pixels
01548 in the region.
01549 */
01550 dx = cos(theta);
01551 dy = sin(theta);
01552 l_min = l_max = w_min = w_max = 0.0;
01553 for(i=0; i<reg_size; i++)
01554 {
01555 l = ( (double) reg[i].x - x) * dx + ( (double) reg[i].y - y) * dy;
01556 w = -( (double) reg[i].x - x) * dy + ( (double) reg[i].y - y) * dx;
01557
01558 if( l > l_max ) l_max = l;
01559 if( l < l_min ) l_min = l;
01560 if( w > w_max ) w_max = w;
01561 if( w < w_min ) w_min = w;
01562 }
01563
01564 /* store values */
01565 rec->x1 = x + l_min * dx;
01566 rec->y1 = y + l_min * dy;
01567 rec->x2 = x + l_max * dx;
01568 rec->y2 = y + l_max * dy;
01569 rec->width = w_max - w_min;
01570 rec->x = x;
01571 rec->y = y;
01572 rec->theta = theta;
01573 rec->dx = dx;
01574 rec->dy = dy;
01575 rec->prec = prec;
01576 rec->p = p;
01577
01578 /* we impose a minimal width of one pixel
01579
01580 A sharp horizontal or vertical step would produce a perfectly
01581 horizontal or vertical region. The width computed would be
01582 zero. But that corresponds to a one pixels width transition in
01583 the image.
01584 */
01585 if( rec->width < 1.0 ) rec->width = 1.0;
01586 }
01214 {
01215 /* check input */
01216 if( i == NULL ) error("ri_end: NULL iterator.");
01217
01218 /* if the current x value is larger than the larger
01219 x value in the rectangle (vx[2]), we know the full
01220 exploration of the rectangle is finished. */
01221 return (double)(i->x) > i->vx[2];
01222 }
01230 {
01231 /* check input */
01232 if( i == NULL ) error("ri_inc: NULL iterator.");
01233
01234 /* if not at end of exploration,
01235 increase y value for next pixel in the 'column' */
01236 if( !ri_end(i) ) i->y++;
01237
01238 /* if the end of the current 'column' is reached,
01239 and it is not the end of exploration,
01240 advance to the next 'column' */
01241 while( (double) (i->y) > i->ye && !ri_end(i) )
01242 {
01243 /* increase x, next 'column' */
01244 i->x++;
01245
01246 /* if end of exploration, return */
01247 if( ri_end(i) ) return;
01248
01249 /* update lower y limit (start) for the new 'column'.
01250
01251 We need to interpolate the y value that corresponds to the
01252 lower side of the rectangle. The first thing is to decide if
01253 the corresponding side is
01254
01255 vx[0],vy[0] to vx[3],vy[3] or
01256 vx[3],vy[3] to vx[2],vy[2]
01257
01258 Then, the side is interpolated for the x value of the
01259 'column'. But, if the side is vertical (as it could happen if
01260 the rectangle is vertical and we are dealing with the first
01261 or last 'columns') then we pick the lower value of the side
01262 by using 'inter_low'.
01263 */
01264 if( (double) i->x < i->vx[3] )
01265 i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]);
01266 else
01267 i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]);
01268
01269 /* update upper y limit (end) for the new 'column'.
01270
01271 We need to interpolate the y value that corresponds to the
01272 upper side of the rectangle. The first thing is to decide if
01273 the corresponding side is
01274
01275 vx[0],vy[0] to vx[1],vy[1] or
01276 vx[1],vy[1] to vx[2],vy[2]
01277
01278 Then, the side is interpolated for the x value of the
01279 'column'. But, if the side is vertical (as it could happen if
01280 the rectangle is vertical and we are dealing with the first
01281 or last 'columns') then we pick the lower value of the side
01282 by using 'inter_low'.
01283 */
01284 if( (double)i->x < i->vx[1] )
01285 i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]);
01286 else
01287 i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]);
01288
01289 /* new y */
01290 i->y = (int) ceil(i->ys);
01291 }
01292 }
01300 {
01301 double vx[4],vy[4];
01302 int n,offset;
01303 rect_iter * i;
01304
01305 /* check parameters */
01306 if( r == NULL ) error("ri_ini: invalid rectangle.");
01307
01308 /* get memory */
01309 i = (rect_iter *) malloc(sizeof(rect_iter));
01310 if( i == NULL ) error("ri_ini: Not enough memory.");
01311
01312 /* build list of rectangle corners ordered
01313 in a circular way around the rectangle */
01314 vx[0] = r->x1 - r->dy * r->width / 2.0;
01315 vy[0] = r->y1 + r->dx * r->width / 2.0;
01316 vx[1] = r->x2 - r->dy * r->width / 2.0;
01317 vy[1] = r->y2 + r->dx * r->width / 2.0;
01318 vx[2] = r->x2 + r->dy * r->width / 2.0;
01319 vy[2] = r->y2 - r->dx * r->width / 2.0;
01320 vx[3] = r->x1 + r->dy * r->width / 2.0;
01321 vy[3] = r->y1 - r->dx * r->width / 2.0;
01322
01323 /* compute rotation of index of corners needed so that the first
01324 point has the smaller x.
01325
01326 if one side is vertical, thus two corners have the same smaller x
01327 value, the one with the largest y value is selected as the first.
01328 */
01329 if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0;
01330 elseif( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1;
01331 elseif( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2;
01332 else offset = 3;
01333
01334 /* apply rotation of index. */
01335 for(n=0; n<4; n++)
01336 {
01337 i->vx[n] = vx[(offset+n)%4];
01338 i->vy[n] = vy[(offset+n)%4];
01339 }
01340
01341 /* Set a initial condition.
01342
01343 The values are set to values that will cause 'ri_inc' (that will
01344 be called immediately) to initialize correctly the first 'column'
01345 and compute the limits 'ys' and 'ye'.
01346
01347 'y' is set to the integer value of vy[0], the starting corner.
01348
01349 'ys' and 'ye' are set to very small values, so 'ri_inc' will
01350 notice that it needs to start a new 'column'.
01351
01352 The smaller integer coordinate inside of the rectangle is
01353 'ceil(vx[0])'. The current 'x' value is set to that value minus
01354 one, so 'ri_inc' (that will increase x by one) will advance to
01355 the first 'column'.
01356 */
01357 i->x = (int) ceil(i->vx[0]) - 1;
01358 i->y = (int) ceil(i->vy[0]);
01359 i->ys = i->ye = -DBL_MAX;
01360
01361 /* advance to the first pixel */
01362 ri_inc(i);
01363
01364 return i;
01365 }
Here is the call graph for this function:
Generated on Fri Dec 3 10:18:38 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/lsd_8h-source.html
================================================
LSD: lsd.h Source File
00001 /*----------------------------------------------------------------------------
00002
00003 LSD - Line Segment Detector on digital images
00004
00005 Copyright 2007-2010 rafael grompone von gioi (grompone@gmail.com)
00006
00007 This program is free software: you can redistribute it and/or modify
00008 it under the terms of the GNU Affero General Public License as
00009 published by the Free Software Foundation, either version 3 of the
00010 License, or (at your option) any later version.
00011
00012 This program is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015 GNU Affero General Public License for more details.
00016
00017 You should have received a copy of the GNU Affero General Public License
00018 along with this program. If not, see <http://www.gnu.org/licenses/>.
00019
00020 ----------------------------------------------------------------------------*/
00021
00022 /*----------------------------------------------------------------------------*/
00023 /** @file lsd.h
00024 LSD module header
00025 @author rafael grompone von gioi (grompone@gmail.com)
00026 */
00027 /*----------------------------------------------------------------------------*/
00028 #ifndef LSD_HEADER
00029 #define LSD_HEADER
00030
00031
00032 /*----------------------------------------------------------------------------*/
00033 /*----------------------- 'list of n-tuple' data type ------------------------*/
00034 /*----------------------------------------------------------------------------*/
00035 /** 'list of n-tuple' data type
00036
00037 The i component, of the n-tuple number j, of an n-tuple list 'ntl'
00038 is accessed with:
00039
00040 ntl->values[ i + j * ntl->dim ]
00041
00042 The dimension of the n-tuple (n) is:
00043
00044 ntl->dim
00045
00046 The number of number of n-tuples in the list is:
00047
00048 ntl->size
00049
00050 The maximum number of n-tuples that can be stored in the
00051 list with the allocated memory at a given time is given by:
00052
00053 ntl->max_size
00054 */00055typedefstruct ntuple_list_s
00056 {
00057unsignedintsize;
00058unsignedintmax_size;
00059unsignedintdim;
00060double * values;
00061 } * ntuple_list;
00062
00063 voidfree_ntuple_list(ntuple_list in);
00064 ntuple_listnew_ntuple_list(unsignedint dim);
00065
00066
00067 /*----------------------------------------------------------------------------*/
00068 /*----------------------------- Image Data Types -----------------------------*/
00069 /*----------------------------------------------------------------------------*/
00070
00071 /*----------------------------------------------------------------------------*/
00072 /** char image data type
00073
00074 The pixel value at (x,y) is accessed by:
00075
00076 image->data[ x + y * image->xsize ]
00077
00078 with x and y integer.
00079 */00080typedefstruct image_char_s
00081 {
00082unsignedchar * data;
00083unsignedintxsize,ysize;
00084 } * image_char;
00085
00086 voidfree_image_char(image_char i);
00087 image_charnew_image_char(unsignedint xsize, unsignedint ysize);
00088 image_charnew_image_char_ini( unsignedint xsize, unsignedint ysize,
00089 unsignedchar fill_value );
00090
00091 /*----------------------------------------------------------------------------*/
00092 /** int image data type
00093
00094 The pixel value at (x,y) is accessed by:
00095
00096 image->data[ x + y * image->xsize ]
00097
00098 with x and y integer.
00099 */00100typedefstruct image_int_s
00101 {
00102int * data;
00103unsignedintxsize,ysize;
00104 } * image_int;
00105
00106 voidfree_image_int(image_int i);
00107 image_intnew_image_int(unsignedint xsize, unsignedint ysize);
00108 image_intnew_image_int_ini( unsignedint xsize, unsignedint ysize,
00109 int fill_value );
00110
00111 /*----------------------------------------------------------------------------*/
00112 /** double image data type
00113
00114 The pixel value at (x,y) is accessed by:
00115
00116 image->data[ x + y * image->xsize ]
00117
00118 with x and y integer.
00119 */00120typedefstruct image_double_s
00121 {
00122double * data;
00123unsignedintxsize,ysize;
00124 } * image_double;
00125
00126 voidfree_image_double(image_double i);
00127 image_doublenew_image_double(unsignedint xsize, unsignedint ysize);
00128 image_doublenew_image_double_ini( unsignedint xsize, unsignedint ysize,
00129 double fill_value );
00130
00131
00132 /*----------------------------------------------------------------------------*/
00133 /*-------------------------- Line Segment Detector ---------------------------*/
00134 /*----------------------------------------------------------------------------*/
00135
00136 /*----------------------------------------------------------------------------*/
00137 /* LSD Full Interface */
00138 /*----------------------------------------------------------------------------*/
00139 /** LSD Full Interface
00140
00141 @param image Input image.
00142
00143 @param scale When different than 1.0, LSD will scale the image by
00144 Gaussian filtering.
00145 Example: is scale=0.8, the input image will be subsampled
00146 to 80% of its size, and then the line segment detector
00147 will be applied.
00148 Suggested value: 0.8
00149
00150 @param sigma_scale When scale!=1.0, the sigma of the Gaussian filter is:
00151 sigma = sigma_scale / scale, if scale < 1.0
00152 sigma = sigma_scale, if scale >= 1.0
00153 Suggested value: 0.6
00154
00155 @param quant Bound to the quantization error on the gradient norm.
00156 Example: if gray level is quantized to integer steps,
00157 the gradient (computed by finite differences) error
00158 due to quantization will be bounded by 2.0, as the
00159 worst case is when the error are 1 and -1, that
00160 gives an error of 2.0.
00161 Suggested value: 2.0
00162
00163 @param ang_th Gradient angle tolerance in the region growing
00164 algorithm, in degrees.
00165 Suggested value: 22.5
00166
00167 @param eps Detection threshold, -log10(NFA).
00168 The bigger, the more strict the detector is,
00169 and will result in less detections.
00170 (Note that the 'minus sign' makes that this
00171 behavior is opposite to the one of NFA.)
00172 The value -log10(NFA) is equivalent but more
00173 intuitive than NFA:
00174 - -1.0 corresponds to 10 mean false alarms
00175 - 0.0 corresponds to 1 mean false alarm
00176 - 1.0 corresponds to 0.1 mean false alarms
00177 - 2.0 corresponds to 0.01 mean false alarms
00178 .
00179 Suggested value: 0.0
00180
00181 @param density_th Minimal proportion of region points in a rectangle.
00182 Suggested value: 0.7
00183
00184 @param n_bins Number of bins used in the pseudo-ordering of gradient
00185 modulus.
00186 Suggested value: 1024
00187
00188 @param max_grad Gradient modulus in the highest bin. For example,
00189 for images with integer gray levels in [0,255],
00190 the maximum possible gradient value is 255.0.
00191 Suggested value: 255.0
00192
00193 @param region Optional output: an int image where the pixels used
00194 in some line support region are marked. Unused pixels
00195 have the value '0' while the used ones have the
00196 number of the line segment, numbered 1,2,3,...
00197 If desired, a non NULL pointer to an image_int should
00198 be used. The resulting image has the size of the image
00199 used for the processing, that is, the size of the input
00200 image scaled by the given factor 'scale'.
00201 Suggested value: NULL
00202
00203 @return A 5-tuple list, where each 5-tuple corresponds to a
00204 detected line segment. The five values are:
00205 - x1,y1,x2,y2,width
00206 .
00207 for a line segment from (x1,y1) to (x2,y2) and
00208 a width 'width'.
00209 */
00210 ntuple_listLineSegmentDetection( image_double image, double scale,
00211 double sigma_scale, double quant,
00212 double ang_th, double eps, double density_th,
00213 int n_bins, double max_grad,
00214 image_int * region );
00215
00216 /*----------------------------------------------------------------------------*/
00217 /* LSD Simple Interface with Scale */
00218 /*----------------------------------------------------------------------------*/
00219 /** LSD Simple Interface with Scale
00220
00221 @param image Input image.
00222
00223 @param scale When different than 1.0, LSD will scale the image by
00224 Gaussian filtering.
00225 Example: is scale=0.8, the input image will be subsampled
00226 to 80% of its size, and then the line segment detector
00227 will be applied.
00228 Suggested value: 0.8
00229
00230 @return a 5-tuple list of detected line segments.
00231 */
00232 ntuple_listlsd_scale(image_double image, double scale);
00233
00234 /*----------------------------------------------------------------------------*/
00235 /* LSD Simple Interface */
00236 /*----------------------------------------------------------------------------*/
00237 /** LSD Simple Interface
00238
00239 @param image Input image.
00240
00241 @return a 5-tuple list of detected line segments.
00242 */
00243 ntuple_listlsd(image_double image);
00244
00245 #endif /* !LSD_HEADER */
00246 /*----------------------------------------------------------------------------*/
Generated on Fri Dec 3 10:18:18 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/lsd_8h.html
================================================
LSD: lsd.h File Reference
When different than 1.0, LSD will scale the image by Gaussian filtering. Example: is scale=0.8, the input image will be subsampled to 80% of its size, and then the line segment detector will be applied. Suggested value: 0.8
sigma_scale
When scale!=1.0, the sigma of the Gaussian filter is: sigma = sigma_scale / scale, if scale < 1.0 sigma = sigma_scale, if scale >= 1.0 Suggested value: 0.6
quant
Bound to the quantization error on the gradient norm. Example: if gray level is quantized to integer steps, the gradient (computed by finite differences) error due to quantization will be bounded by 2.0, as the worst case is when the error are 1 and -1, that gives an error of 2.0. Suggested value: 2.0
ang_th
Gradient angle tolerance in the region growing algorithm, in degrees. Suggested value: 22.5
eps
Detection threshold, -log10(NFA). The bigger, the more strict the detector is, and will result in less detections. (Note that the 'minus sign' makes that this behavior is opposite to the one of NFA.) The value -log10(NFA) is equivalent but more intuitive than NFA:
-1.0 corresponds to 10 mean false alarms
0.0 corresponds to 1 mean false alarm
1.0 corresponds to 0.1 mean false alarms
2.0 corresponds to 0.01 mean false alarms
Suggested value: 0.0
density_th
Minimal proportion of region points in a rectangle. Suggested value: 0.7
n_bins
Number of bins used in the pseudo-ordering of gradient modulus. Suggested value: 1024
max_grad
Gradient modulus in the highest bin. For example, for images with integer gray levels in [0,255], the maximum possible gradient value is 255.0. Suggested value: 255.0
region
Optional output: an int image where the pixels used in some line support region are marked. Unused pixels have the value '0' while the used ones have the number of the line segment, numbered 1,2,3,... If desired, a non NULL pointer to an image_int should be used. The resulting image has the size of the image used for the processing, that is, the size of the input image scaled by the given factor 'scale'. Suggested value: NULL
Returns:
A 5-tuple list, where each 5-tuple corresponds to a detected line segment. The five values are:
x1,y1,x2,y2,width
for a line segment from (x1,y1) to (x2,y2) and a width 'width'.
01918 {
01919 ntuple_list out = new_ntuple_list(5);
01920 image_double scaled_image,angles,modgrad;
01921 image_char used;
01922 struct coorlist * list_p;
01923 void * mem_p;
01924 struct rect rec;
01925 struct point * reg;
01926 int reg_size,min_reg_size,i;
01927 unsignedint xsize,ysize;
01928 double rho,reg_angle,prec,p,log_nfa,logNT;
01929 int ls_count = 0; /* line segments are numbered 1,2,3,... */
01930
01931
01932 /* check parameters */
01933 if( image==NULL || image->data==NULL || image->xsize==0 || image->ysize==0 )
01934 error("invalid image input.");
01935 if( scale <= 0.0 ) error("'scale' value must be positive.");
01936 if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
01937 if( quant < 0.0 ) error("'quant' value must be positive.");
01938 if( ang_th <= 0.0 || ang_th >= 180.0 )
01939 error("'ang_th' value must be in the range (0,180).");
01940 if( density_th < 0.0 || density_th > 1.0 )
01941 error("'density_th' value must be in the range [0,1].");
01942 if( n_bins <= 0 ) error("'n_bins' value must be positive.");
01943 if( max_grad <= 0.0 ) error("'max_grad' value must be positive.");
01944
01945
01946 /* angle tolerance */
01947 prec = M_PI * ang_th / 180.0;
01948 p = ang_th / 180.0;
01949 rho = quant / sin(prec); /* gradient magnitude threshold */
01950
01951
01952 /* scale image (if necessary) and compute angle at each pixel */
01953 if( scale != 1.0 )
01954 {
01955 scaled_image = gaussian_sampler( image, scale, sigma_scale );
01956 angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
01957 &modgrad, (unsignedint) n_bins, max_grad );
01958 free_image_double(scaled_image);
01959 }
01960 else
01961 angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
01962 (unsignedint) n_bins, max_grad );
01963 xsize = angles->xsize;
01964 ysize = angles->ysize;
01965 logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0;
01966 min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
01967 that can give a meaningful event */
01968
01969
01970 /* initialize some structures */
01971 if( region != NULL ) /* image to output pixel region number, if asked */
01972 *region = new_image_int_ini(angles->xsize,angles->ysize,0);
01973 used = new_image_char_ini(xsize,ysize,NOTUSED);
01974 reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(structpoint) );
01975 if( reg == NULL ) error("not enough memory!");
01976
01977
01978 /* search for line segments */
01979 for(; list_p != NULL; list_p = list_p->next )
01980 if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
01981 angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
01982 /* there is no risk of double comparison problems here
01983 because we are only interested in the exact NOTDEF value */
01984 {
01985 /* find the region of connected point and ~equal angle */
01986 region_grow( list_p->x, list_p->y, angles, reg, ®_size,
01987 ®_angle, used, prec );
01988
01989 /* reject small regions */
01990 if( reg_size < min_reg_size ) continue;
01991
01992 /* construct rectangular approximation for the region */
01993 region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
01994
01995 /* Check if the rectangle exceeds the minimal density of
01996 region points. If not, try to improve the region.
01997 The rectangle will be rejected if the final one does
01998 not fulfill the minimal density condition.
01999 This is an addition to the original LSD algorithm published in
02000 "LSD: A Fast Line Segment Detector with a False Detection Control"
02001 by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
02002 The original algorithm is obtained with density_th = 0.0.
02003 */
02004 if( !refine( reg, ®_size, modgrad, reg_angle,
02005 prec, p, &rec, used, angles, density_th ) ) continue;
02006
02007 /* compute NFA value */
02008 log_nfa = rect_improve(&rec,angles,logNT,eps);
02009 if( log_nfa <= eps ) continue;
02010
02011 /* A New Line Segment was found! */
02012 ++ls_count; /* increase line segment counter */
02013
02014 /*
02015 The gradient was computed with a 2x2 mask, its value corresponds to
02016 points with an offset of (0.5,0.5), that should be added to output.
02017 The coordinates origin is at the center of pixel (0,0).
02018 */
02019 rec.x1 += 0.5; rec.y1 += 0.5;
02020 rec.x2 += 0.5; rec.y2 += 0.5;
02021
02022 /* scale the result values if a subsampling was performed */
02023 if( scale != 1.0 )
02024 {
02025 rec.x1 /= scale; rec.y1 /= scale;
02026 rec.x2 /= scale; rec.y2 /= scale;
02027 rec.width /= scale;
02028 }
02029
02030 /* add line segment found to output */
02031 add_5tuple(out, rec.x1, rec.y1, rec.x2, rec.y2, rec.width);
02032
02033 /* add region number to 'region' image if needed */
02034 if( region != NULL )
02035 for(i=0; i<reg_size; i++)
02036 (*region)->data[reg[i].x+reg[i].y*(*region)->xsize] = ls_count;
02037 }
02038
02039
02040 /* free memory */
02041 free_image_double(angles);
02042 free_image_double(modgrad);
02043 free_image_char(used);
02044 free( (void *) reg );
02045 free( (void *) mem_p );
02046
02047 return out;
02048 }
When different than 1.0, LSD will scale the image by Gaussian filtering. Example: is scale=0.8, the input image will be subsampled to 80% of its size, and then the line segment detector will be applied. Suggested value: 0.8
Generated on Fri Dec 3 10:18:48 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structcoorlist.html
================================================
LSD: coorlist struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structimage__char__s.html
================================================
LSD: image_char_s struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structimage__double__s.html
================================================
LSD: image_double_s struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structimage__int__s.html
================================================
LSD: image_int_s struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structntuple__list__s.html
================================================
LSD: ntuple_list_s struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structpoint.html
================================================
LSD: point struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structrect.html
================================================
LSD: rect struct Reference
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doc/structrect__iter.html
================================================
LSD: rect_iter struct Reference
The integer coordinates of pixels inside a rectangle are iteratively explored. This structure keep track of the process and functions ri_ini(), ri_inc(), ri_end(), and ri_del() are used in the process. An example of how to use the iterator is as follows:
struct rect * rec = XXX; // some rectanglerect_iter * i;
for( i=ri_ini(rec); !ri_end(i); ri_inc(i) )
{
// your code, using 'i->x' and 'i->y' as coordinates
}
ri_del(i); // delete iterator
The pixels are explored 'column' by 'column', where we call 'column' a set of pixels with the same x value that are inside the rectangle. The following is an schematic representation of a rectangle, the 'column' being explored is marked by colons, and the current pixel being explored is 'x,y'.
The first 'column' to be explored is the one with the smaller x value. Each 'column' is explored starting from the pixel of the 'column' (inside the rectangle) with the smaller y value.
The four corners of the rectangle are stored in order that rotates around the corners at the arrays 'vx[]' and 'vy[]'. The first point is always the one with smaller x value.
'x' and 'y' are the coordinates of the pixel being explored. 'ys' and 'ye' are the start and end values of the current column being explored. So, 'ys' < 'ye'.
Generated on Fri Dec 3 10:18:49 2010 for LSD by
1.3.4
================================================
FILE: thirdparty/LSD/doxygen.config
================================================
# Doxyfile 1.3.4
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = LSD
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY =
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
# This tag can be used to specify the encoding used in the generated output.
# The encoding is not always determined by the language that is chosen,
# but also whether or not the output is meant for Windows or non-Windows users.
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
# forces the Windows encoding (this is the default for the Windows binary),
# whereas setting the tag to NO uses a Unix-style encoding (the default for
# all platforms other than Windows).
USE_WINDOWS_ENCODING = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
# members of a class in the documentation of that class as if those members were
# ordinary class members. Constructors, destructors and assignment operators of
# the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user-defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explict @brief command for a brief description.
JAVADOC_AUTOBRIEF = YES
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = YES
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# reimplements.
INHERIT_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 8
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = YES
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
# only. Doxygen will then generate output that is more tailored for Java.
# For instance, namespaces will be presented as packages, qualified scopes
# will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = YES
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
# friend (class|struct|union) declarations.
# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
# documentation blocks found inside the body of a function.
# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consists of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some
# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = lsd.h lsd.c
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
FILE_PATTERNS =
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
# that are symbolic links (a Unix filesystem feature) are excluded from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command , where
# is the value of the INPUT_FILTER tag, and is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = YES
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = NO
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = doc
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You
# can add a path in front of the file if the result should not be
# written to the html output dir.
CHM_FILE =
# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
# be used to specify the location (absolute path including file name) of
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
HHC_LOCATION =
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 180
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimised for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assigments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
# generate a Perl module file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader. This is useful
# if you want to understand what is going on. On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
# function macros are typically used for boiler-plate code, and will confuse the
# parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES option can be used to specify one or more tagfiles.
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off. Note that this
# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
# recommended to install and use dot, since it yields more powerful graphs.
CLASS_DIAGRAMS = YES
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = YES
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similiar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
# generate a call dependency graph for every global function or class method.
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
CALL_GRAPH = YES
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
# graphs generated by dot. A depth value of 3 means that only nodes reachable
# from the root by following a path via at most 3 edges will be shown. Nodes that
# lay further from the root node will be omitted. Note that setting this option to
# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
# note that a graph may be further truncated if the graph's image dimensions are
# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
# If 0 is used for the depth value (the default), the graph is not depth-constrained.
MAX_DOT_GRAPH_DEPTH = 0
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
================================================
FILE: thirdparty/LSD/lsd.c
================================================
/*----------------------------------------------------------------------------
LSD - Line Segment Detector on digital images
Copyright 2007-2010 rafael grompone von gioi (grompone@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** @file lsd.c
LSD module code
@author rafael grompone von gioi (grompone@gmail.com)
*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** @mainpage LSD code documentation
This is an implementation of the Line Segment Detector described
in the paper:
"LSD: A Fast Line Segment Detector with a False Detection Control"
by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
and Gregory Randall, IEEE Transactions on Pattern Analysis and
Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010.
and in more details in the CMLA Technical Report:
"LSD: A Line Segment Detector, Technical Report",
by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
Gregory Randall, CMLA, ENS Cachan, 2010.
The version implemented here includes some further improvements
described on the LSD page at www.ipol.im. That same page includes
more information, including this code and an online demo version:
http://www.ipol.im/pub/algo/gjmr_line_segment_detector
The module's main function is lsd().
The source code is contained in two files: lsd.h and lsd.c.
HISTORY:
- version 1.5 - dic 2010: Changes in 'refine', -W option added,
and more comments added.
- version 1.4 - jul 2010: lsd_scale interface added and doxygen doc.
- version 1.3 - feb 2010: Multiple bug correction and improved code.
- version 1.2 - dic 2009: First full Ansi C Language version.
- version 1.1 - sep 2009: Systematic subsampling to scale 0.8 and
correction to partially handle"angle problem".
- version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language
version.
@author rafael grompone von gioi (grompone@gmail.com)
*/
/*----------------------------------------------------------------------------*/
#include
#include
#include
#include
#include
#include "lsd.h"
/** ln(10) */
#ifndef M_LN10
#define M_LN10 2.30258509299404568402
#endif /* !M_LN10 */
/** PI */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif /* !M_PI */
#ifndef FALSE
#define FALSE 0
#endif /* !FALSE */
#ifndef TRUE
#define TRUE 1
#endif /* !TRUE */
/** Label for pixels with undefined gradient. */
#define NOTDEF -1024.0
/** 3/2 pi */
#define M_3_2_PI 4.71238898038
/** 2 pi */
#define M_2__PI 6.28318530718
/** Label for pixels not used in yet. */
#define NOTUSED 0
/** Label for pixels already used in detection. */
#define USED 1
/*----------------------------------------------------------------------------*/
/** Chained list of coordinates.
*/
struct coorlist
{
int x,y;
struct coorlist * next;
};
/*----------------------------------------------------------------------------*/
/** A point (or pixel).
*/
struct point {int x,y;};
/*----------------------------------------------------------------------------*/
/*------------------------- Miscellaneous functions --------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Fatal error, print a message to standard-error output and exit.
*/
static void error(char * msg)
{
fprintf(stderr,"LSD Error: %s\n",msg);
exit(EXIT_FAILURE);
}
/*----------------------------------------------------------------------------*/
/** Doubles relative error factor
*/
#define RELATIVE_ERROR_FACTOR 100.0
/*----------------------------------------------------------------------------*/
/** Compare doubles by relative error.
The resulting rounding error after floating point computations
depend on the specific operations done. The same number computed by
different algorithms could present different rounding errors. For a
useful comparison, an estimation of the relative rounding error
should be considered and compared to a factor times EPS. The factor
should be related to the cumulated rounding error in the chain of
computation. Here, as a simplification, a fixed factor is used.
*/
static int double_equal(double a, double b)
{
double abs_diff,aa,bb,abs_max;
/* trivial case */
if( a == b ) return TRUE;
abs_diff = fabs(a-b);
aa = fabs(a);
bb = fabs(b);
abs_max = aa > bb ? aa : bb;
/* DBL_MIN is the smallest normalized number, thus, the smallest
number whose relative error is bounded by DBL_EPSILON. For
smaller numbers, the same quantization steps as for DBL_MIN
are used. Then, for smaller numbers, a meaningful "relative"
error should be computed by dividing the difference by DBL_MIN. */
if( abs_max < DBL_MIN ) abs_max = DBL_MIN;
/* equal if relative error <= factor x eps */
return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
}
/*----------------------------------------------------------------------------*/
/** Computes Euclidean distance between point (x1,y1) and point (x2,y2).
*/
static double dist(double x1, double y1, double x2, double y2)
{
return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
}
/*----------------------------------------------------------------------------*/
/*----------------------- 'list of n-tuple' data type ------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Free memory used in n-tuple 'in'.
*/
void free_ntuple_list(ntuple_list in)
{
if( in == NULL || in->values == NULL )
error("free_ntuple_list: invalid n-tuple input.");
free( (void *) in->values );
free( (void *) in );
}
/*----------------------------------------------------------------------------*/
/** Create an n-tuple list and allocate memory for one element.
@param dim the dimension (n) of the n-tuple.
*/
ntuple_list new_ntuple_list(unsigned int dim)
{
ntuple_list n_tuple;
/* check parameters */
if( dim == 0 ) error("new_ntuple_list: 'dim' must be positive.");
/* get memory for list structure */
n_tuple = (ntuple_list) malloc( sizeof(struct ntuple_list_s) );
if( n_tuple == NULL ) error("not enough memory.");
/* initialize list */
n_tuple->size = 0;
n_tuple->max_size = 1;
n_tuple->dim = dim;
/* get memory for tuples */
n_tuple->values = (double *) malloc( dim*n_tuple->max_size * sizeof(double) );
if( n_tuple->values == NULL ) error("not enough memory.");
return n_tuple;
}
/*----------------------------------------------------------------------------*/
/** Enlarge the allocated memory of an n-tuple list.
*/
static void enlarge_ntuple_list(ntuple_list n_tuple)
{
/* check parameters */
if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size == 0 )
error("enlarge_ntuple_list: invalid n-tuple.");
/* duplicate number of tuples */
n_tuple->max_size *= 2;
/* realloc memory */
n_tuple->values = (double *) realloc( (void *) n_tuple->values,
n_tuple->dim * n_tuple->max_size * sizeof(double) );
if( n_tuple->values == NULL ) error("not enough memory.");
}
/*----------------------------------------------------------------------------*/
/** Add a 5-tuple to an n-tuple list.
*/
static void add_5tuple( ntuple_list out, double v1, double v2,
double v3, double v4, double v5 )
{
/* check parameters */
if( out == NULL ) error("add_5tuple: invalid n-tuple input.");
if( out->dim != 5 ) error("add_5tuple: the n-tuple must be a 5-tuple.");
/* if needed, alloc more tuples to 'out' */
if( out->size == out->max_size ) enlarge_ntuple_list(out);
if( out->values == NULL ) error("add_5tuple: invalid n-tuple input.");
/* add new 5-tuple */
out->values[ out->size * out->dim + 0 ] = v1;
out->values[ out->size * out->dim + 1 ] = v2;
out->values[ out->size * out->dim + 2 ] = v3;
out->values[ out->size * out->dim + 3 ] = v4;
out->values[ out->size * out->dim + 4 ] = v5;
/* update number of tuples counter */
out->size++;
}
/*----------------------------------------------------------------------------*/
/*----------------------------- Image Data Types -----------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Free memory used in image_char 'i'.
*/
void free_image_char(image_char i)
{
if( i == NULL || i->data == NULL )
error("free_image_char: invalid input image.");
free( (void *) i->data );
free( (void *) i );
}
/*----------------------------------------------------------------------------*/
/** Create a new image_char of size 'xsize' times 'ysize'.
*/
image_char new_image_char(unsigned int xsize, unsigned int ysize)
{
image_char image;
/* check parameters */
if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size.");
/* get memory */
image = (image_char) malloc( sizeof(struct image_char_s) );
if( image == NULL ) error("not enough memory.");
image->data = (unsigned char *) calloc( (size_t) (xsize*ysize),
sizeof(unsigned char) );
if( image->data == NULL ) error("not enough memory.");
/* set image size */
image->xsize = xsize;
image->ysize = ysize;
return image;
}
/*----------------------------------------------------------------------------*/
/** Create a new image_char of size 'xsize' times 'ysize',
initialized to the value 'fill_value'.
*/
image_char new_image_char_ini( unsigned int xsize, unsigned int ysize,
unsigned char fill_value )
{
image_char image = new_image_char(xsize,ysize); /* create image */
unsigned int N = xsize*ysize;
unsigned int i;
/* check parameters */
if( image == NULL || image->data == NULL )
error("new_image_char_ini: invalid image.");
/* initialize */
for(i=0; idata[i] = fill_value;
return image;
}
/*----------------------------------------------------------------------------*/
/** Free memory used in image_int 'i'.
*/
void free_image_int(image_int i)
{
if( i == NULL || i->data == NULL )
error("free_image_int: invalid input image.");
free( (void *) i->data );
free( (void *) i );
}
/*----------------------------------------------------------------------------*/
/** Create a new image_int of size 'xsize' times 'ysize'.
*/
image_int new_image_int(unsigned int xsize, unsigned int ysize)
{
image_int image;
/* check parameters */
if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size.");
/* get memory */
image = (image_int) malloc( sizeof(struct image_int_s) );
if( image == NULL ) error("not enough memory.");
image->data = (int *) calloc( (size_t) (xsize*ysize), sizeof(int) );
if( image->data == NULL ) error("not enough memory.");
/* set image size */
image->xsize = xsize;
image->ysize = ysize;
return image;
}
/*----------------------------------------------------------------------------*/
/** Create a new image_int of size 'xsize' times 'ysize',
initialized to the value 'fill_value'.
*/
image_int new_image_int_ini( unsigned int xsize, unsigned int ysize,
int fill_value )
{
image_int image = new_image_int(xsize,ysize); /* create image */
unsigned int N = xsize*ysize;
unsigned int i;
/* initialize */
for(i=0; idata[i] = fill_value;
return image;
}
/*----------------------------------------------------------------------------*/
/** Free memory used in image_double 'i'.
*/
void free_image_double(image_double i)
{
if( i == NULL || i->data == NULL )
error("free_image_double: invalid input image.");
free( (void *) i->data );
free( (void *) i );
}
/*----------------------------------------------------------------------------*/
/** Create a new image_double of size 'xsize' times 'ysize'.
*/
image_double new_image_double(unsigned int xsize, unsigned int ysize)
{
image_double image;
/* check parameters */
if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size.");
/* get memory */
image = (image_double) malloc( sizeof(struct image_double_s) );
if( image == NULL ) error("not enough memory.");
image->data = (double *) calloc( (size_t) (xsize*ysize), sizeof(double) );
if( image->data == NULL ) error("not enough memory.");
/* set image size */
image->xsize = xsize;
image->ysize = ysize;
return image;
}
/*----------------------------------------------------------------------------*/
/** Create a new image_double of size 'xsize' times 'ysize',
initialized to the value 'fill_value'.
*/
image_double new_image_double_ini( unsigned int xsize, unsigned int ysize,
double fill_value )
{
image_double image = new_image_double(xsize,ysize); /* create image */
unsigned int N = xsize*ysize;
unsigned int i;
/* initialize */
for(i=0; idata[i] = fill_value;
return image;
}
/*----------------------------------------------------------------------------*/
/*----------------------------- Gaussian filter ------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Compute a Gaussian kernel of length 'kernel->dim',
standard deviation 'sigma', and centered at value 'mean'.
For example, if mean=0.5, the Gaussian will be centered
in the middle point between values 'kernel->values[0]'
and 'kernel->values[1]'.
*/
static void gaussian_kernel(ntuple_list kernel, double sigma, double mean)
{
double sum = 0.0;
double val;
unsigned int i;
/* check parameters */
if( kernel == NULL || kernel->values == NULL )
error("gaussian_kernel: invalid n-tuple 'kernel'.");
if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive.");
/* compute Gaussian kernel */
if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel);
kernel->size = 1;
for(i=0;idim;i++)
{
val = ( (double) i - mean ) / sigma;
kernel->values[i] = exp( -0.5 * val * val );
sum += kernel->values[i];
}
/* normalization */
if( sum >= 0.0 ) for(i=0;idim;i++) kernel->values[i] /= sum;
}
/*----------------------------------------------------------------------------*/
/** Scale the input image 'in' by a factor 'scale' by Gaussian sub-sampling.
For example, scale=0.8 will give a result at 80% of the original size.
The image is convolved with a Gaussian kernel
@f[
G(x,y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}}
@f]
before the sub-sampling to prevent aliasing.
The standard deviation sigma given by:
- sigma = sigma_scale / scale, if scale < 1.0
- sigma = sigma_scale, if scale >= 1.0
To be able to sub-sample at non-integer steps, some interpolation
is needed. In this implementation, the interpolation is done by
the Gaussian kernel, so both operations (filtering and sampling)
are done at the same time. The Gaussian kernel is computed
centered on the coordinates of the required sample. In this way,
when applied, it gives directly the result of convolving the image
with the kernel and interpolated to that particular position.
A fast algorithm is done using the separability of the Gaussian
kernel. Applying the 2D Gaussian kernel is equivalent to applying
first a horizontal 1D Gaussian kernel and then a vertical 1D
Gaussian kernel (or the other way round). The reason is that
@f[
G(x,y) = G(x) * G(y)
@f]
where
@f[
G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{x^2}{2\sigma^2}}.
@f]
The algorithm first apply a combined Gaussian kernel and sampling
in the x axis, and then the combined Gaussian kernel and sampling
in the y axis.
*/
static image_double gaussian_sampler( image_double in, double scale,
double sigma_scale )
{
image_double aux,out;
ntuple_list kernel;
unsigned int N,M,h,n,x,y,i;
int xc,yc,j,double_x_size,double_y_size;
double sigma,xx,yy,sum,prec;
/* check parameters */
if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
error("gaussian_sampler: invalid image.");
if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive.");
if( sigma_scale <= 0.0 )
error("gaussian_sampler: 'sigma_scale' must be positive.");
/* get memory for images */
if( in->xsize * scale > (double) UINT_MAX ||
in->ysize * scale > (double) UINT_MAX )
error("gaussian_sampler: the output image size exceeds the handled size.");
N = (unsigned int) floor( in->xsize * scale );
M = (unsigned int) floor( in->ysize * scale );
aux = new_image_double(N,in->ysize);
out = new_image_double(N,M);
/* sigma, kernel size and memory for the kernel */
sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale;
/*
The size of the kernel is selected to guarantee that the
the first discarded term is at least 10^prec times smaller
than the central value. For that, h should be larger than x, with
e^(-x^2/2sigma^2) = 1/10^prec.
Then,
x = sigma * sqrt( 2 * prec * ln(10) ).
*/
prec = 3.0;
h = (unsigned int) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) );
n = 1+2*h; /* kernel size */
kernel = new_ntuple_list(n);
/* auxiliary double image size variables */
double_x_size = (int) (2 * in->xsize);
double_y_size = (int) (2 * in->ysize);
/* First subsampling: x axis */
for(x=0;xxsize;x++)
{
/*
x is the coordinate in the new image.
xx is the corresponding x-value in the original size image.
xc is the integer value, the pixel coordinate of xx.
*/
xx = (double) x / scale;
/* coordinate (0.0,0.0) is in the center of pixel (0,0),
so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */
xc = (int) floor( xx + 0.5 );
gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc );
/* the kernel must be computed for each x because the fine
offset xx-xc is different in each case */
for(y=0;yysize;y++)
{
sum = 0.0;
for(i=0;idim;i++)
{
j = xc - h + i;
/* symmetry boundary condition */
while( j < 0 ) j += double_x_size;
while( j >= double_x_size ) j -= double_x_size;
if( j >= (int) in->xsize ) j = double_x_size-1-j;
sum += in->data[ j + y * in->xsize ] * kernel->values[i];
}
aux->data[ x + y * aux->xsize ] = sum;
}
}
/* Second subsampling: y axis */
for(y=0;yysize;y++)
{
/*
y is the coordinate in the new image.
yy is the corresponding x-value in the original size image.
yc is the integer value, the pixel coordinate of xx.
*/
yy = (double) y / scale;
/* coordinate (0.0,0.0) is in the center of pixel (0,0),
so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */
yc = (int) floor( yy + 0.5 );
gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc );
/* the kernel must be computed for each y because the fine
offset yy-yc is different in each case */
for(x=0;xxsize;x++)
{
sum = 0.0;
for(i=0;idim;i++)
{
j = yc - h + i;
/* symmetry boundary condition */
while( j < 0 ) j += double_y_size;
while( j >= double_y_size ) j -= double_y_size;
if( j >= (int) in->ysize ) j = double_y_size-1-j;
sum += aux->data[ x + j * aux->xsize ] * kernel->values[i];
}
out->data[ x + y * out->xsize ] = sum;
}
}
/* free memory */
free_ntuple_list(kernel);
free_image_double(aux);
return out;
}
/*----------------------------------------------------------------------------*/
/*--------------------------------- Gradient ---------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Computes the direction of the level line of 'in' at each point.
The result is:
- an image_double with the angle at each pixel, or NOTDEF if not defined.
- the image_double 'modgrad' (a pointer is passed as argument)
with the gradient magnitude at each point.
- a list of pixels 'list_p' roughly ordered by decreasing
gradient magnitude. (The order is made by classifying points
into bins by gradient magnitude. The parameters 'n_bins' and
'max_grad' specify the number of bins and the gradient modulus
at the highest bin. The pixels in the list would be in
decreasing gradient magnitude, up to a precision of the size of
the bins.)
- a pointer 'mem_p' to the memory used by 'list_p' to be able to
free the memory when it is not used anymore.
*/
static image_double ll_angle( image_double in, double threshold,
struct coorlist ** list_p, void ** mem_p,
image_double * modgrad, unsigned int n_bins,
double max_grad )
{
image_double g;
unsigned int n,p,x,y,adr,i;
double com1,com2,gx,gy,norm,norm2;
/* the rest of the variables are used for pseudo-ordering
the gradient magnitude values */
int list_count = 0;
struct coorlist * list;
struct coorlist ** range_l_s; /* array of pointers to start of bin list */
struct coorlist ** range_l_e; /* array of pointers to end of bin list */
struct coorlist * start;
struct coorlist * end;
/* check parameters */
if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
error("ll_angle: invalid image.");
if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive.");
if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'.");
if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'.");
if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'.");
if( n_bins == 0 ) error("ll_angle: 'n_bins' must be positive.");
if( max_grad <= 0.0 ) error("ll_angle: 'max_grad' must be positive.");
/* image size shortcuts */
n = in->ysize;
p = in->xsize;
/* allocate output image */
g = new_image_double(in->xsize,in->ysize);
/* get memory for the image of gradient modulus */
*modgrad = new_image_double(in->xsize,in->ysize);
/* get memory for "ordered" list of pixels */
list = (struct coorlist *) calloc( (size_t) (n*p), sizeof(struct coorlist) );
*mem_p = (void *) list;
range_l_s = (struct coorlist **) calloc( (size_t) n_bins,
sizeof(struct coorlist *) );
range_l_e = (struct coorlist **) calloc( (size_t) n_bins,
sizeof(struct coorlist *) );
if( list == NULL || range_l_s == NULL || range_l_e == NULL )
error("not enough memory.");
for(i=0;idata[(n-1)*p+x] = NOTDEF;
for(y=0;ydata[p*y+p-1] = NOTDEF;
/* compute gradient on the remaining pixels */
for(x=0;xdata[adr+p+1] - in->data[adr];
com2 = in->data[adr+1] - in->data[adr+p];
gx = com1+com2; /* gradient x component */
gy = com1-com2; /* gradient y component */
norm2 = gx*gx+gy*gy;
norm = sqrt( norm2 / 4.0 ); /* gradient norm */
(*modgrad)->data[adr] = norm; /* store gradient norm */
if( norm <= threshold ) /* norm too small, gradient no defined */
g->data[adr] = NOTDEF; /* gradient angle not defined */
else
{
/* gradient angle computation */
g->data[adr] = atan2(gx,-gy);
/* store the point in the right bin according to its norm */
i = (unsigned int) (norm * (double) n_bins / max_grad);
if( i >= n_bins ) i = n_bins-1;
if( range_l_e[i] == NULL )
range_l_s[i] = range_l_e[i] = list+list_count++;
else
{
range_l_e[i]->next = list+list_count;
range_l_e[i] = list+list_count++;
}
range_l_e[i]->x = (int) x;
range_l_e[i]->y = (int) y;
range_l_e[i]->next = NULL;
}
}
/* Make the list of pixels (almost) ordered by norm value.
It starts by the larger bin, so the list starts by the
pixels with higher gradient value. Pixels would be ordered
by norm value, up to a precision given by max_grad/n_bins.
*/
for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--);
start = range_l_s[i];
end = range_l_e[i];
if( start != NULL )
for(i--;i>0; i--)
if( range_l_s[i] != NULL )
{
end->next = range_l_s[i];
end = range_l_e[i];
}
*list_p = start;
/* free memory */
free( (void *) range_l_s );
free( (void *) range_l_e );
return g;
}
/*----------------------------------------------------------------------------*/
/** Is point (x,y) aligned to angle theta, up to precision 'prec'?
*/
static int isaligned( int x, int y, image_double angles, double theta,
double prec )
{
double a;
/* check parameters */
if( angles == NULL || angles->data == NULL )
error("isaligned: invalid image 'angles'.");
if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
error("isaligned: (x,y) out of the image.");
if( prec < 0.0 ) error("isaligned: 'prec' must be positive.");
/* angle at pixel (x,y) */
a = angles->data[ x + y * angles->xsize ];
/* pixels whose level-line angle is not defined
are considered as NON-aligned */
if( a == NOTDEF ) return FALSE; /* there is no need to call the function
'double_equal' here because there is
no risk of problems related to the
comparison doubles, we are only
interested in the exact NOTDEF value */
/* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */
theta -= a;
if( theta < 0.0 ) theta = -theta;
if( theta > M_3_2_PI )
{
theta -= M_2__PI;
if( theta < 0.0 ) theta = -theta;
}
return theta < prec;
}
/*----------------------------------------------------------------------------*/
/** Absolute value angle difference.
*/
static double angle_diff(double a, double b)
{
a -= b;
while( a <= -M_PI ) a += M_2__PI;
while( a > M_PI ) a -= M_2__PI;
if( a < 0.0 ) a = -a;
return a;
}
/*----------------------------------------------------------------------------*/
/** Signed angle difference.
*/
static double angle_diff_signed(double a, double b)
{
a -= b;
while( a <= -M_PI ) a += M_2__PI;
while( a > M_PI ) a -= M_2__PI;
return a;
}
/*----------------------------------------------------------------------------*/
/*----------------------------- NFA computation ------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Computes the natural logarithm of the absolute value of
the gamma function of x using the Lanczos approximation.
See http://www.rskey.org/gamma.htm
The formula used is
@f[
\Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) }
(x+5.5)^{x+0.5} e^{-(x+5.5)}
@f]
so
@f[
\log\Gamma(x) = \log\left( \sum_{n=0}^{N} q_n x^n \right)
+ (x+0.5) \log(x+5.5) - (x+5.5) - \sum_{n=0}^{N} \log(x+n)
@f]
and
q0 = 75122.6331530,
q1 = 80916.6278952,
q2 = 36308.2951477,
q3 = 8687.24529705,
q4 = 1168.92649479,
q5 = 83.8676043424,
q6 = 2.50662827511.
*/
static double log_gamma_lanczos(double x)
{
static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
8687.24529705, 1168.92649479, 83.8676043424,
2.50662827511 };
double a = (x+0.5) * log(x+5.5) - (x+5.5);
double b = 0.0;
int n;
for(n=0;n<7;n++)
{
a -= log( x + (double) n );
b += q[n] * pow( x, (double) n );
}
return a + log(b);
}
/*----------------------------------------------------------------------------*/
/** Computes the natural logarithm of the absolute value of
the gamma function of x using Windschitl method.
See http://www.rskey.org/gamma.htm
The formula used is
@f[
\Gamma(x) = \sqrt{\frac{2\pi}{x}} \left( \frac{x}{e}
\sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } \right)^x
@f]
so
@f[
\log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x
+ 0.5x\log\left( x\sinh(1/x) + \frac{1}{810x^6} \right).
@f]
This formula is a good approximation when x > 15.
*/
static double log_gamma_windschitl(double x)
{
return 0.918938533204673 + (x-0.5)*log(x) - x
+ 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) );
}
/*----------------------------------------------------------------------------*/
/** Computes the natural logarithm of the absolute value of
the gamma function of x. When x>15 use log_gamma_windschitl(),
otherwise use log_gamma_lanczos().
*/
#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
/*----------------------------------------------------------------------------*/
/** Size of the table to store already computed inverse values.
*/
#define TABSIZE 100000
/*----------------------------------------------------------------------------*/
/** Computes -log10(NFA).
NFA stands for Number of False Alarms:
@f[
\mathrm{NFA} = NT \cdot B(n,k,p)
@f]
- NT - number of tests
- B(n,k,p) - tail of binomial distribution with parameters n,k and p:
@f[
B(n,k,p) = \sum_{j=k}^n
\left(\begin{array}{c}n\\j\end{array}\right)
p^{j} (1-p)^{n-j}
@f]
The value -log10(NFA) is equivalent but more intuitive than NFA:
- -1 corresponds to 10 mean false alarms
- 0 corresponds to 1 mean false alarm
- 1 corresponds to 0.1 mean false alarms
- 2 corresponds to 0.01 mean false alarms
- ...
Used this way, the bigger the value, better the detection,
and a logarithmic scale is used.
@param n,k,p binomial parameters.
@param logNT logarithm of Number of Tests
The computation is based in the gamma function by the following
relation:
@f[
\left(\begin{array}{c}n\\k\end{array}\right)
= \frac{ \Gamma(n+1) }{ \Gamma(k+1) \cdot \Gamma(n-k+1) }.
@f]
We use efficient algorithms to compute the logarithm of
the gamma function.
To make the computation faster, not all the sum is computed, part
of the terms are neglected based on a bound to the error obtained
(an error of 10% in the result is accepted).
*/
static double nfa(int n, int k, double p, double logNT)
{
static double inv[TABSIZE]; /* table to keep computed inverse values */
double tolerance = 0.1; /* an error of 10% in the result is accepted */
double log1term,term,bin_term,mult_term,bin_tail,err,p_term;
int i;
/* check parameters */
if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 )
error("nfa: wrong n, k or p values.");
/* trivial cases */
if( n==0 || k==0 ) return -logNT;
if( n==k ) return -logNT - (double) n * log10(p);
/* probability term */
p_term = p / (1.0-p);
/* compute the first term of the series */
/*
binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
where bincoef(n,i) are the binomial coefficients.
But
bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
We use this to compute the first term. Actually the log of it.
*/
log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 )
- log_gamma( (double) (n-k) + 1.0 )
+ (double) k * log(p) + (double) (n-k) * log(1.0-p);
term = exp(log1term);
/* in some cases no more computations are needed */
if( double_equal(term,0.0) ) /* the first term is almost zero */
{
if( (double) k > (double) n * p ) /* at begin or end of the tail? */
return -log1term / M_LN10 - logNT; /* end: use just the first term */
else
return -logNT; /* begin: the tail is roughly 1 */
}
/* compute more terms if needed */
bin_tail = term;
for(i=k+1;i<=n;i++)
{
/*
As
term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
and
bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
then,
term_i / term_i-1 = (n-i+1)/i * p/(1-p)
and
term_i = term_i-1 * (n-i+1)/i * p/(1-p).
1/i is stored in a table as they are computed,
because divisions are expensive.
p/(1-p) is computed only once and stored in 'p_term'.
*/
bin_term = (double) (n-i+1) * ( ii.
Then, the error on the binomial tail when truncated at
the i term can be bounded by a geometric series of form
term_i * sum mult_term_i^j. */
err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) /
(1.0-mult_term) - 1.0 );
/* One wants an error at most of tolerance*final_result, or:
tolerance * abs(-log10(bin_tail)-logNT).
Now, the error that can be accepted on bin_tail is
given by tolerance*final_result divided by the derivative
of -log10(x) when x=bin_tail. that is:
tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
Finally, we truncate the tail if the error is less than:
tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break;
}
}
return -log10(bin_tail) - logNT;
}
/*----------------------------------------------------------------------------*/
/*--------------------------- Rectangle structure ----------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Rectangle structure: line segment with width.
*/
struct rect
{
double x1,y1,x2,y2; /* first and second point of the line segment */
double width; /* rectangle width */
double x,y; /* center of the rectangle */
double theta; /* angle */
double dx,dy; /* vector with the line segment angle */
double prec; /* tolerance angle */
double p; /* probability of a point with angle within 'prec' */
};
/*----------------------------------------------------------------------------*/
/** Copy one rectangle structure to another.
*/
static void rect_copy(struct rect * in, struct rect * out)
{
/* check parameters */
if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'.");
/* copy values */
out->x1 = in->x1;
out->y1 = in->y1;
out->x2 = in->x2;
out->y2 = in->y2;
out->width = in->width;
out->x = in->x;
out->y = in->y;
out->theta = in->theta;
out->dx = in->dx;
out->dy = in->dy;
out->prec = in->prec;
out->p = in->p;
}
/*----------------------------------------------------------------------------*/
/** Rectangle points iterator.
The integer coordinates of pixels inside a rectangle are
iteratively explored. This structure keep track of the process and
functions ri_ini(), ri_inc(), ri_end(), and ri_del() are used in
the process. An example of how to use the iterator is as follows:
\code
struct rect * rec = XXX; // some rectangle
rect_iter * i;
for( i=ri_ini(rec); !ri_end(i); ri_inc(i) )
{
// your code, using 'i->x' and 'i->y' as coordinates
}
ri_del(i); // delete iterator
\endcode
The pixels are explored 'column' by 'column', where we call
'column' a set of pixels with the same x value that are inside the
rectangle. The following is an schematic representation of a
rectangle, the 'column' being explored is marked by colons, and
the current pixel being explored is 'x,y'.
\verbatim
vx[1],vy[1]
* *
* *
* *
* ye
* : *
vx[0],vy[0] : *
* : *
* x,y *
* : *
* : vx[2],vy[2]
* : *
y ys *
^ * *
| * *
| * *
+---> x vx[3],vy[3]
\endverbatim
The first 'column' to be explored is the one with the smaller x
value. Each 'column' is explored starting from the pixel of the
'column' (inside the rectangle) with the smaller y value.
The four corners of the rectangle are stored in order that rotates
around the corners at the arrays 'vx[]' and 'vy[]'. The first
point is always the one with smaller x value.
'x' and 'y' are the coordinates of the pixel being explored. 'ys'
and 'ye' are the start and end values of the current column being
explored. So, 'ys' < 'ye'.
*/
typedef struct
{
double vx[4]; /* rectangle's corner X coordinates in circular order */
double vy[4]; /* rectangle's corner Y coordinates in circular order */
double ys,ye; /* start and end Y values of current 'column' */
int x,y; /* coordinates of currently explored pixel */
} rect_iter;
/*----------------------------------------------------------------------------*/
/** Interpolate y value corresponding to 'x' value given, in
the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the smaller
of 'y1' and 'y2'.
The following restrictions are required:
- x1 <= x2
- x1 <= x
- x <= x2
*/
static double inter_low(double x, double x1, double y1, double x2, double y2)
{
/* check parameters */
if( x1 > x2 || x < x1 || x > x2 )
error("inter_low: unsuitable input, 'x1>x2' or 'xx2'.");
/* interpolation */
if( double_equal(x1,x2) && y1y2 ) return y2;
return y1 + (x-x1) * (y2-y1) / (x2-x1);
}
/*----------------------------------------------------------------------------*/
/** Interpolate y value corresponding to 'x' value given, in
the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the larger
of 'y1' and 'y2'.
The following restrictions are required:
- x1 <= x2
- x1 <= x
- x <= x2
*/
static double inter_hi(double x, double x1, double y1, double x2, double y2)
{
/* check parameters */
if( x1 > x2 || x < x1 || x > x2 )
error("inter_hi: unsuitable input, 'x1>x2' or 'xx2'.");
/* interpolation */
if( double_equal(x1,x2) && y1y2 ) return y1;
return y1 + (x-x1) * (y2-y1) / (x2-x1);
}
/*----------------------------------------------------------------------------*/
/** Free memory used by a rectangle iterator.
*/
static void ri_del(rect_iter * iter)
{
if( iter == NULL ) error("ri_del: NULL iterator.");
free( (void *) iter );
}
/*----------------------------------------------------------------------------*/
/** Check if the iterator finished the full iteration.
See details in \ref rect_iter
*/
static int ri_end(rect_iter * i)
{
/* check input */
if( i == NULL ) error("ri_end: NULL iterator.");
/* if the current x value is larger than the larger
x value in the rectangle (vx[2]), we know the full
exploration of the rectangle is finished. */
return (double)(i->x) > i->vx[2];
}
/*----------------------------------------------------------------------------*/
/** Increment a rectangle iterator.
See details in \ref rect_iter
*/
static void ri_inc(rect_iter * i)
{
/* check input */
if( i == NULL ) error("ri_inc: NULL iterator.");
/* if not at end of exploration,
increase y value for next pixel in the 'column' */
if( !ri_end(i) ) i->y++;
/* if the end of the current 'column' is reached,
and it is not the end of exploration,
advance to the next 'column' */
while( (double) (i->y) > i->ye && !ri_end(i) )
{
/* increase x, next 'column' */
i->x++;
/* if end of exploration, return */
if( ri_end(i) ) return;
/* update lower y limit (start) for the new 'column'.
We need to interpolate the y value that corresponds to the
lower side of the rectangle. The first thing is to decide if
the corresponding side is
vx[0],vy[0] to vx[3],vy[3] or
vx[3],vy[3] to vx[2],vy[2]
Then, the side is interpolated for the x value of the
'column'. But, if the side is vertical (as it could happen if
the rectangle is vertical and we are dealing with the first
or last 'columns') then we pick the lower value of the side
by using 'inter_low'.
*/
if( (double) i->x < i->vx[3] )
i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]);
else
i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]);
/* update upper y limit (end) for the new 'column'.
We need to interpolate the y value that corresponds to the
upper side of the rectangle. The first thing is to decide if
the corresponding side is
vx[0],vy[0] to vx[1],vy[1] or
vx[1],vy[1] to vx[2],vy[2]
Then, the side is interpolated for the x value of the
'column'. But, if the side is vertical (as it could happen if
the rectangle is vertical and we are dealing with the first
or last 'columns') then we pick the lower value of the side
by using 'inter_low'.
*/
if( (double)i->x < i->vx[1] )
i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]);
else
i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]);
/* new y */
i->y = (int) ceil(i->ys);
}
}
/*----------------------------------------------------------------------------*/
/** Create and initialize a rectangle iterator.
See details in \ref rect_iter
*/
static rect_iter * ri_ini(struct rect * r)
{
double vx[4],vy[4];
int n,offset;
rect_iter * i;
/* check parameters */
if( r == NULL ) error("ri_ini: invalid rectangle.");
/* get memory */
i = (rect_iter *) malloc(sizeof(rect_iter));
if( i == NULL ) error("ri_ini: Not enough memory.");
/* build list of rectangle corners ordered
in a circular way around the rectangle */
vx[0] = r->x1 - r->dy * r->width / 2.0;
vy[0] = r->y1 + r->dx * r->width / 2.0;
vx[1] = r->x2 - r->dy * r->width / 2.0;
vy[1] = r->y2 + r->dx * r->width / 2.0;
vx[2] = r->x2 + r->dy * r->width / 2.0;
vy[2] = r->y2 - r->dx * r->width / 2.0;
vx[3] = r->x1 + r->dy * r->width / 2.0;
vy[3] = r->y1 - r->dx * r->width / 2.0;
/* compute rotation of index of corners needed so that the first
point has the smaller x.
if one side is vertical, thus two corners have the same smaller x
value, the one with the largest y value is selected as the first.
*/
if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0;
else if( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1;
else if( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2;
else offset = 3;
/* apply rotation of index. */
for(n=0; n<4; n++)
{
i->vx[n] = vx[(offset+n)%4];
i->vy[n] = vy[(offset+n)%4];
}
/* Set a initial condition.
The values are set to values that will cause 'ri_inc' (that will
be called immediately) to initialize correctly the first 'column'
and compute the limits 'ys' and 'ye'.
'y' is set to the integer value of vy[0], the starting corner.
'ys' and 'ye' are set to very small values, so 'ri_inc' will
notice that it needs to start a new 'column'.
The smaller integer coordinate inside of the rectangle is
'ceil(vx[0])'. The current 'x' value is set to that value minus
one, so 'ri_inc' (that will increase x by one) will advance to
the first 'column'.
*/
i->x = (int) ceil(i->vx[0]) - 1;
i->y = (int) ceil(i->vy[0]);
i->ys = i->ye = -DBL_MAX;
/* advance to the first pixel */
ri_inc(i);
return i;
}
/*----------------------------------------------------------------------------*/
/** Compute a rectangle's NFA value.
*/
static double rect_nfa(struct rect * rec, image_double angles, double logNT)
{
rect_iter * i;
int pts = 0;
int alg = 0;
/* check parameters */
if( rec == NULL ) error("rect_nfa: invalid rectangle.");
if( angles == NULL ) error("rect_nfa: invalid 'angles'.");
/* compute the total number of pixels and of aligned points in 'rec' */
for(i=ri_ini(rec); !ri_end(i); ri_inc(i)) /* rectangle iterator */
if( i->x >= 0 && i->y >= 0 &&
i->x < (int) angles->xsize && i->y < (int) angles->ysize )
{
++pts; /* total number of pixels counter */
if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) )
++alg; /* aligned points counter */
}
ri_del(i); /* delete iterator */
return nfa(pts,alg,rec->p,logNT); /* compute NFA value */
}
/*----------------------------------------------------------------------------*/
/*---------------------------------- Regions ---------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Compute region's angle as the principal inertia axis of the region.
The following is the region inertia matrix A:
@f[
A = \left(\begin{array}{cc}
Ixx & Ixy \\
Ixy & Iyy \\
\end{array}\right)
@f]
where
Ixx = sum_i G(i).(y_i - cx)^2
Iyy = sum_i G(i).(x_i - cy)^2
Ixy = - sum_i G(i).(x_i - cx).(y_i - cy)
and
- G(i) is the gradient norm at pixel i, used as pixel's weight.
- x_i and y_i are the coordinates of pixel i.
- cx and cy are the coordinates of the center of th region.
lambda1 and lambda2 are the eigenvalues of matrix A,
with lambda1 >= lambda2. They are found by solving the
characteristic polynomial:
det( lambda I - A) = 0
that gives:
lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
To get the line segment direction we want to get the angle the
eigenvector assotiated to the smaller eigenvalue. We have to solve
a,b in:
a.Ixx + b.Ixy = a.lambda2
a.Ixy + b.Iyy = b.lambda2
We want the angle theta = atan(b/a). It can be computed with
any of the two equations:
theta = atan( (lambda2-Ixx) / Ixy )
or
theta = atan( Ixy / (lambda2-Iyy) )
When |Ixx| > |Iyy| we use the first, otherwise the second (just to
get better numeric precision).
*/
static double get_theta( struct point * reg, int reg_size, double x, double y,
image_double modgrad, double reg_angle, double prec )
{
double lambda,theta,weight;
double Ixx = 0.0;
double Iyy = 0.0;
double Ixy = 0.0;
int i;
/* check parameters */
if( reg == NULL ) error("get_theta: invalid region.");
if( reg_size <= 1 ) error("get_theta: region size <= 1.");
if( modgrad == NULL || modgrad->data == NULL )
error("get_theta: invalid 'modgrad'.");
if( prec < 0.0 ) error("get_theta: 'prec' must be positive.");
/* compute inertia matrix */
for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ];
Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight;
Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight;
Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight;
}
if( double_equal(Ixx,0.0) && double_equal(Iyy,0.0) && double_equal(Ixy,0.0) )
error("get_theta: null inertia matrix.");
/* compute smallest eigenvalue */
lambda = 0.5 * ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) );
/* compute angle */
theta = fabs(Ixx)>fabs(Iyy) ? atan2(lambda-Ixx,Ixy) : atan2(Ixy,lambda-Iyy);
/* The previous procedure don't cares about orientation,
so it could be wrong by 180 degrees. Here is corrected if necessary. */
if( angle_diff(theta,reg_angle) > prec ) theta += M_PI;
return theta;
}
/*----------------------------------------------------------------------------*/
/** Computes a rectangle that covers a region of points.
*/
static void region2rect( struct point * reg, int reg_size,
image_double modgrad, double reg_angle,
double prec, double p, struct rect * rec )
{
double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max;
int i;
/* check parameters */
if( reg == NULL ) error("region2rect: invalid region.");
if( reg_size <= 1 ) error("region2rect: region size <= 1.");
if( modgrad == NULL || modgrad->data == NULL )
error("region2rect: invalid image 'modgrad'.");
if( rec == NULL ) error("region2rect: invalid 'rec'.");
/* center of the region:
It is computed as the weighted sum of the coordinates
of all the pixels in the region. The norm of the gradient
is used as the weight of a pixel. The sum is as follows:
cx = \sum_i G(i).x_i
cy = \sum_i G(i).y_i
where G(i) is the norm of the gradient of pixel i
and x_i,y_i are its coordinates.
*/
x = y = sum = 0.0;
for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ];
x += (double) reg[i].x * weight;
y += (double) reg[i].y * weight;
sum += weight;
}
if( sum <= 0.0 ) error("region2rect: weights sum equal to zero.");
x /= sum;
y /= sum;
/* theta */
theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec);
/* length and width:
'l' and 'w' are computed as the distance from the center of the
region to pixel i, projected along the rectangle axis (dx,dy) and
to the orthogonal axis (-dy,dx), respectively.
The length of the rectangle goes from l_min to l_max, where l_min
and l_max are the minimum and maximum values of l in the region.
Analogously, the width is selected from w_min to w_max, where
w_min and w_max are the minimum and maximum of w for the pixels
in the region.
*/
dx = cos(theta);
dy = sin(theta);
l_min = l_max = w_min = w_max = 0.0;
for(i=0; i l_max ) l_max = l;
if( l < l_min ) l_min = l;
if( w > w_max ) w_max = w;
if( w < w_min ) w_min = w;
}
/* store values */
rec->x1 = x + l_min * dx;
rec->y1 = y + l_min * dy;
rec->x2 = x + l_max * dx;
rec->y2 = y + l_max * dy;
rec->width = w_max - w_min;
rec->x = x;
rec->y = y;
rec->theta = theta;
rec->dx = dx;
rec->dy = dy;
rec->prec = prec;
rec->p = p;
/* we impose a minimal width of one pixel
A sharp horizontal or vertical step would produce a perfectly
horizontal or vertical region. The width computed would be
zero. But that corresponds to a one pixels width transition in
the image.
*/
if( rec->width < 1.0 ) rec->width = 1.0;
}
/*----------------------------------------------------------------------------*/
/** Build a region of pixels that share the same angle, up to a
tolerance 'prec', starting at point (x,y).
*/
static void region_grow( int x, int y, image_double angles, struct point * reg,
int * reg_size, double * reg_angle, image_char used,
double prec )
{
double sumdx,sumdy;
int xx,yy,i;
/* check parameters */
if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
error("region_grow: (x,y) out of the image.");
if( angles == NULL || angles->data == NULL )
error("region_grow: invalid image 'angles'.");
if( reg == NULL ) error("region_grow: invalid 'reg'.");
if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'.");
if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'.");
if( used == NULL || used->data == NULL )
error("region_grow: invalid image 'used'.");
/* first point of the region */
*reg_size = 1;
reg[0].x = x;
reg[0].y = y;
*reg_angle = angles->data[x+y*angles->xsize]; /* region's angle */
sumdx = cos(*reg_angle);
sumdy = sin(*reg_angle);
used->data[x+y*used->xsize] = USED;
/* try neighbors as new region points */
for(i=0; i<*reg_size; i++)
for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++)
for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++)
if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize &&
used->data[xx+yy*used->xsize] != USED &&
isaligned(xx,yy,angles,*reg_angle,prec) )
{
/* add point */
used->data[xx+yy*used->xsize] = USED;
reg[*reg_size].x = xx;
reg[*reg_size].y = yy;
++(*reg_size);
/* update region's angle */
sumdx += cos( angles->data[xx+yy*angles->xsize] );
sumdy += sin( angles->data[xx+yy*angles->xsize] );
*reg_angle = atan2(sumdy,sumdx);
}
}
/*----------------------------------------------------------------------------*/
/** Try some rectangles variations to improve NFA value. Only if the
rectangle is not meaningful (i.e., log_nfa <= eps).
*/
static double rect_improve( struct rect * rec, image_double angles,
double logNT, double eps )
{
struct rect r;
double log_nfa,log_nfa_new;
double delta = 0.5;
double delta_2 = delta / 2.0;
int n;
log_nfa = rect_nfa(rec,angles,logNT);
if( log_nfa > eps ) return log_nfa;
/* try finer precisions */
rect_copy(rec,&r);
for(n=0; n<5; n++)
{
r.p /= 2.0;
r.prec = r.p * M_PI;
log_nfa_new = rect_nfa(&r,angles,logNT);
if( log_nfa_new > log_nfa )
{
log_nfa = log_nfa_new;
rect_copy(&r,rec);
}
}
if( log_nfa > eps ) return log_nfa;
/* try to reduce width */
rect_copy(rec,&r);
for(n=0; n<5; n++)
{
if( (r.width - delta) >= 0.5 )
{
r.width -= delta;
log_nfa_new = rect_nfa(&r,angles,logNT);
if( log_nfa_new > log_nfa )
{
rect_copy(&r,rec);
log_nfa = log_nfa_new;
}
}
}
if( log_nfa > eps ) return log_nfa;
/* try to reduce one side of the rectangle */
rect_copy(rec,&r);
for(n=0; n<5; n++)
{
if( (r.width - delta) >= 0.5 )
{
r.x1 += -r.dy * delta_2;
r.y1 += r.dx * delta_2;
r.x2 += -r.dy * delta_2;
r.y2 += r.dx * delta_2;
r.width -= delta;
log_nfa_new = rect_nfa(&r,angles,logNT);
if( log_nfa_new > log_nfa )
{
rect_copy(&r,rec);
log_nfa = log_nfa_new;
}
}
}
if( log_nfa > eps ) return log_nfa;
/* try to reduce the other side of the rectangle */
rect_copy(rec,&r);
for(n=0; n<5; n++)
{
if( (r.width - delta) >= 0.5 )
{
r.x1 -= -r.dy * delta_2;
r.y1 -= r.dx * delta_2;
r.x2 -= -r.dy * delta_2;
r.y2 -= r.dx * delta_2;
r.width -= delta;
log_nfa_new = rect_nfa(&r,angles,logNT);
if( log_nfa_new > log_nfa )
{
rect_copy(&r,rec);
log_nfa = log_nfa_new;
}
}
}
if( log_nfa > eps ) return log_nfa;
/* try even finer precisions */
rect_copy(rec,&r);
for(n=0; n<5; n++)
{
r.p /= 2.0;
r.prec = r.p * M_PI;
log_nfa_new = rect_nfa(&r,angles,logNT);
if( log_nfa_new > log_nfa )
{
log_nfa = log_nfa_new;
rect_copy(&r,rec);
}
}
return log_nfa;
}
/*----------------------------------------------------------------------------*/
/** Reduce the region size, by elimination the points far from the
starting point, until that leads to rectangle with the right
density of region points or to discard the region if too small.
*/
static int reduce_region_radius( struct point * reg, int * reg_size,
image_double modgrad, double reg_angle,
double prec, double p, struct rect * rec,
image_char used, image_double angles,
double density_th )
{
double density,rad1,rad2,rad,xc,yc;
int i;
/* check parameters */
if( reg == NULL ) error("reduce_region_radius: invalid pointer 'reg'.");
if( reg_size == NULL )
error("reduce_region_radius: invalid pointer 'reg_size'.");
if( prec < 0.0 ) error("reduce_region_radius: 'prec' must be positive.");
if( rec == NULL ) error("reduce_region_radius: invalid pointer 'rec'.");
if( used == NULL || used->data == NULL )
error("reduce_region_radius: invalid image 'used'.");
if( angles == NULL || angles->data == NULL )
error("reduce_region_radius: invalid image 'angles'.");
/* compute region points density */
density = (double) *reg_size /
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
/* if the density criterion is satisfied there is nothing to do */
if( density >= density_th ) return TRUE;
/* compute region's radius */
xc = (double) reg[0].x;
yc = (double) reg[0].y;
rad1 = dist( xc, yc, rec->x1, rec->y1 );
rad2 = dist( xc, yc, rec->x2, rec->y2 );
rad = rad1 > rad2 ? rad1 : rad2;
/* while the density criterion is not satisfied, remove farther pixels */
while( density < density_th )
{
rad *= 0.75; /* reduce region's radius to 75% of its value */
/* remove points from the region and update 'used' map */
for(i=0; i<*reg_size; i++)
if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad )
{
/* point not kept, mark it as NOTUSED */
used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
/* remove point from the region */
reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */
reg[i].y = reg[*reg_size-1].y;
--(*reg_size);
--i; /* to avoid skipping one point */
}
/* reject if the region is too small.
2 is the minimal region size for 'region2rect' to work. */
if( *reg_size < 2 ) return FALSE;
/* re-compute rectangle */
region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
/* re-compute region points density */
density = (double) *reg_size /
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
}
/* if this point is reached, the density criterion is satisfied */
return TRUE;
}
/*----------------------------------------------------------------------------*/
/** Refine a rectangle.
For that, an estimation of the angle tolerance is performed by the
standard deviation of the angle at points near the region's
starting point. Then, a new region is grown starting from the same
point, but using the estimated angle tolerance. If this fails to
produce a rectangle with the right density of region points,
'reduce_region_radius' is called to try to satisfy this condition.
*/
static int refine( struct point * reg, int * reg_size, image_double modgrad,
double reg_angle, double prec, double p, struct rect * rec,
image_char used, image_double angles, double density_th )
{
double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum;
int i,n;
/* check parameters */
if( reg == NULL ) error("refine: invalid pointer 'reg'.");
if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'.");
if( prec < 0.0 ) error("refine: 'prec' must be positive.");
if( rec == NULL ) error("refine: invalid pointer 'rec'.");
if( used == NULL || used->data == NULL )
error("refine: invalid image 'used'.");
if( angles == NULL || angles->data == NULL )
error("refine: invalid image 'angles'.");
/* compute region points density */
density = (double) *reg_size /
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
/* if the density criterion is satisfied there is nothing to do */
if( density >= density_th ) return TRUE;
/*------ First try: reduce angle tolerance ------*/
/* compute the new mean angle and tolerance */
xc = (double) reg[0].x;
yc = (double) reg[0].y;
ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ];
sum = s_sum = 0.0;
n = 0;
for(i=0; i<*reg_size; i++)
{
used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width )
{
angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ];
ang_d = angle_diff_signed(angle,ang_c);
sum += ang_d;
s_sum += ang_d * ang_d;
++n;
}
}
mean_angle = sum / (double) n;
tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n
+ mean_angle*mean_angle ); /* 2 * standard deviation */
/* find a new region from the same starting point and new angle tolerance */
region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau);
/* if the region is too small, reject */
if( *reg_size < 2 ) return FALSE;
/* re-compute rectangle */
region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
/* re-compute region points density */
density = (double) *reg_size /
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
/*------ Second try: reduce region radius ------*/
if( density < density_th )
return reduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p,
rec, used, angles, density_th );
/* if this point is reached, the density criterion is satisfied */
return TRUE;
}
/*----------------------------------------------------------------------------*/
/*-------------------------- Line Segment Detector ---------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** LSD full interface.
*/
ntuple_list LineSegmentDetection( image_double image, double scale,
double sigma_scale, double quant,
double ang_th, double eps, double density_th,
int n_bins, double max_grad,
image_int * region )
{
ntuple_list out = new_ntuple_list(5);
image_double scaled_image,angles,modgrad;
image_char used;
struct coorlist * list_p;
void * mem_p;
struct rect rec;
struct point * reg;
int reg_size,min_reg_size,i;
unsigned int xsize,ysize;
double rho,reg_angle,prec,p,log_nfa,logNT;
int ls_count = 0; /* line segments are numbered 1,2,3,... */
/* check parameters */
if( image==NULL || image->data==NULL || image->xsize==0 || image->ysize==0 )
error("invalid image input.");
if( scale <= 0.0 ) error("'scale' value must be positive.");
if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
if( quant < 0.0 ) error("'quant' value must be positive.");
if( ang_th <= 0.0 || ang_th >= 180.0 )
error("'ang_th' value must be in the range (0,180).");
if( density_th < 0.0 || density_th > 1.0 )
error("'density_th' value must be in the range [0,1].");
if( n_bins <= 0 ) error("'n_bins' value must be positive.");
if( max_grad <= 0.0 ) error("'max_grad' value must be positive.");
/* angle tolerance */
prec = M_PI * ang_th / 180.0;
p = ang_th / 180.0;
rho = quant / sin(prec); /* gradient magnitude threshold */
/* scale image (if necessary) and compute angle at each pixel */
if( scale != 1.0 )
{
scaled_image = gaussian_sampler( image, scale, sigma_scale );
angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
&modgrad, (unsigned int) n_bins, max_grad );
free_image_double(scaled_image);
}
else
angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
(unsigned int) n_bins, max_grad );
xsize = angles->xsize;
ysize = angles->ysize;
logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0;
min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
that can give a meaningful event */
/* initialize some structures */
if( region != NULL ) /* image to output pixel region number, if asked */
*region = new_image_int_ini(angles->xsize,angles->ysize,0);
used = new_image_char_ini(xsize,ysize,NOTUSED);
reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(struct point) );
if( reg == NULL ) error("not enough memory!");
/* search for line segments */
for(; list_p != NULL; list_p = list_p->next )
if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
/* there is no risk of double comparison problems here
because we are only interested in the exact NOTDEF value */
{
/* find the region of connected point and ~equal angle */
region_grow( list_p->x, list_p->y, angles, reg, ®_size,
®_angle, used, prec );
/* reject small regions */
if( reg_size < min_reg_size ) continue;
/* construct rectangular approximation for the region */
region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
/* Check if the rectangle exceeds the minimal density of
region points. If not, try to improve the region.
The rectangle will be rejected if the final one does
not fulfill the minimal density condition.
This is an addition to the original LSD algorithm published in
"LSD: A Fast Line Segment Detector with a False Detection Control"
by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
The original algorithm is obtained with density_th = 0.0.
*/
if( !refine( reg, ®_size, modgrad, reg_angle,
prec, p, &rec, used, angles, density_th ) ) continue;
/* compute NFA value */
log_nfa = rect_improve(&rec,angles,logNT,eps);
if( log_nfa <= eps ) continue;
/* A New Line Segment was found! */
++ls_count; /* increase line segment counter */
/*
The gradient was computed with a 2x2 mask, its value corresponds to
points with an offset of (0.5,0.5), that should be added to output.
The coordinates origin is at the center of pixel (0,0).
*/
rec.x1 += 0.5; rec.y1 += 0.5;
rec.x2 += 0.5; rec.y2 += 0.5;
/* scale the result values if a subsampling was performed */
if( scale != 1.0 )
{
rec.x1 /= scale; rec.y1 /= scale;
rec.x2 /= scale; rec.y2 /= scale;
rec.width /= scale;
}
/* add line segment found to output */
add_5tuple(out, rec.x1, rec.y1, rec.x2, rec.y2, rec.width);
/* add region number to 'region' image if needed */
if( region != NULL )
for(i=0; idata[reg[i].x+reg[i].y*(*region)->xsize] = ls_count;
}
/* free memory */
free_image_double(angles);
free_image_double(modgrad);
free_image_char(used);
free( (void *) reg );
free( (void *) mem_p );
return out;
}
/*----------------------------------------------------------------------------*/
/** LSD Simple Interface with Scale.
*/
ntuple_list lsd_scale(image_double image, double scale)
{
/* LSD parameters */
double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as
sigma = sigma_scale/scale. */
double quant = 2.0; /* Bound to the quantization error on the
gradient norm. */
double ang_th = 22.5; /* Gradient angle tolerance in degrees. */
double eps = 0.0; /* Detection threshold, -log10(NFA). */
double density_th = 0.7; /* Minimal density of region points in rectangle. */
int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient
modulus. */
double max_grad = 255.0; /* Gradient modulus in the highest bin. The
default value corresponds to the highest
gradient modulus on images with gray
levels in [0,255]. */
return LineSegmentDetection( image, scale, sigma_scale, quant, ang_th, eps,
density_th, n_bins, max_grad, NULL );
}
/*----------------------------------------------------------------------------*/
/** LSD Simple Interface.
*/
ntuple_list lsd(image_double image)
{
/* LSD parameters */
double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */
return lsd_scale(image,scale);
}
/*----------------------------------------------------------------------------*/
================================================
FILE: thirdparty/LSD/lsd.h
================================================
/*----------------------------------------------------------------------------
LSD - Line Segment Detector on digital images
Copyright 2007-2010 rafael grompone von gioi (grompone@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** @file lsd.h
LSD module header
@author rafael grompone von gioi (grompone@gmail.com)
*/
/*----------------------------------------------------------------------------*/
#ifndef LSD_HEADER
#define LSD_HEADER
/*----------------------------------------------------------------------------*/
/*----------------------- 'list of n-tuple' data type ------------------------*/
/*----------------------------------------------------------------------------*/
/** 'list of n-tuple' data type
The i component, of the n-tuple number j, of an n-tuple list 'ntl'
is accessed with:
ntl->values[ i + j * ntl->dim ]
The dimension of the n-tuple (n) is:
ntl->dim
The number of number of n-tuples in the list is:
ntl->size
The maximum number of n-tuples that can be stored in the
list with the allocated memory at a given time is given by:
ntl->max_size
*/
typedef struct ntuple_list_s
{
unsigned int size;
unsigned int max_size;
unsigned int dim;
double * values;
} * ntuple_list;
void free_ntuple_list(ntuple_list in);
ntuple_list new_ntuple_list(unsigned int dim);
/*----------------------------------------------------------------------------*/
/*----------------------------- Image Data Types -----------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** char image data type
The pixel value at (x,y) is accessed by:
image->data[ x + y * image->xsize ]
with x and y integer.
*/
typedef struct image_char_s
{
unsigned char * data;
unsigned int xsize,ysize;
} * image_char;
void free_image_char(image_char i);
image_char new_image_char(unsigned int xsize, unsigned int ysize);
image_char new_image_char_ini( unsigned int xsize, unsigned int ysize,
unsigned char fill_value );
/*----------------------------------------------------------------------------*/
/** int image data type
The pixel value at (x,y) is accessed by:
image->data[ x + y * image->xsize ]
with x and y integer.
*/
typedef struct image_int_s
{
int * data;
unsigned int xsize,ysize;
} * image_int;
void free_image_int(image_int i);
image_int new_image_int(unsigned int xsize, unsigned int ysize);
image_int new_image_int_ini( unsigned int xsize, unsigned int ysize,
int fill_value );
/*----------------------------------------------------------------------------*/
/** double image data type
The pixel value at (x,y) is accessed by:
image->data[ x + y * image->xsize ]
with x and y integer.
*/
typedef struct image_double_s
{
double * data;
unsigned int xsize,ysize;
} * image_double;
void free_image_double(image_double i);
image_double new_image_double(unsigned int xsize, unsigned int ysize);
image_double new_image_double_ini( unsigned int xsize, unsigned int ysize,
double fill_value );
/*----------------------------------------------------------------------------*/
/*-------------------------- Line Segment Detector ---------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/* LSD Full Interface */
/*----------------------------------------------------------------------------*/
/** LSD Full Interface
@param image Input image.
@param scale When different than 1.0, LSD will scale the image by
Gaussian filtering.
Example: is scale=0.8, the input image will be subsampled
to 80% of its size, and then the line segment detector
will be applied.
Suggested value: 0.8
@param sigma_scale When scale!=1.0, the sigma of the Gaussian filter is:
sigma = sigma_scale / scale, if scale < 1.0
sigma = sigma_scale, if scale >= 1.0
Suggested value: 0.6
@param quant Bound to the quantization error on the gradient norm.
Example: if gray level is quantized to integer steps,
the gradient (computed by finite differences) error
due to quantization will be bounded by 2.0, as the
worst case is when the error are 1 and -1, that
gives an error of 2.0.
Suggested value: 2.0
@param ang_th Gradient angle tolerance in the region growing
algorithm, in degrees.
Suggested value: 22.5
@param eps Detection threshold, -log10(NFA).
The bigger, the more strict the detector is,
and will result in less detections.
(Note that the 'minus sign' makes that this
behavior is opposite to the one of NFA.)
The value -log10(NFA) is equivalent but more
intuitive than NFA:
- -1.0 corresponds to 10 mean false alarms
- 0.0 corresponds to 1 mean false alarm
- 1.0 corresponds to 0.1 mean false alarms
- 2.0 corresponds to 0.01 mean false alarms
.
Suggested value: 0.0
@param density_th Minimal proportion of region points in a rectangle.
Suggested value: 0.7
@param n_bins Number of bins used in the pseudo-ordering of gradient
modulus.
Suggested value: 1024
@param max_grad Gradient modulus in the highest bin. For example,
for images with integer gray levels in [0,255],
the maximum possible gradient value is 255.0.
Suggested value: 255.0
@param region Optional output: an int image where the pixels used
in some line support region are marked. Unused pixels
have the value '0' while the used ones have the
number of the line segment, numbered 1,2,3,...
If desired, a non NULL pointer to an image_int should
be used. The resulting image has the size of the image
used for the processing, that is, the size of the input
image scaled by the given factor 'scale'.
Suggested value: NULL
@return A 5-tuple list, where each 5-tuple corresponds to a
detected line segment. The five values are:
- x1,y1,x2,y2,width
.
for a line segment from (x1,y1) to (x2,y2) and
a width 'width'.
*/
ntuple_list LineSegmentDetection( image_double image, double scale,
double sigma_scale, double quant,
double ang_th, double eps, double density_th,
int n_bins, double max_grad,
image_int * region );
/*----------------------------------------------------------------------------*/
/* LSD Simple Interface with Scale */
/*----------------------------------------------------------------------------*/
/** LSD Simple Interface with Scale
@param image Input image.
@param scale When different than 1.0, LSD will scale the image by
Gaussian filtering.
Example: is scale=0.8, the input image will be subsampled
to 80% of its size, and then the line segment detector
will be applied.
Suggested value: 0.8
@return a 5-tuple list of detected line segments.
*/
ntuple_list lsd_scale(image_double image, double scale);
/*----------------------------------------------------------------------------*/
/* LSD Simple Interface */
/*----------------------------------------------------------------------------*/
/** LSD Simple Interface
@param image Input image.
@return a 5-tuple list of detected line segments.
*/
ntuple_list lsd(image_double image);
#endif /* !LSD_HEADER */
/*----------------------------------------------------------------------------*/
================================================
FILE: thirdparty/LSD/lsd_call_example.c
================================================
#include
#include "lsd.h"
int main(void)
{
image_double image;
ntuple_list out;
unsigned int x,y,i,j;
unsigned int X = 128; /* x image size */
unsigned int Y = 128; /* y image size */
/* create a simple image: left half black, right half gray */
image = new_image_double(X,Y);
for(x=0;xdata[ x + y * image->xsize ] = xsize);
for(i=0;isize;i++)
{
for(j=0;jdim;j++)
printf("%f ",out->values[ i * out->dim + j ]);
printf("\n");
}
/* free memory */
free_image_double(image);
free_ntuple_list(out);
return 0;
}
================================================
FILE: thirdparty/LSD/lsd_cmd.c
================================================
/*----------------------------------------------------------------------------
LSD - Line Segment Detector on digital images
Copyright 2007-2010 rafael grompone von gioi (grompone@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** @file lsd_cmd.c
Command line interface for LSD module (Line Segment Detector).
@author rafael grompone von gioi (grompone@gmail.com)
*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Definition of the command line interface. */
#define USE " \
#name: lsd \
#author: rafael grompone von gioi \
#version: 1.5 of December 3, 2010 \
#year: 2007-2010 \
#desc: Line Segment Detector \
#opt: scale | s | double | 0.8 | 0.0 | | \
Scale image by Gaussian filter before processing. \
#opt: sigma_coef | c | double | 0.6 | 0.0 | | \
Sigma for Gaussian filter is computed as sigma_coef/scale. \
#opt: quant | q | double | 2.0 | 0.0 | | \
Bound to quantization error on the gradient norm. \
#opt: ang_th | a | double | 22.5 | 0.0 | 180.0 | \
Gradient angle tolerance in degrees. \
#opt: eps | e | double | 0.0 | | | Detection threshold, -log10(max. NFA) \
#opt: density_th | d | double | 0.7 | 0.0 | 1.0 | \
Minimal density of region points in a rectangle to be accepted. \
#opt: n_bins | b | int | 1024 | 1 | | \
Number of bins in 'ordering' of gradient modulus. \
#opt: max_grad | m | double | 255.0 | 1 | | \
Gradient modulus in the highest bin. \
#opt: reg | R | str | | | | \
Output image showing pixels used by each detection. Scaled size. \
#opt: epsfile | P | str | | | | Output line segments into EPS file 'epsfile'. \
#opt: svgfile | S | str | | | | Output line segments into SVG file 'svgfile'. \
#opt: width | W | double | 1.5 | | | \
LS width used in EPS and SVG files. If <=0, use detected values. \
#req: in | | str | | | | Input image (PGM) \
#req: out | | str | | | | Line Segment output (ascii file: x1,y1,x2,y2,width) \
"
/*----------------------------------------------------------------------------*/
#include
#include
#include
#include
#include
#include "lsd.h"
#ifndef FALSE
#define FALSE 0
#endif /* !FALSE */
#ifndef TRUE
#define TRUE 1
#endif /* !TRUE */
/*----------------------------------------------------------------------------*/
/** Fatal error, print a message to standard-error output and exit.
*/
static void error(char * msg)
{
fprintf(stderr,"%s\n",msg);
exit(EXIT_FAILURE);
}
/*----------------------------------------------------------------------------*/
/*--------------------- Command Line interface handling ----------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
#define FIELD_LENGTH 160
#define VERSION_OPTION "--version"
/*----------------------------------------------------------------------------*/
/** Structure to store one argument definition and read value.
*/
struct argument
{
char name[FIELD_LENGTH]; /* name to internally identify the argument */
char desc[FIELD_LENGTH]; /* description */
char id; /* letter used with '-' to use the option */
char type; /* i=int, d=double, s=str, b=bool */
int required;
int assigned;
int def_value; /* true or false, a default value is assigned? */
char d_value[FIELD_LENGTH]; /* default value */
char s_value[FIELD_LENGTH]; /* string found, also the value if 'str' */
int i_value;
double f_value;
int min_set; /* true or false, is minimal value set? */
double min;
int max_set; /* true or false, is maximal value set? */
double max;
};
/*----------------------------------------------------------------------------*/
/** Structure to store the full set of argument definitions and its values.
*/
struct arguments
{
char name[FIELD_LENGTH];
char author[FIELD_LENGTH];
char version[FIELD_LENGTH];
char desc[FIELD_LENGTH];
char compiled[FIELD_LENGTH];
char year[FIELD_LENGTH];
int arg_num;
int arg_allocated;
struct argument * args;
};
/*----------------------------------------------------------------------------*/
/** Free an 'arguments' structure.
*/
static void free_arguments(struct arguments * arg)
{
free( (void *) arg->args );
free( (void *) arg );
}
/*----------------------------------------------------------------------------*/
/** Accepted characters in field identifier: numbers, letters and '_'.
*/
static int is_id_char(int c)
{
return c=='_' || isalpha(c) || isdigit(c);
}
/*----------------------------------------------------------------------------*/
/** Read next field definition in an argument definition.
*/
static char * get_next_field(char * p, char * id, char * value)
{
int n;
/* search for field id */
while( isspace(*p) ) ++p; /* skip spaces */
if( *p != '#' ) error("Error: missing '#' in 'use description'.");
++p;
for( n=0; is_id_char(*p) && n= FIELD_LENGTH ) error("Error: field too long in 'use description'.");
id[n] = '\0';
if( *(p++) != ':' ) error("Error: missing ':' in 'use description'.");
/* search for field value */
while( isspace(*p) ) ++p; /* skip spaces */
for( n=0; *p != '#' && *p != '\0' && n= FIELD_LENGTH ) error("Error: field too long in 'use description'.");
value[n] = '\0';
/* remove spaces at the end of the field */
while( --n >= 0 && isspace(value[n]) ) value[n] = '\0';
return p;
}
/*----------------------------------------------------------------------------*/
/** Read next token in an argument definition.
*/
static char * get_next_token(char * p, char div, char * value)
{
int n;
if( *p == '\0' )
error("Error: argument token expected in 'use description'.");
while( isspace(*p) ) ++p; /* skip spaces */
for( n=0; *p!=div && *p!='\0' && n= FIELD_LENGTH ) error("Error: field too long in 'use description'.");
value[n] = '\0';
while( --n >= 0 && isspace(value[n]) ) value[n] = '\0';
/* remove 'div' at the end of the token, if present */
if( *p == div ) ++p;
return p;
}
/*----------------------------------------------------------------------------*/
/** Process one argument description.
*/
static void process_new_argument(char * id, char * value,struct arguments * arg)
{
char token[FIELD_LENGTH];
char * p;
int i;
/* allocate memory if needed */
if( arg->arg_num >= arg->arg_allocated )
{
arg->arg_allocated *= 2;
arg->args = (struct argument *) realloc( (void *) arg->args,
arg->arg_allocated * sizeof(struct argument) );
if( arg->args == NULL ) error("Error: not enough memory.");
}
/* argument name */
p = get_next_token(value,'|',arg->args[arg->arg_num].name);
for( i=0; iarg_num; i++ )
if( strcmp(arg->args[i].name,arg->args[arg->arg_num].name) == 0 )
error("Error: argument name used twice in 'use description'.");
/* 'option' letter - to be used with '-' to identify option */
p = get_next_token(p,'|',token);
if( strcmp(id,"opt") == 0 )
{
arg->args[arg->arg_num].required = FALSE;
if( strlen(token) <= 0 || strlen(token) > 1 )
error("Error: invalid option letter in 'use description'.");
arg->args[arg->arg_num].id = token[0];
if( !isalpha(arg->args[arg->arg_num].id) )
error("Error: option id must be a letter in 'use description'.");
for( i=0; iarg_num; i++ )
if( !(arg->args[i].required) &&
arg->args[i].id == arg->args[arg->arg_num].id )
error("Error: option letter used twice in 'use description'.");
}
else /* must be 'req' - required argument */
{
arg->args[arg->arg_num].required = TRUE;
if( strlen(token) > 0 )
error("Error: unused option letter in 'use description'.");
arg->args[arg->arg_num].id = 0;
}
/* argument type */
p = get_next_token(p,'|',token);
if( strcmp(token,"int") == 0 ) arg->args[arg->arg_num].type = 'i';
else if( strcmp(token,"double") == 0 ) arg->args[arg->arg_num].type = 'd';
else if( strcmp(token,"str") == 0 ) arg->args[arg->arg_num].type = 's';
else if( strcmp(token,"bool") == 0 ) arg->args[arg->arg_num].type = 'b';
else error("Error: unknown argument type in 'use description'.");
/* required arguments can't be boolean */
if( arg->args[arg->arg_num].required && arg->args[arg->arg_num].type == 'b' )
error("Error: required arguments can't be boolean in 'use description'.");
/* default value */
p = get_next_token(p,'|',token);
if( strlen(token) > 0 )
{
if( arg->args[arg->arg_num].required )
error("Error: default value in required argument in 'use description'.");
arg->args[arg->arg_num].def_value = TRUE;
arg->args[arg->arg_num].assigned = TRUE;
strcpy(arg->args[arg->arg_num].d_value,token);
strcpy(arg->args[arg->arg_num].s_value,token);
if( arg->args[arg->arg_num].type == 'i' )
arg->args[arg->arg_num].i_value = atoi(token);
if( arg->args[arg->arg_num].type == 'd' )
arg->args[arg->arg_num].f_value = atof(token);
}
else
{
arg->args[arg->arg_num].def_value = FALSE;
arg->args[arg->arg_num].s_value[0] = '\0';
arg->args[arg->arg_num].assigned = FALSE;
}
/* required arguments can't have default value */
if( arg->args[arg->arg_num].required && arg->args[arg->arg_num].def_value )
error("Error: required args can't have default value in 'use description'.");
/* min value */
p = get_next_token(p,'|',token);
if( strlen(token) > 0 )
{
arg->args[arg->arg_num].min_set = TRUE;
arg->args[arg->arg_num].min = atof(token);
}
else
{
arg->args[arg->arg_num].min_set = FALSE;
}
/* max value */
p = get_next_token(p,'|',token);
if( strlen(token) > 0 )
{
arg->args[arg->arg_num].max_set = TRUE;
arg->args[arg->arg_num].max = atof(token);
}
else
{
arg->args[arg->arg_num].max_set = FALSE;
}
/* argument description */
p = get_next_token(p,'|',arg->args[arg->arg_num].desc);
/* the field should end there */
if( *p != '\0' )
error("Error: too many tokens in one argument in 'use description'.");
arg->arg_num++;
}
/*----------------------------------------------------------------------------*/
/** Process an argument definition.
*/
static void process_argument_description( char * desc, struct arguments * arg )
{
char id[FIELD_LENGTH];
char value[FIELD_LENGTH];
/* initialize 'arg' */
arg->name[0] = '\0';
arg->author[0] = '\0';
arg->version[0] = '\0';
arg->year[0] = '\0';
arg->desc[0] = '\0';
arg->compiled[0] = '\0';
arg->arg_num = 0;
arg->arg_allocated = 2;
arg->args = (struct argument *)
malloc( arg->arg_allocated * sizeof(struct argument) );
if( arg->args == NULL ) error("Error: not enough memory.");
/* assign compilation date and time */
strcat(arg->compiled,__DATE__);
strcat(arg->compiled," ");
strcat(arg->compiled,__TIME__);
/* process description */
while( *desc != '\0' )
{
desc = get_next_field(desc,id,value);
if( strcmp(id,"name") == 0 )
{
if( arg->name[0] != '\0' )
error("Error: multiple 'name' fields in 'use description'.");
strcpy(arg->name,value);
}
else if( strcmp(id,"author") == 0 )
{
if( arg->author[0] != '\0' )
error("Error: multiple 'author' fields in 'use description'.");
strcpy(arg->author,value);
}
else if( strcmp(id,"version") == 0 )
{
if( arg->version[0] != '\0' )
error("Error: multiple 'version' fields in 'use description'.");
strcpy(arg->version,value);
}
else if( strcmp(id,"year") == 0 )
{
if( arg->year[0] != '\0' )
error("Error: multiple 'year' fields in 'use description'.");
strcpy(arg->year,value);
}
else if( strcmp(id,"desc") == 0 )
{
if( arg->desc[0] != '\0' )
error("Error: multiple 'desc' fields in 'use description'.");
strcpy(arg->desc,value);
}
else if( strcmp(id,"opt") == 0 || strcmp(id,"req") == 0 )
{
process_new_argument(id,value,arg);
}
else
{
error("Error: unknown token in 'use description'.");
}
}
/* verify required arguments */
if( arg->name[0] == '\0' )
error("Error: program name is required in 'use description'.");
if( arg->author[0] == '\0' )
error("Error: author name is required in 'use description'.");
if( arg->version[0] == '\0' )
error("Error: version is required in 'use description'.");
if( arg->desc[0] == '\0' )
error("Error: program description is required in 'use description'.");
if( arg->year[0] == '\0' )
error("Error: year is required in 'use description'.");
}
/*----------------------------------------------------------------------------*/
/** Print version.
*/
static void print_version(struct arguments * arg, FILE * f)
{
fprintf(f,"Version %s, compiled %s\n",arg->version,arg->compiled);
}
/*----------------------------------------------------------------------------*/
/** Evaluate arguments.
*/
static void evaluate_arguments(int argc, char ** argv, struct arguments * arg)
{
int in_required_args = FALSE;
int n,i;
if( argc <= 0 ) error("Error: unexpected command line: missing command.");
for( n=1; !in_required_args && n < argc; n++ )
{
/* when an argument do not start with "-" it is not optional.
but, if the argument is just "-", then is a non optional
argument with value "-", and will be analyzed later. */
if( argv[n][0] != '-' || (argv[n][0]=='-' && strlen(argv[n])== 1) )
{
in_required_args = TRUE;
--n;
continue;
}
if( strlen(argv[n]) != 2 )
{
/* check if it is the special option 'version' */
if( strcmp(argv[n],VERSION_OPTION) == 0 )
{
print_version(arg,stdout);
free_arguments(arg);
exit(EXIT_SUCCESS);
}
/* otherwise is a bad option */
fprintf(stderr,"Error: %s ",argv[n]);
error("unrecognized option.");
}
for( i=0; iarg_num; i++ )
if( !(arg->args[i].required) && arg->args[i].id == argv[n][1] )
{
arg->args[i].assigned = TRUE;
if( arg->args[i].type != 'b' )
{
/* go for the value */
++n;
/* a value is expected */
if( n >= argc )
{
fprintf(stderr,"Error: in '%s': ",argv[n-1]);
error("a value was expected.");
}
if( strlen(argv[n]) > FIELD_LENGTH )
{
fprintf(stderr,"Error: in '%s': ",argv[n-1]);
error("value too long.");
}
strcpy(arg->args[i].s_value,argv[n]);
if( arg->args[i].type == 'i' )
{
arg->args[i].i_value = atoi(argv[n]);
if( arg->args[i].min_set &&
arg->args[i].i_value < (int) arg->args[i].min )
{
fprintf(stderr,"Error: in '%s': ",argv[n-1]);
error("value out of range.");
}
if( arg->args[i].max_set &&
arg->args[i].i_value > (int) arg->args[i].max )
{
fprintf(stderr,"Error: in '%s': ",argv[n-1]);
error("value out of range.");
}
}
if( arg->args[i].type == 'd' )
{
arg->args[i].f_value = atof(argv[n]);
if( arg->args[i].min_set &&
arg->args[i].f_value < arg->args[i].min )
{
fprintf(stderr,"Error: in '%s': ",argv[n-1]);
error("value out of range.");
}
if( arg->args[i].max_set &&
arg->args[i].f_value > arg->args[i].max )
{
fprintf(stderr,"Error: in '%s': ",argv[n-1]);
error("value out of range.");
}
}
}
i = arg->arg_num; /* argument found, stop search */
}
}
for( i=0; narg_num; i++ )
if( arg->args[i].required )
{
arg->args[i].assigned = TRUE;
strcpy(arg->args[i].s_value,argv[n]);
if( arg->args[i].type == 'i' )
{
arg->args[i].i_value = atoi(argv[n]);
if( arg->args[i].min_set &&
arg->args[i].i_value < (int) arg->args[i].min )
{
fprintf(stderr,"Error: in '%s': ",arg->args[i].name);
error("value out of range.");
}
if( arg->args[i].max_set &&
arg->args[i].i_value > (int) arg->args[i].max )
{
fprintf(stderr,"Error: in '%s': ",arg->args[i].name);
error("value out of range.");
}
}
if( arg->args[i].type == 'd' )
{
arg->args[i].f_value = atof(argv[n]);
if( arg->args[i].min_set &&
arg->args[i].f_value < arg->args[i].min )
{
fprintf(stderr,"Error: in '%s': ",arg->args[i].name);
error("value out of range.");
}
if( arg->args[i].max_set &&
arg->args[i].f_value > arg->args[i].max )
{
fprintf(stderr,"Error: in '%s': ",arg->args[i].name);
error("value out of range.");
}
}
++n;
}
}
/*----------------------------------------------------------------------------*/
/** Print command line interface help.
*/
static void use(struct arguments * arg)
{
int i;
fprintf(stderr,"%s, %s\n",arg->name,arg->desc);
fprintf(stderr,"Copyright (c) %s %s\n",arg->year,arg->author);
print_version(arg,stderr);
fprintf(stderr,"\nUsage: %s",arg->name);
/* always present version option */
fprintf(stderr," [%s]",VERSION_OPTION);
for(i=0;iarg_num;i++)
if( !(arg->args[i].required) )
{
fprintf(stderr," [-%c",arg->args[i].id);
if( arg->args[i].type != 'b' ) fprintf(stderr," %s",arg->args[i].name);
fprintf(stderr,"]");
}
for(i=0;iarg_num;i++)
if( arg->args[i].required )
fprintf(stderr," %s",arg->args[i].name);
fprintf(stderr,"\n\n");
/* option description */
fprintf(stderr," %s\tPrint version and compilation date/time and exit.\n",
VERSION_OPTION);
for(i=0;iarg_num;i++)
if( !(arg->args[i].required) )
{
fprintf(stderr," -%c",arg->args[i].id);
if( arg->args[i].type != 'b' )
{
fprintf(stderr," %s",arg->args[i].name);
}
fprintf(stderr,"\t%s\n",arg->args[i].desc);
if( arg->args[i].type == 'i' )
{
fprintf(stderr,"\t\t'%s' is integer",arg->args[i].name);
fprintf(stderr,", range [");
if( arg->args[i].min_set )
fprintf(stderr,"%d,",(int)arg->args[i].min);
else fprintf(stderr,"-inf,");
if( arg->args[i].max_set )
fprintf(stderr,"%d]",(int)arg->args[i].max);
else fprintf(stderr,"inf]");
if( arg->args[i].def_value )
fprintf(stderr,", default value %d",atoi(arg->args[i].d_value));
fprintf(stderr,"\n");
}
if( arg->args[i].type == 'd' )
{
fprintf(stderr,"\t\t'%s' is double",arg->args[i].name);
fprintf(stderr,", range [");
if( arg->args[i].min_set ) fprintf(stderr,"%g,",arg->args[i].min);
else fprintf(stderr,"-inf,");
if( arg->args[i].max_set ) fprintf(stderr,"%g]",arg->args[i].max);
else fprintf(stderr,"inf]");
if( arg->args[i].def_value )
fprintf(stderr,", default value %g",atof(arg->args[i].d_value));
fprintf(stderr,"\n");
}
}
for(i=0;iarg_num;i++)
if( arg->args[i].required )
{
fprintf(stderr," %s",arg->args[i].name);
fprintf(stderr,"\t%s\n",arg->args[i].desc);
if( arg->args[i].type == 'i' )
{
fprintf(stderr,"\t\t'%s' is integer",arg->args[i].name);
fprintf(stderr,", range [");
if( arg->args[i].min_set )
fprintf(stderr,"%d,",(int)arg->args[i].min);
else fprintf(stderr,"-inf,");
if( arg->args[i].max_set )
fprintf(stderr,"%d]",(int)arg->args[i].max);
else fprintf(stderr,"inf]");
fprintf(stderr,"\n");
}
if( arg->args[i].type == 'd' )
{
fprintf(stderr,"\t\t'%s' is double",arg->args[i].name);
fprintf(stderr,", range [");
if( arg->args[i].min_set ) fprintf(stderr,"%f,",arg->args[i].min);
else fprintf(stderr,"-inf,");
if( arg->args[i].max_set ) fprintf(stderr,"%f]",arg->args[i].max);
else fprintf(stderr,"inf]");
fprintf(stderr,"\n");
}
}
fprintf(stderr,"\n");
free_arguments(arg);
exit(EXIT_FAILURE);
}
/*----------------------------------------------------------------------------*/
/** Process and evaluate a program arguments.
*/
static struct arguments * process_arguments(char * desc, int argc, char ** argv)
{
struct arguments * arg;
int i;
/* get memory */
arg = (struct arguments *) malloc(sizeof(struct arguments));
if( arg == NULL ) error("Error: not enough memory.");
process_argument_description(desc,arg);
evaluate_arguments(argc,argv,arg);
/* if there are missing arguments print the 'use' information */
for(i=0; iarg_num; i++)
if( arg->args[i].required && !(arg->args[i].assigned) ) use(arg);
return arg;
}
/*----------------------------------------------------------------------------*/
/** Test if an argument has a defined value.
*/
static int is_assigned(struct arguments * arg, char * name)
{
int i;
for(i=0; iarg_num; i++)
if( strcmp(name,arg->args[i].name) == 0 ) return arg->args[i].assigned;
error("Error: is_assigned: unknown argument.");
return -1; /* useless, just to prevent warning in strict compilers */
}
/*----------------------------------------------------------------------------*/
/** Get the value of a string argument.
*/
static char * get_str(struct arguments * arg, char * name)
{
int i;
for(i=0; iarg_num; i++)
if( strcmp(name,arg->args[i].name) == 0 )
{
if( arg->args[i].type == 's' )
{
if( !(arg->args[i].assigned) ) return NULL;
return arg->args[i].s_value;
}
else error("Error: get_str: the parameter is not a double.");
}
error("Error: get_str: unknown argument.");
return NULL; /* useless, just to prevent warning in strict compilers */
}
/*----------------------------------------------------------------------------*/
/** Get the value of an integer argument.
*/
static int get_int(struct arguments * arg, char * name)
{
int i;
for(i=0; iarg_num; i++)
if( strcmp(name,arg->args[i].name) == 0 )
{
if( !(arg->args[i].assigned) )
error("Error: get_int: parameter not assigned.");
if( arg->args[i].type == 'i' ) return arg->args[i].i_value;
else error("Error: get_int: the parameter is not an integer.");
}
error("Error: get_int: unknown argument.");
return -1; /* useless, just to prevent warning in strict compilers */
}
/*----------------------------------------------------------------------------*/
/** Get the value of a double argument.
*/
static double get_double(struct arguments * arg, char * name)
{
int i;
for(i=0; iarg_num; i++)
if( strcmp(name,arg->args[i].name) == 0 )
{
if( !(arg->args[i].assigned) )
error("Error: get_double: parameter not assigned.");
if( arg->args[i].type == 'd' ) return arg->args[i].f_value;
else error("Error: get_double: the parameter is not a double.");
}
error("Error: get_double: unknown argument.");
return -1.0; /* useless, just to prevent warning in strict compilers */
}
/*----------------------------------------------------------------------------*/
/*------------------------------ PGM image I/O -------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/** Skip white characters and comments in a PGM file.
*/
static void skip_whites_and_comments(FILE * f)
{
int c;
do
{
while(isspace(c=getc(f))); /* skip spaces */
if(c=='#') /* skip comments */
while( c!='\n' && c!='\r' && c!=EOF )
c=getc(f);
}
while( c == '#' || isspace(c) );
if( c != EOF && ungetc(c,f) == EOF )
error("Error: unable to 'ungetc' while reading PGM file.");
}
/*----------------------------------------------------------------------------*/
/** Read a ASCII number from a PGM file.
*/
static unsigned int get_num(FILE * f)
{
unsigned int num;
int c;
while(isspace(c=getc(f)));
if(!isdigit(c)) error("Error: corrupted PGM file.");
num = (unsigned int) (c - '0');
while( isdigit(c=getc(f)) ) num = 10 * num + c - '0';
if( c != EOF && ungetc(c,f) == EOF )
error("Error: unable to 'ungetc' while reading PGM file.");
return num;
}
/*----------------------------------------------------------------------------*/
/** Read a PGM file into an "image_double".
If the name is "-" the file is read from standard input.
*/
static image_double read_pgm_image_double(char * name)
{
FILE * f;
int c,bin;
unsigned int xsize,ysize,depth,x,y;
image_double image;
/* open file */
if( strcmp(name,"-") == 0 ) f = stdin;
else f = fopen(name,"rb");
if( f == NULL ) error("Error: unable to open input image file.");
/* read header */
if( getc(f) != 'P' ) error("Error: not a PGM file!");
if( (c=getc(f)) == '2' ) bin = FALSE;
else if( c == '5' ) bin = TRUE;
else error("Error: not a PGM file!");
skip_whites_and_comments(f);
xsize = get_num(f); /* X size */
skip_whites_and_comments(f);
ysize = get_num(f); /* Y size */
skip_whites_and_comments(f);
depth = get_num(f); /* depth */
if(depth==0) fprintf(stderr,"Warning: depth=0, probably invalid PGM file\n");
/* white before data */
if(!isspace(c=getc(f))) error("Error: corrupted PGM file.");
/* get memory */
image = new_image_double(xsize,ysize);
/* read data */
for(y=0;ydata[ x + y * xsize ] = bin ? (double) getc(f)
: (double) get_num(f);
/* close file if needed */
if( f != stdin && fclose(f) == EOF )
error("Error: unable to close file while reading PGM file.");
return image;
}
/*----------------------------------------------------------------------------*/
/** Write an "image_int" into a PGM file.
If the name is "-" the file is written to standard output.
*/
static void write_pgm_image_int(image_int image, char * name)
{
FILE * f;
unsigned int x,y,n;
int v,max,min;
/* check min and max values */
max = min = 0;
for(y=0; yysize; y++)
for(x=0; xxsize; x++)
{
v = image->data[ x + y * image->xsize ];
if( v > max ) max = v;
if( v < min ) min = v;
}
if( min < 0 ) fprintf(stderr,
"Warning: write_pgm_image_int: negative values in '%s'.\n",name);
if( max > 65535 ) fprintf(stderr,
"Warning: write_pgm_image_int: values exceeding 65535 in '%s'.\n",name);
/* open file */
if( strcmp(name,"-") == 0 ) f = stdout;
else f = fopen(name,"w");
if( f == NULL ) error("Error: unable to open output image file.");
/* write header */
fprintf(f,"P2\n");
fprintf(f,"%u %u\n",image->xsize,image->ysize);
fprintf(f,"%d\n",max);
/* write data */
for(n=0,y=0; yysize; y++)
for(x=0; xxsize; x++)
{
fprintf(f,"%d ",image->data[ x + y * image->xsize ]);
if(++n==8) /* lines should not be longer than 70 characters */
{
fprintf(f,"\n");
n = 0;
}
}
/* close file if needed */
if( f != stdout && fclose(f) == EOF )
error("Error: unable to close file while writing PGM file.");
}
/*----------------------------------------------------------------------------*/
/*----------------------------- Write EPS File -------------------------------*/
/*----------------------------------------------------------------------------*/
/** Write line segments into an EPS file.
If the name is "-" the file is written to standard output.
According to
Adobe "Encapsulated PostScript File Format Specification",
Version 3.0, 1 May 1992,
and
Adobe "PostScript(R) LANGUAGE REFERENCE", third edition, 1999.
*/
static void write_eps( ntuple_list segs, char * filename,
unsigned int xsize, unsigned int ysize, double width )
{
FILE * eps;
unsigned int i;
/* open file */
if( strcmp(filename,"-") == 0 ) eps = stdout;
else eps = fopen(filename,"w");
if( eps == NULL ) error("Error: unable to open EPS output file.");
/* write EPS header */
fprintf(eps,"%%!PS-Adobe-3.0 EPSF-3.0\n");
fprintf(eps,"%%%%BoundingBox: 0 0 %u %u\n",xsize,ysize);
fprintf(eps,"%%%%Creator: LSD, Line Segment Detector\n");
fprintf(eps,"%%%%Title: (%s)\n",filename);
fprintf(eps,"%%%%EndComments\n");
/* write line segments */
for(i=0;isize;i++)
{
fprintf( eps,"newpath %f %f moveto %f %f lineto %f setlinewidth stroke\n",
segs->values[i*segs->dim+0],
(double) ysize - segs->values[i*segs->dim+1],
segs->values[i*segs->dim+2],
(double) ysize - segs->values[i*segs->dim+3],
width <= 0.0 ? segs->values[i*segs->dim+4] : width );
}
/* close EPS file */
fprintf(eps,"showpage\n");
fprintf(eps,"%%%%EOF\n");
if( eps != stdout && fclose(eps) == EOF )
error("Error: unable to close file while writing EPS file.");
}
/*----------------------------------------------------------------------------*/
/*----------------------------- Write SVG File -------------------------------*/
/*----------------------------------------------------------------------------*/
/** Write line segments into a SVG file.
If the name is "-" the file is written to standard output.
*/
static void write_svg( ntuple_list segs, char * filename,
unsigned int xsize, unsigned int ysize, double width )
{
FILE * svg;
unsigned int i;
/* open file */
if( strcmp(filename,"-") == 0 ) svg = stdout;
else svg = fopen(filename,"w");
if( svg == NULL ) error("Error: unable to open SVG output file.");
/* write SVG header */
fprintf(svg,"\n");
fprintf(svg,"\n");
fprintf(svg,"\n");
if( svg != stdout && fclose(svg) == EOF )
error("Error: unable to close file while writing SVG file.");
}
/*----------------------------------------------------------------------------*/
/* Main */
/*----------------------------------------------------------------------------*/
/** Main function call
*/
int main(int argc, char ** argv)
{
struct arguments * arg = process_arguments(USE,argc,argv);
FILE * output;
image_double image;
ntuple_list out;
image_int region;
unsigned int i,j;
/* read input file */
image = read_pgm_image_double(get_str(arg,"in"));
/* execute LSD */
out = LineSegmentDetection( image,
get_double(arg,"scale"),
get_double(arg,"sigma_coef"),
get_double(arg,"quant"),
get_double(arg,"ang_th"),
get_double(arg,"eps"),
get_double(arg,"density_th"),
get_int(arg,"n_bins"),
get_double(arg,"max_grad"),
is_assigned(arg,"reg") ? ®ion : NULL );
/* output */
if( strcmp(get_str(arg,"out"),"-") == 0 ) output = stdout;
else output = fopen(get_str(arg,"out"),"w");
if( output == NULL ) error("Error: unable to open ASCII output file.");
for(i=0;isize;i++)
{
for(j=0;jdim;j++)
fprintf(output,"%f ",out->values[i*out->dim+j]);
fprintf(output,"\n");
}
if( output != stdout && fclose(output) == EOF ) /* close file if needed */
error("Error: unable to close file while output file.");
/* store region output if needed */
if(is_assigned(arg,"reg"))
{
write_pgm_image_int( region, get_str(arg,"reg") );
free_image_int(region);
}
/* create EPS output if needed */
if(is_assigned(arg,"epsfile"))
write_eps( out, get_str(arg,"epsfile"), image->xsize, image->ysize,
get_double(arg,"width") );
/* create SVG output if needed */
if(is_assigned(arg,"svgfile"))
write_svg( out, get_str(arg,"svgfile"), image->xsize, image->ysize,
get_double(arg,"width") );
/* free memory */
free_image_double(image);
free_ntuple_list(out);
free_arguments(arg);
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------------------*/
================================================
FILE: thirdparty/LSWMS/CMakeLists.txt
================================================
##############################
PROJECT( lineSegment )
##############################
cmake_minimum_required (VERSION 2.6)
# Packages
find_package( OpenCV REQUIRED )
# Includes
include_directories(
${OPENCV_INCLUDE_DIR}
${PROJECT_BINARY_DIR}
)
set(SOURCE_FILES
main.cpp
LSWMS.cpp
LSWMS.h
)
# Add executable and target link libraries
ADD_EXECUTABLE( lineSegment ${SOURCE_FILES})
TARGET_LINK_LIBRARIES( lineSegment ${OpenCV_LIBS})
================================================
FILE: thirdparty/LSWMS/LSWMS.cpp
================================================
#include "LSWMS.h"
#ifdef linux
#include
#endif
#include
#include
#define ABS(a) (((a) < 0) ? -(a) : (a))
#define NOT_A_VALID_ANGLE 5
#define ANGLE_MARGIN 22.5
#define MAX_ERROR 0.19625 // ((22.5/2)*CV_PI/180
using namespace cv;
using namespace std;
static void setTo14Quads(DIR_POINT &dp)
{
if(dp.vx < 0)
{
dp.vx = -dp.vx;
dp.vy = -dp.vy;
}
}
LSWMS::LSWMS(const cv::Size imSize, const int R, const int numMaxLSegs, bool verbose)
{
// **********************************************
// Constructor of class LSWMS (Slice Sampling Weighted
// Mean-Shift)
// Args:
// -> imSize - Size of image
// -> R - accuracy parameter
// -> numMaxLSegs - requested number of line segments.
// if set to 0, the algorithm finds exploring
// the whole image until no more line segments
// can be found
// -> verbose - show messages
// **********************************************
__verbose = verbose;
// Init variables
__imSize = imSize;
__imWidth = imSize.width;
__imHeight = imSize.height;
__R = R;
__numMaxLSegs = numMaxLSegs;
__N = 2*__R + 1;
// Add padding it necessary
if( (__imSize.width + 2*__N) % 4 != 0)
__N = __N + ((__imSize.width + 2*__N) % 4)/2;
__imPadSize.width = __imSize.width + 2*__N;
__imPadSize.height = __imSize.height + 2*__N;
// Init images
__img = cv::Mat(__imSize, CV_8U);
__imgPad = cv::Mat(__imPadSize, CV_8U);
__roiRect = cv::Rect(__N, __N, __imSize.width, __imSize.height);
// Mask image
__M = cv::Mat(__imPadSize, CV_8U);
__M.setTo(255);
// Angle mask
__A = cv::Mat(__imPadSize, CV_32F);
__A.setTo(NOT_A_VALID_ANGLE);
// Gradient images
__G = cv::Mat(__imPadSize, CV_8U);
__G.setTo(0);
__Gx = cv::Mat(__imPadSize, CV_16S);
__Gx.setTo(0);
__Gy = cv::Mat(__imPadSize, CV_16S);
__Gy.setTo(0);
// Iterator
if(__numMaxLSegs != 0)
{
__sampleIterator = std::vector(__imSize.width*__imSize.height, 0);
for(unsigned int k=0; k<__sampleIterator.size(); k++)
__sampleIterator[k] = k;
cv::randShuffle(__sampleIterator);
}
// Angular m_margin
__margin = (float)(ANGLE_MARGIN*CV_PI/180);
}
int LSWMS::run(const cv::Mat &img, std::vector &lSegs, std::vector &errors)
{
// **********************************************
// This function analyses the input image and finds
// line segments that are stored in the given vector
// Args:
// -> img - Color or grayscale input image
// <- lSegs - Output vector of line segments
// <- errors - Output vector of angular errors
// Ret:
// RET_OK - no errors found
// RET_ERROR - errors found
// **********************************************
// Clear line segment container
lSegs.clear();
errors.clear();
// Input image to __img
if(img.channels() == 3)
cv::cvtColor(img, __img, CV_BGR2GRAY);
else
__img = img;
// Add convolution borders
cv::copyMakeBorder(__img, __imgPad, __N, __N, __N, __N, cv::BORDER_REPLICATE); // This way we avoid line segments at the boundaries of the image
// Init Mask matrix
__M.setTo(255);
__imgPadROI = __M(__roiRect);
__imgPadROI.setTo(0);
// Compute Gradient map
// Call to the computation of the gradient and angle maps (SOBEL)
int retP = computeGradientMaps(__imgPad, __G, __Gx, __Gy);
if(retP == RET_ERROR)
{
if(__verbose) { printf("ERROR: Probability map could not be computed\n"); }
return RET_ERROR;
}
// Set padding to zero
int NN = __N + __R;
setPaddingToZero(__Gx, NN);
setPaddingToZero(__Gy, NN);
setPaddingToZero(__G, NN);
// Line segment finder
int retLS = findLineSegments(__G, __Gx, __Gy, __A, __M, lSegs, errors);
return retLS;
return RET_OK;
}
int LSWMS::computeGradientMaps(const cv::Mat &img, cv::Mat &G, cv::Mat &Gx, cv::Mat &Gy)
{
// **********************************************
// SOBEL mode
//
// This function obtains the gradient image (G, Gx, Gy),
// and fills the angular map A.
//
// Args:
// -> img - Grayscale input image
// <- G - Gradient magnitude image
// <- Gx - Gradient x-magnitude image
// <- Gy - Gradient y-magnitude image
// Ret:
// RET_OK - no errors found
// RET_ERROR - errors found
// **********************************************
if(__verbose) { printf("Compute gradient maps..."); fflush(stdout); }
// Sobel operator
int ddepth = CV_16S;
cv::Mat absGx, absGy;
cv::Sobel(img, Gx, ddepth, 1, 0);
convertScaleAbs(Gx, absGx, (double)1/8);
cv::Sobel(img, Gy, ddepth, 0, 1);
convertScaleAbs(Gy, absGy, (double)1/8);
//cv::addWeighted(absGx, 0.5, absGy, 0.5, 0, G, CV_8U);
cv::add(absGx, absGy, G);
// Obtain the threshold
cv::Scalar meanG = cv::mean(G);
__meanG = (int)meanG.val[0];
if(__verbose) { printf(" computed: __meanG = %d\n", __meanG); }
// Move from 2nd to 4th and from 3rd to 1st
// From 2nd to 4th,
//if( gx < 0 && gy > 0 ) {gx = -gx; gy = -gy;} // from 2 to 4
//if( gx < 0 && gy < 0 ) {gx = -gx; gy = -gy;} // from 3 to 1
//if( gx < 0 ) {gx = -gx; gy = -gy;}
int movedCounter = 0;
for(int j=0; j<__imPadSize.height; ++j)
{
short *ptRowGx = Gx.ptr(j);
short *ptRowGy = Gy.ptr(j);
for(int i=0; i<__imPadSize.width; ++i)
{
if(ptRowGx[i] < 0)
{
ptRowGy[i] = -ptRowGy[i];
ptRowGx[i] = -ptRowGx[i];
movedCounter++;
}
}
}
if(__verbose) { printf("Moved %d/%d (%.2f%%) elements to 1st4th quadrant\n", movedCounter, __imPadSize.height*__imPadSize.width, ((double)100*movedCounter)/((double)__imPadSize.height*__imPadSize.width)); }
if(__meanG > 0 && __meanG < 256)
return RET_OK;
else
return RET_ERROR;
}
int LSWMS::findLineSegments(const cv::Mat &G, const cv::Mat &Gx, const cv::Mat &Gy, cv::Mat &A, cv::Mat &M, std::vector &lSegs, std::vector &errors)
{
// **********************************************
// This function finds line segments using the
// probability map P, the gradient components
// Gx, and Gy and the angle map A.
//
// Args:
// -> G - Gradient magnitude map
// -> Gx - Gradient x-magnitude image
// -> Gy - Gradient y-magnitude image
// <- M - Mask image of visited pixels
// <- lSegs - vector of detected line segments
// <- errors - vector of angular errors
// Ret:
// RET_OK - no errors found
// RET_ERROR - errors found
// **********************************************
// Loop over the image
int x0, y0;
int kIterator = 0;
int imgSize = __img.cols*__img.rows;
while(true)
{
if (kIterator == imgSize)
{
// This is the end
break;
}
if(__numMaxLSegs == 0)
{
x0 = kIterator%__img.cols;
y0 = kIterator/__img.cols;
}
else
{
x0 = __sampleIterator[kIterator]%__img.cols;
y0 = __sampleIterator[kIterator]/__img.cols;
}
kIterator++;
// Add padding
x0 = x0 + __N;
y0 = y0 + __N;
// Check mask and value
if(__M.at(y0,x0)==0 && G.at(y0,x0) > __meanG)
{
// The sample is (x0, y0)
cv::Point ptOrig(x0, y0);
float gX = (float)Gx.at(y0,x0);
float gY = (float)Gy.at(y0,x0);
DIR_POINT dpOrig(ptOrig, gX, gY); // Since it is computed from Gx, Gy, it is in 1º4º
// Line segment generation
float error = 0;
if(__verbose) { printf("-------------------------------\n"); }
if(__verbose) { printf("Try dpOrig=(%d,%d,%.2f,%.2f)...\n", dpOrig.pt.x, dpOrig.pt.y, dpOrig.vx, dpOrig.vy); }
int retLS = lineSegmentGeneration(dpOrig, __lSeg, error);
if( (retLS == RET_OK) && error < MAX_ERROR )
{
if(__verbose) { printf("lSeg generated=(%d,%d)->(%d,%d)...\n", __lSeg[0].x, __lSeg[0].y, __lSeg[1].x, __lSeg[1].y); }
if(__verbose) { printf("-------------------------------\n"); }
lSegs.push_back(__lSeg);
errors.push_back((double)error);
if(__numMaxLSegs != 0 && lSegs.size() >= (unsigned int)__numMaxLSegs)
break;
}
else
{
// Mark as visited
cv::Rect w(x0-__R, y0 -__R, __N, __N);
cv::Mat roi = __M(w);
roi.setTo(255);
}
}
}
return RET_OK;
}
int LSWMS::lineSegmentGeneration(const DIR_POINT &dpOrig, LSEG &lSeg, float &error)
{
// **********************************************
// Starts at dpOrig and generates lSeg
//
// Args:
// -> dpOrig - starting DIR_POINT
// <- lSeg - detected line segment
// Ret:
// RET_OK - lSeg created
// RET_ERROR - lSeg not created
// **********************************************
// Check input data
if(dpOrig.pt.x < 0 || dpOrig.pt.x >= __G.cols || dpOrig.pt.y<0 || dpOrig.pt.y >= __G.rows)
return RET_ERROR;
// Find best candidate with Mean-Shift
// -----------------------------------------------------
DIR_POINT dpCentr = dpOrig;
if(__verbose)
{
printf("\tMean-Shift(Centr): from (%d,%d,%.2f,%.2f) to...", dpOrig.pt.x, dpOrig.pt.y, dpOrig.vx, dpOrig.vy);
fflush(stdout);
}
int retMSC = weightedMeanShift(dpOrig, dpCentr, __M); /// COMO LE PASO __M, TIENE EN CUENTA SI SE HA VISITADO O NO
if(__verbose) { printf(" (%d,%d,%.2f, %.2f)\n", dpCentr.pt.x, dpCentr.pt.y, dpCentr.vx, dpCentr.vy); }
if(retMSC == RET_ERROR)
{
if(__verbose) { printf("\tMean-Shift reached not a valid point\n"); }
return RET_ERROR;
}
// Grow in two directions from dpCentr
// -----------------------------------------------------
if(__verbose) { printf("\tGROW 1:"); fflush(stdout); }
cv::Point pt1;
float retG1 = grow(dpCentr, pt1, 1);
float d1 = (float)((dpCentr.pt.x - pt1.x)*(dpCentr.pt.x - pt1.x) + (dpCentr.pt.y - pt1.y)*(dpCentr.pt.y - pt1.y));
if(__verbose) { printf("\tpt1(%d,%d), dist = %.2f, error=%.4f\n", pt1.x, pt1.y, d1, retG1); }
if(__verbose) { printf("\tGROW 2:"); fflush(stdout); }
cv::Point pt2;
float retG2 = grow(dpCentr, pt2, 2);
float d2 = (float)((dpCentr.pt.x - pt2.x)*(dpCentr.pt.x - pt2.x) + (dpCentr.pt.y - pt2.y)*(dpCentr.pt.y - pt2.y));
if(__verbose) { printf("\tpt2(%d,%d), dist = %.2f, error=%.4f\n", pt2.x, pt2.y, d2, retG2); }
if(retG1 == -1 && retG2 == -1)
return RET_ERROR;
// Select the most distant extremum
if(d10)
{
dirX = dirX/norm;
dirY = dirY/norm;
DIR_POINT dpAux(dpCentr.pt, -(-dirY), -dirX); // DIR_POINT must be filled ALWAYS with gradient vectors
float retG = grow(dpAux, pt2, 1);
error = retG;
}
else
{
pt2 = dpCentr.pt;
}
// Check
dirX = (float)(pt1.x -pt2.x);
dirY = (float)(pt1.y -pt2.y);
if( sqrt(dirX*dirX + dirY*dirY) < __N)
{
if(__verbose) { printf("Line segment not generated: Too short.\n"); }
return RET_ERROR;
}
// Output line segment
if(__verbose) { printf("LSeg = (%d,%d)-(%d,%d)\n", pt2.x, pt2.y, pt1.x, pt1.y); }
lSeg.clear();
lSeg.push_back(cv::Point(pt2.x - 2*__R, pt2.y - 2*__R));
lSeg.push_back(cv::Point(pt1.x - 2*__R, pt1.y - 2*__R));
// Update visited positions matrix
updateMask(pt1,pt2);
return RET_OK;
}
void LSWMS::updateMask(cv::Point pt1, cv::Point pt2)
{
// Bresenham from one extremum to the other
int x1 = pt1.x, x2 = pt2.x, y1 = pt1.y, y2 = pt2.y;
int dx = ABS(x2-x1);
int dy = ABS(y2-y1);
int sx, sy, err, e2;
if(x1 < x2) sx = 1; else sx = -1;
if(y1 < y2) sy = 1; else sy = -1;
err = dx-dy;
while(true)
{
// Current value is (x1,y1)
// -------------------------------
// Do...
// Set window to "visited=255"
for(int j=y1-__R; j<=y1+__R; ++j)
{
unsigned char* ptRowM = __M.ptr(j);
for(int i=x1-__R; i<=x1+__R; ++i)
ptRowM[i] = 255;
}
// -------------------------------
// Check end
if (x1 == x2 && y1 == y2) break;
// Update position for next iteration
e2 = 2*err;
if(e2 > -dy) { err = err - dy; x1 = x1 + sx;}
if(e2 < dx) { err = err + dx; y1 = y1 + sy;}
}
}
int LSWMS::weightedMeanShift(const DIR_POINT &dpOrig, DIR_POINT &dpDst, const cv::Mat &M)
{
// **********************************************
// Refines dpOrig and creates dpDst
//
// Args:
// -> dpOrig - starting DIR_POINT
// <- dpDst - refined DIR_POINT
// Ret:
// RET_OK - dpDst created
// RET_ERROR - dpDst not found
//
// Called from "lineSegmentGeneration"
// **********************************************
// MAIN LOOP: loop until MS generates no movement (or dead-loop)
__seeds.clear();
DIR_POINT dpCurr = dpOrig; // The initial dp is in 1º4º
dpDst = dpOrig;
while(true)
{
// Check point
if(dpCurr.pt.x < 0 || dpCurr.pt.x >= __G.cols || dpCurr.pt.y<0 || dpCurr.pt.y >= __G.rows)
return RET_ERROR;
// Check direction
if(dpCurr.vx==0 && dpCurr.vy == 0)
return RET_ERROR;
// Convert to 1º4º (maybe not needed)
setTo14Quads(dpCurr);
// Check already visited
if(!M.empty())
{
if(M.at(dpCurr.pt.y, dpCurr.pt.x) == 255)
{
return RET_ERROR;
}
}
// Check if previously used as seed for this MS-central (this is to avoid dead-loops)
for(unsigned int i=0; i<__seeds.size(); i++)
{
if(__seeds[i].x == dpCurr.pt.x && __seeds[i].y == dpCurr.pt.y)
{
dpDst = dpCurr;
return RET_ERROR;
}
}
// Define bounds
int xMin = dpCurr.pt.x - __R;
int yMin = dpCurr.pt.y - __R;
int xMax = dpCurr.pt.x + __R;
int yMax = dpCurr.pt.y + __R;
int offX = __R;
int offY = __R;
if( xMin < 0 || yMin < 0 || xMax >= __G.cols || yMax >= __G.rows)
return RET_ERROR;
__seeds.push_back(dpCurr.pt);
// Define rois
cv::Rect roi(xMin, yMin, xMax-xMin+1, yMax-yMin+1);
cv::Mat gBlock = cv::Mat(__G, roi);
cv::Mat gXBlock = cv::Mat(__Gx, roi);
cv::Mat gYBlock = cv::Mat(__Gy, roi);
cv::Mat aBlock = cv::Mat(__A, roi);
cv::Mat insideBlock = cv::Mat(gBlock.size(), CV_8U); // 0: outside, 1:inside
insideBlock.setTo(1);
// Update angles (this is to compute angles only once)
for(int j=0; j(j,i) = atan2((float)gYBlock.at(j,i), (float)gXBlock.at(j,i));
}
}
//if(__verbose) printf("dpCurr(%d,%d)(%.2f,%.2f)\n", dpCurr.pt.x, dpCurr.pt.y, dpCurr.vx, dpCurr.vy);
//if(__verbose) std::cout << "gBlock" << gBlock << endl;
//if(__verbose) std::cout << "gXBlock" << gXBlock << endl;
//if(__verbose) std::cout << "gYBlock" << gYBlock << endl;
//if(__verbose) std::cout << "aBlock" << aBlock << endl;
// ----------------------------------
// Angle analysis
float currentAngle = atan2(dpCurr.vy, dpCurr.vx); // output is between (-CV_PI/2, CV_PI/2)
//if(__verbose) printf("currentAngle = %.2f\n", currentAngle);
// ----------------------------------
float angleShift = 0;
int outsideCounter = 0;
if(currentAngle - __margin < -PI_2)
{
// Shift angles according to currentAngle to avoid discontinuities
//if(__verbose) printf("shift angles since %.2f - %.2f < %.2f\n", currentAngle, __margin, -PI_2);
angleShift = currentAngle;
aBlock = aBlock - currentAngle;
currentAngle = 0;
float minAngle = currentAngle - __margin;
float maxAngle = currentAngle + __margin;
for(int j=0; j(j);
uchar *ptRowGBlock = gBlock.ptr(j);
for(int i=0; i PI_2) ptRowABlock[i] -= (float)CV_PI;
if(ptRowABlock[i] < minAngle || ptRowABlock[i] > maxAngle)
{
//ptRowGBlock[i] = -1;
insideBlock.at(j,i) = 0;
outsideCounter++;
}
}
}
// Restore
aBlock = aBlock + angleShift;
}
else if(currentAngle + __margin > PI_2)
{
// Shift angles according to currentAngle to avoid discontinuities
//if(__verbose) printf("shift angles since %.2f + %.2f > %.2f\n", currentAngle, __margin, PI_2);
angleShift = currentAngle;
aBlock = aBlock - currentAngle;
currentAngle = 0;
float minAngle = currentAngle - __margin;
float maxAngle = currentAngle + __margin;
for(int j=0; j(j);
uchar *ptRowGBlock = gBlock.ptr(j);
for(int i=0; i PI_2) ptRowABlock[i] -= (float)CV_PI;
if(ptRowABlock[i] < minAngle || ptRowABlock[i] > maxAngle)
{
//ptRowGBlock[i] = -1;
insideBlock.at(j,i) = 0;
outsideCounter++;
}
}
}
// Restore
aBlock = aBlock + angleShift;
}
else
{
angleShift = 0;
float minAngle = currentAngle - __margin;
float maxAngle = currentAngle + __margin;
for(int j=0; j(j);
uchar *ptRowGBlock = gBlock.ptr(j);
for(int i=0; i maxAngle)
{
//ptRowGBlock[i] = -1;
insideBlock.at(j,i) = 0;
outsideCounter++;
}
}
}
}
//if(__verbose) std::cout << "insideBlock" << insideBlock << endl;
//if(__verbose) std::cout << "aBlock(after computing insideBlock" << aBlock << endl;
// Check number of samples inside the bandwidth
if(outsideCounter == (2*__R+1)*(2*__R+1))
return RET_ERROR;
// New (Circular) Mean angle (weighted by G)
float sumWeight = 0;
float foffX = 0;
float foffY = 0;
float meanAngle = 0;
for(int j=0; j(j);
float *ptRowABlock = aBlock.ptr(j);
for(int i=0; i(j,i) != 0)
{
// This sample is inside the Mean-Shift bandwidth
// Weighted mean of positons
foffX += (float)(i+1)*ptRowGBlock[i]; // This cannot be precomputed...
foffY += (float)(j+1)*ptRowGBlock[i];
// Weighted mean of angle
meanAngle += ptRowABlock[i]*ptRowGBlock[i];
sumWeight += ptRowGBlock[i];
}
}
}
foffX /= sumWeight; foffX--;
foffY /= sumWeight; foffY--;
meanAngle /= sumWeight;
//if(__verbose) printf("meanAngle = %.2f\n", meanAngle);
// Check convergence (movement with respect to the center)
if(cvRound(foffX) == offX && cvRound(foffY) == offY)
{
// Converged. Assign and return.
dpDst = DIR_POINT(dpCurr.pt, cos(meanAngle), sin(meanAngle));
setTo14Quads(dpDst);
return RET_OK;
}
else
{
// Not converged: update dpCurr and iterate
dpCurr.pt.x += cvRound(foffX) - offX;
dpCurr.pt.y += cvRound(foffY) - offY;
dpCurr.vx = cos(meanAngle);
dpCurr.vy = sin(meanAngle);
}
}
return RET_OK;
}
float LSWMS::grow(const DIR_POINT &dpOrig, cv::Point &ptDst, int dir)
{
// **********************************************
// Finds end-point ptDst starting from dpOrig
//
// Args:
// -> dpOrig - starting DIR_POINT
// <- ptDst - end-point
// -> dir - growing direction (1(+) or 2(-))
// Ret:
// error - error of line segment
//
// Called from lineSegmentGeneration
// **********************************************
cv::Point ptEnd1, ptEnd2; //auxiliar
DIR_POINT dpEnd, dpRef; // auxiliar
// Init output
ptDst = dpOrig.pt;
// Starting gradient vector and director vector
float gX, gY;
if(dir == 1)
{
gX = dpOrig.vx;
gY = dpOrig.vy;
}
else if(dir == 2)
{
gX = -dpOrig.vx;
gY = -dpOrig.vy;
}
else return RET_ERROR;
// Compute currentAngle in 1º4º
float error1 = 0;
float growAngle, auxAngle, minAngle, maxAngle, diffAngle;
//if(gX < 0) // In this case, direction must not be fliped to 1º4º, because otherwise the sense is lost for the second grow procedure...
//{
// // Move to 1º4º
// gX = -gX;
// gY = -gY;
//}
growAngle = atan2(gY, gX);
// Starting point and angle - Bresenham
cv::Point pt1 = dpOrig.pt;
cv::Point pt2(pt1.x + (int)(1000*(-gY)), pt1.y + (int)(1000*(gX)));
cv::clipLine(__imPadSize, pt1, pt2);
// Loop - Bresenham
int k1=0;
int x1 = pt1.x, x2 = pt2.x, y1 = pt1.y, y2 = pt2.y;
int dx = ABS(x2-x1);
int dy = ABS(y2-y1);
int sx, sy, err, e2;
if(__verbose) { printf("From (%d,%d) to (%d,%d)...", x1, y1, x2, y2); fflush(stdout); }
if(x1 < x2) sx = 1; else sx = -1;
if(y1 < y2) sy = 1; else sy = -1;
err = dx-dy;
int maxNumZeroPixels = 2*__R, countZeroPixels=0;
while(true)
{
// Current value is (x1,y1)
//if(__verbose) { printf("\n\tBresenham(%d,%d)", x1, y1); fflush(stdout); }
// -------------------------------
// Do...
// Check if angle has been computed
if(__A.at(y1,x1) != NOT_A_VALID_ANGLE)
auxAngle = __A.at(y1,x1);
else
{
auxAngle = atan2((float)__Gy.at(y1,x1), (float)__Gx.at(y1,x1));
__A.at(y1,x1) = auxAngle;
}
// Check early-termination of Bresenham
if(__G.at(y1,x1) == 0)
{
//if(__verbose) printf("Zero-pixel num. %d\n", countZeroPixels);
countZeroPixels++;
if(countZeroPixels >= maxNumZeroPixels)
break; // No gradient point
}
// Check angular limits
if(growAngle - __margin < -PI_2) // e.g. currentAngle = -80º, margin = 20º
{
minAngle = growAngle - __margin + (float)CV_PI; // e.g. -80 -20 +180 = 80º
maxAngle = growAngle + __margin; // e.g. -80 +20 =-60º
if( auxAngle < 0)
{
if( auxAngle > maxAngle ) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) > maxAngle(%.2f) && auxAngle < 0\n", auxAngle, maxAngle);
diffAngle = ABS(growAngle - auxAngle);
}
else // auxAngle > 0
{
if( auxAngle < minAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) < minAngle(%.2f) && auxAngle > 0\n", auxAngle, minAngle);
diffAngle = ABS(growAngle - (auxAngle - (float)CV_PI));
}
}
else if(growAngle + __margin > PI_2) // e.g. currentAngle = 80º, margin = 20º
{
minAngle = growAngle - __margin; // e.g. 80 -20 = 60º
maxAngle = growAngle + __margin - (float)CV_PI; // e.g. 80 +20 -180 = -80º
if( auxAngle > 0 )
{
if( auxAngle < minAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) < minAngle(%.2f) && auxAngle > 0\n", auxAngle, minAngle);
diffAngle = ABS(growAngle - auxAngle);
}
else // auxAngle < 0
{
if( auxAngle > maxAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) > maxAngle(%.2f) && auxAngle < 0\n", auxAngle, maxAngle);
diffAngle = ABS(growAngle - (auxAngle + (float)CV_PI));
}
}
else
{
minAngle = growAngle - __margin;
maxAngle = growAngle + __margin;
if(auxAngle < minAngle || auxAngle > maxAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) < minAngle(%.2f) || > maxAngle(%.2f)\n", auxAngle, minAngle, maxAngle);
diffAngle = ABS(growAngle - auxAngle);
}
// If arrived here, the point is valid (inside the angular limits, and with G!=0)
//error1 += ABS(ABS(__Gx.at(y1,x1)) - ABS(gX)) +
// ABS(ABS(__Gy.at(y1,x1)) - ABS(gY));
//error1 += ABS(auxAngle - growAngle); // OJO, SI HA HABIDO DISCONTINUIDAD, ESTO NO ES CORRECTO...
error1 += diffAngle;
ptEnd1 = cv::Point(x1,y1);
k1++;
// -------------------------------
// Check end
if (x1 == x2 && y1 == y2) break;
// Update position for next iteration
e2 = 2*err;
if(e2 > -dy) { err = err - dy; x1 = x1 + sx;}
if(e2 < dx) { err = err + dx; y1 = y1 + sy;}
}
// "k1": how many points have been visited
// "ptEnd": last valid point
if( k1==0 ) // this means that even the closest point has not been accepted
{
ptEnd1 = dpOrig.pt;
error1 = (float)CV_PI;
}
else error1 /= k1;
if(__verbose) { printf(", Arrived to (%d,%d), error=%.2f", ptEnd1.x, ptEnd1.y, error1); fflush(stdout); }
// Set ptDst
ptDst = ptEnd1;
// Apply Mean-Shift to refine the end point
//if(__verbose) printf("Check grow movement: From (%d,%d) to (%d,%d)\n", dpOrig.pt.x, dpOrig.pt.y, ptEnd1.x, ptEnd1.y);
if(__verbose) { printf(", Dist = (%d,%d)\n", ABS(ptEnd1.x - dpOrig.pt.x), ABS(ptEnd1.y - dpOrig.pt.y)); }
if(ABS(ptEnd1.x - dpOrig.pt.x) > __R || ABS(ptEnd1.y - dpOrig.pt.y) > __R) // ONLY IF THERE HAS BEEN (SIGNIFICANT) MOTION FROM PREVIOUS MEAN-SHIFT MAXIMA
{
int counter = 0;
while(true)
{
if(__verbose) { printf("\tMean-Shift(Ext): from (%d,%d,%.2f,%.2f) to...", ptEnd1.x, ptEnd1.y, gX, gY); fflush(stdout); }
counter++;
// Mean-Shift on the initial extremum
// -------------------------------------------------------------
dpEnd.pt = ptEnd1; dpEnd.vx = gX; dpEnd.vy = gY; // gX and gY have been update in the last iter
dpRef.pt = ptEnd1; dpRef.vx = gX; dpRef.vy = gY;
int retMSExt = weightedMeanShift(dpEnd, dpRef);
if(__verbose) { printf("(%d,%d,%.2f,%.2f)\n", dpRef.pt.x, dpRef.pt.y, dpRef.vx, dpRef.vy); }
if(retMSExt == RET_ERROR)
{
// The refinement gave and incorrect value, keep last Bresenham value
ptDst = ptEnd1;
return RET_OK;
}
// Check motion caused by Mean-Shift
if(dpRef.pt.x == dpEnd.pt.x && dpRef.pt.y == dpEnd.pt.y)
{
ptDst = dpRef.pt;
return RET_OK;
}
// Check displacement from dpOrig
gX = (float)(dpRef.pt.y - dpOrig.pt.y); // float dX = dpRef.x - dpOrig.x; and gX = dY;
gY = (float)(dpOrig.pt.x - dpRef.pt.x); // float dY = dpRef.y - dpOrig.y; and gY = -dX;
if(gX == 0 && gY == 0)
{
ptDst = dpRef.pt;
return RET_OK;
}
float norm = sqrt(gX*gX + gY*gY);
gX /= norm;
gY /= norm;
// New Bresenham procedure
if(gX < 0)
{
// MOve to 1º4º
gX = -gX;
gY = -gY;
}
growAngle = atan2(gY, gX);
int k2=0;
float error2 = 0;
pt2.x = pt1.x + (int)(1000*(-gY)); pt2.y = pt1.y + (int)(1000*(gX));
x1 = pt1.x; x2 = pt2.x; y1 = pt1.y; y2 = pt2.y;
dx = ABS(x2-x1);
dy = ABS(y2-y1);
if(x1 < x2) sx = 1; else sx = -1;
if(y1 < y2) sy = 1; else sy = -1,
err = dx-dy;
if(__verbose) { printf("\tRefined GROW: From (%d,%d) to (%d,%d)...", x1, y1, x2, y2); fflush(stdout); }
while(true)
{
// Current value is (x1,y1)
//if(__verbose) { printf("\n\tBresenham(%d,%d)", x1, y1); fflush(stdout); }
// -------------------------------
// Do...
// Check if angle has been computed
if(__A.at(y1,x1) != NOT_A_VALID_ANGLE)
auxAngle = __A.at(y1,x1);
else
{
auxAngle = atan2((float)__Gy.at(y1,x1), (float)__Gx.at(y1,x1));
__A.at(y1,x1) = auxAngle;
}
// Check early-termination of Bresenham
if(__G.at(y1,x1) == 0)
{
//if(__verbose) printf("Zero-pixel num. %d\n", countZeroPixels);
countZeroPixels++;
if(countZeroPixels >= maxNumZeroPixels)
break; // No gradient point
}
// Check angular limits
if(growAngle - __margin < -PI_2) // e.g. currentAngle = -80º, margin = 20º
{
minAngle = growAngle - __margin + (float)CV_PI; // e.g. -80 -20 +180 = 80º
maxAngle = growAngle + __margin; // e.g. -80 +20 =-60º
if( auxAngle < 0)
{
if( auxAngle > maxAngle ) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) > maxAngle(%.2f) && auxAngle < 0\n", auxAngle, maxAngle);
diffAngle = ABS(growAngle - auxAngle);
}
else // auxAngle > 0
{
if( auxAngle < minAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) < minAngle(%.2f) && auxAngle > 0\n", auxAngle, minAngle);
diffAngle = ABS(growAngle - (auxAngle - (float)CV_PI));
}
}
else if(growAngle + __margin > PI_2) // e.g. currentAngle = 80º, margin = 20º
{
minAngle = growAngle - __margin; // e.g. 80 -20 = 60º
maxAngle = growAngle + __margin - (float)CV_PI; // e.g. 80 +20 -180 = -80º
if( auxAngle > 0 )
{
if( auxAngle < minAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) < minAngle(%.2f) && auxAngle > 0\n", auxAngle, minAngle);
diffAngle = ABS(growAngle - auxAngle);
}
else // auxAngle < 0
{
if( auxAngle > maxAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) > maxAngle(%.2f) && auxAngle < 0\n", auxAngle, maxAngle);
diffAngle = ABS(growAngle - (auxAngle + (float)CV_PI));
}
}
else
{
minAngle = growAngle - __margin;
maxAngle = growAngle + __margin;
if(auxAngle < minAngle || auxAngle > maxAngle) break; //if(__verbose) printf("Early-termination: auxAngle(%.2f) < minAngle(%.2f) || > maxAngle(%.2f)\n", auxAngle, minAngle, maxAngle);
diffAngle = ABS(growAngle - auxAngle);
}
error2 += diffAngle;
ptEnd2 = cv::Point(x1,y1);
k2++;
// -------------------------------
// Check end
if (x1 == x2 && y1 == y2) break;
// Update position for next iteration
e2 = 2*err;
if(e2 > -dy) { err = err - dy; x1 = x1 + sx;}
if(e2 < dx) { err = err + dx; y1 = y1 + sy;}
} // Bresenham while
// "k2": how many points have been visited
// "ptEnd2": last valid point
if( k2==0 ) // this means that even the closest point has not been accepted
{
ptEnd2 = dpOrig.pt;
error2 = (float)CV_PI;
}
else error2 = error2 / k2;
fflush(stdout); // Don't really know why, but this is necessary to avoid dead loops...
if(__verbose) { printf(", Arrived to (%d,%d), error=%.2f", ptEnd2.x, ptEnd2.y, error2); fflush(stdout); }
if(__verbose) { printf(", Dist = (%d,%d)\n", ABS(ptEnd2.x - dpOrig.pt.x), ABS(ptEnd1.y - dpOrig.pt.y)); }
// Compare obtained samples
if(error1 <= error2)
{
ptDst = ptEnd1;
return error1;
}
else
{
// Update ptEnd1 with ptEnd2 because it is better and iterate
ptEnd1 = ptEnd2;
k1 = k2;
error1 = error2;
}
} // Mean-Shift while
}
//else if(__verbose)
// printf("Not enough movement\n");
//return RET_OK;
return error1;
}
void LSWMS::setPaddingToZero(cv::Mat &img, int NN)
{
cv::rectangle(img, cv::Point(0,0), cv::Point(img.cols-1, NN-1), cv::Scalar(0), CV_FILLED);
cv::rectangle(img, cv::Point(0,0), cv::Point(NN-1, img.rows-1), cv::Scalar(0), CV_FILLED);
cv::rectangle(img, cv::Point(0,img.rows-NN), cv::Point(img.cols-1, img.rows-1), cv::Scalar(0), CV_FILLED);
cv::rectangle(img, cv::Point(img.cols-NN,0), cv::Point(img.cols-1, img.rows-1), cv::Scalar(0), CV_FILLED);
}
void LSWMS::drawLSegs(cv::Mat &img, std::vector &lSegs, cv::Scalar color, int thickness)
{
for(unsigned int i=0; i &lSegs, std::vector &errors, int thickness)
{
std::vector colors;
colors.push_back(CV_RGB(255,0,0));
colors.push_back(CV_RGB(200,0,0));
colors.push_back(CV_RGB(150,0,0));
colors.push_back(CV_RGB(50,0,0));
for(unsigned int i=0; i LSEG;
typedef struct _DIR_POINT
{
cv::Point pt;
float vx;
float vy;
_DIR_POINT(cv::Point _pt, float _vx, float _vy)
{
pt = _pt;
vx = _vx;
vy = _vy;
};
_DIR_POINT()
{
pt = cv::Point(0,0);
vx = 0;
vy = 0;
}
}DIR_POINT;
class LSWMS
{
private:
//Verbose
bool __verbose;
// Sizes
cv::Size __imSize, __imPadSize;
int __imWidth, __imHeight;
// Window parameters
int __R, __N;
// Line Segments
LSEG __lSeg;
int __numMaxLSegs;
// Images and maps
cv::Mat __img, __imgPad;
cv::Rect __roiRect;
cv::Mat __imgPadROI;
cv::Mat __G, __Gx, __Gy; // Map of probability of line segments
cv::Mat __M; // Map of visited pixels
cv::Mat __A; // Map of angles
cv::Mat __imAux; // For debugging
// Thresholds and variables
int __meanG;
std::vector __sampleIterator;
float __margin;
// Mean-Shift central
std::vector __seeds;
// Functions
void setPaddingToZero(cv::Mat &img, int NN);
void updateMask(cv::Point pt1, cv::Point pt2);
// SOBEL-specific functions
int computeGradientMaps(const cv::Mat &img, cv::Mat &G, cv::Mat &Gx, cv::Mat &Gy);
// General functions
int findLineSegments(const cv::Mat &G, const cv::Mat &Gx, const cv::Mat &Gy, cv::Mat &A, cv::Mat &M, std::vector &lSegs, std::vector &errors);
int lineSegmentGeneration(const DIR_POINT &dpOrig, LSEG &lSeg, float &error);
// Growing and Mean-Shift
float grow(const DIR_POINT &dpOrig, cv::Point &ptDst, int dir);
int weightedMeanShift(const DIR_POINT &dpOrig, DIR_POINT &dpDst, const cv::Mat &M=cv::Mat());
public:
LSWMS(const cv::Size imSize, const int R, const int numMaxLSegs=0, bool verbose=false);
int run(const cv::Mat &img, std::vector &lSegs, std::vector &errors);
void drawLSegs(cv::Mat &img, std::vector &lSegs, cv::Scalar color=CV_RGB(255,0,0), int thickness=1);
void drawLSegs(cv::Mat &img, std::vector &lSegs, std::vector &errors, int thickness=1);
};
#endif // __LSWMS_H__
================================================
FILE: thirdparty/LSWMS/main.cpp
================================================
/*
* Project: lineSegments (LSWMS Line Segment using Weighted Mean-Shift)
*
* File: main.cpp
*
* Contents: Creation, initialisation and usage of LSWMS object
* for the detection of line segments in images or videos
*
* Author: Marcos Nieto
*
* Homepage: www.marcosnieto.net
*/
#ifdef WIN32
#include
#include
#endif
#ifdef linux
#include
#include
#include
#endif
#include
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "LSWMS.h"
using namespace std;
using namespace cv;
// Timing
#ifdef WIN32
double t1, t2;
#else
int t1, t2;
struct timeval ts;
#endif
double t;
void help()
{
cout << "/*\n"
<< " **************************************************************************************************\n"
<< " * Line segment detection using WMS \n"
<< " * ----------------------------------------------------\n"
<< " * \n"
<< " * Author:Marcos Nieto\n"
<< " * www.marcosnieto.net\n"
<< " * marcos.nieto.doncel@gmail.com\n"
<< " * \n"
<< " * Date:01/04/2012\n"
<< " **************************************************************************************************\n"
<< " * \n"
<< " * Usage: \n"
<< " * -video # Specifies video file as input (if not specified, camera is used) \n"
<< " * -image # Specifies image file as input (if not specified, camera is used) \n"
<< " * -verbose # Actives verbose: ON, OFF (default)\n"
<< " * -play # ON: the video runs until the end; OFF: frame by frame (key press event)\n"
<< " * -resizedWidth # Specifies the desired width of the image (the height is computed to keep aspect ratio)\n"
<< " * -numMaxLSegs # Specifies the maximum number of line segments to detected.\n"
<< " *\n"
<< " * -hough # ON: Applies OpenCV HoughLinesP for comparison (PPHT)\n"
<< " *\n"
<< " * Example:\n"
<< " * lineSegment -video myVideo.avi -verbose ON -play OFF\n"
<< " * lineSegment -image myImage.jpg -resizedWidth 300 -numMaxLSegs 100\n"
<< " * lineSegment -image myImage.jpg -hough ON\n"
<< " * \n"
<< " * Keys:\n"
<< " * Esc: Quit\n"
<< " */\n" << endl;
}
void processPPHT(cv::Mat &img, std::vector &lSegs)
{
cv::Mat imgGRAY;
cv::cvtColor(img, imgGRAY, CV_RGB2GRAY);
cv::Mat dst;
cv::Canny(imgGRAY, dst, 20, 80, 3);
std::vector lines;
cv::HoughLinesP(dst, lines, 1, CV_PI/180, 80, 30, 10);
LSEG lSeg;
cv::Point p1, p2;
lSegs.clear();
for(size_t i=0; i &lSegs, cv::Scalar color)
{
for(size_t i=0; i lSegs, lSegsPPHT;
std::vector errors;
// Start showing help
help();
// Parse arguments
if(argc < 2)
return -1;
for(int i=1; i> inputImg;
}
else
{
printf("-------------------------\n");
}
if( inputImg.empty() )
break;
// Resize to processing size
cv::resize(inputImg, inputImg, procSize);
// Color Conversion
if(inputImg.channels() == 3)
{
cv::cvtColor(inputImg, imgGRAY, CV_BGR2GRAY);
inputImg.copyTo(outputImg);
if(usePPHT)
inputImg.copyTo(outputImgPPHT);
}
else
{
inputImg.copyTo(imgGRAY);
cv::cvtColor(inputImg, outputImg, CV_GRAY2BGR);
if(usePPHT)
cv::cvtColor(inputImg, outputImgPPHT, CV_GRAY2BGR);
}
// ++++++++++++++++++++++++++++++++++++++++
// Process LSWMS
#ifdef WIN32
t1 = ::GetTickCount();
#else
gettimeofday(&ts,0);
t1 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));
#endif
lswms.run(inputImg, lSegs, errors);
#ifdef WIN32
t2 = ::GetTickCount();
#else
gettimeofday(&ts,0);
t2 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));
#endif
// process time = t2 - t1
t = (double)t2-(double)t1;
cv::Scalar mean, stddev;
cv::meanStdDev(errors, mean, stddev);
if(!stillImage)
printf("Fr.#%d - LSWMS: %d lines / %.0f ms , Ang.Error: (Mean, Std)=(%.2f, %.2f)(deg)\n", frameNum, lSegs.size(), t, mean.val[0]*180/CV_PI, stddev.val[0]*180/CV_PI);
else
printf("LSWMS: %d segments\nAngular Error: Mean = %.2f (deg), Std = %.2f (deg)\nProcess Time = %.0f (ms)\n", lSegs.size(), mean.val[0]*180/CV_PI, stddev.val[0]*180/CV_PI, t);
//lswms.drawLSegs(outputImg, lSegs,CV_RGB(255,0,0), 2); // drawing all line segments the same
lswms.drawLSegs(outputImg, lSegs, errors); // drawing according to errors
// ++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++
if(usePPHT)
{
// Process PPHT
#ifdef WIN32
t1 = ::GetTickCount();
#else
gettimeofday(&ts,0);
t1 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));
#endif
processPPHT(inputImg, lSegsPPHT);
#ifdef WIN32
t2 = ::GetTickCount();
#else
gettimeofday(&ts,0);
t2 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));
#endif
// process time = t2 - t1
t = (double)t2-(double)t1;
drawPPHT(outputImgPPHT, lSegsPPHT, CV_RGB(0,0,255));
if(!stillImage)
printf("Fr.#%d - PPHT: %d lines / %.0f ms\n", frameNum, lSegsPPHT.size(), t);
else
printf("\nPPHT: %d segments\nProcess Time = %.0f (ms)\n", lSegsPPHT.size(), t);
// ++++++++++++++++++++++++++++++++++++++++
}
// View
imshow("LSWMS", outputImg);
if(usePPHT)
imshow("PPHT", outputImgPPHT);
if(stillImage)
{
cv::imwrite("lswms.bmp", outputImg);
if(usePPHT)
cv::imwrite("ppht.bmp", outputImgPPHT);
}
if(playMode)
cv::waitKey(1);
else
cv::waitKey(0);
char q = (char)waitKey(1);
if( q == 27 )
{
printf("\nStopped by user request\n");
break;
}
if(stillImage)
break;
} // main while
if(!stillImage)
video.release();
return 0;
}
================================================
FILE: thirdparty/MCMLSD/MCMLSD/Readme.txt
================================================
many function in the line segmentation algorithm were mex file, if you are using
windows or linux system, please recompile following c code into mex file.
mexRemoveVotes_v3_scale.c
mexVoteEdges_v3_scale.c
the c code can be compiled by command
mex xxxxxxxxx.c
Please use matlab coder to compile following .m file into mex file:
hmmParameters.m
returnLines.m
sampleLine.m
the matlab coder is an app in matlab to automatically compile .m code into mex file,
The entry point for the code is demo_start_v2.m. The instruction is in following
select No for "Does this code use global variables?"
The input variable type should be configurated in following
sampleLine.m
rho double(1x1)
theta double(1x1)
m double(1x1)
n double(1x1)
pp double(1x2)
returnLines.m
points_proj double(:infx2)
final_labels double(1x:inf)
hmmParameters.m
exist_edge_on double(1x40)
exist_edge_off double(1x40)
ang_dev_on double(1x181)
ang_dev_off double(1x181)
node_idx double(:infx1)
Psi_t double(2x2)
pi_var double(2x1)
y_h double(:infx1)
distances double(:infx1)
range_dis double(1x40)
nexist_edge_on double(1x40)
nexist_edge_off double(1x40)
prob_on double(1x1)
prob_off double(1x1)
prob_stay_on double(1x1)
prob_leave_off double(1x1)
prob_leave_on double(1x1)
prob_stay_off double(1x1)
ang_dev double(:infx1)
range_ang double(1x181)
4. Select MEX as output format also remove the _mex ending in the output file name section
for example if you are compiling sampleLine.m, output the file name as sampleLine instead of sampleLine_mex
the line segment detector has two steps
1. compute the kernel for the image size
you only need to compute the kernal once for one an image size
[kernels, kernels_flip, kernel_params] =kernelInitialization(img);
the kernels, kernels_flip, kernel_params are the kernel parameter for the line segment algorithm below
2. line segment detection
[lines, fullLines] =lineSegmentation_HighRes(img,kernels, kernels_flip, kernel_params);
the lines variable contains the detected line segmentations it arranged as
[x1 y1 x2 y2 probability]
The fullLines are the detected lines. It is arranged as [rho theta probability]
please run the demo_start_v2.m for demonstration
the code in Evaluation Code.zip are line evaluation algorithms
run_create_contours_MCMLSD.m is the algorithm to get line segment statistics for
MCMLSD, you can modify it to process other line segment detection algorithms
run_eval_PR_4Methods.m and run_eval_PR.m process the statistical information from
the run_create_contours_MCMLSD.m result and perform evaluations
run_plot_PR.m and run_plot_PR4Methods.m plot the results in a graph
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/checkInParam.m
================================================
function [out] = checkInput(img1,g1_mag1,g1_dir1,g1_sc1,g2_mag1,g2_sc1,g2_all1,noise1,...
EDGE_W1,subpixelflag1,maxscale1)
load('input.mat');
out = 0;
if ~isequal(img1, img)
display('error img');
out = out + 1;
end
if ~isequal(g1_mag1, g1_mag)
display('error g1_mag');
out = out + 1;
end
if ~isequal(g1_dir1, g1_dir)
display('error g1_dir');
out = out + 1;
end
if ~isequal(g1_sc1, g1_sc)
display('error g1_sc');
out = out + 1;
end
if ~isequal(g2_mag1, g2_mag)
display('error g2_mag');
out = out + 1;
end
if ~isequal(g2_sc1, g2_sc)
display('error g2_sc');
out = out + 1;
end
if ~isequal(g2_all1, g2_all)
display('error g2_all');
out = out + 1;
end
if noise1 ~= noise
display('error noise');
out = out + 1;
end
if EDGE_W1 ~= edgew
display('error EDGE_W');
out = out + 1;
end
if subpixelflag1 ~= subpixelflag
display('error subpixelflag');
out = out + 1;
end
if maxscale1 ~= scale
display('error scale');
out = out + 1;
end
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/checkOutParam.m
================================================
function [out] = checkOutParam(edge_map1,blur_map1,dark_map1,light_map1,...
xzero1_map1,yzero1_map1,xzero2_map1,yzero2_map1)
load('output.mat');
out = 0;
if ~isequal(edge_map1, edge_map)
display('error edge_map');
out = out + 1;
end
if ~isequal(blur_map1, blur_map)
display('error blur_map');
out = out + 1;
end
if ~isequal(dark_map1, dark_map)
display('error dark_map');
out = out + 1;
end
if ~isequal(light_map1, light_map)
display('error light_map');
out = out + 1;
end
if ~isequal(xzero1_map1, xzero1_map)
display('error xzero1_map');
out = out + 1;
end
if ~isequal(yzero1_map1, yzero1_map)
display('error yzero1_map');
out = out + 1;
end
if ~isequal(xzero2_map1, xzero2_map)
display('error xzero2_map');
out = out + 1;
end
if ~isequal(yzero2_map1, yzero2_map)
display('error yzero2_map');
out = out + 1;
end
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/check_save_requirements.m
================================================
function[sfilenames] = check_save_requirements(save_flags,outputdir,etype);
name1 = ['edge',etype];
sfilenames = {name1,'blur.mat','dark.mat','light.mat',...
'g1mag.mat','g1dir.mat','g1scale.mat','g2mag.mat','g2scale.mat',...
'xzero1.mat','yzero1.mat','xzero2.mat','yzero2.mat'};
if any(save_flags)
% Output directory does not exist:
if ((exist(outputdir) ~= 7) & (length(outputdir) ~= 0))
mkdir(outputdir); % Create this directory
mess1 = 'Requested output directory has been created.';
uiwait(msgbox(mess1,'Output Directory Creation','none','modal'));
end;
if (length(outputdir) == 0)
outputdir = 'tmp_output';
opmessage = ['Output directory name not specified. ',...
'Files will be saved in directory "tmp_output".'];
uiwait(msgbox(opmessage,'Output Directory Warning','warn','modal'));
if (exist(outputdir) ~= 7)
mkdir(outputdir);
end;
end;
filestosave = sfilenames(find(save_flags));
x = dir(outputdir);
xx = {x.name}';
commonfiles = intersect(filestosave,xx);
if ~isempty(commonfiles)
maxnumx = 0;
for k = 1:1:length(xx)
y = xx{k};
numx = 0;
if (length(y)>4)
if (y(end-4)=='x')
numx = sum(y=='x');
end;
end;
if (numx > maxnumx)
maxnumx = numx;
end;
end;
assignin('base','maxnumx',maxnumx+1);
assignin('base','commonfiles',commonfiles);
% uiwait(outputdir_erase_info); % Call GUI
%
% changename_flag = evalin('base','changename_flag;');
%
% if changename_flag
% repx = repmat('x',1,maxnumx+1);
% for k = 1:1:length(sfilenames)
% s = sfilenames{k};
% s = [s(1:end-4),repx,s(end-3:end)];
% sfilenames{k} = s;
% end;
% end;
evalin('base','clear changename_flag maxnumx commonfiles;');
end;
end;
for k = 1:1:length(sfilenames)
s = sfilenames{k};
s = [outputdir,'/',s];
sfilenames{k} = s;
end;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/convolve_2.m
================================================
%##############################################################
%
% [cimg] = convolve_2(mimg,filt,bc)
%
% Convolution of two matrices with the boundaries handled
% depending on the value of the boundary condition variable.
% - 0 for extension convolution
% - 1 for no overlap convolution
%
%##############################################################
function[cimg] = convolve_2(mimg,filt,bc)
if (bc == 0)
pad_img = pad_matrix(mimg,size(filt));
cimg = conv2(pad_img,filt,'same');
cimg = trim_matrix(cimg,size(filt));
else
cimg = conv2(mimg,filt,'same');
if (size(filt,1) < size(filt,2))
k = floor(0.5*length(filt));
cimg(:,1:k) = 0;
cimg(:,end-k+1:end) = 0;
else
k = floor(0.5*length(filt));
cimg(1:k,:) = 0;
cimg(end-k+1:end,:) = 0;
end;
end;
return;
%########################
% Function to pad matrix:
%########################
function[r] = pad_matrix(m,d)
if (d(1) < d(2))
k = floor(0.5*d(2));
ad1 = repmat(m(:,1),1,k);
ad2 = repmat(m(:,end),1,k);
r = [ad1 m ad2];
else
k = floor(0.5*d(1));
ad1 = repmat(m(1,:),k,1);
ad2 = repmat(m(end,:),k,1);
r = [ad1; m; ad2];
end;
return;
%#########################
% Function to trim matrix:
%#########################
function[r] = trim_matrix(m,d)
if (d(1) < d(2))
k = floor(0.5*d(2));
r = m(:,k+1:end-k);
else
k = floor(0.5*d(1));
r = m(k+1:end-k,:);
end;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/d2gauss.m
================================================
%################################################################
%
% d2gauss.m returns a 2-d Gaussian filter with kernal attributes:
% size: n1*n2
% theta: CCW-angle tkernat filter rotated
% sigma1: standard deviation of 1st gaussian
% sigma2: standard deviation of 2nd gaussian
%
%################################################################
function[kern] = d2gauss(n1,std1,n2,std2,theta,max1);
[I,J] = meshgrid(1:1:n2,1:1:n1);
It = I - (n2+1)/2;
Jt = J - (n1+1)/2;
u1 = cos(theta)*Jt' - sin(theta)*It';
u2 = sin(theta)*Jt' + cos(theta)*It';
kern = gauss(u1,std1) .* gauss(u2,std2);
%############################################
% Normalise the kernal and confine to limits:
%############################################
kern = kern / sqrt(sum(sum(kern.*kern)));
max2 = max(max(kern));
kern = kern / (max2/max1);
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/derivative2nd.m
================================================
%############################################################
%
% derivative2nd(g1dir,maxscale,noise,gauss_a,conv_type,fpath)
%
%############################################################
function[g2mag,g2sc,g2all] = derivative2nd(g1dir,maxscale,noise,...
gauss_a,conv_type);
fm2 = '.ascii';
%############
% Initialise:
%############
g2mag = 0;
g2sc = 0;
nrows = size(g1dir,1);
g2all = zeros(maxscale*nrows,size(g1dir,2));
for scale = 1:1:maxscale
%#############################
% Set scale value for filters:
%#############################
if (scale == 1)
g2scaleval = '05';
else
g2scaleval = '1';
end;
mimg = gauss_a(:,:,scale);
%kern1 = load(strcat(fpath,filesep,'gx',g2scaleval,fm2));
kern1 = load(strcat('gx',g2scaleval,fm2));
rc1 = convolve_2(mimg,kern1,conv_type);
%kern2 = load(strcat(fpath,filesep,'g2y',g2scaleval,fm2));
kern2 = load(strcat('g2y',g2scaleval,fm2));
rc2 = convolve_2(rc1,kern2,conv_type);
%kern3 = load(strcat(fpath,filesep,'gy',g2scaleval,fm2));
kern3 = load(strcat('gy',g2scaleval,fm2));
rc3 = convolve_2(mimg,kern3,conv_type);
%kern4 = load(strcat(fpath,filesep,'g2x',g2scaleval,fm2));
kern4 = load(strcat('g2x',g2scaleval,fm2));
rc4 = convolve_2(rc3,kern4,conv_type);
%kern5 = load(strcat(fpath,filesep,'g1x',g2scaleval,fm2));
kern5 = load(strcat('g1x',g2scaleval,fm2));
rc5 = convolve_2(mimg,kern5,conv_type);
%kern6 = load(strcat(fpath,filesep,'g1y',g2scaleval,fm2));
kern6 = load(strcat('g1y',g2scaleval,fm2));
rc6 = convolve_2(rc5,kern6,conv_type);
%#######################################
% Calculate the 2nd Gaussian derivative:
%#######################################
g2 = g2steer(rc4,rc2,rc6,g1dir);
g2all((scale-1)*nrows+1:scale*nrows,:) = g2; % For subpixel loc'n.
%##############################################################
% Augment multi-scale Gaussian directional 2nd derivative maps:
%##############################################################
[g2mag,g2sc] = g2scale(g2mag,g2,g2sc,scale,noise,0);
end;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/elderEdge.m
================================================
function [edgeStruct] = elderEdge()
%Run local scale control edge detector on image (Elder & Zucker, 1998)
noise = 1; %estimated SD of pixel noise
imagefile='P1020177.jpg'; %name of image file. Most formats are handled. May be RGB or grayscale.
maxscale=5; %maximum scale, must be positive integer. Scales increase exponentially from 0.5 to 2^{maxscale-2}.
edgew=10; %maximum edge blur (distance in pixels between extrema in the 2nd derivative)
conss=1; %boundary condition for scalespace computation. 0 for extension, 1 for no overlap
congrd=0; %boundary condition for derivative computation. 0 for extension, 1 for no overlap
subpixelflag=0; %generate subpixel-localized edges
%run edge detection
edgeStruct = main_edge(imagefile,maxscale,noise,edgew,conss,congrd,subpixelflag);
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/elderEdge.m~
================================================
function elderEdge()
%Run local scale control edge detector on image (Elder & Zucker, 1998)
noise = 1; %estimated SD of pixel noise
imagefile='img.jpg'; %name of image file
maxscale=5; %maximum scale, must be positive integer. Scales increase exponentially from 0.5 to 2^{maxscale-2}.
edgew=10; %maximum edge blur (distance in pixels between extrema in the 2nd derivative)
conss=1; %boundary condition for scalespace computation. 0 for extension, 1 for no overlap
congrd=0; %boundary condition for derivative computation. 0 for extension, 1 for no overlap
save_v = zeros(0,11); %results to save
view_v = zeros(0,11); %results to view
save_v(1) = 1; %edge map
save_v(2) = 1; %blur map
save_v(3) = 1; %dark map
save_v(4) = 1; %light map
save_v(5) = 0; %gradient magnitude map
save_v(6) = 1; %gradient direction map
save_v(7) = 0; %gradient scale map
save_v(8) = 0; %2nd derivative map
save_v(9) = 0; %2nd derivativ scale map
save_v(10) = 0; %X zero crossing offset
save_v(11) = 0;%Y zero crossing offset
view_v(1) = 0; %edge map
view_v(2) = 0; %blur map
view_v(3) = 0; %dark map
view_v(4) = 0; %light map
view_v(5) = 0; %gradient magnitude map
view_v(6) = 0; %gradient direction map
view_v(7) = 0; %gradient scale map
view_v(8) = 0; %2nd derivative map
view_v(9) = 0; %2nd derivativ scale map
view_v(10) = 0; %X zero crossing offset
view_v(11) = 0;%Y zero crossing offset
%change this path to your own local directory for the filters
filtpath='~/Dropbox/code/elderlab/edge/mar03/filters';
edgetype='.jpg'; %format for output edge map (.mat also allowed)
edgelplotflag=0; %show edges on image
imgplotflag=0; %view original image
outputdir=pwd;
sfilenames = check_save_requirements(view_v,outputdir,edgetype);
subpixelflag=0;
outputdata=main_edge(imagefile,maxscale,noise,edgew,...
conss,congrd,view_v,view_v,filtpath,...
edgetype,edgelplotflag,imgplotflag,sfilenames,...
subpixelflag);
reblur=uint8(imreconandblur(1,3));
img=imread(imagefile);
if size(img,3)==3
img=rgb2gray(img);
end
imwrite(img,strcat(resultdir,'img.jpg'));
imwrite(outputdata.edge_map,strcat(resultdir,'edge.jpg'));
imwrite(reblur,strcat(resultdir,'recon.jpg'));
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/find_edges.c
================================================
/************************************************************************************************
*
* Routine Name: lvedge - Detect luminance edges by selecting triads of significant
* 2nd derivatives of opposite sign flanking a significant 1st derivative of consistent direction.
*
* Purpose: vgtriad
* Detect luminance edges by selecting triads of significant
* 2nd derivatives of opposite sign flanking a directional
* maximum in the 1st derivative of consistent direction.
*
* Pixels with maximal gradient magnitude in the gradient
* direction are considered as candidate edge pixels. The image
* is scanned along the gradient line for pixels with significant
* 2nd derivative of opposite sign, consistent with the edge
* hypothesis. Scanning is terminated if the line intersects a
* pixel with inconsistent or unreliable gradient direction, or
* inconsistent 2nd derivative sign if a pixel of consistent sign
* has already been detected. Otherwise, the scan selects the
* line segment connecting the edge pixel with the maximum 2nd
* derivative pixels. The local blur width of the blurred
* edge is derived as well. The width is derived from the
* distance between G2 terminators of opposite sign, based on an
* assumption of Gaussian blur.
*
* Input: img - Original byte image
* g1mag - Multi-scale gradient magnitude map
* g1dir - Multi-Scale Gradient direction map
* g1scale - Gradient scale map
* g2 - Multi-Scale 2nd Derivative Map
* g2scale - G2 Scale Map
* noise - Estimated sensor noise
* maxwidth - Maximum edge width detected
* Output: gewidth - Estimate of blur scale based on edgewidth.
* Type is VFF_TYP_FLOAT.
* dark - Estimate of luminance on dark side of edge.
* Type is VFF_TYP_1_BYTE.
* light - Estimate of luminance on light side of edge
* Returns: TRUE (1) on success, FALSE (0) on failure
* Restrictions: Restricted to real, single-band images.
* Does not work on explicit location data.
* Written By: James Elder
* Date: Oct 13, 1997
* Verified:
* Side Effects:
* Examples:
* Modifications:
****************************************************************/
#define char16_t uint16_T /*JE Sept 2014. Needed for MATLAB 2012a under OSX19.0 and Xcode 5.1 */
#include "mex.h"
#include /* NULL */
#include
#include
#define notDblMtx(it) (mxIsComplex(it))
double g2max1, /* Max G2 response of correct sign */
g2max2, /* Max G2 response of incorrect sign */
g2maxscale, /* Scale of max G2 response of correct sign */
xfirst, yfirst, zfirst, /* Endpoints for interpolation */
/* Sensor noise threshold */
k_thresh, /* Min. contrib. to linear interp. for pixel to be labeled */
pixerr[6] = {9.5,8,3.5,0.25,0.04,0.0}, /* Half-range of gradient direction */
/* uncertainty due to pixelation */
/* as function of G1 scale. */
g2thresh = 0.0000596, /* Min on sig. g2 value for interpolation */
edgewidth, /* Estimated width of edge */
xend, yend; /* Estimated gradient ray termination coordinates */
int g1len_to_max,maxg1len, /* Current and max number of G1 pixels supporting edge assertion */
g1max, /* Is current pixel directional max of gradient? */
g2len, /* Number of G2 pixels supporting edge */
rows, cols, /* Columns in image */
g1offsets[1024], /* Offsets to G1 pixels in current path */
g2offsets[4], /* Offsets to G2 pixels in current path */
edgescale = 0, /* Max scale of G1 or G2 response in path */
acuity_problem, /* Flag to indicate 1-pixel acuity will not be obtained */
zcross, /* Flag to indicate zero-crossing found */
keepoff1,keepoff2; /* Offsets to pixels from which 2nd derivative calculated */
double G2fact1, G2fact2; /* Factor in recalculation of G2val: "ki * pow(cos(ddiri),2)" */
int Xx1,Xx2,Yy1,Yy2; /* Offsets returned from g2_path for subpixel method 2 */
double KPI = 3.141593;
double noise; /* Sensor noise threshold */
FILE *f;
/* Internal functions */
int round_int(double rd);
int g2_path(double theta,int bx,int by,double *g1mag,double *g1dir,double *g1scale,
double *g2,double *g2scale,int g2sign);
int compare_dir(double g1mag1,double g1dir1,int g1scale1,double g1mag2,double g1dir2,int g1scale2);
int sign(double r);
int
find_edges(double *img,double *g1mag,double *g1dir,double *g1scale,double *g2,
double *g2scale,double *g2all,double sensor_noise,int maxwidth,int sublocflag,int maxscale,
double *xout,double *xout_blur,
double *xout_dark,double *xout_light,double *xout_x1zero,double *xout_y1zero,
double *xout_x2zero,double *xout_y2zero)
{
double theta; /* Either in gradient direction or against gradient direction */
int x,y,i; /* Image dimensions and indices */
double *g1magd,*g1dird,*g2d; /* Input G1 and G2 map data */
double *imgd;
double g2maxp,g2maxn; /* Used to detect false edge pixels */
double g2scalep,g2scalen; /* Scale of max pos and min neg G2 responses */
double blurvar;
double nxend,nyend,pxend,pyend; /* Estimated gradient line terminations */
double nwidth,pwidth; /*distance from current pixel to g2 extrema */
double erf_const,C,D,L,meanl;
double l,d;
double nxfirst, nyfirst, pxfirst, pyfirst, pzfirst, nzfirst, xinter, yinter;
int g2sc1, g2sc2, g2sc3, g2sc4, off_side1, off_side2, off_side3, off_side4;
int gind;
double G2factor1, G2factor2, G2factor3, G2factor4;
int X1,X2,X3,X4,Y1,Y2,Y3,Y4,match_flag;
int newoff1,newoff2,newoff3,newoff4;
double new_nz,new_pz,bestsnr,bestnz,bestpz;
static double g2norms[6] = {1.873,0.2443,0.0306,0.003817,0.00047715,0.0000596}; /* Thresholds */
int count=0;
g1magd = g1mag;
g1dird = g1dir;
g2d = g2;
imgd = img;
noise = sensor_noise;
g2thresh = 5.2*g2thresh*noise; /* threshold on interpolated g2 values */
maxg1len = maxwidth*2; /* Up to 2 pixels per step */
k_thresh = 0.1; /* Min coeff. in linear interp. to include pixel in edge */
erf_const = 0.68268949213709;
for (i=0;i<5;i++) /* Convert pixelation uncertainty from deg. to radians */
pixerr[i] = pixerr[i] * KPI / 180.0;
/**** Order (x,y) reversed from cantata code ****/
for (x=0;x 0.0 && *g2d >= 0.0) /* Non-negative 2nd derivative */
{
acuity_problem = 0;
g2max1 = 0;
g2max2 = 0.0;
g2maxscale = 0.0;
g1len_to_max = 0;
g2len = 0;
zcross = 0;
edgewidth = 0.0;
xfirst = 0.0;
yfirst = 0.0;
zfirst = 0.0;
keepoff1 = 0;
keepoff2 = 0;
G2fact1 = 0;
G2fact2 = 0;
/* Find g2_path, last parameter '1' makes g2_path selective for negative G2 responses */
g2_path(theta,x,y,g1magd,g1dird,g1scale+count,g2d,g2scale+count,1);
if (g2max1 != 0.0 && g1max) /* Gradient max and found significant negative G2 response */
{
nxfirst = xfirst;
nyfirst = yfirst;
nzfirst = -zfirst;
off_side1 = keepoff1;
off_side2 = keepoff2;
G2factor1 = G2fact1;
G2factor2 = G2fact2;
X1 = Xx1;
X2 = Xx2;
Y1 = Yy1;
Y2 = Yy2;
nxend = xend; /* Save estimated edge ray termination */
nyend = yend;
nwidth = sqrt(pow(nxend,2) + pow(nyend,2));
g2maxn = g2max1; /* Save min negative G2 response */
g2maxp = g2max2; /* Save max positive G2 response */
g2scalen = g2maxscale; /* Save scale of min negative G2 response */
g2max1 = 0.0;
g2max2 = 0.0;
g2maxscale = 0.0;
if (theta > 0) /* Now scan opposite to gradient direction */
theta = theta - KPI;
else
theta = theta + KPI;
xfirst = 0.0;
yfirst = 0.0;
zfirst = 0.0;
keepoff1 = 0;
keepoff2 = 0;
G2fact1 = 0;
G2fact2 = 0;
/* Find g2_path, last parameter '0' makes g2_path selective for positive G2 responses */
g2_path(theta,x,y,g1magd,g1dird,g1scale+count,g2d,g2scale+count,0);
pxend = xend; /* Save estimated edge ray termination */
pyend = yend;
pwidth = sqrt(pow(pxend,2) + pow(pyend,2));
if ((g2max1>g2maxp) && (g2max2>g2maxn) && g1max)
{
pxfirst = xfirst;
pyfirst = yfirst;
pzfirst = zfirst;
off_side3 = keepoff1;
off_side4 = keepoff2;
G2factor3 = G2fact1;
G2factor4 = G2fact2;
X3 = Xx1;
X4 = Xx2;
Y3 = Yy1;
Y4 = Yy2;
if ((!acuity_problem && zcross) || (acuity_problem && fabs(pwidth-nwidth) 0.0)
*(xout_blur+count) = sqrt(blurvar);
else
*(xout_blur+count) = 0.1;
l = *(imgd+round_int(nxend)*rows+round_int(nyend));
d = *(imgd+round_int(pxend)*rows+round_int(pyend));
if (l<=d)
{
meanl = round_int(0.5*(l+d));
*(xout_dark+count) = meanl;
*(xout_light+count) = meanl;
}
else if (blurvar < 1)
{
*(xout_dark+count) = round_int(d);
*(xout_light+count) = round_int(l);
}
else
{
C = (l - d) / erf_const;
D = d - 0.5 * C * (1 - erf_const);
L = C + D;
if (D<0)
*(xout_dark+count) = 0;
else if (D> 255)
*(xout_dark+count) = 255.0;
else
*(xout_dark+count) = round_int(D);
if (L<0)
*(xout_light+count)= 0;
else if (L> 255)
*(xout_light+count) = 255.0;
else
*(xout_light+count) = round_int(L);
}
if (sublocflag)
{
/* METHOD 1: Interpolate to find better estimate of zero-crossing.
Store resultant x and y in *xout_xzero and *xout_yzero. */
xinter = (nzfirst * (pxfirst+x+1) + pzfirst * (nxfirst+x+1)) / (nzfirst + pzfirst);
yinter = (nzfirst * (pyfirst+y+1) + pzfirst * (nyfirst+y+1)) / (nzfirst + pzfirst);
/* Update in xout_x1zero and xout_y1zero matrices */
xinter = (xinter > cols) ? cols : xinter;
*(xout_x1zero+count) = (xinter < 0) ? 0 : xinter;
yinter = (yinter > rows) ? rows : yinter;
*(xout_y1zero+count) = (yinter < 0) ? 0 : yinter;
/* METHOD 2: Interpolate as with Method 1, except that scale at the
4 flanking pixels must match. */
g2sc1 = (int)*(g2scale+count+off_side1);
g2sc2 = (int)*(g2scale+count+off_side2);
g2sc3 = (int)*(g2scale+count+off_side3);
g2sc4 = (int)*(g2scale+count+off_side4);
if ((g2sc1==g2sc2) && (g2sc1==g2sc3) && (g2sc1==g2sc4)) /* Scales match */
{
*(xout_x2zero+count) = *(xout_x1zero+count);
*(xout_y2zero+count) = *(xout_y1zero+count);
}
else /* Scales do not match */
{
match_flag = 0;
gind = 1; /* g2 scale index */
bestsnr = 0;
while (!match_flag && (gind<=maxscale))
{
newoff1 = (x+X1) * rows * maxscale + (y+Y1) + rows * (gind-1);
newoff2 = (x+X2) * rows * maxscale + (y+Y2) + rows * (gind-1);
newoff3 = (x+X3) * rows * maxscale + (y+Y3) + rows * (gind-1);
newoff4 = (x+X4) * rows * maxscale + (y+Y4) + rows * (gind-1);
new_nz = -G2factor1 * (*(g2all+newoff1)) - G2factor2 * (*(g2all+newoff2));
new_pz = G2factor3 * (*(g2all+newoff3)) + G2factor4 * (*(g2all+newoff4));
/* If there is no scale for which all 4 are significant, will need to
* choose the one with the best SNR, therefore store it. */
if ((new_nz+new_pz)>bestsnr)
{
bestsnr = new_nz + new_pz;
bestnz = new_nz;
bestpz = new_pz;
}
/* If new estimates exceed the threshold, have found answer */
if ((new_nz > 5.2*noise*g2norms[gind-1]) &&
(new_pz > 5.2*noise*g2norms[gind-1]))
{
xinter = (new_nz * (pxfirst+x+1) + new_pz * (nxfirst+x+1)) /
(new_nz + new_pz);
yinter = (new_nz * (pyfirst+y+1) + new_pz * (nyfirst+y+1)) /
(new_nz + new_pz);
match_flag = 1;
}
gind++;
}
/* Use scale with best SNR if none match */
if (!match_flag)
{
xinter = (bestnz * (pxfirst+x+1) + bestpz * (nxfirst+x+1)) / bestsnr;
yinter = (bestnz * (pyfirst+y+1) + bestpz * (nyfirst+y+1)) / bestsnr;
}
/* Update in xout_x2zero and xout_y2zero matrices */
xinter = (xinter > cols) ? cols : xinter;
*(xout_x2zero+count) = (xinter < 0) ? 0 : xinter;
yinter = (yinter > rows) ? rows : yinter;
*(xout_y2zero+count) = (yinter < 0) ? 0 : yinter;
}
}
}
}
}
}
count++;
}
return(1);
}
/****************************************************************/
int round_int(double rd)
{
double t1, t2;
int xf, xc;
xf = (int)floor(rd);
xc = (int)ceil(rd);
t1 = fabs(rd-xf);
t2 = fabs(rd-xc);
if (t1 >= t2)
return(xc);
else
return(xf);
}
/*****************************************************************/
/* g2_path */
int g2_path(double theta,int bx,int by,double *g1mag,double *g1dir,double *g1scale,
double *g2,double *g2scale,int g2signbit)
{
int x1, y1, x2, y2, dx, dy;
int off1=0;
int off2=0; /* Offsets and indices for linear interp. */
int end_of_path; /* Flag signalling when max (min) found or continuity broken */
int same_dir1, same_dir2; /* Flags indicating gradient direction compatibility */
int g1len, g2halflen; /* Num of G2 pixels terminating current ray */
double tan_theta, x, y; /* Real coords of points on path */
double g1magval, g1dirval; /* Gradient at centre pixel */
double g1mag1, g1dir1, g1mag2, g1dir2; /* Gradients at pixel pair on path */
double k1, k2; /* Linear interp. coefficients */
double g1val, g2val; /* Interpolated G1 and G2 responses on path */
int g1scaleval; /* Scale of gradient at centre pixel */
int g1scale1, g1scale2, g2scale1, g2scale2; /* Scales of pixels on path */
double ddir1, ddir2; /* gradient dir differences along grad line. */
double g1_prev_val, acuity, length;
int found_interp_flag=0;
/*#########################################*/
/* Select the 1st 2 pixels flanking the ray
from 0 in the theta direction. */
/*#########################################*/
if (theta > 0)
dy = -1;
else
dy = 1;
if (fabs(theta) < KPI*0.5)
dx = 1;
else
dx = -1;
x1 = dx;
y1 = 0;
x2 = 0;
y2 = dy;
tan_theta = tan(theta);
/* If theta < 45deg -> (x2,y2) = (dx,dy), else (x1,y1) = (dx,dy) */
if (fabs(tan_theta) < 1)
x2 = dx;
else
y1 = dy;
/* End of block */
/*##############*/
acuity = 20.0;
g2max2 = 0.0;
g1magval = (float)*g1mag;
g1dirval = (float)*g1dir;
g1scaleval = (int)*g1scale;
g2val = (float)*g2;
g1_prev_val = g1magval;
if (g2val != 0.0 && sign(g2val) == g2signbit) /* sig. G2 of right sign */
{
g2maxscale = (float) *g2scale;
g2max1 = g2val;
g2offsets[g2len++] = 0;
g2halflen = 1;
if (!found_interp_flag)
{
G2fact1 = 1;
G2fact2 = 0;
Xx1 = 0;
Xx2 = 0;
Yy1 = 0;
Yy2 = 0;
zfirst = g2max1;
xfirst = 0;
yfirst = 0;
keepoff1 = off1;
keepoff2 = off2;
found_interp_flag = 1;
}
}
else /* Initialize G2 variables */
{
g2max1 = 0.0;
g2maxscale = 0.0;
g2halflen = 0;
}
end_of_path = 0;
xend = yend = 0.0;
g1len = g1len_to_max;
g1max = 1;
/*#####################################
Scan for G2 max of the correct sign,
until length limit or interrupted
#####################################*/
while (!end_of_path && g1len < maxg1len)
{
if ((g2val==0.0) && (g2max1 == 0))
acuity_problem = 1;
/* Check if flanking pixels go out of range of image */
if ((bx+x1<0) || (bx+x1>=cols) || (bx+x2<0) || (bx+x2>=cols) || (by+y1<0) ||
(by+y1>=rows) || (by+y2<0) || (by+y2>=rows))
end_of_path = 1;
else
{
/* Derive linear interpolation coefficients */
if (x1 == x2)
{
y = -x1 * tan_theta;
k1 = fabs(y-y2);
k2 = fabs(y-y1);
}
else
{
x = -y1 / tan_theta;
k1 = fabs(x-x2);
k2 = fabs(x-x1);
}
/*########################################################*/
/* Get G1 and G2 information for the two pixels on path */
/*########################################################*/
/**** cols and rows changed from cantata code here as Matlab processes columnwise ****/
off1 = x1 * rows + y1;
off2 = x2 * rows + y2;
x = k1 * x1 + k2 * x2;
y = k1 * y1 + k2 * y2;
length = sqrt(x*x + y*y);
g1dir1 = *(g1dir+off1);
g1dir2 = *(g1dir+off2);
g1mag1 = *(g1mag+off1);
g1mag2 = *(g1mag+off2);
g1scale1 = (int)*(g1scale+off1);
g1scale2 = (int)*(g1scale+off2);
g2scale1 = (int)*(g2scale+off1);
g2scale2 = (int)*(g2scale+off2);
/* Interpolate the G1 estimate */
g1val = k1 * g1mag1 + k2 * g1mag2;
if (g1val > g1magval)
g1max = 1;
/* Pixel 1 compatible? */
same_dir1 = compare_dir(g1magval,g1dirval,(int)g1scaleval,g1mag1,g1dir1,(int)g1scale1);
/* Pixel 2 compatible? */
same_dir2 = compare_dir(g1magval,g1dirval,(int)g1scaleval,g1mag2,g1dir2,(int)g1scale2);
ddir1 = fabs(g1dir1-g1dirval);
ddir2 = fabs(g1dir2-g1dirval);
/* Interpolate the G2 estimate */
g2val = k1 * pow(cos(ddir1),2) * (*(g2+off1)) + k2 * pow(cos(ddir2),2) * (*(g2+off2));
if ((g1len==0) && (g2val<0.0))
zcross = 1;
if ((!(g2max1==0.0 && fabs(g2val)>g2thresh && sign(g2val) == g2signbit) &&
!((same_dir1 && k1>k_thresh) || (same_dir2 && k2>k_thresh)))
|| (fabs(g2val) > g2thresh && (sign(g2val) != g2signbit) && zcross)
|| ((fabs(g2val) < g2thresh) && (length > acuity)))
{
end_of_path = 1;
}
else if ((sign(g2val) == g2signbit) && (fabs(g2val) > fabs(g2max1)))
{
g2max1 = g2val;
/* Derive scale of G2 response */
if (g2scale1 == 0)
g2maxscale = (float) g2scale2;
else if (g2scale2 == 0)
g2maxscale = (float) g2scale1;
else
g2maxscale = k1 * g2scale1 + k2 * g2scale2;
g1len_to_max = g1len; /* Update g1 index */
g2len -= g2halflen; /* Erase previous G2 terminators */
g2halflen = 0;
if (k1 > k_thresh) /* If path lies near this pixel, label as G2 */
{
g2offsets[g2len++] = off1;
g2halflen++;
}
if (k2 > k_thresh)
{
g2offsets[g2len++] = off2;
g2halflen++;
}
xend = k1 * x1 + k2 * x2; /* Real coordinates of G2 terminator */
yend = k1 * y1 + k2 * y2;
if (!found_interp_flag)
{
G2fact1 = k1 * pow(cos(ddir1),2);
G2fact2 = k2 * pow(cos(ddir2),2);
Xx1 = x1;
Xx2 = x2;
Yy1 = y1;
Yy2 = y2;
zfirst = g2max1;
xfirst = xend;
yfirst = yend;
keepoff1 = off1;
keepoff2 = off2;
found_interp_flag = 1;
}
}
else if (((sign(g2val) != g2signbit) || g2val == 0.0) && g2max1 != 0.0)
{
end_of_path = 1;
}
else if ((sign(g2val) != g2signbit) && (fabs(g2val) > fabs(g2max2)))
{
g2max2 = g2val; /* Largest sig. G2 response of incorrect sign */
}
g1_prev_val = g1val;
}
if (!end_of_path)
{
if (same_dir1) /* 1st pixel in pair is compatible */
{
g1offsets[g1len++] = off1; /* Label as interior pixel */
if (g1scale1 > edgescale) /* Update scale to max */
edgescale = g1scale1;
}
if (same_dir2) /* 2nd pixel in pair is compatible */
{
g1offsets[g1len++] = off2; /* Label as interior pixel */
if (g1scale2 > edgescale) /* Update scale to max */
edgescale = g1scale1;
/**** Is this meant to be g1scale1 or g1scale2? ****/
}
/* Step to next intersection of ray with pixel grid */
if (x1 == x2)
{
x1 += dx;
if (abs(y2) < fabs(x1*tan_theta))
y1+= dy;
else
x2+= dx;
}
else
{
y2 += dy;
if (abs(y2) > fabs(x1*tan_theta))
x2+= dx;
else
y1+= dy;
}
}
}
if (g2maxscale > edgescale) /* Update edgescale to max of G1 and G2 scales */
edgescale = (int) g2maxscale;
edgewidth += sqrt(xend*xend + yend*yend); /* Add distance from 0 to terminator */
return (1);
}
/******************************************************************************/
/* compare_dir */
int compare_dir(double g1mag1,double g1dir1,int g1scale1,double g1mag2,double g1dir2,int g1scale2)
{
double a1, a2; /* Pixelation uncertainty at respective scales */
double ddir, tolerance; /* actual and tolerated gradient direction discrepancy */
static double g1noise_norms[6] = {0.765,0.199,0.0499,0.0125,0.00312,0.00078};
if (g1dir2 == 4) /* Gradient not significant */
return(0);
a1 = pixerr[g1scale1-1];
a2 = pixerr[g1scale2-1];
ddir = fabs(g1dir1 - g1dir2);
if (ddir > KPI)
ddir = 2*KPI - ddir;
/* tolerance is sum of half-ranges due to pixelation uncertainty
plus the range due to sensor noise */
tolerance = a1 + a2 + 5.2 * noise * sqrt(pow(g1noise_norms[g1scale1-1]/g1mag1,2.0)
+ pow(g1noise_norms[g1scale2-1]/g1mag2,2.0));
if (ddir <= tolerance)
return(1);
return(0);
}
/***************************************************************************/
/* signbit */
int sign(double r)
{
if (r<0)
return (1);
else
return (0);
}
/******************************************************************************/
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
{
double *img;
double *g1mag;
double *g1dir;
double *g1scale;
double *g2;
double *g2scale;
double *g2all;
int x_dim, y_dim;
double sensor_noise;
int maxwidth, maxscale;
int sublocflag;
double dmwidth,mxscale;
double subflag;
double *x;
double *x_blur;
double *x_dark;
double *x_light;
double *x_x1zero;
double *x_y1zero;
double *x_x2zero;
double *x_y2zero;
int warnings = 1;
mxArray *arg;
double *mxMat;
/* copy start */
if (nrhs < 11 )
mexErrMsgTxt("requres at least 11 args.");
/* ARG 1: IMAGE */
arg = prhs[0];
if notDblMtx(arg) mexErrMsgTxt("IMAGE arg must be a real non-sparse matrix.");
img = mxGetPr(arg);
x_dim = (int) mxGetM(arg); /* X is inner index! */
y_dim = (int) mxGetN(arg);
rows = x_dim;
cols = y_dim;
/* ARG 2: g1mag */
arg = prhs[1];
if notDblMtx(arg) mexErrMsgTxt("g1mag arg must be a real non-sparse matrix.");
g1mag = mxGetPr(arg);
/* ARG 3: g1dir */
arg = prhs[2];
if notDblMtx(arg) mexErrMsgTxt("g1dir arg must be a real matrix");
g1dir = mxGetPr(arg);
/* ARG 4: g1scale */
arg = prhs[3];
if (notDblMtx(arg)) mexErrMsgTxt(" g1scale arg must be a int matrix");
g1scale = mxGetPr(arg);
/* ARG 5: g2 */
arg = prhs[4];
if notDblMtx(arg) mexErrMsgTxt("g2 arg must be a real matrix");
g2 = mxGetPr(arg);
/* ARG 6: g2scale */
arg = prhs[5];
if (notDblMtx(arg)) mexErrMsgTxt("g2scale arg must be a int matrix");
g2scale = mxGetPr(arg);
/* ARG 7: g2all */
arg = prhs[6];
if (notDblMtx(arg)) mexErrMsgTxt("g2all arg must be a int matrix");
g2all = mxGetPr(arg);
/* ARG 8: sensor_noise */
arg = prhs[7];
if notDblMtx(arg) mexErrMsgTxt("sensor_noise arg must be a real matrix");
if (mxGetM(arg)*mxGetN(arg) != 1)
mexErrMsgTxt("sensor_noise arg must be a scalar");
mxMat = mxGetPr(arg);
sensor_noise = *mxMat;
/* ARG 9: maxwidth */
arg = prhs[8];
if (notDblMtx(arg)) mexErrMsgTxt("maxwidth arg must be a int matrix");
if (mxGetM(arg)*mxGetN(arg) != 1)
mexErrMsgTxt("maxwidth arg must be a scalar");
mxMat = mxGetPr(arg);
dmwidth = *mxMat;
/* ARG 10: subpixel */
arg = prhs[9];
if (notDblMtx(arg)) mexErrMsgTxt("subpixelflag arg must be a int matrix");
if (mxGetM(arg)*mxGetN(arg) != 1)
mexErrMsgTxt("subpixelflag arg must be a scalar");
mxMat = mxGetPr(arg);
subflag = *mxMat;
/* ARG 11: maxscale */
arg = prhs[10];
if (notDblMtx(arg)) mexErrMsgTxt("maxscale arg must be a int matrix");
if (mxGetM(arg)*mxGetN(arg) != 1)
mexErrMsgTxt("maxscale arg must be a scalar");
mxMat = mxGetPr(arg);
mxscale = *mxMat;
/* ARG 6: WARNINGS */
if (nrhs>11)
{
arg = prhs[4];
if notDblMtx(arg) mexErrMsgTxt("WARINGS arg must be a real scalar.");
if (mxGetM(arg) * mxGetN(arg) != 1)
mexErrMsgTxt("WARNINGS arg must be a real scalar.");
mxMat = mxGetPr(arg);
warnings = (int) *mxMat;
}
plhs[0] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[0] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x = mxGetPr(plhs[0]);
plhs[1] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[1] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_blur = mxGetPr(plhs[1]);
plhs[2] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[2] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_dark = mxGetPr(plhs[2]);
plhs[3] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[3] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_light = mxGetPr(plhs[3]);
plhs[4] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[4] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_x1zero = mxGetPr(plhs[4]);
plhs[5] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[5] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_y1zero = mxGetPr(plhs[5]);
plhs[6] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[6] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_x2zero = mxGetPr(plhs[6]);
plhs[7] = mxCreateDoubleMatrix(x_dim,y_dim,mxREAL);
if (plhs[7] == NULL) mexErrMsgTxt("Cannot allocate result matrix");
x_y2zero = mxGetPr(plhs[7]);
/* copy ends */
maxwidth = (int)dmwidth;
maxscale = (int)mxscale;
sublocflag = (int)subflag;
find_edges(img,g1mag,g1dir,g1scale,g2,g2scale,g2all,sensor_noise,maxwidth,sublocflag,
maxscale,x,x_blur,x_dark,x_light,x_x1zero,x_y1zero,x_x2zero,x_y2zero);
}
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/g1scale.m
================================================
%###########################################################################
%
% g1scale(g1mag1,g1dir1,g1mag2,g1dir2,g1scale1,scale,noise,b_est)
%
% Purpose: Augments multi-scale Gaussian Gradient maps with significant
% estimates at a new scale. Pixels for which the magnitude
% of the Gaussian gradient is under threshold in the multi-scale
% map (gradient magnitude input 1) but over threshold
% at the new scale (gradient magnitude input 2) are updated
% with the gradient magnitude, direction and scale value of
% the new scale.
%
% Input: g1mag1 - Multi-scale Gaussian gradient magnitude image
% g1dir1 - Multi-scale Gaussian gradient direction image
% g1mag2 - Gaussian gradient magnitude image at new scale
% g1dir2 - Gaussian gradient direction image at new scale
% g1scale1 - Multi-scale scale map
% g1scale2 - Scale of new gradient estimates
% noise - Estimated sensor noise
% b_est - Derivatives near boundary estimated by reflecting
% intensity function.
% Output: g1mag1 - Integrated multi-scale gradient magnitude map
% g1dir1 - Integrated multi-scale gradient direction map
% g1scale1 - Integrated multi-scale scale map
%
%###########################################################################
function[g1mag1,g1dir1,g1sc1] = g1scale(g1mag1,g1dir1,g1mag2,g1dir2,g1sc1,...
scale,noise,b_est)
norms12 = [0.765, 0.199, 0.0499, 0.0125, 0.00312, 0.00078];
thresh = 5.6 * noise * norms12(scale);
if ((scale<3) | b_est)
krad = 1;
else
krad = ceil(4.6*sqrt(pow2(2*(scale-2))-1));
end
if (scale == 1)
g1mag1 = zeros(size(g1mag2));
g1dir1 = 4 * ones(size(g1mag2));
g1sc1 = zeros(size(g1mag2));
f = find(g1mag2 >= thresh);
g1mag1(f) = g1mag2(f);
g1dir1(f) = g1dir2(f);
g1sc1(f) = scale;
else
sz = size(g1mag2);
[i,j] = meshgrid(krad+1:1:sz(1)-krad,krad+1:1:sz(2)-krad);
itrans = i';
jtrans = j';
if ~(isempty(itrans) | isempty(jtrans))
K = sub2ind(size(g1mag2),itrans(:),jtrans(:));
smat = g1sc1(krad+1:end-krad,krad+1:end-krad);
mmat = g1mag2(krad+1:end-krad,krad+1:end-krad);
f = find((smat==0) & (mmat>=thresh));
g1mag1(K(f)) = g1mag2(K(f));
g1dir1(K(f)) = g1dir2(K(f));
g1sc1(K(f)) = scale;
end;
end;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/g1steer.m
================================================
%######################################################################
%
% g1steer - Computes magnitude and direction of the gradient of the
% luminance function based on x and y basis functions for 1st Gaussian
% derivative.
%
%
% Input: g1x - X basis for Gaussian gradient
% g1y - Y basis for Gaussian gradient
% Output: g1mag - Gradient Magnitude Estimate
% g1dir - Gradient Direction Estimate
%
%######################################################################
function[g1mag,g1dir] = g1steer(g1x,g1y)
g1mag = zeros(size(g1x));
g1dir = 4 * ones(size(g1x));
f = find((g1x ~= 0) & (g1y ~= 0));
g1dir(f) = atan2(-g1y(f),g1x(f));
g1mag(f) = sqrt(g1y(f).^2 + g1x(f).^2);
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/g2scale.m
================================================
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% g2scale - Augments multi-scale Gaussian directional 2nd derivative maps
% with significant estimates at a new scale. Pixels for which the magnitude
% of the 2nd derivative is under threshold in the multi-scale map
% (2nd derivative input 1) but over threshold at the new scale (2nd derivative
% input 2) are updated with the 2nd derivative magnitude and scale value of the
% new scale.
%
% Input: g2mag1 - Multi-scale Gaussian directional 2nd derivative image
% g2mag2 - Gaussian directional 2nd derivative image at new scale
% g1scale1 - Multi-scale scale map
% noise - Estimated sensor noise
% b_est - Estimate derivatives near boundaries?
%
% Output: g2mag - Integrated multi-scale directional 2nd derivative map
% g2scale - Integrated multi-scale 2nd derivative scale map
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function[g2mag1,g2sc1] = g2scale(g2mag1,g2mag2,g2sc1,scale,noise,b_est)
norms12 = [1.873 0.2443 0.0306 0.003817 0.00047715 0.0000596 0.000007455];
thresh = 5.2 * noise * norms12(scale);
if ((scale<3) | b_est)
krad = 1;
else
krad = ceil(4.6*sqrt(pow2(2*(scale-2))-1.0));
end;
if (scale == 1)
g2mag1 = zeros(size(g2mag2));
g2sc1 = zeros(size(g2mag2));
f = find(abs(g2mag2) >= thresh);
g2sc1(f) = scale;
g2mag1(f) = g2mag2(f);
else
sz = size(g2mag1);
[i,j] = meshgrid(krad+1:sz(1)-krad,krad+1:sz(2)-krad);
itrans = i';
jtrans = j';
if ~(isempty(itrans) | isempty(jtrans))
K = sub2ind(size(g2mag1),itrans(:),jtrans(:));
magmat1 = g2mag1(krad+1:end-krad,krad+1:end-krad);
magmat2 = g2mag2(krad+1:end-krad,krad+1:end-krad);
f = find(abs(magmat1) == 0 & abs(magmat2) >= thresh);
g2mag1(K(f)) = g2mag2(K(f));
g2sc1(K(f)) = scale;
end;
end;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/g2steer.m
================================================
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% g2steer - Computes the second Gaussian derivative of the luminance function
% in specified direction ( normally the Gradient direction). The three input
% basis function are used to steer the derivative. The units of the direction
% map are radians and the range is between -pi and pi. A value of -4 indicate
% that no direction was measurable. The 2nd derivative is taken only for valid
% directions.
%
% Input: g2x - Response map for 1st G2 basis function
% g2y - Response map for 2nd G2 basis function
% g2xy - Response map for 3rd G2 basis function
% g1dir - Luminance gradient direction map
%
% Output: g2 - Second derivative response map
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function[g2] = g2steer(g2x,g2y,g2xy,g1dir)
g2 = zeros(size(g2x));
f = find((g1dir ~= 4.0) & (g2x ~= 0) & (g2xy ~= 0) & (g2y ~= 0));
cdir = cos(2*g1dir);
sdir = sin(2*g1dir);
g2(f) = 0.5 * (1 + cdir(f)) .* g2x(f) - sdir(f) .* g2xy(f) + ...
0.5 * (1 - cdir(f)) .* g2y(f);
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/gauss.m
================================================
% gauss.m: Calculates Gaussian pdf values.
function[y] = gauss(x,std)
y = exp(-x.^2/(2*std^2)) / (std*sqrt(2*pi));
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/gradient.m
================================================
%############################################################
%
% gradient(maxscale,noise,gauss_a,conv_type,filtpath)
% Computes non-zero gradient in the luminance function by
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function[g1mag,g1dir,g1sc] = gradient(maxscale,noise,gauss_a,...
conv_type)
fm2 = '.ascii';
%############
% Initialise:
%############
g1mag = 0;
g1dir = 0;
g1sc = 0;
for scale = 1:1:maxscale
% set scale values for filters
if (scale == 1)
g1scaleval = '05';
else
g1scaleval = '1';
end;
mimg = gauss_a(:,:,scale);
%#####################################################
% Compute response of first derivative Gaussian filter
% to the blurred image in an arbitrary direction:
%#####################################################
%kern1 = load(strcat(filtpath,'/gy',g1scaleval,fm2));
kern1 = load(strcat('gy',g1scaleval,fm2));
rc1 = convolve_2(mimg,kern1,conv_type);
%kern2 = load(strcat(filtpath,'/g1x',g1scaleval,fm2));
kern2 = load(strcat('g1x',g1scaleval,fm2));
rc2 = convolve_2(rc1,kern2,conv_type); % x basis input
%kern3 = load(strcat(filtpath,'/gx',g1scaleval,fm2));
kern3 = load(strcat('gx',g1scaleval,fm2));
rc3 = convolve_2(mimg,kern3,conv_type);
%kern4 = load(strcat(filtpath,'/g1y',g1scaleval,fm2));
kern4 = load(strcat('g1y',g1scaleval,fm2));
rc4 = convolve_2(rc3,kern4,conv_type); % y basis input
%###################################################
% Calculate magnitude and direction of the gradient:
%###################################################
[m2,d2] = g1steer(rc2,rc4);
omag = g1mag;
odir = g1dir;
osc = g1sc;
%############################################
% Augment multi-scale Gaussian Gradient maps:
%############################################
[g1mag,g1dir,g1sc] = g1scale(g1mag,g1dir,m2,d2,g1sc,scale,noise,0);
end;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/main_edge.m
================================================
%############################################################
%
% main_edge.m: Main function called from edge GUI interface.
%
%############################################################
function[OutputData] = main_edge(img,scale,noise,edgew,...
conss,congrd,subpixelflag)
%#############
% Input Image:
%#############
if (size(img,3)~=1)
img = rgb2gray(img);
end;
img=double(img);
%########################
% Compose blurred images:
%########################
gauss_imgs = scalespace(img,scale,conss);
%########################
% Calculate gradient map:
%########################
[g1_mag,g1_dir,g1_sc] = gradient(scale,noise,gauss_imgs,congrd);
%##############################
% Calculate 2nd derivative map:
%##############################
[g2_mag,g2_sc,g2_all] = derivative2nd(g1_dir,scale,noise,gauss_imgs,congrd);
% save input.mat img g1_mag g1_dir g1_sc g2_mag g2_sc g2_all noise edgew subpixelflag scale
%inparTest = checkInParam(img,g1_mag,g1_dir,g1_sc,g2_mag,g2_sc,g2_all,noise,...
% edgew,subpixelflag,scale);
%if inparTest > 0
% display('stop');
%end
%####################
% Calculate edge map:
%####################
clear find_edges
[edge_map,blur_map,dark_map,light_map,xzero1_map,yzero1_map,xzero2_map,yzero2_map] = ...
find_edges(img,g1_mag,g1_dir,g1_sc,g2_mag,g2_sc,g2_all,noise,...
edgew,subpixelflag,scale);
clear find_edges
% outparTest = checkOutParam(edge_map,blur_map,dark_map,light_map,xzero1_map,yzero1_map,xzero2_map,yzero2_map);
% if outparTest > 0
% display('stop');
% end
%save output.mat edge_map blur_map dark_map light_map xzero1_map yzero1_map xzero2_map yzero2_map
% Branka's code leaves dark and light as double which need rounding:
dark_map = round(dark_map);
light_map = round(light_map);
%#######################
% Output data in struct:
%#######################
% OutputData = struct('edge',edge_map,'dark_map',dark_map,...
% 'light_map',light_map,'blur_map',blur_map,...
% 'g1mag',g1_mag,'g1dir',g1_dir,'g1scale',g1_sc,...
% 'g2mag',g2_mag,'g2scale',g2_sc,...
% 'x1zero',xzero1_map,'y1zero',yzero1_map,...
% 'x2zero',xzero2_map,'y2zero',yzero2_map,...
% 'noise',noise);
OutputData = struct('edge',edge_map,'blur',blur_map,...
'dark',dark_map,'light',light_map,...
'g1mag',g1_mag,'g1dir',g1_dir,'g1scale',g1_sc,...
'g2mag',g2_mag,'g2scale',g2_sc,'g2_all',g2_all,...
'noise',noise,'xzero',xzero1_map,'yzero',yzero1_map);
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/mat2hough_scale.m
================================================
function [ r, th] = mat2hough_scale(m, n, r_max, r_res, th_res)
% MAT2HOUGH converts matrix coordinates to Hough map values
%
% Inputs:
% r_max: Maximum value r can take
% m: vertical matrix index
% n: horizontal matrix index
%
% Output:
% r: Hough domain r value equivalent to n
% th: Hough domain \theta value equivalent to m
%
% Author:
% Ron Tal
%
% Date:
% February 16, 2009
%
% Description:
% This function converts the coordinates of a matlab matrix
% representing a Hough map, to the equivalent Hough domain
% values
%
th = round2frac((m - 1) * th_res, th_res);
r = round2frac((n - 1) * r_res - r_max, r_res);
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/plot_edgelmap.m
================================================
% Function to plot gradient direction vectors at both pixel
% and subpixel locations:
function[] = plot_edgelmap(img,edgemap,xzeromap,yzeromap,g1dir,...
newarrcol,leg1,leg2,imtitle);
[M,N] = size(edgemap);
[I,J] = find(edgemap==255);
K = find(edgemap==255);
X = J;
Y = I;
% Set arrowlength, then define:
% (Uarrow,Varrow) = arrow lengths in (x,y) directions
% (Xarrow,Yarrow) = arrow tail points
arrowlen = 1;
Graddir = g1dir(K) + pi/2;
Uarrow = arrowlen * cos(Graddir);
Varrow = -arrowlen * sin(Graddir);
Xtp = X - 0.5*Uarrow;
Ytp = Y - 0.5*Varrow;
Xarrow = xzeromap(K) - 0.5*Uarrow;
Yarrow = yzeromap(K) - 0.5*Varrow;
figure;
imagesc(img);
axis image;
colormap(gray);
hold on;
p1 = plot(0,0);
set(p1,'Color',[0 0 1]);
set(p1,'Visible','off');
p2 = plot(0,0);
set(p2,'Color',newarrcol);
set(p2,'Visible','off');
%q1 = quiver(Xtp,Ytp,Uarrow,Varrow,0); JE Jul08 - suppress pixel-localized
%edges
q2 = quiver(Xarrow,Yarrow,Uarrow,Varrow,0); %
%set(q1,'Color',[0 0 1]);
set(q2,'Color',newarrcol);
L = legend([p1,p2],leg1,leg2,-1);
set(L,'Fontsize',8);
twords = ['Edgels for Image ',imtitle];
tt = title(twords);
set(tt,'Interpreter','none');
hold off;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/round2frac.m
================================================
%% round2frac() Rounds the input data to the nearest fraction
%
% Description:
%
% Takes 'data' a matrix of floats and rounds it to the nearest 'frac'
%
% Author:
%
% Ron Tal
%
% Date Created:
%
% November 4, 2008
%
function [round_data] = round2frac(data, frac)
% if frac > 1, error('Fraction must be smaller than 1'); end
round_data = data.*(1/frac);
round_data = round(round_data);
round_data = round_data.*frac;
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/run_elderEdge.m
================================================
clc
clear
close all
for i = 1:9
clear mex
edgeStruct = elderEdge();
figure;
imshow(edgeStruct.edge_map);
i
length(find(edgeStruct.edge_map))
diffs{i} = edgeStruct.edge_map;
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/scalespace.m
================================================
%################################################################
%
% r = scalescape(mimg,maxscale,conv_type)
%
% Create a series of Gaussian blurred images according to a
% given maximum scale.
%
% Input: mimg: image for convolution w/ Gaussian filter
% maxscale: maximum scale value
% conv_type: convolution type flag
%
% Output: blurred_imgs: maxscale blurred images
%
%################################################################
function[blurred_imgs] = scalespace(mimg,maxscale,conv_type)
for scale = 1:1:maxscale
if (scale < 3)
% Image is unblurred:
blurred_imgs(:,:,scale) = mimg;
else
% Set values for generating Gaussian filter at given scale:
[stdd,sizz] = setvalues(scale);
kern = d2gauss(sizz,stdd,1,1,0,1/(stdd*sqrt(2*pi)));
c1mimg = convolve_2(mimg,kern,conv_type);
cres = convolve_2(c1mimg,rot90(kern,3),conv_type);
blurred_imgs(:,:,scale) = cres;
end;
end;
return;
% Results differ to cantata output for scale >= 3 in the 4th d.p.
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/code/setvalues.m
================================================
%##########################################################################
%
% [stdd,sizz] = setvalues(scale)
%
% Set values for generating 2d Gaussian filter according to
% input scale.
%
% Input: scale
%
% Output: stdd: std dev'n along width (along height assumed to be 1)
% size: width of output = #columns (height assumed to be 1)
%
%##########################################################################
function[stdd,sizz] = setvalues(scale);
if (scale < 3)
stdd = 1;
else
stdd = sqrt((2^(scale-2))^2 - 1);
end;
sizz = 2 * ceil(4.6 * stdd) + 1;
return;
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g1x05.ascii
================================================
0.750938 0 -0.750938
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g1x1.ascii
================================================
0.000535321 0.0132955 0.107982 0.241971 -0 -0.241971 -0.107982 -0.0132955 -0.000535321
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g1y05.ascii
================================================
0.750938
0
-0.750938
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g1y1.ascii
================================================
0.000535321
0.0132955
0.107982
0.241971
-0
-0.241971
-0.107982
-0.0132955
-0.000535321
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g2x05.ascii
================================================
0.0594001 1.02157 -2.16194 1.02157 0.0594001
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g2x1.ascii
================================================
0.00200744 0.0354546 0.161972 0 -0.39894 0 0.161972 0.0354546 0.00200744
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g2y05.ascii
================================================
0.0594002
1.02157
-2.16194
1.02157
0.0594002
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/g2y1.ascii
================================================
0.00200744
0.0354546
0.161972
0
-0.39894
0
0.161972
0.0354546
0.00200744
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/gx05.ascii
================================================
0.157727 0.684552 0.157727
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/gx1.ascii
================================================
0.00443185 0.053991 0.241971 0.398942 0.241971 0.053991 0.00443185
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/gy05.ascii
================================================
0.157726
0.68455
0.157726
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/ElderZuckerEdgeDetector/filters/gy1.ascii
================================================
0.00443185
0.053991
0.241971
0.398942
0.241971
0.053991
0.00443185
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/computeKernel.m
================================================
function [kern,ind]=computeKernel(e,r_max,SIGMA_X, SIGMA_TH, r_res, th_res)
%computing the kernel for the probabilistic hough tranform
r_th = e;
C =[SIGMA_X^2 + (r_th^2) * (SIGMA_TH^2), r_th * (SIGMA_TH^2); r_th * (SIGMA_TH^2), SIGMA_TH^2];
C_inv = inv(C);
C_det = det(C);
N = 1 / (2*pi * sqrt(C_det));
R = -round2frac(3 *sqrt(C(1,1)), r_res):r_res:round2frac(3 *sqrt(C(1,1)), r_res);
TH = -round2frac(3 * SIGMA_TH, th_res):th_res:round2frac(3 * SIGMA_TH, th_res);
c=C_inv(1,1);
d=C_inv(1,2);
g=C_inv(2,1);
f=C_inv(2,2);
[a,b]=meshgrid(R,TH);
vals=N * exp(-0.5*((a*c+b*g).*a+(a*d+b*f).*b));
[r, c] = find(vals > 0.05*max(vals(:)));
m_c = min(c) - 1;
m_r = min(r) - 1;
linearInd = sub2ind([length(TH),length(R)], r, c);
kern=[c- m_c,r- m_r, vals(linearInd)];
ind = e + r_max + 1;
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/determine_hough_lines_kernel_vote_remove.m
================================================
function [ lines, edgeStruct ] = determine_hough_lines_kernel_vote_remove( im, pp, kernels, kernels_flip, num, kernel_params, r_res, th_res, sig_bound, frac,scale_factor)
%edge detection
edgeStruct = main_edge(im,5,1,10,1,0,1);
%find the pixel location that has edge value
edge_index = find(edgeStruct.edge);
edge_img_locations = [edgeStruct.xzero(edge_index),edgeStruct.yzero(edge_index)];
edge_gradients = edgeStruct.g1dir(edge_index);
%create a hough map
map = mexVoteEdges_v3_scale(edge_img_locations, edge_gradients, kernels, kernels_flip, r_res,th_res, pp, kernel_params,scale_factor);
%iteratively extract lines from hough map
[P] = hough_find_peaks_vote_removal(map, num, r_res, th_res, edgeStruct, kernels, kernels_flip, pp, kernel_params, 0.0917, 0.3934, sig_bound, frac, scale_factor);
%the lines consist of [rho, theta, strength]
lines = P(:, 1:3);
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/get_all_segments_assoc_edgeremoval.m
================================================
function [ begin_points, end_points,ls_likelihoods] = get_all_segments_assoc_edgeremoval( line_data, edgeStruct, pp, LIKE_MODEL,scale_factor)
begin_points = [];
end_points = [];
ls_likelihoods = [];
for i = 1:size(line_data, 1)
[~, begin_pointsi, end_pointsi, ~,~, ~, ~, ~, ~, likelihoods, edgeMap] = get_line_segment_DP_edgeremoval( edgeStruct, line_data(i,1), line_data(i,2), pp, LIKE_MODEL, scale_factor);
for j = 1:size(likelihoods,2)
begin_points = [begin_points; begin_pointsi(j,:)];
end_points = [end_points; end_pointsi(j,:)];
ls_likelihoods = [ls_likelihoods; likelihoods(j)];
end
edgeStruct.edge = edgeMap;
end
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/get_line_segment_DP_edgeremoval.m
================================================
function [final_labels, begin_points, end_points, begin_point, end_point, posterior, lslength, logratioposterior, ls_probDiff, likelihoods, edgemap] = get_line_segment_DP_edgeremoval( edgeStruct, rho, theta, pp, probStruct, scale_factor)
% Description: Line segment detector that removes the edges associated with
% each detected line segment.
final_labels = [];
begin_points = [];
end_points = [];
begin_point = [];
end_point = [];
posterior = [];
lslength = [];
logratioposterior = [];
ls_probDiff = [];
edgemap = edgeStruct.edge;
edge_gradients = edgeStruct.g1dir;
exist_edge_on = probStruct.exist_edge_on;
exist_edge_off = probStruct.exist_edge_off;
ang_dev_on = probStruct.ang_dev_on_hist;
ang_dev_off = probStruct.ang_dev_off_hist;
nexist_edge_on = probStruct.nexist_edge_on;
nexist_edge_off = probStruct.nexist_edge_off;
%the transition probability for the line segments
%it is generated from sampling in the YorkUDB dataset
prob_leave_on = 0.0051/scale_factor;
prob_stay_on =1-prob_leave_on;
prob_leave_off = 0.0014/scale_factor;
prob_stay_off = 1-prob_leave_off;
prob_on = 0.247;
prob_off = 0.753;
[m, n] = size(edgeStruct.edge);
% GET EQUATION OF THE LINE
X_1 = rho * cos(theta) - 2000*cos(theta+pi/2) + pp(1);
X_2 = rho * cos(theta) + 2000*cos(theta+pi/2) + pp(1);
Y_1 = rho * sin(theta) - 2000*sin(theta+pi/2) + pp(2);
Y_2 = rho * sin(theta) + 2000*sin(theta+pi/2) + pp(2);
M = (Y_2-Y_1)/(X_2-X_1 + 1e-7);
b = Y_2 - X_2*M;
% GET BOUNDARIES OF THE LINE IN IMAGE FRAME
p1 = [1, M + b];
p2 = [n, M*n + b];
p3 = [(1-b)/M,1];
p4 = [(m-b)/M, m];
points = [p1;p2;p3;p4];
X = points(:,1);
Y = points(:,2);
X_valid = (X <= n).*(X >= 1);
Y_valid = (Y <= m).*(Y >= 1);
p_valid = X_valid.*Y_valid;
points_valid = points(p_valid>0, :); %changed find to logical index
X_l = points_valid(:,1)';
Y_l = points_valid(:,2)';
if isempty(X_l) || isempty(Y_l)
return;
end
%get the line points
indexXY=sampleLine(rho,theta,n,m,pp);
yy=ceil(indexXY(:,2)+pp(2));
xx=ceil(indexXY(:,1)+pp(1));
index = sub2ind([m,n],yy,xx);
p1 = [X_l(1), Y_l(1)];
p2 = [X_l(2), Y_l(2)];
% FIND POINTS CONSIDERED
node_idx=index;
y_h = edgemap(node_idx);
% y_h(y_h > 0) = 1;
% FIND ORIENTATION DIF AND DIST:
TH = pi-theta;
if TH<0, TH=TH+pi;
elseif TH>pi, TH=TH-pi;
end
edge_gradientsmap=edge_gradients(node_idx);
edgeind=edge_gradientsmap < 0;
edge_gradientsmap(edgeind) = edge_gradientsmap(edgeind) + pi;
edgeind=edge_gradientsmap>pi;
edge_gradientsmap(edgeind)= edge_gradientsmap(edgeind)-pi;
edge_img_locations = [edgeStruct.xzero(node_idx),edgeStruct.yzero(node_idx)];
ang_dev = round((edge_gradientsmap - TH)*180/pi);
ang_dev(ang_dev < -90) = ang_dev(ang_dev < -90) + 180;
ang_dev(ang_dev > 90) = ang_dev(ang_dev > 90) - 180;
range_ang = -90:1:90;
range_dis = 0:0.05:5;
loc_points = [xx, yy];
loc_points(y_h > 0,:) = edge_img_locations(y_h > 0,:);
if size(loc_points,1) == 0
loc_points = [-1000, -1000];
end
%Get the projection of points along the line defined by p1 and p2
[points_proj, distances] = project_point_to_line_segment_vec(p1,p2,loc_points);
distances = interp1(range_dis, range_dis, abs(distances), 'nearest');
range_dis = 0:0.05:1.95;
% Eliminate GROSS OUTLIERS
node_idx(distances > 2) = [];
ang_dev(distances > 2) = [];
y_h(distances > 2) = [];
points_proj(distances > 2,:) = [];
distances(distances > 2) = [];
% SORT:
if abs(M) > 1
[~, idx] = sort(points_proj(:,2));
else
[~, idx] = sort(points_proj(:,1));
end
points_proj = points_proj(idx,:);
node_idx = node_idx(idx);
distances = distances(idx);
ang_dev = ang_dev(idx);
y_h = y_h(idx);
ang_dev(y_h == 0) = NaN;
%INIT
begin_point = points_proj(1,:);
end_point = points_proj(end,:);
Psi_t = [prob_stay_on prob_leave_off; prob_leave_on prob_stay_off];
pi_var = [prob_on; prob_off];
[~, obslik,~,DP_table, aux_table, z]=hmmParameters(exist_edge_on,exist_edge_off,ang_dev_on,...
ang_dev_off,node_idx,Psi_t,pi_var,y_h,distances,range_dis,nexist_edge_on,...
nexist_edge_off,prob_on,prob_off,prob_stay_on,prob_leave_off,prob_leave_on,...
prob_stay_off,ang_dev,range_ang);
[lsjoint, lslogz,final_labels, node_on, likelihoods] = trace_Back(DP_table, aux_table, z, node_idx, obslik);
edgemap(node_on) = 0;
final_labels(final_labels == 2) = 0;
% RETURN LINE SEGMENTS
[begin_points,end_points,lslength,startpositions,endpositions]=returnLines(points_proj,final_labels);
%compute p(x=0|y) for each line segment
logjointoff = zeros(size(begin_points,1), 1);
for j = 1:size(begin_points,1) %number of line segments
for i = startpositions(j):endpositions(j)
if startpositions(j) == 1
transition = prob_off;
past = 0;
elseif i == startpositions(j)
transition = prob_leave_on;
past = DP_table(1,i-1);
else
transition = prob_stay_off;
past = DP_table(2,i-1);
end
if y_h(i)
[~, nn] = min(abs(distances(i) - range_dis));
[~, mm] = min(abs(ang_dev(i) - range_ang));
p_y_off_1 = exist_edge_off(nn);
p_t_off_1 = ang_dev_off(mm);
loc_off = p_y_off_1*p_t_off_1; %likelihood of y given x = off
logjointoff(j) = logjointoff(j) + log(loc_off) + log(transition) + past;
else
[~, nn] = min(abs(distances(i) - range_dis));
loc_off = nexist_edge_off(nn);
logjointoff(j) = logjointoff(j) + log(loc_off) + log(transition) + past;
end
end
end
for i = 1:size(logjointoff,1)
if logjointoff(i) > lslogz(i) || logjointoff(i) > lsjoint(i)
display('Error');
end
end
if size(lsjoint,1) > 0
logratioposterior = lsjoint - logjointoff;
else
logratioposterior = 0;
end
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/hough_find_peaks_vote_removal.m
================================================
%% Function P = hough_find_peaks_with_near_peak_supression(map, num_lines, NHOOD)
% Input
% map Hough Map
% num_lines Maximum number of lines
% NHOOD [R, TH] neighborhood to be
% surpressed
%
% Output
% P Hough Peaks
%
%
function [P] = hough_find_peaks_vote_removal(map, num, r_res, th_res, edgeStruct, kernels, kernels_flip, pp, kernel_params, sig_th, sig_r, sig_bound, frac, scale_factor)
% Loop until no more peaks or number of desired peaks have been
% selected
COUNT = 0;
MAX_R = 400*scale_factor;
P =[];
edgeMap = edgeStruct.edge;
iter = 1;
[m,n]=size(edgeMap);
while 1
iter = iter + 1;
% Find max peak:
[maxmap,ind]=max(map(:));
[r,c] = ind2sub(size(map), ind);
if length(r) > 1, r = r(1); c = c(1); end
%here are some stopping criteria
if isempty(r)
return;
end
[rho, theta] = mat2hough_scale(r, c, MAX_R, r_res, th_res);
if ismember([rho, theta, maxmap], P, 'rows', 'legacy')
return;
end
if COUNT > 0
if (maxmap/P(1,3)) < frac
return;
end
end
% Save max peak
P = [P; [rho theta maxmap]];
COUNT = COUNT + 1;
% If reached number of lines, return
if size(P, 1) == num
return;
end
%there following block of code identify the location of edges that
%need to be removed from the hough map and edge map
indexXY=sampleLine(rho,theta,n,m,pp);
yy=ceil(indexXY(:,2)+pp(2));
xx=ceil(indexXY(:,1)+pp(1));
edge_index = sub2ind([m,n],yy,xx);
X_1 = rho * cos(theta) - 2000*cos(theta+pi/2) + pp(1);
X_2 = rho * cos(theta) + 2000*cos(theta+pi/2) + pp(1);
Y_1 = rho * sin(theta) - 2000*sin(theta+pi/2) + pp(2);
Y_2 = rho * sin(theta) + 2000*sin(theta+pi/2) + pp(2);
M = (Y_2-Y_1)/(X_2-X_1+1e-7);
b = Y_2 - X_2*M;
line_eq = [M, -1, b];
edge_img_locations = [edgeStruct.xzero(edge_index),edgeStruct.yzero(edge_index), ones(length(edge_index(:)),1)];
edge_gradients = edgeStruct.g1dir(edge_index);
edge_gradients2 = edge_gradients;
indexless0=edge_gradients2 < 0;
indexgreaterpi=edge_gradients2>pi;
edge_gradients2(indexless0) = edge_gradients2(indexless0) + pi;
edge_gradients2(indexgreaterpi)= edge_gradients2(indexgreaterpi)-pi;
edge_gradients2 = pi - edge_gradients2;
edgetetalesspi=edge_gradients2 - theta<-pi/2;
edge_gradients2(edgetetalesspi)= edge_gradients2(edgetetalesspi)+pi;
edgethetagreaterpi=edge_gradients2-theta>pi/2;
edge_gradients2(edgethetagreaterpi)= edge_gradients2(edgethetagreaterpi)-pi;
orientationDiff = abs(edge_gradients2 - theta);
distances = (abs(line_eq*edge_img_locations')'/norm(line_eq(1:2)));
distanceBoundary=(distances <= sig_bound*sig_r);
edge_img_locations_remove = edge_img_locations(distanceBoundary, 1:2);
edge_gradients_remove = edge_gradients(distanceBoundary);
orientationDiff = orientationDiff(distanceBoundary);
edge_index = edge_index(distanceBoundary);
orientatinDiffboundary=orientationDiff <=sig_bound*sig_th;
edge_img_locations_remove = edge_img_locations_remove(orientatinDiffboundary, :);
edge_gradients_remove = edge_gradients_remove(orientatinDiffboundary);
edge_index = edge_index(orientatinDiffboundary);
if isempty(edge_img_locations_remove)
break;
end
edgeMap(edge_index) = 0;
%remove the detected lines from the hough map
map=mexRemoveVotes_v3_scale(edge_img_locations_remove, edge_gradients_remove, kernels, kernels_flip, r_res, th_res, pp, kernel_params, map, scale_factor);
%map = mexRemoveVotes_v3_scale(edge_img_locations_remove, edge_gradients_remove, kernels, kernels_flip, r_res, th_res, pp, kernel_params, map, scale_factor);
end
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/kernelInitialization.m
================================================
function [kernels, kernels_flip, kernel_params] =kernelInitialization(img)
[m,n,~]=size(img);
scale_factor = sqrt(m^2+n^2)/800; %800 is the diagnal length of 640x480 image
maxr = floor(400*scale_factor);
sigmax = 0.4;
r_res = 0.2;
th_res = 0.002;
sigmatheta = 5.4*(pi/180);
[kernels, kernels_flip, kernel_params] = precompute_kernels_sparse_res2(maxr, sigmax, sigmatheta, r_res, th_res);
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/lineSegmentation_HighRes.m
================================================
function [lines, fullLines] =lineSegmentation_HighRes(img,kernels, kernels_flip, kernel_params)
%use 640x480 image as a reference. the diagonal size of this image is 800
%if image size 640x480 scale factor = 1
%if image size 1280x960 scale factor = 2 ... etc.
%the lines variable contains the detected line segmentations it arranged as
%[x1 y1 x2 y2 probability]
%The fullLines are the detected lines. It is arranged as [rho theta probability]
pp=[307.551305282635,251.454244960136]; %principle point
sig_bound = 3; %threshold for the line detection
r_res = 0.2; %the theshold for the rho
th_res = 0.002; %the threshold for theta
[m,n,~]=size(img);
scale_factor = sqrt(m^2+n^2)/800; %800 is the diagnal length of 640x480 image
%the kernel of the algorithm is calibrated on 640x480 image
%rescale the principle point
pp(1)=pp(1)*n/640;
pp(2)=pp(2)*m/480;
[lines, fullLines] = run_lineSegmentAlgorithm(kernels, kernels_flip, kernel_params, sig_bound, r_res, th_res, img, scale_factor,pp);
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/mexfiles/hmmParameters.m
================================================
function [alpha_t, obslik,T,DP_table, aux_table, z]=hmmParameters(exist_edge_on,exist_edge_off,ang_dev_on,...
ang_dev_off,node_idx,Psi_t,pi_var,y_h,distances,range_dis,nexist_edge_on,...
nexist_edge_off,prob_on,prob_off,prob_stay_on,prob_leave_off,prob_leave_on,...
prob_stay_off,ang_dev,range_ang)
node_idx_length=length(node_idx);
DP_table = zeros(2, node_idx_length);
aux_table = zeros(2, node_idx_length);
% aux_table(1,1) = -Inf;
% aux_table(2,1) = -Inf;
z = zeros(node_idx_length,1); % E.A. List of log evidences: sum(log(z)) from 1:t.
alpha_t = zeros(2,node_idx_length); %E.A. 2x1 vector (on/off) of posterior prob.: e.g. alpha_1 = [p(x=on|x); p(x=off|x)]
obslik = zeros(2,node_idx_length); %E.A. 2x1 observation likelihood
if y_h(1)
[val, nn] = min(abs(distances(1) - range_dis));
[val, mm] = min(abs(ang_dev(1) - range_ang));
p_y_on_1 = exist_edge_on(nn);
p_y_off_1 = exist_edge_off(nn);
p_t_on_1 = ang_dev_on(mm);
p_t_off_1 = ang_dev_off(mm);
loc_on = p_y_on_1*p_t_on_1; %likelihood of y given x = on
loc_off = p_y_off_1*p_t_off_1; %likelihood of y given x = off
DP_table(1,1) = log(loc_on) + log(prob_on);
DP_table(2,1) = log(loc_off) + log(prob_off);
%Compute prob. of the evidence. E.A.
obslik(:,1) = [loc_on; loc_off];
z(1) = sum(obslik(:,1) .* pi_var);
alpha_t(:,1) = [ loc_on * pi_var(1); loc_off * pi_var(2)] / z(1);
% if (round(sum(alpha_t(:,1))) ~= 1)
% display('Error');
% end
%% Compute log likelihood ration and evidenct -- E.A
% log_like_ratio_list(1) = log((p_y_on_1*p_t_on_1)/(p_y_off_1*p_t_off_1)); %E.A. log likelihood ratio (1st position, edge)
% evidence(1) = log(p_y_on_1*p_t_on_1*prob_on + p_y_off_1*p_t_off_1*prob_off);
%%
else
[val, nn] = min(abs(distances(1) - range_dis));
loc_on = nexist_edge_on(nn);
loc_off = nexist_edge_off(nn);
DP_table(1,1) = log(loc_on) + log(prob_on);
DP_table(2,1) = log(loc_off) + log(prob_off);
%Compute prob. of the evidence. E.A.
%psi = [loc_on; loc_off];
obslik(:,1) = [loc_on; loc_off];
z(1) = sum(obslik(:,1) .* pi_var);
alpha_t(:,1) = [ loc_on * pi_var(1); loc_off * pi_var(2)] / z(1);
%obslik(:,1) = [loc_on; loc_off];
% if (round(sum(alpha_t(:,1))) ~= 1)
% display('Error');
% end
%
%% Compute log likelihood ration and evidenct -- E.A
% log_like_ratio_list(1) = log(p_y_on_1/p_y_off_1); %E.A. log likelihood ratio (1st position, no edge)
% evidence(1) = log(p_y_on_1*prob_on + p_y_off_1*prob_off);
%%
end
% LOOP THROUGH
for i =2:node_idx_length
if y_h(i)
[val, nn] = min(abs(distances(i) - range_dis));
[val, mm] = min(abs(ang_dev(i) - range_ang));
p_y_on_1 = exist_edge_on(nn);
p_y_off_1 = exist_edge_off(nn);
p_t_on_1 = ang_dev_on(mm);
p_t_off_1 = ang_dev_off(mm);
loc_on = p_y_on_1*p_t_on_1; %likelihood of y given x = on
loc_off = p_y_off_1*p_t_off_1; %likelihood of y given x = off
[DP_table(1,i), aux_table(1,i)] = max([log(loc_on) + log(prob_stay_on) + DP_table(1,i-1), log(loc_on) + log(prob_leave_off) + DP_table(2,i-1)]);
[DP_table(2,i), aux_table(2,i)] = max([log(loc_off) + log(prob_leave_on) + DP_table(1,i-1), log(loc_off) + log(prob_stay_off) + DP_table(2,i-1)]);
%Compute prob. of the evidence. E.A.
%psi = [loc_on; loc_off];
obslik(:,i) = [loc_on; loc_off];
predict = Psi_t * alpha_t(:,i-1);
z(i) = sum(obslik(:,i) .* predict);
alpha_t(:,i) = [ loc_on * predict(1); loc_off * predict(2)] / z(i);
%obslik(:,i) = [loc_on; loc_off];
% if (round(sum(alpha_t(:,1))) ~= 1)
% display('Error');
% end
%% Compute log likelihood ration and evidenct -- E.A
% log_like_ratio_list(i) = log((p_y_on_1*p_t_on_1)/(p_y_off_1*p_t_off_1)); %E.A. store likelihood
% evidence(i) = evidence(i-1) + log(p_y_on_1*p_t_on_1*prob_stay_on + p_y_on_1*p_t_on_1*prob_stay_off + ...
% p_y_off_1*p_t_off_1*prob_leave_on + p_y_off_1*p_t_off_1*prob_stay_off); %E.A. log evidence 1..i
%%
else
[val, nn] = min(abs(distances(i) - range_dis));
loc_on = nexist_edge_on(nn);
loc_off = nexist_edge_off(nn);
[DP_table(1,i), aux_table(1,i)] = max([log(loc_on) + log(prob_stay_on) + DP_table(1,i-1), log(loc_on) + log(prob_leave_off) + DP_table(2,i-1)]);
[DP_table(2,i), aux_table(2,i)] = max([log(loc_off) + log(prob_leave_on) + DP_table(1,i-1), log(loc_off) + log(prob_stay_off) + DP_table(2,i-1)]);
%Compute prob. of the evidence. E.A.
obslik(:,i) = [loc_on; loc_off];
predict = Psi_t * alpha_t(:,i-1);
z(i) = sum(obslik(:,i) .* predict);
alpha_t(:,i) = [ loc_on * predict(1); loc_off * predict(2)] / z(i);
% if (round(sum(alpha_t(:,1))) ~= 1)
% display('Error');
% end
%% Compute log likelihood ration and evidence -- E.A
% log_like_ratio_list(i) = log(p_y_on_1/p_y_off_1); %E.A. store likelihood
% evidence(i) = evidence(i-1) + log(p_y_on_1*prob_stay_on + p_y_on_1*prob_leave_off + ...
% p_y_off_1*prob_leave_on + p_y_off_1*prob_stay_off); %E.A. log evidence 1..i
%%
end
end
T = size(DP_table,2);
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/mexfiles/hmmParameters.prj
================================================
falseoption.WorkingFolder.Projectoption.BuildFolder.Projecttruetruetruetrueoption.GlobalDataSyncMethod.SyncAlwaystrueoption.DynamicMemoryAllocation.Threshold65536200000option.FilePartitionMethod.MapMFileToCFiletruefalsefalsetruefalsefalseoption.VerificationMode.Noneoption.VerificationStatus.Passedfalse40000option.PreserveVariableNames.UserNamesoption.TargetLang.Ctrue102004000true64truetrueoption.ConstantInputs.CheckValuesfalseoption.WorkingFolder.Projectoption.BuildFolder.Projecttruetrueoption.DynamicMemoryAllocation.Threshold65536200000option.FilePartitionMethod.MapMFileToCFiletruefalsetruefalsefalsefalsetrueAutomatically locate an installed toolchainoption.target.TargetType.MatlabHostGenericMATLAB Host Computertrue81632646432646464option.HardwareEndianness.Littletruetrueoption.HardwareAtomicIntegerSize.Charoption.HardwareAtomicFloatSize.Noneoption.HardwareDivisionRounding.ZeroGenericMATLAB Host Computerfalse81632646432646464option.HardwareEndianness.Littletruetrueoption.HardwareAtomicIntegerSize.Charoption.HardwareAtomicFloatSize.Noneoption.HardwareDivisionRounding.Zero40000option.PreserveVariableNames.UserNamesoption.TargetLang.C102004000true64truetrueoption.WorkingFolder.Projectoption.BuildFolder.Projecttruefalseoption.objective.cgenerateCodedemo_start_v2option.UseGlobals.No${PROJECT_ROOT}/hmmParameters_mex.mexmaci64truetruetrue3530211709falsefalsetruecodegen/mex/hmmParameters4179426945codegen/mex/hmmParameters1281319812demo_start_v2<?xml version="1.0" encoding="UTF-8" standalone="yes"?><checksum><includedFiles><file>/Users/yimingqian/Dropbox/My Publications/CVPR 2017/code/LineSegDet2017/code/mexfiles/hmmParameters.m</file></includedFiles><value>1634473109</value></checksum><?xml version="1.0" encoding="UTF-8" standalone="yes"?><sourceModel><primarySourceFiles><file>/Users/yimingqian/Dropbox/My Publications/CVPR 2017/code/LineSegDet2017/code/mexfiles/hmmParameters.m</file></primarySourceFiles><fixedPointSourceFiles/><fixedPointSourceRegistered>false</fixedPointSourceRegistered><fixedPointSourceSelected>false</fixedPointSourceSelected></sourceModel>falsefalsetruehmmParameters_mexhmmParametersoption.target.artifact.mex${PROJECT_ROOT}/hmmParameters_mex.mexmaci64truefalseoption.FixedPointMode.None1640fimath('RoundingMethod', 'Floor', 'OverflowAction', 'Wrap', 'ProductMode', 'FullPrecision', 'MaxProductWordLength', 128, 'SumMode', 'FullPrecision', 'MaxSumWordLength', 128)option.FixedPointTypeSource.SimAndDerivedfalsefalsefalsefalsetruetruefalse_fixptfalsefalseoption.DefaultFixedPointSignedness.Automaticoption.FixedPointTypeProposalMode.ProposeFractionLengthsdouble1 x 40falsefalsedouble1 x 40falsefalsedouble1 x 181falsefalsedouble1 x 181falsefalsedouble:inf x 1truefalsedouble2 x 2falsefalsedouble2 x 1falsefalsedouble:inf x 1truefalsedouble:inf x 1truefalsedouble1 x 40falsefalsedouble1 x 40falsefalsedouble1 x 40falsefalsedouble1 x 1falsefalsedouble1 x 1falsefalsedouble1 x 1falsefalsedouble1 x 1falsefalsedouble1 x 1falsefalsedouble1 x 1falsefalsedouble:inf x 1truefalsedouble1 x 181falsefalse/Users/yimingqian/Dropbox/My Publications/CVPR 2017/code/LineSegDet2017/demo_start_v2.m/Users/yimingqian/Dropbox/My Publications/CVPR 2017/code/LineSegDet2017/code/mexfiles/hmmParameters_mex.mexmaci64/Applications/MATLAB_R2015b.apptruetruetruetruetruetruefalsefalsefalsefalsefalsefalse10.12.6falsetruemaci64true
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/mexfiles/mexRemoveVotes_v3_scale.c
================================================
/*
* Function: mexRemoveVote_v2
*
* Input: Kernel value matrices (regular and flipped), and dimensional
* arguments, along with the voting map
*
* Output: Updated voting map
*
* Author: Ron Tal
*
*
* Usage: Compiled and called through Matlab
*
*/
/* Include the following header files */
# include "mex.h"
#include "matrix.h"
#include
#include
#define PI 3.141
#define RMAX 400.0
/* Input Arguments */
extern mxArray *mxCreateSharedDataCopy(const mxArray *pr);
#define EDG_L prhs[0]
#define EDG_G prhs[1]
#define KERN prhs[2]
#define KERNF prhs[3]
#define R_RES prhs[4]
#define T_RES prhs[5]
#define PP prhs[6]
#define KMAX prhs[7]
#define MAP prhs[8]
#define SCALE prhs[9]
/* Output Arguments */
#define NMAP plhs[0]
/* STRUCTS */
typedef struct
{
int m;
int n;
} matPoint;
typedef struct
{
double r;
double th;
} houghPoint;
/* Global variables: */
mwSize mmap, nmap, m_edge_loc, n_edge_loc, m_edge_grad, n_edge_grad, m_kern, m_kern_f;
double* edge_loc, * edge_grad, * pp, r_res, th_res;
double* map, * scale;
mxArray* kernels, *kernels_flip, * k_max;
double *outMatrix;
int round_f (double val)
{
return floor(val + 0.5);
}
double round2frac(double val, double res)
{
double lrg = val/res;
double rnd = (double) round_f(lrg);
return rnd*res;
}
matPoint hough2mat(double r, double th, double rres, double thres)
{
matPoint val;
val.n = round_f((r + (double)RMAX**scale ) / rres + 1);
val.m = round_f(th / thres + 1);
return val;
}
houghPoint mat2hough(int m, int n, double rres, double thres)
{
houghPoint val;
val.th = round2frac(m*thres, thres);
val.r = round2frac(n*rres - (double)RMAX**scale, rres);
return val;
}
void houghRemoveVotes(matPoint center, mxArray * k, mxArray * k_f, mxArray * k_m)
{
/* Get Kernel properties */
int i;
int m = center.m;
int n = center.n;
int m_ker, n_max, m_max, n_lim_pos, n_lim_neg, m_lim_pos, m_lim_neg;
double * kernel;
double * kernel_flip;
double * k_mx;
double val;
m_ker = mxGetM(k);
kernel = mxGetPr(k);
kernel_flip = mxGetPr(k_f);
k_mx = mxGetPr(k_m);
n_max = k_mx[0];
m_max = k_mx[1];
n_lim_pos = n + (n_max-1)/2 - 1;
n_lim_neg = n - (n_max-1)/2 - 1;
m_lim_pos = m + (m_max-1)/2 - 1;
m_lim_neg = m - (m_max-1)/2 - 1;
/* Vote for each valid kernel value */
for (i = 0; i < m_ker; i++)
{
int m_on_map;
int n_on_map;
m_on_map = m_lim_neg + (int)kernel[m_ker + i] - 1;
n_on_map = n_lim_neg + (int)kernel[i] - 1;
/* Out of R bounds: do nothing */
if (n_on_map <= 0 ||n_on_map >= nmap) continue;
/* Handle wraparound in TH domain: to be added later: */
else if( m_on_map < 0)
{
m_on_map = mmap + m_on_map ;
n_on_map = nmap - n_on_map ;
if (n_on_map < 0 ||n_on_map >= nmap) continue;
if(map[(n_on_map )*mmap + m_on_map] - kernel[2*m_ker + (m_ker - i - 1)] >= 0)
map[(n_on_map )*mmap + m_on_map] -= kernel[2*m_ker + (m_ker - i - 1)];
}else if(m_on_map >= mmap)
{
m_on_map = m_on_map - mmap ;
n_on_map = nmap - n_on_map ;
if (map[(n_on_map )*mmap + m_on_map] - kernel[2*m_ker + (m_ker - i)] >= 0)
map[(n_on_map )*mmap + m_on_map] -= kernel[2*m_ker + (m_ker - i)];
}
/* No wraparound */
else
{
if(map[(n_on_map )*mmap + m_on_map] - kernel[2*m_ker + i] >= 0)
map[(n_on_map )*mmap + m_on_map] -= kernel[2*m_ker + i];
}
}
}
void loopThroughRemove()
{
int i = 0;
for (i = 0; i < m_edge_loc;i++ )
{
double r, mag;
double * kernel, * kernel_flip, * k_m, * m_l;
int r_th, ind;
matPoint center;
double x = (double)edge_loc[i] - (double)pp[0];
double y = (double)edge_loc[m_edge_loc + i] - (double)pp[1];
double th = edge_grad[i];
if (th == 4.0) continue;
if (th < 0) th += PI;
if (th > PI) th -= PI;
th = PI - th;
r = x * cos(th) + y * sin(th);
th = round2frac(th, th_res);
r = round2frac(r, r_res);
r_th = round_f(- x * sin(th) + y * cos(th));
if (r_th > (int)(400**scale)) r_th = (int)(400**scale); else if(r_th < - (int)(400**scale)) r_th = -(int)(400**scale);
if (r_th == 0)
{
ind = 0;
}
else
{
ind = r_th + (double)RMAX**scale;
}
kernel = mxGetCell(kernels, ind);
kernel_flip = mxGetCell(kernels_flip, ind);
k_m = mxGetCell(k_max, ind);
/* Center of kernel on voting map */
center = hough2mat(r, th, r_res, th_res);
houghRemoveVotes(center,kernel, kernel_flip, k_m);
}
}
void mexFunction ( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
/*
* Function: mexFunction
*
* Inputs: nrhs -> Integer representing the number of input arguments
* sent from Matlab.
*
* prhs[] -> An mxArray structure of size nrhs holding pointers
* to arguments sent from Matlab.
*
* nlhs -> Integer representing the number of output arguments
* requested from Matlab.
*
* plhs[] -> An mxArray structure of size nlhs holding pointers
* to the location of output arguments.
*
* Outputs: (void)
*
* Functionality: Analogous to a 'main' function. This function will
* be activated by default when Matlab calls this C
* function. Used to interface between C and Matlab,
* as to ensure output is in the location and format
* Matlab expecs.
*/
{
/* Declerations and initializations */
/* Check for proper number of arguments */
if (nrhs != 10) {
mexErrMsgTxt("Nine input arguments required.");
}
/* Get edge locations */
m_edge_loc = mxGetM(EDG_L);
n_edge_loc = mxGetN(EDG_L);
edge_loc = mxGetPr(EDG_L);
if (n_edge_loc != 2) {
mexErrMsgTxt("Wrong edge locations format.");
}
/* Get edge gradients */
m_edge_grad = mxGetM(EDG_G);
n_edge_grad = mxGetN(EDG_G);
if (n_edge_grad != 1) {
mexErrMsgTxt("Wrong edge gradient format.");
}
if (m_edge_loc != m_edge_grad) {
mexErrMsgTxt("Locations and gradients must correspond.");
}
edge_grad = mxGetPr(EDG_G);
/* Get pp */
pp = mxGetPr(PP);
scale=mxGetPr(SCALE);
r_res = mxGetScalar(R_RES);
th_res = mxGetScalar(T_RES);
/* Get map*/
nmap = (int)(800.0**scale/r_res) + 1;
mmap = (int)(3.141/th_res) + 1;
map = mxGetPr(MAP);
/* Get Kernels*/
m_kern = mxGetNumberOfElements(KERN);
m_kern_f = mxGetNumberOfElements(KERNF);
kernels = KERN;
kernels_flip = KERNF;
k_max = KMAX;
loopThroughRemove();
NMAP=mxCreateSharedDataCopy(MAP);
return;
}
/*end mexFunction*/
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/mexfiles/mexVoteEdges_v3_scale.c
================================================
/*
* Function: mexVoteEdges_v2
*
* Input: Kernel value matrices (regular and flipped), and dimensional
* arguments, along with the voting map
*
* Output: Updated voting map
*
* Author: Ron Tal
*
*
* Usage: Compiled and called through Matlab
*
*/
/* Include the following header files */
# include "mex.h"
#include "matrix.h"
#include
#include
#define PI 3.141
#define RMAX 400.0
/* Input Arguments */
#define EDG_L prhs[0]
#define EDG_G prhs[1]
#define KERN prhs[2]
#define KERNF prhs[3]
#define R_RES prhs[4]
#define T_RES prhs[5]
#define PP prhs[6]
#define KMAX prhs[7]
#define SCALE prhs[8]
/* Output Arguments */
#define NMAP plhs[0]
/* STRUCTS */
typedef struct
{
int m;
int n;
} matPoint;
typedef struct
{
double r;
double th;
} houghPoint;
/* Global variables: */
mwSize mmap, nmap, m_edge_loc, n_edge_loc, m_edge_grad, n_edge_grad, m_kern, m_kern_f;
double* edge_loc, * edge_grad, * pp, r_res, th_res;
double* map,*scale;
mxArray* kernels, *kernels_flip, * k_max;
int round_f (double val)
{
return floor(val + 0.5);
}
double round2frac(double val, double res)
{
double lrg = val/res;
double rnd = (double) round_f(lrg);
return rnd*res;
}
matPoint hough2mat(double r, double th, double rres, double thres)
{
matPoint val;
val.n = round_f((r + (double)RMAX**scale ) / rres + 1);
val.m = round_f(th / thres + 1);
return val;
}
houghPoint mat2hough(int m, int n, double rres, double thres)
{
houghPoint val;
val.th = round2frac(m*thres, thres);
val.r = round2frac(n*rres - (double)RMAX**scale, rres);
return val;
}
void houghVoteMap(matPoint center, mxArray * k, mxArray * k_f, mxArray * k_m, int x, int y, int r_th, int xd, int yd)
{
/* Get Kernel properties */
int i;
int m = center.m;
int n = center.n;
int m_ker, n_max, m_max, n_lim_pos, n_lim_neg, m_lim_pos, m_lim_neg, n_ker;
double * kernel;
double * kernel_flip;
double * k_mx;
double val;
double kvalue;
int xx, yy;
m_ker = mxGetM(k);
n_ker = mxGetN(k);
kernel = mxGetPr(k);
kernel_flip = mxGetPr(k_f);
k_mx = mxGetPr(k_m);
n_max = k_mx[0];
m_max = k_mx[1];
n_lim_pos = n + (n_max-1)/2 - 1;
n_lim_neg = n - (n_max-1)/2 - 1;
m_lim_pos = m + (m_max-1)/2 - 1;
m_lim_neg = m - (m_max-1)/2 - 1;
xx = round_f(x + (double)pp[0]);
yy = round_f(y + (double)pp[1]);
/* Vote for each valid kernel value */
for (i = 0; i < m_ker; i++)
{
int m_on_map;
int n_on_map;
m_on_map = m_lim_neg + (int)kernel[m_ker + i] - 1;
n_on_map = n_lim_neg + (int)kernel[i] - 1;
/* Out of R bounds: do nothing */
if (n_on_map <= 0 ||n_on_map >= nmap) continue;
/* Handle wraparound in TH domain: to be added later: */
else if( m_on_map < 0)
{
m_on_map = mmap + m_on_map ;
n_on_map = nmap - n_on_map ;
if (n_on_map < 0 ||n_on_map >= nmap) continue;
kvalue = kernel[2*m_ker + (m_ker - i - 1)];
map[(n_on_map )*mmap + m_on_map] += kvalue;
}else if(m_on_map >= mmap)
{
m_on_map = m_on_map - mmap ;
n_on_map = nmap - n_on_map ;
kvalue = kernel[2*m_ker + (m_ker - i)];
map[(n_on_map )*mmap + m_on_map] += kvalue;
}
/* No wraparound */
else
{
kvalue = kernel[2*m_ker + i];
map[(n_on_map )*mmap + m_on_map] += kvalue;
}
}
}
void loopThrough()
{
int xx, yy;
int i = 0;
for (i = 0; i < m_edge_loc;i++ )
{
double r, th;
double * kernel, * kernel_flip, * k_m, * m_l;
int r_th;
matPoint center;
int xx, yy, ind;
double x = (double)edge_loc[i] - (double)pp[0];
double y = (double)edge_loc[m_edge_loc + i] - (double)pp[1];
int xd = round_f((double)edge_loc[i]);
int yd = round_f((double)edge_loc[m_edge_loc + i]);
th = edge_grad[i];
if (th == 4.0) continue;
if (th < 0) th += PI;
if (th > PI) th -= PI;
th = PI - th;
r = x * cos(th) + y * sin(th);
th = round2frac(th, th_res);
r = round2frac(r, r_res);
r_th = round_f(- x * sin(th) + y * cos(th));
if (r_th > (int)(400**scale)) r_th = (int)(400**scale); else if(r_th < - (int)(400**scale)) r_th = -(int)(400**scale);
if (r_th == 0)
{
ind = 0;
}
else
{
ind = r_th + (double)RMAX**scale;
}
kernel = mxGetCell(kernels, ind);
kernel_flip = mxGetCell(kernels_flip, ind);
k_m = mxGetCell(k_max, ind);
/* Center of kernel on voting map */
center = hough2mat(r, th, r_res, th_res);
houghVoteMap(center,kernel, kernel_flip, k_m, x, y, r_th, xd, yd);
}
}
void mexFunction ( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )
/*
* Function: mexFunction
*
* Inputs: nrhs -> Integer representing the number of input arguments
* sent from Matlab.
*
* prhs[] -> An mxArray structure of size nrhs holding pointers
* to arguments sent from Matlab.
*
* nlhs -> Integer representing the number of output arguments
* requested from Matlab.
*
* plhs[] -> An mxArray structure of size nlhs holding pointers
* to the location of output arguments.
*
* Outputs: (void)
*
* Functionality: Analogous to a 'main' function. This function will
* be activated by default when Matlab calls this C
* function. Used to interface between C and Matlab,
* as to ensure output is in the location and format
* Matlab expecs.
*/
{
/* Check for proper number of arguments */
if (nrhs != 9) {
mexErrMsgTxt("Eight input arguments required.");
}
printf("Inside C function_v2\n");
/* Get edge locations */
m_edge_loc = mxGetM(EDG_L);
n_edge_loc = mxGetN(EDG_L);
edge_loc = mxGetPr(EDG_L);
if (n_edge_loc != 2) {
mexErrMsgTxt("Wrong edge locations format.");
}
/* Get edge gradients */
m_edge_grad = mxGetM(EDG_G);
n_edge_grad = mxGetN(EDG_G);
if (n_edge_grad != 1) {
mexErrMsgTxt("Wrong edge gradient format.");
}
if (m_edge_loc != m_edge_grad) {
mexErrMsgTxt("Locations and gradients must correspond.");
}
edge_grad = mxGetPr(EDG_G);
/* Get pp */
pp = mxGetPr(PP);
r_res = mxGetScalar(R_RES);
th_res = mxGetScalar(T_RES);
scale=mxGetPr(SCALE);
/* Get map*/
nmap = (int)(800**scale/r_res) + 1;
mmap = (int)(3.141/th_res) + 1;
NMAP = mxCreateDoubleMatrix(mmap, nmap, mxREAL);
map = mxGetPr(NMAP);
/* Get Kernels*/
m_kern = mxGetNumberOfElements(KERN);
m_kern_f = mxGetNumberOfElements(KERNF);
kernels = KERN;
kernels_flip = KERNF;
k_max = KMAX;
loopThrough();
return;
}
/*end mexFunction*/
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/mexfiles/returnLines.m
================================================
function [begin_points,end_points,lslength,startpositions,endpositions]=returnLines(points_proj,final_labels)
count = 0;
on_seg = false;
begin_pointsT = zeros(20,2);
counter_begin_points=1;
end_pointsT = zeros(20,2);
counter_end_points=1;
lslengthT = zeros(20,1);
counter_lslength=1;
startpos = 0;
startpositionsT = zeros(20,1);
counter_startpositions=1;
endpositionsT = zeros(20,1);
counter_endpositions=1;
for i = 1:length(final_labels);
if ~on_seg
if final_labels(i) == 1
begin_pointsT(counter_begin_points,:)=points_proj(i,:);
counter_begin_points=counter_begin_points+1;
startpositionsT(counter_startpositions) = i;
counter_startpositions=counter_startpositions+1;
startpos = i;
on_seg = true;
count = count + 1;
end
elseif i == length(final_labels)
end_pointsT(counter_end_points,:) =points_proj(i,:);
counter_end_points=counter_end_points+1;
endpositionsT(counter_endpositions) = i;
counter_endpositions=counter_endpositions+1;
lslengthT(counter_lslength) = i-startpos;
counter_lslength=counter_lslength+1;
on_seg = false;
elseif final_labels(i) == 0
end_pointsT(counter_end_points,:) = points_proj(i-1,:);
counter_end_points=counter_end_points+1;
endpositionsT(counter_endpositions) = i-1;
counter_endpositions=counter_endpositions+1;
lslengthT(counter_lslength) = i-startpos;
counter_lslength=counter_lslength+1;
on_seg = false;
end
end
begin_points = begin_pointsT(1:counter_begin_points-1,:);
end_points =end_pointsT(1:counter_end_points-1,:);
lslength =lslengthT(1:counter_lslength-1);
startpositions =startpositionsT(1:counter_startpositions);
endpositions =endpositionsT(1:counter_endpositions-1);
end
================================================
FILE: thirdparty/MCMLSD/MCMLSD/code/mexfiles/returnLines.prj
================================================