from ultralytics import YOLO import cv2 import math import matplotlib.pyplot as plt def getAnglebyline(line1, line2): dx1 = line1[0][0] - line1[1][0] dy1 = line1[0][1] - line1[1][1] dx2 = line2[0][0] - line2[1][0] dy2 = line2[0][1] - line2[1][1] # 求斜率 m1 = dy1 / dx1 m2 = dy2 / dx2 insideAngle = math.atan(abs((m2 - m1) / (1 + (m1 * m2)))) angle = insideAngle / math.pi * 180 if angle > -370 and angle < 370: angle = int(angle) return angle def aspectRatio(boxes): boxes = boxes.cpu().numpy().astype('uint32') x = [boxes[0], boxes[2]] y = [boxes[1], boxes[3]] width = x[1] - x[0] height = y[1] - y[0] radio = width / height return radio def getAnglebypoint(point_a, point_b, point_c): a_x, b_x, c_x = point_a[0], point_b[0], point_c[0] # 点a、b、c的x坐标 a_y, b_y, c_y = point_a[1], point_b[1], point_c[1] # 点a、b、c的y坐标 if len(point_a) == len(point_b) == len(point_c) == 3: # print("坐标点为3维坐标形式") a_z, b_z, c_z = point_a[2], point_b[2], point_c[2] # 点a、b、c的z坐标 else: a_z, b_z, c_z = 0, 0, 0 # 坐标点为2维坐标形式,z 坐标默认值设为0 # print("坐标点为2维坐标形式,z 坐标默认值设为0") # 向量 m=(x1,y1,z1), n=(x2,y2,z2) x1, y1, z1 = (a_x - b_x), (a_y - b_y), (a_z - b_z) x2, y2, z2 = (c_x - b_x), (c_y - b_y), (c_z - b_z) # 两个向量的夹角,即角点b的夹角余弦值 cos_b = (x1 * x2 + y1 * y2 + z1 * z2) / ( math.sqrt(x1 ** 2 + y1 ** 2 + z1 ** 2) * (math.sqrt(x2 ** 2 + y2 ** 2 + z2 ** 2)) + 0.01) # 角点b的夹角余弦值 B = math.degrees(math.acos(cos_b)) # 角点b的夹角值 return B # getAnglebypoint((3 ** 0.5, 1), (0, 0), (3 ** 0.5, 0)) # 结果为 30° # getAnglebypoint((1, 1), (0, 0), (1, 0)) # 结果为 45° # getAnglebypoint((-1, 1), (0, 0), (1, 0)) # 结果为 135° def is_fallen(keypoints, boxes): keypoints = keypoints.cpu().numpy().astype('uint32') Left_Shoulder = keypoints[5][:2] Right_Shoulder = keypoints[6][:2] Left_Hip = keypoints[11][:2] Right_Hip = keypoints[12][:2] Left_Knee = keypoints[13][:2] Right_Knee = keypoints[15][:2] Left_Ankle = keypoints[15][:2] Right_Ankle = keypoints[16][:2] Shoulders_c = [(Left_Shoulder[0] + Right_Shoulder[0]) // 2, (Left_Shoulder[1] + Right_Shoulder[1]) // 2] hips_c = [(Left_Hip[0] + Right_Hip[0]) // 2, (Left_Hip[1] + Right_Hip[1]) // 2] Knee_c = [(Left_Knee[0] + Right_Knee[0]) // 2, (Left_Knee[1] + Right_Knee[1]) // 2] Ankle_c = [(Left_Ankle[0] + Right_Ankle[0]) // 2, (Left_Ankle[1] + Right_Ankle[1]) // 2] '''计算身体中心线与水平线夹角''' human_angle = getAnglebyline([Shoulders_c, hips_c], [[0, 0], [10, 0]]) '''计算检测区域宽高比''' aspect_ratio = aspectRatio(boxes) '''计算肩部中心点与胯部中心点的垂直距离差''' human_shoulderhip = abs(Shoulders_c[1] - hips_c[1]) '''计算肩部胯部膝盖夹角''' Hip_Knee_Shoulders_angle = getAnglebypoint(Shoulders_c, hips_c, Knee_c) Hip_Knee_Right_angle = getAnglebypoint(Right_Shoulder.tolist(), Right_Hip.tolist(), Right_Knee.tolist()) '''计算胯部膝盖小腿夹角''' Ankle_Knee_Hip_angle = getAnglebypoint(hips_c, Knee_c, Ankle_c) Ankle_Knee_Right_angle = getAnglebypoint(Right_Hip.tolist(), Right_Knee.tolist(), Right_Ankle.tolist()) '''计算胯部膝盖是否处于相似的垂直位置''' vertical_threshold = Left_Knee[1] - Left_Shoulder[1] '''计算胯部膝盖是否处于相似的水平位置''' horizontal_threshold = Left_Shoulder[0] - Left_Knee[0] status_score = {'Stand': 0.0, 'Fall': 0.0, 'Sit': 0.0, 'other': 0.0} _weight = '' '''判断Shoulder、Hip、Knee是否被检测到''' if Knee_c[0] == 0 and Knee_c[1] == 0 and hips_c[0] == 0 and hips_c[1] == 0: status_score['Sit'] += 0.69 status_score['Fall'] += -0.8 * 2 status_score['Stand'] += -0.8 * 2 _weight = f'[1]Sit:+0.2, Fall:-1.6 ,Stand: -1.6' elif Shoulders_c[1] == 0 and Shoulders_c[0] == 0 and hips_c[0] == 0 and hips_c[1] == 0: status_score['Sit'] += -0.8 * 2 status_score['Fall'] += -0.8 * 2 status_score['Stand'] += 0.69 '''身体中心线与水平线夹角+-25''' if human_angle in range(-25, 25): status_score['Fall'] += 0.8 status_score['Sit'] += 0.1 _weight = f'{_weight}, [2]Fall:+0.8, Sit:+0.1' else: status_score['Fall'] += 0.2 * ((90 - human_angle) / 90) _weight = f'{_weight}, [3]Fall:+{0.8 * ((90 - human_angle) / 90)}' '''宽高比小与0.6则为站立''' if (aspect_ratio < 0.6 and human_angle in range(65, 115)): status_score['Stand'] += 0.8 _weight = f'{_weight}, [4]Stand:+0.8' elif (aspect_ratio > 1 / 0.6): # 5/3 status_score['Fall'] += 0.8 _weight = f'{_weight}, [5]Fall:+0.8' if horizontal_threshold < 30: status_score['Fall'] += 0.6 status_score['Sit'] += -0.15 score_max, status_max = max(zip(status_score.values(), status_score.keys())) return status_max, score_max def draw_boxes(boxes, image, label): boxes = boxes.cpu().numpy().astype('uint32') x = [boxes[0], boxes[2]] y = [boxes[1], boxes[3]] X = x[0] Y = y[0] color = (0, 255, 0) cv2.rectangle(image, (int(x[0]), int(y[0])), (int(x[1]), int(y[1])), color, 2) cv2.putText(image, label, (int(X + 5), int(Y + 5)), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2) if __name__ == '__main__': model = YOLO('yolov8x-pose.pt') source = 'images' # 换成自己的图片路径 results = model(source) y_train = [] for result in results: keypoints = result.keypoints.xy boxes = result.boxes.xyxy image_path = result.path image = cv2.imread(image_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # print(keypoints.shape) # print(image_path.split("images")[0]) # with open(f"{image_path}", "r") as f: # for line in f.readlines(): # line = line.strip('\n') # 去掉列表中每一个元素的换行符 # y_train.append(line[0]) # print(f"第{i}张图片:") # y2 = [] for keypoin, box in zip(keypoints, boxes): # print(keypoin,box) # keypoint = keypoin.cpu().numpy().astype('uint32') status_max, score_max = is_fallen(keypoin, box) if status_max == 'Fall': draw_boxes(box, image, f'{status_max}') # y2.append(status_max) # y1.append(y2) plt.imshow(image) plt.show() # print(y1) # class BinaryClassifier(nn.Module): # def __init__(self, input_features): # super(BinaryClassifier, self).__init__() # # 定义网络结构 # self.fc1 = nn.Linear(input_features, 64) # 第一个全连接层 # self.fc2 = nn.Linear(64, 32) # 第二个全连接层 # self.fc3 = nn.Linear(32, 2) # 输出层,2个神经元代表两个类别 # # def forward(self, x): # x = F.relu(self.fc1(x)) # 使用ReLU激活函数 # x = F.relu(self.fc2(x)) # 使用ReLU激活函数 # x = self.fc3(x) # 输出层,不需要激活函数,因为后面会使用softmax # return x # # # # 实例化网络模型 # input_features = 17 # 输入特征数量 # model = BinaryClassifier(input_features) # # # 定义损失函数和优化器 # criterion = nn.CrossEntropyLoss() # 交叉熵损失函数,适用于多分类问题 # optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam优化器 # # # 假设有一些训练数据 # # X_train是训练特征,y_train是训练标签(0或1) # X_train = torch.randn(100, input_features) # 100个样本,每个样本17个特征 # y_train = torch.randint(0, 2, (100,)) # 100个样本的标签,0或1 # # # 训练网络 # num_epochs = 10 # 训练轮数 # for epoch in range(num_epochs): # # 前向传播 # outputs = model(X_train) # loss = criterion(outputs, y_train) # # # 反向传播和优化 # optimizer.zero_grad() # 清空过往梯度 # loss.backward() # 反向传播,计算当前梯度 # optimizer.step() # 根据梯度更新网络参数 # # print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')