spyglass 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+ }