Skip to content
Open
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 apps/dash-3d-image-partitioning/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ tags
*~
venv*
__pycache__
.venv
17 changes: 11 additions & 6 deletions apps/dash-3d-image-partitioning/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@

## Setup

run `./setup` to create the virtualenv and download the necessary libraries.
This will also set up the git submodules.
run `./setup` to install the cairo system library (via brew, apt or dnf),
create the virtualenv and download the necessary libraries. Needs python 3.12
or newer.

To run programs in this directory, do `source venv/bin/activate` after running
To run programs in this directory, do `source .venv/bin/activate` after running
`./setup`.

Alternatively, with [uv](https://docs.astral.sh/uv/), run `uv sync`. Note that
this does not install cairo; run `./setup` first or install it yourself.

## Running

A recommended command line is

```bash
LOAD_SUPERPIXEL=assets/BraTS19_2013_10_1_flair_superpixels.npz.gz \
PYTHONPATH=plotly-common \
python app.py
python run_dev.py
```

This runs `app.py` with `LOAD_SUPERPIXEL` pointing at the precomputed
superpixel file in `assets`.

Then you can navigate to the displayed link in your browser. You can also run
without specifying `LOAD_SUPERPIXEL`, in which case the segmentation will happen
when the app loads.
Expand Down
9 changes: 4 additions & 5 deletions apps/dash-3d-image-partitioning/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import dash
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_html_components as html
import dash_core_components as dcc
from dash import html, dcc
import plotly.graph_objects as go
from skimage import data, img_as_ubyte, segmentation, measure
from dash_canvas.utils import array_to_data_url
Expand Down Expand Up @@ -53,7 +52,7 @@ def make_seg_image(img):
is clear (alpha=0) where there are no boundaries. """
segb = np.zeros_like(img).astype("uint8")
seg = segmentation.slic(
img, start_label=1, multichannel=False, compactness=0.1, n_segments=300
img, start_label=1, channel_axis=None, compactness=0.1, n_segments=300
)
# Only keep superpixels with an average intensity greater than threshold
# in order to remove superpixels of the background
Expand Down Expand Up @@ -122,7 +121,7 @@ def make_default_figure(


img = image.load_img("assets/BraTS19_2013_10_1_flair.nii")
img = img.get_data().transpose(2, 0, 1)[::-1].astype("float")
img = img.get_fdata().transpose(2, 0, 1)[::-1].astype("float")
img = img_as_ubyte((img - img.min()) / (img.max() - img.min()))


Expand Down Expand Up @@ -1023,4 +1022,4 @@ def update_click_output(button_click, close_click):


if __name__ == "__main__":
app.run_server(debug=DEBUG)
app.run(debug=DEBUG)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numpy as np
import plotly.graph_objects as go
import skimage.util
from plotly.utils import ImageUriValidator
from _plotly_utils.basevalidators import ImageUriValidator
import re


Expand Down
24 changes: 24 additions & 0 deletions apps/dash-3d-image-partitioning/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[project]
name = "dash-3d-image-partitioning"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"dash>=4.4.1",
"dash-canvas>=0.1.0",
"plotly>=7.0.0",
"scikit-image>=0.26.0",
"numpy>=2.5.2",
"nilearn>=0.14.1",
"nibabel>=5.4.2",
"cairosvg>=2.9.0",
"pillow>=12.3.0",
"gunicorn>=26.2.0",
]

[build-system]
requires = ["setuptools>=80"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
package-dir = { "" = "plotly-common" }
py-modules = ["plot_common", "image_utils", "shape_utils", "app_utils"]
130 changes: 56 additions & 74 deletions apps/dash-3d-image-partitioning/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,75 +1,57 @@
appdirs==1.4.4
attrs==19.3.0
backcall==0.2.0
black==19.10b0
Brotli==1.0.7
cairocffi==1.1.0
CairoSVG==2.4.2
cffi==1.14.0
click==7.1.2
cssselect2==0.3.0
cycler==0.10.0
dash==1.14.0
-e .
annotated-types==0.8.0
blinker==1.9.0
cairocffi==1.7.1
cairosvg==2.9.0
certifi==2026.7.22
cffi==2.1.1
charset-normalizer==3.5.1
click==8.5.0
cloudpickle==3.1.2
comm==0.2.3
cssselect2==0.10.1
dash==4.4.1
dash-canvas==0.1.0
dash-core-components==1.10.2
dash-html-components==1.0.3
dash-renderer==1.6.0
dash-table==4.9.0
decorator==4.4.2
defusedxml==0.6.0
Flask==1.1.2
Flask-Compress==1.5.0
future==0.18.2
gunicorn==20.0.4
imageio==2.8.0
importlib-metadata==1.6.1
ipython==7.15.0
ipython-genutils==0.2.0
itsdangerous==1.1.0
jedi==0.17.0
Jinja2==2.11.2
joblib==0.15.1
jsonschema==3.2.0
jupyter-core==4.6.3
kiwisolver==1.2.0
MarkupSafe==1.1.1
matplotlib==3.2.1
nbformat==5.0.7
networkx==2.4
nibabel==3.1.0
nilearn==0.6.2
numpy==1.18.5
packaging==20.4
pandas==1.0.4
parso==0.7.0
pathspec==0.8.0
pexpect==4.8.0
pickleshare==0.7.5
Pillow==8.2.0
plotly==4.8.2
prompt-toolkit==3.0.5
ptyprocess==0.6.0
pycparser==2.20
Pygments==2.6.1
pyparsing==2.4.7
pyrsistent==0.16.0
python-dateutil==2.8.1
pytz==2020.1
PyWavelets==1.1.1
regex==2020.6.8
retrying==1.3.3
scikit-image==0.17.2
scikit-learn==0.23.1
scipy==1.4.1
six==1.15.0
sklearn==0.0
threadpoolctl==2.1.0
tifffile==2020.6.3
tinycss2==1.0.2
toml==0.10.1
traitlets==4.3.3
typed-ast==1.4.1
wcwidth==0.2.4
webencodings==0.5.1
Werkzeug==1.0.1
zipp==3.1.0
defusedxml==0.7.1
flask==3.1.3
gunicorn==26.2.0
idna==3.19
imageio==2.37.4
importlib-metadata==9.0.1
itsdangerous==2.2.0
janus==2.0.0
jinja2==3.1.6
joblib==1.6.0
lazy-loader==0.5
markupsafe==3.0.3
narwhals==2.25.0
nest-asyncio==1.6.0
networkx==3.6.1
nibabel==5.4.2
nilearn==0.14.1
numpy==2.5.2
packaging==26.3
pandas==3.0.5
pillow==12.3.0
plotly==7.0.0
pycparser==3.0 ; implementation_name != 'PyPy'
pydantic==2.13.5
pydantic-core==2.46.5
python-dateutil==2.9.0.post0
requests==2.34.2
retrying==1.4.2
scikit-image==0.26.0
scikit-learn==1.8.0
scipy==1.18.1
setuptools==84.0.0
six==1.17.0
threadpoolctl==3.6.0
tifffile==2026.8.23
tinycss2==1.5.1
typing-extensions==4.16.0
typing-inspection==0.4.4
tzdata==2026.3 ; sys_platform == 'emscripten' or sys_platform == 'win32'
urllib3==2.7.0
webencodings==0.6.1
werkzeug==3.1.8
zipp==4.1.0
6 changes: 5 additions & 1 deletion apps/dash-3d-image-partitioning/run_app.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
gunicorn \
# On Apple Silicon, Homebrew installs libcairo under /opt/homebrew/lib, which
# macOS does not search by default. No-op elsewhere.
[ -d /opt/homebrew/lib ] && export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib

python -m gunicorn \
--pythonpath plotly-common \
-e LOAD_SUPERPIXEL=assets/BraTS19_2013_10_1_flair_superpixels.npz.gz \
app:server
31 changes: 31 additions & 0 deletions apps/dash-3d-image-partitioning/run_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Start the Dash development server with the environment app.py expects.

Works the same on macOS, Linux and Windows:

uv run run_dev.py (or: python run_dev.py)

Any environment variable you set yourself (LOAD_SUPERPIXEL, SAVE_SUPERPIXEL,
DEBUG, ...) is passed through unchanged.
"""

import os
import subprocess
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
PRECOMPUTED = "assets/BraTS19_2013_10_1_flair_superpixels.npz.gz"

env = dict(os.environ)

# if no LOAD/SAVE path given: load the precomputed superpixels from assets/
if not env.get("LOAD_SUPERPIXEL") and not env.get("SAVE_SUPERPIXEL"):
env["LOAD_SUPERPIXEL"] = PRECOMPUTED

# On Apple Silicon, Homebrew installs libcairo under /opt/homebrew/lib, which
# macOS does not search by default. No-op elsewhere.
if sys.platform == "darwin" and os.path.isdir("/opt/homebrew/lib"):
env.setdefault("DYLD_FALLBACK_LIBRARY_PATH", "/opt/homebrew/lib")

sys.exit(
subprocess.call([sys.executable, "app.py", *sys.argv[1:]], cwd=HERE, env=env)
)
29 changes: 23 additions & 6 deletions apps/dash-3d-image-partitioning/setup
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
#!/bin/bash


[ -d "venv" ] && (echo "venv already exisits, aborting..." && exit 1)
[ "$VIRTUAL_ENV" == "" ] || (echo "virtual env already active, aborting..." && exit 2)
[ -d ".venv" ] && { echo ".venv already exists, aborting..."; exit 1; }
[ "$VIRTUAL_ENV" == "" ] || { echo "virtual env already active, aborting..."; exit 2; }

# need python 3.12 or newer
python3 -c 'import sys; sys.exit(sys.version_info < (3, 12))' 2> /dev/null \
|| { echo "needs python 3.12 or newer, found: $(python3 --version 2>&1)"; exit 3; }

# install the cairo library that cairosvg needs
if [ "$(uname)" == "Darwin" ]; then
command -v brew > /dev/null || { echo "Homebrew is needed to install cairo: https://brew.sh"; exit 1; }
brew list cairo > /dev/null 2>&1 && echo "cairo already installed" || brew install cairo
elif command -v apt-get > /dev/null; then
dpkg -s libcairo2 > /dev/null 2>&1 && echo "cairo already installed" || sudo apt-get install -y libcairo2
elif command -v dnf > /dev/null; then
rpm -q cairo > /dev/null 2>&1 && echo "cairo already installed" || sudo dnf install -y cairo
else
echo "install the cairo library with your package manager, then rerun ./setup"
exit 1
fi

# create virtual environment
[ -d "venv" ] || virtualenv --prompt '(`basename $PWD`/`basename "$VIRTUAL_ENV"`)' venv
python3 -m venv --prompt "$(basename "$PWD")" .venv

source venv/bin/activate
source .venv/bin/activate

# install requirements
[ "$VIRTUAL_ENV" == "" ] \
&& echo "Error starting virtual environment." \
|| (pip install -r requirements.txt && cat <<DOC
|| (python -m pip install -r requirements.txt && cat <<DOC

setup successful
don't forget to
source venv/bin/activate
source .venv/bin/activate
before getting to work :-)
DOC
)
Loading