Threading Causes Program To Abruptly Stop

Question:
For some reason, my program abruptly stops at face_locations = face_recognition.face_locations(frame) and the program quite simply just stops. No errors, nothing. I am not running this on Replit, but on VS Code on my computer. Here’s the code:

people = []
people_lock = threading.Lock()

def scan_frames(frames, model):
    global people
    for i, frame in enumerate(frames):
        print(f"Processing frame {i+1}")
        try:
            frame = ensure_valid_color_shape(frame)
            expected_height, expected_width = frames[0].shape[:2]
            if frame is None or frame.shape != (expected_height, expected_width, 3):
                print(f"Frame {i+1} is invalid")
                continue
            # Detect faces and generate encodings
            face_locations = face_recognition.face_locations(frame)
            face_encodings = face_recognition.face_encodings(frame, face_locations)
            for face_encoding in face_encodings:
                matches = {
                    label: np.mean(face_recognition.face_distance(face_images_encodings, face_encoding))
                    for label, face_images_encodings in model.items()
                    if face_images_encodings
                }
                if matches:
                    best_match = min(matches, key=matches.get)
                    if matches[best_match] < 0.5:  # Adjustable threshold for matching similarity
                        with people_lock:
                            if not (best_match in people):
                                people.append(best_match)
        except Exception as e:
            print(f"An exception occurred while processing frame {i+1}: {e}")

def ai_face_recog_from_video(video_path):
    people = []
    # Capture video
    video_capture = cv2.VideoCapture(video_path)
    if not video_capture.isOpened():
        raise FileNotFoundError(f'Video not found: {video_path}')

    # Initialize variables
    recog_model = {}
    frame_count = 0
    frame_amount = video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
    frame_ranges = [
            list(range(1, int(frame_amount) // 2 + 1, 2)),
            list(range(int(frame_amount) // 2 + 1, int(frame_amount) + 1, 2))
    ]

    print(1)

    for i in range(len(frame_ranges)):
        for j in range(len(frame_ranges[i])):
            # Set the frame index to the current frame number
            frame_number = frame_ranges[i][j]
            video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
            # Read the frame
            ret, frame = video_capture.read()
            if ret:
                frame_ranges[i][j] = frame  # Replace frame number with the actual frame
            else:
                print(f"Error: Could not retrieve frame at index {frame_number}.")
                break  # Stop if a frame could not be retrieved
    print(2)
    # Load recognition model only once
    for subfolder in os.listdir('recog_model'):
        subfolder_path = os.path.join('recog_model', subfolder)
        if os.path.isdir(subfolder_path):
            recog_model[subfolder] = [
                face_recognition.face_encodings(face_recognition.load_image_file(os.path.join(subfolder_path, item)))[0]
                for item in os.listdir(subfolder_path)
                if os.path.isfile(os.path.join(subfolder_path, item))
                and face_recognition.face_encodings(face_recognition.load_image_file(os.path.join(subfolder_path, item)))
            ]
    print(3)
    # Grab a single frame of video
    # video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_count-1)
    # ret, frame = video_capture.read()
    # if not ret:
    #     break  # End of video

    # Test the modified function with the first 10 frames from the first half
    print(4)
    first_half = threading.Thread(target=scan_frames, args=(frame_ranges[0], recog_model, ))
    second_half = threading.Thread(target=scan_frames, args=(frame_ranges[1], recog_model, ))
    print(5)
    start = time.time()

    # Start threads
    first_half.start()
    second_half.start()
    print(6)
    # Join threads to the main thread
    first_half.join()
    print(7)
    second_half.join()
    print(8)
    print(people)

    print("Time: " + str(time.time() - start))
    video_capture.release()
    print(f'Processed {frame_count/5} frames and {len(people)} face(s).')
    return people

def ai_face_recog_from_video_old(video_path):
    people = []
    # Capture video
    video_capture = cv2.VideoCapture(video_path)
    if not video_capture.isOpened():
        raise FileNotFoundError(f'Video not found: {video_path}')

    # Initialize variables
    recog_model = {}
    frame_count = 0

    # Load recognition model only once
    for subfolder in os.listdir('recog_model'):
        subfolder_path = os.path.join('recog_model', subfolder)
        if os.path.isdir(subfolder_path):
            recog_model[subfolder] = [
                face_recognition.face_encodings(face_recognition.load_image_file(os.path.join(subfolder_path, item)))[0]
                for item in os.listdir(subfolder_path)
                if os.path.isfile(os.path.join(subfolder_path, item))
                and face_recognition.face_encodings(face_recognition.load_image_file(os.path.join(subfolder_path, item)))
            ]
    start = time.perf_counter()
    while True:
        # Grab a single frame of video
        video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_count-1)
        ret, frame = video_capture.read()
        if not ret:
            break  # End of video

        # Detect faces and generate encodings
        face_locations = face_recognition.face_locations(frame)
        face_encodings = face_recognition.face_encodings(frame, face_locations)

        # Process detected faces and save them
        for face_encoding in face_encodings:
            matches = {
                label: np.mean(face_recognition.face_distance(face_images_encodings, face_encoding))
                for label, face_images_encodings in recog_model.items()
                if face_images_encodings
            }
            if matches:
                best_match = min(matches, key=matches.get)
                if matches[best_match] < 0.5:  # Adjustable threshold for matching similarity
                    if not(best_match in people):
                        people.append(best_match)
        frame_count += 2
    print("took " + str(time.perf_counter() - start))
    video_capture.release()
    print(f'Processed {frame_count/5} frames and {len(people)} face(s).')
    return people

start = time.perf_counter()

faces = ai_face_recog_from_video('test1.mp4')

# print(f"People in video: {', '.join(faces)}\nTime: " + str(time.perf_counter() - start))

print(f"People in video: {faces}\nTime: " + str(time.perf_counter() - start))

I dunno.
Maybe catch BaseException?

Threading won’t speed up your code. You’ll have to use multiprocessing.

The command line interface for face_recognition has some multiprocessing functionality. Maybe take a look at this, at process_images_in_process_pool(), it looks like it implements exactly what you are looking for.

@NuclearPasta0 I’m looking for a way to run multiple of the same function at the same time. Is that not what threading is for? If not, what should I replace it with to have the intended functionality?

It is a common misunderstanding that the threading module in python can provide performance.
I did post in the other topic about this.

According to the docs,

In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.

Therefore, you should use multiprocessing — Process-based parallelism — Python 3.12.1 documentation, like in the link I put in the other post above.

After much debugging, I got my project to run with the threading. Now, I just have to work on the multiprocessing, as @NuclearPasta0 mentioned.

Note: Could someone merge other three posts in here into How Could I Speed Up My Program? - #22 by RedCoder? Thank you!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.