33
33
_integer_ranges = {t : (np .iinfo (t ).min , np .iinfo (t ).max ) for t in _integer_types }
34
34
35
35
36
- def _array_to_b64str (img , ext = ' png' ):
36
+ def _array_to_b64str (img , ext = " png" ):
37
37
pil_img = Image .fromarray (img )
38
38
if ext == "jpg" :
39
- ext = "jpeg"
39
+ ext = "jpeg"
40
40
buff = BytesIO ()
41
41
pil_img .save (buff , format = ext )
42
- if ext == ' png' :
43
- prefix = b' data:image/png;base64,'
44
- elif ext == ' jpeg' :
45
- prefix = b' data:image/jpeg;base64,'
42
+ if ext == " png" :
43
+ prefix = b" data:image/png;base64,"
44
+ elif ext == " jpeg" :
45
+ prefix = b" data:image/jpeg;base64,"
46
46
else :
47
- raise ValueError ("accepted image formats are 'png' and 'jpeg' but %s was passed" % format )
47
+ raise ValueError (
48
+ "accepted image formats are 'png' and 'jpeg' but %s was passed" % format
49
+ )
48
50
image_string = (prefix + base64 .b64encode (buff .getvalue ())).decode ("utf-8" )
49
51
return image_string
50
52
@@ -177,6 +179,22 @@ def imshow(
177
179
- if None, 'equal' is used for numpy arrays and 'auto' for xarrays
178
180
(which have typically heterogeneous coordinates)
179
181
182
+ use_binary_string: bool, default None
183
+ if True, the image data are first rescaled and encoded as uint8 and
184
+ then passed to plotly.js as a b64 PNG string. If False, data are passed
185
+ unchanged as a numerical array. Setting to True may lead to performance
186
+ gains, at the cost of a loss of precision depending on the original data
187
+ type. If None, use_binary_string is set to True for multichannel (eg) RGB
188
+ arrays, and to False for single-channel (2D) arrays. 2D arrays are
189
+ represented as grayscale and with no colorbar if use_binary_string is
190
+ True.
191
+
192
+ contrast_rescaling: 'image', 'dtype', or None
193
+ how to determine data values corresponding to the bounds of the color
194
+ range, when zmin or zmax are not passed. If `image`, the min and max
195
+ values of the image are used. If `dtype`, a heuristic based on the image
196
+ data type is used.
197
+
180
198
Returns
181
199
-------
182
200
fig : graph_objects.Figure containing the displayed image
@@ -203,10 +221,10 @@ def imshow(
203
221
if xarray_imported and isinstance (img , xarray .DataArray ):
204
222
if use_binary_string :
205
223
raise ValueError (
206
- "It is not possible to use binary image strings for xarrays."
207
- "Please pass your data as a numpy array instead using"
208
- "`img.values`"
209
- )
224
+ "It is not possible to use binary image strings for xarrays."
225
+ "Please pass your data as a numpy array instead using"
226
+ "`img.values`"
227
+ )
210
228
y_label , x_label = img .dims [0 ], img .dims [1 ]
211
229
# np.datetime64 is not handled correctly by go.Heatmap
212
230
for ax in [x_label , y_label ]:
@@ -254,7 +272,9 @@ def imshow(
254
272
else :
255
273
has_nans = np .any (np .isnan (img ))
256
274
if has_nans and use_binary_string :
257
- raise ValueError ("Binary strings cannot be used with arrays containing NaNs" )
275
+ raise ValueError (
276
+ "Binary strings cannot be used with arrays containing NaNs"
277
+ )
258
278
259
279
# --------------- Starting from here img is always a numpy array --------
260
280
img = np .asanyarray (img )
@@ -268,8 +288,8 @@ def imshow(
268
288
img = 255 * img .astype (np .uint8 )
269
289
270
290
if contrast_rescaling is None :
271
- contrast_rescaling = ' image' if img .ndim == 2 else ' dtype'
272
- if contrast_rescaling == ' image' :
291
+ contrast_rescaling = " image" if img .ndim == 2 else " dtype"
292
+ if contrast_rescaling == " image" :
273
293
if (zmin is not None or use_binary_string ) and zmax is None :
274
294
zmax = img .max ()
275
295
if (zmax is not None or use_binary_string ) and zmin is None :
@@ -312,14 +332,30 @@ def imshow(
312
332
layout ["coloraxis1" ]["colorbar" ] = dict (title_text = labels ["color" ])
313
333
314
334
# For 2D+RGB data, use Image trace
315
- elif img .ndim == 3 and img .shape [- 1 ] in [3 , 4 ] or (img .ndim == 2 and use_binary_string ):
335
+ elif (
336
+ img .ndim == 3
337
+ and img .shape [- 1 ] in [3 , 4 ]
338
+ or (img .ndim == 2 and use_binary_string )
339
+ ):
316
340
zmin , zmax = _vectorize_zvalue (zmin ), _vectorize_zvalue (zmax )
317
341
if use_binary_string :
318
342
if img .ndim == 2 :
319
- img_rescaled = rescale_intensity (img , in_range = (zmin [0 ], zmax [0 ]), out_range = np .uint8 )
343
+ img_rescaled = rescale_intensity (
344
+ img , in_range = (zmin [0 ], zmax [0 ]), out_range = np .uint8
345
+ )
320
346
else :
321
- img_rescaled = np .dstack ([rescale_intensity (img [..., ch ], in_range = (zmin [ch ], zmax [ch ]), out_range = np .uint8 )
322
- for ch in range (img .shape [- 1 ])])
347
+ img_rescaled = np .dstack (
348
+ [
349
+ rescale_intensity (
350
+ img [..., ch ],
351
+ in_range = (zmin [ch ], zmax [ch ]),
352
+ out_range = np .uint8 ,
353
+ )
354
+ for ch in range (img .shape [- 1 ])
355
+ ]
356
+ )
357
+ if origin == "lower" :
358
+ img_rescaled = img_rescaled [::- 1 ]
323
359
img_str = _array_to_b64str (img_rescaled )
324
360
trace = go .Image (source = img_str )
325
361
else :
@@ -343,11 +379,17 @@ def imshow(
343
379
layout_patch ["margin" ] = {"t" : 60 }
344
380
fig = go .Figure (data = trace , layout = layout )
345
381
fig .update_layout (layout_patch )
346
- if not use_binary_string :
347
- fig .update_traces (
348
- hovertemplate = "%s: %%{x}<br>%s: %%{y}<br>%s: %%{z}<extra></extra>"
349
- % (labels ["x" ] or "x" , labels ["y" ] or "y" , labels ["color" ] or "color" ,)
382
+ # does not work yet, Antoine working on it
383
+ hover_name = "z" if not use_binary_string else "colorLabel"
384
+ fig .update_traces (
385
+ hovertemplate = "%s: %%{x}<br>%s: %%{y}<br>%s: %%{%s}<extra></extra>"
386
+ % (
387
+ labels ["x" ] or "x" ,
388
+ labels ["y" ] or "y" ,
389
+ labels ["color" ] or "color" ,
390
+ hover_name ,
350
391
)
392
+ )
351
393
if labels ["x" ]:
352
394
fig .update_xaxes (title_text = labels ["x" ])
353
395
if labels ["y" ]:
0 commit comments