Posteriormente a indagar en diferentes repositorios y sitios de internet al concluir hallamos la solución que te enseñaremos más adelante.
Solución:
Para los cuadros delimitadores alineados con el eje, es relativamente sencillo. “Alineado con el eje” significa que el cuadro delimitador no está girado; o en otras palabras, que las líneas de las cajas son paralelas a los ejes. A continuación, se explica cómo calcular el IoU de dos cuadros delimitadores alineados con el eje.
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
Explicación
Las imágenes son de esta respuesta
¡La respuesta más votada tiene un error matemático si está trabajando con coordenadas de pantalla (píxeles)! Envié una edición hace unas semanas con una larga explicación para todos los lectores para que entendieran las matemáticas. Pero esa edición no fue entendida por los revisores y fue eliminada, así que envié la misma edición nuevamente, pero esta vez resumida más brevemente. (Actualización: Rechazado 2vs1 porque se consideró un "cambio sustancial", je).
Entonces, explicaré completamente el GRAN problema con sus matemáticas aquí en esta respuesta separada.
Entonces, sí, en general, la respuesta más votada es correcta y es una buena manera de calcular el IoU. Pero (como también han señalado otras personas) su matemática es completamente incorrecta para las pantallas de computadora. No puedes simplemente hacer (x2 - x1) * (y2 - y1)
, ya que eso no producirá los cálculos de área correctos en absoluto. La indexación de la pantalla comienza en el píxel 0,0
y termina en width-1,height-1
. El rango de coordenadas de la pantalla es inclusive:inclusive
(inclusive en ambos extremos), por lo que un rango de 0
para 10
en coordenadas de píxeles tiene en realidad 11 píxeles de ancho, porque incluye 0 1 2 3 4 5 6 7 8 9 10
(11 artículos). Entonces, para calcular el área de las coordenadas de la pantalla, DEBE agregar +1 a cada dimensión, de la siguiente manera: (x2 - x1 + 1) * (y2 - y1 + 1)
.
Si está trabajando en algún otro sistema de coordenadas donde el rango no es inclusivo (como un inclusive:exclusive
sistema donde 0
para 10
significa "elementos 0-9 pero no 10"), entonces esta matemática adicional NO sería necesaria. Pero lo más probable es que esté procesando cuadros delimitadores basados en píxeles. Bueno, las coordenadas de la pantalla comienzan en 0,0
y sube desde allí.
A 1920x1080
la pantalla está indexada desde 0
(primer píxel) a 1919
(último píxel horizontalmente) y desde 0
(primer píxel) a 1079
(último píxel verticalmente).
Entonces, si tenemos un rectángulo en "espacio de coordenadas de píxeles", para calcular su área debe agregue 1 en cada dirección. De lo contrario, obtenemos la respuesta incorrecta para el cálculo del área.
Imagina que nuestro 1920x1080
La pantalla tiene un rectángulo basado en coordenadas de píxeles con left=0,top=0,right=1919,bottom=1079
(que cubre todos los píxeles de toda la pantalla).
Bueno, sabemos que 1920x1080
píxeles es 2073600
píxeles, que es el área correcta de una pantalla de 1080p.
Pero con las matemáticas equivocadas area = (x_right - x_left) * (y_bottom - y_top)
, obtendríamos: (1919 - 0) * (1079 - 0)
= 1919 * 1079
= 2070601
píxeles! ¡Eso está mal!
Por eso debemos agregar +1
a cada cálculo, lo que nos da las siguientes matemáticas corregidas: area = (x_right - x_left + 1) * (y_bottom - y_top + 1)
, dándonos: (1919 - 0 + 1) * (1079 - 0 + 1)
= 1920 * 1080
= 2073600
píxeles! ¡Y esa es de hecho la respuesta correcta!
El resumen más corto posible es: Los rangos de coordenadas de píxeles son inclusive:inclusive
, entonces debemos agregar + 1
a cada eje si queremos el true área de un rango de coordenadas de píxeles.
Para obtener algunos detalles más sobre por qué +1
es necesario, consulte la respuesta de Jindil: https://stackoverflow.com/a/51730512/8874388
Además de este artículo de pyimagesearch: https://www.pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection/
Y este comentario de GitHub: https://github.com/AlexeyAB/darknet/issues/3995#issuecomment-535697357
Dado que la matemática corregida no fue aprobada, cualquiera que copie el código de la respuesta más votada con suerte verá esta respuesta y podrá corregirlo ellos mismos, simplemente copiando las afirmaciones corregidas y las líneas de cálculo de área a continuación, que han sido arreglado para inclusive:inclusive
rangos de coordenadas (píxeles):
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)
A Sencillo camino
(La imagen no está dibujada a escala)
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))
El resultado sera 0.138211...
lo que significa 13.82%
.
Te mostramos reseñas y calificaciones
Si estás contento con lo expuesto, eres capaz de dejar una sección acerca de qué te ha parecido esta división.