Asosiy qismga o'tish

Zonalar va chiziqlar: sanash, vaqt va qoidalar

14-bob 7 kod bloki 4204 so'z

Bu bobning kodi: kod/14_zona_chiziq.py — kitob papkasidan ishga tushiring: python kod/14_zona_chiziq.py

Trek lardan biznes savollariga

Mijoz trek larni so'ramaydi. U so'raydi: "Bugun do'konga nechta odam kirdi?", "Kassa oldida odamlar qancha kutyapti?", "Qurilishda kim kaskasiz yuribdi?". Bu bobda trek larni shunday savollarga javobga aylantiramiz — uchta oddiy geometrik qurol bilan:

  1. Chiziq — kim, qaysi tomonga kesib o'tdi (kirish/chiqish soni).
  2. Zona (ko'pburchak) — kim ichida va qancha vaqt (band bo'lganlik, kutish vaqti).
  3. Qoida — box lar orasidagi munosabat ("odam bor, uning boshida kaska yo'q").

Har bir natijani MOT17-09 ning qo'lda belgilangan javobi (GT) bilan tekshiramiz: tracker orqali sanalgan son haqiqatga qanchalik yaqin?

Bitta muhim tanlov: odamning qaysi nuqtasi? Chiziq va zonalar polga chiziladi, shuning uchun box markazi emas, oyoq nuqtasi — box pastki tomonining o'rtasi. Box markazi odam bo'yiga qarab balandda turadi va perspektivada polga to'g'ri kelmaydi.

kod/14_zona_chiziq.pyyangi fayl · 1-qator: from kitob_yordam import fig, show
python36 qator
import inspect
from pathlib import Path

import cv2
import numpy as np
import pandas as pd
import torch
import torchvision
import matplotlib.pyplot as plt
from ultralytics import YOLO
from ultralytics.engine.results import Boxes
from ultralytics.trackers import BYTETracker
from ultralytics.utils import LOGGER, YAML, IterableSimpleNamespace
from ultralytics.utils.checks import check_yaml

LOGGER.setLevel("ERROR")
MOT = Path("datasets/MOT17/train/MOT17-09-FRCNN")
FPS = 30
gt = np.loadtxt(MOT / "gt" / "gt.txt", delimiter=",")
piyoda = gt[(gt[:, 7] == 1) & (gt[:, 6] == 1)]
gt_trek = np.c_[piyoda[:, 0:2], piyoda[:, 2:4], piyoda[:, 2:4] + piyoda[:, 4:6]]   # (kadr, id, x1, y1, x2, y2)

# 11-bobdagi kabi: saqlangan YOLO11n box lari ustida ByteTrack
d = np.loadtxt("out/mot17/MOT17-09_yolo11n_1280.txt", delimiter=",")
det = {k: np.c_[d[d[:, 0] == k][:, 2:4], d[d[:, 0] == k][:, 2:4] + d[d[:, 0] == k][:, 4:6], d[d[:, 0] == k][:, 6]]
       for k in range(1, 526)}
BYTETracker.reset_id()
tracker = BYTETracker(args=IterableSimpleNamespace(**YAML.load(check_yaml("bytetrack.yaml"))), frame_rate=FPS)
trek = np.array([(k, int(t[4]), *t[:4]) for k in range(1, 526)
                 for t in tracker.update(Boxes(np.c_[det[k], np.zeros(len(det[k]))], (1080, 1920)), None)])

def oyoq(boxlar):
    """xyxy box lar -> oyoq nuqtalari (pastki tomon o'rtasi)."""
    return np.c_[(boxlar[:, 0] + boxlar[:, 2]) / 2, boxlar[:, 3]]

print(f"GT: {len(np.unique(gt_trek[:, 1]))} odam, {len(gt_trek)} box | ByteTrack: {len(np.unique(trek[:, 1]))} ID, {len(trek)} box")
Chiqish
GT: 26 odam, 5325 box | ByteTrack: 113 ID, 5324 box

Chiziqni kesib o'tish

Chiziq ikki nuqta bilan beriladi: A va B. P nuqta chiziqning qaysi tomonida? Buni vektor ko'paytma (cross product) belgisi aytadi:

c > 0 — P bir tomonda, c < 0 — boshqa tomonda, c = 0 — aynan chiziq ustida. Odam oyog'i ketma-ket ikki kadrda P₁ dan P₂ ga o'tdi. U AB kesmasini kesib o'tdimi? Ikki shart: P₁ va P₂ chiziqning turli tomonida, va A va B P₁P₂ yo'lining turli tomonida (aks holda odam chiziqning davomini, kesmadan tashqarida kesib o'tgan):

Yo'nalish esa P₂ ning belgisi: odam qaysi tomonga o'tib oldi. Bu usul chiziq qanday qiyalikda chizilishidan qat'iy nazar ishlaydi.

kod/14_zona_chiziq.pydavomi
python35 qator
def c(a, b, p):
    return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0])

def kesib_otish(treklar, A, B):
    """Har bir trek uchun AB kesmani kesib o'tish hodisalari: (kadr, id, yo'nalish)."""
    hodisalar = []
    for tid in np.unique(treklar[:, 1]):
        t = treklar[treklar[:, 1] == tid]
        t = t[np.argsort(t[:, 0])]
        f = oyoq(t[:, 2:6])
        for i in range(1, len(f)):
            if c(A, B, f[i - 1]) * c(A, B, f[i]) < 0 and c(f[i - 1], f[i], A) * c(f[i - 1], f[i], B) < 0:
                hodisalar.append((int(t[i, 0]), int(tid), "chapga" if c(A, B, f[i]) > 0 else "o'ngga"))
    return hodisalar

def moslashtir(gt_h, bizniki, oyna=30):
    """Har bir sanalgan hodisani yo'nalishi bir xil, vaqti +-1 soniya ichidagi GT hodisasi bilan juftlaydi."""
    band, togri = set(), 0
    for k, _, yon in bizniki:
        nomzod = [j for j, (gk, _, gy) in enumerate(gt_h) if j not in band and gy == yon and abs(gk - k) <= oyna]
        if nomzod:
            band.add(min(nomzod, key=lambda j: abs(gt_h[j][0] - k)))
            togri += 1
    return togri, len(bizniki) - togri, len(gt_h) - togri

chiziqlar = {"x = 700": ((700, 500), (700, 1080)), "x = 960 (o'rta)": ((960, 500), (960, 1080)),
             "x = 1300": ((1300, 450), (1300, 1080))}
qatorlar = []
for nom, (A, B) in chiziqlar.items():
    g, b = kesib_otish(gt_trek, A, B), kesib_otish(trek, A, B)
    tp, fp, fn = moslashtir(g, b)
    qatorlar.append({"chiziq": nom, "GT: chapga": sum(h[2] == "chapga" for h in g), "GT: o'ngga": sum(h[2] == "o'ngga" for h in g),
                     "tracker: chapga": sum(h[2] == "chapga" for h in b), "tracker: o'ngga": sum(h[2] == "o'ngga" for h in b),
                     "to'g'ri": tp, "ortiqcha": fp, "o'tkazib yuborilgan": fn})
show(pd.DataFrame(qatorlar))
chiziqGT: chapgaGT: o'nggatracker: chapgatracker: o'nggato'g'riortiqchao'tkazib yuborilgan
x = 7001123101122
x = 960 (o'rta)1102101110
x = 13001142101114

Bu yerda yo'nalish A → B vektoriga nisbatan: A tepada, B pastda bo'lgan vertikal chiziq uchun c > 0 — chap tomon. "Chapga" va "o'ngga" — odam o'tib olgan tomon.

Faqat jami sonni solishtirish aldashi mumkin: bitta ortiqcha va bitta o'tkazib yuborilgan hodisa bir-birini "yopib qo'yadi". Shuning uchun har bir sanalgan kesib o'tishni GT dagi aniq hodisa bilan juftladik — shunda "to'g'ri", "ortiqcha" va "o'tkazib yuborilgan" alohida ko'rinadi.

O'rta chiziqda tracker hamma haqiqiy kesib o'tishlarni topdi, bitta ortiqcha bilan. Kadrning o'ng chetiga yaqin chiziqda esa bir nechta kesib o'tish o'tkazib yuborildi: u yerda odamlar kadrga endigina kirayotgan yoki chiqayotgan bo'ladi, qisman ko'rinadi va trek hali boshlanmagan yoki allaqachon uzilgan bo'lishi mumkin. Amaliy qoida: sanash chizig'ini kadr chetiga emas, odamlar to'liq ko'rinadigan o'rtaga qo'ying.

Ultralytics da ham tayyor hisoblagich bor — solutions.ObjectCounter. Uning kodi (solutions/object_counter.py) bizning yondashuvdan uch joyda farq qiladi:

kod/14_zona_chiziq.pydavomi
python4 qator
from ultralytics.solutions.object_counter import ObjectCounter

kod = inspect.getsource(ObjectCounter.count_objects).splitlines()
print("\n".join(q.rstrip() for q in kod if any(s in q for s in ("counted_ids", "Vertical region", "current_centroid[0] >", "intersects"))))
Chiqish
        if prev_position is None or track_id in self.counted_ids:
            if self.r_s.intersects(self.LineString([prev_position, current_centroid])):
                    # Vertical region: Compare x-coordinates to determine direction
                    if current_centroid[0] > prev_position[0]:  # Moving right
                self.counted_ids.append(track_id)
                if (region_width < region_height and current_centroid[0] > prev_position[0]) or (
                self.counted_ids.append(track_id)
  • U box markazi bilan ishlaydi, biz — oyoq nuqtasi bilan.
  • Har bir ID ni faqat bir marta sanaydi (counted_ids): kirib, keyin chiqqan odam bitta hodisa bo'ladi.
  • Yo'nalishni chiziq "vertikalroq" yoki "gorizontalroq" ekaniga qarab x yoki y o'zgarishidan aniqlaydi — qiya chiziqlarda bu noaniq. Vektor ko'paytma belgisi esa har qanday qiyalikda bir xil ishlaydi.

Zona: ichidami, qancha vaqt

Zona — ko'pburchak (polygon). Nuqta ichidami? Klassik usul — nur tashlash (ray casting): nuqtadan o'ngga gorizontal nur chiqaramiz va u ko'pburchak tomonlarini necha marta kesishini sanaymiz. Toq bo'lsa — ichida, juft bo'lsa — tashqarida:

kod/14_zona_chiziq.pydavomi
python32 qator
def ichidami(p, zona):
    """Nur tashlash: p dan o'ngga ketgan nur zona tomonlarini necha marta kesadi."""
    x, y = p
    ichida = False
    for i in range(len(zona)):
        (x1, y1), (x2, y2) = zona[i], zona[(i + 1) % len(zona)]
        if (y1 > y) != (y2 > y):                                   # tomon nur balandligini kesib o'tadi
            kesish_x = x1 + (y - y1) * (x2 - x1) / (y2 - y1)
            if x < kesish_x:
                ichida = not ichida
    return ichida

zona = np.array([(150, 600), (760, 600), (820, 800), (100, 800)], float)   # do'kon eshigi ichkarisi, pol
rng = np.random.default_rng(0)
nuqtalar = rng.uniform([0, 450], [1000, 950], size=(20000, 2))
bizniki = np.array([ichidami(p, zona) for p in nuqtalar])
opencv = np.array([cv2.pointPolygonTest(zona.astype(np.float32), tuple(map(float, p)), False) > 0 for p in nuqtalar])
print(f"20 000 tasodifiy nuqta: OpenCV pointPolygonTest bilan farq qilganlar — {int((bizniki != opencv).sum())}")

kadr = cv2.imread(str(MOT / "img1" / "000060.jpg"))[:, :, ::-1]
_, ax = plt.subplots(figsize=(10, 5.6))
ax.imshow(kadr)
ax.add_patch(plt.Polygon(zona, fill=True, alpha=0.25, color="yellow"))
for nom, (A, B) in chiziqlar.items():
    ax.plot([A[0], B[0]], [A[1], B[1]], color="red", lw=2)
    ax.text(A[0] + 8, A[1] + 30, nom, color="red", fontsize=9, bbox=dict(fc="white", ec="none", alpha=0.7))
f = oyoq(gt_trek[gt_trek[:, 0] % 3 == 0][:, 2:6])
ax.scatter(f[:, 0], f[:, 1], s=2, color="magenta")
ax.set_xlim(0, 1920)
ax.set_ylim(1080, 0)
ax.axis("off")
fig("zona_chiziqlar.png", "Sariq — zona (do'kon eshigi ichidagi pol), qizil — sanash chiziqlari, pushti nuqtalar — GT odamlarining oyoq nuqtalari (har 3-kadr).")
Chiqish
20 000 tasodifiy nuqta: OpenCV pointPolygonTest bilan farq qilganlar — 0

Sariq — zona (do'kon eshigi ichidagi pol), qizil — sanash chiziqlari, pushti nuqtalar — GT odamlarining oyoq nuqtalari (har 3-kadr).

Sariq — zona (do'kon eshigi ichidagi pol), qizil — sanash chiziqlari, pushti nuqtalar — GT odamlarining oyoq nuqtalari (har 3-kadr).

Zona ikki savolga javob beradi. Birinchisi — band bo'lganlik: hozir zonada necha kishi bor. Buning uchun ID umuman kerak emas — har kadrda oyoq nuqtasi zona ichidagi box larni sanash yetadi. Ikkinchisi — kutish vaqti: har bir odam zonada qancha turdi. Buning uchun esa ID kerak, va tracker xatolari to'g'ridan-to'g'ri natijaga tushadi:

kod/14_zona_chiziq.pydavomi
python27 qator
def zonada(treklar):
    return np.array([ichidami(p, zona) for p in oyoq(treklar[:, 2:6])])

box_det = np.array([(k, -1, *b[:4]) for k in det for b in det[k] if b[4] >= 0.25])   # tracking siz, faqat detector
gt_ichi, trek_ichi, det_ichi = zonada(gt_trek), zonada(trek), zonada(box_det)
band = pd.DataFrame({"GT": np.bincount(gt_trek[gt_ichi, 0].astype(int), minlength=526)[1:],
                     "tracker": np.bincount(trek[trek_ichi, 0].astype(int), minlength=526)[1:],
                     "faqat detector": np.bincount(box_det[det_ichi, 0].astype(int), minlength=526)[1:]})
print("zonadagi odamlar soni, kadrlar bo'yicha:")
show(band.describe().loc[["mean", "max"]].rename(index={"mean": "o'rtacha", "max": "eng ko'p"}).round(2))

def kutish(treklar, ichi):
    """Har bir ID zonada necha soniya bo'ldi (faqat zonaga kirganlar)."""
    ids, soni = np.unique(treklar[ichi, 1], return_counts=True)
    return pd.Series(soni / FPS, index=ids.astype(int))

gk, tk = kutish(gt_trek, gt_ichi), kutish(trek, trek_ichi)
show(pd.DataFrame([{"manba": "GT", "zonaga kirgan ID lar": len(gk), "o'rtacha, s": gk.mean(), "eng uzoq, s": gk.max(), "jami odam-soniya": gk.sum()},
                   {"manba": "tracker", "zonaga kirgan ID lar": len(tk), "o'rtacha, s": tk.mean(), "eng uzoq, s": tk.max(), "jami odam-soniya": tk.sum()}]).round(2))

_, ax = plt.subplots(figsize=(10, 2.8))
for ustun, rang in (("GT", "gray"), ("tracker", "#2563eb")):
    ax.plot(band.index + 1, band[ustun], label=ustun, color=rang, lw=1.5)
ax.set_xlabel("kadr")
ax.set_ylabel("zonadagi odamlar")
ax.legend()
fig("zona_band.png", "Zonadagi odamlar soni har kadrda: GT va tracker.")
Chiqish
zonadagi odamlar soni, kadrlar bo'yicha:
indexGTtrackerfaqat detector
o'rtacha0.71.110.99
eng ko'p234
manbazonaga kirgan ID laro'rtacha, seng uzoq, sjami odam-soniya
GT34.097.2312.27
tracker141.394.8319.43

Zonadagi odamlar soni har kadrda: GT va tracker.

Zonadagi odamlar soni har kadrda: GT va tracker.

Ikki narsa ko'zga tashlanadi. Birinchisi, tracker ham, oddiy detector ham zonada GT dan ko'proq odam ko'radi. Sababi zonaning o'zida: u do'kon oynasi ortida. MOT17-09 GT sida shu zonaga oyog'i tushadigan boshqa belgilar ham bor — har kadrda o'rtacha bitta "turgan odam" (do'kon ichida joyida turgan), bitta "chalg'ituvchi" (odamga o'xshash narsa) va oynadagi akslar. GT ularni piyoda deb sanamaydi, detector esa ulardan bir qismini odam deb topadi. Biznes xulosasi: sanashdan oldin "kim hisoblanadi" ni aniqlang — mijoz, joyida turgan sotuvchi, oynadagi aks, reklama plakatidagi odam — va zonani shunga qarab chizing yoki joyidan qimirlamaydigan trek larni alohida belgilang.

Ikkinchisi, kutish vaqti: GT da zonaga kirgan uch kishi, tracker da esa o'nlab ID, har biri ancha qisqa. Bitta odamning bitta tashrifi bir nechta trek ga bo'linib ketgan (11-bobdagi ID almashishlari) va ularga yana zonadagi boshqa "odamlar" qo'shilgan. Band bo'lganlik ID ga bog'liq emas, kutish vaqti esa to'liq ID sifatiga bog'liq — shuning uchun ikkinchisi doim ehtiyotkorlik bilan hisobot qilinadi.

Issiqlik xaritasi

Do'kon egasi uchun eng tushunarli rasm — odamlar qayerda ko'p yurishi. Har kadrdagi har bir oyoq nuqtasini katak jadvaliga qo'shamiz, keyin silliqlaymiz:

kod/14_zona_chiziq.pydavomi
python13 qator
xarita = np.zeros((1080, 1920), np.float32)
for x, y in oyoq(trek[:, 2:6]).astype(int):
    if 0 <= x < 1920 and 0 <= y < 1080:
        xarita[y, x] += 1
xarita = cv2.GaussianBlur(xarita, (0, 0), 25)                     # har nuqtani ~25 px lik "dog'" ga yoyamiz
qiymat = xarita / xarita.max()
rang = plt.cm.inferno(qiymat)                                      # har piksel uchun rang (RGBA)
rang[..., 3] = np.clip(np.sqrt(qiymat) * 1.2, 0, 0.85)             # shaffoflik qiymatga qarab: bo'sh joy ko'rinmaydi
_, ax = plt.subplots(figsize=(10, 5.6))
ax.imshow(kadr)
ax.imshow(rang)
ax.axis("off")
fig("issiqlik.png", "Tracker oyoq nuqtalaridan issiqlik xaritasi: 525 kadr davomida odamlar eng ko'p bo'lgan joylar.")

Tracker oyoq nuqtalaridan issiqlik xaritasi: 525 kadr davomida odamlar eng ko'p bo'lgan joylar.

Tracker oyoq nuqtalaridan issiqlik xaritasi: 525 kadr davomida odamlar eng ko'p bo'lgan joylar.

Bu yerda ID muhim emas — shuning uchun issiqlik xaritasi tracker xatolariga eng chidamli natija. Lekin uni o'qishda ehtiyot bo'ling: xarita "vaqt x joy" ni ko'rsatadi. Bir joyda uzoq turgan bitta odam (masalan, do'kon ichida joyida turgan sotuvchi) yuzlab kadr davomida bir nuqtaga yig'iladi va trotuardan o'tib ketgan o'nlab odamdan "issiqroq" chiqishi mumkin.

"Kaskasiz odam" qoidasi

7-bobda o'qitilgan model helmet, vest va Person ni yaxshi topdi, no_helmet ni esa yomon (AP50 0.19). O'shanda va'da berdik: "kaskasiz" ni alohida sinf sifatida emas, yaxshi sinflardan mantiq bilan chiqaramiz. Qoida:

  • Person box ining yuqori 30% i — bosh zonasi (biroz yuqoriroq ham: kaska box i boshdan chiqib turadi).
  • Markazi bosh zonasiga tushadigan helmet bo'lsa — kaskali, bo'lmasa — kaskasiz.

Tekshirish uchun GT dan har bir odamning haqiqiy holatini chiqaramiz: bosh zonasida GT helmet bo'lsa — kaskali, GT no_helmet bo'lsa — kaskasiz, ikkalasi ham bo'lmasa — noma'lum (boshi ko'rinmaydi yoki belgilanmagan). Ikki usulni solishtiramiz: A — mantiq, B — modelning no_helmet sinfi.

kod/14_zona_chiziq.pydavomi
python42 qator
ppe = Path("datasets/construction-ppe")
model = YOLO("out/train/ppe_coco/weights/best.pt")
KASKA, ODAM, KASKASIZ = 0, 6, 7

def bosh_holati(odam, kaskalar, kaskasizlar, ulush=0.3):
    x1, y1, x2, y2 = odam
    bosh = lambda b: x1 <= (b[0] + b[2]) / 2 <= x2 and y1 - 0.1 * (y2 - y1) <= (b[1] + b[3]) / 2 <= y1 + ulush * (y2 - y1)
    if any(bosh(b) for b in kaskalar):
        return "kaskali"
    if any(bosh(b) for b in kaskasizlar):
        return "kaskasiz"
    return "noma'lum"

qatorlar = []
for yol in sorted((ppe / "images" / "test").iterdir()):
    rasm = cv2.imread(str(yol))
    H, W = rasm.shape[:2]
    lab = np.loadtxt(ppe / "labels" / "test" / f"{yol.stem}.txt", ndmin=2).reshape(-1, 5)
    g = np.c_[(lab[:, 1] - lab[:, 3] / 2) * W, (lab[:, 2] - lab[:, 4] / 2) * H, (lab[:, 1] + lab[:, 3] / 2) * W, (lab[:, 2] + lab[:, 4] / 2) * H]
    r = model.predict(rasm, conf=0.25, verbose=False)[0]
    b, s = r.boxes.xyxy.cpu().numpy(), r.boxes.cls.cpu().numpy()
    for odam in g[lab[:, 0] == ODAM]:
        haqiqat = bosh_holati(odam, g[lab[:, 0] == KASKA], g[lab[:, 0] == KASKASIZ])
        if haqiqat == "noma'lum":
            continue
        topilgan = b[s == ODAM]
        iou = torchvision.ops.box_iou(torch.tensor(odam[None]), torch.tensor(topilgan)).numpy()[0] if len(topilgan) else np.zeros(1)
        if iou.max() < 0.5:
            qatorlar.append({"haqiqat": haqiqat, "A: mantiq": "odam topilmadi", "B: no_helmet sinfi": "odam topilmadi"})
            continue
        q = topilgan[iou.argmax()]
        qatorlar.append({"haqiqat": haqiqat,
                         "A: mantiq": "kaskali" if bosh_holati(q, b[s == KASKA], []) == "kaskali" else "kaskasiz",
                         "B: no_helmet sinfi": "kaskasiz" if bosh_holati(q, [], b[s == KASKASIZ]) == "kaskasiz" else "kaskali"})
natija = pd.DataFrame(qatorlar)
for usul in ("A: mantiq", "B: no_helmet sinfi"):
    show(pd.crosstab(natija["haqiqat"], natija[usul]).rename_axis(index=f"haqiqat \\ {usul}"))
topildi = natija[natija["A: mantiq"] != "odam topilmadi"]
for usul in ("A: mantiq", "B: no_helmet sinfi"):
    kasiz, kali = topildi[topildi["haqiqat"] == "kaskasiz"], topildi[topildi["haqiqat"] == "kaskali"]
    print(f"{usul:20s}: kaskasizlarning {(kasiz[usul] == 'kaskasiz').mean():.0%} i topildi, "
          f"kaskalilarning {(kali[usul] == 'kaskasiz').mean():.1%} i uchun soxta signal")
Chiqish
A: mantiq           : kaskasizlarning 97% i topildi, kaskalilarning 3.4% i uchun soxta signal
B: no_helmet sinfi  : kaskasizlarning 49% i topildi, kaskalilarning 2.0% i uchun soxta signal
haqiqat \ A: mantiqkaskalikaskasizodam topilmadi
kaskali142517
kaskasiz1345
haqiqat \ B: no_helmet sinfikaskalikaskasizodam topilmadi
kaskali144317
kaskasiz18175

7-bobdagi va'da tasdiqlandi. Mantiq kaskasiz ishchilarning deyarli hammasini topdi, modelning o'z no_helmet sinfi esa ularning yarmini. Evaziga mantiq kaskali ishchilar uchun biroz ko'proq soxta signal berdi — xavfsizlik tizimi uchun bu to'g'ri murosa: kaskasiz odamni o'tkazib yuborish soxta signaldan ancha qimmat. Ikkala usulda ham bir xil cheklov bor: detector odamning o'zini topmasa (jadvaldagi "odam topilmadi"), hech qanday qoida yordam bermaydi. Tizimning "shifti" — odamni topish sifati.

Videoda: vaqt bo'yicha tasdiqlash

Bitta kadr bo'yicha signal berish xavfli: odam boshini egsa, kaska bir kadr ko'rinmay qolishi mumkin. Real tizimda qoida tracker bilan birga ishlaydi: har bir trek uchun "kaskasiz" holati kadrlar bo'yicha yig'iladi va signal faqat u uzluksiz, masalan, 2 soniya (30 fps da 60 kadr) davom etsa beriladi. Bir trek ga bitta signal — bir odamga bir necha yuz marta emas. Construction-PPE dataset i alohida rasmlardan iborat, shuning uchun bu qismni bu yerda o'lchay olmaymiz; III qismda real video oqimida quramiz va o'sha yerda tekshiramiz.

Asosiy fikr

  • Chiziq, zona va qoida — trek larni biznes javobiga aylantiradigan uchta geometrik qurol. Pol bo'yicha hisob uchun box markazi emas, oyoq nuqtasi.
  • Kesib o'tish vektor ko'paytma belgisi bilan har qanday qiyalikdagi chiziqda ishlaydi; zona uchun nur tashlash. Sanash natijasini jami son bilan emas, hodisalarni GT bilan juftlab tekshiring.
  • Band bo'lganlik va issiqlik xaritasi ID ga bog'liq emas va tracker xatolariga chidamli; kutish vaqti esa ID ga bog'liq va parchalangan trek lar uni qisqartiradi. "Kaskasiz" ni mantiq bilan chiqarish modelning o'z no_helmet sinfidan ancha ko'p xavfli holatni topdi.

Mashqlar

  1. MOT17-09 da diagonal chiziq chizing (masalan (300, 1050) dan (1500, 600) gacha) va sanang. Ultralytics ning "vertikal yoki gorizontal" qoidasi bu chiziqda qanday xato qilishi mumkinligini misol bilan ko'rsating.
  2. bosh_holati da ulush ni 0.2 va 0.4 ga o'zgartiring. Topilgan kaskasizlar va soxta signallar qanday o'zgaradi?
  3. Kutish vaqtini 11-bobdagi BoT-SORT bilan hisoblang. Trek lar kamroq parchalansa, o'rtacha kutish vaqti GT ga yaqinlashadimi?

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar