sobakasu-image_science 1.1.3
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/History.txt +39 -0
- data/History.txt.orig +77 -0
- data/Manifest.txt +20 -0
- data/README.txt +73 -0
- data/Rakefile +13 -0
- data/bench.rb +63 -0
- data/bin/image_science +221 -0
- data/bin/image_science_thumb +31 -0
- data/ext/extconf.rb +87 -0
- data/ext/image_science_ext.c.in +803 -0
- data/lib/image_science.rb +157 -0
- data/spec/fixtures/pix.gif +0 -0
- data/spec/fixtures/pix.jpg +0 -0
- data/spec/fixtures/pix.png +0 -0
- data/spec/fixtures/pix2.gif +0 -0
- data/spec/fixtures/pix2.jpg +0 -0
- data/spec/fixtures/pix2.png +0 -0
- data/spec/image_science_spec.rb +531 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +97 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../ext/image_science_ext'
|
2
|
+
|
3
|
+
class ImageScience
|
4
|
+
|
5
|
+
VERSION = "1.1.3"
|
6
|
+
|
7
|
+
##
|
8
|
+
# Returns the type of the image as a string.
|
9
|
+
|
10
|
+
def self.image_type(path)
|
11
|
+
fif_to_string(file_type(path))
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Returns the type of the image as a string.
|
16
|
+
|
17
|
+
def image_type
|
18
|
+
ImageScience.fif_to_string(@file_type)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Returns the colorspace of the image as a string
|
23
|
+
|
24
|
+
def colorspace
|
25
|
+
case colortype
|
26
|
+
when 0 then depth == 1 ? 'InvertedMonochrome' : 'InvertedGrayscale'
|
27
|
+
when 1 then depth == 1 ? 'Monochrome' : 'Grayscale'
|
28
|
+
when 2 then 'RGB'
|
29
|
+
when 3 then 'Indexed'
|
30
|
+
when 4 then 'RGBA'
|
31
|
+
when 5 then 'CMYK'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def colourspace # :nodoc:
|
36
|
+
colorspace
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# alias for buffer()
|
41
|
+
|
42
|
+
def data(*args)
|
43
|
+
buffer(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# call-seq:
|
48
|
+
# thumbnail(size)
|
49
|
+
# thumbnail(size) { |image| ... }
|
50
|
+
#
|
51
|
+
# Creates a proportional thumbnail of the image scaled so its
|
52
|
+
# longest edge is resized to +size+. If a block is given, yields
|
53
|
+
# the new image, else returns true on success.
|
54
|
+
|
55
|
+
def thumbnail(size)
|
56
|
+
w, h = width, height
|
57
|
+
scale = size.to_f / (w > h ? w : h)
|
58
|
+
w = (w * scale).to_i
|
59
|
+
h = (h * scale).to_i
|
60
|
+
|
61
|
+
if block_given?
|
62
|
+
self.resize(w, h) do |image|
|
63
|
+
yield image
|
64
|
+
end
|
65
|
+
else
|
66
|
+
self.resize(w, h)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# call-seq:
|
72
|
+
# cropped_thumbnail(size)
|
73
|
+
# cropped_thumbnail(size) { |image| ... }
|
74
|
+
#
|
75
|
+
# Creates a square thumbnail of the image cropping the longest edge
|
76
|
+
# to match the shortest edge, resizes to +size+. If a block is given,
|
77
|
+
# yields the new image, else returns true on success.
|
78
|
+
|
79
|
+
def cropped_thumbnail(size) # :yields: image
|
80
|
+
w, h = width, height
|
81
|
+
l, t, r, b, half = 0, 0, w, h, (w - h).abs / 2
|
82
|
+
|
83
|
+
l, r = half, half + h if w > h
|
84
|
+
t, b = half, half + w if h > w
|
85
|
+
|
86
|
+
if block_given?
|
87
|
+
with_crop(l, t, r, b) do |img|
|
88
|
+
img.thumbnail(size) do |thumb|
|
89
|
+
yield thumb
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
crop(l, t, r, b) && thumbnail(size)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# resize the image to fit within the max_w and max_h passed in without
|
99
|
+
# changing the aspect ratio of the original image. If a block is given,
|
100
|
+
# yields the new image, else returns true on success.
|
101
|
+
|
102
|
+
def fit_within(max_w, max_h)
|
103
|
+
w, h = width, height
|
104
|
+
|
105
|
+
if w > max_w.to_i or h > max_h.to_i
|
106
|
+
|
107
|
+
w_ratio = max_w.quo(w)
|
108
|
+
h_ratio = max_h.quo(h)
|
109
|
+
|
110
|
+
if (w_ratio < h_ratio)
|
111
|
+
h = (h * w_ratio)
|
112
|
+
w = (w * w_ratio)
|
113
|
+
else
|
114
|
+
h = (h * h_ratio)
|
115
|
+
w = (w * h_ratio)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if block_given?
|
120
|
+
self.resize(w, h) do |image|
|
121
|
+
yield image
|
122
|
+
end
|
123
|
+
else
|
124
|
+
self.resize(w, h)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# call-seq:
|
129
|
+
# img[x, y] -> index
|
130
|
+
# img[x, y] -> [red, green, blue]
|
131
|
+
#
|
132
|
+
# alias for get_pixel_color
|
133
|
+
|
134
|
+
def [](x, y)
|
135
|
+
get_pixel_color(x, y)
|
136
|
+
end
|
137
|
+
|
138
|
+
# call-seq:
|
139
|
+
# img[x, y] = index
|
140
|
+
# img[x, y] = [red, green, blue]
|
141
|
+
#
|
142
|
+
# alias for set_pixel_color
|
143
|
+
|
144
|
+
def []=(x, y, *args)
|
145
|
+
set_pixel_color(x, y, *args)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def self.fif_to_string(fif)
|
151
|
+
file_types = %W{BMP ICO JPEG JNG KOALA IFF MNG PBM PBMRAW PCD PCX PGM
|
152
|
+
PGMRAW PNG PPM PPMRAW RAS TARGA TIFF WBMP PSD CUT XBM
|
153
|
+
XPM DDS GIF HDR FAXG3 SGI EXR J2K JP2}
|
154
|
+
file_types[fif]
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,531 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/image_science'
|
2
|
+
|
3
|
+
include ImageScience::ColorChannels
|
4
|
+
include ImageScience::ColorTypes
|
5
|
+
include ImageScience::ImageFilters
|
6
|
+
include ImageScience::ImageFormats
|
7
|
+
|
8
|
+
describe ImageScience do
|
9
|
+
|
10
|
+
FILE_TYPES = %W{png jpg gif bmp tif xpm}
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@path = 'spec/fixtures'
|
14
|
+
@h = @w = 50
|
15
|
+
end
|
16
|
+
|
17
|
+
after(:each) do
|
18
|
+
FILE_TYPES.each do |ext|
|
19
|
+
File.unlink tmp_image_path(ext) if File.exist? tmp_image_path(ext)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "get_version" do
|
24
|
+
it "should return the image science version" do
|
25
|
+
ImageScience.get_version.should_not be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
FILE_TYPES.each do |ext|
|
30
|
+
|
31
|
+
describe "#{ext}" do
|
32
|
+
|
33
|
+
describe "new" do
|
34
|
+
it "should create a new ImageScience object by reading a file" do
|
35
|
+
img = ImageScience.new(image_path(ext))
|
36
|
+
img.should be_kind_of(ImageScience)
|
37
|
+
[img.width, img.height].should == [@w, @h]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should create a new ImageScience object by reading image data" do
|
41
|
+
data = File.read(image_path(ext))
|
42
|
+
img = ImageScience.new(data)
|
43
|
+
img.should be_kind_of(ImageScience)
|
44
|
+
[img.width, img.height].should == [@w, @h]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "with_image" do
|
49
|
+
it "should raise an error when a file does not exist" do
|
50
|
+
lambda {
|
51
|
+
ImageScience.with_image(image_path(ext) + "nope") {}
|
52
|
+
}.should raise_error
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should fetch image dimensions" do
|
56
|
+
ImageScience.with_image image_path(ext) do |img|
|
57
|
+
img.should be_kind_of(ImageScience)
|
58
|
+
img.height.should == @h
|
59
|
+
img.width.should == @w
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "with_image_from_memory" do
|
65
|
+
it "should raise an error when an empty string is given" do
|
66
|
+
lambda {
|
67
|
+
ImageScience.with_image_from_memory("") {}
|
68
|
+
}.should raise_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "with_image_from_memory" do
|
73
|
+
it "should fetch image dimensions" do
|
74
|
+
data = File.new(image_path(ext)).binmode.read
|
75
|
+
ImageScience.with_image_from_memory data do |img|
|
76
|
+
img.should be_kind_of(ImageScience)
|
77
|
+
img.height.should == @h
|
78
|
+
img.width.should == @w
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "save" do
|
84
|
+
it "should save a new copy of an image" do
|
85
|
+
ImageScience.with_image image_path(ext) do |img|
|
86
|
+
img.save(tmp_image_path(ext)).should be_true
|
87
|
+
end
|
88
|
+
File.exists?(tmp_image_path(ext)).should be_true
|
89
|
+
|
90
|
+
ImageScience.with_image tmp_image_path(ext) do |img|
|
91
|
+
img.should be_kind_of(ImageScience)
|
92
|
+
img.height.should == @h
|
93
|
+
img.width.should == @w
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "resize" do
|
99
|
+
|
100
|
+
it "should resize an image" do
|
101
|
+
ImageScience.with_image image_path(ext) do |img|
|
102
|
+
img.resize(25, 25) do |thumb|
|
103
|
+
thumb.save(tmp_image_path(ext)).should be_true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
File.exists?(tmp_image_path(ext)).should be_true
|
108
|
+
|
109
|
+
ImageScience.with_image tmp_image_path(ext) do |img|
|
110
|
+
img.should be_kind_of(ImageScience)
|
111
|
+
img.height.should == 25
|
112
|
+
img.width.should == 25
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should resize an image given floating point dimensions" do
|
117
|
+
ImageScience.with_image image_path(ext) do |img|
|
118
|
+
img.resize(25.2, 25.7) do |thumb|
|
119
|
+
thumb.save(tmp_image_path(ext)).should be_true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
File.exists?(tmp_image_path(ext)).should be_true
|
124
|
+
|
125
|
+
ImageScience.with_image tmp_image_path(ext) do |img|
|
126
|
+
img.should be_kind_of(ImageScience)
|
127
|
+
img.height.should == 25
|
128
|
+
img.width.should == 25
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# do not accept negative or zero values for width/height
|
133
|
+
it "should raise an error if given invalid width or height" do
|
134
|
+
[ [0, 25], [25, 0], [-25, 25], [25, -25] ].each do |width, height|
|
135
|
+
lambda {
|
136
|
+
ImageScience.with_image image_path(ext) do |img|
|
137
|
+
img.resize(width, height) do |thumb|
|
138
|
+
thumb.save(tmp_image_path(ext))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
}.should raise_error
|
142
|
+
|
143
|
+
File.exists?(tmp_image_path(ext)).should be_false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should resize the image in-place if no block given" do
|
148
|
+
ImageScience.with_image image_path(ext) do |img|
|
149
|
+
img.resize(20, 20).should be_true
|
150
|
+
img.width.should == 20
|
151
|
+
img.height.should == 20
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should resize the image with the given filter" do
|
156
|
+
ImageScience.with_image image_path(ext) do |img|
|
157
|
+
img.resize(20, 20, FILTER_BILINEAR).should be_true
|
158
|
+
img.width.should == 20
|
159
|
+
img.height.should == 20
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "buffer" do
|
166
|
+
it "should return image data" do
|
167
|
+
ImageScience.with_image image_path(ext) do |img|
|
168
|
+
expected = File.size(image_path(ext))
|
169
|
+
tolerance = expected * 0.05
|
170
|
+
|
171
|
+
data = img.buffer
|
172
|
+
data.should_not be_nil
|
173
|
+
#data.length.should be_close(expected, tolerance)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should yield image data" do
|
178
|
+
ImageScience.with_image image_path(ext) do |img|
|
179
|
+
expected = File.size(image_path(ext))
|
180
|
+
tolerance = expected * 0.05
|
181
|
+
|
182
|
+
img.buffer do |data|
|
183
|
+
data.should_not be_nil
|
184
|
+
#data.length.should be_close(expected, tolerance)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should return image data in the given format" do
|
190
|
+
ImageScience.with_image image_path(ext) do |img|
|
191
|
+
[FIF_BMP, FIF_GIF, FIF_JPEG, FIF_PNG].each do |target_format|
|
192
|
+
data = img.buffer(target_format)
|
193
|
+
data.should_not be_nil
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# TODO: convert to use constants
|
199
|
+
it "should accept save flags" do
|
200
|
+
ImageScience.with_image image_path(ext) do |img|
|
201
|
+
jpg_large = img.buffer(FIF_JPEG, 0x80) # jpeg quality superb
|
202
|
+
jpg_small = img.buffer(FIF_JPEG, 0x08) # jpeg quality bad
|
203
|
+
jpg_small.length.should < jpg_large.length
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "fit_within" do
|
210
|
+
|
211
|
+
it "should resize image to fit within given dimensions (yield)" do
|
212
|
+
# 50 x 50 image -> shrink to 20, 50 -> 20, 20
|
213
|
+
ImageScience.with_image image_path(ext) do |img|
|
214
|
+
img.fit_within(20, 50) do |thumb|
|
215
|
+
thumb.width.should == 20
|
216
|
+
thumb.height.should == 20
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should resize image to fit within given dimensions (inline)" do
|
222
|
+
# 50 x 50 image -> shrink to 20, 50 -> 20, 20
|
223
|
+
ImageScience.with_image image_path(ext) do |img|
|
224
|
+
img.fit_within(20, 50)
|
225
|
+
img.width.should == 20
|
226
|
+
img.height.should == 20
|
227
|
+
end
|
228
|
+
|
229
|
+
# 100 x 50 image -> shrink to 50, 100 -> 50, 25
|
230
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
231
|
+
img.fit_within(50, 100)
|
232
|
+
img.width.should == 50
|
233
|
+
img.height.should == 25
|
234
|
+
end
|
235
|
+
|
236
|
+
# 100 x 50 image -> shrink to 150, 25 -> 50, 25
|
237
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
238
|
+
img.fit_within(150, 25)
|
239
|
+
img.width.should == 50
|
240
|
+
img.height.should == 25
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "get_pixel_color" do
|
247
|
+
expected = {
|
248
|
+
:jpg => [[62, 134, 122, 0], [0, 14, 7, 0]],
|
249
|
+
:png => [[62, 134, 121, 0], [1, 2, 2, 0]],
|
250
|
+
:gif => [[59, 135, 119, 0], [0, 2, 0, 0]],
|
251
|
+
:bmp => [[62, 134, 121, 0], [1, 2, 2, 0]],
|
252
|
+
:tif => [[62, 134, 121, 0], [1, 2, 2, 0]],
|
253
|
+
:xpm => [[255, 255, 255, 0], [0, 0, 0, 0]]
|
254
|
+
}
|
255
|
+
|
256
|
+
it "should get pixel color" do
|
257
|
+
ImageScience.with_image image_path(ext) do |img|
|
258
|
+
rgb = img.get_pixel_color(10,7)
|
259
|
+
rgb.should_not be_nil
|
260
|
+
rgb.should == expected[ext.to_sym][0]
|
261
|
+
|
262
|
+
rgb = img.get_pixel_color(24,0)
|
263
|
+
rgb.should_not be_nil
|
264
|
+
rgb.should == expected[ext.to_sym][1]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should get pixel color with []" do
|
269
|
+
ImageScience.with_image image_path(ext) do |img|
|
270
|
+
rgb = img[10,7]
|
271
|
+
rgb.should_not be_nil
|
272
|
+
rgb.should == expected[ext.to_sym][0]
|
273
|
+
|
274
|
+
rgb = img[24,0]
|
275
|
+
rgb.should_not be_nil
|
276
|
+
rgb.should == expected[ext.to_sym][1]
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "set_pixel_color" do
|
282
|
+
it "should set pixel color" do
|
283
|
+
ImageScience.with_image image_path(ext) do |img|
|
284
|
+
if img.colortype == FIC_PALETTE
|
285
|
+
img.set_pixel_color(10, 7, 3).should be_true
|
286
|
+
img[25, 25] = 4
|
287
|
+
else
|
288
|
+
img.set_pixel_color(10, 7, [100, 20, 50]).should be_true
|
289
|
+
img.set_pixel_color(25, 25, 100, 20, 50).should be_true
|
290
|
+
img.set_pixel_color(25, 25, [100, 20, 50, 0]).should be_true
|
291
|
+
img.set_pixel_color(25, 25, 100, 20, 50, 0).should be_true
|
292
|
+
img[25, 25] = [100, 20, 50, 0];
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "thumbnail" do
|
299
|
+
# Note: pix2 is 100x50
|
300
|
+
it "should create a proportional thumbnail" do
|
301
|
+
thumbnail_created = false
|
302
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
303
|
+
img.thumbnail(30) do |thumb|
|
304
|
+
thumb.should_not be_nil
|
305
|
+
thumb.width.should == 30
|
306
|
+
thumb.height.should == thumb.width / 2 # half of width
|
307
|
+
thumbnail_created = true
|
308
|
+
end
|
309
|
+
end
|
310
|
+
thumbnail_created.should be_true
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should create a proportional thumbnail in-place if no block given" do
|
314
|
+
thumbnail_created = false
|
315
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
316
|
+
img.thumbnail(30)
|
317
|
+
img.width.should == 30
|
318
|
+
img.height.should == img.width / 2 # half of width
|
319
|
+
thumbnail_created = true
|
320
|
+
end
|
321
|
+
thumbnail_created.should be_true
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "cropped_thumbnail" do
|
326
|
+
# Note: pix2 is 100x50
|
327
|
+
it "should create a square thumbnail" do
|
328
|
+
thumbnail_created = false
|
329
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
330
|
+
img.cropped_thumbnail(30) do |thumb|
|
331
|
+
thumb.should_not be_nil
|
332
|
+
thumb.width.should == 30
|
333
|
+
thumb.height.should == 30 # same as width
|
334
|
+
thumbnail_created = true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
thumbnail_created.should be_true
|
338
|
+
end
|
339
|
+
|
340
|
+
it "should create a square thumbnail in-place if no block given" do
|
341
|
+
thumbnail_created = false
|
342
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
343
|
+
img.cropped_thumbnail(30)
|
344
|
+
img.width.should == 30
|
345
|
+
img.height.should == 30 # same as width
|
346
|
+
thumbnail_created = true
|
347
|
+
end
|
348
|
+
thumbnail_created.should be_true
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe "crop" do
|
353
|
+
it "should crop the image in-place if no block given" do
|
354
|
+
ImageScience.with_image image_path(ext) do |img|
|
355
|
+
img.crop(0, 0, 25, 20).should be_true
|
356
|
+
img.width.should == 25
|
357
|
+
img.height.should == 20
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# image_type calls ImageScience.file_type, converts to string.
|
363
|
+
# allow calling as a class or instance method
|
364
|
+
describe "image_type" do
|
365
|
+
expected = {
|
366
|
+
'gif' => 'GIF',
|
367
|
+
'jpg' => 'JPEG',
|
368
|
+
'png' => 'PNG',
|
369
|
+
'bmp' => 'BMP',
|
370
|
+
'tif' => 'TIFF',
|
371
|
+
'xpm' => 'XPM'
|
372
|
+
}
|
373
|
+
it "should return the image type (class method)" do
|
374
|
+
ImageScience.image_type(image_path(ext)).should == expected[ext]
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should return the image type (instance method)" do
|
378
|
+
ImageScience.with_image image_path(ext) do |img|
|
379
|
+
img.image_type.should == expected[ext]
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# colorspace calls img.colortype & img.depth, converts to string
|
385
|
+
describe "colorspace" do
|
386
|
+
it "should return the color space" do
|
387
|
+
expected = {
|
388
|
+
'gif' => 'Indexed',
|
389
|
+
'jpg' => 'RGB',
|
390
|
+
'png' => 'RGB',
|
391
|
+
'bmp' => 'RGB',
|
392
|
+
'tif' => 'RGB',
|
393
|
+
'xpm' => 'Indexed'
|
394
|
+
}
|
395
|
+
ImageScience.with_image image_path(ext) do |img|
|
396
|
+
img.colorspace.should == expected[ext]
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
describe "depth" do
|
402
|
+
it "should return the BPP of the image" do
|
403
|
+
expected = {
|
404
|
+
'gif' => 8,
|
405
|
+
'jpg' => 24,
|
406
|
+
'png' => 24,
|
407
|
+
'bmp' => 24,
|
408
|
+
'tif' => 24,
|
409
|
+
'xpm' => 8
|
410
|
+
}
|
411
|
+
ImageScience.with_image image_path(ext) do |img|
|
412
|
+
img.depth.should == expected[ext]
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe "adjust_gamma" do
|
418
|
+
it "should perform gamma correction" do
|
419
|
+
ImageScience.with_image image_path(ext) do |img|
|
420
|
+
# darken image
|
421
|
+
img.adjust_gamma(0.5).should be_true
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
describe "adjust_brightness" do
|
427
|
+
it "should adjust brightness" do
|
428
|
+
ImageScience.with_image image_path(ext) do |img|
|
429
|
+
# 50% brighter
|
430
|
+
img.adjust_brightness(50).should be_true
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe "adjust_contrast" do
|
436
|
+
it "should adjust contrast" do
|
437
|
+
ImageScience.with_image image_path(ext) do |img|
|
438
|
+
# 50% less contrast
|
439
|
+
img.adjust_contrast(-50).should be_true
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
describe "invert" do
|
445
|
+
it "should invert pixel data" do
|
446
|
+
ImageScience.with_image image_path(ext) do |img|
|
447
|
+
# 50% less contrast
|
448
|
+
img.invert.should be_true
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
describe "histogram" do
|
454
|
+
it "should compute the image histogram" do
|
455
|
+
ImageScience.with_image image_path(ext) do |img|
|
456
|
+
h = img.histogram
|
457
|
+
h.should be_kind_of(Array)
|
458
|
+
h.length.should == 256
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
it "should compute the image histogram for a given channel" do
|
463
|
+
ImageScience.with_image image_path(ext) do |img|
|
464
|
+
[FICC_RED, FICC_GREEN, FICC_BLUE].each do |channel|
|
465
|
+
h = img.histogram(channel)
|
466
|
+
h.should be_kind_of(Array)
|
467
|
+
h.length.should == 256
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe "rotate" do
|
474
|
+
|
475
|
+
# test Rotate
|
476
|
+
it "should rotate the image 90 degrees" do
|
477
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
478
|
+
w, h = img.width, img.height
|
479
|
+
img.rotate(90)
|
480
|
+
# width & height reversed
|
481
|
+
img.width.should == h
|
482
|
+
img.height.should == w
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
# test RotateEx
|
487
|
+
it "should rotate the image 120 degrees about an origin" do
|
488
|
+
ImageScience.with_image image_path(ext, "pix2") do |img|
|
489
|
+
w, h = img.width, img.height
|
490
|
+
img.rotate(120, 0, 0, 20, 20)
|
491
|
+
# width & height unchanged
|
492
|
+
img.width.should == w
|
493
|
+
img.height.should == h
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
end
|
498
|
+
|
499
|
+
describe "flip_horizontal" do
|
500
|
+
it "should flip the image horizontally" do
|
501
|
+
ImageScience.with_image image_path(ext) do |img|
|
502
|
+
img.flip_horizontal.should be_true
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
describe "flip_vertical" do
|
508
|
+
it "should flip the image vertically" do
|
509
|
+
ImageScience.with_image image_path(ext) do |img|
|
510
|
+
img.flip_vertical.should be_true
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
private
|
519
|
+
|
520
|
+
def image_path(extension, basename = "pix")
|
521
|
+
raise "extension required" unless extension
|
522
|
+
File.join(@path, "#{basename}.#{extension}")
|
523
|
+
end
|
524
|
+
|
525
|
+
def tmp_image_path(extension, basename = "pix")
|
526
|
+
raise "extension required" unless extension
|
527
|
+
File.join(@path, "#{basename}-tmp.#{extension}")
|
528
|
+
end
|
529
|
+
|
530
|
+
end
|
531
|
+
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|