前言
对于 .NET 开发来说,想要快速上手 OpenCV,OpenCVSharp 是一个非常友好的起点。它是 OpenCV 官方 C++ 库的高质量 .NET 封装,由开发 shimat 维护,并提供了完整的示例项目,非常适合初学者边看边练。
- OpenCVSharp 主仓库:github.com/shimat/open… 、
- 配套示例项目:github.com/shimat/open…
建议第一步:直接运行这些示例,直观感受 OpenCVSharp 能做什么。不必一开始就深究算法原理——先会用,再理解。初期做个"调包侠"完全没问题,等真正需要优化或调试时,再回头研究底层也不迟。
实战示例:基于 ArUco 标记的检测与透视变换(C# / .NET)
本节我们将通过一个经典案例,学习如何使用 OpenCVSharp 检测 ArUco 标记,并基于其位置进行透视校正(Perspective Transformation),常用于图像对齐、虚拟贴图、AR 等场景。
1、效果预览
原始图像如下所示,画面中包含 4 个特殊的黑白方块图案:
程序首先识别出这四个标记的位置:
然后,以这四个标记为参考点,执行透视变换,将中间区域"拉平"为标准矩形:
这些黑白方块就是 ArUco 标记——一种高鲁棒性的二进制编码标记,广泛用于相机标定、位姿估计和增强现实。例如,你可以用它来"框住"一张照片的位置,然后动态替换其中的内容。
注意:目前 OpenCVSharp 支持 ArUco 检测,但暂不支持生成标记。不过检测功能已足够完成多数应用。
2、代码解析
步骤一:定义目标标记 ID
const int upperLeftMarkerId = 160;
const int upperRightMarkerId = 268;
const int lowerRightMarkerId = 176;
const int lowerLeftMarkerId = 168;
我们预先知道这四个角分别对应哪些 ArUco ID,后续将据此定位区域。
步骤二:加载图像并配置检测参数
using var src = Cv2.ImRead(ImagePath);
var detectorParameters = new DetectorParameters
{
CornerRefinementMethod = CornerRefineMethod.Subpix,
CornerRefinementWinSize = 9
};
-
CornerRefinementMethod.Subpix:启用子像素级角点优化,提升定位精度。 -
CornerRefinementWinSize = 9:使用 9×9 的窗口进行局部优化。
步骤三:加载预定义字典并检测标记
using var dictionary = CvAruco.GetPredefinedDictionary(PredefinedDictionaryName.Dict4X4_1000);
CvAruco.DetectMarkers(
src,
dictionary,
out var corners,
out var ids,
detectorParameters,
out var rejectedPoints
);
参数说明:
-
src:输入图像。 -
dictionary:使用4x4_1000字典(即 4×4 编码,共 1000 种唯一标记)。 -
corners:输出每个标记的 4 个角点(按顺时针顺序:左上 → 右上 → 右下 → 左下)。 -
ids:对应每个标记的唯一 ID。 -
rejectedPoints:被初步识别但最终排除的候选区域。
调试建议:打断点观察
corners和ids,你会发现每个标记确实由 4 个点组成,且 ID 与预期一致。
步骤四:可视化检测结果
using var detectedMarkers = src.Clone();
CvAruco.DrawDetectedMarkers(detectedMarkers, corners, ids, Scalar.Crimson);
在图像上绘制出所有检测到的标记及其 ID,便于验证。
步骤五:提取四个关键角点
var upperLeftIndex = Array.FindIndex(ids, id => id == upperLeftMarkerId);
var upperRightIndex = Array.FindIndex(ids, id => id == upperRightMarkerId);
var lowerRightIndex = Array.FindIndex(ids, id => id == lowerRightMarkerId);
var lowerLeftIndex = Array.FindIndex(ids, id => id == lowerLeftMarkerId);
if (upperLeftIndex < 0 || upperRightIndex < 0 ||
lowerRightIndex < 0 || lowerLeftIndex < 0)
{
// 若任一标记未找到,直接退出
return;
}
// 提取每个标记的特定角点
var upperLeftPixel = corners[upperLeftIndex][0]; // 左上标记的左上角
var upperRightPixel = corners[upperRightIndex][1]; // 右上标记的右上角
var lowerRightPixel = corners[lowerRightIndex][2]; // 右下标记的右下角
var lowerLeftPixel = corners[lowerLeftIndex][3]; // 左下标记的左下角
var sourceCoordinates = new List<Point2f>
{
upperLeftPixel, upperRightPixel, lowerRightPixel, lowerLeftPixel
};
这样就构建了一个由四个 ArUco 标记"外角"组成的四边形区域。
步骤六:定义目标坐标并计算透视变换
var destinationCoordinates = new List<Point2f>
{
new Point2f(0, 0), // 左上
new Point2f(1024, 0), // 右上
new Point2f(1024, 1024), // 右下
new Point2f(0, 1024) // 左下
};
using var transform = Cv2.GetPerspectiveTransform(sourceCoordinates, destinationCoordinates);
GetPerspectiveTransform返回一个 3×3 的变换矩阵,将任意四边形映射为指定矩形。
步骤七:应用透视变换
using var normalizedImage = new Mat();
Cv2.WarpPerspective(src, normalizedImage, transform, new Size(1024, 1024));
最终得到一张 1024×1024 像素的标准正视图,中间区域被完美"拉直"。
总结
通过这个例子,我们完成了:
1、使用 OpenCVSharp 检测 ArUco 标记;
2、根据标记 ID 定位关键角点;
3、构建源与目标坐标对;
4、应用透视变换实现图像校正。
整个过程无需深入理解 ArUco 编码原理或透视几何公式,只需掌握 API 的基本用法即可快速实现功能。这正是 OpenCVSharp 对 .NET 开发的最大价值:让计算机视觉变得触手可及。
下一步建议:尝试更换不同图像、调整字典类型,或结合摄像头实现实时检测!
如需完整可运行代码,请参考官方示例库中的 ArucoDetection 相关样例。
关键词
OpenCVSharp、ArUco标记检测、透视变换、计算机视觉、 图像校正、CvAruco、WarpPerspective、角点检测、预定义字典
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
来源:cnblogs.com/mingupupu/p/19219404