diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a425df6..aff7d60 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,6 +32,16 @@ jobs:
pip install flake8
flake8 .
+ - name: Type check with mypy
+ run: |
+ # Pin mypy to <1.19 due to librt dependency in 1.19+
+ # which is not available on PyPy 3.9
+ pip install "mypy<1.19"
+ mypy pyvips/__init__.pyi --no-error-summary
+ mypy examples/affine.py examples/convolve.py examples/try5.py examples/watermark.py \
+ examples/annotate-animation.py examples/join-animation.py examples/progress.py \
+ --no-error-summary
+
- name: Install tox and any other packages
run: pip install tox
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 279ec64..4e15636 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,3 +1,10 @@
+## Version 3.2.0 (released TBA)
+
+- add comprehensive type hints for all pyvips operations via generated stubs [JoshCLWren]
+- add operator overload type hints with array support (int, float, list[int], list[float]) [JoshCLWren]
+- add hand-written binding type hints for common methods [JoshCLWren]
+- add test coverage for type stubs [JoshCLWren]
+
## Version 3.1.1 (released 9 December 2025)
- fix get_gainmap arguments [jcupitt]
diff --git a/README.rst b/README.rst
index 913eb0f..c89c9fc 100644
--- a/README.rst
+++ b/README.rst
@@ -228,6 +228,30 @@ Stylecheck:
$ flake8
+Stylecheck:
+
+.. code-block:: shell
+
+ $ flake8
+
+Type checking:
+
+pyvips includes type hints via PEP 561 type stub files (``pyvips/__init__.pyi``).
+To enable type checking in your project, install a type checker like mypy:
+
+.. code-block:: shell
+
+ $ pip install mypy pyvips
+
+Then run mypy on your code:
+
+.. code-block:: shell
+
+ $ mypy your_script.py
+
+Note: ``pyvips`` methods accept arbitrary keyword arguments for libvips options,
+which may not be fully covered by type hints.
+
Generate HTML docs in ``doc/build/html``:
.. code-block:: shell
@@ -246,6 +270,16 @@ then
Then check and move `enums.py` into `pyvips/`.
+Regenerate type stubs:
+
+After adding new libvips operations or updating libvips itself, regenerate type stubs:
+
+.. code-block:: shell
+
+ $ python examples/generate_type_stubs.py
+
+This updates ``pyvips/__init__.pyi`` with the latest operations.
+
Regenerate autodocs:
Make sure you have installed a libvips with all optional packages enabled,
diff --git a/doc/conf.py b/doc/conf.py
index f9ced58..cdc7f13 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -19,6 +19,7 @@
import os
import sys
import sphinx_rtd_theme
+
sys.path.insert(0, os.path.abspath('..'))
@@ -55,24 +56,24 @@
master_doc = 'index'
# General information about the project.
-project = u'pyvips'
-copyright = u'2019, John Cupitt'
-author = u'John Cupitt'
+project = 'pyvips'
+copyright = '2019, John Cupitt'
+author = 'John Cupitt'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = u'3.1'
+version = u'3.2'
# The full version, including alpha/beta/rc tags.
-release = u'3.1.1'
+release = u'3.2.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
-# Usually you set "language" from the command line for these cases.
+# Usually you set 'language' from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
@@ -103,7 +104,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
+# so a file named 'default.css' will overwrite the builtin 'default.css'.
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
@@ -126,7 +127,7 @@
# each page.
'github_user': 'libvips',
'github_repo': 'pyvips',
- 'github_version': 'master/doc/'
+ 'github_version': 'master/doc/',
}
# -- Options for HTMLHelp output ------------------------------------------
@@ -141,15 +142,12 @@
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
-
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
-
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
-
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
@@ -159,8 +157,7 @@
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'pyvips.tex', u'pyvips Documentation',
- u'john', 'manual'),
+ (master_doc, 'pyvips.tex', 'pyvips Documentation', 'john', 'manual'),
]
@@ -168,10 +165,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [
- (master_doc, 'pyvips', u'pyvips Documentation',
- [author], 1)
-]
+man_pages = [(master_doc, 'pyvips', 'pyvips Documentation', [author], 1)]
# -- Options for Texinfo output -------------------------------------------
@@ -180,19 +174,26 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'pyvips', u'pyvips Documentation',
- author, 'pyvips', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ master_doc,
+ 'pyvips',
+ 'pyvips Documentation',
+ author,
+ 'pyvips',
+ 'One line description of project.',
+ 'Miscellaneous',
+ ),
]
# see https://stackoverflow.com/questions/20569011
# adds autoautosummary directive, see vimage.rst
+
# try to exclude deprecated
def skip_deprecated(app, what, name, obj, skip, options):
if hasattr(obj, "func_dict") and "__deprecated__" in obj.func_dict:
- print("skipping " + name)
+ print('skipping ' + name)
return True
return skip or False
@@ -206,10 +207,9 @@ def setup(app):
from sphinx.util.inspect import safe_getattr
class AutoAutoSummary(Autosummary):
-
option_spec = {
'methods': directives.unchanged,
- 'attributes': directives.unchanged
+ 'attributes': directives.unchanged,
}
required_arguments = 1
@@ -227,8 +227,10 @@ def get_members(obj, typ, include_public=None):
continue
if documenter.objtype == typ:
items.append(name)
- public = [x for x in items
- if x in include_public or not x.startswith('_')]
+ public = [
+ x for x in items
+ if x in include_public or not x.startswith('_')
+ ]
return public, items
def run(self):
@@ -242,17 +244,21 @@ def run(self):
_, methods = self.get_members(c,
'method', ['__init__'])
- self.content = ["~%s.%s" % (clazz, method)
- for method in methods
- if not method.startswith('_')]
+ self.content = [
+ '~%s.%s' % (clazz, method)
+ for method in methods
+ if not method.startswith('_')
+ ]
if 'attributes' in self.options:
_, attribs = self.get_members(c, 'attribute')
- self.content = ["~%s.%s" % (clazz, attrib)
- for attrib in attribs
- if not attrib.startswith('_')]
+ self.content = [
+ '~%s.%s' % (clazz, attrib)
+ for attrib in attribs
+ if not attrib.startswith('_')
+ ]
finally:
return super(AutoAutoSummary, self).run()
- app.add_directive('autoautosummary', AutoAutoSummary)
+ app.add_directive("autoautosummary", AutoAutoSummary)
except BaseException as e:
raise e
diff --git a/examples/generate_type_stubs.py b/examples/generate_type_stubs.py
new file mode 100755
index 0000000..cac0d94
--- /dev/null
+++ b/examples/generate_type_stubs.py
@@ -0,0 +1,545 @@
+#!/usr/bin/env python3
+"""
+Generate pyvips type stubs.
+
+This script generates .pyi stub files for pyvips using introspection.
+Run this to regenerate type stubs after adding new operations.
+
+Usage:
+ python examples/generate_type_stubs.py
+
+RATIONALE:
+---------
+Type stubs are generated rather than handwritten because:
+1. pyvips is a binding around libvips C library, which evolves independently
+2. libvips has 300+ operations with complex signatures
+3. New operations are added to libvips frequently
+4. Generation uses same introspection mechanism as doc generation
+5. Ensures stubs stay synchronized with available operations
+
+This approach follows the existing pattern used for:
+- Enum generation (examples/gen-enums.py)
+- Documentation generation (pyvips.Operation.generate_sphinx_all())
+"""
+
+import typing
+from typing import Any, Optional, Union
+
+import pyvips
+from pyvips import (
+ GValue,
+ Introspect,
+ type_map,
+ type_from_name,
+ nickname_find,
+ at_least_libvips,
+)
+from pyvips import ffi, Error
+from pyvips.voperation import _OPERATION_DEPRECATED
+
+
+def gtype_to_python_type(gtype: int) -> str:
+ """Map a gtype to Python type annotation string."""
+ fundamental = pyvips.gobject_lib.g_type_fundamental(gtype)
+
+ if fundamental == GValue.genum_type:
+ name = pyvips.type_name(gtype)
+ if name.startswith("Vips"):
+ name = name[4:]
+ return f"Union[str, {name}]"
+
+ type_mapping = {
+ GValue.gbool_type: "bool",
+ GValue.gint_type: "int",
+ GValue.guint64_type: "int",
+ GValue.gdouble_type: "float",
+ GValue.gstr_type: "str",
+ GValue.refstr_type: "str",
+ GValue.gflags_type: "int",
+ GValue.gobject_type: "GObject",
+ GValue.image_type: "Image",
+ GValue.array_int_type: "list[int]",
+ GValue.array_double_type: "list[float]",
+ GValue.array_image_type: "list[Image]",
+ GValue.blob_type: "str",
+ GValue.source_type: "Source",
+ GValue.target_type: "Target",
+ }
+
+ if gtype in type_mapping:
+ return type_mapping[gtype]
+ if fundamental in type_mapping:
+ return type_mapping[fundamental]
+ return "Any"
+
+
+PYTHON_KEYWORDS = {
+ "in",
+ "min",
+ "max",
+ "type",
+ "class",
+ "def",
+ "return",
+ "import",
+ "from",
+ "as",
+ "if",
+ "else",
+ "elif",
+ "for",
+ "while",
+ "break",
+ "continue",
+ "pass",
+ "raise",
+ "try",
+ "except",
+ "finally",
+ "with",
+ "lambda",
+ "and",
+ "or",
+ "not",
+ "is",
+ "None",
+ "True",
+ "False",
+}
+
+
+def escape_parameter_name(name: str) -> str:
+ """Escape Python keywords in parameter names."""
+ if name in PYTHON_KEYWORDS:
+ return f"{name}_"
+ return name
+
+
+def generate_method_signature(operation_name: str) -> str:
+ """Generate type signature for an operation method."""
+ intro = Introspect.get(operation_name)
+
+ if (intro.flags & _OPERATION_DEPRECATED) != 0:
+ return None
+
+ args_list = []
+
+ if intro.member_x is not None:
+ # Instance method: self comes first
+ args_list.append("self")
+ else:
+ # Static/class method: no self
+ pass
+
+ # Required args (excluding member_x for instance methods)
+ for name in intro.method_args:
+ py_type = gtype_to_python_type(intro.details[name]["type"])
+ args_list.append(f"{escape_parameter_name(name)}: {py_type}")
+
+ # Optional args (excluding deprecated)
+ for name in intro.doc_optional_input:
+ py_type = gtype_to_python_type(intro.details[name]["type"])
+ args_list.append(f"{escape_parameter_name(name)}: {py_type} = ...")
+
+ # Optional output args
+ for name in intro.doc_optional_output:
+ args_list.append(f"{escape_parameter_name(name)}: bool = ...")
+
+ args_str = ", ".join(args_list)
+ # Return type
+ output_types = [
+ gtype_to_python_type(intro.details[name]["type"])
+ for name in intro.required_output
+ ]
+
+ if len(output_types) == 0:
+ return_type = "None"
+ elif len(output_types) == 1:
+ return_type = output_types[0]
+ else:
+ return_type = f"tuple[{', '.join(output_types)}]"
+
+ # Optional output dicts can contain any metadata value type
+ if len(intro.doc_optional_output) > 0:
+ dict_value_type = (
+ "Union[bool, int, float, str, Image, list[int], list[float], list[Image]]"
+ )
+ return_type = f"Union[{return_type}, tuple[{', '.join(output_types + [f'Dict[str, {dict_value_type}]'])}]]"
+
+ if intro.member_x is not None:
+ return f" def {operation_name}({args_str}) -> {return_type}: ..."
+ else:
+ return f" @staticmethod\n def {operation_name}({args_str}) -> {return_type}: ..."
+
+
+def generate_all_image_operations() -> str:
+ """Generate type stubs for all dynamically generated Image methods."""
+
+ # these names are aliased
+ alias = ["crop"]
+ alias_gtypes = {}
+ for name in alias:
+ gtype = pyvips.type_find("VipsOperation", name)
+ alias_gtypes[gtype] = name
+
+ all_names = []
+
+ def add_name(gtype, a, b):
+ if gtype in alias_gtypes:
+ name = alias_gtypes[gtype]
+ else:
+ name = nickname_find(gtype)
+
+ try:
+ sig = generate_method_signature(name)
+ if sig:
+ all_names.append((name, sig))
+ except Error:
+ pass
+
+ type_map(gtype, add_name)
+ return ffi.NULL
+
+ type_map(type_from_name("VipsOperation"), add_name)
+ all_names.sort()
+
+ # remove operations we have to wrap by hand
+ exclude = ["scale", "ifthenelse", "bandjoin", "bandrank", "composite", "copy"]
+ all_names = [(name, sig) for name, sig in all_names if name not in exclude]
+
+ return "\n".join([sig for _, sig in all_names])
+
+
+def get_all_enum_names() -> list[str]:
+ """Get all enum type names from introspection."""
+
+ enum_names = set()
+
+ def add_enums(gtype, a, b):
+ fundamental = pyvips.gobject_lib.g_type_fundamental(gtype)
+ if fundamental == GValue.genum_type:
+ name = pyvips.type_name(gtype)
+ if name.startswith("Vips"):
+ name = name[4:]
+ enum_names.add(name)
+ type_map(gtype, add_enums)
+ return ffi.NULL
+
+ type_map(type_from_name("VipsOperation"), add_enums)
+
+ # Also add base enums
+ enum_names.update(
+ [
+ "BandFormat",
+ "Interpretation",
+ "Kernel",
+ "Coding",
+ "Extend",
+ "Align",
+ "Direction",
+ "Angle",
+ "Angle45",
+ "Access",
+ "Shrink",
+ "Intent",
+ "PCS",
+ "OperationBoolean",
+ "OperationComplex",
+ "OperationComplex2",
+ "OperationComplexget",
+ "OperationMath",
+ "OperationMath2",
+ "OperationMorphology",
+ "OperationRelational",
+ "OperationRound",
+ "Interesting",
+ "SdfShape",
+ "TextWrap",
+ "Combine",
+ "CombineMode",
+ "CompassDirection",
+ "Precision",
+ "FailOn",
+ "BlendMode",
+ "ForeignDzLayout",
+ "ForeignDzDepth",
+ "ForeignDzContainer",
+ "RegionShrink",
+ "ForeignHeifCompression",
+ "ForeignSubsample",
+ "ForeignHeifEncoder",
+ "ForeignPpmFormat",
+ "ForeignTiffCompression",
+ "ForeignTiffPredictor",
+ "ForeignTiffResunit",
+ "ForeignWebpPreset",
+ "Size",
+ ]
+ )
+
+ return sorted(enum_names)
+
+
+def generate_enum_class(name: str) -> str:
+ """Generate type stub for a single enum class."""
+ if name == "Direction":
+ return """class Direction:
+ HORIZONTAL: str
+ VERTICAL: str
+"""
+ elif name == "Align":
+ return """class Align:
+ LOW: str
+ CENTRE: str
+ HIGH: str
+"""
+ else:
+ return f"class {name}: ..."
+
+
+def generate_stub() -> str:
+ """Generate complete pyvips type stub."""
+
+ # Get all enum names
+ enum_names = get_all_enum_names()
+
+ enum_classes = "\n".join([generate_enum_class(name) for name in enum_names])
+
+ stub = f'''"""Type stubs for pyvips.
+
+# flake8: noqa: E501
+
+This file is automatically generated by examples/generate_type_stubs.py.
+
+RATIONALE FOR GENERATION:
+- pyvips is a binding around libvips C library
+- libvips has 300+ operations that change frequently
+- Manual stub maintenance would be unmaintainable
+- Generation uses same introspection as docs/ enums
+- Ensures stubs stay synchronized with available operations
+
+To regenerate after libvips updates:
+ python examples/generate_type_stubs.py
+"""
+
+from __future__ import annotations
+from typing import Dict, List, Optional, Tuple, TypeVar, Union, overload
+
+# Exception classes
+class Error(Exception): ...
+
+# GObject base classes
+class GObject:
+ def signal_connect(self, name: str, callback: object) -> None: ...
+
+class VipsObject(GObject): ...
+
+class GValue:
+ gbool_type: int
+ gint_type: int
+ guint64_type: int
+ gdouble_type: int
+ gstr_type: int
+ genum_type: int
+ gflags_type: int
+ gobject_type: int
+ image_type: int
+ array_int_type: int
+ array_double_type: int
+ array_image_type: int
+ refstr_type: int
+ blob_type: int
+ source_type: int
+ target_type: int
+ format_type: int
+ blend_mode_type: int
+ ...
+
+# Connection classes
+class Source:
+ @staticmethod
+ def new_from_descriptor(descriptor: int) -> Source: ...
+class SourceCustom(Source):
+ def on_read(self, handler: object) -> None: ...
+ def on_seek(self, handler: object) -> None: ...
+class Target:
+ @staticmethod
+ def new_to_descriptor(descriptor: int) -> Target: ...
+
+# Interpolator class
+class Interpolate:
+ @staticmethod
+ def new(name: str) -> Interpolate: ...
+
+# Enum classes
+{enum_classes}
+
+
+class Image(VipsObject):
+ """Wrap a VipsImage object."""
+
+ # Properties
+ @property
+ def width(self) -> int: ...
+ @property
+ def height(self) -> int: ...
+ @property
+ def bands(self) -> int: ...
+ @property
+ def format(self) -> Union[str, BandFormat]: ...
+ @property
+ def interpretation(self) -> Union[str, Interpretation]: ...
+ @property
+ def xres(self) -> float: ...
+ @property
+ def yres(self) -> float: ...
+ @property
+ def xoffset(self) -> int: ...
+ @property
+ def yoffset(self) -> int: ...
+
+ # Metadata methods
+ # GValue can return: bool, int, float, str, Image, list[int], list[float], list[Image]
+ def get_typeof(self, name: str) -> int: ...
+ def get(self, name: str) -> Union[bool, int, float, str, Image, list[int], list[float], list[Image]]: ...
+ def get_fields(self) -> List[str]: ...
+ def set_type(self, gtype: int, name: str, value: Union[bool, int, float, str, Image, list[int], list[float], list[Image]]) -> None: ...
+ def set(self, name: str, value: Union[bool, int, float, str, Image, list[int], list[float], list[Image]]) -> None: ...
+ def remove(self, name: str) -> bool: ...
+
+ # Constructors
+ @staticmethod
+ def new_from_file(vips_filename: str, **kwargs: object) -> Image: ...
+ @staticmethod
+ def new_from_buffer(data: Union[bytes, bytearray, memoryview], options: str, **kwargs: object) -> Image: ...
+ @staticmethod
+ def new_from_list(array: List[List[float]], scale: float =1.0, offset: float = 0.0) -> Image: ...
+ @classmethod
+ def new_from_array(cls, obj: Union[List, bytes, bytearray, memoryview], scale: float = 1.0, offset: float = 0.0, interpretation: Optional[Union[str, Interpretation]] = None) -> Image: ...
+ @staticmethod
+ def new_from_memory(data: Union[bytes, bytearray, memoryview], width: int, height: int, bands: int, format: Union[str, BandFormat]) -> Image: ...
+ @staticmethod
+ def new_from_source(source: Source, options: str, **kwargs: object) -> Image: ...
+ @staticmethod
+ def new_temp_file(format: str) -> Image: ...
+ def new_from_image(self, value: Union[float, int, List[float], List[int]]) -> Image: ...
+ def copy_memory(self) -> Image: ...
+
+ # Writers
+ def write_to_file(self, vips_filename: str, **kwargs: object) -> None: ...
+ def write_to_buffer(self, format_string: str, **kwargs: object) -> bytes: ...
+ def write_to_target(self, target: Target, format_string: str, **kwargs: object) -> None: ...
+ def write_to_memory(self) -> bytes: ...
+ def write(self, other: Image) -> None: ...
+
+ # Utility methods
+ def invalidate(self) -> None: ...
+ def set_progress(self, progress: bool) -> None: ...
+ def set_kill(self, kill: bool) -> None: ...
+ def copy(self, **kwargs: object) -> Image: ...
+ def tolist(self) -> List[List[float]]: ...
+ # numpy is optional dependency - use TYPE_CHECKING guard
+ def __array__(self, dtype: Optional[str] = None, copy: Optional[bool] = None) -> object: ...
+ def numpy(self, dtype: Optional[str] = None) -> object: ...
+
+ # Hand-written bindings with type hints
+ def floor(self) -> Image: ...
+ def ceil(self) -> Image: ...
+ def rint(self) -> Image: ...
+ def bandsplit(self) -> List[Image]: ...
+ def bandjoin(self, other: Union[Image, float, int, List[Union[Image, float, int]]]) -> Image: ...
+ def bandrank(self, other: Union[Image, List[Image]], **kwargs: object) -> Image: ...
+ def composite(self, other: Union[Image, List[Image]], mode: Union[str, BlendMode, List[Union[str, BlendMode]]], **kwargs: object) -> Image: ...
+ def ifthenelse(self, in1: Union[Image, float, int], in2: Union[Image, float, int], **kwargs: object) -> Image: ...
+ def hasalpha(self) -> bool: ...
+ def get_n_pages(self) -> int: ...
+ def get_page_height(self) -> int: ...
+ def pagesplit(self) -> List[Image]: ...
+ def pagejoin(self, other: Union[Image, List[Image]]) -> Image: ...
+ def scaleimage(self, **kwargs: object) -> Image: ...
+ def erode(self, mask: Union[Image, List[List[int]]]) -> Image: ...
+ def dilate(self, mask: Union[Image, List[List[int]]]) -> Image: ...
+
+ # Dynamically generated operations
+'''
+
+ stub += generate_all_image_operations()
+
+ stub += """
+
+ # Operator overloads
+ def __add__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __radd__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __sub__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rsub__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __mul__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rmul__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __truediv__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rtruediv__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __floordiv__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rfloordiv__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __mod__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __pow__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rpow__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __abs__(self) -> Image: ...
+ def __neg__(self) -> Image: ...
+ def __pos__(self) -> Image: ...
+ def __invert__(self) -> Image: ...
+ def __lshift__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rshift__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __and__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rand__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __or__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __ror__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __xor__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rxor__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __eq__(self, other: object) -> bool: ...
+ def __ne__(self, other: object) -> bool: ...
+ def __gt__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __ge__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __lt__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __le__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+
+ def __getitem__(self, arg: Union[int, slice, List[int], List[bool]]) -> Image: ...
+ def __call__(self, x: int, y: int) -> List[float]: ...
+ def __repr__(self) -> str: ...
+
+
+class Operation: ...
+class Introspect: ...
+
+# Global functions
+def cache_set_max(mx: int) -> None: ...
+def cache_set_max_mem(mx: int) -> None: ...
+def cache_set_max_files(mx: int) -> None: ...
+def cache_set_trace(trace: bool) -> None: ...
+def cache_get_max() -> int: ...
+def cache_get_size() -> int: ...
+def cache_get_max_mem() -> int: ...
+def cache_get_max_files() -> int: ...
+def block_untrusted_set(state: bool) -> None: ...
+def operation_block_set(name: str, state: bool) -> None: ...
+def leak_set(leak: bool) -> None: ...
+def shutdown() -> None: ...
+def call(operation_name: str, *args: object, **kwargs: object) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+
+# Module-level constants
+API_mode: bool
+"""
+
+ return stub
+
+
+if __name__ == "__main__":
+ # Generate stub
+ stub_content = generate_stub()
+
+ # Write to pyvips/__init__.pyi
+ import os
+
+ # Script is in examples/, need to go up one level to find pyvips/
+ stub_file = os.path.join(os.path.dirname(__file__), "..", "pyvips", "__init__.pyi")
+ with open(stub_file, "w") as f:
+ f.write(stub_content)
+
+ print(f"Generated type stub at {stub_file}")
diff --git a/examples/read_profile.py b/examples/read_profile.py
index c31a2fb..d1fb4b3 100755
--- a/examples/read_profile.py
+++ b/examples/read_profile.py
@@ -11,5 +11,5 @@
profile = a.get("icc-profile-data")
-with open('x.icm', 'w') as f:
- f.write(profile)
+with open("x.icm", "w") as f:
+ f.write(profile) # type: ignore[arg-type]
diff --git a/examples/try10.py b/examples/try10.py
index 98c5f51..d61ff50 100755
--- a/examples/try10.py
+++ b/examples/try10.py
@@ -6,6 +6,6 @@
import pyvips
a = pyvips.Image.black(100, 100)
-a = a.draw_circle(128, 50, 50, 20)
+a = a.draw_circle(128, 50, 50, 20) # type: ignore[arg-type]
b = a.hough_circle(scale=1, min_radius=15, max_radius=25)
b.write_to_file("x.v")
diff --git a/examples/try11.py b/examples/try11.py
index b88bddb..5ef1308 100755
--- a/examples/try11.py
+++ b/examples/try11.py
@@ -8,6 +8,6 @@
a = pyvips.Image.new_from_file(sys.argv[1])
ipct = a.get("ipct-data")
-print("ipct = ", ipct.get())
+print("ipct = ", ipct.get()) # type: ignore[union-attr, call-arg]
a.remove("ipct-data")
a.write_to_file("x.jpg")
diff --git a/examples/try5.py b/examples/try5.py
index ebc307f..5acb960 100755
--- a/examples/try5.py
+++ b/examples/try5.py
@@ -11,7 +11,7 @@
def should_equal(test, a, b):
if abs(a - b) > 0.01:
- print(f'{test}: seen {a:g} and {b:g}')
+ print(f"{test}: seen {a:g} and {b:g}")
sys.exit(1)
@@ -23,15 +23,13 @@ def bandsplit(a):
# addition
b = a + 12
-should_equal('add constant', a.avg() + 12, b.avg())
+should_equal("add constant", a.avg() + 12, b.avg())
b = a + [12, 0, 0]
-x = map(lambda x: x.avg())
-bandsplit(a)
-y = map(lambda x: x.avg())
-bandsplit(b)
+x = list(map(lambda img: img.avg(), bandsplit(a)))
+y = list(map(lambda img: img.avg(), bandsplit(b)))
x[0] += 12
-should_equal('add multiband constant', sum(x), sum(y))
+should_equal("add multiband constant", sum(x), sum(y))
b = a + [12, 0, 0]
b = a + b
@@ -48,7 +46,7 @@ def bandsplit(a):
b = a * [12, 1, 1]
b = a * b
b = 12 * a
-b = [12, 1, 1] * a
+b = [12, 1, 1] * a # type: ignore
b = a / 12
b = a / [12, 1, 1]
@@ -66,11 +64,11 @@ def bandsplit(a):
b = a % [12, 1, 1]
b = a % b
-b = a ** 12
+b = a**12
b = a ** [12, 1, 1]
-b = 12 ** a
+b = 12**a
b = [12, 1, 1] ** a
-b = a ** b
+b = a**b
b = a << 12
b = a << [12, 1, 1]
diff --git a/examples/try7.py b/examples/try7.py
index cb936eb..a44d7ca 100755
--- a/examples/try7.py
+++ b/examples/try7.py
@@ -10,6 +10,6 @@
b = a.write_to_memory()
-c = pyvips.Image.new_from_memory(b, a.width, a.height, a.bands, a.bandfmt)
+c = pyvips.Image.new_from_memory(b, a.width, a.height, a.bands, a.format)
c.write_to_file("x.v")
diff --git a/examples/watermark.py b/examples/watermark.py
index 7babd40..2a00f4c 100755
--- a/examples/watermark.py
+++ b/examples/watermark.py
@@ -5,23 +5,27 @@
im = pyvips.Image.new_from_file(sys.argv[1], access="sequential")
-text = pyvips.Image.text(f"{sys.argv[3]}",
- width=500,
- dpi=100,
- align="centre",
- rgba=True)
+text = pyvips.Image.text(
+ f'{sys.argv[3]}',
+ width=500,
+ dpi=100,
+ align="centre",
+ rgba=True,
+)
# scale the alpha down to make the text semi-transparent
-text = (text * [1, 1, 1, 0.3]).cast("uchar")
+text = (text * [1, 1, 1, 0.3]).cast("uchar") # type: ignore
text = text.rotate(45)
# tile to the size of the image page, then tile again to the full image size
text = text.embed(10, 10, text.width + 20, text.width + 20)
page_height = im.get_page_height()
-text = text.replicate(1 + im.width / text.width, 1 + page_height / text.height)
+text = text.replicate(
+ int(1 + im.width / text.width), int(1 + page_height / text.height)
+)
text = text.crop(0, 0, im.width, page_height)
-text = text.replicate(1, 1 + im.height / text.height)
+text = text.replicate(1, int(1 + im.height / text.height))
text = text.crop(0, 0, im.width, im.height)
# composite the two layers
diff --git a/pyvips/__init__.pyi b/pyvips/__init__.pyi
new file mode 100644
index 0000000..bc30b92
--- /dev/null
+++ b/pyvips/__init__.pyi
@@ -0,0 +1,657 @@
+"""Type stubs for pyvips.
+
+# flake8: noqa: E501
+
+This file is automatically generated by examples/generate_type_stubs.py.
+
+RATIONALE FOR GENERATION:
+- pyvips is a binding around libvips C library
+- libvips has 300+ operations that change frequently
+- Manual stub maintenance would be unmaintainable
+- Generation uses same introspection as docs/ enums
+- Ensures stubs stay synchronized with available operations
+
+To regenerate after libvips updates:
+ python examples/generate_type_stubs.py
+"""
+
+from __future__ import annotations
+from typing import Dict, List, Optional, Tuple, TypeVar, Union, overload
+
+# Exception classes
+class Error(Exception): ...
+
+# GObject base classes
+class GObject:
+ def signal_connect(self, name: str, callback: object) -> None: ...
+
+class VipsObject(GObject): ...
+
+class GValue:
+ gbool_type: int
+ gint_type: int
+ guint64_type: int
+ gdouble_type: int
+ gstr_type: int
+ genum_type: int
+ gflags_type: int
+ gobject_type: int
+ image_type: int
+ array_int_type: int
+ array_double_type: int
+ array_image_type: int
+ refstr_type: int
+ blob_type: int
+ source_type: int
+ target_type: int
+ format_type: int
+ blend_mode_type: int
+ ...
+
+# Connection classes
+class Source:
+ @staticmethod
+ def new_from_descriptor(descriptor: int) -> Source: ...
+class SourceCustom(Source):
+ def on_read(self, handler: object) -> None: ...
+ def on_seek(self, handler: object) -> None: ...
+class Target:
+ @staticmethod
+ def new_to_descriptor(descriptor: int) -> Target: ...
+
+# Interpolator class
+class Interpolate:
+ @staticmethod
+ def new(name: str) -> Interpolate: ...
+
+# Enum classes
+class Access: ...
+class Align:
+ LOW: str
+ CENTRE: str
+ HIGH: str
+
+class Angle: ...
+class Angle45: ...
+class BandFormat: ...
+class BlendMode: ...
+class Coding: ...
+class Combine: ...
+class CombineMode: ...
+class CompassDirection: ...
+class Direction:
+ HORIZONTAL: str
+ VERTICAL: str
+
+class Extend: ...
+class FailOn: ...
+class ForeignDzContainer: ...
+class ForeignDzDepth: ...
+class ForeignDzLayout: ...
+class ForeignHeifCompression: ...
+class ForeignHeifEncoder: ...
+class ForeignPpmFormat: ...
+class ForeignSubsample: ...
+class ForeignTiffCompression: ...
+class ForeignTiffPredictor: ...
+class ForeignTiffResunit: ...
+class ForeignWebpPreset: ...
+class Intent: ...
+class Interesting: ...
+class Interpretation: ...
+class Kernel: ...
+class OperationBoolean: ...
+class OperationComplex: ...
+class OperationComplex2: ...
+class OperationComplexget: ...
+class OperationMath: ...
+class OperationMath2: ...
+class OperationMorphology: ...
+class OperationRelational: ...
+class OperationRound: ...
+class PCS: ...
+class Precision: ...
+class RegionShrink: ...
+class SdfShape: ...
+class Shrink: ...
+class Size: ...
+class TextWrap: ...
+
+
+class Image(VipsObject):
+ """Wrap a VipsImage object."""
+
+ # Properties
+ @property
+ def width(self) -> int: ...
+ @property
+ def height(self) -> int: ...
+ @property
+ def bands(self) -> int: ...
+ @property
+ def format(self) -> Union[str, BandFormat]: ...
+ @property
+ def interpretation(self) -> Union[str, Interpretation]: ...
+ @property
+ def xres(self) -> float: ...
+ @property
+ def yres(self) -> float: ...
+ @property
+ def xoffset(self) -> int: ...
+ @property
+ def yoffset(self) -> int: ...
+
+ # Metadata methods
+ # GValue can return: bool, int, float, str, Image, list[int], list[float], list[Image]
+ def get_typeof(self, name: str) -> int: ...
+ def get(self, name: str) -> Union[bool, int, float, str, Image, list[int], list[float], list[Image]]: ...
+ def get_fields(self) -> List[str]: ...
+ def set_type(self, gtype: int, name: str, value: Union[bool, int, float, str, Image, list[int], list[float], list[Image]]) -> None: ...
+ def set(self, name: str, value: Union[bool, int, float, str, Image, list[int], list[float], list[Image]]) -> None: ...
+ def remove(self, name: str) -> bool: ...
+
+ # Constructors
+ @staticmethod
+ def new_from_file(vips_filename: str, **kwargs: object) -> Image: ...
+ @staticmethod
+ def new_from_buffer(data: Union[bytes, bytearray, memoryview], options: str, **kwargs: object) -> Image: ...
+ @staticmethod
+ def new_from_list(array: List[List[float]], scale: float =1.0, offset: float = 0.0) -> Image: ...
+ @classmethod
+ def new_from_array(cls, obj: Union[List, bytes, bytearray, memoryview], scale: float = 1.0, offset: float = 0.0, interpretation: Optional[Union[str, Interpretation]] = None) -> Image: ...
+ @staticmethod
+ def new_from_memory(data: Union[bytes, bytearray, memoryview], width: int, height: int, bands: int, format: Union[str, BandFormat]) -> Image: ...
+ @staticmethod
+ def new_from_source(source: Source, options: str, **kwargs: object) -> Image: ...
+ @staticmethod
+ def new_temp_file(format: str) -> Image: ...
+ def new_from_image(self, value: Union[float, int, List[float], List[int]]) -> Image: ...
+ def copy_memory(self) -> Image: ...
+
+ # Writers
+ def write_to_file(self, vips_filename: str, **kwargs: object) -> None: ...
+ def write_to_buffer(self, format_string: str, **kwargs: object) -> bytes: ...
+ def write_to_target(self, target: Target, format_string: str, **kwargs: object) -> None: ...
+ def write_to_memory(self) -> bytes: ...
+ def write(self, other: Image) -> None: ...
+
+ # Utility methods
+ def invalidate(self) -> None: ...
+ def set_progress(self, progress: bool) -> None: ...
+ def set_kill(self, kill: bool) -> None: ...
+ def copy(self, **kwargs: object) -> Image: ...
+ def tolist(self) -> List[List[float]]: ...
+ # numpy is optional dependency - use TYPE_CHECKING guard
+ def __array__(self, dtype: Optional[str] = None, copy: Optional[bool] = None) -> object: ...
+ def numpy(self, dtype: Optional[str] = None) -> object: ...
+
+ # Hand-written bindings with type hints
+ def floor(self) -> Image: ...
+ def ceil(self) -> Image: ...
+ def rint(self) -> Image: ...
+ def bandsplit(self) -> List[Image]: ...
+ def bandjoin(self, other: Union[Image, float, int, List[Union[Image, float, int]]]) -> Image: ...
+ def bandrank(self, other: Union[Image, List[Image]], **kwargs: object) -> Image: ...
+ def composite(self, other: Union[Image, List[Image]], mode: Union[str, BlendMode, List[Union[str, BlendMode]]], **kwargs: object) -> Image: ...
+ def ifthenelse(self, in1: Union[Image, float, int], in2: Union[Image, float, int], **kwargs: object) -> Image: ...
+ def hasalpha(self) -> bool: ...
+ def get_n_pages(self) -> int: ...
+ def get_page_height(self) -> int: ...
+ def pagesplit(self) -> List[Image]: ...
+ def pagejoin(self, other: Union[Image, List[Image]]) -> Image: ...
+ def scaleimage(self, **kwargs: object) -> Image: ...
+ def erode(self, mask: Union[Image, List[List[int]]]) -> Image: ...
+ def dilate(self, mask: Union[Image, List[List[int]]]) -> Image: ...
+
+ # Dynamically generated operations
+ def CMC2LCh(self) -> Image: ...
+ def CMYK2XYZ(self) -> Image: ...
+ def HSV2sRGB(self) -> Image: ...
+ def LCh2CMC(self) -> Image: ...
+ def LCh2Lab(self) -> Image: ...
+ def Lab2LCh(self) -> Image: ...
+ def Lab2LabQ(self) -> Image: ...
+ def Lab2LabS(self) -> Image: ...
+ def Lab2XYZ(self, temp: list[float] = ...) -> Image: ...
+ def LabQ2Lab(self) -> Image: ...
+ def LabQ2LabS(self) -> Image: ...
+ def LabQ2sRGB(self) -> Image: ...
+ def LabS2Lab(self) -> Image: ...
+ def LabS2LabQ(self) -> Image: ...
+ def XYZ2CMYK(self) -> Image: ...
+ def XYZ2Lab(self, temp: list[float] = ...) -> Image: ...
+ def XYZ2Yxy(self) -> Image: ...
+ def XYZ2scRGB(self) -> Image: ...
+ def Yxy2XYZ(self) -> Image: ...
+ def abs(self) -> Image: ...
+ def add(self, right: Image) -> Image: ...
+ def addalpha(self) -> Image: ...
+ def affine(self, matrix: list[float], interpolate: GObject = ..., oarea: list[int] = ..., odx: float = ..., ody: float = ..., idx: float = ..., idy: float = ..., background: list[float] = ..., premultiplied: bool = ..., extend: Union[str, Extend] = ...) -> Image: ...
+ @staticmethod
+ def analyzeload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def arrayjoin(in_: list[Image], across: int = ..., shim: int = ..., background: list[float] = ..., halign: Union[str, Align] = ..., valign: Union[str, Align] = ..., hspacing: int = ..., vspacing: int = ...) -> Image: ...
+ def autorot(self, angle: bool = ..., flip: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def avg(self) -> float: ...
+ def bandbool(self, boolean: Union[str, OperationBoolean]) -> Image: ...
+ def bandfold(self, factor: int = ...) -> Image: ...
+ def bandjoin_const(self, c: list[float]) -> Image: ...
+ def bandmean(self) -> Image: ...
+ def bandunfold(self, factor: int = ...) -> Image: ...
+ @staticmethod
+ def black(width: int, height: int, bands: int = ...) -> Image: ...
+ def boolean(self, right: Image, boolean: Union[str, OperationBoolean]) -> Image: ...
+ def boolean_const(self, boolean: Union[str, OperationBoolean], c: list[float]) -> Image: ...
+ def buildlut(self) -> Image: ...
+ def byteswap(self) -> Image: ...
+ def canny(self, sigma: float = ..., precision: Union[str, Precision] = ...) -> Image: ...
+ def case(self, cases: list[Image]) -> Image: ...
+ def cast(self, format: Union[str, BandFormat], shift: bool = ...) -> Image: ...
+ def clamp(self, min_: float = ..., max_: float = ...) -> Image: ...
+ def colourspace(self, space: Union[str, Interpretation], source_space: Union[str, Interpretation] = ...) -> Image: ...
+ def compass(self, mask: Image, times: int = ..., angle: Union[str, Angle45] = ..., combine: Union[str, Combine] = ..., precision: Union[str, Precision] = ..., layers: int = ..., cluster: int = ...) -> Image: ...
+ def complex(self, cmplx: Union[str, OperationComplex]) -> Image: ...
+ def complex2(self, right: Image, cmplx: Union[str, OperationComplex2]) -> Image: ...
+ def complexform(self, right: Image) -> Image: ...
+ def complexget(self, get: Union[str, OperationComplexget]) -> Image: ...
+ def composite2(self, overlay: Image, mode: Union[str, BlendMode], x: int = ..., y: int = ..., compositing_space: Union[str, Interpretation] = ..., premultiplied: bool = ...) -> Image: ...
+ def conv(self, mask: Image, precision: Union[str, Precision] = ..., layers: int = ..., cluster: int = ...) -> Image: ...
+ def conva(self, mask: Image, layers: int = ..., cluster: int = ...) -> Image: ...
+ def convasep(self, mask: Image, layers: int = ...) -> Image: ...
+ def convf(self, mask: Image) -> Image: ...
+ def convi(self, mask: Image) -> Image: ...
+ def convsep(self, mask: Image, precision: Union[str, Precision] = ..., layers: int = ..., cluster: int = ...) -> Image: ...
+ def countlines(self, direction: Union[str, Direction]) -> float: ...
+ def crop(self, left: int, top: int, width: int, height: int) -> Image: ...
+ @staticmethod
+ def csvload(filename: str, skip: int = ..., lines: int = ..., whitespace: str = ..., separator: str = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def csvload_source(source: Source, skip: int = ..., lines: int = ..., whitespace: str = ..., separator: str = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def csvsave(self, filename: str, separator: str = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def csvsave_target(self, target: Target, separator: str = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def dE00(self, right: Image) -> Image: ...
+ def dE76(self, right: Image) -> Image: ...
+ def dECMC(self, right: Image) -> Image: ...
+ def deviate(self) -> float: ...
+ def divide(self, right: Image) -> Image: ...
+ def draw_circle(self, ink: list[float], cx: int, cy: int, radius: int, fill: bool = ...) -> Image: ...
+ def draw_flood(self, ink: list[float], x: int, y: int, test: Image = ..., equal: bool = ..., left: bool = ..., top: bool = ..., width: bool = ..., height: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def draw_image(self, sub: Image, x: int, y: int, mode: Union[str, CombineMode] = ...) -> Image: ...
+ def draw_line(self, ink: list[float], x1: int, y1: int, x2: int, y2: int) -> Image: ...
+ def draw_mask(self, ink: list[float], mask: Image, x: int, y: int) -> Image: ...
+ def draw_rect(self, ink: list[float], left: int, top: int, width: int, height: int, fill: bool = ...) -> Image: ...
+ def draw_smudge(self, left: int, top: int, width: int, height: int) -> Image: ...
+ def dzsave(self, filename: str, imagename: str = ..., layout: Union[str, ForeignDzLayout] = ..., suffix: str = ..., overlap: int = ..., tile_size: int = ..., centre: bool = ..., depth: Union[str, ForeignDzDepth] = ..., angle: Union[str, Angle] = ..., container: Union[str, ForeignDzContainer] = ..., compression: int = ..., region_shrink: Union[str, RegionShrink] = ..., skip_blanks: int = ..., id: str = ..., Q: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def dzsave_buffer(self, imagename: str = ..., layout: Union[str, ForeignDzLayout] = ..., suffix: str = ..., overlap: int = ..., tile_size: int = ..., centre: bool = ..., depth: Union[str, ForeignDzDepth] = ..., angle: Union[str, Angle] = ..., container: Union[str, ForeignDzContainer] = ..., compression: int = ..., region_shrink: Union[str, RegionShrink] = ..., skip_blanks: int = ..., id: str = ..., Q: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def dzsave_target(self, target: Target, imagename: str = ..., layout: Union[str, ForeignDzLayout] = ..., suffix: str = ..., overlap: int = ..., tile_size: int = ..., centre: bool = ..., depth: Union[str, ForeignDzDepth] = ..., angle: Union[str, Angle] = ..., container: Union[str, ForeignDzContainer] = ..., compression: int = ..., region_shrink: Union[str, RegionShrink] = ..., skip_blanks: int = ..., id: str = ..., Q: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def embed(self, x: int, y: int, width: int, height: int, extend: Union[str, Extend] = ..., background: list[float] = ...) -> Image: ...
+ def extract_area(self, left: int, top: int, width: int, height: int) -> Image: ...
+ def extract_band(self, band: int, n: int = ...) -> Image: ...
+ @staticmethod
+ def eye(width: int, height: int, uchar: bool = ..., factor: float = ...) -> Image: ...
+ def falsecolour(self) -> Image: ...
+ def fastcor(self, ref: Image) -> Image: ...
+ def fill_nearest(self, distance: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def find_trim(self, threshold: float = ..., background: list[float] = ..., line_art: bool = ...) -> tuple[int, int, int, int]: ...
+ @staticmethod
+ def fitsload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def fitsload_source(source: Source, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def fitssave(self, filename: str, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def flatten(self, background: list[float] = ..., max_alpha: float = ...) -> Image: ...
+ def flip(self, direction: Union[str, Direction]) -> Image: ...
+ def float2rad(self) -> Image: ...
+ @staticmethod
+ def fractsurf(width: int, height: int, fractal_dimension: float) -> Image: ...
+ def freqmult(self, mask: Image) -> Image: ...
+ def fwfft(self) -> Image: ...
+ def gamma(self, exponent: float = ...) -> Image: ...
+ def gaussblur(self, sigma: float, min_ampl: float = ..., precision: Union[str, Precision] = ...) -> Image: ...
+ @staticmethod
+ def gaussmat(sigma: float, min_ampl: float, separable: bool = ..., precision: Union[str, Precision] = ...) -> Image: ...
+ @staticmethod
+ def gaussnoise(width: int, height: int, sigma: float = ..., mean: float = ..., seed: int = ...) -> Image: ...
+ def getpoint(self, x: int, y: int, unpack_complex: bool = ...) -> list[float]: ...
+ @staticmethod
+ def gifload(filename: str, n: int = ..., page: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def gifload_buffer(buffer: str, n: int = ..., page: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def gifload_source(source: Source, n: int = ..., page: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def gifsave(self, filename: str, dither: float = ..., effort: int = ..., bitdepth: int = ..., interframe_maxerror: float = ..., reuse: bool = ..., interpalette_maxerror: float = ..., interlace: bool = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def gifsave_buffer(self, dither: float = ..., effort: int = ..., bitdepth: int = ..., interframe_maxerror: float = ..., reuse: bool = ..., interpalette_maxerror: float = ..., interlace: bool = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def gifsave_target(self, target: Target, dither: float = ..., effort: int = ..., bitdepth: int = ..., interframe_maxerror: float = ..., reuse: bool = ..., interpalette_maxerror: float = ..., interlace: bool = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def globalbalance(self, gamma: float = ..., int_output: bool = ...) -> Image: ...
+ def gravity(self, direction: Union[str, CompassDirection], width: int, height: int, extend: Union[str, Extend] = ..., background: list[float] = ...) -> Image: ...
+ @staticmethod
+ def grey(width: int, height: int, uchar: bool = ...) -> Image: ...
+ def grid(self, tile_height: int, across: int, down: int) -> Image: ...
+ @staticmethod
+ def heifload(filename: str, page: int = ..., n: int = ..., thumbnail: bool = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def heifload_buffer(buffer: str, page: int = ..., n: int = ..., thumbnail: bool = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def heifload_source(source: Source, page: int = ..., n: int = ..., thumbnail: bool = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def heifsave(self, filename: str, Q: int = ..., bitdepth: int = ..., lossless: bool = ..., compression: Union[str, ForeignHeifCompression] = ..., effort: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., encoder: Union[str, ForeignHeifEncoder] = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def heifsave_buffer(self, Q: int = ..., bitdepth: int = ..., lossless: bool = ..., compression: Union[str, ForeignHeifCompression] = ..., effort: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., encoder: Union[str, ForeignHeifEncoder] = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def heifsave_target(self, target: Target, Q: int = ..., bitdepth: int = ..., lossless: bool = ..., compression: Union[str, ForeignHeifCompression] = ..., effort: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., encoder: Union[str, ForeignHeifEncoder] = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def hist_cum(self) -> Image: ...
+ def hist_entropy(self) -> float: ...
+ def hist_equal(self, band: int = ...) -> Image: ...
+ def hist_find(self, band: int = ...) -> Image: ...
+ def hist_find_indexed(self, index: Image, combine: Union[str, Combine] = ...) -> Image: ...
+ def hist_find_ndim(self, bins: int = ...) -> Image: ...
+ def hist_ismonotonic(self) -> bool: ...
+ def hist_local(self, width: int, height: int, max_slope: int = ...) -> Image: ...
+ def hist_match(self, ref: Image) -> Image: ...
+ def hist_norm(self) -> Image: ...
+ def hist_plot(self) -> Image: ...
+ def hough_circle(self, scale: int = ..., min_radius: int = ..., max_radius: int = ...) -> Image: ...
+ def hough_line(self, width: int = ..., height: int = ...) -> Image: ...
+ def icc_export(self, pcs: Union[str, PCS] = ..., intent: Union[str, Intent] = ..., black_point_compensation: bool = ..., output_profile: str = ..., depth: int = ...) -> Image: ...
+ def icc_import(self, pcs: Union[str, PCS] = ..., intent: Union[str, Intent] = ..., black_point_compensation: bool = ..., embedded: bool = ..., input_profile: str = ...) -> Image: ...
+ def icc_transform(self, output_profile: str, pcs: Union[str, PCS] = ..., intent: Union[str, Intent] = ..., black_point_compensation: bool = ..., embedded: bool = ..., input_profile: str = ..., depth: int = ...) -> Image: ...
+ @staticmethod
+ def identity(bands: int = ..., ushort: bool = ..., size: int = ...) -> Image: ...
+ def insert(self, sub: Image, x: int, y: int, expand: bool = ..., background: list[float] = ...) -> Image: ...
+ def invert(self) -> Image: ...
+ def invertlut(self, size: int = ...) -> Image: ...
+ def invfft(self, real: bool = ...) -> Image: ...
+ def join(self, in2: Image, direction: Union[str, Direction], expand: bool = ..., shim: int = ..., background: list[float] = ..., align: Union[str, Align] = ...) -> Image: ...
+ @staticmethod
+ def jp2kload(filename: str, page: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def jp2kload_buffer(buffer: str, page: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def jp2kload_source(source: Source, page: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def jp2ksave(self, filename: str, tile_width: int = ..., tile_height: int = ..., lossless: bool = ..., Q: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def jp2ksave_buffer(self, tile_width: int = ..., tile_height: int = ..., lossless: bool = ..., Q: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def jp2ksave_target(self, target: Target, tile_width: int = ..., tile_height: int = ..., lossless: bool = ..., Q: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ @staticmethod
+ def jpegload(filename: str, shrink: int = ..., autorotate: bool = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def jpegload_buffer(buffer: str, shrink: int = ..., autorotate: bool = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def jpegload_source(source: Source, shrink: int = ..., autorotate: bool = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def jpegsave(self, filename: str, Q: int = ..., optimize_coding: bool = ..., interlace: bool = ..., trellis_quant: bool = ..., overshoot_deringing: bool = ..., optimize_scans: bool = ..., quant_table: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., restart_interval: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def jpegsave_buffer(self, Q: int = ..., optimize_coding: bool = ..., interlace: bool = ..., trellis_quant: bool = ..., overshoot_deringing: bool = ..., optimize_scans: bool = ..., quant_table: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., restart_interval: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def jpegsave_mime(self, Q: int = ..., optimize_coding: bool = ..., interlace: bool = ..., trellis_quant: bool = ..., overshoot_deringing: bool = ..., optimize_scans: bool = ..., quant_table: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., restart_interval: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def jpegsave_target(self, target: Target, Q: int = ..., optimize_coding: bool = ..., interlace: bool = ..., trellis_quant: bool = ..., overshoot_deringing: bool = ..., optimize_scans: bool = ..., quant_table: int = ..., subsample_mode: Union[str, ForeignSubsample] = ..., restart_interval: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ @staticmethod
+ def jxlload(filename: str, page: int = ..., n: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def jxlload_buffer(buffer: str, page: int = ..., n: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def jxlload_source(source: Source, page: int = ..., n: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def jxlsave(self, filename: str, tier: int = ..., distance: float = ..., effort: int = ..., lossless: bool = ..., Q: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def jxlsave_buffer(self, tier: int = ..., distance: float = ..., effort: int = ..., lossless: bool = ..., Q: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def jxlsave_target(self, target: Target, tier: int = ..., distance: float = ..., effort: int = ..., lossless: bool = ..., Q: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def labelregions(self, segments: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def linear(self, a: list[float], b: list[float], uchar: bool = ...) -> Image: ...
+ def linecache(self, tile_height: int = ..., access: Union[str, Access] = ..., threaded: bool = ..., persistent: bool = ...) -> Image: ...
+ @staticmethod
+ def logmat(sigma: float, min_ampl: float, separable: bool = ..., precision: Union[str, Precision] = ...) -> Image: ...
+ @staticmethod
+ def magickload(filename: str, density: str = ..., page: int = ..., n: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def magickload_buffer(buffer: str, density: str = ..., page: int = ..., n: int = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def magicksave(self, filename: str, format: str = ..., quality: int = ..., optimize_gif_frames: bool = ..., optimize_gif_transparency: bool = ..., bitdepth: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def magicksave_buffer(self, format: str = ..., quality: int = ..., optimize_gif_frames: bool = ..., optimize_gif_transparency: bool = ..., bitdepth: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def mapim(self, index: Image, interpolate: GObject = ..., background: list[float] = ..., premultiplied: bool = ..., extend: Union[str, Extend] = ...) -> Image: ...
+ def maplut(self, lut: Image, band: int = ...) -> Image: ...
+ @staticmethod
+ def mask_butterworth(width: int, height: int, order: float, frequency_cutoff: float, amplitude_cutoff: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_butterworth_band(width: int, height: int, order: float, frequency_cutoff_x: float, frequency_cutoff_y: float, radius: float, amplitude_cutoff: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_butterworth_ring(width: int, height: int, order: float, frequency_cutoff: float, amplitude_cutoff: float, ringwidth: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_fractal(width: int, height: int, fractal_dimension: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_gaussian(width: int, height: int, frequency_cutoff: float, amplitude_cutoff: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_gaussian_band(width: int, height: int, frequency_cutoff_x: float, frequency_cutoff_y: float, radius: float, amplitude_cutoff: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_gaussian_ring(width: int, height: int, frequency_cutoff: float, amplitude_cutoff: float, ringwidth: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_ideal(width: int, height: int, frequency_cutoff: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_ideal_band(width: int, height: int, frequency_cutoff_x: float, frequency_cutoff_y: float, radius: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ @staticmethod
+ def mask_ideal_ring(width: int, height: int, frequency_cutoff: float, ringwidth: float, uchar: bool = ..., nodc: bool = ..., reject: bool = ..., optical: bool = ...) -> Image: ...
+ def match(self, sec: Image, xr1: int, yr1: int, xs1: int, ys1: int, xr2: int, yr2: int, xs2: int, ys2: int, hwindow: int = ..., harea: int = ..., search: bool = ..., interpolate: GObject = ...) -> Image: ...
+ def math(self, math: Union[str, OperationMath]) -> Image: ...
+ def math2(self, right: Image, math2: Union[str, OperationMath2]) -> Image: ...
+ def math2_const(self, math2: Union[str, OperationMath2], c: list[float]) -> Image: ...
+ @staticmethod
+ def matload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def matrixinvert(self) -> Image: ...
+ @staticmethod
+ def matrixload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def matrixload_source(source: Source, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def matrixprint(self, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def matrixsave(self, filename: str, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def matrixsave_target(self, target: Target, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def max(self, size: int = ..., x: bool = ..., y: bool = ..., out_array: bool = ..., x_array: bool = ..., y_array: bool = ...) -> Union[float, tuple[float, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def maxpair(self, right: Image) -> Image: ...
+ def measure(self, h: int, v: int, left: int = ..., top: int = ..., width: int = ..., height: int = ...) -> Image: ...
+ def merge(self, sec: Image, direction: Union[str, Direction], dx: int, dy: int, mblend: int = ...) -> Image: ...
+ def min(self, size: int = ..., x: bool = ..., y: bool = ..., out_array: bool = ..., x_array: bool = ..., y_array: bool = ...) -> Union[float, tuple[float, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def minpair(self, right: Image) -> Image: ...
+ def morph(self, mask: Image, morph: Union[str, OperationMorphology]) -> Image: ...
+ def mosaic(self, sec: Image, direction: Union[str, Direction], xref: int, yref: int, xsec: int, ysec: int, hwindow: int = ..., harea: int = ..., mblend: int = ..., bandno: int = ..., dx0: bool = ..., dy0: bool = ..., scale1: bool = ..., angle1: bool = ..., dy1: bool = ..., dx1: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def mosaic1(self, sec: Image, direction: Union[str, Direction], xr1: int, yr1: int, xs1: int, ys1: int, xr2: int, yr2: int, xs2: int, ys2: int, hwindow: int = ..., harea: int = ..., search: bool = ..., interpolate: GObject = ..., mblend: int = ...) -> Image: ...
+ def msb(self, band: int = ...) -> Image: ...
+ def multiply(self, right: Image) -> Image: ...
+ @staticmethod
+ def openexrload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def openslideload(filename: str, level: int = ..., autocrop: bool = ..., associated: str = ..., attach_associated: bool = ..., rgb: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def openslideload_source(source: Source, level: int = ..., autocrop: bool = ..., associated: str = ..., attach_associated: bool = ..., rgb: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def pdfload(filename: str, page: int = ..., n: int = ..., dpi: float = ..., scale: float = ..., background: list[float] = ..., password: str = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def pdfload_buffer(buffer: str, page: int = ..., n: int = ..., dpi: float = ..., scale: float = ..., background: list[float] = ..., password: str = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def pdfload_source(source: Source, page: int = ..., n: int = ..., dpi: float = ..., scale: float = ..., background: list[float] = ..., password: str = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def percent(self, percent: float) -> int: ...
+ @staticmethod
+ def perlin(width: int, height: int, cell_size: int = ..., uchar: bool = ..., seed: int = ...) -> Image: ...
+ def phasecor(self, in2: Image) -> Image: ...
+ @staticmethod
+ def pngload(filename: str, unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def pngload_buffer(buffer: str, unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def pngload_source(source: Source, unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def pngsave(self, filename: str, compression: int = ..., interlace: bool = ..., filter: int = ..., palette: bool = ..., Q: int = ..., dither: float = ..., bitdepth: int = ..., effort: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def pngsave_buffer(self, compression: int = ..., interlace: bool = ..., filter: int = ..., palette: bool = ..., Q: int = ..., dither: float = ..., bitdepth: int = ..., effort: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def pngsave_target(self, target: Target, compression: int = ..., interlace: bool = ..., filter: int = ..., palette: bool = ..., Q: int = ..., dither: float = ..., bitdepth: int = ..., effort: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ @staticmethod
+ def ppmload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def ppmload_source(source: Source, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def ppmsave(self, filename: str, format: Union[str, ForeignPpmFormat] = ..., ascii: bool = ..., bitdepth: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def ppmsave_target(self, target: Target, format: Union[str, ForeignPpmFormat] = ..., ascii: bool = ..., bitdepth: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def premultiply(self, max_alpha: float = ...) -> Image: ...
+ def prewitt(self) -> Image: ...
+ def profile(self) -> tuple[Image, Image]: ...
+ @staticmethod
+ def profile_load(name: str) -> str: ...
+ def project(self) -> tuple[Image, Image]: ...
+ def quadratic(self, coeff: Image, interpolate: GObject = ...) -> Image: ...
+ def rad2float(self) -> Image: ...
+ @staticmethod
+ def radload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def radload_buffer(buffer: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def radload_source(source: Source, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def radsave(self, filename: str, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def radsave_buffer(self, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def radsave_target(self, target: Target, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def rank(self, width: int, height: int, index: int) -> Image: ...
+ @staticmethod
+ def rawload(filename: str, width: int, height: int, bands: int, offset: int = ..., format: Union[str, BandFormat] = ..., interpretation: Union[str, Interpretation] = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def rawsave(self, filename: str, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def rawsave_buffer(self, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def rawsave_target(self, target: Target, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def recomb(self, m: Image) -> Image: ...
+ def reduce(self, hshrink: float, vshrink: float, kernel: Union[str, Kernel] = ..., gap: float = ...) -> Image: ...
+ def reduceh(self, hshrink: float, kernel: Union[str, Kernel] = ..., gap: float = ...) -> Image: ...
+ def reducev(self, vshrink: float, kernel: Union[str, Kernel] = ..., gap: float = ...) -> Image: ...
+ def relational(self, right: Image, relational: Union[str, OperationRelational]) -> Image: ...
+ def relational_const(self, relational: Union[str, OperationRelational], c: list[float]) -> Image: ...
+ def remainder(self, right: Image) -> Image: ...
+ def remainder_const(self, c: list[float]) -> Image: ...
+ def replicate(self, across: int, down: int) -> Image: ...
+ def resize(self, scale: float, kernel: Union[str, Kernel] = ..., gap: float = ..., vscale: float = ...) -> Image: ...
+ def rot(self, angle: Union[str, Angle]) -> Image: ...
+ def rot45(self, angle: Union[str, Angle45] = ...) -> Image: ...
+ def rotate(self, angle: float, interpolate: GObject = ..., background: list[float] = ..., odx: float = ..., ody: float = ..., idx: float = ..., idy: float = ...) -> Image: ...
+ def round(self, round: Union[str, OperationRound]) -> Image: ...
+ def sRGB2HSV(self) -> Image: ...
+ def sRGB2scRGB(self) -> Image: ...
+ def scRGB2BW(self, depth: int = ...) -> Image: ...
+ def scRGB2XYZ(self) -> Image: ...
+ def scRGB2sRGB(self, depth: int = ...) -> Image: ...
+ def scharr(self) -> Image: ...
+ @staticmethod
+ def sdf(width: int, height: int, shape: Union[str, SdfShape], r: float = ..., a: list[float] = ..., b: list[float] = ..., corners: list[float] = ...) -> Image: ...
+ def sequential(self, tile_height: int = ...) -> Image: ...
+ def sharpen(self, sigma: float = ..., x1: float = ..., y2: float = ..., y3: float = ..., m1: float = ..., m2: float = ...) -> Image: ...
+ def shrink(self, hshrink: float, vshrink: float, ceil: bool = ...) -> Image: ...
+ def shrinkh(self, hshrink: int, ceil: bool = ...) -> Image: ...
+ def shrinkv(self, vshrink: int, ceil: bool = ...) -> Image: ...
+ def sign(self) -> Image: ...
+ def similarity(self, scale: float = ..., angle: float = ..., interpolate: GObject = ..., background: list[float] = ..., odx: float = ..., ody: float = ..., idx: float = ..., idy: float = ...) -> Image: ...
+ @staticmethod
+ def sines(width: int, height: int, uchar: bool = ..., hfreq: float = ..., vfreq: float = ...) -> Image: ...
+ def smartcrop(self, width: int, height: int, interesting: Union[str, Interesting] = ..., premultiplied: bool = ..., attention_x: bool = ..., attention_y: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def sobel(self) -> Image: ...
+ def spcor(self, ref: Image) -> Image: ...
+ def spectrum(self) -> Image: ...
+ def stats(self) -> Image: ...
+ def stdif(self, width: int, height: int, s0: float = ..., b: float = ..., m0: float = ..., a: float = ...) -> Image: ...
+ def subsample(self, xfac: int, yfac: int, point: bool = ...) -> Image: ...
+ def subtract(self, right: Image) -> Image: ...
+ @staticmethod
+ def sum(in_: list[Image]) -> Image: ...
+ @staticmethod
+ def svgload(filename: str, dpi: float = ..., scale: float = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def svgload_buffer(buffer: str, dpi: float = ..., scale: float = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def svgload_source(source: Source, dpi: float = ..., scale: float = ..., unlimited: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def switch(tests: list[Image]) -> Image: ...
+ @staticmethod
+ def system(cmd_format: str, in_: list[Image] = ..., out_format: str = ..., in_format: str = ..., out: bool = ..., log: bool = ...) -> Union[None, tuple[Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def text(text: str, font: str = ..., width: int = ..., height: int = ..., align: Union[str, Align] = ..., justify: bool = ..., dpi: int = ..., spacing: int = ..., fontfile: str = ..., rgba: bool = ..., wrap: Union[str, TextWrap] = ..., autofit_dpi: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def thumbnail(filename: str, width: int, height: int = ..., size: Union[str, Size] = ..., no_rotate: bool = ..., crop: Union[str, Interesting] = ..., linear: bool = ..., import_profile: str = ..., export_profile: str = ..., intent: Union[str, Intent] = ..., fail_on: Union[str, FailOn] = ...) -> Image: ...
+ @staticmethod
+ def thumbnail_buffer(buffer: str, width: int, option_string: str = ..., height: int = ..., size: Union[str, Size] = ..., no_rotate: bool = ..., crop: Union[str, Interesting] = ..., linear: bool = ..., import_profile: str = ..., export_profile: str = ..., intent: Union[str, Intent] = ..., fail_on: Union[str, FailOn] = ...) -> Image: ...
+ def thumbnail_image(self, width: int, height: int = ..., size: Union[str, Size] = ..., no_rotate: bool = ..., crop: Union[str, Interesting] = ..., linear: bool = ..., import_profile: str = ..., export_profile: str = ..., intent: Union[str, Intent] = ..., fail_on: Union[str, FailOn] = ...) -> Image: ...
+ @staticmethod
+ def thumbnail_source(source: Source, width: int, option_string: str = ..., height: int = ..., size: Union[str, Size] = ..., no_rotate: bool = ..., crop: Union[str, Interesting] = ..., linear: bool = ..., import_profile: str = ..., export_profile: str = ..., intent: Union[str, Intent] = ..., fail_on: Union[str, FailOn] = ...) -> Image: ...
+ @staticmethod
+ def tiffload(filename: str, page: int = ..., subifd: int = ..., n: int = ..., autorotate: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def tiffload_buffer(buffer: str, page: int = ..., subifd: int = ..., n: int = ..., autorotate: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def tiffload_source(source: Source, page: int = ..., subifd: int = ..., n: int = ..., autorotate: bool = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def tiffsave(self, filename: str, compression: Union[str, ForeignTiffCompression] = ..., Q: int = ..., predictor: Union[str, ForeignTiffPredictor] = ..., tile: bool = ..., tile_width: int = ..., tile_height: int = ..., pyramid: bool = ..., miniswhite: bool = ..., bitdepth: int = ..., resunit: Union[str, ForeignTiffResunit] = ..., xres: float = ..., yres: float = ..., bigtiff: bool = ..., properties: bool = ..., region_shrink: Union[str, RegionShrink] = ..., level: int = ..., lossless: bool = ..., depth: Union[str, ForeignDzDepth] = ..., subifd: bool = ..., premultiply: bool = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def tiffsave_buffer(self, compression: Union[str, ForeignTiffCompression] = ..., Q: int = ..., predictor: Union[str, ForeignTiffPredictor] = ..., tile: bool = ..., tile_width: int = ..., tile_height: int = ..., pyramid: bool = ..., miniswhite: bool = ..., bitdepth: int = ..., resunit: Union[str, ForeignTiffResunit] = ..., xres: float = ..., yres: float = ..., bigtiff: bool = ..., properties: bool = ..., region_shrink: Union[str, RegionShrink] = ..., level: int = ..., lossless: bool = ..., depth: Union[str, ForeignDzDepth] = ..., subifd: bool = ..., premultiply: bool = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def tiffsave_target(self, target: Target, compression: Union[str, ForeignTiffCompression] = ..., Q: int = ..., predictor: Union[str, ForeignTiffPredictor] = ..., tile: bool = ..., tile_width: int = ..., tile_height: int = ..., pyramid: bool = ..., miniswhite: bool = ..., bitdepth: int = ..., resunit: Union[str, ForeignTiffResunit] = ..., xres: float = ..., yres: float = ..., bigtiff: bool = ..., properties: bool = ..., region_shrink: Union[str, RegionShrink] = ..., level: int = ..., lossless: bool = ..., depth: Union[str, ForeignDzDepth] = ..., subifd: bool = ..., premultiply: bool = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def tilecache(self, tile_width: int = ..., tile_height: int = ..., max_tiles: int = ..., access: Union[str, Access] = ..., threaded: bool = ..., persistent: bool = ...) -> Image: ...
+ @staticmethod
+ def tonelut(in_max: int = ..., out_max: int = ..., Lb: float = ..., Lw: float = ..., Ps: float = ..., Pm: float = ..., Ph: float = ..., S: float = ..., M: float = ..., H: float = ...) -> Image: ...
+ def transpose3d(self, page_height: int = ...) -> Image: ...
+ def unpremultiply(self, max_alpha: float = ..., alpha_band: int = ...) -> Image: ...
+ @staticmethod
+ def vipsload(filename: str, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def vipsload_source(source: Source, memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def vipssave(self, filename: str, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def vipssave_target(self, target: Target, keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ @staticmethod
+ def webpload(filename: str, page: int = ..., n: int = ..., scale: float = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def webpload_buffer(buffer: str, page: int = ..., n: int = ..., scale: float = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ @staticmethod
+ def webpload_source(source: Source, page: int = ..., n: int = ..., scale: float = ..., memory: bool = ..., access: Union[str, Access] = ..., fail_on: Union[str, FailOn] = ..., revalidate: bool = ..., flags: bool = ...) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+ def webpsave(self, filename: str, Q: int = ..., lossless: bool = ..., preset: Union[str, ForeignWebpPreset] = ..., smart_subsample: bool = ..., near_lossless: bool = ..., alpha_q: int = ..., min_size: bool = ..., kmin: int = ..., kmax: int = ..., effort: int = ..., target_size: int = ..., mixed: bool = ..., smart_deblock: bool = ..., passes: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def webpsave_buffer(self, Q: int = ..., lossless: bool = ..., preset: Union[str, ForeignWebpPreset] = ..., smart_subsample: bool = ..., near_lossless: bool = ..., alpha_q: int = ..., min_size: bool = ..., kmin: int = ..., kmax: int = ..., effort: int = ..., target_size: int = ..., mixed: bool = ..., smart_deblock: bool = ..., passes: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> str: ...
+ def webpsave_mime(self, Q: int = ..., lossless: bool = ..., preset: Union[str, ForeignWebpPreset] = ..., smart_subsample: bool = ..., near_lossless: bool = ..., alpha_q: int = ..., min_size: bool = ..., kmin: int = ..., kmax: int = ..., effort: int = ..., target_size: int = ..., mixed: bool = ..., smart_deblock: bool = ..., passes: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ def webpsave_target(self, target: Target, Q: int = ..., lossless: bool = ..., preset: Union[str, ForeignWebpPreset] = ..., smart_subsample: bool = ..., near_lossless: bool = ..., alpha_q: int = ..., min_size: bool = ..., kmin: int = ..., kmax: int = ..., effort: int = ..., target_size: int = ..., mixed: bool = ..., smart_deblock: bool = ..., passes: int = ..., keep: int = ..., background: list[float] = ..., page_height: int = ..., profile: str = ...) -> None: ...
+ @staticmethod
+ def worley(width: int, height: int, cell_size: int = ..., seed: int = ...) -> Image: ...
+ def wrap(self, x: int = ..., y: int = ...) -> Image: ...
+ @staticmethod
+ def xyz(width: int, height: int, csize: int = ..., dsize: int = ..., esize: int = ...) -> Image: ...
+ @staticmethod
+ def zone(width: int, height: int, uchar: bool = ...) -> Image: ...
+ def zoom(self, xfac: int, yfac: int) -> Image: ...
+
+ # Operator overloads
+ def __add__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __radd__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __sub__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rsub__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __mul__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rmul__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __truediv__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rtruediv__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __floordiv__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rfloordiv__(self, other: Union[float, int, List[float], List[int]]) -> Image: ...
+ def __mod__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __pow__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rpow__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __abs__(self) -> Image: ...
+ def __neg__(self) -> Image: ...
+ def __pos__(self) -> Image: ...
+ def __invert__(self) -> Image: ...
+ def __lshift__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rshift__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __and__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rand__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __or__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __ror__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __xor__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __rxor__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __eq__(self, other: object) -> bool: ...
+ def __ne__(self, other: object) -> bool: ...
+ def __gt__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __ge__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __lt__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+ def __le__(self, other: Union[Image, float, int, List[float], List[int]]) -> Image: ...
+
+ def __getitem__(self, arg: Union[int, slice, List[int], List[bool]]) -> Image: ...
+ def __call__(self, x: int, y: int) -> List[float]: ...
+ def __repr__(self) -> str: ...
+
+
+class Operation: ...
+class Introspect: ...
+
+# Global functions
+def cache_set_max(mx: int) -> None: ...
+def cache_set_max_mem(mx: int) -> None: ...
+def cache_set_max_files(mx: int) -> None: ...
+def cache_set_trace(trace: bool) -> None: ...
+def cache_get_max() -> int: ...
+def cache_get_size() -> int: ...
+def cache_get_max_mem() -> int: ...
+def cache_get_max_files() -> int: ...
+def block_untrusted_set(state: bool) -> None: ...
+def operation_block_set(name: str, state: bool) -> None: ...
+def leak_set(leak: bool) -> None: ...
+def shutdown() -> None: ...
+def call(operation_name: str, *args: object, **kwargs: object) -> Union[Image, tuple[Image, Dict[str, Union[bool, int, float, str, Image, list[int], list[float], list[Image]]]]]: ...
+
+# Module-level constants
+API_mode: bool
diff --git a/pyvips/version.py b/pyvips/version.py
index 7942927..b263dbd 100644
--- a/pyvips/version.py
+++ b/pyvips/version.py
@@ -1,4 +1,4 @@
# this is used in pyproject.toml and imported into __init__.py
-__version__ = '3.1.1'
+__version__ = "3.2.0"
-__all__ = ['__version__']
+__all__ = ["__version__"]
diff --git a/tests/test_type_hints.py b/tests/test_type_hints.py
new file mode 100644
index 0000000..d86cffd
--- /dev/null
+++ b/tests/test_type_hints.py
@@ -0,0 +1,39 @@
+"""
+Test type stubs are valid and work with mypy.
+
+This test verifies the type stubs can be used for type checking.
+Run with: pytest -q tests/test_type_hints.py
+
+Also verify with mypy:
+ mypy tests/test_type_hints.py
+"""
+
+
+def test_type_stubs_basic():
+ """Test basic type stubs functionality."""
+ import pyvips
+
+ # Basic type checking - these should all be valid
+ img = pyvips.Image.black(100, 100, bands=3)
+ assert isinstance(img, pyvips.Image)
+
+ # Test method calls
+ inverted = img.invert()
+ assert isinstance(inverted, pyvips.Image)
+
+ resized = img.resize(0.5)
+ assert isinstance(resized, pyvips.Image)
+
+ # Test operators
+ result = img + 5
+ assert isinstance(result, pyvips.Image)
+
+ # Test metadata
+ typeof_val = img.get_typeof("xres")
+ assert isinstance(typeof_val, int)
+
+ # Test get returns union type
+ metadata = img.get("xres")
+ assert isinstance(metadata, float)
+
+ print("Type stubs test passed!")