vips 8.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.travis.yml +47 -0
- data/.yardopts +2 -0
- data/Gemfile +4 -0
- data/README.md +64 -0
- data/Rakefile +28 -0
- data/example/annotate.rb +17 -0
- data/example/daltonize8.rb +75 -0
- data/example/example1.rb +12 -0
- data/example/example2.rb +34 -0
- data/example/example3.rb +19 -0
- data/example/example4.rb +18 -0
- data/example/example5.rb +31 -0
- data/example/inheritance_with_refcount.rb +286 -0
- data/example/thumb.rb +31 -0
- data/example/trim8.rb +41 -0
- data/example/watermark.rb +44 -0
- data/example/wobble.rb +36 -0
- data/ext/Rakefile +35 -0
- data/lib/vips.rb +597 -0
- data/lib/vips/access.rb +11 -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/bandformat.rb +20 -0
- data/lib/vips/coding.rb +14 -0
- data/lib/vips/compass_direction.rb +17 -0
- data/lib/vips/direction.rb +11 -0
- data/lib/vips/extend.rb +17 -0
- data/lib/vips/gobject.rb +122 -0
- data/lib/vips/gvalue.rb +281 -0
- data/lib/vips/image.rb +1500 -0
- data/lib/vips/interesting.rb +14 -0
- data/lib/vips/interpolate.rb +62 -0
- data/lib/vips/interpretation.rb +28 -0
- data/lib/vips/kernel.rb +22 -0
- data/lib/vips/methods.rb +2145 -0
- data/lib/vips/object.rb +244 -0
- data/lib/vips/operation.rb +365 -0
- data/lib/vips/operationboolean.rb +14 -0
- data/lib/vips/operationcomplex.rb +12 -0
- data/lib/vips/operationcomplex2.rb +10 -0
- data/lib/vips/operationcomplexget.rb +11 -0
- data/lib/vips/operationmath.rb +18 -0
- data/lib/vips/operationmath2.rb +10 -0
- data/lib/vips/operationrelational.rb +15 -0
- data/lib/vips/operationround.rb +11 -0
- data/lib/vips/size.rb +13 -0
- data/lib/vips/version.rb +4 -0
- data/vips.gemspec +36 -0
- metadata +209 -0
data/lib/vips/image.rb
ADDED
@@ -0,0 +1,1500 @@
|
|
1
|
+
# This module provides an interface to the vips image processing library
|
2
|
+
# via ruby-ffi.
|
3
|
+
#
|
4
|
+
# Author:: John Cupitt (mailto:jcupitt@gmail.com)
|
5
|
+
# License:: MIT
|
6
|
+
|
7
|
+
require 'ffi'
|
8
|
+
|
9
|
+
module Vips
|
10
|
+
private
|
11
|
+
|
12
|
+
attach_function :vips_image_new_matrix_from_array,
|
13
|
+
[:int, :int, :pointer, :int], :pointer
|
14
|
+
|
15
|
+
attach_function :vips_image_copy_memory, [:pointer], :pointer
|
16
|
+
|
17
|
+
attach_function :vips_filename_get_filename, [:string], :string
|
18
|
+
attach_function :vips_filename_get_options, [:string], :string
|
19
|
+
|
20
|
+
attach_function :vips_foreign_find_load, [:string], :string
|
21
|
+
attach_function :vips_foreign_find_save, [:string], :string
|
22
|
+
attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
|
23
|
+
attach_function :vips_foreign_find_save_buffer, [:string], :string
|
24
|
+
|
25
|
+
attach_function :vips_image_write_to_memory,
|
26
|
+
[:pointer, SizeStruct.ptr], :pointer
|
27
|
+
|
28
|
+
attach_function :vips_image_get_typeof, [:pointer, :string], :GType
|
29
|
+
attach_function :vips_image_get,
|
30
|
+
[:pointer, :string, GObject::GValue.ptr], :int
|
31
|
+
|
32
|
+
# vips_image_get_fields was added in libvips 8.5
|
33
|
+
begin
|
34
|
+
attach_function :vips_image_get_fields, [:pointer], :pointer
|
35
|
+
rescue FFI::NotFoundError
|
36
|
+
end
|
37
|
+
|
38
|
+
# vips_addalpha was added in libvips 8.6
|
39
|
+
if Vips::at_least_libvips?(8, 6)
|
40
|
+
attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
|
41
|
+
end
|
42
|
+
if Vips::at_least_libvips?(8, 5)
|
43
|
+
attach_function :vips_image_hasalpha, [:pointer], :int
|
44
|
+
end
|
45
|
+
|
46
|
+
attach_function :vips_image_set,
|
47
|
+
[:pointer, :string, GObject::GValue.ptr], :void
|
48
|
+
attach_function :vips_image_remove, [:pointer, :string], :void
|
49
|
+
|
50
|
+
attach_function :vips_band_format_iscomplex, [:int], :int
|
51
|
+
attach_function :vips_band_format_isfloat, [:int], :int
|
52
|
+
|
53
|
+
attach_function :nickname_find, :vips_nickname_find, [:GType], :string
|
54
|
+
|
55
|
+
public
|
56
|
+
|
57
|
+
# This class represents a libvips image. See the {Vips} module documentation
|
58
|
+
# for an introduction to using this class.
|
59
|
+
|
60
|
+
class Image < Vips::Object
|
61
|
+
alias_method :parent_get_typeof, :get_typeof
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# the layout of the VipsImage struct
|
66
|
+
module ImageLayout
|
67
|
+
def self.included base
|
68
|
+
base.class_eval do
|
69
|
+
layout :parent, Vips::Object::Struct
|
70
|
+
# rest opaque
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Struct < Vips::Object::Struct
|
76
|
+
include ImageLayout
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class ManagedStruct < Vips::Object::ManagedStruct
|
81
|
+
include ImageLayout
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
class GenericPtr < FFI::Struct
|
86
|
+
layout :value, :pointer
|
87
|
+
end
|
88
|
+
|
89
|
+
# handy for overloads ... want to be able to apply a function to an
|
90
|
+
# array or to a scalar
|
91
|
+
def self.smap x, &block
|
92
|
+
x.is_a?(Array) ? x.map {|y| smap(y, &block)} : block.(x)
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.complex? format
|
96
|
+
format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
|
97
|
+
Vips::vips_band_format_iscomplex(format_number) != 0
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.float? format
|
101
|
+
format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
|
102
|
+
Vips::vips_band_format_isfloat(format_number) != 0
|
103
|
+
end
|
104
|
+
|
105
|
+
# run a complex operation on a complex image, or an image with an even
|
106
|
+
# number of bands ... handy for things like running .polar on .index
|
107
|
+
# images
|
108
|
+
def self.run_cmplx image, &block
|
109
|
+
original_format = image.format
|
110
|
+
|
111
|
+
unless Image::complex? image.format
|
112
|
+
if image.bands % 2 != 0
|
113
|
+
raise Error, "not an even number of bands"
|
114
|
+
end
|
115
|
+
|
116
|
+
unless Image::float? image.format
|
117
|
+
image = image.cast :float
|
118
|
+
end
|
119
|
+
|
120
|
+
new_format = image.format == :double ? :dpcomplex : :complex
|
121
|
+
image = image.copy format: new_format, bands: image.bands / 2
|
122
|
+
end
|
123
|
+
|
124
|
+
image = block.(image)
|
125
|
+
|
126
|
+
unless Image::complex? original_format
|
127
|
+
new_format = image.format == :dpcomplex ? :double : :float
|
128
|
+
image = image.copy format: new_format, bands: image.bands * 2
|
129
|
+
end
|
130
|
+
|
131
|
+
image
|
132
|
+
end
|
133
|
+
|
134
|
+
# handy for expanding enum operations
|
135
|
+
def call_enum(name, other, enum)
|
136
|
+
if other.is_a?(Vips::Image)
|
137
|
+
Vips::Operation.call name.to_s, [self, other, enum]
|
138
|
+
else
|
139
|
+
Vips::Operation.call name.to_s + "_const", [self, enum, other]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Write can fail due to no file descriptors and memory can fill if
|
144
|
+
# large objects are not collected fairly soon. We can't try a
|
145
|
+
# write and GC and retry on fail, since the write may take a
|
146
|
+
# long time and may not be repeatable.
|
147
|
+
#
|
148
|
+
# GCing before every write would have a horrible effect on
|
149
|
+
# performance, so as a compromise we GC every @@gc_interval writes.
|
150
|
+
#
|
151
|
+
# ruby2.1 introduced a generational GC which is fast enough to be
|
152
|
+
# able to GC on every write.
|
153
|
+
|
154
|
+
@@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
|
155
|
+
|
156
|
+
@@gc_interval = 100
|
157
|
+
@@gc_countdown = @@gc_interval
|
158
|
+
|
159
|
+
def write_gc
|
160
|
+
if @@generational_gc
|
161
|
+
GC.start full_mark: false
|
162
|
+
else
|
163
|
+
@@gc_countdown -= 1
|
164
|
+
if @@gc_countdown < 0
|
165
|
+
@@gc_countdown = @@gc_interval
|
166
|
+
GC.start
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
public
|
172
|
+
|
173
|
+
def inspect
|
174
|
+
"#<Image #{width}x#{height} #{format}, #{bands} bands, " +
|
175
|
+
"#{interpretation}>"
|
176
|
+
end
|
177
|
+
|
178
|
+
def respond_to? name, include_all = false
|
179
|
+
# To support keyword args, we need to tell Ruby that final image
|
180
|
+
# arguments cannot be hashes of keywords.
|
181
|
+
#
|
182
|
+
# https://makandracards.com/makandra/36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
|
183
|
+
return false if name == :to_hash
|
184
|
+
|
185
|
+
# respond to all vips operations by nickname
|
186
|
+
return true if Vips::type_find("VipsOperation", name.to_s) != 0
|
187
|
+
|
188
|
+
super
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.respond_to? name, include_all = false
|
192
|
+
# respond to all vips operations by nickname
|
193
|
+
return true if Vips::type_find("VipsOperation", name.to_s) != 0
|
194
|
+
|
195
|
+
super
|
196
|
+
end
|
197
|
+
|
198
|
+
# Invoke a vips operation with {Vips::Operation.call}, using self as
|
199
|
+
# the first input argument.
|
200
|
+
#
|
201
|
+
# @param name [String] vips operation to call
|
202
|
+
# @return result of vips operation
|
203
|
+
def method_missing name, *args, **options
|
204
|
+
Vips::Operation.call name.to_s, [self, *args], options
|
205
|
+
end
|
206
|
+
|
207
|
+
# Invoke a vips operation with {Vips::Operation.call}.
|
208
|
+
def self.method_missing name, *args, **options
|
209
|
+
Vips::Operation.call name.to_s, args, options
|
210
|
+
end
|
211
|
+
|
212
|
+
# Return a new {Image} for a file on disc. This method can load
|
213
|
+
# images in any format supported by vips. The filename can include
|
214
|
+
# load options, for example:
|
215
|
+
#
|
216
|
+
# ```
|
217
|
+
# image = Vips::new_from_file "fred.jpg[shrink=2]"
|
218
|
+
# ```
|
219
|
+
#
|
220
|
+
# You can also supply options as a hash, for example:
|
221
|
+
#
|
222
|
+
# ```
|
223
|
+
# image = Vips::new_from_file "fred.jpg", shrink: 2
|
224
|
+
# ```
|
225
|
+
#
|
226
|
+
# The full set of options available depend upon the load operation that
|
227
|
+
# will be executed. Try something like:
|
228
|
+
#
|
229
|
+
# ```
|
230
|
+
# $ vips jpegload
|
231
|
+
# ```
|
232
|
+
#
|
233
|
+
# at the command-line to see a summary of the available options for the
|
234
|
+
# JPEG loader.
|
235
|
+
#
|
236
|
+
# Loading is fast: only enough of the image is loaded to be able to fill
|
237
|
+
# out the header. Pixels will only be decompressed when they are needed.
|
238
|
+
#
|
239
|
+
# @!macro [new] vips.loadopts
|
240
|
+
# @param opts [Hash] set of options
|
241
|
+
# @option opts [Boolean] :disc (true) Open large images via a
|
242
|
+
# temporary disc file
|
243
|
+
# @option opts [Vips::Access] :access (:random) Access mode for file
|
244
|
+
#
|
245
|
+
# @param name [String] the filename to load from
|
246
|
+
# @macro vips.loadopts
|
247
|
+
# @return [Image] the loaded image
|
248
|
+
def self.new_from_file name, opts = {}
|
249
|
+
# very common, and Vips::vips_filename_get_filename will segv if we
|
250
|
+
# pass this
|
251
|
+
raise Vips::Error, "filename is nil" if name == nil
|
252
|
+
|
253
|
+
filename = Vips::vips_filename_get_filename name
|
254
|
+
option_string = Vips::vips_filename_get_options name
|
255
|
+
loader = Vips::vips_foreign_find_load filename
|
256
|
+
raise Vips::Error if loader == nil
|
257
|
+
|
258
|
+
Operation.call loader, [filename], opts, option_string
|
259
|
+
end
|
260
|
+
|
261
|
+
# Create a new {Image} for an image encoded, in a format such as
|
262
|
+
# JPEG, in a memory string. Load options may be passed as
|
263
|
+
# strings or appended as a hash. For example:
|
264
|
+
#
|
265
|
+
# ```
|
266
|
+
# image = Vips::Image.new_from_buffer memory_buffer, "shrink=2"
|
267
|
+
# ```
|
268
|
+
#
|
269
|
+
# or alternatively:
|
270
|
+
#
|
271
|
+
# ```
|
272
|
+
# image = Vips::Image.new_from_buffer memory_buffer, "", shrink: 2
|
273
|
+
# ```
|
274
|
+
#
|
275
|
+
# The options available depend on the file format. Try something like:
|
276
|
+
#
|
277
|
+
# ```
|
278
|
+
# $ vips jpegload_buffer
|
279
|
+
# ```
|
280
|
+
#
|
281
|
+
# at the command-line to see the available options. Not all loaders
|
282
|
+
# support load from buffer, but at least JPEG, PNG and
|
283
|
+
# TIFF images will work.
|
284
|
+
#
|
285
|
+
# Loading is fast: only enough of the image is loaded to be able to fill
|
286
|
+
# out the header. Pixels will only be decompressed when they are needed.
|
287
|
+
#
|
288
|
+
# @param data [String] the data to load from
|
289
|
+
# @param option_string [String] load options as a string
|
290
|
+
# @macro vips.loadopts
|
291
|
+
# @return [Image] the loaded image
|
292
|
+
def self.new_from_buffer data, option_string, opts = {}
|
293
|
+
loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
|
294
|
+
raise Vips::Error if loader == nil
|
295
|
+
|
296
|
+
Vips::Operation.call loader, [data], opts, option_string
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.matrix_from_array width, height, array
|
300
|
+
ptr = FFI::MemoryPointer.new :double, array.length
|
301
|
+
ptr.write_array_of_double array
|
302
|
+
image = Vips::vips_image_new_matrix_from_array width, height,
|
303
|
+
ptr, array.length
|
304
|
+
Vips::Image.new image
|
305
|
+
end
|
306
|
+
|
307
|
+
# Create a new Image from a 1D or 2D array. A 1D array becomes an
|
308
|
+
# image with height 1. Use `scale` and `offset` to set the scale and
|
309
|
+
# offset fields in the header. These are useful for integer
|
310
|
+
# convolutions.
|
311
|
+
#
|
312
|
+
# For example:
|
313
|
+
#
|
314
|
+
# ```
|
315
|
+
# image = Vips::new_from_array [1, 2, 3]
|
316
|
+
# ```
|
317
|
+
#
|
318
|
+
# or
|
319
|
+
#
|
320
|
+
# ```
|
321
|
+
# image = Vips::new_from_array [
|
322
|
+
# [-1, -1, -1],
|
323
|
+
# [-1, 16, -1],
|
324
|
+
# [-1, -1, -1]], 8
|
325
|
+
# ```
|
326
|
+
#
|
327
|
+
# for a simple sharpening mask.
|
328
|
+
#
|
329
|
+
# @param array [Array] the pixel data as an array of numbers
|
330
|
+
# @param scale [Real] the convolution scale
|
331
|
+
# @param offset [Real] the convolution offset
|
332
|
+
# @return [Image] the image
|
333
|
+
def self.new_from_array array, scale = 1, offset = 0
|
334
|
+
# we accept a 1D array and assume height == 1, or a 2D array
|
335
|
+
# and check all lines are the same length
|
336
|
+
unless array.is_a? Array
|
337
|
+
raise Vips::Error, "Argument is not an array."
|
338
|
+
end
|
339
|
+
|
340
|
+
if array[0].is_a? Array
|
341
|
+
height = array.length
|
342
|
+
width = array[0].length
|
343
|
+
unless array.all? {|x| x.is_a? Array}
|
344
|
+
raise Vips::Error, "Not a 2D array."
|
345
|
+
end
|
346
|
+
unless array.all? {|x| x.length == width}
|
347
|
+
raise Vips::Error, "Array not rectangular."
|
348
|
+
end
|
349
|
+
array = array.flatten
|
350
|
+
else
|
351
|
+
height = 1
|
352
|
+
width = array.length
|
353
|
+
end
|
354
|
+
|
355
|
+
unless array.all? {|x| x.is_a? Numeric}
|
356
|
+
raise Vips::Error, "Not all array elements are Numeric."
|
357
|
+
end
|
358
|
+
|
359
|
+
image = Vips::Image.matrix_from_array width, height, array
|
360
|
+
raise Vips::Error if image == nil
|
361
|
+
|
362
|
+
# be careful to set them as double
|
363
|
+
image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
|
364
|
+
image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
|
365
|
+
|
366
|
+
return image
|
367
|
+
end
|
368
|
+
|
369
|
+
# A new image is created with the same width, height, format,
|
370
|
+
# interpretation, resolution and offset as self, but with every pixel
|
371
|
+
# set to the specified value.
|
372
|
+
#
|
373
|
+
# You can pass an array to make a many-band image, or a single value to
|
374
|
+
# make a one-band image.
|
375
|
+
#
|
376
|
+
# @param value [Real, Array<Real>] value to put in each pixel
|
377
|
+
# @return [Image] constant image
|
378
|
+
def new_from_image value
|
379
|
+
pixel = (Vips::Image.black(1, 1) + value).cast(format)
|
380
|
+
image = pixel.embed 0, 0, width, height, extend: :copy
|
381
|
+
image.copy interpretation: interpretation,
|
382
|
+
xres: xres, yres: yres, xoffset: xoffset, yoffset: yoffset
|
383
|
+
end
|
384
|
+
|
385
|
+
# Write this image to a file. Save options may be encoded in the
|
386
|
+
# filename or given as a hash. For example:
|
387
|
+
#
|
388
|
+
# ```
|
389
|
+
# image.write_to_file "fred.jpg[Q=90]"
|
390
|
+
# ```
|
391
|
+
#
|
392
|
+
# or equivalently:
|
393
|
+
#
|
394
|
+
# ```
|
395
|
+
# image.write_to_file "fred.jpg", Q: 90
|
396
|
+
# ```
|
397
|
+
#
|
398
|
+
# The full set of save options depend on the selected saver. Try
|
399
|
+
# something like:
|
400
|
+
#
|
401
|
+
# ```
|
402
|
+
# $ vips jpegsave
|
403
|
+
# ```
|
404
|
+
#
|
405
|
+
# to see all the available options for JPEG save.
|
406
|
+
#
|
407
|
+
# @!macro [new] vips.saveopts
|
408
|
+
# @param opts [Hash] set of options
|
409
|
+
# @option opts [Boolean] :strip (false) Strip all metadata from image
|
410
|
+
# @option opts [Array<Float>] :background (0) Background colour to
|
411
|
+
# flatten alpha against, if necessary
|
412
|
+
#
|
413
|
+
# @param name [String] filename to write to
|
414
|
+
def write_to_file name, opts = {}
|
415
|
+
filename = Vips::vips_filename_get_filename name
|
416
|
+
option_string = Vips::vips_filename_get_options name
|
417
|
+
saver = Vips::vips_foreign_find_save filename
|
418
|
+
if saver == nil
|
419
|
+
raise Vips::Error, "No known saver for '#{filename}'."
|
420
|
+
end
|
421
|
+
|
422
|
+
Vips::Operation.call saver, [self, filename], opts, option_string
|
423
|
+
|
424
|
+
write_gc
|
425
|
+
end
|
426
|
+
|
427
|
+
# Write this image to a memory buffer. Save options may be encoded in
|
428
|
+
# the format_string or given as a hash. For example:
|
429
|
+
#
|
430
|
+
# ```
|
431
|
+
# buffer = image.write_to_buffer ".jpg[Q=90]"
|
432
|
+
# ```
|
433
|
+
#
|
434
|
+
# or equivalently:
|
435
|
+
#
|
436
|
+
# ```
|
437
|
+
# image.write_to_buffer ".jpg", Q: 90
|
438
|
+
# ```
|
439
|
+
#
|
440
|
+
# The full set of save options depend on the selected saver. Try
|
441
|
+
# something like:
|
442
|
+
#
|
443
|
+
# ```
|
444
|
+
# $ vips jpegsave
|
445
|
+
# ```
|
446
|
+
#
|
447
|
+
# to see all the available options for JPEG save.
|
448
|
+
#
|
449
|
+
# @param format_string [String] save format plus options
|
450
|
+
# @macro vips.saveopts
|
451
|
+
# @return [String] the image saved in the specified format
|
452
|
+
def write_to_buffer format_string, opts = {}
|
453
|
+
filename = Vips::vips_filename_get_filename format_string
|
454
|
+
option_string = Vips::vips_filename_get_options format_string
|
455
|
+
saver = Vips::vips_foreign_find_save_buffer filename
|
456
|
+
if saver == nil
|
457
|
+
raise Vips::Error, "No known saver for '#{filename}'."
|
458
|
+
end
|
459
|
+
|
460
|
+
buffer = Vips::Operation.call saver, [self], opts, option_string
|
461
|
+
raise Vips::Error if buffer == nil
|
462
|
+
|
463
|
+
write_gc
|
464
|
+
|
465
|
+
return buffer
|
466
|
+
end
|
467
|
+
|
468
|
+
# Write this image to a large memory buffer.
|
469
|
+
#
|
470
|
+
# @return [String] the pixels as a huge binary string
|
471
|
+
def write_to_memory
|
472
|
+
len = Vips::SizeStruct.new
|
473
|
+
ptr = Vips::vips_image_write_to_memory self, len
|
474
|
+
|
475
|
+
# wrap up as an autopointer
|
476
|
+
ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
|
477
|
+
|
478
|
+
ptr.get_bytes 0, len[:value]
|
479
|
+
end
|
480
|
+
|
481
|
+
# Fetch a `GType` from an image. `GType` will be 0 for no such field.
|
482
|
+
#
|
483
|
+
# @see get
|
484
|
+
# @param name [String] Metadata field to fetch
|
485
|
+
# @return [Integer] GType
|
486
|
+
def get_typeof name
|
487
|
+
# on libvips before 8.5, property types must be searched first,
|
488
|
+
# since vips_image_get_typeof returned built-in enums as int
|
489
|
+
unless Vips::at_least_libvips?(8, 5)
|
490
|
+
gtype = parent_get_typeof name
|
491
|
+
return gtype if gtype != 0
|
492
|
+
end
|
493
|
+
|
494
|
+
Vips::vips_image_get_typeof self, name
|
495
|
+
end
|
496
|
+
|
497
|
+
# Get a metadata item from an image. Ruby types are constructed
|
498
|
+
# automatically from the `GValue`, if possible.
|
499
|
+
#
|
500
|
+
# For example, you can read the ICC profile from an image like this:
|
501
|
+
#
|
502
|
+
# ```
|
503
|
+
# profile = image.get "icc-profile-data"
|
504
|
+
# ```
|
505
|
+
#
|
506
|
+
# and profile will be an array containing the profile.
|
507
|
+
#
|
508
|
+
# @param name [String] Metadata field to get
|
509
|
+
# @return [Object] Value of field
|
510
|
+
def get name
|
511
|
+
# with old libvips, we must fetch properties (as opposed to
|
512
|
+
# metadata) via VipsObject
|
513
|
+
unless Vips::at_least_libvips?(8, 5)
|
514
|
+
return super if parent_get_typeof(name) != 0
|
515
|
+
end
|
516
|
+
|
517
|
+
gvalue = GObject::GValue.alloc
|
518
|
+
result = Vips::vips_image_get self, name, gvalue
|
519
|
+
raise Vips::Error if result != 0
|
520
|
+
|
521
|
+
return gvalue.get
|
522
|
+
end
|
523
|
+
|
524
|
+
# Get the names of all fields on an image. Use this to loop over all
|
525
|
+
# image metadata.
|
526
|
+
#
|
527
|
+
# @return [[String]] array of field names
|
528
|
+
def get_fields
|
529
|
+
# vips_image_get_fields() was added in libvips 8.5
|
530
|
+
return [] unless Vips.respond_to? :vips_image_get_fields
|
531
|
+
|
532
|
+
array = Vips::vips_image_get_fields self
|
533
|
+
|
534
|
+
names = []
|
535
|
+
p = array
|
536
|
+
until ((q = p.read_pointer).null?)
|
537
|
+
names << q.read_string
|
538
|
+
GLib::g_free q
|
539
|
+
p += FFI::Type::POINTER.size
|
540
|
+
end
|
541
|
+
GLib::g_free array
|
542
|
+
|
543
|
+
return names
|
544
|
+
end
|
545
|
+
|
546
|
+
# Create a metadata item on an image, of the specifed type. Ruby types
|
547
|
+
# are automatically
|
548
|
+
# transformed into the matching `GType`, if possible.
|
549
|
+
#
|
550
|
+
# For example, you can use this to set an image's ICC profile:
|
551
|
+
#
|
552
|
+
# ```
|
553
|
+
# x = y.set_type Vips::BLOB_TYPE, "icc-profile-data", profile
|
554
|
+
# ```
|
555
|
+
#
|
556
|
+
# where `profile` is an ICC profile held as a binary string object.
|
557
|
+
#
|
558
|
+
# @see set
|
559
|
+
# @param gtype [Integer] GType of item
|
560
|
+
# @param name [String] Metadata field to set
|
561
|
+
# @param value [Object] Value to set
|
562
|
+
def set_type gtype, name, value
|
563
|
+
gvalue = GObject::GValue.alloc
|
564
|
+
gvalue.init gtype
|
565
|
+
gvalue.set value
|
566
|
+
Vips::vips_image_set self, name, gvalue
|
567
|
+
end
|
568
|
+
|
569
|
+
# Set the value of a metadata item on an image. The metadata item must
|
570
|
+
# already exist. Ruby types are automatically
|
571
|
+
# transformed into the matching `GValue`, if possible.
|
572
|
+
#
|
573
|
+
# For example, you can use this to set an image's ICC profile:
|
574
|
+
#
|
575
|
+
# ```
|
576
|
+
# x = y.set "icc-profile-data", profile
|
577
|
+
# ```
|
578
|
+
#
|
579
|
+
# where `profile` is an ICC profile held as a binary string object.
|
580
|
+
#
|
581
|
+
# @see set_type
|
582
|
+
# @param name [String] Metadata field to set
|
583
|
+
# @param value [Object] Value to set
|
584
|
+
def set name, value
|
585
|
+
set_type get_typeof(name), name, value
|
586
|
+
end
|
587
|
+
|
588
|
+
# Remove a metadata item from an image.
|
589
|
+
#
|
590
|
+
# @param name [String] Metadata field to remove
|
591
|
+
def remove name
|
592
|
+
Vips::vips_image_remove self, name
|
593
|
+
end
|
594
|
+
|
595
|
+
# compatibility: old name for get
|
596
|
+
def get_value name
|
597
|
+
get name
|
598
|
+
end
|
599
|
+
|
600
|
+
# compatibility: old name for set
|
601
|
+
def set_value name, value
|
602
|
+
set name, value
|
603
|
+
end
|
604
|
+
|
605
|
+
# Get image width, in pixels.
|
606
|
+
#
|
607
|
+
# @return [Integer] image width, in pixels
|
608
|
+
def width
|
609
|
+
get "width"
|
610
|
+
end
|
611
|
+
|
612
|
+
# Get image height, in pixels.
|
613
|
+
#
|
614
|
+
# @return [Integer] image height, in pixels
|
615
|
+
def height
|
616
|
+
get "height"
|
617
|
+
end
|
618
|
+
|
619
|
+
# Get number of image bands.
|
620
|
+
#
|
621
|
+
# @return [Integer] number of image bands
|
622
|
+
def bands
|
623
|
+
get "bands"
|
624
|
+
end
|
625
|
+
|
626
|
+
# Get image format.
|
627
|
+
#
|
628
|
+
# @return [Symbol] image format
|
629
|
+
def format
|
630
|
+
get "format"
|
631
|
+
end
|
632
|
+
|
633
|
+
# Get image interpretation.
|
634
|
+
#
|
635
|
+
# @return [Symbol] image interpretation
|
636
|
+
def interpretation
|
637
|
+
get "interpretation"
|
638
|
+
end
|
639
|
+
|
640
|
+
# Get image coding.
|
641
|
+
#
|
642
|
+
# @return [Symbol] image coding
|
643
|
+
def coding
|
644
|
+
get "coding"
|
645
|
+
end
|
646
|
+
|
647
|
+
# Get image filename, if any.
|
648
|
+
#
|
649
|
+
# @return [String] image filename
|
650
|
+
def filename
|
651
|
+
get "filename"
|
652
|
+
end
|
653
|
+
|
654
|
+
# Get image xoffset.
|
655
|
+
#
|
656
|
+
# @return [Integer] image xoffset
|
657
|
+
def xoffset
|
658
|
+
get "xoffset"
|
659
|
+
end
|
660
|
+
|
661
|
+
# Get image yoffset.
|
662
|
+
#
|
663
|
+
# @return [Integer] image yoffset
|
664
|
+
def yoffset
|
665
|
+
get "yoffset"
|
666
|
+
end
|
667
|
+
|
668
|
+
# Get image x resolution.
|
669
|
+
#
|
670
|
+
# @return [Float] image x resolution
|
671
|
+
def xres
|
672
|
+
get "xres"
|
673
|
+
end
|
674
|
+
|
675
|
+
# Get image y resolution.
|
676
|
+
#
|
677
|
+
# @return [Float] image y resolution
|
678
|
+
def yres
|
679
|
+
get "yres"
|
680
|
+
end
|
681
|
+
|
682
|
+
# Get scale metadata.
|
683
|
+
#
|
684
|
+
# @return [Float] image scale
|
685
|
+
def scale
|
686
|
+
return 1 if get_typeof("scale") == 0
|
687
|
+
|
688
|
+
get "scale"
|
689
|
+
end
|
690
|
+
|
691
|
+
# Get offset metadata.
|
692
|
+
#
|
693
|
+
# @return [Float] image offset
|
694
|
+
def offset
|
695
|
+
return 0 if get_typeof("offset") == 0
|
696
|
+
|
697
|
+
get "offset"
|
698
|
+
end
|
699
|
+
|
700
|
+
# Get the image size.
|
701
|
+
#
|
702
|
+
# @return [Integer, Integer] image width and height
|
703
|
+
def size
|
704
|
+
[width, height]
|
705
|
+
end
|
706
|
+
|
707
|
+
if Vips::at_least_libvips?(8, 5)
|
708
|
+
# Detect if image has an alpha channel
|
709
|
+
#
|
710
|
+
# @return [Boolean] true if image has an alpha channel.
|
711
|
+
def has_alpha?
|
712
|
+
return Vips::vips_image_hasalpha(self) != 0
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
# vips_addalpha was added in libvips 8.6
|
717
|
+
if Vips::at_least_libvips?(8, 6)
|
718
|
+
# Append an alpha channel to an image.
|
719
|
+
#
|
720
|
+
# @return [Image] new image
|
721
|
+
def add_alpha
|
722
|
+
ptr = GenericPtr.new
|
723
|
+
result = Vips::vips_addalpha self, ptr
|
724
|
+
raise Vips::Error if result != 0
|
725
|
+
|
726
|
+
Vips::Image.new ptr[:value]
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
# Copy an image to a memory area.
|
731
|
+
#
|
732
|
+
# This can be useful for reusing results, but can obviously use a lot of
|
733
|
+
# memory for large images. See {Image#tilecache} for a way of caching
|
734
|
+
# parts of an image.
|
735
|
+
#
|
736
|
+
# @return [Image] new memory image
|
737
|
+
def copy_memory
|
738
|
+
new_image = Vips::vips_image_copy_memory self
|
739
|
+
Vips::Image.new new_image
|
740
|
+
end
|
741
|
+
|
742
|
+
# Draw a point on an image.
|
743
|
+
#
|
744
|
+
# See {Image#draw_rect}.
|
745
|
+
#
|
746
|
+
# @return [Image] modified image
|
747
|
+
def draw_point ink, left, top, opts = {}
|
748
|
+
draw_rect ink, left, top, 1, 1, opts
|
749
|
+
end
|
750
|
+
|
751
|
+
# Add an image, constant or array.
|
752
|
+
#
|
753
|
+
# @param other [Image, Real, Array<Real>] Thing to add to self
|
754
|
+
# @return [Image] result of addition
|
755
|
+
def + other
|
756
|
+
other.is_a?(Vips::Image) ?
|
757
|
+
add(other) : linear(1, other)
|
758
|
+
end
|
759
|
+
|
760
|
+
# Subtract an image, constant or array.
|
761
|
+
#
|
762
|
+
# @param other [Image, Real, Array<Real>] Thing to subtract from self
|
763
|
+
# @return [Image] result of subtraction
|
764
|
+
def - other
|
765
|
+
other.is_a?(Vips::Image) ?
|
766
|
+
subtract(other) : linear(1, Image::smap(other) {|x| x * -1})
|
767
|
+
end
|
768
|
+
|
769
|
+
# Multiply an image, constant or array.
|
770
|
+
#
|
771
|
+
# @param other [Image, Real, Array<Real>] Thing to multiply by self
|
772
|
+
# @return [Image] result of multiplication
|
773
|
+
def * other
|
774
|
+
other.is_a?(Vips::Image) ?
|
775
|
+
multiply(other) : linear(other, 0)
|
776
|
+
end
|
777
|
+
|
778
|
+
# Divide an image, constant or array.
|
779
|
+
#
|
780
|
+
# @param other [Image, Real, Array<Real>] Thing to divide self by
|
781
|
+
# @return [Image] result of division
|
782
|
+
def / other
|
783
|
+
other.is_a?(Vips::Image) ?
|
784
|
+
divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0)
|
785
|
+
end
|
786
|
+
|
787
|
+
# Remainder after integer division with an image, constant or array.
|
788
|
+
#
|
789
|
+
# @param other [Image, Real, Array<Real>] self modulo this
|
790
|
+
# @return [Image] result of modulo
|
791
|
+
def % other
|
792
|
+
other.is_a?(Vips::Image) ?
|
793
|
+
remainder(other) : remainder_const(other)
|
794
|
+
end
|
795
|
+
|
796
|
+
# Raise to power of an image, constant or array.
|
797
|
+
#
|
798
|
+
# @param other [Image, Real, Array<Real>] self to the power of this
|
799
|
+
# @return [Image] result of power
|
800
|
+
def ** other
|
801
|
+
call_enum "math2", other, :pow
|
802
|
+
end
|
803
|
+
|
804
|
+
# Integer left shift with an image, constant or array.
|
805
|
+
#
|
806
|
+
# @param other [Image, Real, Array<Real>] shift left by this much
|
807
|
+
# @return [Image] result of left shift
|
808
|
+
def << other
|
809
|
+
call_enum "boolean", other, :lshift
|
810
|
+
end
|
811
|
+
|
812
|
+
# Integer right shift with an image, constant or array.
|
813
|
+
#
|
814
|
+
# @param other [Image, Real, Array<Real>] shift right by this much
|
815
|
+
# @return [Image] result of right shift
|
816
|
+
def >> other
|
817
|
+
call_enum "boolean", other, :rshift
|
818
|
+
end
|
819
|
+
|
820
|
+
# Integer bitwise OR with an image, constant or array.
|
821
|
+
#
|
822
|
+
# @param other [Image, Real, Array<Real>] bitwise OR with this
|
823
|
+
# @return [Image] result of bitwise OR
|
824
|
+
def | other
|
825
|
+
call_enum "boolean", other, :or
|
826
|
+
end
|
827
|
+
|
828
|
+
# Integer bitwise AND with an image, constant or array.
|
829
|
+
#
|
830
|
+
# @param other [Image, Real, Array<Real>] bitwise AND with this
|
831
|
+
# @return [Image] result of bitwise AND
|
832
|
+
def & other
|
833
|
+
call_enum "boolean", other, :and
|
834
|
+
end
|
835
|
+
|
836
|
+
# Integer bitwise EOR with an image, constant or array.
|
837
|
+
#
|
838
|
+
# @param other [Image, Real, Array<Real>] bitwise EOR with this
|
839
|
+
# @return [Image] result of bitwise EOR
|
840
|
+
def ^ other
|
841
|
+
call_enum "boolean", other, :eor
|
842
|
+
end
|
843
|
+
|
844
|
+
# Equivalent to image ^ -1
|
845
|
+
#
|
846
|
+
# @return [Image] image with bits flipped
|
847
|
+
def !
|
848
|
+
self ^ -1
|
849
|
+
end
|
850
|
+
|
851
|
+
# Equivalent to image ^ -1
|
852
|
+
#
|
853
|
+
# @return [Image] image with bits flipped
|
854
|
+
def ~
|
855
|
+
self ^ -1
|
856
|
+
end
|
857
|
+
|
858
|
+
# @return [Image] image
|
859
|
+
def +@
|
860
|
+
self
|
861
|
+
end
|
862
|
+
|
863
|
+
# Equivalent to image * -1
|
864
|
+
#
|
865
|
+
# @return [Image] negative of image
|
866
|
+
def -@
|
867
|
+
self * -1
|
868
|
+
end
|
869
|
+
|
870
|
+
# Relational less than with an image, constant or array.
|
871
|
+
#
|
872
|
+
# @param other [Image, Real, Array<Real>] relational less than with this
|
873
|
+
# @return [Image] result of less than
|
874
|
+
def < other
|
875
|
+
call_enum "relational", other, :less
|
876
|
+
end
|
877
|
+
|
878
|
+
# Relational less than or equal to with an image, constant or array.
|
879
|
+
#
|
880
|
+
# @param other [Image, Real, Array<Real>] relational less than or
|
881
|
+
# equal to with this
|
882
|
+
# @return [Image] result of less than or equal to
|
883
|
+
def <= other
|
884
|
+
call_enum "relational", other, :lesseq
|
885
|
+
end
|
886
|
+
|
887
|
+
# Relational more than with an image, constant or array.
|
888
|
+
#
|
889
|
+
# @param other [Image, Real, Array<Real>] relational more than with this
|
890
|
+
# @return [Image] result of more than
|
891
|
+
def > other
|
892
|
+
call_enum "relational", other, :more
|
893
|
+
end
|
894
|
+
|
895
|
+
# Relational more than or equal to with an image, constant or array.
|
896
|
+
#
|
897
|
+
# @param other [Image, Real, Array<Real>] relational more than or
|
898
|
+
# equal to with this
|
899
|
+
# @return [Image] result of more than or equal to
|
900
|
+
def >= other
|
901
|
+
call_enum "relational", other, :moreeq
|
902
|
+
end
|
903
|
+
|
904
|
+
# Compare equality to nil, an image, constant or array.
|
905
|
+
#
|
906
|
+
# @param other [nil, Image, Real, Array<Real>] test equality to this
|
907
|
+
# @return [Image] result of equality
|
908
|
+
def == other
|
909
|
+
# for equality, we must allow tests against nil
|
910
|
+
if other == nil
|
911
|
+
false
|
912
|
+
else
|
913
|
+
call_enum "relational", other, :equal
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
# Compare inequality to nil, an image, constant or array.
|
918
|
+
#
|
919
|
+
# @param other [nil, Image, Real, Array<Real>] test inequality to this
|
920
|
+
# @return [Image] result of inequality
|
921
|
+
def != other
|
922
|
+
# for equality, we must allow tests against nil
|
923
|
+
if other == nil
|
924
|
+
true
|
925
|
+
else
|
926
|
+
call_enum "relational", other, :noteq
|
927
|
+
end
|
928
|
+
end
|
929
|
+
|
930
|
+
# Fetch bands using a number or a range
|
931
|
+
#
|
932
|
+
# @param index [Numeric, Range] extract these band(s)
|
933
|
+
# @return [Image] extracted band(s)
|
934
|
+
def [] index
|
935
|
+
if index.is_a? Range
|
936
|
+
n = index.size
|
937
|
+
extract_band index.begin, n: n
|
938
|
+
elsif index.is_a? Numeric
|
939
|
+
extract_band index
|
940
|
+
else
|
941
|
+
raise Vips::Error, "[] index is not range or numeric."
|
942
|
+
end
|
943
|
+
end
|
944
|
+
|
945
|
+
# Convert to an Array. This will be slow for large images.
|
946
|
+
#
|
947
|
+
# @return [Array] array of Fixnum
|
948
|
+
def to_a
|
949
|
+
# we render the image to a big string, then unpack
|
950
|
+
# as a Ruby array of the correct type
|
951
|
+
memory = write_to_memory
|
952
|
+
|
953
|
+
# make the template for unpack
|
954
|
+
template = {
|
955
|
+
:char => 'c',
|
956
|
+
:uchar => 'C',
|
957
|
+
:short => 's_',
|
958
|
+
:ushort => 'S_',
|
959
|
+
:int => 'i_',
|
960
|
+
:uint => 'I_',
|
961
|
+
:float => 'f',
|
962
|
+
:double => 'd',
|
963
|
+
:complex => 'f',
|
964
|
+
:dpcomplex => 'd'
|
965
|
+
}[format] + '*'
|
966
|
+
|
967
|
+
# and unpack into something like [1, 2, 3, 4 ..]
|
968
|
+
array = memory.unpack(template)
|
969
|
+
|
970
|
+
# gather band elements together
|
971
|
+
pixel_array = array.each_slice(bands).to_a
|
972
|
+
|
973
|
+
# build rows
|
974
|
+
row_array = pixel_array.each_slice(width).to_a
|
975
|
+
|
976
|
+
return row_array
|
977
|
+
end
|
978
|
+
|
979
|
+
# Return the largest integral value not greater than the argument.
|
980
|
+
#
|
981
|
+
# @return [Image] floor of image
|
982
|
+
def floor
|
983
|
+
round :floor
|
984
|
+
end
|
985
|
+
|
986
|
+
# Return the smallest integral value not less than the argument.
|
987
|
+
#
|
988
|
+
# @return [Image] ceil of image
|
989
|
+
def ceil
|
990
|
+
round :ceil
|
991
|
+
end
|
992
|
+
|
993
|
+
# Return the nearest integral value.
|
994
|
+
#
|
995
|
+
# @return [Image] rint of image
|
996
|
+
def rint
|
997
|
+
round :rint
|
998
|
+
end
|
999
|
+
|
1000
|
+
# AND the bands of an image together
|
1001
|
+
#
|
1002
|
+
# @return [Image] all bands ANDed together
|
1003
|
+
def bandand
|
1004
|
+
bandbool :and
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# OR the bands of an image together
|
1008
|
+
#
|
1009
|
+
# @return [Image] all bands ORed together
|
1010
|
+
def bandor
|
1011
|
+
bandbool :or
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# EOR the bands of an image together
|
1015
|
+
#
|
1016
|
+
# @return [Image] all bands EORed together
|
1017
|
+
def bandeor
|
1018
|
+
bandbool :eor
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# Split an n-band image into n separate images.
|
1022
|
+
#
|
1023
|
+
# @return [Array<Image>] Array of n one-band images
|
1024
|
+
def bandsplit
|
1025
|
+
(0...bands).map {|i| extract_band i}
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
# Join a set of images bandwise.
|
1029
|
+
#
|
1030
|
+
# @param other [Image, Array<Image>, Real, Array<Real>] bands to append
|
1031
|
+
# @return [Image] many band image
|
1032
|
+
def bandjoin other
|
1033
|
+
unless other.is_a? Array
|
1034
|
+
other = [other]
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# if other is just Numeric, we can use bandjoin_const
|
1038
|
+
not_all_real = !other.all?{|x| x.is_a? Numeric}
|
1039
|
+
|
1040
|
+
if not_all_real
|
1041
|
+
Vips::Image.bandjoin([self] + other)
|
1042
|
+
else
|
1043
|
+
bandjoin_const other
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
# Composite a set of images with a set of blend modes.
|
1048
|
+
#
|
1049
|
+
# @param other [Image, Array<Image>, Real, Array<Real>] bands to append
|
1050
|
+
# @return [Image] many band image
|
1051
|
+
def composite other, mode, opts = {}
|
1052
|
+
unless other.is_a? Array
|
1053
|
+
other = [other]
|
1054
|
+
end
|
1055
|
+
unless mode.is_a? Array
|
1056
|
+
mode = [mode]
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
mode = mode.map do |x|
|
1060
|
+
GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
Vips::Image.composite([self] + other, mode, opts)
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
# Return the coordinates of the image maximum.
|
1067
|
+
#
|
1068
|
+
# @return [Real, Real, Real] maximum value, x coordinate of maximum, y
|
1069
|
+
# coordinate of maximum
|
1070
|
+
def maxpos
|
1071
|
+
v, opts = max x: true, y: true
|
1072
|
+
x = opts['x']
|
1073
|
+
y = opts['y']
|
1074
|
+
return v, x, y
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
# Return the coordinates of the image minimum.
|
1078
|
+
#
|
1079
|
+
# @return [Real, Real, Real] minimum value, x coordinate of minimum, y
|
1080
|
+
# coordinate of minimum
|
1081
|
+
def minpos
|
1082
|
+
v, opts = min x: true, y: true
|
1083
|
+
x = opts['x']
|
1084
|
+
y = opts['y']
|
1085
|
+
return v, x, y
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
# get the value of a pixel as an array
|
1089
|
+
#
|
1090
|
+
# @param x [Integer] x coordinate to sample
|
1091
|
+
# @param y [Integer] y coordinate to sample
|
1092
|
+
# @return [Array<Float>] the pixel values as an array
|
1093
|
+
def getpoint x, y
|
1094
|
+
# vips has an operation that does this, but we can't call it via
|
1095
|
+
# gobject-introspection 3.1 since it's missing array double
|
1096
|
+
# get
|
1097
|
+
#
|
1098
|
+
# remove this def when gobject-introspection updates
|
1099
|
+
crop(x, y, 1, 1).bandsplit.map(&:avg)
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
# a median filter
|
1103
|
+
#
|
1104
|
+
# @param size [Integer] size of filter window
|
1105
|
+
# @return [Image] result of median filter
|
1106
|
+
def median size = 3
|
1107
|
+
rank size, size, (size * size) / 2
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
# Return the real part of a complex image.
|
1111
|
+
#
|
1112
|
+
# @return [Image] real part of complex image
|
1113
|
+
def real
|
1114
|
+
complexget :real
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
# Return the imaginary part of a complex image.
|
1118
|
+
#
|
1119
|
+
# @return [Image] imaginary part of complex image
|
1120
|
+
def imag
|
1121
|
+
complexget :imag
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
# Return an image with rectangular pixels converted to polar.
|
1125
|
+
#
|
1126
|
+
# The image
|
1127
|
+
# can be complex, in which case the return image will also be complex,
|
1128
|
+
# or must have an even number of bands, in which case pairs of
|
1129
|
+
# bands are treated as (x, y) coordinates.
|
1130
|
+
#
|
1131
|
+
# @see xyz
|
1132
|
+
# @return [Image] image converted to polar coordinates
|
1133
|
+
def polar
|
1134
|
+
Image::run_cmplx(self) {|x| x.complex :polar}
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
# Return an image with polar pixels converted to rectangular.
|
1138
|
+
#
|
1139
|
+
# The image
|
1140
|
+
# can be complex, in which case the return image will also be complex,
|
1141
|
+
# or must have an even number of bands, in which case pairs of
|
1142
|
+
# bands are treated as (x, y) coordinates.
|
1143
|
+
#
|
1144
|
+
# @see xyz
|
1145
|
+
# @return [Image] image converted to rectangular coordinates
|
1146
|
+
def rect
|
1147
|
+
Image::run_cmplx(self) {|x| x.complex :rect}
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
# Return the complex conjugate of an image.
|
1151
|
+
#
|
1152
|
+
# The image
|
1153
|
+
# can be complex, in which case the return image will also be complex,
|
1154
|
+
# or must have an even number of bands, in which case pairs of
|
1155
|
+
# bands are treated as (x, y) coordinates.
|
1156
|
+
#
|
1157
|
+
# @return [Image] complex conjugate
|
1158
|
+
def conj
|
1159
|
+
Image::run_cmplx(self) {|x| x.complex :conj}
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
# Calculate the cross phase of two images.
|
1163
|
+
#
|
1164
|
+
# @param other [Image, Real, Array<Real>] cross phase with this
|
1165
|
+
# @return [Image] cross phase
|
1166
|
+
def cross_phase other
|
1167
|
+
complex2 other, :cross_phase
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
# Return the sine of an image in degrees.
|
1171
|
+
#
|
1172
|
+
# @return [Image] sine of each pixel
|
1173
|
+
def sin
|
1174
|
+
math :sin
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
# Return the cosine of an image in degrees.
|
1178
|
+
#
|
1179
|
+
# @return [Image] cosine of each pixel
|
1180
|
+
def cos
|
1181
|
+
math :cos
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
# Return the tangent of an image in degrees.
|
1185
|
+
#
|
1186
|
+
# @return [Image] tangent of each pixel
|
1187
|
+
def tan
|
1188
|
+
math :tan
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
# Return the inverse sine of an image in degrees.
|
1192
|
+
#
|
1193
|
+
# @return [Image] inverse sine of each pixel
|
1194
|
+
def asin
|
1195
|
+
math :asin
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
# Return the inverse cosine of an image in degrees.
|
1199
|
+
#
|
1200
|
+
# @return [Image] inverse cosine of each pixel
|
1201
|
+
def acos
|
1202
|
+
math :acos
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Return the inverse tangent of an image in degrees.
|
1206
|
+
#
|
1207
|
+
# @return [Image] inverse tangent of each pixel
|
1208
|
+
def atan
|
1209
|
+
math :atan
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
# Return the natural log of an image.
|
1213
|
+
#
|
1214
|
+
# @return [Image] natural log of each pixel
|
1215
|
+
def log
|
1216
|
+
math :log
|
1217
|
+
end
|
1218
|
+
|
1219
|
+
# Return the log base 10 of an image.
|
1220
|
+
#
|
1221
|
+
# @return [Image] base 10 log of each pixel
|
1222
|
+
def log10
|
1223
|
+
math :log10
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
# Return e ** pixel.
|
1227
|
+
#
|
1228
|
+
# @return [Image] e ** pixel
|
1229
|
+
def exp
|
1230
|
+
math :exp
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
# Return 10 ** pixel.
|
1234
|
+
#
|
1235
|
+
# @return [Image] 10 ** pixel
|
1236
|
+
def exp10
|
1237
|
+
math :exp10
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
# Flip horizontally.
|
1241
|
+
#
|
1242
|
+
# @return [Image] image flipped horizontally
|
1243
|
+
def fliphor
|
1244
|
+
flip :horizontal
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
# Flip vertically.
|
1248
|
+
#
|
1249
|
+
# @return [Image] image flipped vertically
|
1250
|
+
def flipver
|
1251
|
+
flip :vertical
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
# Erode with a structuring element.
|
1255
|
+
#
|
1256
|
+
# The structuring element must be an array with 0 for black, 255 for
|
1257
|
+
# white and 128 for don't care.
|
1258
|
+
#
|
1259
|
+
# @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
|
1260
|
+
# element
|
1261
|
+
# @return [Image] eroded image
|
1262
|
+
def erode mask
|
1263
|
+
morph mask, :erode
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
# Dilate with a structuring element.
|
1267
|
+
#
|
1268
|
+
# The structuring element must be an array with 0 for black, 255 for
|
1269
|
+
# white and 128 for don't care.
|
1270
|
+
#
|
1271
|
+
# @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
|
1272
|
+
# element
|
1273
|
+
# @return [Image] dilated image
|
1274
|
+
def dilate mask
|
1275
|
+
morph mask, :dilate
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
# Rotate by 90 degrees clockwise.
|
1279
|
+
#
|
1280
|
+
# @return [Image] rotated image
|
1281
|
+
def rot90
|
1282
|
+
rot :d90
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
# Rotate by 180 degrees clockwise.
|
1286
|
+
#
|
1287
|
+
# @return [Image] rotated image
|
1288
|
+
def rot180
|
1289
|
+
rot :d180
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
# Rotate by 270 degrees clockwise.
|
1293
|
+
#
|
1294
|
+
# @return [Image] rotated image
|
1295
|
+
def rot270
|
1296
|
+
rot :d270
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
# Select pixels from `th` if `self` is non-zero and from `el` if
|
1300
|
+
# `self` is zero. Use the `:blend` option to fade smoothly
|
1301
|
+
# between `th` and `el`.
|
1302
|
+
#
|
1303
|
+
# @param th [Image, Real, Array<Real>] true values
|
1304
|
+
# @param el [Image, Real, Array<Real>] false values
|
1305
|
+
# @param opts [Hash] set of options
|
1306
|
+
# @option opts [Boolean] :blend (false) Blend smoothly between th and el
|
1307
|
+
# @return [Image] merged image
|
1308
|
+
def ifthenelse(th, el, opts = {})
|
1309
|
+
match_image = [th, el, self].find {|x| x.is_a? Vips::Image}
|
1310
|
+
|
1311
|
+
unless th.is_a? Vips::Image
|
1312
|
+
th = Operation.imageize match_image, th
|
1313
|
+
end
|
1314
|
+
unless el.is_a? Vips::Image
|
1315
|
+
el = Operation.imageize match_image, el
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
Vips::Operation.call "ifthenelse", [self, th, el], opts
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
# Scale an image to uchar. This is the vips `scale` operation, but
|
1322
|
+
# renamed to avoid a clash with the `.scale` property.
|
1323
|
+
#
|
1324
|
+
# @param opts [Hash] Set of options
|
1325
|
+
# @return [Vips::Image] Output image
|
1326
|
+
def scaleimage opts = {}
|
1327
|
+
Vips::Image.scale self, opts
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
# This method generates yard comments for all the dynamically bound
|
1333
|
+
# vips operations.
|
1334
|
+
#
|
1335
|
+
# Regenerate with something like:
|
1336
|
+
#
|
1337
|
+
# ```
|
1338
|
+
# $ ruby > methods.rb
|
1339
|
+
# require 'vips'; Vips::generate_yard
|
1340
|
+
# ^D
|
1341
|
+
# ```
|
1342
|
+
|
1343
|
+
def self.generate_yard
|
1344
|
+
# these have hand-written methods, see above
|
1345
|
+
no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
|
1346
|
+
|
1347
|
+
# map gobject's type names to Ruby
|
1348
|
+
map_go_to_ruby = {
|
1349
|
+
"gboolean" => "Boolean",
|
1350
|
+
"gint" => "Integer",
|
1351
|
+
"gdouble" => "Float",
|
1352
|
+
"gfloat" => "Float",
|
1353
|
+
"gchararray" => "String",
|
1354
|
+
"VipsImage" => "Vips::Image",
|
1355
|
+
"VipsInterpolate" => "Vips::Interpolate",
|
1356
|
+
"VipsArrayDouble" => "Array<Double>",
|
1357
|
+
"VipsArrayInt" => "Array<Integer>",
|
1358
|
+
"VipsArrayImage" => "Array<Image>",
|
1359
|
+
"VipsArrayString" => "Array<String>",
|
1360
|
+
}
|
1361
|
+
|
1362
|
+
generate_operation = lambda do |gtype, nickname, op|
|
1363
|
+
op_flags = op.get_flags
|
1364
|
+
return if (op_flags & OPERATION_DEPRECATED) != 0
|
1365
|
+
return if no_generate.include? nickname
|
1366
|
+
description = Vips::vips_object_get_description op
|
1367
|
+
|
1368
|
+
# find and classify all the arguments the operator can take
|
1369
|
+
required_input = []
|
1370
|
+
optional_input = []
|
1371
|
+
required_output = []
|
1372
|
+
optional_output = []
|
1373
|
+
member_x = nil
|
1374
|
+
op.argument_map do |pspec, argument_class, argument_instance|
|
1375
|
+
arg_flags = argument_class[:flags]
|
1376
|
+
next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
|
1377
|
+
next if (arg_flags & ARGUMENT_DEPRECATED) != 0
|
1378
|
+
|
1379
|
+
name = pspec[:name].tr("-", "_")
|
1380
|
+
# 'in' as a param name confuses yard
|
1381
|
+
name = "im" if name == "in"
|
1382
|
+
gtype = pspec[:value_type]
|
1383
|
+
fundamental = GObject::g_type_fundamental gtype
|
1384
|
+
type_name = GObject::g_type_name gtype
|
1385
|
+
if map_go_to_ruby.include? type_name
|
1386
|
+
type_name = map_go_to_ruby[type_name]
|
1387
|
+
end
|
1388
|
+
if fundamental == GObject::GFLAGS_TYPE ||
|
1389
|
+
fundamental == GObject::GENUM_TYPE
|
1390
|
+
type_name = "Vips::" + type_name[/Vips(.*)/, 1]
|
1391
|
+
end
|
1392
|
+
blurb = GObject::g_param_spec_get_blurb pspec
|
1393
|
+
value = {:name => name,
|
1394
|
+
:flags => arg_flags,
|
1395
|
+
:gtype => gtype,
|
1396
|
+
:type_name => type_name,
|
1397
|
+
:blurb => blurb}
|
1398
|
+
|
1399
|
+
if (arg_flags & ARGUMENT_INPUT) != 0
|
1400
|
+
if (arg_flags & ARGUMENT_REQUIRED) != 0
|
1401
|
+
# note the first required input image, if any ... we
|
1402
|
+
# will be a method of this instance
|
1403
|
+
if !member_x && gtype == Vips::IMAGE_TYPE
|
1404
|
+
member_x = value
|
1405
|
+
else
|
1406
|
+
required_input << value
|
1407
|
+
end
|
1408
|
+
else
|
1409
|
+
optional_input << value
|
1410
|
+
end
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
# MODIFY INPUT args count as OUTPUT as well
|
1414
|
+
if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
|
1415
|
+
((arg_flags & ARGUMENT_INPUT) != 0 &&
|
1416
|
+
(arg_flags & ARGUMENT_MODIFY) != 0)
|
1417
|
+
if (arg_flags & ARGUMENT_REQUIRED) != 0
|
1418
|
+
required_output << value
|
1419
|
+
else
|
1420
|
+
optional_output << value
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
print "# @!method "
|
1427
|
+
print "self." unless member_x
|
1428
|
+
print "#{nickname}("
|
1429
|
+
print required_input.map{|x| x[:name]}.join(", ")
|
1430
|
+
print ", " if required_input.length > 0
|
1431
|
+
puts "opts = {})"
|
1432
|
+
|
1433
|
+
puts "# #{description.capitalize}."
|
1434
|
+
|
1435
|
+
required_input.each do |arg|
|
1436
|
+
puts "# @param #{arg[:name]} [#{arg[:type_name]}] " +
|
1437
|
+
"#{arg[:blurb]}"
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
puts "# @param opts [Hash] Set of options"
|
1441
|
+
optional_input.each do |arg|
|
1442
|
+
puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
|
1443
|
+
"#{arg[:blurb]}"
|
1444
|
+
end
|
1445
|
+
optional_output.each do |arg|
|
1446
|
+
print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
|
1447
|
+
puts " Output #{arg[:blurb]}"
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
print "# @return ["
|
1451
|
+
if required_output.length == 0
|
1452
|
+
print "nil"
|
1453
|
+
elsif required_output.length == 1
|
1454
|
+
print required_output.first[:type_name]
|
1455
|
+
elsif
|
1456
|
+
print "Array<"
|
1457
|
+
print required_output.map{|x| x[:type_name]}.join(", ")
|
1458
|
+
print ">"
|
1459
|
+
end
|
1460
|
+
if optional_output.length > 0
|
1461
|
+
print ", Hash<Symbol => Object>"
|
1462
|
+
end
|
1463
|
+
print "] "
|
1464
|
+
print required_output.map{|x| x[:blurb]}.join(", ")
|
1465
|
+
if optional_output.length > 0
|
1466
|
+
print ", " if required_output.length > 0
|
1467
|
+
print "Hash of optional output items"
|
1468
|
+
end
|
1469
|
+
puts ""
|
1470
|
+
|
1471
|
+
puts ""
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
generate_class = lambda do |gtype, a|
|
1475
|
+
nickname = Vips::nickname_find gtype
|
1476
|
+
|
1477
|
+
if nickname
|
1478
|
+
begin
|
1479
|
+
# can fail for abstract types
|
1480
|
+
op = Vips::Operation.new nickname
|
1481
|
+
rescue
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
generate_operation.(gtype, nickname, op) if op
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
Vips::vips_type_map gtype, generate_class, nil
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
puts "module Vips"
|
1491
|
+
puts " class Image"
|
1492
|
+
puts ""
|
1493
|
+
|
1494
|
+
generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
|
1495
|
+
|
1496
|
+
puts " end"
|
1497
|
+
puts "end"
|
1498
|
+
end
|
1499
|
+
|
1500
|
+
end
|