opencv-ffi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +15 -0
- data/README.md +126 -0
- data/Rakefile +52 -0
- data/docs/DocsIndex.md +1 -0
- data/docs/examples/load_image.rb +25 -0
- data/ext/Rakefile +13 -0
- data/ext/aishack-sift/.gitignore +4 -0
- data/ext/aishack-sift/Descriptor.h +34 -0
- data/ext/aishack-sift/KeyPoint.h +38 -0
- data/ext/aishack-sift/README +20 -0
- data/ext/aishack-sift/SIFT.cpp +1036 -0
- data/ext/aishack-sift/SIFT.h +84 -0
- data/ext/aishack-sift/example/.gitignore +2 -0
- data/ext/aishack-sift/example/Makefile +24 -0
- data/ext/aishack-sift/example/MySIFT.cpp +29 -0
- data/ext/aishack-sift/mkrf_conf.rb +13 -0
- data/ext/aishack-sift/siftlib.cpp +85 -0
- data/ext/eigen/.gitignore +4 -0
- data/ext/eigen/eigen_polynomial.cpp +41 -0
- data/ext/eigen/eigen_svd.cpp +100 -0
- data/ext/eigen/mkrf_conf.rb +14 -0
- data/ext/mkrf-monkey.rb +85 -0
- data/ext/mkrf-rakehelper-monkey.rb +52 -0
- data/ext/mkrf_conf.rb +3 -0
- data/ext/opencv-ffi/.gitignore +4 -0
- data/ext/opencv-ffi/matcher_helper.cpp +56 -0
- data/ext/opencv-ffi/mkrf_conf.rb +12 -0
- data/ext/opencv-ffi/vector_math.cpp +39 -0
- data/ext/opensurf/.gitignore +4 -0
- data/ext/opensurf/README +38 -0
- data/ext/opensurf/fasthessian.cpp +376 -0
- data/ext/opensurf/fasthessian.h +108 -0
- data/ext/opensurf/integral.cpp +58 -0
- data/ext/opensurf/integral.h +55 -0
- data/ext/opensurf/ipoint.cpp +108 -0
- data/ext/opensurf/ipoint.h +76 -0
- data/ext/opensurf/kmeans.h +172 -0
- data/ext/opensurf/mkrf_conf.rb +10 -0
- data/ext/opensurf/responselayer.h +92 -0
- data/ext/opensurf/surf.cpp +317 -0
- data/ext/opensurf/surf.h +66 -0
- data/ext/opensurf/surflib.cpp +98 -0
- data/ext/opensurf/surflib.h +96 -0
- data/ext/opensurf/utils.cpp +357 -0
- data/ext/opensurf/utils.h +63 -0
- data/lib/.gitignore +1 -0
- data/lib/opencv-ffi-ext/eigen.rb +84 -0
- data/lib/opencv-ffi-ext/features2d.rb +4 -0
- data/lib/opencv-ffi-ext/matcher_helper.rb +24 -0
- data/lib/opencv-ffi-ext/opensurf.rb +217 -0
- data/lib/opencv-ffi-ext/sift.rb +118 -0
- data/lib/opencv-ffi-ext/vector_math.rb +115 -0
- data/lib/opencv-ffi-wrappers.rb +7 -0
- data/lib/opencv-ffi-wrappers/core.rb +24 -0
- data/lib/opencv-ffi-wrappers/core/iplimage.rb +50 -0
- data/lib/opencv-ffi-wrappers/core/mat.rb +268 -0
- data/lib/opencv-ffi-wrappers/core/misc_draw.rb +44 -0
- data/lib/opencv-ffi-wrappers/core/point.rb +286 -0
- data/lib/opencv-ffi-wrappers/core/rect.rb +40 -0
- data/lib/opencv-ffi-wrappers/core/scalar.rb +104 -0
- data/lib/opencv-ffi-wrappers/core/size.rb +88 -0
- data/lib/opencv-ffi-wrappers/enumerable.rb +10 -0
- data/lib/opencv-ffi-wrappers/features2d.rb +17 -0
- data/lib/opencv-ffi-wrappers/features2d/image_patch.rb +322 -0
- data/lib/opencv-ffi-wrappers/features2d/star.rb +111 -0
- data/lib/opencv-ffi-wrappers/features2d/surf.rb +115 -0
- data/lib/opencv-ffi-wrappers/highgui.rb +10 -0
- data/lib/opencv-ffi-wrappers/imgproc.rb +4 -0
- data/lib/opencv-ffi-wrappers/imgproc/features.rb +35 -0
- data/lib/opencv-ffi-wrappers/imgproc/geometric.rb +39 -0
- data/lib/opencv-ffi-wrappers/matcher.rb +297 -0
- data/lib/opencv-ffi-wrappers/matrix.rb +37 -0
- data/lib/opencv-ffi-wrappers/misc.rb +41 -0
- data/lib/opencv-ffi-wrappers/misc/params.rb +34 -0
- data/lib/opencv-ffi-wrappers/sequence.rb +37 -0
- data/lib/opencv-ffi-wrappers/vectors.rb +38 -0
- data/lib/opencv-ffi.rb +12 -0
- data/lib/opencv-ffi/calib3d.rb +26 -0
- data/lib/opencv-ffi/core.rb +15 -0
- data/lib/opencv-ffi/core/draw.rb +68 -0
- data/lib/opencv-ffi/core/dynamic.rb +13 -0
- data/lib/opencv-ffi/core/library.rb +5 -0
- data/lib/opencv-ffi/core/operations.rb +122 -0
- data/lib/opencv-ffi/core/point.rb +22 -0
- data/lib/opencv-ffi/core/types.rb +172 -0
- data/lib/opencv-ffi/cvffi.rb +8 -0
- data/lib/opencv-ffi/features2d.rb +7 -0
- data/lib/opencv-ffi/features2d/library.rb +6 -0
- data/lib/opencv-ffi/features2d/star.rb +30 -0
- data/lib/opencv-ffi/features2d/surf.rb +38 -0
- data/lib/opencv-ffi/highgui.rb +31 -0
- data/lib/opencv-ffi/imgproc.rb +9 -0
- data/lib/opencv-ffi/imgproc/features.rb +37 -0
- data/lib/opencv-ffi/imgproc/geometric.rb +42 -0
- data/lib/opencv-ffi/imgproc/library.rb +6 -0
- data/lib/opencv-ffi/imgproc/misc.rb +39 -0
- data/lib/opencv-ffi/version.rb +3 -0
- data/opencv-ffi.gemspec +26 -0
- data/test/core/test_draw.rb +46 -0
- data/test/core/test_operations.rb +135 -0
- data/test/core/test_size.rb +14 -0
- data/test/core/test_text.rb +52 -0
- data/test/ext/test_eigen.rb +105 -0
- data/test/ext/test_opensurf.rb +35 -0
- data/test/ext/test_sift.rb +26 -0
- data/test/ext/test_vector_math.rb +85 -0
- data/test/features2d/test_surf.rb +63 -0
- data/test/imgproc/test_goodfeatures.rb +18 -0
- data/test/setup.rb +65 -0
- data/test/test_calib3d.rb +38 -0
- data/test/test_core.rb +26 -0
- data/test/test_ext.rb +8 -0
- data/test/test_features2d.rb +9 -0
- data/test/test_files/images/IMG_7088.JPG +0 -0
- data/test/test_files/images/IMG_7088_small.JPG +0 -0
- data/test/test_files/images/IMG_7089.JPG +0 -0
- data/test/test_highgui.rb +26 -0
- data/test/test_imgproc.rb +35 -0
- data/test/test_wrappers.rb +8 -0
- data/test/wrappers/core/test_draw.rb +41 -0
- data/test/wrappers/core/test_mat.rb +40 -0
- data/test/wrappers/core/test_operations.rb +35 -0
- data/test/wrappers/core/test_types.rb +235 -0
- data/test/wrappers/features2d/test_image_patch.rb +108 -0
- data/test/wrappers/test_imgproc.rb +87 -0
- data/test/wrappers/test_matcher.rb +96 -0
- data/test/wrappers/test_star.rb +28 -0
- data/test/wrappers/test_surf.rb +36 -0
- metadata +234 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
require 'opencv-ffi/core/types'
|
3
|
+
require 'opencv-ffi-wrappers/core/point'
|
4
|
+
|
5
|
+
module CVFFI
|
6
|
+
|
7
|
+
module CvSizeFunctions
|
8
|
+
def x; self.width; end
|
9
|
+
def y; self.height; end
|
10
|
+
|
11
|
+
def x=(a); self.width=a; end
|
12
|
+
def y=(a); self.height=a; end
|
13
|
+
|
14
|
+
def ===(b)
|
15
|
+
width === b.width and height === b.height
|
16
|
+
end
|
17
|
+
def ==(b)
|
18
|
+
width == b.width and height == b.height
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module CvSizeCastMethods
|
23
|
+
def to_CvSize2D64f
|
24
|
+
CvSize2D64f.new( :width => x, :height => y )
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_CvSize2D32f
|
28
|
+
CvSize2D32f.new( :width => x, :height => y )
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_CvSize
|
32
|
+
CvSize.new( :width => x.to_i, :height => y.to_i )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class CvSizeBase
|
37
|
+
include CvSizeFunctions
|
38
|
+
include CvSizeCastMethods
|
39
|
+
end
|
40
|
+
|
41
|
+
class CvSize; def to_CvSize; self; end; end
|
42
|
+
class CvSize2D32f; def to_CvSize2D32f; self; end; end
|
43
|
+
class CvSize2D64f; def to_CvSize2D64f; self; end; end
|
44
|
+
|
45
|
+
class Size < Point
|
46
|
+
include CvSizeCastMethods
|
47
|
+
|
48
|
+
#attr_accessor :height, :width
|
49
|
+
alias :width :x
|
50
|
+
alias :height :y
|
51
|
+
alias :width= :x=
|
52
|
+
alias :height= :y=
|
53
|
+
|
54
|
+
def initialize( *args )
|
55
|
+
if args.length == 2
|
56
|
+
@x= args[0]
|
57
|
+
@y= args[1]
|
58
|
+
else
|
59
|
+
args = args.shift
|
60
|
+
case args
|
61
|
+
when Hash
|
62
|
+
@x= args[:width] || args[:x]
|
63
|
+
@y= args[:height] || args[:y]
|
64
|
+
when Array
|
65
|
+
@x = args[0]
|
66
|
+
@y = args[1]
|
67
|
+
else
|
68
|
+
@x = args.width || args.x
|
69
|
+
@y = args.height || args.y
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def area
|
75
|
+
height*width
|
76
|
+
end
|
77
|
+
|
78
|
+
def floor
|
79
|
+
@x = x.to_i
|
80
|
+
@y = y.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'opencv-ffi-wrappers/features2d/surf'
|
2
|
+
require 'opencv-ffi-wrappers/features2d/star'
|
3
|
+
require 'opencv-ffi-wrappers/features2d/image_patch'
|
4
|
+
|
5
|
+
module CVFFI
|
6
|
+
def self.draw_keypoints( img, pts, opts = {} )
|
7
|
+
color = opts[:color] || CVFFI::CvScalar.new( {:w=>255, :x=>255, :y=>255, :z=>0} )
|
8
|
+
radius = opts[:radius] || 2
|
9
|
+
fill = opts[:fill] || true
|
10
|
+
|
11
|
+
pts.each { |kp|
|
12
|
+
CVFFI::cvCircle( img, CVFFI::Point.new( kp ).to_CvPoint,
|
13
|
+
radius, color, (fill ? -1: 1), 8, 0 )
|
14
|
+
}
|
15
|
+
img
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,322 @@
|
|
1
|
+
require 'opencv-ffi'
|
2
|
+
require 'opencv-ffi-wrappers/core/iplimage'
|
3
|
+
require 'opencv-ffi-wrappers/core/point'
|
4
|
+
require 'opencv-ffi-ext/eigen'
|
5
|
+
|
6
|
+
module CVFFI
|
7
|
+
|
8
|
+
module ImagePatch
|
9
|
+
|
10
|
+
class Mask
|
11
|
+
attr_reader :size
|
12
|
+
def initialize(size)
|
13
|
+
@size = size
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make( params )
|
17
|
+
case params.shape
|
18
|
+
when :square
|
19
|
+
SquareMask.new( params.size )
|
20
|
+
when :circle, :circular
|
21
|
+
CircularMask.new(params.size )
|
22
|
+
else
|
23
|
+
raise "Don't know how to make shape #{params.shape}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class SquareMask < Mask
|
31
|
+
def initialize(size)
|
32
|
+
super(size)
|
33
|
+
end
|
34
|
+
def valid?(i,j)
|
35
|
+
raise "Requesting point outside mask" unless ((0...@size).include? i and (0...@size).include? j)
|
36
|
+
true
|
37
|
+
end
|
38
|
+
def to_a
|
39
|
+
Array.new(size) { |i| Array.new(size) { |j| true } }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class CircularMask < Mask
|
44
|
+
def initialize(size)
|
45
|
+
super(size)
|
46
|
+
|
47
|
+
@mask = build_circle( size/2.0 )
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_circle( radius )
|
51
|
+
## Returns row-major Array of Arrays
|
52
|
+
center = CVFFI::Point.new(radius,radius)
|
53
|
+
@mask = Array.new(radius.ceil) { |i|
|
54
|
+
Array.new(radius.ceil) { |j|
|
55
|
+
pt = CVFFI::Point.new( 0.5 + j, 0.5 + i )
|
56
|
+
center.l2distance( pt ) <= radius ? true : false
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
# Now mirror the one quadrant across Y, then across X
|
61
|
+
@mask.map! { |a| a + a.reverse }
|
62
|
+
@mask + @mask.reverse
|
63
|
+
end
|
64
|
+
|
65
|
+
def valid?(i,j)
|
66
|
+
@mask[i][j]
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_a
|
70
|
+
@mask.map { |m| m.map { |m| m ? true: false } }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Result
|
75
|
+
|
76
|
+
attr_accessor :center
|
77
|
+
attr_accessor :angle
|
78
|
+
|
79
|
+
def initialize( center, patch, angle = 0.0, isOriented = false )
|
80
|
+
@center = CVFFI::Point.new center
|
81
|
+
@patch = CVFFI::Mat.rows( patch, :CV_8U )
|
82
|
+
@angle = angle
|
83
|
+
|
84
|
+
# Set this if the patch has already been rotated
|
85
|
+
@oriented_patch = @patch if isOriented
|
86
|
+
end
|
87
|
+
|
88
|
+
def ==(b)
|
89
|
+
@center == b.center and
|
90
|
+
@angle == b.angle and
|
91
|
+
@patch == b.patch
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO: Consider whether rotating just the patch is ever a good idea
|
95
|
+
# or if you should always get the patch by rotating the original image
|
96
|
+
def orient_patch
|
97
|
+
rotmat = CVFFI::CvMat.new CVFFI::cvCreateMat( 2,3, :CV_32F )
|
98
|
+
CVFFI::cv2DRotationMatrix( CVFFI::CvPoint2D32f.new( [ @patch.width/2.0, @patch.height/2.0 ]), -@angle*180.0/Math::PI, 1.0, rotmat )
|
99
|
+
|
100
|
+
dstimg = @patch.twin
|
101
|
+
CVFFI::cvWarpAffine( @patch, dstimg, rotmat )
|
102
|
+
|
103
|
+
dstimg
|
104
|
+
end
|
105
|
+
|
106
|
+
def oriented_patch
|
107
|
+
@oriented_patch ||= orient_patch
|
108
|
+
end
|
109
|
+
|
110
|
+
def patch( oriented = true )
|
111
|
+
return @patch if angle == 0.0 or oriented == false
|
112
|
+
oriented_patch
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_a
|
116
|
+
[ center.x, center.y, angle, oriented_patch.to_a ]
|
117
|
+
end
|
118
|
+
|
119
|
+
def distance_to( b )
|
120
|
+
patch.l2distance( b.patch )
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.from_a(a)
|
124
|
+
# Serialized results are always oriented
|
125
|
+
Result.new( CVFFI::Point.new( a[0],a[1] ), a[3],a[2], true )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class ResultsArray < Array
|
130
|
+
attr_reader :mask
|
131
|
+
|
132
|
+
def initialize( params, mask = nil )
|
133
|
+
@params = params
|
134
|
+
@mask = mask || Mask::make( params )
|
135
|
+
end
|
136
|
+
|
137
|
+
def patch_size
|
138
|
+
@params.size
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_a
|
142
|
+
each.map { |r|
|
143
|
+
r.to_a
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.from_a(a, params = {} )
|
148
|
+
r = ResultsArray.new( Params.new params )
|
149
|
+
a.each { |a| r << Result.from_a( a ) }
|
150
|
+
r
|
151
|
+
end
|
152
|
+
|
153
|
+
def draw_index_image( opts = {} )
|
154
|
+
border = 5
|
155
|
+
max_cols = opts[:max_cols] || 20
|
156
|
+
|
157
|
+
# Aim for square index
|
158
|
+
cols = Math::sqrt(size).ceil
|
159
|
+
cols = [cols,max_cols].min
|
160
|
+
|
161
|
+
rows = (size*1.0/cols).ceil
|
162
|
+
# puts "Building index #{cols} x #{rows}"
|
163
|
+
# puts "Image patch size #{patch_size}"
|
164
|
+
|
165
|
+
img_size = CVFFI::CvSize.new( [ (cols)*(patch_size+border) + border, (rows)*(patch_size+border) + border ] )
|
166
|
+
|
167
|
+
img = CVFFI::cvCreateImage(img_size, 8, 1 )
|
168
|
+
CVFFI::cvSet( img, CVFFI::CvScalar.new( [ 200.0,0.0,0.0,0.0 ] ), nil )
|
169
|
+
|
170
|
+
each_with_index { |patch,i|
|
171
|
+
c = i%cols
|
172
|
+
r = (i/cols).floor
|
173
|
+
|
174
|
+
xoffset = c*(patch_size+border)+border
|
175
|
+
yoffset = r*(patch_size+border)+border
|
176
|
+
|
177
|
+
# puts "Offsets: #{xoffset} x #{yoffset}"
|
178
|
+
|
179
|
+
patch.oriented_patch.each_with_indices { |value, i, j|
|
180
|
+
if mask.valid?(i,j)
|
181
|
+
CVFFI::cvSet2D( img, yoffset+i, xoffset+j, CVFFI::CvScalar.new( [ value, 0, 0, 0 ] ) )
|
182
|
+
end
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
img
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Params
|
191
|
+
|
192
|
+
DEFAULTS = { size: 9,
|
193
|
+
oriented: false,
|
194
|
+
shape: :square }
|
195
|
+
|
196
|
+
def initialize( opts = {} )
|
197
|
+
|
198
|
+
@params = {}
|
199
|
+
DEFAULTS.each_key { |k|
|
200
|
+
@params[k] = (opts[k] or opts[k.to_s] or DEFAULTS[k])
|
201
|
+
define_singleton_method( k ) { @params[k] }
|
202
|
+
}
|
203
|
+
|
204
|
+
# Ensure the shape is a symbol
|
205
|
+
@params[:shape] = @params[:shape].to_sym
|
206
|
+
end
|
207
|
+
|
208
|
+
def check_params
|
209
|
+
raise "Image patches need to be odd" unless size.odd?
|
210
|
+
end
|
211
|
+
|
212
|
+
def to_hash
|
213
|
+
@params
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.describe( img, keypoints, params )
|
218
|
+
img = img.to_IplImage.ensure_greyscale
|
219
|
+
preOriented = false
|
220
|
+
|
221
|
+
half_size = (params.size/2).floor
|
222
|
+
|
223
|
+
results = ResultsArray.new( params )
|
224
|
+
mask = results.mask
|
225
|
+
|
226
|
+
puts "Extracting #{keypoints.length} keypoints"
|
227
|
+
keypoints.each_with_index { |kp,idx|
|
228
|
+
next if kp.x < half_size or
|
229
|
+
kp.y < half_size or
|
230
|
+
(img.width - kp.x) <= half_size or
|
231
|
+
(img.height - kp.y) <= half_size
|
232
|
+
|
233
|
+
angle = 0.0
|
234
|
+
rect = Rect.new( [ kp.x-half_size, kp.y-half_size, params.size, params.size ] )
|
235
|
+
CVFFI::cvSetImageROI( img, rect.to_CvRect )
|
236
|
+
#
|
237
|
+
## Simple single-channel implementation
|
238
|
+
# Patch is row-major (i == row == y, j = column == x)
|
239
|
+
patch = Array.new( params.size ) { |i|
|
240
|
+
Array.new( params.size ) { |j|
|
241
|
+
mask.valid?(i,j) ? CVFFI::cvGetReal2D( img, i,j ) : 0.0
|
242
|
+
}
|
243
|
+
}
|
244
|
+
CVFFI::cvResetImageROI( img )
|
245
|
+
|
246
|
+
if params.oriented == true
|
247
|
+
# Calculate covariance matrix by Yingen Xiong
|
248
|
+
patch_sum = mxi = mxj = 0.0
|
249
|
+
patch.each_index { |i|
|
250
|
+
patch[i].each_index { |j|
|
251
|
+
if mask.valid?(i,j)
|
252
|
+
patch_sum += patch[i][j]
|
253
|
+
mxi += i*patch[i][j]
|
254
|
+
mxj += j*patch[i][j]
|
255
|
+
end
|
256
|
+
}
|
257
|
+
}
|
258
|
+
mxi /= patch_sum
|
259
|
+
mxj /= patch_sum
|
260
|
+
|
261
|
+
# puts "Medoids = " + [mxi,mxj].join(',')
|
262
|
+
|
263
|
+
c11 = c12 = c22 = 0.0
|
264
|
+
patch.each_index { |i|
|
265
|
+
patch[i].each_index { |j|
|
266
|
+
if mask.valid?(i,j)
|
267
|
+
c11 += patch[i][j] * (i-mxi)*(i-mxi)
|
268
|
+
c12 += patch[i][j] * (i-mxi)*(j-mxj)
|
269
|
+
c22 += patch[i][j] * (j-mxj)*(j-mxj)
|
270
|
+
end
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
c = Matrix.rows( [ [c11,c12],[c12,c22] ] )
|
275
|
+
d,v = CVFFI::Eigen.eigen( c )
|
276
|
+
|
277
|
+
d = d.to_a
|
278
|
+
i = if d[0] == d[1]
|
279
|
+
# Equal eigenvalues
|
280
|
+
0
|
281
|
+
else
|
282
|
+
d.find_index( d.max )
|
283
|
+
end
|
284
|
+
|
285
|
+
vec = v.to_Matrix.column_vectors[i]
|
286
|
+
|
287
|
+
# Eigenvector corresponding to larger eignvector defines orientation
|
288
|
+
# However it's in i-j space, which is OpenCV (y-down, x-right)
|
289
|
+
# Subtract from 2PI to put in mathematical (x-right, y-up) space
|
290
|
+
angle = 2*Math::PI - Math::atan2( vec[0],vec[1] )
|
291
|
+
angle %= 2*Math::PI
|
292
|
+
#puts "Computed angle #{angle * 180.0/Math::PI}"
|
293
|
+
|
294
|
+
## Pre-orient patch
|
295
|
+
# puts "Pre-orienting patch"
|
296
|
+
rotmat = CVFFI::CvMat.new CVFFI::cvCreateMat( 2,3, :CV_32F )
|
297
|
+
CVFFI::cv2DRotationMatrix( kp.to_CvPoint2D32f, -angle*180.0/Math::PI, 1.0, rotmat )
|
298
|
+
|
299
|
+
dstimg = img.twin
|
300
|
+
CVFFI::cvWarpAffine( img, dstimg, rotmat )
|
301
|
+
CVFFI::cvSetImageROI( dstimg, rect.to_CvRect )
|
302
|
+
patch = Array.new( params.size ) { |i|
|
303
|
+
Array.new( params.size ) { |j|
|
304
|
+
mask.valid?(i,j) ? CVFFI::cvGetReal2D( dstimg, i,j ) : 0.0
|
305
|
+
}
|
306
|
+
}
|
307
|
+
CVFFI::cvResetImageROI( dstimg )
|
308
|
+
CVFFI::cvReleaseImage( dstimg )
|
309
|
+
preOriented = true
|
310
|
+
|
311
|
+
GC.start if (idx % 5) == 0
|
312
|
+
|
313
|
+
end
|
314
|
+
|
315
|
+
results << Result.new( kp, patch, angle, preOriented )
|
316
|
+
}
|
317
|
+
|
318
|
+
results
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'opencv-ffi/features2d'
|
2
|
+
require 'opencv-ffi-wrappers/core/iplimage'
|
3
|
+
require 'opencv-ffi-wrappers/core/misc_draw'
|
4
|
+
require 'opencv-ffi-wrappers/sequence'
|
5
|
+
require 'opencv-ffi-wrappers/enumerable'
|
6
|
+
require 'opencv-ffi-wrappers/vectors'
|
7
|
+
require 'opencv-ffi-ext/vector_math'
|
8
|
+
|
9
|
+
module CVFFI
|
10
|
+
|
11
|
+
|
12
|
+
module STAR
|
13
|
+
|
14
|
+
class Result
|
15
|
+
attr_accessor :kp
|
16
|
+
def initialize( kp )
|
17
|
+
@kp = CVFFI::CvStarKeypoint.new(kp)
|
18
|
+
end
|
19
|
+
|
20
|
+
def pt; @kp.pt; end
|
21
|
+
def x; pt.x; end
|
22
|
+
def y; pt.y; end
|
23
|
+
|
24
|
+
def size; @kp.size; end
|
25
|
+
def response; @kp.response; end
|
26
|
+
|
27
|
+
def to_vector( homogeneous = true )
|
28
|
+
homogeneous ? Vector.[]( x, y, 1 ) : Vector.[](x,y)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_Point
|
32
|
+
pt.to_Point
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ResultsArray
|
37
|
+
include Enumerable
|
38
|
+
|
39
|
+
attr_accessor :kp
|
40
|
+
|
41
|
+
def initialize( kp )
|
42
|
+
@kp = Sequence.new(kp)
|
43
|
+
@results = Array.new( @kp.length )
|
44
|
+
end
|
45
|
+
|
46
|
+
def result(i)
|
47
|
+
@results[i] ||= Result.new( @kp[i] )
|
48
|
+
end
|
49
|
+
|
50
|
+
def each
|
51
|
+
@results.each_index { |i|
|
52
|
+
yield result(i)
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def [](i)
|
57
|
+
result(i)
|
58
|
+
end
|
59
|
+
|
60
|
+
def size
|
61
|
+
@kp.size
|
62
|
+
end
|
63
|
+
alias :length :size
|
64
|
+
|
65
|
+
def mark_on_image( img, opts )
|
66
|
+
each { |r|
|
67
|
+
CVFFI::draw_circle( img, r.kp.pt, opts )
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Params
|
73
|
+
|
74
|
+
DEFAULTS = { maxSize: 45,
|
75
|
+
responseThreshold: 30,
|
76
|
+
lineThresholdProjected: 10,
|
77
|
+
lineThresholdBinarized: 8,
|
78
|
+
suppressNonmaxSize: 5 }
|
79
|
+
|
80
|
+
def initialize( opts = {} )
|
81
|
+
|
82
|
+
@params = {}
|
83
|
+
DEFAULTS.each_key { |k|
|
84
|
+
@params[k] = (opts[k] or DEFAULTS[k])
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_CvStarDetectorParams
|
89
|
+
par = @params
|
90
|
+
par.delete_if { |k,v| DEFAULTS.keys.include? k == false }
|
91
|
+
p par
|
92
|
+
CVFFI::CvStarDetectorParams.new( par )
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_hash
|
96
|
+
@params
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.detect( img, params )
|
101
|
+
params = params.to_CvStarDetectorParams unless params.is_a?( CvStarDetectorParams )
|
102
|
+
raise ArgumentError unless params.is_a?( CvStarDetectorParams )
|
103
|
+
|
104
|
+
img = img.to_IplImage.ensure_greyscale
|
105
|
+
mem_storage = CVFFI::cvCreateMemStorage( 0 )
|
106
|
+
|
107
|
+
keypoints = CVFFI::CvSeq.new CVFFI::cvGetStarKeypoints( img, mem_storage, params )
|
108
|
+
ResultsArray.new( keypoints )
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|