opencv-ffi-fast 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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