-
Notifications
You must be signed in to change notification settings - Fork 24
ImageSeriesWidget fixes #196
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
base: master
Are you sure you want to change the base?
Changes from 44 commits
0606860
eaa77b8
8e38592
22396a5
3603e17
68a4e80
4940ee7
3a6a197
f5e21cb
b438b7c
234f37d
e5024ad
6a45abd
45b2098
b91e39a
5211089
85f71d8
45b1f7c
b6bcaad
1095759
bf5fa4d
1136fac
2d02901
a7c06e7
34a053c
8906b88
9782709
17d9495
d2b7d59
22a34ff
0288883
4d785c2
7dda8db
f31e312
f73fde3
798a528
c8a622f
aa04d9f
d29ea9b
5509487
b5af95c
0213ccc
9737047
29b003e
8e72ae9
9bb4cf0
3add69a
ae8e9cc
c2e2be0
fe7d182
85a0d0e
3ea7f58
329d702
6541993
089ed60
1d131bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,94 +1,173 @@ | ||
| from pathlib import Path, PureWindowsPath | ||
| from pathlib import Path | ||
| from typing import Union | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
| import plotly.graph_objects as go | ||
| import pynwb | ||
| from ipywidgets import widgets, fixed, Layout | ||
| from ipywidgets import widgets, Layout | ||
| from pynwb.image import GrayscaleImage, ImageSeries, RGBImage | ||
| from tifffile import imread, TiffFile | ||
|
|
||
| from .base import fig2widget | ||
| from .controllers import StartAndDurationController | ||
| from .utils.cmaps import linear_transfer_function | ||
| from .utils.imageseries import get_frame_count, get_frame | ||
| from .utils.timeseries import ( | ||
| get_timeseries_maxt, | ||
| get_timeseries_mint, | ||
| timeseries_time_to_ind, | ||
| ) | ||
|
|
||
| PathType = Union[str, Path] | ||
|
|
||
|
|
||
| class ImageSeriesWidget(widgets.VBox): | ||
| """Widget showing ImageSeries.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| imageseries: ImageSeries, | ||
| foreign_time_window_controller: StartAndDurationController = None, | ||
| **kwargs | ||
| foreign_time_slider: widgets.FloatSlider = None, | ||
| neurodata_vis_spec: dict = None, | ||
| ): | ||
| super().__init__() | ||
| self.imageseries = imageseries | ||
| self.controls = {} | ||
| self.out_fig = None | ||
| self.figure = None | ||
| self.time_slider = foreign_time_slider | ||
|
|
||
| # Set controller | ||
| if foreign_time_window_controller is None: | ||
| tmin = get_timeseries_mint(imageseries) | ||
| if imageseries.external_file and imageseries.rate: | ||
| tif = TiffFile(imageseries.external_file[0]) | ||
| tmax = imageseries.starting_time + len(tif.pages) / imageseries.rate | ||
| else: | ||
| tmax = get_timeseries_maxt(imageseries) | ||
| self.time_window_controller = StartAndDurationController(tmax, tmin) | ||
| if imageseries.external_file is not None: | ||
|
|
||
| # set time slider: | ||
| tmax = ( | ||
| imageseries.starting_time | ||
| + get_frame_count(imageseries.external_file[0]) / imageseries.rate | ||
| ) | ||
| if self.time_slider is None: | ||
| self.time_slider = widgets.FloatSlider( | ||
| min=imageseries.starting_time, | ||
| max=tmax, | ||
| orientation="horizontal", | ||
| description="time(s)", | ||
| ) | ||
| external_file = imageseries.external_file[0] | ||
| self.file_selector = None | ||
| # set file selector: | ||
| if len(imageseries.external_file) > 1: | ||
| self.file_selector = widgets.Dropdown(options=imageseries.external_file) | ||
| external_file = self.file_selector.value | ||
|
|
||
| def update_time_slider(value): | ||
Saksham20 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| path_ext_file = value["new"] | ||
| # Read first frame | ||
| nonlocal external_file | ||
| external_file = path_ext_file | ||
| tmax = ( | ||
| imageseries.starting_time | ||
| + get_frame_count(path_ext_file) / imageseries.rate | ||
| ) | ||
| tmin = 0 | ||
|
||
| self.time_slider.max = tmax | ||
| self.time_slider.min = tmin | ||
| self._set_figure_external(tmin, external_file, tmin) | ||
|
|
||
| self.file_selector.observe(update_time_slider, names="value") | ||
|
|
||
| # set time slider callbacks: | ||
| def change_fig(change): | ||
| time = change["new"] | ||
| starting_time = change["owner"].min | ||
| self._set_figure_external(time, external_file, starting_time) | ||
|
|
||
| self.time_slider.observe(change_fig, names="value") | ||
| self._set_figure_external( | ||
| imageseries.starting_time, external_file, imageseries.starting_time | ||
| ) | ||
| # set children: | ||
| self.children = self.get_children(self.file_selector) | ||
| else: | ||
| self.time_window_controller = foreign_time_window_controller | ||
| self.set_controls(**kwargs) | ||
| if len(imageseries.data.shape) == 3: | ||
| self._set_figure_2d(0) | ||
|
|
||
| # Make widget figure | ||
| self.set_out_fig() | ||
| def time_slider_callback(change): | ||
| frame_number = self.time_to_index(change["new"]) | ||
| self._set_figure_2d(frame_number) | ||
|
|
||
| self.children = [self.out_fig, self.time_window_controller] | ||
| elif len(imageseries.data.shape) == 4: | ||
| self._set_figure_3d(0) | ||
|
|
||
| def time_to_index(self, time): | ||
| if self.imageseries.external_file and self.imageseries.rate: | ||
| return int((time - self.imageseries.starting_time) * self.imageseries.rate) | ||
| else: | ||
| return timeseries_time_to_ind(self.imageseries, time) | ||
| def time_slider_callback(change): | ||
| frame_number = self.time_to_index(change["new"]) | ||
| self._set_figure_3d(frame_number) | ||
|
|
||
| def set_controls(self, **kwargs): | ||
| self.controls.update( | ||
| timeseries=fixed(self.imageseries), time_window=self.time_window_controller | ||
| else: | ||
| raise NotImplementedError | ||
|
|
||
| # creat time window controller: | ||
| tmin = get_timeseries_mint(imageseries) | ||
| tmax = get_timeseries_maxt(imageseries) | ||
| if self.time_slider is None: | ||
| self.time_slider = widgets.FloatSlider( | ||
| value=tmin, | ||
| min=tmin, | ||
| max=tmax, | ||
| orientation="horizontal", | ||
| description="time(s)", | ||
| ) | ||
| self.time_slider.observe(time_slider_callback, names="value") | ||
| self.children = self.get_children() | ||
|
|
||
| def _set_figure_3d(self, frame_number): | ||
| import ipyvolume.pylab as p3 | ||
|
|
||
| output = widgets.Output() | ||
| p3.figure() | ||
| p3.volshow( | ||
| self.imageseries.data[frame_number].transpose([1, 0, 2]), | ||
| tf=linear_transfer_function([0, 0, 0], max_opacity=0.3), | ||
| ) | ||
| self.controls.update({key: widgets.fixed(val) for key, val in kwargs.items()}) | ||
| output.clear_output(wait=True) | ||
| self.figure = output | ||
| with output: | ||
| p3.show() | ||
|
|
||
| def _set_figure_2d(self, frame_number): | ||
| data = self.imageseries.data[frame_number].T | ||
| if self.figure is None: | ||
| self.figure = go.FigureWidget(data=dict(type="image", z=data)) | ||
| else: | ||
| self._add_fig_trace(data, frame_number) | ||
|
|
||
| def get_frame(self, idx): | ||
| if self.imageseries.external_file is not None: | ||
| return imread(self.imageseries.external_file, key=idx) | ||
| def _set_figure_external(self, time, ext_file_path, starting_time): | ||
| frame_number = self.time_to_index(time, starting_time) | ||
| data = get_frame(ext_file_path, frame_number) | ||
| if self.figure is None: | ||
| self.figure = go.FigureWidget(data=dict(type="image", z=data)) | ||
| else: | ||
| return self.image_series.data[idx].T | ||
| self._add_fig_trace(data, frame_number) | ||
|
|
||
| def set_out_fig(self): | ||
| def _add_fig_trace(self, img_data: np.ndarray, index): | ||
| self.figure.data[0]["z"] = img_data | ||
| self.figure.layout.title = f"Frame no: {index}" | ||
|
|
||
| self.out_fig = go.FigureWidget( | ||
| data=go.Heatmap( | ||
| z=self.get_frame(0), | ||
| colorscale="gray", | ||
| showscale=False, | ||
| ) | ||
| ) | ||
| self.out_fig.update_layout( | ||
| xaxis=go.layout.XAxis(showticklabels=False, ticks=""), | ||
| yaxis=go.layout.YAxis( | ||
| showticklabels=False, ticks="", scaleanchor="x", scaleratio=1 | ||
| ), | ||
| def time_to_index(self, time, starting_time=None): | ||
| starting_time = ( | ||
| starting_time | ||
| if starting_time is not None | ||
| else self.imageseries.starting_time | ||
| ) | ||
| if self.imageseries.external_file and self.imageseries.rate: | ||
| return int((time - starting_time) * self.imageseries.rate) | ||
| else: | ||
| return timeseries_time_to_ind(self.imageseries, time) | ||
|
|
||
| def on_change(change): | ||
| # Read frame | ||
| frame_number = self.time_to_index(change["new"][0]) | ||
| image = self.get_frame(frame_number) | ||
| self.out_fig.data[0].z = image | ||
| def get_children(self, *widgets): | ||
| set_widgets = [wid for wid in widgets if wid is not None] | ||
| return [self.figure, self.time_slider, *set_widgets] | ||
|
||
|
|
||
| self.controls["time_window"].observe(on_change) | ||
| def get_frame(self, idx): | ||
| if self.imageseries.external_file is not None: | ||
| return get_frame(self.imageseries.external_file[0]) | ||
| else: | ||
| return self.imageseries.data[idx].T | ||
|
|
||
|
|
||
| def show_image_series(image_series: ImageSeries, neurodata_vis_spec: dict): | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.