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
Binary file
@@ -0,0 +1,6 @@
1
+ RSpec::Matchers.define :be_close_to do |expected, opts|
2
+ threshold = opts[:threshold] || 10
3
+ match do |actual|
4
+ (expected-threshold..expected+threshold).include? actual
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ require 'spyglass'
2
+ require 'tmpdir'
3
+
4
+ require 'matchers/close_to'
5
+
6
+ def fixture_path(fixture)
7
+ File.join(File.dirname(__FILE__), 'fixtures', fixture)
8
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::BackgroundSubtractor do
4
+ let(:bg) { Spyglass::BackgroundSubtractor.new }
5
+ let(:lena) { Spyglass::Image.load(fixture_path('lena.jpg')) }
6
+
7
+ describe '.new' do
8
+ it 'should work with no arguments' do
9
+ expect( bg ).to be_a Spyglass::BackgroundSubtractor
10
+ end
11
+
12
+ it 'should work with an options hash' do
13
+ expect { Spyglass::BackgroundSubtractor.new(history: 50, threshold: 64) }.not_to raise_error
14
+ end
15
+ end
16
+
17
+ describe '#subtract' do
18
+ it 'should return a Spyglass::Image' do
19
+ delta = bg.subtract(lena, 1)
20
+ expect( delta ).to be_a Spyglass::Image
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::CascadeClassifier do
4
+ let(:classifier) { Spyglass::CascadeClassifier.new(fixture_path('haarcascade_frontalface_default.xml')) }
5
+ let(:lena) { Spyglass::Image.load(fixture_path('lena.jpg')) }
6
+
7
+ describe '.new' do
8
+ it 'should receive an argument' do
9
+ expect { Spyglass::CascadeClassifier.new }.to raise_error ArgumentError;
10
+ expect( classifier ).to be_a Spyglass::CascadeClassifier
11
+ end
12
+ end
13
+
14
+ describe '.detect' do
15
+ it 'should return an array of Spyglass::Rect' do
16
+ rects = classifier.detect(lena, scale_factor: 1.3)
17
+
18
+ expect( rects ).to be_a Array
19
+
20
+ rect = rects.first
21
+ expect( rect ).to be_a Spyglass::Rect
22
+ expect( rect.x ).to be_close_to 215, threshold: 10
23
+ expect( rect.y ).to be_close_to 202, threshold: 10
24
+ expect( rect.width ).to be_close_to 174, threshold: 10
25
+ expect( rect.height ).to be_close_to 174, threshold: 10
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::ColorSpace do
4
+ describe '[]' do
5
+ it 'should return a valid color space code when the conversion is defined' do
6
+ expect(Spyglass::ColorSpace[:RGB => :Gray]).to eq(7)
7
+ end
8
+
9
+ it 'should throw an exception when a conversion isnt defined' do
10
+ expect { Spyglass::ColorSpace[:RGB => :FairyDust] }.to raise_error ArgumentError
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::Color do
4
+ let(:color) { Spyglass::Color.new(255, 123, 234) }
5
+
6
+ describe '.new' do
7
+ it 'should accept between 1 to 4 arguments' do
8
+ expect( Spyglass::Color.new(255) ).to be_a Spyglass::Color
9
+ expect( Spyglass::Color.new(255, 255) ).to be_a Spyglass::Color
10
+ expect( Spyglass::Color.new(255, 255, 255) ).to be_a Spyglass::Color
11
+ expect( Spyglass::Color.new(255, 255, 255, 255) ).to be_a Spyglass::Color
12
+
13
+ expect { Spyglass::Color.new }.to raise_error ArgumentError
14
+ end
15
+ end
16
+
17
+ describe 'getters' do
18
+ describe '#to_a' do
19
+ it 'should return an array with the color values' do
20
+ expected = [255, 123, 234, 0]
21
+ colors = color.to_a
22
+
23
+ expect( colors ).to eq(expected)
24
+ end
25
+ end
26
+
27
+ describe '#[]' do
28
+ it 'should return the correct color values for indices between 0..3' do
29
+ expected = [255, 123, 234, 0]
30
+ colors = color.to_a
31
+
32
+ 4.times do |idx|
33
+ expect( color[idx] ).to eq(expected[idx])
34
+ end
35
+ end
36
+
37
+ it 'should return nil for anything < 0 or > 3' do
38
+ expect( color[-2] ).to be_nil
39
+ expect( color[4] ).to be_nil
40
+ end
41
+ end
42
+
43
+ describe '#zeros?' do
44
+ it 'should return true when the color is comprised only by zeros' do
45
+ zeros = Spyglass::Color.new(0, 0, 0, 0)
46
+ expect( zeros.zeros? ).to be_true
47
+ end
48
+
49
+ it 'should return false when the color has at least one element that is not zero' do
50
+ expect( color.zeros? ).to be_false
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::Contour do
4
+ let(:empty_contour) { Spyglass::Contour.new }
5
+ let(:contour) do
6
+ points = 10.times.map { |i| Spyglass::Point.new(i + 1, i + 1) }
7
+ Spyglass::Contour.new points
8
+ end
9
+
10
+ describe '.new' do
11
+ it 'should work without arguments' do
12
+ expect( empty_contour ).to be_a Spyglass::Contour
13
+ end
14
+
15
+ it 'should work with an array of points' do
16
+ expect { contour }.not_to raise_error
17
+ end
18
+ end
19
+
20
+ describe '#rect' do
21
+ it 'should return the bounding rect' do
22
+ expect( contour.rect.size.width ).to eq(10)
23
+ expect( contour.rect.size.height ).to eq(10)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::GUI::Window, gui: true do
4
+ let(:window) { Spyglass::GUI::Window.new("Image preview") }
5
+ let(:lena) { Spyglass::Image.load(fixture_path('lena.jpg')) }
6
+
7
+ describe '.new' do
8
+ it 'should require one argument' do
9
+ expect { Spyglass::GUI::Window.new }.to raise_error ArgumentError
10
+ expect( window ).to be_a Spyglass::GUI::Window
11
+ end
12
+ end
13
+
14
+ describe 'accessors' do
15
+ describe '#title' do
16
+ it 'should return the correct title' do
17
+ expect( window.title ).to eq("Image preview")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::Image do
4
+ let(:lena) { Spyglass::Image.load(fixture_path('lena.jpg')) }
5
+
6
+ describe '.load' do
7
+ it 'should require an argument' do
8
+ expect { Spyglass::Image.load }.to raise_error ArgumentError
9
+ end
10
+
11
+ it 'should return an instance of OpenCV::Image when passing a path' do
12
+ expect( Spyglass::Image.load(fixture_path('lena.jpg')) ).to be_a described_class
13
+ end
14
+ end
15
+
16
+ describe '.zeros' do
17
+ it 'should require a size' do
18
+ expect { Spyglass::Image.zeros }.to raise_error ArgumentError
19
+
20
+ expect( Spyglass::Image.zeros(Spyglass::Size.new(20, 20)) ).to be_a Spyglass::Image
21
+ end
22
+
23
+ it 'should create an image filled with zeros' do
24
+ img = Spyglass::Image.zeros Spyglass::Size.new(50, 50)
25
+ mean = img.mean
26
+
27
+ expect( mean.zeros? ).to be_true
28
+ end
29
+ end
30
+
31
+ describe 'accessors' do
32
+ describe '#rows' do
33
+ it 'should return the right number of rows for an image' do
34
+ expect( lena.rows ).to eq(512)
35
+ end
36
+ end
37
+
38
+ describe '#cols' do
39
+ it 'should return the right number of columns for an image' do
40
+ expect( lena.cols ).to eq(512)
41
+ end
42
+ end
43
+
44
+ describe '#size' do
45
+ it 'should be an instance of Spyglass::Size' do
46
+ expect( lena.size ).to be_a Spyglass::Size
47
+ end
48
+
49
+ it 'should return the correct values' do
50
+ expect( lena.size.width ).to eq( lena.cols )
51
+ expect( lena.size.height ).to eq( lena.rows )
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#erode' do
57
+ it 'should erode the image' do
58
+ eroded = lena.erode
59
+
60
+ expect( eroded.cols ).to eq( lena.cols )
61
+ expect( eroded.rows ).to eq( lena.rows )
62
+ end
63
+ end
64
+
65
+ describe '#crop' do
66
+ it 'should crop the image to the correct proportions' do
67
+ rect = Spyglass::Rect.new(0, 0, 256, 256)
68
+ cropped = lena.crop(rect)
69
+
70
+ expect( cropped.cols ).to eq(256)
71
+ expect( cropped.rows ).to eq(256)
72
+ end
73
+ end
74
+
75
+ describe '#crop!' do
76
+ it 'should crop the image in place to the correct proportions' do
77
+ rect = Spyglass::Rect.new(0, 0, 256, 256)
78
+ lena.crop!(rect)
79
+
80
+ expect( lena.cols ).to eq(256)
81
+ expect( lena.rows ).to eq(256)
82
+ end
83
+ end
84
+
85
+ describe '#mean' do
86
+ it 'should return a Spyglass::Color' do
87
+ expect( lena.mean ).to be_a Spyglass::Color
88
+ end
89
+ end
90
+
91
+ describe '#write' do
92
+ it 'should write the image onto disk' do
93
+ Dir.mktmpdir do |dir|
94
+ path = File.join(dir, 'lena.jpg')
95
+
96
+ res = lena.write(path)
97
+ expect( File.exists?(path) ).to be_true
98
+ expect( res ).to be_true
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#copy!' do
104
+ it 'should copy the image inplace' do
105
+ im = Spyglass::Image.new
106
+
107
+ expect( im.rows ).to eq(0)
108
+ expect( im.cols ).to eq(0)
109
+
110
+ im.copy!(lena)
111
+
112
+ expect( im.rows ).to eq(512)
113
+ expect( im.cols ).to eq(512)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::Point do
4
+ let(:point) { Spyglass::Point.new(10, 20) }
5
+
6
+ describe '.new' do
7
+ it 'should require two arguments' do
8
+ expect { Spyglass::Point.new }.to raise_error ArgumentError
9
+ expect( point ).to be_a Spyglass::Point
10
+ end
11
+ end
12
+
13
+ describe 'accessors' do
14
+ describe '#x' do
15
+ it 'should return the correct X coordinate' do
16
+ expect( point.x ).to eq(10)
17
+ end
18
+ end
19
+
20
+ describe '#y' do
21
+ it 'should return the correct Y coordinate' do
22
+ expect( point.y ).to eq(20)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe 'setters' do
28
+ describe '#x=' do
29
+ it 'should set the value of the X coordinate' do
30
+ point.x = 20
31
+ expect( point.x ).to eq(20)
32
+ end
33
+ end
34
+ describe '#y=' do
35
+ it 'should set the value of the Y coordinate' do
36
+ point.y = 40
37
+ expect( point.y ).to eq(40)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spyglass::Rect do
4
+ let(:rect) { Spyglass::Rect.new(10, 20, 100, 200) }
5
+
6
+ describe '.new' do
7
+ it 'should require four arguments' do
8
+ expect { Spyglass::Rect.new }.to raise_error ArgumentError
9
+ expect( rect ).to be_a Spyglass::Rect
10
+ end
11
+ end
12
+
13
+ describe 'accessors' do
14
+ describe '#x' do
15
+ it 'should return the correct X coordinate' do
16
+ expect( rect.x ).to eq(10)
17
+ end
18
+ end
19
+
20
+ describe '#y' do
21
+ it 'should return the correct Y coordinate' do
22
+ expect( rect.y ).to eq(20)
23
+ end
24
+ end
25
+
26
+ describe '#width' do
27
+ it 'should return the correct value' do
28
+ expect( rect.width ).to eq(100)
29
+ end
30
+ end
31
+
32
+ describe '#height' do
33
+ it 'should return the correct value' do
34
+ expect( rect.height ).to eq(200)
35
+ end
36
+ end
37
+
38
+ describe '#area' do
39
+ it 'should return `width * height`' do
40
+ expect( rect.area ).to eq(20_000)
41
+ end
42
+ end
43
+
44
+ describe '#size' do
45
+ it 'should be an instance of Spyglass::Size' do
46
+ expect( rect.size ).to be_a Spyglass::Size
47
+ end
48
+
49
+ it 'should return the correct values' do
50
+ expect( rect.size.width ).to eq( rect.width )
51
+ expect( rect.size.height ).to eq( rect.height )
52
+ end
53
+ end
54
+
55
+ describe '#point' do
56
+ it 'should be an instance of Spyglass::Point' do
57
+ expect( rect.point ).to be_a Spyglass::Point
58
+ end
59
+
60
+ it 'should return the correct values' do
61
+ expect( rect.point.x ).to eq( rect.x )
62
+ expect( rect.point.y ).to eq( rect.y )
63
+ end
64
+ end
65
+ end
66
+
67
+ describe 'setters' do
68
+ describe '#height=' do
69
+ it 'should set the value of the height' do
70
+ rect.height = 150
71
+
72
+ expect( rect.height ).to eq(150)
73
+ expect( rect.area ).to eq(15_000)
74
+ end
75
+ end
76
+
77
+ describe '#width=' do
78
+ it 'should set the value of the width' do
79
+ rect.width = 300
80
+
81
+ expect( rect.width ).to eq(300)
82
+ expect( rect.area ).to eq(60_000)
83
+ end
84
+ end
85
+
86
+ describe '#x=' do
87
+ it 'should set the value of the X coordinate' do
88
+ rect.x = 20
89
+
90
+ expect( rect.x ).to eq(20)
91
+ end
92
+ end
93
+
94
+ describe '#y=' do
95
+ it 'should set the value of the Y coordinate' do
96
+ rect.y = 40
97
+
98
+ expect( rect.y ).to eq(40)
99
+ end
100
+ end
101
+ end
102
+
103
+ end