axon 0.0.2 → 0.1.0
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.
- data/CHANGELOG.rdoc +9 -3
- data/README.rdoc +29 -36
- data/Rakefile +26 -21
- data/TODO.rdoc +1 -6
- data/ext/axon/axon.c +6 -15
- data/ext/axon/extconf.rb +19 -9
- data/ext/axon/interpolation.c +147 -0
- data/ext/axon/jpeg.c +1207 -0
- data/ext/axon/png.c +542 -0
- data/lib/axon.rb +235 -32
- data/lib/axon/cropper.rb +80 -18
- data/lib/axon/fit.rb +69 -19
- data/lib/axon/generators.rb +109 -0
- data/lib/axon/scalers.rb +160 -0
- data/test/helper.rb +151 -6
- data/test/reader_tests.rb +37 -82
- data/test/scaler_tests.rb +102 -0
- data/test/stress_helper.rb +58 -0
- data/test/stress_tests.rb +8 -5
- data/test/test_bilinear_scaler.rb +60 -2
- data/test/test_cropper.rb +68 -1
- data/test/test_fit.rb +35 -0
- data/test/test_generators.rb +21 -0
- data/test/test_image.rb +61 -0
- data/test/test_jpeg_reader.rb +96 -94
- data/test/test_jpeg_writer.rb +95 -8
- data/test/test_nearest_neighbor_scaler.rb +28 -4
- data/test/test_png_reader.rb +12 -8
- data/test/test_png_writer.rb +8 -6
- data/test/writer_tests.rb +129 -111
- metadata +71 -128
- data/.gemtest +0 -0
- data/ext/axon/bilinear_interpolation.c +0 -115
- data/ext/axon/interpolation.h +0 -7
- data/ext/axon/jpeg_common.c +0 -118
- data/ext/axon/jpeg_common.h +0 -37
- data/ext/axon/jpeg_native_writer.c +0 -248
- data/ext/axon/jpeg_reader.c +0 -774
- data/ext/axon/nearest_neighbor_interpolation.c +0 -50
- data/ext/axon/png_common.c +0 -21
- data/ext/axon/png_common.h +0 -18
- data/ext/axon/png_native_writer.c +0 -166
- data/ext/axon/png_reader.c +0 -381
- data/lib/axon/axon.so +0 -0
- data/lib/axon/bilinear_scaler.rb +0 -60
- data/lib/axon/jpeg_writer.rb +0 -41
- data/lib/axon/nearest_neighbor_scaler.rb +0 -39
- data/lib/axon/png_writer.rb +0 -35
- data/lib/axon/scaler.rb +0 -41
- data/lib/axon/solid.rb +0 -23
- data/test/_test_readme.rb +0 -34
- data/test/test_exif.rb +0 -39
- data/test/test_generator.rb +0 -10
- data/test/test_icc.rb +0 -18
- data/test/test_jpeg.rb +0 -9
- data/test/test_png.rb +0 -9
@@ -0,0 +1,102 @@
|
|
1
|
+
module Axon
|
2
|
+
module ScalerTests
|
3
|
+
def test_dimensions
|
4
|
+
[
|
5
|
+
[7, 8],
|
6
|
+
[71, 82],
|
7
|
+
[@image.width, @image.height],
|
8
|
+
[1, 1]
|
9
|
+
].each do |dims|
|
10
|
+
im = Solid.new(100, 200)
|
11
|
+
|
12
|
+
w, h = dims[0], dims[1]
|
13
|
+
|
14
|
+
s = @scalerclass.new(im, w, h)
|
15
|
+
assert_image_dimensions(s, w, h)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_bad_dimensions
|
20
|
+
[ [0, 0], [0, 1], [1, 0], [-1, -3] ].each do |dims|
|
21
|
+
w, h = dims[0], dims[1]
|
22
|
+
|
23
|
+
assert_raises ArgumentError do
|
24
|
+
@scalerclass.new(@image, w, h).gets
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_scalar
|
30
|
+
[1, 2, 3, 1.1, 2.1, 0.9, 0.1].each do |scale|
|
31
|
+
im = Solid.new(100, 200)
|
32
|
+
width = (im.width * scale).to_i
|
33
|
+
height = (im.height * scale).to_i
|
34
|
+
|
35
|
+
s = @scalerclass.new(im, width, height)
|
36
|
+
assert_image_dimensions(s, width, height)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_bad_scalar
|
41
|
+
[0, 0.0, -1, -0.1].each do |scale|
|
42
|
+
assert_raises ArgumentError do
|
43
|
+
@scalerclass.new(@image, scale).gets
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def scale_test(*coords)
|
49
|
+
cache = Repeater.new(Noise.new 100, 200)
|
50
|
+
|
51
|
+
[1.0, 5.0, 0.3, 2.8].each do |scale|
|
52
|
+
width = (cache.width * scale).to_i
|
53
|
+
height = (cache.height * scale).to_i
|
54
|
+
|
55
|
+
cache.rewind
|
56
|
+
test_scaler = @scalertestclass.new(cache, width, height)
|
57
|
+
cache.rewind
|
58
|
+
s = @scalerclass.new(cache, width, height)
|
59
|
+
resized = []
|
60
|
+
s.height.times{ resized << s.gets }
|
61
|
+
|
62
|
+
right = width - 1
|
63
|
+
bottom = height - 1
|
64
|
+
|
65
|
+
coords.each do |coord|
|
66
|
+
x = coord[0]
|
67
|
+
y = coord[1]
|
68
|
+
|
69
|
+
x = right if x < 0
|
70
|
+
y = bottom if y < 0
|
71
|
+
|
72
|
+
result = resized[y][x * 3, 3].chars.map do |c|
|
73
|
+
c.respond_to?(:ord) ? c.ord : c[0]
|
74
|
+
end
|
75
|
+
|
76
|
+
expected = test_scaler.calc(x, y)
|
77
|
+
3.times do |i|
|
78
|
+
assert_in_delta expected[i], result[i], 1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_pixel_values
|
85
|
+
scale_test [10, 10], [29, 20], [9, 15]
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_edge_pixel_values
|
89
|
+
scale_test [0, 10], [29, 0], [-1, 15], [17, -1]
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_corner_pixel_values
|
93
|
+
scale_test [0, 0], [-1, 0], [0, -1], [-1, -1]
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_small_scaling
|
97
|
+
im = Solid.new(10, 20)
|
98
|
+
sc = @scalerclass.new(im, 2, 5)
|
99
|
+
assert_image_dimensions(sc, 2, 5)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
class MiniTest::Unit::TestCase
|
3
|
+
WARMUP_TESTS = 10
|
4
|
+
MAX_TEST_TIME = 3
|
5
|
+
MAX_TESTS = 3000
|
6
|
+
|
7
|
+
def vmsize
|
8
|
+
GC.start
|
9
|
+
GC.disable
|
10
|
+
vms = IO.read("/proc/#{$$}/status")[/^VmSize:\s+(\d+)/, 1].to_i
|
11
|
+
GC.enable
|
12
|
+
vms
|
13
|
+
end
|
14
|
+
|
15
|
+
def vmdelta
|
16
|
+
start_vmsize = vmsize
|
17
|
+
yield
|
18
|
+
vmsize - start_vmsize
|
19
|
+
end
|
20
|
+
|
21
|
+
def limited_loop
|
22
|
+
start_time = Time.now
|
23
|
+
test_counter = 0
|
24
|
+
|
25
|
+
until Time.now - start_time > MAX_TEST_TIME || test_counter > MAX_TESTS do
|
26
|
+
yield
|
27
|
+
test_counter += 1
|
28
|
+
end
|
29
|
+
|
30
|
+
return test_counter
|
31
|
+
end
|
32
|
+
|
33
|
+
def mem_loop
|
34
|
+
result = yield
|
35
|
+
WARMUP_TESTS.times{ yield }
|
36
|
+
num_tests = 0
|
37
|
+
|
38
|
+
delta = vmdelta do
|
39
|
+
num_tests = limited_loop do
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# The idea here is that a legitimate memory leak will leak at least one
|
45
|
+
# byte per iteration.
|
46
|
+
if delta >= num_tests
|
47
|
+
puts "\n#{self.class} #{__name__}: #{delta}"
|
48
|
+
end
|
49
|
+
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
alias_method :old_run, :run
|
54
|
+
|
55
|
+
def run(*args)
|
56
|
+
mem_loop{ old_run(*args) }
|
57
|
+
end
|
58
|
+
end
|
data/test/stress_tests.rb
CHANGED
@@ -4,9 +4,12 @@ module Axon
|
|
4
4
|
class TestStressCases < AxonTestCase
|
5
5
|
def setup
|
6
6
|
@velvet = "\x0A\x14\x69"
|
7
|
-
@image =
|
8
|
-
|
9
|
-
|
7
|
+
@image = Solid.new 1000, 1500, @velvet
|
8
|
+
|
9
|
+
io = StringIO.new
|
10
|
+
JPEG.write(@image, io)
|
11
|
+
@jpeg_data = io.string
|
12
|
+
@readerclass = JPEG::Reader
|
10
13
|
end
|
11
14
|
|
12
15
|
class RandomRaiseIO
|
@@ -15,7 +18,7 @@ module Axon
|
|
15
18
|
@die_at = rand(source.size)
|
16
19
|
end
|
17
20
|
|
18
|
-
def read
|
21
|
+
def read(*args)
|
19
22
|
if @die_at < 2
|
20
23
|
raise 'random death!'
|
21
24
|
end
|
@@ -40,7 +43,7 @@ module Axon
|
|
40
43
|
@sequence = sequence
|
41
44
|
end
|
42
45
|
|
43
|
-
def read
|
46
|
+
def read(*args)
|
44
47
|
if !@sequence || @sequence.empty?
|
45
48
|
raise "random death!"
|
46
49
|
@sequence = nil
|
@@ -1,9 +1,67 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'scaler_tests'
|
2
3
|
|
3
4
|
module Axon
|
4
5
|
class TestBilinearScaler < AxonTestCase
|
5
|
-
|
6
|
-
|
6
|
+
include ScalerTests
|
7
|
+
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
@scalerclass = BilinearScaler
|
11
|
+
@scalertestclass = TestBilinearScaler
|
12
|
+
end
|
13
|
+
|
14
|
+
class TestBilinearScaler
|
15
|
+
def initialize(original, width, height)
|
16
|
+
@original = original
|
17
|
+
@scale_x_inv = original.width / width.to_f
|
18
|
+
@scale_y_inv = original.height / height.to_f
|
19
|
+
|
20
|
+
# pad right and bottom with one pixel
|
21
|
+
@data = []
|
22
|
+
@original.height.times do
|
23
|
+
sl = @original.gets
|
24
|
+
@data << sl + sl[-@original.components, @original.components]
|
25
|
+
end
|
26
|
+
@data << @data.last
|
27
|
+
end
|
28
|
+
|
29
|
+
def calc(x, y)
|
30
|
+
sample_x = x * @scale_x_inv
|
31
|
+
sample_y = y * @scale_y_inv
|
32
|
+
|
33
|
+
sample_x_i = sample_x.floor
|
34
|
+
sample_y_i = sample_y.floor
|
35
|
+
|
36
|
+
tx = sample_x - sample_x_i
|
37
|
+
ty = sample_y - sample_y_i
|
38
|
+
|
39
|
+
ret = []
|
40
|
+
|
41
|
+
cmp = @original.components
|
42
|
+
|
43
|
+
cmp.times do |i|
|
44
|
+
smp_x = sample_x_i * @original.components + i
|
45
|
+
|
46
|
+
c00 = @data[sample_y_i][smp_x]
|
47
|
+
c10 = @data[sample_y_i][smp_x + cmp]
|
48
|
+
c01 = @data[sample_y_i + 1][smp_x]
|
49
|
+
c11 = @data[sample_y_i + 1][smp_x + cmp]
|
50
|
+
|
51
|
+
unless RUBY_VERSION == '1.8.7'
|
52
|
+
c00 = c00.ord
|
53
|
+
c10 = c10.ord
|
54
|
+
c01 = c01.ord
|
55
|
+
c11 = c11.ord
|
56
|
+
end
|
57
|
+
|
58
|
+
a = (1 - tx) * c00 + tx * c10
|
59
|
+
b = (1 - tx) * c01 + tx * c11
|
60
|
+
ret << ((1 - ty) * a + ty * b).floor
|
61
|
+
end
|
62
|
+
|
63
|
+
ret
|
64
|
+
end
|
7
65
|
end
|
8
66
|
end
|
9
67
|
end
|
data/test/test_cropper.rb
CHANGED
@@ -3,7 +3,74 @@ require 'helper'
|
|
3
3
|
module Axon
|
4
4
|
class TestCropper < AxonTestCase
|
5
5
|
def test_crop
|
6
|
-
|
6
|
+
i = Solid.new(200, 100)
|
7
|
+
c = Cropper.new(i, 4, 55)
|
8
|
+
|
9
|
+
assert_equal 4, c.width
|
10
|
+
assert_equal 55, c.height
|
11
|
+
|
12
|
+
55.times do |i|
|
13
|
+
assert_equal i, c.lineno
|
14
|
+
assert_equal 4 * c.components, c.gets.size
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_equal nil, c.gets
|
18
|
+
assert_equal 55, c.lineno
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_crop_too_tall
|
22
|
+
i = Solid.new(200, 100)
|
23
|
+
c = Cropper.new(i, 4, 133)
|
24
|
+
assert_image_dimensions(c, 4, 100)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_crop_too_wide
|
28
|
+
i = Solid.new(200, 100)
|
29
|
+
c = Cropper.new(i, 233, 33)
|
30
|
+
assert_image_dimensions(c, 200, 33)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_crop_negative_values
|
34
|
+
i = Solid.new(200, 100)
|
35
|
+
|
36
|
+
assert_raises(ArgumentError) do
|
37
|
+
Cropper.new(i, -233, 33)
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_raises(ArgumentError) do
|
41
|
+
Cropper.new(i, 100, -33)
|
42
|
+
end
|
43
|
+
|
44
|
+
assert_raises(ArgumentError) do
|
45
|
+
Cropper.new(i, 0, 23)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_crop_with_offset
|
50
|
+
i = Noise.new(100, 200)
|
51
|
+
|
52
|
+
data = []
|
53
|
+
i.height.times{ data << i.gets }
|
54
|
+
c = Cropper.new(ArrayWrapper.new(data), 42, 51, 22, 31)
|
55
|
+
|
56
|
+
assert_equal 42, c.width
|
57
|
+
assert_equal 51, c.height
|
58
|
+
|
59
|
+
51.times do |i|
|
60
|
+
assert_equal i, c.lineno
|
61
|
+
sl = c.gets
|
62
|
+
assert_equal 42 * c.components, sl.size
|
63
|
+
assert_equal data[i + 31][22 * c.components], sl[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
assert_equal nil, c.gets
|
67
|
+
assert_equal 51, c.lineno
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_crop_with_offset_oob
|
71
|
+
i = Solid.new(200, 100)
|
72
|
+
c = Cropper.new(i, 30, 40, 220, 10)
|
73
|
+
assert_nil c.gets
|
7
74
|
end
|
8
75
|
end
|
9
76
|
end
|
data/test/test_fit.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Axon
|
4
|
+
class TestFit < AxonTestCase
|
5
|
+
def test_width_determines_downscaling
|
6
|
+
im = Solid.new(10, 20)
|
7
|
+
r = Fit.new(im, 5, 20)
|
8
|
+
assert_image_dimensions(r, 5, 10)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_height_determines_downscaling
|
12
|
+
im = Solid.new(10, 20)
|
13
|
+
r = Fit.new(im, 20, 5)
|
14
|
+
assert_image_dimensions(r, 2, 5)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_width_determines_upscaling
|
18
|
+
im = Solid.new(10, 20)
|
19
|
+
r = Fit.new(im, 100, 900)
|
20
|
+
assert_image_dimensions(r, 100, 200)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_height_determines_upscaling
|
24
|
+
im = Solid.new(10, 20)
|
25
|
+
r = Fit.new(im, 1000, 200)
|
26
|
+
assert_image_dimensions(r, 100, 200)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_dimensions_unchanged
|
30
|
+
im = Solid.new(10, 20)
|
31
|
+
r = Fit.new(im, 10, 20)
|
32
|
+
assert_image_dimensions(r, 10, 20)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Axon
|
4
|
+
class TestGenerators < AxonTestCase
|
5
|
+
def test_solid_color
|
6
|
+
g = Solid.new 10, 15, "\x0A\x14\x69"
|
7
|
+
sl = g.gets
|
8
|
+
assert_equal "\x0A\x14\x69", sl[9, 3]
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_solid_dimensions
|
12
|
+
g = Solid.new(23, 33)
|
13
|
+
assert_image_dimensions(g, 23, 33)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_noise_dimensions
|
17
|
+
im = Noise.new(100, 200, :components => 1, :color_model => :GRAYSCALE)
|
18
|
+
assert_image_dimensions(im, 100, 200)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/test/test_image.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module Axon
|
5
|
+
class TestImage < AxonTestCase
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
io = StringIO.new
|
9
|
+
JPEG.write(Solid.new(10, 20), io)
|
10
|
+
@jpeg_data = io.string
|
11
|
+
|
12
|
+
io = StringIO.new
|
13
|
+
PNG.write(Solid.new(10, 20), io)
|
14
|
+
@png_data = io.string
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_jpeg_helper_io
|
18
|
+
image = Axon.jpeg(StringIO.new(@jpeg_data))
|
19
|
+
assert_image_dimensions(image, 10, 20)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_jpeg_helper_stringdata
|
23
|
+
image = Axon.jpeg(@jpeg_data)
|
24
|
+
assert_image_dimensions(image, 10, 20)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_png_helper_io
|
28
|
+
image = Axon.png(StringIO.new(@png_data))
|
29
|
+
assert_image_dimensions(image, 10, 20)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_jpeg_helper_stringdata
|
33
|
+
image = Axon.png(@png_data)
|
34
|
+
assert_image_dimensions(image, 10, 20)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_bilinear
|
38
|
+
image = Axon.jpeg(@jpeg_data)
|
39
|
+
image.scale_bilinear(50, 75)
|
40
|
+
assert_image_dimensions(image, 50, 75)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_nearest
|
44
|
+
image = Axon.jpeg(@jpeg_data)
|
45
|
+
image.scale_nearest(50, 75)
|
46
|
+
assert_image_dimensions(image, 50, 75)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_fit
|
50
|
+
image = Axon.jpeg(@jpeg_data)
|
51
|
+
image.fit(50, 80)
|
52
|
+
assert_image_dimensions(image, 40, 80)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_crop
|
56
|
+
image = Axon.jpeg(@jpeg_data)
|
57
|
+
image.crop(5, 10, 6, 2)
|
58
|
+
assert_image_dimensions(image, 4, 10)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|