├── 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 | 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 | --------------------------------------------------------------------------------