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,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,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
|