import torch
import cv2
import mediapipe as mp
import numpy as np
import concurrent.futures

# Load YOLOv5 Model for Object Detection
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')  # Pre-trained small YOLOv5 model

# Initialize MediaPipe Pose class
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils

# Function to estimate pose for a single person and recognize activity
def estimate_pose_on_person(cropped_image):
    with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:
        image_rgb = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2RGB)
        results = pose.process(image_rgb)

        # Activity recognition based on pose landmarks
        activity = recognize_activity(results.pose_landmarks)

        if results.pose_landmarks:
            mp_drawing.draw_landmarks(cropped_image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
        
        return cropped_image, activity

# Function to recognize activities based on pose landmarks
def recognize_activity(landmarks):
    if not landmarks:
        return "Unknown"

    # Example simple rule-based activity recognition
    # You can extend this to train an ML model on top of pose data
    # Sample landmark checks for walking, lifting, digging
    # You will have to add further refinement for real use cases
    left_shoulder = landmarks.landmark[mp_pose.PoseLandmark.LEFT_SHOULDER].y
    right_shoulder = landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER].y
    left_hip = landmarks.landmark[mp_pose.PoseLandmark.LEFT_HIP].y
    right_hip = landmarks.landmark[mp_pose.PoseLandmark.RIGHT_HIP].y

    if abs(left_shoulder - right_shoulder) < 0.05 and abs(left_hip - right_hip) < 0.05:
        return "Standing"
    elif left_shoulder < left_hip and right_shoulder < right_hip:
        return "Digging"
    else:
        return "Walking"

# Function to handle each frame and perform multi-person pose estimation
def process_frame(frame):
    # Use YOLOv5 to detect persons in the frame
    results = model(frame)
    
    # Get detection results as pandas dataframe
    detections = results.pandas().xyxy[0]  # Bounding boxes, confidence, and class predictions

    cropped_images = []
    bboxes = []
    activities = []

    for _, row in detections.iterrows():
        if row['name'] == 'person':  # Ensure the detected class is 'person'
            x1, y1, x2, y2 = int(row['xmin']), int(row['ymin']), int(row['xmax']), int(row['ymax'])
            cropped_image = frame[y1:y2, x1:x2]
            cropped_images.append(cropped_image)
            bboxes.append((x1, y1, x2, y2))

    # Use multithreading to process pose estimation for each person detected
    with concurrent.futures.ThreadPoolExecutor() as executor:
        results = list(executor.map(estimate_pose_on_person, cropped_images))
    
    # Draw bounding boxes, pose landmarks, and activity labels on the original frame
    for idx, (cropped, activity) in enumerate(results):
        x1, y1, x2, y2 = bboxes[idx]
        frame[y1:y2, x1:x2] = cropped

        # Draw the bounding box for each person
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        
        # Put the activity text label
        cv2.putText(frame, activity, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)

    return frame

# Open the video stream (replace with webcam or video file path)
cap = cv2.VideoCapture('/Users/apple/Movies/hiv00032.mp4')

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # Process the current frame
    output_frame = process_frame(frame)

    # Show the processed frame with bounding boxes, pose landmarks, and activity labels
    cv2.imshow('Activity Recognition', output_frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()


write steps and code to optimize the algorithm with code to implement object tracking and recognition of multiple object in the video , activity recognition of the object . first compress losslessly  with out loosing the quality and cropped the video to remove unnessary things . focus on the moving pixels or objects than the stationary pixels and objects . write a very optimixed code