ganeshsar / UnityPythonMediaPipeAvatar

Creating a multi-threaded full body tracking solution supporting arbitrary humanoid avatars for Unity using Google Mediapipe Pose Python bindings.
MIT License
91 stars 20 forks source link

Can't run on Mac #6

Open suyashcjoshi opened 10 months ago

suyashcjoshi commented 10 months ago

I'm unable to run the python on Mac, the windows where you see the markers don't show up. Anyone else running into same problem or know a fix? I think it might have to do with threading but not sure.

jerryum commented 9 months ago

I have the same issue. I had to turn the debug mode off to skip the part to see the avatar's move. This is a known issue of opencv - it doesn't support multi-threads in the MacOS. You should use the main thread in the MacOS. https://github.com/opencv/opencv/issues/22602

Traceback (most recent call last): File "/opt/homebrew/Cellar/python@3.10/3.10.13_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/threading.py", line 1016, in _bootstrap_inner self.run() File "/Users/jerrylee/Works/UnityPythonMediaPipeAvatar/mediapipeavatar/body.py", line 92, in run cv2.imshow('Body Tracking', image) cv2.error: Unknown C++ exception from OpenCV code

jerryum commented 9 months ago

FYI, you can add main_for_mac.py with the code. No multi-thread and it works in MacOS.

import mediapipe as mp
import cv2
import time
import global_vars
import struct
from clientUDP import ClientUDP

class BodyTracker:
    def __init__(self):
        self.cap = None
        self.client = None
        self.pipe = None
        self.data = ""

    def setup_capture(self):
        self.cap = cv2.VideoCapture(global_vars.CAM_INDEX)
        if global_vars.USE_CUSTOM_CAM_SETTINGS:
            self.cap.set(cv2.CAP_PROP_FPS, global_vars.FPS)
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, global_vars.WIDTH)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, global_vars.HEIGHT)
        print("Opened Capture @ %s fps" % str(self.cap.get(cv2.CAP_PROP_FPS)))

    def run(self):
        self.setup_capture()
        self.setup_comms()

        mp_drawing = mp.solutions.drawing_utils
        mp_pose = mp.solutions.pose

        with mp_pose.Pose(min_detection_confidence=0.80, min_tracking_confidence=0.5, model_complexity=global_vars.MODEL_COMPLEXITY, static_image_mode=False, enable_segmentation=True) as pose:
            while True:
                ret, frame = self.cap.read()
                if not ret:
                    break

                # Image transformations
                frame = cv2.flip(frame, 1)
                frame.flags.writeable = global_vars.DEBUG

                # Process the image
                results = pose.process(frame)

                # Render results
                if global_vars.DEBUG:
                    if results.pose_landmarks:
                        mp_drawing.draw_landmarks(frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS,
                                                  mp_drawing.DrawingSpec(color=(255, 100, 0), thickness=2, circle_radius=4),
                                                  mp_drawing.DrawingSpec(color=(255, 255, 255), thickness=2, circle_radius=2))
                    cv2.imshow('Body Tracking', frame)
                    if cv2.waitKey(3) & 0xFF == 27:
                        break

                # Prepare and send data
                self.prepare_data(results)
                self.send_data(self.data)

        self.cleanup()

    def prepare_data(self, results):
        self.data = ""
        if results.pose_world_landmarks:
            for i in range(0, 33):
                landmark = results.pose_world_landmarks.landmark[i]
                self.data += "{}|{}|{}|{}\n".format(i, landmark.x, landmark.y, landmark.z)

    def setup_comms(self):
        if not global_vars.USE_LEGACY_PIPES:
            self.client = ClientUDP(global_vars.HOST, global_vars.PORT)
            self.client.start()
        else:
            print("Using Pipes for interprocess communication (not supported on OSX or Linux).")

    def send_data(self, message):
        if not global_vars.USE_LEGACY_PIPES:
            self.client.sendMessage(message)
        else:
            # this is for MacOs. 
            print("Using Pipes for interprocess communication (not supported on OSX or Linux).")
            pass  

    def cleanup(self):
        if self.cap:
            self.cap.release()
        cv2.destroyAllWindows()
        if self.pipe:
            self.pipe.close()

if __name__ == "__main__":
    tracker = BodyTracker()
    tracker.run()
ganeshsar commented 9 months ago

This is great thanks for clarifying that this issue was related to multi-threading. I will try to create an update for the project at some point.