-
Notifications
You must be signed in to change notification settings - Fork 544
[WIP] BSP-OT: Sparse transport plans between discrete measures in loglinear time #768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dcoeurjo
wants to merge
75
commits into
PythonOT:master
Choose a base branch
from
dcoeurjo:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
75 commits
Select commit
Hold shift + click to select a range
1ae8472
Add TBA section to README
dcoeurjo e991d04
Merge branch 'master' into master
rflamary 4e9f1a4
add files
rflamary 5db0676
cleanup files
rflamary a42a0cf
remove readme line
rflamary ca8711b
debug cython now build fail because of c++ !
rflamary da9cc30
fix h file
rflamary f9b9c91
Adding static Eigen 3.4.0
dcoeurjo bf485bc
Merge branch 'master' into master
dcoeurjo 60f3ec3
up BSP-OT wrapper
baptiste-genest 15683c9
up BSP-OT wrapper
baptiste-genest d05597d
up BSP-OT wrapper for merging
baptiste-genest 3ed3b14
Merge branch 'master' into master
rflamary b64f5e5
Merging conflict
dcoeurjo 7e4962a
Detecting openmp flags on macOS (with libomp from brew) --lint
dcoeurjo 777ec24
Merging conflict
dcoeurjo fd0053f
std=c++17
dcoeurjo d9d92f0
RELEASE.md
dcoeurjo e6dbc3f
Removing static eigen
dcoeurjo bf96176
Eigen as submodule
dcoeurjo 88c795d
submodules in the GithubActions
dcoeurjo 81af86f
add norm as cost, change metric parameter name
baptiste-genest 6210214
fix merge
baptiste-genest 85c0755
fix merging
baptiste-genest 59ec8a4
Missing 'with submodule' in GA
dcoeurjo 13daaae
Moving eigen to deps
dcoeurjo 7bd21f1
Moving eigen to deps
dcoeurjo 06d6b37
Merge branch 'master' into master
dcoeurjo 1bdd23c
add initial plan in bijection compute
baptiste-genest 3cc66ea
add bsp_wrap.cpp to gitignore
baptiste-genest b29b46e
remove bsp_wrap.cpp
baptiste-genest f9daf2f
fix eigen path
baptiste-genest b153ae2
undo
baptiste-genest eec2629
remove filesystem and timing code
baptiste-genest b248092
Merge branch 'master' into master
rflamary 7f3dc42
Reverting openmp compilation flags
dcoeurjo a50746a
Reverting openmp compilation flags (ruff-format)
dcoeurjo 969c604
Removing deadcode
dcoeurjo 70bec56
Merge branch 'master' into master
dcoeurjo 1fa6bb9
Merge branch 'openmpFixMacos'
dcoeurjo 2b7fa53
Merging from PR#797
dcoeurjo 866befb
Manifest fix
dcoeurjo 560262e
Manifest (eigen)
dcoeurjo 135df1c
std++17 for windows
dcoeurjo c1f5848
Fixing path
dcoeurjo fee71ae
add example code and tests for bsp-ot
baptiste-genest 232f69e
Merge branch 'master' of github.com:dcoeurjo/POT
baptiste-genest 1476be6
fix file name in manifest
baptiste-genest e90453b
Merge branch 'master' into master
dcoeurjo 5d922f0
Merge branch 'master' of github.com:dcoeurjo/POT
baptiste-genest 1fea14e
fix compile_args in setup file
baptiste-genest e2849a2
fix formatting
baptiste-genest f61d618
remove parallelism in bijection merging, not needed and problems with…
baptiste-genest d367798
try to avoid openmp over range based for loop
baptiste-genest 19cd81f
upadte config.yml
rflamary c12374f
switch to 3.12
rflamary f115fb4
fix manifet
rflamary 567c4ca
verbose build for not being kille don circleci
rflamary cc80e72
try better ressource
rflamary 1a83fd8
micro fixes
baptiste-genest f659065
change wrapper code to handle generic backends
baptiste-genest 79fa638
fix backend code, add test for torch backend
baptiste-genest ffbe847
rename bsp_solve to compute_bspot_bijection
baptiste-genest 411d47c
add bsp module to doc
baptiste-genest e33415b
change bspot module description
baptiste-genest d4af9e0
add color transfer in example
baptiste-genest 2208c44
add gaussian slicing as an explicit option in the binding
baptiste-genest 5a1b90b
skip pytorch test if uninstalled
baptiste-genest 7d546f8
skip pytorch test if uninstalled, the right way
baptiste-genest c39389a
skip pytorch test if uninstalled, using POT backend
baptiste-genest 48227d4
modify API, add ref to paper, add seed for bspot, add heuristic for g…
baptiste-genest 5c16de6
Update RELEASES.md
baptiste-genest 09e6f3a
Add branch specification for eigen submodule
baptiste-genest da0a036
modify API, and push seed in wrapper
baptiste-genest 2fd0757
Merge branch 'master' of github.com:dcoeurjo/POT
baptiste-genest File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| [submodule "deps/eigen"] | ||
| path = deps/eigen | ||
| url = https://gitlab.com/libeigen/eigen.git | ||
| branch = 3.4 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule eigen
added at
d71c30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ API and modules | |
| backend | ||
| batch | ||
| bregman | ||
| bsp | ||
| coot | ||
| da | ||
| datasets | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| """ | ||
| =================== | ||
| Fast and accurate bijections using BSP-OT | ||
| =================== | ||
|
|
||
| This example shows two use cases for the bijections provided by BSP-OT, | ||
| between two large point clouds: shape morphing (and animated morphing) | ||
| and full color transfer (pixel permutation). | ||
|
|
||
| [83] Genest, B., Bonneel, N., Nivoliers, V., Coeurjolly, D. | ||
| BSP-OT: Sparse transport plans between discrete measures in log-linear time | ||
| ACM Transactions on Graphics, Siggraph Asia (2025). | ||
|
|
||
| """ | ||
|
|
||
| # Author: Baptiste Genest <baptistegenest@gmail.com> | ||
| # | ||
| # License: MIT License | ||
|
|
||
| import ot.utils | ||
| import ot.bsp | ||
| import numpy as np | ||
| import time | ||
| import matplotlib.pyplot as plt | ||
| from matplotlib.animation import FuncAnimation | ||
|
|
||
| ############################################################################## | ||
| # Shape interpolation and morphing | ||
| # ---------------------------------- | ||
|
|
||
| ############################################################################## | ||
| # Data generation | ||
| # ---------------------------------- | ||
|
|
||
| # %% Two large 2D point clouds | ||
| # For this example, let's create two large 2D point clouds, | ||
| # one sampled from a single ball, and the other from two smaller balls. | ||
|
|
||
|
|
||
| def sample_ball(n, radius=1.0, center=(0.0, 0.0)): | ||
| np.random.seed(0) | ||
| theta = 2 * np.pi * np.random.rand(n) | ||
| r = radius * np.sqrt(np.random.rand(n)) | ||
|
|
||
| x = r * np.cos(theta) + center[0] | ||
| y = r * np.sin(theta) + center[1] | ||
|
|
||
| return np.stack((x, y), axis=1) | ||
|
|
||
|
|
||
| def sample_two_balls(n, radius=1.0, centers=((-1, 0), (1, 1))): | ||
| assert n % 2 == 0, "n must be even" | ||
|
|
||
| n_half = n // 2 | ||
| X1 = sample_ball(n_half, radius, centers[0]) | ||
| X2 = sample_ball(n_half, radius, centers[1]) | ||
|
|
||
| return np.vstack((X1, X2)) | ||
|
|
||
|
|
||
| # ---------------------------- | ||
| # Load point clouds | ||
| # ---------------------------- | ||
| N = 100000 | ||
| X_s = sample_ball(N) | ||
| X_t = sample_two_balls(N, 0.5) | ||
|
|
||
| N = X_s.shape[0] | ||
|
|
||
|
|
||
| ############################################################################## | ||
| # Bijection computation | ||
| # ---------------------------------- | ||
|
|
||
| ot.utils.tic() | ||
|
|
||
| # %% call BSP-OT solver | ||
| # The solver returns the transport cost, the final bijection and the | ||
| # intermediary ones used to compute the final one (here we set k = 64). | ||
| # Here we only use the final bijection. | ||
| cost, perm, _ = ot.bsp.compute_bspot_bijection(X_s, X_t, 64, 2) | ||
| print( | ||
| "Bijection computed between {} points, with cost {} in {}s".format( | ||
| N, cost, ot.utils.toc() | ||
| ) | ||
| ) | ||
|
|
||
| # %% Reordering | ||
| # As the plan is a bijection, it is simply stored as permutation (e.g. a list of numbers) | ||
| # such that X_s[i] is assigned to X_t[perm[i]]. | ||
| # For the sake of the animation, we reorder X_t according to the obtained bijection | ||
| # such that the points are in correspondence along the morphing animation | ||
| # using simply X_s*(1-t) + X_t*t | ||
| X_t_perm = X_t[perm] | ||
|
|
||
| ############################################################################## | ||
| # Setup animation | ||
| # ---------------------------- | ||
|
|
||
| # Animation parameters | ||
| FRAMES = 100 | ||
| INTERVAL = 20 | ||
|
|
||
| # Plot setup | ||
| fig, ax = plt.subplots(figsize=(6, 6)) | ||
| ax.set_aspect("equal") | ||
| ax.set_title("Bijective Point Cloud Morphing") | ||
|
|
||
| scat = ax.scatter(X_s[:, 0], X_s[:, 1], s=0.05, c="tab:blue") | ||
|
|
||
| all_pts = np.vstack([X_s, X_t_perm]) | ||
| pad = 0.5 | ||
| ax.set_xlim(all_pts[:, 0].min() - pad, all_pts[:, 0].max() + pad) | ||
| ax.set_ylim(all_pts[:, 1].min() - pad, all_pts[:, 1].max() + pad) | ||
|
|
||
|
|
||
| # ---------------------------- | ||
| # Animation update | ||
| # ---------------------------- | ||
| def update(frame): | ||
| t = frame / (FRAMES - 1) | ||
| t = t * t * (3 - 2 * t) | ||
| P = (1 - t) * X_s + t * X_t_perm | ||
| scat.set_offsets(P) | ||
| return (scat,) | ||
|
|
||
|
|
||
| # ---------------------------- | ||
| # Run animation | ||
| # ---------------------------- | ||
| ani = FuncAnimation(fig, update, frames=FRAMES, interval=INTERVAL, blit=True) | ||
|
|
||
| plt.show() | ||
|
|
||
|
|
||
| ############################################################################## | ||
| # Other example: full color transfer (pixel permutation) using BSP-OT | ||
| # ---------------------------- | ||
|
|
||
| import os | ||
| from pathlib import Path | ||
| import numpy as np | ||
| from PIL import Image | ||
|
|
||
|
|
||
| def im2mat(img): | ||
| """Converts an image to matrix (one pixel per line)""" | ||
| return img.reshape((img.shape[0] * img.shape[1], img.shape[2])) | ||
|
|
||
|
|
||
| def mat2im(X, shape): | ||
| """Converts back a matrix to an image""" | ||
| return X.reshape(shape) | ||
|
|
||
|
|
||
| def minmax(img): | ||
| return np.clip(img, 0, 1) | ||
|
|
||
|
|
||
| this_file = os.path.realpath("__file__") | ||
| data_path = os.path.join(Path(this_file).parent.parent, "data") | ||
|
|
||
|
|
||
| I1_pil = Image.open(os.path.join(data_path, "ocean_day.jpg")).convert("RGB") | ||
| I2_pil = Image.open(os.path.join(data_path, "ocean_sunset.jpg")).convert("RGB") | ||
|
|
||
| # force same size to obtain bijection between all pixels | ||
| I1_pil = I1_pil.resize(I2_pil.size, Image.BILINEAR) | ||
|
|
||
|
|
||
| I1 = np.asarray(I1_pil).astype(np.float64) / 255.0 | ||
| I2 = np.asarray(I2_pil).astype(np.float64) / 255.0 | ||
|
|
||
| X1 = im2mat(I1) | ||
| X2 = im2mat(I2) | ||
|
|
||
|
|
||
| start = ot.utils.tic() | ||
|
|
||
| _, perm, _ = ot.bsp.compute_bspot_bijection(X1, X2, 16) | ||
|
|
||
| print("Bijection computed between {} pixels in {}s".format(X1.shape[0], ot.utils.toc())) | ||
|
|
||
| # reorder the second image according to the obtained bijection | ||
| X2_perm = X2[perm] | ||
|
|
||
| # reshape back to image | ||
| I2_perm = mat2im(X2_perm, I1.shape) | ||
|
|
||
| plt.figure(figsize=(12, 6)) | ||
|
|
||
| plt.subplot(1, 3, 1) | ||
| plt.imshow(I2) | ||
| plt.title("Color image") | ||
| plt.axis("off") | ||
|
|
||
| plt.subplot(1, 3, 2) | ||
| plt.imshow(I1) | ||
| plt.title("Target image") | ||
| plt.axis("off") | ||
|
|
||
| plt.subplot(1, 3, 3) | ||
| plt.imshow(I2_perm) | ||
| plt.title("Permuted image") | ||
| plt.axis("off") | ||
|
|
||
| plt.tight_layout() | ||
| plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please fix the version/commit of eigen here. fdor instance with branch=5.0