有空就学学的实例分割1——tensorflow2搭建maskr-cnn实例分割平台(代码片段)

Bubbliiiing Bubbliiiing     2023-03-12     365

关键词:

有空就学学的实例分割1——Tensorflow2搭建Mask R-CNN实例分割平台

学习前言

把Mask RCNN用tensorflow2实现了一遍,至少要跟得上时代对不对。

什么是Mask R-CNN


Mask R-CNN是He Kaiming大神2017年的力作,其在进行目标检测的同时进行实例分割,取得了出色的效果。
其网络的设计也比较简单,在Faster R-CNN基础上,在原本的两个分支上(分类+坐标回归)增加了一个分支进行语义分割,

源码下载

https://github.com/bubbliiiing/mask-rcnn-tf2
喜欢的可以点个star噢。

Mask R-CNN实现思路

一、预测部分

1、主干网络介绍


Mask-RCNN使用Resnet101作为主干特征提取网络,对应着图像中的CNN部分,其对输入进来的图片有尺寸要求,需要可以整除2的6次方。在进行特征提取后,利用长宽压缩了两次、三次、四次、五次的特征层来进行特征金字塔结构的构造。

ResNet101有两个基本的块,分别名为Conv Block和Identity Block,其中Conv Block输入和输出的维度是不一样的,所以不能连续串联,它的作用是改变网络的维度;Identity Block输入维度和输出维度相同,可以串联,用于加深网络的。
Conv Block的结构如下:

Identity Block的结构如下:

这两个都是残差网络结构。

以官方使用的coco数据集输入的shape为例,输入的shape为1024x1024,shape变化如下:

我们取出长宽压缩了两次、三次、四次、五次的结果来进行特征金字塔结构的构造。

实现代码:

from tensorflow.keras.layers import (Activation, Add, BatchNormalization,
                                     Conv2D, MaxPooling2D, ZeroPadding2D)
from tensorflow.keras.regularizers import l2


#----------------------------------------------#
#   conv_block和identity_block的区别主要就是:
#   conv_block会压缩输入进来的特征层的宽高
#   identity_block用于加深网络
#----------------------------------------------#
def identity_block(input_tensor, kernel_size, filters, stage, block, use_bias=True, weight_decay=0, train_bn=True):
    nb_filter1, nb_filter2, nb_filter3 = filters
    conv_name_base = 'res' + str(stage) + block + '_branch'
    bn_name_base = 'bn' + str(stage) + block + '_branch'

    x = Conv2D(nb_filter1, (1, 1), name=conv_name_base + '2a', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(input_tensor)
    x = BatchNormalization(name=bn_name_base + '2a')(x, training=train_bn)
    x = Activation('relu')(x)

    x = Conv2D(nb_filter2, (kernel_size, kernel_size), padding='same', name=conv_name_base + '2b', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(x)
    x = BatchNormalization(name=bn_name_base + '2b')(x, training=train_bn)
    x = Activation('relu')(x)

    x = Conv2D(nb_filter3, (1, 1), name=conv_name_base + '2c', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(x)
    x = BatchNormalization(name=bn_name_base + '2c')(x, training=train_bn)

    x = Add()([x, input_tensor])
    x = Activation('relu', name='res' + str(stage) + block + '_out')(x)
    return x

def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2), use_bias=True, weight_decay=0, train_bn=True):

    nb_filter1, nb_filter2, nb_filter3 = filters
    conv_name_base = 'res' + str(stage) + block + '_branch'
    bn_name_base = 'bn' + str(stage) + block + '_branch'

    x = Conv2D(nb_filter1, (1, 1), strides=strides, name=conv_name_base + '2a', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(input_tensor)
    x = BatchNormalization(name=bn_name_base + '2a')(x, training=train_bn)
    x = Activation('relu')(x)

    x = Conv2D(nb_filter2, (kernel_size, kernel_size), padding='same', name=conv_name_base + '2b', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(x)
    x = BatchNormalization(name=bn_name_base + '2b')(x, training=train_bn)
    x = Activation('relu')(x)

    x = Conv2D(nb_filter3, (1, 1), name=conv_name_base + '2c', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(x)
    x = BatchNormalization(name=bn_name_base + '2c')(x, training=train_bn)

    shortcut = Conv2D(nb_filter3, (1, 1), strides=strides, name=conv_name_base + '1', use_bias=use_bias, kernel_regularizer=l2(weight_decay))(input_tensor)
    shortcut = BatchNormalization(name=bn_name_base + '1')(shortcut, training=train_bn)

    x = Add()([x, shortcut])
    x = Activation('relu', name='res' + str(stage) + block + '_out')(x)
    return x

#----------------------------------------------#
#   获得resnet的主干部分
#----------------------------------------------#
def get_resnet(input_image, train_bn=True, weight_decay=0):
    #----------------------------------------------#
    #   假设输入进来的图片为1024,1024,3
    #----------------------------------------------#

    # 1024,1024,3 -> 512,512,64
    x = ZeroPadding2D((3, 3))(input_image)
    x = Conv2D(64, (7, 7), strides=(2, 2), name='conv1', use_bias=True, kernel_regularizer=l2(weight_decay))(x)
    x = BatchNormalization(name='bn_conv1')(x, training=train_bn)
    x = Activation('relu')(x)

    # 512,512,64 -> 256,256,64
    x = MaxPooling2D((3, 3), strides=(2, 2), padding="same")(x)
    C1 = x

    # 256,256,64 -> 256,256,256
    x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1), weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [64, 64, 256], stage=2, block='b', weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [64, 64, 256], stage=2, block='c', weight_decay=weight_decay, train_bn=train_bn)
    C2 = x

    # 256,256,256 -> 128,128,512
    x = conv_block(x, 3, [128, 128, 512], stage=3, block='a', weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='b', weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='c', weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='d', weight_decay=weight_decay, train_bn=train_bn)
    C3 = x
    
    # 128,128,512 -> 64,64,1024
    x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a', weight_decay=weight_decay, train_bn=train_bn)
    block_count = 22
    for i in range(block_count):
        x = identity_block(x, 3, [256, 256, 1024], stage=4, block=chr(98 + i), weight_decay=weight_decay, train_bn=train_bn)
    C4 = x
    
    # 64,64,1024 -> 32,32,2048
    x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a', weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b', weight_decay=weight_decay, train_bn=train_bn)
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c', weight_decay=weight_decay, train_bn=train_bn)
    C5 = x 
    return [C1, C2, C3, C4, C5]

2、特征金字塔FPN的构建


特征金字塔FPN的构建是为了实现特征多尺度的融合,在Mask R-CNN当中,我们取出在主干特征提取网络中长宽压缩了两次C2、三次C3、四次C4、五次C5的结果来进行特征金字塔结构的构造。

提取到的P2、P3、P4、P5、P6可以作为RPN网络的有效特征层,利用RPN建议框网络对有效特征层进行下一步的操作,对先验框进行解码获得建议框。

提取到的P2、P3、P4、P5可以作为Classifier和Mask网络的有效特征层,利用Classifier预测框网络对有效特征层进行下一步的操作,对建议框解码获得最终预测框;利用Mask语义分割网络对有效特征层进行下一步的操作,获得每一个预测框内部的语义分割结果。

实现代码如下:

#----------------------------------------------#
#   组合成特征金字塔的结构
#   P5长宽共压缩了5次
#   P5为32,32,256
#----------------------------------------------#
P5 = Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c5p5')(C5)
#----------------------------------------------#
#   将P5上采样和P4进行相加
#   P4长宽共压缩了4次
#   P4为64,64,256
#----------------------------------------------#
P4 = Add(name="fpn_p4add")([
    UpSampling2D(size=(2, 2), name="fpn_p5upsampled")(P5),
    Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c4p4')(C4)])
#----------------------------------------------#
#   将P4上采样和P3进行相加
#   P3长宽共压缩了3次
#   P3为128,128,256
#----------------------------------------------#
P3 = Add(name="fpn_p3add")([
    UpSampling2D(size=(2, 2), name="fpn_p4upsampled")(P4),
    Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c3p3')(C3)])
#----------------------------------------------#
#   将P3上采样和P2进行相加
#   P2长宽共压缩了2次
#   P2为256,256,256
#----------------------------------------------#
P2 = Add(name="fpn_p2add")([
    UpSampling2D(size=(2, 2), name="fpn_p3upsampled")(P3),
    Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c2p2')(C2)])
    
#-----------------------------------------------------------#
#   各自进行一次256通道的卷积,此时P2、P3、P4、P5通道数相同
#   P2为256,256,256
#   P3为128,128,256
#   P4为64,64,256
#   P5为32,32,256
#-----------------------------------------------------------#
P2 = Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p2")(P2)
P3 = Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p3")(P3)
P4 = Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p4")(P4)
P5 = Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p5")(P5)
#----------------------------------------------#
#   在建议框网络里面还有一个P6用于获取建议框
#   P5为16,16,256
#----------------------------------------------#
P6 = MaxPooling2D(pool_size=(1, 1), strides=2, name="fpn_p6")(P5)

#----------------------------------------------#
#   P2, P3, P4, P5, P6可以用于获取建议框
#----------------------------------------------#
rpn_feature_maps    = [P2, P3, P4, P5, P6]
#----------------------------------------------#
#   P2, P3, P4, P5用于获取mask信息
#----------------------------------------------#
mrcnn_feature_maps  = [P2, P3, P4, P5]

3、获得Proposal建议框


由上一步获得的有效特征层在图像中就是Feature Map,其有两个应用,一个是和ROIAsign结合使用、另一个是进入到Region Proposal Network进行建议框的获取。

在进行建议框获取的时候,我们使用的有效特征层是P2、P3、P4、P5、P6,它们使用同一个RPN建议框网络获取先验框调整参数,还有先验框内部是否包含物体。

在Mask R-cnn中,RPN建议框网络的结构和Faster RCNN中的RPN建议框网络类似。

首先进行一次3x3的通道数为512的卷积。

然后再分别进行一次anchors_per_location x 4的卷积 和一次anchors_per_location x 2的卷积

anchors_per_location x 4的卷积 用于预测 公用特征层上 每一个网格点上 每一个先验框的变化情况。(为什么说是变化情况呢,这是因为Faster-RCNN的预测结果需要结合先验框获得预测框,预测结果就是先验框的变化情况。)

anchors_per_location x 2的卷积 用于预测 公用特征层上 每一个网格点上 每一个预测框内部是否包含了物体。

当我们输入的图片的shape是1024x1024x3的时候,公用特征层的shape就是256x256x256、128x128x256、64x64x256、32x32x256、16x16x256,相当于把输入进来的图像分割成不同大小的网格,然后每个网格默认存在3(anchors_per_location )个先验框,这些先验框有不同的大小,在图像上密密麻麻。

anchors_per_location x 4的卷积的结果会对这些先验框进行调整,获得一个新的框。
anchors_per_location x 2的卷积会判断上述获得的新框是否包含物体。

到这里我们可以获得了一些有用的框,这些框会利用anchors_per_location x 2的卷积判断是否存在物体。

到此位置还只是粗略的一个框的获取,也就是一个建议框。然后我们会在建议框里面继续找东西。

实现代码为:

#------------------------------------#
#   五个不同大小的特征层会传入到
#   RPN当中,获得建议框
#------------------------------------#
def rpn_graph(feature_map, anchors_per_location, weight_decay=0):
    #------------------------------------#
    #   利用一个3x3卷积进行特征整合
    #------------------------------------#
    shared = Conv2D(512, (3, 3), padding='same', activation='relu',
                       name='rpn_conv_shared', kernel_regularizer=l2(weight_decay))(feature_map)
    
    #------------------------------------#
    #   batch_size, num_anchors, 2
    #   代表这个先验框是否包含物体
    #------------------------------------#
    x = Conv2D(anchors_per_location * 2, (1, 1), padding='valid', activation='linear', name='rpn_class_raw', kernel_regularizer=l2(weight_decay))(shared)
    rpn_class_logits = Reshape([-1,2])(x)
    rpn_probs = Activation("softmax", name="rpn_class_xxx")(rpn_class_logits)
    
    #------------------------------------#
    #   batch_size, num_anchors, 4
    #   这个先验框的调整参数
    #------------------------------------#
    x = Conv2D(anchors_per_location * 4, (1, 1), padding="valid", activation='linear', name='rpn_bbox_pred', kernel_regularizer=l2(weight_decay))(shared)
    rpn_bbox = Reshape([-1, 4])(x)

    return [rpn_class_logits, rpn_probs, rpn_bbox]

#------------------------------------#
#   建立建议框网络模型
#   RPN模型
#------------------------------------#
def build_rpn_model(anchors_per_location, depth, weight_decay=0):
    input_feature_map = Input查看详情  

用nvidiatensorcores和tensorflow2加速医学图像分割(代码片段)

用NVIDIATensorCores和TensorFlow2加速医学图像分割AcceleratingMedicalImageSegmentationwithNVIDIATensorCoresandTensorFlow2医学图像分割是当前学术界研究的热点。这方面正在进行的挑战、竞赛和研究项目的数量证明了这一点,这些项目的数量只是逐年... 查看详情

tensorflow2深度学习实战(十三):语义分割算法segnet实战(代码片段)

...部分对SegNet算法进行必要的讲解,然后在实战部分,使用TensorFlow2框架搭建SegNet网络,实现对场景中的目标(矿堆)进行分割。分割结果如下:文章目录一、SegNet算法详解 查看详情

《30天吃掉那只tensorflow2.0》4-1张量的结构操作(代码片段)

4-1张量的结构操作文章目录4-1张量的结构操作一,创建张量二,索引切片三,维度变换四,合并分割张量的操作主要包括张量的结构操作和张量的数学运算。张量结构操作诸如:张量创建,索引切片,... 查看详情

tensorflow2图像分割代码(代码片段)

importtensorflowastffromtensorflow_examples.models.pix2piximportpix2piximporttensorflow_datasetsastfdstfds.disable_progress_bar()importmatplotlib.pyplotaspltfromIPython.displayimportclear_outputfromIP 查看详情

tensorflow2.0用1.0的代码

参考技术Aimporttensorflow.compat.v1astftf.compat.v1.disable_eager_execution()或importtensorflow.compat.v1astftf.disable_v2_behavior()替换importtensorflowastf只用把最上面注释替换就行了 查看详情

我想学学ajax,请指教

本人对Ajax不懂,我想学学ajax,我问两个问题1.学ajax需要什么基础吗?譬如要求对那个语言要熟悉?2.假如我在vs2005环境下学,我还要安装什么吗?我是新手能回答的略细一点吗。谢谢!1.要学习ajax最好要有javascript的基础,但这... 查看详情

《30天吃掉那只tensorflow2.0》3-1低阶api示范(代码片段)

3-1低阶API示范下面的范例使用TensorFlow的低阶API实现线性回归模型和DNN二分类模型。低阶API主要包括张量操作,计算图和自动微分。importtensorflowastf#打印时间分割线@tf.functiondefprintbar():today_ts=tf.timestamp()% 查看详情

tensorflow2.autograph(代码片段)

...我机子里的版本1.13.1升级到了2.2.?版本...然后各种报错...tensorflow2.?里面貌似连版本1的tf.mod(...)都没有了...  查看详情

白嫖1年阿里云,反手就搭一个java环境

...器。有便宜不占王八蛋,二话不说,打开我82年的电脑,就是一顿操作。没成想,这次的活动体验如此之好,也就3分钟的时间,就搞到了一年的阿里云服务器。活动页面:1.领取ECS服务器打开活动页面:选择最后一关注下面二维... 查看详情

《30天吃掉那只tensorflow2.0》4-1张量的结构操作(代码片段)

4-1张量的结构操作文章目录4-1张量的结构操作一,创建张量二,索引切片三,维度变换四,合并分割张量的操作主要包括张量的结构操作和张量的数学运算。张量结构操作诸如:张量创建,索引切片,... 查看详情

基于typescript/node从0到1搭建一款爬虫工具(代码片段)

...了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。应用实例:1、一个班级只有一个班主任。2、Windows是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件 查看详情

tensorflow2.0(2)-自定义dense层以及训练过程

参考技术A  之前展示了tensorflow2.0的一个初级的实用例子作为开始,对比1.x版本操作还是有好很多的。接下来也将继续从比较基础的层面去了解tf2.0的各种实现  tensorflow2.0在上以keras去搭建网络,这种封装好的基... 查看详情

深度学习之图像分割——前言(霹雳吧啦wz)

文章目录一、什么是语义分割1.实例分割和语义分割的区别2.全景分割3.语义分割出现的问题及解决办法4.语义分割的实现流程二、暂定的学习规划三、语义分割任务常见数据集格式1.PASCALVOC——PNG图片(P模式)2.MSCOCO四、... 查看详情

多线程实例——遍历文件夹分割文件识别文件内容(代码片段)

上篇写完,感觉作为一个程序员,没有撸到底好像有点不过瘾对不对?大家都知道,C#早已进阶到8.0时代了,还用原始的Thread来写感觉有点low呀,而且通篇到最后居然还有线程最大值限制,技术控不能忍!!!那么本篇就干脆继... 查看详情

不平衡图像数据集 (Tensorflow2)

】不平衡图像数据集(Tensorflow2)【英文标题】:ImbalancedImageDataset(Tensorflow2)【发布时间】:2021-05-0701:18:32【问题描述】:我正在尝试解决二值图像分类问题,但是这两个类(分别为1类和2类的~590和~5900个实例)严重倾斜,但仍然非... 查看详情

1.5:基于聚类的“图像分割”实例编写(代码片段)

...基于聚类分析、小波变换等理论完成图像分割。  实例描述目标:利用K-means聚类算法对图像像素点颜色进行聚类实现简单的图像分割输出:同一聚类中的点使用相同颜色标记,不同聚类颜色不同技术路线:sklearn.cluster.KMea... 查看详情

单阶段实例分割solo-v1&solo-v2论文笔记(代码片段)

...gmentation1.概述导读:SOLO系列的文章解决的是单阶段的实例分割任务,相比之前的检测-分割(top-down)的方法在操作流程上简化不少,同时取得的效果也是不错的。在单阶段的实例分割任务中由于缺少检测带来... 查看详情