tabula-extractor 0.0.1-java → 0.5.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -0
- data/Gemfile +0 -3
- data/README.md +19 -2
- data/Rakefile +4 -5
- data/bin/tabula +27 -7
- data/ext/COPYING +661 -0
- data/ext/Makefile.OSX +15 -0
- data/ext/Makefile.defaults +9 -0
- data/ext/Makefile.linux32 +11 -0
- data/ext/Makefile.linux64 +12 -0
- data/ext/Makefile.mingw +10 -0
- data/ext/liblsd-linux32.so +0 -0
- data/ext/liblsd-linux64.so +0 -0
- data/ext/liblsd.def +3 -0
- data/ext/liblsd.dll +0 -0
- data/ext/liblsd.dylib +0 -0
- data/ext/lsd.c +2270 -0
- data/ext/lsd.h +283 -0
- data/lib/tabula.rb +6 -0
- data/lib/tabula/core_ext.rb +21 -0
- data/lib/tabula/entities.rb +141 -20
- data/lib/tabula/line_segment_detector.rb +99 -0
- data/lib/tabula/pdf_dump.rb +10 -8
- data/lib/tabula/pdf_render.rb +64 -0
- data/lib/tabula/table_extractor.rb +19 -20
- data/lib/tabula/version.rb +1 -1
- data/lib/tabula/writers.rb +1 -1
- data/tabula-extractor.gemspec +3 -2
- data/target/{pdfbox-app-1.8.0.jar → pdfbox-app-2.0.0-SNAPSHOT.jar} +0 -0
- data/test/tests.rb +7 -6
- metadata +22 -5
data/ext/Makefile.OSX
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
include Makefile.defaults
|
2
|
+
|
3
|
+
lib: lib$(NAME).$(VERSION).dylib
|
4
|
+
|
5
|
+
lib$(NAME).$(VERSION).dylib: $(NAME).o
|
6
|
+
$(CC) -dynamiclib -lm -o lib$(NAME).dylib $^
|
7
|
+
|
8
|
+
clean:
|
9
|
+
$(RM) *.o
|
10
|
+
|
11
|
+
$(NAME)_test: lib$(NAME).$(VERSION).dylib
|
12
|
+
$(CC) lsd_call_example.c -o $@ -L. -l$(NAME)
|
13
|
+
|
14
|
+
test: $(NAME)_test
|
15
|
+
LD_LIBRARY_PATH=. ./$(NAME)_test
|
@@ -0,0 +1,11 @@
|
|
1
|
+
include Makefile.defaults
|
2
|
+
|
3
|
+
# link statically with musl-libc
|
4
|
+
CC = /home/manuel/tabula-build/musl-32/bin/musl-gcc
|
5
|
+
CFLAGS := -fPIC -Wall -Werror
|
6
|
+
|
7
|
+
lib: lib$(NAME).$(VERSION).so
|
8
|
+
|
9
|
+
lib$(NAME).$(VERSION).so: $(NAME).o
|
10
|
+
$(CC) -shared -static -o lib$(NAME)-linux32.so $^
|
11
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# to compile a x86_64 lib in an ubuntu i386 box
|
2
|
+
include Makefile.defaults
|
3
|
+
|
4
|
+
# link statically with musl-libc
|
5
|
+
CC = /home/manuel/tabula-build/musl-64/bin/musl-gcc
|
6
|
+
CFLAGS := -fPIC -Wall -Werror -m64
|
7
|
+
|
8
|
+
lib: lib$(NAME).$(VERSION).so
|
9
|
+
|
10
|
+
lib$(NAME).$(VERSION).so: $(NAME).o
|
11
|
+
@LDEMULATION=elf_x86_64 $(CC) -m64 -shared -static -o lib$(NAME)-linux64.so $^
|
12
|
+
|
data/ext/Makefile.mingw
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
include Makefile.defaults
|
2
|
+
|
3
|
+
#CC = /usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin/i586-mingw32-gcc
|
4
|
+
CC = /usr/bin/i686-w64-mingw32-gcc-4.6
|
5
|
+
CFLAGS := -Wall -Werror
|
6
|
+
|
7
|
+
lib: lib$(NAME).$(VERSION).dll
|
8
|
+
|
9
|
+
lib$(NAME).$(VERSION).dll: $(NAME).o
|
10
|
+
$(CC) -shared -o lib$(NAME).dll liblsd.def $^
|
Binary file
|
Binary file
|
data/ext/liblsd.def
ADDED
data/ext/liblsd.dll
ADDED
Binary file
|
data/ext/liblsd.dylib
ADDED
Binary file
|
data/ext/lsd.c
ADDED
@@ -0,0 +1,2270 @@
|
|
1
|
+
/*----------------------------------------------------------------------------
|
2
|
+
|
3
|
+
LSD - Line Segment Detector on digital images
|
4
|
+
|
5
|
+
This code is part of the following publication and was subject
|
6
|
+
to peer review:
|
7
|
+
|
8
|
+
"LSD: a Line Segment Detector" by Rafael Grompone von Gioi,
|
9
|
+
Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall,
|
10
|
+
Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd
|
11
|
+
http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd
|
12
|
+
|
13
|
+
Copyright (c) 2007-2011 rafael grompone von gioi <grompone@gmail.com>
|
14
|
+
|
15
|
+
This program is free software: you can redistribute it and/or modify
|
16
|
+
it under the terms of the GNU Affero General Public License as
|
17
|
+
published by the Free Software Foundation, either version 3 of the
|
18
|
+
License, or (at your option) any later version.
|
19
|
+
|
20
|
+
This program is distributed in the hope that it will be useful,
|
21
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
22
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
23
|
+
GNU Affero General Public License for more details.
|
24
|
+
|
25
|
+
You should have received a copy of the GNU Affero General Public License
|
26
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
27
|
+
|
28
|
+
Additional permission under GNU GPL version 3 section 7
|
29
|
+
|
30
|
+
If you modify this Program, or any covered work, by linking or
|
31
|
+
combining it with Tabula (or a modified version of that library),
|
32
|
+
containing parts covered by the terms of "MIT License", the
|
33
|
+
licensors of this Program grant you additional permission to convey
|
34
|
+
the resulting work. Corresponding Source for a non-source form of
|
35
|
+
such a combination shall include the source code for the parts of
|
36
|
+
Tabula used as well as that of the covered work.
|
37
|
+
|
38
|
+
|
39
|
+
----------------------------------------------------------------------------*/
|
40
|
+
|
41
|
+
/*----------------------------------------------------------------------------*/
|
42
|
+
/** @file lsd.c
|
43
|
+
LSD module code
|
44
|
+
@author rafael grompone von gioi <grompone@gmail.com>
|
45
|
+
*/
|
46
|
+
/*----------------------------------------------------------------------------*/
|
47
|
+
|
48
|
+
/*----------------------------------------------------------------------------*/
|
49
|
+
/** @mainpage LSD code documentation
|
50
|
+
|
51
|
+
This is an implementation of the Line Segment Detector described
|
52
|
+
in the paper:
|
53
|
+
|
54
|
+
"LSD: A Fast Line Segment Detector with a False Detection Control"
|
55
|
+
by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
|
56
|
+
and Gregory Randall, IEEE Transactions on Pattern Analysis and
|
57
|
+
Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010.
|
58
|
+
|
59
|
+
and in more details in the CMLA Technical Report:
|
60
|
+
|
61
|
+
"LSD: A Line Segment Detector, Technical Report",
|
62
|
+
by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
|
63
|
+
Gregory Randall, CMLA, ENS Cachan, 2010.
|
64
|
+
|
65
|
+
The version implemented here includes some further improvements
|
66
|
+
described in the following publication, of which this code is part:
|
67
|
+
|
68
|
+
"LSD: a Line Segment Detector" by Rafael Grompone von Gioi,
|
69
|
+
Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall,
|
70
|
+
Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd
|
71
|
+
http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd
|
72
|
+
|
73
|
+
The module's main function is lsd().
|
74
|
+
|
75
|
+
The source code is contained in two files: lsd.h and lsd.c.
|
76
|
+
|
77
|
+
HISTORY:
|
78
|
+
- version 1.6 - nov 2011:
|
79
|
+
- changes in the interface,
|
80
|
+
- max_grad parameter removed,
|
81
|
+
- the factor 11 was added to the number of test
|
82
|
+
to consider the different precision values
|
83
|
+
tested,
|
84
|
+
- a minor bug corrected in the gradient sorting
|
85
|
+
code,
|
86
|
+
- the algorithm now also returns p and log_nfa
|
87
|
+
for each detection,
|
88
|
+
- a minor bug was corrected in the image scaling,
|
89
|
+
- the angle comparison in "isaligned" changed
|
90
|
+
from < to <=,
|
91
|
+
- "eps" variable renamed "log_eps",
|
92
|
+
- "lsd_scale_region" interface was added,
|
93
|
+
- minor changes to comments.
|
94
|
+
- version 1.5 - dec 2010: Changes in 'refine', -W option added,
|
95
|
+
and more comments added.
|
96
|
+
- version 1.4 - jul 2010: lsd_scale interface added and doxygen doc.
|
97
|
+
- version 1.3 - feb 2010: Multiple bug correction and improved code.
|
98
|
+
- version 1.2 - dec 2009: First full Ansi C Language version.
|
99
|
+
- version 1.1 - sep 2009: Systematic subsampling to scale 0.8 and
|
100
|
+
correction to partially handle "angle problem".
|
101
|
+
- version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language
|
102
|
+
version.
|
103
|
+
|
104
|
+
@author rafael grompone von gioi <grompone@gmail.com>
|
105
|
+
*/
|
106
|
+
/*----------------------------------------------------------------------------*/
|
107
|
+
|
108
|
+
#include <stdio.h>
|
109
|
+
#include <stdlib.h>
|
110
|
+
#include <math.h>
|
111
|
+
#include <limits.h>
|
112
|
+
#include <float.h>
|
113
|
+
#include "lsd.h"
|
114
|
+
|
115
|
+
/** ln(10) */
|
116
|
+
#ifndef M_LN10
|
117
|
+
#define M_LN10 2.30258509299404568402
|
118
|
+
#endif /* !M_LN10 */
|
119
|
+
|
120
|
+
/** PI */
|
121
|
+
#ifndef M_PI
|
122
|
+
#define M_PI 3.14159265358979323846
|
123
|
+
#endif /* !M_PI */
|
124
|
+
|
125
|
+
#ifndef FALSE
|
126
|
+
#define FALSE 0
|
127
|
+
#endif /* !FALSE */
|
128
|
+
|
129
|
+
#ifndef TRUE
|
130
|
+
#define TRUE 1
|
131
|
+
#endif /* !TRUE */
|
132
|
+
|
133
|
+
/** Label for pixels with undefined gradient. */
|
134
|
+
#define NOTDEF -1024.0
|
135
|
+
|
136
|
+
/** 3/2 pi */
|
137
|
+
#define M_3_2_PI 4.71238898038
|
138
|
+
|
139
|
+
/** 2 pi */
|
140
|
+
#define M_2__PI 6.28318530718
|
141
|
+
|
142
|
+
/** Label for pixels not used in yet. */
|
143
|
+
#define NOTUSED 0
|
144
|
+
|
145
|
+
/** Label for pixels already used in detection. */
|
146
|
+
#define USED 1
|
147
|
+
|
148
|
+
/*----------------------------------------------------------------------------*/
|
149
|
+
/** Chained list of coordinates.
|
150
|
+
*/
|
151
|
+
struct coorlist
|
152
|
+
{
|
153
|
+
int x,y;
|
154
|
+
struct coorlist * next;
|
155
|
+
};
|
156
|
+
|
157
|
+
/*----------------------------------------------------------------------------*/
|
158
|
+
/** A point (or pixel).
|
159
|
+
*/
|
160
|
+
struct point {int x,y;};
|
161
|
+
|
162
|
+
|
163
|
+
/*----------------------------------------------------------------------------*/
|
164
|
+
/*------------------------- Miscellaneous functions --------------------------*/
|
165
|
+
/*----------------------------------------------------------------------------*/
|
166
|
+
|
167
|
+
/*----------------------------------------------------------------------------*/
|
168
|
+
/** Fatal error, print a message to standard-error output and exit.
|
169
|
+
*/
|
170
|
+
static void error(char * msg)
|
171
|
+
{
|
172
|
+
fprintf(stderr,"LSD Error: %s\n",msg);
|
173
|
+
exit(EXIT_FAILURE);
|
174
|
+
}
|
175
|
+
|
176
|
+
/*----------------------------------------------------------------------------*/
|
177
|
+
/** Doubles relative error factor
|
178
|
+
*/
|
179
|
+
#define RELATIVE_ERROR_FACTOR 100.0
|
180
|
+
|
181
|
+
/*----------------------------------------------------------------------------*/
|
182
|
+
/** Compare doubles by relative error.
|
183
|
+
|
184
|
+
The resulting rounding error after floating point computations
|
185
|
+
depend on the specific operations done. The same number computed by
|
186
|
+
different algorithms could present different rounding errors. For a
|
187
|
+
useful comparison, an estimation of the relative rounding error
|
188
|
+
should be considered and compared to a factor times EPS. The factor
|
189
|
+
should be related to the cumulated rounding error in the chain of
|
190
|
+
computation. Here, as a simplification, a fixed factor is used.
|
191
|
+
*/
|
192
|
+
static int double_equal(double a, double b)
|
193
|
+
{
|
194
|
+
double abs_diff,aa,bb,abs_max;
|
195
|
+
|
196
|
+
/* trivial case */
|
197
|
+
if( a == b ) return TRUE;
|
198
|
+
|
199
|
+
abs_diff = fabs(a-b);
|
200
|
+
aa = fabs(a);
|
201
|
+
bb = fabs(b);
|
202
|
+
abs_max = aa > bb ? aa : bb;
|
203
|
+
|
204
|
+
/* DBL_MIN is the smallest normalized number, thus, the smallest
|
205
|
+
number whose relative error is bounded by DBL_EPSILON. For
|
206
|
+
smaller numbers, the same quantization steps as for DBL_MIN
|
207
|
+
are used. Then, for smaller numbers, a meaningful "relative"
|
208
|
+
error should be computed by dividing the difference by DBL_MIN. */
|
209
|
+
if( abs_max < DBL_MIN ) abs_max = DBL_MIN;
|
210
|
+
|
211
|
+
/* equal if relative error <= factor x eps */
|
212
|
+
return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
|
213
|
+
}
|
214
|
+
|
215
|
+
/*----------------------------------------------------------------------------*/
|
216
|
+
/** Computes Euclidean distance between point (x1,y1) and point (x2,y2).
|
217
|
+
*/
|
218
|
+
static double dist(double x1, double y1, double x2, double y2)
|
219
|
+
{
|
220
|
+
return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
|
221
|
+
}
|
222
|
+
|
223
|
+
|
224
|
+
/*----------------------------------------------------------------------------*/
|
225
|
+
/*----------------------- 'list of n-tuple' data type ------------------------*/
|
226
|
+
/*----------------------------------------------------------------------------*/
|
227
|
+
|
228
|
+
/*----------------------------------------------------------------------------*/
|
229
|
+
/** 'list of n-tuple' data type
|
230
|
+
|
231
|
+
The i-th component of the j-th n-tuple of an n-tuple list 'ntl'
|
232
|
+
is accessed with:
|
233
|
+
|
234
|
+
ntl->values[ i + j * ntl->dim ]
|
235
|
+
|
236
|
+
The dimension of the n-tuple (n) is:
|
237
|
+
|
238
|
+
ntl->dim
|
239
|
+
|
240
|
+
The number of n-tuples in the list is:
|
241
|
+
|
242
|
+
ntl->size
|
243
|
+
|
244
|
+
The maximum number of n-tuples that can be stored in the
|
245
|
+
list with the allocated memory at a given time is given by:
|
246
|
+
|
247
|
+
ntl->max_size
|
248
|
+
*/
|
249
|
+
typedef struct ntuple_list_s
|
250
|
+
{
|
251
|
+
unsigned int size;
|
252
|
+
unsigned int max_size;
|
253
|
+
unsigned int dim;
|
254
|
+
double * values;
|
255
|
+
} * ntuple_list;
|
256
|
+
|
257
|
+
/*----------------------------------------------------------------------------*/
|
258
|
+
/** Free memory used in n-tuple 'in'.
|
259
|
+
*/
|
260
|
+
static void free_ntuple_list(ntuple_list in)
|
261
|
+
{
|
262
|
+
if( in == NULL || in->values == NULL )
|
263
|
+
error("free_ntuple_list: invalid n-tuple input.");
|
264
|
+
free( (void *) in->values );
|
265
|
+
free( (void *) in );
|
266
|
+
}
|
267
|
+
|
268
|
+
/*----------------------------------------------------------------------------*/
|
269
|
+
/** Create an n-tuple list and allocate memory for one element.
|
270
|
+
@param dim the dimension (n) of the n-tuple.
|
271
|
+
*/
|
272
|
+
static ntuple_list new_ntuple_list(unsigned int dim)
|
273
|
+
{
|
274
|
+
ntuple_list n_tuple;
|
275
|
+
|
276
|
+
/* check parameters */
|
277
|
+
if( dim == 0 ) error("new_ntuple_list: 'dim' must be positive.");
|
278
|
+
|
279
|
+
/* get memory for list structure */
|
280
|
+
n_tuple = (ntuple_list) malloc( sizeof(struct ntuple_list_s) );
|
281
|
+
if( n_tuple == NULL ) error("not enough memory.");
|
282
|
+
|
283
|
+
/* initialize list */
|
284
|
+
n_tuple->size = 0;
|
285
|
+
n_tuple->max_size = 1;
|
286
|
+
n_tuple->dim = dim;
|
287
|
+
|
288
|
+
/* get memory for tuples */
|
289
|
+
n_tuple->values = (double *) malloc( dim*n_tuple->max_size * sizeof(double) );
|
290
|
+
if( n_tuple->values == NULL ) error("not enough memory.");
|
291
|
+
|
292
|
+
return n_tuple;
|
293
|
+
}
|
294
|
+
|
295
|
+
/*----------------------------------------------------------------------------*/
|
296
|
+
/** Enlarge the allocated memory of an n-tuple list.
|
297
|
+
*/
|
298
|
+
static void enlarge_ntuple_list(ntuple_list n_tuple)
|
299
|
+
{
|
300
|
+
/* check parameters */
|
301
|
+
if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size == 0 )
|
302
|
+
error("enlarge_ntuple_list: invalid n-tuple.");
|
303
|
+
|
304
|
+
/* duplicate number of tuples */
|
305
|
+
n_tuple->max_size *= 2;
|
306
|
+
|
307
|
+
/* realloc memory */
|
308
|
+
n_tuple->values = (double *) realloc( (void *) n_tuple->values,
|
309
|
+
n_tuple->dim * n_tuple->max_size * sizeof(double) );
|
310
|
+
if( n_tuple->values == NULL ) error("not enough memory.");
|
311
|
+
}
|
312
|
+
|
313
|
+
/*----------------------------------------------------------------------------*/
|
314
|
+
/** Add a 7-tuple to an n-tuple list.
|
315
|
+
*/
|
316
|
+
static void add_7tuple( ntuple_list out, double v1, double v2, double v3,
|
317
|
+
double v4, double v5, double v6, double v7 )
|
318
|
+
{
|
319
|
+
/* check parameters */
|
320
|
+
if( out == NULL ) error("add_7tuple: invalid n-tuple input.");
|
321
|
+
if( out->dim != 7 ) error("add_7tuple: the n-tuple must be a 7-tuple.");
|
322
|
+
|
323
|
+
/* if needed, alloc more tuples to 'out' */
|
324
|
+
if( out->size == out->max_size ) enlarge_ntuple_list(out);
|
325
|
+
if( out->values == NULL ) error("add_7tuple: invalid n-tuple input.");
|
326
|
+
|
327
|
+
/* add new 7-tuple */
|
328
|
+
out->values[ out->size * out->dim + 0 ] = v1;
|
329
|
+
out->values[ out->size * out->dim + 1 ] = v2;
|
330
|
+
out->values[ out->size * out->dim + 2 ] = v3;
|
331
|
+
out->values[ out->size * out->dim + 3 ] = v4;
|
332
|
+
out->values[ out->size * out->dim + 4 ] = v5;
|
333
|
+
out->values[ out->size * out->dim + 5 ] = v6;
|
334
|
+
out->values[ out->size * out->dim + 6 ] = v7;
|
335
|
+
|
336
|
+
/* update number of tuples counter */
|
337
|
+
out->size++;
|
338
|
+
}
|
339
|
+
|
340
|
+
|
341
|
+
/*----------------------------------------------------------------------------*/
|
342
|
+
/*----------------------------- Image Data Types -----------------------------*/
|
343
|
+
/*----------------------------------------------------------------------------*/
|
344
|
+
|
345
|
+
/*----------------------------------------------------------------------------*/
|
346
|
+
/** char image data type
|
347
|
+
|
348
|
+
The pixel value at (x,y) is accessed by:
|
349
|
+
|
350
|
+
image->data[ x + y * image->xsize ]
|
351
|
+
|
352
|
+
with x and y integer.
|
353
|
+
*/
|
354
|
+
typedef struct image_char_s
|
355
|
+
{
|
356
|
+
unsigned char * data;
|
357
|
+
unsigned int xsize,ysize;
|
358
|
+
} * image_char;
|
359
|
+
|
360
|
+
/*----------------------------------------------------------------------------*/
|
361
|
+
/** Free memory used in image_char 'i'.
|
362
|
+
*/
|
363
|
+
static void free_image_char(image_char i)
|
364
|
+
{
|
365
|
+
if( i == NULL || i->data == NULL )
|
366
|
+
error("free_image_char: invalid input image.");
|
367
|
+
free( (void *) i->data );
|
368
|
+
free( (void *) i );
|
369
|
+
}
|
370
|
+
|
371
|
+
/*----------------------------------------------------------------------------*/
|
372
|
+
/** Create a new image_char of size 'xsize' times 'ysize'.
|
373
|
+
*/
|
374
|
+
static image_char new_image_char(unsigned int xsize, unsigned int ysize)
|
375
|
+
{
|
376
|
+
image_char image;
|
377
|
+
|
378
|
+
/* check parameters */
|
379
|
+
if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size.");
|
380
|
+
|
381
|
+
/* get memory */
|
382
|
+
image = (image_char) malloc( sizeof(struct image_char_s) );
|
383
|
+
if( image == NULL ) error("not enough memory.");
|
384
|
+
image->data = (unsigned char *) calloc( (size_t) (xsize*ysize),
|
385
|
+
sizeof(unsigned char) );
|
386
|
+
if( image->data == NULL ) error("not enough memory.");
|
387
|
+
|
388
|
+
/* set image size */
|
389
|
+
image->xsize = xsize;
|
390
|
+
image->ysize = ysize;
|
391
|
+
|
392
|
+
return image;
|
393
|
+
}
|
394
|
+
|
395
|
+
/*----------------------------------------------------------------------------*/
|
396
|
+
/** Create a new image_char of size 'xsize' times 'ysize',
|
397
|
+
initialized to the value 'fill_value'.
|
398
|
+
*/
|
399
|
+
static image_char new_image_char_ini( unsigned int xsize, unsigned int ysize,
|
400
|
+
unsigned char fill_value )
|
401
|
+
{
|
402
|
+
image_char image = new_image_char(xsize,ysize); /* create image */
|
403
|
+
unsigned int N = xsize*ysize;
|
404
|
+
unsigned int i;
|
405
|
+
|
406
|
+
/* check parameters */
|
407
|
+
if( image == NULL || image->data == NULL )
|
408
|
+
error("new_image_char_ini: invalid image.");
|
409
|
+
|
410
|
+
/* initialize */
|
411
|
+
for(i=0; i<N; i++) image->data[i] = fill_value;
|
412
|
+
|
413
|
+
return image;
|
414
|
+
}
|
415
|
+
|
416
|
+
/*----------------------------------------------------------------------------*/
|
417
|
+
/** int image data type
|
418
|
+
|
419
|
+
The pixel value at (x,y) is accessed by:
|
420
|
+
|
421
|
+
image->data[ x + y * image->xsize ]
|
422
|
+
|
423
|
+
with x and y integer.
|
424
|
+
*/
|
425
|
+
typedef struct image_int_s
|
426
|
+
{
|
427
|
+
int * data;
|
428
|
+
unsigned int xsize,ysize;
|
429
|
+
} * image_int;
|
430
|
+
|
431
|
+
/*----------------------------------------------------------------------------*/
|
432
|
+
/** Create a new image_int of size 'xsize' times 'ysize'.
|
433
|
+
*/
|
434
|
+
static image_int new_image_int(unsigned int xsize, unsigned int ysize)
|
435
|
+
{
|
436
|
+
image_int image;
|
437
|
+
|
438
|
+
/* check parameters */
|
439
|
+
if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size.");
|
440
|
+
|
441
|
+
/* get memory */
|
442
|
+
image = (image_int) malloc( sizeof(struct image_int_s) );
|
443
|
+
if( image == NULL ) error("not enough memory.");
|
444
|
+
image->data = (int *) calloc( (size_t) (xsize*ysize), sizeof(int) );
|
445
|
+
if( image->data == NULL ) error("not enough memory.");
|
446
|
+
|
447
|
+
/* set image size */
|
448
|
+
image->xsize = xsize;
|
449
|
+
image->ysize = ysize;
|
450
|
+
|
451
|
+
return image;
|
452
|
+
}
|
453
|
+
|
454
|
+
/*----------------------------------------------------------------------------*/
|
455
|
+
/** Create a new image_int of size 'xsize' times 'ysize',
|
456
|
+
initialized to the value 'fill_value'.
|
457
|
+
*/
|
458
|
+
static image_int new_image_int_ini( unsigned int xsize, unsigned int ysize,
|
459
|
+
int fill_value )
|
460
|
+
{
|
461
|
+
image_int image = new_image_int(xsize,ysize); /* create image */
|
462
|
+
unsigned int N = xsize*ysize;
|
463
|
+
unsigned int i;
|
464
|
+
|
465
|
+
/* initialize */
|
466
|
+
for(i=0; i<N; i++) image->data[i] = fill_value;
|
467
|
+
|
468
|
+
return image;
|
469
|
+
}
|
470
|
+
|
471
|
+
/*----------------------------------------------------------------------------*/
|
472
|
+
/** double image data type
|
473
|
+
|
474
|
+
The pixel value at (x,y) is accessed by:
|
475
|
+
|
476
|
+
image->data[ x + y * image->xsize ]
|
477
|
+
|
478
|
+
with x and y integer.
|
479
|
+
*/
|
480
|
+
typedef struct image_double_s
|
481
|
+
{
|
482
|
+
double * data;
|
483
|
+
unsigned int xsize,ysize;
|
484
|
+
} * image_double;
|
485
|
+
|
486
|
+
/*----------------------------------------------------------------------------*/
|
487
|
+
/** Free memory used in image_double 'i'.
|
488
|
+
*/
|
489
|
+
static void free_image_double(image_double i)
|
490
|
+
{
|
491
|
+
if( i == NULL || i->data == NULL )
|
492
|
+
error("free_image_double: invalid input image.");
|
493
|
+
free( (void *) i->data );
|
494
|
+
free( (void *) i );
|
495
|
+
}
|
496
|
+
|
497
|
+
/*----------------------------------------------------------------------------*/
|
498
|
+
/** Create a new image_double of size 'xsize' times 'ysize'.
|
499
|
+
*/
|
500
|
+
static image_double new_image_double(unsigned int xsize, unsigned int ysize)
|
501
|
+
{
|
502
|
+
image_double image;
|
503
|
+
|
504
|
+
/* check parameters */
|
505
|
+
if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size.");
|
506
|
+
|
507
|
+
/* get memory */
|
508
|
+
image = (image_double) malloc( sizeof(struct image_double_s) );
|
509
|
+
if( image == NULL ) error("not enough memory.");
|
510
|
+
image->data = (double *) calloc( (size_t) (xsize*ysize), sizeof(double) );
|
511
|
+
if( image->data == NULL ) error("not enough memory.");
|
512
|
+
|
513
|
+
/* set image size */
|
514
|
+
image->xsize = xsize;
|
515
|
+
image->ysize = ysize;
|
516
|
+
|
517
|
+
return image;
|
518
|
+
}
|
519
|
+
|
520
|
+
/*----------------------------------------------------------------------------*/
|
521
|
+
/** Create a new image_double of size 'xsize' times 'ysize'
|
522
|
+
with the data pointed by 'data'.
|
523
|
+
*/
|
524
|
+
static image_double new_image_double_ptr( unsigned int xsize,
|
525
|
+
unsigned int ysize, double * data )
|
526
|
+
{
|
527
|
+
image_double image;
|
528
|
+
|
529
|
+
/* check parameters */
|
530
|
+
if( xsize == 0 || ysize == 0 )
|
531
|
+
error("new_image_double_ptr: invalid image size.");
|
532
|
+
if( data == NULL ) error("new_image_double_ptr: NULL data pointer.");
|
533
|
+
|
534
|
+
/* get memory */
|
535
|
+
image = (image_double) malloc( sizeof(struct image_double_s) );
|
536
|
+
if( image == NULL ) error("not enough memory.");
|
537
|
+
|
538
|
+
/* set image */
|
539
|
+
image->xsize = xsize;
|
540
|
+
image->ysize = ysize;
|
541
|
+
image->data = data;
|
542
|
+
|
543
|
+
return image;
|
544
|
+
}
|
545
|
+
|
546
|
+
|
547
|
+
/*----------------------------------------------------------------------------*/
|
548
|
+
/*----------------------------- Gaussian filter ------------------------------*/
|
549
|
+
/*----------------------------------------------------------------------------*/
|
550
|
+
|
551
|
+
/*----------------------------------------------------------------------------*/
|
552
|
+
/** Compute a Gaussian kernel of length 'kernel->dim',
|
553
|
+
standard deviation 'sigma', and centered at value 'mean'.
|
554
|
+
|
555
|
+
For example, if mean=0.5, the Gaussian will be centered
|
556
|
+
in the middle point between values 'kernel->values[0]'
|
557
|
+
and 'kernel->values[1]'.
|
558
|
+
*/
|
559
|
+
static void gaussian_kernel(ntuple_list kernel, double sigma, double mean)
|
560
|
+
{
|
561
|
+
double sum = 0.0;
|
562
|
+
double val;
|
563
|
+
unsigned int i;
|
564
|
+
|
565
|
+
/* check parameters */
|
566
|
+
if( kernel == NULL || kernel->values == NULL )
|
567
|
+
error("gaussian_kernel: invalid n-tuple 'kernel'.");
|
568
|
+
if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive.");
|
569
|
+
|
570
|
+
/* compute Gaussian kernel */
|
571
|
+
if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel);
|
572
|
+
kernel->size = 1;
|
573
|
+
for(i=0;i<kernel->dim;i++)
|
574
|
+
{
|
575
|
+
val = ( (double) i - mean ) / sigma;
|
576
|
+
kernel->values[i] = exp( -0.5 * val * val );
|
577
|
+
sum += kernel->values[i];
|
578
|
+
}
|
579
|
+
|
580
|
+
/* normalization */
|
581
|
+
if( sum >= 0.0 ) for(i=0;i<kernel->dim;i++) kernel->values[i] /= sum;
|
582
|
+
}
|
583
|
+
|
584
|
+
/*----------------------------------------------------------------------------*/
|
585
|
+
/** Scale the input image 'in' by a factor 'scale' by Gaussian sub-sampling.
|
586
|
+
|
587
|
+
For example, scale=0.8 will give a result at 80% of the original size.
|
588
|
+
|
589
|
+
The image is convolved with a Gaussian kernel
|
590
|
+
@f[
|
591
|
+
G(x,y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}}
|
592
|
+
@f]
|
593
|
+
before the sub-sampling to prevent aliasing.
|
594
|
+
|
595
|
+
The standard deviation sigma given by:
|
596
|
+
- sigma = sigma_scale / scale, if scale < 1.0
|
597
|
+
- sigma = sigma_scale, if scale >= 1.0
|
598
|
+
|
599
|
+
To be able to sub-sample at non-integer steps, some interpolation
|
600
|
+
is needed. In this implementation, the interpolation is done by
|
601
|
+
the Gaussian kernel, so both operations (filtering and sampling)
|
602
|
+
are done at the same time. The Gaussian kernel is computed
|
603
|
+
centered on the coordinates of the required sample. In this way,
|
604
|
+
when applied, it gives directly the result of convolving the image
|
605
|
+
with the kernel and interpolated to that particular position.
|
606
|
+
|
607
|
+
A fast algorithm is done using the separability of the Gaussian
|
608
|
+
kernel. Applying the 2D Gaussian kernel is equivalent to applying
|
609
|
+
first a horizontal 1D Gaussian kernel and then a vertical 1D
|
610
|
+
Gaussian kernel (or the other way round). The reason is that
|
611
|
+
@f[
|
612
|
+
G(x,y) = G(x) * G(y)
|
613
|
+
@f]
|
614
|
+
where
|
615
|
+
@f[
|
616
|
+
G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{x^2}{2\sigma^2}}.
|
617
|
+
@f]
|
618
|
+
The algorithm first applies a combined Gaussian kernel and sampling
|
619
|
+
in the x axis, and then the combined Gaussian kernel and sampling
|
620
|
+
in the y axis.
|
621
|
+
*/
|
622
|
+
static image_double gaussian_sampler( image_double in, double scale,
|
623
|
+
double sigma_scale )
|
624
|
+
{
|
625
|
+
image_double aux,out;
|
626
|
+
ntuple_list kernel;
|
627
|
+
unsigned int N,M,h,n,x,y,i;
|
628
|
+
int xc,yc,j,double_x_size,double_y_size;
|
629
|
+
double sigma,xx,yy,sum,prec;
|
630
|
+
|
631
|
+
/* check parameters */
|
632
|
+
if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
|
633
|
+
error("gaussian_sampler: invalid image.");
|
634
|
+
if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive.");
|
635
|
+
if( sigma_scale <= 0.0 )
|
636
|
+
error("gaussian_sampler: 'sigma_scale' must be positive.");
|
637
|
+
|
638
|
+
/* compute new image size and get memory for images */
|
639
|
+
if( in->xsize * scale > (double) UINT_MAX ||
|
640
|
+
in->ysize * scale > (double) UINT_MAX )
|
641
|
+
error("gaussian_sampler: the output image size exceeds the handled size.");
|
642
|
+
N = (unsigned int) ceil( in->xsize * scale );
|
643
|
+
M = (unsigned int) ceil( in->ysize * scale );
|
644
|
+
aux = new_image_double(N,in->ysize);
|
645
|
+
out = new_image_double(N,M);
|
646
|
+
|
647
|
+
/* sigma, kernel size and memory for the kernel */
|
648
|
+
sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale;
|
649
|
+
/*
|
650
|
+
The size of the kernel is selected to guarantee that the
|
651
|
+
the first discarded term is at least 10^prec times smaller
|
652
|
+
than the central value. For that, h should be larger than x, with
|
653
|
+
e^(-x^2/2sigma^2) = 1/10^prec.
|
654
|
+
Then,
|
655
|
+
x = sigma * sqrt( 2 * prec * ln(10) ).
|
656
|
+
*/
|
657
|
+
prec = 3.0;
|
658
|
+
h = (unsigned int) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) );
|
659
|
+
n = 1+2*h; /* kernel size */
|
660
|
+
kernel = new_ntuple_list(n);
|
661
|
+
|
662
|
+
/* auxiliary double image size variables */
|
663
|
+
double_x_size = (int) (2 * in->xsize);
|
664
|
+
double_y_size = (int) (2 * in->ysize);
|
665
|
+
|
666
|
+
/* First subsampling: x axis */
|
667
|
+
for(x=0;x<aux->xsize;x++)
|
668
|
+
{
|
669
|
+
/*
|
670
|
+
x is the coordinate in the new image.
|
671
|
+
xx is the corresponding x-value in the original size image.
|
672
|
+
xc is the integer value, the pixel coordinate of xx.
|
673
|
+
*/
|
674
|
+
xx = (double) x / scale;
|
675
|
+
/* coordinate (0.0,0.0) is in the center of pixel (0,0),
|
676
|
+
so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */
|
677
|
+
xc = (int) floor( xx + 0.5 );
|
678
|
+
gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc );
|
679
|
+
/* the kernel must be computed for each x because the fine
|
680
|
+
offset xx-xc is different in each case */
|
681
|
+
|
682
|
+
for(y=0;y<aux->ysize;y++)
|
683
|
+
{
|
684
|
+
sum = 0.0;
|
685
|
+
for(i=0;i<kernel->dim;i++)
|
686
|
+
{
|
687
|
+
j = xc - h + i;
|
688
|
+
|
689
|
+
/* symmetry boundary condition */
|
690
|
+
while( j < 0 ) j += double_x_size;
|
691
|
+
while( j >= double_x_size ) j -= double_x_size;
|
692
|
+
if( j >= (int) in->xsize ) j = double_x_size-1-j;
|
693
|
+
|
694
|
+
sum += in->data[ j + y * in->xsize ] * kernel->values[i];
|
695
|
+
}
|
696
|
+
aux->data[ x + y * aux->xsize ] = sum;
|
697
|
+
}
|
698
|
+
}
|
699
|
+
|
700
|
+
/* Second subsampling: y axis */
|
701
|
+
for(y=0;y<out->ysize;y++)
|
702
|
+
{
|
703
|
+
/*
|
704
|
+
y is the coordinate in the new image.
|
705
|
+
yy is the corresponding x-value in the original size image.
|
706
|
+
yc is the integer value, the pixel coordinate of xx.
|
707
|
+
*/
|
708
|
+
yy = (double) y / scale;
|
709
|
+
/* coordinate (0.0,0.0) is in the center of pixel (0,0),
|
710
|
+
so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */
|
711
|
+
yc = (int) floor( yy + 0.5 );
|
712
|
+
gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc );
|
713
|
+
/* the kernel must be computed for each y because the fine
|
714
|
+
offset yy-yc is different in each case */
|
715
|
+
|
716
|
+
for(x=0;x<out->xsize;x++)
|
717
|
+
{
|
718
|
+
sum = 0.0;
|
719
|
+
for(i=0;i<kernel->dim;i++)
|
720
|
+
{
|
721
|
+
j = yc - h + i;
|
722
|
+
|
723
|
+
/* symmetry boundary condition */
|
724
|
+
while( j < 0 ) j += double_y_size;
|
725
|
+
while( j >= double_y_size ) j -= double_y_size;
|
726
|
+
if( j >= (int) in->ysize ) j = double_y_size-1-j;
|
727
|
+
|
728
|
+
sum += aux->data[ x + j * aux->xsize ] * kernel->values[i];
|
729
|
+
}
|
730
|
+
out->data[ x + y * out->xsize ] = sum;
|
731
|
+
}
|
732
|
+
}
|
733
|
+
|
734
|
+
/* free memory */
|
735
|
+
free_ntuple_list(kernel);
|
736
|
+
free_image_double(aux);
|
737
|
+
|
738
|
+
return out;
|
739
|
+
}
|
740
|
+
|
741
|
+
|
742
|
+
/*----------------------------------------------------------------------------*/
|
743
|
+
/*--------------------------------- Gradient ---------------------------------*/
|
744
|
+
/*----------------------------------------------------------------------------*/
|
745
|
+
|
746
|
+
/*----------------------------------------------------------------------------*/
|
747
|
+
/** Computes the direction of the level line of 'in' at each point.
|
748
|
+
|
749
|
+
The result is:
|
750
|
+
- an image_double with the angle at each pixel, or NOTDEF if not defined.
|
751
|
+
- the image_double 'modgrad' (a pointer is passed as argument)
|
752
|
+
with the gradient magnitude at each point.
|
753
|
+
- a list of pixels 'list_p' roughly ordered by decreasing
|
754
|
+
gradient magnitude. (The order is made by classifying points
|
755
|
+
into bins by gradient magnitude. The parameters 'n_bins' and
|
756
|
+
'max_grad' specify the number of bins and the gradient modulus
|
757
|
+
at the highest bin. The pixels in the list would be in
|
758
|
+
decreasing gradient magnitude, up to a precision of the size of
|
759
|
+
the bins.)
|
760
|
+
- a pointer 'mem_p' to the memory used by 'list_p' to be able to
|
761
|
+
free the memory when it is not used anymore.
|
762
|
+
*/
|
763
|
+
static image_double ll_angle( image_double in, double threshold,
|
764
|
+
struct coorlist ** list_p, void ** mem_p,
|
765
|
+
image_double * modgrad, unsigned int n_bins )
|
766
|
+
{
|
767
|
+
image_double g;
|
768
|
+
unsigned int n,p,x,y,adr,i;
|
769
|
+
double com1,com2,gx,gy,norm,norm2;
|
770
|
+
/* the rest of the variables are used for pseudo-ordering
|
771
|
+
the gradient magnitude values */
|
772
|
+
int list_count = 0;
|
773
|
+
struct coorlist * list;
|
774
|
+
struct coorlist ** range_l_s; /* array of pointers to start of bin list */
|
775
|
+
struct coorlist ** range_l_e; /* array of pointers to end of bin list */
|
776
|
+
struct coorlist * start;
|
777
|
+
struct coorlist * end;
|
778
|
+
double max_grad = 0.0;
|
779
|
+
|
780
|
+
/* check parameters */
|
781
|
+
if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
|
782
|
+
error("ll_angle: invalid image.");
|
783
|
+
if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive.");
|
784
|
+
if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'.");
|
785
|
+
if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'.");
|
786
|
+
if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'.");
|
787
|
+
if( n_bins == 0 ) error("ll_angle: 'n_bins' must be positive.");
|
788
|
+
|
789
|
+
/* image size shortcuts */
|
790
|
+
n = in->ysize;
|
791
|
+
p = in->xsize;
|
792
|
+
|
793
|
+
/* allocate output image */
|
794
|
+
g = new_image_double(in->xsize,in->ysize);
|
795
|
+
|
796
|
+
/* get memory for the image of gradient modulus */
|
797
|
+
*modgrad = new_image_double(in->xsize,in->ysize);
|
798
|
+
|
799
|
+
/* get memory for "ordered" list of pixels */
|
800
|
+
list = (struct coorlist *) calloc( (size_t) (n*p), sizeof(struct coorlist) );
|
801
|
+
*mem_p = (void *) list;
|
802
|
+
range_l_s = (struct coorlist **) calloc( (size_t) n_bins,
|
803
|
+
sizeof(struct coorlist *) );
|
804
|
+
range_l_e = (struct coorlist **) calloc( (size_t) n_bins,
|
805
|
+
sizeof(struct coorlist *) );
|
806
|
+
if( list == NULL || range_l_s == NULL || range_l_e == NULL )
|
807
|
+
error("not enough memory.");
|
808
|
+
for(i=0;i<n_bins;i++) range_l_s[i] = range_l_e[i] = NULL;
|
809
|
+
|
810
|
+
/* 'undefined' on the down and right boundaries */
|
811
|
+
for(x=0;x<p;x++) g->data[(n-1)*p+x] = NOTDEF;
|
812
|
+
for(y=0;y<n;y++) g->data[p*y+p-1] = NOTDEF;
|
813
|
+
|
814
|
+
/* compute gradient on the remaining pixels */
|
815
|
+
for(x=0;x<p-1;x++)
|
816
|
+
for(y=0;y<n-1;y++)
|
817
|
+
{
|
818
|
+
adr = y*p+x;
|
819
|
+
|
820
|
+
/*
|
821
|
+
Norm 2 computation using 2x2 pixel window:
|
822
|
+
A B
|
823
|
+
C D
|
824
|
+
and
|
825
|
+
com1 = D-A, com2 = B-C.
|
826
|
+
Then
|
827
|
+
gx = B+D - (A+C) horizontal difference
|
828
|
+
gy = C+D - (A+B) vertical difference
|
829
|
+
com1 and com2 are just to avoid 2 additions.
|
830
|
+
*/
|
831
|
+
// fprintf(stderr, "pixelvalue %d: %f\n", adr, in->data[adr]);
|
832
|
+
com1 = in->data[adr+p+1] - in->data[adr];
|
833
|
+
com2 = in->data[adr+1] - in->data[adr+p];
|
834
|
+
|
835
|
+
gx = com1+com2; /* gradient x component */
|
836
|
+
gy = com1-com2; /* gradient y component */
|
837
|
+
norm2 = gx*gx+gy*gy;
|
838
|
+
norm = sqrt( norm2 / 4.0 ); /* gradient norm */
|
839
|
+
|
840
|
+
(*modgrad)->data[adr] = norm; /* store gradient norm */
|
841
|
+
|
842
|
+
if( norm <= threshold ) /* norm too small, gradient no defined */
|
843
|
+
g->data[adr] = NOTDEF; /* gradient angle not defined */
|
844
|
+
else
|
845
|
+
{
|
846
|
+
/* gradient angle computation */
|
847
|
+
g->data[adr] = atan2(gx,-gy);
|
848
|
+
|
849
|
+
/* look for the maximum of the gradient */
|
850
|
+
if( norm > max_grad ) max_grad = norm;
|
851
|
+
}
|
852
|
+
}
|
853
|
+
|
854
|
+
/* compute histogram of gradient values */
|
855
|
+
for(x=0;x<p-1;x++)
|
856
|
+
for(y=0;y<n-1;y++)
|
857
|
+
{
|
858
|
+
norm = (*modgrad)->data[y*p+x];
|
859
|
+
|
860
|
+
/* store the point in the right bin according to its norm */
|
861
|
+
i = (unsigned int) (norm * (double) n_bins / max_grad);
|
862
|
+
if( i >= n_bins ) i = n_bins-1;
|
863
|
+
if( range_l_e[i] == NULL )
|
864
|
+
range_l_s[i] = range_l_e[i] = list+list_count++;
|
865
|
+
else
|
866
|
+
{
|
867
|
+
range_l_e[i]->next = list+list_count;
|
868
|
+
range_l_e[i] = list+list_count++;
|
869
|
+
}
|
870
|
+
range_l_e[i]->x = (int) x;
|
871
|
+
range_l_e[i]->y = (int) y;
|
872
|
+
range_l_e[i]->next = NULL;
|
873
|
+
}
|
874
|
+
|
875
|
+
/* Make the list of pixels (almost) ordered by norm value.
|
876
|
+
It starts by the larger bin, so the list starts by the
|
877
|
+
pixels with the highest gradient value. Pixels would be ordered
|
878
|
+
by norm value, up to a precision given by max_grad/n_bins.
|
879
|
+
*/
|
880
|
+
for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--);
|
881
|
+
start = range_l_s[i];
|
882
|
+
end = range_l_e[i];
|
883
|
+
if( start != NULL )
|
884
|
+
while(i>0)
|
885
|
+
{
|
886
|
+
--i;
|
887
|
+
if( range_l_s[i] != NULL )
|
888
|
+
{
|
889
|
+
end->next = range_l_s[i];
|
890
|
+
end = range_l_e[i];
|
891
|
+
}
|
892
|
+
}
|
893
|
+
*list_p = start;
|
894
|
+
|
895
|
+
/* free memory */
|
896
|
+
free( (void *) range_l_s );
|
897
|
+
free( (void *) range_l_e );
|
898
|
+
|
899
|
+
return g;
|
900
|
+
}
|
901
|
+
|
902
|
+
/*----------------------------------------------------------------------------*/
|
903
|
+
/** Is point (x,y) aligned to angle theta, up to precision 'prec'?
|
904
|
+
*/
|
905
|
+
static int isaligned( int x, int y, image_double angles, double theta,
|
906
|
+
double prec )
|
907
|
+
{
|
908
|
+
double a;
|
909
|
+
|
910
|
+
/* check parameters */
|
911
|
+
if( angles == NULL || angles->data == NULL )
|
912
|
+
error("isaligned: invalid image 'angles'.");
|
913
|
+
if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
|
914
|
+
error("isaligned: (x,y) out of the image.");
|
915
|
+
if( prec < 0.0 ) error("isaligned: 'prec' must be positive.");
|
916
|
+
|
917
|
+
/* angle at pixel (x,y) */
|
918
|
+
a = angles->data[ x + y * angles->xsize ];
|
919
|
+
|
920
|
+
/* pixels whose level-line angle is not defined
|
921
|
+
are considered as NON-aligned */
|
922
|
+
if( a == NOTDEF ) return FALSE; /* there is no need to call the function
|
923
|
+
'double_equal' here because there is
|
924
|
+
no risk of problems related to the
|
925
|
+
comparison doubles, we are only
|
926
|
+
interested in the exact NOTDEF value */
|
927
|
+
|
928
|
+
/* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */
|
929
|
+
theta -= a;
|
930
|
+
if( theta < 0.0 ) theta = -theta;
|
931
|
+
if( theta > M_3_2_PI )
|
932
|
+
{
|
933
|
+
theta -= M_2__PI;
|
934
|
+
if( theta < 0.0 ) theta = -theta;
|
935
|
+
}
|
936
|
+
|
937
|
+
return theta <= prec;
|
938
|
+
}
|
939
|
+
|
940
|
+
/*----------------------------------------------------------------------------*/
|
941
|
+
/** Absolute value angle difference.
|
942
|
+
*/
|
943
|
+
static double angle_diff(double a, double b)
|
944
|
+
{
|
945
|
+
a -= b;
|
946
|
+
while( a <= -M_PI ) a += M_2__PI;
|
947
|
+
while( a > M_PI ) a -= M_2__PI;
|
948
|
+
if( a < 0.0 ) a = -a;
|
949
|
+
return a;
|
950
|
+
}
|
951
|
+
|
952
|
+
/*----------------------------------------------------------------------------*/
|
953
|
+
/** Signed angle difference.
|
954
|
+
*/
|
955
|
+
static double angle_diff_signed(double a, double b)
|
956
|
+
{
|
957
|
+
a -= b;
|
958
|
+
while( a <= -M_PI ) a += M_2__PI;
|
959
|
+
while( a > M_PI ) a -= M_2__PI;
|
960
|
+
return a;
|
961
|
+
}
|
962
|
+
|
963
|
+
|
964
|
+
/*----------------------------------------------------------------------------*/
|
965
|
+
/*----------------------------- NFA computation ------------------------------*/
|
966
|
+
/*----------------------------------------------------------------------------*/
|
967
|
+
|
968
|
+
/*----------------------------------------------------------------------------*/
|
969
|
+
/** Computes the natural logarithm of the absolute value of
|
970
|
+
the gamma function of x using the Lanczos approximation.
|
971
|
+
See http://www.rskey.org/gamma.htm
|
972
|
+
|
973
|
+
The formula used is
|
974
|
+
@f[
|
975
|
+
\Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) }
|
976
|
+
(x+5.5)^{x+0.5} e^{-(x+5.5)}
|
977
|
+
@f]
|
978
|
+
so
|
979
|
+
@f[
|
980
|
+
\log\Gamma(x) = \log\left( \sum_{n=0}^{N} q_n x^n \right)
|
981
|
+
+ (x+0.5) \log(x+5.5) - (x+5.5) - \sum_{n=0}^{N} \log(x+n)
|
982
|
+
@f]
|
983
|
+
and
|
984
|
+
q0 = 75122.6331530,
|
985
|
+
q1 = 80916.6278952,
|
986
|
+
q2 = 36308.2951477,
|
987
|
+
q3 = 8687.24529705,
|
988
|
+
q4 = 1168.92649479,
|
989
|
+
q5 = 83.8676043424,
|
990
|
+
q6 = 2.50662827511.
|
991
|
+
*/
|
992
|
+
static double log_gamma_lanczos(double x)
|
993
|
+
{
|
994
|
+
static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
|
995
|
+
8687.24529705, 1168.92649479, 83.8676043424,
|
996
|
+
2.50662827511 };
|
997
|
+
double a = (x+0.5) * log(x+5.5) - (x+5.5);
|
998
|
+
double b = 0.0;
|
999
|
+
int n;
|
1000
|
+
|
1001
|
+
for(n=0;n<7;n++)
|
1002
|
+
{
|
1003
|
+
a -= log( x + (double) n );
|
1004
|
+
b += q[n] * pow( x, (double) n );
|
1005
|
+
}
|
1006
|
+
return a + log(b);
|
1007
|
+
}
|
1008
|
+
|
1009
|
+
/*----------------------------------------------------------------------------*/
|
1010
|
+
/** Computes the natural logarithm of the absolute value of
|
1011
|
+
the gamma function of x using Windschitl method.
|
1012
|
+
See http://www.rskey.org/gamma.htm
|
1013
|
+
|
1014
|
+
The formula used is
|
1015
|
+
@f[
|
1016
|
+
\Gamma(x) = \sqrt{\frac{2\pi}{x}} \left( \frac{x}{e}
|
1017
|
+
\sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } \right)^x
|
1018
|
+
@f]
|
1019
|
+
so
|
1020
|
+
@f[
|
1021
|
+
\log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x
|
1022
|
+
+ 0.5x\log\left( x\sinh(1/x) + \frac{1}{810x^6} \right).
|
1023
|
+
@f]
|
1024
|
+
This formula is a good approximation when x > 15.
|
1025
|
+
*/
|
1026
|
+
static double log_gamma_windschitl(double x)
|
1027
|
+
{
|
1028
|
+
return 0.918938533204673 + (x-0.5)*log(x) - x
|
1029
|
+
+ 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) );
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
/*----------------------------------------------------------------------------*/
|
1033
|
+
/** Computes the natural logarithm of the absolute value of
|
1034
|
+
the gamma function of x. When x>15 use log_gamma_windschitl(),
|
1035
|
+
otherwise use log_gamma_lanczos().
|
1036
|
+
*/
|
1037
|
+
#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
|
1038
|
+
|
1039
|
+
/*----------------------------------------------------------------------------*/
|
1040
|
+
/** Size of the table to store already computed inverse values.
|
1041
|
+
*/
|
1042
|
+
#define TABSIZE 100000
|
1043
|
+
|
1044
|
+
/*----------------------------------------------------------------------------*/
|
1045
|
+
/** Computes -log10(NFA).
|
1046
|
+
|
1047
|
+
NFA stands for Number of False Alarms:
|
1048
|
+
@f[
|
1049
|
+
\mathrm{NFA} = NT \cdot B(n,k,p)
|
1050
|
+
@f]
|
1051
|
+
|
1052
|
+
- NT - number of tests
|
1053
|
+
- B(n,k,p) - tail of binomial distribution with parameters n,k and p:
|
1054
|
+
@f[
|
1055
|
+
B(n,k,p) = \sum_{j=k}^n
|
1056
|
+
\left(\begin{array}{c}n\\j\end{array}\right)
|
1057
|
+
p^{j} (1-p)^{n-j}
|
1058
|
+
@f]
|
1059
|
+
|
1060
|
+
The value -log10(NFA) is equivalent but more intuitive than NFA:
|
1061
|
+
- -1 corresponds to 10 mean false alarms
|
1062
|
+
- 0 corresponds to 1 mean false alarm
|
1063
|
+
- 1 corresponds to 0.1 mean false alarms
|
1064
|
+
- 2 corresponds to 0.01 mean false alarms
|
1065
|
+
- ...
|
1066
|
+
|
1067
|
+
Used this way, the bigger the value, better the detection,
|
1068
|
+
and a logarithmic scale is used.
|
1069
|
+
|
1070
|
+
@param n,k,p binomial parameters.
|
1071
|
+
@param logNT logarithm of Number of Tests
|
1072
|
+
|
1073
|
+
The computation is based in the gamma function by the following
|
1074
|
+
relation:
|
1075
|
+
@f[
|
1076
|
+
\left(\begin{array}{c}n\\k\end{array}\right)
|
1077
|
+
= \frac{ \Gamma(n+1) }{ \Gamma(k+1) \cdot \Gamma(n-k+1) }.
|
1078
|
+
@f]
|
1079
|
+
We use efficient algorithms to compute the logarithm of
|
1080
|
+
the gamma function.
|
1081
|
+
|
1082
|
+
To make the computation faster, not all the sum is computed, part
|
1083
|
+
of the terms are neglected based on a bound to the error obtained
|
1084
|
+
(an error of 10% in the result is accepted).
|
1085
|
+
*/
|
1086
|
+
static double nfa(int n, int k, double p, double logNT)
|
1087
|
+
{
|
1088
|
+
static double inv[TABSIZE]; /* table to keep computed inverse values */
|
1089
|
+
double tolerance = 0.1; /* an error of 10% in the result is accepted */
|
1090
|
+
double log1term,term,bin_term,mult_term,bin_tail,err,p_term;
|
1091
|
+
int i;
|
1092
|
+
|
1093
|
+
/* check parameters */
|
1094
|
+
if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 )
|
1095
|
+
error("nfa: wrong n, k or p values.");
|
1096
|
+
|
1097
|
+
/* trivial cases */
|
1098
|
+
if( n==0 || k==0 ) return -logNT;
|
1099
|
+
if( n==k ) return -logNT - (double) n * log10(p);
|
1100
|
+
|
1101
|
+
/* probability term */
|
1102
|
+
p_term = p / (1.0-p);
|
1103
|
+
|
1104
|
+
/* compute the first term of the series */
|
1105
|
+
/*
|
1106
|
+
binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
|
1107
|
+
where bincoef(n,i) are the binomial coefficients.
|
1108
|
+
But
|
1109
|
+
bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
|
1110
|
+
We use this to compute the first term. Actually the log of it.
|
1111
|
+
*/
|
1112
|
+
log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 )
|
1113
|
+
- log_gamma( (double) (n-k) + 1.0 )
|
1114
|
+
+ (double) k * log(p) + (double) (n-k) * log(1.0-p);
|
1115
|
+
term = exp(log1term);
|
1116
|
+
|
1117
|
+
/* in some cases no more computations are needed */
|
1118
|
+
if( double_equal(term,0.0) ) /* the first term is almost zero */
|
1119
|
+
{
|
1120
|
+
if( (double) k > (double) n * p ) /* at begin or end of the tail? */
|
1121
|
+
return -log1term / M_LN10 - logNT; /* end: use just the first term */
|
1122
|
+
else
|
1123
|
+
return -logNT; /* begin: the tail is roughly 1 */
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
/* compute more terms if needed */
|
1127
|
+
bin_tail = term;
|
1128
|
+
for(i=k+1;i<=n;i++)
|
1129
|
+
{
|
1130
|
+
/*
|
1131
|
+
As
|
1132
|
+
term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
|
1133
|
+
and
|
1134
|
+
bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
|
1135
|
+
then,
|
1136
|
+
term_i / term_i-1 = (n-i+1)/i * p/(1-p)
|
1137
|
+
and
|
1138
|
+
term_i = term_i-1 * (n-i+1)/i * p/(1-p).
|
1139
|
+
1/i is stored in a table as they are computed,
|
1140
|
+
because divisions are expensive.
|
1141
|
+
p/(1-p) is computed only once and stored in 'p_term'.
|
1142
|
+
*/
|
1143
|
+
bin_term = (double) (n-i+1) * ( i<TABSIZE ?
|
1144
|
+
( inv[i]!=0.0 ? inv[i] : ( inv[i] = 1.0 / (double) i ) ) :
|
1145
|
+
1.0 / (double) i );
|
1146
|
+
|
1147
|
+
mult_term = bin_term * p_term;
|
1148
|
+
term *= mult_term;
|
1149
|
+
bin_tail += term;
|
1150
|
+
if(bin_term<1.0)
|
1151
|
+
{
|
1152
|
+
/* When bin_term<1 then mult_term_j<mult_term_i for j>i.
|
1153
|
+
Then, the error on the binomial tail when truncated at
|
1154
|
+
the i term can be bounded by a geometric series of form
|
1155
|
+
term_i * sum mult_term_i^j. */
|
1156
|
+
err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) /
|
1157
|
+
(1.0-mult_term) - 1.0 );
|
1158
|
+
|
1159
|
+
/* One wants an error at most of tolerance*final_result, or:
|
1160
|
+
tolerance * abs(-log10(bin_tail)-logNT).
|
1161
|
+
Now, the error that can be accepted on bin_tail is
|
1162
|
+
given by tolerance*final_result divided by the derivative
|
1163
|
+
of -log10(x) when x=bin_tail. that is:
|
1164
|
+
tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
|
1165
|
+
Finally, we truncate the tail if the error is less than:
|
1166
|
+
tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
|
1167
|
+
if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break;
|
1168
|
+
}
|
1169
|
+
}
|
1170
|
+
return -log10(bin_tail) - logNT;
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
|
1174
|
+
/*----------------------------------------------------------------------------*/
|
1175
|
+
/*--------------------------- Rectangle structure ----------------------------*/
|
1176
|
+
/*----------------------------------------------------------------------------*/
|
1177
|
+
|
1178
|
+
/*----------------------------------------------------------------------------*/
|
1179
|
+
/** Rectangle structure: line segment with width.
|
1180
|
+
*/
|
1181
|
+
struct rect
|
1182
|
+
{
|
1183
|
+
double x1,y1,x2,y2; /* first and second point of the line segment */
|
1184
|
+
double width; /* rectangle width */
|
1185
|
+
double x,y; /* center of the rectangle */
|
1186
|
+
double theta; /* angle */
|
1187
|
+
double dx,dy; /* (dx,dy) is vector oriented as the line segment */
|
1188
|
+
double prec; /* tolerance angle */
|
1189
|
+
double p; /* probability of a point with angle within 'prec' */
|
1190
|
+
};
|
1191
|
+
|
1192
|
+
/*----------------------------------------------------------------------------*/
|
1193
|
+
/** Copy one rectangle structure to another.
|
1194
|
+
*/
|
1195
|
+
static void rect_copy(struct rect * in, struct rect * out)
|
1196
|
+
{
|
1197
|
+
/* check parameters */
|
1198
|
+
if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'.");
|
1199
|
+
|
1200
|
+
/* copy values */
|
1201
|
+
out->x1 = in->x1;
|
1202
|
+
out->y1 = in->y1;
|
1203
|
+
out->x2 = in->x2;
|
1204
|
+
out->y2 = in->y2;
|
1205
|
+
out->width = in->width;
|
1206
|
+
out->x = in->x;
|
1207
|
+
out->y = in->y;
|
1208
|
+
out->theta = in->theta;
|
1209
|
+
out->dx = in->dx;
|
1210
|
+
out->dy = in->dy;
|
1211
|
+
out->prec = in->prec;
|
1212
|
+
out->p = in->p;
|
1213
|
+
}
|
1214
|
+
|
1215
|
+
/*----------------------------------------------------------------------------*/
|
1216
|
+
/** Rectangle points iterator.
|
1217
|
+
|
1218
|
+
The integer coordinates of pixels inside a rectangle are
|
1219
|
+
iteratively explored. This structure keep track of the process and
|
1220
|
+
functions ri_ini(), ri_inc(), ri_end(), and ri_del() are used in
|
1221
|
+
the process. An example of how to use the iterator is as follows:
|
1222
|
+
\code
|
1223
|
+
|
1224
|
+
struct rect * rec = XXX; // some rectangle
|
1225
|
+
rect_iter * i;
|
1226
|
+
for( i=ri_ini(rec); !ri_end(i); ri_inc(i) )
|
1227
|
+
{
|
1228
|
+
// your code, using 'i->x' and 'i->y' as coordinates
|
1229
|
+
}
|
1230
|
+
ri_del(i); // delete iterator
|
1231
|
+
|
1232
|
+
\endcode
|
1233
|
+
The pixels are explored 'column' by 'column', where we call
|
1234
|
+
'column' a set of pixels with the same x value that are inside the
|
1235
|
+
rectangle. The following is an schematic representation of a
|
1236
|
+
rectangle, the 'column' being explored is marked by colons, and
|
1237
|
+
the current pixel being explored is 'x,y'.
|
1238
|
+
\verbatim
|
1239
|
+
|
1240
|
+
vx[1],vy[1]
|
1241
|
+
* *
|
1242
|
+
* *
|
1243
|
+
* *
|
1244
|
+
* ye
|
1245
|
+
* : *
|
1246
|
+
vx[0],vy[0] : *
|
1247
|
+
* : *
|
1248
|
+
* x,y *
|
1249
|
+
* : *
|
1250
|
+
* : vx[2],vy[2]
|
1251
|
+
* : *
|
1252
|
+
y ys *
|
1253
|
+
^ * *
|
1254
|
+
| * *
|
1255
|
+
| * *
|
1256
|
+
+---> x vx[3],vy[3]
|
1257
|
+
|
1258
|
+
\endverbatim
|
1259
|
+
The first 'column' to be explored is the one with the smaller x
|
1260
|
+
value. Each 'column' is explored starting from the pixel of the
|
1261
|
+
'column' (inside the rectangle) with the smallest y value.
|
1262
|
+
|
1263
|
+
The four corners of the rectangle are stored in order that rotates
|
1264
|
+
around the corners at the arrays 'vx[]' and 'vy[]'. The first
|
1265
|
+
point is always the one with smaller x value.
|
1266
|
+
|
1267
|
+
'x' and 'y' are the coordinates of the pixel being explored. 'ys'
|
1268
|
+
and 'ye' are the start and end values of the current column being
|
1269
|
+
explored. So, 'ys' < 'ye'.
|
1270
|
+
*/
|
1271
|
+
typedef struct
|
1272
|
+
{
|
1273
|
+
double vx[4]; /* rectangle's corner X coordinates in circular order */
|
1274
|
+
double vy[4]; /* rectangle's corner Y coordinates in circular order */
|
1275
|
+
double ys,ye; /* start and end Y values of current 'column' */
|
1276
|
+
int x,y; /* coordinates of currently explored pixel */
|
1277
|
+
} rect_iter;
|
1278
|
+
|
1279
|
+
/*----------------------------------------------------------------------------*/
|
1280
|
+
/** Interpolate y value corresponding to 'x' value given, in
|
1281
|
+
the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the smaller
|
1282
|
+
of 'y1' and 'y2'.
|
1283
|
+
|
1284
|
+
The following restrictions are required:
|
1285
|
+
- x1 <= x2
|
1286
|
+
- x1 <= x
|
1287
|
+
- x <= x2
|
1288
|
+
*/
|
1289
|
+
static double inter_low(double x, double x1, double y1, double x2, double y2)
|
1290
|
+
{
|
1291
|
+
/* check parameters */
|
1292
|
+
if( x1 > x2 || x < x1 || x > x2 )
|
1293
|
+
error("inter_low: unsuitable input, 'x1>x2' or 'x<x1' or 'x>x2'.");
|
1294
|
+
|
1295
|
+
/* interpolation */
|
1296
|
+
if( double_equal(x1,x2) && y1<y2 ) return y1;
|
1297
|
+
if( double_equal(x1,x2) && y1>y2 ) return y2;
|
1298
|
+
return y1 + (x-x1) * (y2-y1) / (x2-x1);
|
1299
|
+
}
|
1300
|
+
|
1301
|
+
/*----------------------------------------------------------------------------*/
|
1302
|
+
/** Interpolate y value corresponding to 'x' value given, in
|
1303
|
+
the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the larger
|
1304
|
+
of 'y1' and 'y2'.
|
1305
|
+
|
1306
|
+
The following restrictions are required:
|
1307
|
+
- x1 <= x2
|
1308
|
+
- x1 <= x
|
1309
|
+
- x <= x2
|
1310
|
+
*/
|
1311
|
+
static double inter_hi(double x, double x1, double y1, double x2, double y2)
|
1312
|
+
{
|
1313
|
+
/* check parameters */
|
1314
|
+
if( x1 > x2 || x < x1 || x > x2 )
|
1315
|
+
error("inter_hi: unsuitable input, 'x1>x2' or 'x<x1' or 'x>x2'.");
|
1316
|
+
|
1317
|
+
/* interpolation */
|
1318
|
+
if( double_equal(x1,x2) && y1<y2 ) return y2;
|
1319
|
+
if( double_equal(x1,x2) && y1>y2 ) return y1;
|
1320
|
+
return y1 + (x-x1) * (y2-y1) / (x2-x1);
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
/*----------------------------------------------------------------------------*/
|
1324
|
+
/** Free memory used by a rectangle iterator.
|
1325
|
+
*/
|
1326
|
+
static void ri_del(rect_iter * iter)
|
1327
|
+
{
|
1328
|
+
if( iter == NULL ) error("ri_del: NULL iterator.");
|
1329
|
+
free( (void *) iter );
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
/*----------------------------------------------------------------------------*/
|
1333
|
+
/** Check if the iterator finished the full iteration.
|
1334
|
+
|
1335
|
+
See details in \ref rect_iter
|
1336
|
+
*/
|
1337
|
+
static int ri_end(rect_iter * i)
|
1338
|
+
{
|
1339
|
+
/* check input */
|
1340
|
+
if( i == NULL ) error("ri_end: NULL iterator.");
|
1341
|
+
|
1342
|
+
/* if the current x value is larger than the largest
|
1343
|
+
x value in the rectangle (vx[2]), we know the full
|
1344
|
+
exploration of the rectangle is finished. */
|
1345
|
+
return (double)(i->x) > i->vx[2];
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
/*----------------------------------------------------------------------------*/
|
1349
|
+
/** Increment a rectangle iterator.
|
1350
|
+
|
1351
|
+
See details in \ref rect_iter
|
1352
|
+
*/
|
1353
|
+
static void ri_inc(rect_iter * i)
|
1354
|
+
{
|
1355
|
+
/* check input */
|
1356
|
+
if( i == NULL ) error("ri_inc: NULL iterator.");
|
1357
|
+
|
1358
|
+
/* if not at end of exploration,
|
1359
|
+
increase y value for next pixel in the 'column' */
|
1360
|
+
if( !ri_end(i) ) i->y++;
|
1361
|
+
|
1362
|
+
/* if the end of the current 'column' is reached,
|
1363
|
+
and it is not the end of exploration,
|
1364
|
+
advance to the next 'column' */
|
1365
|
+
while( (double) (i->y) > i->ye && !ri_end(i) )
|
1366
|
+
{
|
1367
|
+
/* increase x, next 'column' */
|
1368
|
+
i->x++;
|
1369
|
+
|
1370
|
+
/* if end of exploration, return */
|
1371
|
+
if( ri_end(i) ) return;
|
1372
|
+
|
1373
|
+
/* update lower y limit (start) for the new 'column'.
|
1374
|
+
|
1375
|
+
We need to interpolate the y value that corresponds to the
|
1376
|
+
lower side of the rectangle. The first thing is to decide if
|
1377
|
+
the corresponding side is
|
1378
|
+
|
1379
|
+
vx[0],vy[0] to vx[3],vy[3] or
|
1380
|
+
vx[3],vy[3] to vx[2],vy[2]
|
1381
|
+
|
1382
|
+
Then, the side is interpolated for the x value of the
|
1383
|
+
'column'. But, if the side is vertical (as it could happen if
|
1384
|
+
the rectangle is vertical and we are dealing with the first
|
1385
|
+
or last 'columns') then we pick the lower value of the side
|
1386
|
+
by using 'inter_low'.
|
1387
|
+
*/
|
1388
|
+
if( (double) i->x < i->vx[3] )
|
1389
|
+
i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]);
|
1390
|
+
else
|
1391
|
+
i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]);
|
1392
|
+
|
1393
|
+
/* update upper y limit (end) for the new 'column'.
|
1394
|
+
|
1395
|
+
We need to interpolate the y value that corresponds to the
|
1396
|
+
upper side of the rectangle. The first thing is to decide if
|
1397
|
+
the corresponding side is
|
1398
|
+
|
1399
|
+
vx[0],vy[0] to vx[1],vy[1] or
|
1400
|
+
vx[1],vy[1] to vx[2],vy[2]
|
1401
|
+
|
1402
|
+
Then, the side is interpolated for the x value of the
|
1403
|
+
'column'. But, if the side is vertical (as it could happen if
|
1404
|
+
the rectangle is vertical and we are dealing with the first
|
1405
|
+
or last 'columns') then we pick the lower value of the side
|
1406
|
+
by using 'inter_low'.
|
1407
|
+
*/
|
1408
|
+
if( (double)i->x < i->vx[1] )
|
1409
|
+
i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]);
|
1410
|
+
else
|
1411
|
+
i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]);
|
1412
|
+
|
1413
|
+
/* new y */
|
1414
|
+
i->y = (int) ceil(i->ys);
|
1415
|
+
}
|
1416
|
+
}
|
1417
|
+
|
1418
|
+
/*----------------------------------------------------------------------------*/
|
1419
|
+
/** Create and initialize a rectangle iterator.
|
1420
|
+
|
1421
|
+
See details in \ref rect_iter
|
1422
|
+
*/
|
1423
|
+
static rect_iter * ri_ini(struct rect * r)
|
1424
|
+
{
|
1425
|
+
double vx[4],vy[4];
|
1426
|
+
int n,offset;
|
1427
|
+
rect_iter * i;
|
1428
|
+
|
1429
|
+
/* check parameters */
|
1430
|
+
if( r == NULL ) error("ri_ini: invalid rectangle.");
|
1431
|
+
|
1432
|
+
/* get memory */
|
1433
|
+
i = (rect_iter *) malloc(sizeof(rect_iter));
|
1434
|
+
if( i == NULL ) error("ri_ini: Not enough memory.");
|
1435
|
+
|
1436
|
+
/* build list of rectangle corners ordered
|
1437
|
+
in a circular way around the rectangle */
|
1438
|
+
vx[0] = r->x1 - r->dy * r->width / 2.0;
|
1439
|
+
vy[0] = r->y1 + r->dx * r->width / 2.0;
|
1440
|
+
vx[1] = r->x2 - r->dy * r->width / 2.0;
|
1441
|
+
vy[1] = r->y2 + r->dx * r->width / 2.0;
|
1442
|
+
vx[2] = r->x2 + r->dy * r->width / 2.0;
|
1443
|
+
vy[2] = r->y2 - r->dx * r->width / 2.0;
|
1444
|
+
vx[3] = r->x1 + r->dy * r->width / 2.0;
|
1445
|
+
vy[3] = r->y1 - r->dx * r->width / 2.0;
|
1446
|
+
|
1447
|
+
/* compute rotation of index of corners needed so that the first
|
1448
|
+
point has the smaller x.
|
1449
|
+
|
1450
|
+
if one side is vertical, thus two corners have the same smaller x
|
1451
|
+
value, the one with the largest y value is selected as the first.
|
1452
|
+
*/
|
1453
|
+
if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0;
|
1454
|
+
else if( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1;
|
1455
|
+
else if( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2;
|
1456
|
+
else offset = 3;
|
1457
|
+
|
1458
|
+
/* apply rotation of index. */
|
1459
|
+
for(n=0; n<4; n++)
|
1460
|
+
{
|
1461
|
+
i->vx[n] = vx[(offset+n)%4];
|
1462
|
+
i->vy[n] = vy[(offset+n)%4];
|
1463
|
+
}
|
1464
|
+
|
1465
|
+
/* Set an initial condition.
|
1466
|
+
|
1467
|
+
The values are set to values that will cause 'ri_inc' (that will
|
1468
|
+
be called immediately) to initialize correctly the first 'column'
|
1469
|
+
and compute the limits 'ys' and 'ye'.
|
1470
|
+
|
1471
|
+
'y' is set to the integer value of vy[0], the starting corner.
|
1472
|
+
|
1473
|
+
'ys' and 'ye' are set to very small values, so 'ri_inc' will
|
1474
|
+
notice that it needs to start a new 'column'.
|
1475
|
+
|
1476
|
+
The smallest integer coordinate inside of the rectangle is
|
1477
|
+
'ceil(vx[0])'. The current 'x' value is set to that value minus
|
1478
|
+
one, so 'ri_inc' (that will increase x by one) will advance to
|
1479
|
+
the first 'column'.
|
1480
|
+
*/
|
1481
|
+
i->x = (int) ceil(i->vx[0]) - 1;
|
1482
|
+
i->y = (int) ceil(i->vy[0]);
|
1483
|
+
i->ys = i->ye = -DBL_MAX;
|
1484
|
+
|
1485
|
+
/* advance to the first pixel */
|
1486
|
+
ri_inc(i);
|
1487
|
+
|
1488
|
+
return i;
|
1489
|
+
}
|
1490
|
+
|
1491
|
+
/*----------------------------------------------------------------------------*/
|
1492
|
+
/** Compute a rectangle's NFA value.
|
1493
|
+
*/
|
1494
|
+
static double rect_nfa(struct rect * rec, image_double angles, double logNT)
|
1495
|
+
{
|
1496
|
+
rect_iter * i;
|
1497
|
+
int pts = 0;
|
1498
|
+
int alg = 0;
|
1499
|
+
|
1500
|
+
/* check parameters */
|
1501
|
+
if( rec == NULL ) error("rect_nfa: invalid rectangle.");
|
1502
|
+
if( angles == NULL ) error("rect_nfa: invalid 'angles'.");
|
1503
|
+
|
1504
|
+
/* compute the total number of pixels and of aligned points in 'rec' */
|
1505
|
+
for(i=ri_ini(rec); !ri_end(i); ri_inc(i)) /* rectangle iterator */
|
1506
|
+
if( i->x >= 0 && i->y >= 0 &&
|
1507
|
+
i->x < (int) angles->xsize && i->y < (int) angles->ysize )
|
1508
|
+
{
|
1509
|
+
++pts; /* total number of pixels counter */
|
1510
|
+
if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) )
|
1511
|
+
++alg; /* aligned points counter */
|
1512
|
+
}
|
1513
|
+
ri_del(i); /* delete iterator */
|
1514
|
+
|
1515
|
+
return nfa(pts,alg,rec->p,logNT); /* compute NFA value */
|
1516
|
+
}
|
1517
|
+
|
1518
|
+
|
1519
|
+
/*----------------------------------------------------------------------------*/
|
1520
|
+
/*---------------------------------- Regions ---------------------------------*/
|
1521
|
+
/*----------------------------------------------------------------------------*/
|
1522
|
+
|
1523
|
+
/*----------------------------------------------------------------------------*/
|
1524
|
+
/** Compute region's angle as the principal inertia axis of the region.
|
1525
|
+
|
1526
|
+
The following is the region inertia matrix A:
|
1527
|
+
@f[
|
1528
|
+
|
1529
|
+
A = \left(\begin{array}{cc}
|
1530
|
+
Ixx & Ixy \\
|
1531
|
+
Ixy & Iyy \\
|
1532
|
+
\end{array}\right)
|
1533
|
+
|
1534
|
+
@f]
|
1535
|
+
where
|
1536
|
+
|
1537
|
+
Ixx = sum_i G(i).(y_i - cx)^2
|
1538
|
+
|
1539
|
+
Iyy = sum_i G(i).(x_i - cy)^2
|
1540
|
+
|
1541
|
+
Ixy = - sum_i G(i).(x_i - cx).(y_i - cy)
|
1542
|
+
|
1543
|
+
and
|
1544
|
+
- G(i) is the gradient norm at pixel i, used as pixel's weight.
|
1545
|
+
- x_i and y_i are the coordinates of pixel i.
|
1546
|
+
- cx and cy are the coordinates of the center of th region.
|
1547
|
+
|
1548
|
+
lambda1 and lambda2 are the eigenvalues of matrix A,
|
1549
|
+
with lambda1 >= lambda2. They are found by solving the
|
1550
|
+
characteristic polynomial:
|
1551
|
+
|
1552
|
+
det( lambda I - A) = 0
|
1553
|
+
|
1554
|
+
that gives:
|
1555
|
+
|
1556
|
+
lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
|
1557
|
+
|
1558
|
+
lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
|
1559
|
+
|
1560
|
+
To get the line segment direction we want to get the angle the
|
1561
|
+
eigenvector associated to the smallest eigenvalue. We have
|
1562
|
+
to solve for a,b in:
|
1563
|
+
|
1564
|
+
a.Ixx + b.Ixy = a.lambda2
|
1565
|
+
|
1566
|
+
a.Ixy + b.Iyy = b.lambda2
|
1567
|
+
|
1568
|
+
We want the angle theta = atan(b/a). It can be computed with
|
1569
|
+
any of the two equations:
|
1570
|
+
|
1571
|
+
theta = atan( (lambda2-Ixx) / Ixy )
|
1572
|
+
|
1573
|
+
or
|
1574
|
+
|
1575
|
+
theta = atan( Ixy / (lambda2-Iyy) )
|
1576
|
+
|
1577
|
+
When |Ixx| > |Iyy| we use the first, otherwise the second (just to
|
1578
|
+
get better numeric precision).
|
1579
|
+
*/
|
1580
|
+
static double get_theta( struct point * reg, int reg_size, double x, double y,
|
1581
|
+
image_double modgrad, double reg_angle, double prec )
|
1582
|
+
{
|
1583
|
+
double lambda,theta,weight;
|
1584
|
+
double Ixx = 0.0;
|
1585
|
+
double Iyy = 0.0;
|
1586
|
+
double Ixy = 0.0;
|
1587
|
+
int i;
|
1588
|
+
|
1589
|
+
/* check parameters */
|
1590
|
+
if( reg == NULL ) error("get_theta: invalid region.");
|
1591
|
+
if( reg_size <= 1 ) error("get_theta: region size <= 1.");
|
1592
|
+
if( modgrad == NULL || modgrad->data == NULL )
|
1593
|
+
error("get_theta: invalid 'modgrad'.");
|
1594
|
+
if( prec < 0.0 ) error("get_theta: 'prec' must be positive.");
|
1595
|
+
|
1596
|
+
/* compute inertia matrix */
|
1597
|
+
for(i=0; i<reg_size; i++)
|
1598
|
+
{
|
1599
|
+
weight = modgrad->data[ reg[i].x + reg[i].y * modgrad->xsize ];
|
1600
|
+
Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight;
|
1601
|
+
Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight;
|
1602
|
+
Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight;
|
1603
|
+
}
|
1604
|
+
if( double_equal(Ixx,0.0) && double_equal(Iyy,0.0) && double_equal(Ixy,0.0) )
|
1605
|
+
error("get_theta: null inertia matrix.");
|
1606
|
+
|
1607
|
+
/* compute smallest eigenvalue */
|
1608
|
+
lambda = 0.5 * ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) );
|
1609
|
+
|
1610
|
+
/* compute angle */
|
1611
|
+
theta = fabs(Ixx)>fabs(Iyy) ? atan2(lambda-Ixx,Ixy) : atan2(Ixy,lambda-Iyy);
|
1612
|
+
|
1613
|
+
/* The previous procedure doesn't cares about orientation,
|
1614
|
+
so it could be wrong by 180 degrees. Here is corrected if necessary. */
|
1615
|
+
if( angle_diff(theta,reg_angle) > prec ) theta += M_PI;
|
1616
|
+
|
1617
|
+
return theta;
|
1618
|
+
}
|
1619
|
+
|
1620
|
+
/*----------------------------------------------------------------------------*/
|
1621
|
+
/** Computes a rectangle that covers a region of points.
|
1622
|
+
*/
|
1623
|
+
static void region2rect( struct point * reg, int reg_size,
|
1624
|
+
image_double modgrad, double reg_angle,
|
1625
|
+
double prec, double p, struct rect * rec )
|
1626
|
+
{
|
1627
|
+
double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max;
|
1628
|
+
int i;
|
1629
|
+
|
1630
|
+
/* check parameters */
|
1631
|
+
if( reg == NULL ) error("region2rect: invalid region.");
|
1632
|
+
if( reg_size <= 1 ) error("region2rect: region size <= 1.");
|
1633
|
+
if( modgrad == NULL || modgrad->data == NULL )
|
1634
|
+
error("region2rect: invalid image 'modgrad'.");
|
1635
|
+
if( rec == NULL ) error("region2rect: invalid 'rec'.");
|
1636
|
+
|
1637
|
+
/* center of the region:
|
1638
|
+
|
1639
|
+
It is computed as the weighted sum of the coordinates
|
1640
|
+
of all the pixels in the region. The norm of the gradient
|
1641
|
+
is used as the weight of a pixel. The sum is as follows:
|
1642
|
+
cx = \sum_i G(i).x_i
|
1643
|
+
cy = \sum_i G(i).y_i
|
1644
|
+
where G(i) is the norm of the gradient of pixel i
|
1645
|
+
and x_i,y_i are its coordinates.
|
1646
|
+
*/
|
1647
|
+
x = y = sum = 0.0;
|
1648
|
+
for(i=0; i<reg_size; i++)
|
1649
|
+
{
|
1650
|
+
weight = modgrad->data[ reg[i].x + reg[i].y * modgrad->xsize ];
|
1651
|
+
x += (double) reg[i].x * weight;
|
1652
|
+
y += (double) reg[i].y * weight;
|
1653
|
+
sum += weight;
|
1654
|
+
}
|
1655
|
+
if( sum <= 0.0 ) error("region2rect: weights sum equal to zero.");
|
1656
|
+
x /= sum;
|
1657
|
+
y /= sum;
|
1658
|
+
|
1659
|
+
/* theta */
|
1660
|
+
theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec);
|
1661
|
+
|
1662
|
+
/* length and width:
|
1663
|
+
|
1664
|
+
'l' and 'w' are computed as the distance from the center of the
|
1665
|
+
region to pixel i, projected along the rectangle axis (dx,dy) and
|
1666
|
+
to the orthogonal axis (-dy,dx), respectively.
|
1667
|
+
|
1668
|
+
The length of the rectangle goes from l_min to l_max, where l_min
|
1669
|
+
and l_max are the minimum and maximum values of l in the region.
|
1670
|
+
Analogously, the width is selected from w_min to w_max, where
|
1671
|
+
w_min and w_max are the minimum and maximum of w for the pixels
|
1672
|
+
in the region.
|
1673
|
+
*/
|
1674
|
+
dx = cos(theta);
|
1675
|
+
dy = sin(theta);
|
1676
|
+
l_min = l_max = w_min = w_max = 0.0;
|
1677
|
+
for(i=0; i<reg_size; i++)
|
1678
|
+
{
|
1679
|
+
l = ( (double) reg[i].x - x) * dx + ( (double) reg[i].y - y) * dy;
|
1680
|
+
w = -( (double) reg[i].x - x) * dy + ( (double) reg[i].y - y) * dx;
|
1681
|
+
|
1682
|
+
if( l > l_max ) l_max = l;
|
1683
|
+
if( l < l_min ) l_min = l;
|
1684
|
+
if( w > w_max ) w_max = w;
|
1685
|
+
if( w < w_min ) w_min = w;
|
1686
|
+
}
|
1687
|
+
|
1688
|
+
/* store values */
|
1689
|
+
rec->x1 = x + l_min * dx;
|
1690
|
+
rec->y1 = y + l_min * dy;
|
1691
|
+
rec->x2 = x + l_max * dx;
|
1692
|
+
rec->y2 = y + l_max * dy;
|
1693
|
+
rec->width = w_max - w_min;
|
1694
|
+
rec->x = x;
|
1695
|
+
rec->y = y;
|
1696
|
+
rec->theta = theta;
|
1697
|
+
rec->dx = dx;
|
1698
|
+
rec->dy = dy;
|
1699
|
+
rec->prec = prec;
|
1700
|
+
rec->p = p;
|
1701
|
+
|
1702
|
+
/* we impose a minimal width of one pixel
|
1703
|
+
|
1704
|
+
A sharp horizontal or vertical step would produce a perfectly
|
1705
|
+
horizontal or vertical region. The width computed would be
|
1706
|
+
zero. But that corresponds to a one pixels width transition in
|
1707
|
+
the image.
|
1708
|
+
*/
|
1709
|
+
if( rec->width < 1.0 ) rec->width = 1.0;
|
1710
|
+
}
|
1711
|
+
|
1712
|
+
/*----------------------------------------------------------------------------*/
|
1713
|
+
/** Build a region of pixels that share the same angle, up to a
|
1714
|
+
tolerance 'prec', starting at point (x,y).
|
1715
|
+
*/
|
1716
|
+
static void region_grow( int x, int y, image_double angles, struct point * reg,
|
1717
|
+
int * reg_size, double * reg_angle, image_char used,
|
1718
|
+
double prec )
|
1719
|
+
{
|
1720
|
+
double sumdx,sumdy;
|
1721
|
+
int xx,yy,i;
|
1722
|
+
|
1723
|
+
/* check parameters */
|
1724
|
+
if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
|
1725
|
+
error("region_grow: (x,y) out of the image.");
|
1726
|
+
if( angles == NULL || angles->data == NULL )
|
1727
|
+
error("region_grow: invalid image 'angles'.");
|
1728
|
+
if( reg == NULL ) error("region_grow: invalid 'reg'.");
|
1729
|
+
if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'.");
|
1730
|
+
if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'.");
|
1731
|
+
if( used == NULL || used->data == NULL )
|
1732
|
+
error("region_grow: invalid image 'used'.");
|
1733
|
+
|
1734
|
+
/* first point of the region */
|
1735
|
+
*reg_size = 1;
|
1736
|
+
reg[0].x = x;
|
1737
|
+
reg[0].y = y;
|
1738
|
+
*reg_angle = angles->data[x+y*angles->xsize]; /* region's angle */
|
1739
|
+
sumdx = cos(*reg_angle);
|
1740
|
+
sumdy = sin(*reg_angle);
|
1741
|
+
used->data[x+y*used->xsize] = USED;
|
1742
|
+
|
1743
|
+
/* try neighbors as new region points */
|
1744
|
+
for(i=0; i<*reg_size; i++)
|
1745
|
+
for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++)
|
1746
|
+
for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++)
|
1747
|
+
if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize &&
|
1748
|
+
used->data[xx+yy*used->xsize] != USED &&
|
1749
|
+
isaligned(xx,yy,angles,*reg_angle,prec) )
|
1750
|
+
{
|
1751
|
+
/* add point */
|
1752
|
+
used->data[xx+yy*used->xsize] = USED;
|
1753
|
+
reg[*reg_size].x = xx;
|
1754
|
+
reg[*reg_size].y = yy;
|
1755
|
+
++(*reg_size);
|
1756
|
+
|
1757
|
+
/* update region's angle */
|
1758
|
+
sumdx += cos( angles->data[xx+yy*angles->xsize] );
|
1759
|
+
sumdy += sin( angles->data[xx+yy*angles->xsize] );
|
1760
|
+
*reg_angle = atan2(sumdy,sumdx);
|
1761
|
+
}
|
1762
|
+
}
|
1763
|
+
|
1764
|
+
/*----------------------------------------------------------------------------*/
|
1765
|
+
/** Try some rectangles variations to improve NFA value. Only if the
|
1766
|
+
rectangle is not meaningful (i.e., log_nfa <= log_eps).
|
1767
|
+
*/
|
1768
|
+
static double rect_improve( struct rect * rec, image_double angles,
|
1769
|
+
double logNT, double log_eps )
|
1770
|
+
{
|
1771
|
+
struct rect r;
|
1772
|
+
double log_nfa,log_nfa_new;
|
1773
|
+
double delta = 0.5;
|
1774
|
+
double delta_2 = delta / 2.0;
|
1775
|
+
int n;
|
1776
|
+
|
1777
|
+
log_nfa = rect_nfa(rec,angles,logNT);
|
1778
|
+
|
1779
|
+
if( log_nfa > log_eps ) return log_nfa;
|
1780
|
+
|
1781
|
+
/* try finer precisions */
|
1782
|
+
rect_copy(rec,&r);
|
1783
|
+
for(n=0; n<5; n++)
|
1784
|
+
{
|
1785
|
+
r.p /= 2.0;
|
1786
|
+
r.prec = r.p * M_PI;
|
1787
|
+
log_nfa_new = rect_nfa(&r,angles,logNT);
|
1788
|
+
if( log_nfa_new > log_nfa )
|
1789
|
+
{
|
1790
|
+
log_nfa = log_nfa_new;
|
1791
|
+
rect_copy(&r,rec);
|
1792
|
+
}
|
1793
|
+
}
|
1794
|
+
|
1795
|
+
if( log_nfa > log_eps ) return log_nfa;
|
1796
|
+
|
1797
|
+
/* try to reduce width */
|
1798
|
+
rect_copy(rec,&r);
|
1799
|
+
for(n=0; n<5; n++)
|
1800
|
+
{
|
1801
|
+
if( (r.width - delta) >= 0.5 )
|
1802
|
+
{
|
1803
|
+
r.width -= delta;
|
1804
|
+
log_nfa_new = rect_nfa(&r,angles,logNT);
|
1805
|
+
if( log_nfa_new > log_nfa )
|
1806
|
+
{
|
1807
|
+
rect_copy(&r,rec);
|
1808
|
+
log_nfa = log_nfa_new;
|
1809
|
+
}
|
1810
|
+
}
|
1811
|
+
}
|
1812
|
+
|
1813
|
+
if( log_nfa > log_eps ) return log_nfa;
|
1814
|
+
|
1815
|
+
/* try to reduce one side of the rectangle */
|
1816
|
+
rect_copy(rec,&r);
|
1817
|
+
for(n=0; n<5; n++)
|
1818
|
+
{
|
1819
|
+
if( (r.width - delta) >= 0.5 )
|
1820
|
+
{
|
1821
|
+
r.x1 += -r.dy * delta_2;
|
1822
|
+
r.y1 += r.dx * delta_2;
|
1823
|
+
r.x2 += -r.dy * delta_2;
|
1824
|
+
r.y2 += r.dx * delta_2;
|
1825
|
+
r.width -= delta;
|
1826
|
+
log_nfa_new = rect_nfa(&r,angles,logNT);
|
1827
|
+
if( log_nfa_new > log_nfa )
|
1828
|
+
{
|
1829
|
+
rect_copy(&r,rec);
|
1830
|
+
log_nfa = log_nfa_new;
|
1831
|
+
}
|
1832
|
+
}
|
1833
|
+
}
|
1834
|
+
|
1835
|
+
if( log_nfa > log_eps ) return log_nfa;
|
1836
|
+
|
1837
|
+
/* try to reduce the other side of the rectangle */
|
1838
|
+
rect_copy(rec,&r);
|
1839
|
+
for(n=0; n<5; n++)
|
1840
|
+
{
|
1841
|
+
if( (r.width - delta) >= 0.5 )
|
1842
|
+
{
|
1843
|
+
r.x1 -= -r.dy * delta_2;
|
1844
|
+
r.y1 -= r.dx * delta_2;
|
1845
|
+
r.x2 -= -r.dy * delta_2;
|
1846
|
+
r.y2 -= r.dx * delta_2;
|
1847
|
+
r.width -= delta;
|
1848
|
+
log_nfa_new = rect_nfa(&r,angles,logNT);
|
1849
|
+
if( log_nfa_new > log_nfa )
|
1850
|
+
{
|
1851
|
+
rect_copy(&r,rec);
|
1852
|
+
log_nfa = log_nfa_new;
|
1853
|
+
}
|
1854
|
+
}
|
1855
|
+
}
|
1856
|
+
|
1857
|
+
if( log_nfa > log_eps ) return log_nfa;
|
1858
|
+
|
1859
|
+
/* try even finer precisions */
|
1860
|
+
rect_copy(rec,&r);
|
1861
|
+
for(n=0; n<5; n++)
|
1862
|
+
{
|
1863
|
+
r.p /= 2.0;
|
1864
|
+
r.prec = r.p * M_PI;
|
1865
|
+
log_nfa_new = rect_nfa(&r,angles,logNT);
|
1866
|
+
if( log_nfa_new > log_nfa )
|
1867
|
+
{
|
1868
|
+
log_nfa = log_nfa_new;
|
1869
|
+
rect_copy(&r,rec);
|
1870
|
+
}
|
1871
|
+
}
|
1872
|
+
|
1873
|
+
return log_nfa;
|
1874
|
+
}
|
1875
|
+
|
1876
|
+
/*----------------------------------------------------------------------------*/
|
1877
|
+
/** Reduce the region size, by elimination the points far from the
|
1878
|
+
starting point, until that leads to rectangle with the right
|
1879
|
+
density of region points or to discard the region if too small.
|
1880
|
+
*/
|
1881
|
+
static int reduce_region_radius( struct point * reg, int * reg_size,
|
1882
|
+
image_double modgrad, double reg_angle,
|
1883
|
+
double prec, double p, struct rect * rec,
|
1884
|
+
image_char used, image_double angles,
|
1885
|
+
double density_th )
|
1886
|
+
{
|
1887
|
+
double density,rad1,rad2,rad,xc,yc;
|
1888
|
+
int i;
|
1889
|
+
|
1890
|
+
/* check parameters */
|
1891
|
+
if( reg == NULL ) error("reduce_region_radius: invalid pointer 'reg'.");
|
1892
|
+
if( reg_size == NULL )
|
1893
|
+
error("reduce_region_radius: invalid pointer 'reg_size'.");
|
1894
|
+
if( prec < 0.0 ) error("reduce_region_radius: 'prec' must be positive.");
|
1895
|
+
if( rec == NULL ) error("reduce_region_radius: invalid pointer 'rec'.");
|
1896
|
+
if( used == NULL || used->data == NULL )
|
1897
|
+
error("reduce_region_radius: invalid image 'used'.");
|
1898
|
+
if( angles == NULL || angles->data == NULL )
|
1899
|
+
error("reduce_region_radius: invalid image 'angles'.");
|
1900
|
+
|
1901
|
+
/* compute region points density */
|
1902
|
+
density = (double) *reg_size /
|
1903
|
+
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
|
1904
|
+
|
1905
|
+
/* if the density criterion is satisfied there is nothing to do */
|
1906
|
+
if( density >= density_th ) return TRUE;
|
1907
|
+
|
1908
|
+
/* compute region's radius */
|
1909
|
+
xc = (double) reg[0].x;
|
1910
|
+
yc = (double) reg[0].y;
|
1911
|
+
rad1 = dist( xc, yc, rec->x1, rec->y1 );
|
1912
|
+
rad2 = dist( xc, yc, rec->x2, rec->y2 );
|
1913
|
+
rad = rad1 > rad2 ? rad1 : rad2;
|
1914
|
+
|
1915
|
+
/* while the density criterion is not satisfied, remove farther pixels */
|
1916
|
+
while( density < density_th )
|
1917
|
+
{
|
1918
|
+
rad *= 0.75; /* reduce region's radius to 75% of its value */
|
1919
|
+
|
1920
|
+
/* remove points from the region and update 'used' map */
|
1921
|
+
for(i=0; i<*reg_size; i++)
|
1922
|
+
if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad )
|
1923
|
+
{
|
1924
|
+
/* point not kept, mark it as NOTUSED */
|
1925
|
+
used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
|
1926
|
+
/* remove point from the region */
|
1927
|
+
reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */
|
1928
|
+
reg[i].y = reg[*reg_size-1].y;
|
1929
|
+
--(*reg_size);
|
1930
|
+
--i; /* to avoid skipping one point */
|
1931
|
+
}
|
1932
|
+
|
1933
|
+
/* reject if the region is too small.
|
1934
|
+
2 is the minimal region size for 'region2rect' to work. */
|
1935
|
+
if( *reg_size < 2 ) return FALSE;
|
1936
|
+
|
1937
|
+
/* re-compute rectangle */
|
1938
|
+
region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
|
1939
|
+
|
1940
|
+
/* re-compute region points density */
|
1941
|
+
density = (double) *reg_size /
|
1942
|
+
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
|
1943
|
+
}
|
1944
|
+
|
1945
|
+
/* if this point is reached, the density criterion is satisfied */
|
1946
|
+
return TRUE;
|
1947
|
+
}
|
1948
|
+
|
1949
|
+
/*----------------------------------------------------------------------------*/
|
1950
|
+
/** Refine a rectangle.
|
1951
|
+
|
1952
|
+
For that, an estimation of the angle tolerance is performed by the
|
1953
|
+
standard deviation of the angle at points near the region's
|
1954
|
+
starting point. Then, a new region is grown starting from the same
|
1955
|
+
point, but using the estimated angle tolerance. If this fails to
|
1956
|
+
produce a rectangle with the right density of region points,
|
1957
|
+
'reduce_region_radius' is called to try to satisfy this condition.
|
1958
|
+
*/
|
1959
|
+
static int refine( struct point * reg, int * reg_size, image_double modgrad,
|
1960
|
+
double reg_angle, double prec, double p, struct rect * rec,
|
1961
|
+
image_char used, image_double angles, double density_th )
|
1962
|
+
{
|
1963
|
+
double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum;
|
1964
|
+
int i,n;
|
1965
|
+
|
1966
|
+
/* check parameters */
|
1967
|
+
if( reg == NULL ) error("refine: invalid pointer 'reg'.");
|
1968
|
+
if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'.");
|
1969
|
+
if( prec < 0.0 ) error("refine: 'prec' must be positive.");
|
1970
|
+
if( rec == NULL ) error("refine: invalid pointer 'rec'.");
|
1971
|
+
if( used == NULL || used->data == NULL )
|
1972
|
+
error("refine: invalid image 'used'.");
|
1973
|
+
if( angles == NULL || angles->data == NULL )
|
1974
|
+
error("refine: invalid image 'angles'.");
|
1975
|
+
|
1976
|
+
/* compute region points density */
|
1977
|
+
density = (double) *reg_size /
|
1978
|
+
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
|
1979
|
+
|
1980
|
+
/* if the density criterion is satisfied there is nothing to do */
|
1981
|
+
if( density >= density_th ) return TRUE;
|
1982
|
+
|
1983
|
+
/*------ First try: reduce angle tolerance ------*/
|
1984
|
+
|
1985
|
+
/* compute the new mean angle and tolerance */
|
1986
|
+
xc = (double) reg[0].x;
|
1987
|
+
yc = (double) reg[0].y;
|
1988
|
+
ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ];
|
1989
|
+
sum = s_sum = 0.0;
|
1990
|
+
n = 0;
|
1991
|
+
for(i=0; i<*reg_size; i++)
|
1992
|
+
{
|
1993
|
+
used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
|
1994
|
+
if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width )
|
1995
|
+
{
|
1996
|
+
angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ];
|
1997
|
+
ang_d = angle_diff_signed(angle,ang_c);
|
1998
|
+
sum += ang_d;
|
1999
|
+
s_sum += ang_d * ang_d;
|
2000
|
+
++n;
|
2001
|
+
}
|
2002
|
+
}
|
2003
|
+
mean_angle = sum / (double) n;
|
2004
|
+
tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n
|
2005
|
+
+ mean_angle*mean_angle ); /* 2 * standard deviation */
|
2006
|
+
|
2007
|
+
/* find a new region from the same starting point and new angle tolerance */
|
2008
|
+
region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau);
|
2009
|
+
|
2010
|
+
/* if the region is too small, reject */
|
2011
|
+
if( *reg_size < 2 ) return FALSE;
|
2012
|
+
|
2013
|
+
/* re-compute rectangle */
|
2014
|
+
region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
|
2015
|
+
|
2016
|
+
/* re-compute region points density */
|
2017
|
+
density = (double) *reg_size /
|
2018
|
+
( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
|
2019
|
+
|
2020
|
+
/*------ Second try: reduce region radius ------*/
|
2021
|
+
if( density < density_th )
|
2022
|
+
return reduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p,
|
2023
|
+
rec, used, angles, density_th );
|
2024
|
+
|
2025
|
+
/* if this point is reached, the density criterion is satisfied */
|
2026
|
+
return TRUE;
|
2027
|
+
}
|
2028
|
+
|
2029
|
+
|
2030
|
+
/*----------------------------------------------------------------------------*/
|
2031
|
+
/*-------------------------- Line Segment Detector ---------------------------*/
|
2032
|
+
/*----------------------------------------------------------------------------*/
|
2033
|
+
|
2034
|
+
/*----------------------------------------------------------------------------*/
|
2035
|
+
/** LSD full interface.
|
2036
|
+
*/
|
2037
|
+
double * LineSegmentDetection( int * n_out,
|
2038
|
+
double * img, int X, int Y,
|
2039
|
+
double scale, double sigma_scale, double quant,
|
2040
|
+
double ang_th, double log_eps, double density_th,
|
2041
|
+
int n_bins,
|
2042
|
+
int ** reg_img, int * reg_x, int * reg_y )
|
2043
|
+
{
|
2044
|
+
image_double image;
|
2045
|
+
ntuple_list out = new_ntuple_list(7);
|
2046
|
+
double * return_value;
|
2047
|
+
image_double scaled_image,angles,modgrad;
|
2048
|
+
image_char used;
|
2049
|
+
image_int region = NULL;
|
2050
|
+
struct coorlist * list_p;
|
2051
|
+
void * mem_p;
|
2052
|
+
struct rect rec;
|
2053
|
+
struct point * reg;
|
2054
|
+
int reg_size,min_reg_size,i;
|
2055
|
+
unsigned int xsize,ysize;
|
2056
|
+
double rho,reg_angle,prec,p,log_nfa,logNT;
|
2057
|
+
int ls_count = 0; /* line segments are numbered 1,2,3,... */
|
2058
|
+
|
2059
|
+
|
2060
|
+
/* check parameters */
|
2061
|
+
if( img == NULL || X <= 0 || Y <= 0 ) error("invalid image input.");
|
2062
|
+
if( scale <= 0.0 ) error("'scale' value must be positive.");
|
2063
|
+
if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
|
2064
|
+
if( quant < 0.0 ) error("'quant' value must be positive.");
|
2065
|
+
if( ang_th <= 0.0 || ang_th >= 180.0 )
|
2066
|
+
error("'ang_th' value must be in the range (0,180).");
|
2067
|
+
if( density_th < 0.0 || density_th > 1.0 )
|
2068
|
+
error("'density_th' value must be in the range [0,1].");
|
2069
|
+
if( n_bins <= 0 ) error("'n_bins' value must be positive.");
|
2070
|
+
|
2071
|
+
|
2072
|
+
/* angle tolerance */
|
2073
|
+
prec = M_PI * ang_th / 180.0;
|
2074
|
+
p = ang_th / 180.0;
|
2075
|
+
rho = quant / sin(prec); /* gradient magnitude threshold */
|
2076
|
+
|
2077
|
+
|
2078
|
+
/* load and scale image (if necessary) and compute angle at each pixel */
|
2079
|
+
image = new_image_double_ptr( (unsigned int) X, (unsigned int) Y, img );
|
2080
|
+
if( scale != 1.0 )
|
2081
|
+
{
|
2082
|
+
scaled_image = gaussian_sampler( image, scale, sigma_scale );
|
2083
|
+
angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
|
2084
|
+
&modgrad, (unsigned int) n_bins );
|
2085
|
+
free_image_double(scaled_image);
|
2086
|
+
}
|
2087
|
+
else
|
2088
|
+
angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
|
2089
|
+
(unsigned int) n_bins );
|
2090
|
+
xsize = angles->xsize;
|
2091
|
+
ysize = angles->ysize;
|
2092
|
+
|
2093
|
+
/* Number of Tests - NT
|
2094
|
+
|
2095
|
+
The theoretical number of tests is Np.(XY)^(5/2)
|
2096
|
+
where X and Y are number of columns and rows of the image.
|
2097
|
+
Np corresponds to the number of angle precisions considered.
|
2098
|
+
As the procedure 'rect_improve' tests 5 times to halve the
|
2099
|
+
angle precision, and 5 more times after improving other factors,
|
2100
|
+
11 different precision values are potentially tested. Thus,
|
2101
|
+
the number of tests is
|
2102
|
+
11 * (X*Y)^(5/2)
|
2103
|
+
whose logarithm value is
|
2104
|
+
log10(11) + 5/2 * (log10(X) + log10(Y)).
|
2105
|
+
*/
|
2106
|
+
logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0
|
2107
|
+
+ log10(11.0);
|
2108
|
+
min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
|
2109
|
+
that can give a meaningful event */
|
2110
|
+
|
2111
|
+
|
2112
|
+
/* initialize some structures */
|
2113
|
+
if( reg_img != NULL && reg_x != NULL && reg_y != NULL ) /* save region data */
|
2114
|
+
region = new_image_int_ini(angles->xsize,angles->ysize,0);
|
2115
|
+
used = new_image_char_ini(xsize,ysize,NOTUSED);
|
2116
|
+
reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(struct point) );
|
2117
|
+
if( reg == NULL ) error("not enough memory!");
|
2118
|
+
|
2119
|
+
|
2120
|
+
/* search for line segments */
|
2121
|
+
for(; list_p != NULL; list_p = list_p->next )
|
2122
|
+
if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
|
2123
|
+
angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
|
2124
|
+
/* there is no risk of double comparison problems here
|
2125
|
+
because we are only interested in the exact NOTDEF value */
|
2126
|
+
{
|
2127
|
+
/* find the region of connected point and ~equal angle */
|
2128
|
+
region_grow( list_p->x, list_p->y, angles, reg, ®_size,
|
2129
|
+
®_angle, used, prec );
|
2130
|
+
|
2131
|
+
/* reject small regions */
|
2132
|
+
if( reg_size < min_reg_size ) continue;
|
2133
|
+
|
2134
|
+
/* construct rectangular approximation for the region */
|
2135
|
+
region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
|
2136
|
+
|
2137
|
+
/* Check if the rectangle exceeds the minimal density of
|
2138
|
+
region points. If not, try to improve the region.
|
2139
|
+
The rectangle will be rejected if the final one does
|
2140
|
+
not fulfill the minimal density condition.
|
2141
|
+
This is an addition to the original LSD algorithm published in
|
2142
|
+
"LSD: A Fast Line Segment Detector with a False Detection Control"
|
2143
|
+
by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
|
2144
|
+
The original algorithm is obtained with density_th = 0.0.
|
2145
|
+
*/
|
2146
|
+
if( !refine( reg, ®_size, modgrad, reg_angle,
|
2147
|
+
prec, p, &rec, used, angles, density_th ) ) continue;
|
2148
|
+
|
2149
|
+
/* compute NFA value */
|
2150
|
+
log_nfa = rect_improve(&rec,angles,logNT,log_eps);
|
2151
|
+
if( log_nfa <= log_eps ) continue;
|
2152
|
+
|
2153
|
+
/* A New Line Segment was found! */
|
2154
|
+
++ls_count; /* increase line segment counter */
|
2155
|
+
|
2156
|
+
/*
|
2157
|
+
The gradient was computed with a 2x2 mask, its value corresponds to
|
2158
|
+
points with an offset of (0.5,0.5), that should be added to output.
|
2159
|
+
The coordinates origin is at the center of pixel (0,0).
|
2160
|
+
*/
|
2161
|
+
rec.x1 += 0.5; rec.y1 += 0.5;
|
2162
|
+
rec.x2 += 0.5; rec.y2 += 0.5;
|
2163
|
+
|
2164
|
+
/* scale the result values if a subsampling was performed */
|
2165
|
+
if( scale != 1.0 )
|
2166
|
+
{
|
2167
|
+
rec.x1 /= scale; rec.y1 /= scale;
|
2168
|
+
rec.x2 /= scale; rec.y2 /= scale;
|
2169
|
+
rec.width /= scale;
|
2170
|
+
}
|
2171
|
+
|
2172
|
+
/* add line segment found to output */
|
2173
|
+
add_7tuple( out, rec.x1, rec.y1, rec.x2, rec.y2,
|
2174
|
+
rec.width, rec.p, log_nfa );
|
2175
|
+
|
2176
|
+
/* add region number to 'region' image if needed */
|
2177
|
+
if( region != NULL )
|
2178
|
+
for(i=0; i<reg_size; i++)
|
2179
|
+
region->data[ reg[i].x + reg[i].y * region->xsize ] = ls_count;
|
2180
|
+
}
|
2181
|
+
|
2182
|
+
|
2183
|
+
/* free memory */
|
2184
|
+
free( (void *) image ); /* only the double_image structure should be freed,
|
2185
|
+
the data pointer was provided to this functions
|
2186
|
+
and should not be destroyed. */
|
2187
|
+
free_image_double(angles);
|
2188
|
+
free_image_double(modgrad);
|
2189
|
+
free_image_char(used);
|
2190
|
+
free( (void *) reg );
|
2191
|
+
free( (void *) mem_p );
|
2192
|
+
|
2193
|
+
/* return the result */
|
2194
|
+
if( reg_img != NULL && reg_x != NULL && reg_y != NULL )
|
2195
|
+
{
|
2196
|
+
if( region == NULL ) error("'region' should be a valid image.");
|
2197
|
+
*reg_img = region->data;
|
2198
|
+
if( region->xsize > (unsigned int) INT_MAX ||
|
2199
|
+
region->xsize > (unsigned int) INT_MAX )
|
2200
|
+
error("region image to big to fit in INT sizes.");
|
2201
|
+
*reg_x = (int) (region->xsize);
|
2202
|
+
*reg_y = (int) (region->ysize);
|
2203
|
+
|
2204
|
+
/* free the 'region' structure.
|
2205
|
+
we cannot use the function 'free_image_int' because we need to keep
|
2206
|
+
the memory with the image data to be returned by this function. */
|
2207
|
+
free( (void *) region );
|
2208
|
+
}
|
2209
|
+
if( out->size > (unsigned int) INT_MAX )
|
2210
|
+
error("too many detections to fit in an INT.");
|
2211
|
+
*n_out = (int) (out->size);
|
2212
|
+
|
2213
|
+
return_value = out->values;
|
2214
|
+
free( (void *) out ); /* only the 'ntuple_list' structure must be freed,
|
2215
|
+
but the 'values' pointer must be keep to return
|
2216
|
+
as a result. */
|
2217
|
+
|
2218
|
+
return return_value;
|
2219
|
+
}
|
2220
|
+
|
2221
|
+
/*----------------------------------------------------------------------------*/
|
2222
|
+
/** LSD Simple Interface with Scale and Region output.
|
2223
|
+
*/
|
2224
|
+
double * lsd_scale_region( int * n_out,
|
2225
|
+
double * img, int X, int Y, double scale,
|
2226
|
+
int ** reg_img, int * reg_x, int * reg_y )
|
2227
|
+
{
|
2228
|
+
/* LSD parameters */
|
2229
|
+
double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as
|
2230
|
+
sigma = sigma_scale/scale. */
|
2231
|
+
double quant = 2.0; /* Bound to the quantization error on the
|
2232
|
+
gradient norm. */
|
2233
|
+
double ang_th = 22.5; /* Gradient angle tolerance in degrees. */
|
2234
|
+
double log_eps = 0.0; /* Detection threshold: -log10(NFA) > log_eps */
|
2235
|
+
double density_th = 0.7; /* Minimal density of region points in rectangle. */
|
2236
|
+
int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient
|
2237
|
+
modulus. */
|
2238
|
+
|
2239
|
+
return LineSegmentDetection( n_out, img, X, Y, scale, sigma_scale, quant,
|
2240
|
+
ang_th, log_eps, density_th, n_bins,
|
2241
|
+
reg_img, reg_x, reg_y );
|
2242
|
+
}
|
2243
|
+
|
2244
|
+
/*----------------------------------------------------------------------------*/
|
2245
|
+
/** LSD Simple Interface with Scale.
|
2246
|
+
*/
|
2247
|
+
double * lsd_scale(int * n_out, double * img, int X, int Y, double scale)
|
2248
|
+
{
|
2249
|
+
return lsd_scale_region(n_out,img,X,Y,scale,NULL,NULL,NULL);
|
2250
|
+
}
|
2251
|
+
|
2252
|
+
/*----------------------------------------------------------------------------*/
|
2253
|
+
/** LSD Simple Interface.
|
2254
|
+
*/
|
2255
|
+
double * lsd(int * n_out, double * img, int X, int Y)
|
2256
|
+
{
|
2257
|
+
/* LSD parameters */
|
2258
|
+
double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */
|
2259
|
+
|
2260
|
+
return lsd_scale(n_out,img,X,Y,scale);
|
2261
|
+
|
2262
|
+
|
2263
|
+
}
|
2264
|
+
/*----------------------------------------------------------------------------*/
|
2265
|
+
|
2266
|
+
/***** added by manuel aristaran ****/
|
2267
|
+
|
2268
|
+
void free_values(double * p) {
|
2269
|
+
free((void *) p);
|
2270
|
+
}
|