Opencv 基本操作四 指针数组、vector与Mat之间的相互转换 | Mat切片成Vector<mat>并还原_vector mat 相互转化-程序员宅基地

技术标签: c++  人工智能  python、C++与C#实践  opencv  

在深度学习模型部署中通常存在读取图像为mat,然后将mat转换为float指针传入模型的操作。为了快捷开发,因此对指针数组、vector与Mat之间的相互转换进行整理。实现了指针数组、vector之间的相互转换;vector与Mat之间的相互转换(含单通道图像和多通道图像)。vector转mat主要应用在语义分割结果的处理中。

1、指针数组与vector之间的相互转换

这里强调一下为什么使用vector而不使用指针数组,因为使用vector可以更为方便的操作数据,就比如说数据的拷贝,裁剪、拼接等。就比如,博主的代码实现了vector的加法重载,可以便捷的实现vector的拼接。

指针转vector

std::vector vp(p, p + 1000);,其中p为指针的名称,1000为指针的数据长度

vector转指针

int* p2 = vp.data();//浅拷贝(p2与vp共用同一片内存区域)

//重载vector的运算符
template <typename T>
vector<T>& operator +(vector<T>& v1, vector<T>& v2)
{
    
	v1.insert(v1.end(), v2.begin(), v2.end());
	return v1;
}

//指针数组转vector
int* p = new int[100];//空间分配方式一
p = (int*)malloc(1000*sizeof(p));//空间分配方式二:malloc赋值方式
p[0] = -1;

std::vector<int> vp(p, p + 1000);//深拷贝(把起始地址到结束地址的值拷贝一遍)
vp[1] = 222;
//-------实现vector的拼接--------
std::vector<int> vp2 = vp + vp;
//vecort转指针数组
int* p2 = vp.data();//浅拷贝(p2与vp共用同一片内存区域)
p2[2] = 3333;
std::cout << "int* p: " << p[0] << ", " << p[1] <<", " << p[2] << std::endl;
std::cout << "vector vp: " << vp[0] << ", " << vp[1] << ", " << vp[2] << std::endl;
std::cout << "int* p2: " << p2[0] << ", " << p2[1] << ", " << p2[2] << std::endl;

在指针与数组的相互转换过程中需要注意的是内存空间的变化,指针转vector后数据被拷贝了一次,而vector转指针后数据并没有被拷贝。具体可以见上述代码的输出,改变vp不会影响p,而改变p2会影响vp
在这里插入图片描述

vector的类型转换

有的时候需要转换vector的数据类型,可以使用以下方法

std::vector<int64> output_data0;//对应seg结果  argmax后为int64
std::vector<uchar> int2uchar(output_data0.begin(), output_data0.end());

2、mat转vector

通常来说mat转vector是十分便捷的,仅需要一个reshape操作即可,reshape(int cn, int rows)表示把数据的通道变为cn,行数变为rows。具体如下所示,下面代码表示把单通道mat转换为vector。

std::vector<float> vec = mat.reshape(1, 1);

然而,对于三通道的mat转换略为麻烦,需要将多通道split为三个单通道才行,具体实现的转换函数如下所示。

std::vector<float> mat2vector(cv::Mat img, cv::Size2d size = {
    512,512}) {
    
	cv::resize(img, img, size);
	cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
	img.convertTo(img, CV_32FC3);
	//数据归一化
	img = img / 255.0;
	//将rgb数据分离为单通道
	std::vector<cv::Mat> mv;
	cv::split(img, mv);
	std::vector<float> R = mv[0].reshape(1, 1);
	std::vector<float> G = mv[1].reshape(1, 1);
	std::vector<float> B = mv[2].reshape(1, 1);
	//RGB数据合并
	std::vector<float> input_data;
	input_data.insert(input_data.end(), R.begin(), R.end());
	input_data.insert(input_data.end(), G.begin(), G.end());
	input_data.insert(input_data.end(), B.begin(), B.end());
	return input_data;
}

3、vector转mat

在深度学习部署场景中,vector转mat分三种情况,情况一:uchar形式的vector转单通道mat;情况二:float形式的vector转单通道mat;情况三:float形式的vector转三通道mat。

vector<uchar>转单通道mat

如果是std::vector<int64>则需要转换为std::vector<uchar>才行,在深度学习中argmax后返回的数据类型通常是int64的。

//语义分割结果转mat
cv::Mat vector2mat(std::vector<uchar> output, cv::Size2d size = {
     512,512 }) {
    
	cv::Mat out_result(size.height, size.width, CV_8UC1, cv::Scalar(0));
	out_result.data = output.data();
	return out_result;
}
vector<float>转单通道mat

因为mat.data 是uchar型的指针,所有vector<float>不能像vector<uchar>那样进行赋值操作,但其有两种赋值方式,分别是memcpy和mat.assign

cv::Mat vector2mat(std::vector<float> output, cv::Size2d size = {
     512,512 }) {
    
	cv::Mat out_result(size.height, size.width, CV_32FC1, cv::Scalar(0));
	memcpy(out_result.data, output.data(), output.size() * sizeof(float));
	//output.assign((float*)out_result.datastart, (float*)out_result.dataend);
	return out_result;
}
vector<float>转三通道mat

这里通过调用上一步实现的函数,实现目标功能,所转换的mat为bgr格式。

//将CHW格式的数据转换为bgr格式的mat
cv::Mat cwhfloat2mat(std::vector<float> output, cv::Size2d size = {
     512,512 }) {
    
	cv::Mat out_result;
	int dis = size.height * size.width;
	//将数据进行切片
	vector<float> r{
     &output[0], &output[0] + dis };
	vector<float> g{
     &output[0] + dis, &output[0] + 2 * dis };
	vector<float> b{
     &output[0] + 2 * dis, &output[0] + 3 * dis };

	vector<cv::Mat> mat_bgr;
	mat_bgr.push_back(vector2mat(b, size));
	mat_bgr.push_back(vector2mat(g, size));
	mat_bgr.push_back(vector2mat(r, size));
	cv::merge(mat_bgr, out_result);
	//out_result = out_result * 255;
	//out_result.convertTo(out_result, CV_8UC3);
	//cv::cvtColor(out_result, out_result, cv::COLOR_BGR2RGB);
	return out_result;
}

4、Mat切片与还原

Mat切片成vector ,vector 还原成mat

#include <iostream>
#include <memory>
#include <chrono>
#include <fstream>
#include <string>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
//rows,cols 用于将切片信息带出模型
int crop_img(vector<Mat> &crops,Mat img,int block_size, int &rows, int &cols) {
    
    float fisze = block_size * 1.0;
    rows = ceil(img.rows / fisze);//向上取整
    cols = ceil(img.cols / fisze);

    //确保切片过程中图像不会超出边界
    if (rows * block_size > img.rows || cols * block_size > img.cols) {
    
        Mat new_img = Mat::zeros({
     cols * block_size,rows * block_size }, img.type());
        Rect rect = {
     0,0,img.cols,img.rows };
        img.copyTo(new_img(rect));
        img = new_img;//用新图替换旧图
    }
    //对图像进行切片
    for (int i = 0; i < rows; i++) {
    
        for (int j = 0; j < cols; j++) {
    
            Rect rect = {
     j * block_size,i * block_size,block_size,block_size };
            Mat crop = img(rect);
            crops.push_back(crop);
        }
    }
    return 0;
}
//根据信息对切片进行还原
Mat merger_crop(vector<Mat> crops, int block_size, int rows, int cols, Size img_size = {
    0,0}) {
    
    Mat save_img = Mat::zeros({
     cols * block_size,rows * block_size }, crops[0].type());
    for (int i = 0; i < rows; i++) {
     // row=>y   行=》高
        for (int j = 0; j < cols; j++) {
    //cols=>x  列=》宽
            int index = i * cols + j;
            Mat crop = crops[index];
            Rect rect = {
     j * block_size,i * block_size,block_size,block_size };
            crop.copyTo(save_img(rect));
        }
    }
    //根据输入的size对图像进行裁剪
    if (img_size.area() > 0) {
    
        Rect rect = {
     {
    0,0},img_size };
        save_img = save_img(rect);
    }
    return save_img;
}
int main() {
    
    Mat img = imread("D:/20141014.jpg");
    int block_size = 256;
    int rows = 0;
    int cols = 0;
    vector<Mat> crops;
    crop_img(crops,img, block_size, rows, cols);

    Mat merge= merger_crop(crops, block_size, rows, cols, img.size());
    imshow("img", img);
    imshow("recover", merge);
    waitKey();
	return -1;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a486259/article/details/126848868

智能推荐

vector冒泡排序-程序员宅基地

记得刚学c的时候老师用c编写了一个冒泡排序,_vector冒泡排序

6. live555 imx8交叉编译_imx8 交叉编译_生如~夏花的博客-程序员宅基地

目录一、下载源码二、编译配置文件修改三、编译源码一、下载源码在虚拟机Ubuntu中打开一个命令行,然后输入命令:wget http://live555.com/liveMedia/public/live555-latest.tar.gz解压源码:tar -zxvf live555-latest.tar.gz二、编译配置文件修改进入源码路径:cd livelive555支持不同平台的编译,首先需要先生成imx8平台下的Makefile,先创建Makef_imx8 交叉编译

endless 题解_endlesssteps csdn-程序员宅基地

endlessendlessendless 题解题目解题方法这道题考点是贪心、二分查找和排序。首先我们设fif_ifi​表示用iii个魔法的最大路程,则我们贪心,每次选最大的那iii个魔法最优,因此fi=l+∑j=1iajf_i=l+\sum_{j=1}^{i}{a_j}fi​=l+∑j=1i​aj​注意,我们要把aaa数组先排序再求解。继续简化上式,可得递推式fi={fi−..._endlesssteps csdn

Redis数据类型——hash-程序员宅基地

简介新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息需要的存储结构:一个存储空间保存多个键值对数据hash类型:底层使用哈希表结构实现数据存储hash存储结构优化 :如果field数量较少,存储结构优化为类数组结构如果field数量较多,存储结构使用HashMap结构数据结构Redis 的字典相当于 Java 语言里面的 HashMap,它是无序字典。...

小程序-多图上传和视频上传(优化版2.0,添加压缩功能)-程序员宅基地

项目中需要一次多图上传和视频上传就封装了一个简单通用的js 使用很简单复制一下代码粘贴到一个空的js,我命名为chooseUpload,将这个文件放到项目文件夹名称为utils中,没有这个文件夹可以自己新建一个 js代码如下: const App = getApp();//多图上传图片 、 单个上传视频const uploadInit = function(){ var up = uploadInit.prototype;//给方法添加属性 //选择上传方式 up....

黑马程序员——C语言基础知识整理——数据类型、常量与变量-程序员宅基地

C语言基础知识整理,数据类型、常量与变量相关知识总结

随便推点

python 文本情感分类_python文本情感分类-程序员宅基地

对于一个简单的文本情感分类来说,其实就是一个二分类,这篇博客主要讲述的是使用scikit-learn来做文本情感分类。分类主要分为两步:1)训练,主要根据训练集来学习分类模型的规则。2)分类,先用已知的测试集评估分类的准确率等,如果效果还可以,那么该模型对无标注的待测样本进行预测。 首先先介绍下我样本集,样本是已经分好词的酒店评论,第一列为标签,第二列为评论,前半部分为积极评论,后半部_python文本情感分类

WPF让一个文本框自动获取焦点:-程序员宅基地

this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => { Keyboard.Focus(TextBox2); }));原文链接

Asp.net源码本周更新(7.2-7.6) -程序员宅基地

- 大学课程管理系统程序源码(印度) Hits:367 2007-7-6使用CSS+SiteMap+UserControl+MasterPage实现简易的Tab 我们在做网站后台管理的时候,往往需要用到Tab形式的导航菜单,博客园如此,BlogEngine也如此,前段时间研究修改BlogEngine的时候看到其Tab实现如此容易,思路..- 邮政影视娱乐网(含注册机源码) Hi_asp.net源码

设备软件可靠性测试-程序员宅基地

设备为达到连续可运行目标,除了在硬件设计中考虑器件可连续无故障运行外,很重要的方面是软件在各种条件下可经受考验,持续工作。这需要在实现基本功能前提下,在软件中设计一系列容错性逻辑去保证。为全面评估软件容错性和故障恢复能力,测试需要制造或模拟一系列条件,包括内部硬件故障条件、外部恶意攻击条件、偶发过载条件、软件资源耗尽条件、周边环境故障条件以及长时间正常负荷持续运行模拟。为了在产品开发的不同

Mysql 存储引擎中InnoDB与Myisam的主要区别-程序员宅基地

原文地址:https://blog.csdn.net/wlzx120/article/details/539241231、事务处理innodb 支持事务功能,myisam 不支持。Myisam 的执行速度更快,性能更好。2、select ,update ,insert ,delete 操作MyISAM:如果执行大量的SELECT,MyISAM是更好的选择...

TortoiseGit 的使用-程序员宅基地

 Git服务器库本文在介绍了软件安装和设置后, 写了TortoiseGit 常用的一些功能, 包括:创建新库添加文件及文件夹创建分支看分支情况及修改log比较版本差异合并分支其他操作: Stash; 忽略文件本文不包括:Git 服务器设置Push 版本到服务器上从其他机器上Pull 版本解决中文