File size: 6,758 Bytes
925cf9e
 
 
01812af
1ed5218
925cf9e
01812af
 
 
925cf9e
01812af
 
 
 
 
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
925cf9e
01812af
925cf9e
 
 
01812af
925cf9e
01812af
 
925cf9e
 
 
 
01812af
 
 
 
 
925cf9e
01812af
 
 
 
925cf9e
01812af
 
925cf9e
01812af
 
 
 
 
 
925cf9e
01812af
 
 
 
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
 
925cf9e
01812af
 
925cf9e
01812af
 
 
925cf9e
01812af
 
 
925cf9e
01812af
 
1ed5218
01812af
 
 
925cf9e
 
 
01812af
925cf9e
01812af
925cf9e
01812af
5b34140
01812af
5b34140
925cf9e
5b34140
01812af
 
 
925cf9e
01812af
 
 
 
 
925cf9e
 
5b34140
01812af
5b34140
 
925cf9e
01812af
5b34140
925cf9e
5b34140
925cf9e
5b34140
 
 
 
925cf9e
01812af
5b34140
01812af
 
5b34140
 
925cf9e
5b34140
925cf9e
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import streamlit as st
import cv2 as cv
import numpy as np
import mediapipe as mp
import tempfile

# Initialize MediaPipe Pose
mp_pose = mp.solutions.pose
pose = mp_pose.Pose()

# Function to rotate an image around its center
def rotate_image(image, angle):
    h, w = image.shape[:2]
    center = (w // 2, h // 2)
    rot_matrix = cv.getRotationMatrix2D(center, angle, 1.0)
    rotated_image = cv.warpAffine(image, rot_matrix, (w, h), flags=cv.INTER_LINEAR)
    return rotated_image

# Function to overlay the image with rotation following arm direction
def overlay_image_alpha_rotated(img, img_overlay, pos, alpha_mask, angle):
    x, y = pos

    # Rotate the overlay image in the opposite direction
    img_overlay_rotated = rotate_image(img_overlay, -angle)  # Negate the angle
    alpha_mask_rotated = rotate_image(alpha_mask, -angle)  # Negate the angle

    # Calculate the new position after rotation
    h, w = img_overlay_rotated.shape[:2]
    new_pos = (x - w // 2, y - h // 2)

    # Image ranges
    y1, y2 = max(0, new_pos[1]), min(img.shape[0], new_pos[1] + h)
    x1, x2 = max(0, new_pos[0]), min(img.shape[1], new_pos[0] + w)

    # Overlay ranges
    y1o, y2o = max(0, -new_pos[1]), min(h, img.shape[0] - new_pos[1])
    x1o, x2o = max(0, -new_pos[0]), min(w, img.shape[1] - new_pos[0])

    # Exit if nothing to overlay
    if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
        return

    # Blend overlay within the determined ranges
    img_crop = img[y1:y2, x1:x2]
    img_overlay_crop = img_overlay_rotated[y1o:y2o, x1o:x2o]
    alpha = alpha_mask_rotated[y1o:y2o, x1o:x2o, np.newaxis]
    alpha_inv = 1.0 - alpha

    img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop

# Function to handle JPG images without alpha channel
def add_alpha_channel(image):
    b_channel, g_channel, r_channel = cv.split(image)
    alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255  # Creating a fully opaque alpha channel
    return cv.merge((b_channel, g_channel, r_channel, alpha_channel))

# Function to detect pose and overlay image following arm direction
def poseDetector(frame, overlay_img):
    # Convert the image to RGB as MediaPipe expects RGB images
    frame_rgb = cv.cvtColor(frame, cv.COLOR_BGR2RGB)

    # Process the image and get the pose landmarks
    results = pose.process(frame_rgb)

    if results.pose_landmarks:
        # Extract relevant landmarks
        right_wrist = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST]
        left_wrist = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST]
        right_elbow = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW]
        left_elbow = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ELBOW]

        # Convert landmark positions to pixel coordinates
        h, w, _ = frame.shape
        right_wrist_coord = (int(right_wrist.x * w), int(right_wrist.y * h))
        left_wrist_coord = (int(left_wrist.x * w), int(left_wrist.y * h))
        right_elbow_coord = (int(right_elbow.x * w), int(right_elbow.y * h))
        left_elbow_coord = (int(left_elbow.x * w), int(left_elbow.y * h))

        # Calculate the length of the hand region (between wrist and elbow)
        right_hand_length = int(np.sqrt((right_wrist_coord[0] - right_elbow_coord[0])**2 + (right_wrist_coord[1] - right_elbow_coord[1])**2) * 0.5)
        left_hand_length = int(np.sqrt((left_wrist_coord[0] - left_elbow_coord[0])**2 + (left_wrist_coord[1] - left_elbow_coord[1])**2) * 0.5)

        # Calculate the angle of the hand for rotation
        right_angle = np.degrees(np.arctan2(right_wrist_coord[1] - right_elbow_coord[1], right_wrist_coord[0] - right_elbow_coord[0]))
        left_angle = np.degrees(np.arctan2(left_wrist_coord[1] - left_elbow_coord[1], left_wrist_coord[0] - left_elbow_coord[0]))

        # Ensure the hand length is positive before resizing
        if right_hand_length > 0:
            # Resize overlay image to fit the hand length
            right_overlay_resized = cv.resize(overlay_img, (right_hand_length, right_hand_length))

            # Adjust position to overlay the image at the wrist
            right_position = right_wrist_coord

            # Overlay the image on the right hand with rotation following the arm direction
            alpha_mask = right_overlay_resized[:, :, 3] / 255.0
            overlay_image_alpha_rotated(frame, right_overlay_resized[:, :, :3], right_position, alpha_mask, right_angle)

        if left_hand_length > 0:
            # Resize overlay image to fit the hand length
            left_overlay_resized = cv.resize(overlay_img, (left_hand_length, left_hand_length))

            # Adjust position to overlay the image at the wrist
            left_position = left_wrist_coord

            # Overlay the image on the left hand with rotation following the arm direction
            alpha_mask = left_overlay_resized[:, :, 3] / 255.0
            overlay_image_alpha_rotated(frame, left_overlay_resized[:, :, :3], left_position, alpha_mask, left_angle)

    return frame

# Streamlit interface
def main():
    st.title("Webcam Pose Detection with Tattoo Overlay")

    # Sidebar for file upload
    st.sidebar.header("Upload Tattoo Image and Video")
    uploaded_tattoo_img = st.sidebar.file_uploader("Upload Tattoo Image", type=["png", "jpg", "jpeg"])
    uploaded_video = st.sidebar.file_uploader("Upload Video File", type=["mp4", "mov", "avi"])

    if uploaded_tattoo_img and uploaded_video:
        # Load the uploaded tattoo image
        file_bytes = np.frombuffer(uploaded_tattoo_img.read(), np.uint8)
        tattoo_img = cv.imdecode(file_bytes, cv.IMREAD_UNCHANGED)

        # If the image is in JPG format (no alpha channel), add an alpha channel
        if tattoo_img.shape[2] == 3:  # Check if image has only 3 channels (BGR)
            tattoo_img = add_alpha_channel(tattoo_img)

        # Checkbox to start the processing
        run = st.checkbox('Run')

        if run:
            # Temporary file to store the uploaded video
            tfile = tempfile.NamedTemporaryFile(delete=False)
            tfile.write(uploaded_video.read())

            # Video capture
            cap = cv.VideoCapture(tfile.name)

            stframe = st.empty()

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

                # Perform pose detection and overlay
                frame = poseDetector(frame, tattoo_img)

                # Convert the frame to RGB for display in Streamlit
                frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
                stframe.image(frame, channels="RGB")

            cap.release()

if __name__ == "__main__":
    main()