diff --git a/modules/util/calc_ste_position.py b/modules/util/calc_ste_position.py deleted file mode 100644 index 63721f8..0000000 --- a/modules/util/calc_ste_position.py +++ /dev/null @@ -1,33 +0,0 @@ -import cv2 -import numpy as np -import modules.util.const as const - - -class CalcStethoscopePosition: - def __init__(self): - self.target_points = np.array( - [ - [const.LEFTSHOLDER_X, const.LEFTSHOLDER_Y], - [const.RIGHTSHOLDER_X, const.RIGHTSHOLDER_Y], - [const.LEFTHIP_X, const.LEFTHIP_Y], - [const.RIGHTHIP_X, const.RIGHTHIP_Y], - ], - dtype=np.float32, - ) - - def calc_affine(self, source_points, stethoscope_x, stethoscope_y): - mat = cv2.getPerspectiveTransform(source_points, self.target_points) - x_0 = mat[0][0] * stethoscope_x + mat[0][1] * stethoscope_y + mat[0][2] - y_0 = mat[1][0] * stethoscope_x + mat[1][1] * stethoscope_y + mat[1][2] - x_1_y_1 = mat[2][0] * stethoscope_x + mat[2][1] * stethoscope_y + mat[2][2] - stethoscope_calc = list((int(x_0 / x_1_y_1), int(y_0 / x_1_y_1))) - - if ( - stethoscope_calc[0] > const.MAXIMAIUM_SIZE - or stethoscope_calc[1] > const.MAXIMAIUM_SIZE - or stethoscope_calc[0] < const.MINIMUM_SIZE - or stethoscope_calc[1] < const.MINIMUM_SIZE - ): - stethoscope_calc = list((0, 0)) - - return stethoscope_calc diff --git a/modules/util/camera_selector.py b/modules/util/camera_selector.py deleted file mode 100644 index 326aa5b..0000000 --- a/modules/util/camera_selector.py +++ /dev/null @@ -1,144 +0,0 @@ -import tkinter as tk -from tkinter import Button, Listbox, Toplevel, Label, messagebox, Frame, Scrollbar -import subprocess -import cv2 -from PIL import Image, ImageTk -import modules.util.const as const -import threading - - -class CameraSelector(Toplevel): - def __init__(self, parent): - super().__init__(parent) - self.title("カメラの選択") - self.geometry("800x400") - self.cap = None - self.stop_thread = True - - self.cameras = self.get_cameras() - - self.setup_gui() - - self.protocol("WM_DELETE_WINDOW", self.on_close) - - def setup_gui(self): - """GUIのセットアップ""" - self.setup_camera_frame() - self.setup_control_frame() - - def setup_camera_frame(self): - """カメラフレームのセットアップ""" - self.camera_frame = Frame(self) - self.camera_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) - self.feed_label = Label(self.camera_frame) - self.feed_label.pack(pady=20, padx=20, fill=tk.BOTH, expand=True) - - def setup_control_frame(self): - """コントロールフレームのセットアップ""" - self.control_frame = Frame(self) - self.control_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=10, pady=10) - scrollbar = Scrollbar(self.control_frame) - scrollbar.pack(side=tk.RIGHT, fill=tk.Y) - self.listbox = Listbox(self.control_frame, yscrollcommand=scrollbar.set) - for cam_id, name in self.cameras: - self.listbox.insert(tk.END, f"[{cam_id}] {name}") - self.listbox.pack(pady=20, padx=20, fill=tk.BOTH, expand=True) - scrollbar.config(command=self.listbox.yview) - self.listbox.bind("", self.on_select_camera_id) - - self.preview_button = Button(self.control_frame, text="カメラ画像確認", command=self.on_preview_camera) - self.preview_button.pack(pady=10) - - self.select_id_button = Button(self.control_frame, text="カメラID確定", command=self.finalize_selection) - self.select_id_button.pack(pady=10) - - def get_cameras(self): - """CameraFinder.exeを使用して利用可能なカメラのIDと名前を取得する""" - result = subprocess.run([const.CAMERA_FINDER_PATH], capture_output=True, text=True) - lines = result.stdout.splitlines() - cameras = [] - for line in lines: - if "[" in line and "]" in line: - idx = line.index("[") - id_end = line.index("]") - cam_id = int(line[idx + 1 : id_end]) - cam_name = line[id_end + 2 :].strip() - cameras.append((cam_id, cam_name)) - return cameras - - def on_select_camera_id(self, event=None): - selected_idx = self.listbox.curselection() - if not selected_idx: - return - self.selected_cam_id = self.cameras[selected_idx[0]][0] - self.listbox.selection_set(selected_idx) - - def on_preview_camera(self): - if not hasattr(self, "selected_cam_id"): - messagebox.showinfo("情報", "カメラIDを選択してください。") - return - - if self.cap and self.cap.isOpened(): - self.cap.release() - - self.stop_thread = False - self.thread = threading.Thread(target=self.show_camera_feed, args=(self.selected_cam_id,)) - self.thread.start() - - def show_camera_feed(self, cam_id): - self.cap = cv2.VideoCapture(cam_id) - if not self.cap.isOpened(): - messagebox.showerror("エラー", "カメラを開けませんでした。") - return - - while not self.stop_thread: - ret, frame = self.cap.read() - if ret: - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - # アスペクト比を保ったままリサイズ - h, w, _ = frame.shape - aspect_ratio = w / h - new_width = int(self.feed_label.winfo_height() * aspect_ratio) - frame = cv2.resize(frame, (new_width, self.feed_label.winfo_height())) - image = Image.fromarray(frame) - photo = ImageTk.PhotoImage(image=image) - self.feed_label.config(image=photo) - self.feed_label.image = photo - - self.cap.release() - - def get_selected_camera_id(self): - """選択されたカメラIDを返す""" - if hasattr(self, "selected_cam_id"): - return self.selected_cam_id - return None - - def finalize_selection(self): - if not hasattr(self, "selected_cam_id"): - messagebox.showinfo("情報", "カメラIDを選択してください。") - return - self.selected_camera_id = self.get_selected_camera_id() - self.master.camera_id = self.selected_camera_id - messagebox.showinfo("情報", f"カメラID {self.selected_camera_id} が確定されました。") - self.destroy() - - def on_close(self): - self.stop_thread = True # スレッドを停止 - if hasattr(self, "thread"): - self.thread.join() # スレッドが終了するのを待つ - if self.cap and self.cap.isOpened(): - self.cap.release() - self.destroy() # ウィンドウを終了 - - @staticmethod - def get_default_camera_id(): - """デフォルトで "VGA Camera" という名前のカメラIDを返す""" - result = subprocess.run([const.CAMERA_FINDER_PATH], capture_output=True, text=True) - lines = result.stdout.splitlines() - for line in lines: - if "VGA Camera" in line: - idx = line.index("[") - id_end = line.index("]") - cam_id = int(line[idx + 1 : id_end]) - return cam_id - return None diff --git a/modules/util/const.py b/modules/util/const.py deleted file mode 100644 index 36bac9c..0000000 --- a/modules/util/const.py +++ /dev/null @@ -1,33 +0,0 @@ -# CameraFinder.exeの場所 -CAMERA_FINDER_PATH = "bin/CameraFinder.exe" - -# 聴診デバイス -BAR_TOR = 64 -ADJUST_VALUE = 48 -VID = 1027 -PID = 24597 -BAUDRATE = 115200 - -# SSDモデルのセットアップ -MODEL_PATH = "./models/ssd/mb1-ssd-second.pth" -LABEL_PATH = "./models/ssd/voc-model-labels.txt" - -# EARS音源のセットアップ -EARS_MAP_PATH = "img/map/" -EARS_SOUND_PATH = "sound/" - -# 聴診位置計算 -LEFTSHOLDER_X = 290 -LEFTSHOLDER_Y = 90 -RIGHTSHOLDER_X = 100 -RIGHTSHOLDER_Y = 90 -LEFTHIP_X = 280 -LEFTHIP_Y = 390 -RIGHTHIP_X = 110 -RIGHTHIP_Y = 390 - -MAXIMAIUM_SIZE = 390 -MINIMUM_SIZE = 0 - -# logフォルダの場所 -LOG_PATH = "./log" diff --git a/modules/util/ears_ai.py b/modules/util/ears_ai.py deleted file mode 100644 index 784fbe1..0000000 --- a/modules/util/ears_ai.py +++ /dev/null @@ -1,92 +0,0 @@ -import cv2 -import numpy as np -import torch -import modules.posenet as posenet -from modules.PytorchSSD.ssd.mobilenetv1_ssd import create_mobilenetv1_ssd, create_mobilenetv1_ssd_predictor -from modules.util import const - - -class EarsAI: - def __init__(self): - self.model_path = const.MODEL_PATH - self.label_path = const.LABEL_PATH - - self.setup_ssd_model() - self.setup_posenet() - - def setup_ssd_model(self): - """SSDモデルのセットアップを行う""" - class_names = [name.strip() for name in open(self.label_path).readlines()] - net = create_mobilenetv1_ssd(len(class_names), is_test=True) - net.load(self.model_path) - self.predictor = create_mobilenetv1_ssd_predictor(net, candidate_size=200) - self.class_names = class_names - - def setup_posenet(self): - """PoseNetのセットアップを行う""" - self.posenet_model = posenet.load_model(101).cuda() - self.output_stride = self.posenet_model.output_stride - - def pose_detect(self, frame, vid): - """姿勢検出を行う""" - print("Entering pose_detect method") # デバッグ出力 - print(f"Frame type: {type(frame)}") # デバッグ出力 - print(f"Frame shape: {frame.shape if hasattr(frame, 'shape') else 'No shape attribute'}") # デバッグ出力 - - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - input_image, draw_image, output_scale = posenet.read_imgfile( - frame, scale_factor=0.7125, output_stride=self.output_stride - ) - with torch.no_grad(): - input_image = torch.Tensor(input_image).cuda() - heatmaps_result, offsets_result, displacement_fwd_result, displacement_bwd_result = self.posenet_model( - input_image - ) - pose_scores, keypoint_scores, keypoint_coords = posenet.decode_multiple_poses( - heatmaps_result.squeeze(0), - offsets_result.squeeze(0), - displacement_fwd_result.squeeze(0), - displacement_bwd_result.squeeze(0), - output_stride=self.output_stride, - max_pose_detections=10, - min_pose_score=0.30, - ) - - keypoint_coords *= output_scale - overlay_image = posenet.draw_skel_and_kp( - frame, pose_scores, keypoint_scores, keypoint_coords, min_pose_score=0.15, min_part_score=0.1 - ) - - # Extract keypoint coordinates - left_shoulder = keypoint_coords[0, posenet.PART_NAMES.index("leftShoulder"), :].astype(np.int32) - right_shoulder = keypoint_coords[0, posenet.PART_NAMES.index("rightShoulder"), :].astype(np.int32) - left_hip = keypoint_coords[0, posenet.PART_NAMES.index("leftHip"), :].astype(np.int32) - right_hip = keypoint_coords[0, posenet.PART_NAMES.index("rightHip"), :].astype(np.int32) - - return overlay_image, left_shoulder, right_shoulder, left_hip, right_hip - - def ssd_detect(self, frame, vid): - """SSDによる検出を行う""" - overlay_image = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) - boxes, labels, probs = self.predictor.predict(overlay_image, 1, 0.20) - overlay_image = cv2.cvtColor(overlay_image, cv2.COLOR_BGR2RGB) - - stethoscope_x, stethoscope_y = 0, 0 - if len(probs) != 0: - max_index = np.argmax(probs) - box = boxes[max_index, :] - cv2.rectangle(overlay_image, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 255), 2) - stethoscope_x = int((box[0] + box[2]) / 2) - stethoscope_y = int((box[1] + box[3]) / 2) - label = f"{self.class_names[labels[max_index]]}: {probs[max_index]:.2f}" - cv2.putText( - overlay_image, - label, - (int(box[0]) + 20, int(box[1]) + 40), - cv2.FONT_HERSHEY_SIMPLEX, - 0.5, - (255, 0, 255), - 1, - ) - - return overlay_image, stethoscope_x, stethoscope_y diff --git a/modules/util/ears_sound.py b/modules/util/ears_sound.py deleted file mode 100644 index 3860e2d..0000000 --- a/modules/util/ears_sound.py +++ /dev/null @@ -1,72 +0,0 @@ -import cv2 -import numpy as np -import pygame -import os -import modules.util.const as const - - -class EarsSound: - def __init__(self, result): - pygame.mixer.init() - self.volume = 0.0 - self.min_volume = 0.0 - self.playing = False - - if result[0] == "none": - self.map_image = None - self.sound = None - else: - self.map_image = cv2.imread(os.path.join(const.EARS_MAP_PATH + result[0])) - self.sound_file = os.path.join(const.EARS_SOUND_PATH + result[1]) - self.sound = pygame.mixer.Sound(self.sound_file) - - def volume_change(self, stethoscope, flag=False, type=None): - """音量を変更する""" - if self.map_image is None: - return - - R, G, B = self.map_image[stethoscope[1], stethoscope[0]] - vol = R if R != 0 else B - VOLUME_CURVE = 9.5 - Y = pow((vol / 255.0), 1.0 / VOLUME_CURVE) - set_volume = Y - - if set_volume > 1: - set_volume = 1 - elif set_volume < 0: - set_volume = 0 - - if type == 1: - set_volume = set_volume * 0.5 - - self.volume = set_volume - if flag and self.volume != 0: - if stethoscope[0] > 195: - self.volume = 0.1 - self.sound.set_volume(self.volume) - - def get_length(self): - """音源の長さを秒単位で取得する""" - if not self.sound: - return 0 - - array = pygame.sndarray.array(self.sound) - sample_rate = pygame.mixer.get_init()[0] - duration = array.shape[0] / float(sample_rate) - return duration - - def set_volume(self, volume): - """音量を設定する""" - self.volume = volume - - def play(self): - """音を再生する""" - self.sound.set_volume(self.volume) - self.sound.play(-1) - - def stop(self): - self.sound.set_volume(self.min_volume) - - def close(self): - """リソースを解放する""" - pygame.mixer.quit() diff --git a/modules/util/logger.py b/modules/util/logger.py deleted file mode 100644 index e04cf67..0000000 --- a/modules/util/logger.py +++ /dev/null @@ -1,79 +0,0 @@ -import logging -import datetime -import os -import cv2 -from modules.util import const - - -class SingletonMeta(type): - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - instance = super().__call__(*args, **kwargs) - cls._instances[cls] = instance - return cls._instances[cls] - - -class Logger(metaclass=SingletonMeta): - def __init__(self): - if not hasattr(self, "initialized"): - # Ensure the base log directory exists - if not os.path.exists(const.LOG_PATH): - os.makedirs(const.LOG_PATH) - - # Create a directory structure based on the current datetime - current_time = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") - self.current_log_dir = os.path.join(const.LOG_PATH, current_time) - - # Ensure the log directory exists - if not os.path.exists(self.current_log_dir): - os.makedirs(self.current_log_dir) - - # Ensure the image directory exists - self.img_path = os.path.join(self.current_log_dir, "img") - if not os.path.exists(self.img_path): - os.makedirs(self.img_path) - - # Create log filename - log_filename = os.path.join(self.current_log_dir, f"log_{current_time}.log") - - # Set up logging as before - self.logger = logging.getLogger(self.__class__.__name__) - self.logger.setLevel(logging.DEBUG) - - fh = logging.FileHandler(log_filename) - fh.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) - self.logger.addHandler(fh) - - ch = logging.StreamHandler() - ch.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) - self.logger.addHandler(ch) - - self.initialized = True - - def debug(self, message): - self.logger.debug(message) - - def info(self, message): - self.logger.info(message) - - def warning(self, message): - self.logger.warning(message) - - def error(self, message): - self.logger.error(message) - - def critical(self, message): - self.logger.critical(message) - - def save_image(self, frame, img_name=None): - # If img_name isn't provided, use the current time as the filename - if img_name is None: - file_name = datetime.datetime.now().strftime("%Y%m%d_%H%M%S%f") + ".jpg" # %f is microseconds - else: - file_name = ( - datetime.datetime.now().strftime("%Y%m%d_%H%M%S%f") + "_" + img_name + ".jpg" - ) # %f is microseconds - img_path = os.path.join(self.img_path, file_name) - cv2.imwrite(img_path, frame) diff --git a/modules/util/touch_sensor.py b/modules/util/touch_sensor.py deleted file mode 100644 index fa524bd..0000000 --- a/modules/util/touch_sensor.py +++ /dev/null @@ -1,74 +0,0 @@ -import serial -from serial.tools import list_ports -import modules.util.const as const - - -class TouchSensor: - def __init__(self): - self.ser = None - self.inhale_start = None - self.exhale_start = None - self.exhale_end = None - self.serial_command = "0" - self.previous = 0 - self.setup_sensor() - - def setup_sensor(self): - """センサーのセットアップを行う""" - self.ser = self.init_serial_communication() - - def init_serial_communication(self): - """シリアル通信の初期化を行う""" - com = "" - ports = list(list_ports.comports()) - for p in ports: - if p.vid == const.VID and p.pid == const.PID: - com = p.device - break - - if not com: - print("Sensor device not found!") - return None - - return serial.Serial(com, const.BAUDRATE, timeout=3) - - def read_sensor_value(self): - """センサーの値を読み取る""" - if not self.ser: - return None - - self.ser.write(self.serial_command.encode()) - dlen = self.ser.inWaiting() - d = self.ser.read(dlen) - strword = d.decode("utf-8", errors="ignore") - - if len(strword) != 0: - touchData = strword[-1:] - value = int(touchData[0]) - self.previous = value - return value - else: - return 2 - - def calc_serial_value(self, current_pos): - """シリアル値を計算する""" - bar = 0 - if not self.ser: - return None - - if int(current_pos) < self.inhale_start: - bar = current_pos * const.BAR_TOR / self.inhale_start - elif int(current_pos) < self.exhale_start: - bar = const.BAR_TOR - elif int(current_pos) < self.exhale_end: - bar = const.BAR_TOR - const.BAR_TOR * (current_pos - self.exhale_start) / ( - self.exhale_end - self.exhale_start - ) - - self.serial_command = chr(int(bar) + const.ADJUST_VALUE) - - def close_connection(self): - """シリアル接続を解除する""" - if self.ser: - self.ser.close() - self.ser = None diff --git a/output_posenet.py b/output_posenet.py new file mode 100644 index 0000000..55be065 --- /dev/null +++ b/output_posenet.py @@ -0,0 +1,113 @@ +import os +import re + +import cv2 + +from util.ears_ai import EarsAI + + +def video_to_frames(video_path, output_dir): + """動画からフレームを抽出して保存""" + os.makedirs(output_dir, exist_ok=True) + video = cv2.VideoCapture(video_path) + if not video.isOpened(): + raise IOError(f"Could not open video file: {video_path}") + + frame_num = 0 + while True: + success, frame = video.read() + if not success: + break + frame_num += 1 + cv2.imwrite(os.path.join(output_dir, f"{frame_num}-frame.png"), frame) + + video.release() + print(f"All frames saved to {output_dir}") + return frame_num + + +def process_frames_with_posenet(frames_dir, output_dir): + """PoseNetで骨格検出を行い、結果を保存""" + ears_ai = EarsAI() + os.makedirs(output_dir, exist_ok=True) + + # フレーム画像を番号順にソート + png_files = sorted( + [f for f in os.listdir(frames_dir) if f.lower().endswith(".png")], + key=lambda x: int(re.search(r"(\d+)", x).group(1)), + ) + + for image_file_name in png_files: + print(f"Processing image: {image_file_name}") + # フレームを読み込み + frame = cv2.imread(os.path.join(frames_dir, image_file_name)) + if frame is None: + continue + + # PoseNetで骨格検出 + pose_overlay_img, *_ = ears_ai.pose_detect(frame, None) + + # 結果を保存 + cv2.imwrite( + os.path.join(output_dir, image_file_name), + cv2.cvtColor(pose_overlay_img, cv2.COLOR_RGB2BGR), + ) + + +def create_video_from_images(image_dir, output_path, fps=30): + """画像シーケンスから動画を作成""" + images = sorted( + [img for img in os.listdir(image_dir) if img.endswith(".png")], + key=lambda x: int(re.search(r"(\d+)", x).group()), + ) + + if not images: + print(f"No images found in {image_dir}") + return + + frame = cv2.imread(os.path.join(image_dir, images[0])) + height, width, _ = frame.shape + + video = cv2.VideoWriter( + output_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height) + ) + + for image in images: + img = cv2.imread(os.path.join(image_dir, image)) + video.write(img) + + video.release() + print(f"Created video: {output_path}") + + +def main(): + # 設定 + video_path = "./video/Test3-1.mp4" # 入力動画のパス + output_base_dir = "output-posenet" # 出力ディレクトリ + frames_dir = os.path.join(output_base_dir, "frames") # フレーム保存ディレクトリ + pose_frames_dir = os.path.join( + output_base_dir, "pose_frames" + ) # ポーズ検出結果保存ディレクトリ + output_video_path = os.path.join( + output_base_dir, "pose_detection.mp4" + ) # 出力動画パス + + # 出力ベースディレクトリの作成 + os.makedirs(output_base_dir, exist_ok=True) + + # メイン処理 + print("Starting video processing...") + print("1. Extracting frames from video...") + video_to_frames(video_path, frames_dir) + + print("2. Processing frames with PoseNet...") + process_frames_with_posenet(frames_dir, pose_frames_dir) + + print("3. Creating output video...") + create_video_from_images(pose_frames_dir, output_video_path) + + print("Processing complete!") + + +if __name__ == "__main__": + main() diff --git a/util/const.py b/util/const.py index dc9341f..36bac9c 100644 --- a/util/const.py +++ b/util/const.py @@ -9,8 +9,8 @@ BAUDRATE = 115200 # SSDモデルのセットアップ -MODEL_PATH = "./models/mb1-ssd-second.pth" -LABEL_PATH = "./models/voc-model-labels.txt" +MODEL_PATH = "./models/ssd/mb1-ssd-second.pth" +LABEL_PATH = "./models/ssd/voc-model-labels.txt" # EARS音源のセットアップ EARS_MAP_PATH = "img/map/" diff --git a/util/ears_ai.py b/util/ears_ai.py index 669b924..c3e669b 100644 --- a/util/ears_ai.py +++ b/util/ears_ai.py @@ -1,9 +1,13 @@ import cv2 import numpy as np import torch + import modules.posenet as posenet -from modules.PytorchSSD.ssd.mobilenetv1_ssd import create_mobilenetv1_ssd, create_mobilenetv1_ssd_predictor -from modules.util import const +from modules.PytorchSSD.ssd.mobilenetv1_ssd import ( + create_mobilenetv1_ssd, + create_mobilenetv1_ssd_predictor, +) +from util import const class EarsAI: @@ -40,29 +44,47 @@ ) with torch.no_grad(): input_image = torch.Tensor(input_image).cuda() - heatmaps_result, offsets_result, displacement_fwd_result, displacement_bwd_result = self.posenet_model( - input_image - ) - pose_scores, keypoint_scores, keypoint_coords = posenet.decode_multiple_poses( - heatmaps_result.squeeze(0), - offsets_result.squeeze(0), - displacement_fwd_result.squeeze(0), - displacement_bwd_result.squeeze(0), - output_stride=self.output_stride, - max_pose_detections=10, - min_pose_score=0.05, + ( + heatmaps_result, + offsets_result, + displacement_fwd_result, + displacement_bwd_result, + ) = self.posenet_model(input_image) + pose_scores, keypoint_scores, keypoint_coords = ( + posenet.decode_multiple_poses( + heatmaps_result.squeeze(0), + offsets_result.squeeze(0), + displacement_fwd_result.squeeze(0), + displacement_bwd_result.squeeze(0), + output_stride=self.output_stride, + max_pose_detections=1, + min_pose_score=0.0, + ) ) keypoint_coords *= output_scale overlay_image = posenet.draw_skel_and_kp( - frame, pose_scores, keypoint_scores, keypoint_coords, min_pose_score=0.15, min_part_score=0.1 + frame, + pose_scores, + keypoint_scores, + keypoint_coords, + min_pose_score=0.0, + min_part_score=0.0, ) # Extract keypoint coordinates - left_shoulder = keypoint_coords[0, posenet.PART_NAMES.index("leftShoulder"), :].astype(np.int32) - right_shoulder = keypoint_coords[0, posenet.PART_NAMES.index("rightShoulder"), :].astype(np.int32) - left_hip = keypoint_coords[0, posenet.PART_NAMES.index("leftHip"), :].astype(np.int32) - right_hip = keypoint_coords[0, posenet.PART_NAMES.index("rightHip"), :].astype(np.int32) + left_shoulder = keypoint_coords[ + 0, posenet.PART_NAMES.index("leftShoulder"), : + ].astype(np.int32) + right_shoulder = keypoint_coords[ + 0, posenet.PART_NAMES.index("rightShoulder"), : + ].astype(np.int32) + left_hip = keypoint_coords[0, posenet.PART_NAMES.index("leftHip"), :].astype( + np.int32 + ) + right_hip = keypoint_coords[0, posenet.PART_NAMES.index("rightHip"), :].astype( + np.int32 + ) return overlay_image, left_shoulder, right_shoulder, left_hip, right_hip @@ -76,7 +98,13 @@ if len(probs) != 0: max_index = np.argmax(probs) box = boxes[max_index, :] - cv2.rectangle(overlay_image, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 255), 2) + cv2.rectangle( + overlay_image, + (int(box[0]), int(box[1])), + (int(box[2]), int(box[3])), + (0, 255, 255), + 2, + ) stethoscope_x = int((box[0] + box[2]) / 2) stethoscope_y = int((box[1] + box[3]) / 2) label = f"{self.class_names[labels[max_index]]}: {probs[max_index]:.2f}"