spyglass 0.0.2 → 0.0.3
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.
- 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
|