1、安裝python
進入cdm,打python要能顯示版本號
>>>(進入python提示符模式)
import sys
sys.path顯示python的安裝路徑,
進入到python.exe的路徑
在python目錄中安裝(ctrl+z退出python交互模式)
2、pip install mediapipe? ? mediapipe 官網?
用阿里云鏡像?? ? mediapipe的github托管
pip install mediapipe -i https://mirrors.aliyun.com/pypi/simple/
3、pip install opencv -python
pip install opencv-python -i https://mirrors.aliyun.com/pypi/simple/
opencv官方網站?(有關攝像頭動捕關鍵點說明)
進入到python.exe的路徑,新建 FaceCapture.py
import cv2
import mediapipe as mp
import socket
import json
import time# 初始化 mediapipe
mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False,max_num_faces=1,refine_landmarks=True,min_detection_confidence=0.8, # 增加檢測置信度min_tracking_confidence=0.8 # 增加檢測置信度
)# 切換攝像頭請切換設備索引
device_index = 0
cap = cv2.VideoCapture(device_index, cv2.CAP_DSHOW)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
target_addr = ('127.0.0.1', 5005)total_duration = 15 # 總運行時間,單位:秒
start_time = time.time()
frame_count = 0
prev_time = 0# 定義需要的關鍵點索引
# 眉毛
left_eyebrow = [70, 74, 78, 336]
right_eyebrow = [300, 304, 308, 105]
# 眼睛
left_eye = [36, 70, 105, 133]
right_eye = [267, 301, 336, 362]
# 嘴巴
mouth = [61, 146, 178, 291]
# 鼻翼
left_nostril = [129]
right_nostril = [358]
# 下巴
chin = [152]
# 眼角
left_inner_corner = [133]
left_outer_corner = [33]
right_inner_corner = [362]
right_outer_corner = [263]
# 嘴角
left_mouth_corner = [61]
right_mouth_corner = [291]# 合并所有需要的關鍵點索引
selected_indices = left_eyebrow + right_eyebrow + left_eye + right_eye + mouth + left_nostril + right_nostril + chin + left_inner_corner + left_outer_corner + right_inner_corner + right_outer_corner + left_mouth_corner + right_mouth_cornerif not cap.isOpened():print("無法打開攝像頭")
else:try:while True:elapsed = time.time() - start_time # 提前計算運行時間remaining = total_duration - elapsedtry:ret, frame = cap.read()frame_count += 1print(f"正在讀取第 {frame_count} 幀,狀態: {ret},程序將在 {remaining:.2f} 秒后自動關閉")if remaining <= 0:print("運行時間已到,自動退出")breakexcept Exception as e:print("讀取視頻幀異常:", e)continueif not ret:print("無法獲取攝像頭數據,幀號:", frame_count)breakcurrent_time = time.time()fps = 1 / (current_time - prev_time)prev_time = current_timergb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)result = face_mesh.process(rgb_frame)data_to_send = {}if result.multi_face_landmarks:landmarks = result.multi_face_landmarks[0].landmarkkeypoints = [{"id": i, "x": landmarks[i].x, "y": landmarks[i].y, "z": landmarks[i].z} for i in selected_indices]data_to_send['keypoints'] = keypoints# 繪制跟蹤點height, width, _ = frame.shapefor keypoint in keypoints:x = int(keypoint["x"] * width)y = int(keypoint["y"] * height)cv2.circle(frame, (x, y), 2, (0, 255, 0), -1)else:data_to_send['keypoints'] = []data_to_send['fps'] = int(fps)json_data = json.dumps(data_to_send)# 實時驅動sock.sendto(json_data.encode('utf-8'), target_addr)# 離線驅動# with open(f"frames/frame_{frame_count:04d}.json", "w") as f:# json.dump(data_to_send, f)# 文字提示,添加 fps 信息countdown_text = f"remainTime: {remaining:.1f} second, FPS: {int(fps)}"cv2.putText(frame, countdown_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1,(0, 255, 0), 2, cv2.LINE_AA)# 進度條提示bar_x, bar_y = 10, 60bar_width = 300bar_height = 20progress = remaining / total_durationcv2.rectangle(frame, (bar_x, bar_y), (bar_x + bar_width, bar_y + bar_height), (180, 180, 180), 2)cv2.rectangle(frame, (bar_x, bar_y), (bar_x + int(bar_width * progress), bar_y + bar_height), (0, 255, 0), -1)cv2.imshow('Face Capture', frame)if cv2.waitKey(1) & 0xFF == ord('q'):print("用戶手動退出")break# 自動退出機制(30 秒)if time.time() - start_time > total_duration:message = "已運行 {} 秒,自動退出".format(total_duration)print(message)breakfinally:# 發送關閉信息close_message = {'type': 'close'}json_close_message = json.dumps(close_message)sock.sendto(json_close_message.encode('utf-8'), target_addr)cap.release()sock.close()cv2.destroyAllWindows()
python安裝目錄,進去后cmd命令提示符,然后 python?? FaceCapture.py
?遇到 ImportError: DLL load failed while importing _framework_bindings: 動態鏈接庫(DLL)初始化例程失敗。
最新受支持的 Visual C++ 可再發行程序包下載 | Microsoft Learn
進入到python.exe的路徑,新建 StartCam.bat 文件
@echo off
python FaceCapture.py
pause
進入到python.exe的路徑,新建 啟動攝像頭.vbs 文件,這樣就可以隱藏掉cmd命令行的界面只顯示攝像頭界面
Set objShell = CreateObject("WScript.Shell")
Dim strPath, strBatFile
strPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName)
strBatFile = strPath & "\StartCam.bat"
objShell.Run strBatFile, 0, False
新建一個max的ms文件GetUDP.ms ,這個可以創建十個dmmy物體,用來接受攝像頭的表情捕捉數據,
--引入必要的 .NET類
dotNet.loadAssembly "System.Threading"
dotNet.loadAssembly "System.Net"
dotNet.loadAssembly "System.Net.Sockets"
dotNet.loadAssembly "System.Text"
dotNet.loadAssembly "Newtonsoft.Json"
-- dotNet.loadAssembly @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Threading.dll"--清理之前的資源
try (if isProperty udpClient "Close" do (udpClient.Close())) catch ()
try (if isProperty thread "Abort" do (thread.Abort())) catch ()--初始化UDP接收器
global localEndpoint = dotNetObject "System.Net.IPEndPoint" (dotNetClass "System.Net.IPAddress").Any 5005
global udpClient = dotNetObject "System.Net.Sockets.UdpClient"
udpClient.ExclusiveAddressUse = false
udpClient.Client.SetSocketOption (dotNetClass "System.Net.Sockets.SocketOptionLevel").Socket (dotNetClass "System.Net.Sockets.SocketOptionName").ReuseAddress true
udpClient.Client.Bind localEndpoint
--定義定時器
global delayTimer = dotnetobject "Windows.Forms.Timer"
-- 定義后臺線程
global BgThread = dotNetObject "System.ComponentModel.BackgroundWorker"
BgThread.WorkerSupportsCancellation = true--重置場景
resetMaxFile #noPrompt-- 創建 10 個 Dummy 物體并命名為 Dummy001, Dummy002, ..., Dummy010
global dummyArray = for i = 1 to 10 collect (local mydummy = dummy() -- 創建 Dummy 物體mydummy.name = "Dummy" + formattedPrint i format:"03d" -- 命名為 Dummy001, Dummy002, ...mydummy.position = [i * 50, 0, 0] -- 設置一個簡單的初始位置,您可以根據需要調整mydummy -- 返回創建的 Dummy 物體
)-- 接收 UDP 數據并解析為 JSON 結構體的函數
fn receiveAndParseJson =
(local remoteEndpoint = dotNetObject "System.Net.IPEndPoint" (dotNetClass "System.Net.IPAddress").Any 0local receiveBytes = udpClient.Receive remoteEndpointlocal jsonString = (dotNetClass "System.Text.Encoding").UTF8.GetString receiveBytestry (-- 引用 System.Web.Extensions(包含 JSON 解析器)dotNet.loadAssembly "System.Web.Extensions"-- 創建內建 JSON 解析器jsonParser = dotNetObject "System.Web.Script.Serialization.JavaScriptSerializer"-- 反序列化 JSON 字符串為 MaxScript 可用的結構體(Hashtable/Array等)jsonStruct = jsonParser.DeserializeObject jsonStringreturn jsonStruct)catch (print("解析 JSON 時出錯:" + getCurrentException())return undefined)
)-- 處理關鍵點數據并更新 Dummy 位置的函數
fn processKeypointsData keypoints =
(if keypoints != undefined do(for id = 0 to 9 do (for pt in keypoints do (if pt.Item["id"] == id then (local x = pt.Item["x"] * 100local y = pt.Item["y"] * 100local z = pt.Item["z"] * 100--dotNetClass "System.Windows.Forms.Control".Invoke (dotNetDelegate updateDummyPosition id x y z)local mydummy = dummyArray[id+1]if isValidNode mydummy do (mydummy.position = [pt.Item["x"]*100, pt.Item["y"]*100, pt.Item["z"]*100] )))print("Dummies 位置更新完成!")))
)-- 定時器觸發時執行的函數
fn timerCallback sender e =
(if not BgThread.CancellationPending do(local jsonStruct = receiveAndParseJson()format "json結構體為:% \n" jsonStructif jsonStruct != undefined do(-- 獲取幀率local fps = jsonStruct.Item["fps"]if fps != undefined do(-- 根據幀率計算定時器間隔(毫秒)local interval = 1000 / fpsdelayTimer.Interval = interval)-- 處理關鍵點數據local keypoints = jsonStruct.Item["keypoints"]processKeypointsData keypoints-- 檢查是否接收到關閉信息,則關閉后臺線程if jsonStruct.Item["type"] == "close" do(BgThread.CancelAsync()return true))sleep 0.1)
)--主執行命令
if (timerCallback sender e )then(delayTimer.enabled = true-- 添加定時器事件處理程序dotnet.AddEventHandler delayTimer "Tick" (timerCallback sender e)-- 添加后臺線程事件處理程序dotnet.AddEventHandler BgThread "DoWork" (fn sender e = timerCallback sender e)-- 監聽鍵盤事件,設置取消標志if keyboard.escPressed do (BgThread.CancelAsync()print "已發送取消請求")
)
else( delayTimer.enabled = falseudpClient.Close()BgThread.CancelAsync()
)
啟動啟動攝像頭.vbs,出現攝像頭窗口,和倒計時進度條,30秒后自動關閉,你可以設定的不關閉。
啟動GetUDP.ms,接收動捕數據,傳遞到做表情的dmmy物體上面
?