开发调试工具ImageWatch
最近工作中在做离屏渲染相关的内容,将渲染结果保存在内存中,然后通过网络传输到远端设备上。离屏渲染通过ReadPixels的方式将渲染的结果直接扔到了内存中,不会创建窗口和在窗口上绘制出来。在调试过程中发现远端设备显示的图像不正确,需要定位问题。由于懒得引入UI及编写代码来创建窗口,想起大学时做图像识别时使用过的调试工具ImageWatch,用它我们可以直接调试内存中的图像。
ImageWatch简介
ImageWatch是一个VisualStudio插件,能够让我们在调试一个OpenCV程序时,直观地看到内存中的图像,并能直接在调试界面中做一系列的图像操作。
ImageWatch最开始是用于OpenCV的,但是如果它只做成支持OpenCV的形式,我们也不会选它了。ImageWatch是支持扩展的,我们可以编写配置让它支持自定义类型的内存形式。帮助文档的EXTENSIBILITY章节的内容详细描述了自定义的使用方法。
当然,如果只是简单地用来调试调试内存中的图像,不打算拓展它,我们也不需要专门写一套配置文件。
所有ImageWatch能做的,可以参考它的官方帮助文档: https://imagewatch.azurewebsites.net/ImageWatchHelp/ImageWatchHelp.htm
给VisualStudio安装ImageWatch插件
联网状态直接下载(推荐,自动匹配VS版本): VisualStudio菜单 -> 扩展 -> 管理扩展 -> 联机 -> 在右上角的搜索栏中直接搜索Image Watch
,下载即可。
脱机下载:
老版本:https://marketplace.visualstudio.com/items?itemName=VisualCPPTeam.ImageWatch
VS2017:https://marketplace.visualstudio.com/items?itemName=VisualCPPTeam.ImageWatch2017
VS2019:https://marketplace.visualstudio.com/items?itemName=VisualCPPTeam.ImageWatch2019
下载后双击安装即可。
调试模式下打开显示ImageWatch窗口
进入程序调试后,ImageWatch的窗口不会自动打开(不用时我们也可以关闭这个窗口)。
通过:VisualStudio菜单 -> 视图 -> 其他窗口 -> Image Watch,即可显示ImageWatch窗口。
查看内存中的图像
ReadPixels拿出来的是裸内存的渲染结果图,是一段图像的内存,是没有图像的宽度高度、图像类型等等属性数据的,直接拿着这样一块内存给ImageWatch是没法显示的(废话,给谁都显示不了)。
我们先来看看ImageWatch针对裸内存图像数据的操作指令:
- @mem(address, type, channels, width, height, stride)
address:图像内存地址,一般是个指针。 type:像素数据类型,如果是一张颜色图一般会是UINT8,如果是一张深度图一般会是FLOAT16。 channels:通道个数,如果是一张带透明的颜色图一般是4,如果是一张深度图一般是1。 width:图像宽度,像素。 height:图像高度,像素。 stride:图像中一行像素的内存长度,针对一些没有对齐好的像素宽度,内存中有可能会存在Padding数据用来做对齐,我们在读取完一行的像素后,要跳过这些Padding数据才能读取下一行。stride的值一般为width*channels+padding。
现在,假设我们准备就绪,有以下的这些伪代码:
1 | // Framebuffer size |
假设我们ReadPixels取出的是最终渲染的Color Attachment上的图像数据,数据类型是RGBA,那么我们在ImageWatch窗口里的监视模式下(选中Watch),输入下面的操作指令:
- @mem(raw, UINT8, 4, fbWidth, fbHeight, fbWidth*4)
就能在右侧的显示中看到我们内存中的图像数据了,放大到最大还可以看到每个像素上的RGBA值。
其他基本功能
@mem操作的结果可以认为是一张能被ImageWatch正确识别的图像类型,我们可以把@mem的输出结果作为其他操作的输入。
ImageWatch还支持的一些基本操作有:
- **@band(img, number)**提取输入图像中第number通道的图像。
- **@thresh(img, threshold)**对输入图像做二值化处理,大于阈值的将像素赋值为1,小于阈值的将像素赋值为0。
- **@clamp(img, min, max)**用给定的最小值和最大值对输入图像做截断。
- **@abs(img)**将输入图像中的像素取绝对值。
- **@scale(img, factor)**将输入图像中的所有像素的像素值乘以factor。
- **@norm8(img)、@norm16(img)**将输入图像的像素值乘以1/255、1/65535。
- **@fliph(img)、@flipv(img)、@flipd(img)**将输入图像沿水平、垂直、对角线方向进行翻转。
- **@rot90(img)、@rot180(img)、@rot270(img)**将输入图像沿顺时针方向旋转90度、180度、270度。
- **@diff(img0, img1)**输入图像0和1,逐像素执行img0–img1得到输出图像。
- **@file(path)**从指定路径中加载图片,例如从D盘的temp文件夹下加载test.png:@file(“d:\temp\debug.png”)。
比如,我们想在ImageWatch的窗口里直接对ReadPixels出来的结果做一下水平镜像,我们就可以用下面的操作指令来达到目的:
- @fliph(@mem(raw, UINT8, 4, fbWidth, fbHeight, fbWidth*4))
协议
本文以上内容遵循CC BY-ND 4.0协议,署名-禁止演绎。
转载请注明出处:https://tis.ac.cn/blog/kongdeyou/imagewatch/
并署名:kongdeyou(https://tis.ac.cn/blog/author/kongdeyou/)
后记
2021年1月24日 周日 阴
还有大约2.5周才到春节
完稿于2021年1月25日13:20