python - 如何使用 2 个 pandas DataFrames 计算 IOU(重叠)
问题描述
在物体检测中,IOU(intersection over union)是一个介于 0 和 1 之间的值,表示在某个图像中,绘制在物体上的 2 个框之间的重叠百分比。
为了帮助您理解那是什么,这里有一个插图:
红框是坐标x1(左上)、y1(左下)、x2(右上)、y2(右下)的实际值。
紫色框是坐标为 x1_predicted、y1_predicted、x2_predicted、y2_predicted 的预测值。
黄色阴影方块是 iou,如果它的值大于某个阈值(按照惯例为 0.5),则预测评估为 True,否则为 False。
下面是计算 2 个盒子的 IOU 的代码:
def get_iou(box_true, box_predicted):
x1, y1, x2, y2 = box_true
x1p, y1p, x2p, y2p = box_predicted
if not all([x2 > x1, y2 > y1, x2p > x1p, y2p > y1p]):
return 0
far_x = np.min([x2, x2p])
near_x = np.max([x1, x1p])
far_y = np.min([y2, y2p])
near_y = np.max([y1, y1p])
inter_area = (far_x - near_x + 1) * (far_y - near_y + 1)
true_box_area = (x2 - x1 + 1) * (y2 - y1 + 1)
pred_box_area = (x2p - x1p + 1) * (y2p - y1p + 1)
iou = inter_area / (true_box_area + pred_box_area - inter_area)
return iou
我有 2 个 csv 文件中包含的预测和实际数据,我将这些文件读入 2 个 pandas DataFrame 对象并从那里开始。
对于每张图像,我提取特定对象类型(例如:汽车)和实际数据的检测,这是 1 个图像中的 1 个对象(汽车)的示例(Beverly_hills1.png)
Actual:
Image Path Object Name X_min Y_min X_max Y_max
3842 Beverly_hills1.png Car 760 432 911 550
3843 Beverly_hills1.png Car 612 427 732 526
3844 Beverly_hills1.png Car 462 412 597 526
3845 Beverly_hills1.png Car 371 432 544 568
Detections:
image object_name x1 y1 x2 y2
594 Beverly_hills1.png Car 612 422 737 539
595 Beverly_hills1.png Car 383 414 560 583
以下是我的比较方式:
def calculate_overlaps(self, detections, actual):
calculations = []
detection_groups = detections.groupby('image')
actual_groups = actual.groupby('Image Path')
for item1, item2 in zip(actual_groups, detection_groups):
for detected_index, detected_row in item2[1].iterrows():
detected_coordinates = detected_row.values[2: 6]
detected_overlaps = []
coords = []
for actual_index, actual_row in item1[1].iterrows():
actual_coordinates = actual_row.values[4: 8]
detected_overlaps.append((
self.get_iou(actual_coordinates, detected_coordinates)))
coords.append(actual_coordinates)
detected_row['max_iou'] = max(detected_overlaps)
x1, y1, x2, y2 = coords[int(np.argmax(detected_overlaps))]
for match, value in zip([f'{item}_match'
for item in ['x1', 'y1', 'x2', 'y2']],
[x1, y1, x2, y2]):
detected_row[match] = value
calculations.append(detected_row)
return pd.DataFrame(calculations)
对于每种对象类型,这都会运行,这是低效的。
最终结果将如下所示:
image object_name x1 ... y1_match x2_match y2_match
594 Beverly_hills1.png Car 612 ... 427 732 526
595 Beverly_hills1.png Car 383 ... 432 544 568
1901 Beverly_hills10.png Car 785 ... 432 940 578
2015 Beverly_hills101.png Car 832 ... 483 1240 579
2708 Beverly_hills103.png Car 376 ... 466 1333 741
... ... ... ... ... ... ... ...
618 Beverly_hills93.png Car 922 ... 406 851 659
625 Beverly_hills93.png Car 1002 ... 406 851 659
1081 Beverly_hills94.png Car 398 ... 426 527 559
1745 Beverly_hills95.png Car 1159 ... 438 470 454
1746 Beverly_hills95.png Car 765 ... 441 772 474
[584 rows x 14 columns]
如何简化/矢量化并消除 for 循环?这可以使用np.where()
吗?
解决方案
首先,我注意到你的get_iou
函数有一个条件:x2 > x1, y2 > y1, x2p > x1p, y2p > y1p
. 您应该确保两个数据帧的条件都成立。
其次,actual
有列Image Path
和Object Name
,而detections
有image
和object_name
。您可能希望将相应的列更改为一个名称。
也就是说,这是我的解决方案merge
:
def area(df,columns):
'''
compute the box area
@param df: a dataframe
@param columns: box coordinates (x_min, y_min, x_max, y_max)
'''
x1,y1,x2,y2 = [df[col] for col in columns]
return (x2-x1)*(y2-y1)
# rename the columns
actual = actual.rename(columns={'Image Path':'image', 'Object Name':'object_name'})
# merge on `image` and `object_name`
total_df = (actual.merge(detections, on=['image', 'object_name'])
# compute the intersection
total_df['x_max_common'] = total_df[['X_max','x2']].min(1)
total_df['x_min_common'] = total_df[['X_min','x1']].max(1)
total_df['y_max_common'] = total_df[['Y_max','y2']].min(1)
total_df['y_min_common'] = total_df[['Y_min','y1']].max(1)
# valid intersection
true_intersect = (total_df['x_max_common'] > total_df['x_min_common']) & \
(total_df['y_max_common'] > total_df['y_min_common'])
# filter total_df with valid intersection
total_df = total_df[true_intersect]
# the various areas
actual_areas = area(total_df, ['X_min','Y_min','X_max','Y_max'])
predicted_areas = area(total_df, ['x1','y1','x2','y2'])
intersect_areas = area(total_df,['x_min_common','y_min_common','x_max_common', 'y_max_common'])
# IOU
iou_areas = intersect_areas/(actual_areas + predicted_areas - intersect_areas)
# assign the IOU to total_df
total_df['IOU'] = iou_areas
推荐阅读
- react-native - MapView Callout onPress 未触发
- r - 如何在 af 选择器功能选择功能中使用上传的 .csv 文件。现在它已被 r shiny 中的导入数据集访问
- python-3.x - 获取 pandas 数据框标头并使用它们创建新的 Access 表
- regex - apache忽略url查询参数顺序重写
- python - pandas:从字典的逆映射创建一列
- php - 无法在数据库中上传照片
- asp.net-mvc-5 - 使用 EF 是否有一个内置过程来引用父级的父级?
- docker - 在 docker 容器中运行的服务调用在另一个 docker 容器中的另一个服务中运行的 Web 服务
- python - python的〜在使用布尔值时发生了什么?
- php - PHP 404 Not Found 标头在 Google Chrome 中显示为 404 ()