1
1
import numpy as np
2
- from plotly .graph_objects import Figure
2
+ from plotly .graph_objects import Figure , Image
3
3
from dash import Dash
4
4
from dash .dependencies import Input , Output , State
5
5
from dash_core_components import Graph , Slider , Store
@@ -29,44 +29,39 @@ def __init__(self, app, volume, axis=0, id=None):
29
29
self ._id = id
30
30
31
31
# Get the slice size (width, height), and max index
32
- arr_shape = list (volume .shape )
33
- arr_shape .pop (self ._axis )
34
- slice_size = list (reversed (arr_shape ))
32
+ # arr_shape = list(volume.shape)
33
+ # arr_shape.pop(self._axis)
34
+ # slice_size = list(reversed(arr_shape))
35
35
self ._max_index = self ._volume .shape [self ._axis ] - 1
36
36
37
+ # Prep low-res slices
38
+ thumbnails = [
39
+ img_array_to_uri (self ._slice (i ), (32 , 32 ))
40
+ for i in range (self ._max_index + 1 )
41
+ ]
42
+
43
+ # Create a placeholder trace
44
+ # todo: can add "%{z[0]}", but that would be the scaled value ...
45
+ trace = Image (source = "" , hovertemplate = "(%{x}, %{y})<extra></extra>" )
37
46
# Create the figure object
38
- fig = Figure ()
47
+ fig = Figure (data = [ trace ] )
39
48
fig .update_layout (
40
49
template = None ,
41
50
margin = dict (l = 0 , r = 0 , b = 0 , t = 0 , pad = 4 ),
42
51
)
43
52
fig .update_xaxes (
53
+ # range=(0, slice_size[0]),
44
54
showgrid = False ,
45
- range = (0 , slice_size [0 ]),
46
55
showticklabels = False ,
47
56
zeroline = False ,
48
57
)
49
58
fig .update_yaxes (
59
+ # range=(slice_size[1], 0), # todo: allow flipping x or y
50
60
showgrid = False ,
51
61
scaleanchor = "x" ,
52
- range = (slice_size [1 ], 0 ), # todo: allow flipping x or y
53
62
showticklabels = False ,
54
63
zeroline = False ,
55
64
)
56
- # Add an empty layout image that we can populate from JS.
57
- fig .add_layout_image (
58
- dict (
59
- source = "" ,
60
- xref = "x" ,
61
- yref = "y" ,
62
- x = 0 ,
63
- y = 0 ,
64
- sizex = slice_size [0 ],
65
- sizey = slice_size [1 ],
66
- sizing = "contain" ,
67
- layer = "below" ,
68
- )
69
- )
70
65
# Wrap the figure in a graph
71
66
# todo: or should the user provide this?
72
67
self .graph = Graph (
@@ -88,6 +83,7 @@ def __init__(self, app, volume, axis=0, id=None):
88
83
Store (id = self ._subid ("slice-index" ), data = volume .shape [self ._axis ] // 2 ),
89
84
Store (id = self ._subid ("_requested-slice-index" ), data = 0 ),
90
85
Store (id = self ._subid ("_slice-data" ), data = "" ),
86
+ Store (id = self ._subid ("_slice-data-lowres" ), data = thumbnails ),
91
87
]
92
88
93
89
self ._create_server_callbacks (app )
@@ -101,7 +97,8 @@ def _slice(self, index):
101
97
"""Sample a slice from the volume."""
102
98
indices = [slice (None ), slice (None ), slice (None )]
103
99
indices [self ._axis ] = index
104
- return self ._volume [tuple (indices )]
100
+ im = self ._volume [tuple (indices )]
101
+ return (im .astype (np .float32 ) * (255 / im .max ())).astype (np .uint8 )
105
102
106
103
def _create_server_callbacks (self , app ):
107
104
"""Create the callbacks that run server-side."""
@@ -112,7 +109,6 @@ def _create_server_callbacks(self, app):
112
109
)
113
110
def upload_requested_slice (slice_index ):
114
111
slice = self ._slice (slice_index )
115
- slice = (slice .astype (np .float32 ) * (255 / slice .max ())).astype (np .uint8 )
116
112
return [slice_index , img_array_to_uri (slice )]
117
113
118
114
def _create_client_callbacks (self , app ):
@@ -158,7 +154,7 @@ def _create_client_callbacks(self, app):
158
154
159
155
app .clientside_callback (
160
156
"""
161
- function handle_incoming_slice(index, index_and_data, ori_figure) {
157
+ function handle_incoming_slice(index, index_and_data, ori_figure, lowres ) {
162
158
let new_index = index_and_data[0];
163
159
let new_data = index_and_data[1];
164
160
// Store data in cache
@@ -167,17 +163,18 @@ def _create_client_callbacks(self, app):
167
163
slice_cache[new_index] = new_data;
168
164
// Get the data we need *now*
169
165
let data = slice_cache[index];
166
+ //slice_cache[new_index] = undefined; // todo: disabled cache for now!
170
167
// Maybe we do not need an update
171
168
if (!data) {
172
- return window.dash_clientside.no_update ;
169
+ data = lowres[index] ;
173
170
}
174
- if (data == ori_figure.layout.images [0].source) {
171
+ if (data == ori_figure.data [0].source) {
175
172
return window.dash_clientside.no_update;
176
173
}
177
174
// Otherwise, perform update
178
175
console.log("updating figure");
179
176
let figure = {...ori_figure};
180
- figure.layout.images [0].source = data;
177
+ figure.data [0].source = data;
181
178
return figure;
182
179
}
183
180
""" .replace (
@@ -188,5 +185,8 @@ def _create_client_callbacks(self, app):
188
185
Input (self ._subid ("slice-index" ), "data" ),
189
186
Input (self ._subid ("_slice-data" ), "data" ),
190
187
],
191
- [State (self ._subid ("graph" ), "figure" )],
188
+ [
189
+ State (self ._subid ("graph" ), "figure" ),
190
+ State (self ._subid ("_slice-data-lowres" ), "data" ),
191
+ ],
192
192
)
0 commit comments