0 概述

  • 论文名称:“Richer Convolutional Features for Edge Detection”
  • 论文链接:https://openaccess.thecvf.com/content_cvpr_2017/papers/Liu_Richer_Convolutional_Features_CVPR_2017_paper.pdf
  • 缩写:RCF

这一篇文论在我看来,是CVPR 2015年 HED网络(holistically-nested edge detection)的一个改进,RCF的论文中也基本上和HED网络处处对比

在上一篇文章中,我们依稀记得HED模型有这样一个图:

其中有HED的五个side output的特征图,下图是RCF论文中的图:

我们从这两个图的区别中来认识RCF相比HED的改进,大家可以看一看图。

揭晓答案:

  • HED是豹子的图片,但是RCF是两只小鸟的图片(手动狗头)
  • HED中的是side output的输出的特征图,而RCF中是conv3_1,conv3_2,这意味着RCF似乎把每一个卷积之后的输出的特征图都作为了一个side output

没错,HED选取了5个side output,每一个side output都是池化层之前的卷积层输出的特征图;而RCF则对每一次卷积的输出特征图都作为side output,换句话说 最终的side output中,同一尺寸的输出可能不止一个

如果还没有理解,请看下面章节,模型结构。

1 模型结构

RCF的backbone是VGG模型:

从图中可以看到:

  • 主干网络上分成state1到5,stage1有两个卷积层,stage2有两个卷积层,总共有13个卷积层,每一次卷积输出的图像,再额外接入一个1x1的卷积,来降低通道数,所以可以看到,图中有大量的21通道的卷积层。
  • 同一个stage的21通道的特征图经过通道拼接,变成42通道或者是63通道的特征图,然后再经过一个1x1的卷积层,来把通道数降低成1,再进过sigmoid层,输出的结果就是一个RCF模型中的side output了

2 损失函数

这里的损失函数其实和HED来说类似:

首先整体来看,损失函数依然使用二值交叉熵

其中

表示 negative的像素值,
表示positive的像素值。

一般来说轮廓检测任务中,positive的样本应该是较少的,因此

的值较小,因此损失函数中第一行,y=0也就是计算非轮廓部分的损失的时候,就会增加一个较小的权重,来避免类别不均衡的问题。

损失函数中有两个常数,一个是

,这个就是权重常数,默认为1.1;另外一个是
。论文中的描述为:

Edge datasets in this community are usually labeled by several annotators using their knowledge about the presences of objects and object parts. Though humans vary in cognition, these human-labeled edges for the same image share high consistency. For each image, we average all the ground truth to generate an edge probability map, which ranges from 0 to 1. Here, 0 means no annotator labeled at this pixel, and 1 means all annotators have labeled at this pixel. We consider the pixels with edge probability higher than η as positive samples and the pixels with edge probability equal to 0 as negative samples. Otherwise, if a pixel is marked by fewer than η of the annotators, this pixel may be semantically controversial to be an edge point. Thus, whether regarding it as positive or negative samples may confuse networks. So we ignore pixels in this category.

大意就是:一般对数据集进行标注,是有多个人来完成的。不同的人虽然有不同的意识,但是他们对于同一个图片的轮廓标注往往是具有一致性。RCF网络最后的输出,是由5个side output融合产生的,因此你这个RCF的输出也应该把大于

的考虑为positive,然后小于
的考虑为negative。

其实这一点我自己在复现的时候并没有考虑,我看网上的github和官方的代码中,都没有考虑这个,都是直接交叉熵。。。我这就也就多此一举的讲解一下论文中的这个

的含义

3 pytorch部分代码

对于这个RCF论文来说,关键就是一个模型的构建,另外一个就是损失函数的构建,这里放出这两部分的代码,来帮助大家更好的理解上面的内容。

3.1 模型部分

下面的代码在上采样部分的写法比较老旧,因为这个网上找来的pytorch版本估计比较老,当时还没有Conv2DTrans这样的函数封装,但是不妨碍大家通过代码来学习RCF。

class RCF(nn.Module):def __init__(self):super(RCF, self).__init__()#lr 1 2 decay 1 0self.conv1_1 = nn.Conv2d(3, 64, 3, padding=1)self.conv1_2 = nn.Conv2d(64, 64, 3, padding=1)self.conv2_1 = nn.Conv2d(64, 128, 3, padding=1)self.conv2_2 = nn.Conv2d(128, 128, 3, padding=1)self.conv3_1 = nn.Conv2d(128, 256, 3, padding=1)self.conv3_2 = nn.Conv2d(256, 256, 3, padding=1)self.conv3_3 = nn.Conv2d(256, 256, 3, padding=1)self.conv4_1 = nn.Conv2d(256, 512, 3, padding=1)self.conv4_2 = nn.Conv2d(512, 512, 3, padding=1)self.conv4_3 = nn.Conv2d(512, 512, 3, padding=1)self.conv5_1 = nn.Conv2d(512, 512, kernel_size=3,stride=1, padding=2, dilation=2)self.conv5_2 = nn.Conv2d(512, 512, kernel_size=3,stride=1, padding=2, dilation=2)self.conv5_3 = nn.Conv2d(512, 512, kernel_size=3,stride=1, padding=2, dilation=2)self.relu = nn.ReLU()self.maxpool = nn.MaxPool2d(2, stride=2, ceil_mode=True)self.maxpool4 = nn.MaxPool2d(2, stride=1, ceil_mode=True)#lr 0.1 0.2 decay 1 0self.conv1_1_down = nn.Conv2d(64, 21, 1, padding=0)self.conv1_2_down = nn.Conv2d(64, 21, 1, padding=0)self.conv2_1_down = nn.Conv2d(128, 21, 1, padding=0)self.conv2_2_down = nn.Conv2d(128, 21, 1, padding=0)self.conv3_1_down = nn.Conv2d(256, 21, 1, padding=0)self.conv3_2_down = nn.Conv2d(256, 21, 1, padding=0)self.conv3_3_down = nn.Conv2d(256, 21, 1, padding=0)self.conv4_1_down = nn.Conv2d(512, 21, 1, padding=0)self.conv4_2_down = nn.Conv2d(512, 21, 1, padding=0)self.conv4_3_down = nn.Conv2d(512, 21, 1, padding=0)self.conv5_1_down = nn.Conv2d(512, 21, 1, padding=0)self.conv5_2_down = nn.Conv2d(512, 21, 1, padding=0)self.conv5_3_down = nn.Conv2d(512, 21, 1, padding=0)#lr 0.01 0.02 decay 1 0self.score_dsn1 = nn.Conv2d(21, 1, 1)self.score_dsn2 = nn.Conv2d(21, 1, 1)self.score_dsn3 = nn.Conv2d(21, 1, 1)self.score_dsn4 = nn.Conv2d(21, 1, 1)self.score_dsn5 = nn.Conv2d(21, 1, 1)#lr 0.001 0.002 decay 1 0self.score_final = nn.Conv2d(5, 1, 1)def forward(self, x):# VGGimg_H, img_W = x.shape[2], x.shape[3]conv1_1 = self.relu(self.conv1_1(x))conv1_2 = self.relu(self.conv1_2(conv1_1))pool1   = self.maxpool(conv1_2)conv2_1 = self.relu(self.conv2_1(pool1))conv2_2 = self.relu(self.conv2_2(conv2_1))pool2   = self.maxpool(conv2_2)conv3_1 = self.relu(self.conv3_1(pool2))conv3_2 = self.relu(self.conv3_2(conv3_1))conv3_3 = self.relu(self.conv3_3(conv3_2))pool3   = self.maxpool(conv3_3)conv4_1 = self.relu(self.conv4_1(pool3))conv4_2 = self.relu(self.conv4_2(conv4_1))conv4_3 = self.relu(self.conv4_3(conv4_2))pool4   = self.maxpool4(conv4_3)conv5_1 = self.relu(self.conv5_1(pool4))conv5_2 = self.relu(self.conv5_2(conv5_1))conv5_3 = self.relu(self.conv5_3(conv5_2))conv1_1_down = self.conv1_1_down(conv1_1)conv1_2_down = self.conv1_2_down(conv1_2)conv2_1_down = self.conv2_1_down(conv2_1)conv2_2_down = self.conv2_2_down(conv2_2)conv3_1_down = self.conv3_1_down(conv3_1)conv3_2_down = self.conv3_2_down(conv3_2)conv3_3_down = self.conv3_3_down(conv3_3)conv4_1_down = self.conv4_1_down(conv4_1)conv4_2_down = self.conv4_2_down(conv4_2)conv4_3_down = self.conv4_3_down(conv4_3)conv5_1_down = self.conv5_1_down(conv5_1)conv5_2_down = self.conv5_2_down(conv5_2)conv5_3_down = self.conv5_3_down(conv5_3)so1_out = self.score_dsn1(conv1_1_down + conv1_2_down)so2_out = self.score_dsn2(conv2_1_down + conv2_2_down)so3_out = self.score_dsn3(conv3_1_down + conv3_2_down + conv3_3_down)so4_out = self.score_dsn4(conv4_1_down + conv4_2_down + conv4_3_down)so5_out = self.score_dsn5(conv5_1_down + conv5_2_down + conv5_3_down)## transpose and crop way weight_deconv2 =  make_bilinear_weights(4, 1).cuda()weight_deconv3 =  make_bilinear_weights(8, 1).cuda()weight_deconv4 =  make_bilinear_weights(16, 1).cuda()weight_deconv5 =  make_bilinear_weights(32, 1).cuda()upsample2 = torch.nn.functional.conv_transpose2d(so2_out, weight_deconv2, stride=2)upsample3 = torch.nn.functional.conv_transpose2d(so3_out, weight_deconv3, stride=4)upsample4 = torch.nn.functional.conv_transpose2d(so4_out, weight_deconv4, stride=8)upsample5 = torch.nn.functional.conv_transpose2d(so5_out, weight_deconv5, stride=8)### center cropso1 = crop(so1_out, img_H, img_W)so2 = crop(upsample2, img_H, img_W)so3 = crop(upsample3, img_H, img_W)so4 = crop(upsample4, img_H, img_W)so5 = crop(upsample5, img_H, img_W)fusecat = torch.cat((so1, so2, so3, so4, so5), dim=1)fuse = self.score_final(fusecat)results = [so1, so2, so3, so4, so5, fuse]results = [torch.sigmoid(r) for r in results]return results

3.2 损失函数部分

def cross_entropy_loss_RCF(prediction, label):label = label.long()mask = label.float()num_positive = torch.sum((mask==1).float()).float()num_negative = torch.sum((mask==0).float()).float()mask[mask == 1] = 1.0 * num_negative / (num_positive + num_negative)mask[mask == 0] = 1.1 * num_positive / (num_positive + num_negative)mask[mask == 2] = 0cost = torch.nn.functional.binary_cross_entropy(prediction.float(),label.float(), weight=mask, reduce=False)return torch.sum(cost)

参考文章:

  1. https://blog.csdn.net/a8039974/article/details/85696282
  2. https://gitee.com/HEART1/RCF-pytorch/blob/master/functions.py
  3. https://openaccess.thecvf.com/content_cvpr_2017/papers/Liu_Richer_Convolutional_Features_CVPR_2017_paper.pdf

轮廓检测_轮廓检测| Richer Convolutional Features | CVPR | 2017相关推荐

  1. Richer Convolutional Features for Edge Detection 论文阅读

    Richer Convolutional Features for Edge Detection是2017年cvpr中一篇边缘检测文章,准备以此为基础,1)了解深度学习中边缘检测的发展:2)如何使用深 ...

  2. 边缘检测:更丰富的卷积特征 Richer Convolutional Features for Edge Detection

    边缘检测:更丰富的卷积特征 Richer Convolutional Features for Edge Detection Source code and paper address Abstrac ...

  3. CNN边缘检测--Richer Convolutional Features for Edge Detection

    Richer Convolutional Features for Edge Detection CVPR2017 Caffe:https://github.com/yun-liu/rcf 本文针对边 ...

  4. 【边缘检测】RCF: Richer Convolutional Features for Edge Detection

    文章目录 一.背景 二.网络结构 三.和 HED[16] 的不同 一.背景 边缘检测是视觉任务中非常基础的任务,现有的基于CNN的边缘检测方法有两个明显的问题: 现有的方法大多只使用CNN的最后一层c ...

  5. (Pytorch)环境配置与代码学习1—边缘检测:更丰富的卷积特征 Richer Convolutional Features for Edge Detection

    (Pytorch)环境配置与代码学习1 - 边缘检测:更丰富的卷积特征 Richer Convolutional Features for Edge Detection Source code and ...

  6. 论文见解之RCF:Richer Convolutional Features for Edge Detection

    论文名:Richer Convolutional Features for Edge Detection code:https://github.com/yun-liu/rcf 这是cvpr2017的 ...

  7. pcb成型板aoi检测_缺陷检测 | PCB AOI质量检测之自动定位核选取算法

    PCB产品AOI检测,需要将模版与实际图像对齐,因此需要定位功能.定位功能就需要选取定位核,定位核的提取方法分为手动和自动.基于人眼视觉特征对区域敏感度判断的手动提取法存在很大的局限性,且当需要较多定 ...

  8. 空间中的语义直线检测_直线检测

    作者:张远学; 陶青川; 王维 期刊:<现代计算机> 为了限制河岸场景下不文明.违规的垂钓行为,提出一种融入深度学习的垂钓行为检测方法.首先使用基于CNN开发的语义分割模型deeplabV ...

  9. 处理veh调试器检测_越狱检测抖音逻辑???

    对于应用安全甲方一般会在这三个方面做防御.按逻辑分类的话应该应该分为这几类, 但如果从实现原理的话, 应该分为两类, 用API实现的 和 不用API实现的(这说的不用 API 实现, 不是指换成 in ...

最新文章

  1. 对象引用与托管指针(object references and managed pointers)
  2. java初始化实例化_Java 类初始化和实例化以及多态理解
  3. sqlserver的基本介绍
  4. spring boot 框架搭建
  5. C#分布式事务(TransactionScope )
  6. 神奇却又随处可见的斐波那契曲线...
  7. 从客户的角度看网站涉及的第一要素
  8. Mybatis各种模糊查询及#和$区别
  9. 编程算法/面试 - K链表翻转
  10. 《htmlxhtml权威指南》部分标签语义学习
  11. Go基础-Go中的Println和Print和Printf之间的区别
  12. 外贸软件出口管理系统亮点及重点
  13. 什么是Alpha通道?
  14. Mac 无法打开淘宝,天猫,京东等
  15. CRM —— 1、搭建开发环境
  16. Vue前端项目-系统监控-数据监控
  17. 记录:MI 10 反复重启的原因之一
  18. 如何快速获取淘宝商品的详细信息?看这里就够了
  19. 学计算机推荐的平板电脑,适合学生的平板电脑_学生平板推荐2020
  20. 栈和队列的相同点和不同点

热门文章

  1. 高并发大流量专题---10、MySQL数据库层的优化
  2. 一个关于pynoi游戏的C语言编程
  3. 为什么手机游戏手柄没有流行起来?
  4. 第一次项目之后...
  5. 3D 相机halcon算子,持续更新
  6. vxWorks下常用的几种延时方法
  7. c语言烟花百度云,C语言实现放烟花的程序
  8. 设置Jupyter notebook 默认工作路径,修改Jupyter notebook 默认浏览器为Chrome
  9. Cookie会话技术
  10. XML基础——extensible markup language