top of page

ตรวจจับมือผ่าน webcam 10 บรรทัด Python Hand Tracking

Updated: Jun 3, 2021

ติดตามตำแหน่ง ท่าทางของมือ แบบ 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 และชวนเพื่อนมาเรียน จะช่วยเราได้มากอย่างที่คุณคิดไม่ถึงเลย :)

6,894 views1 comment
bottom of page