iT邦幫忙

2021 iThome 鐵人賽

DAY 17
15
Software Development

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

[Day17] 不可以比中指! 用Python做一個AI有禮貌神器!

比中指是一個相當不禮貌的行為,但有時候太生氣還是會不小心比出來對吧?
既然無論如何都會比中指的話,那就把中指加上馬賽克吧! (這什麼結論)

開發環境

MediaPipe介紹

MediaPipe是Google Research開發的多媒體機器學習模型應用框架,可支援JavaScript、Python、C++等語言。可以直接使用API實現3D手部標誌追蹤、BlazeFace 人臉檢測、物體檢測等功能。
本篇以3D手部標誌追蹤實現偵測中指的功能,下圖為21個手部3D座標:
https://ithelp.ithome.com.tw/upload/images/20210916/20133286qPbqfwnsuG.png

程式碼

import cv2
import mediapipe as mp
import math

# 針對給定的某個矩形做馬賽克
def mosaic(img, left_up, right_down):
    new_img = img.copy()
    # size代表此馬賽克區塊中每塊小區域的邊長
    size = 10
    for i in range(left_up[1], right_down[1]-size-1, size):
        for j in range(left_up[0], right_down[0]-size-1, size):
            try:
                # 將此小區域中的每個像素都給定為最左上方的像素值
                new_img[i:i + size, j:j + size] = img[i, j, :]
            except:
                pass
    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[1]>40) and (angle_list[2]<40) and (angle_list[3]>40) and (angle_list[4]>40):
            gesture_str = "middle"
    return gesture_str

def detect():
    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:
                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 == "middle":
                        for node in range(9, 13):
                            center_x = int(keypoint_pos[node][0])
                            center_y = int(keypoint_pos[node][1])
                            frame = mosaic(frame, [center_x - 15 , center_y - 10], [center_x + 30, center_y + 50])
        cv2.imshow('MediaPipe Hands', frame)
        if cv2.waitKey(1) & 0xFF == 27:
            break
    cap.release()
if __name__ == '__main__':
    detect()

成果發表會

可以看到在只有比中指的時候才會打馬,因為我只寫一隻手,所以同時有兩個中指的話只會打馬一隻。
下圖是各種馬與不馬的情況:
https://ithelp.ithome.com.tw/upload/images/20210916/20133286drUqJZFtxy.png
從上圖發現,若同時有食指和中指,則中指不打馬。
若只有食指,則不打馬(不會誤認成中指)。
中指反過來,打馬(全面防護)。
中指倒過來,打馬(超全面防護)。

經過上面的例證,證明這個AI有禮貌神器 大! 成! 功!
雖然我不知道這個東西可以用在哪裡,視訊上課可能可以用?


上一篇
[Day16] 再也不用靠線上工具! 用Python把圖片轉成ASCII文字圖!
下一篇
[Day18] 刺激! 居家上班之老闆v.s.員工偷懶攻防戰!
系列文
奇怪的知識增加了!原來程式還可以這樣用?!30
1
咖咖拉
iT邦研究生 2 級 ‧ 2021-09-17 11:58:19

......./images/emoticon/emoticon04.gif

1
juck30808
iT邦新手 4 級 ‧ 2021-09-17 12:04:59

太神了= =

lulu_meat iT邦新手 4 級 ‧ 2021-09-17 15:37:47 檢舉

大家一起有禮貌~

1
copemoe
iT邦新手 4 級 ‧ 2021-09-17 12:05:53

感覺這功能可以延伸做很多事情 比如說偵測比愛心

lulu_meat iT邦新手 4 級 ‧ 2021-09-17 15:38:05 檢舉

這個我要想一下XD

1
dscwferp
iT邦高手 1 級 ‧ 2021-09-17 12:17:10

您下一篇可以做"手指比愛心就放大"
可以嗎?
給我老婆
並忘記初戀...

lulu_meat iT邦新手 4 級 ‧ 2021-09-17 15:38:45 檢舉

這個我要查一下XDD 還沒寫過放大的~

1
johnqq
iT邦新手 5 級 ‧ 2021-09-17 12:49:57

這個作者好厲害

1
Marvin
iT邦新手 5 級 ‧ 2021-09-17 12:56:55

神串留名~~

1
gior__ann
iT邦新手 4 級 ‧ 2021-09-17 13:28:19

千萬不能流傳出去XDD
視訊上課可能可以用 ===> 臉部直接打碼,老師點名可以找代打 XDD

1
我是低調
iT邦新手 5 級 ‧ 2021-09-17 15:03:03

好神... 好奇怎麼找到這些資源的?

lulu_meat iT邦新手 4 級 ‧ 2021-09-17 15:39:23 檢舉

之前我在YT上看到一個用手勢控制YT撥放的影片才知道有這個東西~

1
jiatool
iT邦新手 5 級 ‧ 2021-09-18 10:45:26

這哪是曬照片
只看到一直有人在對我比中指XD

之前有看過 MediaPipe 想玩玩看,但一直都還沒嘗試

lulu_meat iT邦新手 4 級 ‧ 2021-09-18 15:32:51 檢舉

我有打馬喔~

1
obarisk
iT邦新手 4 級 ‧ 2021-09-18 11:27:15

奇怪的知識增加了...

1
胡拔比
iT邦新手 5 級 ‧ 2021-09-18 21:43:55

這程式碼完全複雜就能執行了嗎?
謝謝

看更多先前的回應...收起先前的回應...
lulu_meat iT邦新手 4 級 ‧ 2021-09-18 22:11:31 檢舉

還要pip install mediapipe + pip install opencv喔

胡拔比 iT邦新手 5 級 ‧ 2021-09-18 23:06:51 檢舉

好奇怪~我安裝pip install mediapipe會出現錯誤訊息,如下:

ERROR: Could not find a version that satisfies the requirement mediapipe (from versions: none)
ERROR: No matching distribution found for mediapipe

胡拔比 iT邦新手 5 級 ‧ 2021-09-18 23:09:26 檢舉

我目前PYTHON使用3.6.8 32Bit與3.7.9 32Bit,請問如何安裝mediapipe,謝謝

lulu_meat iT邦新手 4 級 ‧ 2021-09-18 23:29:07 檢舉

剛剛查了一下發現需要64位元版本的Python 3.7+才能安裝 mediapipe喔~
https://ithelp.ithome.com.tw/upload/images/20210918/20133286uKNUrSPeh1.png

胡拔比 iT邦新手 5 級 ‧ 2021-09-18 23:34:47 檢舉

我的心碎了一地~

胡拔比 iT邦新手 5 級 ‧ 2021-09-18 23:41:43 檢舉

我試著裝64位元看看,感謝妳~

1
farfaraway
iT邦新手 5 級 ‧ 2021-09-20 11:18:33

不要浪費才能在奇怪的地方啊啊啊!/images/emoticon/emoticon04.gif

lulu_meat iT邦新手 4 級 ‧ 2021-09-22 22:51:33 檢舉

後天還有更浪費才能的應用喔 XD

1
json_liang
iT邦新手 4 級 ‧ 2021-09-21 14:25:36

這好猛!XD

1
Pulin
iT邦新手 5 級 ‧ 2021-09-22 14:08:17

浪費才能XDD

1
lagagain
iT邦新手 4 級 ‧ 2021-09-28 06:57:29

我相信第一天回覆說

主管剛剛還請我喝咖啡

應該是真的了/images/emoticon/emoticon37.gif


就算現在影像辨識有很多好用的library可以用,但要去了解並搞出來真的蠻強的。/images/emoticon/emoticon34.gif

我要留言

立即登入留言