opencv-ffi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. data/.gitignore +7 -0
  2. data/Gemfile +15 -0
  3. data/README.md +126 -0
  4. data/Rakefile +52 -0
  5. data/docs/DocsIndex.md +1 -0
  6. data/docs/examples/load_image.rb +25 -0
  7. data/ext/Rakefile +13 -0
  8. data/ext/aishack-sift/.gitignore +4 -0
  9. data/ext/aishack-sift/Descriptor.h +34 -0
  10. data/ext/aishack-sift/KeyPoint.h +38 -0
  11. data/ext/aishack-sift/README +20 -0
  12. data/ext/aishack-sift/SIFT.cpp +1036 -0
  13. data/ext/aishack-sift/SIFT.h +84 -0
  14. data/ext/aishack-sift/example/.gitignore +2 -0
  15. data/ext/aishack-sift/example/Makefile +24 -0
  16. data/ext/aishack-sift/example/MySIFT.cpp +29 -0
  17. data/ext/aishack-sift/mkrf_conf.rb +13 -0
  18. data/ext/aishack-sift/siftlib.cpp +85 -0
  19. data/ext/eigen/.gitignore +4 -0
  20. data/ext/eigen/eigen_polynomial.cpp +41 -0
  21. data/ext/eigen/eigen_svd.cpp +100 -0
  22. data/ext/eigen/mkrf_conf.rb +14 -0
  23. data/ext/mkrf-monkey.rb +85 -0
  24. data/ext/mkrf-rakehelper-monkey.rb +52 -0
  25. data/ext/mkrf_conf.rb +3 -0
  26. data/ext/opencv-ffi/.gitignore +4 -0
  27. data/ext/opencv-ffi/matcher_helper.cpp +56 -0
  28. data/ext/opencv-ffi/mkrf_conf.rb +12 -0
  29. data/ext/opencv-ffi/vector_math.cpp +39 -0
  30. data/ext/opensurf/.gitignore +4 -0
  31. data/ext/opensurf/README +38 -0
  32. data/ext/opensurf/fasthessian.cpp +376 -0
  33. data/ext/opensurf/fasthessian.h +108 -0
  34. data/ext/opensurf/integral.cpp +58 -0
  35. data/ext/opensurf/integral.h +55 -0
  36. data/ext/opensurf/ipoint.cpp +108 -0
  37. data/ext/opensurf/ipoint.h +76 -0
  38. data/ext/opensurf/kmeans.h +172 -0
  39. data/ext/opensurf/mkrf_conf.rb +10 -0
  40. data/ext/opensurf/responselayer.h +92 -0
  41. data/ext/opensurf/surf.cpp +317 -0
  42. data/ext/opensurf/surf.h +66 -0
  43. data/ext/opensurf/surflib.cpp +98 -0
  44. data/ext/opensurf/surflib.h +96 -0
  45. data/ext/opensurf/utils.cpp +357 -0
  46. data/ext/opensurf/utils.h +63 -0
  47. data/lib/.gitignore +1 -0
  48. data/lib/opencv-ffi-ext/eigen.rb +84 -0
  49. data/lib/opencv-ffi-ext/features2d.rb +4 -0
  50. data/lib/opencv-ffi-ext/matcher_helper.rb +24 -0
  51. data/lib/opencv-ffi-ext/opensurf.rb +217 -0
  52. data/lib/opencv-ffi-ext/sift.rb +118 -0
  53. data/lib/opencv-ffi-ext/vector_math.rb +115 -0
  54. data/lib/opencv-ffi-wrappers.rb +7 -0
  55. data/lib/opencv-ffi-wrappers/core.rb +24 -0
  56. data/lib/opencv-ffi-wrappers/core/iplimage.rb +50 -0
  57. data/lib/opencv-ffi-wrappers/core/mat.rb +268 -0
  58. data/lib/opencv-ffi-wrappers/core/misc_draw.rb +44 -0
  59. data/lib/opencv-ffi-wrappers/core/point.rb +286 -0
  60. data/lib/opencv-ffi-wrappers/core/rect.rb +40 -0
  61. data/lib/opencv-ffi-wrappers/core/scalar.rb +104 -0
  62. data/lib/opencv-ffi-wrappers/core/size.rb +88 -0
  63. data/lib/opencv-ffi-wrappers/enumerable.rb +10 -0
  64. data/lib/opencv-ffi-wrappers/features2d.rb +17 -0
  65. data/lib/opencv-ffi-wrappers/features2d/image_patch.rb +322 -0
  66. data/lib/opencv-ffi-wrappers/features2d/star.rb +111 -0
  67. data/lib/opencv-ffi-wrappers/features2d/surf.rb +115 -0
  68. data/lib/opencv-ffi-wrappers/highgui.rb +10 -0
  69. data/lib/opencv-ffi-wrappers/imgproc.rb +4 -0
  70. data/lib/opencv-ffi-wrappers/imgproc/features.rb +35 -0
  71. data/lib/opencv-ffi-wrappers/imgproc/geometric.rb +39 -0
  72. data/lib/opencv-ffi-wrappers/matcher.rb +297 -0
  73. data/lib/opencv-ffi-wrappers/matrix.rb +37 -0
  74. data/lib/opencv-ffi-wrappers/misc.rb +41 -0
  75. data/lib/opencv-ffi-wrappers/misc/params.rb +34 -0
  76. data/lib/opencv-ffi-wrappers/sequence.rb +37 -0
  77. data/lib/opencv-ffi-wrappers/vectors.rb +38 -0
  78. data/lib/opencv-ffi.rb +12 -0
  79. data/lib/opencv-ffi/calib3d.rb +26 -0
  80. data/lib/opencv-ffi/core.rb +15 -0
  81. data/lib/opencv-ffi/core/draw.rb +68 -0
  82. data/lib/opencv-ffi/core/dynamic.rb +13 -0
  83. data/lib/opencv-ffi/core/library.rb +5 -0
  84. data/lib/opencv-ffi/core/operations.rb +122 -0
  85. data/lib/opencv-ffi/core/point.rb +22 -0
  86. data/lib/opencv-ffi/core/types.rb +172 -0
  87. data/lib/opencv-ffi/cvffi.rb +8 -0
  88. data/lib/opencv-ffi/features2d.rb +7 -0
  89. data/lib/opencv-ffi/features2d/library.rb +6 -0
  90. data/lib/opencv-ffi/features2d/star.rb +30 -0
  91. data/lib/opencv-ffi/features2d/surf.rb +38 -0
  92. data/lib/opencv-ffi/highgui.rb +31 -0
  93. data/lib/opencv-ffi/imgproc.rb +9 -0
  94. data/lib/opencv-ffi/imgproc/features.rb +37 -0
  95. data/lib/opencv-ffi/imgproc/geometric.rb +42 -0
  96. data/lib/opencv-ffi/imgproc/library.rb +6 -0
  97. data/lib/opencv-ffi/imgproc/misc.rb +39 -0
  98. data/lib/opencv-ffi/version.rb +3 -0
  99. data/opencv-ffi.gemspec +26 -0
  100. data/test/core/test_draw.rb +46 -0
  101. data/test/core/test_operations.rb +135 -0
  102. data/test/core/test_size.rb +14 -0
  103. data/test/core/test_text.rb +52 -0
  104. data/test/ext/test_eigen.rb +105 -0
  105. data/test/ext/test_opensurf.rb +35 -0
  106. data/test/ext/test_sift.rb +26 -0
  107. data/test/ext/test_vector_math.rb +85 -0
  108. data/test/features2d/test_surf.rb +63 -0
  109. data/test/imgproc/test_goodfeatures.rb +18 -0
  110. data/test/setup.rb +65 -0
  111. data/test/test_calib3d.rb +38 -0
  112. data/test/test_core.rb +26 -0
  113. data/test/test_ext.rb +8 -0
  114. data/test/test_features2d.rb +9 -0
  115. data/test/test_files/images/IMG_7088.JPG +0 -0
  116. data/test/test_files/images/IMG_7088_small.JPG +0 -0
  117. data/test/test_files/images/IMG_7089.JPG +0 -0
  118. data/test/test_highgui.rb +26 -0
  119. data/test/test_imgproc.rb +35 -0
  120. data/test/test_wrappers.rb +8 -0
  121. data/test/wrappers/core/test_draw.rb +41 -0
  122. data/test/wrappers/core/test_mat.rb +40 -0
  123. data/test/wrappers/core/test_operations.rb +35 -0
  124. data/test/wrappers/core/test_types.rb +235 -0
  125. data/test/wrappers/features2d/test_image_patch.rb +108 -0
  126. data/test/wrappers/test_imgproc.rb +87 -0
  127. data/test/wrappers/test_matcher.rb +96 -0
  128. data/test/wrappers/test_star.rb +28 -0
  129. data/test/wrappers/test_surf.rb +36 -0
  130. 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,10 @@
1
+
2
+
3
+ # From:http://rpheath.com/posts/341-ruby-inject-with-index
4
+
5
+ module Enumerable
6
+ def inject_with_index(injected)
7
+ each_with_index{ |obj, index| injected = yield(injected, obj, index) }
8
+ injected
9
+ end
10
+ 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