Home > Software engineering >  How to print values dynamically in Flask web app?
How to print values dynamically in Flask web app?

Time:06-14

I am using following code to display camera feed in Flask web app.

App.py

from flask import Flask, render_template, Response
import cv2

app = Flask(__name__)

camera = cv2.VideoCapture(0)


def gen_frames():
    while True:
        success, frame = camera.read()  # read the camera frame
        if not success:
            break
        else:
            ret, buffer = cv2.imencode('.jpg', frame)
            frame = buffer.tobytes()
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n'   frame   b'\r\n')  # concat frame one by one and show result


@app.route('/')
def index():
    return render_template('index.html')

@app.route('/video_feed')
def video_feed():
    return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')


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

templates/index.html

<body>
    <div>
        <img src="{{ url_for('video_feed') }}" width="50%">
    </div>
</body>

I want to print frame[0][0][0] value dynamically using commas at the bottom of the video like below.

Video Feed

51, 37, 222, 67, ...

Could you please help me with that?

Thanks in advance

CodePudding user response:

The simplest method is to run JavaScript code with loop which periodically gest this value from server and put value in HTML.

But frame has to be in global variable to access it in another function in flask. And after converting frame to jpg you have to use different variable for this value.

But this method sometimes may have problem with synchromization. It may get value from new frame but browser may still display old frame.


Minimal working code

It uses setInterval(function, 40) to run function every 40ms (which gives 25 executions per second - like 25 frames per second). And this function uses fetch() to get value (JSON) from url /get_value and display it in <div id="value">

EDIT: I use frame[0,0,0].tolist() instead of frame[0,0].tolist() to get only BLUE (cv2 keeps pixel as B,G,R instead of R,G,B). I also add all values to innerText instead of repalcing previous value.

from flask import Flask, render_template_string, jsonify, Response
import cv2
import time

app = Flask(__name__)

camera = cv2.VideoCapture(0)
#success, frame = camera.read()  # default value at start
success = False  # default value at start
frame   = None   # default value at start


def gen_frames():
    global success
    global frame
    
    while True:
        success, frame = camera.read()  # read the camera frame
        if not success:
            break
        else:
            ret, buffer = cv2.imencode('.jpg', frame)
            image = buffer.tobytes()   # use other variable instead of `frame`
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n'
                   b'\r\n'   image   b'\r\n')  # concat frame one by one and show result
            time.sleep(0.04) # my `Firefox` needs this to have time to display image.
                             # And this gives stream with 25 FPS (Frames Per Second) (1s/0.04s = 25)

@app.route('/get_value')
def get_value():
    #if frame is not None:
    if success:
        value = frame[0,0,2].tolist()
    else:
        value = ['?']
        
    #print(value)
    return jsonify(value)        


@app.route('/')
def index():
    return render_template_string('''
<body>
    <div>
        <img src="{{ url_for('video_feed') }}" width="50%">
    </div>
    <div id="value"></div>
    
<script>
place = document.querySelector("#value");

setInterval(function(){
    console.log("run function");
    fetch("/get_value")
    .then(response => response.json())
    .then(data => {
        if(place.innerText.length > 0){
            place.innerText  = ",";
        }
        place.innerText  = data;
    })
}, 40);
</script
    
</body>
''')


@app.route('/video_feed')
def video_feed():
    return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')


if __name__ == "__main__":
    try:
        app.run(debug=True) #, use_reloader=False)
    except KeyboardInterrupt:
        print("Stopped by `Ctrl C`")
    finally:
        camera.release()
  • Related