Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 97e4378

Browse filesBrowse files
authored
Convert PIL image objects to data uri strings in JSON serialization. (plotly#1991)
This conversion is already done by the layout.image.source validator, but this way it will also happen when serializing from a dict without validation, and for images that show up elsewhere in the figure (as mapbox layers for example)
1 parent de05925 commit 97e4378
Copy full SHA for 97e4378

File tree

Expand file treeCollapse file tree

3 files changed

+43
-9
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+43
-9
lines changed

‎packages/python/plotly/_plotly_utils/basevalidators.py

Copy file name to clipboardExpand all lines: packages/python/plotly/_plotly_utils/basevalidators.py
+14-9Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,20 +2347,25 @@ def validate_coerce(self, v):
23472347
pass
23482348
elif self._PIL and isinstance(v, self._PIL.Image.Image):
23492349
# Convert PIL image to png data uri string
2350-
in_mem_file = io.BytesIO()
2351-
v.save(in_mem_file, format="PNG")
2352-
in_mem_file.seek(0)
2353-
img_bytes = in_mem_file.read()
2354-
base64_encoded_result_bytes = base64.b64encode(img_bytes)
2355-
base64_encoded_result_str = base64_encoded_result_bytes.decode("ascii")
2356-
v = "data:image/png;base64,{base64_encoded_result_str}".format(
2357-
base64_encoded_result_str=base64_encoded_result_str
2358-
)
2350+
v = self.pil_image_to_uri(v)
23592351
else:
23602352
self.raise_invalid_val(v)
23612353

23622354
return v
23632355

2356+
@staticmethod
2357+
def pil_image_to_uri(v):
2358+
in_mem_file = io.BytesIO()
2359+
v.save(in_mem_file, format="PNG")
2360+
in_mem_file.seek(0)
2361+
img_bytes = in_mem_file.read()
2362+
base64_encoded_result_bytes = base64.b64encode(img_bytes)
2363+
base64_encoded_result_str = base64_encoded_result_bytes.decode("ascii")
2364+
v = "data:image/png;base64,{base64_encoded_result_str}".format(
2365+
base64_encoded_result_str=base64_encoded_result_str
2366+
)
2367+
return v
2368+
23642369

23652370
class CompoundValidator(BaseValidator):
23662371
def __init__(self, plotly_name, parent_name, data_class_str, data_docs, **kwargs):

‎packages/python/plotly/_plotly_utils/utils.py

Copy file name to clipboardExpand all lines: packages/python/plotly/_plotly_utils/utils.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55

66
from _plotly_utils.optional_imports import get_module
7+
from _plotly_utils.basevalidators import ImageUriValidator
78

89

910
PY36_OR_LATER = sys.version_info.major == 3 and sys.version_info.minor >= 6
@@ -104,6 +105,7 @@ def default(self, obj):
104105
self.encode_as_date,
105106
self.encode_as_list, # because some values have `tolist` do last.
106107
self.encode_as_decimal,
108+
self.encode_as_pil,
107109
)
108110
for encoding_method in encoding_methods:
109111
try:
@@ -192,6 +194,15 @@ def encode_as_decimal(obj):
192194
else:
193195
raise NotEncodable
194196

197+
@staticmethod
198+
def encode_as_pil(obj):
199+
"""Attempt to convert PIL.Image.Image to base64 data uri"""
200+
pil = get_module("PIL")
201+
if isinstance(obj, pil.Image.Image):
202+
return ImageUriValidator.pil_image_to_uri(obj)
203+
else:
204+
raise NotEncodable
205+
195206

196207
class NotEncodable(Exception):
197208
pass

‎packages/python/plotly/plotly/tests/test_optional/test_utils/test_utils.py

Copy file name to clipboardExpand all lines: packages/python/plotly/plotly/tests/test_optional/test_utils/test_utils.py
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
from nose.plugins.attrib import attr
1717
from pandas.util.testing import assert_series_equal
1818
import json as _json
19+
import os
20+
import base64
1921

2022
from plotly import optional_imports, utils
2123
from plotly.graph_objs import Scatter, Scatter3d, Figure, Data
24+
from PIL import Image
2225

2326

2427
matplotlylib = optional_imports.get_module("plotly.matplotlylib")
@@ -274,6 +277,21 @@ def test_datetime_dot_date(self):
274277
j1 = _json.dumps(a, cls=utils.PlotlyJSONEncoder)
275278
assert j1 == '["2014-01-01", "2014-01-02"]'
276279

280+
def test_pil_image_encoding(self):
281+
import _plotly_utils
282+
283+
img_path = os.path.join(
284+
_plotly_utils.__path__[0], "tests", "resources", "1x1-black.png"
285+
)
286+
287+
with open(img_path, "rb") as f:
288+
hex_bytes = base64.b64encode(f.read()).decode("ascii")
289+
expected_uri = "data:image/png;base64," + hex_bytes
290+
291+
img = Image.open(img_path)
292+
j1 = _json.dumps({"source": img}, cls=utils.PlotlyJSONEncoder)
293+
assert j1 == '{"source": "%s"}' % expected_uri
294+
277295

278296
if matplotlylib:
279297

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.