0

I'm writing an API with Flask, in which I receive an image through a POST, and split it into tiles which I want to send back as response.

I first fetch the image, then open it as an Image object with PIL, and create the tiles from a cropping operation. So far, so good.

Now I would like to send them as binaries, so as to display them in the front as explained here (although I'm not sure whether it's the best way). I tried with flask.jsonify, which told me that a bytes object is not json-serializable.

Therefore, how can I send the list of the tiles created?

My route is as follows:

import io
from flask import request
from PIL import Image
from . import app


@app.route("/map/tileset/prepare", methods=['POST'])
def prepare_tileset():
    image_source = request.files['image']
    width = int(request.form['tileWidth'])
    height = int(request.form['tileHeight'])

    data = image_source.stream.read()
    image_descriptor = io.BytesIO(data)
    original = Image.open(image_descriptor)

    tiles = []
    for i in range(original.width // width):
        for j in range(original.height // height):
            rect = ((width)*i, (height)*j, (width)*(i+1) - 1, (height)*(j+1) - 1)
            tile = original.crop(rect)

            # It works until here, but I don't know what to do after

            tiles.append(tile)

    return tiles
Right leg
  • 16,080
  • 7
  • 48
  • 81
  • Is this what you are looking for: https://stackoverflow.com/questions/11017466/flask-return-image-created-from-database – Janne Karila Oct 14 '18 at 13:18
  • @JanneKarila Only partially, since it doesn't (at least as it is) allow me to send a list of images. – Right leg Oct 14 '18 at 13:34

1 Answers1

1

JSON does not support binary data directly. JSON supports strings, but they are like unicode strings in Python.

Usually the base64 encoding is used for transferring binary data via JSON.

One possible solution is to use the data URLs:

import flask, io
from base64 import b64encode

@app.route("/map/tileset/prepare", methods=['POST'])
def prepare_tileset():
    # ...

    tiles = []
    for i in range(original.width // width):
        for j in range(original.height // height):
            rect = ((width)*i, (height)*j, (width)*(i+1) - 1, (height)*(j+1) - 1)
            tile = original.crop(rect)

            # BytesIO is an in-memory file abstraction so we don't have
            # to write any actual files to disk
            f = io.BytesIO()
            tile.save(f,"PNG")
            tile_png_bytes = f.getvalue()

            tile_png_b64 = b64encode(tile_png_bytes).decode()
            # remove newlines
            tile_png_b64 = tile_png_b64.replace('\n', '')
            tile_data_url = 'data:image/png;base64,' + tile_png_b64

            tiles.append(tile_data_url)

    return flask.jsonify(tiles)

Not tested, hope it works :)

Messa
  • 24,321
  • 6
  • 68
  • 92