web-dev-qa-db-fra.com

Dans quelle mesure deux rectangles se chevauchent-ils?

J'ai deux rectangles a et b avec leurs côtés parallèles aux axes du système de coordonnées. J'ai leurs coordonnées comme x1, y1, x2, y2.

J'essaie de déterminer, non seulement ils se chevauchent, mais COMBIEN se chevauchent-ils? J'essaie de comprendre si ce sont vraiment les mêmes rectangles qui donnent ou prennent un peu de marge de manœuvre. Alors, leur superficie à 95% est-elle la même?

Une aide pour calculer le% de chevauchement?

43
Patrick Collins

Calculez l'aire de l'intersection, qui est également un rectangle:

SI = Max(0, Min(XA2, XB2) - Max(XA1, XB1)) * Max(0, Min(YA2, YB2) - Max(YA1, YB1))

De là, vous calculez la zone de l'union:

SU = SA + SB - SI

Et vous pouvez considérer le rapport

SI / SU

(100% en cas de chevauchement parfait, jusqu'à 0%).

64
Yves Daoust

La formule pour l'intersection sera

SI= Max(0, Min(XA2, XB2) - Max(XA1, XB1)) * Max(0, Min(YA2, YB2) - Max(YA1, YB1))

alors le syndicat sera S=SA+SB-SI

Et enfin, le rapport sera SI / S.

18
user3025064

J'ai récemment rencontré ce problème et appliqué la réponse d'Yves, mais cela a en quelque sorte conduit à une taille incorrecte, alors je l'ai réécrite.

En supposant deux rectangles A et B, découvrez combien ils se chevauchent et si oui, retournez la taille de la zone:

IF A.right < B.left OR A.left > B.right
    OR A.bottom < B.top OR A.top > B.bottom THEN RETURN 0

width := IF A.right > B.right THEN B.right - A.left ELSE A.right - B.left
height := IF A.bottom > B.bottom THEN B.bottom - A.top ELSE A.bottom - B.top

RETURN width * height
9
Ja͢ck

Bien que la réponse acceptée donnée soit correcte, je pense qu'il vaut la peine d'explorer cette réponse d'une manière qui rendra la justification de la réponse tout à fait évidente. C'est un algorithme trop courant pour avoir une réponse incomplète (ou pire, controversée). De plus, avec seulement un coup d'œil passant sur la formule donnée, vous risquez de manquer la beauté et l'extensibilité de l'algorithme, ainsi que les décisions implicites qui sont prises.

Tout d'abord, envisagez une façon de définir une boîte à deux dimensions avec:

  • (x, y) pour le point supérieur gauche
  • (x, y) pour le point inférieur droit

Cela pourrait ressembler à:

Example Rectangle

J'indique le coin supérieur gauche avec un triangle et le coin inférieur droit avec un cercle. C'est pour éviter la syntaxe opaque comme x1, x2 Pour cet exemple.

Deux rectangles qui se chevauchent pourraient ressembler à ceci:

Two Rectangles

Notez que pour trouver le chevauchement, vous recherchez l'endroit où l'orange et le bleu entrent en collision:

Rectangle Overlap

Une fois que vous reconnaissez cela, il devient évident que le chevauchement est le résultat de la recherche et de la multiplication de ces deux lignes sombres:

Defining Overlap

La longueur de chaque ligne est la valeur minimale des points du cercle, moins la valeur maximale des points du triangle.

Ici, j'utilise une forme à deux tons pour montrer que les points orange et bleu sont comparés. La petite lettre après la forme bicolore indique que les triangles sont comparés le long de cet axe (x ou y).

Finding Overlap

Par exemple, pour trouver la longueur de la ligne bleue foncée, vous pouvez voir que les triangles orange et bleu sont comparés pour rechercher la valeur maximale entre les deux. L'attribut qui est comparé est l'attribut x. La valeur maximale x entre les triangles orange et bleu est 210.

Une autre façon de dire la même chose est la suivante: la longueur de la nouvelle ligne qui s'adapte aux deux lignes que nous comparons est trouvée en soustrayant le point le plus proche du côté le plus long de la ligne du point le plus éloigné du côté le plus proche du ligne.

Showing Overlap

La recherche de ces lignes donne des informations complètes sur les zones qui se chevauchent.

The Overlap

Une fois que vous avez cela, trouver le pourcentage de chevauchement est trivial:

Finding the percentage of overlap

Mais attendez, si le rectangle orange ne chevauche pas le bleu, alors vous allez avoir un problème:

A Breaking Example

Avec cet exemple, vous obtenez un -850 pour notre zone de chevauchement, cela ne peut pas être correct. Pire encore, si une détection ne chevauche aucune des deux dimensions (ni sur les axes x ou y), vous obtiendrez toujours un nombre positif car les deux les dimensions sont négatives. C'est pourquoi vous voyez la Max(0, ...) * Max(0, ...) comme faisant partie de la solution; cela garantit que si l'un des chevauchements est négatif, vous obtiendrez un 0 de votre fonction.

La formule finale en accord avec notre symbologie:

The Formula

Il convient de noter que l'utilisation de la fonction max(0, ...) peut ne pas être nécessaire. Vous voudrez peut-être savoir si quelque chose chevauche l'une de ses dimensions plutôt que toutes; si vous utilisez max, vous effacerez ces informations. Pour cette raison, réfléchissez à la manière dont vous souhaitez gérer les images sans chevauchement. Normalement, la fonction max est bien à utiliser, mais cela vaut la peine d'être conscient de ce qu'elle fait.

Enfin, notez que puisque cette comparaison ne concerne que les mesures linéaires, elle peut être mise à l'échelle à des dimensions arbitraires ou à des quadrilatères se chevauchant arbitrairement.

Résumer:

intersecting_area = 
max(0, min(orange.circle.x, blue.circle.x) - max(orange.triangle.x, blue.triangle.x)) * max(0, min(orange.circle.y, blue.circle.y) - max(orange.triangle.y, blue.triangle.y))

percent_coverage = intersecting_area / (orange_area + blue_area - intersecting_area)

9
Connor

Il suffit de corriger les réponses précédentes pour que le rapport soit compris entre 0 et 1 (en utilisant Python):

    # (x1,y1) top-left coord, (x2,y2) bottom-right coord, (w,h) size
    A = {'x1': 0, 'y1': 0, 'x2': 99, 'y2': 99, 'w': 100, 'h': 100}
    B = {'x1': 0, 'y1': 0, 'x2': 49, 'y2': 49, 'w':  50, 'h':  50}

    # overlap between A and B
    SA = A['w']*A['h']
    SB = B['w']*B['h']
    SI = np.max([ 0, 1 + np.min([A['x2'],B['x2']]) - np.max([A['x1'],B['x1']]) ]) * np.max([ 0, 1 + np.min([A['y2'],B['y2']]) - np.max([A['y1'],B['y1']]) ])
    SU = SA + SB - SI
    overlap_AB = float(SI) / float(SU)
    print 'overlap between A and B: %f' % overlap_AB

    # overlap between A and A
    B = A
    SB = B['w']*B['h']
    SI = np.max([ 0, 1 + np.min([A['x2'],B['x2']]) - np.max([A['x1'],B['x1']]) ]) * np.max([ 0, 1 + np.min([A['y2'],B['y2']]) - np.max([A['y1'],B['y1']]) ])
    SU = SA + SB - SI
    overlap_AA = float(SI) / float(SU)
    print 'overlap between A and A: %f' % overlap_AA

La sortie sera:

    overlap between A and B: 0.250000
    overlap between A and A: 1.000000
5
Alessio B

En supposant que le rectangle doit être parallèle aux axes x et y car cela semble être la situation des commentaires et réponses précédents.

Je ne peux pas encore poster de commentaire, mais je voudrais souligner que les deux réponses précédentes semblent ignorer le cas où un rectangle latéral est totalement dans le côté de l'autre rectangle. S'il vous plait corrigez moi si je me trompe.

Considérez le cas

a: (1,1), (4,4)
b: (2,2), (5,3)

Dans ce cas, nous voyons que pour l'intersection, la hauteur doit être bTop - bBottom car la partie verticale de b est entièrement contenue dans a.

Nous avons juste besoin d'ajouter plus de cas comme suit: (Le code peut être raccourci si vous traitez en haut et en bas comme la même chose que droite et gauche, de sorte que vous n'avez pas besoin de dupliquer le bloc conditionnel deux fois, mais cela devrait faire.)

if aRight <= bLeft or bRight <= aLeft or aTop <= bBottom or bTop <= aBottom:
    # There is no intersection in these cases
    return 0
else:
    # There is some intersection

    if aRight >= bRight and aLeft <= bLeft:
        # From x axis point of view, b is wholly contained in a
        width = bRight - bLeft
    Elif bRight >= aRight and bLeft <= aLeft:
        # From x axis point of view, a is wholly contained in b
        width = aRight - aLeft
    Elif aRight >= bRight:
        width = bRight - aLeft
    else:
        width = aRight - bLeft

    if aTop >= bTop and aBottom <= bBottom:
        # From y axis point of view, b is wholly contained in a
        height = bTop - bBottom
    Elif bTop >= aTop and bBottom <= aBottom:
        # From y axis point of view, a is wholly contained in b
        height = aTop - aBottom
    Elif aTop >= bTop:
        height = bTop - aBottom
    else:
        height = aTop - bBottom

return width * height
4
Shar

@ User3025064 est correct et est la solution la plus simple, cependant, l'exclusivité doit d'abord être vérifiée pour les rectangles qui ne se croisent pas, par exemple, pour les rectangles A et B (en Visual Basic):

If A.Top =< B.Bottom or A.Bottom => B.Top or A.Right =< B.Left or A.Left => B.Right then
    Exit sub   'No intersection
else
    width = ABS(Min(XA2, XB2) - Max(XA1, XB1))
    height = ABS(Min(YA2, YB2) - Max(YA1, YB1))
    Area = width * height      'Total intersection area.
End if
2
user3476611
[ymin_a, xmin_a, ymax_a, xmax_a] = list(bbox_a)
[ymin_b, xmin_b, ymax_b, xmax_b] = list(bbox_b)

x_intersection = min(xmax_a, xmax_b) - max(xmin_a, xmin_b) + 1
y_intersection = min(ymax_a, ymax_b) - max(ymin_a, ymin_b) + 1

if x_intersection <= 0 or y_intersection <= 0:
    return 0
else:
    return x_intersection * y_intersection
2
Felix

La réponse de @ user3025064 est la bonne réponse. La réponse acceptée retourne par inadvertance les appels internes MAX et MIN. Nous n'avons pas non plus besoin de vérifier d'abord s'ils se croisent ou non si nous utilisons la formule présentée, MAX (0, x) par opposition à ABS (x). S'ils ne se coupent pas, MAX (0, x) renvoie zéro, ce qui rend la zone d'intersection 0 (c'est-à-dire disjointe).

Je suggère que @Yves Daoust corrige sa réponse car c'est celle acceptée qui apparaît à toute personne qui recherche ce problème. Encore une fois, voici la bonne formule pour l'intersection:

SI = Max(0, Min(XA2, XB2) - Max(XA1, XB1)) * Max(0, Min(YA2, YB2) - Max(YA1, YB1))

Le reste comme d'habitude. Syndicat:

SU = SA + SB - SI

et ratio:

SI/SU

1
Hazem