├── example
├── addons.make
├── bin
│ └── data
│ │ ├── dc_1.jpg
│ │ ├── floor_hunt.jpg
│ │ └── dc_1_Expected-Result.jpg
├── icon.rc
├── src
│ ├── main.cpp
│ ├── testApp.h
│ └── testApp.cpp
├── example.workspace
└── example.cbp
├── ofxaddons_thumbnail.png
├── .gitattributes
├── LICENSE.txt
├── README.md
├── .gitignore
├── src
├── ofxCorrectPerspective.h
└── ofxCorrectPerspective.cpp
└── libs
└── lsd
├── lsd.h
└── lsd.c
/example/addons.make:
--------------------------------------------------------------------------------
1 | ofxOpenCv
2 | ofxCorrectPerspective
3 | ofxCv
4 |
--------------------------------------------------------------------------------
/ofxaddons_thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harisusmani/ofxCorrectPerspective/HEAD/ofxaddons_thumbnail.png
--------------------------------------------------------------------------------
/example/bin/data/dc_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harisusmani/ofxCorrectPerspective/HEAD/example/bin/data/dc_1.jpg
--------------------------------------------------------------------------------
/example/bin/data/floor_hunt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harisusmani/ofxCorrectPerspective/HEAD/example/bin/data/floor_hunt.jpg
--------------------------------------------------------------------------------
/example/bin/data/dc_1_Expected-Result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harisusmani/ofxCorrectPerspective/HEAD/example/bin/data/dc_1_Expected-Result.jpg
--------------------------------------------------------------------------------
/example/icon.rc:
--------------------------------------------------------------------------------
1 | //TODO: figure out how to do debug and release icons
2 | MAINICON ICON "../../../libs/openFrameworksCompiled/project/win_cb/icon.ico"
3 |
--------------------------------------------------------------------------------
/example/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "ofMain.h"
2 | #include "testApp.h"
3 |
4 | //========================================================================
5 | int main( ){
6 | ofSetupOpenGL(1024,768,OF_WINDOW); // <-------- setup the GL context
7 |
8 | // this kicks off the running of my app
9 | // can be OF_WINDOW or OF_FULLSCREEN
10 | // pass in width and height too:
11 | ofRunApp(new testApp());
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example/example.workspace:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The code in this repository is available under the MIT License.
2 |
3 | Copyright (c) 2014 M. Haris Usmani, www.harisusmani.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ofxCorrectPerspective
2 | =====================
3 |
4 | Introduction
5 | ------------
6 | openFrameworks addon that performs automatic 2d rectification of images. It’s based on work done in “Shape from Angle Regularity” by Zaheer et al., ECCV 2012.
7 | http://cvlab.lums.edu.pk/zaheer2012shape/
8 |
9 | Demo Video: https://vimeo.com/95204456
10 |
11 | Project Details: http://goo.gl/FvkFOh
12 |
13 | Other libraries used in this addon:
14 | - LSD (C), http://www.ipol.im/pub/art/2012/gjmr-lsd/
15 | - dlib (C++), http://dlib.net/
16 |
17 | License
18 | -------
19 | The code in this repository is available under the MIT License.
20 |
Copyright (c) 2014 M. Haris Usmani, www.harisusmani.com
21 |
22 | Installation
23 | ------------
24 | - Copy to your openFrameworks/addons folder
25 | - Generate a new project using projectGenerator
26 | - AFTER generating project, download and copy 'dlib' from http://dlib.net to ofxCorrectPerspective/src
27 |
28 | Dependencies
29 | ------------
30 | - ofxCV
31 | - ofxOpenCv
32 |
33 | Compatibility
34 | -------------
35 | openFrameworks V 0.8.0 Windows-CodeBlocks
36 |
37 | Version History
38 | ---------------
39 | #### v1.0 5/15/2014
40 | - initial version
41 |
--------------------------------------------------------------------------------
/example/src/testApp.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 M. Haris Usmani
3 | *
4 | * The code in this repository is available under the MIT License.
5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL
6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution.
7 | *
8 | * See https://github.com/harisusmani/ofxCorrectPerspective for documentation
9 | *
10 | */
11 | #pragma once
12 |
13 | #include "ofMain.h"
14 | #include "ofxCorrectPerspective.h"
15 |
16 | class testApp : public ofBaseApp{
17 |
18 | public:
19 | void setup();
20 | void update();
21 | void draw();
22 |
23 | void keyPressed(int key);
24 | void keyReleased(int key);
25 | void mouseMoved(int x, int y );
26 | void mouseDragged(int x, int y, int button);
27 | void mousePressed(int x, int y, int button);
28 | void mouseReleased(int x, int y, int button);
29 | void windowResized(int w, int h);
30 | void dragEvent(ofDragInfo dragInfo);
31 | void gotMessage(ofMessage msg);
32 |
33 | ofImage my_image;
34 | ofImage output_img;
35 | string filename;
36 | float focal_length;
37 | float sensor_width;
38 |
39 | ofxCorrectPerspective cPerspective;
40 | };
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Some general ignore patterns
2 |
3 | */bin/*
4 | !*/bin/data/
5 | # for bin folder in root
6 | /bin/*
7 | !/bin/data/
8 |
9 | build/
10 | obj/
11 | *.o
12 | Debug*/
13 | Release*/
14 | *.mode*
15 | *.app/
16 | *.pyc
17 | .svn/
18 |
19 | # IDE-specific ignore patterns (e.g. user-specific files)
20 |
21 | #XCode
22 | *.pbxuser
23 | *.perspective
24 | *.perspectivev3
25 | *.mode1v3
26 | *.mode2v3
27 | #XCode 4
28 | xcuserdata
29 | *.xcworkspace
30 |
31 | #Code::Blocks
32 | *.depend
33 | *.layout
34 | *.cbTemp
35 |
36 | #Visual Studio
37 | *.sdf
38 | *.opensdf
39 | *.suo
40 | ipch/
41 |
42 | #Eclipse
43 | .metadata
44 | local.properties
45 | .externalToolBuilders
46 |
47 | # OS-specific ignore patterns
48 |
49 | #Linux
50 | *~
51 | # KDE
52 | .directory
53 |
54 | #OSX
55 | .DS_Store
56 | *.swp
57 | *~.nib
58 | # Thumbnails
59 | ._*
60 |
61 | #Windows
62 | # Windows image file caches
63 | Thumbs.db
64 | # Folder config file
65 | Desktop.ini
66 |
67 | #Android
68 | .csettings
69 |
70 | # Packages
71 | # it's better to unpack these files and commit the raw source
72 | # git has its own built in compression methods
73 | *.7z
74 | *.dmg
75 | *.gz
76 | *.iso
77 | *.jar
78 | *.rar
79 | *.tar
80 | *.zip
81 |
82 | # Logs and databases
83 | *.log
84 | *.sql
85 | *.sqlite
86 |
--------------------------------------------------------------------------------
/example/src/testApp.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 M. Haris Usmani
3 | *
4 | * The code in this repository is available under the MIT License.
5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL
6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution.
7 | *
8 | * See https://github.com/harisusmani/ofxCorrectPerspective for documentation
9 | *
10 | */
11 | #include "testApp.h"
12 |
13 | //--------------------------------------------------------------
14 | void testApp::setup(){
15 | filename="dc_1.jpg"; //Input Image
16 | //filename="floor_hunt.jpg";
17 | /*cout << "Filename: ";
18 | cin >> filename;*/
19 |
20 | my_image.loadImage(filename);
21 | filename.insert(filename.size()-4,"_ofxOUT");
22 |
23 | //Import EXIF Data//...
24 | //Note: This is yet to be Implemented. Please enter Focal
25 | //Length and Sensor Width manually in same unit (mm).
26 | focal_length=22; //Canon EOS M for dc_1.jpg
27 | sensor_width=22.3;
28 |
29 | //focal_length=4; //Nexus 5, for floor_hunt.jpg
30 | //sensor_width=4.59;
31 |
32 | //...EXIF Data Completed//
33 | double alpha, beta;
34 | cPerspective.Rectify_Image(my_image,focal_length,sensor_width,alpha,beta);
35 | cout << alpha << " " << beta;
36 | }
37 |
38 | //--------------------------------------------------------------
39 | void testApp::update(){
40 |
41 | }
42 |
43 | //--------------------------------------------------------------
44 | void testApp::draw(){
45 | my_image.draw(0, 0);
46 | }
47 |
48 | //--------------------------------------------------------------
49 | void testApp::keyPressed(int key){
50 | output_img.grabScreen(0,0,my_image.width,my_image.height);
51 | output_img.saveImage(filename);
52 | }
53 |
54 | //--------------------------------------------------------------
55 | void testApp::keyReleased(int key){
56 |
57 | }
58 |
59 | //--------------------------------------------------------------
60 | void testApp::mouseMoved(int x, int y ){
61 |
62 | }
63 |
64 | //--------------------------------------------------------------
65 | void testApp::mouseDragged(int x, int y, int button){
66 |
67 | }
68 |
69 | //--------------------------------------------------------------
70 | void testApp::mousePressed(int x, int y, int button){
71 |
72 | }
73 |
74 | //--------------------------------------------------------------
75 | void testApp::mouseReleased(int x, int y, int button){
76 |
77 | }
78 |
79 | //--------------------------------------------------------------
80 | void testApp::windowResized(int w, int h){
81 |
82 | }
83 |
84 | //--------------------------------------------------------------
85 | void testApp::gotMessage(ofMessage msg){
86 |
87 | }
88 |
89 | //--------------------------------------------------------------
90 | void testApp::dragEvent(ofDragInfo dragInfo){
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/ofxCorrectPerspective.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 M. Haris Usmani
3 | *
4 | * The code in this repository is available under the MIT License.
5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL
6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution.
7 | *
8 | * See https://github.com/harisusmani/ofxCorrectPerspective for documentation
9 | *
10 | */
11 | #pragma once
12 |
13 | #include "ofxCv.h"
14 | #include "ofxCvImage.h"
15 |
16 | extern "C" {
17 | #include "../libs/lsd/lsd.h"
18 | }
19 |
20 | #include "dlib/optimization.h" //Make sure you place dlib at this path AFTER generating the project, NOT before.
21 | //Download dlib C++ Library from http://dlib.net
22 |
23 | //Then in dlib/optimization/optimization_bobyqa.h:
24 | //Comment Lines: 393, 659, 873, 922
25 | //Uncomment: 394, 660, 874, 923
26 | //This is required to ignore bobyqa failures in the RANSAC Process.
27 |
28 | class ofxCorrectPerspective{
29 |
30 | public:
31 | ofImage my_image;
32 | ofImage my_img_gray;
33 | ofImage output_img;
34 | string filename;
35 | bool talk;
36 |
37 | float focal_length;
38 | float sensor_width;
39 |
40 | bool resize_image;
41 | ofVec2f center;
42 | ofMatrix3x3 K;
43 |
44 | typedef std::pair mypair;
45 | bool comparator ( const mypair& l, const mypair& r);
46 |
47 | ofMesh mesh;
48 | vector > L; //Line Detected
49 |
50 | //For Solver
51 | typedef dlib::matrix column_vector;
52 |
53 | ofVec2f solveLinearSys(double a11,double a12,double a21,double a22,double b1,double b2);
54 | column_vector fitFunc4Lines(std::vector > L_vec,unsigned int r1,unsigned int r2,unsigned int r3,unsigned int r4, float f);
55 | void distFunc(std::vector >L_vec,ofxCorrectPerspective::column_vector modelX,float f,double thresh,std::vector & arIn,std::vector & acIn);
56 |
57 | void Rectify_Image(ofImage & my_image,double focal_length,double sensor_width, double & alpha, double & beta);
58 | };
59 |
60 | class cost_function
61 | {
62 | public:
63 |
64 | cost_function (std::vector > L_vec, double r1, double r2, double r3, double r4, double f)
65 | { //RUN ALL INITIALIZATIONS HERE!
66 |
67 | L = (cv::Mat_(3,4) << L_vec[0][r1], L_vec[0][r2], L_vec[0][r3], L_vec[0][r4],
68 | L_vec[1][r1], L_vec[1][r2], L_vec[1][r3], L_vec[1][r4],
69 | L_vec[2][r1], L_vec[2][r2], L_vec[2][r3], L_vec[2][r4]);
70 |
71 | K_mat = (cv::Mat_(3,3)<< f,0.0,0.0,0.0,f,0.0,0.0,0.0,1.0);
72 |
73 | }
74 |
75 | double operator() ( const ofxCorrectPerspective::column_vector& arg) const //THIS WILL BE CALLED REPEATREDLY!
76 | {
77 | //MAKE ROTATION MATRIX
78 | ofMatrix4x4 R=ofMatrix4x4::newRotationMatrix(arg(0)*180.0/PI, ofVec3f(-1, 0, 0), arg(1)*180.0/PI, ofVec3f(0, -1, 0), 0, ofVec3f(0, 0, -1));
79 | double m[3][3] = {{R(0,0), R(0,1), R(0,2)}, {R(1,0), R(1,1), R(1,2)}, {R(2,0), R(2,1), R(2,2)}};
80 | cv::Mat R_mat = cv::Mat(3, 3, CV_64F, m);
81 |
82 | cv::Mat K_c=K_mat.clone();
83 | K_c=K_c.inv();
84 | R_mat=R_mat.t();
85 | cv::Mat Hinv=K_mat*R_mat*K_c;
86 |
87 | Hinv=Hinv.t();
88 | cv::Mat Lp=Hinv*L;
89 | Lp.resize(2);
90 |
91 | cv::Mat Lp_t=Lp.clone();
92 | Lp_t=Lp_t.t();
93 |
94 | cv::Mat C=Lp_t*Lp;
95 | C=abs(C);
96 |
97 | double total_cost=C.at(2,0)+C.at(3,1)+C.at(0,2)+C.at(1,3);
98 | //cout << "Total Cost: " << total_cost << endl;
99 | return(total_cost);
100 | }
101 |
102 | private:
103 | cv::Mat L;
104 | cv::Mat K_mat;
105 | };
106 |
--------------------------------------------------------------------------------
/libs/lsd/lsd.h:
--------------------------------------------------------------------------------
1 | /*----------------------------------------------------------------------------
2 |
3 | LSD - Line Segment Detector on digital images
4 |
5 | Copyright 2007,2008,2009,2010 rafael grompone von gioi (grompone@gmail.com)
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU Affero General Public License as
9 | published by the Free Software Foundation, either version 3 of the
10 | License, or (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU Affero General Public License for more details.
16 |
17 | You should have received a copy of the GNU Affero General Public License
18 | along with this program. If not, see .
19 |
20 | ----------------------------------------------------------------------------*/
21 |
22 | /*----------------------------------------------------------------------------*/
23 | /** @file lsd.h
24 | @brief LSD module header
25 | @author rafael grompone von gioi (grompone@gmail.com)
26 | */
27 | /*----------------------------------------------------------------------------*/
28 | #ifndef LSD_HEADER
29 | #define LSD_HEADER
30 |
31 |
32 | /*----------------------------------------------------------------------------*/
33 | /*----------------------- 'list of n-tuple' data type ------------------------*/
34 | /*----------------------------------------------------------------------------*/
35 | /** @brief 'list of n-tuple' data type
36 |
37 | The i component, of the n-tuple number j, of an n-tuple list 'ntl'
38 | is accessed with:
39 |
40 | ntl->values[ i + j * ntl->dim ]
41 |
42 | The dimension of the n-tuple (n) is:
43 |
44 | ntl->dim
45 |
46 | The number of number of n-tuples in the list is:
47 |
48 | ntl->size
49 |
50 | The maximum number of n-tuples that can be stored in the
51 | list with the allocated memory at a given time is given by:
52 |
53 | ntl->max_size
54 | */
55 | typedef struct ntuple_list_s
56 | {
57 | unsigned int size;
58 | unsigned int max_size;
59 | unsigned int dim;
60 | double * values;
61 | } * ntuple_list;
62 |
63 | void free_ntuple_list(ntuple_list in);
64 | ntuple_list new_ntuple_list(unsigned int dim);
65 |
66 |
67 | /*----------------------------------------------------------------------------*/
68 | /*----------------------------- Image Data Types -----------------------------*/
69 | /*----------------------------------------------------------------------------*/
70 |
71 | /*----------------------------------------------------------------------------*/
72 | /** @brief char image data type
73 |
74 | The pixel value at (x,y) is accessed by:
75 |
76 | image->data[ x + y * image->xsize ]
77 |
78 | with x and y integer.
79 | */
80 | typedef struct image_char_s
81 | {
82 | unsigned char * data;
83 | unsigned int xsize,ysize;
84 | } * image_char;
85 |
86 | void free_image_char(image_char i);
87 | image_char new_image_char(unsigned int xsize, unsigned int ysize);
88 | image_char new_image_char_ini( unsigned int xsize, unsigned int ysize,
89 | unsigned char fill_value );
90 |
91 | /*----------------------------------------------------------------------------*/
92 | /** @brief int image data type
93 |
94 | The pixel value at (x,y) is accessed by:
95 |
96 | image->data[ x + y * image->xsize ]
97 |
98 | with x and y integer.
99 | */
100 | typedef struct image_int_s
101 | {
102 | int * data;
103 | unsigned int xsize,ysize;
104 | } * image_int;
105 |
106 | void free_image_int(image_int i);
107 | image_int new_image_int(unsigned int xsize, unsigned int ysize);
108 | image_int new_image_int_ini( unsigned int xsize, unsigned int ysize,
109 | int fill_value );
110 |
111 | /*----------------------------------------------------------------------------*/
112 | /** @brief double image data type
113 |
114 | The pixel value at (x,y) is accessed by:
115 |
116 | image->data[ x + y * image->xsize ]
117 |
118 | with x and y integer.
119 | */
120 | typedef struct image_double_s
121 | {
122 | double * data;
123 | unsigned int xsize,ysize;
124 | } * image_double;
125 |
126 | void free_image_double(image_double i);
127 | image_double new_image_double(unsigned int xsize, unsigned int ysize);
128 | image_double new_image_double_ini( unsigned int xsize, unsigned int ysize,
129 | double fill_value );
130 |
131 |
132 | /*----------------------------------------------------------------------------*/
133 | /*-------------------------- Line Segment Detector ---------------------------*/
134 | /*----------------------------------------------------------------------------*/
135 |
136 | /*----------------------------------------------------------------------------*/
137 | /* LSD Full Interface */
138 | /*----------------------------------------------------------------------------*/
139 | /** @brief LSD Full Interface
140 |
141 | @param image Input image.
142 |
143 | @param scale When different than 1.0, LSD will scale the image by
144 | Gaussian filtering.
145 | Example: is scale=0.8, the input image will be subsampled
146 | to 80% of its size, and then the line segment detector
147 | will be applied.
148 | Suggested value: 0.8
149 |
150 | @param sigma_scale When scale!=1.0, the sigma of the Gaussian filter is:
151 | sigma = sigma_scale / scale, if scale < 1.0
152 | sigma = sigma_scale, if scale >= 1.0
153 | Suggested value: 0.6
154 |
155 | @param quant Bound to the quantization error on the gradient norm.
156 | Example: if gray level is quantized to integer steps,
157 | the gradient (computed by finite differences) error
158 | due to quantization will be bounded by 2.0, as the
159 | worst case is when the error are 1 and -1, that
160 | gives an error of 2.0.
161 | Suggested value: 2.0
162 |
163 | @param ang_th Gradient angle tolerance in the region growing
164 | algorithm, in degrees.
165 | Suggested value: 22.5
166 |
167 | @param eps Detection threshold, -log10(NFA).
168 | The bigger, the more strict the detector is,
169 | and will result in less detections.
170 | (Note that the 'minus sign' makes that this
171 | behavior is opposite to the one of NFA.)
172 | The value -log10(NFA) is equivalent but more
173 | intuitive than NFA:
174 | - -1.0 corresponds to 10 mean false alarms
175 | - 0.0 corresponds to 1 mean false alarm
176 | - 1.0 corresponds to 0.1 mean false alarms
177 | - 2.0 corresponds to 0.01 mean false alarms
178 | .
179 | Suggested value: 0.0
180 |
181 | @param density_th Minimal proportion of region points in a rectangle.
182 | Suggested value: 0.7
183 |
184 | @param n_bins Number of bins used in the pseudo-ordering of gradient
185 | modulus.
186 | Suggested value: 1024
187 |
188 | @param max_grad Gradient modulus in the highest bin. For example,
189 | for images with integer gray levels in [0,255],
190 | the maximum possible gradient value is 255.0.
191 | Suggested value: 255.0
192 |
193 | @param region Optional output: an int image where the pixels used
194 | in some line support region are marked. Unused pixels
195 | have the value '0' while the used ones have the
196 | number of the line segment, numbered 1,2,3,...
197 | If desired, a non NULL pointer to an image_int should
198 | be used. The resulting image has the size of the image
199 | used for the processing, that is, the size of the input
200 | image scaled by the given factor 'scale'.
201 | Suggested value: NULL
202 |
203 | @return A 5-tuple list, where each 5-tuple corresponds to a
204 | detected line segment. The five values are:
205 | - x1,y1,x2,y2,width
206 | .
207 | for a line segment from (x1,y1) to (x2,y2) and
208 | a width 'width'.
209 | */
210 | ntuple_list LineSegmentDetection( image_double image, double scale,
211 | double sigma_scale, double quant,
212 | double ang_th, double eps, double density_th,
213 | int n_bins, double max_grad,
214 | image_int * region );
215 |
216 | /*----------------------------------------------------------------------------*/
217 | /* LSD Simple Interface with Scale */
218 | /*----------------------------------------------------------------------------*/
219 | /** @brief LSD Simple Interface with Scale
220 |
221 | @param image Input image.
222 |
223 | @param scale When different than 1.0, LSD will scale the image by
224 | Gaussian filtering.
225 | Example: is scale=0.8, the input image will be subsampled
226 | to 80% of its size, and then the line segment detector
227 | will be applied.
228 | Suggested value: 0.8
229 |
230 | @return a 5-tuple list of detected line segments.
231 | */
232 | ntuple_list lsd_scale(image_double image, double scale);
233 |
234 | /*----------------------------------------------------------------------------*/
235 | /* LSD Simple Interface */
236 | /*----------------------------------------------------------------------------*/
237 | /** @brief LSD Simple Interface
238 |
239 | @param image Input image.
240 |
241 | @return a 5-tuple list of detected line segments.
242 | */
243 | ntuple_list lsd(image_double image);
244 |
245 | #endif /* !LSD_HEADER */
246 | /*----------------------------------------------------------------------------*/
247 |
--------------------------------------------------------------------------------
/src/ofxCorrectPerspective.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 M. Haris Usmani
3 | *
4 | * The code in this repository is available under the MIT License.
5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL
6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution.
7 | *
8 | * See https://github.com/harisusmani/ofxCorrectPerspective for documentation
9 | *
10 | */
11 | #include "ofxCorrectPerspective.h"
12 |
13 | using namespace cv;
14 |
15 | typedef std::pair mypair;
16 | bool comparator ( const mypair& l, const mypair& r){
17 | return l.first < r.first;
18 | }
19 |
20 | ofVec2f ofxCorrectPerspective::solveLinearSys(double a11,double a12,double a21,double a22,double b1,double b2){
21 | ofVec2f out;
22 | double det=(a11*a22)-(a12*a21);
23 | out.set((a22*b1-a12*b2)/det,(-a21*b1+a11*b2)/det);
24 | return out;
25 | }
26 |
27 | ofxCorrectPerspective::column_vector ofxCorrectPerspective::fitFunc4Lines(std::vector > L_vec,unsigned int r1,unsigned int r2,unsigned int r3,unsigned int r4, float f){
28 | column_vector starting_point(2);
29 | starting_point = ofRandom(PI/4),ofRandom(PI/4); // OR 0, 0
30 | find_min_bobyqa(cost_function(L_vec, r1, r2, r3, r4, f),
31 | starting_point,
32 | 5, // number of interpolation points
33 | dlib::uniform_matrix(2,1, -PI/2), // lower bound constraint
34 | dlib::uniform_matrix(2,1, PI/2), // upper bound constraint
35 | PI/10, // initial trust region radius
36 | 1e-100, // stopping trust region radius
37 | 2000 // max number of objective function evaluations
38 | );
39 | return starting_point;
40 | }
41 |
42 | void ofxCorrectPerspective::distFunc(std::vector >L_vec,ofxCorrectPerspective::column_vector modelX,float f,double thresh,std::vector & arIn,std::vector & acIn){
43 | //MAKE ROTATION MATRIX
44 | ofMatrix4x4 R=ofMatrix4x4::newRotationMatrix(modelX(0)*180.0/PI, ofVec3f(-1, 0, 0), modelX(1)*180.0/PI, ofVec3f(0, -1, 0), 0, ofVec3f(0, 0, -1));
45 | double m[3][3] = {{R(0,0), R(0,1), R(0,2)}, {R(1,0), R(1,1), R(1,2)}, {R(2,0), R(2,1), R(2,2)}};
46 | cv::Mat R_mat = cv::Mat(3, 3, CV_64F, m);
47 |
48 | cv::Mat K_mat = (cv::Mat_(3,3)<< f,0.0,0.0,0.0,f,0.0,0.0,0.0,1.0);
49 |
50 | cv::Mat K_c=K_mat.clone();
51 | K_c=K_c.inv();
52 | R_mat=R_mat.t();
53 | cv::Mat Hinv=K_mat*R_mat*K_c;
54 |
55 | double L_vec_D[3][L_vec[0].size()];
56 | for (int i = 0; i < L_vec[0].size(); ++i){
57 | L_vec_D[0][i]=L_vec[0][i];
58 | L_vec_D[1][i]=L_vec[1][i];
59 | L_vec_D[2][i]=L_vec[2][i];
60 | }
61 |
62 | cv::Mat L_vec_M=cv::Mat(3, L_vec[0].size(), CV_64F, L_vec_D);
63 |
64 | Hinv=Hinv.t();
65 | cv::Mat Lp=Hinv*L_vec_M;
66 | Lp.resize(2);
67 |
68 | double mag;
69 | for (int i=0; i(0,i)*Lp.at(0,i))+(Lp.at(1,i)*Lp.at(1,i));
72 | mag=sqrt(mag);
73 | Lp.at(0,i)=Lp.at(0,i)/mag;
74 | Lp.at(1,i)=Lp.at(1,i)/mag;
75 | }
76 |
77 | cv::Mat Lp_T=Lp.clone();
78 | Lp_T=Lp_T.t();
79 | cv::Mat C=Lp_T*Lp;
80 | multiply(C, C, C, 1.0);
81 |
82 | for (int i=0; i(i,j)<=thresh)
87 | {
88 | arIn.push_back(i);
89 | acIn.push_back(j);
90 | }
91 | }
92 | }
93 | }
94 |
95 | void ofxCorrectPerspective::Rectify_Image(ofImage & my_image,double focal_length,double sensor_width, double & alpha, double & beta){
96 | resize_image=1; //To Enable or Disable Resize
97 |
98 | //Rescale Image to be Max in 1000px
99 | if (resize_image && max(my_image.width,my_image.height)>1000)
100 | {
101 | float s=float(1000)/max(my_image.width,my_image.height);
102 | my_image.resize(floor(my_image.width*s),floor(my_image.height*s));
103 | }
104 |
105 | float f=focal_length*(float)max(my_image.width,my_image.height)/sensor_width;
106 | K.set(f,0.0,0.0,0.0,f,0.0,0.0,0.0,1.0);
107 | center.set(double(my_image.width)/2.0,double(my_image.height)/2.0);
108 | if (talk) cout << K;
109 |
110 | my_img_gray=my_image;
111 | my_img_gray.setImageType(OF_IMAGE_GRAYSCALE);
112 |
113 | //Converting Image to Image Double//
114 | image_double dub_image;
115 | ntuple_list lsd_out;
116 | unsigned int w=my_img_gray.width;
117 | unsigned int h=my_img_gray.height;
118 | unsigned char * imgP=my_img_gray.getPixels();
119 |
120 | // LSD parameters start
121 | double scale = 0.8; // Scale the image by Gaussian filter to 'scale'.
122 | double sigma_scale = 0.6; // Sigma for Gaussian filter is computed as sigma = sigma_scale/scale.
123 | double quant = 2.0; // Bound to the quantization error on the gradient norm.
124 | double ang_th = 22.5; // Gradient angle tolerance in degrees.
125 | double eps = 0.0; // Detection threshold, -log10(NFA).
126 | double density_th = 0.7; // Minimal density of region points in rectangle.
127 | int n_bins = 1024; // Number of bins in pseudo-ordering of gradient modulus.
128 | double max_grad = 255.0; // Gradient modulus in the highest bin. The default value corresponds to the highest
129 | // gradient modulus on images with gray levels in [0,255].
130 | // LSD parameters end
131 |
132 | bool verbose=0;
133 |
134 | dub_image = new_image_double(w,h);
135 | double px=0;
136 | cout << "\n--------\nInput data being written to image buffer \n";
137 | for(int j=0;j<(w*h);j++){
138 | px=imgP[j];
139 | dub_image->data[j] = px;
140 | if (verbose){
141 | cout << " " << dub_image->data[j];
142 | }
143 | }
144 | // Call LSD //
145 | lsd_out = LineSegmentDetection( dub_image, scale, sigma_scale, quant, ang_th, eps,
146 | density_th, n_bins, max_grad, NULL );
147 | cout << "LSD has done it's thing!\n";
148 | if (talk) cout << "Number of Lines: "<< lsd_out->size << "Number of Dimensions: " << lsd_out->dim << "\n";
149 |
150 | if (verbose)
151 | {
152 | cout << "LSD Values: " << lsd_out->values[0] << " " << lsd_out->values[1] <<" " << lsd_out->values[2] <<" " << lsd_out->values[3] <<" " << lsd_out->values[4] <<" " << lsd_out->values[5] << "\n";
153 | }
154 |
155 | //Sorting in (Value, Index) pairs
156 | //http://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes
157 | std::vector line_lengths;
158 | double sqd_distance;
159 |
160 | mesh.setMode(OF_PRIMITIVE_LINES);
161 | mesh.enableColors();
162 |
163 | ofVec3f first(0.0,0.0,0.0);
164 | ofVec3f second(0.0,0.0,0.0);
165 | double x1,x2,y1,y2;
166 |
167 | for(int j=0;j<(lsd_out->size*lsd_out->dim);j=j+5){
168 | x1=lsd_out->values[j];
169 | y1=lsd_out->values[j+1];
170 | x2=lsd_out->values[j+2];
171 | y2=lsd_out->values[j+3];
172 | sqd_distance=(x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
173 |
174 | line_lengths.push_back(make_pair(sqd_distance,j));
175 |
176 | //To Draw as Primitive Lines//
177 | /*first.set(x2,y2,0.0);
178 | second.set(x1,y1,0.0);
179 | mesh.addVertex(first);
180 | mesh.addColor(ofFloatColor(1.0, 0.0, 0.0));
181 | mesh.addVertex(second);
182 | mesh.addColor(ofFloatColor(1.0, 0.0, 0.0));*/
183 | //Lines Added, will be drawn//
184 | }
185 | sort(line_lengths.begin(),line_lengths.end());
186 | reverse(line_lengths.begin(), line_lengths.end());
187 |
188 | if (talk) cout << line_lengths[0].first << " " << line_lengths[0].second << " " << line_lengths[1].first << " " << line_lengths[1].second << "\n";
189 |
190 | unsigned int maxlines=700;
191 | unsigned int no_of_lines=min(lsd_out->size,maxlines);
192 | //Store these Lines pairs in a Matrix, in descending order of Distance
193 | cout << "Number of Lines: " << no_of_lines << "\n";
194 | L.resize(4);
195 | for (int i = 0; i < 4; ++i){
196 | L[i].resize(no_of_lines);
197 | }
198 |
199 | for (int j=0; jvalues[line_lengths[j].second];
201 | L[1][j] = lsd_out->values[(line_lengths[j].second)+1];
202 | L[2][j] = lsd_out->values[(line_lengths[j].second)+2];
203 | L[3][j] = lsd_out->values[(line_lengths[j].second)+3];
204 | }
205 |
206 | //LINE EXTENSION
207 | double extension_fac=.15; //For one side of the line
208 | double line_length;
209 | double line_gradient;
210 | double rise_angle;
211 | double delta_x;
212 | double delta_y;
213 | for (int j=0; jx2)
226 | {
227 | L[0][j]+=delta_x;
228 | L[1][j]-=delta_y;
229 | L[2][j]-=delta_x;
230 | L[3][j]+=delta_y;
231 | }
232 | else
233 | {
234 | L[2][j]+=delta_x;
235 | L[3][j]-=delta_y;
236 | L[0][j]-=delta_x;
237 | L[1][j]+=delta_y;
238 | }
239 | }
240 | else
241 | {
242 | if (x1>x2)
243 | {
244 | L[0][j]+=delta_x;
245 | L[1][j]+=delta_y;
246 | L[2][j]-=delta_x;
247 | L[3][j]-=delta_y;
248 | }
249 | else
250 | {
251 | L[2][j]+=delta_x;
252 | L[3][j]+=delta_y;
253 | L[0][j]-=delta_x;
254 | L[1][j]-=delta_y;
255 | }
256 | }
257 | x1=L[0][j];
258 | y1=L[1][j];
259 | x2=L[2][j];
260 | y2=L[3][j];
261 |
262 | L[2][j]-=center.x; //Move Origin to the Principle Point
263 | L[3][j]-=center.y;
264 | L[0][j]-=center.x;
265 | L[1][j]-=center.y;
266 | }
267 |
268 | //Finding ADJACENT Lines//
269 | bool adjflag=1;
270 | std::vector ar; //To hold Adjacent Row Values
271 | std::vector ac; //To hold Adjacent Column Values
272 |
273 | if (adjflag)
274 | {
275 | double athreshadj=10;
276 |
277 | std::vector > adj; //Line x Line Inf Matrix Initialization, adj
278 | //Not Used at the Moment.
279 | adj.resize(no_of_lines); //Height
280 | for (int i = 0; i < no_of_lines; ++i){
281 | adj[i].resize(no_of_lines);
282 | for (int j = 0; j < no_of_lines; ++j){
283 | //adj[i][j]=1.0/0.0;
284 | adj[i][j]=0;
285 | }
286 | }
287 |
288 | ofVec2f v1,v2,x;
289 | athreshadj=abs(cos((athreshadj*PI)/180.0));
290 | for (int i = 0; i < no_of_lines; ++i){
291 | for (int j = i+1; j < no_of_lines; ++j){ //Everyline in-front
292 | v1.set(L[0][i]-L[2][i],L[1][i]-L[3][i]);
293 | v2.set(L[0][j]-L[2][j],L[1][j]-L[3][j]);
294 | v1.normalize();
295 | v2.normalize();
296 | if (abs(v1.dot(v2))=-DBL_EPSILON) && (x.x<=1+DBL_EPSILON) && (x.y>=-DBL_EPSILON) && (x.y<=1+DBL_EPSILON);
302 | adj[j][i]=adj[i][j] || adj[j][i];
303 | if (adj[i][j])
304 | {
305 | ar.push_back(i);
306 | ac.push_back(j);
307 | }
308 | }
309 | }
310 | }
311 | }
312 | }
313 | else
314 | {
315 | for (int i = 0; i < no_of_lines; ++i){ // nC2 Pairs
316 | for (int j = i+1; j < no_of_lines; ++j){
317 | ar.push_back(i);
318 | ac.push_back(j);
319 | }
320 | }
321 | }
322 | cout << "No. of Pairs: "<< ac.size() << "\n";
323 |
324 | //Adjacent Matrix FOUND//
325 |
326 | //Convert Line Segments to vector format for Rectification//
327 | std::vector > L_vec; //Line's Vector Form
328 | L_vec.resize(3); //Height
329 | for (int i = 0; i < 3; ++i){
330 | L_vec[i].resize(no_of_lines);
331 | }
332 |
333 | Mat A(3,3,CV_32F);
334 | Mat s, u, vt;
335 | A.at(0,2)=1;
336 | A.at(1,2)=1;
337 | A.at(2,0)=0;
338 | A.at(2,1)=0;
339 | A.at(2,2)=0;
340 | for (int i = 0; i < no_of_lines; ++i){
341 | A.at(0,0)=L[0][i];
342 | A.at(0,1)=L[1][i];
343 | A.at(1,0)=L[2][i];
344 | A.at(1,1)=L[3][i];
345 | SVD::compute(A, s, u, vt); //YY=U*S*V'
346 | vt=vt.t();
347 | vt.col(0)=vt.col(0)*-1;
348 |
349 | L_vec[0][i]=vt.at(0,2);
350 | L_vec[1][i]=vt.at(1,2);
351 | L_vec[2][i]=vt.at(2,2);
352 | }
353 |
354 | //RANSAC//
355 | //int PairsC2=(ac.size()-1)*ac.size()/2;
356 | unsigned int maxTrials=400; //min(PairsC2*5,200);
357 | cout << "RANSAC: No. of Trials = " << maxTrials << endl;
358 | unsigned int trialcount=0;
359 | unsigned int r1, r2, r3, r4;
360 | unsigned int r_ind1, r_ind2;
361 | column_vector modelX;
362 | std::vector arIn; //To hold Adjacent Row Values
363 | std::vector acIn; //To hold Adjacent Column Values
364 | double thresh=0.001;
365 |
366 | std::vector Best_arIn; //Best Adjacent Row Values
367 | std::vector Best_acIn; //Best Adjacent Column Values
368 | unsigned int Bestscore=0; //Number of Inliers
369 | unsigned int score=0;
370 | column_vector Best_modelX;
371 |
372 | while (trialcount(3,3)<< f,0.0,0.0,0.0,f,0.0,0.0,0.0,1.0);
417 | cv::Mat K_c= K_mat.clone();
418 | K_c=K_c.inv();
419 |
420 | cv::Mat C = (cv::Mat_(3,3)<< 1,0,-center.x,0,1,-center.y,0,0,1);
421 | cv::Mat H=K_mat*R_mat*K_c*C;
422 |
423 | //Calclating Resultant Translation and Scale
424 | std::vector Ref_c;
425 | std::vector Ref_c_out;
426 | Ref_c.resize(4);
427 | Ref_c_out.resize(4);
428 | Ref_c[0].x=0;
429 | Ref_c[0].y=0;
430 | Ref_c[1].x=double(my_image.width);
431 | Ref_c[1].y=0;
432 | Ref_c[2].x=double(my_image.width);
433 | Ref_c[2].y=double(my_image.height);
434 | Ref_c[3].x=0;
435 | Ref_c[3].y=double(my_image.height);
436 |
437 | perspectiveTransform(Ref_c, Ref_c_out, H);
438 |
439 | if (talk) cout << "Ref Out New: " << Ref_c_out << endl;
440 |
441 | //Scalling:
442 | double scale_fac=abs((max(Ref_c_out[1].x,Ref_c_out[2].x)-min(Ref_c_out[0].x,Ref_c_out[3].x))/my_image.width); //Based on Length
443 | if (talk) cout << "Scale Factor: " << scale_fac << endl;
444 |
445 | Ref_c_out[0].x=Ref_c_out[0].x/scale_fac;
446 | Ref_c_out[0].y=Ref_c_out[0].y/scale_fac;
447 | Ref_c_out[1].x=Ref_c_out[1].x/scale_fac;
448 | Ref_c_out[1].y=Ref_c_out[1].y/scale_fac;
449 | Ref_c_out[2].x=Ref_c_out[2].x/scale_fac;
450 | Ref_c_out[2].y=Ref_c_out[2].y/scale_fac;
451 | Ref_c_out[3].x=Ref_c_out[3].x/scale_fac;
452 | Ref_c_out[3].y=Ref_c_out[3].y/scale_fac;
453 |
454 | Ref_c_out[1].x=Ref_c_out[1].x-Ref_c_out[0].x;
455 | Ref_c_out[1].y=Ref_c_out[1].y-Ref_c_out[0].y;
456 | Ref_c_out[2].x=Ref_c_out[2].x-Ref_c_out[0].x;
457 | Ref_c_out[2].y=Ref_c_out[2].y-Ref_c_out[0].y;
458 | Ref_c_out[3].x=Ref_c_out[3].x-Ref_c_out[0].x;
459 | Ref_c_out[3].y=Ref_c_out[3].y-Ref_c_out[0].y;
460 | Ref_c_out[0].x=Ref_c_out[0].x-Ref_c_out[0].x;
461 | Ref_c_out[0].y=Ref_c_out[0].y-Ref_c_out[0].y;
462 | if (talk) (cout << "Ref Out New: " << Ref_c_out << endl);
463 |
464 | H = getPerspectiveTransform( Ref_c, Ref_c_out ); //For the Translated/Scalled Image
465 |
466 | //Applying Homography//
467 | Mat src_img(cv:: Size (my_image.width, my_image.height),CV_8UC3,my_image.getPixels()); //OF to OpenCV
468 | cv::Mat dst_img;
469 | dst_img.create(src_img.size(), src_img.type());
470 |
471 | cv::warpPerspective(src_img, dst_img, H, src_img.size(), cv::INTER_LINEAR);
472 |
473 | //OpenCV to OF
474 | my_image.setFromPixels((unsigned char *) IplImage(dst_img). imageData,dst_img.size().width, dst_img.size().height,OF_IMAGE_COLOR);
475 |
476 | cout << endl <<"Press any key to Save Output Image." << endl;
477 | }
478 |
--------------------------------------------------------------------------------
/example/example.cbp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
--------------------------------------------------------------------------------
/libs/lsd/lsd.c:
--------------------------------------------------------------------------------
1 | /*----------------------------------------------------------------------------
2 |
3 | LSD - Line Segment Detector on digital images
4 |
5 | Copyright 2007,2008,2009,2010 rafael grompone von gioi (grompone@gmail.com)
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU Affero General Public License as
9 | published by the Free Software Foundation, either version 3 of the
10 | License, or (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU Affero General Public License for more details.
16 |
17 | You should have received a copy of the GNU Affero General Public License
18 | along with this program. If not, see .
19 |
20 | ----------------------------------------------------------------------------*/
21 |
22 | /*----------------------------------------------------------------------------*/
23 | /** @file lsd.c
24 | @brief LSD module code
25 | @author rafael grompone von gioi (grompone@gmail.com)
26 | */
27 | /*----------------------------------------------------------------------------*/
28 |
29 | /*----------------------------------------------------------------------------*/
30 | /** @mainpage LSD code documentation
31 |
32 | This is an implementation of the Line Segment Detector described
33 | in the paper:
34 |
35 | "LSD: A Fast Line Segment Detector with a False Detection Control"
36 | by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
37 | and Gregory Randall, IEEE Transactions on Pattern Analysis and
38 | Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010.
39 |
40 | and in more details in the CMLA Technical Report:
41 |
42 | "LSD: A Line Segment Detector, Technical Report",
43 | by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
44 | Gregory Randall, CMLA, ENS Cachan, 2010.
45 |
46 | The module's main function is lsd().
47 |
48 | HISTORY:
49 | - version 1.4 - jul 2010: lsd_scale interface added and doxygen doc.
50 | - version 1.3 - feb 2010: Multiple bug correction and improved code.
51 | - version 1.2 - dic 2009: First full Ansi C Language version.
52 | - version 1.1 - sep 2009: Systematic subsampling to scale 0.8 and
53 | correction to partially handle"angle problem".
54 | - version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language
55 | version.
56 |
57 | @author rafael grompone von gioi (grompone@gmail.com)
58 | */
59 | /*----------------------------------------------------------------------------*/
60 |
61 | #include
62 | #include
63 | #include
64 | #include
65 | #include
66 | #include "lsd.h"
67 | //#include "mex.h"
68 | //#include "matrix.h"
69 |
70 | /** @brief ln(10) */
71 | #ifndef M_LN10
72 | #define M_LN10 2.30258509299404568402
73 | #endif /* !M_LN10 */
74 |
75 | /** @brief PI */
76 | #ifndef M_PI
77 | #define M_PI 3.14159265358979323846
78 | #endif /* !M_PI */
79 |
80 | #ifndef FALSE
81 | #define FALSE 0
82 | #endif /* !FALSE */
83 |
84 | #ifndef TRUE
85 | #define TRUE 1
86 | #endif /* !TRUE */
87 |
88 | /** @brief Label for pixels with undefined gradient. */
89 | #define NOTDEF -1024.0
90 |
91 | /** @brief 3/2 pi */
92 | #define M_3_2_PI 4.71238898038
93 |
94 | /** @brief 2 pi */
95 | #define M_2__PI 6.28318530718
96 |
97 | /** @brief Label for pixels not used in yet. */
98 | #define NOTUSED 0
99 |
100 | /** @brief Label for pixels already used in detection. */
101 | #define USED 1
102 |
103 | /*----------------------------------------------------------------------------*/
104 | /** @brief Chained list of coordinates.
105 | */
106 | struct coorlist
107 | {
108 | int x,y;
109 | struct coorlist * next;
110 | };
111 |
112 | /*----------------------------------------------------------------------------*/
113 | /** @brief A point (or pixel).
114 | */
115 | struct point {int x,y;};
116 |
117 |
118 | /*----------------------------------------------------------------------------*/
119 | /*------------------------- Miscellaneous functions --------------------------*/
120 | /*----------------------------------------------------------------------------*/
121 |
122 | /*----------------------------------------------------------------------------*/
123 | /** @brief Fatal error, print a message to standard-error output and exit.
124 | */
125 | static void error(char * msg)
126 | {
127 | fprintf(stderr,"LSD Error: %s\n",msg);
128 | exit(EXIT_FAILURE);
129 | }
130 |
131 | /*----------------------------------------------------------------------------*/
132 | /** @brief Doubles relative error factor
133 | */
134 | #define RELATIVE_ERROR_FACTOR 100.0
135 |
136 | /*----------------------------------------------------------------------------*/
137 | /** @brief Compare doubles by relative error.
138 |
139 | The resulting rounding error after floating point computations
140 | depend on the specific operations done. The same number computed by
141 | different algorithms could present different rounding errors. For a
142 | useful comparison, an estimation of the relative rounding error
143 | should be considered and compared to a factor times EPS. The factor
144 | should be related to the cumulated rounding error in the chain of
145 | computation. Here, as a simplification, a fixed factor is used.
146 | */
147 | static int double_equal(double a, double b)
148 | {
149 | double abs_diff,aa,bb,abs_max;
150 |
151 | if( a == b ) return TRUE;
152 |
153 | abs_diff = fabs(a-b);
154 | aa = fabs(a);
155 | bb = fabs(b);
156 | abs_max = aa > bb ? aa : bb;
157 |
158 | /* DBL_MIN is the smallest normalized number, thus, the smallest
159 | number whose relative error is bounded by DBL_EPSILON. For
160 | smaller numbers, the same quantization steps as for DBL_MIN
161 | are used. Then, for smaller numbers, a meaningful "relative"
162 | error should be computed by dividing the difference by DBL_MIN. */
163 | if( abs_max < DBL_MIN ) abs_max = DBL_MIN;
164 |
165 | /* equal if relative error <= factor x eps */
166 | return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
167 | }
168 |
169 | /*----------------------------------------------------------------------------*/
170 | /** @brief Computes Euclidean distance between point (x1,y1) and point (x2,y2).
171 | */
172 | static double dist(double x1, double y1, double x2, double y2)
173 | {
174 | return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
175 | }
176 |
177 |
178 | /*----------------------------------------------------------------------------*/
179 | /*----------------------- 'list of n-tuple' data type ------------------------*/
180 | /*----------------------------------------------------------------------------*/
181 |
182 | /*----------------------------------------------------------------------------*/
183 | /** @brief Free memory used in n-tuple 'in'.
184 | */
185 | void free_ntuple_list(ntuple_list in)
186 | {
187 | if( in == NULL || in->values == NULL )
188 | error("free_ntuple_list: invalid n-tuple input.");
189 | free( (void *) in->values );
190 | free( (void *) in );
191 | }
192 |
193 | /*----------------------------------------------------------------------------*/
194 | /** @brief Create an n-tuple list and allocate memory for one element.
195 | @param dim the dimension (n) of the n-tuple.
196 | */
197 | ntuple_list new_ntuple_list(unsigned int dim)
198 | {
199 | ntuple_list n_tuple;
200 |
201 | if( dim <= 0 ) error("new_ntuple_list: 'dim' must be positive.");
202 |
203 | n_tuple = (ntuple_list) malloc( sizeof(struct ntuple_list_s) );
204 | if( n_tuple == NULL ) error("not enough memory.");
205 | n_tuple->size = 0;
206 | n_tuple->max_size = 1;
207 | n_tuple->dim = dim;
208 | n_tuple->values = (double *) malloc( dim*n_tuple->max_size * sizeof(double) );
209 | if( n_tuple->values == NULL ) error("not enough memory.");
210 | return n_tuple;
211 | }
212 |
213 | /*----------------------------------------------------------------------------*/
214 | /** @brief Enlarge the allocated memory of an n-tuple list.
215 | */
216 | static void enlarge_ntuple_list(ntuple_list n_tuple)
217 | {
218 | if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size <= 0 )
219 | error("enlarge_ntuple_list: invalid n-tuple.");
220 | n_tuple->max_size *= 2;
221 | n_tuple->values =
222 | (double *) realloc( (void *) n_tuple->values,
223 | n_tuple->dim * n_tuple->max_size * sizeof(double) );
224 | if( n_tuple->values == NULL ) error("not enough memory.");
225 | }
226 |
227 | /*----------------------------------------------------------------------------*/
228 | /** @brief Add a 5-tuple to an n-tuple list.
229 | */
230 | static void add_5tuple( ntuple_list out, double v1, double v2,
231 | double v3, double v4, double v5 )
232 | {
233 | if( out == NULL ) error("add_5tuple: invalid n-tuple input.");
234 | if( out->dim != 5 ) error("add_5tuple: the n-tuple must be a 5-tuple.");
235 | if( out->size == out->max_size ) enlarge_ntuple_list(out);
236 | if( out->values == NULL ) error("add_5tuple: invalid n-tuple input.");
237 | out->values[ out->size * out->dim + 0 ] = v1;
238 | out->values[ out->size * out->dim + 1 ] = v2;
239 | out->values[ out->size * out->dim + 2 ] = v3;
240 | out->values[ out->size * out->dim + 3 ] = v4;
241 | out->values[ out->size * out->dim + 4 ] = v5;
242 | out->size++;
243 | }
244 |
245 |
246 | /*----------------------------------------------------------------------------*/
247 | /*----------------------------- Image Data Types -----------------------------*/
248 | /*----------------------------------------------------------------------------*/
249 |
250 | /*----------------------------------------------------------------------------*/
251 | /** @brief Free memory used in image_char 'i'.
252 | */
253 | void free_image_char(image_char i)
254 | {
255 | if( i == NULL || i->data == NULL )
256 | error("free_image_char: invalid input image.");
257 | free( (void *) i->data );
258 | free( (void *) i );
259 | }
260 |
261 | /*----------------------------------------------------------------------------*/
262 | /** @brief Create a new image_char of size 'xsize' times 'ysize'.
263 | */
264 | image_char new_image_char(unsigned int xsize, unsigned int ysize)
265 | {
266 | image_char image;
267 |
268 | if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size.");
269 |
270 | image = (image_char) malloc( sizeof(struct image_char_s) );
271 | if( image == NULL ) error("not enough memory.");
272 | image->data = (unsigned char *) calloc( xsize*ysize, sizeof(unsigned char) );
273 | if( image->data == NULL ) error("not enough memory.");
274 |
275 | image->xsize = xsize;
276 | image->ysize = ysize;
277 |
278 | return image;
279 | }
280 |
281 | /*----------------------------------------------------------------------------*/
282 | /** @brief Create a new image_char of size 'xsize' times 'ysize',
283 | initialized to the value 'fill_value'.
284 | */
285 | image_char new_image_char_ini( unsigned int xsize, unsigned int ysize,
286 | unsigned char fill_value )
287 | {
288 | image_char image = new_image_char(xsize,ysize);
289 | unsigned int N = xsize*ysize;
290 | unsigned int i;
291 |
292 | if( image == NULL || image->data == NULL )
293 | error("new_image_char_ini: invalid image.");
294 |
295 | for(i=0; idata[i] = fill_value;
296 |
297 | return image;
298 | }
299 |
300 | /*----------------------------------------------------------------------------*/
301 | /** @brief Free memory used in image_int 'i'.
302 | */
303 | void free_image_int(image_int i)
304 | {
305 | if( i == NULL || i->data == NULL )
306 | error("free_image_int: invalid input image.");
307 | free( (void *) i->data );
308 | free( (void *) i );
309 | }
310 |
311 | /*----------------------------------------------------------------------------*/
312 | /** @brief Create a new image_int of size 'xsize' times 'ysize'.
313 | */
314 | image_int new_image_int(unsigned int xsize, unsigned int ysize)
315 | {
316 | image_int image;
317 |
318 | if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size.");
319 |
320 | image = (image_int) malloc( sizeof(struct image_int_s) );
321 | if( image == NULL ) error("not enough memory.");
322 | image->data = (int *) calloc( xsize*ysize, sizeof(int) );
323 | if( image->data == NULL ) error("not enough memory.");
324 |
325 | image->xsize = xsize;
326 | image->ysize = ysize;
327 |
328 | return image;
329 | }
330 |
331 | /*----------------------------------------------------------------------------*/
332 | /** @brief Create a new image_int of size 'xsize' times 'ysize',
333 | initialized to the value 'fill_value'.
334 | */
335 | image_int new_image_int_ini( unsigned int xsize, unsigned int ysize,
336 | int fill_value )
337 | {
338 | image_int image = new_image_int(xsize,ysize);
339 | unsigned int N = xsize*ysize;
340 | unsigned int i;
341 |
342 | for(i=0; idata[i] = fill_value;
343 |
344 | return image;
345 | }
346 |
347 | /*----------------------------------------------------------------------------*/
348 | /** @brief Free memory used in image_double 'i'.
349 | */
350 | void free_image_double(image_double i)
351 | {
352 | if( i == NULL || i->data == NULL )
353 | error("free_image_double: invalid input image.");
354 | free( (void *) i->data );
355 | free( (void *) i );
356 | }
357 |
358 | /*----------------------------------------------------------------------------*/
359 | /** @brief Create a new image_double of size 'xsize' times 'ysize'.
360 | */
361 | image_double new_image_double(unsigned int xsize, unsigned int ysize)
362 | {
363 | image_double image;
364 |
365 | if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size.");
366 |
367 | image = (image_double) malloc( sizeof(struct image_double_s) );
368 | if( image == NULL ) error("not enough memory.");
369 | image->data = (double *) calloc( xsize * ysize, sizeof(double) );
370 | if( image->data == NULL ) error("not enough memory.");
371 |
372 | image->xsize = xsize;
373 | image->ysize = ysize;
374 |
375 | return image;
376 | }
377 |
378 | /*----------------------------------------------------------------------------*/
379 | /** @brief Create a new image_double of size 'xsize' times 'ysize',
380 | initialized to the value 'fill_value'.
381 | */
382 | image_double new_image_double_ini( unsigned int xsize, unsigned int ysize,
383 | double fill_value )
384 | {
385 | image_double image = new_image_double(xsize,ysize);
386 | unsigned int N = xsize*ysize;
387 | unsigned int i;
388 |
389 | for(i=0; idata[i] = fill_value;
390 |
391 | return image;
392 | }
393 |
394 |
395 | /*----------------------------------------------------------------------------*/
396 | /*----------------------------- Gaussian filter ------------------------------*/
397 | /*----------------------------------------------------------------------------*/
398 |
399 | /*----------------------------------------------------------------------------*/
400 | /** @brief Compute a Gaussian kernel of length 'kernel->dim',
401 | standard deviation 'sigma', and centered at value 'mean'.
402 | For example, if mean=0.5, the Gaussian will be centered
403 | in the middle point between values 'kernel->values[0]'
404 | and 'kernel->values[1]'.
405 | */
406 | static void gaussian_kernel(ntuple_list kernel, double sigma, double mean)
407 | {
408 | double sum = 0.0;
409 | double val;
410 | unsigned int i;
411 |
412 | if( kernel == NULL || kernel->values == NULL )
413 | error("gaussian_kernel: invalid n-tuple 'kernel'.");
414 | if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive.");
415 |
416 | /* compute gaussian kernel */
417 | if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel);
418 | kernel->size = 1;
419 | for(i=0;idim;i++)
420 | {
421 | val = ( (double) i - mean ) / sigma;
422 | kernel->values[i] = exp( -0.5 * val * val );
423 | sum += kernel->values[i];
424 | }
425 |
426 | /* normalization */
427 | if( sum >= 0.0 ) for(i=0;idim;i++) kernel->values[i] /= sum;
428 | }
429 |
430 | /*----------------------------------------------------------------------------*/
431 | /** @brief Subsample image 'in' with Gaussian filtering, to a scale 'scale'
432 | (for example, 0.8 will give a result at 80% of the original size),
433 | using a standard deviation sigma given by:
434 | - sigma = sigma_scale / scale, if scale < 1.0
435 | - sigma = sigma_scale, if scale >= 1.0
436 | */
437 | static image_double gaussian_sampler( image_double in, double scale,
438 | double sigma_scale )
439 | {
440 | image_double aux,out;
441 | ntuple_list kernel;
442 | unsigned int N,M,h,n,x,y,i;
443 | int xc,yc,j,double_x_size,double_y_size;
444 | double sigma,xx,yy,sum,prec;
445 |
446 | if( in == NULL || in->data == NULL || in->xsize <= 0 || in->ysize <= 0 )
447 | error("gaussian_sampler: invalid image.");
448 | if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive.");
449 | if( sigma_scale <= 0.0 )
450 | error("gaussian_sampler: 'sigma_scale' must be positive.");
451 |
452 | /* get memory for images */
453 | if( in->xsize * scale > (double) UINT_MAX ||
454 | in->ysize * scale > (double) UINT_MAX )
455 | error("gaussian_sampler: the output image size exceeds the handled size.");
456 | N = (unsigned int) floor( in->xsize * scale );
457 | M = (unsigned int) floor( in->ysize * scale );
458 | aux = new_image_double(N,in->ysize);
459 | out = new_image_double(N,M);
460 |
461 | /* sigma, kernel size and memory for the kernel */
462 | sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale;
463 | /*
464 | The size of the kernel is selected to guarantee that the
465 | the first discarded term is at least 10^prec times smaller
466 | than the central value. For that, h should be larger than x, with
467 | e^(-x^2/2sigma^2) = 1/10^prec.
468 | Then,
469 | x = sigma * sqrt( 2 * prec * ln(10) ).
470 | */
471 | prec = 3.0;
472 | h = (unsigned int) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) );
473 | n = 1+2*h; /* kernel size */
474 | kernel = new_ntuple_list(n);
475 |
476 | /* auxiliary double image size variables */
477 | double_x_size = (int) (2 * in->xsize);
478 | double_y_size = (int) (2 * in->ysize);
479 |
480 | /* First subsampling: x axis */
481 | for(x=0;xxsize;x++)
482 | {
483 | /*
484 | x is the coordinate in the new image.
485 | xx is the corresponding x-value in the original size image.
486 | xc is the integer value, the pixel coordinate of xx.
487 | */
488 | xx = (double) x / scale;
489 | /* coordinate (0.0,0.0) is in the center of pixel (0,0),
490 | so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */
491 | xc = (int) floor( xx + 0.5 );
492 | gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc );
493 | /* the kernel must be computed for each x because the fine
494 | offset xx-xc is different in each case */
495 |
496 | for(y=0;yysize;y++)
497 | {
498 | sum = 0.0;
499 | for(i=0;idim;i++)
500 | {
501 | j = xc - h + i;
502 |
503 | /* symmetry boundary condition */
504 | while( j < 0 ) j += double_x_size;
505 | while( j >= double_x_size ) j -= double_x_size;
506 | if( j >= (int) in->xsize ) j = double_x_size-1-j;
507 |
508 | sum += in->data[ j + y * in->xsize ] * kernel->values[i];
509 | }
510 | aux->data[ x + y * aux->xsize ] = sum;
511 | }
512 | }
513 |
514 | /* Second subsampling: y axis */
515 | for(y=0;yysize;y++)
516 | {
517 | /*
518 | y is the coordinate in the new image.
519 | yy is the corresponding x-value in the original size image.
520 | yc is the integer value, the pixel coordinate of xx.
521 | */
522 | yy = (double) y / scale;
523 | /* coordinate (0.0,0.0) is in the center of pixel (0,0),
524 | so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */
525 | yc = (int) floor( yy + 0.5 );
526 | gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc );
527 | /* the kernel must be computed for each y because the fine
528 | offset yy-yc is different in each case */
529 |
530 | for(x=0;xxsize;x++)
531 | {
532 | sum = 0.0;
533 | for(i=0;idim;i++)
534 | {
535 | j = yc - h + i;
536 |
537 | /* symmetry boundary condition */
538 | while( j < 0 ) j += double_y_size;
539 | while( j >= double_y_size ) j -= double_y_size;
540 | if( j >= (int) in->ysize ) j = double_y_size-1-j;
541 |
542 | sum += aux->data[ x + j * aux->xsize ] * kernel->values[i];
543 | }
544 | out->data[ x + y * out->xsize ] = sum;
545 | }
546 | }
547 |
548 | /* free memory */
549 | free_ntuple_list(kernel);
550 | free_image_double(aux);
551 |
552 | return out;
553 | }
554 |
555 |
556 | /*----------------------------------------------------------------------------*/
557 | /*------------------------------ Gradient Angle ------------------------------*/
558 | /*----------------------------------------------------------------------------*/
559 |
560 | /*----------------------------------------------------------------------------*/
561 | /** @brief Computes the direction of the level line of 'in' at each point.
562 | It returns:
563 | - an image_double with the angle at each pixel, or NOTDEF if not defined.
564 | - the image_double 'modgrad' (a pointer is passed as argument)
565 | with the gradient magnitude at each point.
566 | - a list of pixels 'list_p' roughly ordered by gradient magnitude.
567 | (the order is made by classing points into bins by gradient magnitude.
568 | the parameters 'n_bins' and 'max_grad' specify the number of
569 | bins and the gradient modulus at the highest bin.)
570 | - a pointer 'mem_p' to the memory used by 'list_p' to be able to
571 | free the memory.
572 | */
573 | static image_double ll_angle( image_double in, double threshold,
574 | struct coorlist ** list_p, void ** mem_p,
575 | image_double * modgrad, unsigned int n_bins,
576 | double max_grad )
577 | {
578 | image_double g;
579 | unsigned int n,p,x,y,adr,i;
580 | double com1,com2,gx,gy,norm,norm2;
581 | /* the rest of the variables are used for pseudo-ordering
582 | the gradient magnitude values */
583 | int list_count = 0;
584 | struct coorlist * list;
585 | struct coorlist ** range_l_s; /* array of pointers to start of bin list */
586 | struct coorlist ** range_l_e; /* array of pointers to end of bin list */
587 | struct coorlist * start;
588 | struct coorlist * end;
589 |
590 | /* check parameters */
591 | if( in == NULL || in->data == NULL || in->xsize <= 0 || in->ysize <= 0 )
592 | error("ll_angle: invalid image.");
593 | if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive.");
594 | if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'.");
595 | if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'.");
596 | if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'.");
597 | if( n_bins <= 0 ) error("ll_angle: 'n_bins' must be positive.");
598 | if( max_grad <= 0.0 ) error("ll_angle: 'max_grad' must be positive.");
599 |
600 | n = in->ysize;
601 | p = in->xsize;
602 |
603 | /* allocate output image */
604 | g = new_image_double(in->xsize,in->ysize);
605 |
606 | /* get memory for the image of gradient modulus */
607 | *modgrad = new_image_double(in->xsize,in->ysize);
608 |
609 | /* get memory for "ordered" coordinate list */
610 | list = (struct coorlist *) calloc(n*p,sizeof(struct coorlist));
611 | *mem_p = (void *) list;
612 | range_l_s = (struct coorlist **) calloc(n_bins,sizeof(struct coorlist *));
613 | range_l_e = (struct coorlist **) calloc(n_bins,sizeof(struct coorlist *));
614 | if( list == NULL || range_l_s == NULL || range_l_e == NULL )
615 | error("not enough memory.");
616 | for(i=0;idata[(n-1)*p+x] = NOTDEF;
620 | for(y=0;ydata[p*y+p-1] = NOTDEF;
621 |
622 | /* remaining part */
623 | for(x=0;xdata[adr+p+1] - in->data[adr];
640 | com2 = in->data[adr+1] - in->data[adr+p];
641 | gx = com1+com2;
642 | gy = com1-com2;
643 | norm2 = gx*gx+gy*gy;
644 | norm = sqrt( norm2 / 4.0 );
645 |
646 | (*modgrad)->data[adr] = norm;
647 |
648 | if( norm <= threshold ) /* norm too small, gradient no defined */
649 | g->data[adr] = NOTDEF;
650 | else
651 | {
652 | /* angle computation */
653 | g->data[adr] = atan2(gx,-gy);
654 |
655 | /* store the point in the right bin according to its norm */
656 | i = (unsigned int) (norm * (double) n_bins / max_grad);
657 | if( i >= n_bins ) i = n_bins-1;
658 | if( range_l_e[i] == NULL )
659 | range_l_s[i] = range_l_e[i] = list+list_count++;
660 | else
661 | {
662 | range_l_e[i]->next = list+list_count;
663 | range_l_e[i] = list+list_count++;
664 | }
665 | range_l_e[i]->x = (int) x;
666 | range_l_e[i]->y = (int) y;
667 | range_l_e[i]->next = NULL;
668 | }
669 | }
670 |
671 | /* Make the list of points "ordered" by norm value.
672 | It starts by the larger bin, so the list starts by the
673 | pixels with higher gradient value.
674 | */
675 | for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--);
676 | start = range_l_s[i];
677 | end = range_l_e[i];
678 | if( start != NULL )
679 | for(i--;i>0; i--)
680 | if( range_l_s[i] != NULL )
681 | {
682 | end->next = range_l_s[i];
683 | end = range_l_e[i];
684 | }
685 | *list_p = start;
686 |
687 | /* free memory */
688 | free( (void *) range_l_s );
689 | free( (void *) range_l_e );
690 |
691 | return g;
692 | }
693 |
694 | /*----------------------------------------------------------------------------*/
695 | /** @brief Is point (x,y) aligned to angle theta, up to precision 'prec'?
696 | */
697 | static int isaligned( int x, int y, image_double angles, double theta,
698 | double prec )
699 | {
700 | double a;
701 |
702 | /* check parameters */
703 | if( angles == NULL || angles->data == NULL )
704 | error("isaligned: invalid image 'angles'.");
705 | if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
706 | error("isaligned: (x,y) out of the image.");
707 | if( prec < 0.0 ) error("isaligned: 'prec' must be positive.");
708 |
709 | a = angles->data[ x + y * angles->xsize ];
710 |
711 | if( a == NOTDEF ) return FALSE; /* there is no risk of double comparison
712 | problem here because we are only
713 | interested in the exact NOTDEF value */
714 |
715 | /* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */
716 | theta -= a;
717 | if( theta < 0.0 ) theta = -theta;
718 | if( theta > M_3_2_PI )
719 | {
720 | theta -= M_2__PI;
721 | if( theta < 0.0 ) theta = -theta;
722 | }
723 |
724 | return theta < prec;
725 | }
726 |
727 | /*----------------------------------------------------------------------------*/
728 | /** @brief Absolute value angle difference.
729 | */
730 | static double angle_diff(double a, double b)
731 | {
732 | a -= b;
733 | while( a <= -M_PI ) a += M_2__PI;
734 | while( a > M_PI ) a -= M_2__PI;
735 | if( a < 0.0 ) a = -a;
736 | return a;
737 | }
738 |
739 | /*----------------------------------------------------------------------------*/
740 | /** @brief Signed angle difference.
741 | */
742 | static double angle_diff_signed(double a, double b)
743 | {
744 | a -= b;
745 | while( a <= -M_PI ) a += M_2__PI;
746 | while( a > M_PI ) a -= M_2__PI;
747 | return a;
748 | }
749 |
750 |
751 | /*----------------------------------------------------------------------------*/
752 | /*----------------------------- NFA computation ------------------------------*/
753 | /*----------------------------------------------------------------------------*/
754 |
755 | /*----------------------------------------------------------------------------*/
756 | /** @brief Computes the natural logarithm of the absolute value of
757 | the gamma function of x using the Lanczos approximation,
758 | see http://www.rskey.org/gamma.htm
759 |
760 | The formula used is
761 | @f[
762 | \Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) }
763 | (x+5.5)^{x+0.5} e^{-(x+5.5)}
764 | @f]
765 | so
766 | @f[
767 | \log\Gamma(x) = \log\left( \sum_{n=0}^{N} q_n x^n \right)
768 | + (x+0.5) \log(x+5.5) - (x+5.5) - \sum_{n=0}^{N} \log(x+n)
769 | @f]
770 | and
771 | q0 = 75122.6331530,
772 | q1 = 80916.6278952,
773 | q2 = 36308.2951477,
774 | q3 = 8687.24529705,
775 | q4 = 1168.92649479,
776 | q5 = 83.8676043424,
777 | q6 = 2.50662827511.
778 | */
779 | static double log_gamma_lanczos(double x)
780 | {
781 | static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
782 | 8687.24529705, 1168.92649479, 83.8676043424,
783 | 2.50662827511 };
784 | double a = (x+0.5) * log(x+5.5) - (x+5.5);
785 | double b = 0.0;
786 | int n;
787 |
788 | for(n=0;n<7;n++)
789 | {
790 | a -= log( x + (double) n );
791 | b += q[n] * pow( x, (double) n );
792 | }
793 | return a + log(b);
794 | }
795 |
796 | /*----------------------------------------------------------------------------*/
797 | /** @brief Computes the natural logarithm of the absolute value of
798 | the gamma function of x using Robert H. Windschitl method,
799 | see http://www.rskey.org/gamma.htm
800 |
801 | The formula used is
802 | @f[
803 | \Gamma(x) = \sqrt{\frac{2\pi}{x}} \left( \frac{x}{e}
804 | \sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } \right)^x
805 | @f]
806 | so
807 | @f[
808 | \log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x
809 | + 0.5x\log\left( x\sinh(1/x) + \frac{1}{810x^6} \right).
810 | @f]
811 | This formula is a good approximation when x > 15.
812 | */
813 | static double log_gamma_windschitl(double x)
814 | {
815 | return 0.918938533204673 + (x-0.5)*log(x) - x
816 | + 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) );
817 | }
818 |
819 | /*----------------------------------------------------------------------------*/
820 | /** @brief Computes the natural logarithm of the absolute value of
821 | the gamma function of x. When x>15 use log_gamma_windschitl(),
822 | otherwise use log_gamma_lanczos().
823 | */
824 | #define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
825 |
826 | /*----------------------------------------------------------------------------*/
827 | /** @brief Size of the table to store already computed inverse values.
828 | */
829 | #define TABSIZE 100000
830 |
831 | /*----------------------------------------------------------------------------*/
832 | /** @brief Computes -log10(NFA)
833 |
834 | NFA stands for Number of False Alarms:
835 |
836 | NFA = NT.b(n,k,p)
837 |
838 | - NT - number of tests
839 | - b(,,) - tail of binomial distribution with parameters n,k and p
840 |
841 | The value -log10(NFA) is equivalent but more intuitive than NFA:
842 | - -1 corresponds to 10 mean false alarms
843 | - 0 corresponds to 1 mean false alarm
844 | - 1 corresponds to 0.1 mean false alarms
845 | - 2 corresponds to 0.01 mean false alarms
846 | - ...
847 |
848 | Used this way, the bigger the value, better the detection,
849 | and a logarithmic scale is used.
850 |
851 | @param n,k,p binomial parameters.
852 | @param logNT logarithm of Number of Tests
853 | */
854 | static double nfa(int n, int k, double p, double logNT)
855 | {
856 | static double inv[TABSIZE]; /* table to keep computed inverse values */
857 | double tolerance = 0.1; /* an error of 10% in the result is accepted */
858 | double log1term,term,bin_term,mult_term,bin_tail,err,p_term;
859 | int i;
860 |
861 | if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 )
862 | error("nfa: wrong n, k or p values.");
863 |
864 | if( n==0 || k==0 ) return -logNT;
865 | if( n==k ) return -logNT - (double) n * log10(p);
866 |
867 | p_term = p / (1.0-p);
868 |
869 | /* compute the first term of the series */
870 | /*
871 | binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
872 | where bincoef(n,i) are the binomial coefficients.
873 | But
874 | bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
875 | We use this to compute the first term. Actually the log of it.
876 | */
877 | log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 )
878 | - log_gamma( (double) (n-k) + 1.0 )
879 | + (double) k * log(p) + (double) (n-k) * log(1.0-p);
880 | term = exp(log1term);
881 |
882 | /* in some cases no more computations are needed */
883 | if( double_equal(term,0.0) ) /* the first term is almost zero */
884 | {
885 | if( (double) k > (double) n * p ) /* at begin or end of the tail? */
886 | return -log1term / M_LN10 - logNT; /* end: use just the first term */
887 | else
888 | return -logNT; /* begin: the tail is roughly 1 */
889 | }
890 |
891 | /* compute more terms if needed */
892 | bin_tail = term;
893 | for(i=k+1;i<=n;i++)
894 | {
895 | /*
896 | As
897 | term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
898 | and
899 | bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
900 | then,
901 | term_i / term_i-1 = (n-i+1)/i * p/(1-p)
902 | and
903 | term_i = term_i-1 * (n-i+1)/i * p/(1-p).
904 | 1/i is stored in a table as they are computed,
905 | because divisions are expensive.
906 | p/(1-p) is computed only once and stored in 'p_term'.
907 | */
908 | bin_term = (double) (n-i+1) * ( ii.
918 | Then, the error on the binomial tail when truncated at
919 | the i term can be bounded by a geometric series of form
920 | term_i * sum mult_term_i^j. */
921 | err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) /
922 | (1.0-mult_term) - 1.0 );
923 |
924 | /* One wants an error at most of tolerance*final_result, or:
925 | tolerance * abs(-log10(bin_tail)-logNT).
926 | Now, the error that can be accepted on bin_tail is
927 | given by tolerance*final_result divided by the derivative
928 | of -log10(x) when x=bin_tail. that is:
929 | tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
930 | Finally, we truncate the tail if the error is less than:
931 | tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
932 | if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break;
933 | }
934 | }
935 | return -log10(bin_tail) - logNT;
936 | }
937 |
938 |
939 | /*----------------------------------------------------------------------------*/
940 | /*--------------------------- Rectangle structure ----------------------------*/
941 | /*----------------------------------------------------------------------------*/
942 |
943 | /*----------------------------------------------------------------------------*/
944 | /** @brief Rectangle structure: line segment with width.
945 | */
946 | struct rect
947 | {
948 | double x1,y1,x2,y2; /* first and second point of the line segment */
949 | double width; /* rectangle width */
950 | double x,y; /* center of the rectangle */
951 | double theta; /* angle */
952 | double dx,dy; /* vector with the line segment angle */
953 | double prec; /* tolerance angle */
954 | double p; /* probability of a point with angle within 'prec' */
955 | };
956 |
957 | /*----------------------------------------------------------------------------*/
958 | /** @brief Copy one rectangle structure to another.
959 | */
960 | static void rect_copy(struct rect * in, struct rect * out)
961 | {
962 | if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'.");
963 | out->x1 = in->x1;
964 | out->y1 = in->y1;
965 | out->x2 = in->x2;
966 | out->y2 = in->y2;
967 | out->width = in->width;
968 | out->x = in->x;
969 | out->y = in->y;
970 | out->theta = in->theta;
971 | out->dx = in->dx;
972 | out->dy = in->dy;
973 | out->prec = in->prec;
974 | out->p = in->p;
975 | }
976 |
977 | /*----------------------------------------------------------------------------*/
978 | /** @brief Rectangle points iterator.
979 | */
980 | typedef struct
981 | {
982 | double vx[4];
983 | double vy[4];
984 | double ys,ye;
985 | int x,y;
986 | } rect_iter;
987 |
988 | /*----------------------------------------------------------------------------*/
989 | /** @brief Rectangle points iterator auxiliary function.
990 | */
991 | static double inter_low(double x, double x1, double y1, double x2, double y2)
992 | {
993 | if( x1 > x2 || x < x1 || x > x2 )
994 | {
995 | fprintf(stderr,"inter_low: x %g x1 %g x2 %g.\n",x,x1,x2);
996 | error("impossible situation.");
997 | }
998 | if( double_equal(x1,x2) && y1y2 ) return y2;
1000 | return y1 + (x-x1) * (y2-y1) / (x2-x1);
1001 | }
1002 |
1003 | /*----------------------------------------------------------------------------*/
1004 | /** @brief Rectangle points iterator auxiliary function.
1005 | */
1006 | static double inter_hi(double x, double x1, double y1, double x2, double y2)
1007 | {
1008 | if( x1 > x2 || x < x1 || x > x2 )
1009 | {
1010 | fprintf(stderr,"inter_hi: x %g x1 %g x2 %g.\n",x,x1,x2);
1011 | error("impossible situation.");
1012 | }
1013 | if( double_equal(x1,x2) && y1y2 ) return y1;
1015 | return y1 + (x-x1) * (y2-y1) / (x2-x1);
1016 | }
1017 |
1018 | /*----------------------------------------------------------------------------*/
1019 | /** @brief Free memory used by a rectangle iterator.
1020 | */
1021 | static void ri_del(rect_iter * iter)
1022 | {
1023 | if( iter == NULL ) error("ri_del: NULL iterator.");
1024 | free( (void *) iter );
1025 | }
1026 |
1027 | /*----------------------------------------------------------------------------*/
1028 | /** @brief Check if the iterator finished the full iteration.
1029 | */
1030 | static int ri_end(rect_iter * i)
1031 | {
1032 | if( i == NULL ) error("ri_end: NULL iterator.");
1033 | return (double)(i->x) > i->vx[2];
1034 | }
1035 |
1036 | /*----------------------------------------------------------------------------*/
1037 | /** @brief Increment a rectangle iterator.
1038 | */
1039 | static void ri_inc(rect_iter * i)
1040 | {
1041 | if( i == NULL ) error("ri_inc: NULL iterator.");
1042 |
1043 | if( (double) (i->x) <= i->vx[2] ) i->y++;
1044 |
1045 | while( (double) (i->y) > i->ye && (double) (i->x) <= i->vx[2] )
1046 | {
1047 | /* new x */
1048 | i->x++;
1049 |
1050 | if( (double) (i->x) > i->vx[2] ) return; /* end of iteration */
1051 |
1052 | /* update lower y limit for the line */
1053 | if( (double) i->x < i->vx[3] )
1054 | i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]);
1055 | else i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]);
1056 |
1057 | /* update upper y limit for the line */
1058 | if( (double)i->x < i->vx[1] )
1059 | i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]);
1060 | else i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]);
1061 |
1062 | /* new y */
1063 | i->y = (int) ceil(i->ys);
1064 | }
1065 | }
1066 |
1067 | /*----------------------------------------------------------------------------*/
1068 | /** @brief Create and initialize a rectangle iterator.
1069 | */
1070 | static rect_iter * ri_ini(struct rect * r)
1071 | {
1072 | double vx[4],vy[4];
1073 | int n,offset;
1074 | rect_iter * i;
1075 |
1076 | if( r == NULL ) error("ri_ini: invalid rectangle.");
1077 |
1078 | i = (rect_iter *) malloc(sizeof(rect_iter));
1079 | if( i == NULL ) error("ri_ini: Not enough memory.");
1080 |
1081 | vx[0] = r->x1 - r->dy * r->width / 2.0;
1082 | vy[0] = r->y1 + r->dx * r->width / 2.0;
1083 | vx[1] = r->x2 - r->dy * r->width / 2.0;
1084 | vy[1] = r->y2 + r->dx * r->width / 2.0;
1085 | vx[2] = r->x2 + r->dy * r->width / 2.0;
1086 | vy[2] = r->y2 - r->dx * r->width / 2.0;
1087 | vx[3] = r->x1 + r->dy * r->width / 2.0;
1088 | vy[3] = r->y1 - r->dx * r->width / 2.0;
1089 |
1090 | if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0;
1091 | else if( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1;
1092 | else if( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2;
1093 | else offset = 3;
1094 |
1095 | for(n=0; n<4; n++)
1096 | {
1097 | i->vx[n] = vx[(offset+n)%4];
1098 | i->vy[n] = vy[(offset+n)%4];
1099 | }
1100 |
1101 | /* starting point */
1102 | i->x = (int) ceil(i->vx[0]) - 1;
1103 | i->y = (int) ceil(i->vy[0]);
1104 | i->ys = i->ye = -DBL_MAX;
1105 |
1106 | /* advance to the first point */
1107 | ri_inc(i);
1108 |
1109 | return i;
1110 | }
1111 |
1112 | /*----------------------------------------------------------------------------*/
1113 | /** @brief Compute a rectangle's NFA value.
1114 | */
1115 | static double rect_nfa(struct rect * rec, image_double angles, double logNT)
1116 | {
1117 | rect_iter * i;
1118 | int pts = 0;
1119 | int alg = 0;
1120 |
1121 | if( rec == NULL ) error("rect_nfa: invalid rectangle.");
1122 | if( angles == NULL ) error("rect_nfa: invalid 'angles'.");
1123 |
1124 | for(i=ri_ini(rec); !ri_end(i); ri_inc(i))
1125 | if( i->x >= 0 && i->y >= 0 &&
1126 | i->x < (int) angles->xsize && i->y < (int) angles->ysize )
1127 | {
1128 | ++pts;
1129 | if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) ) ++alg;
1130 | }
1131 | ri_del(i);
1132 |
1133 | return nfa(pts,alg,rec->p,logNT);
1134 | }
1135 |
1136 |
1137 | /*----------------------------------------------------------------------------*/
1138 | /*---------------------------------- Regions ---------------------------------*/
1139 | /*----------------------------------------------------------------------------*/
1140 |
1141 | /*----------------------------------------------------------------------------*/
1142 | /** @brief Compute a region's angle.
1143 | */
1144 | static double get_theta( struct point * reg, int reg_size, double x, double y,
1145 | image_double modgrad, double reg_angle, double prec )
1146 | {
1147 | double lambda1,lambda2,tmp,theta,weight,sum;
1148 | double Ixx = 0.0;
1149 | double Iyy = 0.0;
1150 | double Ixy = 0.0;
1151 | int i;
1152 |
1153 | /* check parameters */
1154 | if( reg == NULL ) error("get_theta: invalid region.");
1155 | if( reg_size <= 1 ) error("get_theta: region size <= 1.");
1156 | if( modgrad == NULL || modgrad->data == NULL )
1157 | error("get_theta: invalid 'modgrad'.");
1158 | if( prec < 0.0 ) error("get_theta: 'prec' must be positive.");
1159 |
1160 | /*----------- theta ---------------------------------------------------*/
1161 | /*
1162 | Region inertia matrix A:
1163 | Ixx Ixy
1164 | Ixy Iyy
1165 | where
1166 | Ixx = \sum_i y_i^2
1167 | Iyy = \sum_i x_i^2
1168 | Ixy = -\sum_i x_i y_i
1169 |
1170 | lambda1 and lambda2 are the eigenvalues, with lambda1 >= lambda2.
1171 | They are found by solving the characteristic polynomial
1172 | det(\lambda I - A) = 0.
1173 |
1174 | To get the line segment direction we want to get the eigenvector of
1175 | the smaller eigenvalue. We have to solve a,b in:
1176 | a.Ixx + b.Ixy = a.lambda2
1177 | a.Ixy + b.Iyy = b.lambda2
1178 | We want the angle theta = atan(b/a). I can be computed with
1179 | any of the two equations:
1180 | theta = atan( (lambda2-Ixx) / Ixy )
1181 | or
1182 | theta = atan( Ixy / (lambda2-Iyy) )
1183 |
1184 | When |Ixx| > |Iyy| we use the first, otherwise the second
1185 | (just to get better numeric precision).
1186 | */
1187 | sum = 0.0;
1188 | for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ];
1191 | Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight;
1192 | Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight;
1193 | Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight;
1194 | sum += weight;
1195 | }
1196 | if( sum <= 0.0 ) error("get_theta: weights sum less or equal to zero.");
1197 | Ixx /= sum;
1198 | Iyy /= sum;
1199 | Ixy /= sum;
1200 | lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) ) / 2.0;
1201 | lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) ) / 2.0;
1202 | if( fabs(lambda1) < fabs(lambda2) )
1203 | {
1204 | fprintf(stderr,"Ixx %g Iyy %g Ixy %g lamb1 %g lamb2 %g - lamb1 < lamb2\n",
1205 | Ixx,Iyy,Ixy,lambda1,lambda2);
1206 | tmp = lambda1;
1207 | lambda1 = lambda2;
1208 | lambda2 = tmp;
1209 | }
1210 |
1211 | if( fabs(Ixx) > fabs(Iyy) )
1212 | theta = atan2( lambda2-Ixx, Ixy );
1213 | else
1214 | theta = atan2( Ixy, lambda2-Iyy );
1215 |
1216 | /* The previous procedure don't cares about orientation,
1217 | so it could be wrong by 180 degrees. Here is corrected if necessary. */
1218 | if( angle_diff(theta,reg_angle) > prec ) theta += M_PI;
1219 |
1220 | return theta;
1221 | }
1222 |
1223 | /*----------------------------------------------------------------------------*/
1224 | /** @brief Computes a rectangle that covers a region of points.
1225 | */
1226 | static void region2rect( struct point * reg, int reg_size,
1227 | image_double modgrad, double reg_angle,
1228 | double prec, double p, struct rect * rec )
1229 | {
1230 | double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max;
1231 | int i;
1232 |
1233 | /* check parameters */
1234 | if( reg == NULL ) error("region2rect: invalid region.");
1235 | if( reg_size <= 1 ) error("region2rect: region size <= 1.");
1236 | if( modgrad == NULL || modgrad->data == NULL )
1237 | error("region2rect: invalid image 'modgrad'.");
1238 | if( rec == NULL ) error("region2rect: invalid 'rec'.");
1239 |
1240 | /* center */
1241 | x = y = sum = 0.0;
1242 | for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ];
1245 | x += (double) reg[i].x * weight;
1246 | y += (double) reg[i].y * weight;
1247 | sum += weight;
1248 | }
1249 | if( sum <= 0.0 ) error("region2rect: weights sum equal to zero.");
1250 | x /= sum;
1251 | y /= sum;
1252 |
1253 | /* theta */
1254 | theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec);
1255 |
1256 | /* length and width */
1257 | dx = cos(theta);
1258 | dy = sin(theta);
1259 | l_min = l_max = w_min = w_max = 0.0;
1260 | for(i=0; i l_max ) l_max = l;
1266 | if( l < l_min ) l_min = l;
1267 | if( w > w_max ) w_max = w;
1268 | if( w < w_min ) w_min = w;
1269 | }
1270 |
1271 | /* store values */
1272 | rec->x1 = x + l_min * dx;
1273 | rec->y1 = y + l_min * dy;
1274 | rec->x2 = x + l_max * dx;
1275 | rec->y2 = y + l_max * dy;
1276 | rec->width = w_max - w_min;
1277 | rec->x = x;
1278 | rec->y = y;
1279 | rec->theta = theta;
1280 | rec->dx = dx;
1281 | rec->dy = dy;
1282 | rec->prec = prec;
1283 | rec->p = p;
1284 |
1285 | if( rec->width < 1.0 ) rec->width = 1.0;
1286 | }
1287 |
1288 | /*----------------------------------------------------------------------------*/
1289 | /** @brief Found a region of points that share the same angle, up to
1290 | a tolerance 'prec', starting at point (x,y).
1291 | */
1292 | static void region_grow( int x, int y, image_double angles, struct point * reg,
1293 | int * reg_size, double * reg_angle, image_char used,
1294 | double prec )
1295 | {
1296 | double sumdx,sumdy;
1297 | int xx,yy,i;
1298 |
1299 | /* check parameters */
1300 | if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
1301 | error("region_grow: (x,y) out of the image.");
1302 | if( angles == NULL || angles->data == NULL )
1303 | error("region_grow: invalid image 'angles'.");
1304 | if( reg == NULL ) error("region_grow: invalid 'reg'.");
1305 | if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'.");
1306 | if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'.");
1307 | if( used == NULL || used->data == NULL )
1308 | error("region_grow: invalid image 'used'.");
1309 |
1310 | /* first point of the region */
1311 | *reg_size = 1;
1312 | reg[0].x = x;
1313 | reg[0].y = y;
1314 | *reg_angle = angles->data[x+y*angles->xsize];
1315 | sumdx = cos(*reg_angle);
1316 | sumdy = sin(*reg_angle);
1317 | used->data[x+y*used->xsize] = USED;
1318 |
1319 | /* try neighbors as new region points */
1320 | for(i=0; i<*reg_size; i++)
1321 | for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++)
1322 | for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++)
1323 | if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize &&
1324 | used->data[xx+yy*used->xsize] != USED &&
1325 | isaligned(xx,yy,angles,*reg_angle,prec) )
1326 | {
1327 | /* add point */
1328 | used->data[xx+yy*used->xsize] = USED;
1329 | reg[*reg_size].x = xx;
1330 | reg[*reg_size].y = yy;
1331 | ++(*reg_size);
1332 |
1333 | /* update region's angle */
1334 | sumdx += cos( angles->data[xx+yy*angles->xsize] );
1335 | sumdy += sin( angles->data[xx+yy*angles->xsize] );
1336 | *reg_angle = atan2(sumdy,sumdx);
1337 | }
1338 | }
1339 |
1340 | /*----------------------------------------------------------------------------*/
1341 | /** @brief Try some rectangles variations to improve NFA value.
1342 | Only if the rectangle is not meaningful (i.e., log_nfa <= eps).
1343 | */
1344 | static double rect_improve( struct rect * rec, image_double angles,
1345 | double logNT, double eps )
1346 | {
1347 | struct rect r;
1348 | double log_nfa,log_nfa_new;
1349 | double delta = 0.5;
1350 | double delta_2 = delta / 2.0;
1351 | int n;
1352 |
1353 | log_nfa = rect_nfa(rec,angles,logNT);
1354 |
1355 | if( log_nfa > eps ) return log_nfa;
1356 |
1357 | /* try finer precisions */
1358 | rect_copy(rec,&r);
1359 | for(n=0; n<5; n++)
1360 | {
1361 | r.p /= 2.0;
1362 | r.prec = r.p * M_PI;
1363 | log_nfa_new = rect_nfa(&r,angles,logNT);
1364 | if( log_nfa_new > log_nfa )
1365 | {
1366 | log_nfa = log_nfa_new;
1367 | rect_copy(&r,rec);
1368 | }
1369 | }
1370 |
1371 | if( log_nfa > eps ) return log_nfa;
1372 |
1373 | /* try to reduce width */
1374 | rect_copy(rec,&r);
1375 | for(n=0; n<5; n++)
1376 | {
1377 | if( (r.width - delta) >= 0.5 )
1378 | {
1379 | r.width -= delta;
1380 | log_nfa_new = rect_nfa(&r,angles,logNT);
1381 | if( log_nfa_new > log_nfa )
1382 | {
1383 | rect_copy(&r,rec);
1384 | log_nfa = log_nfa_new;
1385 | }
1386 | }
1387 | }
1388 |
1389 | if( log_nfa > eps ) return log_nfa;
1390 |
1391 | /* try to reduce one side of the rectangle */
1392 | rect_copy(rec,&r);
1393 | for(n=0; n<5; n++)
1394 | {
1395 | if( (r.width - delta) >= 0.5 )
1396 | {
1397 | r.x1 += -r.dy * delta_2;
1398 | r.y1 += r.dx * delta_2;
1399 | r.x2 += -r.dy * delta_2;
1400 | r.y2 += r.dx * delta_2;
1401 | r.width -= delta;
1402 | log_nfa_new = rect_nfa(&r,angles,logNT);
1403 | if( log_nfa_new > log_nfa )
1404 | {
1405 | rect_copy(&r,rec);
1406 | log_nfa = log_nfa_new;
1407 | }
1408 | }
1409 | }
1410 |
1411 | if( log_nfa > eps ) return log_nfa;
1412 |
1413 | /* try to reduce the other side of the rectangle */
1414 | rect_copy(rec,&r);
1415 | for(n=0; n<5; n++)
1416 | {
1417 | if( (r.width - delta) >= 0.5 )
1418 | {
1419 | r.x1 -= -r.dy * delta_2;
1420 | r.y1 -= r.dx * delta_2;
1421 | r.x2 -= -r.dy * delta_2;
1422 | r.y2 -= r.dx * delta_2;
1423 | r.width -= delta;
1424 | log_nfa_new = rect_nfa(&r,angles,logNT);
1425 | if( log_nfa_new > log_nfa )
1426 | {
1427 | rect_copy(&r,rec);
1428 | log_nfa = log_nfa_new;
1429 | }
1430 | }
1431 | }
1432 |
1433 | if( log_nfa > eps ) return log_nfa;
1434 |
1435 | /* try even finer precisions */
1436 | rect_copy(rec,&r);
1437 | for(n=0; n<5; n++)
1438 | {
1439 | r.p /= 2.0;
1440 | r.prec = r.p * M_PI;
1441 | log_nfa_new = rect_nfa(&r,angles,logNT);
1442 | if( log_nfa_new > log_nfa )
1443 | {
1444 | log_nfa = log_nfa_new;
1445 | rect_copy(&r,rec);
1446 | }
1447 | }
1448 |
1449 | return log_nfa;
1450 | }
1451 |
1452 | /*----------------------------------------------------------------------------*/
1453 | /** @brief Reduce the region size, by elimination the points far from the
1454 | starting point, until that leads to rectangle with the right
1455 | density of region points or to discard the region if too small.
1456 | */
1457 | static int reduce_region_radius( struct point * reg, int * reg_size,
1458 | image_double modgrad, double reg_angle,
1459 | double prec, double p, struct rect * rec,
1460 | image_char used, image_double angles,
1461 | double density_th, double logNT, double eps )
1462 | {
1463 | double density,rad1,rad2,rad,xc,yc,log_nfa;
1464 | int i;
1465 |
1466 | /* check parameters */
1467 | if( reg == NULL ) error("refine: invalid pointer 'reg'.");
1468 | if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'.");
1469 | if( prec < 0.0 ) error("refine: 'prec' must be positive.");
1470 | if( rec == NULL ) error("refine: invalid pointer 'rec'.");
1471 | if( used == NULL || used->data == NULL )
1472 | error("refine: invalid image 'used'.");
1473 | if( angles == NULL || angles->data == NULL )
1474 | error("refine: invalid image 'angles'.");
1475 |
1476 | /* compute region points density */
1477 | density = (double) *reg_size /
1478 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
1479 |
1480 | if( density >= density_th ) return TRUE;
1481 |
1482 | /* compute region radius */
1483 | xc = (double) reg[0].x;
1484 | yc = (double) reg[0].y;
1485 | rad1 = dist( xc, yc, rec->x1, rec->y1 );
1486 | rad2 = dist( xc, yc, rec->x2, rec->y2 );
1487 | rad = rad1 > rad2 ? rad1 : rad2;
1488 |
1489 | while( density < density_th )
1490 | {
1491 | rad *= 0.75;
1492 |
1493 | /* remove points from the region and update 'used' map */
1494 | for(i=0; i<*reg_size; i++)
1495 | if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad )
1496 | {
1497 | /* point not kept, mark it as NOTUSED */
1498 | used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
1499 | /* remove point from the region */
1500 | reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */
1501 | reg[i].y = reg[*reg_size-1].y;
1502 | --(*reg_size);
1503 | --i; /* to avoid skipping one point */
1504 | }
1505 |
1506 | /* reject if the region is too small.
1507 | 2 is the minimal region size for 'region2rect' to work. */
1508 | if( *reg_size < 2 ) return FALSE;
1509 |
1510 | /* re-compute rectangle */
1511 | region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
1512 |
1513 | /* try to improve the rectangle and compute NFA */
1514 | log_nfa = rect_improve(rec,angles,logNT,eps);
1515 |
1516 | /* re-compute region points density */
1517 | density = (double) *reg_size /
1518 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
1519 | }
1520 |
1521 | /* if the final rectangle is meaningful accept, otherwise reject */
1522 | if( log_nfa > eps ) return TRUE;
1523 | else return FALSE;
1524 | }
1525 |
1526 | /*----------------------------------------------------------------------------*/
1527 | /** @brief Refine a rectangle. For that, an estimation of the angle
1528 | tolerance is performed by the standard deviation of the angle at points
1529 | near the region's starting point. Then, a new region is grown starting
1530 | from the same point, but using the estimated angle tolerance.
1531 | If this fails to produce a rectangle with the right density of
1532 | region points, 'reduce_region_radius' is called to try to
1533 | satisfy this condition.
1534 | */
1535 | static int refine( struct point * reg, int * reg_size, image_double modgrad,
1536 | double reg_angle, double prec, double p, struct rect * rec,
1537 | image_char used, image_double angles, double density_th,
1538 | double logNT, double eps )
1539 | {
1540 | double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum,log_nfa;
1541 | int i,n;
1542 |
1543 | /* check parameters */
1544 | if( reg == NULL ) error("refine: invalid pointer 'reg'.");
1545 | if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'.");
1546 | if( prec < 0.0 ) error("refine: 'prec' must be positive.");
1547 | if( rec == NULL ) error("refine: invalid pointer 'rec'.");
1548 | if( used == NULL || used->data == NULL )
1549 | error("refine: invalid image 'used'.");
1550 | if( angles == NULL || angles->data == NULL )
1551 | error("refine: invalid image 'angles'.");
1552 |
1553 | /* compute region points density */
1554 | density = (double) *reg_size /
1555 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
1556 |
1557 | if( density >= density_th ) return TRUE;
1558 |
1559 | /*------ First try: reduce angle tolerance ------*/
1560 |
1561 | /* compute the new mean angle and tolerance */
1562 | xc = (double) reg[0].x;
1563 | yc = (double) reg[0].y;
1564 | ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ];
1565 | sum = s_sum = 0.0;
1566 | n = 0;
1567 | for(i=0; i<*reg_size; i++)
1568 | {
1569 | used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
1570 | if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width )
1571 | {
1572 | angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ];
1573 | ang_d = angle_diff_signed(angle,ang_c);
1574 | sum += ang_d;
1575 | s_sum += ang_d * ang_d;
1576 | ++n;
1577 | }
1578 | }
1579 | mean_angle = sum / (double) n;
1580 | tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n
1581 | + mean_angle*mean_angle ); /* 2 * standard deviation */
1582 |
1583 | /* find a new region from the same starting point and new angle tolerance */
1584 | region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau);
1585 |
1586 | /* if the region is too small, reject */
1587 | if( *reg_size < 2 ) return FALSE;
1588 |
1589 | /* re-compute rectangle */
1590 | region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
1591 |
1592 | /* try to improve the rectangle and compute NFA */
1593 | log_nfa = rect_improve(rec,angles,logNT,eps);
1594 |
1595 | /* re-compute region points density */
1596 | density = (double) *reg_size /
1597 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
1598 |
1599 | /*------ Second try: reduce region radius ------*/
1600 | if( density < density_th )
1601 | return reduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p,
1602 | rec, used, angles, density_th, logNT, eps );
1603 |
1604 | /* if the final rectangle is meaningful accept, otherwise reject */
1605 | if( log_nfa > eps ) return TRUE;
1606 | else return FALSE;
1607 | }
1608 |
1609 |
1610 | /*----------------------------------------------------------------------------*/
1611 | /*-------------------------- Line Segment Detector ---------------------------*/
1612 | /*----------------------------------------------------------------------------*/
1613 |
1614 | /*----------------------------------------------------------------------------*/
1615 | /** @brief LSD full interface
1616 | */
1617 | ntuple_list LineSegmentDetection( image_double image, double scale,
1618 | double sigma_scale, double quant,
1619 | double ang_th, double eps, double density_th,
1620 | int n_bins, double max_grad,
1621 | image_int * region )
1622 | {
1623 | ntuple_list out = new_ntuple_list(5);
1624 | image_double scaled_image,angles,modgrad;
1625 | image_char used;
1626 | struct coorlist * list_p;
1627 | void * mem_p;
1628 | struct rect rec;
1629 | struct point * reg;
1630 | int reg_size,min_reg_size,i;
1631 | unsigned int xsize,ysize;
1632 | double rho,reg_angle,prec,p,log_nfa,logNT;
1633 | int ls_count = 0; /* line segments are numbered 1,2,3,... */
1634 |
1635 |
1636 | /* check parameters */
1637 | if( image==NULL || image->data==NULL || image->xsize<=0 || image->ysize<=0 )
1638 | error("invalid image input.");
1639 | if( scale <= 0.0 ) error("'scale' value must be positive.");
1640 | if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
1641 | if( quant < 0.0 ) error("'quant' value must be positive.");
1642 | if( ang_th <= 0.0 || ang_th >= 180.0 )
1643 | error("'ang_th' value must be in the range (0,180).");
1644 | if( density_th < 0.0 || density_th > 1.0 )
1645 | error("'density_th' value must be in the range [0,1].");
1646 | if( n_bins <= 0 ) error("'n_bins' value must be positive.");
1647 | if( max_grad <= 0.0 ) error("'max_grad' value must be positive.");
1648 |
1649 |
1650 | /* angle tolerance */
1651 | prec = M_PI * ang_th / 180.0;
1652 | p = ang_th / 180.0;
1653 | rho = quant / sin(prec); /* gradient magnitude threshold */
1654 |
1655 |
1656 | /* scale image (if necessary) and compute angle at each pixel */
1657 | if( scale != 1.0 )
1658 | {
1659 | scaled_image = gaussian_sampler( image, scale, sigma_scale );
1660 | angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
1661 | &modgrad, (unsigned int) n_bins, max_grad );
1662 | free_image_double(scaled_image);
1663 | }
1664 | else
1665 | angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
1666 | (unsigned int) n_bins, max_grad );
1667 | xsize = angles->xsize;
1668 | ysize = angles->ysize;
1669 | logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0;
1670 | min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
1671 | that can give a meaningful event */
1672 |
1673 |
1674 | /* initialize some structures */
1675 | if( region != NULL ) /* image to output pixel region number, if asked */
1676 | *region = new_image_int_ini(angles->xsize,angles->ysize,0);
1677 | used = new_image_char_ini(xsize,ysize,NOTUSED);
1678 | reg = (struct point *) calloc(xsize * ysize, sizeof(struct point));
1679 | if( reg == NULL ) error("not enough memory!");
1680 |
1681 |
1682 | /* search for line segments */
1683 | for(; list_p != NULL; list_p = list_p->next )
1684 | if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
1685 | angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
1686 | /* there is no risk of double comparison problem here
1687 | because we are only interested in the exact NOTDEF value */
1688 | {
1689 | /* find the region of connected point and ~equal angle */
1690 | region_grow( list_p->x, list_p->y, angles, reg, ®_size,
1691 | ®_angle, used, prec );
1692 |
1693 | /* reject small regions */
1694 | if( reg_size < min_reg_size ) continue;
1695 |
1696 | /* construct rectangular approximation for the region */
1697 | region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
1698 |
1699 | /* Check if the rectangle exceeds the minimal density of
1700 | region points. If not, try to improve the region.
1701 | The rectangle will be rejected if the final one does
1702 | not fulfill the minimal density condition.
1703 | This is an addition to the original LSD algorithm published in
1704 | "LSD: A Fast Line Segment Detector with a False Detection Control"
1705 | by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
1706 | The original algorithm is obtained with density_th = 0.0.
1707 | */
1708 | if( !refine( reg, ®_size, modgrad, reg_angle, prec, p,
1709 | &rec, used, angles, density_th, logNT, eps ) ) continue;
1710 |
1711 | /* compute NFA value */
1712 | log_nfa = rect_improve(&rec,angles,logNT,eps);
1713 | if( log_nfa <= eps ) continue;
1714 |
1715 | /* A New Line Segment was found! */
1716 | ++ls_count; /* increase line segment counter */
1717 |
1718 | /*
1719 | The gradient was computed with a 2x2 mask, its value corresponds to
1720 | points with an offset of (0.5,0.5), that should be added to output.
1721 | The coordinates origin is at the center of pixel (0,0).
1722 | */
1723 | rec.x1 += 0.5; rec.y1 += 0.5;
1724 | rec.x2 += 0.5; rec.y2 += 0.5;
1725 |
1726 | /* scale the result values if a subsampling was performed */
1727 | if( scale != 1.0 )
1728 | {
1729 | rec.x1 /= scale; rec.y1 /= scale;
1730 | rec.x2 /= scale; rec.y2 /= scale;
1731 | rec.width /= scale;
1732 | }
1733 |
1734 | /* add line segment found to output */
1735 | add_5tuple(out, rec.x1, rec.y1, rec.x2, rec.y2, rec.width);
1736 |
1737 | /* add region number to 'region' image if needed */
1738 | if( region != NULL )
1739 | for(i=0; idata[reg[i].x+reg[i].y*(*region)->xsize] = ls_count;
1741 | }
1742 |
1743 |
1744 | /* free memory */
1745 | free_image_double(angles);
1746 | free_image_double(modgrad);
1747 | free_image_char(used);
1748 | free( (void *) reg );
1749 | free( (void *) mem_p );
1750 |
1751 | return out;
1752 | }
1753 |
1754 | /*----------------------------------------------------------------------------*/
1755 | /** @brief LSD Simple Interface with Scale
1756 | /*/
1757 | ntuple_list lsd_scale(image_double image, double scale)
1758 | {
1759 | /* LSD parameters */
1760 | double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as
1761 | sigma = sigma_scale/scale. */
1762 | double quant = 2.0; /* Bound to the quantization error on the
1763 | gradient norm. */
1764 | double ang_th = 22.5; /* Gradient angle tolerance in degrees. */
1765 | double eps = 0.0; /* Detection threshold, -log10(NFA). */
1766 | double density_th = 0.7; /* Minimal density of region points in rectangle. */
1767 | int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient
1768 | modulus. */
1769 | double max_grad = 255.0; /* Gradient modulus in the highest bin. The
1770 | default value corresponds to the highest
1771 | gradient modulus on images with gray
1772 | levels in [0,255]. */
1773 |
1774 | return LineSegmentDetection( image, scale, sigma_scale, quant, ang_th, eps,
1775 | density_th, n_bins, max_grad, NULL );
1776 | }
1777 |
1778 | /*----------------------------------------------------------------------------*/
1779 | /** @brief LSD Simple Interface
1780 | */
1781 | ntuple_list lsd(image_double image)
1782 | {
1783 | /* LSD parameters */
1784 | double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */
1785 |
1786 | return lsd_scale(image,scale);
1787 | }
1788 | /*----------------------------------------------------------------------------*/
1789 |
--------------------------------------------------------------------------------