3

I'm new to python. I have a task to build a API endpoint that takes an image and returns the image. So, I chose flask to get my job done.

I followed this SO question - Can a flask-based API return a file for an API endpoint to upload image file.

The code is as follows:

from flask import Flask, render_template , request , jsonify
from PIL import Image
import os , io , sys
import numpy as np 
import cv2
import base64

app = Flask(__name__)

start_point = (0, 0) 
end_point = (110, 110) 
color = (255, 0, 0)
thickness = 2

@app.route('/image' , methods=['POST'])
def mask_image():
    file = request.files['image'].read()
    npimg = np.fromstring(file, np.uint8)
    img = cv2.imdecode(npimg,cv2.IMREAD_COLOR)
    img = Image.fromarray(img.astype("uint8"))
    rawBytes = io.BytesIO()
    img.save(rawBytes, "png")
    rawBytes.seek(0)
    img_base64 = base64.b64encode(rawBytes.read())
    return jsonify({'status':str(img_base64)})


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

Then I sent a request to the API using python requests file upload.

But I'm not able to decode the base64 response. Code I have tried

import requests
import base64

files = {'image': open('image.png','rb')}
r = requests.post("http://localhost:5000/image", files=files)
print(base64.decodestring(r.text))

But it's throwing an error

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/anaconda3/envs/py37/lib/python3.7/base64.py in _input_type_check(s)
    509     try:
--> 510         m = memoryview(s)
    511     except TypeError as err:

TypeError: memoryview: a bytes-like object is required, not 'str'

The above exception was the direct cause of the following exception:

TypeError                                 Traceback (most recent call last)
<ipython-input-192-e8ba5f9daae3> in <module>
----> 1 base64.decodestring(r.text)

~/anaconda3/envs/py37/lib/python3.7/base64.py in decodestring(s)
    552                   "use decodebytes()",
    553                   DeprecationWarning, 2)
--> 554     return decodebytes(s)
    555
    556

~/anaconda3/envs/py37/lib/python3.7/base64.py in decodebytes(s)
    543 def decodebytes(s):
    544     """Decode a bytestring of base-64 data into a bytes object."""
--> 545     _input_type_check(s)
    546     return binascii.a2b_base64(s)
    547

~/anaconda3/envs/py37/lib/python3.7/base64.py in _input_type_check(s)
    511     except TypeError as err:
    512         msg = "expected bytes-like object, not %s" % s.__class__.__name__
--> 513         raise TypeError(msg) from err
    514     if m.format not in ('c', 'b', 'B'):
    515         msg = ("expected single byte elements, not %r from %s" %

TypeError: expected bytes-like object, not str

How do I decode the image?

jps
  • 20,041
  • 15
  • 75
  • 79
hunter
  • 33
  • 4

3 Answers3

1

Two things, first base64.decodestring expects bytes, not a string. You need to use either r.content or r.text.encode('utf8') to obtain bytes. As well, decodestring is deprecated, so you shouldn't use that. Typically, unless you have a good reason not to do so, you should use base64.b64decode to decode base64 data.

Second, the /image endpoint returns a JSON object containing the base64 image. Your client will have to first extract the image data from the JSON response, then decode it. The response object contains a .json() method that is useful for this:

import requests
import base64

files = {'image': open('image.png','rb')}
r = requests.post("http://localhost:5000/image", files=files)
print(base64.b64decode(r.json()['status']))
T0xicCode
  • 4,583
  • 2
  • 37
  • 50
1

The tagged answer is my own. Hence, I'll provide the answer.

One of the users @ToxicCode has already given 99% of the answer. But there is a catch here. The script sends the response as byte string wrapped in a string like the below.

"b'iVBORw0KGgoAAAANSUhEUgAABXYAAAOOCAIAAAAru93tAAEAAElEQVR4nOz9eZRk+V3ffX5+98a+ZkbutbZ2gZAx6DFgsEGHHRtrsC0xFgezGGg3to8H2+NlQGjmSAI/D4wfGD/40AiZRTDgBwkMGI9AwjoCzGLZgLHYZEmou5bMjFwiMjPWu37nj4iqzOqur
...
..

The byte b is already there in the string. So, without taking care of this if you follow the @ToxicCode, you will face error. So, as a bad approach you can use ast.literal_eval to convert to a string and then follow @ToxicCode code.

Important Note: Don't use ast.literal_eval() in your production server. Change the implementation accordingly.

import ast
import requests
import base64

files = {'image': open('image.png','rb')}
r = requests.post("http://localhost:5000/image", files=files)
data = ast.literval_eval(r.json()['status'])
print(base64.b64decode(data))
bigbounty
  • 16,526
  • 5
  • 37
  • 65
0

Try r.content which is bytes instead of r.text which is a string.

Ben
  • 178
  • 2
  • 12
  • I don't use Anaconda, after a cursory glance, the only other thing can suggest right now is to do ```print(base64.decodebytes(r.content))``` as it was working for me &```decodestring``` gives me a "DeprecationWarning: decodestring() is a deprecated alias since Python 3.1" – Ben Jul 17 '20 at 15:32