Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ venv/
*.swo

dev/
ref/

*copy*
48 changes: 28 additions & 20 deletions notebooks/example.api.high.ipynb

Large diffs are not rendered by default.

44 changes: 31 additions & 13 deletions notebooks/example.api.low.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
"import matplotlib.pyplot as plt\n",
"from matplotlib.colors import Normalize\n",
"\n",
"from pywarper.surface import fit_sac_surface, build_mapping\n",
"from pywarper.surface import fit_surface, build_mapping # fit_sac_surface is also available as a legacy alias\n",
"from pywarper.warpers import warp_nodes, normalize_nodes, warp_skeleton\n",
"from pywarper.stats import get_convex_hull, get_hull_centroid, get_xy_center_of_mass\n",
"from pywarper.utils import read_sumbul_et_al_chat_bands\n",
"\n"
"from pywarper.utils import read_sumbul_et_al_chat_bands\n"
]
},
{
Expand Down Expand Up @@ -54,8 +53,11 @@
"outputs": [],
"source": [
"# Fit surfaces with pygridfit under the hood\n",
"off_sac_surface, xgridmax, ygridmax = fit_sac_surface(x=off_sac['x'], y=off_sac['y'], z=off_sac['z'], smoothness=15)\n",
"on_sac_surface, xgridmin, ygridmin = fit_sac_surface(x=on_sac['x'], y=on_sac['y'], z=on_sac['z'], smoothness=15)"
"off_sac_surface, xgridmax, ygridmax = fit_surface(x=off_sac['x'], y=off_sac['y'], z=off_sac['z'], smoothness=15)\n",
"on_sac_surface, xgridmin, ygridmin = fit_surface(x=on_sac['x'], y=on_sac['y'], z=on_sac['z'], smoothness=15)\n",
"# legacy API\n",
"# off_sac_surface, xgridmax, ygridmax = fit_sac_surface(x=off_sac['x'], y=off_sac['y'], z=off_sac['z'], smoothness=15)\n",
"# on_sac_surface, xgridmin, ygridmin = fit_sac_surface(x=on_sac['x'], y=on_sac['y'], z=on_sac['z'], smoothness=15)"
]
},
{
Expand All @@ -68,17 +70,32 @@
"name": "stdout",
"output_type": "stream",
"text": [
"↳ mapping ON (min) surface …\n",
" done in 0.01 seconds.\n",
"↳ mapping OFF (max) surface …\n",
" done in 0.01 seconds.\n"
"↳ mapping 'on_sac' surface …\n",
" done in 0.02 seconds.\n",
"↳ mapping 'off_sac' surface …\n",
" done in 0.02 seconds.\n"
]
}
],
"source": [
"# fit quasi-conformally mapping\n",
"arbor_boundaries = np.array([skel.nodes[:, 0].min(), skel.nodes[:, 0].max(), skel.nodes[:, 1].min(), skel.nodes[:, 1].max()])\n",
"surface_mapping = build_mapping(on_sac_surface, off_sac_surface, arbor_boundaries, conformal_jump=2, n_anchors=16, verbose=True)"
"surface_mapping = build_mapping( \n",
" {\"on_sac\": on_sac_surface, \"off_sac\": off_sac_surface}, \n",
" bounds=arbor_boundaries, \n",
" conformal_jump=2, \n",
" n_anchors=16, \n",
" verbose=True,\n",
")\n",
"# legacy API\n",
"# surface_mapping = build_mapping(\n",
"# on_sac_surface=on_sac_surface,\n",
"# off_sac_surface=off_sac_surface,\n",
"# bounds=arbor_boundaries,\n",
"# conformal_jump=2,\n",
"# n_anchors=16,\n",
"# verbose=True,\n",
"# )"
]
},
{
Expand All @@ -92,7 +109,8 @@
"output_type": "stream",
"text": [
"[pywarper] Warping skeleton...\n",
" done in 0.36 seconds.\n",
"[pywarper] Flattening with surfaces: \"on_sac\" (z=41.31) and \"off_sac\" (z=56.00)\n",
" done in 0.29 seconds.\n",
"[skeliner] Warning: 58 nodes have zero radius; they were ignored when picking the estimator.\n"
]
}
Expand Down Expand Up @@ -268,7 +286,7 @@
{
"data": {
"text/plain": [
"<matplotlib.legend.Legend at 0x117041310>"
"<matplotlib.legend.Legend at 0x119bb5310>"
]
},
"execution_count": 8,
Expand Down Expand Up @@ -338,7 +356,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Last updated: 2026-02-16 16:06:33 CET\n",
"Last updated: 2026-03-03 15:40:46 CET\n",
"\n",
"Python implementation: CPython\n",
"Python version : 3.13.11\n",
Expand Down
62 changes: 46 additions & 16 deletions notebooks/example.unwarp.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"import skeliner as sk\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from pywarper.surface import fit_sac_surface, build_mapping\n",
"from pywarper.surface import fit_surface, build_mapping # fit_sac_surface is also available as a legacy alias\n",
"from pywarper.warpers import warp_nodes, normalize_nodes, warp_skeleton\n",
"from pywarper.utils import read_sumbul_et_al_chat_bands\n",
"from pywarper.unwarper import unwarp_nodes, unwarp_skeleton\n"
"from pywarper.unwarper import unwarp_nodes, unwarp_skeleton"
]
},
{
Expand Down Expand Up @@ -48,8 +48,11 @@
"outputs": [],
"source": [
"# Fit surfaces with pygridfit under the hood\n",
"off_sac_surface, xgridmax, ygridmax = fit_sac_surface(x=off_sac['x'], y=off_sac['y'], z=off_sac['z'], smoothness=15)\n",
"on_sac_surface, xgridmin, ygridmin = fit_sac_surface(x=on_sac['x'], y=on_sac['y'], z=on_sac['z'], smoothness=15)\n"
"off_sac_surface, xgridmax, ygridmax = fit_surface(x=off_sac['x'], y=off_sac['y'], z=off_sac['z'], smoothness=15)\n",
"on_sac_surface, xgridmin, ygridmin = fit_surface(x=on_sac['x'], y=on_sac['y'], z=on_sac['z'], smoothness=15)\n",
"# legacy API\n",
"# off_sac_surface, xgridmax, ygridmax = fit_sac_surface(x=off_sac['x'], y=off_sac['y'], z=off_sac['z'], smoothness=15)\n",
"# on_sac_surface, xgridmin, ygridmin = fit_sac_surface(x=on_sac['x'], y=on_sac['y'], z=on_sac['z'], smoothness=15)"
]
},
{
Expand All @@ -61,17 +64,32 @@
"name": "stdout",
"output_type": "stream",
"text": [
"↳ mapping ON (min) surface …\n",
" done in 0.02 seconds.\n",
"↳ mapping OFF (max) surface …\n",
" done in 0.02 seconds.\n"
"↳ mapping 'on_sac' surface …\n",
" done in 0.01 seconds.\n",
"↳ mapping 'off_sac' surface …\n",
" done in 0.01 seconds.\n"
]
}
],
"source": [
"# fit quasi-conformally mapping\n",
"arbor_boundaries = np.array([skel.nodes[:, 0].min(), skel.nodes[:, 0].max(), skel.nodes[:, 1].min(), skel.nodes[:, 1].max()])\n",
"surface_mapping = build_mapping(on_sac_surface, off_sac_surface, arbor_boundaries, conformal_jump=2, n_anchors=16, verbose=True)\n"
"surface_mapping = build_mapping(\n",
" {\"on_sac\": on_sac_surface, \"off_sac\": off_sac_surface},\n",
" bounds=arbor_boundaries,\n",
" conformal_jump=2,\n",
" n_anchors=16,\n",
" verbose=True,\n",
")\n",
"# legacy API\n",
"# surface_mapping = build_mapping(\n",
"# on_sac_surface=on_sac_surface,\n",
"# off_sac_surface=off_sac_surface,\n",
"# bounds=arbor_boundaries,\n",
"# conformal_jump=2,\n",
"# n_anchors=16,\n",
"# verbose=True,\n",
"# )"
]
},
{
Expand All @@ -84,7 +102,8 @@
"output_type": "stream",
"text": [
"[pywarper] Warping skeleton...\n",
" done in 0.40 seconds.\n",
"[pywarper] Flattening with surfaces: \"on_sac\" (z=41.31) and \"off_sac\" (z=56.00)\n",
" done in 0.28 seconds.\n",
"[skeliner] Warning: 58 nodes have zero radius; they were ignored when picking the estimator.\n"
]
}
Expand Down Expand Up @@ -113,6 +132,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[pywarper] Unwarping with surfaces: \"on_sac\" (z=41.31) and \"off_sac\" (z=56.00)\n",
"[pywarper] Unwarping with surfaces: \"on_sac\" (z=41.31) and \"off_sac\" (z=56.00)\n",
"Skeleton local_ls error (mean / p95 / max): 0.0060 / 0.0257 / 0.0664\n",
"Skeleton optimize error on subset (mean / p95 / max): 5.2062e-04 / 1.9949e-03 / 2.6957e-02\n"
]
Expand Down Expand Up @@ -145,6 +166,7 @@
" on_sac_pos=0,\n",
" off_sac_pos=12,\n",
" conformal_jump=2,\n",
" verbose=True,\n",
")\n",
"\n",
"# option 2: inverse-by-optimization (iterative; use subset for notebook speed)\n",
Expand All @@ -159,6 +181,7 @@
" max_evals_per_point=100,\n",
" convergence_tol=1e-9,\n",
" bound_xy_to_map=True,\n",
" verbose=True,\n",
")\n",
"\n",
"err_skel_local = np.linalg.norm(unwarped_skel_local - skel.nodes, axis=1)\n",
Expand Down Expand Up @@ -203,7 +226,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -248,17 +271,23 @@
"])\n",
"\n",
"# these are the coordinates that would be annotated in warped space\n",
"annot_prenorm, _, _ = warp_nodes(annot_orig, surface_mapping, conformal_jump=2)\n",
"annot_prenorm, median_depths = warp_nodes(annot_orig, surface_mapping, conformal_jump=2)\n",
"annot_warped = normalize_nodes(\n",
" annot_prenorm,\n",
" med_z_on=med_z_on,\n",
" med_z_off=med_z_off,\n",
" median_depths=median_depths,\n",
")\n",
"# legacy API\n",
"# annot_warped = normalize_nodes(\n",
"# annot_prenorm,\n",
"# med_z_on=med_z_on,\n",
"# med_z_off=med_z_off,\n",
"# )\n",
"\n",
"annot_unwarped_local = unwarp_nodes(\n",
" annot_warped,\n",
" surface_mapping,\n",
" conformal_jump=2,\n",
" verbose=True,\n",
")\n",
"annot_unwarped_opt = unwarp_nodes(\n",
" annot_warped,\n",
Expand All @@ -268,6 +297,7 @@
" max_evals_per_point=100,\n",
" convergence_tol=1e-9,\n",
" bound_xy_to_map=True,\n",
" verbose=True,\n",
")\n",
"\n",
"err_ann_local = np.linalg.norm(annot_unwarped_local - annot_orig, axis=1)\n",
Expand Down Expand Up @@ -301,7 +331,7 @@
"ax.set_xlabel('X (μm)')\n",
"ax.set_ylabel('Z (μm)')\n",
"ax.legend(frameon=False, loc='best')\n",
"fig.tight_layout()\n"
"fig.tight_layout()"
]
},
{
Expand Down Expand Up @@ -371,7 +401,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Last updated: 2026-02-16 16:06:53 CET\n",
"Last updated: 2026-03-03 15:57:32 CET\n",
"\n",
"Python implementation: CPython\n",
"Python version : 3.13.11\n",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "pywarper"
version = "0.2.5"
version = "0.3.0"
description = "Conformal mapping-based warping of retinal morphologies."
authors = []
requires-python = ">=3.10.0"
Expand Down
Loading