W testowaniu algorytmu detekcji obiektów w dużych obrazach sprawdzamy nasze wykryte pola obrywa przeciwko współrzędnym podanym dla prostokątów prawdy gruntu.

Według pascalowych wyzwań LZO, jest to:

Przewidywane pudełko obniża jest uważane za poprawne, jeśli nakłada się na ponad 50% z polem obniża prawdy, w przeciwnym razie obwiednia jest uważana za fałszywe wykrywanie dodatnie. Wiele wykrywania jest karane. Jeśli system przewiduje kilka ograniczających skrzynek, które nakładają się za pomocą pojedynczej skrzynki prawdy, tylko jedna prognoza jest uważana za prawidłową, pozostałe są uważane za fałszywe pozytywy.

Oznacza to, że musimy obliczyć procent nakładania się. Czy to oznacza, że pole ziemią jest 50% objęte wykrytym polem granicznym? Lub że 50% pola ograniczającego jest wchłaniane przez podstawowe pudełko prawdy?

Szukałem, ale nie znalazłem dla tego standardowego algorytmu - co jest zaskakujące, ponieważ pomyślałbym, że jest to coś dość powszechnego w wizji komputera. (Jestem nowy). Nie przegapiłem tego? Czy ktoś wie, co stanowi standardowy algorytm dla tego typu problemu?

42
user961627 17 sierpień 2014, 16:28

7 odpowiedzi

Najlepsza odpowiedź

Odkryłem, że koncepcyjna odpowiedź jest tutaj: http: //pascallin.ecs.soton. AC.UK/Challenges/voc/voc2012/htmldoc/devkit_doc.html#section00054000000000000000000.

Z tego wątku: Porównaj dwie obwiednie pudełka ze sobą matlab

Powinienem być w stanie tego kodować w Pythonie!

2
Community 23 maj 2017, 11:46

Najwyżej głosowana odpowiedź ma błąd matematyczny, jeśli pracujesz z współrzędnymi ekranem (Pixel)! Przesłałem Edycja kilka tygodni temu z długim wyjaśnieniem dla wszystkich czytelników, aby zrozumieli matematykę. Ale ta edycja nie była zrozumiana przez recenzentów i została usunięta, więc ponownie przesłałem tę samą edycję, ale bardziej krótko podsumowano tym razem. (Aktualizacja: Odrzucony 2VS1 ponieważ uznano za "znaczną zmianę", Heh).

Będę więc wyjaśnić duży problem z matematyką tutaj w tej osobnej odpowiedzi.

Tak, w ogóle, ogólnie, głosowana odpowiedź jest poprawna i jest dobrym sposobem na obliczenie IOU. Ale (jak również wskazali inne osoby), jego matematyka jest całkowicie niepoprawna dla ekranów komputerowych. Nie można po prostu zrobić (x2 - x1) * (y2 - y1), ponieważ nie spowoduje tego właściwego obliczenia obszaru. Indeksowanie ekranu Rozpoczyna się w Pixel 0,0 i kończy w width-1,height-1. Zakres współrzędnych ekranów to inclusive:inclusive (włącznie na obu końcach), więc zakres od 0 do 10 w współrzędnych pikseli wynosi 11 pikseli szerokości, ponieważ obejmuje 0 1 2 3 4 5 6 7 8 9 10 (11 pozycji). Tak więc, aby obliczyć obszar współrzędnych ekranu, należy zatem dodać +1 do każdego wymiaru, w następujący sposób: (x2 - x1 + 1) * (y2 - y1 + 1).

Jeśli pracujesz w innym układzie współrzędnych, w którym zakres nie jest włączony (taki jak system inclusive:exclusive, gdzie 0 oznacza "Elementy 0-9, ale nie 10"), Wtedy ta dodatkowa matematyka nie byłaby konieczna. Ale najprawdopodobniej przetwarzasz oparte na pikselach skrzynek. Cóż, współrzędne ekranu zaczynają się od 0,0 i idź tam.

Ekran 1920x1080 jest indeksowany z 0 (pierwszy piksel) do 1919 (ostatni piksel poziomo) i z 0 (pierwszy piksel) do 1079 (Ostatni piksel) pionowo).

Więc jeśli mamy prostokąt w "przestrzeni współrzędnych pikseli", aby obliczyć jego obszar musi dodać 1 w każdym kierunku. W przeciwnym razie otrzymujemy niewłaściwą odpowiedź na obliczenie obszarze.

Wyobraź sobie, że ekran naszego 1920x1080 ma prostokąt na bazie pikseli z left=0,top=0,right=1919,bottom=1079 (obejmując wszystkie piksele na całym ekranie).

Cóż, wiemy, że 1920x1080 piksele to 2073600 piksele, który jest właściwym obszarem ekranu 1080p.

Ale z niewłaściwą matematyką area = (x_right - x_left) * (y_bottom - y_top), otrzymaliśmy: (1919 - 0) * (1079 - 0) = 1919 * 1079 = 2070601 piksele! To jest źle!

Dlatego musimy dodać +1 do każdego obliczenia, co daje nam następującą poprawioną matematykę: area = (x_right - x_left + 1) * (y_bottom - y_top + 1), dając nam: (1919 - 0 + 1) * (1079 - 0 + 1) = 1920 * 1080 = 2073600 piksele ! I to rzeczywiście poprawna odpowiedź!

Najkrótsze możliwe podsumowanie jest: zakresy współrzędnych pikseli są inclusive:inclusive, więc musimy dodać + 1 do każdej osi, jeśli chcemy prawdziwej powierzchni zakresu współrzędnych pikseli.

Za kilka szczegółowych informacji na temat tego, dlaczego +1 jest potrzebna, zobacz odpowiedź Jindil: https://stackoverflow.com/a/51730512 / 8874388.

Jak również ten artykuł Pyimagagesearch: HTTPS: //www.pyImagesearch. COM / 2016/11 / 07 / Wykrywanie przecięcia-over-union-IOU

I ten github komentuje: https://github.com/alexeyab/darknet/issues/3995#issuecomment-535697357 \ t.

Ponieważ stała matematyka nie została zatwierdzona, każdy, kto kopiuje kod z najwyższej odpowiedzi, miejmy nadzieję, że widzi tę odpowiedź, i będzie w stanie sam się poprawić, po prostu skopiując twierdzenia błędów i linie obliczeniowe poniżej, które były Naprawiono zakresy współrzędnych inclusive:inclusive (Pixel):

    assert bb1['x1'] <= bb1['x2']
    assert bb1['y1'] <= bb1['y2']
    assert bb2['x1'] <= bb2['x2']
    assert bb2['y1'] <= bb2['y2']

................................................

    # The intersection of two axis-aligned bounding boxes is always an
    # axis-aligned bounding box.
    # NOTE: We MUST ALWAYS add +1 to calculate area when working in
    # screen coordinates, since 0,0 is the top left pixel, and w-1,h-1
    # is the bottom right pixel. If we DON'T add +1, the result is wrong.
    intersection_area = (x_right - x_left + 1) * (y_bottom - y_top + 1)

    # compute the area of both AABBs
    bb1_area = (bb1['x2'] - bb1['x1'] + 1) * (bb1['y2'] - bb1['y1'] + 1)
    bb2_area = (bb2['x2'] - bb2['x1'] + 1) * (bb2['y2'] - bb2['y1'] + 1)
22
Mitch McMabers 28 wrzesień 2019, 21:14

Droga prosta

Przykład (Obraz nie jest rysowany do skali)

from shapely.geometry import Polygon


def calculate_iou(box_1, box_2):
    poly_1 = Polygon(box_1)
    poly_2 = Polygon(box_2)
    iou = poly_1.intersection(poly_2).area / poly_1.union(poly_2).area
    return iou


box_1 = [[511, 41], [577, 41], [577, 76], [511, 76]]
box_2 = [[544, 59], [610, 59], [610, 94], [544, 94]]

print(calculate_iou(box_1, box_2))

Wynik będzie 0.138211..., co oznacza 13.82%.

17
Uzzal Podder 21 grudzień 2019, 12:32

W przypadku odległości skrzyżowania nie powinniśmy dodawać +1, aby mieć

intersection_area = (x_right - x_left + 1) * (y_bottom - y_top + 1)   

(tak samo dla AABB)
Jak na tym Pyimage Search Post

Zgadzam się (x_right - x_left) x (Y_BOTTOM - Y_TOP) pracuje w matematyce z współrzędnymi punktami, ale odkąd zajmujemy się pikselami, tak myślę.

Rozważ 1D Przykład:
- 2 punkty: x1 = 1 i x2 = 3 , odległość jest rzeczywiście x2-x1 = 2
- 2 pikseli indeksu: i1 = 1 i i2 = 3 , segment z pikseli I1 do I2 zawiera 3 piksele, czyli L = I2 - I1 + 1 ..

5
Jindil 7 sierpień 2018, 15:32

W poniższym fragmencie buduję wielokąt wzdłuż krawędzi pierwszego pudełka. Następnie użyj MATPOTLIB do klipu wielokąt do drugiego pola. Uzyskany wielokąt zawiera cztery wierzchołki, ale interesuje nas tylko górne rogi w lewo i dolne prawe rogi, więc biorę maksimum i minimalne współrzędne, aby uzyskać obwiedni, który jest zwracany do użytkownika.

import numpy as np
from matplotlib import path, transforms

def clip_boxes(box0, box1):
    path_coords = np.array([[box0[0, 0], box0[0, 1]],
                            [box0[1, 0], box0[0, 1]],
                            [box0[1, 0], box0[1, 1]],
                            [box0[0, 0], box0[1, 1]]])

    poly = path.Path(np.vstack((path_coords[:, 0],
                                path_coords[:, 1])).T, closed=True)
    clip_rect = transforms.Bbox(box1)

    poly_clipped = poly.clip_to_bbox(clip_rect).to_polygons()[0]

    return np.array([np.min(poly_clipped, axis=0),
                     np.max(poly_clipped, axis=0)])

box0 = np.array([[0, 0], [1, 1]])
box1 = np.array([[0, 0], [0.5, 0.5]])

print clip_boxes(box0, box1)
0
Stefan van der Walt 15 październik 2014, 15:44

Co powiesz na to podejście? Może zostać przedłużony na dowolną liczbę uzwiązanych kształtów

surface = np.zeros([1024,1024])
surface[1:1+10, 1:1+10] += 1
surface[100:100+500, 100:100+100] += 1
unionArea = (surface==2).sum()
print(unionArea)
-1
Reno Fiedler 8 październik 2018, 02:43

W przypadku skrzynek ograniczających osi są stosunkowo proste. "Asy wyrównane" oznacza, że obwiedni nie jest obracany; lub innymi słowy, które linie pudełka są równoległe do osi. Oto jak obliczyć IOU o dwóch osłonowych skrzynkach.

def get_iou(bb1, bb2):
    """
    Calculate the Intersection over Union (IoU) of two bounding boxes.

    Parameters
    ----------
    bb1 : dict
        Keys: {'x1', 'x2', 'y1', 'y2'}
        The (x1, y1) position is at the top left corner,
        the (x2, y2) position is at the bottom right corner
    bb2 : dict
        Keys: {'x1', 'x2', 'y1', 'y2'}
        The (x, y) position is at the top left corner,
        the (x2, y2) position is at the bottom right corner

    Returns
    -------
    float
        in [0, 1]
    """
    assert bb1['x1'] < bb1['x2']
    assert bb1['y1'] < bb1['y2']
    assert bb2['x1'] < bb2['x2']
    assert bb2['y1'] < bb2['y2']

    # determine the coordinates of the intersection rectangle
    x_left = max(bb1['x1'], bb2['x1'])
    y_top = max(bb1['y1'], bb2['y1'])
    x_right = min(bb1['x2'], bb2['x2'])
    y_bottom = min(bb1['y2'], bb2['y2'])

    if x_right < x_left or y_bottom < y_top:
        return 0.0

    # The intersection of two axis-aligned bounding boxes is always an
    # axis-aligned bounding box
    intersection_area = (x_right - x_left) * (y_bottom - y_top)

    # compute the area of both AABBs
    bb1_area = (bb1['x2'] - bb1['x1']) * (bb1['y2'] - bb1['y1'])
    bb2_area = (bb2['x2'] - bb2['x1']) * (bb2['y2'] - bb2['y1'])

    # compute the intersection over union by taking the intersection
    # area and dividing it by the sum of prediction + ground-truth
    # areas - the interesection area
    iou = intersection_area / float(bb1_area + bb2_area - intersection_area)
    assert iou >= 0.0
    assert iou <= 1.0
    return iou

Wyjaśnienie

enter image description here enter image description here

Obrazy są z Ta odpowiedź

50
Martin Thoma 10 wrzesień 2019, 12:54