技术干货 | 基于 MindSpore 实现图像分割之平均表面距离

今天为大家带来的内容是Mean surface distance 平均表面距离的原理介绍及MindSpore的实现代码。

当我们评价图像分割的质量和模型表现时,经常会用到各类表面距离的计算。比如:

  • Mean surface distance 平均表面距离

  • Hausdorff distance 豪斯多夫距离(也被称为max_surface_distance 最大表面距离MSD)

  • Surface overlap 表面重叠度

  • Surface dice 表面dice值

  • Volumetric dice 三维dice值

等,都可以称为表面距离系列了。今天简单的讲解一下Mean surface distance 平均表面距离。

Mean surface distance 介绍

顾名思义,Mean surface distance这个指标就是P中所有点的表面距离的平均。这个指标又可称为Average Symmetric Surface Distance (ASSD),它也是医疗图像分割竞赛CHAOS中的一个评估指标。

This function is used to compute the Average Surface Distance from `y_pred` to `y` under the default setting. Mean Surface Distance(MSD), the mean of the vector is taken. This tell us how much, on average, the surface varies between the segmentation and the GT.

其实这个表面距离也没啥可介绍的,似乎就这么简单。来看看公式吧:

MindSpore 代码实现

原理已经讲完,话不多说,我们开始上代码。使用的是MindSpore框架实现的代码。

"""MeanSurfaceDistance."""

from scipy.ndimage import morphology
import numpy as np
from mindspore._checkparam import Validator as validator
from .metric import Metric


class MeanSurfaceDistance(Metric):

    def __init__(self, symmetric=False, distance_metric="euclidean"):
        super(MeanSurfaceDistance, self).__init__()
        self.distance_metric_list = ["euclidean", "chessboard", "taxicab"]
        distance_metric = validator.check_value_type("distance_metric", distance_metric, [str])
        self.distance_metric = validator.check_string(distance_metric, self.distance_metric_list, "distance_metric")
        self.symmetric = validator.check_value_type("symmetric", symmetric, [bool])
        self.clear()

    def clear(self):
        """Clears the internal evaluation result."""
        self._y_pred_edges = 0
        self._y_edges = 0
        self._is_update = False

    def _get_surface_distance(self, y_pred_edges, y_edges):
        """
        计算从预测图片边界到真实图片边界的表面距离。
        """

        if not np.any(y_pred_edges):
            return np.array([])

        if not np.any(y_edges):
            dis = np.full(y_edges.shape, np.inf)
        else:
            if self.distance_metric == "euclidean":
                dis = morphology.distance_transform_edt(~y_edges)
            elif self.distance_metric in self.distance_metric_list[-2:]:
                dis = morphology.distance_transform_cdt(~y_edges, metric=self.distance_metric)

        surface_distance = dis[y_pred_edges]

        return surface_distance

    def update(self, *inputs):
        """
        更新输入数据。
        """
        if len(inputs) != 3:
            raise ValueError('MeanSurfaceDistance need 3 inputs (y_pred, y, label), but got {}.'.format(len(inputs)))
        y_pred = self._convert_data(inputs[0])
        y = self._convert_data(inputs[1])
        label_idx = inputs[2]

        if y_pred.size == 0 or y_pred.shape != y.shape:
            raise ValueError("y_pred and y should have same shape, but got {}, {}.".format(y_pred.shape, y.shape))

        if y_pred.dtype != bool:
            y_pred = y_pred == label_idx
        if y.dtype != bool:
            y = y == label_idx

        self._y_pred_edges = morphology.binary_erosion(y_pred) ^ y_pred
        self._y_edges = morphology.binary_erosion(y) ^ y
        self._is_update = True

    def eval(self):
        """
        计算平均表面距离。
        """
        if self._is_update is False:
            raise RuntimeError('Call the update method before calling eval.')

        mean_surface_distance = self._get_surface_distance(self._y_pred_edges, self._y_edges)

        if mean_surface_distance.shape == (0,):
            return np.inf

        avg_surface_distance = mean_surface_distance.mean()

        if not self.symmetric:
            return avg_surface_distance

        contrary_mean_surface_distance = self._get_surface_distance(self._y_edges, self._y_pred_edges)
        if contrary_mean_surface_distance.shape == (0,):
            return np.inf

        contrary_avg_surface_distance = contrary_mean_surface_distance.mean()
        return np.mean((avg_surface_distance, contrary_avg_surface_distance))

使用方法如下:

import numpy as np
from mindspore import Tensor
from mindspore.nn.metrics import MeanSurfaceDistance

def test_mean_surface_distance():
    """test_mean_surface_distance"""
    x = Tensor(np.array([[3, 0, 1], [1, 3, 0], [1, 0, 2]]))
    y = Tensor(np.array([[0, 2, 1], [1, 2, 1], [0, 0, 1]]))
    metric = MeanSurfaceDistance()
    metric.clear()
    metric.update(x, y, 0)
    distance = metric.eval()
    print(distance)


test_mean_surface_distance()

1.4142135623730951

每个batch(比如两组数据)进行计算的时候如下:

import numpy as np
from mindspore import Tensor
from mindspore.nn.metrics import MeanSurfaceDistance


x = Tensor(np.array([[3, 0, 1], [1, 3, 0], [1, 0, 2]]))
y = Tensor(np.array([[0, 2, 1], [1, 2, 1], [0, 0, 1]]))
metric = MeanSurfaceDistance()
metric.clear()
metric.update(x, y, 0)

x1 = Tensor(np.array([[3, 0, 1], [1, 3, 0], [1, 0, 2]]))
y1 = Tensor(np.array([[0, 2, 1], [1, 2, 1], [0, 0, 1]]))
metric.update(x1, y1, 0)

distance = metric.eval()
print(distance)

MindSpore官方资料

官方QQ群 : 486831414

官网:https://www.mindspore.cn/

Gitee : https : //gitee.com/mindspore/mindspore

GitHub : https://github.com/mindspore-ai/mindspore

论坛:https://bbs.huaweicloud.com/forum/forum-1076-1.html 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值