ติดตามตำแหน่ง ท่าทางของมือ แบบ real-time 10 บรรทัดจบ กับ MediaPipe จาก Google เรียนกับ UltimatePython
เราตั้งใจทำทุกบทเรียนแบบ 300%
ถ้าชอบช่วยกด subscribe เป็นกำลังใจให้เราได้
Please consider subscribing to Ultimate Python
Hand Tracking
เป็นการประยุกต์ใช้ computer vision, object detection ในหลายตำแหน่งส่วนสำคัญต่างๆ ของมือ เพื่อตรวจจับ และติดตามตำแหน่งรวมไปถึงท่าทางของมืออีกด้วย ซึ่งเป็นลักษณะเดียวกันกับการทำงานของ Face Mesh หรือโปรแกรมที่ใช้ตรวจจับ ติดตามใบหน้า
การประยุกต์ใช้
Hand Tracking ใช้ติดตามตำแหน่ง และทำความเข้าใจท่าทาง ซึ่งสามารถประยุกต์ใช้สร้างโปรแกรมที่รับ user input ด้วยการทำท่าทางของมือได้ เช่นการปัดมือ, การทำสัญลักษณ์ ok, การกดไลค์, กด dislike เพื่อเชื่อมกับคำสั่งต่างๆ ต่อไปได้
การแสดงการใช้งาน Hand Tracking เชื่อมต่อกับ VR โดย Oculus บริษัทลูกของ Facebook
ทางเชื่อมใหม่ระหว่าง คน-หุ่นยนต์
ในปัจจุับนเราใช้จากปัจจุบันเราใช้ เมาส์ คีย์บอร์ด หรือทัชสกรีน ในการส่งคำสั่งให้กับคอมพิวเตอร์ เทคโนโลยีนี้เข้ามาช่วยให้เราสามารถทำท่าทางเพื่อส่งคำสั่งแทนได้
VR, AR
Virtual Reality พาเราเข้าในไปในโลกของวิดีโอ 3 มิติ และ Augmented Reality ที่ผสมผสานโลกความจริง และโลกเสมือนเข้าด้วยกัน ด้วยการประยุคใช้เทคโนโลยีนี้ การเชื่อมต่อจะยิ่งสมจริงยิ่งขึ้น
การดูแล
ทั้งในเชิงความปลอดภัย หรือในเชิงการพยาบาล ที่อาศัยการใช้คนมานั่งเฝ้าดู สามารถใช้เทคโนโลยีนี้เข้าใจพฤติกรรมของคนได้ดีขึ้น และสร้างเป็นระบบอัตโนมัติขึ้นมาได้
MediaPipe Hands
การสร้าง Hand Tracking มีความท้าทายในการฝึกให้โปรแกรมสามารถตรวจจับมือในขนาดต่างกัน คุณภาพของภาพต่างกัน และอีกหลายปัจจัย ทำให้การสร้างโปรแกรมตรวจจับเองใช้แรงในการสร้างอย่างมาก
แต่วันนี้เราจะเรียนใช้ MediaPipe ซึ่งเป็น Machine Learning Solutions หรือ โปรแกรมสำเร็จรูปจาก Google ที่สามารถใช้ทำ Hand Tracking ได้อย่างแม่นยำ และรวดเร็วขนาดว่าติดตาม real-time ได้เลย เนื่องจาก MediaPipe มีหลาย solutions ให้เลือกใช้ตั้งแต่การตรวจจับท่าทาง ใบหน้า วันนี้เราจะใช้ MediaPipe Hands ที่ใช้ตรวจจับมือ
เรียนรู้เพิ่มเติม / แหล่งอ้างอิงเนื้อหา ภาพ และวิดีโอ https://google.github.io/mediapipe/solutions/hands.html
การทำงานเบื้องต้น
Hand Tracking เริ่มต้นจากการตรวจจับฝ่ามือด้วย Palm Detection Model และจึงระบุตำแหน่งสำคัญของมือจำนวน 21 จุด ผ่านโปรแกรมที่เรียกว่า Hand Landmark Model ที่จำลองท่าทางของมือจากการตรวจจับภาพที่รับเข้ามา
21 จุด Landmarks
สามารถใช้ตรวจจับท่าทางของมือที่ซับซ้อนได้
วันนี้เราจะมาเขียนโปรแกรมที่รับวิดีโอจาก webcam เพื่อตรวจจับท่าทางของมือของเราแบบ Real-Time พร้อมดูข้อมูล และแสดงผลข้อมูลที่เราได้รับจากโปรแกรมนี้
ทุกคนจะได้โปรแกรมแบบนี้ไปใช้งานกัน!
ดาวน์โหลดเครื่องมือ
mediapipe เครื่องมือทำความเข้าใจมือ จาก google
In [ ]:
!pip install mediapipe
opencv library ที่ใช้ทำงานร่วมกับภาพ-vdo
In [ ]:
!pip install opencv-python
นำเข้าเครื่องมือ
เราจะนำ opencv มาใช้โดย opencv จะใช้ชื่อว่า cv2 และนำ mediapipe มาใช้ในชื่อ mp
In [1]:
import cv2
import mediapipe as mp
ข้อมูลจากกล้อง
ระบุกล้อง
ระบุ webcam ที่ต้องการโดยคำสั่ง cv2.VideoCapture() และระบุลำดับกล้องที่ต้องการเป็น Argument โดยกล้องที่เชื่อมต่อกับคอมพิวเตอร์จะมีการบันทึกลำดับตามการเชื่อมต่อ สำหรับการใช้กล้องหลักให้ใส่ 0 หากต้องการใช้กล้องอื่นให้ใช้ 1 หรืออื่นๆ โดยจะได้ object ประเภท VideoCapture
In [2]:
webcam = cv2.VideoCapture(0)
webcam
Out[2]:
<VideoCapture 000002A9D7977790>
รับข้อมูล
ใช้คำสั่ง .read() กับ object ที่ได้จากการระบุกล้อง จะได้ข้อมูลมาเป็น tuple ที่ระบุ (status การดึงข้อมูลเป็น boolean True เมื่อดึงข้อมูลสำเร็จ หรือ False เมื่อล้มเหลว, ข้อมูล pixel ของรูปภาพใน object array เป็นระบบสี BGR)
In [5]:
image = webcam.read()
image
Out[5]:
(True, array([[[ 92, 80, 92],
[ 94, 82, 94],
[ 97, 86, 96],
...,
[ 56, 46, 59],
[ 55, 48, 59],
[ 53, 48, 59]],
...
[[ 30, 24, 39],
[ 30, 24, 39],
[ 29, 22, 37],
...,
[164, 184, 121],
[163, 184, 121],
[163, 185, 123]]], dtype=uint8))
การแสดงผลวิดีโอ
จะใช้การรับข้อมูลจาก webcam อย่างต่อเนื่องโดยใช้ while loop ทำให้ภาพที่ได้มาต่อกันเป็นวิดีโอ โดยผ่านคำสั่ง cv2.imshow("Image",img) และตามด้วยคำสั่ง cv2.waitKey(1) เพื่อกันการ crash
In [6]:
while True:
success, image = webcam.read()
cv2.imshow("Image",image)
cv2.waitKey(1)
ทันทีที่รันโค้ดนี้จะมีหน้าต่างเด้งขึ้นมา ให้ดูการแสดงผลได้จากหน้าต่างนั้น
เมื่อใช้เสร็จให้กดปุ่ม STOP
ที่เป็นรูป สี่เหลี่ยม ด้านบนแถบเครื่องมือเพื่อหยุดการทำงาน
การตรวจจับมือ
ข้อมูลมือ และคำสั่งใช้ตรวจจับมือ
ใช้คำสั่ง mp.solutions.hands.Hands() เพื่อเตรียมข้อมูลสำหรับการตรวจจับมือ
In [3]:
mp_hands = mp.solutions.hands
In [4]:
hands = mp_hands.Hands()
การแปลงระบบสี
สำหรับข้อมูลจาก webcam จะมีระบบสี BGR แต่การโปรแกรมตรวจจับมือของเราวันนี้จะทำงานกับระบบสี RGB ดังนั้นจะต้องมีการแปลงค่าสีด้วยคำสั่ง cv2.cvtColor() ที่รับค่ารูปจากระบบสี BGR หรือรูปจาก webcam และ ตัวแปลงค่าสีจาก BGR ไป RGB ที่อยู่ในคำสั่ง cv2.COLOR_BGR2RGB
In [9]:
image_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
image_rgb
Out[9]:
array([[[ 54, 72, 104],
[ 56, 72, 105],
[ 61, 73, 104],
...,
[ 22, 29, 63],
[ 22, 35, 71],
[ 23, 41, 79]],
...
[[ 23, 5, 12],
[ 25, 7, 14],
[ 24, 8, 16],
...,
[141, 169, 165],
[139, 164, 160],
[140, 162, 158]]], dtype=uint8)
การตรวจจับมือในรูป
จะใช้คำสั่ง .process() ที่รับค่ารูปที่ใช้ค่าสี RGB ซึ่งจะได้ object ที่เก็บค่าต่างๆ ของผลลัพธ์มา
In [10]:
results = hands.process(image_rgb)
results
Out[10]:
mediapipe.python.solution_base.SolutionOutputs
การหาตำแหน่งมือ
จะใช้การระบุตำแหน่งของ landmarks หรือจุดสำคัญต่างๆ ของมือที่ใช้ระบุมือในรูป หรือหากหามือไม่เจอจะได้ผลลัพธ์เป็น None ซึ่งค่าที่ได้ จะเป็น % ของขนาดภาพ สำหรับ x จะเทียบกับความกว้าง และ y จะเทียบกับความสูง เช่นภาพขนาด 1080 x 1080 หากได้ x: 0.50 และ y 0.10 กำลังหมายถึงพิกัดที่ x = 0.5 x 1080 และ y = 0.10 x 1080 หรือ (540, 108) ในพิกัด (x,y) ซึ่งจุด 0,0 เริ่มต้นที่มุมซ้ายบน และมีค่า x,y ที่มากที่สุดที่มุมขวาล่าง
In [11]:
print(results.multi_hand_landmarks)
[landmark {
x: 0.5232510566711426
y: 0.8364323377609253
z: 4.5578068238683045e-05
}
landmark {
x: 0.395709365606308
y: 0.8325181603431702
z: -0.12245161831378937
}
landmark {
x: 0.2948293089866638
y: 0.7223868370056152
z: -0.20187564194202423
}
...
landmark {
x: 0.7169504165649414
y: 0.4236549139022827
z: -0.32376644015312195
}
landmark {
x: 0.7616433501243591
y: 0.34517809748649597
z: -0.37748008966445923
}
landmark {
x: 0.8017270565032959
y: 0.2544715106487274
z: -0.4199370741844177 } ]
ตรวจจับมือในวิดีโอ
จะใช้การรับข้อมูลจาก webcam อย่างต่อเนื่องโดยใช้ while loop ทำให้ภาพที่ได้มาต่อกันเป็นวิดีโอ โดยผ่านคำสั่ง cv2.imshow("Image",img) และตามด้วยคำสั่ง cv2.waitKey(1) เพื่อกันการ crash
ซึ่งแท้จริงแล้วการทำงานกับวิดีโอ ก็คือ การทำงานกับรูปที่ส่งคำสั่งมาประมวลผลอย่างต่อเนื่องติดต่อกันหลายๆครั้งต่อ 1วินาที ซึ่งหากจังหวะใดที่จับภาพมาแล้วเจอมี โค้ดด้านล่างจะแจ้งตำแหน่ง ออกมา หากใน frame ใด ไม่เจอจะเห็นว่าได้ None มา
In [5]:
while True:
success, image = webcam.read()
image_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
print(results.multi_hand_landmarks)
cv2.imshow("Webcam",image)
cv2.waitKey(1)
None
None
landmark {
x: 0.5652047395706177
y: 0.5398375391960144
z: -0.03321327269077301
}
landmark {
x: 0.5684786438941956
y: 0.5497254729270935
z: -0.032162487506866455
}
landmark {
x: 0.5556241273880005
y: 0.5655379891395569
z: -0.03482688590884209
}
...
landmark {
x: 0.5557577610015869
y: 0.5544397234916687
z: -0.03805286064743996
}
landmark {
x: 0.5603874921798706
y: 0.5619086027145386
z: -0.03440577909350395
}
landmark {
x: 0.5648931860923767
y: 0.5706174969673157
z: -0.031657055020332336
}
การเปลี่ยนแปลงของการแสดงผลเมื่อตรวจจับมือเจอแบบ real-time
แสดงผลลัพธ์
เมื่อเราได้ตำแหน่งของ Landmarks มาแล้ว เราจะนำข้อมูลนี้มาแสดงผลตำแหน่งด้วยจุดสีแดงบน Landmark และเส้นเชื่อมต่อจุดสีเขียวเพื่อแสดงรูปมือ
ทันทีที่รันโค้ดนี้จะมีหน้าต่างเด้งขึ้นมา ให้ดูการแสดงผลได้จากหน้าต่างนั้น
เตรียมคำสั่ง
ในการวาดผลลัพธ์ลงไปจะใช้คำสั่งจาก mp.solutions.drawing_utils และจะเรียกใช้คำสั่งจากคำสั่งนี้อีกที
In [13]:
mp_draw = mp.solutions.drawing_utils
mp_draw
Out[13]:
<module 'mediapipe.python.solutions.drawing_utils' from 'C:\\Users\\acer\\anaconda3\\lib\\site-packages\\mediapipe\\python\\solutions\\drawing_utils.py'>
เขียนผลลัพธ์ลงบนวิดีโอ
ตำแหน่งของ landmark
ตำแหน่ง landmark ที่ใช้ระบุรายละเอียดของมือจะถูกเก็บไว้ในตัวแปร results.multi_hand_landmarks ซึ่งประกอบไปด้วยหลาย landmark ดังนั้นเพื่อนำข้อมูลแต่ละ landmark มาแสดงผลเป็นผลลัพธ์เราจะใช้ for loop เข้าไปดึงข้อมูลออกมา
เมื่อได้ตำแหน่ง landmark เราจะนำข้อมูล landmark มาวาดเป็นผลลัพธ์ด้วยคำสั่ง mp_draw.draw_landmarks() ที่รับค่า ข้อมูลที่รับมาจากวิดีโอเพื่อใช้วาดผลลัพธ์ลงไป และระบุ landmark เพื่อระบุจุดที่ต้องการวาดผลลัพธ์
และเราจะเขียนเงื่อนไขเช็คก่อนว่ามีการตรวจจับเจอมือ เพื่อกันการเกิด error
In [14]:
while True:
success, image = webcam.read()
image_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
print(results.multi_hand_landmarks)
if results.multi_hand_landmarks:
for landmark in results.multi_hand_landmarks:
mp_draw.draw_landmarks(image, landmark)
cv2.imshow("Image",image)
cv2.waitKey(1)
None
landmark {
x: 0.5652047395706177
y: 0.5398375391960144
z: -0.03321327269077301
}
landmark {
x: 0.5684786438941956
y: 0.5497254729270935
z: -0.032162487506866455
}
landmark {
x: 0.5556241273880005
y: 0.5655379891395569
z: -0.03482688590884209
}
...
landmark {
x: 0.5557577610015869
y: 0.5544397234916687
z: -0.03805286064743996
}
landmark {
x: 0.5603874921798706
y: 0.5619086027145386
z: -0.03440577909350395
}
landmark {
x: 0.5648931860923767
y: 0.5706174969673157
z: -0.031657055020332336
}
การแสดงผลตำแหน่ง Landmark ผ่าน webcam
ตำแหน่งของ เส้นเชื่อมระหว่าง landmark
เราจะใช้คำสั่ง mp_hand.HAND_CONNECTIONS เพื่อทำการดึงข้อมูลของเส้นที่เชื่อมแต่ละ landmark เอาไว้มาเขียนลงบนผลลัพธ์
In [15]:
while True:
success, image = webcam.read()
image_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
print(results.multi_hand_landmarks)
if results.multi_hand_landmarks:
for landmark in results.multi_hand_landmarks:
mp_draw.draw_landmarks(image, landmark, mp_hands.HAND_CONNECTIONS)
cv2.imshow("Image",image)
cv2.waitKey(1)
None
landmark {
x: 0.5652047395706177
y: 0.5398375391960144
z: -0.03321327269077301
}
landmark {
x: 0.5684786438941956
y: 0.5497254729270935
z: -0.032162487506866455
}
landmark {
x: 0.5556241273880005
y: 0.5655379891395569
z: -0.03482688590884209
}
...
landmark {
x: 0.5557577610015869
y: 0.5544397234916687
z: -0.03805286064743996
}
landmark {
x: 0.5603874921798706
y: 0.5619086027145386
z: -0.03440577909350395
}
landmark {
x: 0.5648931860923767
y: 0.5706174969673157
z: -0.031657055020332336
}
เท่านี้เราก็จะได้โปรแกรมในการ Track และแสดงผลท่าทางของมือมาเรียบร้อยใน 10 บรรทัด! :)
อยากรู้ไหมเราจะเอาข้อมูลนี้ และข้อมูลจากการตรวจจับใบหน้า ไปทำอะไร อย่างไร? ิติดตามได้ในเนื้อหาถัดไป
ถ้าชอบบทเรียนนี้ การกด subscribe และชวนเพื่อนมาเรียน จะช่วยเราได้มากอย่างที่คุณคิดไม่ถึงเลย :)
ถ้าmediapipeติดตั้งไม่ได้ควนทำยังไงครับ