spyglass 0.0.2

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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +6 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +28 -0
  8. data/Rakefile +5 -0
  9. data/examples/background_subtractor.rb +35 -0
  10. data/examples/cascade_classifier.rb +21 -0
  11. data/examples/contours.rb +24 -0
  12. data/examples/images/apple.jpg +0 -0
  13. data/examples/images/beach.jpg +0 -0
  14. data/examples/video_capture.rb +15 -0
  15. data/ext/spyglass/background_subtractor.cc +78 -0
  16. data/ext/spyglass/background_subtractor.h +18 -0
  17. data/ext/spyglass/cascade_classifier.cc +70 -0
  18. data/ext/spyglass/cascade_classifier.h +18 -0
  19. data/ext/spyglass/color.cc +83 -0
  20. data/ext/spyglass/color.h +22 -0
  21. data/ext/spyglass/color_space.cc +39 -0
  22. data/ext/spyglass/color_space.h +13 -0
  23. data/ext/spyglass/contour.cc +92 -0
  24. data/ext/spyglass/contour.h +22 -0
  25. data/ext/spyglass/extconf.rb +6 -0
  26. data/ext/spyglass/gui.cc +27 -0
  27. data/ext/spyglass/gui.h +15 -0
  28. data/ext/spyglass/image.cc +482 -0
  29. data/ext/spyglass/image.h +46 -0
  30. data/ext/spyglass/point.cc +78 -0
  31. data/ext/spyglass/point.h +23 -0
  32. data/ext/spyglass/prelude.h +42 -0
  33. data/ext/spyglass/rect.cc +131 -0
  34. data/ext/spyglass/rect.h +30 -0
  35. data/ext/spyglass/size.cc +89 -0
  36. data/ext/spyglass/size.h +25 -0
  37. data/ext/spyglass/spyglass.cc +35 -0
  38. data/ext/spyglass/spyglass.h +29 -0
  39. data/ext/spyglass/video_capture.cc +96 -0
  40. data/ext/spyglass/video_capture.h +20 -0
  41. data/ext/spyglass/window.cc +93 -0
  42. data/ext/spyglass/window.h +23 -0
  43. data/lib/spyglass.rb +6 -0
  44. data/lib/spyglass/color_space.rb +12 -0
  45. data/lib/spyglass/version.rb +3 -0
  46. data/spec/fixtures/haarcascade_frontalface_default.xml +35712 -0
  47. data/spec/fixtures/lena.jpg +0 -0
  48. data/spec/matchers/close_to.rb +6 -0
  49. data/spec/spec_helper.rb +8 -0
  50. data/spec/spyglass/background_subtractor_spec.rb +23 -0
  51. data/spec/spyglass/cascade_classifier_spec.rb +28 -0
  52. data/spec/spyglass/color_space_spec.rb +13 -0
  53. data/spec/spyglass/color_spec.rb +54 -0
  54. data/spec/spyglass/contour_spec.rb +26 -0
  55. data/spec/spyglass/gui/window_spec.rb +21 -0
  56. data/spec/spyglass/image_spec.rb +116 -0
  57. data/spec/spyglass/point_spec.rb +41 -0
  58. data/spec/spyglass/rect_spec.rb +103 -0
  59. data/spec/spyglass/size_spec.rb +52 -0
  60. data/spyglass.gemspec +26 -0
  61. data/tasks/compile.rake +6 -0
  62. data/tasks/gem.rake +2 -0
  63. data/tasks/rspec.rake +21 -0
  64. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 368c20a70c719acceb1244b9cab1ad6274622eaa
4
+ data.tar.gz: 00e92f3dd911a30304ec83269b254420f5acdc57
5
+ SHA512:
6
+ metadata.gz: b4fac2a901002853912daf86ce7192a6b6fe05d8167c26bcdf7c30ff14e46dacab58c55186527084fcb5eeb80eabc76ac65933e962ad4cbfefbc44dbf0ef6bf3
7
+ data.tar.gz: 6880d0ee03bd34737d78fa8609b0feaf672618074612386ee22e27750da780f120f0246f3996a7a197962ea62715c180d19357c544d269bd451a6bde3e58bc35
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ *.bundle
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,6 @@
1
+ --color
2
+ --format progress
3
+ <% if ENV['TRAVIS'] %>
4
+ --tag ~gui
5
+ <% end %>
6
+
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ before_install:
2
+ - bundle
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+
8
+ install:
9
+ - sudo add-apt-repository -y ppa:limparissoft/opencv-ppa
10
+ - sudo apt-get -y update
11
+ - sudo apt-get -y install pkg-config opencv
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in spyglass.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 André Medeiros
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ Spyglass
2
+ ========
3
+
4
+ OpenCV in Ruby, made simple.
5
+
6
+ Why?
7
+ ====
8
+
9
+ Because I've been working on a computer vision project for 2 months and missed Ruby. Also, I wanted a nice, coherent API that would make me happy to use.
10
+
11
+ Requirements
12
+ ============
13
+
14
+ This gem requires OpenCV 2.4.5 or better. For an in-depth OpenCV installation guide, refer to the [OpenCV Wiki page](http://opencv.willowgarage.com/wiki/InstallGuide). It also depends on `pkg-config` being installed. I'm also accepting pull requests for Windows.
15
+
16
+ TODO
17
+ ====
18
+
19
+ There is a lot left to implement, as I've really only ported the parts of the API that I've used so far. Apart from that, there's:
20
+
21
+ * Documentation
22
+ * More tests
23
+
24
+ Code Status
25
+ ===========
26
+
27
+ * [![Build Status](https://travis-ci.org/andremedeiros/spyglass.png?branch=master)](https://travis-ci.org/andremedeiros/spyglass)
28
+ * [![Dependency Status](https://gemnasium.com/andremedeiros/spyglass.png)](https://gemnasium.com/andremedeiros/spyglass)
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rake'
2
+
3
+ Dir['tasks/*.rake'].sort.each { |f| load f }
4
+
5
+ task :default => [:clean, :compile, :spec]
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH.unshift('lib')
2
+ require 'spyglass'
3
+
4
+ include Spyglass
5
+
6
+ puts <<-eos
7
+ WARNING: This demo might not work well with cameras that do auto-exposure.
8
+ If you can, disable those features for a better feel of this demo.
9
+
10
+ eos
11
+
12
+ bg = BackgroundSubtractor.new
13
+ window = GUI::Window.new("Beach!")
14
+ cap = VideoCapture.new 0, width: 640, height: 480
15
+ frame = Image.new
16
+ result = Image.new
17
+ beach = Image.load File.expand_path('images/beach.jpg', File.dirname(__FILE__))
18
+
19
+ puts "Step off the camera's field of vision!"
20
+ 3.times { |sec| puts "... #{ 3 - sec }"; sleep(1) }
21
+
22
+ puts "Training background subtractor..."
23
+ 20.times { cap >> frame; bg.subtract frame, 0.05 }
24
+
25
+ puts "Show yourself ;-)"
26
+
27
+ loop do
28
+ cap >> frame
29
+
30
+ result.copy!(beach)
31
+ delta = bg.subtract(frame, 0)
32
+ result.copy!(frame, delta);
33
+
34
+ window.show result
35
+ end
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift('lib')
2
+ require 'spyglass'
3
+
4
+ include Spyglass
5
+
6
+ classifier = CascadeClassifier.new("spec/fixtures/haarcascade_frontalface_default.xml")
7
+ window = GUI::Window.new "Video"
8
+ cap = VideoCapture.new 0
9
+ frame = Image.new
10
+
11
+ loop do
12
+ cap >> frame
13
+
14
+ rects = classifier.detect(frame, scale_factor: 1.5, min_size: Size.new(30, 30))
15
+ rects.map { |r| frame.draw_rectangle(r) }
16
+
17
+ window.show(frame)
18
+
19
+ break if GUI::wait_key(10) > 0
20
+ end
21
+
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.unshift('lib')
2
+ require 'spyglass'
3
+
4
+ include Spyglass
5
+
6
+ window = GUI::Window.new "Original"
7
+ apple = Image.load File.expand_path('images/apple.jpg', File.dirname(__FILE__))
8
+
9
+ window.show(apple)
10
+
11
+ canny = apple.convert(ColorSpace[:BGR => :Gray])
12
+ canny.canny!(200, 300)
13
+
14
+ result = Image.new
15
+ result.copy!(apple)
16
+ result.draw_contours(canny.contours)
17
+
18
+ result_window = GUI::Window.new "Contours"
19
+ result_window.show(result)
20
+
21
+ loop do
22
+ break if GUI::wait_key(10) > 0
23
+ end
24
+
Binary file
Binary file
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift('lib')
2
+ require 'spyglass'
3
+
4
+ include Spyglass
5
+
6
+ window = GUI::Window.new "Video"
7
+ cap = VideoCapture.new 0
8
+ frame = Image.new
9
+
10
+ loop do
11
+ cap >> frame
12
+ window.show(frame)
13
+
14
+ break if GUI::wait_key(100) > 0
15
+ end
@@ -0,0 +1,78 @@
1
+ #include "background_subtractor.h"
2
+
3
+ static VALUE BackgroundSubtractorClass;
4
+
5
+ namespace Spyglass {
6
+ namespace BackgroundSubtractor {
7
+ void define_ruby_class() {
8
+ // Class definition
9
+ BackgroundSubtractorClass = rb_define_class_under(Spyglass::get_ruby_module(), "BackgroundSubtractor", rb_cObject);
10
+ rb_define_alloc_func(BackgroundSubtractorClass, rb_alloc);
11
+ rb_define_method(BackgroundSubtractorClass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1);
12
+
13
+ // Instance methods
14
+ rb_define_method(BackgroundSubtractorClass, "subtract", RUBY_METHOD_FUNC(rb_subtract), -1);
15
+ }
16
+
17
+ VALUE get_ruby_class() {
18
+ return BackgroundSubtractorClass;
19
+ }
20
+
21
+ static VALUE rb_alloc(VALUE self) {
22
+ cv::BackgroundSubtractorMOG2 *bg = new cv::BackgroundSubtractorMOG2();
23
+ return Data_Wrap_Struct(BackgroundSubtractorClass, NULL, rb_free, bg);
24
+ }
25
+
26
+ static void rb_free(cv::BackgroundSubtractorMOG2 *bg) {
27
+ bg->~BackgroundSubtractorMOG2();
28
+ delete bg;
29
+ }
30
+
31
+ static VALUE rb_initialize(int argc, VALUE *argv, VALUE self) {
32
+ VALUE opts;
33
+ rb_scan_args(argc, argv, "01", &opts);
34
+
35
+ if(!RTEST(opts))
36
+ return self;
37
+
38
+ cv::BackgroundSubtractorMOG2 *bg = SG_GET_BG_SUBTRACTOR(self);
39
+
40
+ SG_OPTION(opts, int, history, 500, NUM2INT);
41
+ SG_OPTION(opts, double, threshold, 8.0, NUM2DBL);
42
+ SG_OPTION(opts, bool, shadow_detection, true, RTEST);
43
+
44
+ bg->set("history", history);
45
+ bg->set("varThreshold", threshold);
46
+ bg->set("detectShadows", shadow_detection);
47
+
48
+ return self;
49
+ }
50
+
51
+ static VALUE rb_subtract(int argc, VALUE *argv, VALUE self) {
52
+ VALUE image, learn_rate;
53
+ double rate;
54
+
55
+ rb_scan_args(argc, argv, "11", &image, &learn_rate);
56
+
57
+ if(CLASS_OF(image) != Image::get_ruby_class()) {
58
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Spyglass::Image)",
59
+ rb_obj_classname(image));
60
+ }
61
+
62
+
63
+ if(RTEST(learn_rate) && TYPE(learn_rate) != T_FLOAT && TYPE(learn_rate) != T_FIXNUM)
64
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Float or Fixnum)",
65
+ rb_obj_classname(learn_rate));
66
+
67
+ rate = RTEST(learn_rate) ? NUM2DBL(learn_rate) : -1.0;
68
+
69
+ cv::BackgroundSubtractorMOG2 *bg = SG_GET_BG_SUBTRACTOR(self);
70
+ cv::Mat *img = SG_GET_IMAGE(image);
71
+
72
+ cv::Mat *delta = new cv::Mat();
73
+ (*bg)(*img, *delta, rate);
74
+
75
+ return Image::from_cvmat(delta);
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,18 @@
1
+ #ifndef SPYGLASS_BACKGROUND_SUBTRACTOR_H_
2
+ #define SPYGLASS_BACKGROUND_SUBTRACTOR_H_
3
+
4
+ #include "spyglass.h"
5
+
6
+ namespace Spyglass {
7
+ namespace BackgroundSubtractor {
8
+ void define_ruby_class();
9
+ VALUE get_ruby_class();
10
+
11
+ static VALUE rb_alloc(VALUE self);
12
+ static void rb_free(cv::BackgroundSubtractorMOG2 *bg);
13
+ static VALUE rb_initialize(int argc, VALUE *argv, VALUE self);
14
+ static VALUE rb_subtract(int argc, VALUE *argv, VALUE self);
15
+ }
16
+ }
17
+
18
+ #endif // SPYGLASS_BACKGROUND_SUBTRACTOR_H_
@@ -0,0 +1,70 @@
1
+ #include "cascade_classifier.h"
2
+
3
+ static VALUE CascadeClassifierClass;
4
+
5
+ namespace Spyglass {
6
+ namespace CascadeClassifier {
7
+ void define_ruby_class() {
8
+ // Class definition
9
+ CascadeClassifierClass = rb_define_class_under(Spyglass::get_ruby_module(), "CascadeClassifier", rb_cObject);
10
+ rb_define_alloc_func(CascadeClassifierClass, rb_alloc);
11
+ rb_define_method(CascadeClassifierClass, "initialize", RUBY_METHOD_FUNC(rb_initialize), 1);
12
+
13
+ // Instance methods
14
+ rb_define_method(CascadeClassifierClass, "detect", RUBY_METHOD_FUNC(rb_detect), -1);
15
+ }
16
+
17
+ VALUE get_ruby_class() {
18
+ return CascadeClassifierClass;
19
+ }
20
+
21
+ static VALUE rb_alloc(VALUE self) {
22
+ cv::CascadeClassifier *classifier = new cv::CascadeClassifier();
23
+ return Data_Wrap_Struct(CascadeClassifierClass, NULL, rb_free, classifier);
24
+ }
25
+
26
+ static void rb_free(cv::CascadeClassifier *classifier) {
27
+ classifier->~CascadeClassifier();
28
+ delete classifier;
29
+ }
30
+
31
+ static VALUE rb_initialize(VALUE self, VALUE src) {
32
+ Check_Type(src, T_STRING);
33
+
34
+ cv::CascadeClassifier *classifier = SG_GET_CLASSIFIER(self);
35
+
36
+ classifier->load(StringValueCStr(src));
37
+
38
+ return self;
39
+ }
40
+
41
+ static VALUE rb_detect(int argc, VALUE *argv, VALUE self) {
42
+ VALUE image, opts;
43
+ rb_scan_args(argc, argv, "11", &image, &opts);
44
+
45
+ if(CLASS_OF(image) != Image::get_ruby_class()) {
46
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Spyglass::Image)",
47
+ rb_obj_classname(image));
48
+ }
49
+
50
+ cv::CascadeClassifier *classifier = SG_GET_CLASSIFIER(self);
51
+ cv::Mat *img = SG_GET_IMAGE(image);
52
+
53
+ SG_OPTION(opts, double, scale_factor, 1.1, NUM2DBL);
54
+ SG_OPTION(opts, int, min_neighbors, 3, NUM2INT);
55
+ SG_OPTION(opts, cv::Size *, min_size, new cv::Size(), SG_GET_SIZE);
56
+ SG_OPTION(opts, cv::Size *, max_size, new cv::Size(), SG_GET_SIZE);
57
+
58
+ std::vector<cv::Rect> results;
59
+ classifier->detectMultiScale(*img, results, scale_factor, min_neighbors, 0, *min_size, *max_size);
60
+
61
+ VALUE result = rb_ary_new2(results.size());
62
+ for(size_t i = 0; i < results.size(); i++) {
63
+ cv::Rect *rect = new cv::Rect(results[i]);
64
+ rb_ary_store(result, (long)i, Rect::from_cvrect(rect));
65
+ }
66
+
67
+ return result;
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,18 @@
1
+ #ifndef SPYGLASS_CASCADE_CLASSIFIER_H_
2
+ #define SPYGLASS_CASCADE_CLASSIFIER_H_
3
+
4
+ #include "spyglass.h"
5
+
6
+ namespace Spyglass {
7
+ namespace CascadeClassifier {
8
+ void define_ruby_class();
9
+ VALUE get_ruby_class();
10
+
11
+ static VALUE rb_alloc(VALUE self);
12
+ static void rb_free(cv::CascadeClassifier *classifier);
13
+ static VALUE rb_initialize(VALUE self, VALUE src);
14
+ static VALUE rb_detect(int argc, VALUE *argv, VALUE self);
15
+ }
16
+ }
17
+
18
+ #endif // SPYGLASS_CASCADE_CLASSIFIER_H_
@@ -0,0 +1,83 @@
1
+ #include "color.h"
2
+
3
+ static VALUE ColorClass;
4
+
5
+ namespace Spyglass {
6
+ namespace Color {
7
+ void define_ruby_class() {
8
+ // Class definition
9
+ ColorClass = rb_define_class_under(Spyglass::get_ruby_module(), "Color", rb_cObject);
10
+ rb_define_alloc_func(ColorClass, rb_alloc);
11
+ rb_define_method(ColorClass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1);
12
+
13
+ // Instance methods
14
+ rb_define_method(ColorClass, "[]", RUBY_METHOD_FUNC(rb_get_color), 1);
15
+ rb_define_method(ColorClass, "to_a", RUBY_METHOD_FUNC(rb_to_a), 0);
16
+ rb_define_method(ColorClass, "zeros?", RUBY_METHOD_FUNC(rb_is_zeros), 0);
17
+ }
18
+
19
+ VALUE get_ruby_class() {
20
+ return ColorClass;
21
+ }
22
+
23
+ static VALUE rb_alloc(VALUE self) {
24
+ cv::Scalar *color = new cv::Scalar(0, 0, 0, 0);
25
+ return Data_Wrap_Struct(ColorClass, NULL, rb_free, color);
26
+ }
27
+
28
+ static void rb_free(cv::Scalar *color) {
29
+ delete color;
30
+ }
31
+
32
+ static VALUE rb_initialize(int argc, VALUE *argv, VALUE self) {
33
+ if(argc < 1 || argc > 4)
34
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..4)", argc);
35
+
36
+ cv::Scalar *color = SG_GET_COLOR(self);
37
+
38
+ for(int idx = 0; idx < argc; idx++) {
39
+ (*color)[idx] = NUM2DBL(argv[idx]);
40
+ }
41
+
42
+ return self;
43
+ }
44
+
45
+ static VALUE rb_get_color(VALUE self, VALUE index) {
46
+ Check_Type(index, T_FIXNUM);
47
+
48
+ int idx = NUM2INT(index);
49
+
50
+ if(idx < 0 || idx > 3)
51
+ return Qnil;
52
+
53
+ cv::Scalar *color = SG_GET_COLOR(self);
54
+ double val = (*color)[idx];
55
+ return DBL2NUM(val);
56
+ }
57
+
58
+ static VALUE rb_to_a(VALUE self) {
59
+ cv::Scalar *color = SG_GET_COLOR(self);
60
+ VALUE ary = rb_ary_new2(4);
61
+
62
+ for(int idx = 0; idx < 4; idx++)
63
+ rb_ary_store(ary, idx, DBL2NUM((*color)[idx]));
64
+
65
+ return ary;
66
+ }
67
+
68
+ static VALUE rb_is_zeros(VALUE self) {
69
+ cv::Scalar *color = SG_GET_COLOR(self);
70
+ bool all_zeros = true;
71
+ for(int idx = 0; idx < 4; idx++) {
72
+ if((*color)[idx] > 0)
73
+ all_zeros = false;
74
+ }
75
+
76
+ return all_zeros ? Qtrue : Qfalse;
77
+ }
78
+
79
+ VALUE from_cvscalar(cv::Scalar *color) {
80
+ return Data_Wrap_Struct(ColorClass, NULL, rb_free, color);
81
+ }
82
+ }
83
+ }