ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VisionPro二次开发:图片保存与图形标注的完整实现方案

VisionPro二次开发:图片保存与图形标注的完整实现方案 做康耐视VisionPro视觉检测项目图片保存算是最基础的功能但也是最容易被低估的环节。我刚开始带项目时以为保存一张图片就是对话框加个Bitmap.Save结果在QuickBuild里右键能存换成C#二次开发就各种出问题尤其是要给检测结果图叠加定位框、测量线这些“图形标注”再保存折腾了一整天才搞清楚CogCompositeImage、CogImageConvertTool、CogImageFileTool这三个对象是怎么配合的。这篇文章把VisionPro图片的打开、保存、以及带图形格式保存的完整方案理一遍代码都是可以直接抄进自己项目里的适合正在做VisionPro二次开发、或者被QuickBuild脚本卡住的朋友。1. 动手之前先搞懂VisionPro的图像对象体系1.1 灰度图与彩色图的本质区别VisionPro里的图像并不是System.Drawing.Bitmap而是一个CogImage派生对象。最常见的两种是CogImage8Grey和CogImage24PlanarColor。CogImage8Grey就是8位灰度图每个像素0到255工业相机默认输出大多是这种格式尤其是黑白相机。很多彩色相机在未配置好格式时采集回来的也可能是灰度图所以你会发现QuickBuild里显示的界面是黑白的。CogImage8Grey的优点是内存占用小、处理速度快对定位、灰度检测这些算法非常友好。CogImage24PlanarColor是24位真彩色图但它的坑在于“Planar”这个词。它内部不是常见的R、G、B像素交错排列而是把红色平面、绿色平面、蓝色平面分开存储。这意味着你不能直接把它的像素数据塞给System.Drawing.Bitmap去new不然颜色会完全乱掉。想要从CogImage24PlanarColor得到普通Bitmap必须通过CogImageConvertTool转一次或者调用它的专用接口把三个平面合到一起。做图片保存前第一步不是写代码而是先确认你手里拿到的CogImage到底是什么类型。用调试器看一眼Image对象或者直接打印image.GetType().Name这一步能省掉后面一大半的排查时间。很多时候现场反馈“保存出来的图是花的”一查发现是灰度图转彩色图时Conversion参数选错了根本原因就是没搞清楚源图像格式。1.2 为什么“保存”前总要通过转换工具CogImageFileTool虽然能直接保存大多数CogImage派生对象但遇到CogCompositeImage这种组合对象就不行了。CogCompositeImage是什么呢你可以把它理解成一个画板上面叠了一层原始图像又叠了一层图形标注比如十字线、检测框、圆弧、文字。它在显示上很好用但并不是一种标准位图格式CogImageFileTool不认识它。所以带图形保存的操作链必然是这样CogCompositeImage先通过CogImageConvertTool转成CogImage24PlanarColor再把这个转换结果交给CogImageFileTool写入磁盘。三步缺一不可顺序也不能乱。还有一个经验是CogImageConvertTool的Conversion参数常用CogImageConversionConstants.Color输出彩色图用Gray输出灰度图。如果你把彩色组合图用Gray转保存出来画面会变成灰度效果图形颜色信息直接丢了。这些细节官方文档写得绕项目里踩一次就记住了。2. 图片打开与保存的标准姿势CogImageFileTool2.1 保存单张图像的完整代码与执行流程CogImageFileTool是VisionPro专门负责图片读写的工具在QuickBuild里加一个也能用但二次开发时代码操作更灵活。保存一张图标准流程是四步初始化工具、给图像、打开文件流、执行保存。using Cognex.VisionPro; using Cognex.VisionPro.ImageFile; CogImageFileTool saveTool new CogImageFileTool(); saveTool.InputImage cogImage; // cogImage 是 CogImage 派生对象 // 第二个参数是打开模式Write表示写入 saveTool.Operator.Open(D:\Result\sample.bmp, CogImageFileModeConstants.Write); saveTool.Run(); saveTool.Operator.Close();注意最后那行Operator.Close()千万别省。Close的作用是释放文件句柄不调用的话后续对同一个文件做写入或删除时会报“文件被占用”。我见过不少同事在循环里反复保存图片跑了几百张后突然报错就是文件句柄没有释放干净。如果只想保存当前这帧的原始图不做任何叠加处理用上面的代码就够了。要特别提醒的是InputImage必须在Open之前赋值否则Run的时候工具没有输入图像保存出来的是一张黑图或者直接报异常。2.2 保存格式怎么选bmp、jpg、png、tifVisionPro判断图片格式不是靠某个参数指定而是看你Open路径的后缀名。比如写“.bmp”就是BMP格式写“.png”就是PNG格式。所以你想保存什么格式直接在路径里控制就行了不用额外设置属性。格式压缩方式画质体积适用场景BMP无压缩无损大调试阶段、需要逐像素还原PNG无损压缩无损较小检测结果存档、带图形叠加重叠保存JPG有损压缩有损小报表贴图、远程传输TIFF可选压缩无损为主大科研级图像、需要保留附加信息我的建议是现场检测结果图优先存PNG因为无损压缩还能把体积控制在比BMP小不少调试阶段为了省事可以存BMP只有明确要发报告、贴邮件时才用JPG。JPG的压缩对文字和细线非常不友好你把检测框和数值标在图上再存成JPG放大看边缘全是马赛克一样的色块这就是很多人问“图片保存后失真怎么办”的根源。2.3 加载已有图片的两种方式图片加载在二次开发里也很常用比如离线跑历史图片、重新分析NG样本。用CogImageFileTool读取图片是标准做法模式和保存类似只是Open时传Read。CogImageFileTool loadTool new CogImageFileTool(); if (File.Exists(imagePath)) { loadTool.Operator.Open(imagePath, CogImageFileModeConstants.Read); loadTool.Run(); CogImage loadedImage loadTool.OutputImage; loadTool.Operator.Close(); }这样拿到的loadedImage可以直接往下游工具传进行定位、测量、检测和在QuickBuild里拖进来的图片没有本质区别。另一种方式更朴素直接把图片路径赋值给CogImageFileTool的路径属性让它在QuickBuild中运行一次。这种方式适合手动测试不适合程序化批量处理。还有一个细节读取的图片如果是JPG经过压缩后图像细节已经有损失对高精度测量会有影响。做尺寸回溯分析时建议用BMP或PNG的存档JPG只适合人眼看个大概。3. 带图形的保存把检测结果烙进图片里3.1 图形从哪来LastRunRecord与ToolGroup图形记录带图形保存意思不是保存成PSD或者SVG那种矢量工程文件而是把检测工具画出来的十字线、定位框、找圆线圈、文字标签这些图形标注和原始图像合在一起再保存成一张普通位图。客户要的结果图、追溯系统里的NG截图都是这种形态。VisionPro里工具运行后每个工具的显示图形都有记录这些记录存放在ICogRecord里。你可以在QuickBuild界面看到检测框是因为画面控件的显示层读取了工具的ICogRecord。要在代码里抓取这些图形最直接的办法是调用ToolGroup的CreateLastRunRecord方法。ICogRecord rootRecord mToolGroup.CreateLastRunRecord();这个rootRecord是一个树形结构里面有各个工具的图形集合。要注意的是CreateLastRunRecord必须在ToolGroup运行完一次之后调用而且最好是在你希望抓取图形的那次运行之后。如果你在Run之前调用记录里是空的后面叠加图形自然不会出现在保存结果里。拿到根记录后需要遍历它下面所有子记录的Graphics集合。因为工具多了之后记录嵌套层级比较深我建议写一个递归方法把所有ICogGraphic对象收集到一个CogGraphicCollection里再统一交给图像合成器。3.2 合成并保存CogCompositeImage加转换工具三步曲代码带图形保存的完整代码是本文的核心我直接给完整可用的版本关键位置有注释using Cognex.VisionPro; using Cognex.VisionPro.ImageFile; using Cognex.VisionPro.Operations; // 假设 mToolGroup 是已经运行过的ToolGroupimage是当前帧图像 // 步骤1把原始图像放进CogCompositeImage CogCompositeImage composite new CogCompositeImage(); composite.AddImage(image); // 步骤2从ToolGroup收集图形加到组合图像上 ICogRecord rootRecord mToolGroup.CreateLastRunRecord(); CogGraphicCollection graphics CollectGraphics(rootRecord, new CogGraphicCollection()); composite.AddGraphics(graphics, false); // 步骤3组合图像不能直接保存转成24位彩色图 CogImageConvertTool convertTool new CogImageConvertTool(); convertTool.InputImage composite; convertTool.Conversion CogImageConversionConstants.Color; convertTool.Run(); // 步骤4保存成PNG CogImageFileTool saveTool new CogImageFileTool(); saveTool.InputImage convertTool.OutputImage; saveTool.Operator.Open(D:\Result\result.png, CogImageFileModeConstants.Write); saveTool.Run(); saveTool.Operator.Close();递归收集图形的方法private CogGraphicCollection CollectGraphics(ICogRecord record, CogGraphicCollection graphics) { if (record null) { return graphics; } if (record.Graphics ! null) { foreach (ICogGraphic graphic in record.Graphics) { graphics.Add((ICogGraphic)graphic); } } if (record.SubRecords ! null) { foreach (ICogRecord subRecord in record.SubRecords) { CollectGraphics(subRecord, graphics); } } return graphics; }几个关键细节AddGraphics的第二个参数很关键传false表示图形坐标跟随图像传true表示图形固定在显示区域。我们保存检测结果当然要选false否则图形不会跟着图像的坐标走保存出来图形位置会错乱。ConvertTool必须显式指定Conversion为Color。因为CogCompositeImage叠加图形后本质上有RGB信息不转成彩色输出后面存图容易得到一张灰度图。另外我建议保存路径用PNG后缀。图形里的文字、线条这些边缘信息对压缩算法特别敏感PNG无损压缩能最大程度保真。3.3 坐标偏了怎么办图形坐标系换算的坑很多项目不是单一相机直接定位尤其做了标定之后工具的图形坐标可能存放在根坐标系下而这个坐标系和图像的像素坐标系不是一回事。直接AddGraphics就会出现“图形明明在界面显示是对的保存出来却偏移甚至消失”的诡异现象。原因很简单CogCompositeImage叠加图形时默认按像素坐标来处理而标定后的坐标可能是经过变换的物理坐标。处理方法通常是拿到图像坐标空间和输出坐标空间之间的变换矩阵把图形顶点映射回像素空间再加进去。不同版本的API差异较大我在这里说一个通用思路通过image.GetTransform拿到坐标变换然后遍历图形集合对每个顶点做映射。如果你的现场图形数据量不大最稳妥的方案是把处理后的结果图形显示在CogRecordDisplay上然后用Display自带的保存方法所见即所得。// 方式二利用显示控件自带的保存能力 // 前提是界面上已经用CogRecordDisplay显示了带有图形的结果 cogRecordDisplay.SaveImageAs(D:\Result\display_result.bmp, CogImageFileModeConstants.Write);这种方式的优点是不用自己处理坐标映射尤其适合多相机标定、坐标系嵌套的情况。缺点是有UI依赖无人值守的后台程序不好用。如果你做的是带操作界面的上位机我强烈建议直接走Display保存省心又不容易出错。我之前做过一个项目系统做了九宫格标定PMAlign工具的图形全部在根坐标系下直接AddGraphics保存后图形偏了差不多半个画面。后来改成Display保存问题立刻消失。从那以后凡是我给客户做上位机只要界面上有实时画面我就优先用Display方案只有纯后台批量处理才用CogCompositeImage方案。4. 常见问题与排查技巧实录4.1 常见问题速查表现象可能原因解决方法保存出来是黑图InputImage未赋值或图像IsAvailable为false检查输入图像是否有效保存出来没有检测图形忘记AddGraphics或CreateLastRunRecord调用时机在Run之前在Run之后获取记录确认递归收集到图形图形位置偏移坐标系不一致图形用了根坐标而图像是像素坐标做坐标映射或改用CogRecordDisplay保存JPG图片文字边缘模糊有损压缩导致改用PNG或BMP循环保存中报文件占用没有调用Operator.Close用try/finally确保Close中文路径下保存失败VisionPro对中文路径支持欠佳使用纯英文路径4.2 我踩过的几个坑和解决办法第一个坑是漏掉Operator.Close。刚开始写批量保存脚本时我在for循环里不断new CogImageFileTool但忘记Close结果程序跑几十张后抛异常。后来我把保存逻辑单独封装成一个方法用try/finally包裹在finally里Close再也没有出现过这个报错。第二个坑是ToolGroup的CreateLastRunRecord返回空图形。我一开始特别困惑界面上明明有检测框代码里却拿不到。后来调试发现我是在ToolGroup执行Run之前就调了CreateLastRunRecord当然什么都没有。正确顺序是先Run再获取记录最后合成保存。如果你需要在一次运行结束后自动触发保存建议把保存逻辑放在脚本的PostRun事件里或者Job的AfterRun事件里这时候记录一定已经生成好了。第三个坑是图形收集不全。递归CollectGraphics时有个小细节子记录里除了工具图形可能还包含一些额外的组节点图形可能挂在多个层级上。最初我只遍历了第一层SubRecords导致部分图形缺失。改成递归后完整性和稳定性都上来了。如果你保存的图总是少某些检测框优先检查递归方法是否足够深打印一下收集到的图形数量对比界面上实际显示的个数。第四个坑是保存路径拼接。项目现场经常需要按日期和班次分目录保存一开始我用的是字符串拼接结果一天下来目录结构乱七八糟。后来统一用DateTime格式化生成目录和文件名例如“D:\Result\20250101\080000_123_ok.png”既保证唯一性也方便后续追溯。文件名里的时间戳精确到毫秒基本不会重名。4.3 关于“图片失真”的进一步说明很多做上位机的工程师习惯用JPG因为有现成的照片处理经验。但机器视觉行业里JPG一定要慎用尤其在带图形保存这条链路上。JPG的压缩算法会对图像做有损编码检测框边缘的直线会变得模糊文字数字的边缘会出现伪影这对人看还好对后续做OCR或者测量分析就是灾难。如果你要保存的图片会再次进入检测流程或者需要做尺寸复核建议一律用PNG或BMP。如果只是给客户发个报告截图JPG倒是可以接受。我的做法是保存两份原始图像用BMP或PNG存档报表用JPG压缩一份。这样既不浪费磁盘空间又能保证数据可复现。4.4 给二次开发项目的封装建议带图形保存这个功能我建议所有VisionPro二次开发项目都封装成一个独立工具类因为十个项目里至少有八个需要这个能力。类里面放几个方法就够用保存原始图像、保存带图形的图像、加载历史图像。输入参数包括图像对象、ToolGroup引用、保存路径、格式后缀等。封装的好处很直接后续维护时只需要改一个地方不需要满项目搜保存代码。我在好几个项目里复用了同一套封装每次新项目直接拷贝改路径命名规则就行稳定性和效率都提升了不少。如果你担心坐标问题可以在封装里加一个开关允许调用方指定是用CogCompositeImage还是CogRecordDisplay方案这样两种场景都能覆盖。最后分享一个我的个人体会VisionPro这套东西很多功能在QuickBuild里点点鼠标就能实现但一旦跨入二次开发你必须理解它背后对象之间的关系而不是靠猜。图片保存这条路最核心的就是CogImage、CogCompositeImage、CogImageConvertTool、CogImageFileTool这一串链路理解了这条链后面再做其他功能比如报表输出、批次存图、远程检图都会顺很多。希望这篇整理能帮你少走几个弯路。
返回列表