Skip to content
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

ImageSeriesWidget fixes #196

Open
wants to merge 56 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0606860
move tests outside the nwbwidgets package
bendichter Nov 9, 2021
eaa77b8
Merge remote-tracking branch 'origin/master'
bendichter Nov 9, 2021
8e38592
improve plane segmentation coverage
bendichter Nov 9, 2021
22396a5
Merge branch 'master' into improve_plane_seg_coverage
bendichter Nov 9, 2021
3603e17
improve plane segmentation coverage
bendichter Nov 9, 2021
68a4e80
Merge remote-tracking branch 'origin/improve_plane_seg_coverage' into…
bendichter Nov 9, 2021
4940ee7
test show_timeseries_mpl
bendichter Nov 9, 2021
3a6a197
Merge branch 'master' into improve_plane_seg_coverage
bendichter Nov 15, 2021
f5e21cb
Merge branch 'master' into improve_plane_seg_coverage
bendichter Dec 31, 2021
b438b7c
extenral files existance check fix
Saksham20 Jan 4, 2022
234f37d
merging with 2photonseries widget implementation to make parent class
Saksham20 Jan 4, 2022
e5024ad
fixes
Saksham20 Jan 4, 2022
6a45abd
fixes
Saksham20 Jan 5, 2022
45b2098
remove time window controller
Saksham20 Jan 5, 2022
b91e39a
inherit from ImageSeries
Saksham20 Jan 5, 2022
5211089
reformat
Saksham20 Jan 5, 2022
85f71d8
Merge branch 'improve_plane_seg_coverage' into two_photon_series_fixes
Saksham20 Jan 5, 2022
45b1f7c
fix 2pseries tests init arg
Saksham20 Jan 5, 2022
b6bcaad
bug fix
Saksham20 Jan 5, 2022
1095759
propagate neurodata_vis_spec
Saksham20 Jan 5, 2022
bf5fa4d
propagate neurodata_vis_spec
Saksham20 Jan 5, 2022
1136fac
Revert "fix 2pseries tests init arg"
Saksham20 Jan 5, 2022
2d02901
restructure
Saksham20 Jan 6, 2022
a7c06e7
using go.image
Saksham20 Jan 6, 2022
34a053c
Update nwbwidgets/ophys.py
Saksham20 Jan 6, 2022
8906b88
black
Saksham20 Jan 7, 2022
9782709
add imageseries.py
Saksham20 Jan 7, 2022
17d9495
using custom context manager without inheritance
Saksham20 Jan 7, 2022
d2b7d59
remove print st
Saksham20 Jan 7, 2022
22a34ff
tests setup
Saksham20 Jan 8, 2022
0288883
cv2 dependency
Saksham20 Jan 8, 2022
4d785c2
cv2 dependency
Saksham20 Jan 8, 2022
7dda8db
neurodataviz spec not required
Saksham20 Jan 8, 2022
f31e312
neurodataviz spec not required
Saksham20 Jan 8, 2022
f73fde3
return variable novie frames number
Saksham20 Jan 9, 2022
798a528
indentation
Saksham20 Jan 9, 2022
c8a622f
add tests for ImageSeries
Saksham20 Jan 9, 2022
aa04d9f
varible movie frames
Saksham20 Jan 9, 2022
d29ea9b
import specific fixtures
Saksham20 Jan 9, 2022
5509487
kwargs update for videowriter
Saksham20 Jan 10, 2022
b5af95c
fix video writer
Saksham20 Jan 10, 2022
0213ccc
movie frames loop bug fix
Saksham20 Jan 10, 2022
9737047
rename fixtures.py to fixtures.py
Saksham20 Jan 10, 2022
29b003e
Merge branch 'master' into two_photon_series_fixes
Saksham20 Jan 10, 2022
8e72ae9
add all callbacks as class methods
Saksham20 Jan 11, 2022
9bb4cf0
time slider spans frames if video file selected is other than default
Saksham20 Jan 11, 2022
3add69a
bug fix
Saksham20 Jan 11, 2022
ae8e9cc
linking foreign time slider to timeslider
Saksham20 Jan 12, 2022
c2e2be0
using px for faster plotting
Saksham20 Jan 12, 2022
fe7d182
black
Saksham20 Jan 12, 2022
85a0d0e
use self.get_frame
Saksham20 Jan 13, 2022
3ea7f58
use video start_times, visible time slider
Saksham20 Jan 13, 2022
329d702
get fps of video and construct external file frame index
Saksham20 Jan 13, 2022
6541993
fps constant across external video files
Saksham20 Jan 13, 2022
089ed60
movie fps fixes
Saksham20 Jan 13, 2022
1d131bf
external files list, indexing fixes
Saksham20 Jan 13, 2022
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
228 changes: 172 additions & 56 deletions nwbwidgets/image.py
Original file line number Diff line number Diff line change
@@ -1,94 +1,159 @@
from pathlib import Path, PureWindowsPath
from pathlib import Path
from typing import Union

import matplotlib.pyplot as plt
import plotly.express as px
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.timeseries import (
get_timeseries_maxt,
get_timeseries_mint,
timeseries_time_to_ind,
)

try:
import cv2

HAVE_OPENCV = True
except ImportError:
HAVE_OPENCV = False

try:
from tifffile import imread, TiffFile

HAVE_TIF = True
except ImportError:
HAVE_TIF = False

PathType = Union[str, Path]


class ImageSeriesWidget(widgets.VBox):
"""Widget showing ImageSeries."""

def __init__(
self,
imageseries: ImageSeries,
foreign_time_window_controller: StartAndDurationController = None,
**kwargs
self,
imageseries: ImageSeries,
neurodata_vis_spec: dict
):
super().__init__()
self.imageseries = imageseries
self.controls = {}
self.out_fig = None

# 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
self.figure = None
self.time_slider = None
tmin = get_timeseries_mint(imageseries)

# Make widget figure --------
def _add_fig_trace(img_fig: go.Figure, index):
if self.figure is None:
self.figure = go.FigureWidget(img_fig)
else:
self.figure.for_each_trace(lambda trace: trace.update(img_fig.data[0]))
self.figure.layout.title = f"Frame no: {index}"

def _add_time_slider_controller(min, max):
if self.time_slider is None:
self.time_slider = widgets.FloatSlider(value=0,
min=min,
max=max,
orientation="horizontal",
description="time(s)")
Saksham20 marked this conversation as resolved.
Show resolved Hide resolved
else:
tmax = get_timeseries_maxt(imageseries)
self.time_window_controller = StartAndDurationController(tmax, tmin)
self.time_slider.max = max
self.time_slider.min = min
self.time_slider.value = min

if imageseries.external_file is not None:
file_selector = widgets.Dropdown(options=imageseries.external_file)
Saksham20 marked this conversation as resolved.
Show resolved Hide resolved

# Get Frames dimensions
def set_figure(time, ext_file_path):
frame_number = self.time_to_index(time)
img_fig = px.imshow(get_frame(ext_file_path, frame_number), binary_string=True)
_add_fig_trace(img_fig, frame_number)

def update_figure(value):
path_ext_file = value["new"]
# Read first frame
tmax = imageseries.starting_time + get_frame_count(path_ext_file)/imageseries.rate
_add_time_slider_controller(0, tmax)
def change_fig(change):
time = change["new"]
set_figure(time, path_ext_file)

self.time_slider.observe(change_fig, names='value')

file_selector.observe(update_figure, names='value')
# set default figure:
set_figure(self.imageseries.starting_time,
imageseries.external_file[0])
# set default time window contorller
tmax = imageseries.starting_time + \
get_frame_count(imageseries.external_file[0])/imageseries.rate
_add_time_slider_controller(0, tmax)
self.time_slider.observe(lambda x: set_figure(x["new"],imageseries.external_file[0]),
names='value')
self.set_children(file_selector)
else:
self.time_window_controller = foreign_time_window_controller
self.set_controls(**kwargs)
if len(imageseries.data.shape) == 3:
def set_figure(frame_number):
img_fig = px.imshow(
imageseries.data[frame_number].T, binary_string=True
)
_add_fig_trace(img_fig, frame_number)

set_figure(0)
elif len(imageseries.data.shape) == 4:
import ipyvolume.pylab as p3

output = widgets.Output()

def set_figure(frame_number):
p3.figure()
p3.volshow(
imageseries.data[frame_number].transpose([1, 0, 2]),
tf=linear_transfer_function([0, 0, 0], max_opacity=0.3),
)
output.clear_output(wait=True)
self.figure = output
with output:
p3.show()

set_figure(0)
else:
raise NotImplementedError

# Make widget figure
self.set_out_fig()
# create callback:
def update_figure(change):
frame_number = self.time_to_index(change["new"])
set_figure(frame_number)

self.children = [self.out_fig, self.time_window_controller]
# creat time window controller:
tmax = get_timeseries_maxt(imageseries)
_add_time_slider_controller(tmin, tmax)
self.time_slider.observe(update_figure, names='value')
self.set_children()

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)
return int((time - self.imageseries.starting_time)*self.imageseries.rate)
else:
return timeseries_time_to_ind(self.imageseries, time)

def set_controls(self, **kwargs):
self.controls.update(
timeseries=fixed(self.imageseries), time_window=self.time_window_controller
)
self.controls.update({key: widgets.fixed(val) for key, val in kwargs.items()})
def set_children(self, *args):
Saksham20 marked this conversation as resolved.
Show resolved Hide resolved
self.children = [self.figure,
self.time_slider,
*args]

def get_frame(self, idx):
if self.imageseries.external_file is not None:
return imread(self.imageseries.external_file, key=idx)
return get_frame(self.imageseries.external_file[0])
else:
return self.image_series.data[idx].T

def set_out_fig(self):

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 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

self.controls["time_window"].observe(on_change)
return self.imageseries.data[idx].T


def show_image_series(image_series: ImageSeries, neurodata_vis_spec: dict):
Expand Down Expand Up @@ -167,3 +232,54 @@ def show_rbga_image(rgb_image: RGBImage, neurodata_vis_spec=None):
plt.axis("off")

return fig


def get_frame_shape(external_path_file: PathType):
external_path_file = Path(external_path_file)
if external_path_file.suffix in ['.tif', '.tiff']:
assert HAVE_TIF, 'pip install tifffile'
tif = TiffFile(external_path_file)
page = tif.pages[0]
return page.shape
else:
assert HAVE_OPENCV, 'pip install opencv-python'
cap = cv2.VideoCapture(str(external_path_file))
success, frame = cap.read()
cap.release()
return frame.shape


def get_frame_count(external_path_file: PathType):
external_path_file = Path(external_path_file)
if external_path_file.suffix in ['.tif', '.tiff']:
assert HAVE_TIF, 'pip install tifffile'
tif = TiffFile(external_path_file)
return len(tif.pages)
else:
Saksham20 marked this conversation as resolved.
Show resolved Hide resolved
assert HAVE_OPENCV, 'pip install opencv-python'
cap = cv2.VideoCapture(str(external_path_file))
Saksham20 marked this conversation as resolved.
Show resolved Hide resolved
if int(cv2.__version__.split(".")[0]) < 3:
frame_count_arg = cv2.cv.CV_CAP_PROP_FRAME_COUNT
else:
frame_count_arg = cv2.CAP_PROP_FRAME_COUNT
frame_count = cap.get(frame_count_arg)
cap.release()
return frame_count


def get_frame(external_path_file: PathType, index):
external_path_file = Path(external_path_file)
if external_path_file.suffix in ['.tif', '.tiff']:
assert HAVE_TIF, 'pip install tifffile'
return imread(str(external_path_file), key=int(index))
else:
assert HAVE_OPENCV, 'pip install opencv-python'
cap = cv2.VideoCapture(str(external_path_file))
if int(cv2.__version__.split(".")[0]) < 3:
set_arg = cv2.cv.CV_CAP_PROP_POS_FRAMES
else:
set_arg = cv2.CAP_PROP_POS_FRAMES
set_value = cap.set(set_arg, index)
success, frame = cap.read()
cap.release()
return frame
Loading