iT邦幫忙

2021 iThome 鐵人賽

DAY 21
5
Software Development

奇怪的知識增加了!原來程式還可以這樣用?!系列 第 21

[Day21] 你問我愛你有多深? 用Python讓手指愛心超進化!

之前用MediaPipe做過偵測到中指就打馬賽克的主題,
下方有朋友留言提議能不能做「偵測到愛心就放大」的功能,
時隔兩天終於做出來啦!! 謝謝這位朋友的建議 (不然我真的快沒梗了)

開發環境

程式碼

import cv2
import mediapipe as mp
import math

def enlarge(img, left_up, right_down):
    new_img = img.copy()
    size_w = abs(left_up[0] - right_down[0])
    size_h = abs(left_up[1] - right_down[1])
    
    # 得到正方形區域的image
    img_rec = img[left_up[1]:right_down[1], left_up[0]:right_down[0]]
    try:
        # 將此區域放大
        img_rec = cv2.resize(img_rec, (size_w*2, size_h*2), interpolation=cv2.INTER_CUBIC)
    except:
        return None
    
    center = [(left_up[0] + right_down[0])//2, (left_up[1] + right_down[1])//2] # [x, y]
    
    # 將原圖中正方形區域取代為放大後的圖
    for i in range(center[1]-size_h, center[1]+size_h):
        for j in range(center[0]-size_w, center[0]+size_w):
            try:
                new_img[i][j] = img_rec[i - (center[1]-size_h)][j - (center[0]-size_w)]
            except:
                continue
    
    return new_img               

def vector_2d_angle(v1,v2): # 求出v1,v2兩條向量的夾角
    v1_x=v1[0]
    v1_y=v1[1]
    v2_x=v2[0]
    v2_y=v2[1]
    try:
        angle_= math.degrees(math.acos((v1_x*v2_x+v1_y*v2_y)/(((v1_x**2+v1_y**2)**0.5)*((v2_x**2+v2_y**2)**0.5))))
    except:
        angle_ = 100000.
    return angle_

def hand_angle(hand_):
    angle_list = []
    #---------------------------- thumb 大拇指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[2][0])),(int(hand_[0][1])-int(hand_[2][1]))),
        ((int(hand_[3][0])- int(hand_[4][0])),(int(hand_[3][1])- int(hand_[4][1])))
        )
    angle_list.append(angle_)
    #---------------------------- index 食指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])-int(hand_[6][0])),(int(hand_[0][1])- int(hand_[6][1]))),
        ((int(hand_[7][0])- int(hand_[8][0])),(int(hand_[7][1])- int(hand_[8][1])))
        )
    angle_list.append(angle_)
    #---------------------------- middle 中指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[10][0])),(int(hand_[0][1])- int(hand_[10][1]))),
        ((int(hand_[11][0])- int(hand_[12][0])),(int(hand_[11][1])- int(hand_[12][1])))
        )
    angle_list.append(angle_)
    #---------------------------- ring 無名指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[14][0])),(int(hand_[0][1])- int(hand_[14][1]))),
        ((int(hand_[15][0])- int(hand_[16][0])),(int(hand_[15][1])- int(hand_[16][1])))
        )
    angle_list.append(angle_)
    #---------------------------- pink 小拇指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[18][0])),(int(hand_[0][1])- int(hand_[18][1]))),
        ((int(hand_[19][0])- int(hand_[20][0])),(int(hand_[19][1])- int(hand_[20][1])))
        )
    angle_list.append(angle_)
    return angle_list

def hand_gesture(angle_list): # 偵測手指是否比愛心
    gesture_str = None
    if 100000. not in angle_list:
        if (angle_list[0]<60) and (angle_list[1]<100) and (angle_list[2]>40) and (angle_list[3]>40) and (angle_list[4]>40):
            gesture_str = "love"
    return gesture_str

def detect():
    mp_drawing = mp.solutions.drawing_utils
    mp_hands = mp.solutions.hands
    hands = mp_hands.Hands(
            static_image_mode=False,
            max_num_hands=1,
            min_detection_confidence=0.75,
            min_tracking_confidence=0.75)
    # 開啟視訊鏡頭讀取器
    cap = cv2.VideoCapture(0)
    while True:
        # 偵測影像中的手部
        _, frame = cap.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frame= cv2.flip(frame,1)
        results = hands.process(frame)
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)

        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
                keypoint_pos = []
                for i in range(21):
                    x = hand_landmarks.landmark[i].x*frame.shape[1]
                    y = hand_landmarks.landmark[i].y*frame.shape[0]
                    keypoint_pos.append((x,y))
                if keypoint_pos:
                    # 得到各手指的夾角資訊
                    angle_list = hand_angle(keypoint_pos)
                    # 根據角度判斷此手勢是否為愛心
                    gesture_str = hand_gesture(angle_list)
                    if gesture_str == "love":
                        left_up = [int(keypoint_pos[8][0]) - 20 , int(keypoint_pos[8][1]) - 10]
                        right_down = [int(keypoint_pos[3][0]) + 20 , int(keypoint_pos[3][1]) + 10]
                        frame_processed = enlarge(frame, left_up, right_down)
                        if frame_processed is not None:
                            frame = frame_processed
        cv2.imshow('MediaPipe Hands', frame)
        if cv2.waitKey(1) & 0xFF == 27:
            break
    cap.release()

if __name__ == '__main__':
    detect()

成果發表會

不知道是不是我拇指太短還是怎樣,它有時候會偵測不到我在比愛心(扶額
如果有人知道更準確的判斷方法可以跟我說一下嗎 感謝><


上一篇
[Day20] Google Map 評論爬蟲詳細教學
下一篇
[Day22] 會不會覺得QR Code有點單調? 用Python做出動態QR Code吧!
系列文
奇怪的知識增加了!原來程式還可以這樣用?!30
1
彭偉鎧
iT邦新手 2 級 ‧ 2021-09-21 12:13:32

好像一放大手指上端會不見,這個要設定區塊大小,不然不完整的大心,變得有點沒誠意!哈~~

1
dscwferp
iT邦高手 1 級 ‧ 2021-09-21 13:32:03

太好了!
老婆我來了!

0
胡拔比
iT邦新手 5 級 ‧ 2021-09-27 08:41:54

真有趣~

我要留言

立即登入留言