回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页
best-icon

Mind+Python+ Mediapipe项目——AI安保之跌倒报警 简单

头像 云天 2021.12.09 4140 0

Mind+Python+Mediapipe项目——AI健身之哑铃

Mind+Python+Mediapipe项目——AI健身之跳绳

Mind+Python+Mediapipe项目——AI健身之开合跳

Mind+Python+Mediapipe项目——AI健身之仰卧起坐

之前做了AI健身系列,四个项目。每个项目共同点是在Mind+Python模式下,使用了Google开源人工智能Mediapipe。不同之处是每个项目中分别使用了Pinpong库连接外部主控板、Python文件读写、物联网Easy IOT、语音合成模块等。在制作中不断学习与应用。

【项目背景】

【老龄化】

中国现有老龄人口已超过 1.6 亿,且每年以近 800 万的速度增加,最新数据显示,21世纪头 10 年的年均人口增长率为 0.57%,低于上世纪最后 10 年一直保持的1.07%的年增长率,因此中国人口正在进入老龄化,有关专家预测,到 2050 年,中国老龄人口将达到总人口的三分之一。

project-image

【跌倒】

2011年,卫生部发布过《老年人跌倒干预技术指南》,其中提到: 跌倒是我国伤害死亡的第四位原因,而在65岁以上的老年人中则为首位。老年人跌倒死亡率随年龄的增加急剧上升。跌倒除了导致老年人死亡外,还导致大量残疾,并且影响老年人的身心健康。如跌倒后的恐惧心理可以降低老年人的活动能力,使其活动范围受限,生活质量下降。我国已进入老龄化社会,65岁及以上老年人已达1.5亿。按30%的发生率估算每年将有4000多万老年人至少发生1次跌倒。

【家中跌倒】

家中老年人摔倒的概率远大于在街上遇到老年人摔倒。跌倒事无大小,可能会威胁人的生命,而据调查,全球50%以上的老人,跌倒事件都发生在家中,约有15万的老年人死于家中意外摔倒。随着年龄的增长,跌倒死亡风险更高。

【案例】

7月31日,安徽蚌埠一名独居老人在家中阳台摔倒,8月3日,有居民听到敲击声,发现老人倒在家中,急忙报警求助。

消防人员破拆房门之后,将老人抬到床上,经询问,老人已经倒地4天,滴水未进,家中的饭已发霉,获救后老人一口气喝下一瓶水。

project-image

很庆幸,这位老人的身体支撑了4天的消耗,也很开心,老人最终等来了外界的救助,没有生命危险。

【发现】

独居老人,在家中跌倒,若能及时发现送医,伤亡风险会极大降低。

【设计分析】

“跌倒”这个行为进行分析,“跌”——失足,落,降低:因羁绊、地面湿滑使脚失足,产生快速倾倒。“倒”——躺在地上,平躺、趴、侧卧等。

根据“跌倒”这个行为两个特征进行判断。跌倒是瞬间动作,人体的动作、高度和速度等会快速的改变。人体在跌倒过程中,一般是从站着到坐到地板上或者躺倒地板上,人体的中心点从较高的位置快速下降到了地面或者接近地面的高度。

测目标人体中心点的下降速度是第一个检测特征,如果检测到了第一个检测特征,将进一步检测第二个特征,即两髋中心点离地面的高度。地面不好检测,近似采用左脚和右脚连线中点代表地面。

一般情况下,人体跌倒后,身体一般是仰卧式、俯卧式和侧卧式,这时两髋中心点离地面(两脚中点)的高度非常小。

姿势估计是人体关节(通常称为关键点)在图像和视频帧中的定位。通常,每个人都将由多个关键点组成。将在关键点对之间绘制线条,有效地绘制人的粗略形状。基于输入和检测方法的姿势估计方法有很多种。

【项目设计】

        我首先选择两肩中点作为稳定的参考点(与摆动的胳膊和腿比较)。通过对目标人体监测,对返回的骨骼数据实时处理,在每相邻10帧求一次人参考点的下降速度spineV。当spineV>VT(临界值),则认为检测到了第一个跌倒特征。为解决人体在摄像头中近大远小的问题,我将spineV除以人的近似感知高度(两肩中点到两脚中点)ratio=spineV/height_all,当ratio>0.25(阈值),则认为检测到了第一个跌倒特征。

        当检测到了第一个跌倒特征后,在一定时间范围内出现第二特征“倒地”,只要计算两髋中心点距离两脚中点的高度baseH。当baseH<临界值,则认为检测到第二个检测特征。则认定此人跌倒。

【项目实现】

通过Mind+Python模式下加载Google的开源Mediapipe人工智能算法库,识别人体姿态,来判断人跌倒行为,发出电子邮件“报警”提醒并,通过Pinpong库控制LED灯及语音合成实时“报警提醒”。

试验测试】

我们选择UR跌倒检测数据集来测试我的模型,因为它包含不同的跌倒场景。

project-image

在总共30个视频中,该模型正确地识别了30个跌倒,100%的精确度。

【测试代码】

代码
import numpy as np
import time
import cv2
import PoseModule as pm
import math
cap = cv2.VideoCapture('fall.mp4')
detector = pm.poseDetector()
count = 0
dir = 0
pTime = 0
success=True
i=0
bs=0
ratio=0
height_all=0
baseH=0
while success:
  pointlist=[]
  success, img = cap.read()
  if success:
    img = cv2.resize(img, (640, 480))
    imgCanvas = np.zeros((480, 640, 3), np.uint8)
    imgCanvas = detector.findPose(img,imgCanvas,True)
    lmList,bbox = detector.findPosition(img, True)
    
    if len(lmList) != 0:
        i=i+1
        # 两肩中点
        if bs==0:
         if i==1:
          point_one= detector.midpoint(img, 11, 12)
         elif i==10:
          point_ten=detector.midpoint(img, 11, 12)
          point_foot=detector.midpoint(img, 29, 30)
          spineV =int(math.hypot(point_ten['x']-point_one['x'],point_ten['y']-point_one['y']))
          height_all=int(math.hypot(point_foot['x']-point_one['x'],point_foot['y']-point_one['y']))
          ratio=spineV/height_all
          if ratio>0.25:
              bs=1
          else:
              i=0

          #print(length)
        #if bs==1:
        if True:
        #两髋中心点
          point_kuan=detector.midpoint(img, 23, 24,draw=False)
          point_foot=detector.midpoint(img, 29, 30,draw=False)
        # 两脚中点
        
          baseH=(point_kuan['y']-point_foot['y'])
          if baseH>-40 and i<30:
            bs=2
          elif baseH<-40:
            bs=0
            i=0
        if bs==2:
            cv2.putText(img, str("fall"), (450, 100), cv2.FONT_HERSHEY_PLAIN, 5,(255, 255, 0), 5)
        
        
    else:
        bs=0
        i=0
    print("%.3f" %ratio,"%.3f" %baseH,i,bs)
    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime
    cv2.putText(img, str(int(fps)), (50, 100), cv2.FONT_HERSHEY_PLAIN, 5,(255, 0, 0), 5)
    
    cv2.imshow("Image", img)    
    cv2.waitKey(1)
cap.release()
cv2.destroyAllWindows()

【实地测试】

当系统成功检测到跌倒事件,系统会自动做出一系列处理。首先,系统自动切换到RGB画面,对于后台监控可以不必时刻关注监控画面,发生跌倒事件后系统会自动切换到RGB画面,监护人员只需通过RGB画面确认是否有人跌倒即可,极大减少了工作量,并很好地保护被监护人的隐私,因为在正常情况下,本系统只会显示深度图和骨骼图。

【利用物联网】


使用Pinpong库驱动彩灯和发声模块,势必要使用很长的数据线,才能让使用者看到效果。所以采用Python+Easy IOT物联网技术,无线传输数据。让Arduino通过物联网模块接收数据,通过语音合成模块播放报警音,并用LED灯显烁报警,提醒家中人员进行查看。具体内容参考:我的帖子:https://mc.dfrobot.com.cn/thread-311584-1-1.html

【发送邮件】

系统通过邮件服务器自动向指定联系人发送邮件,邮件内容包含被监护人姓名、跌倒事件发生的时间以及现场RGB图片等重要信息,确保相关监护人会在第一时间收到老年人发生跌倒的通知,以便提供及时有效救助,减少损失和伤害。

Python发送电邮件相关操作,可参考我的帖子:https://mc.dfrobot.com.cn/thread-305253-1-1.html

【引用文件】

主程序中引用的“PoseModule.py”

代码



import math
import mediapipe as mp
import cv2
class poseDetector():

    def __init__(self, mode=False, upBody=False, smooth=True,
                 detectionCon=0.85, trackCon=0.5):

        self.mode = mode
        self.upBody = upBody
        self.smooth = smooth
        self.detectionCon = detectionCon
        self.trackCon = trackCon

        self.mpDraw = mp.solutions.drawing_utils
        self.mpPose = mp.solutions.pose
        self.pose = self.mpPose.Pose(self.mode, self.upBody, self.smooth,
                                     self.detectionCon, self.trackCon)

    def findPose(self, img,imgCanvas,draw=True):
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        self.results = self.pose.process(imgRGB)
        if self.results.pose_landmarks:
            if draw:
                self.mpDraw.draw_landmarks(imgCanvas, self.results.pose_landmarks,
                                           self.mpPose.POSE_CONNECTIONS)
        return imgCanvas

    def findPosition(self, img, draw=True):
        xList = []
        yList = []
        bbox = []
        self.lmList = []
        if self.results.pose_landmarks:
            for id, lm in enumerate(self.results.pose_landmarks.landmark):
                h, w, c = img.shape
                # print(id, lm)
                cx, cy = int(lm.x * w), int(lm.y * h)
                self.lmList.append([id, cx, cy])
                xList.append(cx)
                yList.append(cy)
                if draw:
                    cv2.circle(img, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
                    
            xmin, xmax = min(xList), max(xList)
            ymin, ymax = min(yList), max(yList)
            bbox = xmin, ymin, xmax, ymax
            if draw:
                cv2.rectangle(img, (xmin - 20, ymin - 20), (xmax + 20, ymax + 20),(0, 255, 0), 2)
        return self.lmList,bbox
    def findDistance(self, img, p1, p2, draw=True,r=15, t=3):
        x1, y1 = self.lmList[p1][1:]
        x2, y2 = self.lmList[p2][1:]
        cx, cy = (x1 + x2) // 2, (y1 + y2) // 2

        if draw:
            cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), t)
            cv2.circle(img, (x1, y1), r, (255, 0, 255), cv2.FILLED)
            cv2.circle(img, (x2, y2), r, (255, 0, 255), cv2.FILLED)
            cv2.circle(img, (cx, cy), r, (0, 0, 255), cv2.FILLED)
            length = math.hypot(x2 - x1, y2 - y1)

        return length, img, [x1, y1, x2, y2, cx, cy]
    def midpoint(self,img,p1,p2,draw=True):
        x1, y1 = self.lmList[p1][1:]
        x2, y2 = self.lmList[p2][1:] 
        x3=int((x1+x2)/2)
        y3=int((y1+y2)/2)
        if draw:
         cv2.circle(img, (x3, y3), 10, (0, 0, 255), cv2.FILLED)
         cv2.circle(img, (x3, y3), 15, (0, 0, 255), 2)
        point={"x":x3,"y":y3} 
        return point
    def findAngle(self, img, p1, p2, p3, draw=True):

        # Get the landmarks
        x1, y1 = self.lmList[p1][1:]
        x2, y2 = self.lmList[p2][1:]
        x3, y3 = self.lmList[p3][1:]

        # Calculate the Angle
        angle = math.degrees(math.atan2(y3 - y2, x3 - x2) -
                             math.atan2(y1 - y2, x1 - x2))
        if angle < 0:
            angle += 360

        # print(angle)

        # Draw
        if draw:
            cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 3)
            cv2.line(img, (x3, y3), (x2, y2), (255, 255, 255), 3)
            cv2.circle(img, (x1, y1), 10, (0, 0, 255), cv2.FILLED)
            cv2.circle(img, (x1, y1), 15, (0, 0, 255), 2)
            cv2.circle(img, (x2, y2), 10, (0, 0, 255), cv2.FILLED)
            cv2.circle(img, (x2, y2), 15, (0, 0, 255), 2)
            cv2.circle(img, (x3, y3), 10, (0, 0, 255), cv2.FILLED)
            cv2.circle(img, (x3, y3), 15, (0, 0, 255), 2)
            cv2.putText(img, str(int(angle)), (x2 - 50, y2 + 50),
                        cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 255), 2)
        return angle




评论

user-avatar