Skip to content

Commit

Permalink
Implementation of ImageStack (#5751)
Browse files Browse the repository at this point in the history
Co-authored-by: Philipp Rudiger <[email protected]>
Co-authored-by: Andrew Huang <[email protected]>
Co-authored-by: Andrew <[email protected]>
Co-authored-by: MeggyCal <[email protected]>
  • Loading branch information
5 people authored Sep 14, 2023
1 parent fce8dda commit 923cd27
Show file tree
Hide file tree
Showing 19 changed files with 923 additions and 145 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
matrix:
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
python-version: ['3.8', '3.11']
bokeh-version: ['2', '3']
bokeh-version: ['3']
timeout-minutes: 120
defaults:
run:
Expand Down
113 changes: 113 additions & 0 deletions examples/reference/elements/bokeh/ImageStack.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
"<dl class=\"dl-horizontal\">\n",
" <dt>Title</dt> <dd> ImageStack Element</dd>\n",
" <dt>Dependencies</dt> <dd>Bokeh</dd>\n",
" <dt>Backends</dt>\n",
" <dd><a href='./ImageStack.ipynb'>Bokeh</a></dd>\n",
" <dd><a href='../matplotlib/ImageStack.ipynb'>Matplotlib</a></dd>\n",
" <dd><a href='../plotly/ImageStack.ipynb'>Plotly</a></dd>\n",
"</dl>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import holoviews as hv\n",
"hv.extension('bokeh')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"``ImageStack`` facilitates the representation of a regularly spaced 2D grid within a continuous domain of color space values (RGB(A)). The grid's structure aligns closely with that of an Image element. In its simplest form, the grid can be defined through an array with dimensions of ``NxMxL``, where ``L`` represents the number of channels. Alternatively, explicit and uniformly spaced x/y-coordinate arrays can also define the ``ImageStack``.\n",
"\n",
"The core methods for constructing an ``ImageStack`` are:\n",
"\n",
"1. Creating using coordinates and channel values:\n",
" ```\n",
" ImageStack((X, Y, L1, L2, ..., LL), vdims=[\"l1\", \"l2\", ... \"ll\"])\n",
" ```\n",
" Here, ``X`` is a 1D array with ``M`` elements, ``Y`` is a 1D array with ``N`` elements, and ``L1``, ``L2``, ..., ``LL`` represent 2D arrays with dimensions ``NxM``.\n",
"\n",
"2. Creation through a composite array and bounds specification:\n",
" ```\n",
" ImageStack(Z, bounds=(x0, y0, x1, y1))\n",
" ```\n",
" In this scenario, ``Z`` is a 3D array with dimensions ``NxMxL``, and the bounds parameter defines the (left, bottom, right, top) corners of the grid.\n",
"\n",
"For comprehensive information, refer to the [Gridded Datasets](../../../user_guide/09-Gridded_Datasets.ipynb) user guide."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"x = np.arange(0, 3)\n",
"y = np.arange(5, 8)\n",
"a = np.array([[np.nan, np.nan, 1], [np.nan] * 3, [np.nan] * 3])\n",
"b = np.array([[np.nan] * 3, [1, 1, np.nan], [np.nan] * 3])\n",
"c = np.array([[np.nan] * 3, [np.nan] * 3, [1, 1, 1]])\n",
"\n",
"img_stack = hv.ImageStack((x, y, a, b, c), kdims=[\"x\", \"y\"], vdims=[\"a\", \"b\", \"c\"])\n",
"img_stack"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `cmap` can be added to differentiate the different levels."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cmap = [\"red\", \"green\", \"blue\"]\n",
"img_stack.opts(cmap=cmap)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Slicing, sampling, etc. on an ``ImageStack`` all operate in this continuous space, whereas the corresponding operations on a ``Raster`` work on the raw array coordinates.\n",
"\n",
"Here we take slices up to x=0.5 and y=7, which is out of bounds for the red."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"img_stack[:0.5, :7]"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
98 changes: 98 additions & 0 deletions examples/reference/elements/matplotlib/ImageStack.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
"<dl class=\"dl-horizontal\">\n",
" <dt>Title</dt> <dd> ImageStack Element</dd>\n",
" <dt>Dependencies</dt> <dd>Bokeh</dd>\n",
" <dt>Backends</dt>\n",
" <dd><a href='./ImageStack.ipynb'>Bokeh</a></dd>\n",
" <dd><a href='../matplotlib/ImageStack.ipynb'>Matplotlib</a></dd>\n",
" <dd><a href='../plotly/ImageStack.ipynb'>Plotly</a></dd>\n",
"</dl>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import holoviews as hv\n",
"hv.extension('matplotlib')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"``ImageStack`` facilitates the representation of a regularly spaced 2D grid within a continuous domain of color space values (RGB(A)). The grid's structure aligns closely with that of an Image element. In its simplest form, the grid can be defined through an array with dimensions of ``NxMxL``, where ``L`` represents the number of channels. Alternatively, explicit and uniformly spaced x/y-coordinate arrays can also define the ``ImageStack``.\n",
"\n",
"The core methods for constructing an ``ImageStack`` are:\n",
"\n",
"1. Creating using coordinates and channel values:\n",
" ```\n",
" ImageStack((X, Y, L1, L2, ..., LL), vdims=[\"l1\", \"l2\", ... \"ll\"])\n",
" ```\n",
" Here, ``X`` is a 1D array with ``M`` elements, ``Y`` is a 1D array with ``N`` elements, and ``L1``, ``L2``, ..., ``LL`` represent 2D arrays with dimensions ``NxM``.\n",
"\n",
"2. Creation through a composite array and bounds specification:\n",
" ```\n",
" ImageStack(Z, bounds=(x0, y0, x1, y1))\n",
" ```\n",
" In this scenario, ``Z`` is a 3D array with dimensions ``NxMxL``, and the bounds parameter defines the (left, bottom, right, top) corners of the grid.\n",
"\n",
"For comprehensive information, refer to the [Gridded Datasets](../../../user_guide/09-Gridded_Datasets.ipynb) user guide.\n",
"\n",
"*Note* The `datashader` library must be installed to use ``ImageStack`` with the matplotlib backend."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"x = np.arange(0, 3)\n",
"y = np.arange(5, 8)\n",
"a = np.array([[np.nan, np.nan, 1], [np.nan] * 3, [np.nan] * 3])\n",
"b = np.array([[np.nan] * 3, [1, 1, np.nan], [np.nan] * 3])\n",
"c = np.array([[np.nan] * 3, [np.nan] * 3, [1, 1, 1]])\n",
"\n",
"img_stack = hv.ImageStack((x, y, a, b, c), kdims=[\"x\", \"y\"], vdims=[\"a\", \"b\", \"c\"])\n",
"img_stack"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Slicing, sampling, etc. on an ``ImageStack`` all operate in this continuous space, whereas the corresponding operations on a ``Raster`` work on the raw array coordinates.\n",
"\n",
"Here we take slices up to x=0.5 and y=7, which is out of bounds for the red."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"img_stack[:0.5, :7]"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
98 changes: 98 additions & 0 deletions examples/reference/elements/plotly/ImageStack.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
"<dl class=\"dl-horizontal\">\n",
" <dt>Title</dt> <dd> ImageStack Element</dd>\n",
" <dt>Dependencies</dt> <dd>Bokeh</dd>\n",
" <dt>Backends</dt>\n",
" <dd><a href='./ImageStack.ipynb'>Bokeh</a></dd>\n",
" <dd><a href='../matplotlib/ImageStack.ipynb'>Matplotlib</a></dd>\n",
" <dd><a href='../plotly/ImageStack.ipynb'>Plotly</a></dd>\n",
"</dl>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import holoviews as hv\n",
"hv.extension('plotly')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"``ImageStack`` facilitates the representation of a regularly spaced 2D grid within a continuous domain of color space values (RGB(A)). The grid's structure aligns closely with that of an Image element. In its simplest form, the grid can be defined through an array with dimensions of ``NxMxL``, where ``L`` represents the number of channels. Alternatively, explicit and uniformly spaced x/y-coordinate arrays can also define the ``ImageStack``.\n",
"\n",
"The core methods for constructing an ``ImageStack`` are:\n",
"\n",
"1. Creating using coordinates and channel values:\n",
" ```\n",
" ImageStack((X, Y, L1, L2, ..., LL), vdims=[\"l1\", \"l2\", ... \"ll\"])\n",
" ```\n",
" Here, ``X`` is a 1D array with ``M`` elements, ``Y`` is a 1D array with ``N`` elements, and ``L1``, ``L2``, ..., ``LL`` represent 2D arrays with dimensions ``NxM``.\n",
"\n",
"2. Creation through a composite array and bounds specification:\n",
" ```\n",
" ImageStack(Z, bounds=(x0, y0, x1, y1))\n",
" ```\n",
" In this scenario, ``Z`` is a 3D array with dimensions ``NxMxL``, and the bounds parameter defines the (left, bottom, right, top) corners of the grid.\n",
"\n",
"For comprehensive information, refer to the [Gridded Datasets](../../../user_guide/09-Gridded_Datasets.ipynb) user guide.\n",
"\n",
"*Note* The `datashader` library must be installed to use ``ImageStack`` with the plotly backend."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"x = np.arange(0, 3)\n",
"y = np.arange(5, 8)\n",
"a = np.array([[np.nan, np.nan, 1], [np.nan] * 3, [np.nan] * 3])\n",
"b = np.array([[np.nan] * 3, [1, 1, np.nan], [np.nan] * 3])\n",
"c = np.array([[np.nan] * 3, [np.nan] * 3, [1, 1, 1]])\n",
"\n",
"img_stack = hv.ImageStack((x, y, a, b, c), kdims=[\"x\", \"y\"], vdims=[\"a\", \"b\", \"c\"])\n",
"img_stack"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Slicing, sampling, etc. on an ``ImageStack`` all operate in this continuous space, whereas the corresponding operations on a ``Raster`` work on the raw array coordinates.\n",
"\n",
"Here we take slices up to x=0.5 and y=7, which is out of bounds for the red."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"img_stack[:0.5, :7]"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
5 changes: 3 additions & 2 deletions examples/user_guide/15-Large_Data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@
"metadata": {},
"outputs": [],
"source": [
"gaussians = {i: hv.Points(rand_gauss2d(i), kdims, \"i\") for i in range(num_ks)}\n",
"gaussians = {str(i): hv.Points(rand_gauss2d(i), kdims, \"i\") for i in range(num_ks)}\n",
"\n",
"c = dynspread(datashade(hv.NdOverlay(gaussians, kdims='k'), aggregator=ds.by('k', ds.count())))\n",
"m = dynspread(datashade(hv.NdOverlay(gaussians, kdims='k'), aggregator=ds.by('k', ds.mean(\"i\"))))\n",
"\n",
Expand All @@ -462,7 +463,7 @@
"metadata": {},
"outputs": [],
"source": [
"lines = {i: hv.Curve(time_series(N=10000, S0=200+np.random.rand())) for i in range(num_ks)}\n",
"lines = {str(i): hv.Curve(time_series(N=10000, S0=200+np.random.rand())) for i in range(num_ks)}\n",
"lineoverlay = hv.NdOverlay(lines, kdims='k')\n",
"datashade(lineoverlay, pixel_ratio=2, line_width=4, aggregator=ds.by('k', ds.count())).opts(width=800)"
]
Expand Down
10 changes: 7 additions & 3 deletions holoviews/core/data/xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def retrieve_unit_and_label(dim):
if isinstance(label, str):
vdim.label = label
elif len(vdim_param.default) == 1:
vdim = vdim_param.default[0]
vdim = asdim(vdim_param.default[0])
if vdim.name in data.dims:
raise DataError("xarray DataArray does not define a name, "
"and the default of '%s' clashes with a "
Expand All @@ -121,8 +121,12 @@ def retrieve_unit_and_label(dim):
"supply an explicit vdim." % eltype.__name__,
cls)
if not packed:
vdims = [vdim]
data = data.to_dataset(name=vdim.name)
if vdim in data.dims:
data = data.to_dataset(vdim.name)
vdims = [asdim(vd) for vd in data.data_vars]
else:
vdims = [vdim]
data = data.to_dataset(name=vdim.name)

if not isinstance(data, (xr.Dataset, xr.DataArray)):
if kdims is None:
Expand Down
Loading

0 comments on commit 923cd27

Please sign in to comment.