├── bin └── .gitignore ├── docs └── .gitignore ├── lib └── .gitignore ├── include ├── Makefile ├── minpq.h ├── kdtree.h ├── imgfeatures.h ├── sift.h ├── xform.h └── utils.h ├── beaver.png ├── beaver_xform.png ├── .gitignore ├── Makefile ├── THANKS ├── src ├── Makefile ├── dspfeat.c ├── match.c ├── match_num.c ├── minpq.c ├── siftfeat.c ├── utils.c ├── imgfeatures.c ├── kdtree.c ├── xform.c └── sift.c ├── mainpage ├── README.md ├── LICENSE └── beaver.sift /bin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /include/Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | rm -f *~ 3 | 4 | .PHONY: clean 5 | -------------------------------------------------------------------------------- /beaver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwhess/opensift/HEAD/beaver.png -------------------------------------------------------------------------------- /beaver_xform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwhess/opensift/HEAD/beaver_xform.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS += -O3 3 | BIN_DIR = ./bin 4 | SRC_DIR = ./src 5 | DOC_DIR = ./docs 6 | INC_DIR = ./include 7 | LIB_DIR = ./lib 8 | BIN = siftfeat match dspfeat match_num 9 | 10 | all: $(BIN) libopensift.a docs 11 | 12 | docs: 13 | doxygen Doxyfile 14 | 15 | libopensift.a: 16 | make -C $(SRC_DIR) $@ 17 | 18 | $(BIN): 19 | make -C $(SRC_DIR) $@ 20 | 21 | clean: 22 | make -C $(SRC_DIR) $@; \ 23 | make -C $(INC_DIR) $@; \ 24 | 25 | distclean: clean 26 | rm -f $(LIB_DIR)/* 27 | rm -f $(BIN_DIR)/* 28 | 29 | docsclean: 30 | rm -rf $(DOC_DIR)/html/ 31 | 32 | .PHONY: docs clean docsclean libopensift.a 33 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | Special thanks to all those who have helped me maintain this 2 | distribution, including: 3 | 4 | * Fadi Chakik 5 | * Werner Pomwenger 6 | * Joel Sole 7 | * Xu Jie 8 | * Gerrit Verstraete 9 | * Vincent Rabaud 10 | * Yuan Gong 11 | * Sebastien Henrotin 12 | * Hannes Bistry 13 | * Peter Backes 14 | * Keun Dong Lee 15 | 16 | If you've helped and aren't listed, it isn't intentional. Just remind 17 | me, and I'll put you in here. 18 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS += 3 | BIN_DIR = ../bin 4 | INC_DIR = ../include 5 | LIB_DIR = ../lib 6 | INCL = -I$(INC_DIR) `pkg-config --cflags opencv gtk+-2.0` 7 | LIBS = -L$(LIB_DIR) -lopensift -lm `pkg-config --libs opencv gtk+-2.0` 8 | OBJ = imgfeatures.o utils.o sift.o kdtree.o minpq.o xform.o 9 | BIN = siftfeat match dspfeat match_num 10 | 11 | all: $(BIN) libopensift.a 12 | 13 | libopensift.a: $(OBJ) 14 | ar rc $(LIB_DIR)/$@ $(OBJ) 15 | ranlib $(LIB_DIR)/$@ 16 | 17 | siftfeat: libopensift.a siftfeat.c 18 | $(CC) $(CFLAGS) $(INCL) siftfeat.c -o $(BIN_DIR)/$@ $(LIBS) 19 | 20 | match: libopensift.a match.c 21 | $(CC) $(CFLAGS) $(INCL) match.c -o $(BIN_DIR)/$@ $(LIBS) 22 | 23 | match_num: libopensift.a match.c 24 | $(CC) $(CFLAGS) $(INCL) match_num.c -o $(BIN_DIR)/$@ $(LIBS) 25 | 26 | dspfeat: libopensift.a dspfeat.c 27 | $(CC) $(CFLAGS) $(INCL) dspfeat.c -o $(BIN_DIR)/$@ $(LIBS) 28 | 29 | imgfeatures.o: imgfeatures.c $(INC_DIR)/imgfeatures.h 30 | $(CC) $(CFLAGS) $(INCL) -c imgfeatures.c -o $@ 31 | 32 | utils.o: utils.c $(INC_DIR)/utils.h 33 | $(CC) $(CFLAGS) $(INCL) -c utils.c -o $@ 34 | 35 | sift.o: sift.c $(INC_DIR)/sift.h 36 | $(CC) $(CFLAGS) $(INCL) -c sift.c -o $@ 37 | 38 | kdtree.o: kdtree.c $(INC_DIR)/kdtree.h 39 | $(CC) $(CFLAGS) $(INCL) -c kdtree.c -o $@ 40 | 41 | minpq.o: minpq.c $(INC_DIR)/minpq.h 42 | $(CC) $(CFLAGS) $(INCL) -c minpq.c -o $@ 43 | 44 | xform.o: xform.c $(INC_DIR)/xform.h 45 | $(CC) $(CFLAGS) $(INCL) -c xform.c -o $@ 46 | 47 | clean: 48 | rm -f *~ *.o core 49 | 50 | .PHONY: clean 51 | -------------------------------------------------------------------------------- /mainpage: -------------------------------------------------------------------------------- 1 | /**@mainpage 2 | 3 | Contents:
4 | 1. Intro
5 | 2. Requirements
6 | 3. Building
7 | 4. License
8 | 9 | 10 | 1. Intro 11 | 12 | This is a collection of code I've put together to detect SIFT features 13 | in images and to use SIFT (or other) features to compute image 14 | transforms with RANSAC. It includes a SIFT function library as well as 15 | some executables to detect, match, and display keypoints. For more 16 | information on SIFT, refer to the paper by Lowe: 17 | 18 | Lowe, D. Distinctive image features from scale-invariant keypoints. 19 | International Journal of Computer Vision, 60, 2 (2004), 20 | pp.91--110. 21 | 22 | Or see Lowe's website:
23 | http://www.cs.ubc.ca/~lowe/keypoints/ 24 | 25 | Some of the code also works with affine-invariant features from the code 26 | by the VGG at oxford:
27 | http://www.robots.ox.ac.uk:5000/~vgg/research/affine/index.html 28 | 29 | Check out match.c for an example of how to use the RANSAC function. Try 30 | `match beaver.png beaver_xform.png` to see it work. 31 | 32 | Use the navigation bar at the top of this page to browse the 33 | documentation. 34 | 35 | Help is available for executables using the '-h' command line option. 36 | 37 |
38 | 39 | 2. Requirements 40 | 41 | All code in this package requires the OpenCV library (known working 42 | version is 2.0):
43 | http://sourceforge.net/projects/opencvlibrary/ 44 | 45 | Some functions require GDK/GTK+2 (known working version is 2.18.4):
46 | http://www.gtk.org/ 47 | 48 |
49 | 50 | 3. Building 51 | 52 | To build everything, use make: 53 | 54 |
55 | > make
56 | 
57 | 58 | This should produce a few executables in bin/, a static library 59 | lib/libfeat.a, and some HTML documentation in docs/. You can use the -h 60 | argument to get help with any of the executables. libfeat.a can be 61 | compiled into your own code using the standard method: 62 | 63 |
64 | > gcc -I/path/to/sift/include/ -L/path/to/sift/lib/ yourcode.c -o yourexecutable -lfeat
65 | 
66 | 67 | The documentation in docs/ describes all of the functions available in 68 | libfeat.a as well as #defines, etc. Use the documentation to determine 69 | what header files from include/ to include in your code. 70 | 71 | You can also individually build any of the executables or libfeat.a, 72 | e.g. 73 | 74 |
75 | > make libfeat.a
76 | 
77 |
78 | 79 | 4. License 80 | 81 | See the file LICENSE for more information on the legal terms of the use 82 | of this package. 83 | 84 | */ 85 | -------------------------------------------------------------------------------- /include/minpq.h: -------------------------------------------------------------------------------- 1 | /**@file 2 | Functions and structures for implementing a minimizing priority queue. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | 10 | #ifndef MINPQ_H 11 | #define MINPQ_H 12 | 13 | #include 14 | 15 | 16 | /******************************* Defs and macros *****************************/ 17 | 18 | /* initial # of priority queue elements for which to allocate space */ 19 | #define MINPQ_INIT_NALLOCD 512 20 | 21 | /********************************** Structures *******************************/ 22 | 23 | /** an element in a minimizing priority queue */ 24 | struct pq_node 25 | { 26 | void* data; 27 | int key; 28 | }; 29 | 30 | 31 | /** a minimizing priority queue */ 32 | struct min_pq 33 | { 34 | struct pq_node* pq_array; /* array containing priority queue */ 35 | int nallocd; /* number of elements allocated */ 36 | int n; /**< number of elements in pq */ 37 | }; 38 | 39 | 40 | /*************************** Function Prototypes *****************************/ 41 | 42 | /** 43 | Creates a new minimizing priority queue. 44 | */ 45 | extern struct min_pq* minpq_init(); 46 | 47 | 48 | /** 49 | Inserts an element into a minimizing priority queue. 50 | 51 | @param min_pq a minimizing priority queue 52 | @param data the data to be inserted 53 | @param key the key to be associated with \a data 54 | 55 | @return Returns 0 on success or 1 on failure. 56 | */ 57 | extern int minpq_insert( struct min_pq* min_pq, void* data, int key ); 58 | 59 | 60 | /** 61 | Returns the element of a minimizing priority queue with the smallest key 62 | without removing it from the queue. 63 | 64 | @param min_pq a minimizing priority queue 65 | 66 | @return Returns the element of \a min_pq with the smallest key or NULL 67 | if \a min_pq is empty 68 | */ 69 | extern void* minpq_get_min( struct min_pq* min_pq ); 70 | 71 | 72 | /** 73 | Removes and returns the element of a minimizing priority queue with the 74 | smallest key. 75 | 76 | @param min_pq a minimizing priority queue 77 | 78 | @return Returns the element of \a min_pq with the smallest key of NULL 79 | if \a min_pq is empty 80 | */ 81 | extern void* minpq_extract_min( struct min_pq* min_pq ); 82 | 83 | 84 | /** 85 | De-allocates the memory held by a minimizing priorioty queue 86 | 87 | @param min_pq pointer to a minimizing priority queue 88 | */ 89 | extern void minpq_release( struct min_pq** min_pq ); 90 | 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *Update: This project is essentially unmaintained at this point, since I don't have time to work on it, and my focus has moved on to other things. Note that this very code was long ago incorporated directly into OpenCV and, as far as I know, still serves as the basis of OpenCV's SIFT implementation. Thus, if you have any issues with this project, I encourage you to use OpenCV's SIFT implementation directly.* 2 | 3 | Intro 4 | ===== 5 | 6 | This is a collection of code I've put together to detect SIFT features 7 | in images and to use SIFT (or other) features to compute image 8 | transforms with RANSAC. It includes a SIFT function library as well as 9 | some executables to detect, match, and display keypoints. For more 10 | information on SIFT, refer to the paper by Lowe: 11 | 12 | Lowe, D. Distinctive image features from scale-invariant keypoints. 13 | International Journal of Computer Vision, 60, 2 (2004), pp.91--110. 14 | 15 | Or see Lowe's website: 16 | http://www.cs.ubc.ca/~lowe/keypoints/ 17 | 18 | Some of the code also works with affine-invariant features from the code 19 | by the VGG at oxford: 20 | http://www.robots.ox.ac.uk:5000/~vgg/research/affine/index.html 21 | 22 | Check out match.c for an example of how to use the RANSAC function. Try 23 | `match beaver.png beaver_xform.png` to see it work. 24 | 25 | Documentation is included in the docs/ directory. If it is not there, 26 | use `make docs` to build it (you need Doxygen). 27 | 28 | Help is available for executables using the '-h' command line option. 29 | 30 | 31 | Requirements 32 | ============ 33 | 34 | All code in this package requires the OpenCV library (known working 35 | version is 2.3): 36 | http://sourceforge.net/projects/opencvlibrary/ 37 | 38 | Some functions require GDK/GTK+2 (known working version is 2.18.4): 39 | http://www.gtk.org/ 40 | 41 | Building 42 | ======== 43 | 44 | To build everything, use make: 45 | 46 | make 47 | 48 | This should produce a few executables in bin/, a static library 49 | lib/libopensift.a, and some HTML documentation in docs/. You can use the -h 50 | argument to get help with any of the executables. libopensift.a can be 51 | compiled into your own code using the standard method: 52 | 53 | gcc -I/path/to/opensift/include/ -L/path/to/opensift/lib/ yourcode.c -o yourexecutable -lopensift 54 | 55 | The documentation in docs/ describes all of the functions available in 56 | libopensift.a as well as #defines, etc. Use the documentation to determine 57 | what header files from include/ to include in your code. 58 | 59 | You can also individually build any of the executables or libopensift.a, 60 | e.g. 61 | 62 | make libopensift.a 63 | 64 | 65 | License 66 | ======= 67 | 68 | See the file LICENSE for more information on the legal terms of the use 69 | of this package. 70 | -------------------------------------------------------------------------------- /src/dspfeat.c: -------------------------------------------------------------------------------- 1 | /* 2 | Displays image features from a file on an image 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #include "imgfeatures.h" 10 | #include "utils.h" 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #define OPTIONS ":oh" 18 | 19 | /*************************** Function Prototypes *****************************/ 20 | 21 | static void usage( char* ); 22 | static void arg_parse( int, char** ); 23 | 24 | /******************************** Globals ************************************/ 25 | 26 | char* pname; 27 | char* feat_file; 28 | char* img_file; 29 | int feat_type = FEATURE_LOWE; 30 | 31 | 32 | /********************************** Main *************************************/ 33 | 34 | 35 | int main( int argc, char** argv ) 36 | { 37 | IplImage* img; 38 | struct feature* feat; 39 | char* name; 40 | int n; 41 | 42 | arg_parse( argc, argv ); 43 | 44 | img = cvLoadImage( img_file, 1 ); 45 | if( ! img ) 46 | fatal_error( "unable to load image from %s", img_file ); 47 | n = import_features( feat_file, feat_type, &feat ); 48 | if( n == -1 ) 49 | fatal_error( "unable to import features from %s", feat_file ); 50 | name = feat_file; 51 | 52 | draw_features( img, feat, n ); 53 | cvNamedWindow( name, 1 ); 54 | cvShowImage( name, img ); 55 | cvWaitKey( 0 ); 56 | return 0; 57 | } 58 | 59 | 60 | /************************** Function Definitions *****************************/ 61 | 62 | /* 63 | Print usage info for this program. 64 | 65 | @param name program name 66 | */ 67 | static void usage( char* name ) 68 | { 69 | fprintf( stderr, "%s: display image features on an image\n\n", name ); 70 | fprintf( stderr, "Usage: %s [options] \n", name ); 71 | fprintf( stderr, "Options:\n" ); 72 | fprintf( stderr, " -h Display this message and exit\n" ); 73 | fprintf( stderr, " -o Specifies that is an" \ 74 | " Oxford-type feature file. By\n"); 75 | fprintf( stderr, " default, it is treated as a" \ 76 | " Lowe-type file.\n" ); 77 | } 78 | 79 | 80 | 81 | /* 82 | arg_parse() parses the command line arguments, setting appropriate globals. 83 | 84 | argc and argv should be passed directly from the command line 85 | */ 86 | static void arg_parse( int argc, char** argv ) 87 | { 88 | pname = basename( argv[0] ); 89 | int arg; 90 | while( 1 ) 91 | { 92 | arg = getopt( argc, argv, OPTIONS ); 93 | if( arg == -1 ) 94 | break; 95 | 96 | switch( arg ) 97 | { 98 | case ':': 99 | fatal_error( "-%c option requires an argument\n" \ 100 | "Try '%s -h' for help.", optopt, pname ); 101 | break; 102 | 103 | case 'h': 104 | usage( pname ); 105 | exit(0); 106 | break; 107 | 108 | case 'o': 109 | feat_type = FEATURE_OXFD; 110 | break; 111 | } 112 | } 113 | if( argc - optind != 2 ) 114 | fatal_error( "bad argument count.\nTry '%s -h' for help.", pname ); 115 | feat_file = argv[optind++]; 116 | img_file = argv[optind]; 117 | } 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2012, Rob Hess 2 | All rights reserved. 3 | 4 | The following patent has been issued for methods embodied in this 5 | software: "Method and apparatus for identifying scale invariant features 6 | in an image and use of same for locating an object in an image," David 7 | G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application 8 | filed March 8, 1999. Asignee: The University of British Columbia. For 9 | further details, contact David Lowe (lowe@cs.ubc.ca) or the 10 | University-Industry Liaison Office of the University of British 11 | Columbia. 12 | 13 | Note that restrictions imposed by this patent (and possibly others) 14 | exist independently of and may be in conflict with the freedoms granted 15 | in this license, which refers to copyright of the program, not patents 16 | for any methods that it implements. Both copyright and patent law must 17 | be obeyed to legally use and redistribute this program and it is not the 18 | purpose of this license to induce you to infringe any patents or other 19 | property right claims or to contest validity of any such claims. If you 20 | redistribute or use the program, then this license merely protects you 21 | from committing copyright infringement. It does not protect you from 22 | committing patent infringement. So, before you do anything with this 23 | program, make sure that you have permission to do so not merely in terms 24 | of copyright, but also in terms of patent law. 25 | 26 | Please note that this license is not to be understood as a guarantee 27 | either. If you use the program according to this license, but in 28 | conflict with patent law, it does not mean that the licensor will refund 29 | you for any losses that you incur if you are sued for your patent 30 | infringement. 31 | 32 | Redistribution and use in source and binary forms, with or without 33 | modification, are permitted provided that the following conditions are 34 | met: 35 | * Redistributions of source code must retain the above copyright and 36 | patent notices, this list of conditions and the following 37 | disclaimer. 38 | * Redistributions in binary form must reproduce the above copyright 39 | notice, this list of conditions and the following disclaimer in 40 | the documentation and/or other materials provided with the 41 | distribution. 42 | * Neither the name of Oregon State University nor the names of its 43 | contributors may be used to endorse or promote products derived 44 | from this software without specific prior written permission. 45 | 46 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 47 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 48 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 49 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 50 | HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 51 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 52 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 53 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 54 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 55 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 56 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | 58 | -------------------------------------------------------------------------------- /src/match.c: -------------------------------------------------------------------------------- 1 | /* 2 | Detects SIFT features in two images and finds matches between them. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #include "sift.h" 10 | #include "imgfeatures.h" 11 | #include "kdtree.h" 12 | #include "utils.h" 13 | #include "xform.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | 22 | /* the maximum number of keypoint NN candidates to check during BBF search */ 23 | #define KDTREE_BBF_MAX_NN_CHKS 200 24 | 25 | /* threshold on squared ratio of distances between NN and 2nd NN */ 26 | #define NN_SQ_DIST_RATIO_THR 0.49 27 | 28 | 29 | int main( int argc, char** argv ) 30 | { 31 | IplImage* img1, * img2, * stacked; 32 | struct feature* feat1, * feat2, * feat; 33 | struct feature** nbrs; 34 | struct kd_node* kd_root; 35 | CvPoint pt1, pt2; 36 | double d0, d1; 37 | int n1, n2, k, i, m = 0; 38 | 39 | if( argc != 3 ) 40 | fatal_error( "usage: %s ", argv[0] ); 41 | 42 | img1 = cvLoadImage( argv[1], 1 ); 43 | if( ! img1 ) 44 | fatal_error( "unable to load image from %s", argv[1] ); 45 | img2 = cvLoadImage( argv[2], 1 ); 46 | if( ! img2 ) 47 | fatal_error( "unable to load image from %s", argv[2] ); 48 | stacked = stack_imgs( img1, img2 ); 49 | 50 | fprintf( stderr, "Finding features in %s...\n", argv[1] ); 51 | n1 = sift_features( img1, &feat1 ); 52 | fprintf( stderr, "Finding features in %s...\n", argv[2] ); 53 | n2 = sift_features( img2, &feat2 ); 54 | fprintf( stderr, "Building kd tree...\n" ); 55 | kd_root = kdtree_build( feat2, n2 ); 56 | for( i = 0; i < n1; i++ ) 57 | { 58 | feat = feat1 + i; 59 | k = kdtree_bbf_knn( kd_root, feat, 2, &nbrs, KDTREE_BBF_MAX_NN_CHKS ); 60 | if( k == 2 ) 61 | { 62 | d0 = descr_dist_sq( feat, nbrs[0] ); 63 | d1 = descr_dist_sq( feat, nbrs[1] ); 64 | if( d0 < d1 * NN_SQ_DIST_RATIO_THR ) 65 | { 66 | pt1 = cvPoint( cvRound( feat->x ), cvRound( feat->y ) ); 67 | pt2 = cvPoint( cvRound( nbrs[0]->x ), cvRound( nbrs[0]->y ) ); 68 | pt2.y += img1->height; 69 | cvLine( stacked, pt1, pt2, CV_RGB(255,0,255), 1, 8, 0 ); 70 | m++; 71 | feat1[i].fwd_match = nbrs[0]; 72 | } 73 | } 74 | free( nbrs ); 75 | } 76 | 77 | fprintf( stderr, "Found %d total matches\n", m ); 78 | display_big_img( stacked, "Matches" ); 79 | cvWaitKey( 0 ); 80 | 81 | /* 82 | UNCOMMENT BELOW TO SEE HOW RANSAC FUNCTION WORKS 83 | 84 | Note that this line above: 85 | 86 | feat1[i].fwd_match = nbrs[0]; 87 | 88 | is important for the RANSAC function to work. 89 | */ 90 | /* 91 | { 92 | CvMat* H; 93 | IplImage* xformed; 94 | H = ransac_xform( feat1, n1, FEATURE_FWD_MATCH, lsq_homog, 4, 0.01, 95 | homog_xfer_err, 3.0, NULL, NULL ); 96 | if( H ) 97 | { 98 | xformed = cvCreateImage( cvGetSize( img2 ), IPL_DEPTH_8U, 3 ); 99 | cvWarpPerspective( img1, xformed, H, 100 | CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, 101 | cvScalarAll( 0 ) ); 102 | cvNamedWindow( "Xformed", 1 ); 103 | cvShowImage( "Xformed", xformed ); 104 | cvWaitKey( 0 ); 105 | cvReleaseImage( &xformed ); 106 | cvReleaseMat( &H ); 107 | } 108 | } 109 | */ 110 | 111 | cvReleaseImage( &stacked ); 112 | cvReleaseImage( &img1 ); 113 | cvReleaseImage( &img2 ); 114 | kdtree_release( kd_root ); 115 | free( feat1 ); 116 | free( feat2 ); 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /src/match_num.c: -------------------------------------------------------------------------------- 1 | /* 2 | Detects SIFT features in two images and finds matches between them. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #include "sift.h" 10 | #include "imgfeatures.h" 11 | #include "kdtree.h" 12 | #include "utils.h" 13 | #include "xform.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #define DEBUG 1 23 | 24 | /* the maximum number of keypoint NN candidates to check during BBF search */ 25 | #define KDTREE_BBF_MAX_NN_CHKS 200 26 | 27 | /* threshold on squared ratio of distances between NN and 2nd NN */ 28 | #define NN_SQ_DIST_RATIO_THR 0.49 29 | 30 | void* process_image(void* arg); 31 | 32 | typedef struct feature_data_t { 33 | size_t count; 34 | struct feature* features; 35 | } FeatureData; 36 | 37 | struct thread_data { 38 | int thread_id; 39 | char* filename; 40 | FeatureData fdata; 41 | }; 42 | 43 | void* process_image(void* arg) { 44 | int n; 45 | struct thread_data* ctx; 46 | IplImage* img; 47 | 48 | ctx = (struct thread_data*)arg; 49 | img = cvLoadImage(ctx->filename, 1); 50 | if (!img) fatal_error("Unable to load image from %s", ctx->filename); 51 | ctx->fdata.count = sift_features(img, &(ctx->fdata.features)); 52 | if (DEBUG) 53 | fprintf(stderr, "Found %d features in %s...\n", ctx->fdata.count, ctx->filename); 54 | cvReleaseImage(&img); 55 | pthread_exit(NULL); 56 | } 57 | 58 | int compare_features(FeatureData* f0, FeatureData* f1) 59 | { 60 | struct kd_node* kd_root; 61 | double d0, d1; 62 | struct feature** nbrs; 63 | int num_matches; 64 | size_t i; /* Serves as an index into an array, hence, size_t */ 65 | 66 | /* Build KD Tree */ 67 | kd_root = kdtree_build(f1->features, f1->count); 68 | 69 | /* Compare feature distances */ 70 | for(i = 0; i < f0->count; ++i) { 71 | int k; 72 | struct feature* feat; 73 | feat = f0->features + i; 74 | k = kdtree_bbf_knn( kd_root, feat, 2, &nbrs, KDTREE_BBF_MAX_NN_CHKS ); 75 | if( k == 2 ) { 76 | d0 = descr_dist_sq( feat, nbrs[0] ); 77 | d1 = descr_dist_sq( feat, nbrs[1] ); 78 | if( d0 < d1 * NN_SQ_DIST_RATIO_THR ) ++num_matches; 79 | } 80 | free( nbrs ); 81 | } 82 | kdtree_release( kd_root ); 83 | return num_matches; 84 | } 85 | 86 | int main( int argc, char** argv ) { 87 | int i; 88 | pthread_t threads[2]; 89 | struct thread_data td[2]; 90 | pthread_attr_t attr; 91 | void* status; 92 | 93 | /* Handle filenames */ 94 | if( argc != 3 ) fatal_error( "usage: %s ", argv[0] ); 95 | td[0].filename=argv[1]; 96 | td[1].filename=argv[2]; 97 | 98 | /* Launch processing threads */ 99 | pthread_attr_init(&attr); 100 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 101 | 102 | for (i=0; i < 2; ++i) { 103 | int rc; 104 | rc = pthread_create(&threads[i], &attr, process_image, (void*)&td[i]); 105 | if (rc) fatal_error("Unable to create processing thread (err %d).",rc); 106 | } 107 | 108 | pthread_attr_destroy(&attr); 109 | 110 | /* Wait for threads to join */ 111 | for (i=0; i < 2; ++i) { 112 | int rc; 113 | rc = pthread_join(threads[i], &status); 114 | if (rc) fatal_error("Return code from thread pthread_join is %d",rc); 115 | } 116 | 117 | fprintf( stdout, "%d\n", compare_features(&(td[0].fdata), &(td[1].fdata))); 118 | 119 | /* Release structures */ 120 | for (i=0; i<2; ++i) free(td[i].fdata.features); 121 | pthread_exit(NULL); 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /include/kdtree.h: -------------------------------------------------------------------------------- 1 | /**@file 2 | Functions and structures for maintaining a k-d tree database of image 3 | features. 4 | 5 | For more information, refer to: 6 | 7 | Beis, J. S. and Lowe, D. G. Shape indexing using approximate 8 | nearest-neighbor search in high-dimensional spaces. In Conference 9 | on Computer Vision and Pattern Recognition (CVPR) (2003), 10 | pp. 1000--1006. 11 | 12 | Copyright (C) 2006-2012 Rob Hess 13 | 14 | @version 1.1.2-20100521 15 | */ 16 | 17 | 18 | #ifndef KDTREE_H 19 | #define KDTREE_H 20 | 21 | #include "cxcore.h" 22 | 23 | 24 | /********************************* Structures ********************************/ 25 | 26 | struct feature; 27 | 28 | /** a node in a k-d tree */ 29 | struct kd_node 30 | { 31 | int ki; /**< partition key index */ 32 | double kv; /**< partition key value */ 33 | int leaf; /**< 1 if node is a leaf, 0 otherwise */ 34 | struct feature* features; /**< features at this node */ 35 | int n; /**< number of features */ 36 | struct kd_node* kd_left; /**< left child */ 37 | struct kd_node* kd_right; /**< right child */ 38 | }; 39 | 40 | 41 | /*************************** Function Prototypes *****************************/ 42 | 43 | /** 44 | A function to build a k-d tree database from keypoints in an array. 45 | 46 | @param features an array of features; this function rearranges the order 47 | of the features in this array, so you should take appropriate measures if 48 | you are relying on the order of the features (e.g. call this function 49 | before order is important) 50 | @param n the number of features in \a features 51 | 52 | @return Returns the root of a kd tree built from \a features. 53 | */ 54 | extern struct kd_node* kdtree_build( struct feature* features, int n ); 55 | 56 | 57 | 58 | /** 59 | Finds an image feature's approximate k nearest neighbors in a kd tree using 60 | Best Bin First search. 61 | 62 | @param kd_root root of an image feature kd tree 63 | @param feat image feature for whose neighbors to search 64 | @param k number of neighbors to find 65 | @param nbrs pointer to an array in which to store pointers to neighbors 66 | in order of increasing descriptor distance; memory for this array is 67 | allocated by this function and must be freed by the caller using 68 | free(*nbrs) 69 | @param max_nn_chks search is cut off after examining this many tree entries 70 | 71 | @return Returns the number of neighbors found and stored in \a nbrs, or 72 | -1 on error. 73 | */ 74 | extern int kdtree_bbf_knn( struct kd_node* kd_root, struct feature* feat, 75 | int k, struct feature*** nbrs, int max_nn_chks ); 76 | 77 | 78 | /** 79 | Finds an image feature's approximate k nearest neighbors within a specified 80 | spatial region in a kd tree using Best Bin First search. 81 | 82 | @param kd_root root of an image feature kd tree 83 | @param feat image feature for whose neighbors to search 84 | @param k number of neighbors to find 85 | @param nbrs pointer to an array in which to store pointers to neighbors 86 | in order of increasing descriptor distance; memory for this array is 87 | allocated by this function and must be freed by the caller using 88 | free(*nbrs) 89 | @param max_nn_chks search is cut off after examining this many tree entries 90 | @param rect rectangular region in which to search for neighbors 91 | @param model if true, spatial search is based on kdtree features' model 92 | locations; otherwise it is based on their image locations 93 | 94 | @return Returns the number of neighbors found and stored in \a nbrs 95 | (in case \a k neighbors could not be found before examining 96 | \a max_nn_checks keypoint entries). 97 | */ 98 | extern int kdtree_bbf_spatial_knn( struct kd_node* kd_root, 99 | struct feature* feat, int k, 100 | struct feature*** nbrs, int max_nn_chks, 101 | CvRect rect, int model ); 102 | 103 | 104 | /** 105 | De-allocates memory held by a kd tree 106 | 107 | @param kd_root pointer to the root of a kd tree 108 | */ 109 | extern void kdtree_release( struct kd_node* kd_root ); 110 | 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /include/imgfeatures.h: -------------------------------------------------------------------------------- 1 | /**@file 2 | Functions and structures for dealing with image features. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #ifndef IMGFEATURES_H 10 | #define IMGFEATURES_H 11 | 12 | #include "cxcore.h" 13 | 14 | /** FEATURE_OXFD
FEATURE_LOWE */ 15 | enum feature_type 16 | { 17 | FEATURE_OXFD, 18 | FEATURE_LOWE, 19 | }; 20 | 21 | /** FEATURE_FWD_MATCH
FEATURE_BCK_MATCH
FEATURE_MDL_MATCH */ 22 | enum feature_match_type 23 | { 24 | FEATURE_FWD_MATCH, 25 | FEATURE_BCK_MATCH, 26 | FEATURE_MDL_MATCH, 27 | }; 28 | 29 | 30 | /* colors in which to display different feature types */ 31 | #define FEATURE_OXFD_COLOR CV_RGB(255,255,0) 32 | #define FEATURE_LOWE_COLOR CV_RGB(255,0,255) 33 | 34 | /** max feature descriptor length */ 35 | #define FEATURE_MAX_D 128 36 | 37 | 38 | /** 39 | Structure to represent an affine invariant image feature. The fields 40 | x, y, a, b, c represent the affine region around the feature: 41 | 42 | a(x-u)(x-u) + 2b(x-u)(y-v) + c(y-v)(y-v) = 1 43 | */ 44 | struct feature 45 | { 46 | double x; /**< x coord */ 47 | double y; /**< y coord */ 48 | double a; /**< Oxford-type affine region parameter */ 49 | double b; /**< Oxford-type affine region parameter */ 50 | double c; /**< Oxford-type affine region parameter */ 51 | double scl; /**< scale of a Lowe-style feature */ 52 | double ori; /**< orientation of a Lowe-style feature */ 53 | int d; /**< descriptor length */ 54 | double descr[FEATURE_MAX_D]; /**< descriptor */ 55 | int type; /**< feature type, OXFD or LOWE */ 56 | int category; /**< all-purpose feature category */ 57 | struct feature* fwd_match; /**< matching feature from forward image */ 58 | struct feature* bck_match; /**< matching feature from backmward image */ 59 | struct feature* mdl_match; /**< matching feature from model */ 60 | CvPoint2D64f img_pt; /**< location in image */ 61 | CvPoint2D64f mdl_pt; /**< location in model */ 62 | void* feature_data; /**< user-definable data */ 63 | }; 64 | 65 | 66 | /** 67 | Reads image features from file. The file should be formatted either as 68 | from the code provided by the Visual Geometry Group at Oxford or from 69 | the code provided by David Lowe. 70 | 71 | 72 | @param filename location of a file containing image features 73 | @param type determines how features are input. If \a type is FEATURE_OXFD, 74 | the input file is treated as if it is from the code provided by the VGG 75 | at Oxford: http://www.robots.ox.ac.uk:5000/~vgg/research/affine/index.html 76 |

77 | If \a type is FEATURE_LOWE, the input file is treated as if it is from 78 | David Lowe's SIFT code: http://www.cs.ubc.ca/~lowe/keypoints 79 | @param feat pointer to an array in which to store imported features; memory 80 | for this array is allocated by this function and must be released by 81 | the caller using free(*feat) 82 | 83 | @return Returns the number of features imported from filename or -1 on error 84 | */ 85 | extern int import_features( char* filename, int type, struct feature** feat ); 86 | 87 | 88 | /** 89 | Exports a feature set to a file formatted depending on the type of 90 | features, as specified in the feature struct's type field. 91 | 92 | @param filename name of file to which to export features 93 | @param feat feature array 94 | @param n number of features 95 | 96 | @return Returns 0 on success or 1 on error 97 | */ 98 | extern int export_features( char* filename, struct feature* feat, int n ); 99 | 100 | 101 | /** 102 | Displays a set of features on an image 103 | 104 | @param img image on which to display features 105 | @param feat array of Oxford-type features 106 | @param n number of features 107 | */ 108 | extern void draw_features( IplImage* img, struct feature* feat, int n ); 109 | 110 | 111 | /** 112 | Calculates the squared Euclidian distance between two feature descriptors. 113 | 114 | @param f1 first feature 115 | @param f2 second feature 116 | 117 | @return Returns the squared Euclidian distance between the descriptors of 118 | \a f1 and \a f2. 119 | */ 120 | extern double descr_dist_sq( struct feature* f1, struct feature* f2 ); 121 | 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /include/sift.h: -------------------------------------------------------------------------------- 1 | /**@file 2 | Functions for detecting SIFT image features. 3 | 4 | For more information, refer to: 5 | 6 | Lowe, D. Distinctive image features from scale-invariant keypoints. 7 | International Journal of Computer Vision, 60, 2 (2004), 8 | pp.91--110. 9 | 10 | Copyright (C) 2006-2012 Rob Hess 11 | 12 | Note: The SIFT algorithm is patented in the United States and cannot be 13 | used in commercial products without a license from the University of 14 | British Columbia. For more information, refer to the file LICENSE.ubc 15 | that accompanied this distribution. 16 | 17 | @version 1.1.2-20100521 18 | */ 19 | 20 | #ifndef SIFT_H 21 | #define SIFT_H 22 | 23 | #include "cxcore.h" 24 | 25 | /******************************** Structures *********************************/ 26 | 27 | /** holds feature data relevant to detection */ 28 | struct detection_data 29 | { 30 | int r; 31 | int c; 32 | int octv; 33 | int intvl; 34 | double subintvl; 35 | double scl_octv; 36 | }; 37 | 38 | struct feature; 39 | 40 | 41 | /******************************* Defs and macros *****************************/ 42 | 43 | /** default number of sampled intervals per octave */ 44 | #define SIFT_INTVLS 3 45 | 46 | /** default sigma for initial gaussian smoothing */ 47 | #define SIFT_SIGMA 1.6 48 | 49 | /** default threshold on keypoint contrast |D(x)| */ 50 | #define SIFT_CONTR_THR 0.04 51 | 52 | /** default threshold on keypoint ratio of principle curvatures */ 53 | #define SIFT_CURV_THR 10 54 | 55 | /** double image size before pyramid construction? */ 56 | #define SIFT_IMG_DBL 1 57 | 58 | /** default width of descriptor histogram array */ 59 | #define SIFT_DESCR_WIDTH 4 60 | 61 | /** default number of bins per histogram in descriptor array */ 62 | #define SIFT_DESCR_HIST_BINS 8 63 | 64 | /* assumed gaussian blur for input image */ 65 | #define SIFT_INIT_SIGMA 0.5 66 | 67 | /* width of border in which to ignore keypoints */ 68 | #define SIFT_IMG_BORDER 5 69 | 70 | /* maximum steps of keypoint interpolation before failure */ 71 | #define SIFT_MAX_INTERP_STEPS 5 72 | 73 | /* default number of bins in histogram for orientation assignment */ 74 | #define SIFT_ORI_HIST_BINS 36 75 | 76 | /* determines gaussian sigma for orientation assignment */ 77 | #define SIFT_ORI_SIG_FCTR 1.5 78 | 79 | /* determines the radius of the region used in orientation assignment */ 80 | #define SIFT_ORI_RADIUS 3.0 * SIFT_ORI_SIG_FCTR 81 | 82 | /* number of passes of orientation histogram smoothing */ 83 | #define SIFT_ORI_SMOOTH_PASSES 2 84 | 85 | /* orientation magnitude relative to max that results in new feature */ 86 | #define SIFT_ORI_PEAK_RATIO 0.8 87 | 88 | /* determines the size of a single descriptor orientation histogram */ 89 | #define SIFT_DESCR_SCL_FCTR 3.0 90 | 91 | /* threshold on magnitude of elements of descriptor vector */ 92 | #define SIFT_DESCR_MAG_THR 0.2 93 | 94 | /* factor used to convert floating-point descriptor to unsigned char */ 95 | #define SIFT_INT_DESCR_FCTR 512.0 96 | 97 | /* returns a feature's detection data */ 98 | #define feat_detection_data(f) ( (struct detection_data*)(f->feature_data) ) 99 | 100 | 101 | /*************************** Function Prototypes *****************************/ 102 | 103 | /** 104 | Finds SIFT features in an image using default parameter values. All 105 | detected features are stored in the array pointed to by \a feat. 106 | 107 | @param img the image in which to detect features 108 | @param feat a pointer to an array in which to store detected features; 109 | memory for this array is allocated by this function and must be freed by 110 | the caller using free(*feat) 111 | 112 | @return Returns the number of features stored in \a feat or -1 on failure 113 | @see _sift_features() 114 | */ 115 | extern int sift_features( IplImage* img, struct feature** feat ); 116 | 117 | 118 | 119 | /** 120 | Finda SIFT features in an image using user-specified parameter values. All 121 | detected features are stored in the array pointed to by \a feat. 122 | 123 | @param img the image in which to detect features 124 | @param feat a pointer to an array in which to store detected features; 125 | memory for this array is allocated by this function and must be freed by 126 | the caller using free(*feat) 127 | @param intvls the number of intervals sampled per octave of scale space 128 | @param sigma the amount of Gaussian smoothing applied to each image level 129 | before building the scale space representation for an octave 130 | @param contr_thr a threshold on the value of the scale space function 131 | \f$\left|D(\hat{x})\right|\f$, where \f$\hat{x}\f$ is a vector specifying 132 | feature location and scale, used to reject unstable features; assumes 133 | pixel values in the range [0, 1] 134 | @param curv_thr threshold on a feature's ratio of principle curvatures 135 | used to reject features that are too edge-like 136 | @param img_dbl should be 1 if image doubling prior to scale space 137 | construction is desired or 0 if not 138 | @param descr_width the width, \f$n\f$, of the \f$n \times n\f$ array of 139 | orientation histograms used to compute a feature's descriptor 140 | @param descr_hist_bins the number of orientations in each of the 141 | histograms in the array used to compute a feature's descriptor 142 | 143 | @return Returns the number of keypoints stored in \a feat or -1 on failure 144 | @see sift_features() 145 | */ 146 | extern int _sift_features( IplImage* img, struct feature** feat, int intvls, 147 | double sigma, double contr_thr, int curv_thr, 148 | int img_dbl, int descr_width, int descr_hist_bins ); 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/minpq.c: -------------------------------------------------------------------------------- 1 | /* 2 | Functions and structures for implementing a minimizing priority queue. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #include "minpq.h" 10 | #include "utils.h" 11 | 12 | #include 13 | 14 | /************************* Local Function Prototypes *************************/ 15 | 16 | static void restore_minpq_order( struct pq_node*, int, int ); 17 | static void decrease_pq_node_key( struct pq_node*, int, int ); 18 | 19 | 20 | /************************** Local Inline Functions ***************************/ 21 | 22 | /* returns the array index of element i's parent */ 23 | static inline int parent( int i ) 24 | { 25 | return ( i - 1 ) / 2; 26 | } 27 | 28 | 29 | /* returns the array index of element i's right child */ 30 | static inline int right( int i ) 31 | { 32 | return 2 * i + 2; 33 | } 34 | 35 | 36 | /* returns the array index of element i's left child */ 37 | static inline int left( int i ) 38 | { 39 | return 2 * i + 1; 40 | } 41 | 42 | 43 | /********************** Functions prototyped in minpq.h **********************/ 44 | 45 | 46 | /* 47 | Creates a new minimizing priority queue. 48 | */ 49 | struct min_pq* minpq_init() 50 | { 51 | struct min_pq* min_pq; 52 | 53 | min_pq = malloc( sizeof( struct min_pq ) ); 54 | min_pq->pq_array = calloc( MINPQ_INIT_NALLOCD, sizeof( struct pq_node ) ); 55 | min_pq->nallocd = MINPQ_INIT_NALLOCD; 56 | min_pq->n = 0; 57 | 58 | return min_pq; 59 | } 60 | 61 | 62 | 63 | /** 64 | Inserts an element into a minimizing priority queue. 65 | 66 | @param min_pq a minimizing priority queue 67 | @param data the data to be inserted 68 | @param key the key to be associated with \a data 69 | 70 | @return Returns 0 on success or 1 on failure. 71 | */ 72 | int minpq_insert( struct min_pq* min_pq, void* data, int key ) 73 | { 74 | int n = min_pq->n; 75 | 76 | /* double array allocation if necessary */ 77 | if( min_pq->nallocd == n ) 78 | { 79 | min_pq->nallocd = array_double( (void**)&min_pq->pq_array, 80 | min_pq->nallocd, 81 | sizeof( struct pq_node ) ); 82 | if( ! min_pq->nallocd ) 83 | { 84 | fprintf( stderr, "Warning: unable to allocate memory, %s, line %d\n", 85 | __FILE__, __LINE__ ); 86 | return 1; 87 | } 88 | } 89 | 90 | min_pq->pq_array[n].data = data; 91 | min_pq->pq_array[n].key = INT_MAX; 92 | decrease_pq_node_key( min_pq->pq_array, min_pq->n, key ); 93 | min_pq->n++; 94 | 95 | return 0; 96 | } 97 | 98 | 99 | 100 | /* 101 | Returns the element of a minimizing priority queue with the smallest key 102 | without removing it from the queue. 103 | 104 | @param min_pq a minimizing priority queue 105 | 106 | @return Returns the element of \a min_pq with the smallest key or NULL 107 | if \a min_pq is empty 108 | */ 109 | void* minpq_get_min( struct min_pq* min_pq ) 110 | { 111 | if( min_pq->n < 1 ) 112 | { 113 | fprintf( stderr, "Warning: PQ empty, %s line %d\n", __FILE__, __LINE__ ); 114 | return NULL; 115 | } 116 | return min_pq->pq_array[0].data; 117 | } 118 | 119 | 120 | 121 | /* 122 | Removes and returns the element of a minimizing priority queue with the 123 | smallest key. 124 | 125 | @param min_pq a minimizing priority queue 126 | 127 | @return Returns the element of \a min_pq with the smallest key of NULL 128 | if \a min_pq is empty 129 | */ 130 | void* minpq_extract_min( struct min_pq* min_pq ) 131 | { 132 | void* data; 133 | 134 | if( min_pq->n < 1 ) 135 | { 136 | fprintf( stderr, "Warning: PQ empty, %s line %d\n", __FILE__, __LINE__ ); 137 | return NULL; 138 | } 139 | data = min_pq->pq_array[0].data; 140 | min_pq->n--; 141 | min_pq->pq_array[0] = min_pq->pq_array[min_pq->n]; 142 | restore_minpq_order( min_pq->pq_array, 0, min_pq->n ); 143 | 144 | return data; 145 | } 146 | 147 | 148 | /* 149 | De-allocates the memory held by a minimizing priorioty queue 150 | 151 | @param min_pq pointer to a minimizing priority queue 152 | */ 153 | void minpq_release( struct min_pq** min_pq ) 154 | { 155 | if( ! min_pq ) 156 | { 157 | fprintf( stderr, "Warning: NULL pointer error, %s line %d\n", __FILE__, 158 | __LINE__ ); 159 | return; 160 | } 161 | if( *min_pq && (*min_pq)->pq_array ) 162 | { 163 | free( (*min_pq)->pq_array ); 164 | free( *min_pq ); 165 | *min_pq = NULL; 166 | } 167 | } 168 | 169 | 170 | /************************ Functions prototyped here **************************/ 171 | 172 | /* 173 | Decrease a minimizing pq element's key, rearranging the pq if necessary 174 | 175 | @param pq_array minimizing priority queue array 176 | @param i index of the element whose key is to be decreased 177 | @param key new value of element i's key; if greater than current 178 | key, no action is taken 179 | */ 180 | static void decrease_pq_node_key( struct pq_node* pq_array, int i, int key ) 181 | { 182 | struct pq_node tmp; 183 | 184 | if( key > pq_array[i].key ) 185 | return; 186 | 187 | pq_array[i].key = key; 188 | while( i > 0 && pq_array[i].key < pq_array[parent(i)].key ) 189 | { 190 | tmp = pq_array[parent(i)]; 191 | pq_array[parent(i)] = pq_array[i]; 192 | pq_array[i] = tmp; 193 | i = parent(i); 194 | } 195 | } 196 | 197 | 198 | 199 | /* 200 | Recursively restores correct priority queue order to a minimizing pq array 201 | 202 | @param pq_array a minimizing priority queue array 203 | @param i index at which to start reordering 204 | @param n number of elements in \a pq_array 205 | */ 206 | static void restore_minpq_order( struct pq_node* pq_array, int i, int n ) 207 | { 208 | struct pq_node tmp; 209 | int l, r, min = i; 210 | 211 | l = left( i ); 212 | r = right( i ); 213 | if( l < n ) 214 | if( pq_array[l].key < pq_array[i].key ) 215 | min = l; 216 | if( r < n ) 217 | if( pq_array[r].key < pq_array[min].key ) 218 | min = r; 219 | 220 | if( min != i ) 221 | { 222 | tmp = pq_array[min]; 223 | pq_array[min] = pq_array[i]; 224 | pq_array[i] = tmp; 225 | restore_minpq_order( pq_array, min, n ); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /include/xform.h: -------------------------------------------------------------------------------- 1 | /**@file 2 | Functions for computing transforms from image feature correspondences. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #ifndef XFORM_H 10 | #define XFORM_H 11 | 12 | #include 13 | 14 | 15 | /********************************** Structures *******************************/ 16 | 17 | struct feature; 18 | 19 | /** holds feature data relevant to ransac */ 20 | struct ransac_data 21 | { 22 | void* orig_feat_data; 23 | int sampled; 24 | }; 25 | 26 | /******************************* Defs and macros *****************************/ 27 | 28 | /* RANSAC error tolerance in pixels */ 29 | #define RANSAC_ERR_TOL 3 30 | 31 | /** pessimistic estimate of fraction of inlers for RANSAC */ 32 | #define RANSAC_INLIER_FRAC_EST 0.25 33 | 34 | /** estimate of the probability that a correspondence supports a bad model */ 35 | #define RANSAC_PROB_BAD_SUPP 0.10 36 | 37 | /* extracts a feature's RANSAC data */ 38 | #define feat_ransac_data( feat ) ( (struct ransac_data*) (feat)->feature_data ) 39 | 40 | 41 | /** 42 | Prototype for transformation functions passed to ransac_xform(). Functions 43 | of this type should compute a transformation matrix given a set of point 44 | correspondences. 45 | 46 | @param pts array of points 47 | @param mpts array of corresponding points; each \a pts[\a i], \a i=0..\a 48 | n-1, corresponds to \a mpts[\a i] 49 | @param n number of points in both \a pts and \a mpts 50 | 51 | @return Should return a transformation matrix that transforms each point in 52 | \a pts to the corresponding point in \a mpts or NULL on failure. 53 | */ 54 | typedef CvMat* (*ransac_xform_fn)( CvPoint2D64f* pts, CvPoint2D64f* mpts, 55 | int n ); 56 | 57 | 58 | /** 59 | Prototype for error functions passed to ransac_xform(). For a given 60 | point, its correspondence, and a transform, functions of this type should 61 | compute a measure of error between the correspondence and the point after 62 | the point has been transformed by the transform. 63 | 64 | @param pt a point 65 | @param mpt \a pt's correspondence 66 | @param T a transform 67 | 68 | @return Should return a measure of error between \a mpt and \a pt after 69 | \a pt has been transformed by the transform \a T. 70 | */ 71 | typedef double (*ransac_err_fn)( CvPoint2D64f pt, CvPoint2D64f mpt, CvMat* T ); 72 | 73 | 74 | /***************************** Function Prototypes ***************************/ 75 | 76 | 77 | /** 78 | Calculates a best-fit image transform from image feature correspondences 79 | using RANSAC. 80 | 81 | For more information refer to: 82 | 83 | Fischler, M. A. and Bolles, R. C. Random sample consensus: a paradigm for 84 | model fitting with applications to image analysis and automated cartography. 85 | Communications of the ACM, 24, 6 (1981), pp. 381--395. 86 | 87 | @param features an array of features; only features with a non-NULL match 88 | of type \a mtype are used in homography computation 89 | @param n number of features in \a feat 90 | @param mtype determines which of each feature's match fields to use 91 | for transform computation; should be one of FEATURE_FWD_MATCH, 92 | FEATURE_BCK_MATCH, or FEATURE_MDL_MATCH; if this is FEATURE_MDL_MATCH, 93 | correspondences are assumed to be between a feature's img_pt field 94 | and its match's mdl_pt field, otherwise correspondences are assumed to 95 | be between the the feature's img_pt field and its match's img_pt field 96 | @param xform_fn pointer to the function used to compute the desired 97 | transformation from feature correspondences 98 | @param m minimum number of correspondences necessary to instantiate the 99 | transform computed by \a xform_fn 100 | @param p_badxform desired probability that the final transformation 101 | returned by RANSAC is corrupted by outliers (i.e. the probability that 102 | no samples of all inliers were drawn) 103 | @param err_fn pointer to the function used to compute a measure of error 104 | between putative correspondences and for a given transform 105 | @param err_tol correspondences within this distance of each other are 106 | considered as inliers for a given transform 107 | @param inliers if not NULL, output as an array of pointers to the final 108 | set of inliers; memory for this array is allocated by this function and 109 | must be freed by the caller using free(*inliers) 110 | @param n_in if not NULL, output as the final number of inliers 111 | 112 | @return Returns a transformation matrix computed using RANSAC or NULL 113 | on error or if an acceptable transform could not be computed. 114 | */ 115 | extern CvMat* ransac_xform( struct feature* features, int n, int mtype, 116 | ransac_xform_fn xform_fn, int m, 117 | double p_badxform, ransac_err_fn err_fn, 118 | double err_tol, struct feature*** inliers, 119 | int* n_in ); 120 | 121 | 122 | /** 123 | Calculates a planar homography from point correspondeces using the direct 124 | linear transform. Intended for use as a ransac_xform_fn. 125 | 126 | @param pts array of points 127 | @param mpts array of corresponding points; each \a pts[\a i], \a i=0..\a 128 | n-1, corresponds to \a mpts[\a i] 129 | @param n number of points in both \a pts and \a mpts; must be at least 4 130 | 131 | @return Returns the \f$3 \times 3\f$ planar homography matrix that 132 | transforms points in \a pts to their corresponding points in \a mpts 133 | or NULL if fewer than 4 correspondences were provided 134 | */ 135 | extern CvMat* dlt_homog( CvPoint2D64f* pts, CvPoint2D64f* mpts, int n ); 136 | 137 | 138 | /** 139 | Calculates a least-squares planar homography from point correspondeces. 140 | Intended for use as a ransac_xform_fn. 141 | 142 | @param pts array of points 143 | @param mpts array of corresponding points; each \a pts[\a i], \a i=0..\a 144 | n-1, corresponds to \a mpts[\a i] 145 | @param n number of points in both \a pts and \a mpts; must be at least 4 146 | 147 | @return Returns the \f$3 \times 3\f$ least-squares planar homography 148 | matrix that transforms points in \a pts to their corresponding points 149 | in \a mpts or NULL if fewer than 4 correspondences were provided 150 | */ 151 | extern CvMat* lsq_homog( CvPoint2D64f* pts, CvPoint2D64f* mpts, int n ); 152 | 153 | 154 | /** 155 | Calculates the transfer error between a point and its correspondence for 156 | a given homography, i.e. for a point \f$x\f$, it's correspondence \f$x'\f$, 157 | and homography \f$H\f$, computes \f$d(x', Hx)^2\f$. Intended for use as 158 | a ransac_err_fn. 159 | 160 | @param pt a point 161 | @param mpt \a pt's correspondence 162 | @param H a homography matrix 163 | 164 | @return Returns the transfer error between \a pt and \a mpt given \a H 165 | */ 166 | extern double homog_xfer_err( CvPoint2D64f pt, CvPoint2D64f mpt, CvMat* H ); 167 | 168 | 169 | /** 170 | Performs a perspective transformation on a single point. That is, for a 171 | point \f$(x, y)\f$ and a \f$3 \times 3\f$ matrix \f$T\f$ this function 172 | returns the point \f$(u, v)\f$, where
173 | 174 | \f$[x' \ y' \ w']^T = T \times [x \ y \ 1]^T\f$,
175 | 176 | and
177 | 178 | \f$(u, v) = (x'/w', y'/w')\f$. 179 | 180 | Note that affine transforms are a subset of perspective transforms. 181 | 182 | @param pt a 2D point 183 | @param T a perspective transformation matrix 184 | 185 | @return Returns the point \f$(u, v)\f$ as above. 186 | */ 187 | extern CvPoint2D64f persp_xform_pt( CvPoint2D64f pt, CvMat* T ); 188 | 189 | 190 | #endif 191 | -------------------------------------------------------------------------------- /include/utils.h: -------------------------------------------------------------------------------- 1 | /**@file 2 | Miscellaneous utility functions. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #ifndef UTILS_H 10 | #define UTILS_H 11 | 12 | #include "cxcore.h" 13 | 14 | #include 15 | #include 16 | 17 | 18 | /* absolute value */ 19 | #ifndef ABS 20 | #define ABS(x) ( ( (x) < 0 )? -(x) : (x) ) 21 | #endif 22 | 23 | /***************************** Inline Functions ******************************/ 24 | 25 | 26 | /** 27 | A function to get a pixel value from an 8-bit unsigned image. 28 | 29 | @param img an image 30 | @param r row 31 | @param c column 32 | @return Returns the value of the pixel at (\a r, \a c) in \a img 33 | */ 34 | static inline int pixval8( IplImage* img, int r, int c ) 35 | { 36 | return (int)( ( (uchar*)(img->imageData + img->widthStep*r) )[c] ); 37 | } 38 | 39 | 40 | /** 41 | A function to set a pixel value in an 8-bit unsigned image. 42 | 43 | @param img an image 44 | @param r row 45 | @param c column 46 | @param val pixel value 47 | */ 48 | static inline void setpix8( IplImage* img, int r, int c, uchar val) 49 | { 50 | ( (uchar*)(img->imageData + img->widthStep*r) )[c] = val; 51 | } 52 | 53 | 54 | /** 55 | A function to get a pixel value from a 32-bit floating-point image. 56 | 57 | @param img an image 58 | @param r row 59 | @param c column 60 | @return Returns the value of the pixel at (\a r, \a c) in \a img 61 | */ 62 | static inline float pixval32f( IplImage* img, int r, int c ) 63 | { 64 | return ( (float*)(img->imageData + img->widthStep*r) )[c]; 65 | } 66 | 67 | 68 | /** 69 | A function to set a pixel value in a 32-bit floating-point image. 70 | 71 | @param img an image 72 | @param r row 73 | @param c column 74 | @param val pixel value 75 | */ 76 | static inline void setpix32f( IplImage* img, int r, int c, float val ) 77 | { 78 | ( (float*)(img->imageData + img->widthStep*r) )[c] = val; 79 | } 80 | 81 | 82 | /** 83 | A function to get a pixel value from a 64-bit floating-point image. 84 | 85 | @param img an image 86 | @param r row 87 | @param c column 88 | @return Returns the value of the pixel at (\a r, \a c) in \a img 89 | */ 90 | static inline double pixval64f( IplImage* img, int r, int c ) 91 | { 92 | return (double)( ( (double*)(img->imageData + img->widthStep*r) )[c] ); 93 | } 94 | 95 | 96 | /** 97 | A function to set a pixel value in a 64-bit floating-point image. 98 | 99 | @param img an image 100 | @param r row 101 | @param c column 102 | @param val pixel value 103 | */ 104 | static inline void setpix64f( IplImage* img, int r, int c, double val ) 105 | { 106 | ( (double*)(img->imageData + img->widthStep*r) )[c] = val; 107 | } 108 | 109 | 110 | /**************************** Function Prototypes ****************************/ 111 | 112 | 113 | /** 114 | Prints an error message and aborts the program. The error message is 115 | of the form "Error: ...", where the ... is specified by the \a format 116 | argument 117 | 118 | @param format an error message format string (as with \c printf(3)). 119 | */ 120 | extern void fatal_error( char* format, ... ); 121 | 122 | 123 | /** 124 | Replaces a file's extension, which is assumed to be everything after the 125 | last dot ('.') character. 126 | 127 | @param file the name of a file 128 | 129 | @param extn a new extension for \a file; should not include a dot (i.e. 130 | \c "jpg", not \c ".jpg") unless the new file extension should contain 131 | two dots. 132 | 133 | @return Returns a new string formed as described above. If \a file does 134 | not have an extension, this function simply adds one. 135 | */ 136 | extern char* replace_extension( const char* file, const char* extn ); 137 | 138 | 139 | /** 140 | Prepends a path to a filename. 141 | 142 | @param path a path 143 | @param file a file name 144 | 145 | @return Returns a new string containing a full path name consisting of 146 | \a path prepended to \a file. 147 | */ 148 | extern char* prepend_path( const char* path, const char* file ); 149 | 150 | 151 | /** 152 | A function that removes the path from a filename. Similar to the Unix 153 | basename command. 154 | 155 | @param pathname a (full) path name 156 | 157 | @return Returns the basename of \a pathname. 158 | */ 159 | extern char* basename( const char* pathname ); 160 | 161 | 162 | /** 163 | Displays progress in the console with a spinning pinwheel. Every time this 164 | function is called, the state of the pinwheel is incremented. The pinwheel 165 | has four states that loop indefinitely: '|', '/', '-', '\'. 166 | 167 | @param done if 0, this function simply increments the state of the pinwheel; 168 | otherwise it prints "done" 169 | */ 170 | extern void progress( int done ); 171 | 172 | 173 | /** 174 | Erases a specified number of characters from a stream. 175 | 176 | @param stream the stream from which to erase characters 177 | @param n the number of characters to erase 178 | */ 179 | extern void erase_from_stream( FILE* stream, int n ); 180 | 181 | 182 | /** 183 | Doubles the size of an array with error checking 184 | 185 | @param array pointer to an array whose size is to be doubled 186 | @param n number of elements allocated for \a array 187 | @param size size in bytes of elements in \a array 188 | 189 | @return Returns the new number of elements allocated for \a array. If no 190 | memory is available, returns 0 and frees \a array. 191 | */ 192 | extern int array_double( void** array, int n, int size ); 193 | 194 | 195 | /** 196 | Calculates the squared distance between two points. 197 | 198 | @param p1 a point 199 | @param p2 another point 200 | */ 201 | extern double dist_sq_2D( CvPoint2D64f p1, CvPoint2D64f p2 ); 202 | 203 | 204 | /** 205 | Draws an x on an image. 206 | 207 | @param img an image 208 | @param pt the center point of the x 209 | @param r the x's radius 210 | @param w the x's line weight 211 | @param color the color of the x 212 | */ 213 | extern void draw_x( IplImage* img, CvPoint pt, int r, int w, CvScalar color ); 214 | 215 | 216 | /** 217 | Combines two images by scacking one on top of the other 218 | 219 | @param img1 top image 220 | @param img2 bottom image 221 | 222 | @return Returns the image resulting from stacking \a img1 on top if \a img2 223 | */ 224 | extern IplImage* stack_imgs( IplImage* img1, IplImage* img2 ); 225 | 226 | 227 | /** 228 | Displays an image, making sure it fits on screen. cvWaitKey() must be 229 | called after this function so the event loop is entered and the 230 | image is displayed. 231 | 232 | @param img an image, possibly too large to display on-screen 233 | @param title the title of the window in which \a img is displayed 234 | */ 235 | extern void display_big_img( IplImage* img, char* title ); 236 | 237 | 238 | /** 239 | Allows user to view an array of images as a video. Keyboard controls 240 | are as follows: 241 | 242 |
    243 |
  • Space - start and pause playback
  • 244 |
  • Page Up - skip forward 10 frames
  • 245 |
  • Page Down - jump back 10 frames
  • 246 |
  • Right Arrow - skip forward 1 frame
  • 247 |
  • Left Arrow - jump back 1 frame
  • 248 |
  • Backspace - jump back to beginning
  • 249 |
  • Esc - exit playback
  • 250 |
  • Closing the window also exits playback
  • 251 |
252 | 253 | @param imgs an array of images 254 | @param n number of images in \a imgs 255 | @param win_name name of window in which images are displayed 256 | */ 257 | extern void vid_view( IplImage** imgs, int n, char* win_name ); 258 | 259 | 260 | /** 261 | Checks if a HighGUI window is still open or not 262 | 263 | @param name the name of the window we're checking 264 | 265 | @return Returns 1 if the window named \a name has been closed or 0 otherwise 266 | */ 267 | extern int win_closed( char* name ); 268 | 269 | #endif 270 | -------------------------------------------------------------------------------- /src/siftfeat.c: -------------------------------------------------------------------------------- 1 | /* 2 | This program detects image features using SIFT keypoints. For more info, 3 | refer to: 4 | 5 | Lowe, D. Distinctive image features from scale-invariant keypoints. 6 | International Journal of Computer Vision, 60, 2 (2004), pp.91--110. 7 | 8 | Copyright (C) 2006-2012 Rob Hess 9 | 10 | Note: The SIFT algorithm is patented in the United States and cannot be 11 | used in commercial products without a license from the University of 12 | British Columbia. For more information, refer to the file LICENSE.ubc 13 | that accompanied this distribution. 14 | 15 | Version: 1.1.2-20100521 16 | */ 17 | 18 | #include "sift.h" 19 | #include "imgfeatures.h" 20 | #include "utils.h" 21 | 22 | #include 23 | 24 | #include 25 | 26 | #define OPTIONS ":o:m:i:s:c:r:n:b:dxh" 27 | 28 | /*************************** Function Prototypes *****************************/ 29 | 30 | static void usage( char* ); 31 | static void arg_parse( int, char** ); 32 | 33 | /******************************** Globals ************************************/ 34 | 35 | char* pname; 36 | char* img_file_name; 37 | char* out_file_name = NULL; 38 | char* out_img_name = NULL; 39 | int intvls = SIFT_INTVLS; 40 | double sigma = SIFT_SIGMA; 41 | double contr_thr = SIFT_CONTR_THR; 42 | int curv_thr = SIFT_CURV_THR; 43 | int img_dbl = SIFT_IMG_DBL; 44 | int descr_width = SIFT_DESCR_WIDTH; 45 | int descr_hist_bins = SIFT_DESCR_HIST_BINS; 46 | int display = 1; 47 | 48 | 49 | /********************************** Main *************************************/ 50 | 51 | int main( int argc, char** argv ) 52 | { 53 | IplImage* img; 54 | struct feature* features; 55 | int n = 0; 56 | 57 | arg_parse( argc, argv ); 58 | 59 | fprintf( stderr, "Finding SIFT features...\n" ); 60 | img = cvLoadImage( img_file_name, 1 ); 61 | if( ! img ) 62 | fatal_error( "unable to load image from %s", img_file_name ); 63 | n = _sift_features( img, &features, intvls, sigma, contr_thr, curv_thr, 64 | img_dbl, descr_width, descr_hist_bins ); 65 | fprintf( stderr, "Found %d features.\n", n ); 66 | 67 | if( display ) 68 | { 69 | draw_features( img, features, n ); 70 | display_big_img( img, img_file_name ); 71 | cvWaitKey( 0 ); 72 | } 73 | 74 | if( out_file_name != NULL ) 75 | export_features( out_file_name, features, n ); 76 | 77 | if( out_img_name != NULL ) 78 | cvSaveImage( out_img_name, img, NULL ); 79 | return 0; 80 | } 81 | 82 | 83 | /************************** Function Definitions *****************************/ 84 | 85 | // print usage for this program 86 | static void usage( char* name ) 87 | { 88 | fprintf(stderr, "%s: detect SIFT keypoints in an image\n\n", name); 89 | fprintf(stderr, "Usage: %s [options] \n", name); 90 | fprintf(stderr, "Options:\n"); 91 | fprintf(stderr, " -h Display this message and exit\n"); 92 | fprintf(stderr, " -o Output keypoints to text file\n"); 93 | fprintf(stderr, " -m Output keypoint image file (format" \ 94 | " determined by extension)\n"); 95 | fprintf(stderr, " -i Set number of sampled intervals per" \ 96 | " octave in scale space\n"); 97 | fprintf(stderr, " pyramid (default %d)\n", 98 | SIFT_INTVLS); 99 | fprintf(stderr, " -s Set sigma for initial gaussian" \ 100 | " smoothing at each octave\n"); 101 | fprintf(stderr, " (default %06.4f)\n", SIFT_SIGMA); 102 | fprintf(stderr, " -c Set threshold on keypoint contrast" \ 103 | " |D(x)| based on [0,1]\n"); 104 | fprintf(stderr, " pixel values (default %06.4f)\n", 105 | SIFT_CONTR_THR); 106 | fprintf(stderr, " -r Set threshold on keypoint ratio of" \ 107 | " principle curvatures\n"); 108 | fprintf(stderr, " (default %d)\n", SIFT_CURV_THR); 109 | fprintf(stderr, " -n Set width of descriptor histogram" \ 110 | " array (default %d)\n", SIFT_DESCR_WIDTH); 111 | fprintf(stderr, " -b Set number of bins per histogram" \ 112 | " in descriptor array\n"); 113 | fprintf(stderr, " (default %d)\n", SIFT_DESCR_HIST_BINS); 114 | fprintf(stderr, " -d Toggle image doubling (default %s)\n", 115 | SIFT_IMG_DBL == 0 ? "off" : "on"); 116 | fprintf(stderr, " -x Turn off keypoint display\n"); 117 | } 118 | 119 | 120 | 121 | /* 122 | arg_parse() parses the command line arguments, setting appropriate globals. 123 | 124 | argc and argv should be passed directly from the command line 125 | */ 126 | static void arg_parse( int argc, char** argv ) 127 | { 128 | //extract program name from command line (remove path, if present) 129 | pname = basename( argv[0] ); 130 | 131 | //parse commandline options 132 | while( 1 ) 133 | { 134 | char* arg_check; 135 | int arg = getopt( argc, argv, OPTIONS ); 136 | if( arg == -1 ) 137 | break; 138 | 139 | switch( arg ) 140 | { 141 | // catch unsupplied required arguments and exit 142 | case ':': 143 | fatal_error( "-%c option requires an argument\n" \ 144 | "Try '%s -h' for help.", optopt, pname ); 145 | break; 146 | 147 | // read out_file_name 148 | case 'o': 149 | if( ! optarg ) 150 | fatal_error( "error parsing arguments at -%c\n" \ 151 | "Try '%s -h' for help.", arg, pname ); 152 | out_file_name = optarg; 153 | break; 154 | 155 | // read out_img_name 156 | case 'm': 157 | if( ! optarg ) 158 | fatal_error( "error parsing arguments at -%c\n" \ 159 | "Try '%s -h' for help.", arg, pname ); 160 | out_img_name = optarg; 161 | break; 162 | 163 | // read intervals 164 | case 'i': 165 | // ensure argument provided 166 | if( ! optarg ) 167 | fatal_error( "error parsing arguments at -%c\n" \ 168 | "Try '%s -h' for help.", arg, pname ); 169 | 170 | // parse argument and ensure it is an integer 171 | intvls = strtol( optarg, &arg_check, 10 ); 172 | if( arg_check == optarg || *arg_check != '\0' ) 173 | fatal_error( "-%c option requires an integer argument\n" \ 174 | "Try '%s -h' for help.", arg, pname ); 175 | break; 176 | 177 | // read sigma 178 | case 's' : 179 | // ensure argument provided 180 | if( ! optarg ) 181 | fatal_error( "error parsing arguments at -%c\n" \ 182 | "Try '%s -h' for help.", arg, pname ); 183 | 184 | // parse argument and ensure it is a floating point number 185 | sigma = strtod( optarg, &arg_check ); 186 | if( arg_check == optarg || *arg_check != '\0' ) 187 | fatal_error( "-%c option requires a floating point argument\n" \ 188 | "Try '%s -h' for help.", arg, pname ); 189 | break; 190 | 191 | // read contrast_thresh 192 | case 'c' : 193 | // ensure argument provided 194 | if( ! optarg ) 195 | fatal_error( "error parsing arguments at -%c\n" \ 196 | "Try '%s -h' for help.", arg, pname ); 197 | 198 | // parse argument and ensure it is a floating point number 199 | contr_thr = strtod( optarg, &arg_check ); 200 | if( arg_check == optarg || *arg_check != '\0' ) 201 | fatal_error( "-%c option requires a floating point argument\n" \ 202 | "Try '%s -h' for help.", arg, pname ); 203 | break; 204 | 205 | // read curvature_thresh 206 | case 'r' : 207 | // ensure argument provided 208 | if( ! optarg ) 209 | fatal_error( "error parsing arguments at -%c\n" \ 210 | "Try '%s -h' for help.", arg, pname ); 211 | 212 | // parse argument and ensure it is a floating point number 213 | curv_thr = strtol( optarg, &arg_check, 10 ); 214 | if( arg_check == optarg || *arg_check != '\0' ) 215 | fatal_error( "-%c option requires an integer argument\n" \ 216 | "Try '%s -h' for help.", arg, pname ); 217 | break; 218 | 219 | // read descr_width 220 | case 'n' : 221 | // ensure argument provided 222 | if( ! optarg ) 223 | fatal_error( "error parsing arguments at -%c\n" \ 224 | "Try '%s -h' for help.", arg, pname ); 225 | 226 | // parse argument and ensure it is a floating point number 227 | descr_width = strtol( optarg, &arg_check, 10 ); 228 | if( arg_check == optarg || *arg_check != '\0' ) 229 | fatal_error( "-%c option requires an integer argument\n" \ 230 | "Try '%s -h' for help.", arg, pname ); 231 | break; 232 | 233 | // read descr_histo_bins 234 | case 'b' : 235 | // ensure argument provided 236 | if( ! optarg ) 237 | fatal_error( "error parsing arguments at -%c\n" \ 238 | "Try '%s -h' for help.", arg, pname ); 239 | 240 | // parse argument and ensure it is a floating point number 241 | descr_hist_bins = strtol( optarg, &arg_check, 10 ); 242 | if( arg_check == optarg || *arg_check != '\0' ) 243 | fatal_error( "-%c option requires an integer argument\n" \ 244 | "Try '%s -h' for help.", arg, pname ); 245 | break; 246 | 247 | // read double_image 248 | case 'd' : 249 | img_dbl = ( img_dbl == 1 )? 0 : 1; 250 | break; 251 | 252 | // read display 253 | case 'x' : 254 | display = 0; 255 | break; 256 | 257 | // user asked for help 258 | case 'h': 259 | usage( pname ); 260 | exit(0); 261 | break; 262 | 263 | // catch invalid arguments 264 | default: 265 | fatal_error( "-%c: invalid option.\nTry '%s -h' for help.", 266 | optopt, pname ); 267 | } 268 | } 269 | 270 | // make sure an input file is specified 271 | if( argc - optind < 1 ) 272 | fatal_error( "no input file specified.\nTry '%s -h' for help.", pname ); 273 | 274 | // make sure there aren't too many arguments 275 | if( argc - optind > 1 ) 276 | fatal_error( "too many arguments.\nTry '%s -h' for help.", pname ); 277 | 278 | // copy image file name from command line argument 279 | img_file_name = argv[optind]; 280 | } 281 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | Miscellaneous utility functions. 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #include "utils.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | /*************************** Function Definitions ****************************/ 24 | 25 | 26 | /* 27 | Prints an error message and aborts the program. The error message is 28 | of the form "Error: ...", where the ... is specified by the \a format 29 | argument 30 | 31 | @param format an error message format string (as with \c printf(3)). 32 | */ 33 | void fatal_error(char* format, ...) 34 | { 35 | va_list ap; 36 | 37 | fprintf( stderr, "Error: "); 38 | 39 | va_start( ap, format ); 40 | vfprintf( stderr, format, ap ); 41 | va_end( ap ); 42 | fprintf( stderr, "\n" ); 43 | abort(); 44 | } 45 | 46 | 47 | 48 | /* 49 | Replaces a file's extension, which is assumed to be everything after the 50 | last dot ('.') character. 51 | 52 | @param file the name of a file 53 | 54 | @param extn a new extension for \a file; should not include a dot (i.e. 55 | \c "jpg", not \c ".jpg") unless the new file extension should contain 56 | two dots. 57 | 58 | @return Returns a new string formed as described above. If \a file does 59 | not have an extension, this function simply adds one. 60 | */ 61 | char* replace_extension( const char* file, const char* extn ) 62 | { 63 | char* new_file, * lastdot; 64 | 65 | new_file = calloc( strlen( file ) + strlen( extn ) + 2, sizeof( char ) ); 66 | strcpy( new_file, file ); 67 | lastdot = strrchr( new_file, '.' ); 68 | if( lastdot ) 69 | *(lastdot + 1) = '\0'; 70 | else 71 | strcat( new_file, "." ); 72 | strcat( new_file, extn ); 73 | 74 | return new_file; 75 | } 76 | 77 | 78 | 79 | /* 80 | Prepends a path to a filename. 81 | 82 | @param path a path 83 | @param file a file name 84 | 85 | @return Returns a new string containing a full path name consisting of 86 | \a path prepended to \a file. 87 | */ 88 | char* prepend_path( const char* path, const char* file ) 89 | { 90 | int n = strlen(path) + strlen(file) + 2; 91 | char* pathname = calloc( n, sizeof(char) ); 92 | 93 | snprintf( pathname, n, "%s/%s", path, file ); 94 | 95 | return pathname; 96 | } 97 | 98 | 99 | 100 | /* 101 | A function that removes the path from a filename. Similar to the Unix 102 | basename command. 103 | 104 | @param pathname a (full) path name 105 | 106 | @return Returns the basename of \a pathname. 107 | */ 108 | char* basename( const char* pathname ) 109 | { 110 | char* base, * last_slash; 111 | 112 | last_slash = strrchr( pathname, '/' ); 113 | if( ! last_slash ) 114 | { 115 | base = calloc( strlen( pathname ) + 1, sizeof( char ) ); 116 | strcpy( base, pathname ); 117 | } 118 | else 119 | { 120 | base = calloc( strlen( last_slash++ ), sizeof( char ) ); 121 | strcpy( base, last_slash ); 122 | } 123 | 124 | return base; 125 | } 126 | 127 | 128 | 129 | /* 130 | Displays progress in the console with a spinning pinwheel. Every time this 131 | function is called, the state of the pinwheel is incremented. The pinwheel 132 | has four states that loop indefinitely: '|', '/', '-', '\'. 133 | 134 | @param done if 0, this function simply increments the state of the pinwheel; 135 | otherwise it prints "done" 136 | */ 137 | void progress( int done ) 138 | { 139 | char state[4] = { '|', '/', '-', '\\' }; 140 | static int cur = -1; 141 | 142 | if( cur == -1 ) 143 | fprintf( stderr, " " ); 144 | 145 | if( done ) 146 | { 147 | fprintf( stderr, "\b\bdone\n"); 148 | cur = -1; 149 | } 150 | else 151 | { 152 | cur = ( cur + 1 ) % 4; 153 | fprintf( stdout, "\b\b%c ", state[cur] ); 154 | fflush(stderr); 155 | } 156 | } 157 | 158 | 159 | 160 | /* 161 | Erases a specified number of characters from a stream. 162 | 163 | @param stream the stream from which to erase characters 164 | @param n the number of characters to erase 165 | */ 166 | void erase_from_stream( FILE* stream, int n ) 167 | { 168 | int j; 169 | for( j = 0; j < n; j++ ) 170 | fprintf( stream, "\b" ); 171 | for( j = 0; j < n; j++ ) 172 | fprintf( stream, " " ); 173 | for( j = 0; j < n; j++ ) 174 | fprintf( stream, "\b" ); 175 | } 176 | 177 | 178 | 179 | /* 180 | Doubles the size of an array with error checking 181 | 182 | @param array pointer to an array whose size is to be doubled 183 | @param n number of elements allocated for \a array 184 | @param size size in bytes of elements in \a array 185 | 186 | @return Returns the new number of elements allocated for \a array. If no 187 | memory is available, returns 0. 188 | */ 189 | int array_double( void** array, int n, int size ) 190 | { 191 | void* tmp; 192 | 193 | tmp = realloc( *array, 2 * n * size ); 194 | if( ! tmp ) 195 | { 196 | fprintf( stderr, "Warning: unable to allocate memory in array_double()," 197 | " %s line %d\n", __FILE__, __LINE__ ); 198 | if( *array ) 199 | free( *array ); 200 | *array = NULL; 201 | return 0; 202 | } 203 | *array = tmp; 204 | return n*2; 205 | } 206 | 207 | 208 | 209 | /* 210 | Calculates the squared distance between two points. 211 | 212 | @param p1 a point 213 | @param p2 another point 214 | */ 215 | double dist_sq_2D( CvPoint2D64f p1, CvPoint2D64f p2 ) 216 | { 217 | double x_diff = p1.x - p2.x; 218 | double y_diff = p1.y - p2.y; 219 | 220 | return x_diff * x_diff + y_diff * y_diff; 221 | } 222 | 223 | 224 | 225 | /* 226 | Draws an x on an image. 227 | 228 | @param img an image 229 | @param pt the center point of the x 230 | @param r the x's radius 231 | @param w the x's line weight 232 | @param color the color of the x 233 | */ 234 | void draw_x( IplImage* img, CvPoint pt, int r, int w, CvScalar color ) 235 | { 236 | cvLine( img, pt, cvPoint( pt.x + r, pt.y + r), color, w, 8, 0 ); 237 | cvLine( img, pt, cvPoint( pt.x - r, pt.y + r), color, w, 8, 0 ); 238 | cvLine( img, pt, cvPoint( pt.x + r, pt.y - r), color, w, 8, 0 ); 239 | cvLine( img, pt, cvPoint( pt.x - r, pt.y - r), color, w, 8, 0 ); 240 | } 241 | 242 | 243 | 244 | /* 245 | Combines two images by scacking one on top of the other 246 | 247 | @param img1 top image 248 | @param img2 bottom image 249 | 250 | @return Returns the image resulting from stacking \a img1 on top if \a img2 251 | */ 252 | extern IplImage* stack_imgs( IplImage* img1, IplImage* img2 ) 253 | { 254 | IplImage* stacked = cvCreateImage( cvSize( MAX(img1->width, img2->width), 255 | img1->height + img2->height ), 256 | IPL_DEPTH_8U, 3 ); 257 | 258 | cvZero( stacked ); 259 | cvSetImageROI( stacked, cvRect( 0, 0, img1->width, img1->height ) ); 260 | cvAdd( img1, stacked, stacked, NULL ); 261 | cvSetImageROI( stacked, cvRect(0, img1->height, img2->width, img2->height) ); 262 | cvAdd( img2, stacked, stacked, NULL ); 263 | cvResetImageROI( stacked ); 264 | 265 | return stacked; 266 | } 267 | 268 | 269 | 270 | /* 271 | Displays an image, making sure it fits on screen. cvWaitKey() must be 272 | called after this function so the event loop is entered and the 273 | image is displayed. 274 | 275 | @param img an image, possibly too large to display on-screen 276 | @param title the title of the window in which \a img is displayed 277 | */ 278 | void display_big_img( IplImage* img, char* title ) 279 | { 280 | IplImage* small; 281 | GdkScreen* scr; 282 | int scr_width, scr_height; 283 | double img_aspect, scr_aspect, scale; 284 | 285 | /* determine screen size to see if image fits on screen */ 286 | gdk_init( NULL, NULL ); 287 | scr = gdk_screen_get_default(); 288 | scr_width = gdk_screen_get_width( scr ); 289 | scr_height = gdk_screen_get_height( scr ); 290 | 291 | if( img->width >= 0.90 * scr_width || img->height >= 0.90 * scr_height ) 292 | { 293 | img_aspect = (double)(img->width) / img->height; 294 | scr_aspect = (double)(scr_width) / scr_height; 295 | 296 | if( img_aspect > scr_aspect ) 297 | scale = 0.90 * scr_width / img->width; 298 | else 299 | scale = 0.90 * scr_height / img->height; 300 | 301 | small = cvCreateImage( cvSize( img->width * scale, img->height * scale ), 302 | img->depth, img->nChannels ); 303 | cvResize( img, small, CV_INTER_AREA ); 304 | } 305 | else 306 | small = cvCloneImage( img ); 307 | 308 | cvNamedWindow( title, 1 ); 309 | cvShowImage( title, small ); 310 | cvReleaseImage( &small ); 311 | } 312 | 313 | 314 | 315 | /* 316 | Allows user to view an array of images as a video. Keyboard controls 317 | are as follows: 318 | 319 |
    320 |
  • Space - start and pause playback
  • 321 |
  • Page Down - skip forward 10 frames
  • 322 |
  • Page Up - jump back 10 frames
  • 323 |
  • Right Arrow - skip forward 1 frame
  • 324 |
  • Left Arrow - jump back 1 frame
  • 325 |
  • Backspace - jump back to beginning
  • 326 |
  • Esc - exit playback
  • 327 |
  • Closing the window also exits playback
  • 328 |
329 | 330 | @param imgs an array of images 331 | @param n number of images in \a imgs 332 | @param win_name name of window in which images are displayed 333 | */ 334 | void vid_view( IplImage** imgs, int n, char* win_name ) 335 | { 336 | int k, i = 0, playing = 0; 337 | 338 | display_big_img( imgs[i], win_name ); 339 | while( ! win_closed( win_name ) ) 340 | { 341 | /* if already playing, advance frame and check for pause */ 342 | if( playing ) 343 | { 344 | i = MIN( i + 1, n - 1 ); 345 | display_big_img( imgs[i], win_name ); 346 | k = cvWaitKey( 33 ); 347 | if( k == ' ' || i == n - 1 ) 348 | playing = 0; 349 | } 350 | 351 | else 352 | { 353 | k = cvWaitKey( 0 ); 354 | switch( k ) 355 | { 356 | /* space */ 357 | case ' ': 358 | playing = 1; 359 | break; 360 | 361 | /* esc */ 362 | case 27: 363 | case 1048603: 364 | cvDestroyWindow( win_name ); 365 | break; 366 | 367 | /* backspace */ 368 | case '\b': 369 | i = 0; 370 | display_big_img( imgs[i], win_name ); 371 | break; 372 | 373 | /* left arrow */ 374 | case 65288: 375 | case 1113937: 376 | i = MAX( i - 1, 0 ); 377 | display_big_img( imgs[i], win_name ); 378 | break; 379 | 380 | /* right arrow */ 381 | case 65363: 382 | case 1113939: 383 | i = MIN( i + 1, n - 1 ); 384 | display_big_img( imgs[i], win_name ); 385 | break; 386 | 387 | /* page up */ 388 | case 65365: 389 | case 1113941: 390 | i = MAX( i - 10, 0 ); 391 | display_big_img( imgs[i], win_name ); 392 | break; 393 | 394 | /* page down */ 395 | case 65366: 396 | case 1113942: 397 | i = MIN( i + 10, n - 1 ); 398 | display_big_img( imgs[i], win_name ); 399 | break; 400 | } 401 | } 402 | } 403 | } 404 | 405 | 406 | 407 | /* 408 | Checks if a HighGUI window is still open or not 409 | 410 | @param name the name of the window we're checking 411 | 412 | @return Returns 1 if the window named \a name has been closed or 0 otherwise 413 | */ 414 | int win_closed( char* win_name ) 415 | { 416 | if( ! cvGetWindowHandle(win_name) ) 417 | return 1; 418 | return 0; 419 | } 420 | -------------------------------------------------------------------------------- /src/imgfeatures.c: -------------------------------------------------------------------------------- 1 | /* 2 | Functions and structures for dealing with image features 3 | 4 | Copyright (C) 2006-2012 Rob Hess 5 | 6 | @version 1.1.2-20100521 7 | */ 8 | 9 | #include "utils.h" 10 | #include "imgfeatures.h" 11 | 12 | #include 13 | 14 | static int import_oxfd_features( char*, struct feature** ); 15 | static int export_oxfd_features( char*, struct feature*, int ); 16 | static void draw_oxfd_features( IplImage*, struct feature*, int ); 17 | static void draw_oxfd_feature( IplImage*, struct feature*, CvScalar ); 18 | 19 | static int import_lowe_features( char*, struct feature** ); 20 | static int export_lowe_features( char*, struct feature*, int ); 21 | static void draw_lowe_features( IplImage*, struct feature*, int ); 22 | static void draw_lowe_feature( IplImage*, struct feature*, CvScalar ); 23 | 24 | 25 | /* 26 | Reads image features from file. The file should be formatted as from 27 | the code provided by the Visual Geometry Group at Oxford: 28 | 29 | 30 | @param filename location of a file containing image features 31 | @param type determines how features are input. If \a type is FEATURE_OXFD, 32 | the input file is treated as if it is from the code provided by the VGG 33 | at Oxford: 34 | 35 | http://www.robots.ox.ac.uk:5000/~vgg/research/affine/index.html 36 | 37 | If \a type is FEATURE_LOWE, the input file is treated as if it is from 38 | David Lowe's SIFT code: 39 | 40 | http://www.cs.ubc.ca/~lowe/keypoints 41 | @param features pointer to an array in which to store features 42 | 43 | @return Returns the number of features imported from filename or -1 on error 44 | */ 45 | int import_features( char* filename, int type, struct feature** feat ) 46 | { 47 | int n; 48 | 49 | switch( type ) 50 | { 51 | case FEATURE_OXFD: 52 | n = import_oxfd_features( filename, feat ); 53 | break; 54 | case FEATURE_LOWE: 55 | n = import_lowe_features( filename, feat ); 56 | break; 57 | default: 58 | fprintf( stderr, "Warning: import_features(): unrecognized feature" \ 59 | "type, %s, line %d\n", __FILE__, __LINE__ ); 60 | return -1; 61 | } 62 | 63 | if( n == -1 ) 64 | fprintf( stderr, "Warning: unable to import features from %s," \ 65 | " %s, line %d\n", filename, __FILE__, __LINE__ ); 66 | return n; 67 | } 68 | 69 | 70 | 71 | /* 72 | Exports a feature set to a file formatted depending on the type of 73 | features, as specified in the feature struct's type field. 74 | 75 | @param filename name of file to which to export features 76 | @param feat feature array 77 | @param n number of features 78 | 79 | @return Returns 0 on success or 1 on error 80 | */ 81 | int export_features( char* filename, struct feature* feat, int n ) 82 | { 83 | int r, type; 84 | 85 | if( n <= 0 || ! feat ) 86 | { 87 | fprintf( stderr, "Warning: no features to export, %s line %d\n", 88 | __FILE__, __LINE__ ); 89 | return 1; 90 | } 91 | type = feat[0].type; 92 | switch( type ) 93 | { 94 | case FEATURE_OXFD: 95 | r = export_oxfd_features( filename, feat, n ); 96 | break; 97 | case FEATURE_LOWE: 98 | r = export_lowe_features( filename, feat, n ); 99 | break; 100 | default: 101 | fprintf( stderr, "Warning: export_features(): unrecognized feature" \ 102 | "type, %s, line %d\n", __FILE__, __LINE__ ); 103 | return -1; 104 | } 105 | 106 | if( r ) 107 | fprintf( stderr, "Warning: unable to export features to %s," \ 108 | " %s, line %d\n", filename, __FILE__, __LINE__ ); 109 | return r; 110 | } 111 | 112 | 113 | /* 114 | Draws a set of features on an image 115 | 116 | @param img image on which to draw features 117 | @param feat array of Oxford-type features 118 | @param n number of features 119 | */ 120 | void draw_features( IplImage* img, struct feature* feat, int n ) 121 | { 122 | int type; 123 | 124 | if( n <= 0 || ! feat ) 125 | { 126 | fprintf( stderr, "Warning: no features to draw, %s line %d\n", 127 | __FILE__, __LINE__ ); 128 | return; 129 | } 130 | type = feat[0].type; 131 | switch( type ) 132 | { 133 | case FEATURE_OXFD: 134 | draw_oxfd_features( img, feat, n ); 135 | break; 136 | case FEATURE_LOWE: 137 | draw_lowe_features( img, feat, n ); 138 | break; 139 | default: 140 | fprintf( stderr, "Warning: draw_features(): unrecognized feature" \ 141 | " type, %s, line %d\n", __FILE__, __LINE__ ); 142 | break; 143 | } 144 | } 145 | 146 | 147 | 148 | /* 149 | Calculates the squared Euclidian distance between two feature descriptors. 150 | 151 | @param f1 first feature 152 | @param f2 second feature 153 | 154 | @return Returns the squared Euclidian distance between the descriptors of 155 | f1 and f2. 156 | */ 157 | double descr_dist_sq( struct feature* f1, struct feature* f2 ) 158 | { 159 | double diff, dsq = 0; 160 | double* descr1, * descr2; 161 | int i, d; 162 | 163 | d = f1->d; 164 | if( f2->d != d ) 165 | return DBL_MAX; 166 | descr1 = f1->descr; 167 | descr2 = f2->descr; 168 | 169 | for( i = 0; i < d; i++ ) 170 | { 171 | diff = descr1[i] - descr2[i]; 172 | dsq += diff*diff; 173 | } 174 | return dsq; 175 | } 176 | 177 | 178 | 179 | /***************************** Local Functions *******************************/ 180 | 181 | 182 | /* 183 | Reads image features from file. The file should be formatted as from 184 | the code provided by the Visual Geometry Group at Oxford: 185 | 186 | http://www.robots.ox.ac.uk:5000/~vgg/research/affine/index.html 187 | 188 | @param filename location of a file containing image features 189 | @param features pointer to an array in which to store features 190 | 191 | @return Returns the number of features imported from filename or -1 on error 192 | */ 193 | static int import_oxfd_features( char* filename, struct feature** features ) 194 | { 195 | struct feature* f; 196 | int i, j, n, d; 197 | double x, y, a, b, c, dv; 198 | FILE* file; 199 | 200 | if( ! features ) 201 | fatal_error( "NULL pointer error, %s, line %d", __FILE__, __LINE__ ); 202 | if( ! ( file = fopen( filename, "r" ) ) ) 203 | { 204 | fprintf( stderr, "Warning: error opening %s, %s, line %d\n", 205 | filename, __FILE__, __LINE__ ); 206 | return -1; 207 | } 208 | 209 | /* read dimension and number of features */ 210 | if( fscanf( file, " %d %d ", &d, &n ) != 2 ) 211 | { 212 | fprintf( stderr, "Warning: file read error, %s, line %d\n", 213 | __FILE__, __LINE__ ); 214 | return -1; 215 | } 216 | if( d > FEATURE_MAX_D ) 217 | { 218 | fprintf( stderr, "Warning: descriptor too long, %s, line %d\n", 219 | __FILE__, __LINE__ ); 220 | return -1; 221 | } 222 | 223 | 224 | f = calloc( n, sizeof(struct feature) ); 225 | for( i = 0; i < n; i++ ) 226 | { 227 | /* read affine region parameters */ 228 | if( fscanf( file, " %lf %lf %lf %lf %lf ", &x, &y, &a, &b, &c ) != 5 ) 229 | { 230 | fprintf( stderr, "Warning: error reading feature #%d, %s, line %d\n", 231 | i+1, __FILE__, __LINE__ ); 232 | free( f ); 233 | return -1; 234 | } 235 | f[i].img_pt.x = f[i].x = x; 236 | f[i].img_pt.y = f[i].y = y; 237 | f[i].a = a; 238 | f[i].b = b; 239 | f[i].c = c; 240 | f[i].d = d; 241 | f[i].type = FEATURE_OXFD; 242 | 243 | /* read descriptor */ 244 | for( j = 0; j < d; j++ ) 245 | { 246 | if( ! fscanf( file, " %lf ", &dv ) ) 247 | { 248 | fprintf( stderr, "Warning: error reading feature descriptor" \ 249 | " #%d, %s, line %d\n", i+1, __FILE__, __LINE__ ); 250 | free( f ); 251 | return -1; 252 | } 253 | f[i].descr[j] = dv; 254 | } 255 | 256 | f[i].scl = f[i].ori = 0; 257 | f[i].category = 0; 258 | f[i].fwd_match = f[i].bck_match = f[i].mdl_match = NULL; 259 | f[i].mdl_pt.x = f[i].mdl_pt.y = -1; 260 | f[i].feature_data = NULL; 261 | } 262 | 263 | if( fclose(file) ) 264 | { 265 | fprintf( stderr, "Warning: file close error, %s, line %d\n", 266 | __FILE__, __LINE__ ); 267 | free( f ); 268 | return -1; 269 | } 270 | 271 | *features = f; 272 | return n; 273 | } 274 | 275 | 276 | 277 | 278 | /* 279 | Exports a feature set to a file formatted as one from the code provided 280 | by the Visual Geometry Group at Oxford: 281 | 282 | http://www.robots.ox.ac.uk:5000/~vgg/research/affine/index.html 283 | 284 | @param filename name of file to which to export features 285 | @param feat feature array 286 | @param n number of features 287 | 288 | @return Returns 0 on success or 1 on error 289 | */ 290 | static int export_oxfd_features( char* filename, struct feature* feat, int n ) 291 | { 292 | FILE* file; 293 | int i, j, d; 294 | 295 | if( n <= 0 ) 296 | { 297 | fprintf( stderr, "Warning: feature count %d, %s, line %d\n", 298 | n, __FILE__, __LINE__ ); 299 | return 1; 300 | } 301 | if( ! ( file = fopen( filename, "w" ) ) ) 302 | { 303 | fprintf( stderr, "Warning: error opening %s, %s, line %d\n", 304 | filename, __FILE__, __LINE__ ); 305 | return 1; 306 | } 307 | 308 | d = feat[0].d; 309 | fprintf( file, "%d\n%d\n", d, n ); 310 | for( i = 0; i < n; i++ ) 311 | { 312 | fprintf( file, "%f %f %f %f %f", feat[i].x, feat[i].y, feat[i].a, 313 | feat[i].b, feat[i].c ); 314 | for( j = 0; j < d; j++ ) 315 | fprintf( file, " %f", feat[i].descr[j] ); 316 | fprintf( file, "\n" ); 317 | } 318 | 319 | if( fclose(file) ) 320 | { 321 | fprintf( stderr, "Warning: file close error, %s, line %d\n", 322 | __FILE__, __LINE__ ); 323 | return 1; 324 | } 325 | return 0; 326 | } 327 | 328 | 329 | 330 | /* 331 | Draws Oxford-type affine features 332 | 333 | @param img image on which to draw features 334 | @param feat array of Oxford-type features 335 | @param n number of features 336 | */ 337 | static void draw_oxfd_features( IplImage* img, struct feature* feat, int n ) 338 | { 339 | CvScalar color = CV_RGB( 255, 255, 255 ); 340 | int i; 341 | 342 | if( img-> nChannels > 1 ) 343 | color = FEATURE_OXFD_COLOR; 344 | for( i = 0; i < n; i++ ) 345 | draw_oxfd_feature( img, feat + i, color ); 346 | } 347 | 348 | 349 | 350 | /* 351 | Draws a single Oxford-type feature 352 | 353 | @param img image on which to draw 354 | @param feat feature to be drawn 355 | @param color color in which to draw 356 | */ 357 | static void draw_oxfd_feature( IplImage* img, struct feature* feat, 358 | CvScalar color ) 359 | { 360 | double m[4] = { feat->a, feat->b, feat->b, feat->c }; 361 | double v[4] = { 0 }; 362 | double e[2] = { 0 }; 363 | CvMat M, V, E; 364 | double alpha, l1, l2; 365 | 366 | /* compute axes and orientation of ellipse surrounding affine region */ 367 | cvInitMatHeader( &M, 2, 2, CV_64FC1, m, CV_AUTOSTEP ); 368 | cvInitMatHeader( &V, 2, 2, CV_64FC1, v, CV_AUTOSTEP ); 369 | cvInitMatHeader( &E, 2, 1, CV_64FC1, e, CV_AUTOSTEP ); 370 | cvEigenVV( &M, &V, &E, DBL_EPSILON, 0, 0 ); 371 | l1 = 1 / sqrt( e[1] ); 372 | l2 = 1 / sqrt( e[0] ); 373 | alpha = -atan2( v[1], v[0] ); 374 | alpha *= 180 / M_PI; 375 | 376 | cvEllipse( img, cvPoint( feat->x, feat->y ), cvSize( l2, l1 ), alpha, 377 | 0, 360, CV_RGB(0,0,0), 3, 8, 0 ); 378 | cvEllipse( img, cvPoint( feat->x, feat->y ), cvSize( l2, l1 ), alpha, 379 | 0, 360, color, 1, 8, 0 ); 380 | cvLine( img, cvPoint( feat->x+2, feat->y ), cvPoint( feat->x-2, feat->y ), 381 | color, 1, 8, 0 ); 382 | cvLine( img, cvPoint( feat->x, feat->y+2 ), cvPoint( feat->x, feat->y-2 ), 383 | color, 1, 8, 0 ); 384 | } 385 | 386 | 387 | 388 | /* 389 | Reads image features from file. The file should be formatted as from 390 | the code provided by David Lowe: 391 | 392 | http://www.cs.ubc.ca/~lowe/keypoints/ 393 | 394 | @param filename location of a file containing image features 395 | @param features pointer to an array in which to store features 396 | 397 | @return Returns the number of features imported from filename or -1 on error 398 | */ 399 | static int import_lowe_features( char* filename, struct feature** features ) 400 | { 401 | struct feature* f; 402 | int i, j, n, d; 403 | double x, y, s, o, dv; 404 | FILE* file; 405 | 406 | if( ! features ) 407 | fatal_error( "NULL pointer error, %s, line %d", __FILE__, __LINE__ ); 408 | if( ! ( file = fopen( filename, "r" ) ) ) 409 | { 410 | fprintf( stderr, "Warning: error opening %s, %s, line %d\n", 411 | filename, __FILE__, __LINE__ ); 412 | return -1; 413 | } 414 | 415 | /* read number of features and dimension */ 416 | if( fscanf( file, " %d %d ", &n, &d ) != 2 ) 417 | { 418 | fprintf( stderr, "Warning: file read error, %s, line %d\n", 419 | __FILE__, __LINE__ ); 420 | return -1; 421 | } 422 | if( d > FEATURE_MAX_D ) 423 | { 424 | fprintf( stderr, "Warning: descriptor too long, %s, line %d\n", 425 | __FILE__, __LINE__ ); 426 | return -1; 427 | } 428 | 429 | f = calloc( n, sizeof(struct feature) ); 430 | for( i = 0; i < n; i++ ) 431 | { 432 | /* read affine region parameters */ 433 | if( fscanf( file, " %lf %lf %lf %lf ", &y, &x, &s, &o ) != 4 ) 434 | { 435 | fprintf( stderr, "Warning: error reading feature #%d, %s, line %d\n", 436 | i+1, __FILE__, __LINE__ ); 437 | free( f ); 438 | return -1; 439 | } 440 | f[i].img_pt.x = f[i].x = x; 441 | f[i].img_pt.y = f[i].y = y; 442 | f[i].scl = s; 443 | f[i].ori = o; 444 | f[i].d = d; 445 | f[i].type = FEATURE_LOWE; 446 | 447 | /* read descriptor */ 448 | for( j = 0; j < d; j++ ) 449 | { 450 | if( ! fscanf( file, " %lf ", &dv ) ) 451 | { 452 | fprintf( stderr, "Warning: error reading feature descriptor" \ 453 | " #%d, %s, line %d\n", i+1, __FILE__, __LINE__ ); 454 | free( f ); 455 | return -1; 456 | } 457 | f[i].descr[j] = dv; 458 | } 459 | 460 | f[i].a = f[i].b = f[i].c = 0; 461 | f[i].category = 0; 462 | f[i].fwd_match = f[i].bck_match = f[i].mdl_match = NULL; 463 | f[i].mdl_pt.x = f[i].mdl_pt.y = -1; 464 | f[i].feature_data = NULL; 465 | } 466 | 467 | if( fclose(file) ) 468 | { 469 | fprintf( stderr, "Warning: file close error, %s, line %d\n", 470 | __FILE__, __LINE__ ); 471 | free( f ); 472 | return -1; 473 | } 474 | 475 | *features = f; 476 | return n; 477 | } 478 | 479 | 480 | 481 | /* 482 | Exports a feature set to a file formatted as one from the code provided 483 | by David Lowe: 484 | 485 | http://www.cs.ubc.ca/~lowe/keypoints/ 486 | 487 | @param filename name of file to which to export features 488 | @param feat feature array 489 | @param n number of features 490 | 491 | @return Returns 0 on success or 1 on error 492 | */ 493 | static int export_lowe_features( char* filename, struct feature* feat, int n ) 494 | { 495 | FILE* file; 496 | int i, j, d; 497 | 498 | if( n <= 0 ) 499 | { 500 | fprintf( stderr, "Warning: feature count %d, %s, line %d\n", 501 | n, __FILE__, __LINE__ ); 502 | return 1; 503 | } 504 | if( ! ( file = fopen( filename, "w" ) ) ) 505 | { 506 | fprintf( stderr, "Warning: error opening %s, %s, line %d\n", 507 | filename, __FILE__, __LINE__ ); 508 | return 1; 509 | } 510 | 511 | d = feat[0].d; 512 | fprintf( file, "%d %d\n", n, d ); 513 | for( i = 0; i < n; i++ ) 514 | { 515 | fprintf( file, "%f %f %f %f", feat[i].y, feat[i].x, 516 | feat[i].scl, feat[i].ori ); 517 | for( j = 0; j < d; j++ ) 518 | { 519 | /* write 20 descriptor values per line */ 520 | if( j % 20 == 0 ) 521 | fprintf( file, "\n" ); 522 | fprintf( file, " %d", (int)(feat[i].descr[j]) ); 523 | } 524 | fprintf( file, "\n" ); 525 | } 526 | 527 | if( fclose(file) ) 528 | { 529 | fprintf( stderr, "Warning: file close error, %s, line %d\n", 530 | __FILE__, __LINE__ ); 531 | return 1; 532 | } 533 | return 0; 534 | } 535 | 536 | 537 | /* 538 | Draws Lowe-type features 539 | 540 | @param img image on which to draw features 541 | @param feat array of Oxford-type features 542 | @param n number of features 543 | */ 544 | static void draw_lowe_features( IplImage* img, struct feature* feat, int n ) 545 | { 546 | CvScalar color = CV_RGB( 255, 255, 255 ); 547 | int i; 548 | 549 | if( img-> nChannels > 1 ) 550 | color = FEATURE_LOWE_COLOR; 551 | for( i = 0; i < n; i++ ) 552 | draw_lowe_feature( img, feat + i, color ); 553 | } 554 | 555 | 556 | 557 | /* 558 | Draws a single Lowe-type feature 559 | 560 | @param img image on which to draw 561 | @param feat feature to be drawn 562 | @param color color in which to draw 563 | */ 564 | static void draw_lowe_feature( IplImage* img, struct feature* feat, 565 | CvScalar color ) 566 | { 567 | int len, hlen, blen, start_x, start_y, end_x, end_y, h1_x, h1_y, h2_x, h2_y; 568 | double scl, ori; 569 | double scale = 5.0; 570 | double hscale = 0.75; 571 | CvPoint start, end, h1, h2; 572 | 573 | /* compute points for an arrow scaled and rotated by feat's scl and ori */ 574 | start_x = cvRound( feat->x ); 575 | start_y = cvRound( feat->y ); 576 | scl = feat->scl; 577 | ori = feat->ori; 578 | len = cvRound( scl * scale ); 579 | hlen = cvRound( scl * hscale ); 580 | blen = len - hlen; 581 | end_x = cvRound( len * cos( ori ) ) + start_x; 582 | end_y = cvRound( len * -sin( ori ) ) + start_y; 583 | h1_x = cvRound( blen * cos( ori + CV_PI / 18.0 ) ) + start_x; 584 | h1_y = cvRound( blen * -sin( ori + CV_PI / 18.0 ) ) + start_y; 585 | h2_x = cvRound( blen * cos( ori - CV_PI / 18.0 ) ) + start_x; 586 | h2_y = cvRound( blen * -sin( ori - CV_PI / 18.0 ) ) + start_y; 587 | start = cvPoint( start_x, start_y ); 588 | end = cvPoint( end_x, end_y ); 589 | h1 = cvPoint( h1_x, h1_y ); 590 | h2 = cvPoint( h2_x, h2_y ); 591 | 592 | cvLine( img, start, end, color, 1, 8, 0 ); 593 | cvLine( img, end, h1, color, 1, 8, 0 ); 594 | cvLine( img, end, h2, color, 1, 8, 0 ); 595 | } 596 | 597 | 598 | -------------------------------------------------------------------------------- /src/kdtree.c: -------------------------------------------------------------------------------- 1 | /* 2 | Functions and structures for maintaining a k-d tree database of image 3 | features. 4 | 5 | For more information, refer to: 6 | 7 | Beis, J. S. and Lowe, D. G. Shape indexing using approximate 8 | nearest-neighbor search in high-dimensional spaces. In Conference 9 | on Computer Vision and Pattern Recognition (CVPR) (2003), 10 | pp. 1000--1006. 11 | 12 | Copyright (C) 2006-2012 Rob Hess 13 | 14 | @version 1.1.2-20100521 15 | */ 16 | 17 | #include "kdtree.h" 18 | #include "minpq.h" 19 | #include "imgfeatures.h" 20 | #include "utils.h" 21 | 22 | #include 23 | 24 | #include 25 | 26 | struct bbf_data 27 | { 28 | double d; 29 | void* old_data; 30 | }; 31 | 32 | /************************* Local Function Prototypes *************************/ 33 | 34 | static struct kd_node* kd_node_init( struct feature*, int ); 35 | static void expand_kd_node_subtree( struct kd_node* ); 36 | static void assign_part_key( struct kd_node* ); 37 | static double median_select( double*, int ); 38 | static double rank_select( double*, int, int ); 39 | static void insertion_sort( double*, int ); 40 | static int partition_array( double*, int, double ); 41 | static void partition_features( struct kd_node* ); 42 | static struct kd_node* explore_to_leaf( struct kd_node*, struct feature*, 43 | struct min_pq* ); 44 | static int insert_into_nbr_array( struct feature*, struct feature**, int, int ); 45 | static int within_rect( CvPoint2D64f, CvRect ); 46 | 47 | 48 | /******************** Functions prototyped in keyptdb.h **********************/ 49 | 50 | 51 | /* 52 | A function to build a k-d tree database from keypoints in an array. 53 | 54 | @param features an array of features 55 | @param n the number of features in features 56 | 57 | @return Returns the root of a kd tree built from features or NULL on 58 | error. 59 | */ 60 | struct kd_node* kdtree_build( struct feature* features, int n ) 61 | { 62 | struct kd_node* kd_root; 63 | 64 | if( ! features || n <= 0 ) 65 | { 66 | fprintf( stderr, "Warning: kdtree_build(): no features, %s, line %d\n", 67 | __FILE__, __LINE__ ); 68 | return NULL; 69 | } 70 | 71 | kd_root = kd_node_init( features, n ); 72 | expand_kd_node_subtree( kd_root ); 73 | 74 | return kd_root; 75 | } 76 | 77 | 78 | 79 | /* 80 | Finds an image feature's approximate k nearest neighbors in a kd tree using 81 | Best Bin First search. 82 | 83 | @param kd_root root of an image feature kd tree 84 | @param feat image feature for whose neighbors to search 85 | @param k number of neighbors to find 86 | @param nbrs pointer to an array in which to store pointers to neighbors 87 | in order of increasing descriptor distance 88 | @param max_nn_chks search is cut off after examining this many tree entries 89 | 90 | @return Returns the number of neighbors found and stored in nbrs, or 91 | -1 on error. 92 | */ 93 | int kdtree_bbf_knn( struct kd_node* kd_root, struct feature* feat, int k, 94 | struct feature*** nbrs, int max_nn_chks ) 95 | { 96 | struct kd_node* expl; 97 | struct min_pq* min_pq; 98 | struct feature* tree_feat, ** _nbrs; 99 | struct bbf_data* bbf_data; 100 | int i, t = 0, n = 0; 101 | 102 | if( ! nbrs || ! feat || ! kd_root ) 103 | { 104 | fprintf( stderr, "Warning: NULL pointer error, %s, line %d\n", 105 | __FILE__, __LINE__ ); 106 | return -1; 107 | } 108 | 109 | _nbrs = calloc( k, sizeof( struct feature* ) ); 110 | min_pq = minpq_init(); 111 | minpq_insert( min_pq, kd_root, 0 ); 112 | while( min_pq->n > 0 && t < max_nn_chks ) 113 | { 114 | expl = (struct kd_node*)minpq_extract_min( min_pq ); 115 | if( ! expl ) 116 | { 117 | fprintf( stderr, "Warning: PQ unexpectedly empty, %s line %d\n", 118 | __FILE__, __LINE__ ); 119 | goto fail; 120 | } 121 | 122 | expl = explore_to_leaf( expl, feat, min_pq ); 123 | if( ! expl ) 124 | { 125 | fprintf( stderr, "Warning: PQ unexpectedly empty, %s line %d\n", 126 | __FILE__, __LINE__ ); 127 | goto fail; 128 | } 129 | 130 | for( i = 0; i < expl->n; i++ ) 131 | { 132 | tree_feat = &expl->features[i]; 133 | bbf_data = malloc( sizeof( struct bbf_data ) ); 134 | if( ! bbf_data ) 135 | { 136 | fprintf( stderr, "Warning: unable to allocate memory," 137 | " %s line %d\n", __FILE__, __LINE__ ); 138 | goto fail; 139 | } 140 | bbf_data->old_data = tree_feat->feature_data; 141 | bbf_data->d = descr_dist_sq(feat, tree_feat); 142 | tree_feat->feature_data = bbf_data; 143 | n += insert_into_nbr_array( tree_feat, _nbrs, n, k ); 144 | } 145 | t++; 146 | } 147 | 148 | minpq_release( &min_pq ); 149 | for( i = 0; i < n; i++ ) 150 | { 151 | bbf_data = _nbrs[i]->feature_data; 152 | _nbrs[i]->feature_data = bbf_data->old_data; 153 | free( bbf_data ); 154 | } 155 | *nbrs = _nbrs; 156 | return n; 157 | 158 | fail: 159 | minpq_release( &min_pq ); 160 | for( i = 0; i < n; i++ ) 161 | { 162 | bbf_data = _nbrs[i]->feature_data; 163 | _nbrs[i]->feature_data = bbf_data->old_data; 164 | free( bbf_data ); 165 | } 166 | free( _nbrs ); 167 | *nbrs = NULL; 168 | return -1; 169 | } 170 | 171 | 172 | 173 | /* 174 | Finds an image feature's approximate k nearest neighbors within a specified 175 | spatial region in a kd tree using Best Bin First search. 176 | 177 | @param kd_root root of an image feature kd tree 178 | @param feat image feature for whose neighbors to search 179 | @param k number of neighbors to find 180 | @param nbrs pointer to an array in which to store pointers to neighbors 181 | in order of increasing descriptor distance 182 | @param max_nn_chks search is cut off after examining this many tree entries 183 | @param rect rectangular region in which to search for neighbors 184 | @param model if true, spatial search is based on kdtree features' model 185 | locations; otherwise it is based on their image locations 186 | 187 | @return Returns the number of neighbors found and stored in \a nbrs 188 | (in case \a k neighbors could not be found before examining 189 | \a max_nn_checks keypoint entries). 190 | */ 191 | int kdtree_bbf_spatial_knn( struct kd_node* kd_root, struct feature* feat, 192 | int k, struct feature*** nbrs, int max_nn_chks, 193 | CvRect rect, int model ) 194 | { 195 | struct feature** all_nbrs, ** sp_nbrs; 196 | CvPoint2D64f pt; 197 | int i, n, t = 0; 198 | 199 | n = kdtree_bbf_knn( kd_root, feat, max_nn_chks, &all_nbrs, max_nn_chks ); 200 | sp_nbrs = calloc( k, sizeof( struct feature* ) ); 201 | for( i = 0; i < n; i++ ) 202 | { 203 | if( model ) 204 | pt = all_nbrs[i]->mdl_pt; 205 | else 206 | pt = all_nbrs[i]->img_pt; 207 | 208 | if( within_rect( pt, rect ) ) 209 | { 210 | sp_nbrs[t++] = all_nbrs[i]; 211 | if( t == k ) 212 | goto end; 213 | } 214 | } 215 | end: 216 | free( all_nbrs ); 217 | *nbrs = sp_nbrs; 218 | return t; 219 | } 220 | 221 | 222 | 223 | /* 224 | De-allocates memory held by a kd tree 225 | 226 | @param kd_root pointer to the root of a kd tree 227 | */ 228 | void kdtree_release( struct kd_node* kd_root ) 229 | { 230 | if( ! kd_root ) 231 | return; 232 | kdtree_release( kd_root->kd_left ); 233 | kdtree_release( kd_root->kd_right ); 234 | free( kd_root ); 235 | } 236 | 237 | 238 | /************************ Functions prototyped here **************************/ 239 | 240 | 241 | /* 242 | Initializes a kd tree node with a set of features. The node is not 243 | expanded, and no ordering is imposed on the features. 244 | 245 | @param features an array of image features 246 | @param n number of features 247 | 248 | @return Returns an unexpanded kd-tree node. 249 | */ 250 | static struct kd_node* kd_node_init( struct feature* features, int n ) 251 | { 252 | struct kd_node* kd_node; 253 | 254 | kd_node = malloc( sizeof( struct kd_node ) ); 255 | memset( kd_node, 0, sizeof( struct kd_node ) ); 256 | kd_node->ki = -1; 257 | kd_node->features = features; 258 | kd_node->n = n; 259 | 260 | return kd_node; 261 | } 262 | 263 | 264 | 265 | /* 266 | Recursively expands a specified kd tree node into a tree whose leaves 267 | contain one entry each. 268 | 269 | @param kd_node an unexpanded node in a kd tree 270 | */ 271 | static void expand_kd_node_subtree( struct kd_node* kd_node ) 272 | { 273 | /* base case: leaf node */ 274 | if( kd_node->n == 1 || kd_node->n == 0 ) 275 | { 276 | kd_node->leaf = 1; 277 | return; 278 | } 279 | 280 | assign_part_key( kd_node ); 281 | partition_features( kd_node ); 282 | 283 | if( kd_node->kd_left ) 284 | expand_kd_node_subtree( kd_node->kd_left ); 285 | if( kd_node->kd_right ) 286 | expand_kd_node_subtree( kd_node->kd_right ); 287 | } 288 | 289 | 290 | 291 | /* 292 | Determines the descriptor index at which and the value with which to 293 | partition a kd tree node's features. 294 | 295 | @param kd_node a kd tree node 296 | */ 297 | static void assign_part_key( struct kd_node* kd_node ) 298 | { 299 | struct feature* features; 300 | double kv, x, mean, var, var_max = 0; 301 | double* tmp; 302 | int d, n, i, j, ki = 0; 303 | 304 | features = kd_node->features; 305 | n = kd_node->n; 306 | d = features[0].d; 307 | 308 | /* partition key index is that along which descriptors have most variance */ 309 | for( j = 0; j < d; j++ ) 310 | { 311 | mean = var = 0; 312 | for( i = 0; i < n; i++ ) 313 | mean += features[i].descr[j]; 314 | mean /= n; 315 | for( i = 0; i < n; i++ ) 316 | { 317 | x = features[i].descr[j] - mean; 318 | var += x * x; 319 | } 320 | var /= n; 321 | 322 | if( var > var_max ) 323 | { 324 | ki = j; 325 | var_max = var; 326 | } 327 | } 328 | 329 | /* partition key value is median of descriptor values at ki */ 330 | tmp = calloc( n, sizeof( double ) ); 331 | for( i = 0; i < n; i++ ) 332 | tmp[i] = features[i].descr[ki]; 333 | kv = median_select( tmp, n ); 334 | free( tmp ); 335 | 336 | kd_node->ki = ki; 337 | kd_node->kv = kv; 338 | } 339 | 340 | 341 | 342 | /* 343 | Finds the median value of an array. The array's elements are re-ordered 344 | by this function. 345 | 346 | @param array an array; the order of its elelemts is reordered 347 | @param n number of elements in array 348 | 349 | @return Returns the median value of array. 350 | */ 351 | static double median_select( double* array, int n ) 352 | { 353 | return rank_select( array, n, (n - 1) / 2 ); 354 | } 355 | 356 | 357 | 358 | /* 359 | Finds the element of a specified rank in an array using the linear time 360 | median-of-medians algorithm by Blum, Floyd, Pratt, Rivest, and Tarjan. 361 | The elements of the array are re-ordered by this function. 362 | 363 | @param array an array; the order of its elelemts is reordered 364 | @param n number of elements in array 365 | @param r the zero-based rank of the element to be selected 366 | 367 | @return Returns the element from array with zero-based rank r. 368 | */ 369 | static double rank_select( double* array, int n, int r ) 370 | { 371 | double* tmp, med; 372 | int gr_5, gr_tot, rem_elts, i, j; 373 | 374 | /* base case */ 375 | if( n == 1 ) 376 | return array[0]; 377 | 378 | /* divide array into groups of 5 and sort them */ 379 | gr_5 = n / 5; 380 | gr_tot = cvCeil( n / 5.0 ); 381 | rem_elts = n % 5; 382 | tmp = array; 383 | for( i = 0; i < gr_5; i++ ) 384 | { 385 | insertion_sort( tmp, 5 ); 386 | tmp += 5; 387 | } 388 | insertion_sort( tmp, rem_elts ); 389 | 390 | /* recursively find the median of the medians of the groups of 5 */ 391 | tmp = calloc( gr_tot, sizeof( double ) ); 392 | for( i = 0, j = 2; i < gr_5; i++, j += 5 ) 393 | tmp[i] = array[j]; 394 | if( rem_elts ) 395 | tmp[i++] = array[n - 1 - rem_elts/2]; 396 | med = rank_select( tmp, i, ( i - 1 ) / 2 ); 397 | free( tmp ); 398 | 399 | /* partition around median of medians and recursively select if necessary */ 400 | j = partition_array( array, n, med ); 401 | if( r == j ) 402 | return med; 403 | else if( r < j ) 404 | return rank_select( array, j, r ); 405 | else 406 | { 407 | array += j+1; 408 | return rank_select( array, ( n - j - 1 ), ( r - j - 1 ) ); 409 | } 410 | } 411 | 412 | 413 | 414 | /* 415 | Sorts an array in place into increasing order using insertion sort. 416 | 417 | @param array an array 418 | @param n number of elements 419 | */ 420 | static void insertion_sort( double* array, int n ) 421 | { 422 | double k; 423 | int i, j; 424 | 425 | for( i = 1; i < n; i++ ) 426 | { 427 | k = array[i]; 428 | j = i-1; 429 | while( j >= 0 && array[j] > k ) 430 | { 431 | array[j+1] = array[j]; 432 | j -= 1; 433 | } 434 | array[j+1] = k; 435 | } 436 | } 437 | 438 | 439 | 440 | /* 441 | Partitions an array around a specified value. 442 | 443 | @param array an array 444 | @param n number of elements 445 | @param pivot value around which to partition 446 | 447 | @return Returns index of the pivot after partitioning 448 | */ 449 | static int partition_array( double* array, int n, double pivot ) 450 | { 451 | double tmp; 452 | int p, i, j; 453 | 454 | i = -1; 455 | for( j = 0; j < n; j++ ) 456 | if( array[j] <= pivot ) 457 | { 458 | tmp = array[++i]; 459 | array[i] = array[j]; 460 | array[j] = tmp; 461 | if( array[i] == pivot ) 462 | p = i; 463 | } 464 | array[p] = array[i]; 465 | array[i] = pivot; 466 | 467 | return i; 468 | } 469 | 470 | 471 | 472 | /* 473 | Partitions the features at a specified kd tree node to create its two 474 | children. 475 | 476 | @param kd_node a kd tree node whose partition key is set 477 | */ 478 | static void partition_features( struct kd_node* kd_node ) 479 | { 480 | struct feature* features, tmp; 481 | double kv; 482 | int n, ki, p, i, j = -1; 483 | 484 | features = kd_node->features; 485 | n = kd_node->n; 486 | ki = kd_node->ki; 487 | kv = kd_node->kv; 488 | for( i = 0; i < n; i++ ) 489 | if( features[i].descr[ki] <= kv ) 490 | { 491 | tmp = features[++j]; 492 | features[j] = features[i]; 493 | features[i] = tmp; 494 | if( features[j].descr[ki] == kv ) 495 | p = j; 496 | } 497 | tmp = features[p]; 498 | features[p] = features[j]; 499 | features[j] = tmp; 500 | 501 | /* if all records fall on same side of partition, make node a leaf */ 502 | if( j == n - 1 ) 503 | { 504 | kd_node->leaf = 1; 505 | return; 506 | } 507 | 508 | kd_node->kd_left = kd_node_init( features, j + 1 ); 509 | kd_node->kd_right = kd_node_init( features + ( j + 1 ), ( n - j - 1 ) ); 510 | } 511 | 512 | 513 | 514 | /* 515 | Explores a kd tree from a given node to a leaf. Branching decisions are 516 | made at each node based on the descriptor of a given feature. Each node 517 | examined but not explored is put into a priority queue to be explored 518 | later, keyed based on the distance from its partition key value to the 519 | given feature's desctiptor. 520 | 521 | @param kd_node root of the subtree to be explored 522 | @param feat feature upon which branching decisions are based 523 | @param min_pq a minimizing priority queue into which tree nodes are placed 524 | as described above 525 | 526 | @return Returns a pointer to the leaf node at which exploration ends or 527 | NULL on error. 528 | */ 529 | static struct kd_node* explore_to_leaf( struct kd_node* kd_node, 530 | struct feature* feat, 531 | struct min_pq* min_pq ) 532 | { 533 | struct kd_node* unexpl, * expl = kd_node; 534 | double kv; 535 | int ki; 536 | 537 | while( expl && ! expl->leaf ) 538 | { 539 | ki = expl->ki; 540 | kv = expl->kv; 541 | 542 | if( ki >= feat->d ) 543 | { 544 | fprintf( stderr, "Warning: comparing imcompatible descriptors, %s" \ 545 | " line %d\n", __FILE__, __LINE__ ); 546 | return NULL; 547 | } 548 | if( feat->descr[ki] <= kv ) 549 | { 550 | unexpl = expl->kd_right; 551 | expl = expl->kd_left; 552 | } 553 | else 554 | { 555 | unexpl = expl->kd_left; 556 | expl = expl->kd_right; 557 | } 558 | 559 | if( minpq_insert( min_pq, unexpl, ABS( kv - feat->descr[ki] ) ) ) 560 | { 561 | fprintf( stderr, "Warning: unable to insert into PQ, %s, line %d\n", 562 | __FILE__, __LINE__ ); 563 | return NULL; 564 | } 565 | } 566 | 567 | return expl; 568 | } 569 | 570 | 571 | 572 | /* 573 | Inserts a feature into the nearest-neighbor array so that the array remains 574 | in order of increasing descriptor distance from the search feature. 575 | 576 | @param feat feature to be inderted into the array; it's feature_data field 577 | should be a pointer to a bbf_data with d equal to the squared descriptor 578 | distance between feat and the search feature 579 | @param nbrs array of nearest neighbors neighbors 580 | @param n number of elements already in nbrs and 581 | @param k maximum number of elements in nbrs 582 | 583 | @return If feat was successfully inserted into nbrs, returns 1; otherwise 584 | returns 0. 585 | */ 586 | static int insert_into_nbr_array( struct feature* feat, struct feature** nbrs, 587 | int n, int k ) 588 | { 589 | struct bbf_data* fdata, * ndata; 590 | double dn, df; 591 | int i, ret = 0; 592 | 593 | if( n == 0 ) 594 | { 595 | nbrs[0] = feat; 596 | return 1; 597 | } 598 | 599 | /* check at end of array */ 600 | fdata = (struct bbf_data*)feat->feature_data; 601 | df = fdata->d; 602 | ndata = (struct bbf_data*)nbrs[n-1]->feature_data; 603 | dn = ndata->d; 604 | if( df >= dn ) 605 | { 606 | if( n == k ) 607 | { 608 | feat->feature_data = fdata->old_data; 609 | free( fdata ); 610 | return 0; 611 | } 612 | nbrs[n] = feat; 613 | return 1; 614 | } 615 | 616 | /* find the right place in the array */ 617 | if( n < k ) 618 | { 619 | nbrs[n] = nbrs[n-1]; 620 | ret = 1; 621 | } 622 | else 623 | { 624 | nbrs[n-1]->feature_data = ndata->old_data; 625 | free( ndata ); 626 | } 627 | i = n-2; 628 | while( i >= 0 ) 629 | { 630 | ndata = (struct bbf_data*)nbrs[i]->feature_data; 631 | dn = ndata->d; 632 | if( dn <= df ) 633 | break; 634 | nbrs[i+1] = nbrs[i]; 635 | i--; 636 | } 637 | i++; 638 | nbrs[i] = feat; 639 | 640 | return ret; 641 | } 642 | 643 | 644 | 645 | /* 646 | Determines whether a given point lies within a specified rectangular region 647 | 648 | @param pt point 649 | @param rect rectangular region 650 | 651 | @return Returns 1 if pt is inside rect or 0 otherwise 652 | */ 653 | static int within_rect( CvPoint2D64f pt, CvRect rect ) 654 | { 655 | if( pt.x < rect.x || pt.y < rect.y ) 656 | return 0; 657 | if( pt.x > rect.x + rect.width || pt.y > rect.y + rect.height ) 658 | return 0; 659 | return 1; 660 | } 661 | -------------------------------------------------------------------------------- /src/xform.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file contains definitions for functions to compute transforms from 3 | image feature correspondences 4 | 5 | Copyright (C) 2006-2012 Rob Hess 6 | 7 | @version 1.1.2-20100521 8 | */ 9 | 10 | #include "xform.h" 11 | #include "imgfeatures.h" 12 | #include "utils.h" 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | /************************* Local Function Prototypes *************************/ 20 | 21 | static inline struct feature* get_match( struct feature*, int ); 22 | static int get_matched_features( struct feature*, int, int, struct feature*** ); 23 | static int calc_min_inliers( int, int, double, double ); 24 | static inline double log_factorial( int ); 25 | static struct feature** draw_ransac_sample( struct feature**, int, int ); 26 | static void extract_corresp_pts( struct feature**, int, int, CvPoint2D64f**, 27 | CvPoint2D64f** ); 28 | static int find_consensus( struct feature**, int, int, CvMat*, ransac_err_fn, 29 | double, struct feature*** ); 30 | static inline void release_mem( CvPoint2D64f*, CvPoint2D64f*, 31 | struct feature** ); 32 | 33 | /********************** Functions prototyped in model.h **********************/ 34 | 35 | 36 | /* 37 | Calculates a best-fit image transform from image feature correspondences 38 | using RANSAC. 39 | 40 | For more information refer to: 41 | 42 | Fischler, M. A. and Bolles, R. C. Random sample consensus: a paradigm for 43 | model fitting with applications to image analysis and automated cartography. 44 | Communications of the ACM, 24, 6 (1981), pp. 381--395. 45 | 46 | @param features an array of features; only features with a non-NULL match 47 | of type mtype are used in homography computation 48 | @param n number of features in feat 49 | @param mtype determines which of each feature's match fields to use 50 | for model computation; should be one of FEATURE_FWD_MATCH, 51 | FEATURE_BCK_MATCH, or FEATURE_MDL_MATCH; if this is FEATURE_MDL_MATCH, 52 | correspondences are assumed to be between a feature's img_pt field 53 | and its match's mdl_pt field, otherwise correspondences are assumed to 54 | be between the the feature's img_pt field and its match's img_pt field 55 | @param xform_fn pointer to the function used to compute the desired 56 | transformation from feature correspondences 57 | @param m minimum number of correspondences necessary to instantiate the 58 | model computed by xform_fn 59 | @param p_badxform desired probability that the final transformation 60 | returned by RANSAC is corrupted by outliers (i.e. the probability that 61 | no samples of all inliers were drawn) 62 | @param err_fn pointer to the function used to compute a measure of error 63 | between putative correspondences and a computed model 64 | @param err_tol correspondences within this distance of a computed model are 65 | considered as inliers 66 | @param inliers if not NULL, output as an array of pointers to the final 67 | set of inliers 68 | @param n_in if not NULL and \a inliers is not NULL, output as the final 69 | number of inliers 70 | 71 | @return Returns a transformation matrix computed using RANSAC or NULL 72 | on error or if an acceptable transform could not be computed. 73 | */ 74 | CvMat* ransac_xform( struct feature* features, int n, int mtype, 75 | ransac_xform_fn xform_fn, int m, double p_badxform, 76 | ransac_err_fn err_fn, double err_tol, 77 | struct feature*** inliers, int* n_in ) 78 | { 79 | struct feature** matched, ** sample, ** consensus, ** consensus_max = NULL; 80 | struct ransac_data* rdata; 81 | CvPoint2D64f* pts, * mpts; 82 | CvMat* M = NULL; 83 | double p, in_frac = RANSAC_INLIER_FRAC_EST; 84 | int i, nm, in, in_min, in_max = 0, k = 0; 85 | 86 | nm = get_matched_features( features, n, mtype, &matched ); 87 | if( nm < m ) 88 | { 89 | fprintf( stderr, "Warning: not enough matches to compute xform, %s" \ 90 | " line %d\n", __FILE__, __LINE__ ); 91 | goto end; 92 | } 93 | 94 | srandom( time(NULL) ); 95 | 96 | in_min = calc_min_inliers( nm, m, RANSAC_PROB_BAD_SUPP, p_badxform ); 97 | p = pow( 1.0 - pow( in_frac, m ), k ); 98 | while( p > p_badxform ) 99 | { 100 | sample = draw_ransac_sample( matched, nm, m ); 101 | extract_corresp_pts( sample, m, mtype, &pts, &mpts ); 102 | M = xform_fn( pts, mpts, m ); 103 | if( ! M ) 104 | goto iteration_end; 105 | in = find_consensus( matched, nm, mtype, M, err_fn, err_tol, &consensus); 106 | if( in > in_max ) 107 | { 108 | if( consensus_max ) 109 | free( consensus_max ); 110 | consensus_max = consensus; 111 | in_max = in; 112 | in_frac = (double)in_max / nm; 113 | } 114 | else 115 | free( consensus ); 116 | cvReleaseMat( &M ); 117 | 118 | iteration_end: 119 | release_mem( pts, mpts, sample ); 120 | p = pow( 1.0 - pow( in_frac, m ), ++k ); 121 | } 122 | 123 | /* calculate final transform based on best consensus set */ 124 | if( in_max >= in_min ) 125 | { 126 | extract_corresp_pts( consensus_max, in_max, mtype, &pts, &mpts ); 127 | M = xform_fn( pts, mpts, in_max ); 128 | in = find_consensus( matched, nm, mtype, M, err_fn, err_tol, &consensus); 129 | cvReleaseMat( &M ); 130 | release_mem( pts, mpts, consensus_max ); 131 | extract_corresp_pts( consensus, in, mtype, &pts, &mpts ); 132 | M = xform_fn( pts, mpts, in ); 133 | if( inliers ) 134 | { 135 | *inliers = consensus; 136 | consensus = NULL; 137 | } 138 | if( n_in ) 139 | *n_in = in; 140 | release_mem( pts, mpts, consensus ); 141 | } 142 | else if( consensus_max ) 143 | { 144 | if( inliers ) 145 | *inliers = NULL; 146 | if( n_in ) 147 | *n_in = 0; 148 | free( consensus_max ); 149 | } 150 | 151 | end: 152 | for( i = 0; i < nm; i++ ) 153 | { 154 | rdata = feat_ransac_data( matched[i] ); 155 | matched[i]->feature_data = rdata->orig_feat_data; 156 | free( rdata ); 157 | } 158 | free( matched ); 159 | return M; 160 | } 161 | 162 | 163 | 164 | /* 165 | Calculates a planar homography from point correspondeces using the direct 166 | linear transform. Intended for use as a ransac_xform_fn. 167 | 168 | @param pts array of points 169 | @param mpts array of corresponding points; each pts[i], i=0..n-1, 170 | corresponds to mpts[i] 171 | @param n number of points in both pts and mpts; must be at least 4 172 | 173 | @return Returns the 3x3 planar homography matrix that transforms points 174 | in pts to their corresponding points in mpts or NULL if fewer than 4 175 | correspondences were provided 176 | */ 177 | CvMat* dlt_homog( CvPoint2D64f* pts, CvPoint2D64f* mpts, int n ) 178 | { 179 | CvMat* H, * A, * VT, * D, h, v9; 180 | double _h[9]; 181 | int i; 182 | 183 | if( n < 4 ) 184 | return NULL; 185 | 186 | /* set up matrices so we can unstack homography into h; Ah = 0 */ 187 | A = cvCreateMat( 2*n, 9, CV_64FC1 ); 188 | cvZero( A ); 189 | for( i = 0; i < n; i++ ) 190 | { 191 | cvmSet( A, 2*i, 3, -pts[i].x ); 192 | cvmSet( A, 2*i, 4, -pts[i].y ); 193 | cvmSet( A, 2*i, 5, -1.0 ); 194 | cvmSet( A, 2*i, 6, mpts[i].y * pts[i].x ); 195 | cvmSet( A, 2*i, 7, mpts[i].y * pts[i].y ); 196 | cvmSet( A, 2*i, 8, mpts[i].y ); 197 | cvmSet( A, 2*i+1, 0, pts[i].x ); 198 | cvmSet( A, 2*i+1, 1, pts[i].y ); 199 | cvmSet( A, 2*i+1, 2, 1.0 ); 200 | cvmSet( A, 2*i+1, 6, -mpts[i].x * pts[i].x ); 201 | cvmSet( A, 2*i+1, 7, -mpts[i].x * pts[i].y ); 202 | cvmSet( A, 2*i+1, 8, -mpts[i].x ); 203 | } 204 | D = cvCreateMat( 9, 9, CV_64FC1 ); 205 | VT = cvCreateMat( 9, 9, CV_64FC1 ); 206 | cvSVD( A, D, NULL, VT, CV_SVD_MODIFY_A + CV_SVD_V_T ); 207 | v9 = cvMat( 1, 9, CV_64FC1, NULL ); 208 | cvGetRow( VT, &v9, 8 ); 209 | h = cvMat( 1, 9, CV_64FC1, _h ); 210 | cvCopy( &v9, &h, NULL ); 211 | h = cvMat( 3, 3, CV_64FC1, _h ); 212 | H = cvCreateMat( 3, 3, CV_64FC1 ); 213 | cvConvert( &h, H ); 214 | 215 | cvReleaseMat( &A ); 216 | cvReleaseMat( &D ); 217 | cvReleaseMat( &VT ); 218 | return H; 219 | } 220 | 221 | 222 | 223 | /* 224 | Calculates a least-squares planar homography from point correspondeces. 225 | 226 | @param pts array of points 227 | @param mpts array of corresponding points; each pts[i], i=1..n, corresponds 228 | to mpts[i] 229 | @param n number of points in both pts and mpts; must be at least 4 230 | 231 | @return Returns the 3 x 3 least-squares planar homography matrix that 232 | transforms points in pts to their corresponding points in mpts or NULL if 233 | fewer than 4 correspondences were provided 234 | */ 235 | CvMat* lsq_homog( CvPoint2D64f* pts, CvPoint2D64f* mpts, int n ) 236 | { 237 | CvMat* H, * A, * B, X; 238 | double x[9]; 239 | int i; 240 | 241 | if( n < 4 ) 242 | { 243 | fprintf( stderr, "Warning: too few points in lsq_homog(), %s line %d\n", 244 | __FILE__, __LINE__ ); 245 | return NULL; 246 | } 247 | 248 | /* set up matrices so we can unstack homography into X; AX = B */ 249 | A = cvCreateMat( 2*n, 8, CV_64FC1 ); 250 | B = cvCreateMat( 2*n, 1, CV_64FC1 ); 251 | X = cvMat( 8, 1, CV_64FC1, x ); 252 | H = cvCreateMat(3, 3, CV_64FC1); 253 | cvZero( A ); 254 | for( i = 0; i < n; i++ ) 255 | { 256 | cvmSet( A, i, 0, pts[i].x ); 257 | cvmSet( A, i+n, 3, pts[i].x ); 258 | cvmSet( A, i, 1, pts[i].y ); 259 | cvmSet( A, i+n, 4, pts[i].y ); 260 | cvmSet( A, i, 2, 1.0 ); 261 | cvmSet( A, i+n, 5, 1.0 ); 262 | cvmSet( A, i, 6, -pts[i].x * mpts[i].x ); 263 | cvmSet( A, i, 7, -pts[i].y * mpts[i].x ); 264 | cvmSet( A, i+n, 6, -pts[i].x * mpts[i].y ); 265 | cvmSet( A, i+n, 7, -pts[i].y * mpts[i].y ); 266 | cvmSet( B, i, 0, mpts[i].x ); 267 | cvmSet( B, i+n, 0, mpts[i].y ); 268 | } 269 | cvSolve( A, B, &X, CV_SVD ); 270 | x[8] = 1.0; 271 | X = cvMat( 3, 3, CV_64FC1, x ); 272 | cvConvert( &X, H ); 273 | 274 | cvReleaseMat( &A ); 275 | cvReleaseMat( &B ); 276 | return H; 277 | } 278 | 279 | 280 | 281 | /* 282 | Calculates the transfer error between a point and its correspondence for 283 | a given homography, i.e. for a point x, it's correspondence x', and 284 | homography H, computes d(x', Hx)^2. 285 | 286 | @param pt a point 287 | @param mpt pt's correspondence 288 | @param H a homography matrix 289 | 290 | @return Returns the transfer error between pt and mpt given H 291 | */ 292 | double homog_xfer_err( CvPoint2D64f pt, CvPoint2D64f mpt, CvMat* H ) 293 | { 294 | CvPoint2D64f xpt = persp_xform_pt( pt, H ); 295 | 296 | return sqrt( dist_sq_2D( xpt, mpt ) ); 297 | } 298 | 299 | 300 | 301 | /* 302 | Performs a perspective transformation on a single point. That is, for a 303 | point (x, y) and a 3 x 3 matrix T this function returns the point 304 | (u, v), where 305 | 306 | [x' y' w']^T = T * [x y 1]^T, 307 | 308 | and 309 | 310 | (u, v) = (x'/w', y'/w'). 311 | 312 | Note that affine transforms are a subset of perspective transforms. 313 | 314 | @param pt a 2D point 315 | @param T a perspective transformation matrix 316 | 317 | @return Returns the point (u, v) as above. 318 | */ 319 | CvPoint2D64f persp_xform_pt( CvPoint2D64f pt, CvMat* T ) 320 | { 321 | CvMat XY, UV; 322 | double xy[3] = { pt.x, pt.y, 1.0 }, uv[3] = { 0 }; 323 | CvPoint2D64f rslt; 324 | 325 | cvInitMatHeader( &XY, 3, 1, CV_64FC1, xy, CV_AUTOSTEP ); 326 | cvInitMatHeader( &UV, 3, 1, CV_64FC1, uv, CV_AUTOSTEP ); 327 | cvMatMul( T, &XY, &UV ); 328 | rslt = cvPoint2D64f( uv[0] / uv[2], uv[1] / uv[2] ); 329 | 330 | return rslt; 331 | } 332 | 333 | 334 | /************************ Local funciton definitions *************************/ 335 | 336 | /* 337 | Returns a feature's match according to a specified match type 338 | 339 | @param feat feature 340 | @param mtype match type, one of FEATURE_FWD_MATCH, FEATURE_BCK_MATCH, or 341 | FEATURE_MDL_MATCH 342 | 343 | @return Returns feat's match corresponding to mtype or NULL for bad mtype 344 | */ 345 | static inline struct feature* get_match( struct feature* feat, int mtype ) 346 | { 347 | if( mtype == FEATURE_MDL_MATCH ) 348 | return feat->mdl_match; 349 | if( mtype == FEATURE_BCK_MATCH ) 350 | return feat->bck_match; 351 | if( mtype == FEATURE_FWD_MATCH ) 352 | return feat->fwd_match; 353 | return NULL; 354 | } 355 | 356 | 357 | 358 | /* 359 | Finds all features with a match of a specified type and stores pointers 360 | to them in an array. Additionally initializes each matched feature's 361 | feature_data field with a ransac_data structure. 362 | 363 | @param features array of features 364 | @param n number of features in features 365 | @param mtype match type, one of FEATURE_{FWD,BCK,MDL}_MATCH 366 | @param matched output as an array of pointers to features with a match of 367 | the specified type 368 | 369 | @return Returns the number of features output in matched. 370 | */ 371 | static int get_matched_features( struct feature* features, int n, int mtype, 372 | struct feature*** matched ) 373 | { 374 | struct feature** _matched; 375 | struct ransac_data* rdata; 376 | int i, m = 0; 377 | 378 | _matched = calloc( n, sizeof( struct feature* ) ); 379 | for( i = 0; i < n; i++ ) 380 | if( get_match( features + i, mtype ) ) 381 | { 382 | rdata = malloc( sizeof( struct ransac_data ) ); 383 | memset( rdata, 0, sizeof( struct ransac_data ) ); 384 | rdata->orig_feat_data = features[i].feature_data; 385 | _matched[m] = features + i; 386 | _matched[m]->feature_data = rdata; 387 | m++; 388 | } 389 | *matched = _matched; 390 | return m; 391 | } 392 | 393 | 394 | 395 | /* 396 | Calculates the minimum number of inliers as a function of the number of 397 | putative correspondences. Based on equation (7) in 398 | 399 | Chum, O. and Matas, J. Matching with PROSAC -- Progressive Sample Consensus. 400 | In Conference on Computer Vision and Pattern Recognition (CVPR), 401 | (2005), pp. 220--226. 402 | 403 | @param n number of putative correspondences 404 | @param m min number of correspondences to compute the model in question 405 | @param p_badsupp prob. that a bad model is supported by a correspondence 406 | @param p_badxform desired prob. that the final transformation returned is bad 407 | 408 | @return Returns the minimum number of inliers required to guarantee, based 409 | on p_badsupp, that the probability that the final transformation returned 410 | by RANSAC is less than p_badxform 411 | */ 412 | static int calc_min_inliers( int n, int m, double p_badsupp, double p_badxform ) 413 | { 414 | double pi, sum; 415 | int i, j; 416 | 417 | for( j = m+1; j <= n; j++ ) 418 | { 419 | sum = 0; 420 | for( i = j; i <= n; i++ ) 421 | { 422 | pi = (i-m) * log( p_badsupp ) + (n-i+m) * log( 1.0 - p_badsupp ) + 423 | log_factorial( n - m ) - log_factorial( i - m ) - 424 | log_factorial( n - i ); 425 | /* 426 | * Last three terms above are equivalent to log( n-m choose i-m ) 427 | */ 428 | sum += exp( pi ); 429 | } 430 | if( sum < p_badxform ) 431 | break; 432 | } 433 | return j; 434 | } 435 | 436 | 437 | 438 | /* 439 | Calculates the natural log of the factorial of a number 440 | 441 | @param n number 442 | 443 | @return Returns log( n! ) 444 | */ 445 | static inline double log_factorial( int n ) 446 | { 447 | double f = 0; 448 | int i; 449 | 450 | for( i = 1; i <= n; i++ ) 451 | f += log( i ); 452 | 453 | return f; 454 | } 455 | 456 | 457 | /* 458 | Draws a RANSAC sample from a set of features. 459 | 460 | @param features array of pointers to features from which to sample 461 | @param n number of features in features 462 | @param m size of the sample 463 | 464 | @return Returns an array of pointers to the sampled features; the sampled 465 | field of each sampled feature's ransac_data is set to 1 466 | */ 467 | static struct feature** draw_ransac_sample( struct feature** features, int n, 468 | int m ) 469 | { 470 | struct feature** sample, * feat; 471 | struct ransac_data* rdata; 472 | int i, x; 473 | 474 | for( i = 0; i < n; i++ ) 475 | { 476 | rdata = feat_ransac_data( features[i] ); 477 | rdata->sampled = 0; 478 | } 479 | 480 | sample = calloc( m, sizeof( struct feature* ) ); 481 | for( i = 0; i < m; i++ ) 482 | { 483 | do 484 | { 485 | x = random() % n; 486 | feat = features[x]; 487 | rdata = feat_ransac_data( feat ); 488 | } 489 | while( rdata->sampled ); 490 | sample[i] = feat; 491 | rdata->sampled = 1; 492 | } 493 | 494 | return sample; 495 | } 496 | 497 | 498 | 499 | /* 500 | Extrancs raw point correspondence locations from a set of features 501 | 502 | @param features array of features from which to extract points and match 503 | points; each of these is assumed to have a match of type mtype 504 | @param n number of features 505 | @param mtype match type; if FEATURE_MDL_MATCH correspondences are assumed 506 | to be between each feature's img_pt field and it's match's mdl_pt field, 507 | otherwise, correspondences are assumed to be between img_pt and img_pt 508 | @param pts output as an array of raw point locations from features 509 | @param mpts output as an array of raw point locations from features' matches 510 | */ 511 | static void extract_corresp_pts( struct feature** features, int n, int mtype, 512 | CvPoint2D64f** pts, CvPoint2D64f** mpts ) 513 | { 514 | struct feature* match; 515 | CvPoint2D64f* _pts, * _mpts; 516 | int i; 517 | 518 | _pts = calloc( n, sizeof( CvPoint2D64f ) ); 519 | _mpts = calloc( n, sizeof( CvPoint2D64f ) ); 520 | 521 | if( mtype == FEATURE_MDL_MATCH ) 522 | for( i = 0; i < n; i++ ) 523 | { 524 | match = get_match( features[i], mtype ); 525 | if( ! match ) 526 | fatal_error( "feature does not have match of type %d, %s line %d", 527 | mtype, __FILE__, __LINE__ ); 528 | _pts[i] = features[i]->img_pt; 529 | _mpts[i] = match->mdl_pt; 530 | } 531 | 532 | else 533 | for( i = 0; i < n; i++ ) 534 | { 535 | match = get_match( features[i], mtype ); 536 | if( ! match ) 537 | fatal_error( "feature does not have match of type %d, %s line %d", 538 | mtype, __FILE__, __LINE__ ); 539 | _pts[i] = features[i]->img_pt; 540 | _mpts[i] = match->img_pt; 541 | } 542 | 543 | *pts = _pts; 544 | *mpts = _mpts; 545 | } 546 | 547 | 548 | 549 | /* 550 | For a given model and error function, finds a consensus from a set of 551 | feature correspondences. 552 | 553 | @param features set of pointers to features; every feature is assumed to 554 | have a match of type mtype 555 | @param n number of features in features 556 | @param mtype determines the match field of each feature against which to 557 | measure error; if this is FEATURE_MDL_MATCH, correspondences are assumed 558 | to be between the feature's img_pt field and the match's mdl_pt field; 559 | otherwise matches are assumed to be between img_pt and img_pt 560 | @param M model for which a consensus set is being found 561 | @param err_fn error function used to measure distance from M 562 | @param err_tol correspondences within this distance of M are added to the 563 | consensus set 564 | @param consensus output as an array of pointers to features in the 565 | consensus set 566 | 567 | @return Returns the number of points in the consensus set 568 | */ 569 | static int find_consensus( struct feature** features, int n, int mtype, 570 | CvMat* M, ransac_err_fn err_fn, double err_tol, 571 | struct feature*** consensus ) 572 | { 573 | struct feature** _consensus; 574 | struct feature* match; 575 | CvPoint2D64f pt, mpt; 576 | double err; 577 | int i, in = 0; 578 | 579 | _consensus = calloc( n, sizeof( struct feature* ) ); 580 | 581 | if( mtype == FEATURE_MDL_MATCH ) 582 | for( i = 0; i < n; i++ ) 583 | { 584 | match = get_match( features[i], mtype ); 585 | if( ! match ) 586 | fatal_error( "feature does not have match of type %d, %s line %d", 587 | mtype, __FILE__, __LINE__ ); 588 | pt = features[i]->img_pt; 589 | mpt = match->mdl_pt; 590 | err = err_fn( pt, mpt, M ); 591 | if( err <= err_tol ) 592 | _consensus[in++] = features[i]; 593 | } 594 | 595 | else 596 | for( i = 0; i < n; i++ ) 597 | { 598 | match = get_match( features[i], mtype ); 599 | if( ! match ) 600 | fatal_error( "feature does not have match of type %d, %s line %d", 601 | mtype, __FILE__, __LINE__ ); 602 | pt = features[i]->img_pt; 603 | mpt = match->img_pt; 604 | err = err_fn( pt, mpt, M ); 605 | if( err <= err_tol ) 606 | _consensus[in++] = features[i]; 607 | } 608 | *consensus = _consensus; 609 | return in; 610 | } 611 | 612 | 613 | 614 | /* 615 | Releases memory and reduces code size above 616 | 617 | @param pts1 an array of points 618 | @param pts2 an array of points 619 | @param features an array of pointers to features; can be NULL 620 | */ 621 | static inline void release_mem( CvPoint2D64f* pts1, CvPoint2D64f* pts2, 622 | struct feature** features ) 623 | { 624 | free( pts1 ); 625 | free( pts2 ); 626 | if( features ) 627 | free( features ); 628 | } 629 | -------------------------------------------------------------------------------- /src/sift.c: -------------------------------------------------------------------------------- 1 | /* 2 | Functions for detecting SIFT image features. 3 | 4 | For more information, refer to: 5 | 6 | Lowe, D. Distinctive image features from scale-invariant keypoints. 7 | International Journal of Computer Vision, 60, 2 (2004), 8 | pp.91--110. 9 | 10 | Copyright (C) 2006-2012 Rob Hess 11 | 12 | Note: The SIFT algorithm is patented in the United States and cannot be 13 | used in commercial products without a license from the University of 14 | British Columbia. For more information, refer to the file LICENSE.ubc 15 | that accompanied this distribution. 16 | 17 | @version 1.1.2-20100521 18 | */ 19 | 20 | #include "sift.h" 21 | #include "imgfeatures.h" 22 | #include "utils.h" 23 | 24 | #include 25 | #include 26 | 27 | /************************* Local Function Prototypes *************************/ 28 | 29 | static IplImage* create_init_img( IplImage*, int, double ); 30 | static IplImage* convert_to_gray32( IplImage* ); 31 | static IplImage*** build_gauss_pyr( IplImage*, int, int, double ); 32 | static IplImage* downsample( IplImage* ); 33 | static IplImage*** build_dog_pyr( IplImage***, int, int ); 34 | static CvSeq* scale_space_extrema( IplImage***, int, int, double, int, 35 | CvMemStorage*); 36 | static int is_extremum( IplImage***, int, int, int, int ); 37 | static struct feature* interp_extremum( IplImage***, int, int, int, int, int, 38 | double); 39 | static void interp_step( IplImage***, int, int, int, int, double*, double*, 40 | double* ); 41 | static CvMat* deriv_3D( IplImage***, int, int, int, int ); 42 | static CvMat* hessian_3D( IplImage***, int, int, int, int ); 43 | static double interp_contr( IplImage***, int, int, int, int, double, double, 44 | double ); 45 | static struct feature* new_feature( void ); 46 | static int is_too_edge_like( IplImage*, int, int, int ); 47 | static void calc_feature_scales( CvSeq*, double, int ); 48 | static void adjust_for_img_dbl( CvSeq* ); 49 | static void calc_feature_oris( CvSeq*, IplImage*** ); 50 | static double* ori_hist( IplImage*, int, int, int, int, double ); 51 | static int calc_grad_mag_ori( IplImage*, int, int, double*, double* ); 52 | static void smooth_ori_hist( double*, int ); 53 | static double dominant_ori( double*, int ); 54 | static void add_good_ori_features( CvSeq*, double*, int, double, 55 | struct feature* ); 56 | static struct feature* clone_feature( struct feature* ); 57 | static void compute_descriptors( CvSeq*, IplImage***, int, int ); 58 | static double*** descr_hist( IplImage*, int, int, double, double, int, int ); 59 | static void interp_hist_entry( double***, double, double, double, double, int, 60 | int); 61 | static void hist_to_descr( double***, int, int, struct feature* ); 62 | static void normalize_descr( struct feature* ); 63 | static int feature_cmp( void*, void*, void* ); 64 | static void release_descr_hist( double****, int ); 65 | static void release_pyr( IplImage****, int, int ); 66 | 67 | 68 | /*********************** Functions prototyped in sift.h **********************/ 69 | 70 | 71 | /** 72 | Finds SIFT features in an image using default parameter values. All 73 | detected features are stored in the array pointed to by \a feat. 74 | 75 | @param img the image in which to detect features 76 | @param feat a pointer to an array in which to store detected features 77 | 78 | @return Returns the number of features stored in \a feat or -1 on failure 79 | @see _sift_features() 80 | */ 81 | int sift_features( IplImage* img, struct feature** feat ) 82 | { 83 | return _sift_features( img, feat, SIFT_INTVLS, SIFT_SIGMA, SIFT_CONTR_THR, 84 | SIFT_CURV_THR, SIFT_IMG_DBL, SIFT_DESCR_WIDTH, 85 | SIFT_DESCR_HIST_BINS ); 86 | } 87 | 88 | 89 | 90 | /** 91 | Finds SIFT features in an image using user-specified parameter values. All 92 | detected features are stored in the array pointed to by \a feat. 93 | 94 | @param img the image in which to detect features 95 | @param fea a pointer to an array in which to store detected features 96 | @param intvls the number of intervals sampled per octave of scale space 97 | @param sigma the amount of Gaussian smoothing applied to each image level 98 | before building the scale space representation for an octave 99 | @param cont_thr a threshold on the value of the scale space function 100 | \f$\left|D(\hat{x})\right|\f$, where \f$\hat{x}\f$ is a vector specifying 101 | feature location and scale, used to reject unstable features; assumes 102 | pixel values in the range [0, 1] 103 | @param curv_thr threshold on a feature's ratio of principle curvatures 104 | used to reject features that are too edge-like 105 | @param img_dbl should be 1 if image doubling prior to scale space 106 | construction is desired or 0 if not 107 | @param descr_width the width, \f$n\f$, of the \f$n \times n\f$ array of 108 | orientation histograms used to compute a feature's descriptor 109 | @param descr_hist_bins the number of orientations in each of the 110 | histograms in the array used to compute a feature's descriptor 111 | 112 | @return Returns the number of keypoints stored in \a feat or -1 on failure 113 | @see sift_keypoints() 114 | */ 115 | int _sift_features( IplImage* img, struct feature** feat, int intvls, 116 | double sigma, double contr_thr, int curv_thr, 117 | int img_dbl, int descr_width, int descr_hist_bins ) 118 | { 119 | IplImage* init_img; 120 | IplImage*** gauss_pyr, *** dog_pyr; 121 | CvMemStorage* storage; 122 | CvSeq* features; 123 | int octvs, i, n = 0; 124 | 125 | /* check arguments */ 126 | if( ! img ) 127 | fatal_error( "NULL pointer error, %s, line %d", __FILE__, __LINE__ ); 128 | if( ! feat ) 129 | fatal_error( "NULL pointer error, %s, line %d", __FILE__, __LINE__ ); 130 | 131 | /* build scale space pyramid; smallest dimension of top level is ~4 pixels */ 132 | init_img = create_init_img( img, img_dbl, sigma ); 133 | octvs = log( MIN( init_img->width, init_img->height ) ) / log(2) - 2; 134 | gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma ); 135 | dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls ); 136 | 137 | storage = cvCreateMemStorage( 0 ); 138 | features = scale_space_extrema( dog_pyr, octvs, intvls, contr_thr, 139 | curv_thr, storage ); 140 | calc_feature_scales( features, sigma, intvls ); 141 | if( img_dbl ) 142 | adjust_for_img_dbl( features ); 143 | calc_feature_oris( features, gauss_pyr ); 144 | compute_descriptors( features, gauss_pyr, descr_width, descr_hist_bins ); 145 | 146 | /* sort features by decreasing scale and move from CvSeq to array */ 147 | cvSeqSort( features, (CvCmpFunc)feature_cmp, NULL ); 148 | n = features->total; 149 | *feat = calloc( n, sizeof(struct feature) ); 150 | *feat = cvCvtSeqToArray( features, *feat, CV_WHOLE_SEQ ); 151 | for( i = 0; i < n; i++ ) 152 | { 153 | free( (*feat)[i].feature_data ); 154 | (*feat)[i].feature_data = NULL; 155 | } 156 | 157 | cvReleaseMemStorage( &storage ); 158 | cvReleaseImage( &init_img ); 159 | release_pyr( &gauss_pyr, octvs, intvls + 3 ); 160 | release_pyr( &dog_pyr, octvs, intvls + 2 ); 161 | return n; 162 | } 163 | 164 | 165 | /************************ Functions prototyped here **************************/ 166 | 167 | /* 168 | Converts an image to 8-bit grayscale and Gaussian-smooths it. The image is 169 | optionally doubled in size prior to smoothing. 170 | 171 | @param img input image 172 | @param img_dbl if true, image is doubled in size prior to smoothing 173 | @param sigma total std of Gaussian smoothing 174 | */ 175 | static IplImage* create_init_img( IplImage* img, int img_dbl, double sigma ) 176 | { 177 | IplImage* gray, * dbl; 178 | double sig_diff; 179 | 180 | gray = convert_to_gray32( img ); 181 | if( img_dbl ) 182 | { 183 | sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4 ); 184 | dbl = cvCreateImage( cvSize( img->width*2, img->height*2 ), 185 | IPL_DEPTH_32F, 1 ); 186 | cvResize( gray, dbl, CV_INTER_CUBIC ); 187 | cvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff ); 188 | cvReleaseImage( &gray ); 189 | return dbl; 190 | } 191 | else 192 | { 193 | sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA ); 194 | cvSmooth( gray, gray, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff ); 195 | return gray; 196 | } 197 | } 198 | 199 | 200 | 201 | /* 202 | Converts an image to 32-bit grayscale 203 | 204 | @param img a 3-channel 8-bit color (BGR) or 8-bit gray image 205 | 206 | @return Returns a 32-bit grayscale image 207 | */ 208 | static IplImage* convert_to_gray32( IplImage* img ) 209 | { 210 | IplImage* gray8, * gray32; 211 | 212 | gray32 = cvCreateImage( cvGetSize(img), IPL_DEPTH_32F, 1 ); 213 | if( img->nChannels == 1 ) 214 | gray8 = cvClone( img ); 215 | else 216 | { 217 | gray8 = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 ); 218 | cvCvtColor( img, gray8, CV_BGR2GRAY ); 219 | } 220 | cvConvertScale( gray8, gray32, 1.0 / 255.0, 0 ); 221 | 222 | cvReleaseImage( &gray8 ); 223 | return gray32; 224 | } 225 | 226 | 227 | 228 | /* 229 | Builds Gaussian scale space pyramid from an image 230 | 231 | @param base base image of the pyramid 232 | @param octvs number of octaves of scale space 233 | @param intvls number of intervals per octave 234 | @param sigma amount of Gaussian smoothing per octave 235 | 236 | @return Returns a Gaussian scale space pyramid as an octvs x (intvls + 3) 237 | array 238 | */ 239 | static IplImage*** build_gauss_pyr( IplImage* base, int octvs, 240 | int intvls, double sigma ) 241 | { 242 | IplImage*** gauss_pyr; 243 | const int _intvls = intvls; 244 | double sig[_intvls+3], sig_total, sig_prev, k; 245 | int i, o; 246 | 247 | gauss_pyr = calloc( octvs, sizeof( IplImage** ) ); 248 | for( i = 0; i < octvs; i++ ) 249 | gauss_pyr[i] = calloc( intvls + 3, sizeof( IplImage *) ); 250 | 251 | /* 252 | precompute Gaussian sigmas using the following formula: 253 | 254 | \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2 255 | 256 | sig[i] is the incremental sigma value needed to compute 257 | the actual sigma of level i. Keeping track of incremental 258 | sigmas vs. total sigmas keeps the gaussian kernel small. 259 | */ 260 | k = pow( 2.0, 1.0 / intvls ); 261 | sig[0] = sigma; 262 | sig[1] = sigma * sqrt( k*k- 1 ); 263 | for (i = 2; i < intvls + 3; i++) 264 | sig[i] = sig[i-1] * k; 265 | 266 | for( o = 0; o < octvs; o++ ) 267 | for( i = 0; i < intvls + 3; i++ ) 268 | { 269 | if( o == 0 && i == 0 ) 270 | gauss_pyr[o][i] = cvCloneImage(base); 271 | 272 | /* base of new octvave is halved image from end of previous octave */ 273 | else if( i == 0 ) 274 | gauss_pyr[o][i] = downsample( gauss_pyr[o-1][intvls] ); 275 | 276 | /* blur the current octave's last image to create the next one */ 277 | else 278 | { 279 | gauss_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i-1]), 280 | IPL_DEPTH_32F, 1 ); 281 | cvSmooth( gauss_pyr[o][i-1], gauss_pyr[o][i], 282 | CV_GAUSSIAN, 0, 0, sig[i], sig[i] ); 283 | } 284 | } 285 | 286 | return gauss_pyr; 287 | } 288 | 289 | 290 | 291 | /* 292 | Downsamples an image to a quarter of its size (half in each dimension) 293 | using nearest-neighbor interpolation 294 | 295 | @param img an image 296 | 297 | @return Returns an image whose dimensions are half those of img 298 | */ 299 | static IplImage* downsample( IplImage* img ) 300 | { 301 | IplImage* smaller = cvCreateImage( cvSize(img->width / 2, img->height / 2), 302 | img->depth, img->nChannels ); 303 | cvResize( img, smaller, CV_INTER_NN ); 304 | 305 | return smaller; 306 | } 307 | 308 | 309 | 310 | /* 311 | Builds a difference of Gaussians scale space pyramid by subtracting adjacent 312 | intervals of a Gaussian pyramid 313 | 314 | @param gauss_pyr Gaussian scale-space pyramid 315 | @param octvs number of octaves of scale space 316 | @param intvls number of intervals per octave 317 | 318 | @return Returns a difference of Gaussians scale space pyramid as an 319 | octvs x (intvls + 2) array 320 | */ 321 | static IplImage*** build_dog_pyr( IplImage*** gauss_pyr, int octvs, int intvls ) 322 | { 323 | IplImage*** dog_pyr; 324 | int i, o; 325 | 326 | dog_pyr = calloc( octvs, sizeof( IplImage** ) ); 327 | for( i = 0; i < octvs; i++ ) 328 | dog_pyr[i] = calloc( intvls + 2, sizeof(IplImage*) ); 329 | 330 | for( o = 0; o < octvs; o++ ) 331 | for( i = 0; i < intvls + 2; i++ ) 332 | { 333 | dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i]), 334 | IPL_DEPTH_32F, 1 ); 335 | cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog_pyr[o][i], NULL ); 336 | } 337 | 338 | return dog_pyr; 339 | } 340 | 341 | 342 | 343 | /* 344 | Detects features at extrema in DoG scale space. Bad features are discarded 345 | based on contrast and ratio of principal curvatures. 346 | 347 | @param dog_pyr DoG scale space pyramid 348 | @param octvs octaves of scale space represented by dog_pyr 349 | @param intvls intervals per octave 350 | @param contr_thr low threshold on feature contrast 351 | @param curv_thr high threshold on feature ratio of principal curvatures 352 | @param storage memory storage in which to store detected features 353 | 354 | @return Returns an array of detected features whose scales, orientations, 355 | and descriptors are yet to be determined. 356 | */ 357 | static CvSeq* scale_space_extrema( IplImage*** dog_pyr, int octvs, int intvls, 358 | double contr_thr, int curv_thr, 359 | CvMemStorage* storage ) 360 | { 361 | CvSeq* features; 362 | double prelim_contr_thr = 0.5 * contr_thr / intvls; 363 | struct feature* feat; 364 | struct detection_data* ddata; 365 | int o, i, r, c; 366 | unsigned long* feature_mat; 367 | 368 | features = cvCreateSeq( 0, sizeof(CvSeq), sizeof(struct feature), storage ); 369 | for( o = 0; o < octvs; o++ ) 370 | { 371 | feature_mat = calloc( dog_pyr[o][0]->height * dog_pyr[o][0]->width, sizeof(unsigned long) ); 372 | for( i = 1; i <= intvls; i++ ) 373 | for(r = SIFT_IMG_BORDER; r < dog_pyr[o][0]->height-SIFT_IMG_BORDER; r++) 374 | for(c = SIFT_IMG_BORDER; c < dog_pyr[o][0]->width-SIFT_IMG_BORDER; c++) 375 | /* perform preliminary check on contrast */ 376 | if( ABS( pixval32f( dog_pyr[o][i], r, c ) ) > prelim_contr_thr ) 377 | if( is_extremum( dog_pyr, o, i, r, c ) ) 378 | { 379 | feat = interp_extremum(dog_pyr, o, i, r, c, intvls, contr_thr); 380 | if( feat ) 381 | { 382 | ddata = feat_detection_data( feat ); 383 | if( ! is_too_edge_like( dog_pyr[ddata->octv][ddata->intvl], 384 | ddata->r, ddata->c, curv_thr ) ) 385 | { 386 | if( ddata->intvl > sizeof(unsigned long) ) 387 | cvSeqPush( features, feat ); 388 | else if( (feature_mat[dog_pyr[o][0]->width * ddata->r + ddata->c] & (1 << ddata->intvl-1)) == 0 ) 389 | { 390 | cvSeqPush( features, feat ); 391 | feature_mat[dog_pyr[o][0]->width * ddata->r + ddata->c] += 1 << ddata->intvl-1; 392 | } 393 | } 394 | else 395 | free( ddata ); 396 | free( feat ); 397 | } 398 | } 399 | free( feature_mat ); 400 | } 401 | return features; 402 | } 403 | 404 | 405 | 406 | /* 407 | Determines whether a pixel is a scale-space extremum by comparing it to it's 408 | 3x3x3 pixel neighborhood. 409 | 410 | @param dog_pyr DoG scale space pyramid 411 | @param octv pixel's scale space octave 412 | @param intvl pixel's within-octave interval 413 | @param r pixel's image row 414 | @param c pixel's image col 415 | 416 | @return Returns 1 if the specified pixel is an extremum (max or min) among 417 | it's 3x3x3 pixel neighborhood. 418 | */ 419 | static int is_extremum( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) 420 | { 421 | double val = pixval32f( dog_pyr[octv][intvl], r, c ); 422 | int i, j, k; 423 | 424 | /* check for maximum */ 425 | if( val > 0 ) 426 | { 427 | for( i = -1; i <= 1; i++ ) 428 | for( j = -1; j <= 1; j++ ) 429 | for( k = -1; k <= 1; k++ ) 430 | if( val < pixval32f( dog_pyr[octv][intvl+i], r + j, c + k ) ) 431 | return 0; 432 | } 433 | 434 | /* check for minimum */ 435 | else 436 | { 437 | for( i = -1; i <= 1; i++ ) 438 | for( j = -1; j <= 1; j++ ) 439 | for( k = -1; k <= 1; k++ ) 440 | if( val > pixval32f( dog_pyr[octv][intvl+i], r + j, c + k ) ) 441 | return 0; 442 | } 443 | 444 | return 1; 445 | } 446 | 447 | 448 | 449 | /* 450 | Interpolates a scale-space extremum's location and scale to subpixel 451 | accuracy to form an image feature. Rejects features with low contrast. 452 | Based on Section 4 of Lowe's paper. 453 | 454 | @param dog_pyr DoG scale space pyramid 455 | @param octv feature's octave of scale space 456 | @param intvl feature's within-octave interval 457 | @param r feature's image row 458 | @param c feature's image column 459 | @param intvls total intervals per octave 460 | @param contr_thr threshold on feature contrast 461 | 462 | @return Returns the feature resulting from interpolation of the given 463 | parameters or NULL if the given location could not be interpolated or 464 | if contrast at the interpolated loation was too low. If a feature is 465 | returned, its scale, orientation, and descriptor are yet to be determined. 466 | */ 467 | static struct feature* interp_extremum( IplImage*** dog_pyr, int octv, 468 | int intvl, int r, int c, int intvls, 469 | double contr_thr ) 470 | { 471 | struct feature* feat; 472 | struct detection_data* ddata; 473 | double xi, xr, xc, contr; 474 | int i = 0; 475 | 476 | while( i < SIFT_MAX_INTERP_STEPS ) 477 | { 478 | interp_step( dog_pyr, octv, intvl, r, c, &xi, &xr, &xc ); 479 | if( ABS( xi ) < 0.5 && ABS( xr ) < 0.5 && ABS( xc ) < 0.5 ) 480 | break; 481 | 482 | c += cvRound( xc ); 483 | r += cvRound( xr ); 484 | intvl += cvRound( xi ); 485 | 486 | if( intvl < 1 || 487 | intvl > intvls || 488 | c < SIFT_IMG_BORDER || 489 | r < SIFT_IMG_BORDER || 490 | c >= dog_pyr[octv][0]->width - SIFT_IMG_BORDER || 491 | r >= dog_pyr[octv][0]->height - SIFT_IMG_BORDER ) 492 | { 493 | return NULL; 494 | } 495 | 496 | i++; 497 | } 498 | 499 | /* ensure convergence of interpolation */ 500 | if( i >= SIFT_MAX_INTERP_STEPS ) 501 | return NULL; 502 | 503 | contr = interp_contr( dog_pyr, octv, intvl, r, c, xi, xr, xc ); 504 | if( ABS( contr ) < contr_thr / intvls ) 505 | return NULL; 506 | 507 | feat = new_feature(); 508 | ddata = feat_detection_data( feat ); 509 | feat->img_pt.x = feat->x = ( c + xc ) * pow( 2.0, octv ); 510 | feat->img_pt.y = feat->y = ( r + xr ) * pow( 2.0, octv ); 511 | ddata->r = r; 512 | ddata->c = c; 513 | ddata->octv = octv; 514 | ddata->intvl = intvl; 515 | ddata->subintvl = xi; 516 | 517 | return feat; 518 | } 519 | 520 | 521 | 522 | /* 523 | Performs one step of extremum interpolation. Based on Eqn. (3) in Lowe's 524 | paper. 525 | 526 | @param dog_pyr difference of Gaussians scale space pyramid 527 | @param octv octave of scale space 528 | @param intvl interval being interpolated 529 | @param r row being interpolated 530 | @param c column being interpolated 531 | @param xi output as interpolated subpixel increment to interval 532 | @param xr output as interpolated subpixel increment to row 533 | @param xc output as interpolated subpixel increment to col 534 | */ 535 | 536 | static void interp_step( IplImage*** dog_pyr, int octv, int intvl, int r, int c, 537 | double* xi, double* xr, double* xc ) 538 | { 539 | CvMat* dD, * H, * H_inv, X; 540 | double x[3] = { 0 }; 541 | 542 | dD = deriv_3D( dog_pyr, octv, intvl, r, c ); 543 | H = hessian_3D( dog_pyr, octv, intvl, r, c ); 544 | H_inv = cvCreateMat( 3, 3, CV_64FC1 ); 545 | cvInvert( H, H_inv, CV_SVD ); 546 | cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP ); 547 | cvGEMM( H_inv, dD, -1, NULL, 0, &X, 0 ); 548 | 549 | cvReleaseMat( &dD ); 550 | cvReleaseMat( &H ); 551 | cvReleaseMat( &H_inv ); 552 | 553 | *xi = x[2]; 554 | *xr = x[1]; 555 | *xc = x[0]; 556 | } 557 | 558 | 559 | 560 | /* 561 | Computes the partial derivatives in x, y, and scale of a pixel in the DoG 562 | scale space pyramid. 563 | 564 | @param dog_pyr DoG scale space pyramid 565 | @param octv pixel's octave in dog_pyr 566 | @param intvl pixel's interval in octv 567 | @param r pixel's image row 568 | @param c pixel's image col 569 | 570 | @return Returns the vector of partial derivatives for pixel I 571 | { dI/dx, dI/dy, dI/ds }^T as a CvMat* 572 | */ 573 | static CvMat* deriv_3D( IplImage*** dog_pyr, int octv, int intvl, int r, int c ) 574 | { 575 | CvMat* dI; 576 | double dx, dy, ds; 577 | 578 | dx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) - 579 | pixval32f( dog_pyr[octv][intvl], r, c-1 ) ) / 2.0; 580 | dy = ( pixval32f( dog_pyr[octv][intvl], r+1, c ) - 581 | pixval32f( dog_pyr[octv][intvl], r-1, c ) ) / 2.0; 582 | ds = ( pixval32f( dog_pyr[octv][intvl+1], r, c ) - 583 | pixval32f( dog_pyr[octv][intvl-1], r, c ) ) / 2.0; 584 | 585 | dI = cvCreateMat( 3, 1, CV_64FC1 ); 586 | cvmSet( dI, 0, 0, dx ); 587 | cvmSet( dI, 1, 0, dy ); 588 | cvmSet( dI, 2, 0, ds ); 589 | 590 | return dI; 591 | } 592 | 593 | 594 | 595 | /* 596 | Computes the 3D Hessian matrix for a pixel in the DoG scale space pyramid. 597 | 598 | @param dog_pyr DoG scale space pyramid 599 | @param octv pixel's octave in dog_pyr 600 | @param intvl pixel's interval in octv 601 | @param r pixel's image row 602 | @param c pixel's image col 603 | 604 | @return Returns the Hessian matrix (below) for pixel I as a CvMat* 605 | 606 | / Ixx Ixy Ixs \
607 | | Ixy Iyy Iys |
608 | \ Ixs Iys Iss / 609 | */ 610 | static CvMat* hessian_3D( IplImage*** dog_pyr, int octv, int intvl, int r, 611 | int c ) 612 | { 613 | CvMat* H; 614 | double v, dxx, dyy, dss, dxy, dxs, dys; 615 | 616 | v = pixval32f( dog_pyr[octv][intvl], r, c ); 617 | dxx = ( pixval32f( dog_pyr[octv][intvl], r, c+1 ) + 618 | pixval32f( dog_pyr[octv][intvl], r, c-1 ) - 2 * v ); 619 | dyy = ( pixval32f( dog_pyr[octv][intvl], r+1, c ) + 620 | pixval32f( dog_pyr[octv][intvl], r-1, c ) - 2 * v ); 621 | dss = ( pixval32f( dog_pyr[octv][intvl+1], r, c ) + 622 | pixval32f( dog_pyr[octv][intvl-1], r, c ) - 2 * v ); 623 | dxy = ( pixval32f( dog_pyr[octv][intvl], r+1, c+1 ) - 624 | pixval32f( dog_pyr[octv][intvl], r+1, c-1 ) - 625 | pixval32f( dog_pyr[octv][intvl], r-1, c+1 ) + 626 | pixval32f( dog_pyr[octv][intvl], r-1, c-1 ) ) / 4.0; 627 | dxs = ( pixval32f( dog_pyr[octv][intvl+1], r, c+1 ) - 628 | pixval32f( dog_pyr[octv][intvl+1], r, c-1 ) - 629 | pixval32f( dog_pyr[octv][intvl-1], r, c+1 ) + 630 | pixval32f( dog_pyr[octv][intvl-1], r, c-1 ) ) / 4.0; 631 | dys = ( pixval32f( dog_pyr[octv][intvl+1], r+1, c ) - 632 | pixval32f( dog_pyr[octv][intvl+1], r-1, c ) - 633 | pixval32f( dog_pyr[octv][intvl-1], r+1, c ) + 634 | pixval32f( dog_pyr[octv][intvl-1], r-1, c ) ) / 4.0; 635 | 636 | H = cvCreateMat( 3, 3, CV_64FC1 ); 637 | cvmSet( H, 0, 0, dxx ); 638 | cvmSet( H, 0, 1, dxy ); 639 | cvmSet( H, 0, 2, dxs ); 640 | cvmSet( H, 1, 0, dxy ); 641 | cvmSet( H, 1, 1, dyy ); 642 | cvmSet( H, 1, 2, dys ); 643 | cvmSet( H, 2, 0, dxs ); 644 | cvmSet( H, 2, 1, dys ); 645 | cvmSet( H, 2, 2, dss ); 646 | 647 | return H; 648 | } 649 | 650 | 651 | 652 | /* 653 | Calculates interpolated pixel contrast. Based on Eqn. (3) in Lowe's 654 | paper. 655 | 656 | @param dog_pyr difference of Gaussians scale space pyramid 657 | @param octv octave of scale space 658 | @param intvl within-octave interval 659 | @param r pixel row 660 | @param c pixel column 661 | @param xi interpolated subpixel increment to interval 662 | @param xr interpolated subpixel increment to row 663 | @param xc interpolated subpixel increment to col 664 | 665 | @param Returns interpolated contrast. 666 | */ 667 | static double interp_contr( IplImage*** dog_pyr, int octv, int intvl, int r, 668 | int c, double xi, double xr, double xc ) 669 | { 670 | CvMat* dD, X, T; 671 | double t[1], x[3] = { xc, xr, xi }; 672 | 673 | cvInitMatHeader( &X, 3, 1, CV_64FC1, x, CV_AUTOSTEP ); 674 | cvInitMatHeader( &T, 1, 1, CV_64FC1, t, CV_AUTOSTEP ); 675 | dD = deriv_3D( dog_pyr, octv, intvl, r, c ); 676 | cvGEMM( dD, &X, 1, NULL, 0, &T, CV_GEMM_A_T ); 677 | cvReleaseMat( &dD ); 678 | 679 | return pixval32f( dog_pyr[octv][intvl], r, c ) + t[0] * 0.5; 680 | } 681 | 682 | 683 | 684 | /* 685 | Allocates and initializes a new feature 686 | 687 | @return Returns a pointer to the new feature 688 | */ 689 | static struct feature* new_feature( void ) 690 | { 691 | struct feature* feat; 692 | struct detection_data* ddata; 693 | 694 | feat = malloc( sizeof( struct feature ) ); 695 | memset( feat, 0, sizeof( struct feature ) ); 696 | ddata = malloc( sizeof( struct detection_data ) ); 697 | memset( ddata, 0, sizeof( struct detection_data ) ); 698 | feat->feature_data = ddata; 699 | feat->type = FEATURE_LOWE; 700 | 701 | return feat; 702 | } 703 | 704 | 705 | 706 | /* 707 | Determines whether a feature is too edge like to be stable by computing the 708 | ratio of principal curvatures at that feature. Based on Section 4.1 of 709 | Lowe's paper. 710 | 711 | @param dog_img image from the DoG pyramid in which feature was detected 712 | @param r feature row 713 | @param c feature col 714 | @param curv_thr high threshold on ratio of principal curvatures 715 | 716 | @return Returns 0 if the feature at (r,c) in dog_img is sufficiently 717 | corner-like or 1 otherwise. 718 | */ 719 | static int is_too_edge_like( IplImage* dog_img, int r, int c, int curv_thr ) 720 | { 721 | double d, dxx, dyy, dxy, tr, det; 722 | 723 | /* principal curvatures are computed using the trace and det of Hessian */ 724 | d = pixval32f(dog_img, r, c); 725 | dxx = pixval32f( dog_img, r, c+1 ) + pixval32f( dog_img, r, c-1 ) - 2 * d; 726 | dyy = pixval32f( dog_img, r+1, c ) + pixval32f( dog_img, r-1, c ) - 2 * d; 727 | dxy = ( pixval32f(dog_img, r+1, c+1) - pixval32f(dog_img, r+1, c-1) - 728 | pixval32f(dog_img, r-1, c+1) + pixval32f(dog_img, r-1, c-1) ) / 4.0; 729 | tr = dxx + dyy; 730 | det = dxx * dyy - dxy * dxy; 731 | 732 | /* negative determinant -> curvatures have different signs; reject feature */ 733 | if( det <= 0 ) 734 | return 1; 735 | 736 | if( tr * tr / det < ( curv_thr + 1.0 )*( curv_thr + 1.0 ) / curv_thr ) 737 | return 0; 738 | return 1; 739 | } 740 | 741 | 742 | 743 | /* 744 | Calculates characteristic scale for each feature in an array. 745 | 746 | @param features array of features 747 | @param sigma amount of Gaussian smoothing per octave of scale space 748 | @param intvls intervals per octave of scale space 749 | */ 750 | static void calc_feature_scales( CvSeq* features, double sigma, int intvls ) 751 | { 752 | struct feature* feat; 753 | struct detection_data* ddata; 754 | double intvl; 755 | int i, n; 756 | 757 | n = features->total; 758 | for( i = 0; i < n; i++ ) 759 | { 760 | feat = CV_GET_SEQ_ELEM( struct feature, features, i ); 761 | ddata = feat_detection_data( feat ); 762 | intvl = ddata->intvl + ddata->subintvl; 763 | feat->scl = sigma * pow( 2.0, ddata->octv + intvl / intvls ); 764 | ddata->scl_octv = sigma * pow( 2.0, intvl / intvls ); 765 | } 766 | } 767 | 768 | 769 | 770 | /* 771 | Halves feature coordinates and scale in case the input image was doubled 772 | prior to scale space construction. 773 | 774 | @param features array of features 775 | */ 776 | static void adjust_for_img_dbl( CvSeq* features ) 777 | { 778 | struct feature* feat; 779 | int i, n; 780 | 781 | n = features->total; 782 | for( i = 0; i < n; i++ ) 783 | { 784 | feat = CV_GET_SEQ_ELEM( struct feature, features, i ); 785 | feat->x /= 2.0; 786 | feat->y /= 2.0; 787 | feat->scl /= 2.0; 788 | feat->img_pt.x /= 2.0; 789 | feat->img_pt.y /= 2.0; 790 | } 791 | } 792 | 793 | 794 | 795 | /* 796 | Computes a canonical orientation for each image feature in an array. Based 797 | on Section 5 of Lowe's paper. This function adds features to the array when 798 | there is more than one dominant orientation at a given feature location. 799 | 800 | @param features an array of image features 801 | @param gauss_pyr Gaussian scale space pyramid 802 | */ 803 | static void calc_feature_oris( CvSeq* features, IplImage*** gauss_pyr ) 804 | { 805 | struct feature* feat; 806 | struct detection_data* ddata; 807 | double* hist; 808 | double omax; 809 | int i, j, n = features->total; 810 | 811 | for( i = 0; i < n; i++ ) 812 | { 813 | feat = malloc( sizeof( struct feature ) ); 814 | cvSeqPopFront( features, feat ); 815 | ddata = feat_detection_data( feat ); 816 | hist = ori_hist( gauss_pyr[ddata->octv][ddata->intvl], 817 | ddata->r, ddata->c, SIFT_ORI_HIST_BINS, 818 | cvRound( SIFT_ORI_RADIUS * ddata->scl_octv ), 819 | SIFT_ORI_SIG_FCTR * ddata->scl_octv ); 820 | for( j = 0; j < SIFT_ORI_SMOOTH_PASSES; j++ ) 821 | smooth_ori_hist( hist, SIFT_ORI_HIST_BINS ); 822 | omax = dominant_ori( hist, SIFT_ORI_HIST_BINS ); 823 | add_good_ori_features( features, hist, SIFT_ORI_HIST_BINS, 824 | omax * SIFT_ORI_PEAK_RATIO, feat ); 825 | free( ddata ); 826 | free( feat ); 827 | free( hist ); 828 | } 829 | } 830 | 831 | 832 | 833 | /* 834 | Computes a gradient orientation histogram at a specified pixel. 835 | 836 | @param img image 837 | @param r pixel row 838 | @param c pixel col 839 | @param n number of histogram bins 840 | @param rad radius of region over which histogram is computed 841 | @param sigma std for Gaussian weighting of histogram entries 842 | 843 | @return Returns an n-element array containing an orientation histogram 844 | representing orientations between 0 and 2 PI. 845 | */ 846 | static double* ori_hist( IplImage* img, int r, int c, int n, int rad, 847 | double sigma ) 848 | { 849 | double* hist; 850 | double mag, ori, w, exp_denom, PI2 = CV_PI * 2.0; 851 | int bin, i, j; 852 | 853 | hist = calloc( n, sizeof( double ) ); 854 | exp_denom = 2.0 * sigma * sigma; 855 | for( i = -rad; i <= rad; i++ ) 856 | for( j = -rad; j <= rad; j++ ) 857 | if( calc_grad_mag_ori( img, r + i, c + j, &mag, &ori ) ) 858 | { 859 | w = exp( -( i*i + j*j ) / exp_denom ); 860 | bin = cvRound( n * ( ori + CV_PI ) / PI2 ); 861 | bin = ( bin < n )? bin : 0; 862 | hist[bin] += w * mag; 863 | } 864 | 865 | return hist; 866 | } 867 | 868 | 869 | 870 | /* 871 | Calculates the gradient magnitude and orientation at a given pixel. 872 | 873 | @param img image 874 | @param r pixel row 875 | @param c pixel col 876 | @param mag output as gradient magnitude at pixel (r,c) 877 | @param ori output as gradient orientation at pixel (r,c) 878 | 879 | @return Returns 1 if the specified pixel is a valid one and sets mag and 880 | ori accordingly; otherwise returns 0 881 | */ 882 | static int calc_grad_mag_ori( IplImage* img, int r, int c, double* mag, 883 | double* ori ) 884 | { 885 | double dx, dy; 886 | 887 | if( r > 0 && r < img->height - 1 && c > 0 && c < img->width - 1 ) 888 | { 889 | dx = pixval32f( img, r, c+1 ) - pixval32f( img, r, c-1 ); 890 | dy = pixval32f( img, r-1, c ) - pixval32f( img, r+1, c ); 891 | *mag = sqrt( dx*dx + dy*dy ); 892 | *ori = atan2( dy, dx ); 893 | return 1; 894 | } 895 | 896 | else 897 | return 0; 898 | } 899 | 900 | 901 | 902 | /* 903 | Gaussian smooths an orientation histogram. 904 | 905 | @param hist an orientation histogram 906 | @param n number of bins 907 | */ 908 | static void smooth_ori_hist( double* hist, int n ) 909 | { 910 | double prev, tmp, h0 = hist[0]; 911 | int i; 912 | 913 | prev = hist[n-1]; 914 | for( i = 0; i < n; i++ ) 915 | { 916 | tmp = hist[i]; 917 | hist[i] = 0.25 * prev + 0.5 * hist[i] + 918 | 0.25 * ( ( i+1 == n )? h0 : hist[i+1] ); 919 | prev = tmp; 920 | } 921 | } 922 | 923 | 924 | 925 | /* 926 | Finds the magnitude of the dominant orientation in a histogram 927 | 928 | @param hist an orientation histogram 929 | @param n number of bins 930 | 931 | @return Returns the value of the largest bin in hist 932 | */ 933 | static double dominant_ori( double* hist, int n ) 934 | { 935 | double omax; 936 | int maxbin, i; 937 | 938 | omax = hist[0]; 939 | maxbin = 0; 940 | for( i = 1; i < n; i++ ) 941 | if( hist[i] > omax ) 942 | { 943 | omax = hist[i]; 944 | maxbin = i; 945 | } 946 | return omax; 947 | } 948 | 949 | 950 | 951 | /* 952 | Interpolates a histogram peak from left, center, and right values 953 | */ 954 | #define interp_hist_peak( l, c, r ) ( 0.5 * ((l)-(r)) / ((l) - 2.0*(c) + (r)) ) 955 | 956 | 957 | 958 | /* 959 | Adds features to an array for every orientation in a histogram greater than 960 | a specified threshold. 961 | 962 | @param features new features are added to the end of this array 963 | @param hist orientation histogram 964 | @param n number of bins in hist 965 | @param mag_thr new features are added for entries in hist greater than this 966 | @param feat new features are clones of this with different orientations 967 | */ 968 | static void add_good_ori_features( CvSeq* features, double* hist, int n, 969 | double mag_thr, struct feature* feat ) 970 | { 971 | struct feature* new_feat; 972 | double bin, PI2 = CV_PI * 2.0; 973 | int l, r, i; 974 | 975 | for( i = 0; i < n; i++ ) 976 | { 977 | l = ( i == 0 )? n - 1 : i-1; 978 | r = ( i + 1 ) % n; 979 | 980 | if( hist[i] > hist[l] && hist[i] > hist[r] && hist[i] >= mag_thr ) 981 | { 982 | bin = i + interp_hist_peak( hist[l], hist[i], hist[r] ); 983 | bin = ( bin < 0 )? n + bin : ( bin >= n )? bin - n : bin; 984 | new_feat = clone_feature( feat ); 985 | new_feat->ori = ( ( PI2 * bin ) / n ) - CV_PI; 986 | cvSeqPush( features, new_feat ); 987 | free( new_feat ); 988 | } 989 | } 990 | } 991 | 992 | 993 | 994 | /* 995 | Makes a deep copy of a feature 996 | 997 | @param feat feature to be cloned 998 | 999 | @return Returns a deep copy of feat 1000 | */ 1001 | static struct feature* clone_feature( struct feature* feat ) 1002 | { 1003 | struct feature* new_feat; 1004 | struct detection_data* ddata; 1005 | 1006 | new_feat = new_feature(); 1007 | ddata = feat_detection_data( new_feat ); 1008 | memcpy( new_feat, feat, sizeof( struct feature ) ); 1009 | memcpy( ddata, feat_detection_data(feat), sizeof( struct detection_data ) ); 1010 | new_feat->feature_data = ddata; 1011 | 1012 | return new_feat; 1013 | } 1014 | 1015 | 1016 | 1017 | /* 1018 | Computes feature descriptors for features in an array. Based on Section 6 1019 | of Lowe's paper. 1020 | 1021 | @param features array of features 1022 | @param gauss_pyr Gaussian scale space pyramid 1023 | @param d width of 2D array of orientation histograms 1024 | @param n number of bins per orientation histogram 1025 | */ 1026 | static void compute_descriptors( CvSeq* features, IplImage*** gauss_pyr, int d, 1027 | int n ) 1028 | { 1029 | struct feature* feat; 1030 | struct detection_data* ddata; 1031 | double*** hist; 1032 | int i, k = features->total; 1033 | 1034 | for( i = 0; i < k; i++ ) 1035 | { 1036 | feat = CV_GET_SEQ_ELEM( struct feature, features, i ); 1037 | ddata = feat_detection_data( feat ); 1038 | hist = descr_hist( gauss_pyr[ddata->octv][ddata->intvl], ddata->r, 1039 | ddata->c, feat->ori, ddata->scl_octv, d, n ); 1040 | hist_to_descr( hist, d, n, feat ); 1041 | release_descr_hist( &hist, d ); 1042 | } 1043 | } 1044 | 1045 | 1046 | 1047 | /* 1048 | Computes the 2D array of orientation histograms that form the feature 1049 | descriptor. Based on Section 6.1 of Lowe's paper. 1050 | 1051 | @param img image used in descriptor computation 1052 | @param r row coord of center of orientation histogram array 1053 | @param c column coord of center of orientation histogram array 1054 | @param ori canonical orientation of feature whose descr is being computed 1055 | @param scl scale relative to img of feature whose descr is being computed 1056 | @param d width of 2d array of orientation histograms 1057 | @param n bins per orientation histogram 1058 | 1059 | @return Returns a d x d array of n-bin orientation histograms. 1060 | */ 1061 | static double*** descr_hist( IplImage* img, int r, int c, double ori, 1062 | double scl, int d, int n ) 1063 | { 1064 | double*** hist; 1065 | double cos_t, sin_t, hist_width, exp_denom, r_rot, c_rot, grad_mag, 1066 | grad_ori, w, rbin, cbin, obin, bins_per_rad, PI2 = 2.0 * CV_PI; 1067 | int radius, i, j; 1068 | 1069 | hist = calloc( d, sizeof( double** ) ); 1070 | for( i = 0; i < d; i++ ) 1071 | { 1072 | hist[i] = calloc( d, sizeof( double* ) ); 1073 | for( j = 0; j < d; j++ ) 1074 | hist[i][j] = calloc( n, sizeof( double ) ); 1075 | } 1076 | 1077 | cos_t = cos( ori ); 1078 | sin_t = sin( ori ); 1079 | bins_per_rad = n / PI2; 1080 | exp_denom = d * d * 0.5; 1081 | hist_width = SIFT_DESCR_SCL_FCTR * scl; 1082 | radius = hist_width * sqrt(2) * ( d + 1.0 ) * 0.5 + 0.5; 1083 | for( i = -radius; i <= radius; i++ ) 1084 | for( j = -radius; j <= radius; j++ ) 1085 | { 1086 | /* 1087 | Calculate sample's histogram array coords rotated relative to ori. 1088 | Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e. 1089 | r_rot = 1.5) have full weight placed in row 1 after interpolation. 1090 | */ 1091 | c_rot = ( j * cos_t - i * sin_t ) / hist_width; 1092 | r_rot = ( j * sin_t + i * cos_t ) / hist_width; 1093 | rbin = r_rot + d / 2 - 0.5; 1094 | cbin = c_rot + d / 2 - 0.5; 1095 | 1096 | if( rbin > -1.0 && rbin < d && cbin > -1.0 && cbin < d ) 1097 | if( calc_grad_mag_ori( img, r + i, c + j, &grad_mag, &grad_ori )) 1098 | { 1099 | grad_ori -= ori; 1100 | while( grad_ori < 0.0 ) 1101 | grad_ori += PI2; 1102 | while( grad_ori >= PI2 ) 1103 | grad_ori -= PI2; 1104 | 1105 | obin = grad_ori * bins_per_rad; 1106 | w = exp( -(c_rot * c_rot + r_rot * r_rot) / exp_denom ); 1107 | interp_hist_entry( hist, rbin, cbin, obin, grad_mag * w, d, n ); 1108 | } 1109 | } 1110 | 1111 | return hist; 1112 | } 1113 | 1114 | 1115 | 1116 | /* 1117 | Interpolates an entry into the array of orientation histograms that form 1118 | the feature descriptor. 1119 | 1120 | @param hist 2D array of orientation histograms 1121 | @param rbin sub-bin row coordinate of entry 1122 | @param cbin sub-bin column coordinate of entry 1123 | @param obin sub-bin orientation coordinate of entry 1124 | @param mag size of entry 1125 | @param d width of 2D array of orientation histograms 1126 | @param n number of bins per orientation histogram 1127 | */ 1128 | static void interp_hist_entry( double*** hist, double rbin, double cbin, 1129 | double obin, double mag, int d, int n ) 1130 | { 1131 | double d_r, d_c, d_o, v_r, v_c, v_o; 1132 | double** row, * h; 1133 | int r0, c0, o0, rb, cb, ob, r, c, o; 1134 | 1135 | r0 = cvFloor( rbin ); 1136 | c0 = cvFloor( cbin ); 1137 | o0 = cvFloor( obin ); 1138 | d_r = rbin - r0; 1139 | d_c = cbin - c0; 1140 | d_o = obin - o0; 1141 | 1142 | /* 1143 | The entry is distributed into up to 8 bins. Each entry into a bin 1144 | is multiplied by a weight of 1 - d for each dimension, where d is the 1145 | distance from the center value of the bin measured in bin units. 1146 | */ 1147 | for( r = 0; r <= 1; r++ ) 1148 | { 1149 | rb = r0 + r; 1150 | if( rb >= 0 && rb < d ) 1151 | { 1152 | v_r = mag * ( ( r == 0 )? 1.0 - d_r : d_r ); 1153 | row = hist[rb]; 1154 | for( c = 0; c <= 1; c++ ) 1155 | { 1156 | cb = c0 + c; 1157 | if( cb >= 0 && cb < d ) 1158 | { 1159 | v_c = v_r * ( ( c == 0 )? 1.0 - d_c : d_c ); 1160 | h = row[cb]; 1161 | for( o = 0; o <= 1; o++ ) 1162 | { 1163 | ob = ( o0 + o ) % n; 1164 | v_o = v_c * ( ( o == 0 )? 1.0 - d_o : d_o ); 1165 | h[ob] += v_o; 1166 | } 1167 | } 1168 | } 1169 | } 1170 | } 1171 | } 1172 | 1173 | 1174 | 1175 | /* 1176 | Converts the 2D array of orientation histograms into a feature's descriptor 1177 | vector. 1178 | 1179 | @param hist 2D array of orientation histograms 1180 | @param d width of hist 1181 | @param n bins per histogram 1182 | @param feat feature into which to store descriptor 1183 | */ 1184 | static void hist_to_descr( double*** hist, int d, int n, struct feature* feat ) 1185 | { 1186 | int int_val, i, r, c, o, k = 0; 1187 | 1188 | for( r = 0; r < d; r++ ) 1189 | for( c = 0; c < d; c++ ) 1190 | for( o = 0; o < n; o++ ) 1191 | feat->descr[k++] = hist[r][c][o]; 1192 | 1193 | feat->d = k; 1194 | normalize_descr( feat ); 1195 | for( i = 0; i < k; i++ ) 1196 | if( feat->descr[i] > SIFT_DESCR_MAG_THR ) 1197 | feat->descr[i] = SIFT_DESCR_MAG_THR; 1198 | normalize_descr( feat ); 1199 | 1200 | /* convert floating-point descriptor to integer valued descriptor */ 1201 | for( i = 0; i < k; i++ ) 1202 | { 1203 | int_val = SIFT_INT_DESCR_FCTR * feat->descr[i]; 1204 | feat->descr[i] = MIN( 255, int_val ); 1205 | } 1206 | } 1207 | 1208 | 1209 | 1210 | /* 1211 | Normalizes a feature's descriptor vector to unitl length 1212 | 1213 | @param feat feature 1214 | */ 1215 | static void normalize_descr( struct feature* feat ) 1216 | { 1217 | double cur, len_inv, len_sq = 0.0; 1218 | int i, d = feat->d; 1219 | 1220 | for( i = 0; i < d; i++ ) 1221 | { 1222 | cur = feat->descr[i]; 1223 | len_sq += cur*cur; 1224 | } 1225 | len_inv = 1.0 / sqrt( len_sq ); 1226 | for( i = 0; i < d; i++ ) 1227 | feat->descr[i] *= len_inv; 1228 | } 1229 | 1230 | 1231 | 1232 | /* 1233 | Compares features for a decreasing-scale ordering. Intended for use with 1234 | CvSeqSort 1235 | 1236 | @param feat1 first feature 1237 | @param feat2 second feature 1238 | @param param unused 1239 | 1240 | @return Returns 1 if feat1's scale is greater than feat2's, -1 if vice versa, 1241 | and 0 if their scales are equal 1242 | */ 1243 | static int feature_cmp( void* feat1, void* feat2, void* param ) 1244 | { 1245 | struct feature* f1 = (struct feature*) feat1; 1246 | struct feature* f2 = (struct feature*) feat2; 1247 | 1248 | if( f1->scl < f2->scl ) 1249 | return 1; 1250 | if( f1->scl > f2->scl ) 1251 | return -1; 1252 | return 0; 1253 | } 1254 | 1255 | 1256 | 1257 | /* 1258 | De-allocates memory held by a descriptor histogram 1259 | 1260 | @param hist pointer to a 2D array of orientation histograms 1261 | @param d width of hist 1262 | */ 1263 | static void release_descr_hist( double**** hist, int d ) 1264 | { 1265 | int i, j; 1266 | 1267 | for( i = 0; i < d; i++) 1268 | { 1269 | for( j = 0; j < d; j++ ) 1270 | free( (*hist)[i][j] ); 1271 | free( (*hist)[i] ); 1272 | } 1273 | free( *hist ); 1274 | *hist = NULL; 1275 | } 1276 | 1277 | 1278 | /* 1279 | De-allocates memory held by a scale space pyramid 1280 | 1281 | @param pyr scale space pyramid 1282 | @param octvs number of octaves of scale space 1283 | @param n number of images per octave 1284 | */ 1285 | static void release_pyr( IplImage**** pyr, int octvs, int n ) 1286 | { 1287 | int i, j; 1288 | for( i = 0; i < octvs; i++ ) 1289 | { 1290 | for( j = 0; j < n; j++ ) 1291 | cvReleaseImage( &(*pyr)[i][j] ); 1292 | free( (*pyr)[i] ); 1293 | } 1294 | free( *pyr ); 1295 | *pyr = NULL; 1296 | } 1297 | -------------------------------------------------------------------------------- /beaver.sift: -------------------------------------------------------------------------------- 1 | 118 128 2 | 101.261937 136.056398 40.126680 0.773786 3 | 0 0 0 0 3 1 0 0 2 23 46 15 18 3 0 0 6 20 13 1 4 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 88 36 0 0 5 | 82 94 55 46 186 115 2 7 186 152 19 6 19 6 1 23 22 0 0 0 6 | 0 0 0 1 0 0 0 0 37 8 0 0 91 12 0 1 186 145 11 36 7 | 186 48 0 0 23 28 8 97 40 1 0 0 0 0 0 4 0 0 0 0 8 | 0 0 0 0 12 5 0 0 4 2 0 0 50 20 0 0 1 0 0 1 9 | 0 0 0 0 0 0 0 0 10 | 127.661997 70.976249 15.728593 -2.028727 11 | 1 2 2 72 61 12 1 2 133 97 1 4 2 7 4 44 133 116 0 0 12 | 0 0 0 19 8 4 0 0 0 0 0 0 23 0 1 9 104 20 1 8 13 | 133 5 0 0 0 1 5 133 133 15 0 0 0 0 8 133 14 1 0 0 14 | 0 0 0 8 27 0 0 0 124 37 8 21 133 48 0 0 0 0 2 51 15 | 130 42 0 0 0 0 2 36 1 0 0 0 0 0 0 2 2 0 0 0 16 | 35 104 79 24 113 16 0 0 0 1 53 64 80 22 0 0 0 0 0 4 17 | 0 0 0 0 0 0 0 0 18 | 127.003926 228.102495 13.242063 1.237457 19 | 0 0 0 3 135 98 0 0 7 25 2 1 48 30 8 20 38 12 2 0 20 | 0 0 16 130 89 0 0 0 0 0 1 116 3 0 0 92 135 22 0 1 21 | 83 5 0 24 65 15 31 135 23 1 0 0 0 0 75 135 7 0 0 0 22 | 0 0 8 76 12 3 0 47 135 14 0 1 135 32 0 11 35 5 1 43 23 | 110 6 0 0 0 0 4 34 0 0 0 0 0 0 0 1 3 0 0 0 24 | 62 59 13 8 135 1 0 0 14 29 21 99 59 0 0 0 0 0 0 17 25 | 0 0 0 0 0 0 0 0 26 | 127.003926 228.102495 13.242063 0.308032 27 | 13 28 14 4 0 0 0 0 145 105 4 1 0 0 0 4 81 19 0 0 28 | 0 0 0 2 0 0 0 0 0 0 0 0 23 7 28 5 1 36 48 4 29 | 145 54 6 1 0 4 4 29 104 43 7 0 0 0 0 9 0 2 0 0 30 | 0 0 0 0 6 1 0 0 27 145 118 2 77 145 42 1 34 108 9 3 31 | 25 145 79 0 0 3 1 0 8 71 3 0 0 0 0 0 0 0 0 0 32 | 26 91 4 0 0 13 8 0 25 145 43 0 50 126 13 0 0 51 79 28 33 | 38 57 0 0 0 0 5 12 34 | 175.383740 179.015481 12.917228 -1.697463 35 | 0 0 13 80 129 0 0 0 82 4 1 57 134 2 1 44 134 4 0 0 36 | 0 0 0 62 0 0 0 0 0 0 0 0 8 3 122 134 28 1 0 1 37 | 134 6 12 52 38 2 2 134 100 0 0 0 0 0 0 134 0 0 0 0 38 | 0 0 0 0 23 5 18 24 18 26 1 1 134 61 2 1 2 3 0 29 39 | 126 23 0 0 0 0 0 19 0 0 0 0 0 0 0 0 8 0 1 3 40 | 8 8 17 27 134 18 0 0 1 2 26 61 134 14 0 0 0 0 0 6 41 | 1 0 0 0 0 0 0 0 42 | 175.383740 179.015481 12.917228 -2.135009 43 | 12 19 5 17 138 60 0 0 138 92 3 2 14 9 0 4 37 6 0 0 44 | 0 0 0 1 0 0 0 0 0 0 0 0 10 2 1 90 138 67 0 3 45 | 138 35 1 9 37 16 1 53 118 15 0 0 0 0 0 18 0 0 0 0 46 | 0 0 0 0 5 8 5 138 39 9 3 0 138 138 4 16 6 6 1 5 47 | 97 138 0 0 0 0 0 1 4 9 0 0 0 0 0 0 0 0 0 17 48 | 21 20 7 0 46 58 0 0 2 21 9 24 138 138 0 0 0 0 2 31 49 | 32 31 0 0 0 0 0 2 50 | 81.296224 20.255786 10.339179 -1.823617 51 | 19 4 1 30 74 11 8 11 90 0 0 0 0 26 32 27 165 12 0 0 52 | 0 1 3 45 59 9 0 0 0 0 9 13 28 0 0 1 69 108 38 60 53 | 78 1 0 0 15 48 57 70 165 64 0 0 2 5 11 64 95 32 0 0 54 | 0 0 0 10 1 0 0 0 17 165 165 37 9 0 0 0 4 51 165 59 55 | 107 5 0 0 0 4 51 133 36 1 0 0 0 0 0 17 0 0 0 0 56 | 0 16 27 0 0 0 0 0 0 2 15 0 0 0 0 0 0 0 0 0 57 | 0 0 0 0 0 0 0 0 58 | 52.486139 113.209899 9.837588 1.318715 59 | 0 15 18 21 109 0 0 0 15 12 15 22 30 3 0 9 170 35 1 1 60 | 3 1 0 61 132 11 0 0 0 0 0 7 0 7 12 34 135 1 1 0 61 | 68 25 29 3 4 2 5 15 170 11 0 0 0 0 0 112 124 5 0 0 62 | 0 0 0 32 4 1 3 27 44 5 4 6 73 6 1 0 1 1 24 43 63 | 170 62 0 0 0 0 2 23 125 11 0 0 0 0 0 21 18 12 2 1 64 | 2 5 8 7 43 7 5 0 0 0 4 21 170 18 0 0 0 0 0 57 65 | 119 5 0 0 0 0 0 15 66 | 106.406048 163.273684 9.236157 1.992948 67 | 0 0 2 139 139 0 0 0 14 20 46 69 23 0 0 6 8 16 42 21 68 | 4 1 0 5 0 1 4 23 5 5 6 3 3 0 3 139 139 0 0 0 69 | 69 6 1 18 17 0 0 24 18 3 0 1 5 13 9 21 2 0 0 0 70 | 1 6 24 29 2 0 9 63 25 30 95 19 49 18 0 1 2 30 85 8 71 | 15 24 8 0 0 3 38 22 13 5 0 0 0 0 26 114 4 0 0 0 72 | 0 37 139 88 1 0 0 0 0 107 139 2 1 1 0 0 0 17 139 68 73 | 0 0 0 0 0 0 128 139 74 | 31.847418 28.193391 9.136873 1.966715 75 | 4 51 84 61 5 0 0 0 5 109 18 0 0 0 0 0 1 25 5 0 76 | 0 0 0 0 0 0 0 0 0 0 0 0 34 49 37 62 10 0 0 9 77 | 39 147 116 15 0 0 0 1 147 147 27 0 0 0 0 1 28 8 0 0 78 | 0 0 0 0 13 27 35 41 1 0 0 1 145 39 56 40 1 0 0 41 79 | 147 41 3 0 0 0 0 91 36 1 0 0 0 0 0 4 6 12 5 13 80 | 5 1 12 38 99 1 2 5 2 0 19 147 147 0 0 0 0 0 0 147 81 | 3 0 0 0 0 0 0 1 82 | 96.154790 70.196074 8.885212 -1.881855 83 | 0 3 28 16 25 4 2 0 48 12 3 2 7 2 2 14 66 1 0 0 84 | 0 4 44 167 49 10 0 0 0 36 143 103 3 4 7 6 27 23 37 2 85 | 117 18 1 0 0 18 19 13 167 13 0 0 0 0 6 163 39 0 0 0 86 | 0 12 65 67 35 12 1 6 8 9 14 11 95 4 0 0 2 50 26 14 87 | 167 52 0 0 0 2 2 13 58 21 0 0 0 0 0 2 49 2 0 0 88 | 7 19 12 41 53 4 0 1 37 36 20 27 167 86 0 0 4 3 4 31 89 | 66 45 0 0 0 0 0 9 90 | 156.101430 97.870948 6.787204 -1.687244 91 | 0 0 0 0 24 12 23 21 131 8 0 0 0 2 21 61 149 25 0 0 92 | 0 0 0 7 21 2 0 0 0 0 0 0 2 0 0 0 1 8 76 110 93 | 81 14 0 0 7 112 82 61 149 63 0 0 1 13 5 30 38 6 0 0 94 | 0 0 0 1 23 0 0 0 0 7 42 149 13 0 0 0 2 149 133 88 95 | 141 2 0 0 0 35 68 147 23 0 0 0 0 0 0 9 73 0 0 0 96 | 0 0 2 90 0 0 0 0 0 20 28 10 1 0 0 0 0 4 26 22 97 | 0 0 0 0 0 0 0 2 98 | 48.675527 33.499496 5.357871 -1.994409 99 | 2 8 12 5 13 13 14 6 62 42 4 0 0 1 14 12 44 9 0 0 100 | 4 21 46 13 7 1 2 7 14 51 21 3 6 8 0 0 36 85 22 16 101 | 60 16 1 0 1 8 26 18 132 11 0 3 24 24 9 25 10 10 11 22 102 | 107 51 7 10 1 10 1 0 65 132 8 1 40 20 3 0 4 46 26 84 103 | 103 16 6 15 18 11 62 79 7 5 4 27 92 34 94 56 0 0 0 0 104 | 10 132 41 0 7 0 0 0 0 124 132 107 3 0 0 0 0 8 132 132 105 | 0 0 0 0 1 11 132 38 106 | 24.219541 71.627231 5.094817 1.358239 107 | 1 8 22 11 15 6 0 0 114 91 12 6 5 5 0 0 160 139 0 0 108 | 0 0 0 0 21 7 0 0 0 0 0 0 0 3 24 10 32 21 8 1 109 | 128 46 16 3 0 3 8 8 160 65 0 0 0 0 0 7 60 4 0 0 110 | 0 0 0 0 0 0 1 5 30 21 8 1 115 2 1 2 1 6 9 92 111 | 160 12 0 0 0 0 0 112 63 11 0 0 0 0 0 0 4 3 6 3 112 | 12 7 6 6 140 1 0 0 0 1 6 143 134 22 0 0 1 2 0 102 113 | 37 36 0 0 0 0 0 0 114 | 44.574690 134.544945 4.821858 1.561343 115 | 0 6 4 0 0 28 32 2 113 13 4 0 0 2 14 55 174 3 0 0 116 | 1 3 1 147 20 1 0 0 5 17 10 40 0 0 0 0 1 32 31 5 117 | 166 14 0 0 4 17 16 22 174 33 0 0 0 3 3 24 32 0 0 0 118 | 1 8 18 37 2 0 0 1 10 15 5 3 146 2 0 0 18 15 1 22 119 | 174 5 0 0 0 0 1 52 46 0 0 0 0 0 8 17 2 0 7 17 120 | 6 0 0 0 130 0 1 9 9 0 0 64 174 0 0 0 0 0 0 105 121 | 25 0 0 0 0 0 0 5 122 | 93.410508 126.299074 4.651622 -1.594368 123 | 2 3 4 23 60 1 1 2 3 15 12 97 97 0 0 0 96 114 15 21 124 | 8 0 0 0 19 16 1 17 122 4 0 0 0 13 24 70 63 0 0 0 125 | 30 6 4 113 122 7 0 2 122 28 5 19 15 1 0 26 33 6 1 54 126 | 122 5 1 3 0 73 94 39 25 1 0 0 26 12 7 22 112 60 2 4 127 | 122 61 0 1 18 20 3 12 34 12 0 8 58 54 56 8 8 107 27 0 128 | 0 25 11 1 17 77 4 1 22 29 6 2 104 39 0 0 7 22 26 63 129 | 95 11 0 0 0 7 56 93 130 | 141.056795 95.863733 4.575146 2.155047 131 | 0 1 48 134 2 0 0 0 35 103 113 43 0 0 0 0 37 50 4 0 132 | 0 0 0 0 0 1 1 0 0 0 0 0 13 4 31 142 64 0 0 3 133 | 142 81 48 43 0 0 0 19 133 49 72 1 0 0 0 3 0 2 96 18 134 | 0 0 0 0 23 5 4 108 55 2 2 19 142 87 69 3 0 0 0 25 135 | 23 50 142 18 0 0 0 0 0 2 142 35 0 0 0 0 3 16 47 13 136 | 1 0 0 2 3 35 90 0 0 0 0 1 1 41 83 4 0 0 0 0 137 | 0 6 47 31 3 0 0 0 138 | 110.202989 64.528948 4.053396 -1.858899 139 | 26 61 0 0 0 0 0 6 169 29 0 0 0 0 0 79 128 0 0 0 140 | 0 0 0 118 3 0 0 0 0 0 0 11 61 21 0 0 2 10 2 12 141 | 169 21 0 0 0 0 0 61 169 3 0 0 0 0 0 38 2 0 0 0 142 | 0 0 0 1 61 1 0 0 9 11 2 5 169 46 0 0 0 0 0 7 143 | 169 28 0 0 0 0 0 3 1 0 0 0 0 0 0 0 38 3 0 0 144 | 13 19 0 1 169 13 0 0 0 0 0 6 169 11 2 0 0 0 0 1 145 | 1 2 5 0 0 0 0 0 146 | 35.932956 47.192777 3.925995 2.814829 147 | 10 6 63 33 0 0 0 15 5 12 135 18 0 0 0 3 1 19 131 3 148 | 0 0 5 4 30 70 52 4 0 0 0 2 11 8 38 54 18 3 0 0 149 | 80 21 25 6 0 0 2 21 11 5 11 40 4 1 15 18 10 53 27 39 150 | 5 1 5 23 13 3 2 12 24 16 4 8 62 32 14 4 0 1 12 23 151 | 5 8 21 52 3 0 57 83 3 1 2 18 1 0 104 142 2 0 2 6 152 | 3 14 142 53 3 2 1 0 0 0 142 142 0 0 0 0 0 0 142 142 153 | 0 0 0 0 0 0 113 142 154 | 60.123173 59.999791 3.649987 -1.614283 155 | 32 24 12 0 0 2 20 31 70 28 5 3 1 4 6 35 4 14 24 26 156 | 10 16 7 2 3 12 4 1 40 39 2 0 53 104 55 6 1 3 11 12 157 | 135 63 1 0 3 29 21 44 21 9 5 6 39 135 24 14 17 14 1 1 158 | 44 21 8 7 57 12 2 1 1 52 104 34 135 17 0 0 1 12 14 69 159 | 60 1 0 0 17 135 76 35 1 0 0 0 47 135 58 5 20 5 0 2 160 | 27 52 28 17 135 0 0 0 2 5 8 132 68 0 4 25 41 58 48 81 161 | 0 6 34 42 69 116 16 0 162 | 94.894371 15.155497 3.332966 -2.503603 163 | 107 145 2 0 0 0 0 0 23 54 22 113 32 0 0 0 96 45 33 56 164 | 11 0 0 9 61 8 0 0 0 0 0 9 64 127 39 5 0 1 3 2 165 | 43 78 107 145 10 0 0 0 145 27 18 46 3 0 0 39 66 0 0 0 166 | 0 0 0 16 16 41 56 6 0 2 0 15 72 23 31 30 7 11 26 46 167 | 145 13 1 3 1 2 6 37 52 4 0 0 0 0 0 2 67 8 2 0 168 | 13 17 4 43 57 1 0 0 0 4 46 119 135 0 0 0 0 0 18 100 169 | 14 0 0 0 0 0 0 0 170 | 143.431022 210.258109 3.297804 0.609261 171 | 2 2 2 0 0 1 30 35 2 2 5 0 0 4 65 26 151 0 0 0 172 | 0 0 15 151 124 0 0 0 0 0 0 106 5 15 57 8 1 0 9 12 173 | 46 35 63 0 0 2 15 12 151 18 2 0 0 0 4 123 111 0 0 0 174 | 0 0 0 52 20 13 7 3 10 4 0 5 77 27 4 0 1 7 7 8 175 | 151 151 0 0 0 0 0 21 69 80 1 0 0 0 0 9 9 11 0 0 176 | 22 10 0 0 23 35 2 3 4 1 0 0 36 151 10 2 4 2 0 0 177 | 7 151 29 0 0 0 0 0 178 | 64.211433 26.529289 3.296519 1.514347 179 | 2 25 102 70 0 0 0 0 11 32 123 49 0 0 2 26 9 123 93 0 180 | 3 12 29 43 1 105 110 19 4 5 2 0 17 3 41 44 30 21 0 7 181 | 123 18 19 18 5 1 8 123 21 7 4 67 97 36 60 123 2 37 39 39 182 | 56 14 2 0 34 6 0 0 28 89 18 9 123 73 14 17 7 18 14 56 183 | 25 22 17 123 110 0 0 10 3 0 7 63 68 0 0 0 31 45 1 0 184 | 0 22 21 6 46 27 10 5 1 9 27 38 8 6 6 48 77 0 0 1 185 | 7 3 1 22 57 0 0 0 186 | 64.211433 26.529289 3.296519 0.905740 187 | 0 0 78 131 7 0 0 0 33 9 131 51 0 5 13 35 5 8 102 32 188 | 18 47 24 17 0 3 21 65 40 5 0 0 18 9 25 131 57 0 0 0 189 | 131 48 18 15 15 10 10 79 41 8 3 8 131 97 18 33 1 3 1 13 190 | 84 40 0 0 14 10 5 47 30 40 18 0 131 131 19 7 7 19 15 8 191 | 32 109 40 29 96 26 0 3 0 1 0 1 91 78 0 0 2 8 3 3 192 | 19 47 35 4 23 52 9 0 0 24 83 31 41 46 27 7 6 1 10 27 193 | 0 2 14 10 43 29 0 0 194 | 31.434813 86.749534 3.250091 0.914083 195 | 9 4 5 3 6 17 8 20 117 79 5 2 4 13 12 16 171 171 0 0 196 | 0 0 0 7 29 87 0 0 0 0 0 1 1 1 10 19 4 8 9 14 197 | 124 19 8 16 3 7 8 10 171 42 0 0 0 0 1 33 51 94 8 1 198 | 6 4 2 12 0 1 5 13 6 23 17 10 98 8 3 4 1 2 30 45 199 | 171 56 0 0 0 0 2 29 59 22 8 3 36 31 2 4 2 0 0 4 200 | 8 11 7 11 26 11 9 3 0 0 8 21 171 126 4 0 0 0 1 3 201 | 92 24 0 0 5 31 6 6 202 | 44.280395 81.235336 3.122882 -1.214778 203 | 1 2 170 170 2 0 0 1 3 38 170 170 11 0 0 0 1 26 44 47 204 | 9 20 1 0 2 3 12 1 2 78 16 0 9 13 94 170 10 11 17 2 205 | 13 54 46 36 13 28 25 5 65 56 39 1 1 2 10 19 21 6 8 0 206 | 1 6 40 24 62 31 37 12 2 7 9 29 14 32 20 0 7 46 55 10 207 | 112 15 0 0 0 11 62 43 46 17 3 1 2 12 35 18 7 18 53 18 208 | 11 29 11 7 60 116 33 1 0 3 10 9 76 32 0 0 2 12 39 36 209 | 11 15 23 9 16 18 9 5 210 | 72.790443 32.929432 2.962420 1.585619 211 | 2 7 26 13 56 39 9 1 12 5 71 63 28 11 0 9 85 17 29 17 212 | 1 0 9 132 12 9 2 20 34 26 74 132 6 4 3 39 80 48 7 2 213 | 69 2 0 2 70 112 11 16 132 56 13 15 5 7 6 132 30 17 17 132 214 | 77 3 9 47 49 48 12 37 6 6 2 2 55 28 0 0 11 109 62 26 215 | 132 45 10 10 2 18 42 74 14 15 12 82 77 0 0 0 48 32 1 3 216 | 7 0 0 0 44 93 10 0 0 2 9 4 11 58 31 8 1 0 8 14 217 | 1 4 7 54 59 0 0 0 218 | 141.176400 197.250896 2.784435 2.101131 219 | 10 32 95 73 2 1 0 0 42 59 12 4 45 25 0 0 1 0 0 19 220 | 75 27 0 0 1 1 0 3 4 4 2 1 24 7 3 2 3 20 29 17 221 | 140 23 0 0 18 39 10 19 11 0 0 2 74 140 10 5 0 0 0 1 222 | 10 22 11 6 10 0 5 25 22 16 38 23 140 13 0 7 27 12 15 36 223 | 25 3 0 14 140 41 2 4 0 1 1 5 38 24 2 0 1 0 3 12 224 | 2 7 140 119 24 0 0 1 10 74 140 76 3 0 0 1 60 140 140 4 225 | 0 0 0 0 12 111 81 0 226 | 114.350641 203.969713 2.722308 0.113737 227 | 1 4 7 7 6 1 0 0 104 23 3 0 0 0 0 2 200 7 0 0 228 | 0 0 0 7 53 4 0 0 0 0 0 1 1 6 11 12 4 0 0 0 229 | 152 27 4 0 0 0 0 1 200 8 0 0 0 0 0 11 67 0 0 0 230 | 0 0 0 3 3 2 0 2 9 2 1 2 172 6 0 0 0 0 0 21 231 | 200 12 0 0 0 0 0 25 60 3 0 0 0 0 0 1 4 0 0 0 232 | 1 0 5 9 99 9 0 0 0 0 3 31 200 85 0 0 0 0 0 4 233 | 51 19 0 0 0 0 0 0 234 | 59.458359 22.033436 2.682051 0.651022 235 | 0 0 89 124 0 0 0 0 0 0 124 123 7 1 6 3 0 18 71 67 236 | 15 5 9 1 0 2 9 39 59 1 0 0 8 3 53 124 8 0 0 0 237 | 115 7 43 30 4 21 43 74 11 7 13 7 45 124 56 20 0 1 3 15 238 | 41 38 0 0 22 12 17 59 32 5 0 0 124 78 8 10 13 11 4 16 239 | 46 17 4 5 124 112 5 6 0 0 0 0 36 103 1 0 9 8 2 30 240 | 44 16 4 0 78 124 35 3 2 7 3 0 15 124 79 23 41 11 0 0 241 | 1 7 5 4 21 43 0 0 242 | 106.357524 72.560325 2.644782 -2.029461 243 | 16 48 14 0 0 0 2 10 42 43 18 0 0 0 4 16 174 17 0 0 244 | 0 0 0 67 81 0 0 0 0 0 0 25 11 63 26 0 0 0 0 0 245 | 77 93 36 0 0 0 3 6 174 56 1 0 0 0 0 16 106 4 0 0 246 | 0 0 0 4 41 15 1 0 0 6 3 9 128 23 1 0 0 0 6 31 247 | 174 77 0 0 0 0 0 46 86 22 0 0 0 0 0 9 11 2 0 0 248 | 3 14 5 7 86 29 0 0 0 0 0 1 174 153 0 0 0 0 0 0 249 | 102 79 0 0 0 0 0 0 250 | 47.600408 174.988387 2.631808 0.955726 251 | 23 15 0 7 14 6 0 0 187 105 0 0 1 0 0 1 115 25 0 0 252 | 0 0 0 0 1 0 0 0 0 0 0 0 28 0 0 9 19 2 0 0 253 | 187 10 0 0 1 0 0 14 187 6 0 0 0 0 0 7 1 0 0 0 254 | 0 0 0 0 29 2 1 9 16 1 0 0 187 18 0 1 1 0 0 10 255 | 187 12 0 0 0 0 0 4 3 2 0 0 0 0 0 0 30 1 1 12 256 | 7 0 0 7 187 1 0 0 0 0 0 64 128 0 0 0 0 0 0 19 257 | 3 1 0 0 0 0 0 0 258 | 129.142246 96.596993 2.593129 -2.554854 259 | 42 3 0 0 0 0 0 22 144 11 0 0 0 0 0 31 168 18 0 0 260 | 0 11 56 62 15 0 0 0 0 62 168 10 34 0 0 0 0 0 1 67 261 | 96 46 11 15 7 0 1 29 168 104 3 1 0 0 0 8 63 5 0 0 262 | 0 2 7 4 9 6 17 21 5 0 1 61 106 6 5 58 22 0 1 34 263 | 168 12 1 1 0 0 0 62 70 2 0 0 0 0 0 5 21 20 34 8 264 | 0 0 0 4 92 13 3 9 2 0 5 41 168 61 0 0 0 0 0 19 265 | 51 14 0 0 0 0 0 0 266 | 129.932802 207.389374 2.543309 0.298207 267 | 1 0 0 1 3 1 1 4 78 0 0 0 0 0 1 45 184 0 0 0 268 | 0 0 0 124 77 0 0 0 0 0 0 24 4 0 0 0 0 0 28 24 269 | 126 0 0 0 0 0 15 65 184 17 0 0 0 0 0 39 80 5 0 0 270 | 0 0 0 5 3 0 10 6 0 0 58 44 72 17 17 4 0 0 44 51 271 | 184 40 0 0 0 0 1 23 98 9 0 0 0 0 0 1 3 2 63 29 272 | 0 0 4 8 47 66 48 13 0 2 7 3 184 120 1 0 0 0 0 1 273 | 84 27 0 0 0 0 0 0 274 | 68.856959 62.685038 2.453918 1.985428 275 | 52 0 0 0 0 0 0 92 57 33 46 23 2 0 0 34 114 141 47 6 276 | 0 0 0 0 39 77 48 73 9 0 0 0 58 2 0 4 6 0 3 56 277 | 34 17 20 91 69 0 0 3 141 66 16 19 6 0 0 18 80 16 9 72 278 | 103 4 1 14 35 1 0 9 33 2 6 30 42 2 0 32 141 16 10 9 279 | 141 95 11 10 9 3 6 16 42 42 24 141 104 1 0 3 23 9 0 8 280 | 21 10 4 5 6 0 0 6 32 26 69 29 52 62 9 4 10 28 41 13 281 | 6 41 27 43 73 19 0 0 282 | 155.388854 213.186964 2.446207 1.025643 283 | 4 2 1 10 34 0 1 3 34 9 2 0 0 3 13 33 21 27 14 0 284 | 12 28 16 16 136 25 5 0 1 2 3 145 12 6 1 5 35 1 0 0 285 | 115 9 0 0 4 1 0 11 92 3 0 5 46 11 1 34 162 9 0 0 286 | 1 1 0 162 10 1 0 2 24 5 5 6 101 15 1 5 5 0 1 10 287 | 94 43 4 30 34 0 0 3 162 162 0 0 0 0 0 17 1 2 4 3 288 | 7 4 3 4 43 16 2 1 3 1 1 4 27 21 1 6 35 14 0 0 289 | 162 162 0 0 2 1 0 0 290 | 196.824715 279.243550 2.409784 -1.302346 291 | 2 17 67 28 0 0 0 0 119 149 20 0 0 0 0 0 128 137 0 0 292 | 0 0 0 0 10 6 0 0 0 0 0 0 6 3 6 5 0 0 0 7 293 | 149 73 1 0 0 0 0 6 149 107 0 0 0 0 0 6 39 5 0 0 294 | 0 0 0 1 7 3 4 10 0 0 0 4 149 11 2 0 0 0 0 49 295 | 149 6 0 0 0 0 0 87 36 0 0 0 0 0 0 8 13 0 0 7 296 | 0 0 0 14 149 0 0 0 0 0 0 98 149 0 0 0 0 0 0 97 297 | 11 0 0 0 0 0 0 4 298 | 36.716361 34.626872 2.341351 -1.043692 299 | 5 0 0 130 130 0 0 3 114 8 2 29 15 0 0 51 81 17 1 2 300 | 12 17 12 26 0 1 0 3 66 59 2 0 34 5 1 113 130 0 0 5 301 | 130 14 1 5 5 0 3 67 38 15 10 34 14 2 7 27 1 1 7 59 302 | 48 5 0 2 22 1 0 49 111 16 57 32 130 50 8 2 1 0 45 41 303 | 12 24 20 59 17 4 15 5 1 0 1 32 9 2 20 53 9 0 0 0 304 | 28 26 130 56 5 3 0 0 1 26 130 26 1 2 2 3 6 56 121 18 305 | 3 0 0 0 0 2 47 130 306 | 78.049316 197.196286 2.189241 0.559586 307 | 0 0 5 14 5 0 0 0 137 28 1 3 1 0 1 2 184 59 0 0 308 | 0 0 0 0 32 4 0 0 0 0 0 0 3 4 2 5 3 0 1 2 309 | 138 19 1 1 1 1 6 7 184 49 0 0 0 0 0 4 57 4 0 0 310 | 0 0 0 1 1 7 3 4 7 1 0 0 145 15 2 1 0 0 0 26 311 | 184 5 0 0 0 0 0 85 52 0 0 0 0 0 0 11 1 0 1 3 312 | 1 1 4 2 169 2 0 0 0 0 1 61 184 0 0 0 0 0 0 104 313 | 15 0 0 0 0 0 0 9 314 | 40.139070 16.451385 2.093528 -2.732593 315 | 49 8 0 1 1 1 1 16 29 5 8 2 0 24 45 36 29 2 19 6 316 | 0 22 71 111 18 0 0 1 0 0 91 118 106 22 2 5 1 0 0 3 317 | 118 20 10 0 0 4 16 31 118 13 16 2 0 9 35 48 118 2 0 0 318 | 0 0 19 54 91 49 1 0 0 0 2 2 68 54 78 22 2 1 5 6 319 | 118 75 84 13 1 1 8 38 118 8 0 0 0 0 0 23 21 15 0 0 320 | 0 4 118 54 10 12 8 7 2 1 118 107 73 7 4 3 1 1 105 118 321 | 55 0 0 0 0 0 1 52 322 | 40.139070 16.451385 2.093528 -1.164746 323 | 0 0 0 4 118 54 21 16 1 0 0 0 1 2 90 49 2 5 1 0 324 | 0 3 106 22 0 1 1 1 1 16 49 8 8 7 2 1 118 107 10 12 325 | 78 22 2 1 5 6 68 55 10 0 0 4 16 31 118 20 8 2 0 24 326 | 46 36 29 5 4 3 1 1 105 118 73 7 85 13 1 1 8 38 118 75 327 | 16 2 0 8 35 48 118 13 19 6 0 21 71 111 29 2 0 0 0 0 328 | 1 52 56 0 0 0 0 0 0 22 118 8 0 0 0 0 18 53 118 2 329 | 0 1 0 0 91 118 18 0 330 | 12.847137 100.997990 2.091580 1.858690 331 | 24 0 0 29 29 3 22 104 100 2 5 50 85 6 3 51 131 1 0 0 332 | 0 0 0 55 2 0 0 0 0 0 0 2 29 5 2 131 77 0 0 40 333 | 131 24 10 106 30 0 0 27 131 7 0 0 0 0 0 19 3 2 1 0 334 | 0 0 0 0 14 1 0 131 131 9 32 49 131 22 0 22 13 6 33 74 335 | 131 20 0 0 0 0 0 3 5 0 0 0 0 0 0 0 6 0 0 19 336 | 49 19 40 109 96 2 0 0 21 43 36 53 123 2 0 0 0 0 0 14 337 | 3 0 0 0 0 0 0 1 338 | 103.841206 107.522634 2.078263 -0.974817 339 | 0 2 3 33 61 5 0 0 72 6 4 41 48 5 1 20 142 13 0 1 340 | 5 1 4 128 11 2 1 28 26 3 6 28 0 0 0 25 102 16 0 0 341 | 102 19 0 13 60 17 6 12 142 40 0 1 16 4 1 29 23 3 0 25 342 | 102 12 0 6 0 0 0 11 34 99 31 2 47 3 0 4 16 36 118 43 343 | 142 34 0 0 12 8 24 47 38 6 0 0 70 90 3 5 2 0 0 0 344 | 2 62 74 14 2 0 0 0 0 24 142 26 142 10 0 0 0 12 56 54 345 | 69 10 0 0 7 104 19 23 346 | 103.497485 117.340375 2.076652 -1.072541 347 | 0 0 0 9 107 36 2 1 86 0 0 1 26 18 6 103 102 0 0 0 348 | 0 0 4 134 8 0 2 8 11 4 12 32 13 4 4 54 88 6 1 3 349 | 134 10 2 5 6 7 7 134 69 3 0 14 22 6 12 133 0 0 12 64 350 | 22 1 1 3 23 5 1 18 91 20 1 4 134 76 1 2 4 3 1 20 351 | 114 29 0 16 83 14 2 14 0 0 2 55 81 10 0 0 3 2 0 9 352 | 50 29 21 5 134 44 0 1 5 3 22 31 134 30 0 0 55 44 1 8 353 | 0 0 0 1 42 80 2 0 354 | 110.782016 209.089035 2.064339 0.104909 355 | 37 17 3 0 0 0 0 0 192 12 0 0 0 0 0 3 139 8 0 0 356 | 0 0 0 2 3 2 0 0 0 0 0 0 51 22 4 0 0 0 0 0 357 | 192 19 0 0 0 0 0 9 181 1 0 0 0 0 0 8 5 0 0 0 358 | 0 0 0 1 60 8 0 0 0 0 0 5 192 12 0 0 0 0 0 24 359 | 177 3 0 0 0 0 0 7 3 1 0 0 0 0 0 0 52 1 0 0 360 | 0 0 1 14 192 23 0 0 0 0 0 18 125 10 0 0 0 0 0 3 361 | 3 1 0 0 0 0 0 0 362 | 39.140461 28.039949 2.044917 -2.619722 363 | 0 0 2 5 16 51 18 2 12 10 0 0 3 41 17 4 77 70 2 0 364 | 0 1 2 21 31 88 43 4 2 17 16 14 19 21 49 14 5 15 12 13 365 | 18 6 26 67 34 39 12 2 133 11 2 8 3 2 3 71 74 2 4 1 366 | 1 56 84 90 3 31 133 25 2 5 5 0 67 48 105 54 3 0 1 1 367 | 133 28 3 3 0 0 0 16 133 35 36 1 0 7 20 63 1 4 15 5 368 | 2 80 133 0 64 40 8 0 0 45 79 11 133 78 9 2 0 0 20 18 369 | 54 68 125 28 4 3 26 19 370 | 83.979489 18.032747 2.004027 2.993494 371 | 0 29 132 70 41 4 0 0 0 3 33 68 132 70 6 0 14 132 16 4 372 | 14 63 55 10 30 132 23 0 0 1 12 8 0 10 128 56 3 7 2 0 373 | 13 18 132 52 12 25 4 0 96 34 26 1 4 46 69 28 45 65 0 0 374 | 4 32 38 48 0 0 30 60 17 26 11 0 35 14 47 22 15 24 3 3 375 | 132 56 10 3 8 15 7 19 59 16 8 9 15 23 13 42 0 9 56 61 376 | 1 0 8 5 9 25 49 19 5 5 6 2 91 132 29 7 2 1 0 5 377 | 75 79 41 22 5 0 0 4 378 | 17.994494 62.659913 2.001080 1.440940 379 | 23 18 0 5 26 0 0 0 159 58 0 0 0 0 0 0 168 59 0 0 380 | 0 0 0 0 38 8 0 0 0 0 0 0 14 5 3 4 10 2 15 14 381 | 168 48 0 0 0 0 4 23 168 37 0 0 0 0 0 13 64 3 0 0 382 | 0 0 0 4 6 27 32 25 7 0 5 10 154 81 8 0 0 0 2 33 383 | 168 9 0 0 0 0 0 53 50 0 0 0 0 0 0 10 4 8 5 12 384 | 22 10 21 10 168 38 1 0 0 0 6 39 168 6 0 0 0 0 0 22 385 | 34 0 0 0 0 0 0 1 386 | 164.985183 175.315591 1.990425 -1.620507 387 | 21 30 20 0 0 1 7 10 179 43 2 0 0 0 0 21 151 8 0 0 388 | 0 0 0 8 6 0 0 0 0 0 0 1 63 15 12 0 0 0 2 11 389 | 179 14 0 0 0 0 0 45 178 5 0 0 0 0 0 15 8 1 0 0 390 | 0 0 0 0 60 9 0 0 0 0 5 12 179 33 0 0 0 0 0 9 391 | 173 16 0 0 0 0 0 2 10 1 0 0 0 0 0 0 42 23 0 0 392 | 0 0 0 2 179 51 0 0 0 0 0 2 161 21 0 0 0 0 0 0 393 | 8 2 0 0 0 0 0 0 394 | 166.007437 154.878340 1.947519 -1.394026 395 | 50 3 0 0 0 0 0 12 166 0 0 0 0 0 0 54 165 0 0 0 396 | 0 0 0 29 9 0 0 0 0 0 0 1 84 3 0 0 0 0 0 27 397 | 166 6 0 0 0 0 0 65 166 2 0 0 0 0 0 26 7 0 0 0 398 | 0 0 0 3 92 7 0 0 0 0 0 19 166 82 0 0 0 0 0 12 399 | 166 29 0 0 0 0 0 4 7 0 0 0 0 0 0 1 36 4 3 1 400 | 0 0 0 26 166 40 3 1 0 0 0 14 166 8 0 0 0 0 0 12 401 | 4 0 0 0 0 0 0 1 402 | 110.587097 78.592621 1.945299 -1.977351 403 | 6 5 2 0 0 6 19 15 29 9 0 0 0 1 4 41 166 2 0 0 404 | 0 0 0 139 93 0 0 0 0 0 0 70 1 51 32 0 0 1 1 2 405 | 85 57 15 0 0 0 1 26 166 10 0 0 0 0 0 98 82 0 0 0 406 | 0 0 0 33 3 53 39 0 0 2 1 1 151 102 23 0 0 0 0 1 407 | 166 19 0 0 0 0 0 22 77 3 0 0 0 0 0 5 19 10 2 0 408 | 0 8 10 4 156 25 2 0 0 5 12 52 166 7 0 0 0 0 0 72 409 | 38 1 0 0 0 0 0 10 410 | 108.721386 110.216521 1.937894 -0.960769 411 | 54 4 1 20 39 7 1 11 144 22 0 0 2 0 1 69 34 6 1 29 412 | 48 4 4 23 0 0 11 68 21 0 0 0 55 12 0 11 41 14 11 11 413 | 144 32 0 0 6 2 3 37 58 4 0 14 132 22 1 14 0 0 1 32 414 | 78 26 0 0 16 0 0 1 6 28 115 34 144 29 0 0 3 4 34 53 415 | 84 13 0 0 69 107 7 11 0 0 0 0 31 116 2 0 0 0 0 0 416 | 0 17 109 17 127 5 0 0 0 13 62 52 114 15 0 0 6 92 24 41 417 | 0 0 0 0 11 144 18 0 418 | 156.999360 128.140928 1.909415 1.509117 419 | 0 0 0 66 192 1 0 0 65 1 0 39 110 0 0 23 57 4 6 24 420 | 3 0 0 21 0 0 11 34 3 0 0 0 2 0 0 62 192 0 0 0 421 | 136 8 2 26 109 0 0 11 40 6 7 49 5 0 0 4 0 0 16 48 422 | 4 0 0 0 3 0 0 12 192 9 0 1 100 7 4 10 105 5 1 15 423 | 14 6 21 83 4 0 0 0 0 1 39 37 0 0 0 0 2 0 0 36 424 | 192 3 0 2 27 1 0 12 68 4 2 14 1 0 1 34 31 2 0 0 425 | 0 0 10 18 7 1 0 0 426 | 49.160231 77.558886 1.869405 -1.007067 427 | 1 1 1 38 39 29 32 1 34 70 33 34 21 10 8 9 88 46 21 2 428 | 0 3 13 46 3 4 13 0 0 22 42 11 2 2 5 19 28 88 68 6 429 | 64 5 1 8 23 122 72 21 122 20 0 0 0 11 20 69 24 7 0 0 430 | 0 54 99 24 61 111 22 1 2 26 26 12 32 20 0 0 20 122 83 14 431 | 122 73 0 0 1 62 65 33 82 76 0 2 5 13 29 8 113 122 1 0 432 | 0 0 0 2 102 83 0 0 0 33 30 10 34 12 0 0 24 103 41 10 433 | 77 56 12 3 9 10 2 3 434 | 103.429489 89.942068 1.851623 -0.983208 435 | 84 21 0 0 0 0 0 8 28 2 0 0 2 24 27 30 0 0 0 0 436 | 6 114 68 5 5 0 0 0 0 16 121 28 126 9 0 0 0 0 0 52 437 | 86 1 0 0 0 13 83 126 3 0 0 0 7 113 126 21 0 0 0 0 438 | 0 21 126 34 126 18 0 0 0 0 1 21 126 20 0 0 2 4 10 32 439 | 25 6 0 4 47 71 34 16 23 0 0 0 7 28 55 60 99 18 0 1 440 | 7 4 9 22 126 50 0 0 0 0 1 18 52 5 0 0 3 21 93 62 441 | 15 0 0 0 0 12 126 84 442 | 103.429489 89.942068 1.851623 -2.920116 443 | 72 66 16 4 0 0 0 0 58 95 20 1 0 0 0 13 53 54 40 4 444 | 0 0 2 15 88 122 40 3 0 0 0 0 111 73 0 0 0 0 0 19 445 | 122 55 6 1 0 0 4 50 38 14 34 38 2 1 18 48 12 34 107 102 446 | 0 0 0 1 84 15 8 2 0 0 1 47 74 122 74 9 0 0 0 6 447 | 7 32 122 122 0 0 0 1 1 3 112 122 0 0 0 0 11 12 19 14 448 | 0 0 2 3 0 18 90 55 0 0 0 0 0 5 122 82 0 0 0 0 449 | 2 26 72 47 0 0 0 0 450 | 35.002463 98.071175 1.814396 1.246802 451 | 2 0 0 0 1 17 31 9 44 0 0 1 3 23 34 61 174 0 0 0 452 | 0 0 4 174 90 1 0 0 5 5 3 48 6 0 0 0 8 12 7 18 453 | 140 10 0 0 12 14 6 21 174 16 0 0 3 3 2 69 70 9 0 0 454 | 13 9 3 12 4 15 6 3 8 7 1 5 138 7 2 8 14 12 1 6 455 | 174 9 0 0 2 6 1 33 62 3 0 0 12 34 2 11 0 15 32 7 456 | 0 0 0 0 139 7 15 16 1 0 0 26 174 0 0 0 0 2 0 84 457 | 33 0 0 0 16 27 1 10 458 | 30.531355 54.530484 1.745098 -1.688314 459 | 0 5 56 38 26 66 36 10 4 3 28 89 17 12 37 50 0 0 5 73 460 | 29 47 28 2 0 4 10 8 6 24 22 25 10 4 34 24 120 61 0 0 461 | 109 13 29 17 8 14 32 61 12 0 0 0 3 97 120 21 0 0 0 0 462 | 0 51 120 23 10 0 0 0 120 109 1 3 120 18 0 0 19 22 5 44 463 | 52 8 0 0 4 23 73 63 0 0 0 0 4 75 110 18 2 0 0 3 464 | 120 97 0 1 101 82 10 5 39 15 1 24 50 120 30 0 0 1 18 50 465 | 2 36 22 3 13 49 23 10 466 | 41.895083 121.632690 1.715336 1.624305 467 | 2 0 0 0 0 2 19 21 48 0 0 0 0 0 19 61 190 0 0 0 468 | 0 0 0 190 55 0 0 0 4 1 0 66 6 0 0 0 0 5 11 26 469 | 124 1 0 0 0 0 10 44 190 6 0 0 0 0 0 66 61 0 0 0 470 | 8 1 0 10 4 5 2 6 3 2 6 11 122 29 2 0 0 0 1 10 471 | 190 41 0 0 0 0 0 2 69 4 0 0 4 1 0 1 0 3 6 13 472 | 10 1 0 0 73 39 9 4 0 0 0 0 190 63 0 0 0 0 0 2 473 | 68 7 0 0 1 0 0 0 474 | 124.271453 101.712085 1.700937 0.023508 475 | 2 2 6 47 41 9 1 2 59 7 4 10 8 3 12 58 19 1 0 18 476 | 52 13 29 62 0 0 0 38 63 3 1 1 9 4 1 9 143 110 0 0 477 | 143 19 2 4 11 1 0 30 44 3 1 54 126 3 1 12 0 0 0 29 478 | 70 8 8 0 10 2 0 0 143 143 1 1 143 31 4 7 24 31 3 19 479 | 22 10 5 41 90 19 3 5 0 0 0 10 61 15 13 2 1 0 0 0 480 | 123 143 9 1 39 1 0 0 25 143 58 55 5 0 0 4 73 35 6 11 481 | 0 0 0 10 51 13 0 0 482 | 146.345315 192.972963 1.700451 2.234250 483 | 20 66 63 25 10 2 1 0 122 113 18 4 19 18 2 4 6 4 1 17 484 | 115 40 1 1 0 0 10 44 20 19 0 0 33 17 6 13 35 16 8 3 485 | 139 92 2 4 20 8 1 8 35 11 2 20 139 58 0 3 1 1 5 40 486 | 65 22 0 0 23 0 0 3 27 56 32 8 139 9 0 0 9 15 7 63 487 | 53 1 0 0 99 139 17 22 0 0 0 0 71 126 10 0 14 2 1 21 488 | 80 30 10 8 139 29 0 3 15 4 3 53 61 8 1 40 65 57 8 13 489 | 0 0 0 25 85 72 2 0 490 | 46.905898 166.065127 1.680455 0.989833 491 | 0 1 1 1 12 17 2 0 121 116 2 1 5 4 0 0 180 153 0 0 492 | 0 0 0 0 21 8 0 0 0 0 0 0 0 0 4 15 16 7 0 0 493 | 134 26 1 5 19 9 0 2 180 79 0 0 0 0 0 7 55 9 0 0 494 | 0 0 0 1 2 0 3 13 7 5 1 1 136 3 0 8 15 5 1 16 495 | 180 10 0 0 0 0 0 55 56 1 0 0 0 0 0 10 1 0 0 3 496 | 11 7 0 0 139 3 0 6 15 3 0 13 180 8 0 0 0 0 0 33 497 | 37 4 0 0 0 0 0 2 498 | 70.472364 51.145706 1.643458 2.230609 499 | 45 3 0 0 0 0 13 61 128 40 1 0 0 0 8 31 128 62 8 2 500 | 64 59 9 6 15 2 2 0 32 54 71 118 36 16 24 5 0 0 20 64 501 | 64 72 104 17 0 0 12 29 128 128 43 2 5 5 1 5 43 45 13 8 502 | 7 5 6 32 6 13 44 60 9 1 0 0 106 39 78 29 0 0 0 23 503 | 128 55 11 3 0 0 0 46 23 70 66 34 3 0 0 1 16 5 10 101 504 | 26 0 0 5 128 16 9 14 0 0 0 89 101 22 6 4 1 3 10 66 505 | 1 6 28 48 38 7 2 1 506 | 124.414925 206.255726 1.614870 0.255730 507 | 8 2 0 0 0 0 4 5 73 0 0 0 0 0 2 34 188 0 0 0 508 | 0 0 0 135 88 2 0 0 0 0 0 28 9 0 0 0 0 0 10 15 509 | 160 1 0 0 0 2 10 24 188 6 0 0 0 0 0 64 70 3 0 0 510 | 0 0 0 16 17 0 0 0 0 0 7 21 134 7 0 0 0 1 8 22 511 | 188 60 0 0 0 0 0 6 75 15 0 0 0 0 0 1 2 0 0 0 512 | 0 1 45 24 49 4 0 0 0 1 47 24 188 68 0 0 0 0 3 11 513 | 85 20 0 0 0 0 0 1 514 | 35.454770 10.881149 1.581962 -2.844091 515 | 72 22 9 0 0 0 1 18 21 11 38 20 7 7 12 12 134 32 6 6 516 | 3 1 1 7 48 2 0 0 0 0 0 5 49 40 41 26 5 0 0 1 517 | 28 39 134 69 10 7 7 8 134 94 29 6 0 0 2 28 69 4 0 0 518 | 0 0 0 4 14 35 18 13 6 2 38 49 19 5 10 16 11 17 134 134 519 | 134 10 2 0 0 0 40 134 39 0 0 0 0 0 0 19 2 2 0 0 520 | 0 0 134 134 0 0 0 0 0 0 134 134 4 0 0 0 0 0 30 88 521 | 3 0 0 0 0 0 1 6 522 | 155.386342 111.890723 1.553299 0.918965 523 | 17 21 3 2 23 13 0 7 26 34 17 38 28 1 0 8 158 56 7 18 524 | 11 0 0 15 50 15 9 22 13 8 0 3 1 13 4 3 56 57 21 3 525 | 37 29 34 40 12 45 25 5 158 15 5 13 6 5 1 26 40 3 8 55 526 | 40 0 0 4 5 1 0 0 13 158 63 8 29 5 4 12 39 137 33 11 527 | 158 59 2 7 16 21 3 8 24 17 3 30 56 1 0 0 0 0 0 0 528 | 34 158 30 0 2 2 2 11 139 158 3 0 93 70 1 6 28 9 0 0 529 | 31 28 1 7 22 5 0 0 530 | 61.764010 17.370670 1.548797 0.679565 531 | 0 7 59 111 15 2 0 0 1 19 74 60 5 1 1 2 4 5 58 58 532 | 3 1 53 54 1 14 27 23 2 33 97 16 11 20 19 24 19 8 0 1 533 | 46 20 29 23 48 9 1 6 157 13 4 3 3 11 54 137 15 0 0 0 534 | 74 119 77 32 17 6 10 43 7 0 2 7 98 9 8 67 32 1 0 2 535 | 157 7 0 0 3 4 1 23 45 0 0 0 142 69 1 7 0 0 29 60 536 | 24 2 1 1 102 13 17 32 1 0 0 8 157 55 0 0 1 0 0 7 537 | 36 14 1 2 103 13 0 0 538 | 76.734291 161.965788 1.541478 -2.724514 539 | 52 68 45 5 1 0 2 13 101 50 9 1 29 27 5 21 8 0 0 0 540 | 55 123 40 23 2 0 1 1 50 43 29 31 111 62 26 2 4 6 11 19 541 | 136 38 1 2 4 15 29 81 19 3 1 14 37 136 84 28 1 1 3 10 542 | 24 94 89 33 71 3 0 0 2 9 102 81 136 31 2 9 6 5 18 89 543 | 40 19 12 56 64 78 23 11 1 10 19 55 50 47 35 3 64 6 0 0 544 | 0 2 31 55 123 64 11 1 1 2 12 42 22 35 37 33 25 46 13 6 545 | 6 6 5 14 15 34 46 7 546 | 85.433351 26.014151 1.458955 -1.413648 547 | 122 108 1 2 3 0 0 9 30 39 4 34 107 5 0 0 4 0 0 12 548 | 39 9 15 38 9 0 0 0 0 3 35 122 122 52 3 2 0 0 0 22 549 | 56 51 21 92 38 0 0 1 117 16 5 17 9 0 4 82 91 1 0 0 550 | 0 0 6 122 79 12 12 10 1 0 0 48 59 12 24 60 29 0 0 7 551 | 122 28 1 5 3 0 0 28 122 47 3 0 0 0 0 68 15 3 16 19 552 | 3 37 69 15 54 10 28 22 2 16 37 35 122 9 6 32 4 0 1 77 553 | 71 102 97 40 3 0 0 8 554 | 60.787965 189.055419 1.410406 0.719523 555 | 45 10 0 2 5 0 4 18 201 73 0 0 0 0 0 10 96 23 0 0 556 | 0 0 0 2 3 1 0 0 0 0 0 4 46 14 8 3 1 1 3 10 557 | 201 48 0 0 0 0 0 21 166 12 0 0 0 0 0 10 8 1 0 0 558 | 0 0 0 5 40 9 6 5 7 2 1 11 201 28 0 0 0 0 0 37 559 | 166 8 0 0 0 0 0 13 8 4 0 0 0 0 0 3 42 5 3 0 560 | 1 1 1 8 201 4 0 0 0 0 0 34 110 2 0 0 0 0 0 15 561 | 9 1 0 0 0 0 0 1 562 | 107.769593 66.305696 1.345846 -1.568813 563 | 8 4 0 0 15 45 8 2 79 10 0 0 4 13 16 80 162 1 0 0 564 | 0 0 0 162 43 0 0 0 0 0 0 73 37 16 0 0 1 13 10 14 565 | 126 6 0 0 0 7 38 162 88 0 0 0 0 0 2 162 9 1 0 0 566 | 0 3 3 21 49 8 0 0 0 0 12 31 162 46 0 0 0 0 3 58 567 | 135 6 0 0 0 0 0 41 2 0 0 0 0 4 6 5 48 3 0 0 568 | 0 0 6 26 162 3 0 0 0 0 3 32 134 1 0 0 0 0 0 6 569 | 4 0 0 0 0 0 0 0 570 | 107.769593 66.305696 1.345846 -2.225862 571 | 33 81 15 0 0 0 0 0 163 113 0 0 0 0 0 0 89 18 0 0 572 | 0 0 1 2 3 5 0 0 0 0 2 1 48 27 2 0 0 14 38 30 573 | 163 54 0 0 0 0 2 55 145 31 0 0 0 0 1 7 5 2 0 0 574 | 0 0 5 5 19 33 11 0 0 2 16 12 146 163 7 0 0 0 1 21 575 | 73 163 2 0 0 0 0 1 11 33 0 0 0 0 0 0 14 10 3 0 576 | 0 0 0 8 41 73 2 0 0 0 0 23 85 163 0 0 0 0 0 4 577 | 35 133 0 0 0 0 0 0 578 | 22.409927 83.472452 1.340181 0.982703 579 | 28 5 2 0 0 0 4 19 136 57 2 0 0 0 1 11 63 136 3 0 580 | 0 1 2 3 11 61 0 0 0 0 0 0 22 6 5 9 5 3 4 4 581 | 136 15 2 0 0 12 21 89 51 106 3 0 0 112 53 33 30 136 0 0 582 | 0 15 4 0 39 13 5 16 4 0 0 3 136 76 0 0 0 3 4 26 583 | 92 70 0 0 0 135 27 8 17 76 0 0 1 135 9 1 46 12 1 6 584 | 5 0 0 0 136 28 0 0 0 1 3 39 97 86 0 0 0 14 4 13 585 | 13 14 0 0 10 90 2 1 586 | 139.432998 102.871834 1.336358 -2.639470 587 | 15 48 12 0 0 0 0 2 48 50 6 0 2 14 16 9 103 28 0 0 588 | 0 112 155 30 7 2 2 3 1 132 155 5 39 18 6 0 0 0 0 28 589 | 93 30 12 5 0 0 0 10 155 34 1 0 0 6 19 14 44 2 0 0 590 | 0 42 107 8 42 15 3 0 0 0 3 17 87 9 4 3 1 2 11 51 591 | 155 34 0 0 0 0 0 37 71 8 0 0 0 0 0 5 33 3 1 0 592 | 0 0 2 26 37 21 19 19 0 0 2 26 155 155 19 9 0 0 0 6 593 | 84 38 0 0 0 0 0 1 594 | 148.931564 98.535225 1.298849 1.870550 595 | 23 12 6 7 3 0 1 15 108 25 8 21 3 3 2 4 179 26 0 0 596 | 0 0 0 5 41 3 0 0 0 0 0 3 21 15 16 21 2 4 2 9 597 | 151 20 4 8 2 8 9 14 179 33 0 0 0 0 0 8 49 7 3 0 598 | 0 0 0 2 34 2 2 3 3 21 21 35 132 6 9 7 7 17 13 38 599 | 179 12 6 1 0 1 0 39 40 6 40 16 0 0 0 2 14 7 34 27 600 | 5 6 5 12 118 12 35 14 1 0 2 45 179 28 67 35 0 0 0 36 601 | 6 3 159 73 1 0 0 0 602 | 32.941459 68.513114 1.286638 -1.898322 603 | 10 26 84 43 3 0 1 7 36 35 58 32 4 0 2 23 35 3 5 26 604 | 7 3 17 103 3 5 7 46 20 8 15 12 25 29 96 63 4 0 0 1 605 | 125 53 20 12 4 1 3 90 47 4 19 120 54 3 6 53 0 1 56 125 606 | 9 0 0 0 49 21 46 22 6 29 44 71 125 45 15 46 17 0 5 125 607 | 19 7 17 125 80 1 1 4 5 9 26 87 33 2 0 0 14 2 5 6 608 | 4 51 125 84 32 1 3 95 56 1 15 104 0 0 2 76 50 16 11 1 609 | 1 1 1 9 19 30 20 3 610 | 111.808372 22.279895 1.249622 -0.099083 611 | 65 6 0 0 60 120 4 11 107 15 0 0 2 15 25 52 100 120 0 0 612 | 2 13 7 4 21 41 3 0 0 0 0 0 38 1 0 0 94 120 7 10 613 | 120 8 0 0 11 63 25 44 60 69 0 0 11 119 32 10 55 113 1 0 614 | 2 5 0 0 6 0 0 0 106 120 2 2 120 17 0 0 36 74 13 22 615 | 54 16 0 0 55 120 6 5 66 86 0 0 24 46 1 0 0 0 0 0 616 | 21 59 2 0 48 5 0 0 9 73 39 29 36 4 0 0 17 46 4 14 617 | 28 12 0 0 15 86 12 6 618 | 144.521033 201.076813 1.240835 2.395417 619 | 0 0 2 2 13 39 58 5 26 0 0 7 46 35 11 19 136 1 0 0 620 | 3 4 5 108 41 2 0 6 51 24 8 20 2 0 1 37 69 52 27 12 621 | 66 14 0 43 89 31 4 23 136 54 0 1 1 5 15 129 32 9 3 9 622 | 29 96 31 23 2 4 19 112 74 10 1 2 81 12 1 36 33 10 15 87 623 | 136 24 1 8 9 1 4 136 23 6 16 73 37 11 1 13 1 18 38 28 624 | 13 14 22 7 99 32 6 14 6 4 29 86 136 24 1 10 21 3 4 63 625 | 5 0 4 53 78 5 0 0 626 | 144.521033 201.076813 1.240835 2.002912 627 | 2 0 2 5 18 27 21 9 132 43 0 0 9 18 5 6 132 44 0 0 628 | 8 13 23 37 0 0 0 0 23 108 45 3 0 0 0 1 27 79 49 13 629 | 114 70 1 5 46 51 3 7 132 132 1 2 1 3 15 37 18 20 2 38 630 | 40 43 27 5 6 3 2 22 109 40 18 14 54 18 1 14 98 23 10 26 631 | 132 101 1 1 5 1 2 46 45 23 1 20 78 24 0 5 1 8 35 62 632 | 55 6 1 0 15 17 18 25 41 19 6 14 132 123 5 2 3 0 1 76 633 | 53 36 2 11 43 18 1 17 634 | 101.969069 204.516778 1.209063 0.112399 635 | 2 4 4 1 2 2 5 1 87 7 1 0 0 1 7 22 203 2 0 0 636 | 0 0 0 50 74 2 0 0 0 0 0 9 14 10 7 2 2 2 7 4 637 | 137 26 1 0 2 5 6 16 203 36 0 0 0 0 0 30 72 21 0 0 638 | 0 0 0 3 10 30 6 0 0 0 0 1 131 39 2 0 1 3 2 19 639 | 203 32 0 0 0 0 0 32 78 6 0 0 0 0 0 8 6 17 8 0 640 | 1 2 0 0 123 31 2 0 0 0 0 14 203 24 0 0 0 0 0 32 641 | 67 2 0 0 0 0 0 8 642 | 67.422940 194.079194 1.191078 0.692225 643 | 34 3 1 1 2 1 1 11 193 11 0 0 0 0 0 24 143 6 0 0 644 | 0 0 0 8 9 5 0 0 0 0 0 0 51 6 3 0 0 0 1 6 645 | 193 8 0 0 0 0 0 21 172 3 0 0 0 0 0 15 10 1 0 0 646 | 0 0 0 4 52 12 8 0 0 1 0 12 193 20 0 0 0 0 0 29 647 | 177 5 0 0 0 0 0 8 5 1 0 0 0 0 0 5 51 3 0 0 648 | 0 3 2 16 193 1 0 0 0 0 0 36 120 2 0 0 0 0 0 10 649 | 5 3 0 0 0 0 0 0 650 | 157.742213 187.074540 1.164216 -2.832103 651 | 30 31 0 2 17 5 0 4 142 142 39 1 6 1 0 0 12 142 142 5 652 | 1 0 0 0 0 142 142 1 0 0 0 0 27 21 29 12 14 5 0 8 653 | 142 38 5 5 10 1 0 17 47 25 52 25 44 0 0 0 2 51 129 40 654 | 23 0 0 0 15 9 16 6 0 0 19 123 117 15 7 3 2 1 16 100 655 | 21 13 17 71 29 0 0 1 1 5 9 69 34 0 0 0 7 2 2 6 656 | 1 1 32 133 1 1 9 30 16 7 23 28 1 1 9 56 23 0 0 0 657 | 2 2 4 23 35 8 0 1 658 | 159.110991 111.659499 1.159854 -1.455906 659 | 0 0 1 82 31 3 9 9 11 0 1 100 43 1 3 25 52 0 0 3 660 | 5 8 10 58 154 0 0 0 0 0 0 134 6 1 9 154 19 1 1 1 661 | 125 11 6 49 9 5 5 43 132 13 1 7 45 30 7 27 154 32 0 0 662 | 1 0 0 31 5 0 4 45 14 19 46 19 81 58 11 6 10 11 13 10 663 | 93 45 18 26 34 2 1 11 154 82 0 0 2 0 0 5 3 2 2 9 664 | 46 12 18 19 9 12 2 4 30 6 9 20 58 14 3 0 1 0 5 54 665 | 154 67 0 0 1 0 1 9 666 | 73.585006 15.961603 1.149732 -2.423642 667 | 2 6 5 27 5 1 2 2 45 49 11 21 1 0 0 2 36 25 6 16 668 | 10 1 5 125 4 0 0 7 5 1 32 128 0 0 2 52 26 2 23 9 669 | 98 14 4 10 0 0 38 37 128 38 4 2 0 0 5 128 48 19 14 112 670 | 32 2 13 128 18 0 0 7 13 2 52 77 20 2 2 0 0 1 127 118 671 | 120 76 44 11 4 1 29 62 35 62 112 74 15 0 2 15 36 1 0 0 672 | 1 1 24 75 14 3 10 12 27 14 17 42 11 11 60 32 24 7 6 12 673 | 98 10 22 12 4 0 13 128 674 | 19.165670 83.303723 1.123803 1.645167 675 | 13 5 9 3 0 1 6 53 67 7 1 0 1 5 18 121 139 10 0 0 676 | 0 0 0 49 32 0 0 0 0 0 0 5 43 0 2 0 5 9 31 139 677 | 45 15 0 1 109 75 50 87 139 38 0 0 13 4 0 14 39 2 0 0 678 | 0 0 0 3 139 0 0 0 13 7 4 122 43 1 0 0 139 79 3 16 679 | 139 21 0 0 28 13 1 16 40 4 0 0 0 0 0 2 104 0 0 0 680 | 10 3 2 34 19 0 0 0 139 30 0 3 139 12 0 0 36 9 0 7 681 | 41 3 0 0 0 0 0 2 682 | 19.165670 83.303723 1.123803 -1.280132 683 | 0 0 0 4 10 0 0 0 6 0 0 43 136 0 0 0 136 1 1 20 684 | 45 0 0 24 37 0 0 36 34 0 0 8 0 0 0 9 25 0 0 0 685 | 20 3 0 80 136 0 0 0 136 11 2 25 44 0 0 12 33 2 4 114 686 | 88 0 0 2 0 0 0 21 48 0 0 0 24 3 1 62 136 7 0 2 687 | 136 46 74 79 28 4 0 8 9 5 62 136 35 0 0 0 0 0 0 45 688 | 55 0 0 0 1 1 10 136 119 2 0 0 4 10 57 128 21 4 2 1 689 | 1 2 27 64 3 6 7 2 690 | 22.404503 120.638834 1.118787 -1.812760 691 | 0 0 0 5 64 9 0 0 55 6 0 1 152 48 1 4 152 22 2 2 692 | 8 4 0 6 17 13 12 8 1 0 0 0 2 1 1 2 55 11 0 0 693 | 54 6 0 7 152 39 0 2 152 16 1 8 40 6 0 7 34 43 15 23 694 | 47 3 0 0 3 3 2 4 33 11 2 0 42 2 0 9 152 62 1 7 695 | 152 8 0 1 58 19 1 22 51 31 0 7 127 23 0 0 8 6 0 2 696 | 25 5 1 0 28 4 0 23 152 13 0 0 152 27 0 6 41 5 0 1 697 | 38 8 0 0 87 50 0 0 698 | 22.404503 120.638834 1.118787 1.457973 699 | 110 26 0 1 53 7 0 2 40 6 0 19 149 3 0 4 149 1 0 6 700 | 68 2 0 52 52 2 0 0 4 3 0 9 116 3 0 2 61 27 0 19 701 | 66 8 0 66 149 0 0 3 149 25 0 17 51 0 0 25 42 8 2 0 702 | 3 2 1 8 30 0 0 3 39 31 20 27 43 3 0 45 149 3 2 12 703 | 149 11 0 6 39 1 0 32 45 3 0 0 2 1 2 5 0 0 0 5 704 | 37 8 9 4 32 4 0 39 149 11 1 1 149 13 0 5 19 1 0 10 705 | 33 1 0 0 0 1 1 8 706 | 161.617873 176.371780 1.078735 -1.671292 707 | 7 16 42 9 0 0 2 2 66 26 35 11 0 1 10 24 194 33 1 0 708 | 0 0 0 52 76 9 0 0 0 0 4 11 33 14 9 3 0 1 7 20 709 | 143 20 0 0 0 0 12 51 194 9 0 0 0 0 0 67 79 7 1 0 710 | 0 0 0 15 13 9 2 3 3 3 13 15 134 64 9 1 0 0 0 7 711 | 194 77 0 0 0 0 0 13 62 17 7 3 0 0 0 10 1 1 2 18 712 | 9 5 14 9 85 23 6 5 2 1 4 35 194 64 0 0 0 0 0 37 713 | 69 20 1 0 0 0 0 7 714 | 166.564489 192.160380 1.043775 -2.344532 715 | 15 11 8 3 2 4 2 4 165 5 7 4 3 2 0 91 165 1 0 0 716 | 0 0 0 75 4 1 0 0 0 0 0 3 28 12 7 7 5 1 0 6 717 | 165 11 1 4 3 0 0 83 148 5 0 1 0 0 1 37 9 1 0 0 718 | 0 0 0 3 37 7 0 11 22 1 1 9 165 89 0 4 9 0 0 8 719 | 120 97 1 0 0 0 0 1 9 8 0 0 0 0 0 0 34 1 1 18 720 | 23 1 2 17 165 81 5 4 3 0 0 37 58 165 7 0 0 0 0 0 721 | 11 53 5 0 0 0 0 0 722 | 23.418102 128.699054 1.042463 -1.380677 723 | 0 3 4 3 5 1 0 0 0 6 35 33 5 3 0 0 2 34 39 10 724 | 1 1 0 0 2 8 4 0 0 0 0 0 0 1 2 30 30 2 0 0 725 | 15 26 70 134 110 0 0 0 134 130 70 37 5 0 0 3 28 14 2 0 726 | 0 0 0 3 0 0 1 72 83 0 0 0 57 7 2 134 134 1 0 36 727 | 134 18 2 11 11 0 0 96 43 2 1 1 0 0 0 9 0 0 0 88 728 | 114 0 0 0 85 0 0 73 115 3 1 98 131 4 8 3 1 0 0 134 729 | 16 6 8 2 0 0 0 6 730 | 23.418102 128.699054 1.042463 1.374339 731 | 8 0 0 2 68 14 21 19 50 11 1 20 162 6 0 3 162 43 1 2 732 | 14 1 0 6 39 5 0 0 1 1 0 5 1 0 1 4 82 22 3 2 733 | 54 8 0 15 162 112 5 3 162 35 0 2 22 15 5 20 66 5 0 0 734 | 0 0 2 11 1 0 0 2 26 20 1 0 11 0 0 0 117 162 58 23 735 | 161 5 0 0 8 48 72 162 37 7 0 0 0 1 6 19 0 0 0 0 736 | 2 7 5 0 0 1 0 0 2 31 36 6 3 3 0 0 0 9 36 42 737 | 2 4 0 0 0 0 5 5 738 | 53.646560 176.396663 1.028631 1.042752 739 | 2 1 1 5 32 6 0 1 126 7 0 4 19 8 1 28 184 6 0 0 740 | 0 0 0 84 55 5 1 0 0 0 0 16 1 4 6 8 12 9 0 0 741 | 149 22 1 0 12 18 3 8 184 32 0 0 0 0 0 7 60 9 3 0 742 | 0 0 1 3 9 10 3 14 17 3 1 0 132 13 0 0 4 6 4 33 743 | 184 11 0 0 0 0 0 56 64 2 0 0 0 1 1 9 6 7 2 17 744 | 10 1 3 3 160 19 0 0 0 0 1 37 184 1 0 0 0 0 0 107 745 | 29 1 0 0 0 0 0 13 746 | 76.033763 38.582573 1.022603 -0.846966 747 | 1 8 92 57 25 0 0 0 130 42 17 26 10 0 0 8 67 11 0 20 748 | 81 10 0 4 0 0 0 16 66 28 0 0 19 107 150 30 2 0 0 0 749 | 150 98 17 4 5 1 0 33 74 2 2 24 118 15 1 20 0 0 2 51 750 | 60 28 0 0 71 31 6 3 6 2 3 27 150 52 3 4 4 0 0 46 751 | 63 14 6 57 114 6 0 2 0 0 8 43 66 14 0 0 78 0 0 0 752 | 1 1 7 50 150 2 1 4 3 0 0 82 21 2 2 55 77 6 0 3 753 | 0 0 20 54 39 5 1 0 754 | 71.112607 193.667104 1.020983 0.655098 755 | 8 5 1 1 4 5 2 3 110 11 2 1 3 2 0 13 208 23 0 0 756 | 0 0 0 14 45 4 1 0 0 0 3 9 7 11 19 6 3 2 0 1 757 | 127 35 12 3 0 2 2 8 208 59 0 0 0 0 0 15 74 9 1 0 758 | 0 0 1 5 9 1 2 2 3 20 10 6 135 5 0 0 0 2 10 35 759 | 208 13 0 0 0 0 0 46 75 8 0 0 0 0 0 5 2 1 2 6 760 | 9 12 5 1 112 20 3 1 0 1 1 5 208 12 0 0 0 0 0 6 761 | 56 1 0 0 0 0 0 6 762 | 99.057201 134.950687 1.019631 -2.065920 763 | 8 8 94 76 3 9 13 8 20 62 115 28 1 7 19 38 67 115 77 5 764 | 0 0 18 53 81 98 15 3 0 1 5 19 9 3 6 7 4 30 51 49 765 | 52 26 3 1 52 51 40 57 115 69 0 0 3 4 50 87 87 45 4 0 766 | 0 11 98 73 14 21 19 13 18 12 7 10 48 33 13 3 37 54 14 16 767 | 115 115 5 0 2 20 17 47 81 115 31 1 0 12 24 35 9 16 49 25 768 | 13 9 6 4 35 63 64 5 2 8 5 10 112 62 29 2 0 4 7 27 769 | 100 58 13 5 5 3 0 28 770 | 107.933860 59.684981 1.007887 -1.582969 771 | 66 21 0 0 0 7 24 17 194 74 0 0 0 1 2 10 102 9 0 0 772 | 0 0 0 16 4 0 0 0 0 1 1 6 61 10 3 0 0 12 17 26 773 | 194 13 0 0 0 2 3 38 158 5 0 0 0 0 0 9 8 2 0 0 774 | 0 0 0 2 62 9 1 0 0 0 15 46 194 11 0 0 0 0 3 35 775 | 161 5 0 0 0 0 0 6 9 1 0 0 0 0 0 1 50 8 0 0 776 | 0 0 11 44 194 2 0 0 0 0 1 80 103 2 0 0 0 0 1 15 777 | 7 0 0 0 0 0 0 2 778 | 129.564668 92.045897 0.996182 -2.632879 779 | 60 18 11 54 10 0 0 17 185 85 2 0 0 0 0 7 101 18 0 0 780 | 0 0 0 3 1 1 1 0 0 0 1 1 60 23 6 12 7 0 0 22 781 | 185 40 1 0 0 0 0 32 161 18 0 0 0 0 0 4 3 5 1 0 782 | 0 0 2 2 95 0 0 0 4 3 1 44 185 1 0 0 0 0 0 69 783 | 144 7 0 0 0 0 1 18 8 4 0 0 0 0 0 3 96 6 0 1 784 | 5 1 1 51 185 20 0 0 0 0 0 45 108 18 0 0 0 0 0 5 785 | 3 1 0 0 0 0 0 2 786 | 162.526154 95.288088 0.994658 -1.123028 787 | 8 2 1 21 19 4 11 21 51 7 2 8 7 6 13 32 13 8 1 1 788 | 12 16 50 31 45 2 2 8 22 6 5 47 28 3 4 28 30 10 4 11 789 | 138 3 0 3 6 11 33 138 16 0 0 2 95 86 97 86 45 0 0 2 790 | 36 15 32 138 24 3 1 3 15 45 23 15 138 64 0 0 6 16 5 45 791 | 98 14 0 0 32 138 72 51 22 0 0 0 3 18 48 138 23 8 3 12 792 | 53 55 16 24 138 8 0 0 12 9 4 138 87 2 0 0 0 8 12 44 793 | 2 0 0 0 0 2 12 18 794 | 13.886341 46.545553 0.989916 1.764112 795 | 92 49 0 0 0 0 0 10 187 41 0 0 0 0 0 1 98 10 1 0 796 | 0 0 0 2 1 2 2 2 0 0 0 1 102 25 1 0 0 0 0 19 797 | 187 42 0 0 0 0 0 6 142 13 1 3 0 0 0 0 5 4 3 1 798 | 0 0 0 0 128 17 0 0 0 0 0 32 187 12 0 0 0 0 0 40 799 | 146 9 0 1 0 0 0 7 6 4 1 1 0 0 1 1 113 18 0 0 800 | 0 0 0 16 187 8 0 0 0 0 0 56 80 1 0 0 0 0 1 25 801 | 1 0 0 0 0 0 5 4 802 | 35.666125 107.431468 0.986612 1.071431 803 | 39 13 3 24 11 0 1 0 194 62 0 0 0 0 0 1 111 16 0 0 804 | 1 5 0 2 1 2 0 0 26 64 1 0 55 7 2 11 25 1 0 6 805 | 194 21 0 0 0 0 0 40 150 16 6 0 0 0 0 10 2 2 2 0 806 | 13 20 0 3 65 8 1 8 8 2 3 7 194 29 0 0 0 0 0 18 807 | 160 10 0 0 0 0 0 13 2 0 0 1 3 3 1 6 57 12 1 2 808 | 1 0 2 8 194 26 0 0 0 0 0 9 144 13 0 0 0 0 0 3 809 | 3 0 0 0 1 1 1 1 810 | 128.301610 148.296380 0.985083 0.352996 811 | 34 50 37 5 0 0 1 10 69 77 61 9 0 0 0 30 36 31 106 41 812 | 0 0 7 54 24 11 50 48 6 2 9 21 53 117 47 35 1 0 0 2 813 | 43 102 117 82 0 0 0 1 117 91 117 43 2 0 1 42 41 14 56 117 814 | 46 3 6 40 24 35 61 24 1 0 2 11 85 58 63 59 0 0 0 14 815 | 117 83 69 82 6 0 0 16 44 44 64 117 16 1 0 12 6 6 46 18 816 | 14 1 0 5 45 59 16 3 0 0 1 42 41 62 20 31 13 1 1 13 817 | 5 19 29 37 17 2 0 0 818 | 38.644750 146.654798 0.978945 1.467918 819 | 35 3 5 7 0 2 4 3 204 11 1 0 0 0 0 4 124 7 0 0 820 | 0 0 0 3 2 0 0 0 0 0 0 1 59 8 9 15 21 2 0 1 821 | 204 10 1 0 0 0 0 24 147 3 0 0 0 0 0 13 3 1 0 0 822 | 0 0 0 0 63 6 0 2 45 3 0 0 204 39 0 0 1 0 0 3 823 | 145 14 0 0 0 0 0 4 6 0 0 0 1 0 0 1 40 1 0 4 824 | 20 6 1 8 204 10 0 0 0 0 0 18 136 6 0 0 0 0 0 4 825 | 2 0 0 0 0 0 0 1 826 | 34.892903 100.951505 0.970812 1.230181 827 | 0 4 2 2 11 14 3 0 116 41 2 0 2 5 5 3 192 79 0 0 828 | 0 0 0 1 32 7 0 0 72 26 0 0 0 0 1 4 9 11 29 2 829 | 109 9 1 8 5 5 16 8 192 38 0 0 0 0 0 17 73 7 0 0 830 | 44 32 0 1 2 1 2 34 33 5 9 1 132 10 10 33 6 0 0 7 831 | 192 6 0 0 0 0 0 37 73 4 0 0 11 12 0 7 1 4 4 31 832 | 36 0 0 0 133 3 1 6 6 0 0 42 192 2 0 0 0 0 0 93 833 | 36 4 2 0 1 1 0 8 834 | 112.853086 208.314477 0.965988 0.159766 835 | 61 21 1 0 0 0 0 20 183 3 0 0 0 0 0 99 120 2 0 0 836 | 0 0 0 25 4 3 1 0 0 0 0 1 116 30 1 1 0 0 0 11 837 | 183 47 0 0 0 0 0 24 119 6 0 0 0 0 0 14 5 3 1 0 838 | 0 0 2 4 106 4 0 0 0 0 0 35 183 17 0 0 0 0 0 69 839 | 132 6 0 0 0 0 0 15 3 1 0 0 0 0 2 4 123 1 0 0 840 | 0 0 1 40 183 0 0 0 0 0 0 99 55 4 4 0 0 0 0 19 841 | 3 8 1 0 0 0 0 0 842 | 146.010341 178.034659 0.961971 -0.004200 843 | 34 30 9 19 27 20 3 7 64 21 1 17 40 30 4 10 92 30 1 1 844 | 16 54 20 24 9 11 1 1 12 101 88 23 68 32 1 0 20 41 8 11 845 | 64 33 0 1 60 115 14 13 115 56 1 2 7 19 18 79 21 9 1 5 846 | 49 74 60 31 99 13 1 1 44 113 31 38 89 5 2 6 50 115 64 46 847 | 115 82 2 1 6 68 85 38 72 44 5 5 18 31 28 24 7 0 0 4 848 | 49 96 50 23 27 14 8 6 17 21 47 32 73 38 11 3 20 66 60 37 849 | 79 30 12 5 25 25 20 48 850 | 154.548208 98.746245 0.960532 1.937792 851 | 19 5 13 122 122 5 1 10 75 12 4 11 30 9 12 58 95 38 5 32 852 | 21 6 9 19 107 22 9 51 23 9 4 9 22 11 16 40 101 22 6 23 853 | 58 20 40 97 24 4 3 40 79 66 14 59 32 5 0 3 122 14 5 33 854 | 29 8 1 18 19 15 12 58 63 3 2 10 48 10 21 41 33 56 10 23 855 | 122 11 3 6 16 39 13 67 122 9 0 0 18 50 10 28 62 3 6 36 856 | 16 0 2 45 63 0 0 1 31 96 21 64 122 14 9 13 31 36 13 75 857 | 122 2 5 16 45 25 5 42 858 | 154.548208 98.746245 0.960532 -2.077363 859 | 22 37 51 11 23 36 12 15 6 17 67 17 58 94 0 0 48 73 6 2 860 | 9 24 13 11 33 51 17 1 0 8 19 21 13 28 37 14 57 140 15 10 861 | 15 22 72 14 37 136 6 3 89 28 16 5 20 36 19 45 41 132 23 8 862 | 31 46 5 8 1 26 75 16 29 124 7 0 15 23 32 8 20 109 39 6 863 | 79 41 3 2 10 61 60 16 10 41 8 11 47 93 13 6 0 0 3 0 864 | 7 140 71 0 37 36 9 0 4 140 26 6 57 36 4 3 6 66 52 10 865 | 10 6 5 17 29 71 14 10 866 | 68.623393 95.033572 0.948111 2.082340 867 | 57 66 11 0 0 15 13 5 93 66 14 1 0 0 9 63 56 25 3 1 868 | 4 7 11 69 12 7 19 8 4 7 20 28 22 42 38 4 0 23 11 2 869 | 83 124 25 0 0 1 2 20 124 124 7 0 4 6 31 47 23 2 7 12 870 | 22 22 78 37 22 50 27 7 3 3 1 3 124 101 12 1 0 0 8 76 871 | 124 124 23 3 3 0 3 54 42 45 33 28 33 3 11 27 5 19 8 7 872 | 12 13 10 8 68 124 11 2 4 3 5 72 118 115 14 0 0 0 0 71 873 | 94 67 19 2 0 0 15 46 874 | 68.623393 95.033572 0.948111 2.763265 875 | 38 49 2 1 7 6 2 4 66 19 3 0 20 19 4 49 80 17 2 0 876 | 0 4 54 87 61 14 1 0 0 1 26 49 47 28 9 4 3 2 4 20 877 | 122 41 1 0 1 2 28 60 122 31 0 0 2 7 37 114 14 5 4 7 878 | 11 26 43 40 117 22 7 8 6 4 26 42 122 23 0 0 0 4 70 122 879 | 121 19 6 13 3 13 30 122 4 6 10 19 19 75 44 23 51 18 0 0 880 | 1 3 106 74 122 23 0 0 0 0 48 122 86 40 21 15 2 9 28 64 881 | 12 9 13 26 31 21 24 23 882 | 164.982290 153.967711 0.940685 -1.373956 883 | 28 3 0 0 0 0 0 29 151 4 0 0 0 0 0 94 151 1 0 0 884 | 0 0 0 62 7 2 1 0 0 0 0 3 62 5 2 0 0 0 1 27 885 | 151 16 0 0 0 0 0 115 142 11 0 0 0 0 0 43 7 2 0 0 886 | 0 0 1 5 77 20 6 0 0 0 0 23 151 151 5 2 0 0 0 11 887 | 151 83 0 0 0 0 0 7 12 2 0 0 0 0 0 2 33 1 1 7 888 | 13 0 0 37 151 67 7 17 19 0 0 4 151 26 0 0 0 0 0 3 889 | 9 0 0 0 0 0 0 3 890 | 56.984132 184.521113 0.921827 0.867978 891 | 50 28 1 0 0 0 0 2 187 14 0 0 0 0 0 20 147 5 0 0 892 | 0 0 0 13 6 2 0 0 0 0 1 1 59 9 0 0 0 1 13 18 893 | 187 11 0 0 0 0 1 12 170 7 0 0 0 0 0 7 9 8 1 0 894 | 0 0 0 0 51 0 0 1 1 1 5 35 187 12 0 0 0 0 0 75 895 | 167 12 0 0 0 0 0 12 4 3 0 0 0 0 2 4 85 18 2 3 896 | 0 0 0 14 187 8 0 0 0 0 0 83 88 4 0 0 0 1 1 28 897 | 4 5 0 0 0 0 1 2 898 | 57.872880 23.991598 0.909354 -2.164589 899 | 9 8 24 23 7 2 0 0 138 15 10 1 0 0 0 41 117 0 0 0 900 | 0 0 0 63 3 0 0 15 25 2 0 3 28 3 3 5 28 23 0 1 901 | 138 11 0 0 2 2 0 51 126 5 1 0 0 0 0 33 1 1 0 46 902 | 90 0 0 0 35 21 6 5 9 27 24 9 138 62 0 0 1 15 21 17 903 | 104 51 13 6 1 0 0 3 0 1 7 131 138 0 0 0 67 120 8 7 904 | 16 12 12 5 109 73 20 1 1 19 25 15 80 60 56 27 9 0 0 1 905 | 0 0 20 138 138 2 1 0 906 | 107.388054 164.979364 0.909067 -3.126479 907 | 57 1 2 0 3 18 39 112 57 42 15 11 22 17 10 112 12 22 25 62 908 | 49 24 3 5 16 11 3 12 32 25 19 4 72 42 1 3 9 31 37 71 909 | 33 79 20 40 112 44 11 16 112 88 14 9 23 80 30 23 13 3 5 31 910 | 55 89 20 6 31 24 2 2 5 50 59 54 17 18 19 19 45 98 112 55 911 | 112 15 6 42 30 42 82 85 18 4 4 110 112 48 4 3 32 19 13 17 912 | 18 23 15 15 14 30 99 22 10 23 16 9 26 30 20 41 76 15 13 8 913 | 10 0 1 50 64 35 20 19 914 | 107.388054 164.979364 0.909067 1.255391 915 | 27 4 6 12 9 16 28 20 26 2 11 9 4 29 110 109 10 10 20 7 916 | 1 56 110 41 16 8 7 16 6 10 67 45 40 4 6 8 4 29 36 46 917 | 68 23 100 110 27 5 12 62 109 87 90 38 10 17 34 23 18 6 12 52 918 | 54 37 19 12 3 15 53 38 29 46 28 19 12 7 14 78 58 17 86 82 919 | 110 71 28 27 15 11 54 62 26 41 16 11 52 41 20 10 4 104 110 17 920 | 4 1 1 5 25 83 110 37 8 1 10 29 54 38 37 52 8 1 3 31 921 | 34 35 36 53 0 1 2 16 922 | 21.416703 65.439962 0.904808 2.453988 923 | 49 65 63 1 0 0 0 1 65 56 8 2 9 4 2 6 3 2 0 6 924 | 50 32 7 7 8 0 0 0 3 3 10 41 114 9 0 0 0 2 4 18 925 | 132 18 0 0 2 12 15 88 16 0 0 0 58 120 28 30 43 2 0 0 926 | 12 17 8 39 51 1 0 0 0 4 37 69 132 30 1 1 6 3 10 36 927 | 44 13 2 6 100 57 25 19 6 0 0 0 12 18 78 132 8 0 0 0 928 | 0 2 48 78 73 9 0 0 0 1 55 132 16 7 2 3 7 3 132 132 929 | 0 0 0 0 0 1 132 132 930 | 162.713935 187.031385 0.901632 -1.680706 931 | 2 3 15 31 2 1 13 29 26 1 10 17 0 0 26 156 156 5 0 1 932 | 0 0 1 156 58 1 0 0 0 0 0 68 1 0 4 4 0 0 78 110 933 | 61 18 12 5 0 0 23 125 156 42 1 0 0 0 0 45 89 7 0 0 934 | 0 0 0 7 2 2 23 28 0 0 14 17 88 32 24 16 0 0 2 6 935 | 156 77 0 0 0 0 0 26 84 21 1 0 0 0 0 6 2 13 27 11 936 | 0 0 2 1 40 37 26 10 0 1 4 1 156 105 1 0 0 0 0 4 937 | 99 26 1 0 0 0 1 5 938 | 70.956226 196.377661 0.900613 0.631911 939 | 60 21 6 1 0 0 0 2 195 52 0 0 0 0 0 4 110 14 1 0 940 | 0 0 0 4 1 0 1 0 0 0 1 3 68 10 2 0 0 2 7 18 941 | 195 31 0 0 0 0 1 36 160 13 0 0 0 0 0 8 6 5 0 0 942 | 0 0 0 2 80 5 0 0 0 1 3 12 195 21 0 0 0 0 0 12 943 | 161 7 0 0 0 0 0 4 8 6 0 0 0 0 0 1 46 23 5 1 944 | 0 0 0 2 195 25 0 0 0 0 0 4 138 2 0 0 0 0 0 7 945 | 2 0 0 0 0 0 0 4 946 | --------------------------------------------------------------------------------