Zonalar va chiziqlar: sanash, vaqt va qoidalar
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:
- Chiziq — kim, qaysi tomonga kesib o'tdi (kirish/chiqish soni).
- Zona (ko'pburchak) — kim ichida va qancha vaqt (band bo'lganlik, kutish vaqti).
- 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.
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")
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.
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))
| chiziq | GT: chapga | GT: o'ngga | tracker: chapga | tracker: o'ngga | to'g'ri | ortiqcha | o'tkazib yuborilgan |
|---|---|---|---|---|---|---|---|
| x = 700 | 1 | 12 | 3 | 10 | 11 | 2 | 2 |
| x = 960 (o'rta) | 1 | 10 | 2 | 10 | 11 | 1 | 0 |
| x = 1300 | 1 | 14 | 2 | 10 | 11 | 1 | 4 |
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:
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"))))
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:
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).")
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).
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:
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.")
zonadagi odamlar soni, kadrlar bo'yicha:
| index | GT | tracker | faqat detector |
|---|---|---|---|
| o'rtacha | 0.7 | 1.11 | 0.99 |
| eng ko'p | 2 | 3 | 4 |
| manba | zonaga kirgan ID lar | o'rtacha, s | eng uzoq, s | jami odam-soniya |
|---|---|---|---|---|
| GT | 3 | 4.09 | 7.23 | 12.27 |
| tracker | 14 | 1.39 | 4.83 | 19.43 |

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:
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.
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:
Personbox ining yuqori 30% i — bosh zonasi (biroz yuqoriroq ham: kaska box i boshdan chiqib turadi).- Markazi bosh zonasiga tushadigan
helmetbo'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.
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")
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: mantiq | kaskali | kaskasiz | odam topilmadi |
|---|---|---|---|
| kaskali | 142 | 5 | 17 |
| kaskasiz | 1 | 34 | 5 |
| haqiqat \ B: no_helmet sinfi | kaskali | kaskasiz | odam topilmadi |
|---|---|---|---|
| kaskali | 144 | 3 | 17 |
| kaskasiz | 18 | 17 | 5 |
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_helmetsinfidan ancha ko'p xavfli holatni topdi.
Mashqlar
- 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.
bosh_holatidaulushni 0.2 va 0.4 ga o'zgartiring. Topilgan kaskasizlar va soxta signallar qanday o'zgaradi?- 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.