ruby-vips 0.3.14 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +22 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +46 -31
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.md +101 -145
- data/Rakefile +45 -0
- data/TODO +8 -32
- data/VERSION +1 -0
- data/example/annotate.rb +17 -0
- data/example/daltonize8.rb +75 -0
- data/example/example1.rb +84 -0
- data/example/example2.rb +31 -0
- data/example/example3.rb +19 -0
- data/example/example4.rb +18 -0
- data/example/example5.rb +31 -0
- data/example/trim8.rb +41 -0
- data/example/watermark.rb +44 -0
- data/example/wobble.rb +36 -0
- data/lib/vips.rb +151 -14
- data/lib/vips/access.rb +14 -0
- data/lib/vips/align.rb +11 -0
- data/lib/vips/angle.rb +12 -0
- data/lib/vips/angle45.rb +16 -0
- data/lib/vips/argument.rb +163 -0
- data/lib/vips/bandformat.rb +20 -0
- data/lib/vips/call.rb +302 -0
- data/lib/vips/coding.rb +14 -0
- data/lib/vips/demandstyle.rb +35 -0
- data/lib/vips/direction.rb +11 -0
- data/lib/vips/error.rb +30 -0
- data/lib/vips/extend.rb +22 -0
- data/lib/vips/foreignflags.rb +20 -0
- data/lib/vips/image.rb +1382 -0
- data/lib/vips/interpolate.rb +37 -0
- data/lib/vips/interpretation.rb +28 -0
- data/lib/vips/methods.rb +1807 -0
- data/lib/vips/operation.rb +19 -0
- data/ruby-vips8.gemspec +112 -0
- data/spec/image_spec.rb +515 -0
- data/spec/samples/balloon.v +0 -0
- data/spec/samples/ghost.ppm +405 -0
- data/spec/samples/huge.jpg +0 -0
- data/spec/samples/icc.jpg +0 -0
- data/spec/samples/lcd.icc +0 -0
- data/spec/samples/lion.svg +154 -0
- data/spec/samples/sample.csv +7 -0
- data/spec/samples/sample.exr +0 -0
- data/spec/samples/wagon.jpg +0 -0
- data/spec/samples/wagon.v +0 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/vips_spec.rb +74 -0
- metadata +110 -70
- data/ext/extconf.rb +0 -31
- data/ext/header.c +0 -457
- data/ext/header.h +0 -9
- data/ext/image.c +0 -629
- data/ext/image.h +0 -72
- data/ext/image_arithmetic.c +0 -936
- data/ext/image_arithmetic.h +0 -38
- data/ext/image_boolean.c +0 -301
- data/ext/image_boolean.h +0 -8
- data/ext/image_colour.c +0 -590
- data/ext/image_colour.h +0 -36
- data/ext/image_conversion.c +0 -884
- data/ext/image_conversion.h +0 -38
- data/ext/image_convolution.c +0 -368
- data/ext/image_convolution.h +0 -13
- data/ext/image_freq_filt.c +0 -740
- data/ext/image_freq_filt.h +0 -27
- data/ext/image_histograms_lut.c +0 -643
- data/ext/image_histograms_lut.h +0 -28
- data/ext/image_morphology.c +0 -327
- data/ext/image_morphology.h +0 -13
- data/ext/image_mosaicing.c +0 -554
- data/ext/image_mosaicing.h +0 -15
- data/ext/image_relational.c +0 -384
- data/ext/image_relational.h +0 -8
- data/ext/image_resample.c +0 -249
- data/ext/image_resample.h +0 -9
- data/ext/interpolator.c +0 -106
- data/ext/interpolator.h +0 -7
- data/ext/mask.c +0 -347
- data/ext/mask.h +0 -18
- data/ext/reader.c +0 -261
- data/ext/reader.h +0 -2
- data/ext/ruby_vips.c +0 -188
- data/ext/ruby_vips.h +0 -72
- data/ext/tags +0 -450
- data/ext/writer.c +0 -371
- data/ext/writer.h +0 -2
- data/lib/vips/reader.rb +0 -272
- data/lib/vips/version.rb +0 -3
- data/lib/vips/writer.rb +0 -342
- data/ruby-vips.gemspec +0 -100
- data/ruby.supp +0 -134
data/lib/vips/call.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
module Vips
|
2
|
+
|
3
|
+
# call a vips operation ... this will crash if there's a GC during the call,
|
4
|
+
# I've really no idea why :-(
|
5
|
+
#
|
6
|
+
# Workaround: don't call this directly, use call_base (see below) instead.
|
7
|
+
# This will disable the GC, call this operation, then reenable it.
|
8
|
+
|
9
|
+
private
|
10
|
+
def self.call_base_nogc(name, instance, option_string, supplied_values)
|
11
|
+
log "in Vips::call_base"
|
12
|
+
log "name = #{name}"
|
13
|
+
log "instance = #{instance}"
|
14
|
+
log "option_string = #{option_string}"
|
15
|
+
log "supplied_values are:"
|
16
|
+
supplied_values.each {|x| log " #{x}"}
|
17
|
+
|
18
|
+
if supplied_values.last.is_a? Hash
|
19
|
+
optional_values = supplied_values.last
|
20
|
+
supplied_values.delete_at -1
|
21
|
+
else
|
22
|
+
optional_values = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
op = Vips::Operation.new name
|
27
|
+
rescue
|
28
|
+
raise Vips::Error, "no operator '#{name}'"
|
29
|
+
end
|
30
|
+
|
31
|
+
# set string options first
|
32
|
+
log "setting string options ..."
|
33
|
+
if option_string
|
34
|
+
if op.set_from_string(option_string) != 0
|
35
|
+
raise Error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
all_args = op.get_args
|
40
|
+
|
41
|
+
# the instance, if supplied, must be a vips image ... we use it for
|
42
|
+
# match_image, below
|
43
|
+
if instance and not instance.is_a? Vips::Image
|
44
|
+
raise Vips::Error, "@instance is not a Vips::Image."
|
45
|
+
end
|
46
|
+
|
47
|
+
# if the op needs images but the user supplies constants, we expand
|
48
|
+
# them to match the first input image argument ... find the first
|
49
|
+
# image
|
50
|
+
log "searching for first image argument ..."
|
51
|
+
match_image = instance
|
52
|
+
if match_image == nil
|
53
|
+
match_image = supplied_values.find {|x| x.is_a? Vips::Image}
|
54
|
+
end
|
55
|
+
if match_image == nil
|
56
|
+
match = optional_values.find do |name, value|
|
57
|
+
value.is_a? Vips::Image
|
58
|
+
end
|
59
|
+
# if we found a match, it'll be [name, value]
|
60
|
+
if match
|
61
|
+
match_image = match[1]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# find unassigned required input args
|
66
|
+
log "finding unassigned required input arguments ..."
|
67
|
+
required_input = all_args.select do |arg|
|
68
|
+
not arg.isset and
|
69
|
+
(arg.flags & :input) != 0 and
|
70
|
+
(arg.flags & :required) != 0
|
71
|
+
end
|
72
|
+
|
73
|
+
# do we have a non-nil instance? set the first image arg with this
|
74
|
+
if instance != nil
|
75
|
+
log "setting first image arg with instance ..."
|
76
|
+
x = required_input.find do |arg|
|
77
|
+
gtype = GLib::Type["VipsImage"]
|
78
|
+
vtype = arg.prop.value_type
|
79
|
+
|
80
|
+
vtype.type_is_a? gtype
|
81
|
+
end
|
82
|
+
if x == nil
|
83
|
+
raise Vips::Error,
|
84
|
+
"No #{instance.class} argument to #{name}."
|
85
|
+
end
|
86
|
+
x.set_value match_image, instance
|
87
|
+
required_input.delete x
|
88
|
+
end
|
89
|
+
|
90
|
+
if required_input.length != supplied_values.length
|
91
|
+
raise Vips::Error,
|
92
|
+
"Wrong number of arguments. '#{name}' requires " +
|
93
|
+
"#{required_input.length} arguments, you supplied " +
|
94
|
+
"#{supplied_values.length}."
|
95
|
+
end
|
96
|
+
|
97
|
+
log "setting required input arguments ..."
|
98
|
+
required_input.zip(supplied_values).each do |arg, value|
|
99
|
+
arg.set_value match_image, value
|
100
|
+
end
|
101
|
+
|
102
|
+
# find optional unassigned input args
|
103
|
+
log "finding optional unassigned input arguments ..."
|
104
|
+
optional_input = all_args.select do |arg|
|
105
|
+
not arg.isset and
|
106
|
+
(arg.flags & :input) != 0 and
|
107
|
+
(arg.flags & :required) == 0
|
108
|
+
end
|
109
|
+
|
110
|
+
# make a hash from name to arg
|
111
|
+
optional_input = Hash[
|
112
|
+
optional_input.map(&:name).zip(optional_input)]
|
113
|
+
|
114
|
+
# find optional unassigned output args
|
115
|
+
log "finding optional unassigned output arguments ..."
|
116
|
+
optional_output = all_args.select do |arg|
|
117
|
+
not arg.isset and
|
118
|
+
(arg.flags & :output) != 0 and
|
119
|
+
(arg.flags & :required) == 0
|
120
|
+
end
|
121
|
+
optional_output = Hash[
|
122
|
+
optional_output.map(&:name).zip(optional_output)]
|
123
|
+
|
124
|
+
# set all optional args
|
125
|
+
log "setting optional values ..."
|
126
|
+
optional_values.each do |name, value|
|
127
|
+
# we are passed symbols as keys
|
128
|
+
name = name.to_s
|
129
|
+
if optional_input.has_key? name
|
130
|
+
log "setting #{name} to #{value}"
|
131
|
+
optional_input[name].set_value match_image, value
|
132
|
+
elsif optional_output.has_key? name and value != true
|
133
|
+
raise Vips::Error,
|
134
|
+
"Optional output argument #{name} must be true."
|
135
|
+
elsif not optional_output.has_key? name
|
136
|
+
raise Vips::Error, "No such option '#{name}',"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
log "building ..."
|
141
|
+
|
142
|
+
op2 = Vips::cache_operation_lookup op
|
143
|
+
if op2
|
144
|
+
log "cache hit"
|
145
|
+
op = op2
|
146
|
+
|
147
|
+
all_args = op.get_args
|
148
|
+
|
149
|
+
# find optional output args
|
150
|
+
optional_output = all_args.select do |arg|
|
151
|
+
(arg.flags & :output) != 0 and
|
152
|
+
(arg.flags & :required) == 0
|
153
|
+
end
|
154
|
+
optional_output = Hash[
|
155
|
+
optional_output.map(&:name).zip(optional_output)]
|
156
|
+
else
|
157
|
+
log "cache miss ... building"
|
158
|
+
if not op.build
|
159
|
+
raise Vips::Error
|
160
|
+
end
|
161
|
+
showall
|
162
|
+
|
163
|
+
log "adding to cache ... "
|
164
|
+
Vips::cache_operation_add op
|
165
|
+
end
|
166
|
+
|
167
|
+
log "fetching outputs ..."
|
168
|
+
|
169
|
+
# gather output args
|
170
|
+
out = []
|
171
|
+
|
172
|
+
all_args.each do |arg|
|
173
|
+
# required output
|
174
|
+
if (arg.flags & :output) != 0 and
|
175
|
+
(arg.flags & :required) != 0
|
176
|
+
log "fetching required output #{arg.name}"
|
177
|
+
out << arg.get_value
|
178
|
+
end
|
179
|
+
|
180
|
+
# modified input arg ... this will get the result of the
|
181
|
+
# copy() we did in Argument.set_value above
|
182
|
+
if (arg.flags & :input) != 0 and
|
183
|
+
(arg.flags & :modify) != 0
|
184
|
+
log "fetching modified input arg ..."
|
185
|
+
out << arg.get_value
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
opts = {}
|
190
|
+
optional_values.each do |name, value|
|
191
|
+
# we are passed symbols as keys
|
192
|
+
name = name.to_s
|
193
|
+
if optional_output.has_key? name
|
194
|
+
log "fetching optional output arg ..."
|
195
|
+
opts[name] = optional_output[name].get_value
|
196
|
+
end
|
197
|
+
end
|
198
|
+
out << opts if opts != {}
|
199
|
+
|
200
|
+
if out.length == 1
|
201
|
+
out = out[0]
|
202
|
+
elsif out.length == 0
|
203
|
+
out = nil
|
204
|
+
end
|
205
|
+
|
206
|
+
log "unreffing outputs ..."
|
207
|
+
op.unref_outputs
|
208
|
+
op = nil
|
209
|
+
# showall
|
210
|
+
|
211
|
+
log "success! #{name}.out = #{out}"
|
212
|
+
|
213
|
+
return out
|
214
|
+
end
|
215
|
+
|
216
|
+
# run call_base_nogc, with the GC disabled
|
217
|
+
private
|
218
|
+
def self.call_base(name, instance, option_string, supplied_values)
|
219
|
+
gc_was_enabled = GC.disable
|
220
|
+
begin
|
221
|
+
result = call_base_nogc name, instance,
|
222
|
+
option_string, supplied_values
|
223
|
+
ensure
|
224
|
+
GC.enable if gc_was_enabled
|
225
|
+
end
|
226
|
+
|
227
|
+
return result
|
228
|
+
end
|
229
|
+
|
230
|
+
public
|
231
|
+
|
232
|
+
# This is the public entry point for the vips binding. {call} will run
|
233
|
+
# any vips operation, for example:
|
234
|
+
#
|
235
|
+
# ```ruby
|
236
|
+
# out = Vips::call "black", 100, 100, :bands => 12
|
237
|
+
# ```
|
238
|
+
#
|
239
|
+
# will call the C function
|
240
|
+
#
|
241
|
+
# ```C
|
242
|
+
# vips_black( &out, 100, 100, "bands", 12, NULL );
|
243
|
+
# ```
|
244
|
+
#
|
245
|
+
# There are {Image#method_missing} hooks which will run {call} for you
|
246
|
+
# on {Image} for undefined instance or class methods. So you can also
|
247
|
+
# write:
|
248
|
+
#
|
249
|
+
# ```ruby
|
250
|
+
# out = Vips::Image.black 100, 100, :bands => 12
|
251
|
+
# ```
|
252
|
+
#
|
253
|
+
# Or perhaps:
|
254
|
+
#
|
255
|
+
# ```ruby
|
256
|
+
# x = Vips::Image.black 100, 100
|
257
|
+
# y = x.invert
|
258
|
+
# ```
|
259
|
+
#
|
260
|
+
# to run the `vips_invert()` operator.
|
261
|
+
#
|
262
|
+
# There are also a set of operator overloads and some convenience functions,
|
263
|
+
# see {Image}.
|
264
|
+
#
|
265
|
+
# If the operator needs a vector constant, {call} will turn a scalar into a
|
266
|
+
# vector for you. So for `x.linear(a, b)`, which calculates
|
267
|
+
# `x * a + b` where `a` and `b` are vector constants, you can write:
|
268
|
+
#
|
269
|
+
# ```ruby
|
270
|
+
# x = Vips::Image.black 100, 100, :bands => 3
|
271
|
+
# y = x.linear(1, 2)
|
272
|
+
# y = x.linear([1], 4)
|
273
|
+
# y = x.linear([1, 2, 3], 4)
|
274
|
+
# ```
|
275
|
+
#
|
276
|
+
# or any other combination. The operator overloads use this facility to
|
277
|
+
# support all the variations on:
|
278
|
+
#
|
279
|
+
# ```ruby
|
280
|
+
# x = Vips::Image.black 100, 100, :bands => 3
|
281
|
+
# y = x * 2
|
282
|
+
# y = x + [1,2,3]
|
283
|
+
# y = x % [1]
|
284
|
+
# ```
|
285
|
+
#
|
286
|
+
# Similarly, whereever an image is required, you can use a constant. The
|
287
|
+
# constant will be expanded to an image matching the first input image
|
288
|
+
# argument. For example, you can write:
|
289
|
+
#
|
290
|
+
# ```
|
291
|
+
# x = Vips::Image.black 100, 100, :bands => 3
|
292
|
+
# y = x.bandjoin(255)
|
293
|
+
# ```
|
294
|
+
#
|
295
|
+
# to add an extra band to the image where each pixel in the new band has
|
296
|
+
# the constant value 255.
|
297
|
+
|
298
|
+
def self.call(name, *args)
|
299
|
+
Vips::call_base name, nil, "", args
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
data/lib/vips/coding.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Vips
|
2
|
+
|
3
|
+
# How pixels are coded.
|
4
|
+
#
|
5
|
+
# Normally, pixels are uncoded and can be manipulated as you would expect.
|
6
|
+
# However some file formats code pixels for compression, and sometimes it's
|
7
|
+
# useful to be able to manipulate images in the coded format.
|
8
|
+
#
|
9
|
+
# * `:none` pixels are not coded
|
10
|
+
# * `:labq` pixels encode 3 float CIELAB values as 4 uchar
|
11
|
+
# * `:rad` pixels encode 3 float RGB as 4 uchar (Radiance coding)
|
12
|
+
class Coding
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Vips
|
2
|
+
|
3
|
+
# Operations can hint to the VIPS image IO
|
4
|
+
# system about the kind of demand geometry they prefer.
|
5
|
+
#
|
6
|
+
# These demand styles are given below in order of increasing
|
7
|
+
# restrictiveness. When demanding output from a pipeline,
|
8
|
+
# vips_image_generate()
|
9
|
+
# will use the most restrictive of the styles requested by the operations
|
10
|
+
# in the pipeline.
|
11
|
+
#
|
12
|
+
# * `:thinstrip` --- This operation would like to output strips
|
13
|
+
# the width of the image and a few pels high. This is option suitable
|
14
|
+
# for point-to-point operations, such as those in the arithmetic
|
15
|
+
# package.
|
16
|
+
#
|
17
|
+
# This option is only efficient for cases where each output pel depends
|
18
|
+
# upon the pel in the corresponding position in the input image.
|
19
|
+
#
|
20
|
+
# * `:fatstrip` --- This operation would like to output strips
|
21
|
+
# the width of the image and as high as possible. This option is
|
22
|
+
# suitable for area operations which do not violently transform
|
23
|
+
# coordinates, such as vips_conv().
|
24
|
+
#
|
25
|
+
# * `:smalltile` --- This is the most general demand format.
|
26
|
+
# Output is demanded in small (around 100x100 pel) sections. This style
|
27
|
+
# works reasonably efficiently, even for bizzare operations like 45
|
28
|
+
# degree rotate.
|
29
|
+
#
|
30
|
+
# * `:any` --- This image is not being demand-read from a disc
|
31
|
+
# file (even indirectly) so any demand style is OK. It's used for
|
32
|
+
# things like vips_black() where the pixels are calculated.
|
33
|
+
class DemandStyle
|
34
|
+
end
|
35
|
+
end
|
data/lib/vips/error.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Vips
|
2
|
+
|
3
|
+
# The ruby-vips error class.
|
4
|
+
class Error < RuntimeError
|
5
|
+
# @param msg [String] The error message. If this is not supplied, grab
|
6
|
+
# and clear the vips error buffer and use that.
|
7
|
+
def initialize(msg = nil)
|
8
|
+
if msg
|
9
|
+
@details = msg
|
10
|
+
elsif Vips::error_buffer != ""
|
11
|
+
@details = Vips::error_buffer
|
12
|
+
Vips::error_clear
|
13
|
+
else
|
14
|
+
@details = nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Pretty-print a {Vips::Error}.
|
19
|
+
#
|
20
|
+
# @return [String] The error message
|
21
|
+
def to_s
|
22
|
+
if @details != nil
|
23
|
+
@details
|
24
|
+
else
|
25
|
+
super.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/lib/vips/extend.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Vips
|
2
|
+
|
3
|
+
# When the edges of an image are extended, you can specify
|
4
|
+
# how you want the extension done.
|
5
|
+
# See {Vips::Image.embed}, {Vips::Image.conv}, {Vips::Image.affine} and
|
6
|
+
# so on.
|
7
|
+
#
|
8
|
+
# * `:black` new pixels are black, ie. all bits are zero.
|
9
|
+
#
|
10
|
+
# * `:copy` each new pixel takes the value of the nearest edge pixel
|
11
|
+
#
|
12
|
+
# * `:repeat` the image is tiled to fill the new area
|
13
|
+
#
|
14
|
+
# * `:mirror` the image is reflected and tiled to reduce hash edges
|
15
|
+
#
|
16
|
+
# * `:white` new pixels are white, ie. all bits are set
|
17
|
+
#
|
18
|
+
# * `:background` colour set from the @background property
|
19
|
+
|
20
|
+
class Extend
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Vips
|
2
|
+
|
3
|
+
# Some hints about the image loader.
|
4
|
+
#
|
5
|
+
# * `:partial` means that the image can be read directly from the
|
6
|
+
# file without needing to be unpacked to a temporary image first.
|
7
|
+
#
|
8
|
+
# * `:sequential` means that the loader supports lazy reading, but
|
9
|
+
# only top-to-bottom (sequential) access. Formats like PNG can read
|
10
|
+
# sets of scanlines, for example, but only in order.
|
11
|
+
#
|
12
|
+
# If neither partial` or sequential` is set, the loader only supports
|
13
|
+
# whole image read. Setting both partial` and sequential` is an error.
|
14
|
+
#
|
15
|
+
# * `:bigendian` means that image pixels are most-significant byte
|
16
|
+
# first. Depending on the native byte order of the host machine, you may
|
17
|
+
# need to swap bytes. See vips_copy().
|
18
|
+
class ForeignFlags
|
19
|
+
end
|
20
|
+
end
|
data/lib/vips/image.rb
ADDED
@@ -0,0 +1,1382 @@
|
|
1
|
+
# This module provides a set of overrides for the [vips image processing
|
2
|
+
# library](http://www.vips.ecs.soton.ac.uk)
|
3
|
+
# used via the [gobject-introspection
|
4
|
+
# gem](https://rubygems.org/gems/gobject-introspection).
|
5
|
+
#
|
6
|
+
# It needs vips-8.2 or later to be installed,
|
7
|
+
# and `Vips-8.0.typelib`, the vips typelib, needs to be on your
|
8
|
+
# `GI_TYPELIB_PATH`.
|
9
|
+
#
|
10
|
+
# # Example
|
11
|
+
#
|
12
|
+
# ```ruby
|
13
|
+
# require 'vips'
|
14
|
+
#
|
15
|
+
# if ARGV.length < 2
|
16
|
+
# raise "usage: #{$PROGRAM_NAME}: input-file output-file"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# im = Vips::Image.new_from_file ARGV[0], :access => :sequential
|
20
|
+
#
|
21
|
+
# im *= [1, 2, 1]
|
22
|
+
#
|
23
|
+
# mask = Vips::Image.new_from_array [
|
24
|
+
# [-1, -1, -1],
|
25
|
+
# [-1, 16, -1],
|
26
|
+
# [-1, -1, -1]], 8
|
27
|
+
# im = im.conv mask
|
28
|
+
#
|
29
|
+
# im.write_to_file ARGV[1]
|
30
|
+
# ```
|
31
|
+
#
|
32
|
+
# This example loads a file, boosts the green channel (I'm not sure why),
|
33
|
+
# sharpens the image, and saves it back to disc again.
|
34
|
+
#
|
35
|
+
# Reading this example line by line, we have:
|
36
|
+
#
|
37
|
+
# ```ruby
|
38
|
+
# im = Vips::Image.new_from_file ARGV[0], :access => :sequential
|
39
|
+
# ```
|
40
|
+
#
|
41
|
+
# {Image.new_from_file} can load any image file supported by vips. In this
|
42
|
+
# example, we will be accessing pixels top-to-bottom as we sweep through the
|
43
|
+
# image reading and writing, so `:sequential` access mode is best for us. The
|
44
|
+
# default mode is `:random`, this allows for full random access to image pixels,
|
45
|
+
# but is slower and needs more memory. See {Access}
|
46
|
+
# for full details
|
47
|
+
# on the various modes available. You can also load formatted images from
|
48
|
+
# memory buffers or create images that wrap C-style memory arrays.
|
49
|
+
#
|
50
|
+
# The next line:
|
51
|
+
#
|
52
|
+
# ```ruby
|
53
|
+
# im *= [1, 2, 1]
|
54
|
+
# ```
|
55
|
+
#
|
56
|
+
# Multiplying the image by an array constant uses one array element for each
|
57
|
+
# image band. This line assumes that the input image has three bands and will
|
58
|
+
# double the middle band. For RGB images, that's doubling green.
|
59
|
+
#
|
60
|
+
# Next we have:
|
61
|
+
#
|
62
|
+
# ```ruby
|
63
|
+
# mask = Vips::Image.new_from_array [
|
64
|
+
# [-1, -1, -1],
|
65
|
+
# [-1, 16, -1],
|
66
|
+
# [-1, -1, -1]], 8
|
67
|
+
# im = im.conv mask
|
68
|
+
# ```
|
69
|
+
#
|
70
|
+
# {Image.new_from_array} creates an image from an array constant. The 8 at
|
71
|
+
# the end sets the scale: the amount to divide the image by after
|
72
|
+
# integer convolution. See the libvips API docs for `vips_conv()` (the operation
|
73
|
+
# invoked by {Vips::Image.conv}) for details on the convolution operator.
|
74
|
+
#
|
75
|
+
# Finally:
|
76
|
+
#
|
77
|
+
# ```ruby
|
78
|
+
# im.write_to_file ARGV[1]
|
79
|
+
# ```
|
80
|
+
#
|
81
|
+
# {Vips::Image.write_to_file} writes an image back to the filesystem. It can
|
82
|
+
# write any format supported by vips: the file type is set from the filename
|
83
|
+
# suffix. You can also write formatted images to memory buffers, or dump
|
84
|
+
# image data to a raw memory array.
|
85
|
+
#
|
86
|
+
# # How it works
|
87
|
+
#
|
88
|
+
# The C sources to libvips include a set of specially formatted
|
89
|
+
# comments which describe its interfaces. When you compile the library,
|
90
|
+
# gobject-introspection generates `Vips-8.0.typelib`, a file
|
91
|
+
# describing how to use libvips.
|
92
|
+
#
|
93
|
+
# The `gobject-introspection` gem loads this typelib and uses it to let you
|
94
|
+
# call
|
95
|
+
# functions in libvips directly from Ruby. However, the interface you get
|
96
|
+
# from raw gobject-introspection is rather ugly, so the `ruby-vips` gem
|
97
|
+
# adds a set
|
98
|
+
# of overrides which try to make it nicer to use.
|
99
|
+
#
|
100
|
+
# The API you end up with is a Ruby-ish version of the [VIPS C
|
101
|
+
# API](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/).
|
102
|
+
# Full documentation
|
103
|
+
# on the operations and what they do is there, you can use it directly. This
|
104
|
+
# document explains the extra features of the Ruby API and lists the available
|
105
|
+
# operations very briefly.
|
106
|
+
#
|
107
|
+
# # Automatic wrapping
|
108
|
+
#
|
109
|
+
# `ruby-vips` adds a {Image.method_missing} handler to {Image} and uses
|
110
|
+
# it to look up vips operations. For example, the libvips operation `add`, which
|
111
|
+
# appears in C as `vips_add()`, appears in Ruby as {Vips::Image.add}.
|
112
|
+
#
|
113
|
+
# The operation's list of required arguments is searched and the first input
|
114
|
+
# image is set to the value of `self`. Operations which do not take an input
|
115
|
+
# image, such as {Image.black}, appear as class methods. The remainder of
|
116
|
+
# the arguments you supply in the function call are used to set the other
|
117
|
+
# required input arguments. If the final supplied argument is a hash, it is used
|
118
|
+
# to set any optional input arguments. The result is the required output
|
119
|
+
# argument if there is only one result, or an array of values if the operation
|
120
|
+
# produces several results. If the operation has optional output objects, they
|
121
|
+
# are returned as a final hash.
|
122
|
+
#
|
123
|
+
# For example, {Vips::Image.min}, the vips operation that searches an image for
|
124
|
+
# the minimum value, has a large number of optional arguments. You can use it to
|
125
|
+
# find the minimum value like this:
|
126
|
+
#
|
127
|
+
# ```ruby
|
128
|
+
# min_value = image.min
|
129
|
+
# ```
|
130
|
+
#
|
131
|
+
# You can ask it to return the position of the minimum with `:x` and `:y`.
|
132
|
+
#
|
133
|
+
# ```ruby
|
134
|
+
# min_value, opts = min :x => true, :y => true
|
135
|
+
# x_pos = opts['x']
|
136
|
+
# y_pos = opts['y']
|
137
|
+
# ```
|
138
|
+
#
|
139
|
+
# Now `x_pos` and `y_pos` will have the coordinates of the minimum value.
|
140
|
+
# There's actually a convenience function for this, {Vips::Image.minpos}.
|
141
|
+
#
|
142
|
+
# You can also ask for the top *n* minimum, for example:
|
143
|
+
#
|
144
|
+
# ```ruby
|
145
|
+
# min_value, opts = min :size => 10, :x_array => true, :y_array => true
|
146
|
+
# x_pos = opts['x_array']
|
147
|
+
# y_pos = opts['y_array']
|
148
|
+
# ```
|
149
|
+
#
|
150
|
+
# Now `x_pos` and `y_pos` will be 10-element arrays.
|
151
|
+
#
|
152
|
+
# Because operations are member functions and return the result image, you can
|
153
|
+
# chain them. For example, you can write:
|
154
|
+
#
|
155
|
+
# ```ruby
|
156
|
+
# result_image = image.real.cos
|
157
|
+
# ```
|
158
|
+
#
|
159
|
+
# to calculate the cosine of the real part of a complex image.
|
160
|
+
# There are also a full set
|
161
|
+
# of arithmetic operator overloads, see below.
|
162
|
+
#
|
163
|
+
# libvips types are also automatically wrapped. The override looks at the type
|
164
|
+
# of argument required by the operation and converts the value you supply,
|
165
|
+
# when it can. For example, {Vips::Image.linear} takes a `VipsArrayDouble` as
|
166
|
+
# an argument
|
167
|
+
# for the set of constants to use for multiplication. You can supply this
|
168
|
+
# value as an integer, a float, or some kind of compound object and it
|
169
|
+
# will be converted for you. You can write:
|
170
|
+
#
|
171
|
+
# ```ruby
|
172
|
+
# result_image = image.linear 1, 3
|
173
|
+
# result_image = image.linear 12.4, 13.9
|
174
|
+
# result_image = image.linear [1, 2, 3], [4, 5, 6]
|
175
|
+
# result_image = image.linear 1, [4, 5, 6]
|
176
|
+
# ```
|
177
|
+
#
|
178
|
+
# And so on. A set of overloads are defined for {Vips::Image.linear}, see below.
|
179
|
+
#
|
180
|
+
# It does a couple of more ambitious conversions. It will automatically convert
|
181
|
+
# to and from the various vips types, like `VipsBlob` and `VipsArrayImage`. For
|
182
|
+
# example, you can read the ICC profile out of an image like this:
|
183
|
+
#
|
184
|
+
# ```ruby
|
185
|
+
# profile = im.get_value "icc-profile-data"
|
186
|
+
# ```
|
187
|
+
#
|
188
|
+
# and profile will be a byte array.
|
189
|
+
#
|
190
|
+
# If an operation takes several input images, you can use a constant for all but
|
191
|
+
# one of them and the wrapper will expand the constant to an image for you. For
|
192
|
+
# example, {Vips::Image.ifthenelse} uses a condition image to pick pixels
|
193
|
+
# between a then and an else image:
|
194
|
+
#
|
195
|
+
# ```ruby
|
196
|
+
# result_image = condition_image.ifthenelse then_image, else_image
|
197
|
+
# ```
|
198
|
+
#
|
199
|
+
# You can use a constant instead of either the then or the else parts and it
|
200
|
+
# will be expanded to an image for you. If you use a constant for both then and
|
201
|
+
# else, it will be expanded to match the condition image. For example:
|
202
|
+
#
|
203
|
+
# ```ruby
|
204
|
+
# result_image = condition_image.ifthenelse [0, 255, 0], [255, 0, 0]
|
205
|
+
# ```
|
206
|
+
#
|
207
|
+
# Will make an image where true pixels are green and false pixels are red.
|
208
|
+
#
|
209
|
+
# This is useful for {Vips::Image.bandjoin}, the thing to join two or more
|
210
|
+
# images up bandwise. You can write:
|
211
|
+
#
|
212
|
+
# ```ruby
|
213
|
+
# rgba = rgb.bandjoin 255
|
214
|
+
# ```
|
215
|
+
#
|
216
|
+
# to append a constant 255 band to an image, perhaps to add an alpha channel. Of
|
217
|
+
# course you can also write:
|
218
|
+
#
|
219
|
+
# ```ruby
|
220
|
+
# result_image = image1.bandjoin image2
|
221
|
+
# result_image = image1.bandjoin [image2, image3]
|
222
|
+
# result_image = Vips::Image.bandjoin [image1, image2, image3]
|
223
|
+
# result_image = image1.bandjoin [image2, 255]
|
224
|
+
# ```
|
225
|
+
#
|
226
|
+
# and so on.
|
227
|
+
#
|
228
|
+
# # Automatic YARD documentation
|
229
|
+
#
|
230
|
+
# The bulk of these API docs are generated automatically by
|
231
|
+
# {Vips::generate_yard}. It examines
|
232
|
+
# libvips and writes a summary of each operation and the arguments and options
|
233
|
+
# that operation expects.
|
234
|
+
#
|
235
|
+
# Use the [C API
|
236
|
+
# docs](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips)
|
237
|
+
# for more detail.
|
238
|
+
#
|
239
|
+
# # Exceptions
|
240
|
+
#
|
241
|
+
# The wrapper spots errors from vips operations and raises the {Vips::Error}
|
242
|
+
# exception. You can catch it in the usual way.
|
243
|
+
#
|
244
|
+
# # Draw operations
|
245
|
+
#
|
246
|
+
# Paint operations like {Vips::Image.draw_circle} and {Vips::Image.draw_line}
|
247
|
+
# modify their input image. This
|
248
|
+
# makes them hard to use with the rest of libvips: you need to be very careful
|
249
|
+
# about the order in which operations execute or you can get nasty crashes.
|
250
|
+
#
|
251
|
+
# The wrapper spots operations of this type and makes a private copy of the
|
252
|
+
# image in memory before calling the operation. This stops crashes, but it does
|
253
|
+
# make it inefficient. If you draw 100 lines on an image, for example, you'll
|
254
|
+
# copy the image 100 times. The wrapper does make sure that memory is recycled
|
255
|
+
# where possible, so you won't have 100 copies in memory.
|
256
|
+
#
|
257
|
+
# If you want to avoid the copies, you'll need to call drawing operations
|
258
|
+
# yourself.
|
259
|
+
#
|
260
|
+
# # Overloads
|
261
|
+
#
|
262
|
+
# The wrapper defines the usual set of arithmetic, boolean and relational
|
263
|
+
# overloads on image. You can mix images, constants and lists of constants
|
264
|
+
# (almost) freely. For example, you can write:
|
265
|
+
#
|
266
|
+
# ```ruby
|
267
|
+
# result_image = ((image * [1, 2, 3]).abs < 128) | 4
|
268
|
+
# ```
|
269
|
+
#
|
270
|
+
# # Expansions
|
271
|
+
#
|
272
|
+
# Some vips operators take an enum to select an action, for example
|
273
|
+
# {Vips::Image.math} can be used to calculate sine of every pixel like this:
|
274
|
+
#
|
275
|
+
# ```ruby
|
276
|
+
# result_image = image.math :sin
|
277
|
+
# ```
|
278
|
+
#
|
279
|
+
# This is annoying, so the wrapper expands all these enums into separate members
|
280
|
+
# named after the enum. So you can write:
|
281
|
+
#
|
282
|
+
# ```ruby
|
283
|
+
# result_image = image.sin
|
284
|
+
# ```
|
285
|
+
#
|
286
|
+
# # Convenience functions
|
287
|
+
#
|
288
|
+
# The wrapper defines a few extra useful utility functions:
|
289
|
+
# {Vips::Image.get_value}, {Vips::Image.set_value}, {Vips::Image.bandsplit},
|
290
|
+
# {Vips::Image.maxpos}, {Vips::Image.minpos},
|
291
|
+
# {Vips::Image.median}.
|
292
|
+
|
293
|
+
module Vips
|
294
|
+
|
295
|
+
# This class represents a libvips image. See the {Vips} module documentation
|
296
|
+
# for an introduction to using this module.
|
297
|
+
|
298
|
+
class Image
|
299
|
+
private
|
300
|
+
|
301
|
+
# handy for overloads ... want to be able to apply a function to an
|
302
|
+
# array or to a scalar
|
303
|
+
def self.smap(x, &block)
|
304
|
+
x.is_a?(Array) ? x.map {|x| smap(x, &block)} : block.(x)
|
305
|
+
end
|
306
|
+
|
307
|
+
# run a complex operation on a complex image, or an image with an even
|
308
|
+
# number of bands ... handy for things like running .polar on .index
|
309
|
+
# images
|
310
|
+
def self.run_cmplx(image, &block)
|
311
|
+
original_format = image.format
|
312
|
+
|
313
|
+
if not Vips::band_format_iscomplex image.format
|
314
|
+
if image.bands % 2 != 0
|
315
|
+
raise Error, "not an even number of bands"
|
316
|
+
end
|
317
|
+
|
318
|
+
if not Vips::band_format_isfloat image.format
|
319
|
+
image = image.cast :float
|
320
|
+
end
|
321
|
+
|
322
|
+
new_format = image.format == :double ? :dpcomplex : :complex
|
323
|
+
image = image.copy :format => new_format,
|
324
|
+
:bands => image.bands / 2
|
325
|
+
end
|
326
|
+
|
327
|
+
image = block.(image)
|
328
|
+
|
329
|
+
if not Vips::band_format_iscomplex original_format
|
330
|
+
new_format = image.format == :dpcomplex ? :double : :float
|
331
|
+
image = image.copy :format => new_format,
|
332
|
+
:bands => image.bands * 2
|
333
|
+
end
|
334
|
+
|
335
|
+
image
|
336
|
+
end
|
337
|
+
|
338
|
+
# Write can fail due to no file descriptors and memory can fill if
|
339
|
+
# large objects are not collected fairly soon. We can't try a
|
340
|
+
# write and GC and retry on fail, since the write may take a
|
341
|
+
# long time and may not be repeatable.
|
342
|
+
#
|
343
|
+
# GCing before every write would have a horrible effect on
|
344
|
+
# performance, so as a compromise we GC every @@gc_interval writes.
|
345
|
+
#
|
346
|
+
# ruby2.1 introduced a generational GC which is fast enough to be
|
347
|
+
# able to GC on every write.
|
348
|
+
|
349
|
+
@@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
|
350
|
+
|
351
|
+
@@gc_interval = 100
|
352
|
+
@@gc_countdown = @@gc_interval
|
353
|
+
|
354
|
+
def write_gc
|
355
|
+
if @@generational_gc
|
356
|
+
GC.start full_mark: false
|
357
|
+
else
|
358
|
+
@@gc_countdown -= 1
|
359
|
+
if @@gc_countdown < 0
|
360
|
+
@@gc_countdown = @@gc_interval
|
361
|
+
GC.start
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
public
|
367
|
+
|
368
|
+
# Invoke a vips operation with {call}, using self as the first
|
369
|
+
# input image argument.
|
370
|
+
#
|
371
|
+
# @param name [String] vips operation to call
|
372
|
+
# @return result of vips operation
|
373
|
+
def method_missing(name, *args)
|
374
|
+
Vips::call_base(name.to_s, self, "", args)
|
375
|
+
end
|
376
|
+
|
377
|
+
# Invoke a vips operation with {call}.
|
378
|
+
def self.method_missing(name, *args)
|
379
|
+
Vips::call_base name.to_s, nil, "", args
|
380
|
+
end
|
381
|
+
|
382
|
+
# Return a new {Image} for a file on disc. This method can load
|
383
|
+
# images in any format supported by vips. The filename can include
|
384
|
+
# load options, for example:
|
385
|
+
#
|
386
|
+
# ```
|
387
|
+
# image = Vips::new_from_file "fred.jpg[shrink=2]"
|
388
|
+
# ```
|
389
|
+
#
|
390
|
+
# You can also supply options as a hash, for example:
|
391
|
+
#
|
392
|
+
# ```
|
393
|
+
# image = Vips::new_from_file "fred.jpg", :shrink => 2
|
394
|
+
# ```
|
395
|
+
#
|
396
|
+
# The full set of options available depend upon the load operation that
|
397
|
+
# will be executed. Try something like:
|
398
|
+
#
|
399
|
+
# ```
|
400
|
+
# $ vips jpegload
|
401
|
+
# ```
|
402
|
+
#
|
403
|
+
# at the command-line to see a summary of the available options.
|
404
|
+
#
|
405
|
+
# Loading is fast: only enough of the image is loaded to be able to fill
|
406
|
+
# out the header. Pixels will only be processed when they are needed.
|
407
|
+
#
|
408
|
+
# @!macro [new] vips.loadopts
|
409
|
+
# @param [Hash] opts set of options
|
410
|
+
# @option opts [Boolean] :disc (true) Open large images via a
|
411
|
+
# temporary disc file
|
412
|
+
# @option opts [Vips::Access] :access (:random) Access mode for file
|
413
|
+
#
|
414
|
+
# @param name [String] the filename to load from
|
415
|
+
# @macro vips.loadopts
|
416
|
+
# @return [Image] the loaded image
|
417
|
+
def self.new_from_file(name, opts = {})
|
418
|
+
# very common, and Vips::filename_get_filename will segv if we pass
|
419
|
+
# this
|
420
|
+
if name == nil
|
421
|
+
raise Error, "filename is nil"
|
422
|
+
end
|
423
|
+
filename = Vips::filename_get_filename name
|
424
|
+
option_string = Vips::filename_get_options name
|
425
|
+
loader = Vips::Foreign.find_load filename
|
426
|
+
if loader == nil
|
427
|
+
raise Vips::Error
|
428
|
+
end
|
429
|
+
|
430
|
+
Vips::call_base loader, nil, option_string, [filename, opts]
|
431
|
+
end
|
432
|
+
|
433
|
+
# Create a new {Image} for an image encoded in a format, such as
|
434
|
+
# JPEG, in a memory string. Load options may be passed encoded as
|
435
|
+
# strings, or appended as a hash. For example:
|
436
|
+
#
|
437
|
+
# ```
|
438
|
+
# image = Vips::new_from_from_buffer memory_buffer, "shrink=2"
|
439
|
+
# ```
|
440
|
+
#
|
441
|
+
# or alternatively:
|
442
|
+
#
|
443
|
+
# ```
|
444
|
+
# image = Vips::new_from_from_buffer memory_buffer, "", :shrink => 2
|
445
|
+
# ```
|
446
|
+
#
|
447
|
+
# The options available depend on the file format. Try something like:
|
448
|
+
#
|
449
|
+
# ```
|
450
|
+
# $ vips jpegload_buffer
|
451
|
+
# ```
|
452
|
+
#
|
453
|
+
# at the command-line to see the available options. Only JPEG, PNG and
|
454
|
+
# TIFF images can be read from memory buffers.
|
455
|
+
#
|
456
|
+
# Loading is fast: only enough of the image is loaded to be able to fill
|
457
|
+
# out the header. Pixels will only be processed when they are needed.
|
458
|
+
#
|
459
|
+
# @param data [String] the data to load from
|
460
|
+
# @param option_string [String] load options as a string
|
461
|
+
# @macro vips.loadopts
|
462
|
+
# @return [Image] the loaded image
|
463
|
+
def self.new_from_buffer(data, option_string, opts = {})
|
464
|
+
loader = Vips::Foreign.find_load_buffer data
|
465
|
+
if loader == nil
|
466
|
+
raise Vips::Error
|
467
|
+
end
|
468
|
+
|
469
|
+
Vips::call_base loader, nil, option_string, [data, opts]
|
470
|
+
end
|
471
|
+
|
472
|
+
# Create a new Image from a 1D or 2D array. A 1D array becomes an
|
473
|
+
# image with height 1. Use `scale` and `offset` to set the scale and
|
474
|
+
# offset fields in the header. These are useful for integer
|
475
|
+
# convolutions.
|
476
|
+
#
|
477
|
+
# For example:
|
478
|
+
#
|
479
|
+
# ```
|
480
|
+
# image = Vips::new_from_array [1, 2, 3]
|
481
|
+
# ```
|
482
|
+
#
|
483
|
+
# or
|
484
|
+
#
|
485
|
+
# ```
|
486
|
+
# image = Vips::new_from_array [
|
487
|
+
# [-1, -1, -1],
|
488
|
+
# [-1, 16, -1],
|
489
|
+
# [-1, -1, -1]], 8
|
490
|
+
# ```
|
491
|
+
#
|
492
|
+
# for a simple sharpening mask.
|
493
|
+
#
|
494
|
+
# @param array [Array] the pixel data as an array of numbers
|
495
|
+
# @param scale [Real] the convolution scale
|
496
|
+
# @param offset [Real] the convolution offset
|
497
|
+
# @return [Image] the image
|
498
|
+
def self.new_from_array(array, scale = 1, offset = 0)
|
499
|
+
# we accept a 1D array and assume height == 1, or a 2D array
|
500
|
+
# and check all lines are the same length
|
501
|
+
if not array.is_a? Array
|
502
|
+
raise Vips::Error, "Argument is not an array."
|
503
|
+
end
|
504
|
+
|
505
|
+
if array[0].is_a? Array
|
506
|
+
height = array.length
|
507
|
+
width = array[0].length
|
508
|
+
if not array.all? {|x| x.is_a? Array}
|
509
|
+
raise Vips::Error, "Not a 2D array."
|
510
|
+
end
|
511
|
+
if not array.all? {|x| x.length == width}
|
512
|
+
raise Vips::Error, "Array not rectangular."
|
513
|
+
end
|
514
|
+
array = array.flatten
|
515
|
+
else
|
516
|
+
height = 1
|
517
|
+
width = array.length
|
518
|
+
end
|
519
|
+
|
520
|
+
if not array.all? {|x| x.is_a? Numeric}
|
521
|
+
raise Vips::Error, "Not all array elements are Numeric."
|
522
|
+
end
|
523
|
+
|
524
|
+
image = Vips::Image.matrix_from_array width, height, array
|
525
|
+
if image == nil
|
526
|
+
raise Vips::Error
|
527
|
+
end
|
528
|
+
|
529
|
+
# be careful to set them as double
|
530
|
+
image.set_double 'scale', scale.to_f
|
531
|
+
image.set_double 'offset', offset.to_f
|
532
|
+
|
533
|
+
return image
|
534
|
+
end
|
535
|
+
|
536
|
+
# Write this image to a file. Save options may be encoded in the
|
537
|
+
# filename or given as a hash. For example:
|
538
|
+
#
|
539
|
+
# ```
|
540
|
+
# image.write_to_file "fred.jpg[Q=90]"
|
541
|
+
# ```
|
542
|
+
#
|
543
|
+
# or equivalently:
|
544
|
+
#
|
545
|
+
# ```
|
546
|
+
# image.write_to_file "fred.jpg", :Q => 90
|
547
|
+
# ```
|
548
|
+
#
|
549
|
+
# The full set of save options depend on the selected saver. Try
|
550
|
+
# something like:
|
551
|
+
#
|
552
|
+
# ```
|
553
|
+
# $ vips jpegsave
|
554
|
+
# ```
|
555
|
+
#
|
556
|
+
# to see all the available options.
|
557
|
+
#
|
558
|
+
# @!macro [new] vips.saveopts
|
559
|
+
# @param [Hash] opts set of options
|
560
|
+
# @option opts [Boolean] :strip (false) Strip all metadata from image
|
561
|
+
# @option opts [Array<Float>] :background (0) Background colour to
|
562
|
+
# flatten alpha against, if necessary
|
563
|
+
#
|
564
|
+
# @param name [String] filename to write to
|
565
|
+
def write_to_file(name, opts = {})
|
566
|
+
filename = Vips::filename_get_filename name
|
567
|
+
option_string = Vips::filename_get_options name
|
568
|
+
saver = Vips::Foreign.find_save filename
|
569
|
+
if saver == nil
|
570
|
+
raise Vips::Error, "No known saver for '#{filename}'."
|
571
|
+
end
|
572
|
+
|
573
|
+
Vips::call_base saver, self, option_string, [filename, opts]
|
574
|
+
|
575
|
+
write_gc
|
576
|
+
end
|
577
|
+
|
578
|
+
# Write this image to a memory buffer. Save options may be encoded in
|
579
|
+
# the format_string or given as a hash. For example:
|
580
|
+
#
|
581
|
+
# ```
|
582
|
+
# buffer = image.write_to_buffer ".jpg[Q=90]"
|
583
|
+
# ```
|
584
|
+
#
|
585
|
+
# or equivalently:
|
586
|
+
#
|
587
|
+
# ```
|
588
|
+
# image.write_to_buffer ".jpg", :Q => 90
|
589
|
+
# ```
|
590
|
+
#
|
591
|
+
# The full set of save options depend on the selected saver. Try
|
592
|
+
# something like:
|
593
|
+
#
|
594
|
+
# ```
|
595
|
+
# $ vips jpegsave
|
596
|
+
# ```
|
597
|
+
#
|
598
|
+
# to see all the available options.
|
599
|
+
#
|
600
|
+
# @param format_string [String] save format plus options
|
601
|
+
# @macro vips.saveopts
|
602
|
+
# @return [String] the image saved in the specified format
|
603
|
+
def write_to_buffer(format_string, opts = {})
|
604
|
+
filename = Vips::filename_get_filename format_string
|
605
|
+
option_string = Vips::filename_get_options format_string
|
606
|
+
saver = Vips::Foreign.find_save_buffer filename
|
607
|
+
if saver == nil
|
608
|
+
raise Vips::Error, "No known saver for '#{filename}'."
|
609
|
+
end
|
610
|
+
|
611
|
+
buffer = Vips::call_base saver, self, option_string, [opts]
|
612
|
+
|
613
|
+
write_gc
|
614
|
+
|
615
|
+
return buffer
|
616
|
+
end
|
617
|
+
|
618
|
+
# @!attribute [r] width
|
619
|
+
# @return [Integer] image width, in pixels
|
620
|
+
# @!attribute [r] height
|
621
|
+
# @return [Integer] image height, in pixels
|
622
|
+
# @!attribute [r] bands
|
623
|
+
# @return [Integer] image bands
|
624
|
+
# @!attribute [r] format
|
625
|
+
# @return [Vips::BandFormat] image format
|
626
|
+
# @!attribute [r] interpretation
|
627
|
+
# @return [Vips::Interpretation] image interpretation
|
628
|
+
# @!attribute [r] coding
|
629
|
+
# @return [Vips::Coding] image coding
|
630
|
+
# @!attribute [r] filename
|
631
|
+
# @return [String] image filename
|
632
|
+
# @!attribute [r] xres
|
633
|
+
# @return [Float] horizontal image resolution, in pixels per mm
|
634
|
+
# @!attribute [r] yres
|
635
|
+
# @return [Float] vertical image resolution, in pixels per mm
|
636
|
+
|
637
|
+
# Fetch a `GType` from an image. `GType` will be 0 for no such field.
|
638
|
+
#
|
639
|
+
# @see get
|
640
|
+
# @see get_value
|
641
|
+
# @!method get_typeof(name)
|
642
|
+
# @param name [String] Metadata field to fetch
|
643
|
+
# @return [Integer] GType
|
644
|
+
|
645
|
+
# Fetch a `GValue` from an image. The return status is 0 for success, -1
|
646
|
+
# for failure.
|
647
|
+
#
|
648
|
+
# @see get_value
|
649
|
+
# @see get_typeof
|
650
|
+
# @!method get(name)
|
651
|
+
# @param name [String] Metadata field to fetch
|
652
|
+
# @return [Integer, GValue] Return status, GValue from image
|
653
|
+
|
654
|
+
# Set a `GValue` on an image
|
655
|
+
#
|
656
|
+
# @see set_value
|
657
|
+
# @!method set(name, value)
|
658
|
+
# @param name [String] Metadata field to set
|
659
|
+
# @param value [GValue] GValue to set
|
660
|
+
|
661
|
+
# Get a metadata item from an image. Ruby types are constructed
|
662
|
+
# automatically from the `GValue`, if possible.
|
663
|
+
#
|
664
|
+
# For example, you can read the ICC profile from an image like this:
|
665
|
+
#
|
666
|
+
# ```
|
667
|
+
# profile = image.get_value "icc-profile-data"
|
668
|
+
# ```
|
669
|
+
#
|
670
|
+
# and profile will be an array containing the profile.
|
671
|
+
#
|
672
|
+
# @see get
|
673
|
+
# @param name [String] Metadata field to set
|
674
|
+
# @return [Object] Value of field
|
675
|
+
def get_value(name)
|
676
|
+
ret, gval = get name
|
677
|
+
if ret[0] != 0
|
678
|
+
raise Vips::Error, "Field #{name} not found."
|
679
|
+
end
|
680
|
+
value = gval.value
|
681
|
+
|
682
|
+
Argument::unwrap(value)
|
683
|
+
end
|
684
|
+
|
685
|
+
# Set a metadata item on an image. Ruby types are automatically
|
686
|
+
# transformed into the matching `GValue`, if possible.
|
687
|
+
#
|
688
|
+
# For example, you can use this to set an image's ICC profile:
|
689
|
+
#
|
690
|
+
# ```
|
691
|
+
# x = y.set_value "icc-profile-data", profile
|
692
|
+
# ```
|
693
|
+
#
|
694
|
+
# where `profile` is an ICC profile held as a binary string object.
|
695
|
+
#
|
696
|
+
# @see set
|
697
|
+
# @param name [String] Metadata field to set
|
698
|
+
# @param value [Object] Value to set
|
699
|
+
def set_value(name, value)
|
700
|
+
gtype = get_typeof name
|
701
|
+
if gtype != 0
|
702
|
+
# array-ize
|
703
|
+
value = Argument::arrayize gtype, value
|
704
|
+
|
705
|
+
# blob-ize
|
706
|
+
if gtype.type_is_a? GLib::Type["VipsBlob"]
|
707
|
+
if not value.is_a? Vips::Blob
|
708
|
+
value = Vips::Blob.copy value
|
709
|
+
end
|
710
|
+
end
|
711
|
+
|
712
|
+
# image-ize
|
713
|
+
if gtype.type_is_a? GLib::Type["VipsImage"]
|
714
|
+
if not value.is_a? Vips::Image
|
715
|
+
value = imageize match_image, value
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
set name, value
|
722
|
+
end
|
723
|
+
|
724
|
+
# Add an image, constant or array.
|
725
|
+
#
|
726
|
+
# @param other [Image, Real, Array<Real>] Thing to add to self
|
727
|
+
# @return [Image] result of addition
|
728
|
+
def +(other)
|
729
|
+
other.is_a?(Vips::Image) ? add(other) : linear(1, other)
|
730
|
+
end
|
731
|
+
|
732
|
+
# Subtract an image, constant or array.
|
733
|
+
#
|
734
|
+
# @param other [Image, Real, Array<Real>] Thing to subtract from self
|
735
|
+
# @return [Image] result of subtraction
|
736
|
+
def -(other)
|
737
|
+
other.is_a?(Vips::Image) ?
|
738
|
+
subtract(other) : linear(1, Image::smap(other) {|x| x * -1})
|
739
|
+
end
|
740
|
+
|
741
|
+
# Multiply an image, constant or array.
|
742
|
+
#
|
743
|
+
# @param other [Image, Real, Array<Real>] Thing to multiply by self
|
744
|
+
# @return [Image] result of multiplication
|
745
|
+
def *(other)
|
746
|
+
other.is_a?(Vips::Image) ? multiply(other) : linear(other, 0)
|
747
|
+
end
|
748
|
+
|
749
|
+
# Divide an image, constant or array.
|
750
|
+
#
|
751
|
+
# @param other [Image, Real, Array<Real>] Thing to divide self by
|
752
|
+
# @return [Image] result of division
|
753
|
+
def /(other)
|
754
|
+
other.is_a?(Vips::Image) ?
|
755
|
+
divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0)
|
756
|
+
end
|
757
|
+
|
758
|
+
# Remainder after integer division with an image, constant or array.
|
759
|
+
#
|
760
|
+
# @param other [Image, Real, Array<Real>] self modulo this
|
761
|
+
# @return [Image] result of modulo
|
762
|
+
def %(other)
|
763
|
+
other.is_a?(Vips::Image) ?
|
764
|
+
remainder(other) : remainder_const(other)
|
765
|
+
end
|
766
|
+
|
767
|
+
# Raise to power of an image, constant or array.
|
768
|
+
#
|
769
|
+
# @param other [Image, Real, Array<Real>] self to the power of this
|
770
|
+
# @return [Image] result of power
|
771
|
+
def **(other)
|
772
|
+
other.is_a?(Vips::Image) ?
|
773
|
+
math2(other, :pow) : math2_const(other, :pow)
|
774
|
+
end
|
775
|
+
|
776
|
+
# Integer left shift with an image, constant or array.
|
777
|
+
#
|
778
|
+
# @param other [Image, Real, Array<Real>] shift left by this much
|
779
|
+
# @return [Image] result of left shift
|
780
|
+
def <<(other)
|
781
|
+
other.is_a?(Vips::Image) ?
|
782
|
+
boolean(other, :lshift) : boolean_const(other, :lshift)
|
783
|
+
end
|
784
|
+
|
785
|
+
# Integer right shift with an image, constant or array.
|
786
|
+
#
|
787
|
+
# @param other [Image, Real, Array<Real>] shift right by this much
|
788
|
+
# @return [Image] result of right shift
|
789
|
+
def >>(other)
|
790
|
+
other.is_a?(Vips::Image) ?
|
791
|
+
boolean(other, :rshift) : boolean_const(other, :rshift)
|
792
|
+
end
|
793
|
+
|
794
|
+
# Integer bitwise OR with an image, constant or array.
|
795
|
+
#
|
796
|
+
# @param other [Image, Real, Array<Real>] bitwise OR with this
|
797
|
+
# @return [Image] result of bitwise OR
|
798
|
+
def |(other)
|
799
|
+
other.is_a?(Vips::Image) ?
|
800
|
+
boolean(other, :or) : boolean_const(other, :or)
|
801
|
+
end
|
802
|
+
|
803
|
+
# Integer bitwise AND with an image, constant or array.
|
804
|
+
#
|
805
|
+
# @param other [Image, Real, Array<Real>] bitwise AND with this
|
806
|
+
# @return [Image] result of bitwise AND
|
807
|
+
def &(other)
|
808
|
+
other.is_a?(Vips::Image) ?
|
809
|
+
boolean(other, :and) : boolean_const(other, :and)
|
810
|
+
end
|
811
|
+
|
812
|
+
# Integer bitwise EOR with an image, constant or array.
|
813
|
+
#
|
814
|
+
# @param other [Image, Real, Array<Real>] bitwise EOR with this
|
815
|
+
# @return [Image] result of bitwise EOR
|
816
|
+
def ^(other)
|
817
|
+
other.is_a?(Vips::Image) ?
|
818
|
+
boolean(other, :eor) : boolean_const(other, :eor)
|
819
|
+
end
|
820
|
+
|
821
|
+
# Equivalent to image ^ -1
|
822
|
+
#
|
823
|
+
# @return [Image] image with bits flipped
|
824
|
+
def !
|
825
|
+
self ^ -1
|
826
|
+
end
|
827
|
+
|
828
|
+
# Equivalent to image ^ -1
|
829
|
+
#
|
830
|
+
# @return [Image] image with bits flipped
|
831
|
+
def ~
|
832
|
+
self ^ -1
|
833
|
+
end
|
834
|
+
|
835
|
+
# @return [Image] image
|
836
|
+
def +@
|
837
|
+
self
|
838
|
+
end
|
839
|
+
|
840
|
+
# Equivalent to image * -1
|
841
|
+
#
|
842
|
+
# @return [Image] negative of image
|
843
|
+
def -@
|
844
|
+
self * -1
|
845
|
+
end
|
846
|
+
|
847
|
+
# Relational less than with an image, constant or array.
|
848
|
+
#
|
849
|
+
# @param other [Image, Real, Array<Real>] relational less than with this
|
850
|
+
# @return [Image] result of less than
|
851
|
+
def <(other)
|
852
|
+
other.is_a?(Vips::Image) ?
|
853
|
+
relational(other, :less) : relational_const(other, :less)
|
854
|
+
end
|
855
|
+
|
856
|
+
# Relational less than or equal to with an image, constant or array.
|
857
|
+
#
|
858
|
+
# @param other [Image, Real, Array<Real>] relational less than or
|
859
|
+
# equal to with this
|
860
|
+
# @return [Image] result of less than or equal to
|
861
|
+
def <=(other)
|
862
|
+
other.is_a?(Vips::Image) ?
|
863
|
+
relational(other, :lesseq) : relational_const(other, :lesseq)
|
864
|
+
end
|
865
|
+
|
866
|
+
# Relational more than with an image, constant or array.
|
867
|
+
#
|
868
|
+
# @param other [Image, Real, Array<Real>] relational more than with this
|
869
|
+
# @return [Image] result of more than
|
870
|
+
def >(other)
|
871
|
+
other.is_a?(Vips::Image) ?
|
872
|
+
relational(other, :more) : relational_const(other, :more)
|
873
|
+
end
|
874
|
+
|
875
|
+
# Relational more than or equal to with an image, constant or array.
|
876
|
+
#
|
877
|
+
# @param other [Image, Real, Array<Real>] relational more than or
|
878
|
+
# equal to with this
|
879
|
+
# @return [Image] result of more than or equal to
|
880
|
+
def >=(other)
|
881
|
+
other.is_a?(Vips::Image) ?
|
882
|
+
relational(other, :moreeq) : relational_const(other, :moreeq)
|
883
|
+
end
|
884
|
+
|
885
|
+
# Compare equality to nil, an image, constant or array.
|
886
|
+
#
|
887
|
+
# @param other [nil, Image, Real, Array<Real>] test equality to this
|
888
|
+
# @return [Image] result of equality
|
889
|
+
def ==(other)
|
890
|
+
if other == nil
|
891
|
+
false
|
892
|
+
elsif other.is_a?(Vips::Image)
|
893
|
+
relational(other, :equal)
|
894
|
+
else
|
895
|
+
relational_const(other, :equal)
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
# Compare inequality to nil, an image, constant or array.
|
900
|
+
#
|
901
|
+
# @param other [nil, Image, Real, Array<Real>] test inequality to this
|
902
|
+
# @return [Image] result of inequality
|
903
|
+
def !=(other)
|
904
|
+
if other == nil
|
905
|
+
true
|
906
|
+
elsif other.is_a?(Vips::Image)
|
907
|
+
relational(other, :noteq)
|
908
|
+
else
|
909
|
+
relational_const(other, :noteq)
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
# Fetch bands using a number or a range
|
914
|
+
#
|
915
|
+
# @param index [Numeric, Range] extract these band(s)
|
916
|
+
# @return [Image] extracted band(s)
|
917
|
+
def [](index)
|
918
|
+
if index.is_a? Range
|
919
|
+
n = index.end - index.begin
|
920
|
+
n += 1 if not index.exclude_end?
|
921
|
+
extract_band index.begin, :n => n
|
922
|
+
elsif index.is_a? Numeric
|
923
|
+
extract_band index
|
924
|
+
else
|
925
|
+
raise Vips::Error, "[] index is not range or numeric."
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
# Return the largest integral value not greater than the argument.
|
930
|
+
#
|
931
|
+
# @return [Image] floor of image
|
932
|
+
def floor
|
933
|
+
round :floor
|
934
|
+
end
|
935
|
+
|
936
|
+
# Return the smallest integral value not less than the argument.
|
937
|
+
#
|
938
|
+
# @return [Image] ceil of image
|
939
|
+
def ceil
|
940
|
+
round :ceil
|
941
|
+
end
|
942
|
+
|
943
|
+
# Return the nearest integral value.
|
944
|
+
#
|
945
|
+
# @return [Image] rint of image
|
946
|
+
def rint
|
947
|
+
round :rint
|
948
|
+
end
|
949
|
+
|
950
|
+
# AND the bands of an image together
|
951
|
+
#
|
952
|
+
# @return [Image] all bands ANDed together
|
953
|
+
def bandand
|
954
|
+
bandbool :and
|
955
|
+
end
|
956
|
+
|
957
|
+
# OR the bands of an image together
|
958
|
+
#
|
959
|
+
# @return [Image] all bands ORed together
|
960
|
+
def bandor
|
961
|
+
bandbool :or
|
962
|
+
end
|
963
|
+
|
964
|
+
# EOR the bands of an image together
|
965
|
+
#
|
966
|
+
# @return [Image] all bands EORed together
|
967
|
+
def bandeor
|
968
|
+
bandbool :eor
|
969
|
+
end
|
970
|
+
|
971
|
+
# Split an n-band image into n separate images.
|
972
|
+
#
|
973
|
+
# @return [Array<Image>] Array of n one-band images
|
974
|
+
def bandsplit
|
975
|
+
(0...bands).map {|i| extract_band(i)}
|
976
|
+
end
|
977
|
+
|
978
|
+
# Join a set of images bandwise.
|
979
|
+
#
|
980
|
+
# @param other [Image, Array<Image>, Real, Array<Real>] bands to append
|
981
|
+
# @return [Image] many band image
|
982
|
+
def bandjoin(other)
|
983
|
+
if not other.is_a? Array
|
984
|
+
other = [other]
|
985
|
+
end
|
986
|
+
|
987
|
+
Vips::Image.bandjoin([self] + other)
|
988
|
+
end
|
989
|
+
|
990
|
+
# Return the coordinates of the image maximum.
|
991
|
+
#
|
992
|
+
# @return [Real, Real, Real] maximum value, x coordinate of maximum, y
|
993
|
+
# coordinate of maximum
|
994
|
+
def maxpos
|
995
|
+
v, opts = max :x => true, :y => true
|
996
|
+
x = opts['x']
|
997
|
+
y = opts['y']
|
998
|
+
return v, x, y
|
999
|
+
end
|
1000
|
+
|
1001
|
+
# Return the coordinates of the image minimum.
|
1002
|
+
#
|
1003
|
+
# @return [Real, Real, Real] minimum value, x coordinate of minimum, y
|
1004
|
+
# coordinate of minimum
|
1005
|
+
def minpos
|
1006
|
+
v, opts = min :x => true, :y => true
|
1007
|
+
x = opts['x']
|
1008
|
+
y = opts['y']
|
1009
|
+
return v, x, y
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
# get the value of a pixel as an array
|
1013
|
+
#
|
1014
|
+
# @param x [Integer] x coordinate to sample
|
1015
|
+
# @param y [Integer] y coordinate to sample
|
1016
|
+
# @return [Array<Float>] the pixel values as an array
|
1017
|
+
def getpoint(x, y)
|
1018
|
+
# vips has an operation that does this, but we can't call it via
|
1019
|
+
# gobject-introspection 3.0.7 since it's missing array double
|
1020
|
+
# get
|
1021
|
+
#
|
1022
|
+
# remove this def when gobject-introspection updates
|
1023
|
+
crop(x, y, 1, 1).bandsplit.map {|i| i.avg}
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
# a median filter
|
1027
|
+
#
|
1028
|
+
# @param size [Integer] size of filter window
|
1029
|
+
# @return [Image] result of median filter
|
1030
|
+
def median(size = 3)
|
1031
|
+
rank(size, size, (size * size) / 2)
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
# Return the real part of a complex image.
|
1035
|
+
#
|
1036
|
+
# @return [Image] real part of complex image
|
1037
|
+
def real
|
1038
|
+
complexget :real
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
# Return the imaginary part of a complex image.
|
1042
|
+
#
|
1043
|
+
# @return [Image] imaginary part of complex image
|
1044
|
+
def imag
|
1045
|
+
complexget :imag
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
# Return an image with rectangular pixels converted to polar.
|
1049
|
+
#
|
1050
|
+
# The image
|
1051
|
+
# can be complex, in which case the return image will also be complex,
|
1052
|
+
# or must have an even number of bands, in which case pairs of
|
1053
|
+
# bands are treated as (x, y) coordinates.
|
1054
|
+
#
|
1055
|
+
# @see xyz
|
1056
|
+
# @return [Image] image converted to polar coordinates
|
1057
|
+
def polar
|
1058
|
+
Image::run_cmplx(self) {|x| x.complex :polar}
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
# Return an image with polar pixels converted to rectangular.
|
1062
|
+
#
|
1063
|
+
# The image
|
1064
|
+
# can be complex, in which case the return image will also be complex,
|
1065
|
+
# or must have an even number of bands, in which case pairs of
|
1066
|
+
# bands are treated as (x, y) coordinates.
|
1067
|
+
#
|
1068
|
+
# @see xyz
|
1069
|
+
# @return [Image] image converted to rectangular coordinates
|
1070
|
+
def rect
|
1071
|
+
Image::run_cmplx(self) {|x| x.complex :rect}
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
# Return the complex conjugate of an image.
|
1075
|
+
#
|
1076
|
+
# The image
|
1077
|
+
# can be complex, in which case the return image will also be complex,
|
1078
|
+
# or must have an even number of bands, in which case pairs of
|
1079
|
+
# bands are treated as (x, y) coordinates.
|
1080
|
+
#
|
1081
|
+
# @return [Image] complex conjugate
|
1082
|
+
def conj
|
1083
|
+
Image::run_cmplx(self) {|x| x.complex :conj}
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
# Return the sine of an image in degrees.
|
1087
|
+
#
|
1088
|
+
# @return [Image] sine of each pixel
|
1089
|
+
def sin
|
1090
|
+
math :sin
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
# Return the cosine of an image in degrees.
|
1094
|
+
#
|
1095
|
+
# @return [Image] cosine of each pixel
|
1096
|
+
def cos
|
1097
|
+
math :cos
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
# Return the tangent of an image in degrees.
|
1101
|
+
#
|
1102
|
+
# @return [Image] tangent of each pixel
|
1103
|
+
def tan
|
1104
|
+
math :tan
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
# Return the inverse sine of an image in degrees.
|
1108
|
+
#
|
1109
|
+
# @return [Image] inverse sine of each pixel
|
1110
|
+
def asin
|
1111
|
+
math :asin
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# Return the inverse cosine of an image in degrees.
|
1115
|
+
#
|
1116
|
+
# @return [Image] inverse cosine of each pixel
|
1117
|
+
def acos
|
1118
|
+
math :acos
|
1119
|
+
end
|
1120
|
+
|
1121
|
+
# Return the inverse tangent of an image in degrees.
|
1122
|
+
#
|
1123
|
+
# @return [Image] inverse tangent of each pixel
|
1124
|
+
def atan
|
1125
|
+
math :atan
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
# Return the natural log of an image.
|
1129
|
+
#
|
1130
|
+
# @return [Image] natural log of each pixel
|
1131
|
+
def log
|
1132
|
+
math :log
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
# Return the log base 10 of an image.
|
1136
|
+
#
|
1137
|
+
# @return [Image] base 10 log of each pixel
|
1138
|
+
def log10
|
1139
|
+
math :log10
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
# Return e ** pixel.
|
1143
|
+
#
|
1144
|
+
# @return [Image] e ** pixel
|
1145
|
+
def exp
|
1146
|
+
math :exp
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
# Return 10 ** pixel.
|
1150
|
+
#
|
1151
|
+
# @return [Image] 10 ** pixel
|
1152
|
+
def exp10
|
1153
|
+
math :exp10
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
# Flip horizontally.
|
1157
|
+
#
|
1158
|
+
# @return [Image] image flipped horizontally
|
1159
|
+
def fliphor
|
1160
|
+
flip :horizontal
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
# Flip vertically.
|
1164
|
+
#
|
1165
|
+
# @return [Image] image flipped vertically
|
1166
|
+
def flipver
|
1167
|
+
flip :vertical
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
# Erode with a structuring element.
|
1171
|
+
#
|
1172
|
+
# The structuring element must be an array with 0 for black, 255 for
|
1173
|
+
# white and 128 for don't care.
|
1174
|
+
#
|
1175
|
+
# @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
|
1176
|
+
# element
|
1177
|
+
# @return [Image] eroded image
|
1178
|
+
def erode(mask)
|
1179
|
+
morph mask, :erode
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
# Dilate with a structuring element.
|
1183
|
+
#
|
1184
|
+
# The structuring element must be an array with 0 for black, 255 for
|
1185
|
+
# white and 128 for don't care.
|
1186
|
+
#
|
1187
|
+
# @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
|
1188
|
+
# element
|
1189
|
+
# @return [Image] dilated image
|
1190
|
+
def dilate(mask)
|
1191
|
+
morph mask, :dilate
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
# Rotate by 90 degrees clockwise.
|
1195
|
+
#
|
1196
|
+
# @return [Image] rotated image
|
1197
|
+
def rot90
|
1198
|
+
rot :d90
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
# Rotate by 180 degrees clockwise.
|
1202
|
+
#
|
1203
|
+
# @return [Image] rotated image
|
1204
|
+
def rot180
|
1205
|
+
rot :d180
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
# Rotate by 270 degrees clockwise.
|
1209
|
+
#
|
1210
|
+
# @return [Image] rotated image
|
1211
|
+
def rot270
|
1212
|
+
rot :d270
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
# Select pixels from `th` if `self` is non-zero and from `el` if
|
1216
|
+
# `self` is zero. Use the `:blend` option to fade smoothly
|
1217
|
+
# between `th` and `el`.
|
1218
|
+
#
|
1219
|
+
# @param th [Image, Real, Array<Real>] true values
|
1220
|
+
# @param el [Image, Real, Array<Real>] false values
|
1221
|
+
# @param [Hash] opts set of options
|
1222
|
+
# @option opts [Boolean] :blend (false) Blend smoothly between th and el
|
1223
|
+
# @return [Image] merged image
|
1224
|
+
def ifthenelse(th, el, opts = {})
|
1225
|
+
match_image = [th, el, self].find {|x| x.is_a? Vips::Image}
|
1226
|
+
|
1227
|
+
if not th.is_a? Vips::Image
|
1228
|
+
th = Argument::imageize match_image, th
|
1229
|
+
end
|
1230
|
+
if not el.is_a? Vips::Image
|
1231
|
+
el = Argument::imageize match_image, el
|
1232
|
+
end
|
1233
|
+
|
1234
|
+
Vips::call_base "ifthenelse", self, "", [th, el, opts]
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
# This method generates yard comments for all the dynamically bound
|
1240
|
+
# vips operations.
|
1241
|
+
#
|
1242
|
+
# Regenerate with something like:
|
1243
|
+
#
|
1244
|
+
# ruby > methods.rb
|
1245
|
+
# require 'vips'; Vips::generate_yard
|
1246
|
+
# ^D
|
1247
|
+
|
1248
|
+
def self.generate_yard
|
1249
|
+
# these have hand-written methods, see above
|
1250
|
+
no_generate = ["bandjoin", "ifthenelse"]
|
1251
|
+
|
1252
|
+
generate_operation = lambda do |op|
|
1253
|
+
flags = op.flags
|
1254
|
+
return if (flags & :deprecated) != 0
|
1255
|
+
nickname = Vips::nickname_find op.gtype
|
1256
|
+
|
1257
|
+
return if no_generate.include? nickname
|
1258
|
+
|
1259
|
+
all_args = op.get_args.select {|arg| not arg.isset}
|
1260
|
+
|
1261
|
+
# separate args into various categories
|
1262
|
+
|
1263
|
+
required_input = all_args.select do |arg|
|
1264
|
+
(arg.flags & :input) != 0 and
|
1265
|
+
(arg.flags & :required) != 0
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
optional_input = all_args.select do |arg|
|
1269
|
+
(arg.flags & :input) != 0 and
|
1270
|
+
(arg.flags & :required) == 0
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
required_output = all_args.select do |arg|
|
1274
|
+
(arg.flags & :output) != 0 and
|
1275
|
+
(arg.flags & :required) != 0
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
# required input args with :modify are copied and appended to
|
1279
|
+
# output
|
1280
|
+
modified_required_input = required_input.select do |arg|
|
1281
|
+
(arg.flags & :modify) != 0
|
1282
|
+
end
|
1283
|
+
required_output += modified_required_input
|
1284
|
+
|
1285
|
+
optional_output = all_args.select do |arg|
|
1286
|
+
(arg.flags & :output) != 0 and
|
1287
|
+
(arg.flags & :required) == 0
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
# optional input args with :modify are copied and appended to
|
1291
|
+
# output
|
1292
|
+
modified_optional_input = optional_input.select do |arg|
|
1293
|
+
(arg.flags & :modify) != 0
|
1294
|
+
end
|
1295
|
+
optional_output += modified_optional_input
|
1296
|
+
|
1297
|
+
# find the first input image, if any ... we will be a method of this
|
1298
|
+
# instance
|
1299
|
+
member_x = required_input.find do |x|
|
1300
|
+
x.gtype.type_is_a? GLib::Type["VipsImage"]
|
1301
|
+
end
|
1302
|
+
if member_x != nil
|
1303
|
+
required_input.delete member_x
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
print "# @!method "
|
1307
|
+
print "self." if not member_x
|
1308
|
+
print "#{nickname}("
|
1309
|
+
print required_input.map(&:name).join(", ")
|
1310
|
+
puts ", opts = {})"
|
1311
|
+
|
1312
|
+
puts "# #{op.description.capitalize}."
|
1313
|
+
|
1314
|
+
required_input.each do |arg|
|
1315
|
+
puts "# @param #{arg.name} [#{arg.type}] #{arg.blurb}"
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
puts "# @param [Hash] opts Set of options"
|
1319
|
+
optional_input.each do |arg|
|
1320
|
+
puts "# @option opts [#{arg.type}] :#{arg.name} #{arg.blurb}"
|
1321
|
+
end
|
1322
|
+
optional_output.each do |arg|
|
1323
|
+
print "# @option opts [#{arg.type}] :#{arg.name}"
|
1324
|
+
puts " Output #{arg.blurb}"
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
print "# @return ["
|
1328
|
+
if required_output.length == 0
|
1329
|
+
print "nil"
|
1330
|
+
elsif required_output.length == 1
|
1331
|
+
print required_output[0].type
|
1332
|
+
elsif
|
1333
|
+
print "Array<"
|
1334
|
+
print required_output.map(&:type).join(", ")
|
1335
|
+
print ">"
|
1336
|
+
end
|
1337
|
+
if optional_output.length > 0
|
1338
|
+
print ", Hash<Symbol => Object>"
|
1339
|
+
end
|
1340
|
+
print "] "
|
1341
|
+
print required_output.map(&:blurb).join(", ")
|
1342
|
+
if optional_output.length > 0
|
1343
|
+
print ", " if required_output.length > 0
|
1344
|
+
print "Hash of optional output items"
|
1345
|
+
end
|
1346
|
+
puts ""
|
1347
|
+
|
1348
|
+
puts ""
|
1349
|
+
end
|
1350
|
+
|
1351
|
+
generate_class = lambda do |gtype|
|
1352
|
+
begin
|
1353
|
+
# can fail for abstract types
|
1354
|
+
# can't find a way to get to #abstract? from a gtype
|
1355
|
+
op = Vips::Operation.new gtype.name
|
1356
|
+
rescue
|
1357
|
+
op = nil
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
generate_operation.(op) if op
|
1361
|
+
|
1362
|
+
gtype.children.each do |x|
|
1363
|
+
generate_class.(x)
|
1364
|
+
end
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
puts "module Vips"
|
1368
|
+
puts " class Image"
|
1369
|
+
puts ""
|
1370
|
+
|
1371
|
+
# gobject-introspection 3.0.7 crashes a lot if it GCs while doing
|
1372
|
+
# something
|
1373
|
+
GC.disable
|
1374
|
+
|
1375
|
+
generate_class.(GLib::Type["VipsOperation"])
|
1376
|
+
|
1377
|
+
puts " end"
|
1378
|
+
puts "end"
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
end
|
1382
|
+
|