经过前面:
PaddleSeg Validation 中添加 Boundary IoU的计算(1)——val.py文件细节提示
PaddleSeg Validation 中添加 Boundary IoU的计算(2)——inference 部分

相信诸位已经对 PaddleSeg 进行预测已经有了简单的理解,本来如果是随便加的话,直接在inference那个函数后边直接加就行,但是为了优雅一些

咱们首先打开 PaddleSeg 计算指标的函数文件:
paddleseg/utils/metrics.py

参考这篇 boundary IoU 的计算方式

在文件结尾直接添加:


# GitHub repo: https://github.com/bowenc0221/boundary-iou-api
# Reference: https://gist.github.com/bowenc0221/71f7a02afee92646ca05efeeb14d687d# General util function to get the boundary of a binary mask.
# 该函数用于获取二进制 mask 的边界
def mask_to_boundary(mask, dilation_ratio=0.05):"""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)"""mask = mask.astype(np.uint8)h, w = mask.shapeimg_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_erodedef boundary_iou(gt, dt, dilation_ratio=0.005, cls_num=2):"""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 = gt[0, 0]dt = dt[0]gt = gt.numpy().astype(np.uint8)dt = dt.numpy().astype(np.uint8)boundary_iou_list = []for i in range(cls_num):gt_i = (gt == i)dt_i = (dt == i)gt_boundary = mask_to_boundary(gt_i, dilation_ratio)dt_boundary = mask_to_boundary(dt_i, dilation_ratio)intersection = ((gt_boundary * dt_boundary) > 0).sum()union = ((gt_boundary + dt_boundary) > 0).sum()if union < 1:boundary_iou_list.append(0)continueboundary_iou = intersection / unionboundary_iou_list.append( boundary_iou )return np.array(boundary_iou_list)

因为这个是函数,不是以下的组件,所以无需添加装饰器:

manager.MODELS
manager.BACKBONES
manager.DATASETS
manager.TRANSFORMS
manager.LOSSES

paddleseg/utils/__init__.py 中是这样导入的

from . import metrics

所以也无需在某个地方

from metrics import boundary_iou

然后就是在:

# paddleseg/core/val.py# 这里有 metrics 即可
from paddleseg.utils import metrics, TimeAverager, calculate_eta, logger, progbar

直接用函数 boundary_iou 即可,用之前我们再看一下这个类 TimeAverager, 因为我们要用其来计算每一幅图 Boundary IoU 的均值,别看他名字是给时间做均值的,但是Python有一种哲学:“只要你长得像鸭子,那你就是鸭子”
原话是酱紫:

If it walks like a duck and quacks like a duck, it must be a duck

来看 TimeAverager 的源代码:

位置:paddleseg/utils/timer.py

class TimeAverager(object):def __init__(self):self.reset()def reset(self):self._cnt = 0             # 一共添加了多少个元素self._total_time = 0      # 总共多长时间self._total_samples = 0   # 采了多少样? 这个用法其实不明确def record(self, usetime, num_samples=None):self._cnt += 1                # 每次记录,总数得加1吧self._total_time += usetime   # 记录得把总数加上吧if num_samples:# 如果碰到了需要的样本,则把样本数加上self._total_samples += num_samplesdef get_average(self):if self._cnt == 0:# 防止除0错误,直接返回0return 0return self._total_time / float(self._cnt) # 返回值就是总数 / 总记录数 (均值)def get_ips_average(self):if not self._total_samples or self._cnt == 0:# 还没开始记录或者采样数为0,直接给0,防止除0错误return 0# 总采样数 / 总时间 (每多长时间可以采一个样)return float(self._total_samples) / self._total_time

粗看一下我们只需要直接将Boundary IoU 视为 usetime 传入 self.record就好了, 同时不使用num_samples,接下里 self.get_average 来计算均值即可

但是,Boundary IoU 可能用不了,因为 boundary_iou 函数 返回的是 np.ndarray,而这里有一个原地操作:

self._total_time += usetime
# 而 self._total_time 初始化为 int 0
self._total_time = 0

但是你当你用的时候,你发现,竟然可以运行,得益于Python是动态的,而且int和np.ndarray可以直接相加的多态hhh

所以,添加一个记录Boundary IoU的记录器:

progbar_val = progbar.Progbar(target=total_iters, verbose=1 if nranks < 2 else 2)
reader_cost_averager = TimeAverager()
batch_cost_averager = TimeAverager()
inference_averager = TimeAverager()  # 仅仅用于计算推理时间  # < --------------------
biou_averager = TimeAverager()       # 用于记录 BIoU

在你计算推理的代码之后,添加计算 Boundary IoU 的代码:

pred, logits = infer.inference(model,im,ori_shape=ori_shape,transforms=eval_dataset.transforms.transforms,# transforms=[],is_slide=is_slide,stride=stride,crop_size=crop_size)inference_averager.record(time.time() - infer_start, num_samples=len(label))# 计算 BIoUbiou = metrics.boundary_iou(pred, label)    # <------------ 这里计算biou_averager.record(biou)                  # <------------ 这里添加记录

最后添加一行打印结果的记录:

if print_detail:infor = "[EVAL] #Images: {} mIoU: {:.4f} Acc: {:.4f} Kappa: {:.4f} Dice: {:.4f}".format(len(eval_dataset), miou, acc, kappa, mdice)infor = infor + auc_infor if auc_roc else inforlogger.info(infor)logger.info("[EVAL] Class IoU: \n" + str(np.round(class_iou, 4)))logger.info("[EVAL] Class Precision: \n" + str(np.round(class_precision, 4)))logger.info("[EVAL] Class Recall: \n" + str(np.round(class_recall, 4)))mean_biou = biou_averager.get_average()        # <---------------- 这里这里计算全部的均值logger.info("[EVAL] Boundary IoU: \n" + str(np.round(mean_biou, 4))) # 打印

OK了,终于可以执行了

本来是没有下一篇了,因为 Boundary IoU 已经添加完毕了,但是我又发现,还有些其他的细节

于是在下一篇补充一些其他的细节,如 reverse_transform 之类的


建议诸位多看这种工业级别的源码,不光看着舒服,而且能学到不少东西

[PaddleSeg源码阅读] PaddleSeg Validation 中添加 Boundary IoU的计算(3)——添加Boundary IoU相关推荐

  1. [PaddleSeg源码阅读] PaddleSeg 导出静态图 export.py 文件中的道道

    周末去泰山玩♂耍,周六晚上10点半开始爬,周日上午10点26回到住的地方躺下,整整12个小时!! 我一个人爬完全程有些慢,不过起码我不是逃兵 下山到最后几段的时候,脸上摆出极其夸张和痛苦的表情,有几个 ...

  2. [PaddleSeg 源码阅读] PaddleSeg计算 mIoU

    这是我改成的numpy版本的,应该可以直接用,下边是 Paddle 版本的 def calculate_area(pred, label, num_classes, ignore_index=255) ...

  3. java中arraycopy的用法_[jdk源码阅读系列]Java中System.arraycopy()的用法

    本文转载,原文链接: 3分钟了解Java中System.arraycopy的用法 - 伊万夫斯基 - 博客园  https://www.cnblogs.com/benjieqiang/p/114288 ...

  4. sqlmap源码阅读系列init中的_cleanupOptions

    有很多人说sqlmap的源码很难,也有人说sqlmap的源码非常值得一读.我觉得这就像小马过河一样,你不读你就没有发言权.对我而言,截至目前,sqlmap的源码还在可以理解的范围内,至少要比unitt ...

  5. spark.mllib源码阅读:GradientBoostedTrees

    Gradient-Boosted Trees(GBT或者GBDT) 和 RandomForests 都属于集成学习的范畴,相比于单个模型有限的表达能力,组合多个base model后表达能力更加丰富. ...

  6. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  7. MyBatis 源码阅读 -- 核心操作篇

    核心操作包是 MyBatis 进行数据库查询和对象关系映射等工作的包.该包中的类能完成参数解析.数据库查询.结果映射等主要功能.在主要功能的执行过程中还会涉及缓存.懒加载.鉴别器处理.主键自增.插件支 ...

  8. 文本匹配开山之作-DSSM论文笔记及源码阅读(类似于sampled softmax训练方式思考)

    文章目录 前言 DSSM框架简要介绍 模型结构 输入 Encoder层 相似度Score计算 训练方式解读 训练数据 训练目标 训练方式总结 DSSM源码阅读 训练数据中输入有负样本的情况 输入数据 ...

  9. Spring源码阅读之bean对象的创建过程

    Spring源码阅读之bean对象的创建过程 ​ Spring是通过IOC容器来管理对象的,该容器不仅仅只是帮我们创建了对象那么简单,它负责了对象的整个生命周期-创建.装配.销毁.这种方式成为控制反转 ...

最新文章

  1. 承德大数据产业发展态势良好
  2. LESSON 12.8 原理进阶 (1):GBDT数学流程与残差概念辨析
  3. hashcode 和 equals 的关系_Java equals 和 hashCode 的这几个问题可以说明白吗?
  4. 求取给定的二叉树的镜像_17---二叉树的镜像
  5. IE6-IE11兼容性问题列表及解决办法
  6. nndl学习笔记(二)反向传播公式推导与详解
  7. 电脑硬盘坏道怎么检测,又如何修复图文教程
  8. Matlab程序如何打包
  9. Android手机开发总结
  10. 【批处理bat】注释一段文本/字符串
  11. cad完全卸载教程_卸载后 如何彻底删除CAD2010?
  12. mysql查询语句创建临时表_MySQL 临时表_mysql临时表创建_mysql临时表创建
  13. 2021年中国非接触式传感设备市场趋势报告、技术动态创新及2027年市场预测
  14. 2寸照片尺寸大小是多少,两寸照片多大?
  15. 深度篇——实例分割(三) 细说 mask rcnn 实例分割代码 训练自己数据 之 相关网络,数据处理,工具等
  16. 轻松解决汉字和拼音转换问题!!
  17. 计算机ppt以学校生活为主题的作文,校园生活为话题的作文(精选10篇)
  18. 电脑硬盘主分区和逻辑分区的区别是什么
  19. 【安全牛学习笔记】cookie安全问题
  20. android 观察者模式的简单demo,一个简单的demo彻底搞懂观察者模式

热门文章

  1. MySQL_day2笔记
  2. 织梦响应式精密机械模具类网站织梦模板(自适应手机端)
  3. Memcached哈希性能优化(八)——总结报告
  4. 模型稳定度指标PSI 释义及计算示例
  5. python如何撤销_python如何查看微信消息撤回
  6. Kafka分区副本分配规则
  7. 英语长语法难句——状语和状语从句
  8. 光场相机重聚焦之二——Lytro Illum记录光场
  9. 大家一起来找茬(BUG)
  10. 啊哈C——学习4.4一起来找茬