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,115 @@
1
+
2
+ require 'opencv-ffi/features2d'
3
+ require 'opencv-ffi-wrappers/core/iplimage'
4
+ require 'opencv-ffi-wrappers/core/misc_draw'
5
+ require 'opencv-ffi-wrappers/sequence'
6
+ require 'opencv-ffi-wrappers/enumerable'
7
+ require 'opencv-ffi-wrappers/vectors'
8
+ require 'opencv-ffi-ext/vector_math'
9
+
10
+ module CVFFI
11
+
12
+
13
+ module SURF
14
+
15
+ class Result
16
+ attr_accessor :kp, :desc
17
+ def initialize( kp, desc )
18
+ @kp = CVFFI::CvSURFPoint.new(kp)
19
+ @desc = desc
20
+ end
21
+
22
+ def pt; @kp.pt; end
23
+ def x; pt.x; end
24
+ def y; pt.y; end
25
+
26
+ def distance_to( q )
27
+ # Here's the pure-Ruby way to do it
28
+ # @desc.inject_with_index(0.0) { |x,d,i| x + (d-q.desc.d[i])**2 }
29
+
30
+ CVFFI::VectorMath::L2distance( @desc, q.desc )
31
+ end
32
+
33
+ def to_vector
34
+ Vector.[]( x, y, 1 )
35
+ end
36
+
37
+ def to_Point
38
+ pt.to_Point
39
+ end
40
+ end
41
+
42
+ class ResultsArray
43
+ include Enumerable
44
+
45
+ attr_accessor :kp, :desc, :pool
46
+ attr_accessor :desc_type
47
+
48
+ def initialize( kp, desc, pool, desc_type = Float64 )
49
+ @kp = Sequence.new(kp)
50
+ @desc = Sequence.new(desc)
51
+ @pool = pool
52
+ @desc_type = desc_type
53
+ @results = Array.new( @kp.length )
54
+
55
+ raise RuntimeError "SURF Keypoint and descriptor sequences different length (#{@kp.size} != #{@desc.size})" unless (@kp.size == @desc.size)
56
+
57
+ destructor = Proc.new { poolPtr = FFI::MemoryPointer.new :pointer; poolPtr.putPointer( 0, @pool ); cvReleaseMemStorage( poolPtr ) }
58
+ ObjectSpace.define_finalizer( self, destructor )
59
+ end
60
+
61
+ def result(i)
62
+ @results[i] ||= Result.new( @kp[i], @desc_type.new( @desc[i] ) )
63
+ end
64
+
65
+ def each
66
+ @results.each_index { |i|
67
+ yield result(i)
68
+ }
69
+ end
70
+
71
+ def [](i)
72
+ result(i)
73
+ end
74
+
75
+ def size
76
+ @kp.size
77
+ end
78
+ alias :length :size
79
+
80
+ def mark_on_image( img, opts )
81
+ each { |r|
82
+ CVFFI::draw_circle( img, r.kp.pt, opts )
83
+ }
84
+ end
85
+ end
86
+
87
+ def self.detect( img, params )
88
+
89
+ raise ArgumentError unless params.is_a?( CvSURFParams ) || params.is_a?( Params )
90
+
91
+ img = CVFFI::IplImage.new img.to_IplImage
92
+
93
+ if img.nChannels == 3
94
+ greyImg = CVFFI::cvCreateImage( CVFFI::CvSize.new( :height => img.height,
95
+ :width => img.width ),
96
+ :IPL_DEPTH_8U, 1 )
97
+ CVFFI::cvCvtColor( img, greyImg, :CV_RGB2GRAY )
98
+ else
99
+ greyImg = img
100
+ end
101
+
102
+ kp_ptr = FFI::MemoryPointer.new :pointer
103
+ desc_ptr = FFI::MemoryPointer.new :pointer
104
+
105
+ mem_storage = CVFFI::cvCreateMemStorage( 0 )
106
+
107
+ CVFFI::cvExtractSURF( greyImg, nil, kp_ptr, desc_ptr, mem_storage, params, :false )
108
+
109
+ keypoints = CVFFI::CvSeq.new( kp_ptr.read_pointer() )
110
+ descriptors = CVFFI::CvSeq.new( desc_ptr.read_pointer() )
111
+
112
+ ResultsArray.new( keypoints, descriptors, mem_storage )
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,10 @@
1
+
2
+
3
+ require 'opencv-ffi/highgui'
4
+
5
+ module CVFFI
6
+
7
+ def self.save_image( fname, img )
8
+ CVFFI::cvSaveImage( fname, img.to_IplImage )
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+
2
+ require 'opencv-ffi-wrappers/imgproc/features'
3
+ require 'opencv-ffi-wrappers/imgproc/geometric'
4
+
@@ -0,0 +1,35 @@
1
+
2
+ require 'opencv-ffi'
3
+ require 'opencv-ffi-wrappers/misc/params'
4
+
5
+ module CVFFI
6
+
7
+ class GoodFeaturesParams < Params
8
+
9
+ param :max_corners, 1000,
10
+ :quality_level, 0.5,
11
+ :min_distance, 5,
12
+ :block_size, 3,
13
+ :use_harris, false,
14
+ :k, 0.04,
15
+ :mask, nil
16
+
17
+ end
18
+
19
+
20
+ def self.goodFeaturesToTrack( image, params = GoodFeaturesParams.new )
21
+
22
+ # TODO, technically, cvGoodFeaturesToTrack can also take 32FC1 data as well,
23
+ # doesn't necessarily need to be cast to 8UC1 greyscale
24
+ img = image.to_IplImage.ensure_greyscale
25
+ eig_image = img.twin( :IPL_DEPTH_32F )
26
+ temp_image = eig_image.twin
27
+
28
+ CVFFI::cvGoodFeaturesToTrack( img, eig_image, temp_image, params.max_corners,
29
+ params.quality_level, params.min_distance, params.mask,
30
+ params.block_size, params.use_harris, params.k )
31
+ end
32
+
33
+
34
+
35
+ end
@@ -0,0 +1,39 @@
1
+
2
+
3
+ require 'opencv-ffi'
4
+
5
+ module CVFFI
6
+
7
+ # Might be the simplest way to do this
8
+ class GetAffineTransformPoints < NiceFFI::Struct
9
+ layout :a, [ CvPoint2D32f, 3 ]
10
+ end
11
+
12
+ def self.get_affine_transform( src, dst )
13
+ result = CVFFI::cvCreateMat( 2, 3, :CV_32F )
14
+
15
+ srcPts = GetAffineTransformPoints.new '\0'
16
+ dstPts = GetAffineTransformPoints.new '\0'
17
+
18
+ 0.upto(2) { |i|
19
+ srcPts.a[i].x = src[i].x
20
+ srcPts.a[i].y = src[i].y
21
+ dstPts.a[i].x = dst[i].x
22
+ dstPts.a[i].y = dst[i].y
23
+ }
24
+
25
+ CVFFI::cvGetAffineTransform( srcPts, dstPts, result )
26
+ end
27
+
28
+ def self.warp_affine( src, dst, mat, opts={} )
29
+ flags = opts[:flags] || [ :CV_INTER_LINEAR, :CV_WARP_FILL_OUTLIERS ]
30
+ flags << :CV_WARP_INVERSE_MAP if opts[:inverse]
31
+
32
+ flags = flags.inject(0) { |x,i| x + cv_warp_flags_to_i( i ) }
33
+ CVFFI::cvWarpAffine( src.to_IplImage, dst, mat, flags,
34
+ CVFFI::CvScalar.new( [ 0.0, 0.0, 0.0, 0.0 ] ) )
35
+
36
+
37
+ dst
38
+ end
39
+ end
@@ -0,0 +1,297 @@
1
+
2
+
3
+ require 'opencv-ffi-wrappers'
4
+
5
+ module AddMapWithIndex
6
+
7
+ def map_with_index( &blk )
8
+ a = []
9
+ each_with_index { |k,i| a << blk.yield( k,i ) }
10
+ a
11
+ end
12
+ end
13
+
14
+ module CVFFI
15
+
16
+ class Match < NiceFFI::Struct
17
+ layout :train, CvPoint2D32f,
18
+ :query, CvPoint2D32f,
19
+ :distance, :double
20
+ end
21
+
22
+ class MatchSet < NiceFFI::Struct
23
+ layout :length, :int,
24
+ :d, :pointer,
25
+ :err, :pointer
26
+ end
27
+
28
+ class MatchResult
29
+ attr_accessor :train_idx, :query_idx
30
+ attr_accessor :distance
31
+
32
+ alias :tidx :train_idx
33
+ alias :qidx :query_idx
34
+ alias :dist :distance
35
+
36
+ def initialize( t, q, d )
37
+ @train_idx = t
38
+ @query_idx = q
39
+ @distance = d
40
+ end
41
+
42
+ def to_s
43
+ "Match t=#{@train_idx} q=#{@query_idx} distance=#{@distance}"
44
+ end
45
+
46
+ def train
47
+ @train_set[@train_idx]
48
+ end
49
+
50
+ def query
51
+ @query_set[@query_idx]
52
+ end
53
+
54
+ end
55
+
56
+ class MatchResults
57
+ include Enumerable
58
+
59
+ attr_accessor :train_set, :query_set
60
+ attr_accessor :results
61
+
62
+ def initialize( tset, qset )
63
+ @train_set = tset
64
+ @query_set = qset
65
+
66
+ @results = []
67
+ @match_cache = []
68
+
69
+ @by_train = []
70
+ @by_train.extend( AddMapWithIndex )
71
+
72
+ @by_query = []
73
+
74
+ @mask = []
75
+ end
76
+
77
+ def add_result(r)
78
+ raise RuntimeError "index greater than size of training set (#{r.train_idx} > #{train_set.size})" if r.train_idx >= train_set.size
79
+ raise RuntimeError "index greater than size of query set (#{r.query_idx} > #{query_set.size})" if r.query_idx >= query_set.size
80
+ @results << r
81
+ (@by_train[r.train_idx] ||= Array.new) << r
82
+ (@by_query[r.query_idx] ||= Array.new) << r
83
+ r
84
+ end
85
+
86
+ def add( tidx, qidx, dist )
87
+ add_result MatchResult.new( tidx, qidx, dist )
88
+ end
89
+
90
+ def to_s
91
+ [ "Total matches: #{@results.size}" ] +
92
+ @by_train.map_with_index { |r,tidx|
93
+ "Tidx #{tidx} (#{r.nil? ? 0 : r.length}): #{r.nil? ? "" : r.map {|r| sprintf "%d(%.2e)", r.query_idx, r.dist }.join(' ') }"
94
+ }
95
+ end
96
+
97
+ def to_a
98
+ Array.new( size ) { |i|
99
+ [ @results[i].tidx, @results[i].qidx, @results[i].dist ]
100
+ }
101
+ end
102
+
103
+ def length( include_masked = false )
104
+ if( include_masked )
105
+ @results.length
106
+ else
107
+ num_unmasked
108
+ end
109
+ end
110
+
111
+ alias :size :length
112
+
113
+ def [](i)
114
+ if @match_cache[i]
115
+ @match_cache[i]
116
+ else
117
+ m=Match.new( '\0' )
118
+ m.distance = @results[i].distance
119
+
120
+ # This is a bit awkward with nested FFI structs
121
+ m.train.x = @train_set[@results[i].tidx].x
122
+ m.train.y = @train_set[@results[i].tidx].y
123
+ m.query.x = @query_set[@results[i].qidx].x
124
+ m.query.y = @query_set[@results[i].qidx].y
125
+ @match_cache[i] = m
126
+ m
127
+ end
128
+ end
129
+ alias :element :[]
130
+
131
+ # Experiment
132
+ def match_set
133
+ if @match_set
134
+ @match_set
135
+ else
136
+ m = FFI::MemoryPointer.new( Match, length )
137
+ results_each_with_index { |r,i|
138
+ match = Match.new( m + i*Match.size )
139
+ match.distance = r.distance
140
+
141
+ # This is a bit awkward with nested FFI structs
142
+ match.train.x = @train_set[r.tidx].x
143
+ match.train.y = @train_set[r.tidx].y
144
+ match.query.x = @query_set[r.qidx].x
145
+ match.query.y = @query_set[r.qidx].y
146
+ }
147
+ ms = MatchSet.new( :length => length,
148
+ :d => m,
149
+ :err => FFI::MemoryPointer.new( :double, length ) )
150
+ @match_set = ms
151
+ end
152
+ end
153
+
154
+ def invalidate_match_set; @match_set = nil; end
155
+
156
+ # The "public" each yields a Match to the block
157
+ def each( include_masked = false, &blk )
158
+ @results.length.times { |i|
159
+ if !include_masked and !masked?(i)
160
+ blk.yield( element(i) )
161
+ end
162
+ }
163
+ end
164
+
165
+ # the "private" each yelds a MatchResult to the block
166
+ def results_each_with_index( include_masked = false, &blk )
167
+ @results.each_with_index { |r,i|
168
+ if !include_masked and !masked?(i)
169
+ blk.yield( r,i )
170
+ end
171
+ }
172
+ end
173
+
174
+ def results_each( include_masked = false, &blk )
175
+ results_each_with_index { |r,i| blk.yield(r) }
176
+ end
177
+
178
+ def num_masked
179
+ @mask.inject(0) { |m,s| s ? m+1 : m }
180
+ end
181
+
182
+ def num_unmasked
183
+ @results.length - num_masked
184
+ end
185
+
186
+ def masked?( i )
187
+ @mask[i] == true
188
+ end
189
+
190
+ def mask( i )
191
+ invalidate_match_set
192
+ @mask[i] = true
193
+ end
194
+
195
+ def unmask(i)
196
+ invalidate_match_set
197
+ @mask[i] = false
198
+ end
199
+
200
+ def clear_mask(i)
201
+ invalidate_match_set
202
+ @mask = []
203
+ end
204
+
205
+ def get_mask
206
+ @mask
207
+ end
208
+
209
+ def set_mask(m)
210
+ invalidate_match_set
211
+ @mask = m
212
+ end
213
+
214
+ def merge_mask(m)
215
+ invalidate_match_set
216
+ old_mask = @mask
217
+ @mask = Array.new( [old_mask.length, m.length].max ) { |i|
218
+ old_mask[i] or m[i]
219
+ }
220
+ end
221
+
222
+ def to_Points( include_masked = false )
223
+ pointsOne = []
224
+ pointsTwo = []
225
+
226
+ results_each( include_masked ) { |r|
227
+ pointsOne << @train_set[r.tidx].to_Point
228
+ pointsTwo << @query_set[r.qidx].to_Point
229
+ }
230
+
231
+ [pointsOne, pointsTwo]
232
+ end
233
+
234
+
235
+ def to_CvMats( include_masked = false )
236
+ pointsOne = CVFFI::cvCreateMat( num_unmasked, 2, :CV_32F )
237
+ pointsTwo = CVFFI::cvCreateMat( num_unmasked, 2, :CV_32F )
238
+
239
+ results_each_with_index( include_masked ) { |r,i|
240
+ CVFFI::cvSetReal2D( pointsOne, i, 0, @train_set[r.tidx].x )
241
+ CVFFI::cvSetReal2D( pointsOne, i, 1, @train_set[r.tidx].y )
242
+ CVFFI::cvSetReal2D( pointsTwo, i, 0, @query_set[r.qidx].x )
243
+ CVFFI::cvSetReal2D( pointsTwo, i, 1, @query_set[r.qidx].y )
244
+ }
245
+
246
+ [pointsOne, pointsTwo]
247
+ end
248
+
249
+ end
250
+
251
+ class BruteForceMatcher
252
+
253
+ def self.euclidian_distance( a, b )
254
+
255
+
256
+ end
257
+
258
+ def self.bestMatch( train, query, opts = {})
259
+ maxDistance = opts[:max_distance] || nil
260
+ k = opts[:k] || nil
261
+
262
+ results = MatchResults.new( train, query )
263
+ query.each_with_index { |q,qidx|
264
+
265
+ this_result = []
266
+ train.each_with_index { |t,tidx|
267
+ distance = block_given? ? yield( t,q ) : t.distance_to(q)
268
+
269
+ ## Non-optimized algorithms...
270
+ if maxDistance
271
+ if distance < maxDistance
272
+ this_result << MatchResult.new( tidx, qidx, distance )
273
+ end
274
+ elsif k
275
+ if this_result.length < k
276
+ this_result << MatchResult.new( tidx, qidx, distance )
277
+ else
278
+ this_result.sort! { |a,b| a.distance <=> b.distance }
279
+ if distance < this_result[0].distance
280
+ this_result[0] = MatchResult.new( tidx, qidx, distance )
281
+ end
282
+ end
283
+ else
284
+ if this_result.length == 0 or distance < this_result[0].distance
285
+ this_result = [ MatchResult.new( tidx, qidx, distance ) ]
286
+ end
287
+ end
288
+
289
+ }
290
+ this_result.each { |r| results.add_result( r ) }
291
+ }
292
+
293
+ results
294
+ end
295
+
296
+ end
297
+ end