PyPatchMatch 1.0.2__tar.gz → 2.0.0b2__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.0b2/CMakeLists.txt +76 -0
- pypatchmatch-2.0.0b2/PKG-INFO +140 -0
- pypatchmatch-2.0.0b2/README.md +110 -0
- pypatchmatch-2.0.0b2/examples/images/forest_pruned.bmp +0 -0
- pypatchmatch-2.0.0b2/patchmatch/__init__.py +19 -0
- pypatchmatch-2.0.0b2/patchmatch/_lib.py +124 -0
- pypatchmatch-2.0.0b2/patchmatch/_version.py +24 -0
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/inpaint.cpp +28 -15
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/masked_image.cpp +0 -1
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/masked_image.h +1 -0
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/nnf.cpp +40 -13
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/nnf.h +5 -0
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/pyinterface.cpp +1 -2
- pypatchmatch-2.0.0b2/patchmatch/patch_match.py +192 -0
- pypatchmatch-2.0.0b2/patchmatch/py.typed +0 -0
- pypatchmatch-2.0.0b2/pyproject.toml +114 -0
- pypatchmatch-2.0.0b2/scripts/build_opencv.py +132 -0
- pypatchmatch-2.0.0b2/tests/test_e2e.py +157 -0
- pypatchmatch-2.0.0b2/tests/test_patch_match.py +343 -0
- PyPatchMatch-1.0.2/PKG-INFO +0 -103
- PyPatchMatch-1.0.2/PyPatchMatch.egg-info/PKG-INFO +0 -103
- PyPatchMatch-1.0.2/PyPatchMatch.egg-info/SOURCES.txt +0 -20
- PyPatchMatch-1.0.2/PyPatchMatch.egg-info/dependency_links.txt +0 -1
- PyPatchMatch-1.0.2/PyPatchMatch.egg-info/requires.txt +0 -28
- PyPatchMatch-1.0.2/PyPatchMatch.egg-info/top_level.txt +0 -1
- PyPatchMatch-1.0.2/README.md +0 -67
- PyPatchMatch-1.0.2/patchmatch/Makefile +0 -60
- PyPatchMatch-1.0.2/patchmatch/__init__.py +0 -3
- PyPatchMatch-1.0.2/patchmatch/patch_match.py +0 -381
- PyPatchMatch-1.0.2/patchmatch/travis.sh +0 -9
- PyPatchMatch-1.0.2/pyproject.toml +0 -71
- PyPatchMatch-1.0.2/setup.cfg +0 -4
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/LICENSE +0 -0
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/inpaint.h +0 -0
- {PyPatchMatch-1.0.2 → pypatchmatch-2.0.0b2}/patchmatch/csrc/pyinterface.h +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
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_WITH_HIGHGUI "Enable the debug visualization of Inpainting::run (requires OpenCV highgui)" OFF)
|
|
6
|
+
option(PATCHMATCH_FAST_MATH "Compile with fast-math optimizations" ON)
|
|
7
|
+
|
|
8
|
+
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|
9
|
+
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
|
10
|
+
endif()
|
|
11
|
+
|
|
12
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
13
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
14
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
15
|
+
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
16
|
+
|
|
17
|
+
set(PATCHMATCH_OPENCV_COMPONENTS core)
|
|
18
|
+
if(PATCHMATCH_WITH_HIGHGUI)
|
|
19
|
+
list(APPEND PATCHMATCH_OPENCV_COMPONENTS imgproc highgui)
|
|
20
|
+
endif()
|
|
21
|
+
if(PATCHMATCH_BUILD_EXAMPLES)
|
|
22
|
+
list(APPEND PATCHMATCH_OPENCV_COMPONENTS imgcodecs)
|
|
23
|
+
endif()
|
|
24
|
+
find_package(OpenCV REQUIRED COMPONENTS ${PATCHMATCH_OPENCV_COMPONENTS})
|
|
25
|
+
|
|
26
|
+
add_library(patchmatch SHARED
|
|
27
|
+
patchmatch/csrc/inpaint.cpp
|
|
28
|
+
patchmatch/csrc/masked_image.cpp
|
|
29
|
+
patchmatch/csrc/nnf.cpp
|
|
30
|
+
patchmatch/csrc/pyinterface.cpp
|
|
31
|
+
)
|
|
32
|
+
target_include_directories(patchmatch PUBLIC patchmatch/csrc)
|
|
33
|
+
target_link_libraries(patchmatch PUBLIC opencv_core)
|
|
34
|
+
|
|
35
|
+
if(MSVC AND NOT OpenCV_SHARED)
|
|
36
|
+
# A static OpenCV (as built by scripts/build_opencv.py) uses the static MSVC runtime.
|
|
37
|
+
set_property(TARGET patchmatch PROPERTY
|
|
38
|
+
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
|
39
|
+
endif()
|
|
40
|
+
|
|
41
|
+
if(PATCHMATCH_FAST_MATH)
|
|
42
|
+
if(MSVC)
|
|
43
|
+
target_compile_options(patchmatch PRIVATE /fp:fast)
|
|
44
|
+
else()
|
|
45
|
+
target_compile_options(patchmatch PRIVATE -ffast-math)
|
|
46
|
+
endif()
|
|
47
|
+
endif()
|
|
48
|
+
|
|
49
|
+
if(PATCHMATCH_WITH_HIGHGUI)
|
|
50
|
+
target_compile_definitions(patchmatch PRIVATE PATCHMATCH_WITH_HIGHGUI)
|
|
51
|
+
target_link_libraries(patchmatch PRIVATE opencv_imgproc opencv_highgui)
|
|
52
|
+
endif()
|
|
53
|
+
|
|
54
|
+
if(PATCHMATCH_BUILD_EXAMPLES)
|
|
55
|
+
add_executable(cpp_example examples/cpp_example.cpp)
|
|
56
|
+
target_link_libraries(cpp_example PRIVATE patchmatch opencv_imgcodecs)
|
|
57
|
+
endif()
|
|
58
|
+
|
|
59
|
+
# The Python package loads the library via ctypes from its own directory.
|
|
60
|
+
install(TARGETS patchmatch
|
|
61
|
+
LIBRARY DESTINATION patchmatch
|
|
62
|
+
RUNTIME DESTINATION patchmatch
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# A static OpenCV becomes part of the library, so a wheel has to ship its licenses,
|
|
66
|
+
# which scripts/build_opencv.py collects in <prefix>/licenses.
|
|
67
|
+
if(DEFINED SKBUILD_METADATA_DIR AND NOT OpenCV_SHARED)
|
|
68
|
+
set(PATCHMATCH_OPENCV_LICENSES "${OpenCV_INSTALL_PATH}/licenses")
|
|
69
|
+
if(EXISTS "${PATCHMATCH_OPENCV_LICENSES}/opencv-LICENSE")
|
|
70
|
+
install(DIRECTORY "${PATCHMATCH_OPENCV_LICENSES}/"
|
|
71
|
+
DESTINATION "${SKBUILD_METADATA_DIR}/licenses/opencv")
|
|
72
|
+
else()
|
|
73
|
+
message(WARNING "No OpenCV licenses in ${PATCHMATCH_OPENCV_LICENSES}, "
|
|
74
|
+
"the wheel will not include them")
|
|
75
|
+
endif()
|
|
76
|
+
endif()
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyPatchMatch
|
|
3
|
+
Version: 2.0.0b2
|
|
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.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
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.10
|
|
27
|
+
Requires-Dist: numpy
|
|
28
|
+
Requires-Dist: pillow
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# PatchMatch based Inpainting
|
|
32
|
+
|
|
33
|
+
[](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE)
|
|
34
|
+
[](https://pypi.org/project/PyPatchMatch/)
|
|
35
|
+
[](https://pepy.tech/projects/pypatchmatch)
|
|
36
|
+
[](https://github.com/astral-sh/ruff)
|
|
37
|
+
|
|
38
|
+
This library implements the PatchMatch based inpainting algorithm. It provides both C++
|
|
39
|
+
and Python interfaces. This implementation is heavily based on the implementation by
|
|
40
|
+
Younesse ANDAM: [younesse-cv/PatchMatch](https://github.com/younesse-cv/PatchMatch),
|
|
41
|
+
with some bug fixes, and updates.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pip install PyPatchMatch
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Wheels for Linux (glibc, x86_64 and aarch64), macOS 11+ (arm64 and x86_64) and Windows
|
|
50
|
+
(x64) ship the compiled library with a statically linked OpenCV, so no compiler or
|
|
51
|
+
OpenCV installation is needed. On other platforms, e.g. Alpine Linux or Windows on ARM,
|
|
52
|
+
pip builds from the source distribution, which requires a C++17 compiler, CMake and the
|
|
53
|
+
OpenCV development files (e.g. `apt install libopencv-dev` or `brew install opencv`).
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
Python (see
|
|
58
|
+
[examples/py_example.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example.py)):
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import patchmatch
|
|
62
|
+
|
|
63
|
+
image = ... # HxWx3 uint8 numpy array or PIL image
|
|
64
|
+
mask = ... # HxW uint8 or bool numpy array or PIL image, non-zero marks the holes
|
|
65
|
+
result = patchmatch.inpaint(image, mask, patch_size=3)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The mask must have the same height and width as the image. If `mask` is omitted, all
|
|
69
|
+
pure white pixels are treated as holes. The optional keyword argument `global_mask`, in
|
|
70
|
+
the same format, marks pixels that are neither filled nor used as a source; they keep
|
|
71
|
+
their values (see
|
|
72
|
+
[examples/py_example_global_mask.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example_global_mask.py)).
|
|
73
|
+
`patchmatch.patchmatch_available` tells whether the native library could be loaded.
|
|
74
|
+
The previous import path `from patchmatch import patch_match` keeps working.
|
|
75
|
+
|
|
76
|
+
`patchmatch.set_random_seed(seed)` sets the seed of the randomized search and
|
|
77
|
+
`patchmatch.set_verbose(True)` prints the progress of the native code to stderr.
|
|
78
|
+
`patchmatch.inpaint_regularity(image, mask, ijmap)` additionally guides the search with
|
|
79
|
+
a regularity map, an HxWx3 float32 array with the regularity coordinates of each pixel.
|
|
80
|
+
|
|
81
|
+
C++ (see
|
|
82
|
+
[examples/cpp_example.cpp](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example.cpp),
|
|
83
|
+
build and run it with
|
|
84
|
+
[examples/cpp_example_run.sh](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example_run.sh)):
|
|
85
|
+
|
|
86
|
+
```cpp
|
|
87
|
+
#include "inpaint.h"
|
|
88
|
+
|
|
89
|
+
int main() {
|
|
90
|
+
cv::Mat image = ...;
|
|
91
|
+
cv::Mat mask = ...;
|
|
92
|
+
|
|
93
|
+
auto metric = PatchSSDDistanceMetric(5);
|
|
94
|
+
cv::Mat result = Inpainting(image, mask, &metric).run();
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The library is built with CMake; `PATCHMATCH_BUILD_EXAMPLES` builds the example and
|
|
99
|
+
`PATCHMATCH_WITH_HIGHGUI` enables the debug visualization of `Inpainting::run`.
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
The project is managed with [uv](https://docs.astral.sh/uv/):
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
uv sync # create .venv, build the library, install dev deps
|
|
107
|
+
uv run pre-commit install # enable ruff and the other hooks on commit
|
|
108
|
+
uv run pytest # run the test suite
|
|
109
|
+
uv run pytest -m "not e2e" # only the fast unit tests
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Releases are published to PyPI by creating a GitHub release; the version is taken from
|
|
113
|
+
its tag (e.g. `v2.0.0`).
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
PyPatchMatch is released under the
|
|
118
|
+
[MIT License](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE). The wheels
|
|
119
|
+
contain a statically linked build of OpenCV core (Apache-2.0) and its bundled
|
|
120
|
+
third-party code; their licenses are included in the `licenses/opencv` directory of the
|
|
121
|
+
wheel's `.dist-info`.
|
|
122
|
+
|
|
123
|
+
## README and COPYRIGHT by Younesse ANDAM
|
|
124
|
+
|
|
125
|
+
@Author: Younesse ANDAM
|
|
126
|
+
|
|
127
|
+
@Contact: <younesse.andam@gmail.com>
|
|
128
|
+
|
|
129
|
+
Description:
|
|
130
|
+
|
|
131
|
+
This project is a personal implementation of an algorithm called PATCHMATCH
|
|
132
|
+
that restores missing areas in an image. The algorithm is presented in the following
|
|
133
|
+
paper PatchMatch A Randomized Correspondence Algorithm for Structural Image Editing by
|
|
134
|
+
C.Barnes, E.Shechtman, A.Finkelstein and Dan B.Goldman ACM Transactions on Graphics
|
|
135
|
+
(Proc. SIGGRAPH), vol.28, aug-2009
|
|
136
|
+
|
|
137
|
+
For more information please refer to
|
|
138
|
+
<https://gfx.cs.princeton.edu/pubs/Barnes_2009_PAR/>
|
|
139
|
+
|
|
140
|
+
Copyright (c) 2010-2011
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# PatchMatch based Inpainting
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE)
|
|
4
|
+
[](https://pypi.org/project/PyPatchMatch/)
|
|
5
|
+
[](https://pepy.tech/projects/pypatchmatch)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
|
|
8
|
+
This library implements the PatchMatch based inpainting algorithm. It provides both C++
|
|
9
|
+
and Python interfaces. This implementation is heavily based on the implementation by
|
|
10
|
+
Younesse ANDAM: [younesse-cv/PatchMatch](https://github.com/younesse-cv/PatchMatch),
|
|
11
|
+
with some bug fixes, and updates.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pip install PyPatchMatch
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Wheels for Linux (glibc, x86_64 and aarch64), macOS 11+ (arm64 and x86_64) and Windows
|
|
20
|
+
(x64) ship the compiled library with a statically linked OpenCV, so no compiler or
|
|
21
|
+
OpenCV installation is needed. On other platforms, e.g. Alpine Linux or Windows on ARM,
|
|
22
|
+
pip builds from the source distribution, which requires a C++17 compiler, CMake and the
|
|
23
|
+
OpenCV development files (e.g. `apt install libopencv-dev` or `brew install opencv`).
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Python (see
|
|
28
|
+
[examples/py_example.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example.py)):
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import patchmatch
|
|
32
|
+
|
|
33
|
+
image = ... # HxWx3 uint8 numpy array or PIL image
|
|
34
|
+
mask = ... # HxW uint8 or bool numpy array or PIL image, non-zero marks the holes
|
|
35
|
+
result = patchmatch.inpaint(image, mask, patch_size=3)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The mask must have the same height and width as the image. If `mask` is omitted, all
|
|
39
|
+
pure white pixels are treated as holes. The optional keyword argument `global_mask`, in
|
|
40
|
+
the same format, marks pixels that are neither filled nor used as a source; they keep
|
|
41
|
+
their values (see
|
|
42
|
+
[examples/py_example_global_mask.py](https://github.com/mauwii/PyPatchMatch/blob/main/examples/py_example_global_mask.py)).
|
|
43
|
+
`patchmatch.patchmatch_available` tells whether the native library could be loaded.
|
|
44
|
+
The previous import path `from patchmatch import patch_match` keeps working.
|
|
45
|
+
|
|
46
|
+
`patchmatch.set_random_seed(seed)` sets the seed of the randomized search and
|
|
47
|
+
`patchmatch.set_verbose(True)` prints the progress of the native code to stderr.
|
|
48
|
+
`patchmatch.inpaint_regularity(image, mask, ijmap)` additionally guides the search with
|
|
49
|
+
a regularity map, an HxWx3 float32 array with the regularity coordinates of each pixel.
|
|
50
|
+
|
|
51
|
+
C++ (see
|
|
52
|
+
[examples/cpp_example.cpp](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example.cpp),
|
|
53
|
+
build and run it with
|
|
54
|
+
[examples/cpp_example_run.sh](https://github.com/mauwii/PyPatchMatch/blob/main/examples/cpp_example_run.sh)):
|
|
55
|
+
|
|
56
|
+
```cpp
|
|
57
|
+
#include "inpaint.h"
|
|
58
|
+
|
|
59
|
+
int main() {
|
|
60
|
+
cv::Mat image = ...;
|
|
61
|
+
cv::Mat mask = ...;
|
|
62
|
+
|
|
63
|
+
auto metric = PatchSSDDistanceMetric(5);
|
|
64
|
+
cv::Mat result = Inpainting(image, mask, &metric).run();
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The library is built with CMake; `PATCHMATCH_BUILD_EXAMPLES` builds the example and
|
|
69
|
+
`PATCHMATCH_WITH_HIGHGUI` enables the debug visualization of `Inpainting::run`.
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
The project is managed with [uv](https://docs.astral.sh/uv/):
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
uv sync # create .venv, build the library, install dev deps
|
|
77
|
+
uv run pre-commit install # enable ruff and the other hooks on commit
|
|
78
|
+
uv run pytest # run the test suite
|
|
79
|
+
uv run pytest -m "not e2e" # only the fast unit tests
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Releases are published to PyPI by creating a GitHub release; the version is taken from
|
|
83
|
+
its tag (e.g. `v2.0.0`).
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
PyPatchMatch is released under the
|
|
88
|
+
[MIT License](https://github.com/mauwii/PyPatchMatch/blob/main/LICENSE). The wheels
|
|
89
|
+
contain a statically linked build of OpenCV core (Apache-2.0) and its bundled
|
|
90
|
+
third-party code; their licenses are included in the `licenses/opencv` directory of the
|
|
91
|
+
wheel's `.dist-info`.
|
|
92
|
+
|
|
93
|
+
## README and COPYRIGHT by Younesse ANDAM
|
|
94
|
+
|
|
95
|
+
@Author: Younesse ANDAM
|
|
96
|
+
|
|
97
|
+
@Contact: <younesse.andam@gmail.com>
|
|
98
|
+
|
|
99
|
+
Description:
|
|
100
|
+
|
|
101
|
+
This project is a personal implementation of an algorithm called PATCHMATCH
|
|
102
|
+
that restores missing areas in an image. The algorithm is presented in the following
|
|
103
|
+
paper PatchMatch A Randomized Correspondence Algorithm for Structural Image Editing by
|
|
104
|
+
C.Barnes, E.Shechtman, A.Finkelstein and Dan B.Goldman ACM Transactions on Graphics
|
|
105
|
+
(Proc. SIGGRAPH), vol.28, aug-2009
|
|
106
|
+
|
|
107
|
+
For more information please refer to
|
|
108
|
+
<https://gfx.cs.princeton.edu/pubs/Barnes_2009_PAR/>
|
|
109
|
+
|
|
110
|
+
Copyright (c) 2010-2011
|
|
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
|
+
]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""ctypes bindings for the native patchmatch library."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ctypes
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
if sys.platform == "win32":
|
|
12
|
+
LIBRARY_NAME = "patchmatch.dll"
|
|
13
|
+
elif sys.platform == "darwin":
|
|
14
|
+
LIBRARY_NAME = "libpatchmatch.dylib"
|
|
15
|
+
else:
|
|
16
|
+
LIBRARY_NAME = "libpatchmatch.so"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CShapeT(ctypes.Structure):
|
|
20
|
+
_fields_ = [
|
|
21
|
+
("width", ctypes.c_int),
|
|
22
|
+
("height", ctypes.c_int),
|
|
23
|
+
("channels", ctypes.c_int),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CMatT(ctypes.Structure):
|
|
28
|
+
_fields_ = [
|
|
29
|
+
("data_ptr", ctypes.c_void_p),
|
|
30
|
+
("shape", CShapeT),
|
|
31
|
+
("dtype", ctypes.c_int),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Order matches the PM_dtype_e enum in csrc/pyinterface.h.
|
|
36
|
+
_PYMAT_DTYPES = [
|
|
37
|
+
np.dtype(np.uint8),
|
|
38
|
+
np.dtype(np.int8),
|
|
39
|
+
np.dtype(np.uint16),
|
|
40
|
+
np.dtype(np.int16),
|
|
41
|
+
np.dtype(np.int32),
|
|
42
|
+
np.dtype(np.float32),
|
|
43
|
+
np.dtype(np.float64),
|
|
44
|
+
]
|
|
45
|
+
_PYMAT_DTYPE_IDS = {dtype: i for i, dtype in enumerate(_PYMAT_DTYPES)}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def np_to_pymat(npmat: np.ndarray) -> CMatT:
|
|
49
|
+
"""Wrap a contiguous HxWxC array without copying it."""
|
|
50
|
+
if npmat.ndim != 3:
|
|
51
|
+
raise ValueError(f"expected a 3-dimensional array, got {npmat.ndim} dims")
|
|
52
|
+
if not npmat.flags.c_contiguous:
|
|
53
|
+
raise ValueError("expected a C-contiguous array")
|
|
54
|
+
try:
|
|
55
|
+
dtype_id = _PYMAT_DTYPE_IDS[npmat.dtype]
|
|
56
|
+
except KeyError:
|
|
57
|
+
raise TypeError(f"unsupported dtype {npmat.dtype}") from None
|
|
58
|
+
height, width, channels = npmat.shape
|
|
59
|
+
return CMatT(
|
|
60
|
+
ctypes.c_void_p(npmat.ctypes.data),
|
|
61
|
+
CShapeT(width, height, channels),
|
|
62
|
+
dtype_id,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def pymat_to_np(pymat: CMatT) -> np.ndarray:
|
|
67
|
+
"""Copy the data referenced by ``pymat`` into a new numpy array."""
|
|
68
|
+
dtype = _PYMAT_DTYPES[pymat.dtype]
|
|
69
|
+
shape = (pymat.shape.height, pymat.shape.width, pymat.shape.channels)
|
|
70
|
+
buffer = (ctypes.c_byte * (int(np.prod(shape)) * dtype.itemsize)).from_address(
|
|
71
|
+
pymat.data_ptr
|
|
72
|
+
)
|
|
73
|
+
return np.frombuffer(buffer, dtype=dtype).reshape(shape).copy()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def find_library() -> Path:
|
|
77
|
+
"""Locate the shared library that is installed next to this package."""
|
|
78
|
+
package = sys.modules[__package__]
|
|
79
|
+
for directory in package.__path__:
|
|
80
|
+
candidate = Path(directory) / LIBRARY_NAME
|
|
81
|
+
if candidate.is_file():
|
|
82
|
+
return candidate
|
|
83
|
+
raise OSError(
|
|
84
|
+
f"{LIBRARY_NAME} not found in {list(package.__path__)}; "
|
|
85
|
+
"reinstall PyPatchMatch or build it from source with OpenCV available"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load_library() -> ctypes.CDLL:
|
|
90
|
+
lib = ctypes.CDLL(str(find_library()))
|
|
91
|
+
|
|
92
|
+
lib.PM_set_random_seed.argtypes = [ctypes.c_uint]
|
|
93
|
+
lib.PM_set_random_seed.restype = None
|
|
94
|
+
lib.PM_set_verbose.argtypes = [ctypes.c_int]
|
|
95
|
+
lib.PM_set_verbose.restype = None
|
|
96
|
+
lib.PM_free_pymat.argtypes = [CMatT]
|
|
97
|
+
lib.PM_free_pymat.restype = None
|
|
98
|
+
|
|
99
|
+
lib.PM_inpaint.argtypes = [CMatT, CMatT, ctypes.c_int]
|
|
100
|
+
lib.PM_inpaint2.argtypes = [CMatT, CMatT, CMatT, ctypes.c_int]
|
|
101
|
+
lib.PM_inpaint_regularity.argtypes = [
|
|
102
|
+
CMatT,
|
|
103
|
+
CMatT,
|
|
104
|
+
CMatT,
|
|
105
|
+
ctypes.c_int,
|
|
106
|
+
ctypes.c_float,
|
|
107
|
+
]
|
|
108
|
+
lib.PM_inpaint2_regularity.argtypes = [
|
|
109
|
+
CMatT,
|
|
110
|
+
CMatT,
|
|
111
|
+
CMatT,
|
|
112
|
+
CMatT,
|
|
113
|
+
ctypes.c_int,
|
|
114
|
+
ctypes.c_float,
|
|
115
|
+
]
|
|
116
|
+
for func in (
|
|
117
|
+
lib.PM_inpaint,
|
|
118
|
+
lib.PM_inpaint2,
|
|
119
|
+
lib.PM_inpaint_regularity,
|
|
120
|
+
lib.PM_inpaint2_regularity,
|
|
121
|
+
):
|
|
122
|
+
func.restype = CMatT
|
|
123
|
+
|
|
124
|
+
return lib
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '2.0.0b2'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 0, 0, 'b2')
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'gd8a13321b'
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
#include <algorithm>
|
|
2
2
|
#include <iostream>
|
|
3
|
-
#
|
|
3
|
+
#ifdef PATCHMATCH_WITH_HIGHGUI
|
|
4
4
|
#include <opencv2/imgproc.hpp>
|
|
5
5
|
#include <opencv2/highgui.hpp>
|
|
6
|
+
#endif
|
|
6
7
|
|
|
7
8
|
#include "inpaint.h"
|
|
8
9
|
|
|
9
10
|
namespace
|
|
10
11
|
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
void init_kDistance2Similarity()
|
|
12
|
+
std::vector<double> make_distance2similarity()
|
|
14
13
|
{
|
|
15
14
|
double base[11] = {1.0, 0.99, 0.96, 0.83, 0.38, 0.11, 0.02, 0.005, 0.0006, 0.0001, 0};
|
|
16
15
|
int length = (PatchDistanceMetric::kDistanceScale + 1);
|
|
17
|
-
|
|
16
|
+
std::vector<double> table(length);
|
|
18
17
|
for (int i = 0; i < length; ++i)
|
|
19
18
|
{
|
|
20
19
|
double t = (double)i / length;
|
|
@@ -22,8 +21,17 @@ namespace
|
|
|
22
21
|
int k = j + 1;
|
|
23
22
|
double vj = (j < 11) ? base[j] : 0;
|
|
24
23
|
double vk = (k < 11) ? base[k] : 0;
|
|
25
|
-
|
|
24
|
+
table[i] = vj + (100 * t - j) * (vk - vj);
|
|
26
25
|
}
|
|
26
|
+
return table;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Built on first use. The initialization of a local static is thread-safe, which
|
|
30
|
+
// matters because the Python bindings release the GIL during inpainting.
|
|
31
|
+
const std::vector<double> &distance2similarity()
|
|
32
|
+
{
|
|
33
|
+
static const std::vector<double> table = make_distance2similarity();
|
|
34
|
+
return table;
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
inline void _weighted_copy(const MaskedImage &source, int ys, int xs, cv::Mat &target, int yt, int xt, double weight)
|
|
@@ -48,13 +56,13 @@ namespace
|
|
|
48
56
|
*/
|
|
49
57
|
|
|
50
58
|
Inpainting::Inpainting(cv::Mat image, cv::Mat mask, const PatchDistanceMetric *metric)
|
|
51
|
-
: m_initial(image, mask),
|
|
59
|
+
: m_initial(image, mask), m_pyramid(), m_source2target(), m_target2source(), m_distance_metric(metric)
|
|
52
60
|
{
|
|
53
61
|
_initialize_pyramid();
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
Inpainting::Inpainting(cv::Mat image, cv::Mat mask, cv::Mat global_mask, const PatchDistanceMetric *metric)
|
|
57
|
-
: m_initial(image, mask, global_mask),
|
|
65
|
+
: m_initial(image, mask, global_mask), m_pyramid(), m_source2target(), m_target2source(), m_distance_metric(metric)
|
|
58
66
|
{
|
|
59
67
|
_initialize_pyramid();
|
|
60
68
|
}
|
|
@@ -68,16 +76,11 @@ void Inpainting::_initialize_pyramid()
|
|
|
68
76
|
source = source.downsample();
|
|
69
77
|
m_pyramid.push_back(source);
|
|
70
78
|
}
|
|
71
|
-
|
|
72
|
-
if (kDistance2Similarity.size() == 0)
|
|
73
|
-
{
|
|
74
|
-
init_kDistance2Similarity();
|
|
75
|
-
}
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
cv::Mat Inpainting::run(bool verbose, bool verbose_visualize, unsigned int random_seed)
|
|
79
82
|
{
|
|
80
|
-
|
|
83
|
+
NearestNeighborField::seed_random(random_seed);
|
|
81
84
|
const int nr_levels = m_pyramid.size();
|
|
82
85
|
|
|
83
86
|
MaskedImage source, target;
|
|
@@ -104,6 +107,7 @@ cv::Mat Inpainting::run(bool verbose, bool verbose_visualize, unsigned int rando
|
|
|
104
107
|
if (verbose)
|
|
105
108
|
std::cerr << "Initialization done." << std::endl;
|
|
106
109
|
|
|
110
|
+
#ifdef PATCHMATCH_WITH_HIGHGUI
|
|
107
111
|
if (verbose_visualize)
|
|
108
112
|
{
|
|
109
113
|
auto visualize_size = m_initial.size();
|
|
@@ -115,11 +119,19 @@ cv::Mat Inpainting::run(bool verbose, bool verbose_visualize, unsigned int rando
|
|
|
115
119
|
cv::imshow("Target", target_visualize);
|
|
116
120
|
cv::waitKey(0);
|
|
117
121
|
}
|
|
122
|
+
#else
|
|
123
|
+
(void)verbose_visualize;
|
|
124
|
+
#endif
|
|
118
125
|
|
|
119
126
|
target = _expectation_maximization(source, target, level, verbose);
|
|
120
127
|
}
|
|
121
128
|
|
|
122
|
-
|
|
129
|
+
// Globally masked pixels are neither filled nor used as a source, so the pyramid
|
|
130
|
+
// leaves them black. Keep their input values instead.
|
|
131
|
+
cv::Mat result = target.image();
|
|
132
|
+
if (!m_initial.global_mask().empty())
|
|
133
|
+
m_initial.image().copyTo(result, m_initial.global_mask());
|
|
134
|
+
return result;
|
|
123
135
|
}
|
|
124
136
|
|
|
125
137
|
// EM-Like algorithm (see "PatchMatch" - page 6).
|
|
@@ -206,6 +218,7 @@ void Inpainting::_expectation_step(
|
|
|
206
218
|
auto source_size = nnf.source_size();
|
|
207
219
|
auto target_size = nnf.target_size();
|
|
208
220
|
const int patch_size = m_distance_metric->patch_size();
|
|
221
|
+
const auto &kDistance2Similarity = distance2similarity();
|
|
209
222
|
|
|
210
223
|
for (int i = 0; i < source_size.height; ++i)
|
|
211
224
|
{
|
|
@@ -150,7 +150,6 @@ void MaskedImage::compute_image_gradients()
|
|
|
150
150
|
|
|
151
151
|
for (int i = 1; i < size.height - 1; ++i)
|
|
152
152
|
{
|
|
153
|
-
const auto *ptr = m_image.ptr<unsigned char>(i, 0);
|
|
154
153
|
const auto *ptry1 = m_image.ptr<unsigned char>(i + 1, 0);
|
|
155
154
|
const auto *ptry2 = m_image.ptr<unsigned char>(i - 1, 0);
|
|
156
155
|
const auto *ptrx1 = m_image.ptr<unsigned char>(i, 0) + 3;
|