OpenCV之FCN图像分割

  • 💂 个人主页:风间琉璃
  • 🤟 版权: 本文由【风间琉璃】原创、在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.rhkb.cn/news/130890.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV学习笔记(6)_由例程学习高斯图像金字塔和拉普拉斯金字塔

1 图像金字塔 图像金字塔是图像多尺度表达的一种。 尺度&#xff0c;顾名思义&#xff0c;可以理解为图像的尺寸和分辨率。处理图像时&#xff0c;经常对源图像的尺寸进行缩放变换&#xff0c;进而变换为适合我们后续处理的大小的目标图像。这个对尺寸进行放大缩小的变换过程…

c++ 学习 之 常函数 和 常对象

前言 常函数 成员函数后加 const 我们可以称这个函数为 常函数 常函数内不可以修改成员属性 成员属性声明时加关键字 mutable 后&#xff0c;在常函数中依然可以修改 常对象 常对象 声明对象前加 const 称该对象为常对象 常对象只能调用常函数 正文 常函数 class Person…

JAVAEE初阶相关内容第八弹--多线程(初阶)

本文目录 阻塞队列 阻塞队列是什么&#xff1f; 标准库中的阻塞队列 生产者消费者模型 阻塞队列的实现 普通队列实现&#xff1a; 入队列&#xff1a; 出队列&#xff1a; 完整代码&#xff1a; 加阻塞 加锁 加阻塞 阻塞队列 队列&#xff1a;先进先出&#xff0c;…

Redis缓存魔法:如何轻松提升你的应用性能

Redis&#xff0c;作为一个开源的、内存中的数据结构存储系统&#xff0c;已经成为了许多开发者和企业的首选工具。无论是作为数据库、缓存还是消息代理&#xff0c;Redis都展现出了其强大的性能和灵活性。在本文中&#xff0c;我们将深入探讨Redis的魅力&#xff0c;以及如何有…

量化:基于支持向量机的择时策略

文章目录 参考机器学习简介策略简介SVM简介整体流程收集数据准备数据建立模型训练模型测试模型调节参数 参考 Python机器学习算法与量化交易 利用机器学习模型&#xff0c;构建量化择时策略 机器学习简介 机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。…

无涯教程-JavaScript - OCT2BIN函数

描述 OCT2BIN函数将八进制数转换为二进制数。 语法 OCT2BIN (number, [places])争论 Argument描述Required/OptionalNumber 您要转换的八进制数。 数字不能超过10个字符。数字的最高有效位是符号位。其余的29位是幅度位。 负数使用二进制补码表示。 RequiredPlaces 要使用的…

【C语言】字符串函数

文章目录 前言1.strcat2.strncpy3.strncat4.strncmp5.strstr6.strtok7.strerror8.strcat的模拟实现9.strstr的模拟实现 总结 添加链接描述 前言 大家好呀&#xff0c;今天给大家分享一下字符函数和字符串函数C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没…

随机密码生成器(Python)

随机密码生成器 想要生成一个随机密码&#xff0c;需要考虑下面两点&#xff1a; 1.字符集合 2.密码的位数 下面代码中引用了string模块和random模块&#xff0c;string.printable是string中的可打印字符&#xff0c;用strip函数首尾去掉空格&#xff1b;random模块用来取字符&…

C# wpf 实现桌面放大镜

文章目录 前言一、如何实现&#xff1f;1、制作无边框窗口2、Viewbox放大3、截屏显示&#xff08;1&#xff09;、截屏&#xff08;2&#xff09;、转BitmapSource&#xff08;3&#xff09;、显示 4、定时截屏 二、完整代码三、效果预览总结 前言 做桌面截屏功能时需要放大镜…

品牌策划经理工作内容|工作职责|品牌策划经理做什么?

一位美国作家曾说过“品牌是一系列期望、记忆、故事和关系&#xff0c;他们共同构成了消费者最终原则一个产品或者服务的原因。” 所以&#xff0c;品牌经理这个岗位主要是创造感知价值主张&#xff0c;激发消费者购买这个品牌后带来的感知价值&#xff0c;这种回报的本质相对…

C语言实现三子棋游戏(详解)

目录 引言&#xff1a; 1.游戏规则&#xff1a; 2.实现步骤&#xff1a; 2.1实现菜单&#xff1a; 2.2创建棋盘并初始化&#xff1a; 2.3绘制棋盘&#xff1a; 2.4玩家落子&#xff1a; 2.5电脑落子&#xff1a; 2.6判断胜负&#xff1a; 3.源码&#xff1a; 结语&…

论文阅读 (100):Simple Black-box Adversarial Attacks (2019ICML)

文章目录 1 概述1.1 要点1.2 代码1.3 引用 2 背景2.1 目标与非目标攻击2.2 最小化损失2.3 白盒威胁模型2.4 黑盒威胁模型 3 简单黑盒攻击3.1 算法3.2 Cartesian基3.3 离散余弦基3.4 一般基3.5 学习率 ϵ \epsilon ϵ3.6 预算 1 概述 1.1 要点 题目&#xff1a;简单黑盒对抗攻…

力扣刷题-移除指定值的链表元素

力扣203移除元素 题目来源&#xff1a; 力扣203 题目描述&#xff1a; 非常简单的一道题&#xff0c;主要强调两点 链表删除要记录删除位置的前驱节点 头节点没有前驱 因此直接headhead.next为了保持与后两种一致&#xff0c;加上虚拟节点&#xff0c;下一节点指向头节点 /***…

【Linux-Day10-信号量,共享内存,消息队列】

信号量 信号量描述 信号量是一个特殊的变量&#xff0c;一般取正数值。它的值代表允许访问的资源数目&#xff0c;获取资源 时&#xff0c;需要对信号量的值进行原子减一&#xff0c;该操作被称为 P 操作。 当信号量值为 0 时&#xff0c;代表没有资源可用&#xff0c;P 操作…

企业架构LNMP学习笔记27

Keepalived的配置补充&#xff1a; 脑裂&#xff08;裂脑&#xff09;&#xff1a;vip出现在了多台机器上。网络不通畅&#xff0c;禁用了数据包&#xff0c;主备服务器没法通讯&#xff0c;造成备服务器认为主服务器不可用&#xff0c;绑定VIP&#xff0c;主服务器VIP不会释放…

【设计模式】三、概述分类+单例模式

文章目录 概述设计模式类型 单例模式饿汉式&#xff08;静态常量&#xff09;饿汉式&#xff08;静态代码块&#xff09;懒汉式(线程不安全)懒汉式(线程安全&#xff0c;同步方法)懒汉式(线程安全&#xff0c;同步代码块)双重检查静态内部类枚举单例模式在 JDK 应用的源码分析 …

[N0wayback 2023春节红包题] happyGame python反编译

这个反编译的比较深 一&#xff0c;从附件的图标看是python打包的exe文件&#xff0c;先用pyinstxtractor.py 解包 生成的文件在main.exe_extracted目录下&#xff0c;在这里边找到main 二&#xff0c;把main改名为pyc然后加上头 这个头从包里找一个带头的pyc文件&#xff…

做题(1)

1.fileinclude 打开 发现提示flag在flag.php里边 查看页面源代码 $lan $_COOKIE[language]; 这句话是关键 变量lan是用cookie传参的 include("english.php"); 这句话表明了文件包含 include函数用php伪协议 filer绕过 include($lan.".php"); 这句话…

深入实现 MyBatis 底层机制的任务阶段4 - 开发 Mapper 接口和 Mapper.xml

&#x1f600;前言 在我们的自定义 MyBatis 底层机制实现过程中&#xff0c;我们已经深入研究了多个任务阶段&#xff0c;包括配置文件的读取、数据库连接的建立、执行器的编写&#xff0c;以及 SqlSession 的封装。每个任务阶段都为我们揭示了 MyBatis 内部工作原理的一部分&a…

【算法基础】时间复杂度和空间复杂度

目录 1 算法的评价 2 算法复杂度 2.1 时间复杂度&#xff08;Time Complexity&#xff09; 2.1.1 如何计算时间复杂度&#xff1a; 2.1.2 常见的时间复杂度类别与示例 2.2 空间复杂度 2.2.1 如何计算空间复杂度 2.2.2 常见的空间复杂度与示例 3 时间复杂度和空间复杂度…