Ultralytics YOLO27: Bekleme listesine katıl.
Get Started
Üretimde Ultralytics YOLO·Ölçekle ve Gözlemle·Ders 7/9
Dersorta düzey

Çoklu Akışta Çıkarım

Toplu çıkarım, sınırlı kuyruklar ve ölçülmüş kare düşüşleriyle birkaç kamerayı tek bir model üzerinden çalıştır.

Tek kamerayla başa çıkabilen bir sistem, dört kamerayla dakikalarca geride kalabilir ve hiçbir şey çökmez: bellek tükenene kadar sonuçlar giderek daha geç gelir. Burada kullanılan dizüstü bilgisayarın CPU'su tek bir 30 FPS akışı işleyebildi; dört akışta karelerin yarısı, sekiz akışta ise üçte ikisinden fazlası düştü. Bu ders, donanım kapasitesini belirlemenin yolu olduğu için bu sayıyı ölçen işlem hattını oluşturuyor.

Sonuç

İki veya daha fazla akışı tek bir modelden geçir, akış başına işlenen ve düşen kareleri raporla ve donanımının yetişemediği akış sayısını bul.

Hızlı Başlangıç
Nasıl ilerleyeceğini zaten biliyorsan, işte kısa sürüm.
  1. Yerleşik yöntem: kaynakları bir .streams dosyasında listele ve model.track("cams.streams", stream=True) çağrısını yap.

  2. stream_buffer=False (varsayılan), her akış için yalnızca en yeni kareyi tutar; True, kaydedilmiş dosyaların her karesini işler.

  3. Özel yöntem: kod çözücü iş parçacıkları, tek bir sınırlı kuyruk ve modelin sahibi olan, toplu işlem yapan tek bir tüketici döngüsü.

  4. Her akış için düşen kareleri say; kapasite, FPS hedefinde sıfır kare düşüşüyle desteklenen en yüksek akış sayısıdır.

Uygulamalı

Zaman Nerede Harcanıyor#

NVIDIA DeepStream, bir Jetson üzerinde dört kamera akışında Ultralytics YOLO'yu her akışta yaklaşık 12 FPS hızında çalıştırıyor; algılamaların 2×2 ızgarasının yanında her akışa ait PERF günlükleri bulunuyor

Her kare dört aşamadan geçer:

AşamaTipik maliyetÇalıştığı birim
Kod çözme (RTSP veya dosya → dizi)1080p kare başına 5–20 msCPU
Ön işleme (letterbox, normalleştirme)yaklaşık 1 msCPU
Çıkarımbirkaç ms (GPU) ile onlarca ms (CPU) arasıHızlandırıcı
Son işleme, takip, çıktı hedefleribirkaç ms; yavaş veritabanlarında daha uzunCPU

Bunları tek bir iş parçacığında, kamera kamera art arda çalıştırırsan kareler çözümlenirken hızlandırıcı boşta kalır. Çözüm, yukarıdaki DeepStream işlem hattının yaptığı gibi aşamaları birbirinden ayırmaktır: dört akış tek bir aygıtı paylaşır ve her biri kendi FPS değerini (burada yaklaşık 12) raporlar. Python'da aynı yapı şöyle kurulur:

Yerleşik Yöntem: .streams Dosyası#

Ultralytics birden fazla kaynağı senin için toplu işler. Her satıra bir kaynak gelecek şekilde bir .streams dosyası oluştur ve dosyayı Predict veya Track moduna ilet. Her akış kendine ait bir okuyucu iş parçacığı ve izleyici alır; her yinelemede tüm akışlar için tek bir toplu ileri geçiş çalıştırılır:

from collections import Counter
from pathlib import Path

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

base = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
clips = [safe_download(base + name) for name in ("solutions_ci_demo.mp4", "ground-vehicles-airport.mp4")]
Path("cams.streams").write_text("\n".join(str(c) for c in clips))  # production: her satırda bir rtsp:// URL'si

model = YOLO("yolo26n.pt")  # production ortamında optimize edilmiş dışa aktarımını kullan
frames = Counter()
for r in model.track("cams.streams", stream=True, stream_buffer=True, tracker="bytetrack.yaml", verbose=False):
    frames[Path(r.path).name] += 1  # r.path, bu sonucun hangi akıştan geldiğini gösterir
print(frames)
Counter({'solutions_ci_demo.mp4': 62, 'ground-vehicles-airport.mp4': 62})

En kısa akış sona erdiğinde yineleme durduğu için her iki akış da 62 sonuç üretti (havaalanı klibinde 150 kare var). stream_buffer=True her kareyi işledi; bu, kaydedilmiş dosyalar için doğru seçimdir. Canlı kameralar için varsayılan False ayarını kullan; böylece yavaş bir model geride kalmak yerine eski kareleri atlar. Akış başına en yüksek verim için bunu bir TensorRT veya OpenVINO dışa aktarımıyla birlikte kullan.

Özel Yöntem: Düşen Kare Sayaçlarıyla Üretici-Tüketici#

Üç zamanlama diyagramı: tek iş parçacıklı bir işlem her G/Ç isteğinin tamamlanmasını sırayla bekler; çok iş parçacıklı bir işlem G/Ç beklemelerini üst üste bindirir, ancak aynı anda yalnızca GIL'i elinde tutan iş parçacığı işlem yapar; asyncio beklemeleri tek iş parçacığında üst üste bindirir

Diyagram, bu işlem hattında iş parçacıklarının neden işe yaradığını gösteriyor. Kod çözme çoğunlukla beklemeden ibarettir (pembe G/Ç çubukları); OpenCV ve PyTorch çalışırken Python GIL'ini serbest bıraktığı için kod çözücü iş parçacıkları iyi örtüşür (b paneli). Yoğun Python son işleme aşamaları örtüşmez; bunları ayrı süreçlere taşı.

Kendi çıktı hedeflerine, kamera başına özel mantığa veya açık düşen kare ölçümlerine ihtiyacın varsa işlem hattını kendin kur. Kod çözücü iş parçacıkları modele hiçbir zaman dokunmaz; modelin sahibi tek bir tüketici döngüsüdür. Bu, iş parçacığı güvenli çıkarım kurallarına uymanı da sağlar:

import queue
import threading
import time

import cv2

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

base = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
SOURCES = [str(safe_download(base + n)) for n in ("solutions_ci_demo.mp4", "ground-vehicles-airport.mp4")]
frames = queue.Queue(maxsize=16)  # bounded: memory and latency stay flat under overload
dropped = [0] * len(SOURCES)

def decode(stream_id, source):
    """Producer: read frames at the camera's pace and never block on a full queue."""
    cap = cv2.VideoCapture(source)
    interval = 1 / (cap.get(cv2.CAP_PROP_FPS) or 30)
    while True:
        ok, frame = cap.read()
        if not ok:
            break
        try:
            frames.put_nowait((stream_id, frame))
        except queue.Full:
            dropped[stream_id] += 1  # consumer is behind: drop rather than serve stale frames
        time.sleep(interval)  # pace files like a live camera; remove for RTSP sources
    cap.release()

model = YOLO("yolo26n.pt")  # one model, used only by the consumer loop below
model.predict("https://ultralytics.com/images/bus.jpg", verbose=False)  # warm up before cameras start

decoders = [threading.Thread(target=decode, args=(i, s), daemon=True) for i, s in enumerate(SOURCES)]
for t in decoders:
    t.start()

processed = [0] * len(SOURCES)
while any(t.is_alive() for t in decoders) or not frames.empty():
    batch = []
    while len(batch) < 8:  # gather up to 8 frames from any camera
        try:
            batch.append(frames.get(timeout=0.05))
        except queue.Empty:
            break
    if not batch:
        continue
    results = model([frame for _, frame in batch], verbose=False)  # one batched forward pass
    for (stream_id, _), r in zip(batch, results):
        processed[stream_id] += 1  # per-stream postprocessing and sinks go here

print(f"processed={processed} dropped={dropped}")
processed=[56, 131] dropped=[6, 19]

İki klipte 62 ve 150 kare var ve her kare ya işleniyor ya da düşüyor (56 + 6 = 62, 131 + 19 = 150); dolayısıyla sayaçlar her şeyi hesaba katıyor. 212 karenin 25'i düştü: bu dizüstü bilgisayarın CPU'su PyTorch'ta yolo26n modelini iki 30 FPS akışında çalıştıramıyor. Daha fazla kamerayı taklit etmek için klipleri tekrarlayarak tek seferde yaptığımız deneme:

Akışlar (her biri 30 FPS)Düşen kareler
10%
212%
451%
869%

Ölçümlere Göre Kapasite Planla#

Aygıt başına akış sayısı ≈ sürdürülebilir verim (modelin, belirlediğin toplu iş boyutunda saniyede işlediği kare sayısı) ÷ akış başına gereken FPS. Gerçek donanımda önceki derslerde optimize edilmiş dışa aktarımı kullanarak verimi ölç, ani yükler için %20–30 kapasite payı bırak ve düşen kare sayısı sıfırın üzerine çıktığında uyarı ver. Daha yüksek çözünürlük, piksel sayısıyla kabaca orantılı maliyet getirir: 1280×1280, 640×640'ın yaklaşık 4 katı işlem gücü gerektirir. Büyük filolar için Triton Inference Server ve Jetson'da DeepStream, doğal üretim ortamı seçenekleridir.

Yaygın Hatalar#

Python video listesi aktarma

Belirti: model.track(["cam1.mp4", "cam2.mp4"]), listeyi akışlar olarak değil, görüntü grubu olarak değerlendiriyor. Çözüm: Kaynakları, her satırda bir tane olacak şekilde .streams dosyasına yaz.

Kaydedilmiş dosyalarda varsayılan stream_buffer

Belirti: Her dosyadan yalnızca bir veya iki kare geliyor. Çözüm: stream_buffer=False eski kareleri bırakır ve dosya okuyucular modelden daha hızlı ilerler; dosyalar için stream_buffer=True ayarını yap, canlı kameralar için varsayılanı koru.

Çıkarım iş parçacıkları arasında tek bir modeli paylaşma

Belirti: Yük altındayken ara sıra yanlış veya birbirine karışmış sonuçlar alınıyor. Çözüm: Tek bir YOLO örneğini aynı anda birden fazla iş parçacığından asla çağırma. Yukarıda anlatıldığı gibi tek bir tüketicinin model örneğinin sahibi olmasını sağla veya her çıkarım iş parçacığının içinde bir model oluştur.

Her kareyi kuyruğa alma

Belirti: Gecikme her dakika artıyor ve süreç kapanana kadar bellek kullanımı yükseliyor. Çözüm: Dolduğunda öğe düşüren sınırlı bir kuyruk ve bir düşürme sayacı kullan. Gerçek zamanlı sistemler, gecikmek yerine aşırı yük sırasında kareleri düşürmelidir.

Dene

producer_consumer.py komutunu kendi kaynaklarınla (veya iki demo klibiyle) çalıştır, ardından kare düşüşleri başlayana kadar çözüm betiğiyle akış sayısını artır. Donanımında akış sayısına göre düşen kare yüzdesini ve hedef FPS değerinde sıfır düşüşle desteklenen en yüksek akış sayısını içeren bir tablo sun.

Tamamlanma ölçütü
Aşağıdakilerin tümü doğru olduğunda dersi tamamlamış olursun.
  • İki veya daha fazla akış tek bir modelden geçiyor ve her sonuç kaynak akışıyla ilişkilendiriliyor.

  • İşlem hattın sınırlı bir kuyruk kullanıyor ve akış başına işlenen ve düşen kareleri raporluyor; bu sayılar okunan karelerin toplamına eşit.

  • Ölçüm yaparak donanımının hangi akış sayısında kare düşürmeye başladığını belirtebiliyorsun.

Çözümü göster

SOURCES satırının hemen altına producer_consumer.py dosyasına şu satırları ekleyerek akış sayısını bir komut satırı argümanı hâline getir:

import sys

N = int(sys.argv[1]) if len(sys.argv) > 1 else 2  # benzetilecek kamera sayısı
SOURCES = [SOURCES[i % len(SOURCES)] for i in range(N)]

Ardından farklı değerleri dene:

for n in 1 2 4 8; do python producer_consumer.py $n; done
processed=[62] dropped=[0]
processed=[56, 131] dropped=[6, 19]
processed=[23, 82, 23, 81] dropped=[39, 68, 39, 69]
processed=[14, 53, 13, 53, 13, 52, 13, 53] dropped=[48, 97, 49, 97, 49, 98, 49, 97]

Bu makine için sonuç tek akış: kare düşüşleri iki akışta başlıyor. Dört kameraya hizmet vermek için daha hızlı bir çalışma zamanına aktarım yap (3. ve 4. dersler), imgsz değerini düşür veya bir hızlandırıcıya geç; ardından denemeyi yeniden yap.

Sırada ne var

Artık ölçekli çalışabiliriz. Sırada, fark ettirmeden kötüleştiğinde sistemi anlayacak kadar iyi izlemek var.

İlerlemeni kaydetmek için giriş yap