OpenCV之FCN图像分割

news2024/11/27 5:39:57
  • 💂 个人主页:风间琉璃
  • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)订阅专栏

前言

Fully Convolutional Network(FCN)是一种深度学习架构,主要用于图像分割任务。FCN 架构的典型应用包括语义分割、实例分割、物体检测等图像分析任务。它已经成为了计算机视觉领域图像分割任务的重要工具,并在许多竞赛和实际应用中取得了出色的成绩。

一、FCN简介

通常CNN网络在卷积层之后会接上若干个全连接层, 将卷积层产生的特征图(feature map)映射成一个固定长度的特征向量

以AlexNet为代表的经典CNN结构适合于图像级的分类和回归任务,因为它们最后都期望得到整个输入图像的一个数值描述(概率),比如AlexNet的ImageNet模型输出一个1000维的向量表示输入图像属于每一类的概率(softmax归一化)。

下图中的猫, 输入AlexNet, 得到一个长为1000的输出向量, 表示输入图像属于每一类的概率, 其中在“tabby cat”这一类统计概率最高。

FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。 

最后逐个像素计算softmax分类的损失, 相当于每一个像素对应一个训练样本。下图是Longjon用于语义分割所采用的全卷积网络(FCN)的结构示意图:

所以,FCN与CNN的区域在把于CNN最后的全连接层换成卷积层,输出的是一张已经Label好的图片。 

FCN原理: FCN将传统卷积网络后面的全连接层换成卷积层,这样网络输出不再是类别而是heatmap,同时为了解决因为卷积和池化对图像尺寸的影响,提出使用上采样的方式恢复。

核心思想:

  • - 不含全连接层(fc)的全卷积(fully conv)网络。可适应任意尺寸输入。 
  • - 增大数据尺寸的反卷积(deconv)层。能够输出精细的结果。 
  • - 结合不同深度层结果的跳级(skip)结构。同时确保鲁棒性和精确性。

网络结构详图如下,输入可为任意尺寸图像彩色图像,输出与输入尺寸相同,深度为:20类目标+背景=21。 

二、加载网络模型

这里使用Caffe深度学习框架中已经预训练好的FCN网络,需要相应的模型权重文件(.caffemodel)以及模型配置文件(.prototxt)。

加载模型和配置文件如下所示:

String model = "F:/data/CQU/VS/FCN_Segment/fcn8s-heavy-pascal.caffemodel";
String config = "F:/data/CQU/VS/FCN_Segment/fcn8s-heavy-pascal.prototxt";


//加载网络
Net net = readNetFromCaffe(config, model);

使用 CUDA进行加速添加下面两行代码:

//使用cuda加速
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);

三、预处理

将FCN网络对应的目标检测分类标签(20个类别)以及对应颜色调入内存这样便于访问它们,通常这些类别信息存储在txt文件中。 

String label = "F:/data/CQU/VS/FCN_Segment/pascal-classes.txt";

//读取分割对象颜色
vector<Vec3b> readColor(String labelpath)
{
    vector<Vec3b> colors;
    ifstream fp(labelpath);
    if (!fp.is_open())
    {
        printf("could not open the file...\n");
        exit(-1);
    }
	string line;
	while (!fp.eof()) 
	{
		//读取每一行
		getline(fp, line);  
		if (line.length()) 
		{
			stringstream ss(line);
			string name;
			ss >> name;   //分割对象
			int temp;
			Vec3b color;  //依次读取三个通道的颜色值大小
			ss >> temp;
			color[0] = (uchar)temp;
			ss >> temp;
			color[1] = (uchar)temp;
			ss >> temp;
			color[2] = (uchar)temp;
			colors.push_back(color);
		}
	}
	return colors;
}

//读取标签颜色
vector<Vec3b> colors = readColor(label);

 然后需要对输入图像进行预处理,主要要使输入图像尺寸满足网络输入的大小,网络输入的大小可以在配置文件prototxt中查看。

//图像预处理
resize(frame, frame, Size(500, 500));
Mat blobImage = blobFromImage(frame);

四、执行推理

图片预处理完成,就可以利用网络进行预测,这个过程也是把输入图像在网络各层中前向进行传播。

//构建输入
net.setInput(blobImage, "data");
//执行推理
Mat score = net.forward("score");

 data和score是网络模型输入层和输出层的名称,可以网络配置文件prototxt中查看。

五、解析输出

在score中存储着网络的所有输出,还需要对score进行后处理,然后将分割结果显示。

//分割并显示
const int chns = score.size[1];
const int rows = score.size[2];
const int cols = score.size[3];
//每个像素的最大类别(通道)
Mat maxCl(rows, cols, CV_8UC1);
//每个像素的最大值
Mat maxVal(rows, cols, CV_32FC1);

//LUT查找
for(int c = 0; c < chns; c++)
{
	for(int row = 0; row < rows; row++) 
	{
		//获取每个分割对象的置信度
		const float* ptrScore = score.ptr<float>(0, c, row);
		uchar* ptrMaxCl = maxCl.ptr<uchar>(row);
		float* ptrMaxVal = maxVal.ptr<float>(row);
		for(int col = 0; col < cols; col++) 
		{
			//分割结果大于最大值
			if (ptrScore[col] > ptrMaxVal[col]) 
			{
				ptrMaxVal[col] = ptrScore[col];
				ptrMaxCl[col] = (uchar)c;
			}
		}
	}
}

//为每个像素分配一个对应的颜色,并将结果存储在result矩阵
Mat result = Mat::zeros(rows, cols, CV_8UC3);
for (int row = 0; row < rows; row++) 
{
	const uchar* ptrMaxCl = maxCl.ptr<uchar>(row);
	Vec3b* ptrColor = result.ptr<Vec3b>(row);
	for (int col = 0; col < cols; col++) 
	{
		ptrColor[col] = colors[ptrMaxCl[col]];
	}
}
Mat dst;
imshow("FCN", result);
//将两个图像按照一定的权重进行线性叠加,加入背景
//frame,result尺寸要相同
addWeighted(frame, 0.3, result, 0.7, 0, dst);
imshow("Src-FCN", dst);

运行结果:

源码:资源下载:OpenCVFCN图像分割资源-CSDN文库

// FCN_Segment.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <vector>
#include <fstream>

using namespace cv;
using namespace cv::dnn;
using namespace std;


const size_t width = 500;
const size_t height = 500;
String label = "F:/data/CQU/VS/FCN_Segment/pascal-classes.txt";
String model = "F:/data/CQU/VS/FCN_Segment/fcn8s-heavy-pascal.caffemodel";
String config = "F:/data/CQU/VS/FCN_Segment/fcn8s-heavy-pascal.prototxt";

//读取分割对象颜色
vector<Vec3b> readColor(String labelpath)
{
    vector<Vec3b> colors;
    ifstream fp(labelpath);
    if (!fp.is_open())
    {
        printf("could not open the file...\n");
        exit(-1);
    }
	string line;
	while (!fp.eof()) 
	{
		//读取每一行
		getline(fp, line);  
		if (line.length()) 
		{
			stringstream ss(line);
			string name;
			ss >> name;   //分割对象
			int temp;
			Vec3b color;  //依次读取三个通道的颜色值大小
			ss >> temp;
			color[0] = (uchar)temp;
			ss >> temp;
			color[1] = (uchar)temp;
			ss >> temp;
			color[2] = (uchar)temp;
			colors.push_back(color);
		}
	}
	return colors;
}


int main()
{
	Mat frame = imread("F:/data/CQU/VS/FCN_Segment/luo.jpg");
	if (frame.empty()) 
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("src", CV_WINDOW_AUTOSIZE);
	imshow("src", frame);

	//图像预处理
	resize(frame, frame, Size(500, 500));
	Mat blobImage = blobFromImage(frame);

	//读取标签颜色
	vector<Vec3b> colors = readColor(label);

	//加载网络
	Net net = readNetFromCaffe(config, model);
	//使用cuda加速
	net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
	net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
	//构建输入
	net.setInput(blobImage, "data");
	//执行推理
	Mat score = net.forward("score");

	//分割并显示
	const int chns = score.size[1];
	const int rows = score.size[2];
	const int cols = score.size[3];
	//每个像素的最大类别(通道)
	Mat maxCl(rows, cols, CV_8UC1);
	//每个像素的最大值
	Mat maxVal(rows, cols, CV_32FC1);

	//LUT查找
	for(int c = 0; c < chns; c++)
	{
		for(int row = 0; row < rows; row++) 
		{
			//获取每个分割对象的置信度
			const float* ptrScore = score.ptr<float>(0, c, row);
			uchar* ptrMaxCl = maxCl.ptr<uchar>(row);
			float* ptrMaxVal = maxVal.ptr<float>(row);
			for(int col = 0; col < cols; col++) 
			{
				//分割结果大于最大值
				if (ptrScore[col] > ptrMaxVal[col]) 
				{
					ptrMaxVal[col] = ptrScore[col];
					ptrMaxCl[col] = (uchar)c;
				}
			}
		}
	}

	//为每个像素分配一个对应的颜色,并将结果存储在result矩阵
	Mat result = Mat::zeros(rows, cols, CV_8UC3);
	for (int row = 0; row < rows; row++) 
	{
		const uchar* ptrMaxCl = maxCl.ptr<uchar>(row);
		Vec3b* ptrColor = result.ptr<Vec3b>(row);
		for (int col = 0; col < cols; col++) 
		{
			ptrColor[col] = colors[ptrMaxCl[col]];
		}
	}
	Mat dst;
	imshow("FCN", result);
	//将两个图像按照一定的权重进行线性叠加,加入背景
	//frame,result尺寸要相同
	addWeighted(frame, 0.3, result, 0.7, 0, dst);
	imshow("Src-FCN", dst);

	waitKey(0);

	return 0;
}

结束语
感谢你观看我的文章呐~本次航班到这里就结束啦 🛬

希望本篇文章有对你带来帮助 🎉,有学习到一点知识~

躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。

最后,博主要一下你们的三连呀(点赞、评论、收藏),不要钱的还是可以搞一搞的嘛~

不知道评论啥的,即使扣个666也是对博主的鼓舞吖 💞 感谢 💐

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

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

相关文章

buu web [强网杯 2019]随便注

easy_sql 看来这是一道sql注入的题 1、起手试探 1 报错 1# 正确 ps:提交的不显示&#xff0c;想知道提交的东西就看红框的位置&#xff0c;就标了一个&#xff0c;剩下的也一样 2、 测字段数&#xff0c;到三报错&#xff0c;说明字段为二 1 order by 1# 1 order by 2…

去噪方法总结

图像降噪方法如下图&#xff1a; 空间域局部滤波 线性滤波 原图&#xff1a; 给原图加了四类噪声以后&#xff1a; 均值滤波 主要原理&#xff1a; 用像素邻域的灰度均值代替该像素的值 opencv里面有api 直接用api size 用的是7 对这四张图用均值滤波 &#xff08;上下是一一…

记录一些奇怪的报错

错误&#xff1a;AttributeError: module distutils has no attribute version 解决方案&#xff1a; 第一步&#xff1a;pip uninstall setuptools 第二步&#xff1a;conda install setuptools58.0.4 错误&#xff1a;ModuleNotFoundError: No module named _distutils_hac…

Java定时器

对于定时器的设定&#xff0c;想必大家在不少网站或者文章中见到吧&#xff0c;但是所谓的定时器如何去用Java代码来bianx呢&#xff1f;&#xff1f;感兴趣的老铁&#xff0c;可以看一下笔者这篇文章哟~~ 所谓的定时器就是闹钟&#xff01;&#xff01; 设定一个时间&#x…

计算机毕业设计 高校课程评价系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Vray渲染与窗口呈现不一致怎么办?

3D场景渲染过程中&#xff0c;可能大多情况下&#xff0c;大家都会选择Vray渲染器。它是最流行的渲染引擎之一。 但是&#xff0c;在渲染过程中&#xff0c;大家还是会遇到vray渲染和窗口不一致的问题。 在渲染过程中&#xff0c;窗口显示和实际渲染结果不同步的情况。不仅影响…

【DevOps核心理念基础】1. 什么是 devops

一、什么 devops? 1.1 定义 1.2 作用 1.3 核心 1.4. 软件开发流程 1.5. DevOps的核心定义 1.6. 具备的能力 二、DevOps流程中的几个关键概念 2.1 持续集成 2.2 持续交付 2.3 持续部署 2.4 总结 三、DevOps和敏捷开发的演进 一、什么 devops? 1.1 定义 Developme…

基于YOLOv8模型的80类动物目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的80类动物目标检测系统可用于日常生活中检测与定位车辆目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数…

基于springboot+vue的大学社团管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

如何实现微服务

一、问题拆解 1.1、客户端如何访问这些服务 原来的Monolithic方式开发&#xff0c;所有的服务都是本地的&#xff0c;UI可以直接调用&#xff1b;现在按功能拆分成独立的服务&#xff0c;跑在独立的虚拟机上的Java进程了。客户端UI如何访问他的&#xff1f; 后台有N个服务&a…

golang面试题:对未初始化的的chan进行读写,会怎么样?为什么?

问题 对未初始化的的 chan 进行读写&#xff0c;会怎么样&#xff1f;为什么&#xff1f; 怎么答 读写未初始化的 chan 都会阻塞。 举例 写未初始化的 chan package main // 写未初始化的chan func main() {var c chan intc <- 1 }// 输出结果 fatal error: all gorou…

[译] MySQL-恢复被删除的Performance Schema 数据库

原文地址&#xff1a;https://blog.sqlauthority.com/2021/12/23/mysql-recover-dropped-performance-schema-database/ 原文作者&#xff1a;Pinal Dave 登录后复制今天早些时候我在一个在线论坛上看到有用户在找如何恢复被删除的Performance Schema 数据库的方法。 老实说&a…

【Java】什么是过滤器链(FilterChain )?哪些场景可以使用过滤器链?

文章目录 前言1、创建过滤器2、修改 web.xml3、运行项目并查看结果 前言 在一个 Web 应用程序中可以注册多个 Filter 程序&#xff0c;每个 Filter 程序都可以针对某一个 URL 进行拦截。如果多个 Filter 程序都对同一个 URL 进行拦截&#xff0c;那么这些 Filter 就会组成一个…

基于腾讯文档进行应届生个人求职记录

1. 新建一个腾讯文档 电脑登录QQ&#xff0c;点击“腾讯文档”功能键。 2. 可以选择下载客户端&#xff0c;也可以直接进入网页版。&#xff08;本人使用网页版&#xff09; 3. 点击新建&#xff0c;选择在线表格。 4. 编辑表名&#xff0c;表内容。 5. 设置文档权限&#xf…

青大数据机构【2013】

关键字&#xff1a; 邻接表空间复杂度、求无向图连通分量&#xff08;BFS、DFS&#xff09;、B树根节点最小关键字、平均查找长度最小的排序、二叉树排序叶子结点次序不变、不同次序建立二叉排序树及中序遍历、直接插入排序特点、强连通分量、邻接矩阵邻接表 一、单选&#x…

vue2中使用富文本编辑器tinyMCE全过程

第一步&#xff1a;安装TinyMCE $npm install tinymce5.10.0 -S $npm install tinymce/tinymce-vue3.0.1 -S 第二步&#xff1a;在node_modules中找到tinymce文件夹将内部文件移入pubilc/tinymce文件夹中在index.html文件中引入tinymce.min.js 注意&#xff1a;不把js文件放…

Java 基本类型和包装类

Java 是基于对象的&#xff0c;所以我们都需要以对象的想法来进行思维。 但 Java 又提供了 8 个基本类型&#xff0c;这 8 个基本类型基本上都和数字有关&#xff0c;是直接可以使用的类型。 基本类型大小包装器类型boolean/Booleanchar16bitCharacterbyte8bitByteshort16bitS…

[NCTF2019]Fake XML cookbook XML注入

目录 DTD 实体 外部实体 做题 看到这个界面就像admin 123456弱口令试试看 果然进不去 这里有个tips 但是没有办法点击 我们进源代码看看 function doLogin(){var username $("#username").val();var password $("#password").val();if(username …

flex 实现多行项目动态堆叠,随着屏幕尺寸而扩展减少

当您增加或减少屏幕尺寸时&#xff0c;这些 flex 项目会缩小和增长。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…

ARM Linux DIY(八)USB 调试

前言 V3s 带有一个 USB 接口&#xff0c;将其设置为 HOST 或 OTG 模式&#xff0c;这样可以用来接入键盘、鼠标等 USB 外设。 USB 简介 USB 有两种设备&#xff1a;HOST 和 USB 功能设备。 在 USB2.0 中又引入了一个新的概念 OTG&#xff0c;即设备角色可以动态切换。 切换方…