Ultralytics YOLO27: Bekleme listesine katıl.
Get Started
Dersorta düzey

Kodda Maskeleri Kullanma: Alan, Örtüşme, Konturlar, Kesitler

masks.data ve masks.xy değerlerini doğru oku, nesne alanını ve doluluk oranını ölç, kutular yerine maskelerle örtüşmeyi test et ve nesneleri kareden kesip çıkar.

Bir segmentasyon modeli, ancak kod maskelerini bir karara dönüştürdüğünde karşılığını verir. Otobüs fotoğrafında her yayanın kutusu otobüsün kutusuyla %56–74 oranında örtüşürken maskeleri otobüsle %0 örtüşür — kutulara dayalı bir "araç bölgesindeki kişi" uyarısı, kaldırımda duran dört kişinin tamamı için tetiklenirdi. Maskeler bu yanıtları doğru verir; ancak varsayılan görünümlerindeki boyut ve biçim tuzakları, makul görünen ama yanlış sayılara yol açabilir.

Sonuç

Her nesnenin maske alanını, doluluk oranını, kontur boyutunu ve başka bir nesneyle örtüşmesini raporlayan; ayrıca her nesne için şeffaf bir kesit kaydeden betikler çalıştır — her işlem için doğru maske görünümünü kullan.

Hızlı Başlangıç
Nasıl ilerleyeceğini zaten biliyorsan, işte kısa sürüm.
  1. Maskelerin özgün görüntü boyutuyla eşleşmesi için ölçüm veya kırpma yaptığın her durumda retina_masks=True ile tahmin yap.

  2. Alanlar, örtüşmeler ve kesitler için masks.data (N × H × W) kullan; kontur çizmek ve dışa aktarmak için masks.xy (piksel poligonları) kullan.

  3. Alan mask.sum() değeridir; doluluk = maske alanı ÷ kutu alanı; örtüşme = (mask_a & mask_b).sum().

  4. Maskeleri okumadan önce result.masks is None değerini kontrol et: hiçbir şey bulunmadığında bu değer None olur.

Uygulamalı

Aynı maskenin iki görünümü#

Tam karede siyah arka plan üzerinde yerinde bırakılmış, Ultralytics YOLO segmentasyon maskesiyle yalıtılmış bir kişi

result.masks, her örnek maskesini iki gösterimde tutar. Sonraki kod satırının yapacağı işe göre seçim yap; Results başvurusu tüm öznitelikleri listeler.

ÖznitelikŞekilKoordinatlarKullanım amacı
masks.data0/1 değerlerinden oluşan N × H × W tensörüvarsayılan olarak çıkarım boyutu; retina_masks=True ile özgün boyutpiksel alanları, maskeler arası örtüşmeler, kesitler
masks.xyher biri P × 2 boyutunda olan N dizi listesiözgün görüntünün piksellerikontur çizmek, poligonları dışa aktarmak
masks.xynher biri P × 2 boyutunda olan N dizi listesinormalize edilmiş 0–1YOLO poligon etiketlerini dışa aktarmak
masks.data varsayılan olarak çıkarım boyutunu kullanır

1080 × 810 boyutundaki otobüs görüntüsünde masks.data, (5, 640, 480) olarak döner — bu, mektup kutulu çıkarım boyutudur — dolayısıyla piksel sayıları yaklaşık 2.85× az çıkar ve maske özgün görüntünün üzerine yerleştirilemez. retina_masks=True, küçük bir son işleme maliyetiyle maskeleri özgün çözünürlüğe yükseltir. Ölçüm veya kırpma yaptığında bunu kullan.

Her nesnenin ölçümünü al ve kesitini çıkar#

Bu betik her nesne için iki görünümü okur: masks.data ile piksel alanını, masks.xy ile kontur boyutunu ve maskeyi alfa kanalı olarak kullanan şeffaf bir PNG kesiti. Segmentasyon nesnelerini yalıtma kılavuzu daha fazla kesit çeşidini ele alır.

import cv2
import numpy as np

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True)[0]  # masks at original resolution

print("masks.data:", tuple(result.masks.data.shape), "orig_shape:", result.orig_shape)
for i, box in enumerate(result.boxes):
    name = result.names[int(box.cls)]
    mask = result.masks.data[i].cpu().numpy().astype(np.uint8)  # H x W, 1 = object
    polygon = result.masks.xy[i]  # P x 2 outline in original pixels
    x1, y1, x2, y2 = box.xyxy[0].int().tolist()
    mask_area = int(mask.sum())
    box_area = (x2 - x1) * (y2 - y1)
    print(f"{name:6s} conf={float(box.conf):.2f} mask={mask_area:7d}px box={box_area:7d}px fill={mask_area / box_area:.2f} polygon={len(polygon)} pts")

    # Cut the object out on a transparent background
    cutout = np.dstack([result.orig_img, mask * 255])[y1:y2, x1:x2]
    cv2.imwrite(f"{i}_{name}.png", cutout)
masks.data: (5, 1080, 810) orig_shape: (1080, 810)
person conf=0.88 mask=  49331px box=  97219px fill=0.51 polygon=575 pts
person conf=0.86 mask=  21316px box=  67760px fill=0.31 polygon=408 pts
bus    conf=0.86 mask= 255265px box= 409822px fill=0.62 polygon=1172 pts
person conf=0.81 mask=  33524px box=  55842px fill=0.60 polygon=350 pts
person conf=0.64 mask=  11109px box=  23828px fill=0.47 polygon=211 pts

Çıktıda dikkat etmen gerekenler:

  • masks.data: (5, 1080, 810), orig_shape ile eşleşiyor; dolayısıyla retina_masks=True görevini yaptı ve tüm piksel sayıları özgün pikseller üzerinden.
  • i indeksi ikisini birbirine bağlar. result.masks.data[i], result.boxes[i] öğesine aittir; bu nedenle adlar ve güven değerleri kutudan, pikseller ise maskeden gelir.
  • Doluluk, algılayıcının veremeyeceği bir bilgi sunar: otobüs kutusunun %62'sini kaplarken çerçevenin sağ kenarında kesilen ve otobüsün önünde duran ikinci kişi yalnızca %31 doluluk oranına sahiptir.
  • Kesitler, yalnızca kişiyi veya otobüsü içeren, başka hiçbir şey içermeyen BGRA PNG dosyalarıdır — katalog, ikinci bir model veya inceleme kuyruğu için hazırdır.

Yalıtma kılavuzundaki kırpılmış sürüm, şeffaf arka plan yerine siyah arka plan kullanır:

Aynı kişi, sınırlayıcı kutusuna göre kırpılmış ve maske dışındaki her piksel siyaha ayarlanmış

Örtüşme: kutuların kapladığı alanlar#

"Kişi kısıtlı bölgenin içinde" veya "ürün taşıma bandını kaplıyor" gibi kurallar, örtüşme testleridir. Aynı testi kutularla ve maskelerle çalıştır:

import numpy as np

from ultralytics import YOLO

result = YOLO("yolo26n-seg.pt")("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]
names = [result.names[int(c)] for c in result.boxes.cls]
masks = result.masks.data.cpu().numpy().astype(bool)  # N x H x W at original resolution
boxes = np.zeros_like(masks)
for i, (x1, y1, x2, y2) in enumerate(result.boxes.xyxy.int().tolist()):
    boxes[i, y1:y2, x1:x2] = True  # the same objects as filled rectangles

bus = names.index("bus")
for i, name in enumerate(names):
    if name == "person":
        by_box = (boxes[i] & boxes[bus]).sum() / boxes[i].sum()
        by_mask = (masks[i] & masks[bus]).sum() / masks[i].sum()
        print(f"person {i}: {by_box:4.0%} of box overlaps the bus box, {by_mask:4.0%} of mask overlaps the bus mask")
person 0:  67% of box overlaps the bus box,   0% of mask overlaps the bus mask
person 1:  69% of box overlaps the bus box,   0% of mask overlaps the bus mask
person 3:  74% of box overlaps the bus box,   0% of mask overlaps the bus mask
person 4:  56% of box overlaps the bus box,   0% of mask overlaps the bus mask

İnsanlar otobüsün önünde durduğundan otobüs maskesi her birinin konturunda sona erer ve maskeler ortak piksel içermez. Kutular ise her ikisinin çevresine çizilmiş dikdörtgenlerdir; bu nedenle büyük ölçüde örtüşürler. Aynı hesaplama, kendin çizdiğin her bölge için de geçerlidir: cv2.fillPoly ile sıfırlardan oluşan bir diziye poligon doldur ve otobüs maskesinin yerine kullan.

Karar kuralı

İçinde, dokunuyor, kapatıyor veya engelliyor şeklinde ifade edilen her kural, bir örtüşme testidir. Bu testi maskeler üzerinde yap. Birkaç piksellik farkın sonucu değiştirmediği yakınında ve solunda gibi durumlarda kutular uygundur.

Piksellerden gerçek dünya yanıtlarına#

SoruHesaplamaDikkat et
Ne kadar büyük?mask.sum() × (cm²/piksel başına)Bilinen bir nesneyle cm²/piksel değerini bir kez kalibre et. Açılı bir kamerada alan mesafeye göre değişir: her bölgeyi ayrı ayrı kalibre et veya yalnızca tek bir bölgede ölçüm yap.
Bölgenin ne kadarı kaplı?sınıf maskesi pikselleri ÷ bölge pikselleriÜst üste binen örneklerin bir mi, iki mi sayılacağına karar ver.
Bölgenin içinde mi?(mask & zone).sum() / mask.sum()Bölgeyi özgün görüntü boyutunda çiz.
Dış hatları nasıl görünüyor?masks.xy[i]Alanı hesaplamadan önce aşağıdaki bölünmüş nesne uyarısına bak.

Yaygın Hatalar#

Hiçbir nesne bulunmadığında maskeleri okumak

Belirti: Nesne bulunmayan karelerde AttributeError: 'NoneType' object has no attribute 'xy'. Neden: Model hiçbir şey bulamadığında result.masks, boş bir nesne değil, None değeridir. Çözüm: result.boxes üzerinde döngü kur (segment modellerinde boş olur, hiçbir zaman None olmaz) ve masks.py örneğinde olduğu gibi döngünün içinde maskeleri indeksle veya önce result.masks is None değerini denetle.

Bölünmüş nesnelerde çokgen alanı

Belirti: Örtülmüş nesneler için alanlar çok yanlış çıkıyor. Neden: Örtücü bir nesne, bir nesneyi böldüğünde masks.xy tüm konturları tek bir dizide birleştirir ve bu dizi üzerinde cv2.contourArea kullanmak gerçekte var olmayan bir şeklin alanını ölçer. Çözüm: Alanları masks.data piksel sayılarına göre hesapla; çokgenleri çizim için kullan.

Çözünürlükleri karıştırmak

Belirti: İki çalıştırmadaki alanlar yaklaşık 2.85 kat farklı. Neden: Çalıştırmalardan birinde retina_masks=False kullanıldığından alanlar çıkarım pikselleri cinsinden. Çözüm: Her yerde retina_masks=True ile ölç; CUDA üzerinde .numpy() çağrısından önce .cpu() çağır.

Dene

masks.py aracını en az üç nesne içeren kendi görüntünde çalıştır ve doluluk oranı en düşük nesneyi, ayrıca kutusunun boyutunu neden olduğundan büyük gösterdiğini belirt. retina_masks=True olmadan yeniden çalıştır ve masks.data.shape değerinin ve alanların nasıl değiştiğini not et. Ardından, görüntündeki herhangi bir nesnenin karenin alt üçte birlik kısmına çizdiğin dikdörtgenle örtüşüp örtüşmediğini sınamak için overlap.py kodunu uyarla.

Tamamlanma ölçütü
Aşağıdakilerin tümü doğru olduğunda dersi tamamlamış olursun.
  • Görüntündeki tespit edilen her nesne için bir adet şeffaf PNG kesitin var.

  • En düşük doluluk oranını belirtebilir ve masks.data değerinin retina_masks=True ile ve bu olmadan nasıl farklılaştığını açıklayabilirsin.

  • Bölge testin her nesne için kutu tabanlı ve maske tabanlı örtüşme değerlerini yazdırıyor; değerlerin uyuşmadığı nesneleri açıklayabilirsin.

Çözümü göster

retina_masks=True olmadan, otobüs görüntüsü (5, 640, 480) şekline sahip masks.data döndürür: her piksel alanı (1080 × 810) / (640 × 480) ≈ 2.85 kat küçülür ve kesitler için maske artık orig_img ile hizalanmaz. Görüntünün en-boy oranı çıkarım boyutundan farklı olduğunda, letterbox dolgusu ikinci bir hizasızlığa yol açar.

Otobüs görüntüsünde doluluk oranı en düşük olan, ikinci kişidir (0.31): karenin sağ kenarında kesilmiş ve otobüse yaslanarak duruyor; bu nedenle kutunun büyük kısmı arka plan ve otobüsten oluşuyor.

Bölgeyi özgün boyutta bir kez oluştur ve maske testini yeniden kullan:

import cv2

h, w = result.orig_shape
zone = np.zeros((h, w), np.uint8)
cv2.fillPoly(zone, [np.array([[0, 2 * h // 3], [w, 2 * h // 3], [w, h], [0, h]])], 1)
zone = zone.astype(bool)
for i, name in enumerate(names):
    print(f"{name} {i}: box {(boxes[i] & zone).sum() / boxes[i].sum():.0%}, mask {(masks[i] & zone).sum() / masks[i].sum():.0%}")

Otobüs görüntüsünde:

person 0: box 36%, mask 29%
person 1: box 33%, mask 37%
bus 2: box 4%, mask 0%
person 3: box 31%, mask 21%
person 4: box 47%, mask 53%

İnsanların bacakları gerçekten alt üçte birlik kısımda, dolayısıyla kutu ve maske yaklaşık on puan farkla örtüşüyor. Yanlış alarm otobüs: kutusu bölgenin %4'üne giriyor, maskesi ise hiç girmiyor. “İçeride %0'dan fazla” kuralıyla kutular otobüsü işaretler, maskeler işaretlemezdi.

Sırada ne var

Ardından örnekleri tamamen bırakıp anlamsal segmentasyonla her piksele etiket vereceğiz.

İlerlemeni kaydetmek için giriş yap