وسم نماذج الوضعية وتدريبها
اكتب تسميات النقاط المفتاحية مع قيم الرؤية، واضبط kpt_shape وflip_idx بشكل صحيح، ودرّب على COCO8-pose، وحوّل النقاط المفتاحية إلى زوايا مفاصل مشروطة بالثقة.
تحوّل تقديرات الوضعية كل شخص إلى 17 نقطة مرقّمة، ومنها تُستخرج القيم التي تسوّقها المنتجات: زوايا المرفقين لعلم بيئة العمل، وزوايا الركبتين لإعادة التأهيل، وارتفاع الورك لاكتشاف السقوط. يحدد مفتاحان في مجموعة البيانات ما إذا كان من الممكن الوثوق بهذه النقاط، والخطأ في أي منهما لا ينتج عنه خطأ ظاهر — بل نموذج يخلط بين اليمين واليسار، أو يتدرّب بصمت من دون أبسط أساليب زيادة البيانات.
عملي
صفّ تسمية النقاط المفتاحية#

يتكوّن صف تسمية الوضعية من صف كشف أُضيفت إليه نقاط مفتاحية. تُطبّع كل الإحداثيات إلى المجال 0–1، وتظهر النقاط المفتاحية بالترتيب الثابت نفسه في كل صف. وفيما يلي صف حقيقي من COCO8-pose، مقتطع بعد أربع نقاط مفتاحية:
0 0.671279 0.617945 0.645759 0.726859 0.519751 0.381250 2.000000 0.550936 0.348438 2.000000 0.488565 0.367188 2.000000 0.642412 0.354687 2.000000 ...| القيم | المعنى |
|---|---|
0 | فهرس الفئة (شخص) |
0.671279 0.617945 0.645759 0.726859 | مركز الصندوق x، ومركزه y، وعرضه، وارتفاعه |
0.519751 0.381250 2 | النقطة المفتاحية 0 (الأنف): x وy وقيمة الرؤية |
| … | 16 نقطة مفتاحية أخرى بالنمط نفسه: 5 + 17 × 3 = 56 قيمة لكل صف |
تتبع علامة الرؤية اصطلاح COCO: 2 مرئية، و1 موسومة لكنها محجوبة، و0 غير موسومة. يستبعد Ultralytics النقاط المفتاحية ذات القيمة v = 0 من الخسارة، لذا لا يؤدي غياب المعصم إلى تعليم النموذج التنبؤ بالمعاصم عند (0، 0).

استخدم 1 لمفصل محجوب يمكنك تحديد موضعه بثقة — مثل ورك خلف طاولة أو معصم خلف الظهر؛ إذ يظل مفيدًا للتدريب. خصص 0 للمفاصل التي يتعذر تحديد موضعها على أي شخص. إن وضع التسمية 0 لكل مفصل مخفي يهدر الحالات الصعبة التي يحتاج إليها النموذج تحديدًا.
يتناول دليل مجموعة بيانات الوضعية أيضًا الصيغة ذات القيمتين (x y فقط، kpt_shape: [K, 2]) لمجموعات البيانات التي لا تتضمن معلومات الرؤية.
kpt_shape وflip_idx#
يضيف YAML مجموعة البيانات مفتاحين إلى المفاتيح المعتادة path وtrain وval وnames:
kpt_shape: [17, 3] # 17 keypoints, each x, y, visibility
flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]
يحدد flip_idx النقطة المفتاحية التي تتحول إليها كل نقطة عند تطبيق زيادة البيانات بالقلب الأفقي. عند عكس صورة شخص، ينتهي المعصم الأيسر (9) في موضع المعصم الأيمن (10)، لذا تحتوي الخانة 9 من flip_idx على 10، وتحتوي الخانة 10 على 9؛ أما الأنف (0) فيتحول إلى نفسه. تنقل كل زيادة هيكل النقاط مع البكسلات، والقلب وحده يحتاج أيضًا إلى خريطة الفهارس:

ما الذي يحدث عندما تكون flip_idx خاطئة، وفقًا لما تم التحقق منه في المصدر المثبّت:
- مفقودة: يصدر التدريب تحذيرًا ويضبط
fliplrوflipudعلى 0، فتفقد أرخص تعزيز لديك. - طول غير صحيح: يتوقف التدريب مع
ValueErrorيوضح عدم التطابق — وهذا هو الإخفاق الوحيد الذي يصدر تنبيهًا واضحًا. - الطول صحيح، لكن الأزواج خاطئة: لا يصدر أي تحذير. تعلّم نصف العينات المعكوسة النموذج أن اليسار هو اليمين، وتُظهر صور التحقق تبديلًا بين اليسار واليمين في الوضعيات المتناظرة.
التدريب على COCO8-pose#

from ultralytics import YOLO
model = YOLO("yolo26n-pose.pt")
model.train(data="coco8-pose.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box mAP50-95: {metrics.box.map:.3f}")
print(f"pose mAP50-95: {metrics.pose.map:.3f}") Class Images Instances Box(P R mAP50 mAP50-95) Pose(P R mAP50 mAP50-95)
all 4 14 1 0.783 0.893 0.643 0.966 0.571 0.575 0.326
box mAP50-95: 0.643
pose mAP50-95: 0.326كما هو الحال مع التجزئة، ينقسم الجدول إلى قسمين. يقيّم الصندوق صناديق الأشخاص؛ وتقيّم الوضعية مواضع النقاط الرئيسية باستخدام تشابه النقاط الرئيسية للكائن (OKS) ضمن آلية mAP نفسها. تمثل Pose mAP50-95 معيار القبول لإصدار منتج الوضعيات — ويوضح الدرس التالي كيف تحدد OKS ما إذا كانت النقطة الرئيسية صحيحة.
من النقاط الرئيسية إلى زوايا المفاصل#
يحتوي result.keypoints على xy (N × K × 2، بالبكسل)، وxyn (مطبّعة)، وconf (N × K)، وفق ترتيب COCO الموضح أعلاه. زاوية المفصل هي الزاوية بين متجهي الطرفين اللذين يلتقيان عنده — ويمكن حسابها بالضرب النقطي:
import numpy as np
from ultralytics import YOLO
model = YOLO("yolo26n-pose.pt")
result = model("https://ultralytics.com/images/bus.jpg")[0]
kpts = result.keypoints.cpu().numpy() # people x 17 x (x, y, conf)
SHOULDER, ELBOW, WRIST = 5, 7, 9 # left arm, COCO keypoint order
for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
if conf[[SHOULDER, ELBOW, WRIST]].min() < 0.5:
print(f"person {person}: left arm not visible enough")
continue
a, b, c = xy[SHOULDER], xy[ELBOW], xy[WRIST]
cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
print(f"person {person}: left elbow angle {np.degrees(np.arccos(cos)):.0f} deg")person 0: left elbow angle 71 deg
person 1: left elbow angle 96 deg
person 2: left elbow angle 59 deg
person 3: left arm not visible enough
person 4: left arm not visible enoughسطرًا بسطر: kpts.xy[p] هي النقاط الـ17 للشخص p بالبكسل؛ ويمثل a - b وc - b العضد والساعد على هيئة متجهين ينطلقان من المرفق؛ والضرب النقطي المطبّع لهما هو جيب تمام الزاوية بينهما. تكون قراءة الذراع المستقيمة قريبة من 180°، والمنثنية بالكامل قريبة من 30°. لدى الشخصين 3 و4 مفصل واحد على الأقل في الذراع اليسرى بثقة أقل من 0.5، لذا يمتنع البرنامج النصي عن قياسهما.
العَرَض: زوايا تبدو موثوقة لأشخاص أذرعهم خارج الإطار. السبب: يعيد النموذج إحداثية لكل نقطة مفتاحية، سواء أكانت محجوبة أم لا. الحل: تجاوز أي حساب تقل فيه ثقة المفاصل عن حد أدنى (0.5 هنا)، وسجّل عدد مرات التجاوز — فارتفاع معدل التجاوز إشارة مبكرة إلى تغير في الكاميرا أو الإضاءة.
عرض الحل
import numpy as np
from ultralytics import YOLO
ARMS = {"left": (5, 7, 9), "right": (6, 8, 10)} # shoulder, elbow, wrist
kpts = YOLO("yolo26n-pose.pt")("https://ultralytics.com/images/bus.jpg")[0].keypoints.cpu().numpy()
for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
for side, (s, e, w) in ARMS.items():
if conf[[s, e, w]].min() < 0.5:
continue
a, b, c = xy[s], xy[e], xy[w]
cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
print(f"person {person}: {side} elbow {np.degrees(np.arccos(cos)):.0f} deg")person 0: left elbow 71 deg
person 0: right elbow 94 deg
person 1: left elbow 96 deg
person 2: left elbow 59 deg
person 2: right elbow 135 degيمكن قياس كلا ذراعي الشخصين 0 و2. أما الذراع اليمنى للشخص 1 والذراعان للشخصين 3 و4، فتقل ثقتها عن الحد الأدنى البالغ 0.5، لذا يتجاوزها البرنامج النصي بدلًا من التخمين.
يؤدي غياب flip_idx إلى تعطيل تعزيز الانعكاس مع إصدار تحذير؛ ويؤدي الطول غير الصحيح إلى إيقاف التدريب مع ValueError؛ أما flip_idx التي تبدو سليمة لكن أزواجها غير صحيحة، فتؤدي إلى تدريب صامت على تسميات معكوسة، وتظهر المشكلة على شكل تبديلات بين اليسار واليمين أثناء التحقق.