spyglass 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/background_subtractor.rb +2 -0
- data/examples/cascade_classifier.rb +1 -1
- data/examples/contours.rb +1 -1
- data/examples/corners.rb +24 -0
- data/examples/images/card.jpg +0 -0
- data/ext/spyglass/contour.cc +56 -0
- data/ext/spyglass/contour.h +2 -0
- data/ext/spyglass/image.cc +59 -1
- data/ext/spyglass/image.h +3 -0
- data/lib/spyglass/version.rb +1 -1
- data/spec/spyglass/image_spec.rb +18 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea79e0513a6b15e030d22e55cb61f1281976d6d2
|
4
|
+
data.tar.gz: a90524336749778e856803ace12316f3ae0fd88e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a103029705c35d69271b0f527d7bd94e19eaff10218677ec4c70d715164696df900a837b0feea5d4dab35dec14dfff2044c98d2fdf3a38c393a31556d9e2dfad
|
7
|
+
data.tar.gz: e01a216cab093e8db29631f3db6224d6525a2197a38e24c7ac6d2ef169b6a10f42437e0dc2102ba8803d5f06528faf342390443ca57fc0f19408a4be68d320c8
|
data/examples/contours.rb
CHANGED
data/examples/corners.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$LOAD_PATH.unshift('lib')
|
2
|
+
require 'spyglass'
|
3
|
+
|
4
|
+
include Spyglass
|
5
|
+
|
6
|
+
window = GUI::Window.new "Original"
|
7
|
+
output = GUI::Window.new "Output"
|
8
|
+
card = Image.load File.expand_path('images/card.jpg', File.dirname(__FILE__))
|
9
|
+
|
10
|
+
window.show(card)
|
11
|
+
|
12
|
+
canny = card.convert(ColorSpace[:BGR => :Gray])
|
13
|
+
|
14
|
+
canny.canny!(200, 300)
|
15
|
+
contours = canny.contours
|
16
|
+
corners = contours[0].corners
|
17
|
+
|
18
|
+
warped = card.warp_perspective(corners, Size.new(220, 300))
|
19
|
+
|
20
|
+
output.show(warped)
|
21
|
+
|
22
|
+
loop do
|
23
|
+
break if GUI::wait_key(10) > 0
|
24
|
+
end
|
Binary file
|
data/ext/spyglass/contour.cc
CHANGED
@@ -11,7 +11,12 @@ namespace Spyglass {
|
|
11
11
|
rb_define_method(ContourClass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1);
|
12
12
|
|
13
13
|
// Instance methods
|
14
|
+
rb_define_method(ContourClass, "corners", RUBY_METHOD_FUNC(rb_get_corners), 0);
|
15
|
+
rb_define_method(ContourClass, "convex?", RUBY_METHOD_FUNC(rb_is_convex), 0);
|
14
16
|
rb_define_method(ContourClass, "rect", RUBY_METHOD_FUNC(rb_get_rect), 0);
|
17
|
+
|
18
|
+
// Aliases
|
19
|
+
rb_define_alias(ContourClass, "closed?", "convex?");
|
15
20
|
}
|
16
21
|
|
17
22
|
VALUE get_ruby_class() {
|
@@ -52,6 +57,57 @@ namespace Spyglass {
|
|
52
57
|
return self;
|
53
58
|
}
|
54
59
|
|
60
|
+
static VALUE rb_get_corners(VALUE self) {
|
61
|
+
// This method is a bit of a misnomer in terms of how OpenCV works,
|
62
|
+
// seen as it's not a simple getter. It's implemented for ease of
|
63
|
+
// use. This code was blatantly based on:
|
64
|
+
//
|
65
|
+
// http://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/
|
66
|
+
std::vector<cv::Point> contour = to_value_vector(SG_GET_CONTOUR(self));
|
67
|
+
cv::approxPolyDP(contour, contour, cv::arcLength(cv::Mat(contour), true) * 0.02, true);
|
68
|
+
if (contour.size() != 4) {
|
69
|
+
// TODO: Throw exception here?
|
70
|
+
std::cout << "The object is not quadrilateral!" << std::endl;
|
71
|
+
return Qnil;
|
72
|
+
}
|
73
|
+
|
74
|
+
// Get mass center
|
75
|
+
cv::Point center(0,0);
|
76
|
+
for (int i = 0; i < contour.size(); i++)
|
77
|
+
center += contour[i];
|
78
|
+
center *= (1. / contour.size());
|
79
|
+
|
80
|
+
// Grab TL, TR, BL, and BR corners.
|
81
|
+
std::vector<cv::Point> top, bot;
|
82
|
+
for (int i = 0; i < contour.size(); i++) {
|
83
|
+
if (contour[i].y < center.y)
|
84
|
+
top.push_back(contour[i]);
|
85
|
+
else
|
86
|
+
bot.push_back(contour[i]);
|
87
|
+
}
|
88
|
+
|
89
|
+
cv::Point tl = top[0].x > top[1].x ? top[1] : top[0];
|
90
|
+
cv::Point tr = top[0].x > top[1].x ? top[0] : top[1];
|
91
|
+
cv::Point bl = bot[0].x > bot[1].x ? bot[1] : bot[0];
|
92
|
+
cv::Point br = bot[0].x > bot[1].x ? bot[0] : bot[1];
|
93
|
+
|
94
|
+
contour.clear();
|
95
|
+
contour.push_back(tl);
|
96
|
+
contour.push_back(tr);
|
97
|
+
contour.push_back(br);
|
98
|
+
contour.push_back(bl);
|
99
|
+
|
100
|
+
return from_cvpoint_vector(contour);
|
101
|
+
}
|
102
|
+
|
103
|
+
static VALUE rb_is_convex(VALUE self) {
|
104
|
+
std::vector<cv::Point> contour = to_value_vector(SG_GET_CONTOUR(self));
|
105
|
+
std::vector<cv::Point> simplified;
|
106
|
+
cv::approxPolyDP(contour, simplified, cv::arcLength(cv::Mat(contour), true) * 0.02, true);
|
107
|
+
|
108
|
+
return cv::isContourConvex(simplified) ? Qtrue : Qfalse;
|
109
|
+
}
|
110
|
+
|
55
111
|
static VALUE rb_get_rect(VALUE self) {
|
56
112
|
std::vector<cv::Point *> *contour = SG_GET_CONTOUR(self);
|
57
113
|
cv::Rect rect = cv::boundingRect(to_value_vector(contour));
|
data/ext/spyglass/contour.h
CHANGED
@@ -11,7 +11,9 @@ namespace Spyglass {
|
|
11
11
|
static VALUE rb_alloc(VALUE self);
|
12
12
|
static void rb_free(std::vector<cv::Point *> *contour);
|
13
13
|
static VALUE rb_initialize(int argc, VALUE *argv, VALUE self);
|
14
|
+
static VALUE rb_get_corners(VALUE self);
|
14
15
|
static VALUE rb_get_rect(VALUE self);
|
16
|
+
static VALUE rb_is_convex(VALUE self);
|
15
17
|
|
16
18
|
std::vector<cv::Point> to_value_vector(std::vector<cv::Point *> *contour);
|
17
19
|
VALUE from_cvpoint_vector(std::vector<cv::Point> contours);
|
data/ext/spyglass/image.cc
CHANGED
@@ -33,12 +33,15 @@ namespace Spyglass {
|
|
33
33
|
rb_define_method(ImageClass, "fill", RUBY_METHOD_FUNC(rb_fill), -1);
|
34
34
|
rb_define_method(ImageClass, "fill!", RUBY_METHOD_FUNC(rb_fill_inplace), -1);
|
35
35
|
rb_define_method(ImageClass, "mean", RUBY_METHOD_FUNC(rb_mean), -1);
|
36
|
+
rb_define_method(ImageClass, "resize", RUBY_METHOD_FUNC(rb_resize), 1);
|
37
|
+
rb_define_method(ImageClass, "resize!", RUBY_METHOD_FUNC(rb_resize_inplace), 1);
|
36
38
|
rb_define_method(ImageClass, "rows", RUBY_METHOD_FUNC(rb_get_rows), 0);
|
37
39
|
rb_define_method(ImageClass, "size", RUBY_METHOD_FUNC(rb_get_size), 0);
|
38
40
|
rb_define_method(ImageClass, "threshold", RUBY_METHOD_FUNC(rb_threshold), -1);
|
39
41
|
rb_define_method(ImageClass, "threshold!", RUBY_METHOD_FUNC(rb_threshold_inplace), -1);
|
40
42
|
rb_define_method(ImageClass, "threshold_inv", RUBY_METHOD_FUNC(rb_threshold_inv), -1);
|
41
43
|
rb_define_method(ImageClass, "threshold_inv!", RUBY_METHOD_FUNC(rb_threshold_inv_inplace), -1);
|
44
|
+
rb_define_method(ImageClass, "warp_perspective", RUBY_METHOD_FUNC(rb_warp_perspective), 2);
|
42
45
|
rb_define_method(ImageClass, "write", RUBY_METHOD_FUNC(rb_write), 1);
|
43
46
|
|
44
47
|
// Constants
|
@@ -312,9 +315,10 @@ namespace Spyglass {
|
|
312
315
|
|
313
316
|
static VALUE rb_get_contours(VALUE self) {
|
314
317
|
cv::Mat *img = SG_GET_IMAGE(self);
|
318
|
+
cv::Mat tmp(*img);
|
315
319
|
|
316
320
|
std::vector<std::vector<cv::Point> > contours;
|
317
|
-
cv::findContours(
|
321
|
+
cv::findContours(tmp, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
|
318
322
|
|
319
323
|
return Contour::from_contour_vector(contours);
|
320
324
|
}
|
@@ -408,6 +412,33 @@ namespace Spyglass {
|
|
408
412
|
return Size::from_cvmat(img);
|
409
413
|
}
|
410
414
|
|
415
|
+
cv::Mat *_do_resize(VALUE self, VALUE size, bool inplace) {
|
416
|
+
cv::Mat *img = SG_GET_IMAGE(self);
|
417
|
+
cv::Mat *dest;
|
418
|
+
|
419
|
+
cv::Size *_size = SG_GET_SIZE(size);
|
420
|
+
|
421
|
+
if(inplace)
|
422
|
+
dest = img;
|
423
|
+
else
|
424
|
+
dest = new cv::Mat();
|
425
|
+
|
426
|
+
cv::resize(*img, *dest, *_size);
|
427
|
+
|
428
|
+
return dest;
|
429
|
+
}
|
430
|
+
|
431
|
+
static VALUE rb_resize(VALUE self, VALUE size) {
|
432
|
+
cv::Mat *img = _do_resize(self, size, false);
|
433
|
+
return Data_Wrap_Struct(ImageClass, NULL, rb_free, img);
|
434
|
+
}
|
435
|
+
|
436
|
+
static VALUE rb_resize_inplace(VALUE self, VALUE size) {
|
437
|
+
_do_resize(self, size, true);
|
438
|
+
return self;
|
439
|
+
}
|
440
|
+
|
441
|
+
|
411
442
|
cv::Mat *_do_threshold(int argc, VALUE *argv, VALUE self, bool inverse, bool inplace) {
|
412
443
|
VALUE threshold, replacement, opts;
|
413
444
|
rb_scan_args(argc, argv, "21", &threshold, &replacement, &opts);
|
@@ -449,6 +480,33 @@ namespace Spyglass {
|
|
449
480
|
return self;
|
450
481
|
}
|
451
482
|
|
483
|
+
static VALUE rb_warp_perspective(VALUE self, VALUE corners, VALUE size) {
|
484
|
+
std::vector<cv::Point> _corners = Contour::to_value_vector(SG_GET_CONTOUR(corners));
|
485
|
+
cv::Size *_size = SG_GET_SIZE(size);
|
486
|
+
cv::Mat *img = SG_GET_IMAGE(self);
|
487
|
+
cv::Mat *result = new cv::Mat(_size->height, _size->width, CV_8UC3);
|
488
|
+
|
489
|
+
// Convert the corner points to `Point2f`, as `getPerspectiveTransform` only
|
490
|
+
// works with that kind of array.
|
491
|
+
std::vector<cv::Point2f> corner_pts;
|
492
|
+
for(int i = 0; i < _corners.size(); i++) {
|
493
|
+
cv::Point2f pt;
|
494
|
+
pt.x = _corners[i].x;
|
495
|
+
pt.y = _corners[i].y;
|
496
|
+
corner_pts.push_back(pt);
|
497
|
+
}
|
498
|
+
|
499
|
+
std::vector<cv::Point2f> quad_pts;
|
500
|
+
quad_pts.push_back(cv::Point2f(0, 0));
|
501
|
+
quad_pts.push_back(cv::Point2f(_size->width, 0));
|
502
|
+
quad_pts.push_back(cv::Point2f(_size->width, _size->height));
|
503
|
+
quad_pts.push_back(cv::Point2f(0, _size->height));
|
504
|
+
|
505
|
+
cv::Mat transmtx = cv::getPerspectiveTransform(corner_pts, quad_pts);
|
506
|
+
cv::warpPerspective(*img, *result, transmtx, result->size());
|
507
|
+
return Data_Wrap_Struct(ImageClass, NULL, rb_free, result);
|
508
|
+
}
|
509
|
+
|
452
510
|
static VALUE rb_write(VALUE self, VALUE filename) {
|
453
511
|
Check_Type(filename, T_STRING);
|
454
512
|
|
data/ext/spyglass/image.h
CHANGED
@@ -28,6 +28,8 @@ namespace Spyglass {
|
|
28
28
|
static VALUE rb_fill_inplace(int argc, VALUE *argv, VALUE self);
|
29
29
|
static VALUE rb_load(int argc, VALUE *argv, VALUE klass);
|
30
30
|
static VALUE rb_mean(int argc, VALUE *argv, VALUE klass);
|
31
|
+
static VALUE rb_resize(VALUE self, VALUE size);
|
32
|
+
static VALUE rb_resize_inplace(VALUE self, VALUE size);
|
31
33
|
static VALUE rb_threshold(int argc, VALUE *argv, VALUE klass);
|
32
34
|
static VALUE rb_threshold_inplace(int argc, VALUE *argv, VALUE klass);
|
33
35
|
static VALUE rb_threshold_inv(int argc, VALUE *argv, VALUE klass);
|
@@ -36,6 +38,7 @@ namespace Spyglass {
|
|
36
38
|
static VALUE rb_get_contours(VALUE self);
|
37
39
|
static VALUE rb_get_rows(VALUE self);
|
38
40
|
static VALUE rb_get_size(VALUE self);
|
41
|
+
static VALUE rb_warp_perspective(VALUE self, VALUE corners, VALUE size);
|
39
42
|
static VALUE rb_write(VALUE self, VALUE filename);
|
40
43
|
static VALUE rb_zeros(int argc, VALUE *argv, VALUE klass);
|
41
44
|
|
data/lib/spyglass/version.rb
CHANGED
data/spec/spyglass/image_spec.rb
CHANGED
@@ -113,4 +113,22 @@ describe Spyglass::Image do
|
|
113
113
|
expect( im.cols ).to eq(512)
|
114
114
|
end
|
115
115
|
end
|
116
|
+
|
117
|
+
describe '#resize' do
|
118
|
+
it 'should resize the image' do
|
119
|
+
new_img = lena.resize(Spyglass::Size.new(1024, 1024))
|
120
|
+
|
121
|
+
expect( new_img.rows ).to eq(1024)
|
122
|
+
expect( new_img.cols ).to eq(1024)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#resize!' do
|
127
|
+
it 'should resize the image in place' do
|
128
|
+
lena.resize!(Spyglass::Size.new(1024, 1024))
|
129
|
+
|
130
|
+
expect( lena.rows ).to eq(1024)
|
131
|
+
expect( lena.cols ).to eq(1024)
|
132
|
+
end
|
133
|
+
end
|
116
134
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spyglass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Medeiros
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,8 +84,10 @@ files:
|
|
84
84
|
- examples/background_subtractor.rb
|
85
85
|
- examples/cascade_classifier.rb
|
86
86
|
- examples/contours.rb
|
87
|
+
- examples/corners.rb
|
87
88
|
- examples/images/apple.jpg
|
88
89
|
- examples/images/beach.jpg
|
90
|
+
- examples/images/card.jpg
|
89
91
|
- examples/video_capture.rb
|
90
92
|
- ext/spyglass/background_subtractor.cc
|
91
93
|
- ext/spyglass/background_subtractor.h
|