diff --git a/python/packages/gi/overrides/Vips.py b/python/packages/gi/overrides/Vips.py index 1ec3e333..2da074d0 100644 --- a/python/packages/gi/overrides/Vips.py +++ b/python/packages/gi/overrides/Vips.py @@ -1,5 +1,5 @@ # -*- Mode: Python; py-indent-offset: 4 -*- -# vim: tabstop=4 shiftwidth=4 +# vim: tabstop=4 shiftwidth=4 expandtab from __future__ import division @@ -8,8 +8,8 @@ from __future__ import division # copy this file to dist-packages/gi/overrides, eg. # -# sudo cp Vips.py /usr/lib/python2.7/dist-packages/gi/overrides -# sudo cp Vips.py /usr/lib/python3/dist-packages/gi/overrides +# sudo cp Vips.py /usr/lib/python2.7/dist-packages/gi/overrides +# sudo cp Vips.py /usr/lib/python3/dist-packages/gi/overrides # # Alternatively, build vips to another prefix, then copy Vips.py and Vips.pyc # from $prefix/lib/python2.7/dist-packages/gi/overrides to /usr @@ -28,7 +28,7 @@ from __future__ import division # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk @@ -61,1004 +61,1004 @@ vips_type_operation = GObject.GType.from_name("VipsOperation") vips_type_ref_string = GObject.GType.from_name("VipsRefString") def is_2D(value): - if not isinstance(value, list): - return False + if not isinstance(value, list): + return False - for x in value: - if not isinstance(x, list): - return False + for x in value: + if not isinstance(x, list): + return False - if len(x) != len(value[0]): - return False + if len(x) != len(value[0]): + return False - return True + return True def imageize(match_image, value): - logger.debug('imageize match_image=%s, value=%s' % (match_image, value)) + logger.debug('imageize match_image=%s, value=%s' % (match_image, value)) - # 2D arrays become array images - if is_2D(value): - return Vips.Image.new_from_array(value) + # 2D arrays become array images + if is_2D(value): + return Vips.Image.new_from_array(value) - # if there's nothing to match to, also make an array - if match_image is None: - return Vips.Image.new_from_array(value) + # if there's nothing to match to, also make an array + if match_image is None: + return Vips.Image.new_from_array(value) - # assume this is a pixel constant ... expand into an image using - # match as a template - pixel = (Vips.Image.black(1, 1) + value).cast(match_image.format) - image = pixel.embed(0, 0, match_image.width, match_image.height, - extend = Vips.Extend.COPY) - image = image.copy(interpretation = match_image.interpretation, - xres = match_image.xres, - yres = match_image.yres) - return image + # assume this is a pixel constant ... expand into an image using + # match as a template + pixel = (Vips.Image.black(1, 1) + value).cast(match_image.format) + image = pixel.embed(0, 0, match_image.width, match_image.height, + extend = Vips.Extend.COPY) + image = image.copy(interpretation = match_image.interpretation, + xres = match_image.xres, + yres = match_image.yres) + return image # we'd like to use memoryview to avoid copying things like ICC profiles, but # unfortunately pygobject does not support this ... so for blobs we just use # bytes(). unpack_types = [[Vips.Blob, lambda x: bytes(x.get())], - [Vips.RefString, lambda x: x.get()], - [Vips.ArrayDouble, lambda x: x.get()], - [Vips.ArrayImage, lambda x: x.get()], - [Vips.ArrayInt, lambda x: x.get()]] + [Vips.RefString, lambda x: x.get()], + [Vips.ArrayDouble, lambda x: x.get()], + [Vips.ArrayImage, lambda x: x.get()], + [Vips.ArrayInt, lambda x: x.get()]] def unpack(value): - for t, cast in unpack_types: - if isinstance(value, t): - return cast(value) + for t, cast in unpack_types: + if isinstance(value, t): + return cast(value) - return value + return value def array_image_new(array): - match_image = next((x for x in array if isinstance(x, Vips.Image)), None) - if match_image is None: - raise Error('Unable to make image array argument.', - 'Array must contain at least one image.') + match_image = next((x for x in array if isinstance(x, Vips.Image)), None) + if match_image is None: + raise Error('Unable to make image array argument.', + 'Array must contain at least one image.') - for i in range(0, len(array)): - if not isinstance(array[i], Vips.Image): - array[i] = imageize(match_image, array[i]) + for i in range(0, len(array)): + if not isinstance(array[i], Vips.Image): + array[i] = imageize(match_image, array[i]) - return Vips.ArrayImage.new(array) + return Vips.ArrayImage.new(array) arrayize_types = [[vips_type_array_int, Vips.ArrayInt.new], - [vips_type_array_double, Vips.ArrayDouble.new], - [vips_type_array_image, array_image_new]] + [vips_type_array_double, Vips.ArrayDouble.new], + [vips_type_array_image, array_image_new]] def arrayize(gtype, value): - for t, cast in arrayize_types: - if GObject.type_is_a(gtype, t): - if not isinstance(value, list): - value = [value] - return cast(value) + for t, cast in arrayize_types: + if GObject.type_is_a(gtype, t): + if not isinstance(value, list): + value = [value] + return cast(value) - return value + return value def run_cmplx(fn, image): - """Run a complex function on a non-complex image. + """Run a complex function on a non-complex image. - The image needs to be complex, or have an even number of bands. The input - can be int, the output is always float or double. - """ - original_format = image.format + The image needs to be complex, or have an even number of bands. The input + can be int, the output is always float or double. + """ + original_format = image.format - if not Vips.band_format_iscomplex(image.format): - if image.bands % 2 != 0: - raise "not an even number of bands" + if not Vips.band_format_iscomplex(image.format): + if image.bands % 2 != 0: + raise "not an even number of bands" - if not Vips.band_format_isfloat(image.format): - image = image.cast(Vips.BandFormat.FLOAT) + if not Vips.band_format_isfloat(image.format): + image = image.cast(Vips.BandFormat.FLOAT) - if image.format == Vips.BandFormat.DOUBLE: - new_format = Vips.BandFormat.DPCOMPLEX - else: - new_format = Vips.BandFormat.COMPLEX + if image.format == Vips.BandFormat.DOUBLE: + new_format = Vips.BandFormat.DPCOMPLEX + else: + new_format = Vips.BandFormat.COMPLEX - image = image.copy(format = new_format, bands = image.bands / 2) + image = image.copy(format = new_format, bands = image.bands / 2) - image = fn(image) + image = fn(image) - if not Vips.band_format_iscomplex(original_format): - if image.format == Vips.BandFormat.DPCOMPLEX: - new_format = Vips.BandFormat.DOUBLE - else: - new_format = Vips.BandFormat.FLOAT + if not Vips.band_format_iscomplex(original_format): + if image.format == Vips.BandFormat.DPCOMPLEX: + new_format = Vips.BandFormat.DOUBLE + else: + new_format = Vips.BandFormat.FLOAT - image = image.copy(format = new_format, bands = image.bands * 2) + image = image.copy(format = new_format, bands = image.bands * 2) - return image + return image class Error(Exception): - """An error from vips. + """An error from vips. - message -- a high-level description of the error - detail -- a string with some detailed diagnostics - """ - def __init__(self, message, detail = None): - self.message = message - if detail == None or detail == "": - detail = Vips.error_buffer() - Vips.error_clear() - self.detail = detail + message -- a high-level description of the error + detail -- a string with some detailed diagnostics + """ + def __init__(self, message, detail = None): + self.message = message + if detail == None or detail == "": + detail = Vips.error_buffer() + Vips.error_clear() + self.detail = detail - logger.debug('Error %s %s', self.message, self.detail) + logger.debug('Error %s %s', self.message, self.detail) - def __str__(self): - return '%s\n %s' % (self.message, self.detail) + def __str__(self): + return '%s\n %s' % (self.message, self.detail) Vips.Error = Error class Argument(object): - def __init__(self, op, prop): - self.op = op - self.prop = prop - self.name = re.sub("-", "_", prop.name) - self.flags = op.get_argument_flags(self.name) - self.priority = op.get_argument_priority(self.name) - self.isset = op.argument_isset(self.name) + def __init__(self, op, prop): + self.op = op + self.prop = prop + self.name = re.sub("-", "_", prop.name) + self.flags = op.get_argument_flags(self.name) + self.priority = op.get_argument_priority(self.name) + self.isset = op.argument_isset(self.name) - def set_value(self, match_image, value): - logger.debug('assigning %s to %s' % (value, self.name)) - logger.debug('%s needs a %s' % (self.name, self.prop.value_type)) + def set_value(self, match_image, value): + logger.debug('assigning %s to %s' % (value, self.name)) + logger.debug('%s needs a %s' % (self.name, self.prop.value_type)) - # blob-ize - if GObject.type_is_a(self.prop.value_type, vips_type_blob): - if not isinstance(value, Vips.Blob): - value = Vips.Blob.new(None, value) + # blob-ize + if GObject.type_is_a(self.prop.value_type, vips_type_blob): + if not isinstance(value, Vips.Blob): + value = Vips.Blob.new(None, value) - # image-ize - if GObject.type_is_a(self.prop.value_type, vips_type_image): - if not isinstance(value, Vips.Image): - value = imageize(match_image, value) + # image-ize + if GObject.type_is_a(self.prop.value_type, vips_type_image): + if not isinstance(value, Vips.Image): + value = imageize(match_image, value) - # array-ize some types, if necessary - value = arrayize(self.prop.value_type, value) + # array-ize some types, if necessary + value = arrayize(self.prop.value_type, value) - # MODIFY input images need to be copied before assigning them - if self.flags & Vips.ArgumentFlags.MODIFY: - # don't use .copy(): we want to make a new pipeline with no - # reference back to the old stuff ... this way we can free the - # previous image earlier - logger.debug('MODIFY argument: copying image') - new_image = Vips.Image.new_memory() - value.write(new_image) - value = new_image + # MODIFY input images need to be copied before assigning them + if self.flags & Vips.ArgumentFlags.MODIFY: + # don't use .copy(): we want to make a new pipeline with no + # reference back to the old stuff ... this way we can free the + # previous image earlier + logger.debug('MODIFY argument: copying image') + new_image = Vips.Image.new_memory() + value.write(new_image) + value = new_image - logger.debug('assigning %s' % value) + logger.debug('assigning %s' % value) - self.op.props.__setattr__(self.name, value) + self.op.props.__setattr__(self.name, value) - def get_value(self): - value = self.op.props.__getattribute__(self.name) + def get_value(self): + value = self.op.props.__getattribute__(self.name) - logger.debug('read out %s from %s' % (value, self.name)) + logger.debug('read out %s from %s' % (value, self.name)) - return unpack(value) + return unpack(value) - def description(self): - result = self.name - result += " " * (10 - len(self.name)) + " -- " + self.prop.blurb - result += ", " + self.prop.value_type.name + def description(self): + result = self.name + result += " " * (10 - len(self.name)) + " -- " + self.prop.blurb + result += ", " + self.prop.value_type.name - return result + return result Vips.Argument = Argument class Operation(Vips.Operation): - # find all the args for this op, sort into priority order - # we leave deprecated args in this list: for compatibility, we want users - # to be able to set them - # if you are (for example) generating docs, you'll need to filter out the - # deprecated args yourself - def get_args(self): - args = [Argument(self, x) for x in self.props] - args.sort(key = lambda x: x.priority) + # find all the args for this op, sort into priority order + # we leave deprecated args in this list: for compatibility, we want users + # to be able to set them + # if you are (for example) generating docs, you'll need to filter out the + # deprecated args yourself + def get_args(self): + args = [Argument(self, x) for x in self.props] + args.sort(key = lambda x: x.priority) - return args + return args Operation = override(Operation) __all__.append('Operation') # search a list recursively for a Vips.Image object def find_image(x): - if isinstance(x, Vips.Image): - return x - if isinstance(x, list): - for i in x: - y = find_image(i) - if y is not None: - return y - return None + if isinstance(x, Vips.Image): + return x + if isinstance(x, list): + for i in x: + y = find_image(i) + if y is not None: + return y + return None def _call_base(name, required, optional, self = None, option_string = None): - logger.debug('_call_base name=%s, required=%s optional=%s' % - (name, required, optional)) - if self: - logger.debug('_call_base self=%s' % self) - if option_string: - logger.debug('_call_base option_string = %s' % option_string) + logger.debug('_call_base name=%s, required=%s optional=%s' % + (name, required, optional)) + if self: + logger.debug('_call_base self=%s' % self) + if option_string: + logger.debug('_call_base option_string = %s' % option_string) - try: - op = Vips.Operation.new(name) - except TypeError as e: - raise Error('No such operator.') - if op.get_flags() & Vips.OperationFlags.DEPRECATED: - raise Error('No such operator.', 'operator "%s" is deprecated' % name) + try: + op = Vips.Operation.new(name) + except TypeError as e: + raise Error('No such operator.') + if op.get_flags() & Vips.OperationFlags.DEPRECATED: + raise Error('No such operator.', 'operator "%s" is deprecated' % name) - # set str options first so the user can't override things we set - # deliberately and break stuff - if option_string: - if op.set_from_string(option_string) != 0: - raise Error('Bad arguments.') + # set str options first so the user can't override things we set + # deliberately and break stuff + if option_string: + if op.set_from_string(option_string) != 0: + raise Error('Bad arguments.') - args = op.get_args() + args = op.get_args() - enm = Vips.ArgumentFlags + enm = Vips.ArgumentFlags - # find all required, unassigned, undeprecated input args - required_input = [x for x in args if x.flags & enm.INPUT and - x.flags & enm.REQUIRED and - not x.flags & enm.DEPRECATED and - not x.isset] + # find all required, unassigned, undeprecated input args + required_input = [x for x in args if x.flags & enm.INPUT and + x.flags & enm.REQUIRED and + not x.flags & enm.DEPRECATED and + not x.isset] - # do we have a non-None self pointer? this is used to set the first - # compatible input arg - if self is not None: - found = False - for x in required_input: - if GObject.type_is_a(self, x.prop.value_type): - x.set_value(None, self) - required_input.remove(x) - found = True - break + # do we have a non-None self pointer? this is used to set the first + # compatible input arg + if self is not None: + found = False + for x in required_input: + if GObject.type_is_a(self, x.prop.value_type): + x.set_value(None, self) + required_input.remove(x) + found = True + break - if not found: - raise Error('Bad arguments.', 'No %s argument to %s.' % - (str(self.__class__), name)) + if not found: + raise Error('Bad arguments.', 'No %s argument to %s.' % + (str(self.__class__), name)) - if len(required_input) != len(required): - raise Error('Wrong number of arguments.', - '%s needs %d arguments, you supplied %d.' % - (name, len(required_input), len(required))) + if len(required_input) != len(required): + raise Error('Wrong number of arguments.', + '%s needs %d arguments, you supplied %d.' % + (name, len(required_input), len(required))) - # if we need an image arg but the user supplied a number or list of - # numbers, we expand it into an image automatically ... the number is - # expanded to match self, or if that's None, the first image we can find in - # the required or optional arguments - match_image = self - if match_image is None: - for arg in required: - match_image = find_image(arg) - if match_image is not None: - break + # if we need an image arg but the user supplied a number or list of + # numbers, we expand it into an image automatically ... the number is + # expanded to match self, or if that's None, the first image we can find in + # the required or optional arguments + match_image = self + if match_image is None: + for arg in required: + match_image = find_image(arg) + if match_image is not None: + break - if match_image is None: - for arg_name in optional: - match_image = find_image(optional[arg_name]) - if match_image is not None: - break + if match_image is None: + for arg_name in optional: + match_image = find_image(optional[arg_name]) + if match_image is not None: + break - for i in range(len(required_input)): - required_input[i].set_value(match_image, required[i]) + for i in range(len(required_input)): + required_input[i].set_value(match_image, required[i]) - # find all optional, unassigned input args ... make a hash from name to - # Argument - # we let deprecated ones through, we want to allow assigment to them for - # compat - optional_input = {x.name: x for x in args if x.flags & enm.INPUT and - not x.flags & enm.REQUIRED and - not x.isset} + # find all optional, unassigned input args ... make a hash from name to + # Argument + # we let deprecated ones through, we want to allow assigment to them for + # compat + optional_input = {x.name: x for x in args if x.flags & enm.INPUT and + not x.flags & enm.REQUIRED and + not x.isset} - # find all optional output args ... we use "x = True" - # in args to mean add that to output - optional_output = {x.name: x for x in args if x.flags & enm.OUTPUT and - not x.flags & enm.REQUIRED} + # find all optional output args ... we use "x = True" + # in args to mean add that to output + optional_output = {x.name: x for x in args if x.flags & enm.OUTPUT and + not x.flags & enm.REQUIRED} - # set optional input args - for key in list(optional.keys()): - if key in optional_input: - optional_input[key].set_value(match_image, optional[key]) - elif key in optional_output: - # must be a literal True value - if optional[key] is not True: - raise Error('Optional output argument must be True.', - 'Argument %s should equal True.' % key) - else: - raise Error('Unknown argument.', - 'Operator %s has no argument %s.' % (name, key)) + # set optional input args + for key in list(optional.keys()): + if key in optional_input: + optional_input[key].set_value(match_image, optional[key]) + elif key in optional_output: + # must be a literal True value + if optional[key] is not True: + raise Error('Optional output argument must be True.', + 'Argument %s should equal True.' % key) + else: + raise Error('Unknown argument.', + 'Operator %s has no argument %s.' % (name, key)) - # call - logger.debug('_call_base checking cache for op %s' % op) - op2 = Vips.cache_operation_build(op) - logger.debug('_call_base got op2 %s' % op2) - if op2 == None: - raise Error('Error calling operator %s.' % name) + # call + logger.debug('_call_base checking cache for op %s' % op) + op2 = Vips.cache_operation_build(op) + logger.debug('_call_base got op2 %s' % op2) + if op2 == None: + raise Error('Error calling operator %s.' % name) - # rescan args if op2 is different from op - if op2 != op: - logger.debug('_call_base rescanning args') - args = op2.get_args() - optional_output = {x.name: x for x in args if x.flags & enm.OUTPUT and - not x.flags & enm.REQUIRED} + # rescan args if op2 is different from op + if op2 != op: + logger.debug('_call_base rescanning args') + args = op2.get_args() + optional_output = {x.name: x for x in args if x.flags & enm.OUTPUT and + not x.flags & enm.REQUIRED} - # gather output args - logger.debug('_call_base fetching required output args') - out = [] + # gather output args + logger.debug('_call_base fetching required output args') + out = [] - for x in args: - # required non-deprecated output arg - if x.flags & enm.OUTPUT and x.flags & enm.REQUIRED and not x.flags & enm.DEPRECATED: - out.append(x.get_value()) + for x in args: + # required non-deprecated output arg + if x.flags & enm.OUTPUT and x.flags & enm.REQUIRED and not x.flags & enm.DEPRECATED: + out.append(x.get_value()) - # modified input arg ... this will get the memory image we made above - if x.flags & enm.INPUT and x.flags & enm.MODIFY: - out.append(x.get_value()) + # modified input arg ... this will get the memory image we made above + if x.flags & enm.INPUT and x.flags & enm.MODIFY: + out.append(x.get_value()) - logger.debug('_call_base fetching optional output args') - out_dict = {} - for x in list(optional.keys()): - if x in optional_output: - out_dict[x] = optional_output[x].get_value() - if out_dict != {}: - out.append(out_dict) + logger.debug('_call_base fetching optional output args') + out_dict = {} + for x in list(optional.keys()): + if x in optional_output: + out_dict[x] = optional_output[x].get_value() + if out_dict != {}: + out.append(out_dict) - if len(out) == 1: - out = out[0] - elif len(out) == 0: - out = None + if len(out) == 1: + out = out[0] + elif len(out) == 0: + out = None - # unref everything now we have refs to all outputs we want - op2.unref_outputs() + # unref everything now we have refs to all outputs we want + op2.unref_outputs() - logger.debug('success') + logger.debug('success') - return out + return out # general user entrypoint def call(name, *args, **kwargs): - return _call_base(name, args, kwargs) + return _call_base(name, args, kwargs) Vips.call = call # here from getattr ... try to run the attr as a method def _call_instance(self, name, args, kwargs): - return _call_base(name, args, kwargs, self) + return _call_base(name, args, kwargs, self) @classmethod def vips_image_new_from_file(cls, vips_filename, **kwargs): - """Create a new Image from a filename. + """Create a new Image from a filename. - Extra optional arguments depend on the loader selected by libvips. See each - loader for details. - """ - filename = Vips.filename_get_filename(vips_filename) - option_string = Vips.filename_get_options(vips_filename) - loader = Vips.Foreign.find_load(filename) - if loader == None: - raise Error('No known loader for "%s".' % filename) - logger.debug('Image.new_from_file: loader = %s' % loader) + Extra optional arguments depend on the loader selected by libvips. See each + loader for details. + """ + filename = Vips.filename_get_filename(vips_filename) + option_string = Vips.filename_get_options(vips_filename) + loader = Vips.Foreign.find_load(filename) + if loader == None: + raise Error('No known loader for "%s".' % filename) + logger.debug('Image.new_from_file: loader = %s' % loader) - return _call_base(loader, [filename], kwargs, None, option_string) + return _call_base(loader, [filename], kwargs, None, option_string) setattr(Vips.Image, 'new_from_file', vips_image_new_from_file) @classmethod def vips_image_new_from_buffer(cls, data, option_string, **kwargs): - """Create a new Image from binary data in a string. + """Create a new Image from binary data in a string. - data -- binary image data - option_string -- optional arguments in string form + data -- binary image data + option_string -- optional arguments in string form - option_string can be something like "page=10" to load the 10th page of a - tiff file. You can also give load options as keyword arguments. - """ - loader = Vips.Foreign.find_load_buffer(data) - if loader == None: - raise Error('No known loader for buffer.') - logger.debug('Image.new_from_buffer: loader = %s' % loader) + option_string can be something like "page=10" to load the 10th page of a + tiff file. You can also give load options as keyword arguments. + """ + loader = Vips.Foreign.find_load_buffer(data) + if loader == None: + raise Error('No known loader for buffer.') + logger.debug('Image.new_from_buffer: loader = %s' % loader) - return _call_base(loader, [data], kwargs, None, option_string) + return _call_base(loader, [data], kwargs, None, option_string) setattr(Vips.Image, 'new_from_buffer', vips_image_new_from_buffer) @classmethod def vips_image_new_from_array(cls, array, scale = 1, offset = 0): - """Create a new image from an array. + """Create a new image from an array. - The array argument can be a 1D array to create a height == 1 image, or a 2D - array to make a 2D image. Use scale and offset to set the scale factor, - handy for integer convolutions. - """ - # we accept a 1D array and assume height == 1, or a 2D array and check all - # lines are the same length - if not isinstance(array, list): - raise TypeError('new_from_array() takes a list argument') - if not isinstance(array[0], list): - height = 1 - width = len(array) - else: - # must copy the first row, we don't want to modify the passed-in array - flat_array = list(array[0]) - height = len(array) - width = len(array[0]) - for i in range(1, height): - if len(array[i]) != width: - raise TypeError('new_from_array() array not rectangular') - flat_array += array[i] - array = flat_array + The array argument can be a 1D array to create a height == 1 image, or a 2D + array to make a 2D image. Use scale and offset to set the scale factor, + handy for integer convolutions. + """ + # we accept a 1D array and assume height == 1, or a 2D array and check all + # lines are the same length + if not isinstance(array, list): + raise TypeError('new_from_array() takes a list argument') + if not isinstance(array[0], list): + height = 1 + width = len(array) + else: + # must copy the first row, we don't want to modify the passed-in array + flat_array = list(array[0]) + height = len(array) + width = len(array[0]) + for i in range(1, height): + if len(array[i]) != width: + raise TypeError('new_from_array() array not rectangular') + flat_array += array[i] + array = flat_array - image = cls.new_matrix_from_array(width, height, array) + image = cls.new_matrix_from_array(width, height, array) - # be careful to set them as double - image.set('scale', float(scale)) - image.set('offset', float(offset)) + # be careful to set them as double + image.set('scale', float(scale)) + image.set('offset', float(offset)) - return image + return image setattr(Vips.Image, 'new_from_array', vips_image_new_from_array) def generate_docstring(name): - try: - op = Vips.Operation.new(name) - except TypeError as e: - raise Error('No such operator.') - if op.get_flags() & Vips.OperationFlags.DEPRECATED: - raise Error('No such operator.', 'operator "%s" is deprecated' % name) + try: + op = Vips.Operation.new(name) + except TypeError as e: + raise Error('No such operator.') + if op.get_flags() & Vips.OperationFlags.DEPRECATED: + raise Error('No such operator.', 'operator "%s" is deprecated' % name) - # find all the args for this op, sort into priority order - args = op.get_args() + # find all the args for this op, sort into priority order + args = op.get_args() - # we are only interested in non-deprecated args - args = [y for y in args - if not y.flags & Vips.ArgumentFlags.DEPRECATED] + # we are only interested in non-deprecated args + args = [y for y in args + if not y.flags & Vips.ArgumentFlags.DEPRECATED] - enm = Vips.ArgumentFlags + enm = Vips.ArgumentFlags - # find all required, unassigned input args - required_input = [x for x in args if x.flags & enm.INPUT and - x.flags & enm.REQUIRED and - not x.isset] + # find all required, unassigned input args + required_input = [x for x in args if x.flags & enm.INPUT and + x.flags & enm.REQUIRED and + not x.isset] - optional_input = [x for x in args if x.flags & enm.INPUT and - not x.flags & enm.REQUIRED and - not x.isset] + optional_input = [x for x in args if x.flags & enm.INPUT and + not x.flags & enm.REQUIRED and + not x.isset] - required_output = [x for x in args if x.flags & enm.OUTPUT and - x.flags & enm.REQUIRED] + required_output = [x for x in args if x.flags & enm.OUTPUT and + x.flags & enm.REQUIRED] - optional_output = [x for x in args if x.flags & enm.OUTPUT and - not x.flags & enm.REQUIRED] + optional_output = [x for x in args if x.flags & enm.OUTPUT and + not x.flags & enm.REQUIRED] - # find the first required input image, if any ... we will be a member - # function of this instance - member_x = None - for i in range(0, len(required_input)): - x = required_input[i] - if GObject.type_is_a(vips_type_image, x.prop.value_type): - member_x = x - break + # find the first required input image, if any ... we will be a member + # function of this instance + member_x = None + for i in range(0, len(required_input)): + x = required_input[i] + if GObject.type_is_a(vips_type_image, x.prop.value_type): + member_x = x + break - description = op.get_description() - result = description[0].upper() + description[1:] + ".\n\n" - result += "Usage:\n" + description = op.get_description() + result = description[0].upper() + description[1:] + ".\n\n" + result += "Usage:\n" - result += " " + ", ".join([x.name for x in required_output]) + " = " - if member_x: - result += member_x.name + "." + name + "(" - else: - result += "Vips.Image." + name + "(" + result += " " + ", ".join([x.name for x in required_output]) + " = " + if member_x: + result += member_x.name + "." + name + "(" + else: + result += "Vips.Image." + name + "(" - required_input_args = [x.name for x in required_input if x != member_x] - result += ", ".join(required_input_args) - if len(optional_input) > 0 and len(required_input_args) > 0: - result += ", " - result += ", ".join([x.name + " = " + x.prop.value_type.name - for x in optional_input]) - result += ")\n" + required_input_args = [x.name for x in required_input if x != member_x] + result += ", ".join(required_input_args) + if len(optional_input) > 0 and len(required_input_args) > 0: + result += ", " + result += ", ".join([x.name + " = " + x.prop.value_type.name + for x in optional_input]) + result += ")\n" - result += "Where:\n" - for x in required_output: - result += " " + x.description() + "\n" + result += "Where:\n" + for x in required_output: + result += " " + x.description() + "\n" - for x in required_input: - result += " " + x.description() + "\n" + for x in required_input: + result += " " + x.description() + "\n" - if len(optional_input) > 0: - result += "Keyword parameters:\n" - for x in optional_input: - result += " " + x.description() + "\n" + if len(optional_input) > 0: + result += "Keyword parameters:\n" + for x in optional_input: + result += " " + x.description() + "\n" - if len(optional_output) > 0: - result += "Extra output options:\n" - for x in optional_output: - result += " " + x.description() + "\n" + if len(optional_output) > 0: + result += "Extra output options:\n" + for x in optional_output: + result += " " + x.description() + "\n" - return result + return result # apply a function to a thing, or map over a list # we often need to do something like (1.0 / other) and need to work for lists # as well as scalars def smap(func, x): - if isinstance(x, list): - return list(map(func, x)) - else: - return func(x) + if isinstance(x, list): + return list(map(func, x)) + else: + return func(x) # decorator to set docstring def add_doc(value): - def _doc(func): - func.__doc__ = value - return func - return _doc + def _doc(func): + func.__doc__ = value + return func + return _doc class Image(Vips.Image): - # for constructors, see class methods above - - # output - - def write_to_file(self, vips_filename, **kwargs): - """Write an Image to a file. - - The filename can contain save options, for example - "fred.tif[compression=jpeg]", or save options can be given as keyword - arguments. Save options depend on the selected saver. - """ - filename = Vips.filename_get_filename(vips_filename) - option_string = Vips.filename_get_options(vips_filename) - saver = Vips.Foreign.find_save(filename) - if saver == None: - raise Error('No known saver for "%s".' % filename) - logger.debug('Image.write_to_file: saver = %s' % saver) - - _call_base(saver, [filename], kwargs, self, option_string) - - def write_to_buffer(self, format_string, **kwargs): - """Write an Image to memory. - - Return the image as a binary string, encoded in the selected format. - Save options can be given in the format_string, for example - ".jpg[Q=90]". Save options depend on the selected saver. - """ - filename = Vips.filename_get_filename(format_string) - option_string = Vips.filename_get_options(format_string) - saver = Vips.Foreign.find_save_buffer(filename) - if saver == None: - raise Error('No known saver for "%s".' % filename) - logger.debug('Image.write_to_buffer: saver = %s' % saver) - - return _call_base(saver, [], kwargs, self, option_string) - - # we can use Vips.Image.write_to_memory() directly - - # support with in the most trivial way - def __enter__(self): - return self - def __exit__(self, type, value, traceback): - pass - - # operator overloads - - def __getattr__(self, name): - logger.debug('Image.__getattr__ %s' % name) - - # look up in props first, eg. x.props.width - if name in dir(self.props): - return getattr(self.props, name) - - @add_doc(generate_docstring(name)) - def call_function(*args, **kwargs): - return _call_instance(self, name, args, kwargs) - - return call_function - - def __add__(self, other): - if isinstance(other, Vips.Image): - return self.add(other) - else: - return self.linear(1, other) - - def __radd__(self, other): - return self.__add__(other) - - def __sub__(self, other): - if isinstance(other, Vips.Image): - return self.subtract(other) - else: - return self.linear(1, smap(lambda x: -1 * x, other)) - - def __rsub__(self, other): - return self.linear(-1, other) - - def __mul__(self, other): - if isinstance(other, Vips.Image): - return self.multiply(other) - else: - return self.linear(other, 0) - - def __rmul__(self, other): - return self.__mul__(other) - - # a / const has always been a float in vips, so div and truediv are the - # same - def __div__(self, other): - if isinstance(other, Vips.Image): - return self.divide(other) - else: - return self.linear(smap(lambda x: 1.0 / x, other), 0) - - def __rdiv__(self, other): - return (self ** -1) * other - - def __truediv__(self, other): - return self.__div__(other) - - def __rtruediv__(self, other): - return self.__rdiv__(other) - - def __floordiv__(self, other): - if isinstance(other, Vips.Image): - return self.divide(other).floor() - else: - return self.linear(smap(lambda x: 1.0 / x, other), 0).floor() - - def __rfloordiv__(self, other): - return ((self ** -1) * other).floor() - - def __mod__(self, other): - if isinstance(other, Vips.Image): - return self.remainder(other) - else: - return self.remainder_const(other) - - def __pow__(self, other): - if isinstance(other, Vips.Image): - return self.math2(other, Vips.OperationMath2.POW) - else: - return self.math2_const(other, Vips.OperationMath2.POW) - - def __rpow__(self, other): - return self.math2_const(other, Vips.OperationMath2.WOP) - - def __abs__(self): - return self.abs() - - def __lshift__(self, other): - if isinstance(other, Vips.Image): - return self.boolean(other, Vips.OperationBoolean.LSHIFT) - else: - return self.boolean_const(other, Vips.OperationBoolean.LSHIFT) - - def __rshift__(self, other): - if isinstance(other, Vips.Image): - return self.boolean(other, Vips.OperationBoolean.RSHIFT) - else: - return self.boolean_const(other, Vips.OperationBoolean.RSHIFT) - - def __and__(self, other): - if isinstance(other, Vips.Image): - return self.boolean(other, Vips.OperationBoolean.AND) - else: - return self.boolean_const(other, Vips.OperationBoolean.AND) - - def __rand__(self, other): - return self.__and__(other) - - def __or__(self, other): - if isinstance(other, Vips.Image): - return self.boolean(other, Vips.OperationBoolean.OR) - else: - return self.boolean_const(other, Vips.OperationBoolean.OR) - - def __ror__(self, other): - return self.__or__(other) - - def __xor__(self, other): - if isinstance(other, Vips.Image): - return self.boolean(other, Vips.OperationBoolean.EOR) - else: - return self.boolean_const(other, Vips.OperationBoolean.EOR) - - def __rxor__(self, other): - return self.__xor__(other) - - def __neg__(self): - return -1 * self - - def __pos__(self): - return self - - def __invert__(self): - return self ^ -1 - - def __gt__(self, other): - if isinstance(other, Vips.Image): - return self.relational(other, Vips.OperationRelational.MORE) - else: - return self.relational_const(other, Vips.OperationRelational.MORE) - - def __ge__(self, other): - if isinstance(other, Vips.Image): - return self.relational(other, Vips.OperationRelational.MOREEQ) - else: - return self.relational_const(other, Vips.OperationRelational.MOREEQ) - - def __lt__(self, other): - if isinstance(other, Vips.Image): - return self.relational(other, Vips.OperationRelational.LESS) - else: - return self.relational_const(other, Vips.OperationRelational.LESS) - - def __le__(self, other): - if isinstance(other, Vips.Image): - return self.relational(other, Vips.OperationRelational.LESSEQ) - else: - return self.relational_const(other, Vips.OperationRelational.LESSEQ) - - def __eq__(self, other): - # for == and != we need to allow comparison to None - if isinstance(other, Vips.Image): - return self.relational(other, Vips.OperationRelational.EQUAL) - elif isinstance(other, list): - return self.relational_const(other, Vips.OperationRelational.EQUAL) - elif isinstance(other, numbers.Number): - return self.relational_const(other, Vips.OperationRelational.EQUAL) - else: - return False - - def __ne__(self, other): - if isinstance(other, Vips.Image): - return self.relational(other, Vips.OperationRelational.NOTEQ) - elif isinstance(other, list): - return self.relational_const(other, Vips.OperationRelational.NOTEQ) - elif isinstance(other, numbers.Number): - return self.relational_const(other, Vips.OperationRelational.NOTEQ) - else: - return False - - def __getitem__(self, arg): - if isinstance(arg, slice): - i = 0 - if arg.start != None: - i = arg.start - - n = self.bands - i - if arg.stop != None: - if arg.stop < 0: - n = self.bands + arg.stop - i - else: - n = arg.stop - i - elif isinstance(arg, int): - i = arg - n = 1 - else: - raise TypeError - - if i < 0: - i = self.bands + i - - if i < 0 or i >= self.bands: - raise IndexError - - return self.extract_band(i, n = n) - - def __call__(self, x, y): - return self.getpoint(x, y) - - # the cast operators int(), long() and float() must return numeric types, - # so we can't define them for images - - # a few useful things - - def get_value(self, field): - """Get a named item from an Image. - - Fetch an item of metadata and convert it to a Python-friendly format. - For example, VipsBlob values will be converted to bytes(). - """ - value = self.get(field) - - logger.debug('read out %s from %s' % (value, self)) - - return unpack(value) - - def set_value(self, field, value): - """Set a named item on an Image. - - Values are converted from Python types to something libvips can swallow. - For example, bytes() can be used to set VipsBlob fields. - """ - gtype = self.get_typeof(field) - logger.debug('%s.%s = %s' % (self, field, value)) - logger.debug('%s.%s needs a %s' % (self, field, gtype)) - - # if there's a thing of this name already, convert to that type - if gtype != GObject.TYPE_INVALID: - # blob-ize - if GObject.type_is_a(gtype, vips_type_blob): - if not isinstance(value, Vips.Blob): - value = Vips.Blob.new(None, value) - - # image-ize - if GObject.type_is_a(gtype, vips_type_image): - if not isinstance(value, Vips.Image): - value = imageize(self, value) - - # array-ize some types, if necessary - value = arrayize(gtype, value) - - self.set(field, value) - - def floor(self): - """Return the largest integral value not greater than the argument.""" - return self.round(Vips.OperationRound.FLOOR) - - def ceil(self): - """Return the smallest integral value not less than the argument.""" - return self.round(Vips.OperationRound.CEIL) - - def rint(self): - """Return the nearest integral value.""" - return self.round(Vips.OperationRound.RINT) - - def bandand(self): - """AND image bands together.""" - return self.bandbool(Vips.OperationBoolean.AND) - - def bandor(self): - """OR image bands together.""" - return self.bandbool(Vips.OperationBoolean.OR) - - def bandeor(self): - """EOR image bands together.""" - return self.bandbool(Vips.OperationBoolean.EOR) - - def bandsplit(self): - """Split an n-band image into n separate images.""" - return [x for x in self] - - def bandjoin(self, other): - """Append a set of images or constants bandwise.""" - if not isinstance(other, list): - other = [other] - - # if [other] is all numbers, we can use bandjoin_const - non_number = next((x for x in other - if not isinstance(x, numbers.Number)), - None) - - if non_number == None: - return self.bandjoin_const(other) - else: - return _call_base("bandjoin", [[self] + other], {}) + # for constructors, see class methods above + + # output + + def write_to_file(self, vips_filename, **kwargs): + """Write an Image to a file. + + The filename can contain save options, for example + "fred.tif[compression=jpeg]", or save options can be given as keyword + arguments. Save options depend on the selected saver. + """ + filename = Vips.filename_get_filename(vips_filename) + option_string = Vips.filename_get_options(vips_filename) + saver = Vips.Foreign.find_save(filename) + if saver == None: + raise Error('No known saver for "%s".' % filename) + logger.debug('Image.write_to_file: saver = %s' % saver) + + _call_base(saver, [filename], kwargs, self, option_string) + + def write_to_buffer(self, format_string, **kwargs): + """Write an Image to memory. + + Return the image as a binary string, encoded in the selected format. + Save options can be given in the format_string, for example + ".jpg[Q=90]". Save options depend on the selected saver. + """ + filename = Vips.filename_get_filename(format_string) + option_string = Vips.filename_get_options(format_string) + saver = Vips.Foreign.find_save_buffer(filename) + if saver == None: + raise Error('No known saver for "%s".' % filename) + logger.debug('Image.write_to_buffer: saver = %s' % saver) + + return _call_base(saver, [], kwargs, self, option_string) + + # we can use Vips.Image.write_to_memory() directly + + # support with in the most trivial way + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + pass + + # operator overloads + + def __getattr__(self, name): + logger.debug('Image.__getattr__ %s' % name) + + # look up in props first, eg. x.props.width + if name in dir(self.props): + return getattr(self.props, name) + + @add_doc(generate_docstring(name)) + def call_function(*args, **kwargs): + return _call_instance(self, name, args, kwargs) + + return call_function + + def __add__(self, other): + if isinstance(other, Vips.Image): + return self.add(other) + else: + return self.linear(1, other) + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + if isinstance(other, Vips.Image): + return self.subtract(other) + else: + return self.linear(1, smap(lambda x: -1 * x, other)) + + def __rsub__(self, other): + return self.linear(-1, other) + + def __mul__(self, other): + if isinstance(other, Vips.Image): + return self.multiply(other) + else: + return self.linear(other, 0) + + def __rmul__(self, other): + return self.__mul__(other) + + # a / const has always been a float in vips, so div and truediv are the + # same + def __div__(self, other): + if isinstance(other, Vips.Image): + return self.divide(other) + else: + return self.linear(smap(lambda x: 1.0 / x, other), 0) + + def __rdiv__(self, other): + return (self ** -1) * other + + def __truediv__(self, other): + return self.__div__(other) + + def __rtruediv__(self, other): + return self.__rdiv__(other) + + def __floordiv__(self, other): + if isinstance(other, Vips.Image): + return self.divide(other).floor() + else: + return self.linear(smap(lambda x: 1.0 / x, other), 0).floor() + + def __rfloordiv__(self, other): + return ((self ** -1) * other).floor() + + def __mod__(self, other): + if isinstance(other, Vips.Image): + return self.remainder(other) + else: + return self.remainder_const(other) + + def __pow__(self, other): + if isinstance(other, Vips.Image): + return self.math2(other, Vips.OperationMath2.POW) + else: + return self.math2_const(other, Vips.OperationMath2.POW) + + def __rpow__(self, other): + return self.math2_const(other, Vips.OperationMath2.WOP) + + def __abs__(self): + return self.abs() + + def __lshift__(self, other): + if isinstance(other, Vips.Image): + return self.boolean(other, Vips.OperationBoolean.LSHIFT) + else: + return self.boolean_const(other, Vips.OperationBoolean.LSHIFT) + + def __rshift__(self, other): + if isinstance(other, Vips.Image): + return self.boolean(other, Vips.OperationBoolean.RSHIFT) + else: + return self.boolean_const(other, Vips.OperationBoolean.RSHIFT) + + def __and__(self, other): + if isinstance(other, Vips.Image): + return self.boolean(other, Vips.OperationBoolean.AND) + else: + return self.boolean_const(other, Vips.OperationBoolean.AND) + + def __rand__(self, other): + return self.__and__(other) + + def __or__(self, other): + if isinstance(other, Vips.Image): + return self.boolean(other, Vips.OperationBoolean.OR) + else: + return self.boolean_const(other, Vips.OperationBoolean.OR) + + def __ror__(self, other): + return self.__or__(other) + + def __xor__(self, other): + if isinstance(other, Vips.Image): + return self.boolean(other, Vips.OperationBoolean.EOR) + else: + return self.boolean_const(other, Vips.OperationBoolean.EOR) + + def __rxor__(self, other): + return self.__xor__(other) + + def __neg__(self): + return -1 * self + + def __pos__(self): + return self + + def __invert__(self): + return self ^ -1 + + def __gt__(self, other): + if isinstance(other, Vips.Image): + return self.relational(other, Vips.OperationRelational.MORE) + else: + return self.relational_const(other, Vips.OperationRelational.MORE) + + def __ge__(self, other): + if isinstance(other, Vips.Image): + return self.relational(other, Vips.OperationRelational.MOREEQ) + else: + return self.relational_const(other, Vips.OperationRelational.MOREEQ) + + def __lt__(self, other): + if isinstance(other, Vips.Image): + return self.relational(other, Vips.OperationRelational.LESS) + else: + return self.relational_const(other, Vips.OperationRelational.LESS) + + def __le__(self, other): + if isinstance(other, Vips.Image): + return self.relational(other, Vips.OperationRelational.LESSEQ) + else: + return self.relational_const(other, Vips.OperationRelational.LESSEQ) + + def __eq__(self, other): + # for == and != we need to allow comparison to None + if isinstance(other, Vips.Image): + return self.relational(other, Vips.OperationRelational.EQUAL) + elif isinstance(other, list): + return self.relational_const(other, Vips.OperationRelational.EQUAL) + elif isinstance(other, numbers.Number): + return self.relational_const(other, Vips.OperationRelational.EQUAL) + else: + return False + + def __ne__(self, other): + if isinstance(other, Vips.Image): + return self.relational(other, Vips.OperationRelational.NOTEQ) + elif isinstance(other, list): + return self.relational_const(other, Vips.OperationRelational.NOTEQ) + elif isinstance(other, numbers.Number): + return self.relational_const(other, Vips.OperationRelational.NOTEQ) + else: + return False + + def __getitem__(self, arg): + if isinstance(arg, slice): + i = 0 + if arg.start != None: + i = arg.start + + n = self.bands - i + if arg.stop != None: + if arg.stop < 0: + n = self.bands + arg.stop - i + else: + n = arg.stop - i + elif isinstance(arg, int): + i = arg + n = 1 + else: + raise TypeError + + if i < 0: + i = self.bands + i + + if i < 0 or i >= self.bands: + raise IndexError + + return self.extract_band(i, n = n) + + def __call__(self, x, y): + return self.getpoint(x, y) + + # the cast operators int(), long() and float() must return numeric types, + # so we can't define them for images + + # a few useful things + + def get_value(self, field): + """Get a named item from an Image. + + Fetch an item of metadata and convert it to a Python-friendly format. + For example, VipsBlob values will be converted to bytes(). + """ + value = self.get(field) + + logger.debug('read out %s from %s' % (value, self)) + + return unpack(value) + + def set_value(self, field, value): + """Set a named item on an Image. + + Values are converted from Python types to something libvips can swallow. + For example, bytes() can be used to set VipsBlob fields. + """ + gtype = self.get_typeof(field) + logger.debug('%s.%s = %s' % (self, field, value)) + logger.debug('%s.%s needs a %s' % (self, field, gtype)) + + # if there's a thing of this name already, convert to that type + if gtype != GObject.TYPE_INVALID: + # blob-ize + if GObject.type_is_a(gtype, vips_type_blob): + if not isinstance(value, Vips.Blob): + value = Vips.Blob.new(None, value) + + # image-ize + if GObject.type_is_a(gtype, vips_type_image): + if not isinstance(value, Vips.Image): + value = imageize(self, value) + + # array-ize some types, if necessary + value = arrayize(gtype, value) + + self.set(field, value) + + def floor(self): + """Return the largest integral value not greater than the argument.""" + return self.round(Vips.OperationRound.FLOOR) + + def ceil(self): + """Return the smallest integral value not less than the argument.""" + return self.round(Vips.OperationRound.CEIL) + + def rint(self): + """Return the nearest integral value.""" + return self.round(Vips.OperationRound.RINT) + + def bandand(self): + """AND image bands together.""" + return self.bandbool(Vips.OperationBoolean.AND) + + def bandor(self): + """OR image bands together.""" + return self.bandbool(Vips.OperationBoolean.OR) + + def bandeor(self): + """EOR image bands together.""" + return self.bandbool(Vips.OperationBoolean.EOR) + + def bandsplit(self): + """Split an n-band image into n separate images.""" + return [x for x in self] + + def bandjoin(self, other): + """Append a set of images or constants bandwise.""" + if not isinstance(other, list): + other = [other] + + # if [other] is all numbers, we can use bandjoin_const + non_number = next((x for x in other + if not isinstance(x, numbers.Number)), + None) + + if non_number == None: + return self.bandjoin_const(other) + else: + return _call_base("bandjoin", [[self] + other], {}) - def bandrank(self, other, **kwargs): - """Band-wise rank filter a set of images.""" - if not isinstance(other, list): - other = [other] + def bandrank(self, other, **kwargs): + """Band-wise rank filter a set of images.""" + if not isinstance(other, list): + other = [other] - return _call_base("bandrank", [[self] + other], kwargs) + return _call_base("bandrank", [[self] + other], kwargs) - def maxpos(self): - """Return the coordinates of the image maximum.""" - v, opts = self.max(x = True, y = True) - x = opts['x'] - y = opts['y'] - return v, x, y - - def minpos(self): - """Return the coordinates of the image minimum.""" - v, opts = self.min(x = True, y = True) - x = opts['x'] - y = opts['y'] - return v, x, y - - def real(self): - """Return the real part of a complex image.""" - return self.complexget(Vips.OperationComplexget.REAL) - - def imag(self): - """Return the imaginary part of a complex image.""" - return self.complexget(Vips.OperationComplexget.IMAG) - - def polar(self): - """Return an image converted to polar coordinates.""" - return run_cmplx(lambda x: x.complex(Vips.OperationComplex.POLAR), self) - - def rect(self): - """Return an image converted to rectangular coordinates.""" - return run_cmplx(lambda x: x.complex(Vips.OperationComplex.RECT), self) - - def conj(self): - """Return the complex conjugate of an image.""" - return self.complex(Vips.OperationComplex.CONJ) - - def sin(self): - """Return the sine of an image in degrees.""" - return self.math(Vips.OperationMath.SIN) - - def cos(self): - """Return the cosine of an image in degrees.""" - return self.math(Vips.OperationMath.COS) - - def tan(self): - """Return the tangent of an image in degrees.""" - return self.math(Vips.OperationMath.TAN) - - def asin(self): - """Return the inverse sine of an image in degrees.""" - return self.math(Vips.OperationMath.ASIN) - - def acos(self): - """Return the inverse cosine of an image in degrees.""" - return self.math(Vips.OperationMath.ACOS) - - def atan(self): - """Return the inverse tangent of an image in degrees.""" - return self.math(Vips.OperationMath.ATAN) - - def log(self): - """Return the natural log of an image.""" - return self.math(Vips.OperationMath.LOG) - - def log10(self): - """Return the log base 10 of an image.""" - return self.math(Vips.OperationMath.LOG10) - - def exp(self): - """Return e ** pixel.""" - return self.math(Vips.OperationMath.EXP) - - def exp10(self): - """Return 10 ** pixel.""" - return self.math(Vips.OperationMath.EXP10) - - def erode(self, mask): - """Erode with a structuring element.""" - return self.morph(mask, Vips.OperationMorphology.ERODE) - - def dilate(self, mask): - """Dilate with a structuring element.""" - return self.morph(mask, Vips.OperationMorphology.DILATE) - - def median(self, size): - """size x size median filter.""" - return self.rank(size, size, (size * size) / 2) - - def fliphor(self): - """Flip horizontally.""" - return self.flip(Vips.Direction.HORIZONTAL) - - def flipver(self): - """Flip vertically.""" - return self.flip(Vips.Direction.VERTICAL) - - def rot90(self): - """Rotate 90 degrees clockwise.""" - return self.rot(Vips.Angle.D90) - - def rot180(self): - """Rotate 180 degrees.""" - return self.rot(Vips.Angle.D180) - - def rot270(self): - """Rotate 270 degrees clockwise.""" - return self.rot(Vips.Angle.D270) - - # we need different imageize rules for this operator ... we need to - # imageize th and el to match each other first - @add_doc(generate_docstring("ifthenelse")) - def ifthenelse(self, th, el, **kwargs): - for match_image in [th, el, self]: - if isinstance(match_image, Vips.Image): - break + def maxpos(self): + """Return the coordinates of the image maximum.""" + v, opts = self.max(x = True, y = True) + x = opts['x'] + y = opts['y'] + return v, x, y + + def minpos(self): + """Return the coordinates of the image minimum.""" + v, opts = self.min(x = True, y = True) + x = opts['x'] + y = opts['y'] + return v, x, y + + def real(self): + """Return the real part of a complex image.""" + return self.complexget(Vips.OperationComplexget.REAL) + + def imag(self): + """Return the imaginary part of a complex image.""" + return self.complexget(Vips.OperationComplexget.IMAG) + + def polar(self): + """Return an image converted to polar coordinates.""" + return run_cmplx(lambda x: x.complex(Vips.OperationComplex.POLAR), self) + + def rect(self): + """Return an image converted to rectangular coordinates.""" + return run_cmplx(lambda x: x.complex(Vips.OperationComplex.RECT), self) + + def conj(self): + """Return the complex conjugate of an image.""" + return self.complex(Vips.OperationComplex.CONJ) + + def sin(self): + """Return the sine of an image in degrees.""" + return self.math(Vips.OperationMath.SIN) + + def cos(self): + """Return the cosine of an image in degrees.""" + return self.math(Vips.OperationMath.COS) + + def tan(self): + """Return the tangent of an image in degrees.""" + return self.math(Vips.OperationMath.TAN) + + def asin(self): + """Return the inverse sine of an image in degrees.""" + return self.math(Vips.OperationMath.ASIN) + + def acos(self): + """Return the inverse cosine of an image in degrees.""" + return self.math(Vips.OperationMath.ACOS) + + def atan(self): + """Return the inverse tangent of an image in degrees.""" + return self.math(Vips.OperationMath.ATAN) + + def log(self): + """Return the natural log of an image.""" + return self.math(Vips.OperationMath.LOG) + + def log10(self): + """Return the log base 10 of an image.""" + return self.math(Vips.OperationMath.LOG10) + + def exp(self): + """Return e ** pixel.""" + return self.math(Vips.OperationMath.EXP) + + def exp10(self): + """Return 10 ** pixel.""" + return self.math(Vips.OperationMath.EXP10) + + def erode(self, mask): + """Erode with a structuring element.""" + return self.morph(mask, Vips.OperationMorphology.ERODE) + + def dilate(self, mask): + """Dilate with a structuring element.""" + return self.morph(mask, Vips.OperationMorphology.DILATE) + + def median(self, size): + """size x size median filter.""" + return self.rank(size, size, (size * size) / 2) + + def fliphor(self): + """Flip horizontally.""" + return self.flip(Vips.Direction.HORIZONTAL) + + def flipver(self): + """Flip vertically.""" + return self.flip(Vips.Direction.VERTICAL) + + def rot90(self): + """Rotate 90 degrees clockwise.""" + return self.rot(Vips.Angle.D90) + + def rot180(self): + """Rotate 180 degrees.""" + return self.rot(Vips.Angle.D180) + + def rot270(self): + """Rotate 270 degrees clockwise.""" + return self.rot(Vips.Angle.D270) + + # we need different imageize rules for this operator ... we need to + # imageize th and el to match each other first + @add_doc(generate_docstring("ifthenelse")) + def ifthenelse(self, th, el, **kwargs): + for match_image in [th, el, self]: + if isinstance(match_image, Vips.Image): + break - if not isinstance(th, Vips.Image): - th = imageize(match_image, th) - if not isinstance(el, Vips.Image): - el = imageize(match_image, el) - - return _call_base("ifthenelse", [th, el], kwargs, self) + if not isinstance(th, Vips.Image): + th = imageize(match_image, th) + if not isinstance(el, Vips.Image): + el = imageize(match_image, el) + + return _call_base("ifthenelse", [th, el], kwargs, self) # add operators which needs to be class methods @@ -1068,78 +1068,78 @@ class Image(Vips.Image): # see above class_methods = [ - "system", - "sum", - "arrayjoin", - "black", - "gaussnoise", - "text", - "xyz", - "gaussmat", - "logmat", - "eye", - "grey", - "zone", - "sines", - "mask_ideal", - "mask_ideal_ring", - "mask_ideal_band", - "mask_butterworth", - "mask_butterworth_ring", - "mask_butterworth_band", - "mask_gaussian", - "mask_gaussian_ring", - "mask_gaussian_band", - "mask_fractal", - "tonelut", - "identity", - "perlin", - "worley", - "fractsurf", - "radload", - "ppmload", - "csvload", - "matrixload", - "analyzeload", - "rawload", - "vipsload", - "pngload", - "pngload_buffer", - "matload", - "jpegload", - "jpegload_buffer", - "webpload", - "webpload_buffer", - "tiffload", - "tiffload_buffer", - "pdfload", - "pdfload_buffer", - "svgload", - "svgload_buffer", - "gifload", - "gifload_buffer", - "openslideload", - "magickload", - "magickload_buffer", - "fitsload", - "openexrload"] + "system", + "sum", + "arrayjoin", + "black", + "gaussnoise", + "text", + "xyz", + "gaussmat", + "logmat", + "eye", + "grey", + "zone", + "sines", + "mask_ideal", + "mask_ideal_ring", + "mask_ideal_band", + "mask_butterworth", + "mask_butterworth_ring", + "mask_butterworth_band", + "mask_gaussian", + "mask_gaussian_ring", + "mask_gaussian_band", + "mask_fractal", + "tonelut", + "identity", + "perlin", + "worley", + "fractsurf", + "radload", + "ppmload", + "csvload", + "matrixload", + "analyzeload", + "rawload", + "vipsload", + "pngload", + "pngload_buffer", + "matload", + "jpegload", + "jpegload_buffer", + "webpload", + "webpload_buffer", + "tiffload", + "tiffload_buffer", + "pdfload", + "pdfload_buffer", + "svgload", + "svgload_buffer", + "gifload", + "gifload_buffer", + "openslideload", + "magickload", + "magickload_buffer", + "fitsload", + "openexrload"] def generate_class_method(name): - @classmethod - @add_doc(generate_docstring(name)) - def class_method(cls, *args, **kwargs): - return _call_base(name, args, kwargs) + @classmethod + @add_doc(generate_docstring(name)) + def class_method(cls, *args, **kwargs): + return _call_base(name, args, kwargs) - return class_method + return class_method for nickname in class_methods: - logger.debug('adding %s as a class method' % nickname) - # some may be missing in this vips, eg. we might not have "webpload" - try: - method = generate_class_method(nickname) - setattr(Vips.Image, nickname, method) - except Error: - pass + logger.debug('adding %s as a class method' % nickname) + # some may be missing in this vips, eg. we might not have "webpload" + try: + method = generate_class_method(nickname) + setattr(Vips.Image, nickname, method) + except Error: + pass Image = override(Image) __all__.append('Image')