ubo-morph 1.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.
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1,281 @@
1
+ Metadata-Version: 2.5
2
+ Name: ubo-morph
3
+ Version: 1.0.0
4
+ Summary: Add your description here
5
+ Author-email: ndido98 <ndido98@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.10
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: numpy<2.5.0,>=2.0.0
14
+ Requires-Dist: opencv-contrib-python>=5.0.0.93
15
+ Requires-Dist: tqdm>=4.68.4
16
+ Provides-Extra: cupy
17
+ Requires-Dist: cupy-cuda12x[ctk]>=13.0; extra == 'cupy'
18
+ Provides-Extra: dlib
19
+ Requires-Dist: dlib>=20.0.1; extra == 'dlib'
20
+ Provides-Extra: mediapipe
21
+ Requires-Dist: mediapipe>=0.10.35; extra == 'mediapipe'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # UBO Morphing Algorithm
25
+
26
+ This package contains the reference implementation in Python of the UBO landmark-based morphing algorithm
27
+ described in the paper "Decoupling texture blending and shape warping in face mopring" by M. Ferrara et al.
28
+
29
+ ## Quick start
30
+
31
+ UBO Morph supports Python 3.10 and newer.
32
+
33
+ The examples below use MediaPipe and its `face_landmarker.task` model. Put the
34
+ model and two face images in the working directory:
35
+
36
+ ```console
37
+ pip install "ubo-morph[mediapipe]"
38
+ ```
39
+
40
+ ### Morph from the command line
41
+
42
+ Run one morph with a shape-warping factor and a texture-blending factor of
43
+ `0.5` (the defaults):
44
+
45
+ ```console
46
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --output-dir output
47
+ ```
48
+
49
+ The resulting PNG is written under `output/` with an `M_...png` filename.
50
+ Use `--factor` to produce several linked shape and texture blends in one run:
51
+
52
+ ```console
53
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --output-dir output --factor 0.25 0.50 0.75
54
+ ```
55
+
56
+ Inspect the landmark mesh and pipeline images for one factor combination with:
57
+
58
+ ```console
59
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --output-dir output --intermediate-results
60
+ ```
61
+
62
+ This creates a factor-qualified directory containing `morphed.png`, its
63
+ `morphed_annotated.png` landmark-mesh visualization, and the available
64
+ intermediate images.
65
+
66
+ ### Morph from Python
67
+
68
+ OpenCV reads and writes BGR images, matching the package API. This example
69
+ writes the default midpoint morph to `output.png`:
70
+
71
+ ```python
72
+ import cv2
73
+
74
+ from ubo_morph import MediaPipeLandmarkExtractor, morph_images
75
+
76
+ image1 = cv2.imread("first.jpg", cv2.IMREAD_COLOR)
77
+ image2 = cv2.imread("second.jpg", cv2.IMREAD_COLOR)
78
+ if image1 is None or image2 is None:
79
+ raise FileNotFoundError("Could not read first.jpg or second.jpg")
80
+
81
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
82
+ result = morph_images(image1, image2, extractor)
83
+
84
+ if not cv2.imwrite("output.png", result):
85
+ raise OSError("Could not write output.png")
86
+ ```
87
+
88
+ Set `warping_factor` and `blending_factor` independently when the facial shape
89
+ and texture should progress at different rates:
90
+
91
+ ```python
92
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
93
+ result = morph_images(
94
+ image1,
95
+ image2,
96
+ extractor,
97
+ warping_factor=0.25,
98
+ blending_factor=0.75,
99
+ )
100
+ ```
101
+
102
+ ## Python API
103
+
104
+ The high-level entry points and both landmark extractors are available directly
105
+ from `ubo_morph`. Choose one extractor and provide its compatible model file:
106
+
107
+ ```python
108
+ from ubo_morph import (
109
+ DlibLandmarkExtractor,
110
+ MediaPipeLandmarkExtractor,
111
+ morph_images,
112
+ morph_with_landmarks,
113
+ )
114
+
115
+ # MediaPipe returns its face-landmarker mesh.
116
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
117
+ result = morph_images(image1, image2, extractor)
118
+
119
+ # Dlib is an alternative extractor that requires a 68-point shape predictor.
120
+ with DlibLandmarkExtractor("shape_predictor_68_face_landmarks.dat") as extractor:
121
+ result = morph_images(image1, image2, extractor)
122
+ ```
123
+
124
+ Once an extractor is selected, pass it to `morph_images` with any pipeline
125
+ options:
126
+
127
+ ```python
128
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
129
+ # Control the number of points added to each image edge, or disable them.
130
+ result = morph_images(image1, image2, extractor, points_per_border=7)
131
+ result = morph_images(image1, image2, extractor, points_per_border=0)
132
+
133
+ # Cap the shortest detector-input side at 640 px, then morph full-size inputs.
134
+ result = morph_images(
135
+ image1,
136
+ image2,
137
+ extractor,
138
+ landmark_extraction_short_side=640,
139
+ )
140
+
141
+ # Select one of the exact backend names: "cpu" or "cupy".
142
+ result = morph_images(image1, image2, extractor, backend="cupy")
143
+ ```
144
+
145
+ `cpu` is the default. Backend selection is explicit: unavailable accelerators
146
+ raise an error and never fall back to CPU.
147
+
148
+ ## Backend interface
149
+
150
+ The backend contract and lazy selector are available from `ubo_morph.morphing`:
151
+
152
+ ```python
153
+ from ubo_morph.morphing import Backend, BackendName, get_backend
154
+
155
+ cpu = get_backend("cpu")
156
+ assert cpu.name == "cpu"
157
+ ```
158
+
159
+ Concrete classes are exposed only by their backend subpackages:
160
+
161
+ ```python
162
+ from ubo_morph.morphing.cpu import CPUBackend
163
+
164
+ backend = CPUBackend()
165
+ ```
166
+
167
+ Install CuPy support together with at least one landmark extractor before
168
+ selecting `backend="cupy"`.
169
+
170
+ ## Extractors and backends
171
+
172
+ Concrete dlib and MediaPipe extractors are available from the top-level package.
173
+ Both require a compatible model file supplied by the caller and select the
174
+ largest detected face when multiple faces are returned.
175
+
176
+ ```python
177
+ from ubo_morph import DlibLandmarkExtractor, MediaPipeLandmarkExtractor
178
+
179
+ with DlibLandmarkExtractor("shape_predictor_68_face_landmarks.dat") as extractor:
180
+ dlib_result = morph_images(image1, image2, extractor)
181
+
182
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
183
+ mediapipe_result = morph_images(image1, image2, extractor)
184
+ ```
185
+
186
+ Install dlib instead of MediaPipe with:
187
+
188
+ ```console
189
+ pip install "ubo-morph[dlib]"
190
+ ```
191
+
192
+ ## CLI reference
193
+
194
+ Morph one pair directly with either landmark backend:
195
+
196
+ ```console
197
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task
198
+ ubo-morph first.jpg second.jpg --extractor dlib --model shape_predictor_68_face_landmarks.dat
199
+ ```
200
+
201
+ Pass multiple linked factors with `--factor`; each value is used for both
202
+ warping and blending. Separate factor lists produce their Cartesian product:
203
+
204
+ ```console
205
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --factor 0.25 0.50 0.75
206
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --warping-factor 0.25 0.50 --blending-factor 0.50 0.75
207
+ ```
208
+
209
+ Batch input is passed as a positional CSV path:
210
+
211
+ ```console
212
+ ubo-morph pairs.csv --extractor mediapipe --model face_landmarker.task
213
+ ```
214
+
215
+ Headerless CSV files accept two image columns and an optional third output
216
+ filename column. Headered CSV files may use `factor`, or both `warping_factor`
217
+ and `blending_factor`; an optional `output`, `output_filename`, or `filename`
218
+ column controls the destination name. Relative image paths are resolved from the
219
+ CSV directory. CSV factor columns cannot be combined with CLI factor arguments.
220
+ During each CLI run, landmarks are cached in memory by resolved image path, so
221
+ images reused across pairs are extracted only once. The cache is discarded when
222
+ the command exits.
223
+
224
+ By default, a failing pair stops the command. Pass `--skip-failing-pairs` to
225
+ report the affected file or files and failure reason, then continue with the
226
+ remaining pairs.
227
+
228
+ Use `--intermediate-results` to create a factor-qualified `M_...png/` directory
229
+ containing `morphed.png` and every image-valued intermediate field from
230
+ `MorphResult`. Every saved image, including `morphed.png`, has an accompanying
231
+ `_annotated.png` version containing indexed facial landmarks, unindexed border
232
+ points, and the Delaunay triangulation used by the morph. The intermediate
233
+ images include the aligned images before color equalization, the image actually
234
+ changed by equalization when it runs, the warped images, and the blended image
235
+ when background substitution follows. `MorphResult` also exposes the original
236
+ and aligned landmarks for both inputs. Use `--points-per-border COUNT` to change
237
+ the default of five; zero disables border points, as does the
238
+ `--no-border-points` convenience flag. Run `ubo-morph --help` for all alignment,
239
+ retouching, background, and extractor-specific settings.
240
+
241
+ For faster landmark detection on large inputs, set
242
+ `--landmark-extraction-short-side PIXELS`. Images whose shortest side exceeds
243
+ that limit are resized so it equals the limit, with the other side scaled
244
+ proportionally. Only the extractor input is resized; detected coordinates are
245
+ mapped back to the original image size before full-resolution morphing. The
246
+ default value of zero disables this resizing.
247
+
248
+ ## Module layout
249
+
250
+ ```text
251
+ ubo_morph/
252
+ morphing/
253
+ backend.py # typed backend contract and lazy selector
254
+ core.py # shared geometry, retouching, triangulation, and flow
255
+ points.py # shared point and mask operations
256
+ cpu/backend.py # NumPy/OpenCV primitive implementation
257
+ cupy/backend.py # optional CuPy primitive implementation
258
+ ```
259
+
260
+ ## Validation
261
+
262
+ ```console
263
+ uv run pytest -v
264
+ uv run ruff check .
265
+ uv run ty check src tests
266
+ ```
267
+
268
+ ## Contributing
269
+
270
+ Contributions are welcome. Create a focused branch, add or update tests for
271
+ behavior changes, and run the validation commands above before opening a pull
272
+ request. Keep changes scoped to the issue being addressed and include a clear
273
+ description of the behavior change in the pull request. Commit messages must
274
+ follow the Conventional Commits specification, for example `feat: add GPU
275
+ batching` or `fix(cli): report invalid input`. To validate messages before each
276
+ commit, install the optional hook:
277
+
278
+ ```console
279
+ uv sync --group dev
280
+ uv run pre-commit install --hook-type commit-msg
281
+ ```
@@ -0,0 +1,258 @@
1
+ # UBO Morphing Algorithm
2
+
3
+ This package contains the reference implementation in Python of the UBO landmark-based morphing algorithm
4
+ described in the paper "Decoupling texture blending and shape warping in face mopring" by M. Ferrara et al.
5
+
6
+ ## Quick start
7
+
8
+ UBO Morph supports Python 3.10 and newer.
9
+
10
+ The examples below use MediaPipe and its `face_landmarker.task` model. Put the
11
+ model and two face images in the working directory:
12
+
13
+ ```console
14
+ pip install "ubo-morph[mediapipe]"
15
+ ```
16
+
17
+ ### Morph from the command line
18
+
19
+ Run one morph with a shape-warping factor and a texture-blending factor of
20
+ `0.5` (the defaults):
21
+
22
+ ```console
23
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --output-dir output
24
+ ```
25
+
26
+ The resulting PNG is written under `output/` with an `M_...png` filename.
27
+ Use `--factor` to produce several linked shape and texture blends in one run:
28
+
29
+ ```console
30
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --output-dir output --factor 0.25 0.50 0.75
31
+ ```
32
+
33
+ Inspect the landmark mesh and pipeline images for one factor combination with:
34
+
35
+ ```console
36
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --output-dir output --intermediate-results
37
+ ```
38
+
39
+ This creates a factor-qualified directory containing `morphed.png`, its
40
+ `morphed_annotated.png` landmark-mesh visualization, and the available
41
+ intermediate images.
42
+
43
+ ### Morph from Python
44
+
45
+ OpenCV reads and writes BGR images, matching the package API. This example
46
+ writes the default midpoint morph to `output.png`:
47
+
48
+ ```python
49
+ import cv2
50
+
51
+ from ubo_morph import MediaPipeLandmarkExtractor, morph_images
52
+
53
+ image1 = cv2.imread("first.jpg", cv2.IMREAD_COLOR)
54
+ image2 = cv2.imread("second.jpg", cv2.IMREAD_COLOR)
55
+ if image1 is None or image2 is None:
56
+ raise FileNotFoundError("Could not read first.jpg or second.jpg")
57
+
58
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
59
+ result = morph_images(image1, image2, extractor)
60
+
61
+ if not cv2.imwrite("output.png", result):
62
+ raise OSError("Could not write output.png")
63
+ ```
64
+
65
+ Set `warping_factor` and `blending_factor` independently when the facial shape
66
+ and texture should progress at different rates:
67
+
68
+ ```python
69
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
70
+ result = morph_images(
71
+ image1,
72
+ image2,
73
+ extractor,
74
+ warping_factor=0.25,
75
+ blending_factor=0.75,
76
+ )
77
+ ```
78
+
79
+ ## Python API
80
+
81
+ The high-level entry points and both landmark extractors are available directly
82
+ from `ubo_morph`. Choose one extractor and provide its compatible model file:
83
+
84
+ ```python
85
+ from ubo_morph import (
86
+ DlibLandmarkExtractor,
87
+ MediaPipeLandmarkExtractor,
88
+ morph_images,
89
+ morph_with_landmarks,
90
+ )
91
+
92
+ # MediaPipe returns its face-landmarker mesh.
93
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
94
+ result = morph_images(image1, image2, extractor)
95
+
96
+ # Dlib is an alternative extractor that requires a 68-point shape predictor.
97
+ with DlibLandmarkExtractor("shape_predictor_68_face_landmarks.dat") as extractor:
98
+ result = morph_images(image1, image2, extractor)
99
+ ```
100
+
101
+ Once an extractor is selected, pass it to `morph_images` with any pipeline
102
+ options:
103
+
104
+ ```python
105
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
106
+ # Control the number of points added to each image edge, or disable them.
107
+ result = morph_images(image1, image2, extractor, points_per_border=7)
108
+ result = morph_images(image1, image2, extractor, points_per_border=0)
109
+
110
+ # Cap the shortest detector-input side at 640 px, then morph full-size inputs.
111
+ result = morph_images(
112
+ image1,
113
+ image2,
114
+ extractor,
115
+ landmark_extraction_short_side=640,
116
+ )
117
+
118
+ # Select one of the exact backend names: "cpu" or "cupy".
119
+ result = morph_images(image1, image2, extractor, backend="cupy")
120
+ ```
121
+
122
+ `cpu` is the default. Backend selection is explicit: unavailable accelerators
123
+ raise an error and never fall back to CPU.
124
+
125
+ ## Backend interface
126
+
127
+ The backend contract and lazy selector are available from `ubo_morph.morphing`:
128
+
129
+ ```python
130
+ from ubo_morph.morphing import Backend, BackendName, get_backend
131
+
132
+ cpu = get_backend("cpu")
133
+ assert cpu.name == "cpu"
134
+ ```
135
+
136
+ Concrete classes are exposed only by their backend subpackages:
137
+
138
+ ```python
139
+ from ubo_morph.morphing.cpu import CPUBackend
140
+
141
+ backend = CPUBackend()
142
+ ```
143
+
144
+ Install CuPy support together with at least one landmark extractor before
145
+ selecting `backend="cupy"`.
146
+
147
+ ## Extractors and backends
148
+
149
+ Concrete dlib and MediaPipe extractors are available from the top-level package.
150
+ Both require a compatible model file supplied by the caller and select the
151
+ largest detected face when multiple faces are returned.
152
+
153
+ ```python
154
+ from ubo_morph import DlibLandmarkExtractor, MediaPipeLandmarkExtractor
155
+
156
+ with DlibLandmarkExtractor("shape_predictor_68_face_landmarks.dat") as extractor:
157
+ dlib_result = morph_images(image1, image2, extractor)
158
+
159
+ with MediaPipeLandmarkExtractor("face_landmarker.task") as extractor:
160
+ mediapipe_result = morph_images(image1, image2, extractor)
161
+ ```
162
+
163
+ Install dlib instead of MediaPipe with:
164
+
165
+ ```console
166
+ pip install "ubo-morph[dlib]"
167
+ ```
168
+
169
+ ## CLI reference
170
+
171
+ Morph one pair directly with either landmark backend:
172
+
173
+ ```console
174
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task
175
+ ubo-morph first.jpg second.jpg --extractor dlib --model shape_predictor_68_face_landmarks.dat
176
+ ```
177
+
178
+ Pass multiple linked factors with `--factor`; each value is used for both
179
+ warping and blending. Separate factor lists produce their Cartesian product:
180
+
181
+ ```console
182
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --factor 0.25 0.50 0.75
183
+ ubo-morph first.jpg second.jpg --extractor mediapipe --model face_landmarker.task --warping-factor 0.25 0.50 --blending-factor 0.50 0.75
184
+ ```
185
+
186
+ Batch input is passed as a positional CSV path:
187
+
188
+ ```console
189
+ ubo-morph pairs.csv --extractor mediapipe --model face_landmarker.task
190
+ ```
191
+
192
+ Headerless CSV files accept two image columns and an optional third output
193
+ filename column. Headered CSV files may use `factor`, or both `warping_factor`
194
+ and `blending_factor`; an optional `output`, `output_filename`, or `filename`
195
+ column controls the destination name. Relative image paths are resolved from the
196
+ CSV directory. CSV factor columns cannot be combined with CLI factor arguments.
197
+ During each CLI run, landmarks are cached in memory by resolved image path, so
198
+ images reused across pairs are extracted only once. The cache is discarded when
199
+ the command exits.
200
+
201
+ By default, a failing pair stops the command. Pass `--skip-failing-pairs` to
202
+ report the affected file or files and failure reason, then continue with the
203
+ remaining pairs.
204
+
205
+ Use `--intermediate-results` to create a factor-qualified `M_...png/` directory
206
+ containing `morphed.png` and every image-valued intermediate field from
207
+ `MorphResult`. Every saved image, including `morphed.png`, has an accompanying
208
+ `_annotated.png` version containing indexed facial landmarks, unindexed border
209
+ points, and the Delaunay triangulation used by the morph. The intermediate
210
+ images include the aligned images before color equalization, the image actually
211
+ changed by equalization when it runs, the warped images, and the blended image
212
+ when background substitution follows. `MorphResult` also exposes the original
213
+ and aligned landmarks for both inputs. Use `--points-per-border COUNT` to change
214
+ the default of five; zero disables border points, as does the
215
+ `--no-border-points` convenience flag. Run `ubo-morph --help` for all alignment,
216
+ retouching, background, and extractor-specific settings.
217
+
218
+ For faster landmark detection on large inputs, set
219
+ `--landmark-extraction-short-side PIXELS`. Images whose shortest side exceeds
220
+ that limit are resized so it equals the limit, with the other side scaled
221
+ proportionally. Only the extractor input is resized; detected coordinates are
222
+ mapped back to the original image size before full-resolution morphing. The
223
+ default value of zero disables this resizing.
224
+
225
+ ## Module layout
226
+
227
+ ```text
228
+ ubo_morph/
229
+ morphing/
230
+ backend.py # typed backend contract and lazy selector
231
+ core.py # shared geometry, retouching, triangulation, and flow
232
+ points.py # shared point and mask operations
233
+ cpu/backend.py # NumPy/OpenCV primitive implementation
234
+ cupy/backend.py # optional CuPy primitive implementation
235
+ ```
236
+
237
+ ## Validation
238
+
239
+ ```console
240
+ uv run pytest -v
241
+ uv run ruff check .
242
+ uv run ty check src tests
243
+ ```
244
+
245
+ ## Contributing
246
+
247
+ Contributions are welcome. Create a focused branch, add or update tests for
248
+ behavior changes, and run the validation commands above before opening a pull
249
+ request. Keep changes scoped to the issue being addressed and include a clear
250
+ description of the behavior change in the pull request. Commit messages must
251
+ follow the Conventional Commits specification, for example `feat: add GPU
252
+ batching` or `fix(cli): report invalid input`. To validate messages before each
253
+ commit, install the optional hook:
254
+
255
+ ```console
256
+ uv sync --group dev
257
+ uv run pre-commit install --hook-type commit-msg
258
+ ```
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "ubo-morph"
3
+ version = "1.0.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "ndido98", email = "ndido98@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ classifiers = [
11
+ "Programming Language :: Python :: 3",
12
+ "Programming Language :: Python :: 3.10",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
17
+ ]
18
+ dependencies = [
19
+ "numpy>=2.0.0,<2.5.0",
20
+ "opencv-contrib-python>=5.0.0.93",
21
+ "tqdm>=4.68.4",
22
+ ]
23
+
24
+ [project.scripts]
25
+ ubo-morph = "ubo_morph.cli:main"
26
+
27
+ [project.optional-dependencies]
28
+ dlib = [
29
+ "dlib>=20.0.1",
30
+ ]
31
+ mediapipe = [
32
+ "mediapipe>=0.10.35",
33
+ ]
34
+ cupy = [
35
+ "cupy-cuda12x[ctk]>=13.0",
36
+ ]
37
+
38
+ [build-system]
39
+ requires = ["hatchling==1.32.0"]
40
+ build-backend = "hatchling.build"
41
+
42
+ [tool.hatch.build.targets.sdist]
43
+ only-include = ["src", "README.md", "pyproject.toml"]
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/ubo_morph"]
47
+
48
+ [tool.semantic_release]
49
+ branch = "main"
50
+ version_toml = ["pyproject.toml:project.version"]
51
+
52
+ [tool.commitizen]
53
+ name = "cz_conventional_commits"
54
+
55
+ [tool.ty.analysis]
56
+ # CuPy is an optional accelerator and is intentionally absent from base CI.
57
+ allowed-unresolved-imports = ["cupy", "cupyx.**"]
58
+
59
+ [dependency-groups]
60
+ dev = [
61
+ "commitizen>=4.13.0",
62
+ "pre-commit>=4.0.0",
63
+ "pytest>=8.0",
64
+ "ruff>=0.15.21",
65
+ "ty>=0.0.59",
66
+ ]
@@ -0,0 +1,21 @@
1
+ from ubo_morph.landmarks import (
2
+ DlibLandmarkExtractor,
3
+ LandmarkExtractor,
4
+ Landmarks,
5
+ MediaPipeLandmarkExtractor,
6
+ )
7
+ from ubo_morph.morphing import (
8
+ MorphResult,
9
+ morph_images,
10
+ morph_with_landmarks,
11
+ )
12
+
13
+ __all__ = [
14
+ "DlibLandmarkExtractor",
15
+ "LandmarkExtractor",
16
+ "Landmarks",
17
+ "MediaPipeLandmarkExtractor",
18
+ "MorphResult",
19
+ "morph_images",
20
+ "morph_with_landmarks",
21
+ ]