kritsadaK's picture
new version by using mediapipe
01812af
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()