Sayım, Isı Haritaları ve Hız Tahmini
Takibin üzerine çizgi sayacı, bölge doluluk ölçümü, ısı haritaları ve hız tahminleri kur; ardından bunları dürüstçe kalibre et.
Üretimdeki görüntü işleme sistemlerinin çoğu model.predict() değildir. Bunlar, nesnelerin geçtiği bir çizgi, nesnelerin bulunduğu bir bölge ve pikselleri metreye dönüştüren bir ölçek gibi birkaç geometrik işlemle bir takipçinin birleşimidir. Sonuçların yanlış gittiği yer geometridir: bu dersteki örnek klipte bir hız tahmin aracı, yanlış tahmin edilmiş tek bir sayı nedeniyle yürüyen yayaların gerçek hızlarının dörtte biri olan 1 km/sa hızla yürüdüğünü bildiriyor.
Uygulamalı
Takip ve Geometri#
ultralytics.solutions içindeki her çözüm sınıfı, dahili olarak Takip modunu çalıştırır ve ardından izin geçmişine kendi geometrisini uygular. Ek paketleri bir kez pip install "ultralytics[solutions]" ile yükle.
| Sınıf | Geometri | Temel sonuçlar |
|---|---|---|
ObjectCounter | 2 nokta = çizgi, 3 veya daha fazla nokta = çokgen | in_count, out_count, classwise_count |
RegionCounter | Bir veya daha fazla adlandırılmış çokgen | region_counts |
Heatmap | Kutu alanlarını zaman içinde biriktirir | plot_im (bölge verilirse sayımları da içerir) |
SpeedEstimator | İzin yer değiştirmesi × meter_per_pixel | speed_dict (iz kimliği başına km/sa) |
Çözümler varsayılan olarak tracker="tracktrack.yaml" kullanır; önceki derste doğruladığın takipçiyi geçir.
Çizgi Geçişlerini Say#

Sayım çizgisi bir doğru parçasıdır. Bir nesne, kutusunun merkezi iki kare arasında çizgiyi geçtiğinde bir kez sayılır; GİRİŞ veya ÇIKIŞ olacağını nesnenin geldiği taraf belirler. Yukarıdaki konveyörde bu nedenle takip gerekir: kimlik olmadan #015 numaralı kutu çizgiye dokunduğu her karede yeniden sayılırdı.
import cv2
from ultralytics import solutions
from ultralytics.utils.downloads import safe_download
video = safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4")
cap = cv2.VideoCapture(str(video)) # 640x360, 30 FPS, 62 frames
w, h, fps = (int(cap.get(p)) for p in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
writer = cv2.VideoWriter("counted.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
counter = solutions.ObjectCounter(
model="yolo26n.pt",
region=[(0, 180), (640, 180)], # two points: a horizontal line across the middle of the frame
classes=[0], # person
tracker="bytetrack.yaml",
verbose=False,
)
while True:
ok, frame = cap.read()
if not ok:
break
results = counter(frame) # detect + track + count in one call
writer.write(results.plot_im)
cap.release()
writer.release()
print(f"in={results.in_count} out={results.out_count} classwise={results.classwise_count}")in=2 out=2 classwise={'person': {'IN': 2, 'OUT': 2}}Klipteki iki saniye içinde dört kişi meydanın ortasından geçti: her yönde ikişer kişi. Varsayılan TrackTrack yapılandırmasıyla aynı betik in=0 out=1 sonucunu verir; çünkü daha az iz, daha az geçiş demektir. Bir sayıya güvenmeden önce counted.mp4 dosyasını aç ve her geçişi gözünle doğrula.

Yolda aynı çizgi trafiği yönüne göre ayırır: yukarı doğru geçen araçlar GİRİŞ, aşağı doğru geçenler ÇIKIŞ olarak sayılır. Çizgiyi nesnelerin düzgün ve dik açıyla geçtiği bir yere; kadraj kenarlarından, engelleyicilerden ve kutuların çok küçük kaldığı yolun uzak ucundan uzağa yerleştir.
Bir Bölgedeki Doluluğu Say#
Çokgen bir bölge, "Şu anda içeride kaç nesne var?" sorusunu yanıtlar. Bölge sayımı kılavuzunda kullanıma hazır bir RegionCounter bulunur; temelinde, takip edilen her kutu için yapılan tek bir noktanın çokgen içinde olup olmadığı testi vardır:
from shapely.geometry import Point, Polygon
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
zone = Polygon([(100, 100), (500, 100), (500, 300), (100, 300)]) # pixel coordinates
peak = 0
for r in model.track(video, stream=True, tracker="bytetrack.yaml", classes=[0], verbose=False):
if not r.boxes.is_track:
continue
inside = sum(zone.contains(Point(cx, cy)) for cx, cy in r.boxes.xywh[:, :2].tolist())
peak = max(peak, inside)
print(f"peak occupancy: {peak} people inside the zone")peak occupancy: 10 people inside the zonexywh[:, :2] her kutunun merkezidir. Kuyruklar ve bekleme süresi için bölgeye giriş zamanını her kimlik bazında sakla.
Hızı Tahmin Et#

Yukarıdaki her etiket, aracın arkasındaki izden gelir: izin bilinen sayıda kare boyunca piksel cinsinden aldığı mesafe. Hız = piksel mesafesi × piksel başına metre ÷ geçen süre. Sonucun doğru olup olmadığını iki girdi belirler:
- Piksel başına metre. Sahnedeki bilinen bir uzunluğu (yer karosu, şerit işareti, park yeri) nesnelerin hareket ettiği derinlikte ölç. Açılı kameralar için bunun yerine zemin düzlemini dört noktalı homografiyle eşle.
- Gerçek çekim hızı. Hız FPS ile ölçeklenir; yeniden kodlanan veya yavaşlatılan görüntüler tüm hızları aynı oranda değiştirir.
Bu döngü, her kutunun alt orta noktasını (nesnenin zemine temas ettiği yeri) ölçer ve her iz için 10 kare üzerinden ortalama alır:
from collections import defaultdict, deque
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
fps = 30 # the real capture rate, not the playback rate
pixels_per_meter = 33 # example: a 1.2 m floor feature spans 40 px; measure your own scene
prev = {} # track ID -> last bottom-center point
window = defaultdict(lambda: deque(maxlen=10)) # about 0.3 s of speeds per track at 30 FPS
for r in model.track(video, stream=True, tracker="bytetrack.yaml", classes=[0], verbose=False):
if not r.boxes.is_track:
continue
for (x1, y1, x2, y2), tid in zip(r.boxes.xyxy.tolist(), r.boxes.id.int().tolist()):
cx, cy = (x1 + x2) / 2, y2 # bottom-center sits on the ground plane
if tid in prev:
px, py = prev[tid]
meters = ((cx - px) ** 2 + (cy - py) ** 2) ** 0.5 / pixels_per_meter
window[tid].append(meters * fps * 3.6) # km/h
prev[tid] = (cx, cy)
smoothed = sorted(sum(s) / len(s) for s in window.values() if len(s) == s.maxlen)
print(f"{len(smoothed)} tracks; median {smoothed[len(smoothed) // 2]:.1f} km/h")29 tracks; median 2.1 km/hİnsanlar 4–5 km/sa hızla yürür; dolayısıyla 2.1 km/sa, örnekteki ölçeğin bu sahne için yanlış olduğunu gösterir: pixels_per_meter değeri burada ölçülmedi. Bilinen bir hızı medyanla karşılaştırarak yaptığın bu makullük kontrolü, önemli olan alışkanlıktır.
solutions.SpeedEstimator, aynı fikri üretim kullanımı için uygular: her izin kutu merkezini max_hist kare boyunca ölçer, meter_per_pixel ile dönüştürür ve her kimlik için tek bir hız belirler (üst sınır: max_speed):
import cv2
from ultralytics import solutions
from ultralytics.utils.downloads import safe_download
video = safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4")
cap = cv2.VideoCapture(str(video))
speed = solutions.SpeedEstimator(
model="yolo26n.pt",
classes=[0],
tracker="bytetrack.yaml",
fps=30, # the real capture rate, not the playback rate
meter_per_pixel=0.03, # a guess: a ~0.45 m shoulder width spans ~15 px here
max_hist=15, # 0.5 s of history before a speed is locked
verbose=False,
)
speeds = {}
while True:
ok, frame = cap.read()
if not ok:
break
speeds.update(speed(frame).speed_dict) # {track_id: km/h} once locked
cap.release()
kmh = sorted(speeds.values())
print(f"{len(kmh)} tracks; median {kmh[len(kmh) // 2]} km/h, range {kmh[0]}-{kmh[-1]} km/h")25 tracks; median 1 km/h, range 0-4 km/hYine yaklaşık 4 kat sapma var; nedeni de aynı: vücut boyutundan tahmin edilen bir ölçek. Bunun gibi üstten çekim yapan kameralar için kutu merkezi uygundur; açılı kameralarda alt orta nokta döngüsünü veya bir homografiyi tercih et.
Isı Haritası Oluştur#

Bir ısı haritası, her karede izlenen her kutunun altında ısı biriktirir; böylece en yoğun trafiği taşıyan şeritler veya koridorlar en parlak şekilde görünür. Haritaya bir region ver; yukarıdaki otoyol örneğinde olduğu gibi nesneleri de sayar:
import cv2
from ultralytics import solutions
from ultralytics.utils.downloads import safe_download
video = safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4")
cap = cv2.VideoCapture(str(video))
heatmap = solutions.Heatmap(
model="yolo26n.pt",
classes=[0],
tracker="bytetrack.yaml",
colormap=cv2.COLORMAP_PARULA,
verbose=False,
)
while True:
ok, frame = cap.read()
if not ok:
break
results = heatmap(frame)
cap.release()
cv2.imwrite("heatmap.jpg", results.plot_im) # Birikmiş ısı katmanının bulunduğu son kareSaatlerce süren görüntülerde aynı katman, yoğun koridorları, kullanılmayan bölgeleri ve insanların gerçekten izlediği yolları gösterir. Antrenman izleme ve Streamlit canlı çıkarım arayüzü aynı yapı taşlarıyla oluşturulur.
Yaygın Hatalar#
Belirti: Sayaç işlem hattı beklenen FPS'nin yarısında çalışıyor. Çözüm: ObjectCounter, Heatmap ve SpeedEstimator algılama ve izlemeyi kendi içinde yapar. Bunları kendi model.track() döngüne sarmalama ve r.orig_img aktarma; ham kareleri besle.
Belirti: Yayalar 1 km/sa veya arabalar 300 km/sa hızla gidiyor. Çözüm: Nesnelerin hareket ettiği bilinen bir uzunluğu kullanarak meter_per_pixel değerini ölç, kameranın gerçek FPS değerini doğrula ve medyanı bilinen bir hızla (yürüme hızı veya belirtilen hız sınırı) karşılaştır.
Nesne kameraya yaklaşıp kutusu büyüdükçe geometrik merkez yükselir. Alt orta nokta, nesnenin yere temas ettiği yeri yaklaşık olarak gösterir; bu noktadan ölçülen mesafeler gerçek hareketi çok daha yakından takip eder.
Çözümü göster
Karede ölçebileceğin bir şeyden kalibrasyon yap, ardından makullük kontrolü uygula:
# Two points along a floor feature whose real length you know (read them off a saved frame)
(x1, y1), (x2, y2) = (100, 300), (140, 300)
real_length_m = 1.2 # e.g. measured tile width or lane marking
pixels = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
print(f"pixels_per_meter = {pixels / real_length_m:.1f}, meter_per_pixel = {real_length_m / pixels:.4f}")pixels_per_meter = 33.3, meter_per_pixel = 0.0300pixels_per_meter öğesini speed_loop.py içine (veya meter_per_pixel öğesini SpeedEstimator içine) koyup yeniden çalıştır. Yürüyenlerin hızları hâlâ 4–5 km/sa değerinden çok farklı görünüyorsa ölçek yanlış derinlikte ölçülmüştür veya FPS yanlıştır: yarı hızda oynatılan bir klip tüm okumaları yarıya indirir. Demo klibinde her iki betik de aynı tahmini ölçeği kullandı (piksel başına 0.03 m, yani metre başına 33 piksel) ve 2–4 kat düşük hızlar ölçtü; dolayısıyla ya burada bir piksel tahminden daha fazla alanı kapsıyor ya da klibin gerçek çekim hızı 30 FPS değil. Hangisinin geçerli olduğunu ancak sahnede ölçüm yapmak belirler.