当前位置:网站首页>boundary IoU 的计算方式

boundary IoU 的计算方式

2022-07-05 22:03:00 氵文大师

Boundary IoU 说白了就是计算 预测图片的边界GT图片的边界IoU

在这里插入图片描述
就是酱紫的两个边界计算 IoU 就好

问题是怎么计算上图这样的边界呢? ,用Canny边缘检测之类的? 算个梯度? 可是要边界啊,在分割的区域内部也有可能有边缘啊,这个不是边界

我们可以采用Boundary IoU原文的计算方式

这是原图:
在这里插入图片描述

我们可以让原图缩小一圈,就像这样
在这里插入图片描述

然后原图减去缩小版的图,就得到了边界
在这里插入图片描述

那怎么才能得到缩小版的原图呢? 这就请出了主角:腐蚀

可以参考这里:
OpenCV 图像处理之膨胀与腐蚀

腐蚀操作和膨胀操作相反,也就是将毛刺消除,判断方法为:在卷积核大小中对图片进行卷积。
取图像中(3 * 3)区域内的最小值。由于我们是二值图像,也就是取0(黑色)。 总结: 只要原图片3 * 3范围内有黑的,该像素点就是黑的。

接下来直接看看代码吧,代码见 Reference:

# GitHub repo: https://github.com/bowenc0221/boundary-iou-api
# Reference: https://gist.github.com/bowenc0221/71f7a02afee92646ca05efeeb14d687d

import cv2
import numpy as np
import matplotlib.pyplot as plt


# General util function to get the boundary of a binary mask.
# 该函数用于获取二进制 mask 的边界
def mask_to_boundary(mask, dilation_ratio=0.02):
    """ Convert binary mask to boundary mask. :param mask (numpy array, uint8): binary mask :param dilation_ratio (float): ratio to calculate dilation = dilation_ratio * image_diagonal :return: boundary mask (numpy array) """
    h, w = mask.shape
    img_diag = np.sqrt(h ** 2 + w ** 2) # 计算图像对角线长度
    dilation = int(round(dilation_ratio * img_diag))
    if dilation < 1:
        dilation = 1
    # Pad image so mask truncated by the image border is also considered as boundary.
    new_mask = cv2.copyMakeBorder(mask, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=0)
    kernel = np.ones((3, 3), dtype=np.uint8)
    new_mask_erode = cv2.erode(new_mask, kernel, iterations=dilation)
    
    # 因为之前向四周填充了0, 故而这里不再需要四周
    mask_erode = new_mask_erode[1 : h + 1, 1 : w + 1]
    
    # G_d intersects G in the paper.
    return mask - mask_erode
def boundary_iou(gt, dt, dilation_ratio=0.02):
    """ Compute boundary iou between two binary masks. :param gt (numpy array, uint8): binary mask :param dt (numpy array, uint8): binary mask :param dilation_ratio (float): ratio to calculate dilation = dilation_ratio * image_diagonal :return: boundary iou (float) """
    gt_boundary = mask_to_boundary(gt, dilation_ratio)
    dt_boundary = mask_to_boundary(dt, dilation_ratio)
    intersection = ((gt_boundary * dt_boundary) > 0).sum()
    union = ((gt_boundary + dt_boundary) > 0).sum()
    boundary_iou = intersection / union
    return boundary_iou

mask_to_boundary 函数用于计算边界的 mask,而 boundary_iou 用于计算 boundary_iouboundary_iou 中会调用 mask_to_boundary .

这一行用于给原图的四周添加0, 这样连边界区域的目标像素也会被腐蚀掉

# Pad image so mask truncated by the image border is also considered as boundary.
new_mask = cv2.copyMakeBorder(mask, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=0)

以下是 cv2.copyMakeBorder 操作的示意图,其实直接叫 Padding 就好了
在这里插入图片描述

这两行用于给图像做腐蚀操作,kernel size 是(3, 3)

kernel = np.ones((3, 3), dtype=np.uint8)
new_mask_erode = cv2.erode(new_mask, kernel, iterations=dilation) # iterations 指的是腐蚀的次数

再来看下 dilation 的计算:

h, w = mask.shape
img_diag = np.sqrt(h ** 2 + w ** 2) # 计算图像对角线长度
dilation = int(round(dilation_ratio * img_diag))
if dilation < 1:
    dilation = 1

腐蚀的次数与对角线的长度成正比,如果小于1则直接给1,dilation_ratio 是函数的参数

再看最后一步:

# 因为之前向四周填充了0, 故而这里不再需要四周
mask_erode = new_mask_erode[1 : h + 1, 1 : w + 1]

将周边的padding像素去掉,之后再将二者减掉就可:

return mask - mask_erode

最后就得到这个图:
在这里插入图片描述

boundary_iou 的计算方式和一般的 IoU 计算方式一样,有个问题就是,如果交集onion==0时,可能存在除0错误的问题,他这个代码里没有

所以应该改为:

def boundary_iou(gt, dt, dilation_ratio=0.02):
    """ Compute boundary iou between two binary masks. :param gt (numpy array, uint8): binary mask :param dt (numpy array, uint8): binary mask :param dilation_ratio (float): ratio to calculate dilation = dilation_ratio * image_diagonal :return: boundary iou (float) """
    gt_boundary = mask_to_boundary(gt, dilation_ratio)
    dt_boundary = mask_to_boundary(dt, dilation_ratio)
    intersection = ((gt_boundary * dt_boundary) > 0).sum()
    union = ((gt_boundary + dt_boundary) > 0).sum()
    if union < 1:
    	return 0
    boundary_iou = intersection / union
    return boundary_iou
原网站

版权声明
本文为[氵文大师]所创,转载请带上原文链接,感谢
https://blog.csdn.net/HaoZiHuang/article/details/125613577