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
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