ruby-vips 0.3.14 → 1.0.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.
- 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
|
+
|