Search code examples
pythonhtmlformsflaskartificial-intelligence

Image does not render on after ai image is generated and response has finished


I am coming back to development after years of not working on anything so my skills are rather rusty and I apologize for any improper syntax and formatting issues presented in advance.

I am currently working on a little web app project that takes a text prompt and generates an image using googles aivertex and gemini.

Here is some code snippets for review.

HTML Template

<body>
    <h1>Turn your words into art!</h1>
    <p>Enter a description of the image you want to generate.</p>
    <form target="_blank" id="text-form" action="/my_prompt" onsubmit="dontredict('myindex.html')" method="post">
        <label for="text-input">Text Prompt:</label>
        <textarea id="text-input" name="text" rows="5" placeholder="e.g. A cat riding a bicycle on Mars"></textarea>
        <button type="submit">Generate Image</button>
    </form>
    <div id="image-container">
        {% if img_data %}
        <h1>Your generated image will appear here.</h1>
        <img id="picture" style="height: 300px;" src="{{url_for('static',filename='my-output.png')}}">
        {% else %}
        <h1>Image will be render here...</h1>
        {% endif %}
    </div>
        <!--Jquery Cdn -->
        <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
            crossorigin="anonymous"></script>
        
        <script type="text/javascript">
           function dontredict(dynamicUrl){ 
            $(document).on('submit', '#text-form', function (e) {
                e.preventDefault();
                console.log('button smashed');
                $.ajax({
                    type: 'POST',
                    url: '/my_prompt',
                    data: {
                        text: $("#text-input").val()
                    },
                    success: function () {
                        alert('saved');
                    }
                })
            });
        }
        </script>
</body>

Python text-to-image.py

def disp_image():
    if os.path.exists(output_file):
        im = Image.open("static/my-output.png")
        data = io.BytesIO()
        im.save(data, "PNG")
        encoded_img_data = base64.b64encode(data.getvalue())
        img_data=encoded_img_data.decode('utf-8')
        return render_template("myindex.html", img_data=img_data)
    #if os.path.exists(output_file):
            #return redirect(url_for('static/my-output.png'))

@app.route('/my_prompt', methods = ['POST'])
@cross_origin()
def imggen():
   print("inside gen")
   if request.method == 'POST':
      prompt = request.form.get("text")
      logger.info('%s This is the prompt: ', prompt)
      print(prompt); 
      print("after prompt") 
      model = ImageGenerationModel.from_pretrained("imagegeneration@006")
      images = model.generate_images(
        prompt=prompt,
        # Optional parameters
        number_of_images=1,
        language="en",
        # You can't use a seed value and watermark at the same time.
        # add_watermark=False,
        # seed=100,
        aspect_ratio="1:1",
        safety_filter_level="block_some",
        person_generation="allow_adult",
        )
      images[0].save(location=output_file, include_generation_parameters=False)
      print(f"Created output image using {len(images[0]._image_bytes)} bytes")
      print(disp_image())
      return disp_image()
       
   else:
        print("error")

The issues I'm having is when the text is posted and comes back with the generated image it get saved properly to the static image folder (using flask and python) but, it does not display or render to the html page. I am also aware that some of the code may be redundant. Let me know what I am doing wrong, in terms of displaying the image to the actual html page, Thank you.


Solution

  • When you use $.ajax (or built-in modern fetch() or older XMLHttpRequest())
    then browser doesn't update HTML. It is your job.
    This way you may decide what and when to update on page.

    In success() you have to get response from flask and you have to add/replace HTML

    But first: you don't have to send full page but only some part - like <h1> and <img>.
    And you can use render_template_string() for this.

    def disp_image():
    
        # ... code ...
    
        return render_template_string('''       
    <h1>Your generated image will appear here.</h1>
    <img id="picture" style="height: 300px;" 
    src="{{url_for('static',filename='my-output.png')}}">
    ''')
    

    And later you can replace only content inside <div id="image-container">

    $.ajax(
    
      // ... code ...
    
      success: function(response_data) {
        $('#image-container').html(response_data);
        alert('saved');
      }
    )
    

    Because browser may not reload image if it will have always the same name - because it will not know that you saved new image with the same name - so you may send image as base64 in src="..."

    def disp_image():
    
        # ... code ...
    
        img_data = encoded_img_data.decode('utf-8')
    
        return render_template_string('''       
    <h1>Your generated image will appear here.</h1>
    <img id="picture" style="height: 300px;" 
    src="data:image/png;base64, {{base64_image}}">
    ''', base64_image=img_data)
    

    BTW: There is one space after data:image/png;base64,


    Of course you can send only base64_image - ie. as JSON - and JavaScript can get it and replace only src="..." (if it already exists on page)

    from flask import jsonify
                            
    def disp_image():
    
        # ... code ...
    
        img_data = encoded_img_data.decode('utf-8')
    
        return jsonify({'base64_image': img_data})
    
    success: function(response_data) {
        var json = $.parseJSON(response_data);
        var image = json.base64_image;
        $('#picture').attr('src', 'data:image/png;base64, ' + image);
        alert('saved');
    }    
    

    Full working code - with two versions:

    • one sends HTML with h1 and img
    • other sends JSON with base64 and it create img in JavaScript

    Both uses PIL.Image to generate image with text

    import os
    from flask import Flask, request, render_template_string
    from PIL import Image, ImageDraw, ImageFont
    import io
    import base64
    
    os.makedirs('static', exist_ok=True)
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return render_template_string('''<!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
                crossorigin="anonymous"></script>    
    </head>
    <body>
    
    <form target="_blank" id="text-form" action="/my_prompt" method="POST">
        <textarea id="text-input" name="text" rows="5" placeholder="e.g. A cat riding a bicycle on Mars"></textarea></br>
        <button type="submit">Generate Image</button>
    </form>
    
    <form target="_blank" id="text-form-json" action="/my_prompt_json" method="POST">
        <textarea id="text-input-json" name="text" rows="5" placeholder="e.g. A cat riding a bicycle on Mars"></textarea></br>
        <button type="submit">Generate Image</button>
    </form>
    
    <div id="image-container">
    <h1>No Image</h1>
    </div>
    
    <script type="text/javascript">
    $(document).on('submit', '#text-form', function (e) {
        e.preventDefault();
        console.log('button smashed');
        $.ajax({
            type: 'POST',
            url: '/my_prompt',
            data: {
                text: $("#text-input").val()
            },
            success: function(response_data) {
                $('#image-container').html(response_data);
            }
        })
    });
    
    $(document).on('submit', '#text-form-json', function (e) {
        e.preventDefault();
        console.log('button smashed JSON');
        $.ajax({
            type: 'POST',
            url: '/my_prompt_json',
            data: {
                text: $("#text-input-json").val()
            },
            success: function(response_data) {
                picture = $('#picture');
                if(picture.length === 0) {
                    picture = $('<img id="picture" style="height: 300px;">');
                }
                picture.attr('src', 'data:image/png;base64, ' + response_data.base64_image);
                $('#image-container').html(picture);
            }
        })
    });
    
    </script>
    </body>
    </html>''')
    
    def disp_image():  
    
        img = Image.open("static/my-output.png")
        data = io.BytesIO()
        img.save(data, 'PNG')
        encoded_img_data = base64.b64encode(data.getvalue())
        img_data=encoded_img_data.decode('utf-8')
        
        return render_template_string('''       
    <h1>Your generated image will appear here.</h1>
    <img id="picture" style="height: 300px;" 
    src="data:image/png;base64, {{base64_image}}">
    ''', base64_image=img_data)
    
    from flask import jsonify
                            
    def disp_image_json():
    
        img = Image.open("static/my-output.png")
        data = io.BytesIO()
        img.save(data, 'PNG')
        encoded_img_data = base64.b64encode(data.getvalue())
        img_data=encoded_img_data.decode('utf-8')
    
        return jsonify({'base64_image': img_data})
    
    @app.route('/my_prompt', methods=['POST'])
    def image():
        if request.method == "POST":
        
            text = request.form.get('text', '???')
            print('text:', text)
            
            img = Image.new('RGB', (600, 300))
            draw = ImageDraw.Draw(img)
            font = ImageFont.truetype("arial.ttf", size=40)
            draw.text((10, 10), text, font=font, fill=(255, 255, 255))
            img.save('static/my-output.png')
            
            return disp_image()
    
    @app.route('/my_prompt_json', methods=['POST'])
    def image_json():
        if request.method == "POST":
        
            text = request.form.get('text', '???')
            print('text:', text)
            
            img = Image.new('RGB', (600, 300))
            draw = ImageDraw.Draw(img)
            font = ImageFont.truetype("arial.ttf", size=40)
            draw.text((10, 10), text, font=font, fill=(255, 255, 255))
            img.save('static/my-output.png')
            
            return disp_image_json()
    
    if __name__ == '__main__':
        app.debug = True 
        app.run()