Spaces:
Runtime error
Runtime error
new version by using mediapipe
Browse files- app.py +93 -66
- graph_opt.pb +0 -3
app.py
CHANGED
@@ -1,117 +1,141 @@
|
|
1 |
import streamlit as st
|
2 |
import cv2 as cv
|
3 |
import numpy as np
|
|
|
4 |
import tempfile
|
5 |
|
6 |
-
#
|
7 |
-
|
|
|
8 |
|
9 |
-
#
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
"LEye": 15, "REar": 16, "LEar": 17, "Background": 18}
|
18 |
-
|
19 |
-
# Adjust POSE_PAIRS to include only wrist to elbow parts
|
20 |
-
POSE_PAIRS = [["RElbow", "RWrist"], ["LElbow", "LWrist"]]
|
21 |
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
inHeight = height
|
26 |
-
thr = 0.2
|
27 |
|
28 |
-
|
29 |
-
|
|
|
30 |
|
31 |
-
|
32 |
-
|
|
|
33 |
|
34 |
-
|
35 |
-
|
|
|
36 |
|
|
|
37 |
if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
|
38 |
return
|
39 |
|
|
|
40 |
img_crop = img[y1:y2, x1:x2]
|
41 |
-
img_overlay_crop =
|
42 |
-
alpha =
|
43 |
alpha_inv = 1.0 - alpha
|
44 |
|
45 |
img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
out = net.forward()
|
53 |
-
out = out[:, :19, :, :]
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
heatMap = out[0, i, :, :]
|
60 |
|
61 |
-
|
62 |
-
|
63 |
-
y = (frameHeight * point[1]) / out.shape[2]
|
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 |
-
t, _ = net.getPerfProfile()
|
97 |
return frame
|
98 |
|
|
|
99 |
def main():
|
100 |
-
st.title("
|
101 |
|
|
|
102 |
st.sidebar.header("Upload Tattoo Image and Video")
|
103 |
-
uploaded_tattoo_img = st.sidebar.file_uploader("Upload Tattoo Image", type=["png"])
|
104 |
uploaded_video = st.sidebar.file_uploader("Upload Video File", type=["mp4", "mov", "avi"])
|
105 |
|
106 |
if uploaded_tattoo_img and uploaded_video:
|
107 |
-
|
|
|
|
|
108 |
|
|
|
|
|
|
|
|
|
|
|
109 |
run = st.checkbox('Run')
|
110 |
|
111 |
if run:
|
|
|
112 |
tfile = tempfile.NamedTemporaryFile(delete=False)
|
113 |
tfile.write(uploaded_video.read())
|
114 |
|
|
|
115 |
cap = cv.VideoCapture(tfile.name)
|
116 |
|
117 |
stframe = st.empty()
|
@@ -121,7 +145,10 @@ def main():
|
|
121 |
if not ret:
|
122 |
break
|
123 |
|
|
|
124 |
frame = poseDetector(frame, tattoo_img)
|
|
|
|
|
125 |
frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
|
126 |
stframe.image(frame, channels="RGB")
|
127 |
|
|
|
1 |
import streamlit as st
|
2 |
import cv2 as cv
|
3 |
import numpy as np
|
4 |
+
import mediapipe as mp
|
5 |
import tempfile
|
6 |
|
7 |
+
# Initialize MediaPipe Pose
|
8 |
+
mp_pose = mp.solutions.pose
|
9 |
+
pose = mp_pose.Pose()
|
10 |
|
11 |
+
# Function to rotate an image around its center
|
12 |
+
def rotate_image(image, angle):
|
13 |
+
h, w = image.shape[:2]
|
14 |
+
center = (w // 2, h // 2)
|
15 |
+
rot_matrix = cv.getRotationMatrix2D(center, angle, 1.0)
|
16 |
+
rotated_image = cv.warpAffine(image, rot_matrix, (w, h), flags=cv.INTER_LINEAR)
|
17 |
+
return rotated_image
|
18 |
|
19 |
+
# Function to overlay the image with rotation following arm direction
|
20 |
+
def overlay_image_alpha_rotated(img, img_overlay, pos, alpha_mask, angle):
|
21 |
+
x, y = pos
|
|
|
|
|
|
|
|
|
22 |
|
23 |
+
# Rotate the overlay image in the opposite direction
|
24 |
+
img_overlay_rotated = rotate_image(img_overlay, -angle) # Negate the angle
|
25 |
+
alpha_mask_rotated = rotate_image(alpha_mask, -angle) # Negate the angle
|
|
|
|
|
26 |
|
27 |
+
# Calculate the new position after rotation
|
28 |
+
h, w = img_overlay_rotated.shape[:2]
|
29 |
+
new_pos = (x - w // 2, y - h // 2)
|
30 |
|
31 |
+
# Image ranges
|
32 |
+
y1, y2 = max(0, new_pos[1]), min(img.shape[0], new_pos[1] + h)
|
33 |
+
x1, x2 = max(0, new_pos[0]), min(img.shape[1], new_pos[0] + w)
|
34 |
|
35 |
+
# Overlay ranges
|
36 |
+
y1o, y2o = max(0, -new_pos[1]), min(h, img.shape[0] - new_pos[1])
|
37 |
+
x1o, x2o = max(0, -new_pos[0]), min(w, img.shape[1] - new_pos[0])
|
38 |
|
39 |
+
# Exit if nothing to overlay
|
40 |
if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
|
41 |
return
|
42 |
|
43 |
+
# Blend overlay within the determined ranges
|
44 |
img_crop = img[y1:y2, x1:x2]
|
45 |
+
img_overlay_crop = img_overlay_rotated[y1o:y2o, x1o:x2o]
|
46 |
+
alpha = alpha_mask_rotated[y1o:y2o, x1o:x2o, np.newaxis]
|
47 |
alpha_inv = 1.0 - alpha
|
48 |
|
49 |
img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop
|
50 |
|
51 |
+
# Function to handle JPG images without alpha channel
|
52 |
+
def add_alpha_channel(image):
|
53 |
+
b_channel, g_channel, r_channel = cv.split(image)
|
54 |
+
alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255 # Creating a fully opaque alpha channel
|
55 |
+
return cv.merge((b_channel, g_channel, r_channel, alpha_channel))
|
|
|
|
|
56 |
|
57 |
+
# Function to detect pose and overlay image following arm direction
|
58 |
+
def poseDetector(frame, overlay_img):
|
59 |
+
# Convert the image to RGB as MediaPipe expects RGB images
|
60 |
+
frame_rgb = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
|
|
|
61 |
|
62 |
+
# Process the image and get the pose landmarks
|
63 |
+
results = pose.process(frame_rgb)
|
|
|
64 |
|
65 |
+
if results.pose_landmarks:
|
66 |
+
# Extract relevant landmarks
|
67 |
+
right_wrist = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST]
|
68 |
+
left_wrist = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST]
|
69 |
+
right_elbow = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW]
|
70 |
+
left_elbow = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ELBOW]
|
71 |
|
72 |
+
# Convert landmark positions to pixel coordinates
|
73 |
+
h, w, _ = frame.shape
|
74 |
+
right_wrist_coord = (int(right_wrist.x * w), int(right_wrist.y * h))
|
75 |
+
left_wrist_coord = (int(left_wrist.x * w), int(left_wrist.y * h))
|
76 |
+
right_elbow_coord = (int(right_elbow.x * w), int(right_elbow.y * h))
|
77 |
+
left_elbow_coord = (int(left_elbow.x * w), int(left_elbow.y * h))
|
78 |
|
79 |
+
# Calculate the length of the hand region (between wrist and elbow)
|
80 |
+
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)
|
81 |
+
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)
|
82 |
|
83 |
+
# Calculate the angle of the hand for rotation
|
84 |
+
right_angle = np.degrees(np.arctan2(right_wrist_coord[1] - right_elbow_coord[1], right_wrist_coord[0] - right_elbow_coord[0]))
|
85 |
+
left_angle = np.degrees(np.arctan2(left_wrist_coord[1] - left_elbow_coord[1], left_wrist_coord[0] - left_elbow_coord[0]))
|
86 |
|
87 |
+
# Ensure the hand length is positive before resizing
|
88 |
+
if right_hand_length > 0:
|
89 |
+
# Resize overlay image to fit the hand length
|
90 |
+
right_overlay_resized = cv.resize(overlay_img, (right_hand_length, right_hand_length))
|
91 |
|
92 |
+
# Adjust position to overlay the image at the wrist
|
93 |
+
right_position = right_wrist_coord
|
94 |
|
95 |
+
# Overlay the image on the right hand with rotation following the arm direction
|
96 |
+
alpha_mask = right_overlay_resized[:, :, 3] / 255.0
|
97 |
+
overlay_image_alpha_rotated(frame, right_overlay_resized[:, :, :3], right_position, alpha_mask, right_angle)
|
98 |
|
99 |
+
if left_hand_length > 0:
|
100 |
+
# Resize overlay image to fit the hand length
|
101 |
+
left_overlay_resized = cv.resize(overlay_img, (left_hand_length, left_hand_length))
|
102 |
|
103 |
+
# Adjust position to overlay the image at the wrist
|
104 |
+
left_position = left_wrist_coord
|
105 |
|
106 |
+
# Overlay the image on the left hand with rotation following the arm direction
|
107 |
+
alpha_mask = left_overlay_resized[:, :, 3] / 255.0
|
108 |
+
overlay_image_alpha_rotated(frame, left_overlay_resized[:, :, :3], left_position, alpha_mask, left_angle)
|
109 |
|
|
|
110 |
return frame
|
111 |
|
112 |
+
# Streamlit interface
|
113 |
def main():
|
114 |
+
st.title("Webcam Pose Detection with Tattoo Overlay")
|
115 |
|
116 |
+
# Sidebar for file upload
|
117 |
st.sidebar.header("Upload Tattoo Image and Video")
|
118 |
+
uploaded_tattoo_img = st.sidebar.file_uploader("Upload Tattoo Image", type=["png", "jpg", "jpeg"])
|
119 |
uploaded_video = st.sidebar.file_uploader("Upload Video File", type=["mp4", "mov", "avi"])
|
120 |
|
121 |
if uploaded_tattoo_img and uploaded_video:
|
122 |
+
# Load the uploaded tattoo image
|
123 |
+
file_bytes = np.frombuffer(uploaded_tattoo_img.read(), np.uint8)
|
124 |
+
tattoo_img = cv.imdecode(file_bytes, cv.IMREAD_UNCHANGED)
|
125 |
|
126 |
+
# If the image is in JPG format (no alpha channel), add an alpha channel
|
127 |
+
if tattoo_img.shape[2] == 3: # Check if image has only 3 channels (BGR)
|
128 |
+
tattoo_img = add_alpha_channel(tattoo_img)
|
129 |
+
|
130 |
+
# Checkbox to start the processing
|
131 |
run = st.checkbox('Run')
|
132 |
|
133 |
if run:
|
134 |
+
# Temporary file to store the uploaded video
|
135 |
tfile = tempfile.NamedTemporaryFile(delete=False)
|
136 |
tfile.write(uploaded_video.read())
|
137 |
|
138 |
+
# Video capture
|
139 |
cap = cv.VideoCapture(tfile.name)
|
140 |
|
141 |
stframe = st.empty()
|
|
|
145 |
if not ret:
|
146 |
break
|
147 |
|
148 |
+
# Perform pose detection and overlay
|
149 |
frame = poseDetector(frame, tattoo_img)
|
150 |
+
|
151 |
+
# Convert the frame to RGB for display in Streamlit
|
152 |
frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
|
153 |
stframe.image(frame, channels="RGB")
|
154 |
|
graph_opt.pb
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:51f84ea82b3d0143dd4cf362e018c125e832c64771908ee8766fbd8b0328008d
|
3 |
-
size 7804434
|
|
|
|
|
|
|
|