patchmatch-cython 0.1.4__tar.gz → 0.2.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.
- {patchmatch_cython-0.1.4/src/patchmatch_cython.egg-info → patchmatch_cython-0.2.0}/PKG-INFO +74 -27
- patchmatch_cython-0.2.0/README.md +184 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/local_build.py +2 -2
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/pyproject.toml +4 -3
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/setup.py +36 -5
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython/__init__.py +1 -1
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython/cython_solver.py +33 -8
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython/cython_solver_impl.cpp +26285 -15825
- patchmatch_cython-0.2.0/src/patchmatch_cython/cython_solver_impl.pyx +628 -0
- patchmatch_cython-0.2.0/src/patchmatch_cython/imageops.py +719 -0
- patchmatch_cython-0.2.0/src/patchmatch_cython/inpaint.py +368 -0
- patchmatch_cython-0.2.0/src/patchmatch_cython/python_solver.py +402 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython/solver.py +13 -2
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0/src/patchmatch_cython.egg-info}/PKG-INFO +74 -27
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython.egg-info/SOURCES.txt +7 -1
- patchmatch_cython-0.2.0/tests/test_basic_functionality.py +29 -0
- patchmatch_cython-0.2.0/tests/test_imageops.py +300 -0
- patchmatch_cython-0.2.0/tests/test_inpaint_api.py +137 -0
- patchmatch_cython-0.2.0/tests/test_quality_options.py +263 -0
- patchmatch_cython-0.2.0/tests/test_questionable_behaviors.py +241 -0
- patchmatch_cython-0.2.0/tests/test_solvers.py +137 -0
- patchmatch_cython-0.2.0/tests/test_validation.py +75 -0
- patchmatch_cython-0.1.4/README.md +0 -138
- patchmatch_cython-0.1.4/src/patchmatch_cython/cython_solver_impl.pyx +0 -297
- patchmatch_cython-0.1.4/src/patchmatch_cython/imageops.py +0 -200
- patchmatch_cython-0.1.4/src/patchmatch_cython/inpaint.py +0 -139
- patchmatch_cython-0.1.4/src/patchmatch_cython/python_solver.py +0 -318
- patchmatch_cython-0.1.4/tests/test_basic_functionality.py +0 -71
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/LICENSE +0 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/MANIFEST.in +0 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/setup.cfg +0 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython.egg-info/dependency_links.txt +0 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython.egg-info/not-zip-safe +0 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython.egg-info/requires.txt +0 -0
- {patchmatch_cython-0.1.4 → patchmatch_cython-0.2.0}/src/patchmatch_cython.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchmatch-cython
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: High-performance PatchMatch implementation for image inpainting using Cython
|
|
5
5
|
Home-page: https://github.com/Teriks/patchmatch-cython
|
|
6
6
|
Author: Teriks
|
|
@@ -45,6 +45,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
45
45
|
Classifier: Programming Language :: Python :: 3.11
|
|
46
46
|
Classifier: Programming Language :: Python :: 3.12
|
|
47
47
|
Classifier: Programming Language :: Python :: 3.13
|
|
48
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
48
49
|
Classifier: Programming Language :: Cython
|
|
49
50
|
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
50
51
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
@@ -69,16 +70,11 @@ Dynamic: requires-python
|
|
|
69
70
|
|
|
70
71
|
# PatchMatch Cython
|
|
71
72
|
|
|
72
|
-
High-performance PatchMatch implementation for pyramidical image inpainting in
|
|
73
|
+
High-performance PatchMatch implementation for pyramidical image inpainting in Cython.
|
|
73
74
|
|
|
74
|
-
This package can function without `python
|
|
75
|
+
This package can function without OpenCV (`opencv-python` / `opencv-python-headless`) with only slightly reduced quality and speed in a few helpers. When OpenCV is installed it is used for resize (`INTER_LINEAR` / `INTER_AREA`), morphology for valid/query centers, and distance transforms for seam feathering.
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
When OpenCV is not installed, an implementation of bilinear upscaling using numpy will be used.
|
|
79
|
-
|
|
80
|
-
This package provides a pure python solver and a cython solver, the cython solver is
|
|
81
|
-
around 30 to 50 times faster than the python solver depending on patch size.
|
|
77
|
+
The package provides a pure Python solver and a Cython solver; the Cython solver is typically about 30–50× faster depending on patch size.
|
|
82
78
|
|
|
83
79
|
## Installation
|
|
84
80
|
|
|
@@ -101,13 +97,10 @@ pip install .
|
|
|
101
97
|
import cv2
|
|
102
98
|
from patchmatch_cython import inpaint_pyramid
|
|
103
99
|
|
|
104
|
-
# Load image and mask
|
|
105
100
|
image = cv2.imread('image.jpg')
|
|
106
|
-
mask = cv2.imread('mask.png', 0) > 128
|
|
101
|
+
mask = cv2.imread('mask.png', 0) > 128 # True = hole to fill
|
|
107
102
|
|
|
108
|
-
# Inpaint
|
|
109
103
|
result = inpaint_pyramid(image, mask)
|
|
110
|
-
|
|
111
104
|
cv2.imwrite('result.jpg', result)
|
|
112
105
|
```
|
|
113
106
|
|
|
@@ -118,27 +111,84 @@ import numpy as np
|
|
|
118
111
|
from PIL import Image
|
|
119
112
|
from patchmatch_cython import inpaint_pyramid
|
|
120
113
|
|
|
121
|
-
# Load image and mask
|
|
122
114
|
image = np.array(Image.open('image.jpg'))
|
|
123
115
|
mask = np.array(Image.open('mask.png').convert('L')) > 128
|
|
124
116
|
|
|
125
|
-
|
|
126
|
-
result = inpaint_pyramid(image, mask)
|
|
127
|
-
|
|
117
|
+
result = inpaint_pyramid(image, mask) # auto-selects fastest available solver
|
|
128
118
|
Image.fromarray(result).save('result.jpg')
|
|
129
119
|
```
|
|
130
120
|
|
|
131
|
-
### Explicit
|
|
121
|
+
### Explicit solver selection
|
|
132
122
|
|
|
133
123
|
```python
|
|
134
124
|
from patchmatch_cython import PythonSolver, CythonSolver, inpaint_pyramid
|
|
135
125
|
|
|
136
|
-
# Force specific solver
|
|
137
126
|
result = inpaint_pyramid(image, mask, solver_class=CythonSolver) # Fast
|
|
138
127
|
result = inpaint_pyramid(image, mask, solver_class=PythonSolver) # Fallback
|
|
139
128
|
```
|
|
140
129
|
|
|
141
|
-
|
|
130
|
+
### Quality / compose options
|
|
131
|
+
|
|
132
|
+
Both `inpaint_pyramid` and `inpaint_single` accept the same optional knobs.
|
|
133
|
+
Defaults preserve a sensible baseline; turn options on when you need more structure,
|
|
134
|
+
completeness, or softer seams.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from patchmatch_cython import inpaint_pyramid
|
|
138
|
+
|
|
139
|
+
result = inpaint_pyramid(
|
|
140
|
+
image,
|
|
141
|
+
mask,
|
|
142
|
+
patch_size=5, # odd int; square patch side
|
|
143
|
+
seed=0, # reproducible RNG (optional)
|
|
144
|
+
em_loops=2, # coarse match→vote→rematch loops (pyramid: coarse levels only)
|
|
145
|
+
edge_weighted=True, # structure-aware distance (boosted gradients)
|
|
146
|
+
reverse_search=True, # source→query completeness pass + stronger usage penalty
|
|
147
|
+
poisson_blend=True, # harmonic seam soften (default ~8px band)
|
|
148
|
+
hole_refine=3, # extra full-res PatchMatch iterations after the main solve
|
|
149
|
+
mask_feather=6.0, # soft compose width in px from the hole boundary
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Parameter | Default | Description |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `patch_size` | `5` | Odd positive int; square patch size for matching / voting. |
|
|
156
|
+
| `seed` | `None` | RNG seed for reproducible NNF init / search. |
|
|
157
|
+
| `em_loops` | `1` | Outer match→paint→rematch loops. On pyramids, applied at coarse levels only (finest stays `1`). |
|
|
158
|
+
| `edge_weighted` | `False` | `False` = color-only matching. `True` = color + boosted gradient channels. Hole-touching gradients are suppressed so zeroed holes do not create fake edges. |
|
|
159
|
+
| `reverse_search` | `False` | After each EM loop, try assigning underused near-hole sources to better queries (completeness). |
|
|
160
|
+
| `poisson_blend` | `True` | Soften seams by blending the PatchMatch fill with a harmonic membrane near the hole rim. Ignored when `mask_feather > 0` (feather supplies the band). |
|
|
161
|
+
| `hole_refine` | `0` | Extra full-resolution PatchMatch iterations after the main solve (`0` disables). Only hole pixels are painted; the user mask is not modified. |
|
|
162
|
+
| `mask_feather` | `0.0` | Soft compose width in pixels from the hole boundary. Uses a distance ramp toward a harmonic rim pull (not the original hole content). Does **not** change the mask used for matching. |
|
|
163
|
+
|
|
164
|
+
`inpaint_single` is the same API plus `iterations` (PatchMatch passes at one scale):
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from patchmatch_cython import inpaint_single
|
|
168
|
+
|
|
169
|
+
result = inpaint_single(
|
|
170
|
+
image,
|
|
171
|
+
mask,
|
|
172
|
+
iterations=7,
|
|
173
|
+
em_loops=2,
|
|
174
|
+
reverse_search=True,
|
|
175
|
+
hole_refine=2,
|
|
176
|
+
mask_feather=4.0,
|
|
177
|
+
poisson_blend=False, # feather handles seams when mask_feather > 0
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Masks:** accept `bool`, `{0,1}`, or `{0,255}`. `True` / non-zero = region to inpaint. Known (unmasked) pixels are preserved in the output. The library never dilates/erodes the user mask for matching; `mask_feather` only affects final compositing.
|
|
182
|
+
|
|
183
|
+
**OpenCV runtime toggle** (optional):
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from patchmatch_cython.imageops import set_use_opencv, opencv_available
|
|
187
|
+
|
|
188
|
+
set_use_opencv(False) # force NumPy fallbacks even if OpenCV is installed
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Package extras
|
|
142
192
|
|
|
143
193
|
**For examples:**
|
|
144
194
|
|
|
@@ -162,18 +212,16 @@ To build wheels locally for distribution or development:
|
|
|
162
212
|
|
|
163
213
|
### Prerequisites
|
|
164
214
|
|
|
165
|
-
Install build dependencies:
|
|
166
|
-
|
|
167
215
|
```bash
|
|
168
216
|
pip install cibuildwheel
|
|
169
217
|
```
|
|
170
218
|
|
|
171
|
-
### Build
|
|
219
|
+
### Build wheels
|
|
172
220
|
|
|
173
|
-
Use the included `local_build.py` script
|
|
221
|
+
Use the included `local_build.py` script:
|
|
174
222
|
|
|
175
223
|
```bash
|
|
176
|
-
# Build for all supported Python versions (3.10-3.
|
|
224
|
+
# Build for all supported Python versions (3.10-3.14)
|
|
177
225
|
python local_build.py
|
|
178
226
|
|
|
179
227
|
# Build for specific Python versions
|
|
@@ -192,14 +240,13 @@ python local_build.py --no-clean
|
|
|
192
240
|
python local_build.py --help
|
|
193
241
|
```
|
|
194
242
|
|
|
195
|
-
### Build
|
|
243
|
+
### Build options
|
|
196
244
|
|
|
197
245
|
- `--python`: Specify Python versions to build for (e.g., `3.11 3.12`)
|
|
198
246
|
- `--platform`: Target platform (`auto`, `linux`, `macos`, `windows`)
|
|
199
247
|
- `--test`: Test wheel installation after building
|
|
200
248
|
- `--no-clean`: Skip cleaning build artifacts before building
|
|
201
249
|
|
|
202
|
-
|
|
203
250
|
## Requirements
|
|
204
251
|
|
|
205
252
|
- Python 3.10+
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# PatchMatch Cython
|
|
2
|
+
|
|
3
|
+
High-performance PatchMatch implementation for pyramidical image inpainting in Cython.
|
|
4
|
+
|
|
5
|
+
This package can function without OpenCV (`opencv-python` / `opencv-python-headless`) with only slightly reduced quality and speed in a few helpers. When OpenCV is installed it is used for resize (`INTER_LINEAR` / `INTER_AREA`), morphology for valid/query centers, and distance transforms for seam feathering.
|
|
6
|
+
|
|
7
|
+
The package provides a pure Python solver and a Cython solver; the Cython solver is typically about 30–50× faster depending on patch size.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install patchmatch-cython
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**From source:**
|
|
16
|
+
```bash
|
|
17
|
+
git clone <repository-url>
|
|
18
|
+
cd patchmatch-cython
|
|
19
|
+
pip install .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### With OpenCV
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import cv2
|
|
28
|
+
from patchmatch_cython import inpaint_pyramid
|
|
29
|
+
|
|
30
|
+
image = cv2.imread('image.jpg')
|
|
31
|
+
mask = cv2.imread('mask.png', 0) > 128 # True = hole to fill
|
|
32
|
+
|
|
33
|
+
result = inpaint_pyramid(image, mask)
|
|
34
|
+
cv2.imwrite('result.jpg', result)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### With Pillow
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import numpy as np
|
|
41
|
+
from PIL import Image
|
|
42
|
+
from patchmatch_cython import inpaint_pyramid
|
|
43
|
+
|
|
44
|
+
image = np.array(Image.open('image.jpg'))
|
|
45
|
+
mask = np.array(Image.open('mask.png').convert('L')) > 128
|
|
46
|
+
|
|
47
|
+
result = inpaint_pyramid(image, mask) # auto-selects fastest available solver
|
|
48
|
+
Image.fromarray(result).save('result.jpg')
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Explicit solver selection
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from patchmatch_cython import PythonSolver, CythonSolver, inpaint_pyramid
|
|
55
|
+
|
|
56
|
+
result = inpaint_pyramid(image, mask, solver_class=CythonSolver) # Fast
|
|
57
|
+
result = inpaint_pyramid(image, mask, solver_class=PythonSolver) # Fallback
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Quality / compose options
|
|
61
|
+
|
|
62
|
+
Both `inpaint_pyramid` and `inpaint_single` accept the same optional knobs.
|
|
63
|
+
Defaults preserve a sensible baseline; turn options on when you need more structure,
|
|
64
|
+
completeness, or softer seams.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from patchmatch_cython import inpaint_pyramid
|
|
68
|
+
|
|
69
|
+
result = inpaint_pyramid(
|
|
70
|
+
image,
|
|
71
|
+
mask,
|
|
72
|
+
patch_size=5, # odd int; square patch side
|
|
73
|
+
seed=0, # reproducible RNG (optional)
|
|
74
|
+
em_loops=2, # coarse match→vote→rematch loops (pyramid: coarse levels only)
|
|
75
|
+
edge_weighted=True, # structure-aware distance (boosted gradients)
|
|
76
|
+
reverse_search=True, # source→query completeness pass + stronger usage penalty
|
|
77
|
+
poisson_blend=True, # harmonic seam soften (default ~8px band)
|
|
78
|
+
hole_refine=3, # extra full-res PatchMatch iterations after the main solve
|
|
79
|
+
mask_feather=6.0, # soft compose width in px from the hole boundary
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
| Parameter | Default | Description |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `patch_size` | `5` | Odd positive int; square patch size for matching / voting. |
|
|
86
|
+
| `seed` | `None` | RNG seed for reproducible NNF init / search. |
|
|
87
|
+
| `em_loops` | `1` | Outer match→paint→rematch loops. On pyramids, applied at coarse levels only (finest stays `1`). |
|
|
88
|
+
| `edge_weighted` | `False` | `False` = color-only matching. `True` = color + boosted gradient channels. Hole-touching gradients are suppressed so zeroed holes do not create fake edges. |
|
|
89
|
+
| `reverse_search` | `False` | After each EM loop, try assigning underused near-hole sources to better queries (completeness). |
|
|
90
|
+
| `poisson_blend` | `True` | Soften seams by blending the PatchMatch fill with a harmonic membrane near the hole rim. Ignored when `mask_feather > 0` (feather supplies the band). |
|
|
91
|
+
| `hole_refine` | `0` | Extra full-resolution PatchMatch iterations after the main solve (`0` disables). Only hole pixels are painted; the user mask is not modified. |
|
|
92
|
+
| `mask_feather` | `0.0` | Soft compose width in pixels from the hole boundary. Uses a distance ramp toward a harmonic rim pull (not the original hole content). Does **not** change the mask used for matching. |
|
|
93
|
+
|
|
94
|
+
`inpaint_single` is the same API plus `iterations` (PatchMatch passes at one scale):
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from patchmatch_cython import inpaint_single
|
|
98
|
+
|
|
99
|
+
result = inpaint_single(
|
|
100
|
+
image,
|
|
101
|
+
mask,
|
|
102
|
+
iterations=7,
|
|
103
|
+
em_loops=2,
|
|
104
|
+
reverse_search=True,
|
|
105
|
+
hole_refine=2,
|
|
106
|
+
mask_feather=4.0,
|
|
107
|
+
poisson_blend=False, # feather handles seams when mask_feather > 0
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Masks:** accept `bool`, `{0,1}`, or `{0,255}`. `True` / non-zero = region to inpaint. Known (unmasked) pixels are preserved in the output. The library never dilates/erodes the user mask for matching; `mask_feather` only affects final compositing.
|
|
112
|
+
|
|
113
|
+
**OpenCV runtime toggle** (optional):
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from patchmatch_cython.imageops import set_use_opencv, opencv_available
|
|
117
|
+
|
|
118
|
+
set_use_opencv(False) # force NumPy fallbacks even if OpenCV is installed
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Package extras
|
|
122
|
+
|
|
123
|
+
**For examples:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install patchmatch-cython[visualization]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Adds: `matplotlib`, `Pillow`
|
|
130
|
+
|
|
131
|
+
**For development:**
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pip install patchmatch-cython[dev]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Adds: `pytest`, `build`, `cibuildwheel`, `setuptools`, `wheel`
|
|
138
|
+
|
|
139
|
+
## Building
|
|
140
|
+
|
|
141
|
+
To build wheels locally for distribution or development:
|
|
142
|
+
|
|
143
|
+
### Prerequisites
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install cibuildwheel
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Build wheels
|
|
150
|
+
|
|
151
|
+
Use the included `local_build.py` script:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Build for all supported Python versions (3.10-3.14)
|
|
155
|
+
python local_build.py
|
|
156
|
+
|
|
157
|
+
# Build for specific Python versions
|
|
158
|
+
python local_build.py --python 3.11 3.12
|
|
159
|
+
|
|
160
|
+
# Build for specific platform
|
|
161
|
+
python local_build.py --platform linux
|
|
162
|
+
|
|
163
|
+
# Build and test wheel installation
|
|
164
|
+
python local_build.py --test
|
|
165
|
+
|
|
166
|
+
# Build without cleaning previous artifacts
|
|
167
|
+
python local_build.py --no-clean
|
|
168
|
+
|
|
169
|
+
# View all options
|
|
170
|
+
python local_build.py --help
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Build options
|
|
174
|
+
|
|
175
|
+
- `--python`: Specify Python versions to build for (e.g., `3.11 3.12`)
|
|
176
|
+
- `--platform`: Target platform (`auto`, `linux`, `macos`, `windows`)
|
|
177
|
+
- `--test`: Test wheel installation after building
|
|
178
|
+
- `--no-clean`: Skip cleaning build artifacts before building
|
|
179
|
+
|
|
180
|
+
## Requirements
|
|
181
|
+
|
|
182
|
+
- Python 3.10+
|
|
183
|
+
- NumPy (auto-installed)
|
|
184
|
+
- C++ compiler (for building from source)
|
|
@@ -77,8 +77,8 @@ def build_wheels(python_versions=None, platforms=None, clean=True):
|
|
|
77
77
|
print(f"Building for Python versions: {python_versions}")
|
|
78
78
|
else:
|
|
79
79
|
# Build all supported versions (default)
|
|
80
|
-
env["CIBW_BUILD"] = "cp310-* cp311-* cp312-* cp313-*"
|
|
81
|
-
print("Building for all supported Python versions (3.10-3.
|
|
80
|
+
env["CIBW_BUILD"] = "cp310-* cp311-* cp312-* cp313-* cp314-*"
|
|
81
|
+
print("Building for all supported Python versions (3.10-3.14)")
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "patchmatch-cython"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "High-performance PatchMatch implementation for image inpainting using Cython"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -20,6 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.11",
|
|
21
21
|
"Programming Language :: Python :: 3.12",
|
|
22
22
|
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
23
24
|
"Programming Language :: Cython",
|
|
24
25
|
"Topic :: Scientific/Engineering :: Image Processing",
|
|
25
26
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
@@ -57,8 +58,8 @@ include = ["patchmatch_cython*"]
|
|
|
57
58
|
patchmatch_cython = ["*.pyx", "*.pxd", "*.pyd", "*.so", "*.cpp"]
|
|
58
59
|
|
|
59
60
|
[tool.cibuildwheel]
|
|
60
|
-
# Build for Python 3.10-3.
|
|
61
|
-
build = "cp310-* cp311-* cp312-* cp313-*"
|
|
61
|
+
# Build for Python 3.10-3.14 only (skip older versions)
|
|
62
|
+
build = "cp310-* cp311-* cp312-* cp313-* cp314-*"
|
|
62
63
|
|
|
63
64
|
# Configure for different platforms
|
|
64
65
|
[tool.cibuildwheel.linux]
|
|
@@ -13,17 +13,47 @@ try:
|
|
|
13
13
|
except ImportError:
|
|
14
14
|
raise RuntimeError("Cython is required to build this package. Install with: pip install Cython")
|
|
15
15
|
|
|
16
|
+
def macos_target_archs():
|
|
17
|
+
"""Return the macOS architectures this compilation is targeting.
|
|
18
|
+
|
|
19
|
+
cibuildwheel cross-compiles by setting ARCHFLAGS (for example
|
|
20
|
+
``-arch x86_64``) on an Apple Silicon runner. platform.machine() still
|
|
21
|
+
reports the host CPU in that case, so it cannot be used to choose flags.
|
|
22
|
+
"""
|
|
23
|
+
archs = []
|
|
24
|
+
parts = os.environ.get("ARCHFLAGS", "").split()
|
|
25
|
+
for index, part in enumerate(parts):
|
|
26
|
+
if part == "-arch" and index + 1 < len(parts):
|
|
27
|
+
archs.append(parts[index + 1])
|
|
28
|
+
if archs:
|
|
29
|
+
return archs
|
|
30
|
+
|
|
31
|
+
machine = platform.machine().lower()
|
|
32
|
+
if machine in ("arm64", "aarch64"):
|
|
33
|
+
return ["arm64"]
|
|
34
|
+
if machine in ("x86_64", "amd64"):
|
|
35
|
+
return ["x86_64"]
|
|
36
|
+
return [machine]
|
|
37
|
+
|
|
38
|
+
|
|
16
39
|
def get_compiler_args():
|
|
17
40
|
"""Get platform-appropriate compiler arguments"""
|
|
18
41
|
if os.name == 'nt': # Windows
|
|
19
42
|
return ["/O2"]
|
|
20
43
|
elif platform.system() == 'Darwin': # macOS
|
|
21
|
-
|
|
44
|
+
archs = macos_target_archs()
|
|
45
|
+
# -mcpu=apple-m1 is valid only for arm64. cibuildwheel's x86_64 builds
|
|
46
|
+
# run on Apple Silicon runners, and passing that flag makes clang fail
|
|
47
|
+
# with "unsupported option '-mcpu='".
|
|
48
|
+
if archs == ["arm64"]:
|
|
22
49
|
return ["-O3", "-mcpu=apple-m1"]
|
|
23
|
-
|
|
24
|
-
|
|
50
|
+
# Avoid -march=native so wheels remain portable across CPUs.
|
|
51
|
+
return ["-O3"]
|
|
25
52
|
else: # Linux and other Unix-like
|
|
26
|
-
|
|
53
|
+
# Portable default; set PATCHMATCH_NATIVE=1 for local native builds.
|
|
54
|
+
if os.environ.get("PATCHMATCH_NATIVE") == "1":
|
|
55
|
+
return ["-O3", "-march=native"]
|
|
56
|
+
return ["-O3"]
|
|
27
57
|
|
|
28
58
|
# Build from Cython source only
|
|
29
59
|
extensions = [
|
|
@@ -53,7 +83,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
53
83
|
|
|
54
84
|
setup(
|
|
55
85
|
name="patchmatch-cython",
|
|
56
|
-
version="0.
|
|
86
|
+
version="0.2.0",
|
|
57
87
|
author="Teriks",
|
|
58
88
|
author_email="Teriks999@gmail.com",
|
|
59
89
|
description="High-performance PatchMatch implementation for image inpainting using Cython",
|
|
@@ -72,6 +102,7 @@ setup(
|
|
|
72
102
|
"Programming Language :: Python :: 3.11",
|
|
73
103
|
"Programming Language :: Python :: 3.12",
|
|
74
104
|
"Programming Language :: Python :: 3.13",
|
|
105
|
+
"Programming Language :: Python :: 3.14",
|
|
75
106
|
"Programming Language :: Cython",
|
|
76
107
|
"Topic :: Scientific/Engineering :: Image Processing",
|
|
77
108
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
@@ -41,14 +41,22 @@ class CythonSolver(Solver):
|
|
|
41
41
|
self,
|
|
42
42
|
image: np.ndarray,
|
|
43
43
|
mask: np.ndarray,
|
|
44
|
-
patch_size: int
|
|
44
|
+
patch_size: int,
|
|
45
|
+
edge_weighted: bool = False,
|
|
45
46
|
) -> None:
|
|
46
|
-
|
|
47
|
+
# Normalize mask to {0,1} uint8. Plain astype(uint8) leaves {0,255} masks
|
|
48
|
+
# unchanged; boolean indexing bugs are avoided by going through != 0.
|
|
49
|
+
image_f32 = np.ascontiguousarray(image, dtype=np.float32)
|
|
50
|
+
mask_u8 = (np.asarray(mask) != 0).astype(np.uint8)
|
|
51
|
+
super().__init__(
|
|
52
|
+
image_f32, mask_u8.astype(bool), patch_size, edge_weighted=edge_weighted
|
|
53
|
+
)
|
|
47
54
|
|
|
48
55
|
self._solver: typing.Any = CythonSolverImpl(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
patch_size
|
|
56
|
+
image_f32,
|
|
57
|
+
np.ascontiguousarray(mask_u8),
|
|
58
|
+
patch_size,
|
|
59
|
+
edge_weighted=bool(edge_weighted),
|
|
52
60
|
)
|
|
53
61
|
|
|
54
62
|
@property
|
|
@@ -64,7 +72,9 @@ class CythonSolver(Solver):
|
|
|
64
72
|
iterations: int = 5,
|
|
65
73
|
randomize_nnf: bool = True,
|
|
66
74
|
nnf: typing.Optional[np.ndarray] = None,
|
|
67
|
-
seed: typing.Optional[int] = None
|
|
75
|
+
seed: typing.Optional[int] = None,
|
|
76
|
+
em_loops: int = 1,
|
|
77
|
+
reverse_search: bool = False,
|
|
68
78
|
) -> np.ndarray:
|
|
69
79
|
"""
|
|
70
80
|
Run the PatchMatch algorithm using Cython acceleration.
|
|
@@ -85,8 +95,23 @@ class CythonSolver(Solver):
|
|
|
85
95
|
:type nnf: numpy.ndarray or None
|
|
86
96
|
:param seed: Random seed for this specific run (overrides instance seed)
|
|
87
97
|
:type seed: int or None
|
|
88
|
-
:
|
|
98
|
+
:param em_loops: Outer match→vote→rematch loops (1 preserves prior behavior)
|
|
99
|
+
:type em_loops: int
|
|
100
|
+
:param reverse_search: Enable reverse/completeness search after each EM loop
|
|
101
|
+
:type reverse_search: bool
|
|
102
|
+
:return: Inpainted image with masked regions filled (float32)
|
|
89
103
|
:rtype: numpy.ndarray
|
|
90
104
|
"""
|
|
91
105
|
|
|
92
|
-
|
|
106
|
+
# Keep float32 here so callers (and inpaint_*) can round/saturate once.
|
|
107
|
+
return np.asarray(
|
|
108
|
+
self._solver(
|
|
109
|
+
iterations,
|
|
110
|
+
randomize_nnf,
|
|
111
|
+
nnf,
|
|
112
|
+
seed,
|
|
113
|
+
em_loops,
|
|
114
|
+
reverse_search,
|
|
115
|
+
),
|
|
116
|
+
dtype=np.float32,
|
|
117
|
+
)
|