LoginSignup
0
0

python,mediapipe,tkinterを用いて悪い姿勢になったらデクストップ通知するアプリ

Posted at

メモ書き程度にしか記載していないためご了承下さい。
再度編集いたしますので、イメージだけでも掴んでいただきたいです。

今回も作成したアプリは、座った姿勢を改善することを目的としてしています。
mediapipeを用いることで、PC内蔵カメラから座った姿勢での鼻の座標を取得することができます。
そして、鼻の座標が閾値を超えた場合、「姿勢が悪くなっています」
とデスクトップ通知します。

qiita.py
import tkinter
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import cv2
import numpy as np
import time
import mediapipe as mp
from plyer import notification

class Application (tkinter.Frame):
    def __init__(self,root=None):
        super().__init__(root,width=800,height=700,borderwidth=1,relief='groove')
        self.root = root
        self.pack()
        self.pack_propagate(0)
        self.create_widgets()
        self.shoot_interval = 30
        self.notice_time = 3

    def create_widgets(self):
        run_btn = tkinter.Button(self,text="実行",command=self.submit)
        run_btn.pack(side="top")


        message_shoot = tkinter.Message(self,text="撮影間隔(秒)",width=200)
        message_shoot.pack()

        self.shoot =tkinter.Entry(self) 
        self.shoot.pack()
        
        message_notice = tkinter.Message(self,text="悪い姿勢の検出回数(回)",width=200)
        message_notice.pack()
        
        self.notice = tkinter.Entry(self)
        self.notice.pack()

    def submit(self):
        print("実行ボタンが押下されました")
        shoot_interval = self.shoot.get()
        self.shoot_interval = int(shoot_interval)
        print(self.shoot_interval)

        notice_time = self.notice.get()
        self.notice_time = int(notice_time)
        print(self.notice_time)

        self.fig, self.ax = plt.subplots(figsize=(12,4))
        self.canvas = FigureCanvasTkAgg(self.fig,master=self)
        self.canvas.get_tk_widget().pack()
        self.show()
    
    #部位の座標の取得
    def findPoint(self,landmark_num):
        parts_array = {"x":0, "y":0, "z":0,"v":0}
        parts_array["x"] = self.results.pose_landmarks.landmark[landmark_num].x
        parts_array["y"] = self.results.pose_landmarks.landmark[landmark_num].y
        body_array = [0,0]
        body_array[0] = int(parts_array["x"] * self.width + 0.5)
        body_array[1] = int(parts_array["y"] * self.height + 0.5)
        return body_array


    def show(self):
        #初期設定
        mp_drawing = mp.solutions.drawing_utils
        mp_drawing_styles = mp.solutions.drawing_styles
        mp_pose = mp.solutions.pose


        cap = cv2.VideoCapture(0)
        count = 0
        bad_count = 0
        nose_y = []
        nose_y_all = []

        with mp_pose.Pose(
            min_detection_confidence=0.5,
            min_tracking_confidence=0.5) as pose:
            while cap.isOpened():
                count += 1
                print(count)
                success, image = cap.read()
                if not success:
                    print("Ignoring empty camera frame.")
                    continue
                image.flags.writeable = False
                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                self.results = pose.process(image)

                image.flags.writeable = True
                image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
                self.height,self.width = image.shape[0],image.shape[1]
                mp_drawing.draw_landmarks(
                    image,
                    self.results.pose_landmarks,
                    mp_pose.POSE_CONNECTIONS,
                    landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())


                cv2.imshow('MediaPipe Pose', cv2.flip(image, 1))

                #鼻の座標の取得
                nose = self.findPoint(0)
                if len(nose) > 0:
                    # nose_x.append(nose[0])
                    nose_y.append(nose[1])
                    nose_y_all.append(nose[1])

                print(f"鼻のy座標:{nose_y}")

                #一定時間停止
                time.sleep(self.shoot_interval)

                #鼻のy座標の3回の平均
                thre = [i for i in nose_y[:3]]
                thre = np.mean(thre)
                print(thre)


                if count > 3:
                # 閾値を超える鼻の座標
                    if nose[1] > thre:
                        bad_count += 1
                        print("bad posture")

                        #閾値を3回超えたらデクストップ通知する
                        if bad_count == self.notice_time:
                            notification.notify(
                                        title="腰の負担を減らそう",
                                        message="姿勢が悪くなっています",
                                        timeout=10
                                    )
                            bad_count = 0

                            #鼻の座標をクリアする
                            nose_y.clear()
                

                #撮影の終了
                if len(nose_y_all) == 10:
                    print("要素数が基準を満たしました")
                    print(f"nose_yの要素数:{len(nose_y)},nose_y_allの要素数:{len(nose_y_all)}")
                    break
                
                # 停止 スクリプトの終了
                key = cv2.waitKey(100)
                if key == ord('q'): 
                    print("qが押下されました")
                    break
            
        cap.release()

        x_date = [self.shoot_interval * i for i in range(len(nose_y_all))]

        self.ax.plot(x_date,nose_y_all)
        self.ax.grid()
        self.canvas.draw()


root = tkinter.Tk()
root.title("health")
root.geometry("1000x1000")
app = Application(root=root)
app.mainloop()

通知
スクリーンショット 2023-11-12 205855.png

tkinterの出力イメージ
output.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0