• Opencv 基本操作四 指针数组、vector与Mat之间的相互转换


    深度学习模型部署中通常存在读取图像为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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在指针与数组的相互转换过程中需要注意的是内存空间的变化,指针转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());
    
    • 1
    • 2

    2、mat转vector

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

    std::vector<float> vec = mat.reshape(1, 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3、vector转mat

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

    vector转单通道mat

    如果是std::vector则需要转换为std::vector才行,在深度学习中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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    vector转单通道mat

    因为mat.data 是uchar型的指针,所有vector不能像vector那样进行赋值操作,但其有两种赋值方式,分别是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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    vector转三通道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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4、Mat切片与还原

    Mat切片成vector ,vector 还原成mat

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    IPv6通过公网共享文件(Windows)
    L1-012 计算指数 C++
    20. 【Linux教程】emacs 编辑器
    MySQL列(数据)类型介绍(bit类型实例)
    【云原生】基于windows环境搭建Docker
    postman+Newman+jenkins实现接口自动化测试持续集成
    系列三、InputStream常用子类
    相机卡格式化了还能恢复吗?答案在这!(附带恢复教程)
    基于SSM的房屋租售网站
    SAP message TK 248 solved
  • 原文地址:https://blog.csdn.net/a486259/article/details/126848868