1. 这个工具解决的痛点,以及为什么是 WPF + OpenCV 组合
我之前做产线质检上位机的时候,收到过太多类似这种需求:帮我量一下图上这两个焊点的距离,算一下这条划痕大概有多长,看下缺陷点到边缘的像素间距能不能控制在某个范围内。项目技术栈本来就很明确——桌面端用 WPF,图像处理和视觉识别统一走 OpenCV,而且整个产品线都锁在 .NET 4.6.1 上。临时要求频繁来了之后,每次开 PS 拉辅助线再记坐标显然不是办法,不同人截图比例不一样,量出来的结果口径也乱七八糟。
所以我就想写一个轻量工具:加载一张图,鼠标点两个点,立刻显示两点之间的像素距离;再进一步,如果图里有一段已知真实长度的参考物,比如 PCB 上两个固定孔中心距 10 mm,那我就能顺手把像素单位换算成毫米单位。测量结果可以直接复制到剪贴板,方便贴到质量报告里。这套需求放在 WPF 上非常顺,界面布局、鼠标交互、绘制叠加线、状态栏回显都是 WPF 的强项;而 OpenCV 的价值在于图像读取、像素访问和后续想要自动吸附边缘、亚像素精化特征点时都是现成的算法基础。整套工具做完大概一个周末的量,但省下来的时间远远超过开发成本。
这个工具适合谁参考?一种是做 WPF 上位机、经常被临时塞来“量尺寸”需求的开发;另一种是在学 OpenCV 图像处理,想看看 Mat 怎么和 WPF 的 BitmapSource 结合、鼠标落点怎么准确换算到图像真实像素坐标的人。如果你正好也在忙类似的图测工具,这篇文章里我会把容易翻车的坐标系换算、DPI 陷阱、OpenCvSharp 依赖部署这些老坑一个一个讲清楚,内容不会太长篇大论,都是实际调试过的东西。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. .NET 4.6.1 下接 OpenCV:先解决依赖和图像互转
2.1 为什么我选 OpenCvSharp 而不是 C++ 封装
项目限定 .NET Framework 4.6.1 之后,可选的 OpenCV 接入方式就不多了。官方 OpenCV 本身是 C++ 接口,想在 WPF 里直接调必须包一层 C++/CLI,维护成本高,每次 OpenCV 升级要重编一次底层,得不偿失。C# 这边的选择基本是 Emgu CV 和 OpenCvSharp 两家。
实际对比下来,我更推荐 OpenCvSharp。它的 API 风格接近原生 OpenCV,Cv2.ImRead、Cv2.Canny 这些方法写起来几乎和 C++ 一样;Emgu 也不是不行,但 Emgu 的历史包结构在 .NET Framework 项目里相对偏重,我遇到过几次 Image 类型在不同版本间的迁移问题。OpenCvSharp 的使用方式简单得多,用 NuGet 装一个 OpenCvSharp4.Windows 包就够了,它会同时带来托管 DLL 和对应平台的 native 运行时文件。如果你担心包体积,可以只装 OpenCvSharp4 再单独管理 runtime,开发期没这个必要,直接上 Windows 预编译包最快。需要强调,这里的“OpenCV”不是说要跑多重的模型,而是承担图像解码、像素级访问、边缘提取这些脏活;界面渲染和鼠标事件则完全不用碰 OpenCV。
2.2 OpenCvSharp 在 .NET 4.6.1 项目里的实际部署
写 .NET 6 以上项目的朋友可能体会不到,.NET Framework 4.6.1 下引入 OpenCvSharp 时最容易出问题的不是编译,而是运行时崩溃:代码能编译过,一启动就报找不到 OpenCvSharpExtern.dll 或者 DllNotFoundException。原因是 OpenCvSharp 的 native 层并没有像托管程序集那样被 NuGet 自动引用并复制到输出目录,很多时候它被放到了 runtimes/win-x64/native 这样的子目录,如果项目没正确触发 copy-local 逻辑,应用就找不着底层 DLL。
我当时的处理办法是,第一次生成后去 bin\Debug 或 bin\Release 目录看一眼,确认是否存在 OpenCvSharpExtern.dll;没有的话就手动把 packages\opencvsharp4\...\runtimes\win-x64\native 下的几个文件拷到输出目录,再把“复制到输出目录”属性改成“如果较新则复制”。这个问题在开发机上不容易暴露,换一台没装过 OpenCV 的电脑跑才会炸出来。所以发布前最好在干净环境里做一次冒烟测试,别默认自己的开发环境就是客户机器的环境。
2.3 Mat 转 BitmapSource 的经典 BGR/BGRA 坑
OpenCV 默认读图通道顺序是 BGR,WPF 的 BitmapSource 通常需要 BGRA 或 Bgra32 格式,直接拿来用颜色会变得很奇怪,红色和蓝色对调。这个问题几乎每个第一次从 Mat 显示图像到 WPF 的人都会踩一回。正确流程是
