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