Reputation: 706
I want to detect rectangle collision in 2D plan (picture). I Can have many rectangle in my picture.
I stored rectangle coordinate in a data frame that look like this:
Each line correspond to a rectangle. proba is the result of my machine learning model
I want to loop over each line, check if a rectangle has shared coordinate with another one. IF the answer is yes, I want to check the probability of both rectangle, and delete the one with lowest probability
I already have function for collision detection (not sure it's 100% working yet, i'll check it later)
def collision(x1,y1,w1,h1,x2,y2,w2,h2):
if (x1<= x2+w2 and x1+w1>= x2 and y1 <= y2+h2 and y1+h1 >= y2):
return True
else:
return False
Now, how to loop over my data frame and remove collided rectangle with lowest probability?
Thanks for help
Edit: for above example, pseudo code would like this
Collision(line1,line2) > result = True > remove line 1
Now we have only 3 line
Collision(line1,line2) > result = False > Do Nothing
Collision(line1,line3) > result = True> remove line3
EDIT2: Adding reproducible example
#create data frame
fake_data=pd.DataFrame()
fake_data["left"]=(0.04,0.1,0.4,0.3)
fake_data["top"]=(0.31,0.13,0.34,0.28)
fake_data["width"]=(0.82,0.7,0.82,0.84)
fake_data["height"]=(0.57,0.2,0.59,0.55)
fake_data["proba"]=(0.60,0.62,0.34,0.39)
#define function
def collision(x1,y1,w1,h1,x2,y2,w2,h2):
if (x1<= x2+w2 and x1+w1>= x2 and y1 <= y2+h2 and y1+h1 >= y2):
return True
else:
return False
#example how to use function
collision(fake_data.iloc[3,0],fake_data.iloc[3,1],fake_data.iloc[3,2],fake_data.iloc[3,3],fake_data.iloc[1,0],fake_data.iloc[1,1],fake_data.iloc[1,2],fake_data.iloc[1,3])
Upvotes: 1
Views: 61
Reputation: 2062
Your question is not well-defined. Consider the following:
Rectangle 1 collides with Rectangle 2, not with Rectangle 3.
Rectangle 2 collides with Rectangle 3.
Their probabilities are Rectangle 1 > Rectangle 2 > Rectangle 3
Since Rectangle 2's probability is higher than Rectangle 3's, do you delete Rectangle 3 although once you delete Rectangle 2 because of collision with Rectangle 1, Rectangle 3 would be collision-free? By simply iterating over the dataframe, you might end up deleting way more rectangles than actually necessary.
If you don't care about that fact, the script below will obtain your desired results quickly:
def func(coordinates): # function to find collisions with all other rows for a given row
collisions = fake_data.apply(lambda row: (collision(*row[:-1], *coordinates[:-1]) == True), axis=1) # this finds collisions
collisions_w_lower_prob = [i for i, x in enumerate(collisions) if fake_data.iloc[i]['proba']< coordinates['proba']] # this saves those collision rows that had a lower probability
return collisions_w_lower_prob
to_delete = fake_data.apply(lambda row: func(row), axis=1).values # apply said function to your dataframe so all rows are compared to all other rows
to_delete_unique = set([y for z in to_delete for y in z]) #unique indices to delete
fake_data.drop(to_delete_unique)
>>> left top width height proba
0.1 0.13 0.7 0.2 0.62
If you do care, then you will need to delete iteratively, starting with the rectangle with highest probability:
fake_data = fake_data.sort_values('proba', ascending=False) # we want to start with higher probabilities first, as they are guaranteed to stay.
idx = 0
while True:
curr_row = fake_data.iloc[0]
to_delete = func(curr_row) # get deletion candidates
fake_data.drop(to_delete, inplace=True) # drop them from your dataframe
idx += 1
if idx > len(fake_data):
break
fake_data
>>> left top width height proba
0.1 0.13 0.7 0.2 0.62
Note that in your example both ways produce the same result, but that this might not always be the case for the explained reasons.
Upvotes: 1
Reputation: 49
Why can't you create a list of lists that would look like this :
rectangles = [[1,0.5,2,3,0.5],[1.5,0.5,3,4,0.7],[100, 100, 1, 1, 0.9]]
Where each little list represents a rectangle (with x, y, width, height, proba).
Here the rectangle 1 (rectangles[0]
) and the rectangle 2 (rectangles[1]
) collide to each other.
So you can write :
L_indices = []
for i in range(len(rectangles)):
for j in range(len(rectangles)):
if Collision(rectangles[i], rectangles[j]) == True: #if the rectangles are in collision
if rectangles[i][4]<=rectangles[j][4]: #the ith rectangle's proba is lower
L_indices.append(i) #so we get rid of it
else:
L_indices.append(j)
for indice in L_indices:
rectangles.pop(indice)
Then you get rid of the rectangle that has the lowest probability.
So here, 'rectangles' would look like this : rectangles = [[1.5,0.5,3,4,0.7],[100, 100, 1, 1, 0.9]]
Upvotes: 1