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物体上面