opencv-ffi-fast 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ require '../mkrf-monkey'
2
+
3
+ Mkrf::Generator.new('libcvffi_fast', [ "*.c"]) { |g|
4
+ }
5
+
6
+
@@ -0,0 +1,117 @@
1
+ #include <stdlib.h>
2
+ #include "fast.h"
3
+
4
+
5
+ #define Compare(X, Y) ((X)>=(Y))
6
+
7
+ xy* nonmax_suppression(const xy* corners, const int* scores, int num_corners, int* ret_num_nonmax)
8
+ {
9
+ int num_nonmax=0;
10
+ int last_row;
11
+ int* row_start;
12
+ int i, j;
13
+ xy* ret_nonmax;
14
+ const int sz = (int)num_corners;
15
+
16
+ /*Point above points (roughly) to the pixel above the one of interest, if there
17
+ is a feature there.*/
18
+ int point_above = 0;
19
+ int point_below = 0;
20
+
21
+
22
+ if(num_corners < 1)
23
+ {
24
+ *ret_num_nonmax = 0;
25
+ return 0;
26
+ }
27
+
28
+ ret_nonmax = (xy*)malloc(num_corners * sizeof(xy));
29
+
30
+ /* Find where each row begins
31
+ (the corners are output in raster scan order). A beginning of -1 signifies
32
+ that there are no corners on that row. */
33
+ last_row = corners[num_corners-1].y;
34
+ row_start = (int*)malloc((last_row+1)*sizeof(int));
35
+
36
+ for(i=0; i < last_row+1; i++)
37
+ row_start[i] = -1;
38
+
39
+ {
40
+ int prev_row = -1;
41
+ for(i=0; i< num_corners; i++)
42
+ if(corners[i].y != prev_row)
43
+ {
44
+ row_start[corners[i].y] = i;
45
+ prev_row = corners[i].y;
46
+ }
47
+ }
48
+
49
+
50
+
51
+ for(i=0; i < sz; i++)
52
+ {
53
+ int score = scores[i];
54
+ xy pos = corners[i];
55
+
56
+ /*Check left */
57
+ if(i > 0)
58
+ if(corners[i-1].x == pos.x-1 && corners[i-1].y == pos.y && Compare(scores[i-1], score))
59
+ continue;
60
+
61
+ /*Check right*/
62
+ if(i < (sz - 1))
63
+ if(corners[i+1].x == pos.x+1 && corners[i+1].y == pos.y && Compare(scores[i+1], score))
64
+ continue;
65
+
66
+ /*Check above (if there is a valid row above)*/
67
+ if(pos.y != 0 && row_start[pos.y - 1] != -1)
68
+ {
69
+ /*Make sure that current point_above is one
70
+ row above.*/
71
+ if(corners[point_above].y < pos.y - 1)
72
+ point_above = row_start[pos.y-1];
73
+
74
+ /*Make point_above point to the first of the pixels above the current point,
75
+ if it exists.*/
76
+ for(; corners[point_above].y < pos.y && corners[point_above].x < pos.x - 1; point_above++)
77
+ {}
78
+
79
+
80
+ for(j=point_above; corners[j].y < pos.y && corners[j].x <= pos.x + 1; j++)
81
+ {
82
+ int x = corners[j].x;
83
+ if( (x == pos.x - 1 || x ==pos.x || x == pos.x+1) && Compare(scores[j], score))
84
+ goto cont;
85
+ }
86
+
87
+ }
88
+
89
+ /*Check below (if there is anything below)*/
90
+ if(pos.y != last_row && row_start[pos.y + 1] != -1 && point_below < sz) /*Nothing below*/
91
+ {
92
+ if(corners[point_below].y < pos.y + 1)
93
+ point_below = row_start[pos.y+1];
94
+
95
+ /* Make point below point to one of the pixels belowthe current point, if it
96
+ exists.*/
97
+ for(; point_below < sz && corners[point_below].y == pos.y+1 && corners[point_below].x < pos.x - 1; point_below++)
98
+ {}
99
+
100
+ for(j=point_below; j < sz && corners[j].y == pos.y+1 && corners[j].x <= pos.x + 1; j++)
101
+ {
102
+ int x = corners[j].x;
103
+ if( (x == pos.x - 1 || x ==pos.x || x == pos.x+1) && Compare(scores[j],score))
104
+ goto cont;
105
+ }
106
+ }
107
+
108
+ ret_nonmax[num_nonmax++] = corners[i];
109
+ cont:
110
+ ;
111
+ }
112
+
113
+ free(row_start);
114
+ *ret_num_nonmax = num_nonmax;
115
+ return ret_nonmax;
116
+ }
117
+
@@ -0,0 +1,85 @@
1
+
2
+ require 'mkrf'
3
+
4
+ # I admit it, I'm monkey-patching mkrf to get the behavior I want.
5
+ module Mkrf
6
+
7
+ class Generator
8
+
9
+ def rakefile_contents # :nodoc:
10
+ objext = CONFIG['OBJEXT']
11
+ cc = CONFIG['CC'] || 'gcc'
12
+ cpp = CONFIG['CXX'] || 'g++'
13
+ extension_sym = File.basename( @extension_name, ".#{CONFIG['DLEXT']}" ).to_sym
14
+
15
+
16
+ <<-END_RAKEFILE
17
+ # Generated by mkrf, monkey patched for opencv-ffi
18
+ require 'rake/clean'
19
+
20
+
21
+ SRC = FileList[#{sources.join(',')}]
22
+ OBJ = SRC.ext('#{objext}')
23
+ CC = '#{cc}'
24
+ CPP = '#{cpp}'
25
+
26
+ CLEAN.include(OBJ)
27
+ CLOBBER.include('#{@extension_name}', 'mkrf.log', 'Rakefile')
28
+
29
+ ADDITIONAL_OBJECTS = '#{objects}'
30
+
31
+ LDSHARED = "#{@available.ldshared_string} #{ldshared}"
32
+
33
+ LIBPATH = "#{library_path(CONFIG['libdir'])} #{@available.library_paths_compile_string}"
34
+
35
+ INCLUDES = "#{@available.includes_compile_string}"
36
+
37
+ LIBS = "#{@available.library_compile_string}"
38
+
39
+ CFLAGS = "#{cflags} #{defines_compile_string}"
40
+
41
+ RUBYARCHDIR = "\#{ENV["RUBYARCHDIR"]}"
42
+ LIBRUBYARG_SHARED = "#{CONFIG['LIBRUBYARG_SHARED']}"
43
+
44
+ task :default => :build_library
45
+
46
+ # Add one layer of indirection so I can generically call "rake build_library"
47
+ # and have it work ... or not work if the wrong rakefile is being run
48
+ task :build_library => '#{@extension_name}'
49
+
50
+ rule '.#{objext}' => '.c' do |t|
51
+ sh "\#{CC} \#{CFLAGS} \#{INCLUDES} -o \#{t.name} -c \#{t.source}"
52
+ end
53
+
54
+ rule '.#{objext}' => '.cpp' do |t|
55
+ sh "\#{CPP} \#{CFLAGS} \#{INCLUDES} -o \#{t.name} -c \#{t.source}"
56
+ end
57
+
58
+ DEPS = OBJ.clone.add('Rakefile')
59
+ desc "Build this extension"
60
+ file '#{@extension_name}' => DEPS do
61
+ sh "\#{LDSHARED} \#{LIBPATH} #{@available.ld_outfile(@extension_name)} \#{OBJ} \#{ADDITIONAL_OBJECTS} \#{LIBS} \#{LIBRUBYARG_SHARED}"
62
+ end
63
+
64
+ desc "Rebuild rakefile"
65
+ file 'Rakefile' => 'mkrf_conf.rb' do |t|
66
+ ruby 'mkrf_conf.rb'
67
+ puts "Rebuilt Rakefile. Run \'rake #{extension_sym.to_s}\' again"
68
+ end
69
+
70
+ desc "Install this extension"
71
+ task :install => '#{@extension_name}' do
72
+ makedirs "\#{RUBYARCHDIR}"
73
+ install "#{@extension_name}", "\#{RUBYARCHDIR}"
74
+ end
75
+
76
+ #{additional_code}
77
+ END_RAKEFILE
78
+ end
79
+
80
+
81
+ end
82
+
83
+ end
84
+
85
+
@@ -0,0 +1,52 @@
1
+
2
+ require 'mkrf/rakehelper'
3
+
4
+ alias :old_setup_extension :setup_extension
5
+
6
+ module Mkrf
7
+ @all_libs = []
8
+
9
+ def self.all_libs
10
+ @all_libs
11
+ end
12
+
13
+ def self.all_libs=(a)
14
+ @all_libs = a
15
+ end
16
+ end
17
+
18
+ def rake( rakedir, args = nil )
19
+ Dir.chdir( rakedir ) do
20
+ if args
21
+ sh "rake #{args}"
22
+ else
23
+ sh 'rake'
24
+ end
25
+ end
26
+ end
27
+
28
+ def setup_extension(dir, extension)
29
+
30
+ old_setup_extension( dir, extension )
31
+
32
+ ext_dir = "ext/#{dir}"
33
+ ext_so = "#{ext_dir}/#{extension}.#{Config::CONFIG['DLEXT']}"
34
+
35
+ task ext_so => FileList["#{ext_dir}/**/*.c*", "#{ext_dir}/mkrf_conf.rb"]
36
+
37
+ Mkrf::all_libs << extension.to_sym
38
+
39
+ namespace extension.to_sym do
40
+
41
+ desc "Run \"rake clean\" in #{ext_dir}"
42
+ task :clean do
43
+ rake ext_dir, 'clean'
44
+ end
45
+
46
+ desc "Run \"rake clobber\" in #{ext_dir}"
47
+ task :clobber do
48
+ rake extd_dir, 'clobber'
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,3 @@
1
+ #
2
+ # Just a shell to fake out Gem
3
+ #
@@ -0,0 +1 @@
1
+ require "opencv-ffi-fast/version"
@@ -0,0 +1,113 @@
1
+
2
+ require 'nice-ffi'
3
+ require 'opencv-ffi'
4
+
5
+ module CVFFI
6
+ module FAST
7
+
8
+ class FASTResultsArray
9
+ include Enumerable
10
+
11
+ attr_accessor :points
12
+ attr_accessor :nPoints
13
+
14
+ def initialize( pts, nPts )
15
+ @points = pts
16
+ @nPoints = nPts
17
+
18
+ # Define a destructor do dispose of the results
19
+ destructor = Proc.new { pts.free }
20
+ ObjectSpace.define_finalizer( self, destructor )
21
+ end
22
+
23
+ def each
24
+ if @nPoints > 0
25
+ 0.upto(@nPoints-1) { |i|
26
+ yield Xy.new( @points[i] )
27
+ }
28
+ end
29
+ end
30
+
31
+ alias :size :nPoints
32
+
33
+ end
34
+
35
+ class Params
36
+ attr_accessor :points, :threshold
37
+
38
+ VALID_SIZES = [ 9, 10, 11, 12 ]
39
+
40
+ def size_valid?( sz )
41
+ VALID_SIZES.include? sz
42
+ end
43
+
44
+ def initialize( opts = {} )
45
+ p opts
46
+ @points = opts[:points] || 9
47
+ @threshold = opts[:threshold] || 20
48
+
49
+ raise "Hm, invalid size #{@points} specified for FAST keypoint detector" unless size_valid? @points
50
+ end
51
+
52
+ def to_hash
53
+ { :points => @points, :threshold => @threshold }
54
+ end
55
+ end
56
+
57
+
58
+ def self.detect( img, params )
59
+ FASTDetect( params.points, img, params.threshold )
60
+ end
61
+
62
+ def self.FASTDetect( size, img, threshold )
63
+ nResults = FFI::MemoryPointer.new :int
64
+
65
+ if img.is_a?( IplImage )
66
+ # Ensure the image is b&w
67
+ img = img.ensure_greyscale
68
+
69
+ results = FFI::Pointer.new :pointer, method("fast#{size}_detect").call( img.imageData, img.width, img.height, img.widthStep, threshold, nResults )
70
+ else
71
+ raise ArgumentError, "Don't know how to deal with image class #{img.class}"
72
+ end
73
+
74
+ # Dereference the two pointers
75
+ nPoints = nResults.read_int
76
+ points = FFI::Pointer.new Xy, results
77
+
78
+ FASTResultsArray.new( points, nPoints )
79
+ end
80
+
81
+ def self.FAST9Detect( img, threshold ); FASTDetect( 9, img, threshold ); end
82
+ def self.FAST10Detect( img, threshold ); FASTDetect( 10, img, threshold ); end
83
+ def self.FAST11Detect( img, threshold ); FASTDetect( 11, img, threshold ); end
84
+ def self.FAST12Detect( img, threshold ); FASTDetect( 12, img, threshold ); end
85
+
86
+ #
87
+ ## Here's the actual FFI interface
88
+ #
89
+ extend NiceFFI::Library
90
+
91
+ libs_dir = File.dirname(__FILE__) + "/../../ext/fast/"
92
+ pathset = NiceFFI::PathSet::DEFAULT.prepend( libs_dir )
93
+ load_library("cvffi_fast", pathset)
94
+
95
+ class Xy < NiceFFI::Struct
96
+ layout :x, :int,
97
+ :y, :int
98
+ end
99
+
100
+ # Leave result as pointer, not Xy.typed_pointer as it makes it easier to deference
101
+ # it to an array of Xy
102
+
103
+ def self.fast_detect_function( sz )
104
+ attach_function "fast#{sz}_detect".to_sym, [ :pointer, :int, :int, :int, :int, :pointer ], :pointer
105
+ end
106
+
107
+ fast_detect_function 9
108
+ fast_detect_function 10
109
+ fast_detect_function 11
110
+ fast_detect_function 12
111
+
112
+ end
113
+ end
@@ -0,0 +1,5 @@
1
+ module CVFFI
2
+ module FAST
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "opencv-ffi-fast/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "opencv-ffi-fast"
7
+ s.version = CVFFI::FAST::VERSION
8
+ s.authors = ["Aaron Marburg"]
9
+ s.email = ["aaron.marburg@pg.canterbury.ac.nz"]
10
+ s.homepage = "http://github.com/amarburg/opencv-ffi-fast"
11
+ s.summary = %q{Edward Rosten's FAST keypoint detector algorithm, as a plug-in for OpenCV-FFI.}
12
+ s.description = %q{Edward Rosten's FAST keypoint detector algorithm, as a plug-in for OpenCV-FFI.}
13
+
14
+ s.rubyforge_project = "opencv-ffi-fast"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.extensions = "ext/mkrf_conf.rb"
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "nice-ffi"
23
+ s.add_dependency "mkrf"
24
+ s.add_dependency "opencv-ffi"
25
+ end
@@ -0,0 +1,50 @@
1
+
2
+ require 'test/setup'
3
+ require 'opencv-ffi-ext/fast'
4
+ require 'opencv-ffi'
5
+
6
+ class TestSURF < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @img = CVFFI::cvLoadImage( TEST_IMAGE_FILE, CVFFI::CV_LOAD_IMAGE_COLOR )
10
+ end
11
+
12
+
13
+ def test_cvFASTDetector
14
+ greyImg = CVFFI::cvCreateImage( CVFFI::CvSize.new( { :height => @img.height,
15
+ :width => @img.width }),
16
+ :IPL_DEPTH_8U, 1 )
17
+ CVFFI::cvCvtColor( @img, greyImg, :CV_RGB2GRAY )
18
+
19
+ smallGreyImg = CVFFI::cvCreateImage( CVFFI::CvSize.new( { :height => greyImg.height/2,
20
+ :width => greyImg.width/2 } ),
21
+ :IPL_DEPTH_8U, 1 )
22
+
23
+ CVFFI::cvResize( greyImg, smallGreyImg, :CV_INTER_LINEAR )
24
+
25
+ #CVFFI::cvSaveImage( TestSetup::output_filename("greyImage.jpg"), smallGreyImg.to_ptr )
26
+
27
+ nResults = FFI::MemoryPointer.new :int
28
+
29
+ results = FFI::Pointer.new :pointer, CVFFI::FAST::fast12_detect( smallGreyImg.imageData, smallGreyImg.width, smallGreyImg.height, smallGreyImg.widthStep, 50, nResults )
30
+
31
+ # Dereference the two pointers
32
+ nPoints = nResults.read_int
33
+ points = FFI::Pointer.new CVFFI::FAST::Xy, results
34
+
35
+ #p points.inspect
36
+ #p nPoints.inspect
37
+
38
+ 0.upto(nPoints-1) { |i|
39
+ pt = CVFFI::FAST::Xy.new( points[i] )
40
+
41
+ CVFFI::cvCircle( smallGreyImg, CVFFI::CvPoint.new( :x => pt.x.to_i, :y => pt.y.to_i ), 5,
42
+ CVFFI::CvScalar.new( :w=>255, :x=>255, :y=>255, :z=>0 ), -1, 8, 0 )
43
+ }
44
+ CVFFI::cvSaveImage( TestSetup::output_filename("greyImageFASTPts.jpg"), smallGreyImg )
45
+
46
+
47
+ results.free
48
+ end
49
+
50
+ end