Search code examples
pythonmediapipe

Issue with leaked resources and python crash in application that uses mediapipe for face / hand tracking


I have a simple face and hand tracking python application using the mediapipe library that crashes after it has been running a few minutes. In the task manager I can see the memory usage almost continually growing taking all available system memory till the crash. The crash seems to be happening at the python level and is not being caught in my code.

While troubleshooting I noticed that there are hundreds of Dummy-### threads in the VS Code call stack with 2 new dummy threads being created every loop. Is this a red herring or should these be getting cleaned up?

I have spent a lot of time searching for memory or thread issues with mediapipe and haven't turned up any hints.

Here is a simplified version of my code which replicates the "problem" with the threads (assuming that is really the issue)...Any ideas what I can do to troubleshoot this further?

import cv2
import mediapipe as mp
import os
import time

mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_face_mesh = mp.solutions.face_mesh
mp_hands=mp.solutions.hands
counter = 0
try:
    cap = cv2.VideoCapture(0)
    loopStart = time.time()
    while True:               
        success, imageOriginal = cap.read()
        if not success:
            print("Ignoring empty camera frame.")

            continue
        if True:
            image = cv2.flip(imageOriginal, 1)
        else:
            image = imageOriginal.copy()
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)               
        
        results = mp_face_mesh.FaceMesh(max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5).process(image)
        handresults = mp_hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5).process(image)
        print("Counter: " + str(counter) + " Loop Time: " + str(round((time.time() - loopStart), 2)))
        counter = counter + 1
        loopStart = time.time()


except Exception as e:
    import sys
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]   # type: ignore
    print("Error in start_session: " + str(e) + " " + str(exc_type) + " " + str(fname) + " " + str(exc_tb.tb_lineno))

In Windows Event Viewer I see that Python is crashing with this error:

Faulting application name: python.exe, version: 3.10.2150.1013, time stamp: 0x61e579fe
Faulting module name: ucrtbase.dll, version: 10.0.22621.608, time stamp: 0xf5fc15a3
Exception code: 0xc0000409
Fault offset: 0x000000000007f61e
Faulting process id: 0x0x1269C
Faulting application start time: 0x0x1D922268C3678F3
Faulting application path: C:\Program Files\Python310\python.exe
Faulting module path: C:\WINDOWS\System32\ucrtbase.dll
Report Id: efd4c79f-e52e-4050-9f95-b326e4dd2339
Faulting package full name: 
Faulting package-relative application ID: 

Solution

  • I was able to use trial and error to trace the issue to these two lines of code:

    results = mp_face_mesh.FaceMesh(max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5).process(image)
    handresults = mp_hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5).process(image)
    
    

    As the main loop executes these line they permanently leave a Dummy-XXX thread in the call stack and use a little more memory after each loop.

    To fix the issue I moved everything but the processing up to the initial declaration as shown below. With this change, the number of Dummy threads stabilizes at 45 and the application no longer leaks memory and crashes.

    import cv2
    import mediapipe as mp
    import os
    import time
    
    mp_drawing = mp.solutions.drawing_utils
    mp_drawing_styles = mp.solutions.drawing_styles
    mp_face_mesh = mp.solutions.face_mesh.FaceMesh(max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5)
    mp_hands=mp.solutions.hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5)
    counter = 0
    try:
        cap = cv2.VideoCapture(0)
        loopStart = time.time()
        while True:               
            success, imageOriginal = cap.read()
            if not success:
                print("Ignoring empty camera frame.")
    
                continue
            if True:
                image = cv2.flip(imageOriginal, 1)
            else:
                image = imageOriginal.copy()
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)               
            
            results = mp_face_mesh.process(image)
            handresults = mp_hands.process(image)
            print("Counter: " + str(counter) + " Loop Time: " + str(round((time.time() - loopStart), 2)))
            counter = counter + 1
            loopStart = time.time()
    
    
    except Exception as e:
        import sys
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]   # type: ignore
        print("Error in start_session: " + str(e) + " " + str(exc_type) + " " + str(fname) + " " + str(exc_tb.tb_lineno))