1

this is my first question on SO. Please give me directions if the question is not clear.

I am trying to get an image from a public API and write it on a webpage using PyScript.

However, I am not able to get the image to display on the webpage. I tried different packages to read the image (PIL, matplotlib, imageio) and different methods to display the output (Setting "output" at the beginning of pyscript, using pyscript.write() ). Below you can find a full (non-working) example.

img is formatted as a Numpy array with uint8 values.

UPDATE: The data is obtained correctly from the API. If I treat the data as Numpy array I can see all the pixel values. However, I am not able to display the image on the webpage afterwards.

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
        <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
        <py-env>
            - matplotlib
            - imageio
        </py-env>
    </head>
    <body>
        <h1>PyScript - images from API</h1>
        <div>
            <p>
                This webpage fetches a cat image from <a href="https://cataas.com/#/", target="_blank">cataas</a> and displays it below.
            </p>
        </div>
        <div id="image"></div>

    <py-script output="image">
from pyodide.http import pyfetch
import asyncio
from io import BytesIO
import matplotlib.pyplot as plt
import imageio.v3 as iio


response = await pyfetch(url="https://cataas.com/cat", method="GET")

img = iio.imread(BytesIO(await response.bytes()), index=None)

imgplot = plt.imshow(img)

imgplot
    </py-script>
    </body>
</html>

I tested on both Chrome and Firefox, but the image is never displayed.

Thanks in advance!

  • Is your endpoint configured to allow Cross-Origin Resource Sharing (CORS) from anywhere? See [here](https://community.anaconda.cloud/t/pyfetch-example/20316/2) and notes [here](https://stackoverflow.com/a/72453349/8508004). Plus , or alternatively, your code looks like it isn't handling the response like examples I see other places, see [here](https://stackoverflow.com/a/72453349/8508004) & [here](https://github.com/pyscript/pyscript/issues/411#issuecomment-1140304546) & [here](https://pyodide.org/en/stable/usage/faq.html#how-can-i-load-external-files-in-pyodide) – Wayne Jun 03 '22 at 15:30
  • 1
    Hi @Wayne, thanks for your comment. I tried to call the API endpoint from other applications (including the browser), and it works correctly. I can also print with PyScript the numpy array with data of the image I get via the API. However, I am not able to display that image on the webpage. I checked the other examples you posted, but to me it seems Iḿ handling the response the same way. Only I am accessing the `.bytes()` method rather than the `.json()` one, because the data I am getting is an image. I believe the issue is somewhere in the display of the image, not in the data retrieval. – Luigi Palumbo Jun 03 '22 at 15:52

2 Answers2

0

you need to assign the output image to some element in HTML document. Inside pyscript node add something like:

document.querySelector("img").setAttribute("src", img)

will display the image in tag: <img id ="img" src="src"> Use id="img" for .CSS

And, I dont know how your code for downloading the images works but I googled a lot (dont remember the source, most likely this: https://www.jhanley.com/pyscript-loading-python-code-in-the-browser/) and created two functions:

async def download(url):
            filename = Path(url).name
            response = await pyfetch(url)
            if response.status == 200:
                status = response.status
                with open(filename, mode="wb") as file:
                    file.write(await response.bytes())
                return filename, status
            else:
                status = response.status
                filename = None
                return filename, status

async def process_response(url):
            response_content = await loop.run_until_complete(
                download(url)
                )
            if response_content[1] == 200:
                data = base64.b64encode(open(response_content[0], "rb").read()).decode("utf-8")
                src = f"data:image/png;base64,{data}"
                return src
            else:
                src = None
                return src

img = await process_response("url")
document.querySelector("img").setAttribute("src", img)
Johnny R.
  • 16
  • 2
  • Hi @Johnny R., thanks for your answer! I also read thru John Hanley bolg, I think it has great content on PyScript. Would you mind sharing which libraries you imported in the above example? From what I can see, at lease was: `from js import document` But I guess also some other library was used. – Luigi Palumbo Aug 02 '22 at 07:42
  • Ok, I figured it out. The missing import was `from pathlib import Path` , your solution works like a charm! – Luigi Palumbo Aug 02 '22 at 09:48
0

I add this answer as complement of the accepted answer from @Johnny R., as I took some time to figure out how to import the Path element he used in his function. The full working code is below.

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
        <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
        <py-env>
            - matplotlib
            - imageio
        </py-env>
    </head>
    <body>
        <h1>PyScript - images from API</h1>
        <div>
            <p>
                This webpage fetches a cat image from <a href="https://cataas.com/#/", target="_blank">cataas</a> and displays it below.
            </p>
        </div>
        <div><img id ="img" src="src"></div>

    <py-script>
from pyodide.http import pyfetch
import asyncio
from js import document
import base64
from pathlib import Path

async def download(url):
            filename = Path(url).name
            response = await pyfetch(url, method="GET")
            if response.status == 200:
                status = response.status
                with open(filename, mode="wb") as file:
                    file.write(await response.bytes())
                return filename, status
            else:
                status = response.status
                filename = None
                return filename, status

async def process_response(url):
            response_content = await loop.run_until_complete(
                download(url)
                )
            if response_content[1] == 200:
                data = base64.b64encode(open(response_content[0], "rb").read()).decode("utf-8")
                src = f"data:image/png;base64,{data}"
                return src
            else:
                src = None
                return src

img = await process_response("https://cataas.com/cat")
document.querySelector("img").setAttribute("src", img)
    </py-script>
    </body>
</html>