基于全卷积神经网络(FCN)实现图像分割

news2024/11/25 10:49:07

目录

  • 1、作者介绍
  • 2、网络及数据集介绍
    • 2.1 FCN算法
    • 2.2 VOC_2012数据集
    • 2.3 制作自己的语义分割数据集
      • 2.3.1 标注方式一:多边形标注
        • 2.3.1.1 labelMe安装与数据标注
        • 2.3.1.2 数据格式转换
        • 2.3.1.3 数据集分类
      • 2.3.2 标注方式二:像素级涂抹
  • 3、基于RESNet50骨干的FCN语义分割模型实验
    • 3.1 网络结构
    • 3.2 代码实现
      • 3.2.1 RESNet主干
      • 3.2.2 改进膨胀卷积的残差模块
      • 3.2.2 FCN的思想
    • 3.3 训练及测试
  • 参考连接

1、作者介绍

李吉国,西安工程大学电子信息学院,2022级研究生
研究方向:机器视觉与人工智能
电子邮件:1653733740@qq.com

陈梦丹,女,西安工程大学电子信息学院,2022级研究生
研究方向:机器视觉与人工智能
电子邮件:1169738496@qq.com

2、网络及数据集介绍

2.1 FCN算法

CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体?在2015年之前还是一个世界难题。

Jonathan Long发表了论文:《Fully Convolutional Networks for Semantic Segmentation》
论文下载链接:https://arxiv.org/abs/1411.4038

通常CNN网络在卷积层之后会接上若干个全连接层, 将卷积层产生的特征图映射成一个固定长度的特征向量。以AlexNet为代表的经典CNN结构适合于图像级的分类和回归任务,因为它们最后都期望得到整个输入图像的一个数值描述(概率),比如AlexNet的ImageNet模型输出一个1000维的向量表示输入图像属于每一类的概率(softmax归一化)。
在这里插入图片描述
FCN对图像进行像素级的分类,从而解决了语义级别的图像分割问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。最后逐个像素计算softmax分类的损失, 相当于每一个像素对应一个训练样本。下图是Longjon用于语义分割所采用的全卷积网络(FCN)的结构示意图:
在这里插入图片描述
为了便于理解,这里给出一种基于VGG16主干的FCN分割模型结构,如下图,主干为原生的VGG16网络,但是,在最后阶段,并没有像做分类任务一样将特征图展平进行全连接,而是使用FCN的思想对逐像素进行分类,最后使用双线性插值还原原图尺寸,实现分割任务。
在这里插入图片描述

2.2 VOC_2012数据集

VOC_2012数据集来源于PASCAL VOC挑战赛(The PASCAL Visual Object Classes),该数据集主要包括20个类别,可用于目标检测、目标分割、行为识别等任务。可选择在官网下载或者使用百度网盘进行下载。

(1)官网下载地址:
链接1:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
链接2:https://pjreddie.com/projects/pascal-voc-dataset-mirror/
(2)百度网盘下载地址:
链接: https://pan.baidu.com/s/1ln-1Pa2VzXx5P9179PPvvQ
提取码:miao

原始图片(左侧)及标签(右侧)如下图所示:
在这里插入图片描述

2.3 制作自己的语义分割数据集

2.3.1 标注方式一:多边形标注

使用工具:Labelme
在进行Segmentation训练之前需要准备训练集和验证集,本节将要来介绍如何使用LabelMe进行标记。LabelMe是个可以绘制多边形、矩形、圆形、直线、点的一套标记工具,可用于分类、目标检测、语义分割、实例分割任务上的数据标注。

2.3.1.1 labelMe安装与数据标注

首先安装LabelMe,我使用Anaconda 进行安装。打开Anaconda Prompt 执行以下指令:

pip install labelme

接着输入labelme 会出现以下UI 介面,点选Open Dir 选择要标记的图片资料夹Labelme
在这里插入图片描述
在图片上按右键选择Create Polygans 后,就可以开始画标记点了、画好后填入要标记的label
在这里插入图片描述
语义分割时若有多个相同类别的目标物也选择一样的label,不同类别分别标注标签:
在这里插入图片描述
再点击左边的Save,会生成与图片名相同json文件,将其存放在data_mask/jsons文件夹中,最后选择左边的Next Image。json格式文件如下:

{
  "version": "4.5.9",
  "flags": {},
  "shapes": [
    {
      "label": "Gingerbread",
      "points": [
        [
          888.2439024390243,
          409.80487804878044
        ],
        [
          823.6097560975609,
          404.92682926829264
        ],
		...
      ],
      "group_id": null,
      "shape_type": "polygon",
      "flags": {}
    },
    {
      "label": "Coconutmilk",
      "points": [
        [
          882.1463414634146,
          198.8292682926829
        ],
        [
          887.0243902439024,
          263.4634146341463
        ],
		...
      ],
      "group_id": null,
      "shape_type": "polygon",
      "flags": {}
    }
  ],
  "imagePath": "20210801150111.jpg",
  "imageData": "...",
  "imageHeight": 320,
  "imageWidth": 480
}

2.3.1.2 数据格式转换

全部数据集标注完成后,将json文件批量转化为训练所需的标注图像。标注图像所存放的文件夹为data_mask/masks,代码如下:

import os
import cv2
import numpy as np
import json
'''
制作一个只包含分类标注的标签图像,假如我们分类的标签为cat和dog,那么该标签图像中,Background为0,cat为1,dog为2。
我们首先要创建一个和原图大小一致的空白图像,该图像所有像素都是0,这表示在该图像中所有的内容都是Background。
然后根据标签对应的区域使用与之对应的类别索引来填充该图像,也就是说,将cat对应的区域用1填充,dog对应的区域用2填充。
特别注意的是,一定要有Background这一项且一定要放在index为0的位置。
'''
# 分类标签,一定要包含'Background'且必须放在最前面
category_types = ['Background', 'Gingerbread', 'Coconutmilk']
# 将图片标注json文件批量生成训练所需的标签图像png
imgpath_list = os.listdir('data_mask/images')
for img_path in imgpath_list:
    img_name = img_path.split('.')[0]
    img = cv2.imread(os.path.join('data_mask/images', img_path))
    h, w = img.shape[:2]
    # 创建一个大小和原图相同的空白图像
    mask = np.zeros([h, w, 1], np.uint8)

    with open('data_mask/jsons/'+img_name+'.json', encoding='utf-8') as f:
        label = json.load(f)

    shapes = label['shapes']
    for shape in shapes:
        category = shape['label']
        points = shape['points']
        # 将图像标记填充至空白图像
        points_array = np.array(points, dtype=np.int32)
        mask = cv2.fillPoly(mask, [points_array], category_types.index(category))

    # 生成的标注图像必须为png格式
    cv2.imwrite('data_mask/masks/'+img_name+'.png', mask)

此时,我们打开查看生成的标注图像,但是,会发现:怎么是全黑的图像?
在这里插入图片描述
此时,千万不要怀疑,因为这就是正确的标注图像。该标注图像包含背景和我们设定的两个标签,背景位置对应的像素值应该为0,标签图像对应的像素值应该为1或2,该图像只包含0、1和2,因为1和2这个像素值太小了,无法看清,如果打开图像查看的话,就是一个全黑的图像。

如果想查看某个类别的标注情况,可以在mask = cv2.fillPoly(mask, [points_array], category_types.index(category))中将该颜色改为一个清晰可见的颜色,例如将Gingerbread的像素值改成125,将Coconutmilk的像素值改为255,就可以清晰的看到我们标注的图像了。但是最终制作标签的时候要记得将其改回对应的索引值。

for shape in shapes:
    category = shape['label']
    points = shape['points']
    # 将图像标记填充至空白图像
    points_array = np.array(points, dtype=np.int32)
    # mask = cv2.fillPoly(mask, [points_array], category_types.index(category))

    if category == 'Gingerbread':
        # 调试时将某种标注的填充颜色改为255,便于查看用,实际时不需进行该操作
        mask = cv2.fillPoly(mask, [points_array], 125)
    elif category == 'Coconutmilk':
        mask = cv2.fillPoly(mask, [points_array], 255)
    else:
        mask = cv2.fillPoly(mask, [points_array], category_types.index(category))

cv2.imshow('mask', mask)
cv2.waitKey(0)

在这里插入图片描述

2.3.1.3 数据集分类

以上步骤完成后,我们需将数据集分为训练集、验证集和测试集进行训练,在这里划分数据集的代码我也写好了,按照7:2:1进行划分,代码如下:

import os
import random
import shutil


'''
├── data(按照7:2:1比例划分)
│   ├── train 存放用于训练的图片
│   ├── trainannot 存放用于训练的图片标注
│   ├── val 存放用于验证的图片
│   ├── valannot 存放用于验证的图片标注
│   ├── test 存放用于测试的图片
│   ├── testannot 存放用于测试的图片标注
'''

# 创建数据集文件夹
dirpath_list = ['data/train', 'data/trainannot', 'data/val', 'data/valannot', 'data/test', 'data/testannot']
for dirpath in dirpath_list:
    if os.path.exists(dirpath):
        shutil.rmtree(dirpath)   # 删除原有的文件夹
        os.makedirs(dirpath)   # 创建文件夹
    elif not os.path.exists(dirpath):
        os.makedirs(dirpath)

# 训练集、验证集、测试集所占比例
train_percent = 0.7
val_percent = 0.2
test_percent = 0.1

# 数据集原始图片所存放的文件夹,必须为png文件
imagefilepath = 'data_mask/images'
total_img = os.listdir(imagefilepath)
# 所有数据集的图片名列表
total_name_list = [row.split('.')[0] for row in total_img]
num = len(total_name_list)
num_list = range(num)
# 训练集、验证集、测试集所包含的图片数目
train_tol = int(num * train_percent)
val_tol = int(num * val_percent)
test_tol = int(num * test_percent)

# 训练集在total_name_list中的index
train_numlist = random.sample(num_list, train_tol)
# 验证集在total_name_list中的index
val_test_numlist = list(set(num_list) - set(train_numlist))
val_numlist = random.sample(val_test_numlist, val_tol)
# 测试集在total_name_list中的index
test_numlist = list(set(val_test_numlist) - set(val_numlist))

# 将数据集和标签图片安装分类情况依次复制到对应的文件夹
for i in train_numlist:
    img_path = 'data_mask/images/'+total_name_list[i]+'.png'
    new_path = 'data/train/'+total_name_list[i]+'.png'
    shutil.copy(img_path, new_path)
    img_path = 'data_mask/masks/' + total_name_list[i] + '.png'
    new_path = 'data/trainannot/' + total_name_list[i] + '.png'
    shutil.copy(img_path, new_path)
for i in val_numlist:
    img_path = 'data_mask/images/'+total_name_list[i]+'.png'
    new_path = 'data/val/'+total_name_list[i]+'.png'
    shutil.copy(img_path, new_path)
    img_path = 'data_mask/masks/' + total_name_list[i] + '.png'
    new_path = 'data/valannot/' + total_name_list[i] + '.png'
    shutil.copy(img_path, new_path)
for i in test_numlist:
    img_path = 'data_mask/images/'+total_name_list[i]+'.png'
    new_path = 'data/test/'+total_name_list[i]+'.png'
    shutil.copy(img_path, new_path)
    img_path = 'data_mask/masks/' + total_name_list[i] + '.png'
    new_path = 'data/testannot/' + total_name_list[i] + '.png'
    shutil.copy(img_path, new_path)

2.3.2 标注方式二:像素级涂抹

使用工具:精灵标注助手
精灵标注助手是制作数据集中比较容易上手的一款软件。

精灵标注助手官网下载链接: http://www.jinglingbiaozhu.com/
在这里插入图片描述

在精灵标注助手中实际操作的过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
像素级标注时会给你一个画笔,涂抹需要标注的区域并标记标签,最后导出标注文件,这个软件不好的一点是,导出的路径不可以自选,操作如下:
在这里插入图片描述
导出的标签如下,导出的标签是调色板图像,是无法作为数据集的标签的,因为没有背景像素值,需要进行处理,给背景补0即可。
在这里插入图片描述

3、基于RESNet50骨干的FCN语义分割模型实验

3.1 网络结构

RESNet50的结构如下
在这里插入图片描述
基于RESNet50的FCN结构
在这里插入图片描述

3.2 代码实现

3.2.1 RESNet主干

在这里插入图片描述

3.2.2 改进膨胀卷积的残差模块

在这里插入图片描述

3.2.2 FCN的思想

在这里插入图片描述

3.3 训练及测试

完整项目代码地址(百度网盘):
链接: https://pan.baidu.com/s/1HQgce250IuG-I61UY2JNgw
提取码:uy5f

运行train.py即可开始训练(修改里面的相关配置参数以及RESNet的预训练权重)
在这里插入图片描述
测试结果:
在这里插入图片描述

参考连接

https://blog.csdn.net/oJiWuXuan/article/details/119569038
https://blog.csdn.net/qq_40280673/article/details/123682672

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/684803.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 学习记录39(C高级篇)

Linux 学习记录39(C高级篇) 本文目录 Linux 学习记录39(C高级篇)一、gdb调试工具1. gdb工具的使用 二、枚举类型 enum1. 定义 三、Makefile1. 什么是makefile2. 什么是Make3. Make用途4. Makefile的工作过程(1. Makefile分步编译的目的 5. Makefile的语法规则6. Makefile变量的…

【unity每日一记】那些动作基础你还记得吗—移动和旋转相关

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:uni…

农村电商APP软件开发 电商新农村紧跟时代脚步

互联网技术的深入发展让电子商务走向了高潮,不过在一些交通闭塞的乡村地区,电商发展还处于初期阶段。大量农产品从生产到流通渠道的变革,让互联网农产品成为新型农业发展的重要手段,不仅增加了农业产值提高了农民收入,…

《kafka 核心技术与实战》课程学习笔记(六)

生产者消息分区机制原理剖析 为什么分区? Kafka 有主题(Topic)的概念,它是承载真实数据的逻辑容器,而在主题之下还分为若干个分区,也就是说 Kafka 的消息组织方式实际上是三级结构:主题 - 分区…

[CKA]考试之四层负载均衡service

由于最新的CKA考试改版,不允许存储书签,本博客致力怎么一步步从官网把答案找到,如何修改把题做对,下面开始我们的 CKA之旅 题目为: Task 重新配置一个已经存在的front-end的deployment,在名字为nginx的容…

spring boot 整合EasyPoi导入导出,下载模版功能

引入依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></…

【算法题】链表系列之从尾到头打印链表、重建二叉树、用两个栈实现队列

【算法题】链表系列 一、从尾到头打印链表1.1、题目描述1.2、递归法1.3、栈&#xff08;stack&#xff09; 二、重建二叉树2.1、题目描述2.2、前置知识&#xff1a;2.3、分治算法2.4、小结 三、用两个栈实现队列3.1、题目描述3.2、双栈法3.3、小结 总结 一、从尾到头打印链表 …

从零开始 Spring Boot 48:JPA Hibernate

从零开始 Spring Boot 48&#xff1a;JPA & Hibernate 图源&#xff1a;简书 (jianshu.com) 对象关系映射&#xff08;ORM&#xff09;是将Java对象转换为数据库表的过程。换句话说&#xff0c;这允许我们在没有任何SQL的情况下与关系数据库进行交互。Java Persistence AP…

Redis入门(4)-list

redis中list数据会按照插入顺序进行排序&#xff0c;其底层是一个无头结点的双向链表&#xff0c;因此表头和表尾的操作性能较高&#xff0c;但中间元素操作性能较差。 1.lpush key element [element ] 从表头插入元素 lpush nosql redis hbase lpush nosql mongdb2.lrange…

信息熵、条件熵、信息增益

一、信息熵 其中&#xff1a; &#xff1a;样本属于第i个类别的概率 &#xff1a;总样本数 &#xff1a;集合 中属于第 个类别的样本个数 二、条件熵 其中&#xff1a; &#xff1a;属性 的取值个数 &#xff1a;选出属性 取值等于 的样本集合 三、信息增益 信息增…

安全防御 --- IPSec理论(03)

DPD 死亡对等体检测&#xff08;dead peer detection&#xff09;&#xff0c;检查对端IKE SA&#xff08;iskmp sa&#xff09;是否存在。当隧道出现异常&#xff0c;检测出异常重新发起协商&#xff0c;维持隧道。 作用&#xff1a;DPD解决隧道黑洞问题&#xff0c;用于检查…

Docker搭建Hadoop集群

目录 1.拉取centos镜像 2.基础镜像配置(基于centos构建hadoopbase镜像) 3.集群环境配置 1.创建3个容器 2.配置网络 3.配置主机和ip的映射关系 4.配置3个节点的免密登录 4.搭建hadoop集群 1.安装hadoop 2.修改配置文件 3.分发Hadoop及配置文件my_env.sh 5.启动集群 …

使用CLion创建Cmake项目,使用GoogleTest和GoogleMock对代码进行测试

文章目录 1、环境准备2、CLion创建项目3、编写测试用例4、复杂测试用例 1、环境准备 注意版本匹配&#xff0c;我本地是g 8.1.0 的&#xff0c;最开始装了GoogleTest最新版1.10.0结果发现不能用&#xff0c;又回去下载旧的版本。g 8.1.0 应该可以使用 Google Test 1.8.1 版本。…

HLW8032交流电参数模块采样原理讲解

目录 一、文章概述 二、采样说明 1.采样方式 1.1隔离采样 电流型电压互感器&#xff1a; 1.2非隔离采样 2.采样范围 3.采样精度 三、数据说明 四、HLW8032模块说明 一、文章概述 本博文主要讲解HLW8032交流电参数模块的采样原理以及设计说明&#xff0c;才疏学浅&am…

微服务系列文章之 SpringCloud面试

1、什么是 Spring Cloud&#xff1f; Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序&#xff0c;提供与外部系统的集成。Spring cloud Task&#xff0c;一个生命周期短暂的微服务框架&#xff0c;用于快速构建执行有限数据处理的应用程序。 2、使…

团体程序设计天梯赛-练习集L2篇⑦

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

奇异值分解MVDR算法功率谱估计MATLAB完整程序分享

clear ;close all;clc; 产生输入信号 N 1024; %样本点数 snr[20 25 30]; %信噪比 n0:N-1; %数据轴 g100; %蒙特卡诺仿真次数 M14; %阶数 Pmvdr_szeros(3,1024); %存放MVDR谱 signal1 exp(1i*0.1…

Oracle-DG备库应用查询不到数据问题处理

背景: 一套准备上线的Oracle 11G RAC主备集群&#xff0c;应用报告说部分模块测试发现在备库查不到新插入的数据&#xff0c;而且问题发生的频率很高&#xff0c;需确认主备之间同步是否存在问题&#xff0c;此套主备之间同步采用SYNCAFFIRM模式 问题分析: 接到问题之后&#…

2022(二等奖)C2464植物保护管理系统

作品介绍 一、需求分析 1. 应用背景 森林是陆地生态系统的主体&#xff0c;是人类生存与发展的物质基础。以森林为主要经营对象的林业&#xff0c;不仅承担着生态建设的主要任务&#xff0c;而且承担着提供多种林产品的重大使命。进入21世纪&#xff0c;人类正在继农业文明和…

libevent(12)bufferevent的基础知识

一、bufferevent的基本概念 bufferevent 是 libevent 中的一个事件缓冲 IO&#xff0c;内部实现了基本 socket recv/send 操作 &#xff0c;用户只需要调用 bufferevent 的 API 即可实现数据的读写。 &#xff08;1&#xff09;缓冲区&#xff1a;每个 bufferevent 都有一个读…