Asosiy qismga o'tish

Obyekt aniqlash — qayerda va nima

9-bob 12 kod bloki 1483 so'z

Klassifikatsiya "bu rasmda nima bor?" degan savolga javob beradi. Aniqlash esa "qayerda va nima?"

yaml3 qator
Klassifikatsiya:  rasm -> "mushuk"
Aniqlash:         rasm -> [("mushuk", x=120, y=45, w=200, h=180),
                           ("it",     x=380, y=90, w=150, h=140)]

Hujjat uchun bu muhim: "bu sahifada jadval bor" emas, balki "jadval aynan shu koordinatalarda".

Bounding box formatlari

Uch xil format bor va ular tez-tez chalkashtiriladi:

python8 qator
# 1. XYWH  — chap-yuqori burchak + o'lcham (OpenCV)
quti_xywh = (100, 50, 200, 150)

# 2. XYXY  — chap-yuqori + o'ng-past (PyTorch, ko'p model)
quti_xyxy = (100, 50, 300, 200)

# 3. CXCYWH — markaz + o'lcham, NORMALLASHGAN (YOLO)
quti_yolo = (0.4, 0.3, 0.25, 0.2)      # 0.0 dan 1.0 gacha

O'tkazish:

python19 qator
def xywh_to_xyxy(x, y, w, h):
    return (x, y, x + w, y + h)


def xyxy_to_xywh(x1, y1, x2, y2):
    return (x1, y1, x2 - x1, y2 - y1)


def yolo_to_xyxy(cx, cy, w, h, rasm_k, rasm_b):
    """YOLO normallashgan formatdan piksel koordinatalariga."""
    x1 = (cx - w / 2) * rasm_k
    y1 = (cy - h / 2) * rasm_b
    x2 = (cx + w / 2) * rasm_k
    y2 = (cy + h / 2) * rasm_b
    return (round(x1), round(y1), round(x2), round(y2))


print("xywh->xyxy:", xywh_to_xyxy(100, 50, 200, 150))
print("yolo->xyxy:", yolo_to_xyxy(0.4, 0.3, 0.25, 0.2, 640, 480))

Natija:

matn2 qator
xywh->xyxy: (100, 50, 300, 200)
yolo->xyxy: (176, 96, 336, 192)

YOLO formati normallashgan — 0 dan 1 gacha. Shuning uchun rasm o'lchami o'zgarsa ham koordinatalar to'g'ri qoladi.

Bu format xatosi eng ko'p vaqt yo'qotadigan muammolardan biri. Model ishlaydi, lekin qutilar noto'g'ri joyda chiqadi.

IoU — qutilar qanchalik mos

IoU (Intersection over Union) — ikkita quti qanchalik ustma-ust tushishini o'lchaydi. Butun aniqlash sohasi shunga tayanadi.

python1 qator
IoU = kesishma maydoni / birlashma maydoni
python27 qator
def iou(a, b):
    """Ikki XYXY quti orasidagi IoU. 0.0 dan 1.0 gacha."""
    ax1, ay1, ax2, ay2 = a
    bx1, by1, bx2, by2 = b

    # Kesishma to'rtburchagi
    kx1 = max(ax1, bx1)
    ky1 = max(ay1, by1)
    kx2 = min(ax2, bx2)
    ky2 = min(ay2, by2)

    # Kesishma bormi?
    kw = max(0, kx2 - kx1)
    kh = max(0, ky2 - ky1)
    kesishma = kw * kh

    a_maydon = (ax2 - ax1) * (ay2 - ay1)
    b_maydon = (bx2 - bx1) * (by2 - by1)
    birlashma = a_maydon + b_maydon - kesishma

    return kesishma / birlashma if birlashma > 0 else 0.0


print("bir xil     :", iou((0,0,100,100), (0,0,100,100)))
print("yarmi       :", iou((0,0,100,100), (50,0,150,100)))
print("tegmaydi    :", iou((0,0,100,100), (200,200,300,300)))
print("ichida      :", round(iou((0,0,100,100), (25,25,75,75)), 4))

Natija:

matn4 qator
bir xil     : 1.0
yarmi       : 0.3333333333333333
tegmaydi    : 0.0
ichida      : 0.25

"Yarmi" 0.5 emas, 0.33 — bu ko'pchilikni ajablantiradi. Ikki quti yarmi bilan ustma-ust tushsa: kesishma 50x100=5000, birlashma 10000+10000-5000=15000. 5000/15000 = 0.33.

IoU maxrajda birlashmani ishlatadi, shuning uchun u har doim "ko'zga ko'ringan" ustma-ustlikdan kichikroq chiqadi.

max(0, ...) qatorlari muhim. Qutilar kesishmasa, farq manfiy chiqadi va usiz IoU ham manfiy bo'lardi — bu mantiqsiz.

IoU qanday o'qiladi:

IoU Ma'nosi
1.0 aynan bir xil
0.5 dan yuqori odatda "to'g'ri topilgan" hisoblanadi
0.0 umuman tegmaydi

0.5 chegarasi shartli — COCO datasetida 0.5 dan 0.95 gacha bir nechta chegarada o'lchanadi.

NMS — takroriy qutilarni o'chirish

Model bitta obyekt uchun bir nechta quti qaytaradi. Ular deyarli bir xil joyda.

NMS (Non-Maximum Suppression) shularni tozalaydi:

python27 qator
def nms(qutilar, ballar, chegara=0.5):
    """Ustma-ust tushgan qutilardan eng yaxshisini qoldiradi."""
    if not qutilar:
        return []

    # Ball bo'yicha kamayish tartibida
    tartib = sorted(range(len(qutilar)), key=lambda i: ballar[i], reverse=True)
    saqlangan = []

    while tartib:
        eng_yaxshi = tartib.pop(0)
        saqlangan.append(eng_yaxshi)

        # Unga juda o'xshaganlarni tashlaymiz
        tartib = [i for i in tartib
                  if iou(qutilar[eng_yaxshi], qutilar[i]) < chegara]

    return saqlangan


qutilar = [(10,10,110,110), (12,12,112,112), (300,300,400,400)]
ballar  = [0.9, 0.85, 0.95]

qolgan = nms(qutilar, ballar)
print("qolgan indekslar:", qolgan)
for i in qolgan:
    print(f"  quti {i}: {qutilar[i]}  ball {ballar[i]}")

Natija:

python3 qator
qolgan indekslar: [2, 0]
  quti 2: (300, 300, 400, 400)  ball 0.95
  quti 0: (10, 10, 110, 110)  ball 0.9

Uchtadan ikkitasi qoldi. 1-quti (ball 0.85) o'chirildi — u 0-quti bilan deyarli bir xil edi, lekin balli pastroq.

Diqqat: natija ball tartibida, asl tartibda emas.

Birinchi ikkita quti deyarli bir xil — NMS bittasini o'chiradi. Uchinchisi uzoqda, shuning uchun qoladi.

Chegara ta'siri:

  • past chegara (0.3) — ko'proq o'chiradi, yaqin obyektlar yo'qolishi mumkin
  • baland chegara (0.7) — kamroq o'chiradi, takroriy qutilar qoladi

Olomon ichidagi odamlarni aniqlashda bu muammo: ular haqiqatan ustma-ust turadi.

YOLO g'oyasi — bitta o'tishda

Eski usullar rasmni minglab marta tekshirardi (sliding window). YOLO — You Only Look Once — bitta o'tishda hammasini bashorat qiladi.

G'oya: rasmni katakchalarga bo'lamiz.

python10 qator
def yolo_setka(rasm_olcham=416, setka=13):
    """Har bir katakcha qancha piksel qamrab oladi."""
    katak = rasm_olcham // setka
    print(f"rasm {rasm_olcham}x{rasm_olcham}, setka {setka}x{setka}")
    print(f"har bir katak: {katak}x{katak} piksel")
    print(f"jami katak: {setka * setka}")
    return katak


yolo_setka()

Har bir katakcha javob beradi: "menda obyekt markazi bormi? Bo'lsa — qaysi sinf va quti qanday?"

Anchor box — har bir katakcha bir nechta oldindan belgilangan shakldagi qutini sinaydi (uzun, keng, kvadrat). Chunki odam tik, mashina keng.

Amalda: tayyor modeldan foydalanish

YOLO ni noldan o'qitish uchun katta dataset va GPU kerak. Amalda tayyor model ishlatiladi:

python12 qator
# parcha — ultralytics o'rnatilgan bo'lishi kerak
from ultralytics import YOLO

model = YOLO("yolov8n.pt")          # n = nano, eng kichik
natijalar = model("rasm.jpg")

for r in natijalar:
    for quti in r.boxes:
        sinf = model.names[int(quti.cls)]
        ball = float(quti.conf)
        x1, y1, x2, y2 = quti.xyxy[0].tolist()
        print(f"{sinf}: {ball:.2f}  ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f})")

O'rnatish: pip install ultralytics

yolov8n — nano versiya, ~6 MB, CPU da ham ishlaydi. Kattaroqlari: s, m, l, x — aniqroq, lekin sekinroq.

Hujjat uchun: aniqlash kerakmi?

Ko'p hollarda YO'Q. 3-bobdagi morfologiya usuli oddiy hujjatlar uchun yetarli va u:

  • o'qitish talab qilmaydi
  • CPU da bir zumda ishlaydi
  • tushuntirib beriladi

Aniqlash quyidagi hollarda kerak:

  • hujjat formati juda xilma-xil
  • muhr, imzo, logotip topish kerak
  • jadval va rasm aralash sahifa

Amaliy tartib: avval morfologiya bilan sinang. Ishlamasa, keyin aniqlash modeliga o'ting.

O'z ma'lumotingizda o'qitish

Agar aniqlash kerak bo'lsa, belgilash formati:

python4 qator
# har bir rasm uchun bitta .txt fayl
# sinf_id  cx  cy  w  h        (hammasi 0-1 oralig'ida)
0 0.412 0.305 0.250 0.200
1 0.700 0.650 0.180 0.120

Belgilash uchun labelImg yoki CVAT ishlatiladi.

Nechta rasm kerak: har bir sinf uchun kamida 100–200 belgilangan misol. Kamroq bo'lsa transfer learning ham yordam bermaydi.

Bu — eng ko'p vaqt oladigan qism. Model o'qitish bir soat, belgilash esa bir hafta bo'lishi mumkin.


Bu bobni bepul o'qidingiz.

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

Telegramda yozish Narxlar