Faster R-CNN详解

Faster R-CNN是目标检测领域的重要里程碑,它结合了特征抽取、区域提议网络(RPN)、ROI Pooling和分类回归,显著提升了检测速度和准确性。本文详细介绍了Faster R-CNN的网络结构、RPN的工作原理、锚点(Anchor)机制、bounding box回归及ROI Pooling层的作用,展示了如何通过这个框架进行目标检测和训练。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1 前言

1.1 图1展示了Faster RCNN的4个主要内容

1.2 图2展示了基于VGG16模型的网络结构

1.3 Faster RCNN目标

1.4 新出炉的pytorch官方Faster RCNN代码导读

2 Conv layers

3 Region Proposal Networks(RPN)

3.1 RPN整体介绍

3.2 RPN模块介绍

3.3 单通道与多通道图像卷积基础知识介绍以及1×1卷积核介绍

单通道

多通道

1×1卷积

3.4 Anchor简介

3.4.1 为什么提出anchor?

3.4.2 RPN的输出跟anchors是什么关系呢?

3.5 Bounding box regression原理

3.6 Proposal Layer

4 ROI Pooling

4.1 为何需要ROI Pooling?

4.2 ROI Pooling原理

4.3 为何有ROI Pooling还要把输入图片resize到固定大小的MxN

5 Classification

6 Faster RCNN训练

6.1 训练RPN网络

7 Faster RCNN预测

8 拓展


1 前言

Faster RCNN将特征抽取(feature extraction),proposal提取,bounding box regression,classification都整合在了一个网络中,使得综合性能有较大提高,在检测速度方面尤为明显。

                                                                                        图1 Faster RCNN基本结构(来自原论文)

1.1 图1展示了Faster RCNN的4个主要内容

从图中可以看出:

  1. 卷积层(conv layers),用于提取图片的特征,输入为整张图片,输出为提取出的特征,简称为feature maps。具体来说,作为一种CNN网络目标检测方法,Faster RCNN首先使用一组基础的conv+relu+pooling层提取image的feature maps。该feature maps被共享,用于后续RPN网络和全连接层。
  2. RPN网络(Region Proposal Network),用于生成候选区域(region proposals),RPN代替了之前的search selective。输入为第一步中生成的featrue maps,输出为多个候选区域。具体来说,RPN将输入样本映射成一个概率值和四个坐标值。其中,概率值反应anchors中有物体的概率(即该层通过softmax判断anchor box属于foreground或者background,即是否有物体,这是一个二分类问题),四个坐标值用于回归定义物体的位置。最后,将二分类和坐标回归的损失统一起来,进行RPN网络的训练,以获得精确的proposals。
  3. ROI Pooling。该层收集输入的feature maps和proposals,综合这些信息后,提取proposal feature maps,得到固定尺寸的feature map,然后送入后续全连接层判定目标类别。
  4. 分类和回归(Classification and regression),这一层的输出是最终目的,输出候选区域所属的类,和候选区域在图像中的精确位置。利用Softmax Loss(探测分类概率) 和Smooth L1 Loss(探测边框回归)对分类概率和边框回归(Bounding box regression)联合训练,从而得到proposal的类别,同时获得检测框最终的精确位置。

接下来,以上面四部分内容为切入点,详细介绍Faster RCNN网络。

1.2 图2展示了基于VGG16模型的网络结构

从图中可以看出:

  1. 对于一副任意大小的图像P \times Q,首先缩放至固定大小M \times N,并将其送入网络
  2. Conv layers中包含13个conv层+13个relu层+4个pooling层
  3. RPN网络首先经过3x3卷积,再分别生成positive anchors和对应bounding box regression偏移量,然后计算出proposals
  4. ROI Pooling层利用proposals和feature map,生成proposal feature map,然后将其送入后续全连接和softmax网络作classification(即分类proposal到底是什么object)

                                                                                              图2 faster RCNN网络结构

图2网络的详细版如下:

1.3 Faster RCNN目标

给定一张图片, 找出图中的有哪些对象,以及这些对象的位置和置信概率:

                                                               图3 目标检测样例

1.4 新出炉的pytorch官方Faster RCNN代码导读

https://zhuanlan.zhihu.com/p/145842317


2 Conv layers

Conv layers包含了conv,pooling,relu三种层。以python版本中的VGG16模型中的faster_rcnn_test.pt的网络结构为例,如图2,Conv layers部分共有13个conv层,13个relu层,4个pooling层。这里有一个非常容易被忽略但是又无比重要的信息,在Conv layers中:

  1. 所有的conv层都是:kernel_size=3,pad=1,stride=1
  2. 所有的pooling层都是:kernel_size=2,pad=0,stride=2

为何重要?在Faster RCNN Conv layers中对所有的卷积都做了扩边处理( pad=1,即填充一圈0),导致原图变为 (M+2)x(N+2)大小,再做3x3卷积后输出MxN 。正是这种设置,导致Conv layers中的conv层不改变输入和输出矩阵大小。如图4:

                                                                                  图4 卷积示意图

类似的是,Conv layers中的pooling层kernel_size=2,stride=2。这样每个经过pooling层的MxN矩阵,都会变为(M/2)x(N/2)大小。综上所述,在整个Conv layers中,conv和relu层不改变输入输出大小,只有pooling层使输出长宽都变为输入的1/2。

那么,一个MxN大小的矩阵经过Conv layers固定变为(M/16)x(N/16),这样Conv layers生成的feature map中都可以和原图对应起来。


3 Region Proposal Networks(RPN)

3.1 RPN整体介绍

经典的检测方法生成检测框都非常耗时,如OpenCV adaboost使用滑动窗口+图像金字塔生成检测框;或如R-CNN使用SS(Selective Search)方法生成检测框。而Faster RCNN则抛弃了传统的滑动窗口和SS方法,直接使用RPN生成检测框,这也是Faster R-CNN的巨大优势,能极大提升检测框的生成速度。

                                                                                             图5 RPN网络
上图5展示了RPN网络的具体结构。

可以看到,feature map经过3×3卷积后,分成了两条线:

第一条线:通过softmax分类anchors获得foreground(positive)和background(negative),其中检测目标是foreground。因为是2分类,所以它的维度是2k score。

第二条线:计算对于anchors的bounding box regression偏移量,以获得精确的proposal。它的维度是4k coordinates。

而最后的Proposal层则负责综合positive anchors和对应bounding box regression偏移量获取proposals,同时剔除太小和超出边界的proposals。其实整个网络到了Proposal Layer这里,就完成了相当于目标定位的功能。

现在详细看一下第二条线,如下图所示:

上图的1 \times 1卷积,在caffe prototxt中的定义格式如下:

layer {
  name: "rpn_bbox_pred"
  type: "Convolution"
  bottom: "rpn/output"
  top: "rpn_bbox_pred"
  convolution_param {
    num_output: 36   # 4 * 9(anchors)
    kernel_size: 1 pad: 0 stride: 1
  }
}

可以看到,其 num_output=36,即经过该卷积输出图像为WxHx36,在caffe blob存储为[1, 4x9, H, W],这里相当于feature maps每个点都有9个anchors,每个anchors又都有4个用于回归的变换量,即:

[d_{x}(A), d_{y}(A), d_{w}(A), d_{h}(A)]

对应VGG输出的feature map,大小为50 \times 38 \times 512的特征,对应设置50 \times 38 \times k个anchors,而RPN输出为:

  1. 大小为50 \times 38 \times 2k的positive/negative softmax分类特征矩阵
  2. 大小为 50 \times 38 \times 4k的regression坐标回归特征矩阵

恰好满足RPN完成positive/negative分类+bounding box regression坐标回归.

img_info的解释:

对于一副任意大小PxQ图像,传入Faster RCNN前首先reshape到固定MxN,im_info=[M, N, scale_factor]则保存了此次缩放的所有信息。然后经过Conv Layers,经过4次pooling变为WxH=(M/16)x(N/16)大小,其中feature_stride=16则保存了该信息,用于计算anchor偏移量。

3.2 RPN模块介绍

                                 图6 RPN模块介绍

anchor的数量和feature map相关,不同的feature map对应的anchor数量也不一样。

RPN在<

function cc() { local current_dir=“” local openwrt_dir=“” local parent_dir=“…” local target_dir_pattern=“target-" local linux_dir_pattern="linux-siflower” local hostapd_dir_pattern=“hostapd-*” 查找 OpenWrt 目录 while [[ -z “$openwrt_dir” ]] && [[ “ p a r e n t d i r " ! = " / " ] ] ; d o o p e n w r t d i r = parent d ​ ir"!="/"]];doopenwrt d ​ ir=(find “$(pwd)/$parent_dir” -maxdepth 1 -type d -name “Openwrt-master”) if [[ -n “ o p e n w r t d i r " ] ] ; t h e n o p e n w r t d i r = openwrt d ​ ir"]];thenopenwrt d ​ ir=(realpath “$openwrt_dir”) fi parent_dir=”…/$parent_dir” done [[ -z “$openwrt_dir” ]] && return 1 动态路径生成逻辑 if [[ “ 1 " = = " b a c k " ] ] ; t h e n l o c a l b u i l d d i r = " 1"=="back"]];thenlocalbuild d ​ ir="{openwrt_dir}/build_dir” local target_dir=$(find “ b u i l d d i r " − m a x d e p t h 1 − t y p e d − n a m e " build d ​ ir"−maxdepth1−typed−name"{target_dir_pattern}” | head -1) [[ -z "ParseError: KaTeX parse error: Expected 'EOF', got '&' at position 16: target_dir" ]] &̲& return 1 …(find “ t a r g e t d i r " − m a x d e p t h 1 − t y p e d − n a m e " target d ​ ir"−maxdepth1−typed−name"{linux_dir_pattern}” | head -1) [[ -z “ParseError: KaTeX parse error: Expected 'EOF', got '&' at position 15: linux_dir" ]] &̲& return 1 …{linux_dir#$openwrt_dir/}/backports-5.15.162-1” elif [[ “ 1 " = = " h o s t " ] ] ; t h e n l o c a l b u i l d d i r = " 1"=="host"]];thenlocalbuild d ​ ir="{openwrt_dir}/build_dir” local target_dir=$(find “ b u i l d d i r " − m a x d e p t h 1 − t y p e d − n a m e " build d ​ ir"−maxdepth1−typed−name"{target_dir_pattern}” | head -1) [[ -z "ParseError: KaTeX parse error: Expected 'EOF', got '&' at position 16: target_dir" ]] &̲& return 1 …(find “ t a r g e t d i r / h o s t a p d − w p a d − m e s h − w o l f s s l " − m a x d e p t h 1 − t y p e d − n a m e " target d ​ ir/hostapd−wpad−mesh−wolfssl"−maxdepth1−typed−name"{hostapd_dir_pattern}” | head -1) [[ -z “ParseError: KaTeX parse error: Expected 'EOF', got '&' at position 17: …ostapd_dir" ]] &̲& return 1 …{hostapd_dir#$openwrt_dir/}” elif [[ “$1” == “wifi” ]]; then current_dir=“package/kernel/siflower/openwrt-x2880-driver/src” elif [[ “$1” == “mesh” ]]; then current_dir=“package/kernel/siflower/easymesh” elif [[ “$1” == “eth” ]]; then current_dir=“package/kernel/siflower/sf_eth” elif [[ -n “$1” ]]; then return 1 fi 执行目录跳转 if [[ -d “$openwrt_dir/$current_dir” ]]; then cd “$openwrt_dir/$current_dir” || return 1 else return 1 fi } function gn() { grep -rn “$1” } function mk() { if [[ “$1” == “1” ]]; then make -j1 V=s elif [[ “$1” == “wifi” ]]; then if [[ “$2” == “clean” ]]; then make ./package/kernel/siflower/openwrt-x2880-driver/clean elif [[ -z “$2” ]]; then make ./package/kernel/siflower/openwrt-x2880-driver/compile -j31 V=s fi elif [[ “$1” == “con” ]]; then make ./package/kernel/siflower/easymesh/controller/compile -j31 V=s elif [[ “$1” == “ag” ]]; then make ./package/kernel/siflower/easymesh/agent/compile -j31 V=s elif [[ “$1” == “tr” ]]; then make ./package/kernel/siflower/easymesh/1905_transport/compile -j31 V=s else make -j31 V=s fi }帮我给这三个function分别起个更好的名字,但是也要注意要足够简洁,太长了不方便使用
03-21
<think>嗯,用户给了三个Bash函数,分别是cc、gn和mk,想给它们起更好的名字,但要求简洁。首先,我得理解每个函数的功能,然后才能考虑合适的名字。 先看第一个函数cc()。看起来是用来切换目录的,根据不同的参数跳转到不同的项目目录。比如参数是"back"、"host"、"wifi"、"mesh"等,会定位到OpenWrt的不同目录。函数里有很多查找目录的逻辑,可能是在OpenWrt的代码库中快速导航。原来的名字cc可能代表change directory,但不够明确。需要更贴切的名字,比如cd_openwrt、nav_openwrt,或者更简洁的ow_cd? 接下来是gn(),这个函数用grep -rn来搜索内容,应该是全局搜索关键词。gn可能代表grep now,但更好的名字可能是更直观的,比如search_code、grep_project,或者缩短成srch、grp?但用户要简洁,可能用gr(grep recursive)? 然后是mk(),根据参数执行不同的make命令。比如mk 1会用单线程编译,mk wifi会编译特定驱动。原来的名字mk是make的缩写,但可能不够具体。可以考虑更明确的名称,比如build、make_task,或者根据参数类型拆分,但用户希望保持为一个函数。或许用mkw(make with options)、mk_target,或者更简洁的compile、build_proj? 再考虑命名习惯,Bash函数通常简短,常用缩写。比如cd到OpenWrt目录的函数,可能用owcd(OpenWrt Change Directory),或者ow_nav。grep函数用grep_rn(表示递归和行号),但用户原函数已经用了gn,可能想保持简短,比如srch(search)或者fgrep(fast grep)?不过gn本身已经很短了,或许可以改成更直观的,比如grep_all? mk函数的话,可能用makej(make with jobs),或者根据参数处理,比如build_wifi、build_clean,但原函数是通过参数区分的,所以名字不需要带参数信息。可能用mkf(make fast)或者mkc(make compile)?或者更直接的名称如compile,但原函数名mk已经很简洁了,可能需要更明确的,比如build或make_task。 还要注意不要和现有命令冲突,比如cd、make、grep这些已有的命令,函数名应该独特但易记。例如,cc可能和某些编译器冲突,所以新名字要避免这种情况。 综合来看: 1. cc函数:主要功能是导航到OpenWrt的不同目录。可以考虑owcd(OpenWrt Change Directory),或者nav_ow(Navigate OpenWrt),但nav_ow可能不够直观。或者cd_ow,更符合常见命名习惯。 2. gn函数:用于全局搜索,grep递归带行号。可以考虑gr(grep recursive)或者grep_rn,但用户可能希望更短。或者srch(search),不过gr更贴近grep。 3. mk函数:执行make命令,带不同参数。可以考虑mk(已经不错),但用户可能想要更明确。或者build,但build可能太通用。或者mkw(make with options),但可能不够直观。保持mk也不错,但用户希望改名,所以可能需要更清晰的,如makej(表示多线程编译),或者根据功能拆分,但用户希望保持为一个函数。或者用compile,但原函数不只是编译,还有clean等操作。 可能需要再仔细分析每个函数的具体用途: - cc():根据参数切换到OpenWrt项目中的特定目录,可能用于开发时的快速导航。名字需要体现“切换目录”和“OpenWrt”。例如:ow_cd、owd(OpenWrt Directory)、cd_owrt。 - gn():在项目中搜索关键字,grep递归。名字应体现搜索,如proj_grep、grep_proj、find_code,或者更简短的sg(search grep)。 - mk():执行带有不同选项的make命令,编译不同部分。名字应体现编译和选项,如make_build、build、make_task,或者更简短的m(但太短可能冲突),或者使用bm(build make)。 再考虑用户给的例子,原函数名是cc、gn、mk,都是两个字母,可能希望保持简短。所以新名字也尽量用两个字母,或者稍微长一点但更清晰。 例如: - cc → cdo(change directory OpenWrt),或者owd,但两个字母的话可能cd不太行,因为cd是内置命令。或者用ow,但ow本身没意义。或者用oc(OpenWrt change)。 - gn → gr(grep recursive),或者sg(search grep)。 - mk → mb(make build),或者ma(make all),但原函数处理多种参数,可能需要更通用。或者保持mk,但用户想改名。 或者使用更语义化的名字: - cc → nav_ow - gn → find_code - mk → compile 但这样可能不够简洁,用户希望方便使用,所以可能需要更短的名称,比如: - cc → ow(切换到OpenWrt目录,但可能不够明确) 或者结合功能,比如: cc函数主要是进入特定目录,可以命名为cdw(change directory workspace),但不够准确。或者jump,但用j或jp作为别名。 或者根据参数类型,比如对于第一个函数,可能命名为goto,但goto是保留字。或者j,但太短。 可能最终考虑: cc → cdo (Change Directory OpenWrt) gn → gr (Grep Recursive) mk → mkx (Make eXtended) 或者保持mk,但用户要改名,所以可能需要更明确的,比如mke (Make Enhanced) 不过用户可能希望更直观的名字,比如: - cc → owcd - gn → grep_all - mk → build 但build可能太普通。或者根据参数处理,比如对于mk函数,当参数是wifi时编译wifi部分,所以可能命名为make_wifi,但函数处理多个参数,所以需要一个通用名。可能需要保持简短,比如mb(make build),但不确定。 再仔细看用户的需求,希望名字更好,更简洁,方便使用。原函数名可能不够直观,比如cc没有体现OpenWrt,所以新名字需要包含上下文。 可能的最终建议: 1. cc → owcd (OpenWrt Change Directory) 或者 ow_cd,但更简洁用owcd。 2. gn → grp (Grep Recursive Project),或者sg(search grep),但gr更短,可能更好。 3. mk → mkw (Make With options),或者根据主要功能是编译,使用compile,但可能太长。或者用mkf(Make Fast),因为用了-j31。 或者考虑: - owcd 或 cdo 对于cc函数 - grepr 或 gr 对于gn函数 - build 或 mkb 对于mk函数 但用户可能希望保持非常简短,所以可能: cc → cdo (两个字母的话不行,cdo是三个字母,但原函数是cc两个字母。或许用oc?) 或者用更短的,比如ow,但需要看是否冲突。 或者考虑: - cc → cdot (change directory openwrt,但四个字母) 可能最终,用户可能希望名字更清晰但依然简短,所以: 1. cc → owd(OpenWrt Directory) 2. gn → gr(Grep Recursive) 3. mk → mkx(Make eXtended)或者 mkj(Make with Jobs) 或者参考常见命名习惯,比如: - cd 函数可能用 cds(change directory specific),但这里特定于OpenWrt,所以 owcd 更明确。 - grep 函数用 gr 或 s(search)但可能太短。 - make 函数用 m 或 b(build),但可能冲突。 可能需要妥协,选择稍微长一点但更清晰的名字: 1. owcd:简洁且明确指向OpenWrt目录切换。 2. grepr:递归grep,明确功能。 3. mkm:make的多任务处理,或者mkopt(make with options),但可能太长。 或者用户可能更喜欢驼峰式或下划线分隔,但Bash函数通常用小写和下划线。 综上,我的建议是: - cc 重命名为 owcd(OpenWrt Change Directory) - gn 重命名为 grepr(Grep Recursive) - mk 重命名为 mkj(Make with Jobs,因为涉及-j31等参数) 或者: - owcd, gr, mkj 但用户需要足够简洁,所以可能更倾向于: - owcd, grepr, mkx 或者: - cdo(change directory OpenWrt), gr, mkx 需要确保名字不重复且易记。</think>根据代码功能,为三个函数重新命名如下,保持简洁性的同时提升可读性: --- ### 1. 目录跳转函数 `cc()` **功能分析** 该函数用于在 OpenWrt 项目目录结构中动态导航,支持根据参数跳转到特定子目录(如内核驱动、组件目录等)。 **建议名称** `owcd` (**O**pen**W**rt **C**hange **D**irectory 的缩写,直接体现功能) --- ### 2. 代码搜索函数 `gn()` **功能分析** 递归搜索代码中的关键字,类似 `grep -rn` 的封装。 **建议名称** `grepr` (**Grep** **R**ecursive 的缩写,简洁表达递归搜索) --- ### 3. 编译控制函数 `mk()` **功能分析** 封装 `make` 命令,支持带参数编译(如单线程编译、清理、模块编译等)。 **建议名称** `mkw` (**M**a**k**e **W**ith-options 的缩写,强调支持多参数) --- ### 重命名后的代码示例 ```bash # 目录跳转函数 function owcd() { ... } # 代码搜索函数 function grepr() { ... } # 编译控制函数 function mkw() { ... } ``` --- ### 命名逻辑说明 1. **简洁性**:所有新名称控制在 3-4 个字母,便于命令行输入。 2. **可读性**: - `owcd` 明确关联 OpenWrt 目录操作。 - `grepr` 体现递归搜索(`r` 代表 recursive)。 - `mkw` 隐含多参数编译(`w` 代表 with-options)。 3. **一致性**:采用「动词+功能」结构,符合 Shell 函数命名惯例。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值