起因是博主在实习过程中的一个任务:需要将模型预测输出tensor的shape从CHW(严格来说是NCHW,但是N=1所以这里忽略掉)转成OpenCV中的cv::Mat类型(即HWC)数据。
由于博主对C++和C#并不是很熟悉,而且也查阅了tensor对应的类似resize、reshape等方法,发现对于输出tensor并没有封装相应的方法,无奈只好用最原始的办法,就是将输出tensor的值赋值到cv::Mat对应的位置。
原理参考了下面两篇博客,其实如果弄清楚原理了代码还是挺好写的:

  1. 【opencv】——cv mat转tensor(b,c,h,w)
  2. NCHW转化为NHWC

这里借用第二篇参考博客的图,第一行就是我们tensor拉成一维以后的数据的排列方式,红色绿色蓝色分别对应着RGB的通道。同理第二行对应的是cv::Mat中RGB数据的排列方式。
这里我先做几个假设,假设模型输入的图像为BGR,输出也为BGR,这样我就可以直接调用OpenCV的imwrite保存图像。假设模型一次只输入一张图片的tensor并且得到对应一张图片的输出tensor,且输出tensor的shape=[NCHW]=[1,3,H,W]
对于输出tensor的shape可以这样理解,我们按照NCHW的顺序一层层剖析它。首先,N=1可以忽视掉,C=3,就是对应我们三个通道。每个通道里面有H×WH \times WH×W个数据(可以联系上面那个图来理解,对应每种颜色的全部数据)。同理,每行里面里面就有WWW个数据。
我们可以这样思考,首先每个通道都有H×WH \times WH×W个数据,还是联系上面那个图,按照之前我的假设,第一个方块的数据值就对应cv::Mat数据第一个通道第一行第一列的值。此时,第一个方块后面的第H×WH \times WH×W个方块的值就对应cv::Mat数据第二个通道第一行第一列的值。我们可以按照这个规律每次填充cv::Mat不同通道同一个位置的值。当然也可以先填充同一个通道不同位置的值,取决于习惯,这里我按照参考的第一篇博客先填充不同通道同一个位置的值。

如果上面的没有看懂也没关系,可能是我写复杂了,直接看代码并试着结合图看看,说不定这样更好理解。

下面是代码:

auto output_tensor = predictor_->GetOutput(0);
auto output_data = output_tensor->data<float>();
auto output_shape = output_tensor->shape();cv::Mat rgb_img = cv::Mat::zeros(cv::Size(output_shape[3], output_shape[2]), CV_8UC3);int h = rgb_img.rows;
int w = rgb_img.cols;
int c = rgb_img.channels();
for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {for (int k = 0; k < c; k++) {int offset = k * h * w + i * w + j * 1;int tmp_pix = output_data[offset] * 255;rgb_img.at<cv::Vec3b>(i, j)[k] = tmp_pix > 255 ? 255 : tmp_pix;}}
}cv::imwrite("/data/data/com.baidu.paddle.lite.demo.face_detection/test.jpg", rgb_img);return rgb_img;

首先这部分只是为了获取输出tensor以及shape,方便我们构造合适大小的cv::Mat

auto output_tensor = predictor_->GetOutput(0);
auto output_data = output_tensor->data<float>();
auto output_shape = output_tensor->shape();

然后我们初始化cv::Mat用于存储图像数据

cv::Mat rgb_img = cv::Mat::zeros(cv::Size(output_shape[3], output_shape[2]), CV_8UC3);

这部分是核心,由于输出tensor的范围在0-1,这里做了处理转换回0-255

int h = rgb_img.rows;
int w = rgb_img.cols;
int c = rgb_img.channels();
for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {for (int k = 0; k < c; k++) {int offset = k * h * w + i * w + j * 1;int tmp_pix = output_data[offset] * 255;rgb_img.at<cv::Vec3b>(i, j)[k] = tmp_pix > 255 ? 255 : tmp_pix;}}
}

tensor转cv::Mat(即CHW转HWC)原理含C#代码实现相关推荐

  1. OpenCV【零】—————cv::Mat——Mat对象创建方法

    OpenCV (一)--Mat对象创建方法 目录 OpenCV (一)--Mat对象创建方法 1. cv::Mat优点及原理(本质类) 2. Mat类拷贝及对象的创建方法 3. Mat 对象元素的高效 ...

  2. 【FFmpeg】使用sws_scale将AVFrame转换后的图像数据放入cv::Mat中

    1.方法一,伪代码如下 cv::Mat mat; AVFrame avFrame; const int stride[] = {static_cast<int>(mat.step[0])} ...

  3. 【OpenCV】正确创建用于保存YUV420P格式的cv::Mat

    1.问题描述 cv::Mat保存RGB24或BRG24格式时,传入宽.高和格式类型CV_8UC3就行了:今天在创建cv::Mat用来保存YUV420P时,有点懵圈了,因为类型是CV_8UC1,直接传宽 ...

  4. 【OpenCV】cv::Mat和std::vector之间的相互转换

    Mat转换成Vector 以vector 为例,其它模型类似 vector getVector(const Mat & a) { Mat b; a.convertTo(b, CV_64F); ...

  5. cv::Mat使用笔记

    1. 构造 无参数构造方法 Mat::Mat() 创建行数为 rows,列数为 col,类型为 type 的图像 Mat::Mat(int rows, int cols, int type) 创建大小 ...

  6. 错误 LNK2019 无法解析的外部符号 “public: void __cdecl cv::Mat::copyTo(class cv::debug_build_guard::_OutputArray

    错误 LNK2019 无法解析的外部符号 "public: void __cdecl cv::Mat::copyTo(class cv::debug_build_guard::_Output ...

  7. Layout of the output array img is incompatible with cv::Mat (step[ndims-1] !

    Layout of the output array img is incompatible with cv::Mat (step[ndims-1] https://blog.csdn.net/tqc ...

  8. 【opencv】4.初始化Mat的方式、访问cv::Mat中的某个元素

    可以参考:https://www.cnblogs.com/guoben/p/12728390.html 方法1: cv::Mat img_1 = cv::Mat::zeros(cv::Size(col ...

  9. 【OpenCV3】cv::Mat中的数据按行列写入txt文件中

    在使用opencv进行图像处理的过程中,经常会涉及到将文件中的数据读入到cv::Mat中,或者将cv::Mat中的数据写入到txt文件中. 下面就介绍一种我常用的将cv::Mat中的数据写入到txt文 ...

最新文章

  1. 天翼云从业认证(2.3)云计算的价值和核心技术。
  2. C语言实现类似QQ聊天界面抖动功能
  3. 用 Redis 实现分布式锁(Java 版)
  4. iOS笔记之UIKit_UINavigationController
  5. formula 返回list_python正则实现计算器功能
  6. [luogu P4198] 楼房重建(线段树 + 思维)
  7. javascript学习系列(17):数组中的find方法
  8. 使用mocha进行测试 区块链
  9. Hibernate 异常org.hibernate.LazyInitializationException: could not initialize proxy - no Session
  10. python的os模块使用_Python之os模块的常见用法
  11. overflow c语言_C语言表结构(一)
  12. win7下使用U盘安装Ubuntu Kylin完全详解教程
  13. 2020辅警考试计算机知识题,2019年辅警考试题库:计算机概述-计算机软件系统
  14. 小规模纳税人季度申报流程指导
  15. 「游戏」岩浆逃脱2.1
  16. 有关JSON和介绍和使用
  17. 树莓派/图像/人脸识别
  18. Android 10 根文件系统和编译系统(十八):Android.bp语法
  19. Word2016给重复文字全部添加高亮,有截图
  20. 手把手带你从零打造Vue SSR,清晰易懂!

热门文章

  1. webpack-dev-server配置host为WLAN地址(用于移动端调试)
  2. ant-design-vue的时间选择器中英文混合问题
  3. GPS参数中PDOP
  4. MPU 6050姿态角度融合算法
  5. 10 个最好用的 NodeJS 框架
  6. 浏览器广告屏蔽插件,特定类名adver,advertisement
  7. python 基金净值_Python爬虫周记之案例篇——基金净值Selenium动态爬虫
  8. Matlab fmincon函数实例 及 其句柄形式之参数传递
  9. ThreadPoolExecutor源码阅读笔记(二)FutureTask
  10. 精雕细琢!阿里大师53天悉心打磨出来的MyBatis+设计模式架构指南