0

I've implemented a system to capture faces on-the-fly from a live stream using flask and OpenCV. I'm now facing difficulties with closing the stream and releasing the VideoCapture object. Here is a snippet of my code below:

from flask import Flask, Response, render_template
import cv2, imutils
import face_recognition
from skimage.exposure import is_low_contrast

app = Flask(__name__)

cam_feed = cv2.VideoCapture(0) # to get feed from webcam, use 0 as argument


def gen_frames():  # generate frame by frame from camera
    """
    Variable for counting number of images collected
    List for saving encodings also declared here
    """
    image_counter = 0

    name = "John Doe"
    known_names = []
    known_encodings = []


    if not cam_feed.isOpened():
        print("Cannot open camera")
        exit()

    while True:
        # Capture frame-by-frame
        success, frame = cam_feed.read()  # read the camera frame
        if not success:
            print("Can't receive frame (stream end?). Exiting ...")
            break
        else:
            ret, frame = cam_feed.read()
            frame = imutils.resize(frame, width=640)
           
            # if frame is read correctly ret is True
            if not ret:
                print("Can't receive frame (stream end?). Exiting ...")
                break
            # Our operations on the frame come here

            else:
                # face location model can either be HOG or CNN but HOG is faster (but less accurate) and way better if you don't have a GPU   
                boxes = face_recognition.face_locations(frame, model='hog')

                if boxes: # if a face is detected
                    for (top, right, bottom, left) in boxes:
                        if (image_counter < 20):
                            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
                            encodings = face_recognition.face_encodings(frame, boxes)
                            known_names.append(name)
                            known_encodings.append(encodings)
                            image_counter += 1
                    
                        else:
                            break # break while True loop and end stream
            
                ret, buffer = cv2.imencode('.jpg', frame) # encode the frame so that flask can return it as a response in bytes format
                frame = buffer.tobytes() # convert each frame to a byte object
                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

Here are my routes for streaming:

@app.route('/video_feed')
def video_feed():
    #Video streaming route. Put this in the src attribute of an img tag
    return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')


@app.route('/')
def index():
    """Video streaming home page."""
    return render_template('index.html')

When my image count reaches 20, the loop doesn't seem to break because I can see my webcam is still functioning. What I've tried to do is to create another route where I call cam_feed.release() and I add the route using the link in my index.html file; here is the code:

@app.route('/done')
def done():
    if cam_feed.isOpened():
        print("Releasing cam feed")
        cam_feed.release()
    return "Done"

and finally, here is the code that runs the flask server:

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

My question is this: How can I stop the stream and turn off my webcam after I'm done collecting the images? Thanks.

Mosky1970
  • 65
  • 8
  • The loop definitely did end but given cam_feed is started as a global object and release() was **not** called after the loop was done, it keeps your camera open. See [thread](https://stackoverflow.com/questions/48213499/whats-the-meaning-of-cv2-videocapture-release), plus [thread](https://stackoverflow.com/questions/60555538/flask-running-a-rest-server-conflicts-with-opencv-reading-a-webcam-on-ubuntu). – metatoaster May 19 '22 at 00:18

1 Answers1

0

I think it is because you enabled flask app the debug mode (app.run(debug=True)) which will use reloader mode by default. If the reloader mode is enabled, your flask application will be loaded in a separate process in order to continuously update the app according to your file changes, which will leads to cam_feed = cv2.VideoCapture(0) being run twice in two separated processed (main and flask app). When you run cam_feed.release(), it only close the cam in the process that run the flask application while the main process still has cam_feed opened. To avoid this scenario, you can add a check for if the process is main by accessing the environment variable WERKZEUG_RUN_MAIN that is exclusive to main process.

import os
if os.environ.get('WERKZEUG_RUN_MAIN'):
    cam_feed = cv2.VideoCapture(0)

Additionally, you can also turn off debug mode (app.run(debug=False)) and call cam_feed = cv2.VideoCapture(0) like you did in your original code

tax evader
  • 2,082
  • 1
  • 7
  • 9