Nhập các thư viện cần thiết: - Import cv2 có tác dụng xử lý ảnh và video còn numpy để làm việc với mảng arrays và các phép tính toán số học phức và video thời gian thực đặc biệt là Nhận
Trang 1ĐẠI HỌC LẠC HỒNG KHOA CƠ ĐIỆN – ĐIỆN TỬ
Bộ Môn: LẬP TRÌNH KỸ THUẬT
BÁO CÁO MÔN LẬP TRÌNH KỸ THUẬT
ĐỀ TÀI : Dùng cử chỉ điều khiển chuột
GVHD: Lê Tiến Lộc LỚP: 23CD111 SVTH: Nguyễn Văn
Khải Nguyễn Quang Vinh Đậu Phương Nam
Trang 2Mở đầu
Python là một ngôn ngữ lập trình cao cấp phổ biến được sử dụng cho nhiều ứng dụng khác nhau, bao gồm phát triển web, tính toán khoa học, phân tích dữ liệu, trí tuệ nhân tạo và nhiều hơn nữa Nó được phát hành lần đầu vào năm 1991 bởi Guido van Rossum và từ đó đã trở thành một trong những ngôn ngữ lập trình được sử dụng rộng rãi nhất trên thế giới Python được biết đến với tính đơn giản và dễ sử dụng của nó, cũng như tính đọc được và hỗ trợ cho nhiều phong cách lập trình khác nhau, bao gồm lập trình thủ tục, hướng đối tượng và hàm Nó có một thư viện chuẩn lớn, cung cấp nhiều module và hàm hữu ích có thể được sử dụng để thực hiện nhiều nhiệm vụ khác nhau.
Python là phần mềm mã nguồn mở, điều này có nghĩa là nó miễn phí để
sử dụng, phân phối và chỉnh sửa Nó có sẵn trên nhiều nền tảng khác nhau, bao gồm Windows, macOS, Linux và nhiều hệ điều hành khác Ngoài
ra, có nhiều thư viện và framework phổ biến của bên thứ ba được cung cấp cho Python, chẳng hạn như NumPy, Pandas, Django, Flask và
TensorFlow, có thể được sử dụng để mở rộng khả năng của nó và đơn giản hóa quá trình phát triển.
Pygame là một bộ mô-đun Python đa nền tảng được thiết kế để viết trò chơi điện tử Nó bao gồm đồ họa máy tính và thư viện âm thanh được thiết
kế để sử dụng với ngôn ngữ lập trình Python.
Pygame sử dụng thư viện Simple DirectMedia Layer ( SDL ) , với mục đích cho phép phát triển trò chơi máy tính trong thời gian thực mà không cần
cơ chế bậc thấp của ngôn ngữ lập trình C và các dẫn xuất của nó Điều này dựa trên giả định rằng các chức năng đắt tiền nhất bên trong trò chơi
có thể được trừu tượng hóa khỏi logic trò chơi, do đó có thể sử dụng ngôn ngữ lập trình bậc cao, chẳng hạn như Python, để cấu trúc trò chơi.
Các tính năng khác mà SDL không có bao gồm toán học vectơ, phát hiện
va chạm, quản lý độ họa 2d, hỗ trợ MIDI , camera, thao tác mảng pixel, chuyển đổi, lọc, hỗ trợ phông chữ freetype nâng cao và vẽ.
Các ứng dụng sử dụng pygame có thể chạy trên điện thoại và máy tính bảng Andro id với việc sử dụng Bộ phụ pygame cho Android ( pgs4a ) Âm thanh, rung, bàn phím và gia tốc kế được hỗ trợ trên Android.
( Trích nguồn : https://vi.wikipedia.org/wiki/Pygame )
Trang 3LÝ DO CHỌN ĐỀ TÀI
- Tìm hiểu và hiểu thêm về ngôn ngữ python.
- Tìm hiểu cách thức để thực hiện được một cử chỉ mà không cần dùng chuột
- Biết cách tìm kiếm tài liệu và sàng lọc tài liệu.
- Cách thức làm việc nhóm.
- Cách thức thuyết trình trước đám đông.
Lập trình
1 Nhập các thư viện cần thiết:
- Import cv2 có tác dụng xử lý ảnh và video còn numpy để làm việc với mảng (arrays) và các phép tính toán số học phức
và video thời gian thực đặc biệt là Nhận diện bàn tay và theo dõi cử chỉ tay
- import Pyautogui có tác dụng tự động hóa các tác vụ điều khiển giao diện trên máy tính thay vì thao tác thủ công thì
mô phỏng của chuột
- import time được sử dụng để đo số khung hình mỗi giây
(FPS)
2 Khởi tạo các thông số cơ bản:
Trang 4- wCam là chiều rộng của khung hình camera (ở đây là 640 pixels) hCam là chiều dài của khung hình camera (ở đây là
480 pixels)
tránh con trỏ chuột di chuyển ngay sát mép khung hình với khoảng cách cụ thể 100
chuột
em đặt pTime = 0 giúp đảm bảo rằng không có giá trị lỗi khi tính toán thời gian giữa các khung hình đầu tiên
- plocX , plocY có chức năng lưu tọa độ của con trỏ chuột
trong khung hình trước đó Vì Khi bắt đầu, chưa có bất kỳ khung hình nào được xử lý và cũng chưa có con trỏ chuột di
=> chương trình bắt đầu từ một trạng thái không có dữ liệu sai lệch giúp các phép toán và xử lý diễn ra chính xác từ frame đầu tiên của webcam
- Tiếp theo em sẽ tạo ra biến để nhận diện cử chỉ bàn tay , đặc biệt là điều khiển và click chuột :
+ is_clicking có tác dụng xác định người dùng có đang nhấn chuột không. is_clicking là True, điều này có nghĩa là chương trình nhận thấy ngón trỏ và ngón giữa của người dùng đang ở gần nhau, do đó, hệ thống sẽ giả lập hành động nhấn chuột
Trang 5is_clicking là False, có nghĩa là không có nhấn chuột, do đó, chuột sẽ không thực hiện bất kỳ hành động nhấn nào.q
+ click_position: Lưu vị trí ngón trỏ khi click Khi bắt đầu giá trị của click_position là None nhưng khi phát hiện được trạng thái nhấn chuột chương trình sẽ gán tọa độ hiện tại của ngón trỏ vào click_position.Nếu không có nhấn chuột, click_position sẽ vẫn là None
+ first_dot_position: Lưu tọa độ của ngón trỏ khi chỉ di chuyển chuột mà không click first_dot_position được khởi tạo với giá trị
None
3 Cài đặt Webcam và Mediapipe:
- cap = cv2.VideoCapture(0) có tác dụng cho phép chương trình truy cập và sử dụng webcam
- if not cap.isOpened(): có tác dụng kiểm tra xem webcam có
mã sẽ in thông báo lỗi "Không thể mở webcam." và thoát
chương trình bằng exit()
- cap.set(3, wCam) và cap.set(4, hCam):
Trang 6 Dòng này thiết lập độ phân giải của video mà camera
mpHands = mp.solutions.hands Tạo một đối tượng
phát hiện và theo dõi bàn tay trong video
max_num_hands=1: Giới hạn phát hiện tối đa 1 bàn tay trong khung hình
min_detection_confidence=0.7: Thiết lập mức độ tin cậy tối thiểu của phát hiện bàn tay (giá trị từ 0 đến 1) Nếu độ tin cậy của phát hiện bàn tay thấp hơn 0.7, bàn tay sẽ không được phát hiện
4 Lấy kích thước màn hình:
trong camera, các tọa độ từ camera sẽ được chuyển đổi thành tọa độ trên màn hình máy tính, sử dụng thông tin kích
5 Vòng lặp chính (Xử lý mỗi khung hình):
- cap.read() có tác dụng dùng để đọc ảnh từ camera
Trang 7+ success : là Biến Boolean (True hoặc False) cho biết việc đọc ảnh từ camera có thành công hay không Nếu thành công,
success sẽ là True, nếu không thì là False
webcam), success sẽ là False, và chương trình sẽ in thông báo lỗi "Không thể truy cập vào webcam." rồi thoát khỏi
ngay lập tức và chuyển sang đoạn mã tiếp theo sau vòng lặp.)
- if not success: Kiểm tra nếu việc đọc ảnh từ camera không thành công (success là False)
- Nếu không thành công, thông báo lỗi "Không thể truy cập vào webcam." sẽ được in ra và chương trình sẽ dừng vòng
lặp bằng câu lệnh break
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB):
đổi màu sắc của hình ảnh từ không gian màu này sang
không gian màu khác
(Red, Green, Blue), vì Mediapipe yêu cầu đầu vào là hình ảnh ở không gian màu RGB
results = hands.process(imgRGB):
hands.process(imgRGB) là một hàm trong thư viện
Mediapipe dùng để xử lý hình ảnh và phát hiện bàn tay.
kết quả bao gồm thông tin về các bàn tay được phát hiện trong ảnh
.
6 Phát hiện và vẽ các điểm bàn tay:
Trang 8- lmList = []:
điểm (landmarks) trên bàn tay Mỗi điểm trong bàn tay sẽ
có một ID và tọa độ (x, y) trong không gian ảnh
- if results.multi_hand_landmarks:
hay không
results.multi_hand_landmarks là một danh sách các bàn
tay được phát hiện Mỗi bàn tay sẽ có một danh sách các
điểm (landmarks), đại diện cho các khớp và đầu ngón tay
được thực thi
- for handLms in results.multi_hand_landmarks:
multi_hand_landmarks Mỗi bàn tay có các điểm
(landmarks) của bàn tay đó
for id, lm in enumerate(handLms.landmark):
(handLms.landmark)
id là chỉ số của điểm (ví dụ: điểm 0 là cổ tay, điểm 8 là ngón trỏ, v.v.)
lm là đối tượng chứa các thông tin về điểm (x, y, z) của từng landmark lm.x và lm.y là tọa độ của điểm trong
Trang 9không gian tỷ lệ (0 đến 1), trong khi lm.z là chiều sâu
cx, cy = int(lm.x * wCam), int(lm.y * hCam):
Chuyển đổi tọa độ tỷ lệ (0 đến 1) của các điểm (lm.x,
lm.y) sang tọa độ pixel thực tế trên ảnh.
ảnh (tương ứng với độ phân giải của webcam)
o lm.y * hCam: Tính toán tọa độ y theo chiều cao của ảnh
nguyên (int), vì tọa độ pixel phải là số nguyên
lmList.append( id, cx, cy]):
một danh sách con gồm:
o id: Chỉ số của điểm (landmark)
o cx: Tọa độ x (theo pixel)
o cy: Tọa độ y (theo pixel)
mpDraw.draw_landmarks() là một hàm trong Mediapipe
giúp vẽ các điểm và đường nối các điểm trên bàn tay.
được vẽ
handLms: Là các điểm (landmarks) của một bàn tay
nối (đường vẽ) giữa các điểm trên bàn tay, giúp vẽ ra các đường nối giữa các khớp ngón tay, tạo thành hình dạng của bàn tay
7 Xử lí khi phát hiện tối thiểu 21 ngón tay
Trang 10if len(lmList) == 21::
lmList là danh sách chứa các điểm (landmarks) của bàn tay
Mỗi bàn tay có 21 điểm (được đánh số từ 0 đến 20), bao gồm các khớp cổ tay, các ngón tay và đầu ngón tay
chưa, nếu đúng thì mới tiếp tục xử lý Điều này giúp đảm bảo rằng bàn tay đã được phát hiện đầy đủ các điểm,
tránh lỗi khi chỉ phát hiện một phần của bàn tay
x1, y1 = lmList[8][1:] và x2, y2 = lmList[12][1:]:
o lmList[8] là điểm tương ứng với đầu ngón trỏ (chỉ số 8 trong danh sách)
o lmList[12] là điểm tương ứng với đầu ngón giữa (chỉ
số 12 trong danh sách)
lmList[8][1:] trả về tọa độ (x, y) của ngón trỏ, và
lmList[12][1:] trả về tọa độ (x, y) của ngón giữa
fingers = [1 if lmList[i][2] < lmList[i - 2][2] else 0 for i in [8,
12]]:
lên hay không
độ y trong không gian hình ảnh)
có giá trị nhỏ hơn so với điểm gần cổ tay của nó (do
Trang 11 Với mỗi ngón tay (ở chỉ số 8 và 12), chương trình so sánh giá trị y của điểm đầu ngón tay (lmList[i][2] với giá trị y của điểm hai khớp tay gần đó (lmList[i - 2][2]
này cho thấy ngón tay đó đang giơ lên và giá trị là 1
(giơ lên)
o Nếu không, giá trị là 0 (ngón tay không giơ lên)
Kết quả là một danh sách fingers chứa hai giá trị (0 hoặc
1) tương ứng với ngón trỏ và ngón giữa
cv2.rectangle(img, (frameR, frameR), (wCam - frameR, hCam -
frameR), (255, 0, 255), 2):
tay có thể xuất hiện
(frameR, frameR) là điểm góc trên bên trái của hình chữ nhật, xác định bởi giá trị của frameR (biến này điều chỉnh kích thước khu vực vẽ hình chữ nhật)
(wCam - frameR, hCam - frameR) là điểm góc dưới bên phải của hình chữ nhật, nằm trong phạm vi màn hình với
lề frameR
(255, 0, 255): Đây là màu của hình chữ nhật, với giá trị RGB (255, 0, 255) là màu tím
8 Điều khiển chuột:
Trang 12- if fingers[0] == 1 and fingers[1] == 0:
+ Điều kiện này kiểm tra nếu chỉ có ngón trỏ giơ lên và
ngón giữa không giơ lên.
+ fingers[0] == 1: Ngón trỏ đang giơ lên (do ngón trỏ có chỉ số 8 trong lmList)
+ fingers[1] == 0: Ngón giữa không giơ lên (do ngón giữa
có chỉ số 12 trong lmList)
x3 = np.interp(x1, (frameR, wCam - frameR), (0, wScr) và y3 =
np.interp(y1, (frameR, hCam - frameR), (0, hScr) :
hạn là frameR và wCam cho chiều rộng, hCam cho chiều
chiều rộng wScr và chiều cao hScr của màn hình)
o x1 và y1 là tọa độ ngón trỏ trong không gian hình ảnh
o frameR: Đây là lề (margin) mà bạn không muốn
chuột di chuyển quá gần các cạnh của màn hình Giá trị này giúp tạo ra một vùng an toàn
Trang 13o wCam, hCam: Đây là chiều rộng và chiều cao của camera
hình máy tính (lấy từ pyautogui.size())
np.interp(x1, (frameR, wCam - frameR), (0, wScr) : Chuyển đổi tọa độ x1 của ngón trỏ từ phạm vi [frameR, wCam -
frameR] (tỷ lệ theo chiều rộng camera) sang phạm vi màn hình [0, wScr] (tỷ lệ theo chiều rộng màn hình)
Tương tự, np.interp(y1, (frameR, hCam - frameR), (0,
hình
clocX = plocX + (x3 - plocX) / smoothening và clocY = plocY + (y3 - plocY) / smoothening:
việc di chuyển bị giật hoặc quá nhanh
o clocX và clocY là tọa độ mới của chuột (sau khi làm mượt)
mà Số càng lớn sẽ làm cho chuyển động chuột càng mượt, vì nó sẽ tính toán một sự thay đổi nhỏ hơn giữa các lần cập nhật vị trí chuột
Công thức: clocX = plocX + (x3 - plocX) / smoothening sẽ tính ra vị trí chuột mới gần với x3, nhưng sẽ có sự làm
quá nhảy vọt
pyautogui.moveTo(wScr - clocX, clocY):
pyautogui.moveTo() là một hàm trong thư viện
pyautogui dùng để di chuyển chuột đến tọa độ (x, y) đã cho
wScr - clocX: Vì màn hình máy tính thường có hệ tọa độ
với gốc tọa độ (0, 0) ở góc trên bên trái, còn camera có
Trang 14gốc tọa độ ở góc dưới bên trái (vì vậy pyautogui.moveTo()
tính từ trái sang phải, trong khi trục x của camera tính từ phải sang trái Do đó, cần phải lật ngược trục
x của chuột khi di chuyển
plocX, plocY = clocX, clocY:
Cập nhật lại tọa độ cũ (plocX, plocY) bằng tọa độ mới
(clocX, clocY) để sử dụng trong lần quét tiếp theo
Việc này giúp lưu trữ vị trí chuột của lần di chuyển trước
để tính toán sự thay đổi trong lần di chuyển tiếp theo, làm mượt chuyển động chuột
9 Vị trí chấm :
first_dot_position = (int(x1), int(y1)):
x1, y1 là tọa độ của đầu ngón trỏ (từ lmList[8] trong kết quả từ Mediapipe)
dùng int()) để có thể vẽ được chấm trên hình ảnh
first_dot_position được gán giá trị là tọa độ của đầu ngón trỏ, tức là vị trí mà chấm đầu tiên sẽ được vẽ Điều này có nghĩa là, khi ngón trỏ giơ lên, chấm sẽ xuất hiện tại đầu ngón trỏ
else: first_dot_position = None:
giá trị của first_dot_position sẽ được đặt lại thành None
Trang 15 Điều này có nghĩa là, khi ngón trỏ không giơ lên hoặc
không thực hiện thao tác di chuyển chuột, sẽ không có chấm nào được vẽ trên màn hình
10 Chế độ click chuột
if fingers[0] == 1 and fingers[1] == 1:
Điều kiện này kiểm tra xem cả ngón trỏ (fingers[0] và ngón giữa (fingers[1] đều giơ lên Nếu cả hai ngón này đều giơ lên (ngón trỏ và ngón giữa), chương trình sẽ tiếp tục xử lý phần click chuột
length = np hypot(x2 - x1, y2 - y1)
Hàm np hypot(x2 - x1, y2 - y1) tính khoảng cách Euclid (khoảng cách thẳng) giữa hai điểm (x1, y1) (vị trí ngón trỏ)
và (x2, y2) (vị trí ngón giữa)
(y2−y1)2\text{length} = \sqrt{(x2 - x1)^2 + (y2 -
y1)^2}length=(x2−x1)2+(y2−y1)2
ngón trỏ và ngón giữa, từ đó quyết định xem có thực hiện click chuột hay không
if length < 40:
Trang 16 Nếu khoảng cách giữa ngón trỏ và ngón giữa nhỏ hơn 40
bàn tay của người dùng), chương trình sẽ thực hiện click chuột
40 là một giá trị ngưỡng, chỉ khi khoảng cách giữa hai
ngón tay đủ nhỏ (chỉ ra rằng hai ngón tay gần nhau) thì click chuột mới được thực hiện
if not is_clicking:
(chưa nhấn chuột) không Nếu trạng thái là chưa nhấn
(is_clicking là False), chương trình sẽ thực hiện click chuột
(không nhấn liên tục)
pyautogui.click()
chuột trên màn hình
is_clicking = True
Sau khi click chuột, trạng thái is_clicking được đặt thành
True để đảm bảo rằng chỉ có một lần click chuột được
thực hiện cho mỗi lần ngón trỏ và ngón giữa giơ lên
click_position = (int(x1), int(y1))
Lưu vị trí nhấn chuột tại tọa độ đầu ngón trỏ ((x1, y1))
hai hay xử lý thêm thao tác)
else: is_clicking = False
thái nhấn chuột is_clicking sẽ được đặt lại thành False
hai ngón tay quá xa nhau
Trang 17else: is_clicking = False
không phải chế độ click), thì trạng thái is_clicking được đặt lại thành False, ngừng mọi thao tác nhấn chuột
11 Hiển thị các điểm và chế độ điều khiển chuột:
- first_dot_position là vị trí của chấm đầu tiên, được xác định tại đầu ngón trỏ khi nó giơ lên
+ cv2.circle(img, first_dot_position, 15, (0, 255, 0),
cv2.FILLED) vẽ một vòng tròn (chấm) màu xanh lá (0, 255,
0) tại vị trí first_dot_position với bán kính là 15 pixel
+ cv2.FILLED có nghĩa là vẽ vòng tròn đầy (không có viền)
không phải là None, tức là khi ngón trỏ được giơ lên và đang
di chuyển chuột
- click_position là vị trí của ngón trỏ khi đang ở trạng thái click chuột
+ cv2.circle(img, click_position, 15, (0, 0, 255), cv2.FILLED)
vẽ một vòng tròn màu đỏ (0, 0, 255) tại vị trí click_position
với bán kính là 15 pixel
+Điều kiện: Chấm này chỉ được vẽ khi is_clicking là True (tức
là trạng thái nhấn chuột đã được kích hoạt) và click_position
không phải là None