Ultralytics YOLO27: Bekleme listesine katıl.
Get Started
Algılamanın Ötesi: Segmentasyon, Poz, OBB ve Sınıflandırma·Sınıflandır, Birleştir, Gönder·Ders 9/10
Dersorta düzey

Görevleri Tek Bir İşlem Hattında Birleştir

Tespit edip ardından sınıflandırma yapan bir ardışık düzen kur, izlenen her nesneyi her karede değil yalnızca bir kez sınıflandır ve poz anahtar noktalarını kişi başına zaman serilerine dönüştür.

Tespitin ötesindeki en kullanışlı sistemler genellikle tek değil, iki modelden oluşur. Bir dedektör her çalışanı bulur, bir sınıflandırıcı da her kaskın doğru türde olup olmadığını belirler; bir poz modeli eklemleri bulur, bir izleyici de tekrarları sayabileceğin bir zaman çizelgesine dönüştürür. Kod kısadır. Bu sistemleri bozan şey, ikinci aşamanın veri kümesinin ilk aşamadaki kırpımlarla eşleşmemesi ve her karedeki her nesneyle katlanan maliyettir — gelişigüzel çalıştırıldığında, on kişinin bulunduğu 30 FPS'lik bir akış saniyede 300 sınıflandırmaya mal olur.

Sonuç

Tespit edip ardından sınıflandırma yapan bir betik, izlenen her kişiyi yalnızca bir kez sınıflandıran bir sürüm ve kişi başına diz açısını takip eden bir poz izleyicisi çalıştır; sınıflandırıcının eğitim verilerini dedektörün kendi kırpımlarından oluştur.

Hızlı Başlangıç
Nasıl ilerleyeceğini zaten biliyorsan, işte kısa sürüm.
  1. Tespit et, result.orig_img[y1:y2, x1:x2] ile kırp ve tüm kırpımları tek bir toplu çağrıda sınıflandır.

  2. Sınıflandırıcının veri kümesini stüdyo fotoğraflarından değil, dedektörünün kırpımlarından (save_crop=True) oluştur.

  3. Videoda persist=True ile izle, her result.boxes.id değerini bir kez sınıflandır ve etiketi yeniden kullan.

  4. Poz ve izleme için her kişinin anahtar nokta geçmişini izleme kimliğine göre anahtarla.

Uygulamalı

Çoğu ürünü kapsayan iki yaklaşım#

İkisi de aynı kare ve aynı Ultralytics API ile başlar; ikinci aşamanın neyi kullandığı konusunda ayrılır. Nesne izleme için izleyici seçimi, ayarlama ve akış yönetimi ByteTrack, BoT-SORT ve TrackTrack ile Nesneleri İzleme konusuna girer; burada görevleri birleştirmeye odaklanıyoruz.

Tespit et, ardından kırpımları sınıflandır#

from ultralytics import YOLO

detector = YOLO("yolo26n.pt")
classifier = YOLO("yolo26n-cls.pt")  # swap in your own fine-tuned classifier

result = detector("https://ultralytics.com/images/bus.jpg", conf=0.5)[0]
crops, labels = [], []
for box in result.boxes:
    x1, y1, x2, y2 = box.xyxy[0].int().tolist()
    crops.append(result.orig_img[y1:y2, x1:x2])  # BGR numpy crop, same as the detector saw
    labels.append(result.names[int(box.cls)])

for label, crop_result in zip(labels, classifier(crops, verbose=False)):  # one batched call for all crops
    probs = crop_result.probs
    print(f"{label:6s} -> {crop_result.names[probs.top1]} ({float(probs.top1conf):.2f})")
bus    -> bullet_train (0.31)
person -> sweatshirt (0.24)
person -> abaya (0.50)
person -> loudspeaker (0.49)
person -> frilled_lizard (0.24)

İşlem hattı doğru: kırpımlar orig_img üzerinden tam çözünürlükte ve dedektörün kendi piksel koordinatlarında alınır; sınıflandırıcı da hepsini tek bir toplu çağrıda işler. Etiketler bilerek anlamsız seçildi.

İkinci aşamanın başarısı, kırpılmış görüntülerle ne kadar eşleştiğine bağlıdır

ImageNet sınıflandırıcısı, sıkı kırpılmış sokak figürleriyle hiç eğitilmedi; bu yüzden tahminde bulunuyor — 0.50'nin üzerinde güven değerinin olmadığını unutma. Temiz ürün fotoğraflarıyla eğitilmiş bir sınıflandırıcıya bulanık dedektör kırpımları verildiğinde de aynı durum yaşanır.

İkinci aşama veri kümesini kendi kırpımlarından oluştur#

Eşleşen veriler elde etmenin güvenilir yolu, dedektörün bu verileri üretmesini sağlamaktır:

Sınıflandırıcı klasörlerine ayrılmaya hazır, havaalanı konveyör görüntülerinden Ultralytics YOLO26 tarafından kaydedilmiş numaralı bavul kırpımlarının ızgarası

  1. Temsili görüntüler üzerinde dedektörü save_crop=True ile çalıştır; örneğin yolo predict model=best.pt source=footage/ save_crop=True kullan. Kırpımlar runs/detect/predict/crops/<class>/ içine kaydedilir.
  2. Her sınıfa ait kırpımları ayrıntılı sınıf klasörlerine yerleştir (helmet_ok/, helmet_missing/) — bu, önceki derste gördüğün sınıflandırma düzeninin aynısıdır.
  3. Sınıflandırıcıyı bu klasörler üzerinde eğit ve farklı bir gün ya da kameradan alınan kırpımlarla doğrula.

Dedektör kırpımları oluşturduğu için bunlar gerçek çerçeveleme, bulanıklık ve örtülme özelliklerini taşır — yani sınıflandırıcının üretimde göreceği görüntüleri yansıtır — ve bunlar üzerinde ince ayar yapmak olağan bir aktarım öğrenimi uygulamasıdır.

Her izleme kimliği için bir kez sınıflandır#

Kask kareler arasında değişmediğinden her karedeki her kutuyu sınıflandırmak boşa harcanan iştir. Tespitleri izle ve her yeni izleme kimliğini bir kez sınıflandır:

from ultralytics import YOLO

detector = YOLO("yolo26n.pt")
classifier = YOLO("yolo26n-cls.pt")  # swap in your own fine-tuned classifier
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/decelera_portrait_min.mov"

labels = {}  # track id -> (label, confidence), filled once per person
frames = boxes_seen = 0
for result in detector.track(source, classes=[0], stream=True, persist=True, verbose=False):  # people only
    frames += 1
    if result.boxes.id is None:
        continue
    for track_id, (x1, y1, x2, y2) in zip(result.boxes.id.int().tolist(), result.boxes.xyxy.int().tolist()):
        boxes_seen += 1
        if track_id not in labels:  # first sighting: classify once, then reuse
            probs = classifier(result.orig_img[y1:y2, x1:x2], verbose=False)[0].probs
            labels[track_id] = (classifier.names[probs.top1], round(float(probs.top1conf), 2))

print(f"{frames} frames, {boxes_seen} person boxes, {len(labels)} classifier calls")
print(labels)
11 frames, 33 person boxes, 3 classifier calls
{1: ('basketball', 0.51), 2: ('maillot_(tights)', 0.14), 3: ('bulletproof_vest', 0.23)}

11 karelik bir klipte 33 yerine üç sınıflandırıcı çağrısı; uzun bir akışta kazanç, kişi görüntüde kaldığı her kareyle birlikte artar. Etiketler hâlâ ImageNet tahminleridir — ince ayar yaptığın sınıflandırıcıyı kullanırsan kişi başına yanıta dönüşürler.

Karar kuralı

Nitelik, nesne görüntüdeyken değişemiyorsa (kask türü, araç markası, ürün SKU'su) her iz için bir kez sınıflandır. Değişebiliyorsa (kapının açık olması, elin kaldırılması) her N karede bir yeniden sınıflandır. İlk görünümler genellikle küçük ve bulanıksa bunun yerine, izin kutusunun en büyük olduğu karede sınıflandır.

Poz ve izleme#

Vücut iskeleti, 110 derecelik dirsek açısı ve tekrar sayacıyla şınavları sayan Ultralytics YOLO poz kestirimi

İzleme modu, kişi başına kalıcı bir result.boxes.id ekler; böylece anahtar noktalar zaman serisine dönüşür. Bu betik, aynı kısa videoda her kişinin sol diz açısını kaydeder:

from collections import defaultdict

import numpy as np

from ultralytics import YOLO

def angle(a, b, c):
    """Angle at b, in degrees, formed by points a-b-c."""
    ba, bc = a - b, c - b
    return np.degrees(np.arccos(np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))))

model = YOLO("yolo26n-pose.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/decelera_portrait_min.mov"
HIP, KNEE, ANKLE = 11, 13, 15  # left leg, COCO keypoint order
knee_angles = defaultdict(list)  # track id -> knee angle per frame

for result in model.track(source, stream=True, persist=True, verbose=False):
    if result.boxes.id is None:  # no confirmed tracks in this frame
        continue
    kpts = result.keypoints.cpu().numpy()
    for track_id, xy, conf in zip(result.boxes.id.int().tolist(), kpts.xy, kpts.conf):
        if conf[[HIP, KNEE, ANKLE]].min() > 0.5:  # skip occluded legs
            knee_angles[track_id].append(angle(xy[HIP], xy[KNEE], xy[ANKLE]))

for track_id, angles in sorted(knee_angles.items()):
    print(f"id {track_id}: {len(angles)} frames, left knee {min(angles):.0f}-{max(angles):.0f} deg")
id 1: 11 frames, left knee 170-176 deg
id 2: 11 frames, left knee 96-118 deg
id 3: 11 frames, left knee 177-180 deg

2 numaralı kimliğin bükülü dizi klipteki oturan kişiyle eşleşiyor; diğer iki kişi yaklaşık 180° açıyla bacaklarını düz tutarak ayakta duruyor. Buradan tekrar sayacı, her kimliğin serisindeki eşik geçişlerini sayar — bir açının altına inip başka bir açının üzerine tekrar çıkmayı — bu özellik antrenman izleme çözümünde kullanıma hazır hâle getirilmiştir.

Zincirin maliyetini hesapla#

Maliyet etkeniTespit et, sonra sınıflandırPoz ve izleme
Şuna göre artarkare başına nesne sayısıkareler
Temel kaldıraçkırpımları toplu işle; her track ID için bir kez sınıflandır ve sonucu önbelleğe alHareket yavaşsa imgsz veya vid_stride değerini düşür
Hata modusınıflandırıcı, eğitim verilerindekilere benzemeyen kırpımları görürID değişimleri, tek bir kişinin geçmişini ikiye böler
ID değişimleri geçmişi bozar

Belirti: Tekrar sayacı setin ortasında sıfırlanıyor veya aynı kişi iki farklı ID ile görünüyor. Neden: İzleyici kişiyi kaybetti (örtüşme, hızlı dönüş) ve yeni bir ID atadı. Çözüm: Üretim kursunda anlatıldığı gibi izleyiciyi ayarla ve ID başına mantığın kısa kesintileri tolere etmesini sağla — önbelleğe alınmış sınıflandırıcı etiketi yeni ID için yeniden hesaplanır.

Dene

Dedektör çağrısına save_crop=True ekleyerek two_stage.py komutunu çalıştır ve kırpımların sınıf başına klasörlerde göründüğünü doğrula. classify_per_track.py komutunu çalıştır ve sınıflandırıcı çağrılarını kişi kutularıyla karşılaştır. Ardından pose_track.py kodunu, her track ID için diz açısı 120°'nin altında olan kare sayısını yazdıracak şekilde genişlet.

Tamamlanma ölçütü
Aşağıdakilerin tümü doğru olduğunda dersi tamamlamış olursun.
  • Kırpımlar crops/<class>/ altında bulunuyor ve bunları sınıflandırıcı klasörlerine nasıl sıralayacağını açıklayabiliyorsun.

  • Örnek klipte track başına önbelleğe almanın kaç sınıflandırıcı çağrısı tasarrufu sağladığını belirtebiliyorsun.

  • pose_track.py, her track ID için kare sayısını ve 120°'nin altındaki açı sayısını içeren bir satır yazdırır.

Çözümü göster

Dedektör çağrısına save_crop=True ekle:

result = detector("https://ultralytics.com/images/bus.jpg", conf=0.5, save_crop=True)[0]

Kırpımlar runs/detect/predict*/crops/person/ ve crops/bus/ altında görünür (tahmin çalıştırmalarında klasör adı her seferinde artar). Track başına önbelleğe alma, 11 karelik bir klipte 33 kişi kutusu için 3 sınıflandırıcı çağrısı yaptı ve 30 çağrı tasarruf sağladı.

Diz sayımı için pose_track.py kodunun son döngüsünü değiştir:

for track_id, angles in sorted(knee_angles.items()):
    bent = sum(a < 120 for a in angles)
    print(f"id {track_id}: {len(angles)} frames, {bent} with knee below 120 deg")
id 1: 11 frames, 0 with knee below 120 deg
id 2: 11 frames, 11 with knee below 120 deg
id 3: 11 frames, 0 with knee below 120 deg

Oturan kişi olan ID 2, 11 karenin tamamında 120°'nin altındadır; ID 1 ve 3 hiçbir karede bu durumda değildir.

Sırada ne var

Son olarak, her görevi uygun metrikle değerlendirip dağıtım için dışa aktarırız.

İlerlemeni kaydetmek için giriş yap