Skip to content

Commit 5220241

Browse files
committed
Chapter 1 finished
0 parents  commit 5220241

File tree

17 files changed

+459
-0
lines changed

17 files changed

+459
-0
lines changed

Chapter 1 Hand Tracking/Basics.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# cv2.__version__ = 4.10.0, mp.__version__ = 0.10.14
2+
3+
import cv2
4+
import mediapipe as mp
5+
import time
6+
7+
# 打开摄像头
8+
cap = cv2.VideoCapture(0) # 0是默认摄像头
9+
10+
# 初始化手部检测模块
11+
mpHands = mp.solutions.hands # 引用 MediaPipe 的手部解决方案模块
12+
hands = mpHands.Hands() # 创建一个 Hands 对象,用于检测和跟踪手部关键点
13+
mpDraw = mp.solutions.drawing_utils # 引用绘图工具,用于在图像上绘制检测到的手部关键点和连接线
14+
15+
# 初始化时间变量用于计算帧率
16+
pTime = 0 # 表示前一帧的时间
17+
cTime = 0 # 表示当前帧的时间
18+
# cTime - pTime 计算时间差,从而计算帧率。最后将 cTime 赋值给 pTime,以便在下一次循环时使用
19+
20+
while True:
21+
# 读取摄像头图像
22+
success, img = cap.read()
23+
# success:一个布尔值,表示是否成功读取帧
24+
# img:读取的图像帧,如果读取失败,这个值可能为空
25+
26+
# 水平翻转图像
27+
img = cv2.flip(img, 1)
28+
# 第 0 维表示垂直方向(高度),对应图像的行数,上下
29+
# 第 1 维表示水平方向(宽度),对应图像的列数,左右
30+
31+
# 将图像从 BGR 格式转换为 RGB 格式
32+
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
33+
# BGR 是图像在 OpenCV 中的默认颜色格式,代表蓝色(Blue)、绿色(Green)、红色(Red)。这种格式与通常使用的 RGB(红、绿、蓝)顺序相反。转换为 RGB 是因为许多图像处理库(如 MediaPipe)使用这种格式进行处理
34+
35+
# 处理图像以检测手部
36+
results = hands.process(imgRGB)
37+
38+
# 如果检测到手部
39+
if results.multi_hand_landmarks:
40+
# 遍历检测到的每只手
41+
for handLms in results.multi_hand_landmarks:
42+
# results.multi_hand_landmarks 会返回一个列表,其中包含检测到的每只手的关键点信息。如果检测到多只手,它会包含多个元素,每个元素代表一只手的所有关键点
43+
44+
# 遍历手部关键点
45+
for id, lm in enumerate(handLms.landmark):
46+
# enumerate 返回一个迭代器,每次迭代返回一个包含索引和值的元组
47+
# id 是手部关键点的索引,lm 是 landmark 的缩写,表示手部关键点的坐标信息
48+
49+
# 获取图像的尺寸
50+
h, w, c = (img.shape)
51+
# img.shape 返回一个包含图像维度的元组,具体包括:高度(行数)、宽度(列数)、通道数(如 RGB 图像的通道数为 3)
52+
53+
# 计算关键点在图像中的坐标
54+
cx, cy = int(lm.x * w), int(lm.y * h)
55+
# lm.x 和 lm.y 是关键点的归一化坐标,范围在 0 到 1 之间。通过乘以图像的宽度和高度,可以将它们转换为图像中的像素坐标
56+
57+
print(id, cx, cy)
58+
59+
# 在关键点处画一个圆圈
60+
cv2.circle(img, (cx, cy), 15, (255, 0, 255), -1)
61+
# img 表示要绘制图像的地方,(cx, cy) 圆心的坐标,15 圆的半径
62+
# (255, 0, 255) 圆的颜色(BGR格式),这里是紫色
63+
# 红色:(0, 0, 255)、绿色:(0, 255, 0)、蓝色:(255, 0, 0)、黄色:(0, 255, 255)、青色:(255, 255, 0)、品红:(255, 0, 255)、白色:(255, 255, 255)、黑色:(0, 0, 0)
64+
# cv2.FILLED 或 -1 填充圆的实心样式,也可以为具体的数字(值为边框厚度)
65+
66+
# 绘制手部关键点和连接线
67+
mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS)
68+
# img 要绘制的图像,handLms 手部关键点的坐标
69+
# mpHands.HAND_CONNECTIONS 定义手部关键点之间的连接关系,用于绘制骨架结构
70+
71+
# 计算帧率
72+
cTime = time.time()
73+
fps = 1 / (cTime - pTime)
74+
pTime = cTime
75+
76+
# 在图像上显示帧率
77+
cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3)
78+
# img 要绘制文本的地方,str(int(fps)) 要显示的文本内容,这里是帧率的整数部分
79+
# (10, 70) 文本的左下角坐标,cv2.FONT_HERSHEY_PLAIN 字体样式
80+
# 3 字体大小,(255, 0, 255) 文本颜色(紫色,BGR格式),3 文本的粗细
81+
# cv2.putText 不支持关键字传参,必须按照顺序提供参数
82+
83+
# 显示图像
84+
cv2.imshow("Image", img) # 在窗口中显示图像,窗口标题为“Image”
85+
cv2.waitKey(1) # 等待键盘事件,参数为 1 表示等待 1 毫秒
86+
# 它也允许图像窗口响应用户输入(如关闭窗口)
87+
88+
# 检测退出键
89+
if cv2.waitKey(1) & 0xFF == ord("q"): # ord('q') 获取字符 'q' 的 ASCII 值
90+
# cv2.waitKey(1) & 0xFF 用来读取键盘输入
91+
# cv2.waitKey(1) 返回的是一个 32 位整数,其中低 8 位是实际的键值,& 0xFF 是一个位运算,用于提取这 8 位
92+
# "低 8 位"指的是一个数值的二进制表示中最右边的 8 位。这些位表示数值的较小部分,与"高 8 位"(最左边的 8 位)相对,后者表示数值的较大部分。对于 32 位整数来说,低 8 位用于表示键盘输入的实际键值
93+
break
94+
95+
cap.release() # 释放摄像头资源
96+
cv2.destroyAllWindows() # 关闭所有 OpenCV 窗口
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# cv2.__version__ = 4.10.0, mp.__version__ = 0.10.14
2+
3+
import cv2
4+
import mediapipe as mp
5+
import time
6+
7+
8+
# 定义手部检测类
9+
class handDetector:
10+
def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):
11+
# 初始化参数
12+
self.mode = mode # 静态图像模式
13+
self.maxHands = maxHands # 最大检测手数
14+
self.detectionCon = detectionCon # 检测置信度
15+
self.trackCon = trackCon # 跟踪置信度
16+
self.mpHands = mp.solutions.hands # Mediapipe手部解决方案
17+
self.hands = self.mpHands.Hands(
18+
static_image_mode=self.mode,
19+
max_num_hands=self.maxHands,
20+
min_detection_confidence=self.detectionCon,
21+
min_tracking_confidence=self.trackCon,
22+
)
23+
self.mpDraw = mp.solutions.drawing_utils # 用于绘制手部连接
24+
25+
def findHands(self, img, draw=True):
26+
# 将图像从BGR转换为RGB
27+
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
28+
# 处理图像,检测手部
29+
self.results = self.hands.process(imgRGB)
30+
# 如果检测到手部
31+
if self.results.multi_hand_landmarks:
32+
# 遍历每个手部
33+
for handLms in self.results.multi_hand_landmarks:
34+
if draw:
35+
# 绘制手部连接
36+
self.mpDraw.draw_landmarks(
37+
img, handLms, self.mpHands.HAND_CONNECTIONS
38+
)
39+
return img
40+
41+
def findPosition(self, img, handNo=0, draw=True):
42+
# 初始化列表存储手部位置
43+
lmList = []
44+
# 如果检测到手部
45+
if self.results.multi_hand_landmarks:
46+
# self.results 是在 findHands 方法中定义的。由于 findHands 方法在 findPosition 方法之前被调用,因此 self.results 会被正确地初始化并存储检测结果
47+
# 这种设计依赖于调用顺序,我们得确保在调用 findPosition 之前已经调用过 findHands,否则 self.results 可能没有数据,导致 findPosition 无法正常工作
48+
# 在 Python 中,self 参数用于引用类的实例。只要在类的方法中通过 self 定义了属性(例如 self.results),该属性就可以在同一个类的其他方法中访问和使用。这样可以在不同的方法之间共享数据
49+
50+
# 获取指定手部
51+
myHand = self.results.multi_hand_landmarks[handNo]
52+
# 遍历每个关键点
53+
for id, lm in enumerate(myHand.landmark):
54+
# 获取图像尺寸
55+
h, w, c = img.shape
56+
# 计算关键点在图像中的位置
57+
cx, cy = int(lm.x * w), int(lm.y * h)
58+
lmList.append([id, cx, cy])
59+
if draw:
60+
# 在图像上绘制关键点
61+
cv2.circle(img, (cx, cy), 1, (255, 0, 255), -1)
62+
# 由于现有的 findHands,再有的 handDetector,所以“关键点”的图层在“手部连接”之上,前一个程序则相反
63+
64+
return lmList
65+
66+
67+
# 主函数
68+
def main():
69+
pTime = 0 # 前一帧时间
70+
cTime = 0 # 当前时间
71+
cap = cv2.VideoCapture(0) # 打开摄像头
72+
detector = handDetector() # 创建手部检测器
73+
while True:
74+
success, img = cap.read() # 读取摄像头图像
75+
img = cv2.flip(img, 1) # 水平翻转图像
76+
img = detector.findHands(img) # 检测手部
77+
lmList = detector.findPosition(img) # 获取手部关键点位置
78+
if len(lmList) != 0:
79+
print(lmList[4]) # 打印大拇指指尖位置
80+
cTime = time.time()
81+
fps = 1 / (cTime - pTime) # 计算帧率
82+
pTime = cTime
83+
# 在图像上显示帧率
84+
cv2.putText(
85+
img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3
86+
)
87+
cv2.imshow("Image", img) # 显示图像
88+
if cv2.waitKey(1) & 0xFF == ord("q"):
89+
break # 按下'q'退出
90+
91+
92+
if __name__ == "__main__":
93+
main()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import cv2
2+
import mediapipe as mp
3+
import time
4+
import HandTrackingModule as htm
5+
6+
pTime = 0
7+
cTime = 0
8+
cap = cv2.VideoCapture(0)
9+
detector = htm.handDetector()
10+
11+
while True:
12+
success, img = cap.read()
13+
img = detector.findHands(img, draw=True)
14+
lmList = detector.findPosition(img, draw=False)
15+
16+
if len(lmList) != 0:
17+
print(lmList[4])
18+
19+
cTime = time.time()
20+
fps = 1 / (cTime - pTime)
21+
pTime = cTime
22+
23+
# cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,
24+
# (255, 0, 255), 3)
25+
26+
cv2.imshow("Image", img)
27+
cv2.waitKey(1)

Chapter 1 Hand Tracking/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# mp.solutions.hands
2+
3+
`mpHands = mp.solutions.hands` 引用 MediaPipe 的手部解决方案模块
4+
5+
`hands = mpHands.Hands()` 创建一个 Hands 对象,用于检测和跟踪手部关键点
6+
7+
`results = hands.process(imgRGB)` 处理图像以检测手部
8+
9+
`for handLms in results.multi_hand_landmarks` results.multi_hand_landmarks会返回一个列表,其中包含检测到的每只手的关键点信息
10+
11+
`for id, lm in enumerate(handLms.landmark)` id 是手部关键点的索引,lm 是 landmark 的缩写,表示手部关键点的坐标信息
12+
13+
lm.x 和 lm.y 是关键点的归一化坐标,范围在 0 到 1 之间。通过乘以图像的宽度和高度,可以将它们转换为图像中的像素坐标
14+
15+
Hands 对象的参数:
16+
```python
17+
static_image_mode: 是否将每帧作为静态图像处理
18+
max_num_hands: 最大检测手数
19+
min_detection_confidence: 检测置信度阈值
20+
min_tracking_confidence: 跟踪置信度阈值
21+
```
22+
23+
# mp.solutions.drawing_utils
24+
25+
`mpDraw = mp.solutions.drawing_utils` 引用绘图工具,用于在图像上绘制检测到的手部关键点和连接线
26+
27+
`mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS)` 绘制手部关键点和连接线,img 要绘制的图像,handLms 手部关键点的坐标,mpHands.HAND_CONNECTIONS 定义手部关键点之间的连接关系,用于绘制骨架结构
28+
29+
# pycache文件夹
30+
31+
当导入一个 .py 文件时,Python 会编译它,并将编译后的字节码存储在 __pycache__ 目录中。这有助于提高程序的执行效率,因为下次运行时可以直接使用编译后的字节码,而不必重新编译源代码
32+
33+
“Cache” 是指缓存,一种用于临时存储数据的机制,以便更快速地访问。缓存可以减少数据的重复计算或从慢速存储设备读取的次数,从而提高程序性能
34+
35+
对于HandTrackingModule.py而言,当你在其他文件中导入这个模块时,只有类 handDetector 和其中的方法会被使用,而 main() 函数不会,main() 函数只有在直接运行该脚本时才会执行
Binary file not shown.
977 KB
Loading
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import cv2
2+
import mediapipe as mp
3+
import time
4+
5+
# 初始化 Mediapipe 的绘图工具和姿势检测模块
6+
mpDraw = mp.solutions.drawing_utils
7+
mpPose = mp.solutions.pose
8+
pose = mpPose.Pose()
9+
10+
# 打开视频文件
11+
cap = cv2.VideoCapture("E:\\Advance Computer Vision with Python\\Chapter 2 Pose Estimation\\PoseVideos\\3.mp4")
12+
13+
if not cap.isOpened():
14+
print("Error: Could not open video.")
15+
exit()
16+
17+
pTime = 0 # 前一帧的时间
18+
19+
# 创建可调整大小的窗口
20+
cv2.namedWindow("Image", cv2.WINDOW_NORMAL)
21+
22+
while True:
23+
success, img = cap.read() # 读取视频帧
24+
25+
if not success:
26+
print("Failed to read frame")
27+
break
28+
29+
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 将图像从 BGR 转换为 RGB
30+
results = pose.process(imgRGB) # 处理图像,检测姿势
31+
32+
if results.pose_landmarks:
33+
# 绘制姿势连接
34+
mpDraw.draw_landmarks(img, results.pose_landmarks, mpPose.POSE_CONNECTIONS)
35+
36+
# 遍历每个关键点
37+
for id, lm in enumerate(results.pose_landmarks.landmark):
38+
h, w, c = img.shape # 获取图像尺寸
39+
cx, cy = int(lm.x * w), int(lm.y * h) # 计算关键点在图像中的位置
40+
cv2.circle(
41+
img, (cx, cy), 5, (255, 0, 0), cv2.FILLED
42+
) # 在关键点位置绘制圆圈
43+
44+
cTime = time.time()
45+
fps = 1 / (cTime - pTime) # 计算帧率
46+
pTime = cTime
47+
48+
# 在图像上显示帧率
49+
cv2.putText(img, str(int(fps)), (70, 50), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3)
50+
51+
cv2.imshow("Image", img)
52+
53+
if cv2.waitKey(1) & 0xFF == ord("q"):
54+
break
55+
56+
cap.release()
57+
cv2.destroyAllWindows()

0 commit comments

Comments
 (0)