PyPatchMatch 1.0.1__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. pypatchmatch-2.0.0/CLAUDE.md +155 -0
  2. pypatchmatch-2.0.0/CMakeLists.txt +104 -0
  3. pypatchmatch-2.0.0/PKG-INFO +176 -0
  4. pypatchmatch-2.0.0/README.md +146 -0
  5. pypatchmatch-2.0.0/SECURITY.md +20 -0
  6. pypatchmatch-2.0.0/examples/images/forest_pruned.bmp +0 -0
  7. pypatchmatch-2.0.0/patchmatch/__init__.py +19 -0
  8. pypatchmatch-2.0.0/patchmatch/_lib.py +122 -0
  9. pypatchmatch-2.0.0/patchmatch/_version.py +24 -0
  10. pypatchmatch-2.0.0/patchmatch/csrc/inpaint.cpp +389 -0
  11. pypatchmatch-2.0.0/patchmatch/csrc/inpaint.h +31 -0
  12. pypatchmatch-2.0.0/patchmatch/csrc/masked_image.cpp +175 -0
  13. {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/patchmatch/csrc/masked_image.h +28 -35
  14. pypatchmatch-2.0.0/patchmatch/csrc/nnf.cpp +332 -0
  15. {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/patchmatch/csrc/nnf.h +48 -47
  16. pypatchmatch-2.0.0/patchmatch/csrc/pyinterface.cpp +183 -0
  17. {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/patchmatch/csrc/pyinterface.h +12 -13
  18. pypatchmatch-2.0.0/patchmatch/patch_match.py +239 -0
  19. pypatchmatch-2.0.0/patchmatch/py.typed +0 -0
  20. pypatchmatch-2.0.0/pyproject.toml +162 -0
  21. pypatchmatch-2.0.0/scripts/build_opencv.py +197 -0
  22. pypatchmatch-2.0.0/scripts/evaluate_inpainting.py +518 -0
  23. pypatchmatch-2.0.0/scripts/render_readme_example.py +36 -0
  24. pypatchmatch-2.0.0/tests/conftest.py +6 -0
  25. pypatchmatch-2.0.0/tests/test_e2e.py +210 -0
  26. pypatchmatch-2.0.0/tests/test_patch_match.py +490 -0
  27. pypatchmatch-2.0.0/tests/test_properties.py +159 -0
  28. PyPatchMatch-1.0.1/PKG-INFO +0 -103
  29. PyPatchMatch-1.0.1/PyPatchMatch.egg-info/PKG-INFO +0 -103
  30. PyPatchMatch-1.0.1/PyPatchMatch.egg-info/SOURCES.txt +0 -20
  31. PyPatchMatch-1.0.1/PyPatchMatch.egg-info/dependency_links.txt +0 -1
  32. PyPatchMatch-1.0.1/PyPatchMatch.egg-info/requires.txt +0 -28
  33. PyPatchMatch-1.0.1/PyPatchMatch.egg-info/top_level.txt +0 -1
  34. PyPatchMatch-1.0.1/README.md +0 -67
  35. PyPatchMatch-1.0.1/patchmatch/Makefile +0 -60
  36. PyPatchMatch-1.0.1/patchmatch/__init__.py +0 -3
  37. PyPatchMatch-1.0.1/patchmatch/csrc/inpaint.cpp +0 -288
  38. PyPatchMatch-1.0.1/patchmatch/csrc/inpaint.h +0 -27
  39. PyPatchMatch-1.0.1/patchmatch/csrc/masked_image.cpp +0 -173
  40. PyPatchMatch-1.0.1/patchmatch/csrc/nnf.cpp +0 -316
  41. PyPatchMatch-1.0.1/patchmatch/csrc/pyinterface.cpp +0 -133
  42. PyPatchMatch-1.0.1/patchmatch/patch_match.py +0 -381
  43. PyPatchMatch-1.0.1/patchmatch/travis.sh +0 -9
  44. PyPatchMatch-1.0.1/pyproject.toml +0 -71
  45. PyPatchMatch-1.0.1/setup.cfg +0 -4
  46. {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/LICENSE +0 -0
@@ -0,0 +1,155 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in
4
+ this repository. The details of the native code, the C interface, packaging and CI are
5
+ path-scoped rules in `.claude/rules/`, which load when a matching file is read or
6
+ edited; read the rule before changing such a file through Bash. The sanitizer, C++
7
+ coverage and fill-quality runs are skills in `.claude/skills/`.
8
+
9
+ ## Project
10
+
11
+ PyPatchMatch implements PatchMatch based image inpainting (Barnes et al., SIGGRAPH 2009):
12
+ a C++17 library on top of OpenCV core, and a thin Python package that loads it through
13
+ `ctypes`. It is published on PyPI as `PyPatchMatch` (import name `patchmatch`) and used
14
+ by InvokeAI. This repository (`mauwii/PyPatchMatch`) is the maintained fork of
15
+ `invoke-ai/PyPatchMatch`, which derives from `vacancy/PyPatchMatch` and
16
+ `younesse-cv/PatchMatch`.
17
+
18
+ ## Commands
19
+
20
+ The project is managed with uv (`.python-version` pins 3.12 for the local venv). Building
21
+ needs a C++17 compiler, CMake >= 3.21 and an OpenCV that `find_package(OpenCV)` finds:
22
+ Homebrew `opencv`, `libopencv-dev`, or an installation prefix in `OpenCV_ROOT`.
23
+
24
+ ```sh
25
+ uv sync # .venv, editable install, compiles the library
26
+ uv run pre-commit install
27
+ uv run pytest # all tests, with the coverage gate
28
+ uv run pytest -m "not e2e" # fast unit tests only
29
+ uv run pytest -m e2e --no-cov # end-to-end tests only
30
+ uv run pytest tests/test_patch_match.py::test_inpaint_global_mask --no-cov
31
+ uv run pytest -k regularity --no-cov
32
+ uv run mypy # strict, also a pre-commit hook
33
+ uv run pre-commit run --all-files # what the CI lint job runs
34
+ uv run ruff check --fix . && uv run ruff format .
35
+ uv build # sdist and wheel into dist/
36
+ ```
37
+
38
+ - Pass `--no-cov` for any partial run: `addopts` always enables coverage, and
39
+ `fail_under = 95` (branch coverage, subprocesses included through
40
+ `patch = ["subprocess"]`) fails the run otherwise.
41
+ - Use `uv run pytest`, not `python -m pytest`. The latter puts the repository root on
42
+ `sys.path`, which imports the source tree `patchmatch/` without the compiled library;
43
+ the symptoms are the warning `patchmatch failed to load: libpatchmatch.* not found`
44
+ and `RuntimeError: the patchmatch native library is not available` in every test.
45
+ - `--cov=patchmatch` measures the source tree, so coverage only works with the editable
46
+ install that `uv sync` creates. With a non-editable install (`uv sync --no-editable`,
47
+ `uv pip install .`) it reports 0 % and the gate fails.
48
+ - `uv sync` rebuilds the library only when a `tool.uv.cache-keys` entry changes
49
+ (`CMakeLists.txt`, `patchmatch/csrc/*`, `pyproject.toml`, git commit or tags). Force it
50
+ with `uv sync --reinstall-package pypatchmatch`.
51
+ - CI builds the library with warnings as errors. Locally:
52
+ `SKBUILD_CMAKE_DEFINE=CMAKE_COMPILE_WARNING_AS_ERROR=ON uv sync --reinstall-package pypatchmatch`.
53
+
54
+ Build against the same static, core-only OpenCV as the release wheels:
55
+
56
+ ```sh
57
+ export OpenCV_ROOT=/tmp/opencv
58
+ uv run --no-project python scripts/build_opencv.py # pinned version and SHA-256
59
+ uv sync --reinstall-package pypatchmatch
60
+ ```
61
+
62
+ Other entry points:
63
+
64
+ - `examples/cpp_example_run.sh` builds the library and `examples/cpp_example.cpp` with
65
+ CMake into `build/cpp` and runs it. It needs OpenCV `imgcodecs`, so a full OpenCV, not
66
+ the core-only build of the script.
67
+ - `uv run python examples/py_example.py` (and `py_example_global_mask.py`) write
68
+ `examples/images/forest_recovered.bmp`, which is git-ignored.
69
+ - `uv run python scripts/render_readme_example.py` runs `py_example.py` and renders the
70
+ picture at the top of the README, `examples/images/readme_example.jpg`; rerun it and
71
+ commit the picture after changes that affect the fills.
72
+ - CMake options: `PATCHMATCH_BUILD_EXAMPLES` (OFF), `PATCHMATCH_FAST_MATH` (ON,
73
+ `-ffast-math` or `/fp:fast`), `PATCHMATCH_SANITIZE` (OFF, ASan and UBSan with
74
+ `float-cast-overflow`, every finding aborts; turns off fast math, which lets the
75
+ compiler drop checks; GCC and Clang only), `PATCHMATCH_COVERAGE` (OFF, gcov counters
76
+ at `-Og`; GCC and Clang only).
77
+
78
+ ## Architecture
79
+
80
+ ### Layers, in the call order of `patchmatch.inpaint`
81
+
82
+ 1. `patchmatch/__init__.py` re-exports the public API of `patch_match.py` and
83
+ `__version__` from the generated `_version.py`. `test_top_level_exports` checks that
84
+ every name in `patch_match.__all__` is re-exported.
85
+ 2. `patchmatch/patch_match.py` is the public API. It validates and canonicalizes the
86
+ inputs (`_prepare_inputs`, `_check_patch_size`, the `ijmap` and `guide_weight`
87
+ checks), picks the entry point (`PM_inpaint`, `PM_inpaint2` with a global mask, and
88
+ their `_regularity` variants) and runs it through `_call`. It loads the library at
89
+ import time; on `OSError` it logs a warning and sets `patchmatch_available = False`,
90
+ and every function then raises `RuntimeError` through `_get_lib()`.
91
+ 3. `patchmatch/_lib.py` is the ctypes layer: the `CShapeT`/`CMatT` structures, the dtype
92
+ table, `np_to_pymat`/`pymat_to_np`, `find_library` (looks for `libpatchmatch.so`,
93
+ `libpatchmatch.dylib` or `patchmatch.dll` in every directory of `patchmatch.__path__`)
94
+ and `load_library` (argtypes and restype of every `PM_*` function).
95
+ 4. `patchmatch/csrc/pyinterface.{h,cpp}` is the `extern "C"` API: the `PM_*` functions,
96
+ the conversion between `PM_mat_t` and `cv::Mat`, the global settings and the error
97
+ reporting.
98
+ 5. `csrc/inpaint.{h,cpp}`: `Inpainting`, the image pyramid and the EM loop.
99
+ 6. `csrc/nnf.{h,cpp}`: `NearestNeighborField` (propagation and random search) and the
100
+ distance metrics `PatchSSDDistanceMetric` and `RegularityGuidedPatchDistanceMetricV2`.
101
+ 7. `csrc/masked_image.{h,cpp}`: `MaskedImage`, an image with its hole mask and optional
102
+ global mask, down- and upsampling, gradients.
103
+
104
+ The C++ headers double as the C++ API: `examples/cpp_example.cpp` uses `Inpainting` and
105
+ `PatchSSDDistanceMetric` directly.
106
+
107
+ ### ctypes instead of a CPython extension
108
+
109
+ The library does not use the CPython ABI, so the wheels are tagged
110
+ `py3-none-<platform>` (`wheel.py-api = "py3"`): one wheel per platform serves all Python
111
+ versions, and cibuildwheel only builds `cp311-*`. Keep `csrc` free of `Python.h`,
112
+ pybind11 or nanobind. The C++ sources are excluded from the wheel (`wheel.exclude`);
113
+ CMake installs the shared library into the package directory.
114
+
115
+ ## Conventions
116
+
117
+ - Python >= 3.11: keep `from __future__ import annotations`. Ruff targets py311 with the
118
+ rules B, C4, E, F, I, RUF, SIM, UP and W, and formats at 88 columns. `patchmatch/csrc`
119
+ is excluded from ruff. C++ is formatted by clang-format (Microsoft style, 120 columns,
120
+ includes grouped as own header, `<...>`, `"..."`); naming is not checked: `m_`
121
+ members, `k` constants. Commit a reformat separately and add its hash to
122
+ `.git-blame-ignore-revs`.
123
+ - The package ships `py.typed`; public functions are fully annotated. mypy checks
124
+ `patchmatch` and `tests` in strict mode (`[tool.mypy]`); the tests have to use the
125
+ API correctly but need no annotations of their own. A deliberately wrong argument
126
+ goes through a test parameter instead of a `# type: ignore`.
127
+ - Backwards compatibility: `from patchmatch import patch_match` and
128
+ `patch_match.CShapeT`/`CMatT` keep working (tested).
129
+ - Comments explain why: which crash a check prevents, why a construct is avoided.
130
+ - No `NOSONAR` markers: change the code so that the SonarCloud rule is satisfied, and ask
131
+ when that seems impossible. The only exception is `nnf.cpp`: cpp:S2245 flags every
132
+ declaration of `std::mt19937`, which only drives the randomized search.
133
+ - Tests check invariants instead of golden images: holes filled, all other pixels
134
+ unchanged, determinism with `set_random_seed`, fills that blend in with their
135
+ surroundings. `test_patch_match.py` and `test_e2e.py` seed through an autouse fixture.
136
+ Unit tests use a small synthetic image; the `e2e` tests use
137
+ `examples/images/forest_pruned.bmp` and mirror the workflows of the README and the
138
+ examples. `test_properties.py` checks the invariants on random inputs with Hypothesis
139
+ (in the `test` group, so the wheel tests run it too); it seeds inside the tests,
140
+ because Hypothesis rejects function-scoped fixtures, and has no deadline, because
141
+ the sanitizer and coverage builds are much slower.
142
+ - The README documents the behavior (mask semantics, `patch_size` as a radius, the
143
+ fallback installation); update it together with API or installation changes.
144
+ - Commit messages: imperative subject in sentence case without a prefix (e.g. "Reject
145
+ invalid patch sizes and empty regularity maps"), and a body that explains the reason,
146
+ with measurements where relevant. Changes go through PRs from branches such as
147
+ `fix/<topic>`.
148
+
149
+ ## Git and GitHub
150
+
151
+ - `origin` is `mauwii/PyPatchMatch`, where PRs and releases go; `upstream` is
152
+ `invoke-ai/PyPatchMatch`. `gh` resolves the default repository to upstream, so pass
153
+ `-R mauwii/PyPatchMatch`.
154
+ - Releases: creating a GitHub release with a tag like `v2.0.0` (or `v2.0.0rc2` for
155
+ pre-releases) builds the wheels and publishes them to PyPI.
@@ -0,0 +1,104 @@
1
+ cmake_minimum_required(VERSION 3.21)
2
+ project(patchmatch LANGUAGES CXX)
3
+
4
+ option(PATCHMATCH_BUILD_EXAMPLES "Build the C++ example (requires OpenCV imgcodecs)" OFF)
5
+ option(PATCHMATCH_FAST_MATH "Compile with fast-math optimizations" ON)
6
+ option(PATCHMATCH_SANITIZE "Build with AddressSanitizer and UndefinedBehaviorSanitizer, without fast math (GCC, Clang)" OFF)
7
+ option(PATCHMATCH_COVERAGE "Build with gcov coverage counters at -Og (GCC, Clang)" OFF)
8
+
9
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
10
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
11
+ endif()
12
+
13
+ set(CMAKE_CXX_STANDARD 17)
14
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
15
+ set(CMAKE_CXX_EXTENSIONS OFF)
16
+ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
17
+
18
+ set(PATCHMATCH_OPENCV_COMPONENTS core)
19
+ if(PATCHMATCH_BUILD_EXAMPLES)
20
+ list(APPEND PATCHMATCH_OPENCV_COMPONENTS imgcodecs)
21
+ endif()
22
+ find_package(OpenCV REQUIRED COMPONENTS ${PATCHMATCH_OPENCV_COMPONENTS})
23
+
24
+ add_library(patchmatch SHARED
25
+ patchmatch/csrc/inpaint.cpp
26
+ patchmatch/csrc/masked_image.cpp
27
+ patchmatch/csrc/nnf.cpp
28
+ patchmatch/csrc/pyinterface.cpp
29
+ )
30
+ target_include_directories(patchmatch PUBLIC patchmatch/csrc)
31
+ target_link_libraries(patchmatch PUBLIC opencv_core)
32
+
33
+ # CI adds CMAKE_COMPILE_WARNING_AS_ERROR; the OpenCV headers are system headers
34
+ if(MSVC)
35
+ target_compile_options(patchmatch PRIVATE /W4)
36
+ else()
37
+ target_compile_options(patchmatch PRIVATE -Wall -Wextra -Wpedantic -Wshadow)
38
+ endif()
39
+
40
+ if(MSVC AND NOT OpenCV_SHARED)
41
+ # A static OpenCV (as built by scripts/build_opencv.py) uses the static MSVC runtime.
42
+ set_property(TARGET patchmatch PROPERTY
43
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
44
+ endif()
45
+
46
+ if(PATCHMATCH_FAST_MATH AND NOT PATCHMATCH_SANITIZE)
47
+ if(MSVC)
48
+ target_compile_options(patchmatch PRIVATE /fp:fast)
49
+ else()
50
+ target_compile_options(patchmatch PRIVATE -ffast-math)
51
+ endif()
52
+ endif()
53
+
54
+ if(PATCHMATCH_SANITIZE)
55
+ if(MSVC)
56
+ message(FATAL_ERROR "PATCHMATCH_SANITIZE needs GCC or Clang")
57
+ endif()
58
+ # GCC's -fsanitize=undefined leaves out float-cast-overflow
59
+ set(PATCHMATCH_SANITIZERS -fsanitize=address,undefined,float-cast-overflow)
60
+ target_compile_options(patchmatch PRIVATE ${PATCHMATCH_SANITIZERS}
61
+ -fno-sanitize-recover=all -fno-omit-frame-pointer -g)
62
+ target_link_options(patchmatch PRIVATE ${PATCHMATCH_SANITIZERS})
63
+ endif()
64
+
65
+ if(PATCHMATCH_COVERAGE)
66
+ if(MSVC)
67
+ message(FATAL_ERROR "PATCHMATCH_COVERAGE needs GCC or Clang")
68
+ endif()
69
+ # atomic counters, since a test inpaints in parallel; -O0 would triple the test time
70
+ target_compile_options(patchmatch PRIVATE --coverage -fprofile-update=atomic -Og)
71
+ target_link_options(patchmatch PRIVATE --coverage)
72
+ endif()
73
+
74
+ if(PATCHMATCH_BUILD_EXAMPLES)
75
+ add_executable(cpp_example examples/cpp_example.cpp)
76
+ target_link_libraries(cpp_example PRIVATE patchmatch opencv_imgcodecs)
77
+ endif()
78
+
79
+ # The Python package loads the library via ctypes from its own directory.
80
+ install(TARGETS patchmatch
81
+ LIBRARY DESTINATION patchmatch
82
+ RUNTIME DESTINATION patchmatch
83
+ )
84
+
85
+ # A static OpenCV becomes part of the library, so a wheel has to ship its licenses and
86
+ # an SBOM (PEP 770), which scripts/build_opencv.py writes to <prefix>/licenses and
87
+ # <prefix>/sboms.
88
+ if(DEFINED SKBUILD_METADATA_DIR AND NOT OpenCV_SHARED)
89
+ set(PATCHMATCH_OPENCV_LICENSES "${OpenCV_INSTALL_PATH}/licenses")
90
+ if(EXISTS "${PATCHMATCH_OPENCV_LICENSES}/opencv-LICENSE")
91
+ install(DIRECTORY "${PATCHMATCH_OPENCV_LICENSES}/"
92
+ DESTINATION "${SKBUILD_METADATA_DIR}/licenses/opencv")
93
+ else()
94
+ message(WARNING "No OpenCV licenses in ${PATCHMATCH_OPENCV_LICENSES}, "
95
+ "the wheel will not include them")
96
+ endif()
97
+ set(PATCHMATCH_OPENCV_SBOM "${OpenCV_INSTALL_PATH}/sboms/opencv.cdx.json")
98
+ if(EXISTS "${PATCHMATCH_OPENCV_SBOM}")
99
+ install(FILES "${PATCHMATCH_OPENCV_SBOM}" DESTINATION "${SKBUILD_METADATA_DIR}/sboms")
100
+ else()
101
+ message(WARNING "No OpenCV SBOM at ${PATCHMATCH_OPENCV_SBOM}, "
102
+ "the wheel will not include it")
103
+ endif()
104
+ endif()
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyPatchMatch
3
+ Version: 2.0.0
4
+ Summary: This library implements the PatchMatch based inpainting algorithm.
5
+ Keywords: inpainting,patchmatch,image-processing,opencv
6
+ Author-Email: The InvokeAI Project <lincoln.stein@gmail.com>, Jiayuan Mao <maojiayuan@gmail.com>, Kyle Schouviller <kyle0654@hotmail.com>, Matthias Wild <mauwii@outlook.de>, Younesse ANDAM <younesse.andam@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Operating System :: MacOS
11
+ Classifier: Operating System :: Microsoft :: Windows
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: C++
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
20
+ Classifier: Topic :: Multimedia :: Graphics
21
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
22
+ Classifier: Typing :: Typed
23
+ Project-URL: Source Code, https://github.com/mauwii/PyPatchMatch
24
+ Project-URL: Issues, https://github.com/mauwii/PyPatchMatch/issues
25
+ Project-URL: Release Notes, https://github.com/mauwii/PyPatchMatch/releases
26
+ Requires-Python: >=3.11
27
+ Requires-Dist: numpy>=1.23.2
28
+ Requires-Dist: pillow>=9.2.0
29
+ Description-Content-Type: text/markdown
30
+
31
+ # PatchMatch based Inpainting
32
+
33
+ [![CI](https://github.com/mauwii/PyPatchMatch/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mauwii/PyPatchMatch/actions/workflows/ci.yml)
34
+ [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=mauwii_PyPatchMatch&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=mauwii_PyPatchMatch)
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blueviolet.svg)](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE)
36
+ [![PyPI](https://img.shields.io/pypi/v/PyPatchMatch)](https://pypi.org/project/PyPatchMatch/)
37
+ [![Downloads](https://static.pepy.tech/badge/pypatchmatch)](https://pepy.tech/projects/pypatchmatch)
38
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
39
+
40
+ This library implements the PatchMatch based inpainting algorithm. It provides both C++
41
+ and Python interfaces. This implementation is heavily based on the implementation by
42
+ Younesse ANDAM: [younesse-cv/PatchMatch](https://github.com/younesse-cv/PatchMatch),
43
+ with some bug fixes, and updates.
44
+
45
+ ![Birdhouses removed from a photo by patchmatch.inpaint](https://raw.githubusercontent.com/mauwii/PyPatchMatch/v2.0.0/examples/images/readme_example.jpg)
46
+
47
+ From left to right: the photo, the input of
48
+ [examples/py_example.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example.py)
49
+ with the birdhouses painted out in white, and its result.
50
+
51
+ ## Installation
52
+
53
+ ```sh
54
+ pip install PyPatchMatch
55
+ ```
56
+
57
+ Wheels for Linux (glibc, x86_64 and aarch64), macOS 11+ (arm64 and x86_64) and Windows
58
+ (x64) ship the compiled library with a statically linked OpenCV, so no compiler or
59
+ OpenCV installation is needed. On other platforms, e.g. Alpine Linux or Windows on ARM,
60
+ pip builds from the source distribution, which requires a C++17 compiler, CMake and the
61
+ OpenCV development files (e.g. `apt install libopencv-dev` or `brew install opencv`).
62
+
63
+ If that build fails, PyPatchMatch is installed without the native library instead of
64
+ blocking the installation: `patchmatch.patchmatch_available` is `False` and the
65
+ inpainting functions raise `RuntimeError`. After installing the missing tools, reinstall
66
+ it without the cached build:
67
+
68
+ ```sh
69
+ pip install --force-reinstall --no-deps --no-cache-dir PyPatchMatch
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ Python (see
75
+ [examples/py_example.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example.py)):
76
+
77
+ ```python
78
+ import patchmatch
79
+
80
+ image = ... # HxWx3 uint8 numpy array or PIL image
81
+ mask = ... # HxW uint8 or bool numpy array or PIL image, non-zero marks the holes
82
+ result = patchmatch.inpaint(image, mask, patch_size=3)
83
+ ```
84
+
85
+ The mask must have the same height and width as the image. If `mask` is omitted, all
86
+ pure white pixels are treated as holes. Only the holes are filled; all other pixels keep
87
+ their values, and the colors under the holes have no influence on the fill. Holes need
88
+ hard edges: an anti-aliased brush leaves light pixels along the edge of its strokes that
89
+ are not pure white, so they count as known and the fill continues them. Paint without
90
+ anti-aliasing or pass a mask that covers those pixels as well. The optional keyword
91
+ argument `global_mask`, in the same format, marks pixels that are neither filled nor
92
+ used as a source; they keep their values as well, and their colors have no influence on
93
+ the fill (see
94
+ [examples/py_example_global_mask.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example_global_mask.py)).
95
+ `patchmatch.patchmatch_available` tells whether the native library could be loaded.
96
+ The previous import path `from patchmatch import patch_match` keeps working.
97
+
98
+ `patch_size` is the radius of the compared patches, which span `2 * patch_size + 1`
99
+ pixels in each direction. Larger patches follow larger structures but are much slower;
100
+ the default of 15 compares 31x31 patches, the examples use 3.
101
+
102
+ `patchmatch.set_random_seed(seed)` sets the seed of the randomized search and
103
+ `patchmatch.set_verbose(True)` prints the progress of the native code to stderr.
104
+ `patchmatch.inpaint_regularity(image, mask, ijmap)` additionally guides the search with
105
+ a regularity map, an HxWx3 float32 array with the regularity coordinates of each pixel
106
+ in its first two channels. Its `guide_weight` must be at least 0.
107
+
108
+ C++ (see
109
+ [examples/cpp_example.cpp](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example.cpp),
110
+ build and run it with
111
+ [examples/cpp_example_run.sh](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example_run.sh)):
112
+
113
+ ```cpp
114
+ #include "inpaint.h"
115
+
116
+ int main() {
117
+ cv::Mat image = ...;
118
+ cv::Mat mask = ...;
119
+
120
+ auto metric = PatchSSDDistanceMetric(5);
121
+ cv::Mat result = Inpainting(image, mask, &metric).run();
122
+ }
123
+ ```
124
+
125
+ The library is built with CMake; `PATCHMATCH_BUILD_EXAMPLES` builds the example.
126
+
127
+ ## Development
128
+
129
+ The project is managed with [uv](https://docs.astral.sh/uv/):
130
+
131
+ ```sh
132
+ uv sync # create .venv, build the library, install dev deps
133
+ uv run pre-commit install # enable ruff and the other hooks on commit
134
+ uv run pytest # run the test suite
135
+ uv run pytest -m "not e2e" # only the fast unit tests
136
+ uv run python scripts/evaluate_inpainting.py # measure the fill quality
137
+ ```
138
+
139
+ The evaluation cuts holes into the images of `examples/images`, compares the fills with
140
+ the original content and writes `examples/images/evaluation.html`, which shows them side
141
+ by side. `--baseline` evaluates another build of the library as well, e.g. of the main
142
+ branch; the docstring of the script explains the columns.
143
+ Pull requests that change the C++ code get this comparison with their base commit in the
144
+ summary of the workflow "Fill quality", and the HTML report as its artifact.
145
+
146
+ Releases are published to PyPI by creating a GitHub release; the version is taken from
147
+ its tag (e.g. `v2.0.0`). Every released wheel and sdist carries a build provenance
148
+ attestation: `gh attestation verify <file> -R mauwii/PyPatchMatch`.
149
+
150
+ ## License
151
+
152
+ PyPatchMatch is released under the
153
+ [MIT License](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE). The wheels
154
+ contain a statically linked build of OpenCV core (Apache-2.0) and its bundled
155
+ third-party code; their licenses are included in the `licenses/opencv` directory of the
156
+ wheel's `.dist-info`, and a CycloneDX SBOM of OpenCV (PEP 770) in
157
+ `sboms/opencv.cdx.json`, so that vulnerability scanners can find it.
158
+
159
+ ## README and COPYRIGHT by Younesse ANDAM
160
+
161
+ @Author: Younesse ANDAM
162
+
163
+ @Contact: <younesse.andam@gmail.com>
164
+
165
+ Description:
166
+
167
+ This project is a personal implementation of an algorithm called PATCHMATCH
168
+ that restores missing areas in an image. The algorithm is presented in the following
169
+ paper PatchMatch A Randomized Correspondence Algorithm for Structural Image Editing by
170
+ C.Barnes, E.Shechtman, A.Finkelstein and Dan B.Goldman ACM Transactions on Graphics
171
+ (Proc. SIGGRAPH), vol.28, aug-2009
172
+
173
+ For more information please refer to
174
+ <https://gfx.cs.princeton.edu/pubs/Barnes_2009_PAR/>
175
+
176
+ Copyright (c) 2010-2011
@@ -0,0 +1,146 @@
1
+ # PatchMatch based Inpainting
2
+
3
+ [![CI](https://github.com/mauwii/PyPatchMatch/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mauwii/PyPatchMatch/actions/workflows/ci.yml)
4
+ [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=mauwii_PyPatchMatch&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=mauwii_PyPatchMatch)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blueviolet.svg)](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE)
6
+ [![PyPI](https://img.shields.io/pypi/v/PyPatchMatch)](https://pypi.org/project/PyPatchMatch/)
7
+ [![Downloads](https://static.pepy.tech/badge/pypatchmatch)](https://pepy.tech/projects/pypatchmatch)
8
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+
10
+ This library implements the PatchMatch based inpainting algorithm. It provides both C++
11
+ and Python interfaces. This implementation is heavily based on the implementation by
12
+ Younesse ANDAM: [younesse-cv/PatchMatch](https://github.com/younesse-cv/PatchMatch),
13
+ with some bug fixes, and updates.
14
+
15
+ ![Birdhouses removed from a photo by patchmatch.inpaint](examples/images/readme_example.jpg)
16
+
17
+ From left to right: the photo, the input of
18
+ [examples/py_example.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example.py)
19
+ with the birdhouses painted out in white, and its result.
20
+
21
+ ## Installation
22
+
23
+ ```sh
24
+ pip install PyPatchMatch
25
+ ```
26
+
27
+ Wheels for Linux (glibc, x86_64 and aarch64), macOS 11+ (arm64 and x86_64) and Windows
28
+ (x64) ship the compiled library with a statically linked OpenCV, so no compiler or
29
+ OpenCV installation is needed. On other platforms, e.g. Alpine Linux or Windows on ARM,
30
+ pip builds from the source distribution, which requires a C++17 compiler, CMake and the
31
+ OpenCV development files (e.g. `apt install libopencv-dev` or `brew install opencv`).
32
+
33
+ If that build fails, PyPatchMatch is installed without the native library instead of
34
+ blocking the installation: `patchmatch.patchmatch_available` is `False` and the
35
+ inpainting functions raise `RuntimeError`. After installing the missing tools, reinstall
36
+ it without the cached build:
37
+
38
+ ```sh
39
+ pip install --force-reinstall --no-deps --no-cache-dir PyPatchMatch
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ Python (see
45
+ [examples/py_example.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example.py)):
46
+
47
+ ```python
48
+ import patchmatch
49
+
50
+ image = ... # HxWx3 uint8 numpy array or PIL image
51
+ mask = ... # HxW uint8 or bool numpy array or PIL image, non-zero marks the holes
52
+ result = patchmatch.inpaint(image, mask, patch_size=3)
53
+ ```
54
+
55
+ The mask must have the same height and width as the image. If `mask` is omitted, all
56
+ pure white pixels are treated as holes. Only the holes are filled; all other pixels keep
57
+ their values, and the colors under the holes have no influence on the fill. Holes need
58
+ hard edges: an anti-aliased brush leaves light pixels along the edge of its strokes that
59
+ are not pure white, so they count as known and the fill continues them. Paint without
60
+ anti-aliasing or pass a mask that covers those pixels as well. The optional keyword
61
+ argument `global_mask`, in the same format, marks pixels that are neither filled nor
62
+ used as a source; they keep their values as well, and their colors have no influence on
63
+ the fill (see
64
+ [examples/py_example_global_mask.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example_global_mask.py)).
65
+ `patchmatch.patchmatch_available` tells whether the native library could be loaded.
66
+ The previous import path `from patchmatch import patch_match` keeps working.
67
+
68
+ `patch_size` is the radius of the compared patches, which span `2 * patch_size + 1`
69
+ pixels in each direction. Larger patches follow larger structures but are much slower;
70
+ the default of 15 compares 31x31 patches, the examples use 3.
71
+
72
+ `patchmatch.set_random_seed(seed)` sets the seed of the randomized search and
73
+ `patchmatch.set_verbose(True)` prints the progress of the native code to stderr.
74
+ `patchmatch.inpaint_regularity(image, mask, ijmap)` additionally guides the search with
75
+ a regularity map, an HxWx3 float32 array with the regularity coordinates of each pixel
76
+ in its first two channels. Its `guide_weight` must be at least 0.
77
+
78
+ C++ (see
79
+ [examples/cpp_example.cpp](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example.cpp),
80
+ build and run it with
81
+ [examples/cpp_example_run.sh](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example_run.sh)):
82
+
83
+ ```cpp
84
+ #include "inpaint.h"
85
+
86
+ int main() {
87
+ cv::Mat image = ...;
88
+ cv::Mat mask = ...;
89
+
90
+ auto metric = PatchSSDDistanceMetric(5);
91
+ cv::Mat result = Inpainting(image, mask, &metric).run();
92
+ }
93
+ ```
94
+
95
+ The library is built with CMake; `PATCHMATCH_BUILD_EXAMPLES` builds the example.
96
+
97
+ ## Development
98
+
99
+ The project is managed with [uv](https://docs.astral.sh/uv/):
100
+
101
+ ```sh
102
+ uv sync # create .venv, build the library, install dev deps
103
+ uv run pre-commit install # enable ruff and the other hooks on commit
104
+ uv run pytest # run the test suite
105
+ uv run pytest -m "not e2e" # only the fast unit tests
106
+ uv run python scripts/evaluate_inpainting.py # measure the fill quality
107
+ ```
108
+
109
+ The evaluation cuts holes into the images of `examples/images`, compares the fills with
110
+ the original content and writes `examples/images/evaluation.html`, which shows them side
111
+ by side. `--baseline` evaluates another build of the library as well, e.g. of the main
112
+ branch; the docstring of the script explains the columns.
113
+ Pull requests that change the C++ code get this comparison with their base commit in the
114
+ summary of the workflow "Fill quality", and the HTML report as its artifact.
115
+
116
+ Releases are published to PyPI by creating a GitHub release; the version is taken from
117
+ its tag (e.g. `v2.0.0`). Every released wheel and sdist carries a build provenance
118
+ attestation: `gh attestation verify <file> -R mauwii/PyPatchMatch`.
119
+
120
+ ## License
121
+
122
+ PyPatchMatch is released under the
123
+ [MIT License](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE). The wheels
124
+ contain a statically linked build of OpenCV core (Apache-2.0) and its bundled
125
+ third-party code; their licenses are included in the `licenses/opencv` directory of the
126
+ wheel's `.dist-info`, and a CycloneDX SBOM of OpenCV (PEP 770) in
127
+ `sboms/opencv.cdx.json`, so that vulnerability scanners can find it.
128
+
129
+ ## README and COPYRIGHT by Younesse ANDAM
130
+
131
+ @Author: Younesse ANDAM
132
+
133
+ @Contact: <younesse.andam@gmail.com>
134
+
135
+ Description:
136
+
137
+ This project is a personal implementation of an algorithm called PATCHMATCH
138
+ that restores missing areas in an image. The algorithm is presented in the following
139
+ paper PatchMatch A Randomized Correspondence Algorithm for Structural Image Editing by
140
+ C.Barnes, E.Shechtman, A.Finkelstein and Dan B.Goldman ACM Transactions on Graphics
141
+ (Proc. SIGGRAPH), vol.28, aug-2009
142
+
143
+ For more information please refer to
144
+ <https://gfx.cs.princeton.edu/pubs/Barnes_2009_PAR/>
145
+
146
+ Copyright (c) 2010-2011
@@ -0,0 +1,20 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Security fixes are released as a new version of PyPatchMatch on PyPI. Only the latest
6
+ release is supported; older versions are not updated.
7
+
8
+ ## Reporting a Vulnerability
9
+
10
+ Please report vulnerabilities privately through GitHub's
11
+ [vulnerability reporting](https://github.com/mauwii/PyPatchMatch/security/advisories/new),
12
+ not in a public issue.
13
+
14
+ ## OpenCV
15
+
16
+ The wheels on PyPI contain a statically linked, core-only build of OpenCV; its version
17
+ is listed in the CycloneDX SBOM in `sboms/opencv.cdx.json` of each wheel's
18
+ `.dist-info`. A vulnerability in OpenCV core is fixed by a new PyPatchMatch release
19
+ with an updated OpenCV. Installations built from the source distribution link the
20
+ OpenCV of the system and get its fixes from there.
@@ -0,0 +1,19 @@
1
+ """PatchMatch based inpainting."""
2
+
3
+ from ._version import __version__
4
+ from .patch_match import (
5
+ inpaint,
6
+ inpaint_regularity,
7
+ patchmatch_available,
8
+ set_random_seed,
9
+ set_verbose,
10
+ )
11
+
12
+ __all__ = [
13
+ "__version__",
14
+ "inpaint",
15
+ "inpaint_regularity",
16
+ "patchmatch_available",
17
+ "set_random_seed",
18
+ "set_verbose",
19
+ ]