比中指是一個相當不禮貌的行為,但有時候太生氣還是會不小心比出來對吧?
既然無論如何都會比中指的話,那就把中指加上馬賽克吧! (這什麼結論)
開發環境
MediaPipe介紹
MediaPipe是Google Research開發的多媒體機器學習模型應用框架,可支援JavaScript、Python、C++等語言。可以直接使用API實現3D手部標誌追蹤、BlazeFace 人臉檢測、物體檢測等功能。
本篇以3D手部標誌追蹤實現偵測中指的功能,下圖為21個手部3D座標:
程式碼
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()
成果發表會
可以看到在只有比中指的時候才會打馬,因為我只寫一隻手,所以同時有兩個中指的話只會打馬一隻。
下圖是各種馬與不馬的情況:
從上圖發現,若同時有食指和中指,則中指不打馬。
若只有食指,則不打馬(不會誤認成中指)。
中指反過來,打馬(全面防護)。
中指倒過來,打馬(超全面防護)。
經過上面的例證,證明這個AI有禮貌神器 大! 成! 功!
雖然我不知道這個東西可以用在哪裡,視訊上課可能可以用?
您下一篇可以做"手指比愛心就放大"
可以嗎?
給我老婆
並忘記初戀...
這個我要查一下XDD 還沒寫過放大的~
這哪是曬照片
只看到一直有人在對我比中指XD
之前有看過 MediaPipe 想玩玩看,但一直都還沒嘗試
我有打馬喔~
這程式碼完全複雜就能執行了嗎?
謝謝
還要pip install mediapipe + pip install opencv喔
好奇怪~我安裝pip install mediapipe會出現錯誤訊息,如下:
ERROR: Could not find a version that satisfies the requirement mediapipe (from versions: none)
ERROR: No matching distribution found for mediapipe
我目前PYTHON使用3.6.8 32Bit與3.7.9 32Bit,請問如何安裝mediapipe,謝謝
剛剛查了一下發現需要64位元版本的Python 3.7+才能安裝 mediapipe喔~
我的心碎了一地~
我試著裝64位元看看,感謝妳~
我相信第一天回覆說
主管剛剛還請我喝咖啡
應該是真的了
就算現在影像辨識有很多好用的library可以用,但要去了解並搞出來真的蠻強的。