Skip to main content
  1. Posts/

Build Web API with Flask -- Post and Receive Image

··657 words·4 mins·
Table of Contents

In this post, I want to write about how to build a simple image processing web API that returns the size of an image. The topics include how to build this web API with Flask and how to post image to this web API and get response.

There are mainly two ways by which we can send an image to the web service. Based on how you send the image, the way to get the uploaded image on the server side also varies.

Post binary image
#

You can directly post binary image to the server using the files parameter of requests.post():

url = 'http://127.0.0.1:5000/im_size'
my_img = {'image': open('test.jpg', 'rb')}
r = requests.post(url, files=my_img)

# convert server response into JSON format.
print(r.json())

In the above code, the Content-Type of the Header of the POST request will be multipart/form-data. Then in the server side, you can get the posted image like this:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/im_size", methods=["POST"])
def process_image():
    file = request.files['image']
    # Read the image via file.stream
    img = Image.open(file.stream)

    return jsonify({'msg': 'success', 'size': [img.width, img.height]})


if __name__ == "__main__":
    app.run(debug=True)

In the server side, the posted image will be in request.files['image'], which is of type werkzeug.datastructures.FileStorage. You can save the image to disk via save() method of this object:

file.save('im-received.jpg')

The image is also stored in file.stream, which is a file-like object so that you can easily read the image for later processing:

# img is PIL Image object
img = Image.open(file.stream)

Finally, we construct a Python dict and convert it to JSON format via the jsonify() method provided by Flask.

How to post multiple files
#

To post multiple images to the server, you can post a list of file tuples like the following:

multiple_files = [
    ('image', ('test.jpg', open('test.jpg', 'rb'))),
    ('image', ('test.jpg', open('test.jpg', 'rb')))
]
# simplified form
# multiple_files = [
#     ('image', open('test.jpg', 'rb')),
#     ('image', open('test.jpg', 'rb'))
# ]
r = requests.post(url, files=multiple_files, data=data)

In the server side, you can still receive the posted images using request.files:

from flask import Flask, request
# ... other code
files = request.files.to_dict(flat=False) ## files is a list containing two images.
for i, file in enumerate(files):
    file.save(f'image-{i}.jpg')

Post additional data
#

We usually want to post more meta info than merely the image itself. We can use the data parameter in requests.post():

payload = {'id': '123', 'type': 'jpg'}
r = requests.post(url, files=files, data=payload)

To get the payload dict in the server side, we use request.form, which is of type werkzeug.datastructures.ImmutableMultiDict. We use request.form.to_dict() to convert the received form data into Python dict:

payload = request.form.to_dict()
id = payload['id']
im_type = payload['type']

Post base64 encoded image
#

Another way is to just encode the image with base64 and post all the info via data parameter in requests.post() in the client side.

import base64

with open('test.jpg', 'rb') as f:
    im_b64 = base64.b64encode(f.read())

payload = {'id': '123', 'type': 'jpg', 'box': [0, 0, 100, 100], 'image': im_b64}

In the server side, we fetch the posted payload, get the base64 encoded image and decode it:

import base64
import io
from PIL import Image

payload = request.form.to_dict(flat=False)

im_b64 = payload['image'][0]  # remember that now each key corresponds to list.
# see https://jdhao.github.io/2020/03/17/base64_opencv_pil_image_conversion/
# for more info on how to convert base64 image to PIL Image object.
im_binary = base64.b64decode(im_b64)
buf = io.BytesIO(im_binary)
img = Image.open(buf)

Post multiple based64 encoded image
#

To post multiple base64 encoded images to the server, post them as a list of base64 string:

b64_ims = []
for im_path in im_paths:
    with open(im_path, 'rb') as f:
        im_b64 = base64.encode(f.read())
    b64_ims.append(im_b64)
payload = {"images": b64_ims}

In the server side, you can get the posted dict and decode the base64 image one by one, just like what I have shown above for a single image.

References
#

Related

Build Web API with Flask --- Work with JSON-like Dict
··435 words·3 mins
Build A Web API Service Using Flask -- The Basics
··1014 words·5 mins
Post Nested Data Structure to the Server Using Requests
··394 words·2 mins