jupyter-widgets-contrib / ipycanvas

Interactive Canvas in Jupyter
https://ipycanvas.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
685 stars 62 forks source link

Images with alpha fail: OSError: cannot write mode RGBA as JPEG #277

Closed mforbes closed 1 year ago

mforbes commented 2 years ago

Trying to add an alpha chanel to the example in the docs leads to an error OSError: cannot write mode RGBA as JPEG on Mac OS X:

import numpy as np

from ipycanvas import Canvas

x = np.linspace(-1, 1, 600)
y = np.linspace(-1, 1, 600)

x_grid, y_grid = np.meshgrid(x, y)

blue_channel = np.array(np.sin(x_grid**2 + y_grid**2) * 255, dtype=np.int32)
red_channel = np.zeros_like(blue_channel) + 200
green_channel = np.zeros_like(blue_channel) + 50
alpha_channel = np.zeros_like(blue_channel) + 1

image_data = np.stack((red_channel, blue_channel, green_channel, alpha_channel), axis=2)

canvas = Canvas(width=image_data.shape[0], height=image_data.shape[1])
canvas.put_image_data(image_data, 0, 0)

canvas
Exception details: `OSError: cannot write mode RGBA as JPEG` ``` KeyError Traceback (most recent call last) File ~/work/mmfbb/gpe-explorer/envs/super_hydro/lib/python3.9/site-packages/PIL/JpegImagePlugin.py:633, in _save(im, fp, filename) 632 try: --> 633 rawmode = RAWMODE[im.mode] 634 except KeyError as e: KeyError: 'RGBA' The above exception was the direct cause of the following exception: OSError Traceback (most recent call last) Input In [3], in () 15 image_data = np.stack((red_channel, blue_channel, green_channel, alpha_channel), axis=2) 17 canvas = Canvas(width=image_data.shape[0], height=image_data.shape[1]) ---> 18 canvas.put_image_data(image_data, 0, 0) 20 canvas File ~/work/mmfbb/gpe-explorer/envs/super_hydro/lib/python3.9/site-packages/ipycanvas/canvas.py:1374, in Canvas.put_image_data(self, image_data, x, y) 1367 def put_image_data(self, image_data, x=0, y=0): 1368 """Draw an image on the Canvas. 1369 1370 ``image_data`` should be a NumPy array containing the image to draw and ``x`` and ``y`` the pixel position where to 1371 draw. Unlike the CanvasRenderingContext2D.putImageData method, this method **is** affected by the canvas transformation 1372 matrix, and supports transparency. 1373 """ -> 1374 image_buffer = binary_image(image_data) 1375 _CANVAS_MANAGER.send_draw_command( 1376 self, COMMANDS["putImageData"], [x, y], [image_buffer] 1377 ) File ~/work/mmfbb/gpe-explorer/envs/super_hydro/lib/python3.9/site-packages/ipycanvas/utils.py:29, in binary_image(ar, quality) 27 def binary_image(ar, quality=75): 28 f = BytesIO() ---> 29 PILImage.fromarray(ar.astype(np.uint8), "RGB" if ar.shape[2] == 3 else "RGBA").save( 30 f, "JPEG", quality=quality 31 ) 32 return f.getvalue() File ~/work/mmfbb/gpe-explorer/envs/super_hydro/lib/python3.9/site-packages/PIL/Image.py:2300, in Image.save(self, fp, format, **params) 2297 fp = builtins.open(filename, "w+b") 2299 try: -> 2300 save_handler(self, fp, filename) 2301 except Exception: 2302 if open_fp: File ~/work/mmfbb/gpe-explorer/envs/super_hydro/lib/python3.9/site-packages/PIL/JpegImagePlugin.py:635, in _save(im, fp, filename) 633 rawmode = RAWMODE[im.mode] 634 except KeyError as e: --> 635 raise OSError(f"cannot write mode {im.mode} as JPEG") from e 637 info = im.encoderinfo 639 dpi = [round(x) for x in info.get("dpi", (0, 0))] OSError: cannot write mode RGBA as JPEG ```

I installed this in a clean conda environment:

conda create -n tst python=3.9
conda activate tst
python3 -m pip install numpy notebook ipycanvas
Environment creation details ```bash $ conda create -n tst python=3.9 Collecting package metadata (current_repodata.json): done Solving environment: done ## Package Plan ## environment location: /Users/mforbes/.conda/envs/tst added / updated specs: - python=3.9 The following NEW packages will be INSTALLED: ca-certificates pkgs/main/osx-64::ca-certificates-2022.4.26-hecd8cb5_0 certifi pkgs/main/osx-64::certifi-2022.5.18.1-py39hecd8cb5_0 libcxx pkgs/main/osx-64::libcxx-12.0.0-h2f01273_0 libffi pkgs/main/osx-64::libffi-3.3-hb1e8313_2 ncurses pkgs/main/osx-64::ncurses-6.3-hca72f7f_2 openssl pkgs/main/osx-64::openssl-1.1.1o-hca72f7f_0 pip pkgs/main/osx-64::pip-21.2.4-py39hecd8cb5_0 python pkgs/main/osx-64::python-3.9.12-hdfd78df_1 readline pkgs/main/osx-64::readline-8.1.2-hca72f7f_1 setuptools pkgs/main/osx-64::setuptools-61.2.0-py39hecd8cb5_0 sqlite pkgs/main/osx-64::sqlite-3.38.3-h707629a_0 tk pkgs/main/osx-64::tk-8.6.12-h5d9f67b_0 tzdata pkgs/main/noarch::tzdata-2022a-hda174b7_0 wheel pkgs/main/noarch::wheel-0.37.1-pyhd3eb1b0_0 xz pkgs/main/osx-64::xz-5.2.5-hca72f7f_1 zlib pkgs/main/osx-64::zlib-1.2.12-h4dc903c_2 Proceed ([y]/n)? y Preparing transaction: done Verifying transaction: done Executing transaction: done # # To activate this environment, use # # $ conda activate tst # # To deactivate an active environment, use # # $ conda deactivate $ conda activate tst (tst) $ python3 -m pip install numpy notebook ipycanvas Collecting numpy Using cached numpy-1.22.4-cp39-cp39-macosx_10_14_x86_64.whl (17.7 MB) Collecting notebook Using cached notebook-6.4.12-py3-none-any.whl (9.9 MB) Collecting ipycanvas Using cached ipycanvas-0.12.0-py2.py3-none-any.whl (256 kB) Collecting ipython-genutils Using cached ipython_genutils-0.2.0-py2.py3-none-any.whl (26 kB) Collecting jinja2 Using cached Jinja2-3.1.2-py3-none-any.whl (133 kB) Collecting jupyter-client>=5.3.4 Using cached jupyter_client-7.3.4-py3-none-any.whl (132 kB) Collecting nest-asyncio>=1.5 Using cached nest_asyncio-1.5.5-py3-none-any.whl (5.2 kB) Collecting jupyter-core>=4.6.1 Using cached jupyter_core-4.10.0-py3-none-any.whl (87 kB) Collecting ipykernel Using cached ipykernel-6.15.0-py3-none-any.whl (133 kB) Collecting argon2-cffi Using cached argon2_cffi-21.3.0-py3-none-any.whl (14 kB) Collecting terminado>=0.8.3 Using cached terminado-0.15.0-py3-none-any.whl (16 kB) Collecting traitlets>=4.2.1 Using cached traitlets-5.2.2.post1-py3-none-any.whl (106 kB) Collecting nbformat Using cached nbformat-5.4.0-py3-none-any.whl (73 kB) Collecting prometheus-client Using cached prometheus_client-0.14.1-py3-none-any.whl (59 kB) Collecting Send2Trash>=1.8.0 Using cached Send2Trash-1.8.0-py3-none-any.whl (18 kB) Collecting pyzmq>=17 Using cached pyzmq-23.1.0-cp39-cp39-macosx_10_9_x86_64.whl (1.3 MB) Collecting nbconvert>=5 Using cached nbconvert-6.5.0-py3-none-any.whl (561 kB) Collecting tornado>=6.1 Using cached tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl (416 kB) Collecting ipywidgets>=7.6.0 Using cached ipywidgets-7.7.0-py2.py3-none-any.whl (123 kB) Collecting pillow>=6.0 Using cached Pillow-9.1.1-cp39-cp39-macosx_10_10_x86_64.whl (3.1 MB) Collecting ipython>=4.0.0 Using cached ipython-8.4.0-py3-none-any.whl (750 kB) Collecting widgetsnbextension~=3.6.0 Using cached widgetsnbextension-3.6.0-py2.py3-none-any.whl (1.6 MB) Collecting jupyterlab-widgets>=1.0.0 Using cached jupyterlab_widgets-1.1.0-py3-none-any.whl (245 kB) Collecting packaging Using cached packaging-21.3-py3-none-any.whl (40 kB) Collecting matplotlib-inline>=0.1 Using cached matplotlib_inline-0.1.3-py3-none-any.whl (8.2 kB) Collecting debugpy>=1.0 Using cached debugpy-1.6.0-py2.py3-none-any.whl (4.1 MB) Collecting appnope Using cached appnope-0.1.3-py2.py3-none-any.whl (4.4 kB) Collecting psutil Using cached psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl (239 kB) Collecting pickleshare Using cached pickleshare-0.7.5-py2.py3-none-any.whl (6.9 kB) Collecting pygments>=2.4.0 Using cached Pygments-2.12.0-py3-none-any.whl (1.1 MB) Collecting decorator Using cached decorator-5.1.1-py3-none-any.whl (9.1 kB) Collecting backcall Using cached backcall-0.2.0-py2.py3-none-any.whl (11 kB) Requirement already satisfied: setuptools>=18.5 in ./.conda/envs/tst/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets>=7.6.0->ipycanvas) (61.2.0) Collecting stack-data Using cached stack_data-0.3.0-py3-none-any.whl (23 kB) Collecting pexpect>4.3 Using cached pexpect-4.8.0-py2.py3-none-any.whl (59 kB) Collecting jedi>=0.16 Using cached jedi-0.18.1-py2.py3-none-any.whl (1.6 MB) Collecting prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 Using cached prompt_toolkit-3.0.29-py3-none-any.whl (381 kB) Collecting parso<0.9.0,>=0.8.0 Using cached parso-0.8.3-py2.py3-none-any.whl (100 kB) Collecting entrypoints Using cached entrypoints-0.4-py3-none-any.whl (5.3 kB) Collecting python-dateutil>=2.8.2 Using cached python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB) Collecting tinycss2 Using cached tinycss2-1.1.1-py3-none-any.whl (21 kB) Collecting defusedxml Using cached defusedxml-0.7.1-py2.py3-none-any.whl (25 kB) Collecting beautifulsoup4 Using cached beautifulsoup4-4.11.1-py3-none-any.whl (128 kB) Collecting jupyterlab-pygments Using cached jupyterlab_pygments-0.2.2-py2.py3-none-any.whl (21 kB) Collecting nbclient>=0.5.0 Using cached nbclient-0.6.4-py3-none-any.whl (71 kB) Collecting pandocfilters>=1.4.1 Using cached pandocfilters-1.5.0-py2.py3-none-any.whl (8.7 kB) Collecting mistune<2,>=0.8.1 Using cached mistune-0.8.4-py2.py3-none-any.whl (16 kB) Collecting bleach Using cached bleach-5.0.0-py3-none-any.whl (160 kB) Collecting MarkupSafe>=2.0 Using cached MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl (13 kB) Collecting jsonschema>=2.6 Using cached jsonschema-4.6.0-py3-none-any.whl (80 kB) Collecting fastjsonschema Using cached fastjsonschema-2.15.3-py3-none-any.whl (22 kB) Collecting attrs>=17.4.0 Using cached attrs-21.4.0-py2.py3-none-any.whl (60 kB) Collecting pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 Using cached pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl (81 kB) Collecting ptyprocess>=0.5 Using cached ptyprocess-0.7.0-py2.py3-none-any.whl (13 kB) Collecting wcwidth Using cached wcwidth-0.2.5-py2.py3-none-any.whl (30 kB) Collecting six>=1.5 Using cached six-1.16.0-py2.py3-none-any.whl (11 kB) Collecting argon2-cffi-bindings Using cached argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl (53 kB) Collecting cffi>=1.0.1 Using cached cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl (178 kB) Collecting pycparser Using cached pycparser-2.21-py2.py3-none-any.whl (118 kB) Collecting soupsieve>1.2 Using cached soupsieve-2.3.2.post1-py3-none-any.whl (37 kB) Collecting webencodings Using cached webencodings-0.5.1-py2.py3-none-any.whl (11 kB) Collecting pyparsing!=3.0.5,>=2.0.2 Using cached pyparsing-3.0.9-py3-none-any.whl (98 kB) Collecting pure-eval Using cached pure_eval-0.2.2-py3-none-any.whl (11 kB) Collecting asttokens Using cached asttokens-2.0.5-py2.py3-none-any.whl (20 kB) Collecting executing Using cached executing-0.8.3-py2.py3-none-any.whl (16 kB) Installing collected packages: traitlets, six, pyrsistent, attrs, wcwidth, tornado, pyzmq, python-dateutil, pycparser, pure-eval, ptyprocess, parso, nest-asyncio, jupyter-core, jsonschema, fastjsonschema, executing, entrypoints, asttokens, webencodings, stack-data, soupsieve, pyparsing, pygments, prompt-toolkit, pickleshare, pexpect, nbformat, matplotlib-inline, MarkupSafe, jupyter-client, jedi, decorator, cffi, backcall, appnope, tinycss2, psutil, pandocfilters, packaging, nbclient, mistune, jupyterlab-pygments, jinja2, ipython, defusedxml, debugpy, bleach, beautifulsoup4, argon2-cffi-bindings, terminado, Send2Trash, prometheus-client, nbconvert, ipython-genutils, ipykernel, argon2-cffi, notebook, widgetsnbextension, jupyterlab-widgets, pillow, numpy, ipywidgets, ipycanvas Successfully installed MarkupSafe-2.1.1 Send2Trash-1.8.0 appnope-0.1.3 argon2-cffi-21.3.0 argon2-cffi-bindings-21.2.0 asttokens-2.0.5 attrs-21.4.0 backcall-0.2.0 beautifulsoup4-4.11.1 bleach-5.0.0 cffi-1.15.0 debugpy-1.6.0 decorator-5.1.1 defusedxml-0.7.1 entrypoints-0.4 executing-0.8.3 fastjsonschema-2.15.3 ipycanvas-0.12.0 ipykernel-6.15.0 ipython-8.4.0 ipython-genutils-0.2.0 ipywidgets-7.7.0 jedi-0.18.1 jinja2-3.1.2 jsonschema-4.6.0 jupyter-client-7.3.4 jupyter-core-4.10.0 jupyterlab-pygments-0.2.2 jupyterlab-widgets-1.1.0 matplotlib-inline-0.1.3 mistune-0.8.4 nbclient-0.6.4 nbconvert-6.5.0 nbformat-5.4.0 nest-asyncio-1.5.5 notebook-6.4.12 numpy-1.22.4 packaging-21.3 pandocfilters-1.5.0 parso-0.8.3 pexpect-4.8.0 pickleshare-0.7.5 pillow-9.1.1 prometheus-client-0.14.1 prompt-toolkit-3.0.29 psutil-5.9.1 ptyprocess-0.7.0 pure-eval-0.2.2 pycparser-2.21 pygments-2.12.0 pyparsing-3.0.9 pyrsistent-0.18.1 python-dateutil-2.8.2 pyzmq-23.1.0 six-1.16.0 soupsieve-2.3.2.post1 stack-data-0.3.0 terminado-0.15.0 tinycss2-1.1.1 tornado-6.1 traitlets-5.2.2.post1 wcwidth-0.2.5 webencodings-0.5.1 widgetsnbextension-3.6.0 ```