本帖最后由 云天 于 2023-4-10 15:08 编辑
【项目背景】
最近在某商场见到一种互动屏,人们可以进行体感互动,脚底生花,人走过去就会根据人走的轨迹自然开花,和鱼水互动,人走在水里面鱼会跟着人的脚步游动。产生令人瞩目的效果,为受众提供良好的交互体验感。
【项目设计】
本项目,使用Mediapipe姿态识别,结合比例(P)、积分(I)算法,实现手部对一串小球图形进行运行控制,产生互动效果。硬件使用熊猫板加电视机。
【姿态识别】
MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。在谷歌,一系列重要产品,如 YouTube、Google Lens、ARCore、Google Home 以及 Nest,都已深度整合了 MediaPipe。
-
- import cv2
- import mediapipe as mp
- import numpy as np
- mp_drawing = mp.solutions.drawing_utils
- #mp_drawing_styles = mp.solutions.drawing_styles
- mp_pose = mp.solutions.pose
-
-
- # For webcam input:
- cap = cv2.VideoCapture(0)
- with mp_pose.Pose(
- min_detection_confidence=0.5,
- min_tracking_confidence=0.5) as pose:
- while cap.isOpened():
- success, image = cap.read()
- if not success:
- print("Ignoring empty camera frame.")
- # If loading a video, use 'break' instead of 'continue'.
- continue
-
- # To improve performance, optionally mark the image as not writeable to
- # pass by reference.
- image.flags.writeable = False
- image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
- results = pose.process(image)
-
- # Draw the pose annotation on the image.
- image.flags.writeable = True
- image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
- mp_drawing.draw_landmarks(
- image,
- results.pose_landmarks,
- mp_pose.POSE_CONNECTIONS,
- )
- # Flip the image horizontally for a selfie-view display.
- cv2.imshow('MediaPipe Pose', cv2.flip(image, 1))
- if cv2.waitKey(5) & 0xFF == 27:
- break
- cap.release()
复制代码
【提取关键点坐标】
cvzone是一个计算机视觉工具包,可方便的图像处理和实现视觉AI功能。核心是使用OpenCV和Mediapipe库。
-
- from cvzone.PoseModule import PoseDetector
- import cv2
- import numpy as np
- cap = cv2.VideoCapture(0)
- detector = PoseDetector()
-
- while True:
- success, img = cap.read()
- img = detector.findPose(img,draw=False)
- img_show = np.zeros([480,640,3],np.uint8)
- lmList = []
- if detector.results.pose_landmarks:
- for id, lm in enumerate(detector.results.pose_landmarks.landmark):
- h, w, c = img.shape
- cx, cy, cz = int(lm.x * w), int(lm.y * h), int(lm.z * w)
- lmList.append([id, cx, cy, cz])
- cv2.circle(img_show, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
-
- cv2.imshow("Image", img_show)
- cv2.imshow("Image1", img)
- if cv2.waitKey(1) & 0xFF == ord('q'):
- break
- cap.release()
- cv2.destroyAllWindows()
复制代码
【PD算法】
第一个小球图形,坐标跟随手部坐标,如(lmList[15][1],lmList[15][2]),其它小球图形使用比例P、积分D控制进行跟随,产生弹性效果。
- disX=MycircleR[i][0]-MycircleR[i-1][0]
- disY=MycircleR[i][1]-MycircleR[i-1][1]
- sumdisRX[i]+=disX
- sumdisRY[i]+=disY
- MycircleR[i][0]=int(MycircleR[i-1][0]+0.05*disX-0.001*sumdisRX[i])
- MycircleR[i][1]=int(MycircleR[i-1][1]+0.05*disY+i*10-0.001*sumdisRY[i])
复制代码
【完整程序】
-
- from cvzone.PoseModule import PoseDetector
- import cv2
- import numpy as np
- cap = cv2.VideoCapture(0)
- detector = PoseDetector()
- MycircleL=[]
- sumdisLX=[]
- sumdisLY=[]
- MycircleR=[]
- sumdisRX=[]
- sumdisRY=[]
- num=10
- for i in range(num):
- MycircleL.append([0,0])
- sumdisLX.append(0)
- sumdisLY.append(0)
- MycircleR.append([0,0])
- sumdisRX.append(0)
- sumdisRY.append(0)
- PoseList=[8,6,6,5,5,4,4,0,0,1,1,2,2,3,3,7,10,9,18,20,18,16,20,16,22,16,16,14,14,12,12,11,12,24,24,23,24,26,26,28,28,32,32,30,28,30,11,13,13,15,15,21,15,17,17,19,19,15,11,23,23,25,25,27,27,29,29,31,31,27]
- def Myline(pose1,pose2):
- global img_show,lmList
- cv2.line(img_show,(lmList[pose1][1],lmList[pose1][2]),(lmList[pose2][1],lmList[pose2][2]),(0,255,255),4)
- while True:
- success, img = cap.read()
- img=cv2.resize(img,(1366,768))
- img = detector.findPose(img,draw=False)
- img_show = np.zeros([768,1366,3],np.uint8)
- lmList = []
-
- if detector.results.pose_landmarks:
- for id, lm in enumerate(detector.results.pose_landmarks.landmark):
- h, w, c = img.shape
- cx, cy, cz = int(lm.x * w), int(lm.y * h), int(lm.z * w)
- lmList.append([id, cx, cy, cz])
- cv2.circle(img_show, (cx, cy), 8, (255, 0, 0), cv2.FILLED)
- for i in range(num-1,0,-1):
- disX=MycircleL[i][0]-MycircleL[i-1][0]
- disY=MycircleL[i][1]-MycircleL[i-1][1]
- sumdisLX[i]+=disX
- sumdisLY[i]+=disY
- MycircleL[i][0]=int(MycircleL[i-1][0]+0.05*disX-0.001*sumdisLX[i])
- MycircleL[i][1]=int(MycircleL[i-1][1]+0.05*disY+i*10-0.001*sumdisLY[i])
- MycircleL[0][0]=lmList[15][1]
- MycircleL[0][1]=lmList[15][2]
- for i in range(num-1,0,-1):
- disX=MycircleR[i][0]-MycircleR[i-1][0]
- disY=MycircleR[i][1]-MycircleR[i-1][1]
- sumdisRX[i]+=disX
- sumdisRY[i]+=disY
- MycircleR[i][0]=int(MycircleR[i-1][0]+0.05*disX-0.001*sumdisRX[i])
- MycircleR[i][1]=int(MycircleR[i-1][1]+0.05*disY+i*10-0.001*sumdisRY[i])
- MycircleR[0][0]=lmList[16][1]
- MycircleR[0][1]=lmList[16][2]
-
- for i in range(0,len(PoseList)-1,2):
- print(i)
- Myline(PoseList[i],PoseList[i+1])
- for i in range(num):
- cv2.circle(img_show, (MycircleL[i][0], MycircleL[i][1]), 8, (0, 0, 255), cv2.FILLED)
- cv2.circle(img_show, (MycircleR[i][0], MycircleR[i][1]), 8, (0, 0, 255), cv2.FILLED)
-
- cv2.imshow("Image", img_show)
- cv2.imshow("Image1", img)
- if cv2.waitKey(1) & 0xFF == ord('q'):
- break
- cap.release()
- cv2.destroyAllWindows()
复制代码
【演示视频】
|