Search code examples
pythonflaskartificial-intelligencegradio

How can I convert Gradio app to flask api?


I am trying to convert Gradio app to flask api. In Gradio app, it is using components like Image, Text, and so on. I have to change that component to request form data. However, I am suffering with image processing. I need any help.

This is my code. cloth_image = gr.Image(label="Your label...", type="pil") cloth_mask_image = gr.Image(label="Your label...", type="pil")

How can I get the same return value as gr.Image component by preprocessing of cloth_image = request.files['cloth_image']


Solution

  • If you need image (as PIL) only on server then you can use directly

    from PIL import Image
    
    cloth_image = Image.open( request.files["cloth_image"] )
    

    Because files["cloth_image"] has method .read() so it can be treated as file-like object, and Image.open() can use file-like object instead of filename.

    And you can convert between pillow and numpy(and opencv)

    # from pillow to numpy/opencv
    arr = numpy.array(cloth_image)
    
    # from numpy/opencv to pillow
    cloth_image = Image.fromarray(arr)
    

    (opencv may need to convert colors between RGB and BGR)


    Minimal working code:

    from flask import Flask, request
    from PIL import Image
    
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def index():
        if request.method == 'POST':
            if "cloth_image" in request.files:
                img = request.files["cloth_image"]  # it has method .read() so it can be treated as file-like object
                cloth_image = Image.open(img)  # it can use file-like object instead of filename
                # ... process image ...
    
        return """
        <form method="POST" enctype="multipart/form-data">
        <input type="file" name="cloth_image">
        <button type="submit">Send</button>
        </form>"""
    
    if __name__ == '__main__':
        app.run()
    

    But if you need to send it back then you may need to use io.BytesIO to save image in file-like object in memory, convert it to base64, and display it as

    html = '<img src="data:image/png;base64,{}">'.format(data)
    

    More complex code which gets image, draw rectangle and send it back to browser.

    from flask import Flask, request
    from PIL import Image, ImageDraw
    import io
    import base64
    
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def index():
        img = 'empty'
        
        if request.method == 'POST':
            if "image" in request.files:
                print(request.files)
    
                # get image
                img = request.files["image"]   # it has method .read()
    
                # read to pillow
                image = Image.open(img)
    
                # draw something
                draw = ImageDraw.Draw(image)                                      # https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html
                draw.rectangle([(20,20), (280,280)], outline=(255,0,0), width=3)  # https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.ImageDraw.rectangle
                draw.text((15,5), "Hello World!")                                 # https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.ImageDraw.text
    
                # convert to file-like data
                obj = io.BytesIO()             # file in memory to save image without using disk  #
                image.save(obj, format='png')  # save in file (BytesIO)                           # https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save
                obj.seek(0)
    
                # convert to bases64
                data = obj.read()              # get data from file (BytesIO)
                data = base64.b64encode(data)  # convert to base64 as bytes
                data = data.decode()           # convert bytes to string
    
                # convert to <img> with embed image
                img = '<img src="data:image/png;base64,{}">'.format(data)
    
        return '<form method="POST" enctype="multipart/form-data"><input type="file" name="image"><button type="submit">Send</button></form><br>' + img
    
    if __name__ == '__main__':
        app.run()
    

    And if you need it without reloading page then you may have to use JavaScript. But I skip this problem.