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.
- pypatchmatch-2.0.0/CLAUDE.md +155 -0
- pypatchmatch-2.0.0/CMakeLists.txt +104 -0
- pypatchmatch-2.0.0/PKG-INFO +176 -0
- pypatchmatch-2.0.0/README.md +146 -0
- pypatchmatch-2.0.0/SECURITY.md +20 -0
- pypatchmatch-2.0.0/examples/images/forest_pruned.bmp +0 -0
- pypatchmatch-2.0.0/patchmatch/__init__.py +19 -0
- pypatchmatch-2.0.0/patchmatch/_lib.py +122 -0
- pypatchmatch-2.0.0/patchmatch/_version.py +24 -0
- pypatchmatch-2.0.0/patchmatch/csrc/inpaint.cpp +389 -0
- pypatchmatch-2.0.0/patchmatch/csrc/inpaint.h +31 -0
- pypatchmatch-2.0.0/patchmatch/csrc/masked_image.cpp +175 -0
- {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/patchmatch/csrc/masked_image.h +28 -35
- pypatchmatch-2.0.0/patchmatch/csrc/nnf.cpp +332 -0
- {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/patchmatch/csrc/nnf.h +48 -47
- pypatchmatch-2.0.0/patchmatch/csrc/pyinterface.cpp +183 -0
- {PyPatchMatch-1.0.1 → pypatchmatch-2.0.0}/patchmatch/csrc/pyinterface.h +12 -13
- pypatchmatch-2.0.0/patchmatch/patch_match.py +239 -0
- pypatchmatch-2.0.0/patchmatch/py.typed +0 -0
- pypatchmatch-2.0.0/pyproject.toml +162 -0
- pypatchmatch-2.0.0/scripts/build_opencv.py +197 -0
- pypatchmatch-2.0.0/scripts/evaluate_inpainting.py +518 -0
- pypatchmatch-2.0.0/scripts/render_readme_example.py +36 -0
- pypatchmatch-2.0.0/tests/conftest.py +6 -0
- pypatchmatch-2.0.0/tests/test_e2e.py +210 -0
- pypatchmatch-2.0.0/tests/test_patch_match.py +490 -0
- pypatchmatch-2.0.0/tests/test_properties.py +159 -0
- PyPatchMatch-1.0.1/PKG-INFO +0 -103
- PyPatchMatch-1.0.1/PyPatchMatch.egg-info/PKG-INFO +0 -103
- PyPatchMatch-1.0.1/PyPatchMatch.egg-info/SOURCES.txt +0 -20
- PyPatchMatch-1.0.1/PyPatchMatch.egg-info/dependency_links.txt +0 -1
- PyPatchMatch-1.0.1/PyPatchMatch.egg-info/requires.txt +0 -28
- PyPatchMatch-1.0.1/PyPatchMatch.egg-info/top_level.txt +0 -1
- PyPatchMatch-1.0.1/README.md +0 -67
- PyPatchMatch-1.0.1/patchmatch/Makefile +0 -60
- PyPatchMatch-1.0.1/patchmatch/__init__.py +0 -3
- PyPatchMatch-1.0.1/patchmatch/csrc/inpaint.cpp +0 -288
- PyPatchMatch-1.0.1/patchmatch/csrc/inpaint.h +0 -27
- PyPatchMatch-1.0.1/patchmatch/csrc/masked_image.cpp +0 -173
- PyPatchMatch-1.0.1/patchmatch/csrc/nnf.cpp +0 -316
- PyPatchMatch-1.0.1/patchmatch/csrc/pyinterface.cpp +0 -133
- PyPatchMatch-1.0.1/patchmatch/patch_match.py +0 -381
- PyPatchMatch-1.0.1/patchmatch/travis.sh +0 -9
- PyPatchMatch-1.0.1/pyproject.toml +0 -71
- PyPatchMatch-1.0.1/setup.cfg +0 -4
- {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
|
+
[](https://github.com/mauwii/PyPatchMatch/actions/workflows/ci.yml)
|
|
34
|
+
[](https://sonarcloud.io/summary/new_code?id=mauwii_PyPatchMatch)
|
|
35
|
+
[](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE)
|
|
36
|
+
[](https://pypi.org/project/PyPatchMatch/)
|
|
37
|
+
[](https://pepy.tech/projects/pypatchmatch)
|
|
38
|
+
[](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
|
+

|
|
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
|
+
[](https://github.com/mauwii/PyPatchMatch/actions/workflows/ci.yml)
|
|
4
|
+
[](https://sonarcloud.io/summary/new_code?id=mauwii_PyPatchMatch)
|
|
5
|
+
[](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE)
|
|
6
|
+
[](https://pypi.org/project/PyPatchMatch/)
|
|
7
|
+
[](https://pepy.tech/projects/pypatchmatch)
|
|
8
|
+
[](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
|
+

|
|
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.
|
|
Binary file
|
|
@@ -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
|
+
]
|