opencv-ffi 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.
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