
简介这份资源面向正在学习 HALCON 与 C# 联合编程的开发者尤其是想掌握机器视觉中 CAD 读取与显示这一基础环节的初学者和进阶者。它解决的是如何在 C# 工程中调用 HALCON 的算子把 DXF 格式的 CAD 图纸加载并显示到界面上的问题属于图像处理入门到实战的典型场景。压缩包共 34 个文件约 452KB包含 9 个 cs 源码文件、3 个 exe 可执行程序、1 个 dxf 测试图纸、1 个 dll 动态库以及 csproj、sln、config、resx 等工程配置与资源文件结构完整可直接打开运行。已有 2135 人学习下载说明该示例在同类教程中具有一定参考价值。读者拿到后能获得一套可运行的完整工程其中附带测试用 CAD 文件方便对照源码理解 HALCON 读取与显示流程工程采用相对路径需将文件夹放到桌面运行若 halcondotnet.dll 引用异常也可自行添加本机对应引用便于快速排错与二次开发。1. 从一张 DXF 图纸说起HALCON 和 C# 联合编程读取 CAD 并显示到底在做什么产线上新来一套视觉检测工位机械工程师丢过来一份 DXF 图纸说“轮廓都在这上面你照着把相机拍到的工件边缘跟它对一对”。这时候你打开 Visual Studio新建一个 WinForm 工程引好 HALCON 的 .NET 程序集然后发现第一个问题就卡住了HALCON 里读 CAD 的算子叫什么读进来的是 XLD 轮廓还是 Region怎么把它画到 HWindowControl 上还不闪这就是 HALCON 和 C# 联合编程之读取一个CAD并显示要解决的核心链路——用 C# 做界面和工程骨架用 HALCON 做几何数据的解析与渲染把一份 DXF/DWG 图纸变成可缩放、可叠加、可参与后续匹配的轮廓对象。它适合三类人做上位机视觉软件的 C# 开发者、需要把 CAD 轮廓当模板做形状匹配的算法工程师、以及要把图纸比对功能塞进现有 MES 客户端的集成人员。读完你应该能自己搭出一个最小可运行工程知道读 CAD 该用哪个算子、显示该走哪条渲染路径、以及为什么很多人第一次做出来轮廓是歪的。2. 读 CAD 之前先把三件事定下来算子、坐标系、显示容器2.1 HALCON 读 CAD 的算子选型read_contour_xld_dxf 与 read_polygon_xld_dxf 的区别HALCON 里跟 CAD 读取直接相关的算子不多最常用的是read_contour_xld_dxf它把 DXF 文件里的实体解析成 XLD 轮廓。另一个是read_polygon_xld_dxf它把圆弧、样条这类曲线离散成多边形折线。两者返回的都是 XLD但数据密度和后续用途完全不同。选型逻辑很简单如果你只是要显示、要目测比对用read_polygon_xld_dxf因为它把曲线打散成折线段渲染快、点数可控如果你要把 CAD 轮廓当形状匹配的模板或者要做亚像素级的边缘距离计算用read_contour_xld_dxf它保留圆弧和样条的解析表达fit_*系列算子还能反推出几何参数。// C# 侧调用 HALCON 读 DXF 的最小片段 HOperatorSet.GenEmptyObj(out HObject cadContour); // 读解析轮廓保留圆弧/样条 HOperatorSet.ReadContourXldDxf(out cadContour, part_a.dxf, new HTuple(), new HTuple()); // 若只要折线近似换成下面这行 // HOperatorSet.ReadPolygonXldDxf(out cadContour, part_a.dxf, // new HTuple(), new HTuple());逻辑说明ReadContourXldDxf的第三个参数是 DXF 的图层名筛选传空元组表示读所有图层第四个参数是控制点生成方式的通用参数一般留空走默认。参数说明文件名建议用绝对路径HALCON 对相对路径的解析依赖当前工作目录在 WinForm 里这个目录经常是bin\Debug很容易读不到文件却报“文件不存在”让你怀疑人生。2.2 坐标系对齐CAD 的 Y 轴朝上图像的 Y 轴朝下这是第一次做 CAD 显示最容易翻车的地方。DXF 文件遵循数学坐标系原点在左下Y 轴向上而 HALCON 的图像坐标系原点在左上Y 轴向下。你直接把 CAD 轮廓画到 HWindowControl 上得到的是一张上下镜像的图。工件本身对称时你看不出来一旦有方向性特征匹配就全错。常见做法是在读取后做一次仿射变换把 Y 轴翻过来。但更稳的做法是不要急着翻先把 CAD 轮廓和图像轮廓都放到同一个物理坐标系里用标定得到的像素当量做缩放再统一做一次刚体变换。下面这段是翻转的写法// 对读入的 CAD 轮廓做 Y 轴镜像适配图像坐标系 HObject cadFlipped; HOperatorSet.HomMat2dIdentity(out HTuple homMat); // 以轮廓中心为基准做镜像避免整体偏移 HOperatorSet.AreaCenterXld(cadContour, out HTuple area, out HTuple row, out HTuple col, out HTuple pointOrder); HOperatorSet.HomMat2dTranslate(homMat, -row, -col, out homMat); HOperatorSet.HomMat2dScale(homMat, 1.0, -1.0, out homMat); HOperatorSet.HomMat2dTranslate(homMat, row, col, out homMat); HOperatorSet.AffineTransContourXld(cadContour, out cadFlipped, homMat);逻辑说明先平移到轮廓中心再对 Y 做 -1 倍缩放实现镜像最后平移回去。参数说明AreaCenterXld返回的 row/col 是轮廓的几何中心用它做镜像基准比用原点稳因为 CAD 图纸的原点经常在图纸角落而不是工件中心。如果你跳过这一步直接scale轮廓会整体跑到窗口外面。2.3 显示容器HWindowControl 还是 HSmartWindowControlC# 里显示 HALCON 对象有两个控件可选。HWindowControl是老控件基于 GDI刷新逻辑要自己管缩放和平移得手写鼠标事件。HSmartWindowControl是新控件自带缩放、拖动、适应窗口底层还是 HALCON 的窗口但交互封装好了。做 CAD 显示我一般直接用HSmartWindowControl。原因很实际CAD 图纸的尺寸跨度大一张 A0 图纸和一个小零件轮廓可能差两个数量级没有内置缩放用户根本没法看。HSmartWindowControl的SetFullImagePart一行就能让轮廓铺满窗口省掉一堆坐标换算。// 在 HSmartWindowControl 上显示 CAD 轮廓 hSmartWindowControl1.HalconWindow.ClearWindow(); // 设置显示颜色和线宽 hSmartWindowControl1.HalconWindow.SetColor(green); hSmartWindowControl1.HalconWindow.SetLineWidth(2); // 让轮廓自适应窗口 hSmartWindowControl1.SetFullImagePart(cadFlipped); // 绘制 hSmartWindowControl1.HalconWindow.DispObj(cadFlipped);逻辑说明SetFullImagePart会根据对象的包围盒自动调整窗口的显示区域相当于把视图缩放到刚好容纳轮廓。参数说明SetColor传字符串颜色名HALCON 支持 green、red、blue 等SetLineWidth单位是像素CAD 轮廓线细设 1 到 2 比较合适太粗会盖住细节。注意DispObj之后如果窗口被重绘内容会丢需要在Paint事件里重新调一次或者用HSmartWindowControl的缓冲机制。3. 把读取和显示串成可运行工程从新建项目到轮廓上屏3.1 工程引用与初始化halcondotnet 的版本匹配新建一个 WinForm 项目后第一件事是加引用。HALCON 的 .NET 程序集叫halcondotnet.dll在安装目录的bin\dotnet35或bin\dotnet下。这里有个版本坑HALCON 的运行时版本和程序集版本必须一致你机器上装的是 20.11就不能引用 19.11 的 dll否则运行时报“无法加载 HALCON 库”。# 查看本机 HALCON 安装的版本目录示例路径按实际安装位置调整 ls C:\Program Files\MVTec\HALCON-20.11-Progress\bin\dotnet35 # 输出应包含 halcondotnet.dll逻辑说明先确认安装目录下的 dll 存在再在 Visual Studio 里“添加引用”指向它。参数说明dotnet35对应 .NET Framework 3.5 及以上dotnet目录对应 .NET Core/.NET 5。如果你的项目是 .NET 6要引dotnet目录下的版本引错了会在编译期就报类型找不到。初始化方面HALCON 的 .NET 封装在第一次调用算子时会自动加载本地库一般不需要手动Init。但如果你把程序部署到没装 HALCON 运行时的机器上需要把halcon.dll、hcanvas.dll等本地库一起拷过去并设置好PATH或放在 exe 同目录。3.2 读取 DXF 并转成可显示对象完整代码与逐行说明下面是一个完整的读取加显示方法放在 Form 的按钮事件里就能跑。private HObject _cadContour; // 保存读入的 CAD 轮廓供后续匹配用 private void btnLoadCad_Click(object sender, EventArgs e) { OpenFileDialog dlg new OpenFileDialog(); dlg.Filter DXF 文件|*.dxf; if (dlg.ShowDialog() ! DialogResult.OK) return; try { // 1. 读 DXF保留解析轮廓 HOperatorSet.ReadContourXldDxf(out _cadContour, dlg.FileName, new HTuple(), new HTuple()); // 2. 统计读到了多少条轮廓判断文件是否有效 HOperatorSet.CountObj(_cadContour, out HTuple count); if (count.I 0) { MessageBox.Show(DXF 中没有可识别的轮廓实体); return; } // 3. Y 轴镜像适配图像坐标系 HOperatorSet.AreaCenterXld(_cadContour, out HTuple area, out HTuple row, out HTuple col, out HTuple ptOrder); HOperatorSet.HomMat2dIdentity(out HTuple homMat); HOperatorSet.HomMat2dTranslate(homMat, -row, -col, out homMat); HOperatorSet.HomMat2dScale(homMat, 1.0, -1.0, out homMat); HOperatorSet.HomMat2dTranslate(homMat, row, col, out homMat); HOperatorSet.AffineTransContourXld(_cadContour, out HObject flipped, homMat); // 4. 显示 hSmartWindowControl1.HalconWindow.ClearWindow(); hSmartWindowControl1.HalconWindow.SetColor(green); hSmartWindowControl1.HalconWindow.SetLineWidth(2); hSmartWindowControl1.SetFullImagePart(flipped); hSmartWindowControl1.HalconWindow.DispObj(flipped); // 5. 更新状态 lblStatus.Text $已加载 {count.I} 条轮廓; } catch (HalconException hex) { MessageBox.Show(HALCON 异常: hex.GetErrorMessage()); } }逻辑说明第一步读文件第二步用CountObj做有效性检查因为 DXF 里可能只有标注、文字这些 HALCON 不解析的实体读出来是空的。第三步做镜像第四步显示第五步给界面反馈。参数说明CountObj返回的是对象元组里的对象个数对 XLD 轮廓来说就是轮廓条数AffineTransContourXld的输入输出都是 HObject变换矩阵是 2D 齐次矩阵顺序不能反先平移再缩放再平移回来。3.3 轮廓叠加显示把 CAD 和相机图像画在同一窗口实际项目里 CAD 轮廓很少单独显示通常要叠在相机拍的图像上做比对。做法是先显示图像再显示轮廓注意显示顺序和颜色区分。// 假设 image 是已经采集到的 HObject 图像 hSmartWindowControl1.HalconWindow.ClearWindow(); hSmartWindowControl1.HalconWindow.DispObj(image); // 先画图像 hSmartWindowControl1.HalconWindow.SetColor(red); hSmartWindowControl1.HalconWindow.SetDraw(margin); // 轮廓画在图像边缘 hSmartWindowControl1.HalconWindow.DispObj(_cadContour); // 再画 CAD 轮廓逻辑说明HALCON 窗口的绘制是覆盖式的后画的盖在先画的上面所以图像先画、轮廓后画。参数说明SetDraw(margin)让轮廓以边缘模式绘制不填充适合叠加如果设成fill轮廓会填充成实心把图像盖住。颜色上图像用灰度轮廓用红或绿对比最明显。4. 避坑与排查CAD 读取显示里最容易踩的五个坑4.1 轮廓读出来是空的但文件明明有图形现象ReadContourXldDxf不报错但CountObj返回 0窗口上什么都没有。原因DXF 里的实体类型 HALCON 不认。HALCON 主要解析 LINE、ARC、CIRCLE、POLYLINE、SPLINE 这几类如果你的图纸是块引用INSERT或者三维实体3DSOLID它读不出来。另一个常见原因是图层被冻结或关闭HALCON 默认只读可见图层。解决在 CAD 软件里把块炸开EXPLODE把三维实体转成二维投影确认所有目标图层都是打开且解冻状态。如果不想改原图可以在读取时指定图层名参数但前提是图层本身可见。4.2 轮廓显示位置偏移跟图像对不上现象CAD 轮廓能显示但跟相机图像里的工件错开一段距离。原因CAD 图纸的坐标原点和图像的像素原点没有建立对应关系。CAD 里的 (0,0) 可能在图纸左下角而图像 (0,0) 是左上角第一个像素两者之间差了一个平移加缩放。解决不要指望 CAD 和图像天然对齐。正确做法是用标定板或已知尺寸的工件算出像素当量和图像原点在 CAD 坐标系里的位置然后构造一个仿射矩阵把 CAD 轮廓变换到图像坐标系。这个矩阵通常包含缩放、旋转、平移三个分量用vector_to_hom_mat2d从至少三对对应点求出来。4.3 大图纸显示卡顿缩放时轮廓闪烁现象A0 幅面的 DXF 读进来有几万条轮廓DispObj一次要几百毫秒拖动缩放时画面撕裂。原因XLD 轮廓的每条线段都是一个独立对象对象数太多时 HALCON 的渲染管线压力大。另外HSmartWindowControl在拖动时会频繁重绘每次都重新DispObj全量轮廓自然卡。解决显示用的轮廓和匹配用的轮廓分开。显示时用read_polygon_xld_dxf读折线近似版点数少很多或者用gen_contour_polygon_xld对轮廓做抽稀。匹配时再用高精度的解析轮廓。另外把HSmartWindowControl的KeepAspectRatio设为 true减少重绘时的计算量。4.4 圆弧变成折线匹配精度下降现象用read_polygon_xld_dxf读进来的圆弧是一段段直线做形状匹配时得分偏低。原因read_polygon_xld_dxf会把圆弧按固定角度步长离散成折线步长默认值在曲率大的地方不够密圆弧看起来像多边形。解决如果后续要做高精度匹配改用read_contour_xld_dxf它保留圆弧的解析表达fit_circle_contour_xld能直接拟合出圆心和半径。如果必须用折线版在读取后对轮廓做smooth_contours_xld平滑或者用gen_polygons_xld重新以更小的公差逼近。4.5 部署到客户机报“找不到 halcon 库”现象开发机上跑得好好的拷到客户机双击 exe 就弹异常提示无法加载 halcondotnet 或 halcon。原因客户机没装 HALCON 运行时或者装了但版本跟编译时引用的 dll 不一致。HALCON 的 .NET 程序集是托管代码但它依赖一堆本地 dllhalcon.dll、hcanvas.dll、hdevengine.dll 等这些不会自动跟着走。解决把 HALCON 安装目录下bin\x64-win64里的本地库全部拷到 exe 同目录或者用 HALCON 提供的运行时安装包在客户机装一遍。注意位数要匹配64 位程序配 64 位库32 位配 32 位混了会在加载时直接崩。5. 让 CAD 轮廓真正参与匹配从显示到形状模板的进阶用法显示只是第一步CAD 轮廓的价值在于它能当形状匹配的模板省去从图像里手动抠模板的麻烦。下面说一个我常用的做法把 CAD 轮廓转成形状模板然后用它去匹配相机图像。核心思路是先用create_shape_model_xld从 CAD 轮廓生成模板再用find_shape_model在图像里找。这里有个关键点CAD 轮廓的坐标是物理尺寸图像是像素尺寸生成模板前必须把 CAD 轮廓缩放到像素当量。// 假设 pixelSize 是标定得到的像素当量mm/pixel double pixelSize 0.05; // 把 CAD 轮廓缩放到像素坐标系 HOperatorSet.HomMat2dIdentity(out HTuple scaleMat); HOperatorSet.HomMat2dScale(scaleMat, 1.0 / pixelSize, 1.0 / pixelSize, out scaleMat); HOperatorSet.AffineTransContourXld(_cadContour, out HObject cadInPixel, scaleMat); // 从 CAD 轮廓生成形状模板 HOperatorSet.CreateShapeModelXld(cadInPixel, auto, -0.39, 0.79, auto, auto, use_polarity, auto, auto, out HTuple modelId); // 在图像里查找 HOperatorSet.FindShapeModel(image, modelId, -0.39, 0.79, 0.5, 1, 0.5, least_squares, 0, 0.9, out HTuple matchRow, out HTuple matchCol, out HTuple matchAngle, out HTuple matchScore);逻辑说明第一步把 CAD 轮廓从毫米坐标缩放到像素坐标缩放因子是像素当量的倒数。第二步CreateShapeModelXld从 XLD 轮廓直接生成模板不需要图像。第三步在图像里搜索。参数说明CreateShapeModelXld的第二个参数是金字塔层数auto 让 HALCON 自动决定第三、四个参数是起始和终止角度单位是弧度-0.39 到 0.79 约等于 -22 度到 45 度use_polarity表示匹配时考虑对比度方向如果工件有反光导致对比度翻转改成ignore_global_polarity。FindShapeModel的 0.5 是最小匹配分数低于这个值的结果不返回实际项目里根据误检率调到 0.6 到 0.8 之间。验证匹配效果时把匹配到的位姿用AffineTransContourXld变换到图像上跟原图叠加看轮廓是否贴合。如果贴合但分数不高检查像素当量是否准确如果分数高但轮廓偏移检查 CAD 轮廓的镜像和旋转是否处理对了。我自己的习惯是每次拿到新图纸先用read_contour_xld_dxf读一遍在窗口里单独显示确认轮廓完整、方向正确再去做缩放和模板生成。这一步花两分钟能省掉后面半小时的排查。CAD 读取这件事坑不在代码在坐标系和单位把这两个对齐了剩下的都是体力活。希望帮到你。本文还有配套的精品资源点击获取