Flask: How to update values on a web page

Question:

I have the following code:


app = Flask(__name__)


@app.route("/")
def Tracking():

    lower = np.array([35, 192, 65])
    upper = np.array([179, 255, 255])

    video = cv2.VideoCapture(1, 0)

    times = []  
    total = 0  
    is_round = False  

    while True:
        success, img = video.read()
        image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(image, lower, upper)
        blur = cv2.GaussianBlur(mask, (15, 15), 0)

        circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1, 14,
                                   param1=34, param2=10, minRadius=4, maxRadius=10)

        circles = np.uint16(np.around(circles))


        if (len(circles[0, :]) == 7) and not is_round:
            start_time = time.time()  
            is_round = True
            curr_count = 0 
            round_total = 0  


        elif is_round:
            if len(circles[0, :]) == 1:
                end_time = time.time()  
                is_round = False
                time_taken = end_time - start_time
                print('Round time: ', str(
                    datetime.timedelta(seconds=time_taken))[2:7])

                times.append(time_taken)
                average = sum(times) / len(times)
                print('Average time: ', str(
                    datetime.timedelta(seconds=average))[2:7])

            elif len(circles[0, :]) < 7:
                curr_count = (7 - round_total) - len(circles[0, :])
                total += curr_count 
                round_total += curr_count 

            for i in circles[0, :]:
                cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
                cv2.circle(img, (i[0], i[1]), 2, (0, 0, 255), 3)


        return render_template('theme1.html', output2=total)


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

HTML Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <meta http-equiv="refresh" content="10" >

    <link rel= "stylesheet"  href= "{{ url_for('static', filename='styles.css')}}">

    

</head>
<body>


        <div class="data">
        <p>{{  output2  }}</p>

        </div>
   




 
</body>
</html>

I need these values to update every 10 seconds or so, i was recommended to use ajax but i have no clue how to apply this, any help would be really appreciated, the python script uses opencv to detect objects in a live feed, "total" prints the amount of objects as an integer, this is what i am trying to get updated on my web page.

Asked By: Ethan0619

||

Answers:

A function will stop when it hits a return statement so your code will stop at the first iteration.

So instead create two routes, first delivers the webpage and second only the data.

create a periodic function in the main webpage which will fetch the data from the second route using get method.

also the http-eqiv=refresh will refresh the webpage everytime it fetches the new data. But this implementation will not refresh the webpage but still update the webpage dynamically.

As your open cv code is in a loop you can create a generator function which will give the next value on each call. I will leave that to you.

here is my implementation

app = Flask(__name__)


@app.route("/")
def home():
    return render_template('theme1.html')

@app.get("/update")
def update():

    total = str(random.random()) ## replace this with what you want to send

    return total

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

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <link rel= "stylesheet"  href= "{{ url_for('static', filename='styles.css')}}">

</head>
<body>

    <div class="data">
    <p id="output"></p>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
        function update(){
            $.get("/update", function(data){
                $("#output").html(data)
            });
        }
        update()
        var intervalId = setInterval(function() {
            update()
        }, 10000);

    </script>
        
</body>
</html>

EDIT: Here’s a version with generator functions. This was made in a hurry so there are better ways to do this

app = Flask(__name__)

def output():
    lower = np.array([35, 192, 65])
    upper = np.array([179, 255, 255])

    video = cv2.VideoCapture(1, 0)

    times = []  
    total = 0  
    is_round = False  

    while True:
        success, img = video.read()
        image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(image, lower, upper)
        blur = cv2.GaussianBlur(mask, (15, 15), 0)

        circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1, 14,
                                   param1=34, param2=10, minRadius=4, maxRadius=10)

        circles = np.uint16(np.around(circles))


        if (len(circles[0, :]) == 7) and not is_round:
            start_time = time.time()  
            is_round = True
            curr_count = 0 
            round_total = 0  


        elif is_round:
            if len(circles[0, :]) == 1:
                end_time = time.time()  
                is_round = False
                time_taken = end_time - start_time
                print('Round time: ', str(
                    datetime.timedelta(seconds=time_taken))[2:7])

                times.append(time_taken)
                average = sum(times) / len(times)
                print('Average time: ', str(
                    datetime.timedelta(seconds=average))[2:7])

            elif len(circles[0, :]) < 7:
                curr_count = (7 - round_total) - len(circles[0, :])
                total += curr_count 
                round_total += curr_count 

            for i in circles[0, :]:
                cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
                cv2.circle(img, (i[0], i[1]), 2, (0, 0, 255), 3)
        
        yield total # Yield the value to get values on demand
        



@app.route("/")
def home():
    return render_template('theme1.html')

gen_total = output() # initate the function out of the scope of update route

@app.get("/update")
def update():
    global gen_total # get the global iterator
    
    # return the next value in iterator
    return str(next(gen_total)) 
    

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